diff --git a/docs/docsrc/examples/examples_presets/detailing_test.go b/docs/docsrc/examples/examples_presets/detailing_test.go index 6005d760f..307522848 100644 --- a/docs/docsrc/examples/examples_presets/detailing_test.go +++ b/docs/docsrc/examples/examples_presets/detailing_test.go @@ -290,3 +290,50 @@ func TestPresetsDetailSectionLabel(t *testing.T) { }) } } + +func TestPresetsDetailSectionCancel(t *testing.T) { + pb := presets.New().DataOperator(gorm2op.DataOperator(TestDB)) + PresetsDetailSectionLabel(pb, TestDB) + + cases := []multipartestutils.TestCase{ + { + Name: "cancel section list", + Debug: true, + ReqFunc: func() *http.Request { + detailData.TruncatePut(SqlDB) + return multipartestutils.NewMultipartBuilder(). + PageURL("/customers"). + Query("__execute_event__", "presets_Detailing_List_Field_Save"). + Query("detailField", "CreditCards"). + Query("detailListFieldSaveBtn_CreditCards", "0"). + Query("id", "12"). + Query("isCancel", "true"). + BuildEventFuncRequest() + }, + ExpectPortalUpdate0ContainsInOrder: []string{"mdi-square-edit-outline"}, + ExpectPortalUpdate0NotContains: []string{"Save"}, + }, + { + Name: "cancel section", + Debug: true, + ReqFunc: func() *http.Request { + detailData.TruncatePut(SqlDB) + return multipartestutils.NewMultipartBuilder(). + PageURL("/customers"). + Query("__execute_event__", "presets_Detailing_Field_Save"). + Query("detailField", "section2"). + Query("id", "12"). + Query("isCancel", "true"). + BuildEventFuncRequest() + }, + ExpectPortalUpdate0ContainsInOrder: []string{"mdi-square-edit-outline"}, + ExpectPortalUpdate0NotContains: []string{"Save"}, + }, + } + + for _, c := range cases { + t.Run(c.Name, func(t *testing.T) { + multipartestutils.RunCase(t, c, pb) + }) + } +} diff --git a/go.mod b/go.mod index 036976d1b..769a372d7 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/pkg/errors v0.9.1 github.com/pquerna/otp v1.4.0 github.com/qor/oss v0.0.0-20240729105053-88484a799a79 - github.com/qor5/web/v3 v3.0.5 + github.com/qor5/web/v3 v3.0.6-0.20240806032534-6c0e81ec7b12 github.com/qor5/x/v3 v3.0.7-0.20240805015803-ab891c3771bb github.com/samber/lo v1.46.0 github.com/shurcooL/sanitized_anchor_name v1.0.0 diff --git a/go.sum b/go.sum index 3e2ddb11d..db10dc7eb 100644 --- a/go.sum +++ b/go.sum @@ -319,6 +319,8 @@ github.com/qor5/web v1.3.2 h1:zw796YJeDLe8vRwGR1cM+uS1ZuSkPutchBEXv2GgOhI= github.com/qor5/web v1.3.2/go.mod h1:LszskQJbFQDJwOeZC6j6afOiHxxyjrzz8B3zuBwfgKQ= github.com/qor5/web/v3 v3.0.5 h1:v+HfJ3diZ80JHmy2PriRicwuNbmvWqsP8Kjjr61aacQ= github.com/qor5/web/v3 v3.0.5/go.mod h1:32vdHHcZb2JimlcaclW9hLUyimdXjrllZDHTh3rl6d0= +github.com/qor5/web/v3 v3.0.6-0.20240806032534-6c0e81ec7b12 h1:CUJ8p/m0HFw/cizcrvUfKt7BkPvYMaVUv2qXDfs00ew= +github.com/qor5/web/v3 v3.0.6-0.20240806032534-6c0e81ec7b12/go.mod h1:32vdHHcZb2JimlcaclW9hLUyimdXjrllZDHTh3rl6d0= github.com/qor5/x/v3 v3.0.7-0.20240805015803-ab891c3771bb h1:tkSVV1kpHphL4b06YvJesNp4Fo/02/s96GGXkfQdyYw= github.com/qor5/x/v3 v3.0.7-0.20240805015803-ab891c3771bb/go.mod h1:p8LBZGmJ9pozop2jMACV5tN/1gkpAskteOhlwcfFxpw= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= diff --git a/presets/detailing.go b/presets/detailing.go index 3bad026c1..68b74a87a 100644 --- a/presets/detailing.go +++ b/presets/detailing.go @@ -393,11 +393,19 @@ func (b *DetailingBuilder) EditDetailField(ctx *web.EventContext) (r web.EventRe // SaveDetailField EventFunc: click save button func (b *DetailingBuilder) SaveDetailField(ctx *web.EventContext) (r web.EventResponse, err error) { - key := ctx.Queries().Get(SectionFieldName) - id := ctx.Queries().Get(ParamID) + key := ctx.Param(SectionFieldName) + id := ctx.Param(ParamID) + isCancel := ctx.ParamAsBool(SectionIsCancel) f := b.Section(key) + field := &FieldContext{ + ModelInfo: b.mb.modelInfo, + FormKey: f.name, + Name: f.name, + Label: f.label, + } + obj := b.mb.NewModel() obj, err = b.GetFetchFunc()(obj, id, ctx) if err != nil { @@ -407,6 +415,14 @@ func (b *DetailingBuilder) SaveDetailField(ctx *web.EventContext) (r web.EventRe f.setter(obj, ctx) } + if isCancel { + r.UpdatePortals = append(r.UpdatePortals, &web.PortalUpdate{ + Name: f.FieldPortalName(), + Body: f.viewComponent(obj, field, ctx), + }) + return + } + if b.mb.Info().Verifier().Do(PermUpdate).ObjectOn(obj).WithReq(ctx.R).IsAllowed() != nil { ShowMessage(&r, perm.PermissionDenied.Error(), "warning") return @@ -421,24 +437,14 @@ func (b *DetailingBuilder) SaveDetailField(ctx *web.EventContext) (r web.EventRe if _, ok := ctx.Flash.(*web.ValidationErrors); ok { r.UpdatePortals = append(r.UpdatePortals, &web.PortalUpdate{ Name: f.FieldPortalName(), - Body: f.editComponent(obj, &FieldContext{ - ModelInfo: b.mb.modelInfo, - FormKey: f.name, - Name: f.name, - Label: f.label, - }, ctx), + Body: f.editComponent(obj, field, ctx), }) return } r.UpdatePortals = append(r.UpdatePortals, &web.PortalUpdate{ Name: f.FieldPortalName(), - Body: f.viewComponent(obj, &FieldContext{ - ModelInfo: b.mb.modelInfo, - FormKey: f.name, - Name: f.name, - Label: f.label, - }, ctx), + Body: f.viewComponent(obj, field, ctx), }) r.Emit(b.mb.NotifModelsUpdated(), PayloadModelsUpdated{ @@ -496,16 +502,16 @@ func (b *DetailingBuilder) EditDetailListField(ctx *web.EventContext) (r web.Eve func (b *DetailingBuilder) SaveDetailListField(ctx *web.EventContext) (r web.EventResponse, err error) { var ( fieldName string - index int64 + index int + isCancel bool ) - fieldName = ctx.Queries().Get(SectionFieldName) + isCancel = ctx.ParamAsBool(SectionIsCancel) + fieldName = ctx.Param(SectionFieldName) + f := b.Section(fieldName) - index, err = strconv.ParseInt(ctx.Queries().Get(f.SaveBtnKey()), 10, 64) - if err != nil { - return - } + index = ctx.ParamAsInt(f.SaveBtnKey()) obj := b.mb.NewModel() obj, err = b.GetFetchFunc()(obj, ctx.Queries().Get(ParamID), ctx) @@ -516,6 +522,14 @@ func (b *DetailingBuilder) SaveDetailListField(ctx *web.EventContext) (r web.Eve f.setter(obj, ctx) } + if isCancel { + r.UpdatePortals = append(r.UpdatePortals, &web.PortalUpdate{ + Name: f.FieldPortalName(), + Body: f.listComponent(obj, nil, ctx, -1, -1, index), + }) + return + } + if b.mb.Info().Verifier().Do(PermUpdate).ObjectOn(obj).WithReq(ctx.R).IsAllowed() != nil { ShowMessage(&r, perm.PermissionDenied.Error(), "warning") return @@ -530,14 +544,14 @@ func (b *DetailingBuilder) SaveDetailListField(ctx *web.EventContext) (r web.Eve if _, ok := ctx.Flash.(*web.ValidationErrors); ok { r.UpdatePortals = append(r.UpdatePortals, &web.PortalUpdate{ Name: f.FieldPortalName(), - Body: f.listComponent(obj, nil, ctx, -1, int(index), -1), + Body: f.listComponent(obj, nil, ctx, -1, index, -1), }) return } r.UpdatePortals = append(r.UpdatePortals, &web.PortalUpdate{ Name: f.FieldPortalName(), - Body: f.listComponent(obj, nil, ctx, -1, -1, int(index)), + Body: f.listComponent(obj, nil, ctx, -1, -1, index), }) return diff --git a/presets/messages.go b/presets/messages.go index 1a782aca8..856ebc631 100644 --- a/presets/messages.go +++ b/presets/messages.go @@ -57,6 +57,7 @@ type Messages struct { Colon string NotFoundPageNotice string ButtonLabelActionsMenu string + Save string } func (msgr *Messages) DeleteConfirmationText(id string) string { @@ -147,6 +148,7 @@ var Messages_en_US = &Messages{ Colon: ":", NotFoundPageNotice: "Sorry, the requested page cannot be found. Please check the URL.", ButtonLabelActionsMenu: "Actions", + Save: "Save", } var Messages_zh_CN = &Messages{ @@ -202,6 +204,7 @@ var Messages_zh_CN = &Messages{ Colon: ":", NotFoundPageNotice: "很抱歉,所请求的页面不存在,请检查URL。", ButtonLabelActionsMenu: "菜单", + Save: "保存", } var Messages_ja_JP = &Messages{ @@ -256,4 +259,5 @@ var Messages_ja_JP = &Messages{ Colon: ":", NotFoundPageNotice: "申し訳ありませんが、リクエストされたページは見つかりませんでした。URLを確認してください。", ButtonLabelActionsMenu: "メニュー", + Save: "Save (need JP translation)", } diff --git a/presets/section.go b/presets/section.go index 9cb6ebb61..694c43132 100644 --- a/presets/section.go +++ b/presets/section.go @@ -20,10 +20,11 @@ import ( const ( SectionFieldName = "detailField" + SectionIsCancel = "isCancel" - detailListFieldEditBtnKey = "detailListFieldEditBtn" - detailListFieldSaveBtnKey = "detailListFieldSaveBtn" - detailListFieldDeleteBtnKey = "detailListFieldDeleteBtn" + sectionListFieldEditBtnKey = "detailListFieldEditBtn" + sectionListFieldSaveBtnKey = "detailListFieldSaveBtn" + sectionListFieldDeleteBtnKey = "detailListFieldDeleteBtn" detailListFieldEditing = "detailListFieldEditing" ) @@ -449,9 +450,18 @@ func (b *SectionBuilder) editComponent(obj interface{}, field *FieldContext, ctx id = slugIf.PrimarySlug() } } + cancelBtn := VBtn(i18n.T(ctx.R, CoreI18nModuleKey, "Cancel")).Size(SizeSmall).Variant(VariantFlat).Color(ColorSecondaryDarken2). + Attr("style", "text-transform: none;"). + Attr("@click", web.Plaid(). + URL(ctx.R.URL.Path). + EventFunc(actions.DoSaveDetailingField). + Query(SectionFieldName, b.name). + Query(ParamID, id). + Query(SectionIsCancel, true). + Go()) disableEditBtn := b.father.mb.Info().Verifier().Do(PermUpdate).ObjectOn(obj).WithReq(ctx.R).IsAllowed() != nil - btn := VBtn("Save").Size(SizeSmall).Variant(VariantFlat).Color(ColorSecondaryDarken2).Disabled(disableEditBtn). + saveBtn := VBtn(i18n.T(ctx.R, CoreI18nModuleKey, "Save")).Size(SizeSmall).Variant(VariantFlat).Color(ColorSecondaryDarken2).Disabled(disableEditBtn). Attr("style", "text-transform: none;"). Attr("@click", web.Plaid(). URL(ctx.R.URL.Path). @@ -483,8 +493,11 @@ func (b *SectionBuilder) editComponent(obj interface{}, field *FieldContext, ctx // detailFields h.Div(b.componentEditFunc(obj, field, ctx)). Class("flex-grow-1"), - // save btn - h.Div(btn).Class("align-self-end mt-4"), + // save saveBtn + h.Div( + cancelBtn, + saveBtn.Class("ml-2"), + ).Class("align-self-end mt-4"), ).Class("d-flex flex-column"), ), ).Variant(VariantOutlined).Class("mb-6"), @@ -655,15 +668,15 @@ func (b *SectionBuilder) listComponent(obj interface{}, _ *FieldContext, ctx *we } func (b *SectionBuilder) EditBtnKey() string { - return fmt.Sprintf("%s_%s", detailListFieldEditBtnKey, b.name) + return fmt.Sprintf("%s_%s", sectionListFieldEditBtnKey, b.name) } func (b *SectionBuilder) SaveBtnKey() string { - return fmt.Sprintf("%s_%s", detailListFieldSaveBtnKey, b.name) + return fmt.Sprintf("%s_%s", sectionListFieldSaveBtnKey, b.name) } func (b *SectionBuilder) DeleteBtnKey() string { - return fmt.Sprintf("%s_%s", detailListFieldDeleteBtnKey, b.name) + return fmt.Sprintf("%s_%s", sectionListFieldDeleteBtnKey, b.name) } func (b *SectionBuilder) ListElementIsEditing(index int) string { @@ -741,7 +754,18 @@ func (b *SectionBuilder) editElement(obj any, index, _ int, ctx *web.EventContex h.Div(deleteBtn).Class("d-flex pl-3"), ).Class("d-flex justify-space-between mb-4") - saveBtn := VBtn("Save").Size(SizeSmall).Variant(VariantFlat).Color(ColorSecondaryDarken2). + cancelBtn := VBtn(i18n.T(ctx.R, CoreI18nModuleKey, "Cancel")).Size(SizeSmall).Variant(VariantFlat).Color(ColorSecondaryDarken2). + Attr("style", "text-transform: none;"). + Attr("@click", web.Plaid(). + URL(ctx.R.URL.Path). + EventFunc(actions.DoSaveDetailingListField). + Query(SectionFieldName, b.name). + Query(SectionIsCancel, true). + Query(ParamID, ctx.Param(ParamID)). + Query(b.SaveBtnKey(), strconv.Itoa(index)). + Go()) + + saveBtn := VBtn(i18n.T(ctx.R, CoreI18nModuleKey, "Save")).Size(SizeSmall).Variant(VariantFlat).Color(ColorSecondaryDarken2). Attr("style", "text-transform: none;"). Attr("@click", web.Plaid(). URL(ctx.R.URL.Path). @@ -755,7 +779,10 @@ func (b *SectionBuilder) editElement(obj any, index, _ int, ctx *web.EventContex VCardText( h.Div( contentDiv, - h.Div(saveBtn).Class("ms-auto"), + h.Div( + cancelBtn, + saveBtn.Class("ml-2"), + ).Class("ms-auto"), ).Class("d-flex flex-column"), ), h.Input("").Type("hidden").Attr(web.VField(b.ListElementIsEditing(index), true)...), diff --git a/utils/testflow/gentool/go.mod b/utils/testflow/gentool/go.mod index b9736f040..b4eb9e642 100644 --- a/utils/testflow/gentool/go.mod +++ b/utils/testflow/gentool/go.mod @@ -6,7 +6,7 @@ require ( github.com/gobuffalo/flect v1.0.2 github.com/pkg/errors v0.9.1 github.com/qor5/admin/v3 v3.0.1-0.20240424102851-d75759576158 - github.com/qor5/web/v3 v3.0.5 + github.com/qor5/web/v3 v3.0.6-0.20240806032534-6c0e81ec7b12 github.com/sergi/go-diff v1.3.1 mvdan.cc/gofumpt v0.6.0 ) diff --git a/utils/testflow/gentool/go.sum b/utils/testflow/gentool/go.sum index 493197804..ef7a3a761 100644 --- a/utils/testflow/gentool/go.sum +++ b/utils/testflow/gentool/go.sum @@ -23,6 +23,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/qor5/web/v3 v3.0.5-0.20240723094007-d24081129126 h1:LfWbM+CBJbgDqKDyRk8GeYYOoZ8UgQG17t5DwoYUX9o= github.com/qor5/web/v3 v3.0.5-0.20240723094007-d24081129126/go.mod h1:32vdHHcZb2JimlcaclW9hLUyimdXjrllZDHTh3rl6d0= github.com/qor5/web/v3 v3.0.5/go.mod h1:32vdHHcZb2JimlcaclW9hLUyimdXjrllZDHTh3rl6d0= +github.com/qor5/web/v3 v3.0.6-0.20240806032534-6c0e81ec7b12/go.mod h1:32vdHHcZb2JimlcaclW9hLUyimdXjrllZDHTh3rl6d0= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=