@webiny/app-headless-cms 6.5.0-beta.2 → 6.5.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/ContentEntriesModule.js +9 -2
  2. package/ContentEntriesModule.js.map +1 -1
  3. package/admin/components/ContentEntries/Table/Actions/DuplicateEntry.d.ts +2 -0
  4. package/admin/components/ContentEntries/Table/Actions/DuplicateEntry.js +34 -0
  5. package/admin/components/ContentEntries/Table/Actions/DuplicateEntry.js.map +1 -0
  6. package/admin/components/ContentEntries/Table/Actions/index.d.ts +1 -0
  7. package/admin/components/ContentEntries/Table/Actions/index.js +1 -0
  8. package/admin/components/Dialogs/DuplicateEntryConfirmDialog.d.ts +2 -0
  9. package/admin/components/Dialogs/DuplicateEntryConfirmDialog.js +22 -0
  10. package/admin/components/Dialogs/DuplicateEntryConfirmDialog.js.map +1 -0
  11. package/features/contentEntry/CmsEntryError.d.ts +10 -0
  12. package/features/contentEntry/CmsEntryError.js +11 -0
  13. package/features/contentEntry/CmsEntryError.js.map +1 -0
  14. package/features/contentEntry/createEntry/CreateEntryGateway.js +2 -1
  15. package/features/contentEntry/createEntry/CreateEntryGateway.js.map +1 -1
  16. package/features/contentEntry/createRevisionFrom/CreateRevisionFromGateway.js +2 -1
  17. package/features/contentEntry/createRevisionFrom/CreateRevisionFromGateway.js.map +1 -1
  18. package/features/contentEntry/duplicateEntry/DuplicateEntryGateway.d.ts +14 -0
  19. package/features/contentEntry/duplicateEntry/DuplicateEntryGateway.js +43 -0
  20. package/features/contentEntry/duplicateEntry/DuplicateEntryGateway.js.map +1 -0
  21. package/features/contentEntry/duplicateEntry/DuplicateEntryRepository.d.ts +15 -0
  22. package/features/contentEntry/duplicateEntry/DuplicateEntryRepository.js +34 -0
  23. package/features/contentEntry/duplicateEntry/DuplicateEntryRepository.js.map +1 -0
  24. package/features/contentEntry/duplicateEntry/DuplicateEntryUseCase.d.ts +11 -0
  25. package/features/contentEntry/duplicateEntry/DuplicateEntryUseCase.js +18 -0
  26. package/features/contentEntry/duplicateEntry/DuplicateEntryUseCase.js.map +1 -0
  27. package/features/contentEntry/duplicateEntry/abstractions.d.ts +26 -0
  28. package/features/contentEntry/duplicateEntry/abstractions.js +7 -0
  29. package/features/contentEntry/duplicateEntry/abstractions.js.map +1 -0
  30. package/features/contentEntry/duplicateEntry/feature.d.ts +3 -0
  31. package/features/contentEntry/duplicateEntry/feature.js +21 -0
  32. package/features/contentEntry/duplicateEntry/feature.js.map +1 -0
  33. package/features/contentEntry/feature.js +2 -0
  34. package/features/contentEntry/feature.js.map +1 -1
  35. package/features/contentEntry/singletonEntry/UpdateSingletonEntryGateway.js +2 -1
  36. package/features/contentEntry/singletonEntry/UpdateSingletonEntryGateway.js.map +1 -1
  37. package/features/contentEntry/updateEntry/UpdateEntryGateway.js +2 -1
  38. package/features/contentEntry/updateEntry/UpdateEntryGateway.js.map +1 -1
  39. package/features/formModel/CmsFormModelBuilder.d.ts +8 -0
  40. package/features/formModel/CmsFormModelBuilder.js +25 -2
  41. package/features/formModel/CmsFormModelBuilder.js.map +1 -1
  42. package/features/formModel/CmsFormModelBuilder.test.d.ts +1 -0
  43. package/features/formModel/CmsFormModelBuilder.test.js +111 -0
  44. package/features/formModel/CmsFormModelBuilder.test.js.map +1 -0
  45. package/package.json +20 -20
  46. package/presentation/contentEntries/form/ContentEntryFormPresenter.js +3 -1
  47. package/presentation/contentEntries/form/ContentEntryFormPresenter.js.map +1 -1
  48. package/presentation/contentEntries/form/toFormErrors.d.ts +7 -0
  49. package/presentation/contentEntries/form/toFormErrors.js +49 -0
  50. package/presentation/contentEntries/form/toFormErrors.js.map +1 -0
  51. package/presentation/contentEntries/form/toFormErrors.test.d.ts +1 -0
  52. package/presentation/contentEntries/form/toFormErrors.test.js +175 -0
  53. package/presentation/contentEntries/form/toFormErrors.test.js.map +1 -0
  54. package/presentation/contentEntries/list/ContentEntriesPresenter.d.ts +6 -2
  55. package/presentation/contentEntries/list/ContentEntriesPresenter.js +27 -2
  56. package/presentation/contentEntries/list/ContentEntriesPresenter.js.map +1 -1
  57. package/presentation/contentEntries/list/ContentEntriesPresenter.test.js +58 -1
  58. package/presentation/contentEntries/list/ContentEntriesPresenter.test.js.map +1 -1
  59. package/presentation/contentEntries/list/abstractions.d.ts +12 -0
  60. package/presentation/contentEntries/list/abstractions.js.map +1 -1
  61. package/presentation/contentEntries/singleEntry/SingleEntryPresenter.js +3 -1
  62. package/presentation/contentEntries/singleEntry/SingleEntryPresenter.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"features/contentEntry/feature.js","sources":["../../../src/features/contentEntry/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/admin\";\nimport { ContentEntriesCacheProvider } from \"./ContentEntriesCacheProvider.js\";\nimport { GetEntryFeature } from \"./getEntry/feature.js\";\nimport { ListEntriesFeature } from \"./listEntries/feature.js\";\nimport { CreateEntryFeature } from \"./createEntry/feature.js\";\nimport { UpdateEntryFeature } from \"./updateEntry/feature.js\";\nimport { PublishEntryFeature } from \"./publishEntry/feature.js\";\nimport { UnpublishEntryFeature } from \"./unpublishEntry/feature.js\";\nimport { DeleteEntryFeature } from \"./deleteEntry/feature.js\";\nimport { DeleteEntryRevisionFeature } from \"./deleteEntryRevision/feature.js\";\nimport { CreateRevisionFromFeature } from \"./createRevisionFrom/feature.js\";\nimport { ListRevisionsFeature } from \"./listRevisions/feature.js\";\nimport { UpdateRevisionDescriptionFeature } from \"./updateRevisionDescription/feature.js\";\nimport { SingletonEntryFeature } from \"./singletonEntry/feature.js\";\nimport { MoveEntryFeature } from \"./moveEntry/feature.js\";\nimport { BulkActionFeature } from \"./bulkAction/feature.js\";\nimport { CmsTrashBinFeature } from \"./trashBin/feature.js\";\nimport { SearchContentEntriesFeature } from \"./searchContentEntries/feature.js\";\nimport { GetContentEntriesFeature } from \"./getContentEntries/feature.js\";\nimport { ValueTransformersFeature } from \"./valueTransformers/feature.js\";\nimport { EntryGraphQLFields } from \"./EntryGraphQLFields.js\";\n\nexport const ContentEntryFeature = createFeature({\n name: \"CmsContentEntry\",\n register(container) {\n container.register(EntryGraphQLFields).inSingletonScope();\n container.register(ContentEntriesCacheProvider).inSingletonScope();\n\n ValueTransformersFeature.register(container);\n GetEntryFeature.register(container);\n ListEntriesFeature.register(container);\n SearchContentEntriesFeature.register(container);\n GetContentEntriesFeature.register(container);\n CreateEntryFeature.register(container);\n UpdateEntryFeature.register(container);\n PublishEntryFeature.register(container);\n UnpublishEntryFeature.register(container);\n DeleteEntryFeature.register(container);\n DeleteEntryRevisionFeature.register(container);\n CreateRevisionFromFeature.register(container);\n ListRevisionsFeature.register(container);\n UpdateRevisionDescriptionFeature.register(container);\n SingletonEntryFeature.register(container);\n MoveEntryFeature.register(container);\n BulkActionFeature.register(container);\n CmsTrashBinFeature.register(container);\n }\n});\n"],"names":["ContentEntryFeature","createFeature","container","EntryGraphQLFields","ContentEntriesCacheProvider","ValueTransformersFeature","GetEntryFeature","ListEntriesFeature","SearchContentEntriesFeature","GetContentEntriesFeature","CreateEntryFeature","UpdateEntryFeature","PublishEntryFeature","UnpublishEntryFeature","DeleteEntryFeature","DeleteEntryRevisionFeature","CreateRevisionFromFeature","ListRevisionsFeature","UpdateRevisionDescriptionFeature","SingletonEntryFeature","MoveEntryFeature","BulkActionFeature","CmsTrashBinFeature"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsBO,MAAMA,sBAAsBC,cAAc;IAC7C,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC,oBAAoB,gBAAgB;QACvDD,UAAU,QAAQ,CAACE,6BAA6B,gBAAgB;QAEhEC,yBAAyB,QAAQ,CAACH;QAClCI,gBAAgB,QAAQ,CAACJ;QACzBK,mBAAmB,QAAQ,CAACL;QAC5BM,4BAA4B,QAAQ,CAACN;QACrCO,yBAAyB,QAAQ,CAACP;QAClCQ,mBAAmB,QAAQ,CAACR;QAC5BS,mBAAmB,QAAQ,CAACT;QAC5BU,oBAAoB,QAAQ,CAACV;QAC7BW,sBAAsB,QAAQ,CAACX;QAC/BY,mBAAmB,QAAQ,CAACZ;QAC5Ba,2BAA2B,QAAQ,CAACb;QACpCc,0BAA0B,QAAQ,CAACd;QACnCe,qBAAqB,QAAQ,CAACf;QAC9BgB,iCAAiC,QAAQ,CAAChB;QAC1CiB,sBAAsB,QAAQ,CAACjB;QAC/BkB,iBAAiB,QAAQ,CAAClB;QAC1BmB,kBAAkB,QAAQ,CAACnB;QAC3BoB,mBAAmB,QAAQ,CAACpB;IAChC;AACJ"}
1
+ {"version":3,"file":"features/contentEntry/feature.js","sources":["../../../src/features/contentEntry/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/admin\";\nimport { ContentEntriesCacheProvider } from \"./ContentEntriesCacheProvider.js\";\nimport { GetEntryFeature } from \"./getEntry/feature.js\";\nimport { ListEntriesFeature } from \"./listEntries/feature.js\";\nimport { CreateEntryFeature } from \"./createEntry/feature.js\";\nimport { DuplicateEntryFeature } from \"./duplicateEntry/feature.js\";\nimport { UpdateEntryFeature } from \"./updateEntry/feature.js\";\nimport { PublishEntryFeature } from \"./publishEntry/feature.js\";\nimport { UnpublishEntryFeature } from \"./unpublishEntry/feature.js\";\nimport { DeleteEntryFeature } from \"./deleteEntry/feature.js\";\nimport { DeleteEntryRevisionFeature } from \"./deleteEntryRevision/feature.js\";\nimport { CreateRevisionFromFeature } from \"./createRevisionFrom/feature.js\";\nimport { ListRevisionsFeature } from \"./listRevisions/feature.js\";\nimport { UpdateRevisionDescriptionFeature } from \"./updateRevisionDescription/feature.js\";\nimport { SingletonEntryFeature } from \"./singletonEntry/feature.js\";\nimport { MoveEntryFeature } from \"./moveEntry/feature.js\";\nimport { BulkActionFeature } from \"./bulkAction/feature.js\";\nimport { CmsTrashBinFeature } from \"./trashBin/feature.js\";\nimport { SearchContentEntriesFeature } from \"./searchContentEntries/feature.js\";\nimport { GetContentEntriesFeature } from \"./getContentEntries/feature.js\";\nimport { ValueTransformersFeature } from \"./valueTransformers/feature.js\";\nimport { EntryGraphQLFields } from \"./EntryGraphQLFields.js\";\n\nexport const ContentEntryFeature = createFeature({\n name: \"CmsContentEntry\",\n register(container) {\n container.register(EntryGraphQLFields).inSingletonScope();\n container.register(ContentEntriesCacheProvider).inSingletonScope();\n\n ValueTransformersFeature.register(container);\n GetEntryFeature.register(container);\n ListEntriesFeature.register(container);\n SearchContentEntriesFeature.register(container);\n GetContentEntriesFeature.register(container);\n CreateEntryFeature.register(container);\n DuplicateEntryFeature.register(container);\n UpdateEntryFeature.register(container);\n PublishEntryFeature.register(container);\n UnpublishEntryFeature.register(container);\n DeleteEntryFeature.register(container);\n DeleteEntryRevisionFeature.register(container);\n CreateRevisionFromFeature.register(container);\n ListRevisionsFeature.register(container);\n UpdateRevisionDescriptionFeature.register(container);\n SingletonEntryFeature.register(container);\n MoveEntryFeature.register(container);\n BulkActionFeature.register(container);\n CmsTrashBinFeature.register(container);\n }\n});\n"],"names":["ContentEntryFeature","createFeature","container","EntryGraphQLFields","ContentEntriesCacheProvider","ValueTransformersFeature","GetEntryFeature","ListEntriesFeature","SearchContentEntriesFeature","GetContentEntriesFeature","CreateEntryFeature","DuplicateEntryFeature","UpdateEntryFeature","PublishEntryFeature","UnpublishEntryFeature","DeleteEntryFeature","DeleteEntryRevisionFeature","CreateRevisionFromFeature","ListRevisionsFeature","UpdateRevisionDescriptionFeature","SingletonEntryFeature","MoveEntryFeature","BulkActionFeature","CmsTrashBinFeature"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuBO,MAAMA,sBAAsBC,cAAc;IAC7C,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC,oBAAoB,gBAAgB;QACvDD,UAAU,QAAQ,CAACE,6BAA6B,gBAAgB;QAEhEC,yBAAyB,QAAQ,CAACH;QAClCI,gBAAgB,QAAQ,CAACJ;QACzBK,mBAAmB,QAAQ,CAACL;QAC5BM,4BAA4B,QAAQ,CAACN;QACrCO,yBAAyB,QAAQ,CAACP;QAClCQ,mBAAmB,QAAQ,CAACR;QAC5BS,sBAAsB,QAAQ,CAACT;QAC/BU,mBAAmB,QAAQ,CAACV;QAC5BW,oBAAoB,QAAQ,CAACX;QAC7BY,sBAAsB,QAAQ,CAACZ;QAC/Ba,mBAAmB,QAAQ,CAACb;QAC5Bc,2BAA2B,QAAQ,CAACd;QACpCe,0BAA0B,QAAQ,CAACf;QACnCgB,qBAAqB,QAAQ,CAAChB;QAC9BiB,iCAAiC,QAAQ,CAACjB;QAC1CkB,sBAAsB,QAAQ,CAAClB;QAC/BmB,iBAAiB,QAAQ,CAACnB;QAC1BoB,kBAAkB,QAAQ,CAACpB;QAC3BqB,mBAAmB,QAAQ,CAACrB;IAChC;AACJ"}
@@ -2,6 +2,7 @@ import { CmsGraphQLClient } from "../../graphQLClient/abstractions.js";
2
2
  import { EntryGraphQLFields } from "../abstractions.js";
3
3
  import { UpdateSingletonEntryGateway } from "./abstractions.js";
4
4
  import { EntryDataPreparer } from "../valueTransformers/EntryDataPreparer.js";
5
+ import { CmsEntryError } from "../CmsEntryError.js";
5
6
  function createMutation(model, fields) {
6
7
  return `
7
8
  mutation CmsUpdate${model.singularApiName}($data: ${model.singularApiName}Input!, $options: UpdateCmsEntryOptionsInput) {
@@ -31,7 +32,7 @@ class UpdateSingletonEntryGatewayImpl {
31
32
  }
32
33
  });
33
34
  const { data: entry, error } = response.content;
34
- if (!entry) throw new Error(error?.message || "Could not update singleton entry");
35
+ if (!entry) throw new CmsEntryError(error, "Could not update singleton entry");
35
36
  return entry;
36
37
  }
37
38
  prepareData(data, fields) {
@@ -1 +1 @@
1
- {"version":3,"file":"features/contentEntry/singletonEntry/UpdateSingletonEntryGateway.js","sources":["../../../../src/features/contentEntry/singletonEntry/UpdateSingletonEntryGateway.ts"],"sourcesContent":["import { CmsGraphQLClient } from \"~/features/graphQLClient/abstractions.js\";\nimport type { CmsContentEntry, CmsErrorResponse, CmsModel, CmsModelField } from \"~/types.js\";\nimport { EntryGraphQLFields } from \"../abstractions.js\";\nimport {\n UpdateSingletonEntryGateway as GatewayAbstraction,\n type IUpdateSingletonEntryParams\n} from \"./abstractions.js\";\nimport { EntryDataPreparer } from \"~/features/contentEntry/valueTransformers/EntryDataPreparer.js\";\n\ninterface UpdateSingletonEntryResponse {\n content: {\n data: CmsContentEntry | null;\n error: CmsErrorResponse | null;\n };\n}\n\nfunction createMutation(model: CmsModel, fields: EntryGraphQLFields.Interface) {\n return /* GraphQL */ `\n mutation CmsUpdate${model.singularApiName}($data: ${model.singularApiName}Input!, $options: UpdateCmsEntryOptionsInput) {\n content: update${model.singularApiName}(data: $data, options: $options) {\n data {\n ${fields.getSystemFields(model)}\n ${fields.getValuesBlock(model)}\n }\n error { message code data }\n }\n }\n `;\n}\n\nclass UpdateSingletonEntryGatewayImpl implements GatewayAbstraction.Interface {\n constructor(\n private client: CmsGraphQLClient.Interface,\n private preparer: EntryDataPreparer.Interface,\n private fields: EntryGraphQLFields.Interface\n ) {}\n\n async execute({ model, data, options }: IUpdateSingletonEntryParams) {\n const preparedData = this.prepareData(data, model.fields);\n\n const response = await this.client.execute<UpdateSingletonEntryResponse>({\n query: createMutation(model, this.fields),\n variables: { data: preparedData, options }\n });\n\n const { data: entry, error } = response.content;\n\n if (!entry) {\n throw new Error(error?.message || \"Could not update singleton entry\");\n }\n\n return entry;\n }\n\n private prepareData(\n data: Record<string, unknown>,\n fields: CmsModelField[]\n ): Record<string, unknown> {\n const values = data.values;\n if (!values || typeof values !== \"object\") {\n return data;\n }\n return {\n ...data,\n values: this.preparer.prepare(values as Record<string, unknown>, fields)\n };\n }\n}\n\nexport const UpdateSingletonEntryGateway = GatewayAbstraction.createImplementation({\n implementation: UpdateSingletonEntryGatewayImpl,\n dependencies: [CmsGraphQLClient, EntryDataPreparer, EntryGraphQLFields]\n});\n"],"names":["createMutation","model","fields","UpdateSingletonEntryGatewayImpl","client","preparer","data","options","preparedData","response","entry","error","Error","values","UpdateSingletonEntryGateway","GatewayAbstraction","CmsGraphQLClient","EntryDataPreparer","EntryGraphQLFields"],"mappings":";;;;AAgBA,SAASA,eAAeC,KAAe,EAAEC,MAAoC;IACzE,OAAqB,CAAC;0BACA,EAAED,MAAM,eAAe,CAAC,QAAQ,EAAEA,MAAM,eAAe,CAAC;2BACvD,EAAEA,MAAM,eAAe,CAAC;;oBAE/B,EAAEC,OAAO,eAAe,CAACD,OAAO;oBAChC,EAAEC,OAAO,cAAc,CAACD,OAAO;;;;;IAK/C,CAAC;AACL;AAEA,MAAME;IACF,YACYC,MAAkC,EAClCC,QAAqC,EACrCH,MAAoC,CAC9C;aAHUE,MAAM,GAANA;aACAC,QAAQ,GAARA;aACAH,MAAM,GAANA;IACT;IAEH,MAAM,QAAQ,EAAED,KAAK,EAAEK,IAAI,EAAEC,OAAO,EAA+B,EAAE;QACjE,MAAMC,eAAe,IAAI,CAAC,WAAW,CAACF,MAAML,MAAM,MAAM;QAExD,MAAMQ,WAAW,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAA+B;YACrE,OAAOT,eAAeC,OAAO,IAAI,CAAC,MAAM;YACxC,WAAW;gBAAE,MAAMO;gBAAcD;YAAQ;QAC7C;QAEA,MAAM,EAAE,MAAMG,KAAK,EAAEC,KAAK,EAAE,GAAGF,SAAS,OAAO;QAE/C,IAAI,CAACC,OACD,MAAM,IAAIE,MAAMD,OAAO,WAAW;QAGtC,OAAOD;IACX;IAEQ,YACJJ,IAA6B,EAC7BJ,MAAuB,EACA;QACvB,MAAMW,SAASP,KAAK,MAAM;QAC1B,IAAI,CAACO,UAAU,AAAkB,YAAlB,OAAOA,QAClB,OAAOP;QAEX,OAAO;YACH,GAAGA,IAAI;YACP,QAAQ,IAAI,CAAC,QAAQ,CAAC,OAAO,CAACO,QAAmCX;QACrE;IACJ;AACJ;AAEO,MAAMY,0DAA8BC,4BAAAA,oBAAuC,CAAC;IAC/E,gBAAgBZ;IAChB,cAAc;QAACa;QAAkBC;QAAmBC;KAAmB;AAC3E"}
1
+ {"version":3,"file":"features/contentEntry/singletonEntry/UpdateSingletonEntryGateway.js","sources":["../../../../src/features/contentEntry/singletonEntry/UpdateSingletonEntryGateway.ts"],"sourcesContent":["import { CmsGraphQLClient } from \"~/features/graphQLClient/abstractions.js\";\nimport type { CmsContentEntry, CmsErrorResponse, CmsModel, CmsModelField } from \"~/types.js\";\nimport { EntryGraphQLFields } from \"../abstractions.js\";\nimport {\n UpdateSingletonEntryGateway as GatewayAbstraction,\n type IUpdateSingletonEntryParams\n} from \"./abstractions.js\";\nimport { EntryDataPreparer } from \"~/features/contentEntry/valueTransformers/EntryDataPreparer.js\";\nimport { CmsEntryError } from \"~/features/contentEntry/CmsEntryError.js\";\n\ninterface UpdateSingletonEntryResponse {\n content: {\n data: CmsContentEntry | null;\n error: CmsErrorResponse | null;\n };\n}\n\nfunction createMutation(model: CmsModel, fields: EntryGraphQLFields.Interface) {\n return /* GraphQL */ `\n mutation CmsUpdate${model.singularApiName}($data: ${model.singularApiName}Input!, $options: UpdateCmsEntryOptionsInput) {\n content: update${model.singularApiName}(data: $data, options: $options) {\n data {\n ${fields.getSystemFields(model)}\n ${fields.getValuesBlock(model)}\n }\n error { message code data }\n }\n }\n `;\n}\n\nclass UpdateSingletonEntryGatewayImpl implements GatewayAbstraction.Interface {\n constructor(\n private client: CmsGraphQLClient.Interface,\n private preparer: EntryDataPreparer.Interface,\n private fields: EntryGraphQLFields.Interface\n ) {}\n\n async execute({ model, data, options }: IUpdateSingletonEntryParams) {\n const preparedData = this.prepareData(data, model.fields);\n\n const response = await this.client.execute<UpdateSingletonEntryResponse>({\n query: createMutation(model, this.fields),\n variables: { data: preparedData, options }\n });\n\n const { data: entry, error } = response.content;\n\n if (!entry) {\n throw new CmsEntryError(error, \"Could not update singleton entry\");\n }\n\n return entry;\n }\n\n private prepareData(\n data: Record<string, unknown>,\n fields: CmsModelField[]\n ): Record<string, unknown> {\n const values = data.values;\n if (!values || typeof values !== \"object\") {\n return data;\n }\n return {\n ...data,\n values: this.preparer.prepare(values as Record<string, unknown>, fields)\n };\n }\n}\n\nexport const UpdateSingletonEntryGateway = GatewayAbstraction.createImplementation({\n implementation: UpdateSingletonEntryGatewayImpl,\n dependencies: [CmsGraphQLClient, EntryDataPreparer, EntryGraphQLFields]\n});\n"],"names":["createMutation","model","fields","UpdateSingletonEntryGatewayImpl","client","preparer","data","options","preparedData","response","entry","error","CmsEntryError","values","UpdateSingletonEntryGateway","GatewayAbstraction","CmsGraphQLClient","EntryDataPreparer","EntryGraphQLFields"],"mappings":";;;;;AAiBA,SAASA,eAAeC,KAAe,EAAEC,MAAoC;IACzE,OAAqB,CAAC;0BACA,EAAED,MAAM,eAAe,CAAC,QAAQ,EAAEA,MAAM,eAAe,CAAC;2BACvD,EAAEA,MAAM,eAAe,CAAC;;oBAE/B,EAAEC,OAAO,eAAe,CAACD,OAAO;oBAChC,EAAEC,OAAO,cAAc,CAACD,OAAO;;;;;IAK/C,CAAC;AACL;AAEA,MAAME;IACF,YACYC,MAAkC,EAClCC,QAAqC,EACrCH,MAAoC,CAC9C;aAHUE,MAAM,GAANA;aACAC,QAAQ,GAARA;aACAH,MAAM,GAANA;IACT;IAEH,MAAM,QAAQ,EAAED,KAAK,EAAEK,IAAI,EAAEC,OAAO,EAA+B,EAAE;QACjE,MAAMC,eAAe,IAAI,CAAC,WAAW,CAACF,MAAML,MAAM,MAAM;QAExD,MAAMQ,WAAW,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAA+B;YACrE,OAAOT,eAAeC,OAAO,IAAI,CAAC,MAAM;YACxC,WAAW;gBAAE,MAAMO;gBAAcD;YAAQ;QAC7C;QAEA,MAAM,EAAE,MAAMG,KAAK,EAAEC,KAAK,EAAE,GAAGF,SAAS,OAAO;QAE/C,IAAI,CAACC,OACD,MAAM,IAAIE,cAAcD,OAAO;QAGnC,OAAOD;IACX;IAEQ,YACJJ,IAA6B,EAC7BJ,MAAuB,EACA;QACvB,MAAMW,SAASP,KAAK,MAAM;QAC1B,IAAI,CAACO,UAAU,AAAkB,YAAlB,OAAOA,QAClB,OAAOP;QAEX,OAAO;YACH,GAAGA,IAAI;YACP,QAAQ,IAAI,CAAC,QAAQ,CAAC,OAAO,CAACO,QAAmCX;QACrE;IACJ;AACJ;AAEO,MAAMY,0DAA8BC,4BAAAA,oBAAuC,CAAC;IAC/E,gBAAgBZ;IAChB,cAAc;QAACa;QAAkBC;QAAmBC;KAAmB;AAC3E"}
@@ -2,6 +2,7 @@ import { CmsGraphQLClient } from "../../graphQLClient/abstractions.js";
2
2
  import { EntryGraphQLFields } from "../abstractions.js";
3
3
  import { UpdateEntryGateway } from "./abstractions.js";
4
4
  import { EntryDataPreparer } from "../valueTransformers/EntryDataPreparer.js";
5
+ import { CmsEntryError } from "../CmsEntryError.js";
5
6
  function createMutation(model, fields) {
6
7
  return `
7
8
  mutation CmsUpdate${model.singularApiName}($revision: ID!, $data: ${model.singularApiName}Input!, $options: UpdateCmsEntryOptionsInput) {
@@ -32,7 +33,7 @@ class UpdateEntryGatewayImpl {
32
33
  }
33
34
  });
34
35
  const { data: entry, error } = response.content;
35
- if (!entry) throw new Error(error?.message || "Could not update entry");
36
+ if (!entry) throw new CmsEntryError(error, "Could not update entry");
36
37
  return entry;
37
38
  }
38
39
  prepareData(data, fields) {
@@ -1 +1 @@
1
- {"version":3,"file":"features/contentEntry/updateEntry/UpdateEntryGateway.js","sources":["../../../../src/features/contentEntry/updateEntry/UpdateEntryGateway.ts"],"sourcesContent":["import { CmsGraphQLClient } from \"~/features/graphQLClient/abstractions.js\";\nimport type { CmsContentEntry, CmsErrorResponse, CmsModel, CmsModelField } from \"~/types.js\";\nimport { EntryGraphQLFields } from \"../abstractions.js\";\nimport {\n UpdateEntryGateway as GatewayAbstraction,\n type IUpdateEntryParams\n} from \"./abstractions.js\";\nimport { EntryDataPreparer } from \"~/features/contentEntry/valueTransformers/EntryDataPreparer.js\";\n\ninterface UpdateEntryResponse {\n content: {\n data: CmsContentEntry | null;\n error: CmsErrorResponse | null;\n };\n}\n\nfunction createMutation(model: CmsModel, fields: EntryGraphQLFields.Interface) {\n return /* GraphQL */ `\n mutation CmsUpdate${model.singularApiName}($revision: ID!, $data: ${model.singularApiName}Input!, $options: UpdateCmsEntryOptionsInput) {\n content: update${model.singularApiName}(revision: $revision, data: $data, options: $options) {\n data {\n ${fields.getSystemFields(model)}\n ${fields.getValuesBlock(model)}\n }\n error { message code data }\n }\n }\n `;\n}\n\nclass UpdateEntryGatewayImpl implements GatewayAbstraction.Interface {\n constructor(\n private client: CmsGraphQLClient.Interface,\n private preparer: EntryDataPreparer.Interface,\n private fields: EntryGraphQLFields.Interface\n ) {}\n\n async execute({ model, revisionId, data, options }: IUpdateEntryParams) {\n const preparedData = this.prepareData(data, model.fields);\n\n const response = await this.client.execute<UpdateEntryResponse>({\n query: createMutation(model, this.fields),\n variables: { revision: revisionId, data: preparedData, options }\n });\n\n const { data: entry, error } = response.content;\n\n if (!entry) {\n throw new Error(error?.message || \"Could not update entry\");\n }\n\n return entry;\n }\n\n private prepareData(\n data: Record<string, unknown>,\n fields: CmsModelField[]\n ): Record<string, unknown> {\n const values = data.values;\n if (!values || typeof values !== \"object\") {\n return data;\n }\n return {\n ...data,\n values: this.preparer.prepare(values as Record<string, unknown>, fields)\n };\n }\n}\n\nexport const UpdateEntryGateway = GatewayAbstraction.createImplementation({\n implementation: UpdateEntryGatewayImpl,\n dependencies: [CmsGraphQLClient, EntryDataPreparer, EntryGraphQLFields]\n});\n"],"names":["createMutation","model","fields","UpdateEntryGatewayImpl","client","preparer","revisionId","data","options","preparedData","response","entry","error","Error","values","UpdateEntryGateway","GatewayAbstraction","CmsGraphQLClient","EntryDataPreparer","EntryGraphQLFields"],"mappings":";;;;AAgBA,SAASA,eAAeC,KAAe,EAAEC,MAAoC;IACzE,OAAqB,CAAC;0BACA,EAAED,MAAM,eAAe,CAAC,wBAAwB,EAAEA,MAAM,eAAe,CAAC;2BACvE,EAAEA,MAAM,eAAe,CAAC;;oBAE/B,EAAEC,OAAO,eAAe,CAACD,OAAO;oBAChC,EAAEC,OAAO,cAAc,CAACD,OAAO;;;;;IAK/C,CAAC;AACL;AAEA,MAAME;IACF,YACYC,MAAkC,EAClCC,QAAqC,EACrCH,MAAoC,CAC9C;aAHUE,MAAM,GAANA;aACAC,QAAQ,GAARA;aACAH,MAAM,GAANA;IACT;IAEH,MAAM,QAAQ,EAAED,KAAK,EAAEK,UAAU,EAAEC,IAAI,EAAEC,OAAO,EAAsB,EAAE;QACpE,MAAMC,eAAe,IAAI,CAAC,WAAW,CAACF,MAAMN,MAAM,MAAM;QAExD,MAAMS,WAAW,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAsB;YAC5D,OAAOV,eAAeC,OAAO,IAAI,CAAC,MAAM;YACxC,WAAW;gBAAE,UAAUK;gBAAY,MAAMG;gBAAcD;YAAQ;QACnE;QAEA,MAAM,EAAE,MAAMG,KAAK,EAAEC,KAAK,EAAE,GAAGF,SAAS,OAAO;QAE/C,IAAI,CAACC,OACD,MAAM,IAAIE,MAAMD,OAAO,WAAW;QAGtC,OAAOD;IACX;IAEQ,YACJJ,IAA6B,EAC7BL,MAAuB,EACA;QACvB,MAAMY,SAASP,KAAK,MAAM;QAC1B,IAAI,CAACO,UAAU,AAAkB,YAAlB,OAAOA,QAClB,OAAOP;QAEX,OAAO;YACH,GAAGA,IAAI;YACP,QAAQ,IAAI,CAAC,QAAQ,CAAC,OAAO,CAACO,QAAmCZ;QACrE;IACJ;AACJ;AAEO,MAAMa,wCAAqBC,mBAAAA,oBAAuC,CAAC;IACtE,gBAAgBb;IAChB,cAAc;QAACc;QAAkBC;QAAmBC;KAAmB;AAC3E"}
1
+ {"version":3,"file":"features/contentEntry/updateEntry/UpdateEntryGateway.js","sources":["../../../../src/features/contentEntry/updateEntry/UpdateEntryGateway.ts"],"sourcesContent":["import { CmsGraphQLClient } from \"~/features/graphQLClient/abstractions.js\";\nimport type { CmsContentEntry, CmsErrorResponse, CmsModel, CmsModelField } from \"~/types.js\";\nimport { EntryGraphQLFields } from \"../abstractions.js\";\nimport {\n UpdateEntryGateway as GatewayAbstraction,\n type IUpdateEntryParams\n} from \"./abstractions.js\";\nimport { EntryDataPreparer } from \"~/features/contentEntry/valueTransformers/EntryDataPreparer.js\";\nimport { CmsEntryError } from \"~/features/contentEntry/CmsEntryError.js\";\n\ninterface UpdateEntryResponse {\n content: {\n data: CmsContentEntry | null;\n error: CmsErrorResponse | null;\n };\n}\n\nfunction createMutation(model: CmsModel, fields: EntryGraphQLFields.Interface) {\n return /* GraphQL */ `\n mutation CmsUpdate${model.singularApiName}($revision: ID!, $data: ${model.singularApiName}Input!, $options: UpdateCmsEntryOptionsInput) {\n content: update${model.singularApiName}(revision: $revision, data: $data, options: $options) {\n data {\n ${fields.getSystemFields(model)}\n ${fields.getValuesBlock(model)}\n }\n error { message code data }\n }\n }\n `;\n}\n\nclass UpdateEntryGatewayImpl implements GatewayAbstraction.Interface {\n constructor(\n private client: CmsGraphQLClient.Interface,\n private preparer: EntryDataPreparer.Interface,\n private fields: EntryGraphQLFields.Interface\n ) {}\n\n async execute({ model, revisionId, data, options }: IUpdateEntryParams) {\n const preparedData = this.prepareData(data, model.fields);\n\n const response = await this.client.execute<UpdateEntryResponse>({\n query: createMutation(model, this.fields),\n variables: { revision: revisionId, data: preparedData, options }\n });\n\n const { data: entry, error } = response.content;\n\n if (!entry) {\n throw new CmsEntryError(error, \"Could not update entry\");\n }\n\n return entry;\n }\n\n private prepareData(\n data: Record<string, unknown>,\n fields: CmsModelField[]\n ): Record<string, unknown> {\n const values = data.values;\n if (!values || typeof values !== \"object\") {\n return data;\n }\n return {\n ...data,\n values: this.preparer.prepare(values as Record<string, unknown>, fields)\n };\n }\n}\n\nexport const UpdateEntryGateway = GatewayAbstraction.createImplementation({\n implementation: UpdateEntryGatewayImpl,\n dependencies: [CmsGraphQLClient, EntryDataPreparer, EntryGraphQLFields]\n});\n"],"names":["createMutation","model","fields","UpdateEntryGatewayImpl","client","preparer","revisionId","data","options","preparedData","response","entry","error","CmsEntryError","values","UpdateEntryGateway","GatewayAbstraction","CmsGraphQLClient","EntryDataPreparer","EntryGraphQLFields"],"mappings":";;;;;AAiBA,SAASA,eAAeC,KAAe,EAAEC,MAAoC;IACzE,OAAqB,CAAC;0BACA,EAAED,MAAM,eAAe,CAAC,wBAAwB,EAAEA,MAAM,eAAe,CAAC;2BACvE,EAAEA,MAAM,eAAe,CAAC;;oBAE/B,EAAEC,OAAO,eAAe,CAACD,OAAO;oBAChC,EAAEC,OAAO,cAAc,CAACD,OAAO;;;;;IAK/C,CAAC;AACL;AAEA,MAAME;IACF,YACYC,MAAkC,EAClCC,QAAqC,EACrCH,MAAoC,CAC9C;aAHUE,MAAM,GAANA;aACAC,QAAQ,GAARA;aACAH,MAAM,GAANA;IACT;IAEH,MAAM,QAAQ,EAAED,KAAK,EAAEK,UAAU,EAAEC,IAAI,EAAEC,OAAO,EAAsB,EAAE;QACpE,MAAMC,eAAe,IAAI,CAAC,WAAW,CAACF,MAAMN,MAAM,MAAM;QAExD,MAAMS,WAAW,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAsB;YAC5D,OAAOV,eAAeC,OAAO,IAAI,CAAC,MAAM;YACxC,WAAW;gBAAE,UAAUK;gBAAY,MAAMG;gBAAcD;YAAQ;QACnE;QAEA,MAAM,EAAE,MAAMG,KAAK,EAAEC,KAAK,EAAE,GAAGF,SAAS,OAAO;QAE/C,IAAI,CAACC,OACD,MAAM,IAAIE,cAAcD,OAAO;QAGnC,OAAOD;IACX;IAEQ,YACJJ,IAA6B,EAC7BL,MAAuB,EACA;QACvB,MAAMY,SAASP,KAAK,MAAM;QAC1B,IAAI,CAACO,UAAU,AAAkB,YAAlB,OAAOA,QAClB,OAAOP;QAEX,OAAO;YACH,GAAGA,IAAI;YACP,QAAQ,IAAI,CAAC,QAAQ,CAAC,OAAO,CAACO,QAAmCZ;QACrE;IACJ;AACJ;AAEO,MAAMa,wCAAqBC,mBAAAA,oBAAuC,CAAC;IACtE,gBAAgBb;IAChB,cAAc;QAACc;QAAkBC;QAAmBC;KAAmB;AAC3E"}
@@ -5,9 +5,17 @@ import type { ICmsFieldRenderer } from "../../presentation/fieldRenderers/abstra
5
5
  declare class CmsFormModelBuilderImpl implements ICmsFormModelBuilder {
6
6
  private mappers;
7
7
  private rendererMap;
8
+ private renderers;
8
9
  constructor(customMappers: ICmsFieldTypeMapper[] | undefined, fieldRenderers: ICmsFieldRenderer[] | undefined);
9
10
  build(model: CmsModel): IFormModelConfig;
10
11
  private mapField;
12
+ /**
13
+ * A list renderer can't render a single value: `dateTimeInputs` calls `.map()` on a
14
+ * stored date string and crashes the form. When a single-value field is configured with
15
+ * a renderer that only accepts list fields, drop it so the field type's default
16
+ * single-value renderer is used instead.
17
+ */
18
+ private withCompatibleRenderer;
11
19
  }
12
20
  export declare const CmsFormModelBuilder: typeof CmsFormModelBuilderImpl & {
13
21
  __abstraction: import("@webiny/di").Abstraction<ICmsFormModelBuilder>;
@@ -10,7 +10,11 @@ class CmsFormModelBuilderImpl {
10
10
  for (const mapper of createBuiltInMappers())this.mappers.set(mapper.type, mapper);
11
11
  for (const mapper of customMappers || [])this.mappers.set(mapper.type, mapper);
12
12
  this.rendererMap = new Map();
13
- for (const renderer of fieldRenderers || [])this.rendererMap.set(renderer.rendererName, renderer.formRenderer);
13
+ this.renderers = new Map();
14
+ for (const renderer of fieldRenderers || []){
15
+ this.rendererMap.set(renderer.rendererName, renderer.formRenderer);
16
+ this.renderers.set(renderer.rendererName, renderer);
17
+ }
14
18
  }
15
19
  build(model) {
16
20
  const context = {
@@ -34,11 +38,30 @@ class CmsFormModelBuilderImpl {
34
38
  layout: model.layout ? (layout)=>mapCmsLayout(model.layout, layout, idToFieldId) : void 0
35
39
  };
36
40
  }
37
- mapField(field, registry, context) {
41
+ mapField(modelField, registry, context) {
42
+ const field = this.withCompatibleRenderer(modelField, context.model);
38
43
  const mapper = this.mappers.get(field.type);
39
44
  if (mapper) return mapper.map(field, registry, context);
40
45
  return applyFieldProps(registry.text(), field, context.rendererMap);
41
46
  }
47
+ withCompatibleRenderer(field, model) {
48
+ if (field.list || !field.renderer || "object" != typeof field.renderer) return field;
49
+ const renderer = this.renderers.get(field.renderer.name);
50
+ if (!renderer || renderer.canUse({
51
+ field,
52
+ model
53
+ })) return field;
54
+ if (!renderer.canUse({
55
+ field: {
56
+ ...field,
57
+ list: true
58
+ },
59
+ model
60
+ })) return field;
61
+ if ("development" === process.env.NODE_ENV) console.warn(`[CmsFormModelBuilder] Renderer "${field.renderer.name}" is for list fields; field "${field.fieldId}" is not a list and uses its default renderer instead.`);
62
+ const { renderer: _renderer, ...rest } = field;
63
+ return rest;
64
+ }
42
65
  }
43
66
  const CmsFormModelBuilder_CmsFormModelBuilder = CmsFormModelBuilder.createImplementation({
44
67
  implementation: CmsFormModelBuilderImpl,
@@ -1 +1 @@
1
- {"version":3,"file":"features/formModel/CmsFormModelBuilder.js","sources":["../../../src/features/formModel/CmsFormModelBuilder.ts"],"sourcesContent":["import type {\n IFieldBuilder,\n IFieldBuilderRegistry,\n IFormModelConfig\n} from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport {\n isLayoutField,\n type CmsEditorFieldsLayout,\n type CmsModel,\n type CmsModelField\n} from \"~/types.js\";\nimport type {\n ICmsFieldTypeMapper,\n ICmsFieldMapperContext,\n ICmsFormModelBuilder\n} from \"./abstractions.js\";\nimport { CmsFormModelBuilder as BuilderAbstraction } from \"./abstractions.js\";\nimport { CmsFieldTypeMapper } from \"./abstractions.js\";\nimport { CmsFieldRenderer } from \"~/presentation/fieldRenderers/abstractions.js\";\nimport type { ICmsFieldRenderer } from \"~/presentation/fieldRenderers/abstractions.js\";\nimport { mapCmsLayout } from \"./CmsLayoutMapper.js\";\nimport { createBuiltInMappers } from \"./mappers/index.js\";\nimport { applyFieldProps } from \"./mappers/applyFieldProps.js\";\n\nclass CmsFormModelBuilderImpl implements ICmsFormModelBuilder {\n private mappers: Map<string, ICmsFieldTypeMapper>;\n private rendererMap: Map<string, string>;\n\n constructor(\n customMappers: ICmsFieldTypeMapper[] | undefined,\n fieldRenderers: ICmsFieldRenderer[] | undefined\n ) {\n this.mappers = new Map();\n for (const mapper of createBuiltInMappers()) {\n this.mappers.set(mapper.type, mapper);\n }\n for (const mapper of customMappers || []) {\n this.mappers.set(mapper.type, mapper);\n }\n\n this.rendererMap = new Map();\n for (const renderer of fieldRenderers || []) {\n this.rendererMap.set(renderer.rendererName, renderer.formRenderer);\n }\n }\n\n build(model: CmsModel): IFormModelConfig {\n const context: ICmsFieldMapperContext = {\n model,\n rendererMap: this.rendererMap,\n mapField: (field, registry) => this.mapField(field, registry, context)\n };\n\n const idToFieldId = new Map<string, string>();\n for (const field of model.fields) {\n idToFieldId.set(field.id, field.fieldId);\n }\n\n const fieldIdsInLayout = model.layout ? collectFieldIds(model.layout, idToFieldId) : null;\n\n return {\n fields: registry => {\n const result: Record<string, IFieldBuilder> = {};\n for (const field of model.fields) {\n const builder = this.mapField(field, registry, context);\n if (fieldIdsInLayout && !fieldIdsInLayout.has(field.fieldId)) {\n builder.hidden();\n }\n result[field.fieldId] = builder;\n }\n return result;\n },\n layout: model.layout\n ? layout => mapCmsLayout(model.layout!, layout, idToFieldId)\n : undefined\n };\n }\n\n private mapField(\n field: CmsModelField,\n registry: IFieldBuilderRegistry,\n context: ICmsFieldMapperContext\n ): IFieldBuilder {\n const mapper = this.mappers.get(field.type);\n if (mapper) {\n return mapper.map(field, registry, context);\n }\n\n return applyFieldProps(registry.text(), field, context.rendererMap);\n }\n}\n\nexport const CmsFormModelBuilder = BuilderAbstraction.createImplementation({\n implementation: CmsFormModelBuilderImpl,\n dependencies: [\n [CmsFieldTypeMapper, { multiple: true, optional: true }],\n [CmsFieldRenderer, { multiple: true, optional: true }]\n ]\n});\n\nfunction collectFieldIds(\n layout: CmsEditorFieldsLayout,\n idToFieldId: Map<string, string>\n): Set<string> {\n const fieldIds = new Set<string>();\n\n for (const row of layout) {\n for (const cell of row) {\n if (typeof cell === \"string\") {\n const fieldId = idToFieldId.get(cell);\n if (fieldId) {\n fieldIds.add(fieldId);\n }\n } else if (isLayoutField(cell) && cell.type === \"tabs\") {\n const tabsField = cell as { tabs: Array<{ layout: CmsEditorFieldsLayout }> };\n for (const tab of tabsField.tabs) {\n for (const id of collectFieldIds(tab.layout, idToFieldId)) {\n fieldIds.add(id);\n }\n }\n }\n }\n }\n\n return fieldIds;\n}\n"],"names":["CmsFormModelBuilderImpl","customMappers","fieldRenderers","Map","mapper","createBuiltInMappers","renderer","model","context","field","registry","idToFieldId","fieldIdsInLayout","collectFieldIds","result","builder","layout","mapCmsLayout","undefined","applyFieldProps","CmsFormModelBuilder","BuilderAbstraction","CmsFieldTypeMapper","CmsFieldRenderer","fieldIds","Set","row","cell","fieldId","isLayoutField","tabsField","tab","id"],"mappings":";;;;;;AAwBA,MAAMA;IAIF,YACIC,aAAgD,EAChDC,cAA+C,CACjD;QACE,IAAI,CAAC,OAAO,GAAG,IAAIC;QACnB,KAAK,MAAMC,UAAUC,uBACjB,IAAI,CAAC,OAAO,CAAC,GAAG,CAACD,OAAO,IAAI,EAAEA;QAElC,KAAK,MAAMA,UAAUH,iBAAiB,EAAE,CACpC,IAAI,CAAC,OAAO,CAAC,GAAG,CAACG,OAAO,IAAI,EAAEA;QAGlC,IAAI,CAAC,WAAW,GAAG,IAAID;QACvB,KAAK,MAAMG,YAAYJ,kBAAkB,EAAE,CACvC,IAAI,CAAC,WAAW,CAAC,GAAG,CAACI,SAAS,YAAY,EAAEA,SAAS,YAAY;IAEzE;IAEA,MAAMC,KAAe,EAAoB;QACrC,MAAMC,UAAkC;YACpCD;YACA,aAAa,IAAI,CAAC,WAAW;YAC7B,UAAU,CAACE,OAAOC,WAAa,IAAI,CAAC,QAAQ,CAACD,OAAOC,UAAUF;QAClE;QAEA,MAAMG,cAAc,IAAIR;QACxB,KAAK,MAAMM,SAASF,MAAM,MAAM,CAC5BI,YAAY,GAAG,CAACF,MAAM,EAAE,EAAEA,MAAM,OAAO;QAG3C,MAAMG,mBAAmBL,MAAM,MAAM,GAAGM,gBAAgBN,MAAM,MAAM,EAAEI,eAAe;QAErF,OAAO;YACH,QAAQD,CAAAA;gBACJ,MAAMI,SAAwC,CAAC;gBAC/C,KAAK,MAAML,SAASF,MAAM,MAAM,CAAE;oBAC9B,MAAMQ,UAAU,IAAI,CAAC,QAAQ,CAACN,OAAOC,UAAUF;oBAC/C,IAAII,oBAAoB,CAACA,iBAAiB,GAAG,CAACH,MAAM,OAAO,GACvDM,QAAQ,MAAM;oBAElBD,MAAM,CAACL,MAAM,OAAO,CAAC,GAAGM;gBAC5B;gBACA,OAAOD;YACX;YACA,QAAQP,MAAM,MAAM,GACdS,CAAAA,SAAUC,aAAaV,MAAM,MAAM,EAAGS,QAAQL,eAC9CO;QACV;IACJ;IAEQ,SACJT,KAAoB,EACpBC,QAA+B,EAC/BF,OAA+B,EAClB;QACb,MAAMJ,SAAS,IAAI,CAAC,OAAO,CAAC,GAAG,CAACK,MAAM,IAAI;QAC1C,IAAIL,QACA,OAAOA,OAAO,GAAG,CAACK,OAAOC,UAAUF;QAGvC,OAAOW,gBAAgBT,SAAS,IAAI,IAAID,OAAOD,QAAQ,WAAW;IACtE;AACJ;AAEO,MAAMY,0CAAsBC,oBAAAA,oBAAuC,CAAC;IACvE,gBAAgBrB;IAChB,cAAc;QACV;YAACsB;YAAoB;gBAAE,UAAU;gBAAM,UAAU;YAAK;SAAE;QACxD;YAACC;YAAkB;gBAAE,UAAU;gBAAM,UAAU;YAAK;SAAE;KACzD;AACL;AAEA,SAASV,gBACLG,MAA6B,EAC7BL,WAAgC;IAEhC,MAAMa,WAAW,IAAIC;IAErB,KAAK,MAAMC,OAAOV,OACd,KAAK,MAAMW,QAAQD,IACf,IAAI,AAAgB,YAAhB,OAAOC,MAAmB;QAC1B,MAAMC,UAAUjB,YAAY,GAAG,CAACgB;QAChC,IAAIC,SACAJ,SAAS,GAAG,CAACI;IAErB,OAAO,IAAIC,cAAcF,SAASA,AAAc,WAAdA,KAAK,IAAI,EAAa;QACpD,MAAMG,YAAYH;QAClB,KAAK,MAAMI,OAAOD,UAAU,IAAI,CAC5B,KAAK,MAAME,MAAMnB,gBAAgBkB,IAAI,MAAM,EAAEpB,aACzCa,SAAS,GAAG,CAACQ;IAGzB;IAIR,OAAOR;AACX"}
1
+ {"version":3,"file":"features/formModel/CmsFormModelBuilder.js","sources":["../../../src/features/formModel/CmsFormModelBuilder.ts"],"sourcesContent":["import type {\n IFieldBuilder,\n IFieldBuilderRegistry,\n IFormModelConfig\n} from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport {\n isLayoutField,\n type CmsEditorFieldsLayout,\n type CmsModel,\n type CmsModelField\n} from \"~/types.js\";\nimport type {\n ICmsFieldTypeMapper,\n ICmsFieldMapperContext,\n ICmsFormModelBuilder\n} from \"./abstractions.js\";\nimport { CmsFormModelBuilder as BuilderAbstraction } from \"./abstractions.js\";\nimport { CmsFieldTypeMapper } from \"./abstractions.js\";\nimport { CmsFieldRenderer } from \"~/presentation/fieldRenderers/abstractions.js\";\nimport type { ICmsFieldRenderer } from \"~/presentation/fieldRenderers/abstractions.js\";\nimport { mapCmsLayout } from \"./CmsLayoutMapper.js\";\nimport { createBuiltInMappers } from \"./mappers/index.js\";\nimport { applyFieldProps } from \"./mappers/applyFieldProps.js\";\n\nclass CmsFormModelBuilderImpl implements ICmsFormModelBuilder {\n private mappers: Map<string, ICmsFieldTypeMapper>;\n private rendererMap: Map<string, string>;\n private renderers: Map<string, ICmsFieldRenderer>;\n\n constructor(\n customMappers: ICmsFieldTypeMapper[] | undefined,\n fieldRenderers: ICmsFieldRenderer[] | undefined\n ) {\n this.mappers = new Map();\n for (const mapper of createBuiltInMappers()) {\n this.mappers.set(mapper.type, mapper);\n }\n for (const mapper of customMappers || []) {\n this.mappers.set(mapper.type, mapper);\n }\n\n this.rendererMap = new Map();\n this.renderers = new Map();\n for (const renderer of fieldRenderers || []) {\n this.rendererMap.set(renderer.rendererName, renderer.formRenderer);\n this.renderers.set(renderer.rendererName, renderer);\n }\n }\n\n build(model: CmsModel): IFormModelConfig {\n const context: ICmsFieldMapperContext = {\n model,\n rendererMap: this.rendererMap,\n mapField: (field, registry) => this.mapField(field, registry, context)\n };\n\n const idToFieldId = new Map<string, string>();\n for (const field of model.fields) {\n idToFieldId.set(field.id, field.fieldId);\n }\n\n const fieldIdsInLayout = model.layout ? collectFieldIds(model.layout, idToFieldId) : null;\n\n return {\n fields: registry => {\n const result: Record<string, IFieldBuilder> = {};\n for (const field of model.fields) {\n const builder = this.mapField(field, registry, context);\n if (fieldIdsInLayout && !fieldIdsInLayout.has(field.fieldId)) {\n builder.hidden();\n }\n result[field.fieldId] = builder;\n }\n return result;\n },\n layout: model.layout\n ? layout => mapCmsLayout(model.layout!, layout, idToFieldId)\n : undefined\n };\n }\n\n private mapField(\n modelField: CmsModelField,\n registry: IFieldBuilderRegistry,\n context: ICmsFieldMapperContext\n ): IFieldBuilder {\n const field = this.withCompatibleRenderer(modelField, context.model);\n const mapper = this.mappers.get(field.type);\n if (mapper) {\n return mapper.map(field, registry, context);\n }\n\n return applyFieldProps(registry.text(), field, context.rendererMap);\n }\n\n /**\n * A list renderer can't render a single value: `dateTimeInputs` calls `.map()` on a\n * stored date string and crashes the form. When a single-value field is configured with\n * a renderer that only accepts list fields, drop it so the field type's default\n * single-value renderer is used instead.\n */\n private withCompatibleRenderer(field: CmsModelField, model: CmsModel): CmsModelField {\n if (field.list || !field.renderer || typeof field.renderer !== \"object\") {\n return field;\n }\n const renderer = this.renderers.get(field.renderer.name);\n if (!renderer || renderer.canUse({ field, model })) {\n return field;\n }\n if (!renderer.canUse({ field: { ...field, list: true }, model })) {\n return field;\n }\n if (process.env.NODE_ENV === \"development\") {\n console.warn(\n `[CmsFormModelBuilder] Renderer \"${field.renderer.name}\" is for list fields; ` +\n `field \"${field.fieldId}\" is not a list and uses its default renderer instead.`\n );\n }\n // Mappers treat a missing renderer as \"use the field type's default\".\n const { renderer: _renderer, ...rest } = field;\n return rest as CmsModelField;\n }\n}\n\nexport const CmsFormModelBuilder = BuilderAbstraction.createImplementation({\n implementation: CmsFormModelBuilderImpl,\n dependencies: [\n [CmsFieldTypeMapper, { multiple: true, optional: true }],\n [CmsFieldRenderer, { multiple: true, optional: true }]\n ]\n});\n\nfunction collectFieldIds(\n layout: CmsEditorFieldsLayout,\n idToFieldId: Map<string, string>\n): Set<string> {\n const fieldIds = new Set<string>();\n\n for (const row of layout) {\n for (const cell of row) {\n if (typeof cell === \"string\") {\n const fieldId = idToFieldId.get(cell);\n if (fieldId) {\n fieldIds.add(fieldId);\n }\n } else if (isLayoutField(cell) && cell.type === \"tabs\") {\n const tabsField = cell as { tabs: Array<{ layout: CmsEditorFieldsLayout }> };\n for (const tab of tabsField.tabs) {\n for (const id of collectFieldIds(tab.layout, idToFieldId)) {\n fieldIds.add(id);\n }\n }\n }\n }\n }\n\n return fieldIds;\n}\n"],"names":["CmsFormModelBuilderImpl","customMappers","fieldRenderers","Map","mapper","createBuiltInMappers","renderer","model","context","field","registry","idToFieldId","fieldIdsInLayout","collectFieldIds","result","builder","layout","mapCmsLayout","undefined","modelField","applyFieldProps","process","console","_renderer","rest","CmsFormModelBuilder","BuilderAbstraction","CmsFieldTypeMapper","CmsFieldRenderer","fieldIds","Set","row","cell","fieldId","isLayoutField","tabsField","tab","id"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,111 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { Container } from "@webiny/di";
3
+ import { FormModelFeature } from "@webiny/app-admin/features/formModel/feature.js";
4
+ import { IdentityContextFeature } from "@webiny/app-admin/features/security/IdentityContext/feature.js";
5
+ import { FormModelFactory } from "@webiny/app-admin/features/formModel/abstractions.js";
6
+ import { CmsFormModelBuilder } from "./abstractions.js";
7
+ import { CmsFormModelFeature } from "./feature.js";
8
+ import { DateTimeInputRenderer } from "../../presentation/fieldTypes/renderers/dateTime/DateTimeInputRenderer.js";
9
+ import { DateTimeInputsRenderer } from "../../presentation/fieldTypes/renderers/dateTime/DateTimeInputsRenderer.js";
10
+ import { TextInputRenderer } from "../../presentation/fieldTypes/renderers/text/TextInputRenderer.js";
11
+ import { TextInputsRenderer } from "../../presentation/fieldTypes/renderers/text/TextInputsRenderer.js";
12
+ const field = (fieldId, type, list, renderer, extra = {})=>({
13
+ id: fieldId,
14
+ fieldId,
15
+ type,
16
+ label: fieldId,
17
+ tags: [],
18
+ list,
19
+ renderer: {
20
+ name: renderer,
21
+ settings: {}
22
+ },
23
+ validation: [],
24
+ listValidation: [],
25
+ settings: {},
26
+ predefinedValues: {
27
+ enabled: false,
28
+ values: []
29
+ },
30
+ ...extra
31
+ });
32
+ function createCmsForm(fields) {
33
+ const container = new Container();
34
+ IdentityContextFeature.register(container);
35
+ FormModelFeature.register(container);
36
+ CmsFormModelFeature.register(container);
37
+ container.register(DateTimeInputRenderer);
38
+ container.register(DateTimeInputsRenderer);
39
+ container.register(TextInputRenderer);
40
+ container.register(TextInputsRenderer);
41
+ const model = {
42
+ modelId: "test",
43
+ fields,
44
+ layout: fields.map((f)=>[
45
+ f.fieldId
46
+ ])
47
+ };
48
+ const config = container.resolve(CmsFormModelBuilder).build(model);
49
+ return container.resolve(FormModelFactory).create(config);
50
+ }
51
+ describe("CmsFormModelBuilder renderer cardinality", ()=>{
52
+ it("keeps renderers that match the field's cardinality", ()=>{
53
+ const form = createCmsForm([
54
+ field("single", "datetime", false, "date-time-input"),
55
+ field("many", "datetime", true, "date-time-inputs")
56
+ ]);
57
+ expect(form.field("single").vm.renderer).toBe("dateTimeInput");
58
+ expect(form.field("many").vm.renderer).toBe("dateTimeInputs");
59
+ });
60
+ it("falls back to the single-value renderer when a list renderer is set on a non-list field", ()=>{
61
+ const form = createCmsForm([
62
+ field("openTime", "datetime", false, "date-time-inputs"),
63
+ field("name", "text", false, "text-inputs")
64
+ ]);
65
+ expect(form.field("openTime").vm.renderer).toBe("dateTimeInput");
66
+ expect(form.field("name").vm.renderer).toBe("textInput");
67
+ });
68
+ it("keeps the list renderer for list datetime fields with a subtype", ()=>{
69
+ const form = createCmsForm([
70
+ "date",
71
+ "time",
72
+ "dateTimeWithTimezone",
73
+ "dateTimeWithoutTimezone"
74
+ ].map((type)=>field(type, "datetime", true, "date-time-inputs", {
75
+ settings: {
76
+ type
77
+ }
78
+ })));
79
+ expect(form.field("date").vm.renderer).toBe("dateTimeInputs");
80
+ expect(form.field("time").vm.renderer).toBe("dateTimeInputs");
81
+ expect(form.field("dateTimeWithTimezone").vm.renderer).toBe("dateTimeInputs");
82
+ expect(form.field("dateTimeWithoutTimezone").vm.renderer).toBe("dateTimeInputs");
83
+ });
84
+ it("applies the fallback to fields nested in object lists", ()=>{
85
+ const form = createCmsForm([
86
+ field("holidayHours", "object", true, "objects-accordion", {
87
+ settings: {
88
+ fields: [
89
+ field("openTime", "datetime", false, "date-time-inputs")
90
+ ],
91
+ layout: [
92
+ [
93
+ "openTime"
94
+ ]
95
+ ]
96
+ }
97
+ })
98
+ ]);
99
+ form.setData({
100
+ holidayHours: [
101
+ {
102
+ openTime: "2026-12-24T09:00:00.000Z"
103
+ }
104
+ ]
105
+ });
106
+ const item = form.field("holidayHours").items[0];
107
+ expect(item.children.get("openTime").vm.renderer).toBe("dateTimeInput");
108
+ });
109
+ });
110
+
111
+ //# sourceMappingURL=CmsFormModelBuilder.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features/formModel/CmsFormModelBuilder.test.js","sources":["../../../src/features/formModel/CmsFormModelBuilder.test.ts"],"sourcesContent":["import { describe, it, expect } from \"vitest\";\nimport { Container } from \"@webiny/di\";\nimport { FormModelFeature } from \"@webiny/app-admin/features/formModel/feature.js\";\nimport { IdentityContextFeature } from \"@webiny/app-admin/features/security/IdentityContext/feature.js\";\nimport { FormModelFactory } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport { CmsFormModelBuilder } from \"~/features/formModel/abstractions.js\";\nimport { CmsFormModelFeature } from \"~/features/formModel/feature.js\";\nimport { DateTimeInputRenderer } from \"~/presentation/fieldTypes/renderers/dateTime/DateTimeInputRenderer.js\";\nimport { DateTimeInputsRenderer } from \"~/presentation/fieldTypes/renderers/dateTime/DateTimeInputsRenderer.js\";\nimport { TextInputRenderer } from \"~/presentation/fieldTypes/renderers/text/TextInputRenderer.js\";\nimport { TextInputsRenderer } from \"~/presentation/fieldTypes/renderers/text/TextInputsRenderer.js\";\n\nconst field = (fieldId: string, type: string, list: boolean, renderer: string, extra = {}) => ({\n id: fieldId,\n fieldId,\n type,\n label: fieldId,\n tags: [],\n list,\n renderer: { name: renderer, settings: {} },\n validation: [],\n listValidation: [],\n settings: {},\n predefinedValues: { enabled: false, values: [] },\n ...extra\n});\n\nfunction createCmsForm(fields: any[]) {\n const container = new Container();\n IdentityContextFeature.register(container);\n FormModelFeature.register(container);\n CmsFormModelFeature.register(container);\n container.register(DateTimeInputRenderer);\n container.register(DateTimeInputsRenderer);\n container.register(TextInputRenderer);\n container.register(TextInputsRenderer);\n\n const model = {\n modelId: \"test\",\n fields,\n layout: fields.map(f => [f.fieldId])\n } as any;\n const config = container.resolve(CmsFormModelBuilder).build(model);\n return container.resolve(FormModelFactory).create(config);\n}\n\ndescribe(\"CmsFormModelBuilder renderer cardinality\", () => {\n it(\"keeps renderers that match the field's cardinality\", () => {\n const form = createCmsForm([\n field(\"single\", \"datetime\", false, \"date-time-input\"),\n field(\"many\", \"datetime\", true, \"date-time-inputs\")\n ]);\n\n expect(form.field(\"single\").vm.renderer).toBe(\"dateTimeInput\");\n expect(form.field(\"many\").vm.renderer).toBe(\"dateTimeInputs\");\n });\n\n it(\"falls back to the single-value renderer when a list renderer is set on a non-list field\", () => {\n const form = createCmsForm([\n field(\"openTime\", \"datetime\", false, \"date-time-inputs\"),\n field(\"name\", \"text\", false, \"text-inputs\")\n ]);\n\n expect(form.field(\"openTime\").vm.renderer).toBe(\"dateTimeInput\");\n expect(form.field(\"name\").vm.renderer).toBe(\"textInput\");\n });\n\n it(\"keeps the list renderer for list datetime fields with a subtype\", () => {\n const form = createCmsForm(\n [\"date\", \"time\", \"dateTimeWithTimezone\", \"dateTimeWithoutTimezone\"].map(type =>\n field(type, \"datetime\", true, \"date-time-inputs\", { settings: { type } })\n )\n );\n\n expect(form.field(\"date\").vm.renderer).toBe(\"dateTimeInputs\");\n expect(form.field(\"time\").vm.renderer).toBe(\"dateTimeInputs\");\n expect(form.field(\"dateTimeWithTimezone\").vm.renderer).toBe(\"dateTimeInputs\");\n expect(form.field(\"dateTimeWithoutTimezone\").vm.renderer).toBe(\"dateTimeInputs\");\n });\n\n it(\"applies the fallback to fields nested in object lists\", () => {\n const form = createCmsForm([\n field(\"holidayHours\", \"object\", true, \"objects-accordion\", {\n settings: {\n fields: [field(\"openTime\", \"datetime\", false, \"date-time-inputs\")],\n layout: [[\"openTime\"]]\n }\n })\n ]);\n\n form.setData({ holidayHours: [{ openTime: \"2026-12-24T09:00:00.000Z\" }] });\n const item = (form.field(\"holidayHours\") as any).items[0];\n\n expect(item.children.get(\"openTime\").vm.renderer).toBe(\"dateTimeInput\");\n });\n});\n"],"names":["field","fieldId","type","list","renderer","extra","createCmsForm","fields","container","Container","IdentityContextFeature","FormModelFeature","CmsFormModelFeature","DateTimeInputRenderer","DateTimeInputsRenderer","TextInputRenderer","TextInputsRenderer","model","f","config","CmsFormModelBuilder","FormModelFactory","describe","it","form","expect","item"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webiny/app-headless-cms",
3
- "version": "6.5.0-beta.2",
3
+ "version": "6.5.0-beta.4",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -28,25 +28,25 @@
28
28
  "@fortawesome/react-fontawesome": "3.5.0",
29
29
  "@lexical/utils": "0.48.0",
30
30
  "@types/react": "18.3.31",
31
- "@webiny/admin-ui": "6.5.0-beta.2",
32
- "@webiny/app": "6.5.0-beta.2",
33
- "@webiny/app-aco": "6.5.0-beta.2",
34
- "@webiny/app-admin": "6.5.0-beta.2",
35
- "@webiny/app-graphql-playground": "6.5.0-beta.2",
36
- "@webiny/app-headless-cms-common": "6.5.0-beta.2",
37
- "@webiny/cms-sdk": "6.5.0-beta.2",
31
+ "@webiny/admin-ui": "6.5.0-beta.4",
32
+ "@webiny/app": "6.5.0-beta.4",
33
+ "@webiny/app-aco": "6.5.0-beta.4",
34
+ "@webiny/app-admin": "6.5.0-beta.4",
35
+ "@webiny/app-graphql-playground": "6.5.0-beta.4",
36
+ "@webiny/app-headless-cms-common": "6.5.0-beta.4",
37
+ "@webiny/cms-sdk": "6.5.0-beta.4",
38
38
  "@webiny/di": "1.0.2",
39
- "@webiny/feature": "6.5.0-beta.2",
40
- "@webiny/form": "6.5.0-beta.2",
41
- "@webiny/frontend-settings": "6.5.0-beta.2",
42
- "@webiny/icons": "6.5.0-beta.2",
43
- "@webiny/lexical-editor": "6.5.0-beta.2",
44
- "@webiny/lexical-nodes": "6.5.0-beta.2",
45
- "@webiny/plugins": "6.5.0-beta.2",
46
- "@webiny/react-composition": "6.5.0-beta.2",
47
- "@webiny/react-properties": "6.5.0-beta.2",
48
- "@webiny/utils": "6.5.0-beta.2",
49
- "@webiny/validation": "6.5.0-beta.2",
39
+ "@webiny/feature": "6.5.0-beta.4",
40
+ "@webiny/form": "6.5.0-beta.4",
41
+ "@webiny/frontend-settings": "6.5.0-beta.4",
42
+ "@webiny/icons": "6.5.0-beta.4",
43
+ "@webiny/lexical-editor": "6.5.0-beta.4",
44
+ "@webiny/lexical-nodes": "6.5.0-beta.4",
45
+ "@webiny/plugins": "6.5.0-beta.4",
46
+ "@webiny/react-composition": "6.5.0-beta.4",
47
+ "@webiny/react-properties": "6.5.0-beta.4",
48
+ "@webiny/utils": "6.5.0-beta.4",
49
+ "@webiny/validation": "6.5.0-beta.4",
50
50
  "dot-prop-immutable": "2.1.1",
51
51
  "graphql": "16.14.2",
52
52
  "graphql-tag": "2.12.7",
@@ -63,7 +63,7 @@
63
63
  "zod": "4.4.3"
64
64
  },
65
65
  "devDependencies": {
66
- "@webiny/build-tools": "6.5.0-beta.2",
66
+ "@webiny/build-tools": "6.5.0-beta.4",
67
67
  "rimraf": "6.1.3",
68
68
  "typescript": "7.0.2",
69
69
  "vitest": "4.1.10"
@@ -13,6 +13,7 @@ import { CmsModelContext } from "../../../features/contentEntry/abstractions.js"
13
13
  import { ContentEntryFormModelModifier, ContentEntryFormPresenter } from "./abstractions.js";
14
14
  import { TRASH_ENTRY_DIALOG } from "../list/ContentEntriesPresenter.js";
15
15
  import { CreateRevisionFromUseCase } from "../../../features/contentEntry/createRevisionFrom/abstractions.js";
16
+ import { toFormErrors } from "./toFormErrors.js";
16
17
  const PUBLISH_ENTRY_DIALOG = "publish-entry";
17
18
  class ContentEntryFormPresenterImpl {
18
19
  constructor(formModelFactory, cmsFormModelBuilder, confirmation, modelAccessor, getEntryUseCase, createEntryUseCase, createRevisionFromUseCase, updateEntryUseCase, publishEntryUseCase, unpublishEntryUseCase, deleteEntryUseCase, updateRevisionDescriptionUseCase, formModelModifiers){
@@ -119,7 +120,8 @@ class ContentEntryFormPresenterImpl {
119
120
  });
120
121
  }
121
122
  return true;
122
- } catch {
123
+ } catch (error) {
124
+ this.form.setErrors(toFormErrors(error, this.model));
123
125
  return false;
124
126
  } finally{
125
127
  runInAction(()=>{
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/contentEntries/form/ContentEntryFormPresenter.js","sources":["../../../../src/presentation/contentEntries/form/ContentEntryFormPresenter.ts"],"sourcesContent":["import { computed, makeAutoObservable, runInAction, toJS } from \"mobx\";\nimport { FormModel, FormModelFactory } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport { Confirmation } from \"@webiny/app-admin/features/confirmation/abstractions.js\";\nimport type { CmsContentEntry } from \"~/types.js\";\nimport { GetEntryUseCase } from \"~/features/contentEntry/getEntry/abstractions.js\";\nimport { CreateEntryUseCase } from \"~/features/contentEntry/createEntry/abstractions.js\";\nimport { UpdateEntryUseCase } from \"~/features/contentEntry/updateEntry/abstractions.js\";\nimport { PublishEntryUseCase } from \"~/features/contentEntry/publishEntry/abstractions.js\";\nimport { UnpublishEntryUseCase } from \"~/features/contentEntry/unpublishEntry/abstractions.js\";\nimport { DeleteEntryUseCase } from \"~/features/contentEntry/deleteEntry/abstractions.js\";\nimport { UpdateRevisionDescriptionUseCase } from \"~/features/contentEntry/updateRevisionDescription/abstractions.js\";\nimport { CmsFormModelBuilder } from \"~/features/formModel/abstractions.js\";\nimport { CmsModelContext } from \"~/features/contentEntry/abstractions.js\";\nimport {\n ContentEntryFormModelModifier,\n ContentEntryFormPresenter as Abstraction\n} from \"./abstractions.js\";\nimport { TRASH_ENTRY_DIALOG } from \"~/presentation/contentEntries/list/ContentEntriesPresenter.js\";\nimport { CreateRevisionFromUseCase } from \"~/features/contentEntry/createRevisionFrom/abstractions.js\";\n\ninterface PublishEntryDialogData {\n revisionDescription: string;\n}\n\ninterface SaveEntryParams {\n entry: CmsContentEntry;\n data: Record<string, unknown>;\n skipValidation?: boolean;\n}\n\nexport const PUBLISH_ENTRY_DIALOG = \"publish-entry\";\n\nclass ContentEntryFormPresenterImpl implements Abstraction.Interface {\n private entry: CmsContentEntry | null = null;\n private form: FormModel.Interface | null = null;\n private loading: string | null = null;\n private folderId: string | null = null;\n\n constructor(\n private readonly formModelFactory: FormModelFactory.Interface,\n private readonly cmsFormModelBuilder: CmsFormModelBuilder.Interface,\n private readonly confirmation: Confirmation.Interface,\n private readonly modelAccessor: CmsModelContext.Interface,\n private readonly getEntryUseCase: GetEntryUseCase.Interface,\n private readonly createEntryUseCase: CreateEntryUseCase.Interface,\n private readonly createRevisionFromUseCase: CreateRevisionFromUseCase.Interface,\n private readonly updateEntryUseCase: UpdateEntryUseCase.Interface,\n private readonly publishEntryUseCase: PublishEntryUseCase.Interface,\n private readonly unpublishEntryUseCase: UnpublishEntryUseCase.Interface,\n private readonly deleteEntryUseCase: DeleteEntryUseCase.Interface,\n private readonly updateRevisionDescriptionUseCase: UpdateRevisionDescriptionUseCase.Interface,\n private readonly formModelModifiers: ContentEntryFormModelModifier.Interface[]\n ) {\n makeAutoObservable<\n ContentEntryFormPresenterImpl,\n | \"formModelFactory\"\n | \"cmsFormModelBuilder\"\n | \"confirmation\"\n | \"modelAccessor\"\n | \"getEntryUseCase\"\n | \"createEntryUseCase\"\n | \"updateEntryUseCase\"\n | \"publishEntryUseCase\"\n | \"unpublishEntryUseCase\"\n | \"deleteEntryUseCase\"\n | \"updateRevisionDescriptionUseCase\"\n | \"formModelModifiers\"\n >(this, {\n formModelFactory: false,\n cmsFormModelBuilder: false,\n confirmation: false,\n modelAccessor: false,\n getEntryUseCase: false,\n createEntryUseCase: false,\n updateEntryUseCase: false,\n publishEntryUseCase: false,\n unpublishEntryUseCase: false,\n deleteEntryUseCase: false,\n updateRevisionDescriptionUseCase: false,\n formModelModifiers: false,\n vm: computed\n });\n }\n\n private get model() {\n return this.modelAccessor.getModel();\n }\n\n get vm(): Abstraction.ViewModel {\n const entry = this.entry;\n const meta = entry?.meta;\n const isLocked = meta?.locked ?? false;\n const status = meta?.status;\n /**\n * Can save if the entry is locked and published/unpublished - it will trigger create new revision.\n * Or if the entry is not locked and the form is not null (meaning we are creating a new entry or editing an existing one).\n */\n const canSave =\n (isLocked && (status === \"published\" || status === \"unpublished\")) ||\n (!isLocked && this.form !== null);\n\n return {\n loading: this.loading,\n model: this.model,\n entry: toJS(this.entry),\n form: this.form?.vm ?? null,\n canSave,\n status,\n canCreateNewRevision:\n !!status && canSave && [\"published\", \"unpublished\"].includes(status),\n canPublish: this.entry !== null && status !== \"published\",\n canUnpublish: this.entry !== null && status === \"published\",\n canDelete: this.entry !== null,\n isNewEntry: this.entry === null && this.form !== null,\n isDirty: this.form?.isDirty ?? false\n };\n }\n\n async saveRevision(options?: { skipValidation?: boolean }): Promise<boolean> {\n if (!this.form) {\n return false;\n }\n\n const skipValidation = options?.skipValidation ?? true;\n const data = await this.form.submit({ skipValidation });\n\n if (!data) {\n return false;\n }\n\n runInAction(() => {\n this.loading = \"Saving...\";\n });\n\n try {\n if (this.entry) {\n /**\n * In case the entry already exists, and it is NOT locked, we will update the existing revision.\n * If it is locked, we will create a new revision from the existing one, and then update that new revision.\n */\n const entry = await this.saveEntry({\n entry: this.entry,\n data,\n skipValidation\n });\n\n runInAction(() => {\n this.entry = entry;\n this.form!.setData(entry.values);\n this.form!.reset();\n });\n } else {\n const createData: Record<string, unknown> = { values: data };\n if (this.folderId) {\n createData.wbyAco_location = { folderId: this.folderId };\n }\n\n const entry = await this.createEntryUseCase.execute({\n model: this.model,\n data: createData,\n options: { skipValidation }\n });\n\n runInAction(() => {\n this.entry = entry;\n this.form!.setData(entry.values);\n this.form!.reset();\n });\n }\n\n return true;\n } catch {\n return false;\n } finally {\n runInAction(() => {\n this.loading = null;\n });\n }\n }\n\n async publishRevision(): Promise<boolean> {\n if (!this.entry) {\n return false;\n }\n\n const result = await this.confirmation.confirm<PublishEntryDialogData>(\n PUBLISH_ENTRY_DIALOG,\n { entry: this.entry }\n );\n\n if (result === false) {\n return false;\n }\n\n runInAction(() => {\n this.loading = \"Publishing...\";\n });\n\n try {\n if (result.revisionDescription) {\n await this.updateRevisionDescriptionUseCase.execute({\n model: this.model,\n id: this.entry.id,\n revisionDescription: result.revisionDescription\n });\n }\n\n const entry = await this.publishEntryUseCase.execute({\n model: this.model,\n revisionId: this.entry.id\n });\n\n runInAction(() => {\n this.entry = entry;\n });\n\n return true;\n } catch {\n return false;\n } finally {\n runInAction(() => {\n this.loading = null;\n });\n }\n }\n\n async unpublishRevision(): Promise<boolean> {\n if (!this.entry) {\n return false;\n }\n\n this.loading = \"Unpublishing...\";\n\n try {\n const entry = await this.unpublishEntryUseCase.execute({\n model: this.model,\n revisionId: this.entry.id\n });\n\n runInAction(() => {\n this.entry = entry;\n });\n\n return true;\n } catch {\n return false;\n } finally {\n runInAction(() => {\n this.loading = null;\n });\n }\n }\n\n async deleteEntry(): Promise<boolean> {\n if (!this.entry) {\n return false;\n }\n\n const model = this.model;\n const entry = this.entry;\n\n return this.confirmation.confirm(\n TRASH_ENTRY_DIALOG,\n { entryId: entry.entryId },\n async () => {\n try {\n await this.deleteEntryUseCase.execute({\n model,\n id: entry.entryId\n });\n\n runInAction(() => {\n this.entry = null;\n this.form = null;\n });\n\n return true;\n } catch {\n return false;\n }\n }\n );\n }\n\n async loadRevision(id: string): Promise<void> {\n this.loading = \"Loading entry...\";\n\n try {\n const entry = await this.getEntryUseCase.execute({\n model: this.model,\n id\n });\n\n runInAction(() => {\n this.entry = entry;\n this.buildAndPopulateForm(entry);\n });\n } finally {\n runInAction(() => {\n this.loading = null;\n });\n }\n }\n\n setFolderId(folderId: string | null): void {\n this.folderId = folderId;\n }\n\n newEntry(initialValues?: Record<string, unknown>): void {\n this.entry = null;\n this.form = this.buildForm();\n if (initialValues) {\n this.form.setData(initialValues, { dirty: true });\n }\n }\n\n patchEntryMeta(meta: Partial<CmsContentEntry[\"meta\"]>): void {\n if (!this.entry) {\n return;\n }\n this.entry = {\n ...this.entry,\n meta: { ...this.entry.meta, ...meta }\n };\n }\n\n reset(): void {\n this.form = null;\n this.entry = null;\n }\n\n private buildForm(): FormModel.Interface {\n const formConfig = this.cmsFormModelBuilder.build(this.model);\n const form = this.formModelFactory.create(formConfig);\n for (const modifier of this.formModelModifiers) {\n modifier.modifyForm(form, this.model);\n }\n return form;\n }\n\n private buildAndPopulateForm(entry: CmsContentEntry): void {\n this.form = this.buildForm();\n this.form.setData(entry.values);\n this.form.reset();\n }\n /**\n * Depending on whether the entry is locked or not, this method will either create a new revision from the existing one and update it, or simply update the existing revision.\n */\n private async saveEntry(params: SaveEntryParams): Promise<any> {\n const { entry, data, skipValidation } = params;\n\n if (entry.meta.locked) {\n return await this.createRevisionFromUseCase.execute({\n model: this.model,\n revisionId: entry.id,\n data: {\n values: data\n },\n options: { skipValidation }\n });\n }\n return await this.updateEntryUseCase.execute({\n model: this.model,\n revisionId: entry.id,\n data: {\n values: data\n },\n options: { skipValidation }\n });\n }\n}\n\nexport const ContentEntryFormPresenter = Abstraction.createImplementation({\n implementation: ContentEntryFormPresenterImpl,\n dependencies: [\n FormModelFactory,\n CmsFormModelBuilder,\n Confirmation,\n CmsModelContext,\n GetEntryUseCase,\n CreateEntryUseCase,\n CreateRevisionFromUseCase,\n UpdateEntryUseCase,\n PublishEntryUseCase,\n UnpublishEntryUseCase,\n DeleteEntryUseCase,\n UpdateRevisionDescriptionUseCase,\n [ContentEntryFormModelModifier, { multiple: true }]\n ]\n});\n"],"names":["PUBLISH_ENTRY_DIALOG","ContentEntryFormPresenterImpl","formModelFactory","cmsFormModelBuilder","confirmation","modelAccessor","getEntryUseCase","createEntryUseCase","createRevisionFromUseCase","updateEntryUseCase","publishEntryUseCase","unpublishEntryUseCase","deleteEntryUseCase","updateRevisionDescriptionUseCase","formModelModifiers","makeAutoObservable","computed","entry","meta","isLocked","status","canSave","toJS","options","skipValidation","data","runInAction","createData","result","model","TRASH_ENTRY_DIALOG","id","folderId","initialValues","formConfig","form","modifier","params","ContentEntryFormPresenter","Abstraction","FormModelFactory","CmsFormModelBuilder","Confirmation","CmsModelContext","GetEntryUseCase","CreateEntryUseCase","CreateRevisionFromUseCase","UpdateEntryUseCase","PublishEntryUseCase","UnpublishEntryUseCase","DeleteEntryUseCase","UpdateRevisionDescriptionUseCase","ContentEntryFormModelModifier"],"mappings":"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"}
1
+ {"version":3,"file":"presentation/contentEntries/form/ContentEntryFormPresenter.js","sources":["../../../../src/presentation/contentEntries/form/ContentEntryFormPresenter.ts"],"sourcesContent":["import { computed, makeAutoObservable, runInAction, toJS } from \"mobx\";\nimport { FormModel, FormModelFactory } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport { Confirmation } from \"@webiny/app-admin/features/confirmation/abstractions.js\";\nimport type { CmsContentEntry } from \"~/types.js\";\nimport { GetEntryUseCase } from \"~/features/contentEntry/getEntry/abstractions.js\";\nimport { CreateEntryUseCase } from \"~/features/contentEntry/createEntry/abstractions.js\";\nimport { UpdateEntryUseCase } from \"~/features/contentEntry/updateEntry/abstractions.js\";\nimport { PublishEntryUseCase } from \"~/features/contentEntry/publishEntry/abstractions.js\";\nimport { UnpublishEntryUseCase } from \"~/features/contentEntry/unpublishEntry/abstractions.js\";\nimport { DeleteEntryUseCase } from \"~/features/contentEntry/deleteEntry/abstractions.js\";\nimport { UpdateRevisionDescriptionUseCase } from \"~/features/contentEntry/updateRevisionDescription/abstractions.js\";\nimport { CmsFormModelBuilder } from \"~/features/formModel/abstractions.js\";\nimport { CmsModelContext } from \"~/features/contentEntry/abstractions.js\";\nimport {\n ContentEntryFormModelModifier,\n ContentEntryFormPresenter as Abstraction\n} from \"./abstractions.js\";\nimport { TRASH_ENTRY_DIALOG } from \"~/presentation/contentEntries/list/ContentEntriesPresenter.js\";\nimport { CreateRevisionFromUseCase } from \"~/features/contentEntry/createRevisionFrom/abstractions.js\";\nimport { toFormErrors } from \"./toFormErrors.js\";\n\ninterface PublishEntryDialogData {\n revisionDescription: string;\n}\n\ninterface SaveEntryParams {\n entry: CmsContentEntry;\n data: Record<string, unknown>;\n skipValidation?: boolean;\n}\n\nexport const PUBLISH_ENTRY_DIALOG = \"publish-entry\";\n\nclass ContentEntryFormPresenterImpl implements Abstraction.Interface {\n private entry: CmsContentEntry | null = null;\n private form: FormModel.Interface | null = null;\n private loading: string | null = null;\n private folderId: string | null = null;\n\n constructor(\n private readonly formModelFactory: FormModelFactory.Interface,\n private readonly cmsFormModelBuilder: CmsFormModelBuilder.Interface,\n private readonly confirmation: Confirmation.Interface,\n private readonly modelAccessor: CmsModelContext.Interface,\n private readonly getEntryUseCase: GetEntryUseCase.Interface,\n private readonly createEntryUseCase: CreateEntryUseCase.Interface,\n private readonly createRevisionFromUseCase: CreateRevisionFromUseCase.Interface,\n private readonly updateEntryUseCase: UpdateEntryUseCase.Interface,\n private readonly publishEntryUseCase: PublishEntryUseCase.Interface,\n private readonly unpublishEntryUseCase: UnpublishEntryUseCase.Interface,\n private readonly deleteEntryUseCase: DeleteEntryUseCase.Interface,\n private readonly updateRevisionDescriptionUseCase: UpdateRevisionDescriptionUseCase.Interface,\n private readonly formModelModifiers: ContentEntryFormModelModifier.Interface[]\n ) {\n makeAutoObservable<\n ContentEntryFormPresenterImpl,\n | \"formModelFactory\"\n | \"cmsFormModelBuilder\"\n | \"confirmation\"\n | \"modelAccessor\"\n | \"getEntryUseCase\"\n | \"createEntryUseCase\"\n | \"updateEntryUseCase\"\n | \"publishEntryUseCase\"\n | \"unpublishEntryUseCase\"\n | \"deleteEntryUseCase\"\n | \"updateRevisionDescriptionUseCase\"\n | \"formModelModifiers\"\n >(this, {\n formModelFactory: false,\n cmsFormModelBuilder: false,\n confirmation: false,\n modelAccessor: false,\n getEntryUseCase: false,\n createEntryUseCase: false,\n updateEntryUseCase: false,\n publishEntryUseCase: false,\n unpublishEntryUseCase: false,\n deleteEntryUseCase: false,\n updateRevisionDescriptionUseCase: false,\n formModelModifiers: false,\n vm: computed\n });\n }\n\n private get model() {\n return this.modelAccessor.getModel();\n }\n\n get vm(): Abstraction.ViewModel {\n const entry = this.entry;\n const meta = entry?.meta;\n const isLocked = meta?.locked ?? false;\n const status = meta?.status;\n /**\n * Can save if the entry is locked and published/unpublished - it will trigger create new revision.\n * Or if the entry is not locked and the form is not null (meaning we are creating a new entry or editing an existing one).\n */\n const canSave =\n (isLocked && (status === \"published\" || status === \"unpublished\")) ||\n (!isLocked && this.form !== null);\n\n return {\n loading: this.loading,\n model: this.model,\n entry: toJS(this.entry),\n form: this.form?.vm ?? null,\n canSave,\n status,\n canCreateNewRevision:\n !!status && canSave && [\"published\", \"unpublished\"].includes(status),\n canPublish: this.entry !== null && status !== \"published\",\n canUnpublish: this.entry !== null && status === \"published\",\n canDelete: this.entry !== null,\n isNewEntry: this.entry === null && this.form !== null,\n isDirty: this.form?.isDirty ?? false\n };\n }\n\n async saveRevision(options?: { skipValidation?: boolean }): Promise<boolean> {\n if (!this.form) {\n return false;\n }\n\n const skipValidation = options?.skipValidation ?? true;\n const data = await this.form.submit({ skipValidation });\n\n if (!data) {\n return false;\n }\n\n runInAction(() => {\n this.loading = \"Saving...\";\n });\n\n try {\n if (this.entry) {\n /**\n * In case the entry already exists, and it is NOT locked, we will update the existing revision.\n * If it is locked, we will create a new revision from the existing one, and then update that new revision.\n */\n const entry = await this.saveEntry({\n entry: this.entry,\n data,\n skipValidation\n });\n\n runInAction(() => {\n this.entry = entry;\n this.form!.setData(entry.values);\n this.form!.reset();\n });\n } else {\n const createData: Record<string, unknown> = { values: data };\n if (this.folderId) {\n createData.wbyAco_location = { folderId: this.folderId };\n }\n\n const entry = await this.createEntryUseCase.execute({\n model: this.model,\n data: createData,\n options: { skipValidation }\n });\n\n runInAction(() => {\n this.entry = entry;\n this.form!.setData(entry.values);\n this.form!.reset();\n });\n }\n\n return true;\n } catch (error) {\n this.form.setErrors(toFormErrors(error, this.model));\n return false;\n } finally {\n runInAction(() => {\n this.loading = null;\n });\n }\n }\n\n async publishRevision(): Promise<boolean> {\n if (!this.entry) {\n return false;\n }\n\n const result = await this.confirmation.confirm<PublishEntryDialogData>(\n PUBLISH_ENTRY_DIALOG,\n { entry: this.entry }\n );\n\n if (result === false) {\n return false;\n }\n\n runInAction(() => {\n this.loading = \"Publishing...\";\n });\n\n try {\n if (result.revisionDescription) {\n await this.updateRevisionDescriptionUseCase.execute({\n model: this.model,\n id: this.entry.id,\n revisionDescription: result.revisionDescription\n });\n }\n\n const entry = await this.publishEntryUseCase.execute({\n model: this.model,\n revisionId: this.entry.id\n });\n\n runInAction(() => {\n this.entry = entry;\n });\n\n return true;\n } catch {\n return false;\n } finally {\n runInAction(() => {\n this.loading = null;\n });\n }\n }\n\n async unpublishRevision(): Promise<boolean> {\n if (!this.entry) {\n return false;\n }\n\n this.loading = \"Unpublishing...\";\n\n try {\n const entry = await this.unpublishEntryUseCase.execute({\n model: this.model,\n revisionId: this.entry.id\n });\n\n runInAction(() => {\n this.entry = entry;\n });\n\n return true;\n } catch {\n return false;\n } finally {\n runInAction(() => {\n this.loading = null;\n });\n }\n }\n\n async deleteEntry(): Promise<boolean> {\n if (!this.entry) {\n return false;\n }\n\n const model = this.model;\n const entry = this.entry;\n\n return this.confirmation.confirm(\n TRASH_ENTRY_DIALOG,\n { entryId: entry.entryId },\n async () => {\n try {\n await this.deleteEntryUseCase.execute({\n model,\n id: entry.entryId\n });\n\n runInAction(() => {\n this.entry = null;\n this.form = null;\n });\n\n return true;\n } catch {\n return false;\n }\n }\n );\n }\n\n async loadRevision(id: string): Promise<void> {\n this.loading = \"Loading entry...\";\n\n try {\n const entry = await this.getEntryUseCase.execute({\n model: this.model,\n id\n });\n\n runInAction(() => {\n this.entry = entry;\n this.buildAndPopulateForm(entry);\n });\n } finally {\n runInAction(() => {\n this.loading = null;\n });\n }\n }\n\n setFolderId(folderId: string | null): void {\n this.folderId = folderId;\n }\n\n newEntry(initialValues?: Record<string, unknown>): void {\n this.entry = null;\n this.form = this.buildForm();\n if (initialValues) {\n this.form.setData(initialValues, { dirty: true });\n }\n }\n\n patchEntryMeta(meta: Partial<CmsContentEntry[\"meta\"]>): void {\n if (!this.entry) {\n return;\n }\n this.entry = {\n ...this.entry,\n meta: { ...this.entry.meta, ...meta }\n };\n }\n\n reset(): void {\n this.form = null;\n this.entry = null;\n }\n\n private buildForm(): FormModel.Interface {\n const formConfig = this.cmsFormModelBuilder.build(this.model);\n const form = this.formModelFactory.create(formConfig);\n for (const modifier of this.formModelModifiers) {\n modifier.modifyForm(form, this.model);\n }\n return form;\n }\n\n private buildAndPopulateForm(entry: CmsContentEntry): void {\n this.form = this.buildForm();\n this.form.setData(entry.values);\n this.form.reset();\n }\n /**\n * Depending on whether the entry is locked or not, this method will either create a new revision from the existing one and update it, or simply update the existing revision.\n */\n private async saveEntry(params: SaveEntryParams): Promise<any> {\n const { entry, data, skipValidation } = params;\n\n if (entry.meta.locked) {\n return await this.createRevisionFromUseCase.execute({\n model: this.model,\n revisionId: entry.id,\n data: {\n values: data\n },\n options: { skipValidation }\n });\n }\n return await this.updateEntryUseCase.execute({\n model: this.model,\n revisionId: entry.id,\n data: {\n values: data\n },\n options: { skipValidation }\n });\n }\n}\n\nexport const ContentEntryFormPresenter = Abstraction.createImplementation({\n implementation: ContentEntryFormPresenterImpl,\n dependencies: [\n FormModelFactory,\n CmsFormModelBuilder,\n Confirmation,\n CmsModelContext,\n GetEntryUseCase,\n CreateEntryUseCase,\n CreateRevisionFromUseCase,\n UpdateEntryUseCase,\n PublishEntryUseCase,\n UnpublishEntryUseCase,\n DeleteEntryUseCase,\n UpdateRevisionDescriptionUseCase,\n [ContentEntryFormModelModifier, { multiple: true }]\n ]\n});\n"],"names":["PUBLISH_ENTRY_DIALOG","ContentEntryFormPresenterImpl","formModelFactory","cmsFormModelBuilder","confirmation","modelAccessor","getEntryUseCase","createEntryUseCase","createRevisionFromUseCase","updateEntryUseCase","publishEntryUseCase","unpublishEntryUseCase","deleteEntryUseCase","updateRevisionDescriptionUseCase","formModelModifiers","makeAutoObservable","computed","entry","meta","isLocked","status","canSave","toJS","options","skipValidation","data","runInAction","createData","error","toFormErrors","result","model","TRASH_ENTRY_DIALOG","id","folderId","initialValues","formConfig","form","modifier","params","ContentEntryFormPresenter","Abstraction","FormModelFactory","CmsFormModelBuilder","Confirmation","CmsModelContext","GetEntryUseCase","CreateEntryUseCase","CreateRevisionFromUseCase","UpdateEntryUseCase","PublishEntryUseCase","UnpublishEntryUseCase","DeleteEntryUseCase","UpdateRevisionDescriptionUseCase","ContentEntryFormModelModifier"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { FormModel } from "@webiny/app-admin/features/formModel/abstractions.js";
2
+ import type { CmsModel } from "../../../types.js";
3
+ /**
4
+ * Converts a rejected save into form errors: per-field validation errors from the API are
5
+ * placed on their fields, anything else is shown as a form-level error.
6
+ */
7
+ export declare const toFormErrors: (error: unknown, model: CmsModel) => FormModel.FormError[];
@@ -0,0 +1,49 @@
1
+ import { CmsEntryError } from "../../../features/contentEntry/CmsEntryError.js";
2
+ const isApiFieldError = (value)=>{
3
+ const item = value;
4
+ return "string" == typeof item?.fieldId && "string" == typeof item.error;
5
+ };
6
+ const isIndex = (value)=>/^\d+$/.test(String(value));
7
+ const toFieldPath = (model, error)=>{
8
+ const parents = error.parents ?? [];
9
+ const path = [];
10
+ let fields = model.fields;
11
+ for(let i = 0; i < parents.length; i++){
12
+ const field = fields.find((f)=>f.fieldId === String(parents[i]));
13
+ if (!field) return [
14
+ ...parents,
15
+ error.fieldId
16
+ ].join(".");
17
+ path.push(field.fieldId);
18
+ if (field.list && isIndex(parents[i + 1])) path.push(String(parents[++i]));
19
+ if ("dynamicZone" === field.type) {
20
+ const templates = field.settings?.templates ?? [];
21
+ const template = templates.find((t)=>t.gqlTypeName === String(parents[i + 1]));
22
+ i++;
23
+ fields = template?.fields ?? [];
24
+ } else fields = field.settings?.fields ?? [];
25
+ }
26
+ return [
27
+ ...path,
28
+ error.fieldId
29
+ ].join(".");
30
+ };
31
+ const toFormErrors = (error, model)=>{
32
+ if (error instanceof CmsEntryError && Array.isArray(error.data)) {
33
+ const fieldErrors = error.data.filter(isApiFieldError);
34
+ if (fieldErrors.length > 0) return fieldErrors.map((item)=>({
35
+ path: toFieldPath(model, item),
36
+ message: item.error
37
+ }));
38
+ }
39
+ const message = error instanceof Error ? error.message : "Could not save the entry.";
40
+ return [
41
+ {
42
+ path: "",
43
+ message
44
+ }
45
+ ];
46
+ };
47
+ export { toFormErrors };
48
+
49
+ //# sourceMappingURL=toFormErrors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presentation/contentEntries/form/toFormErrors.js","sources":["../../../../src/presentation/contentEntries/form/toFormErrors.ts"],"sourcesContent":["import type { FormModel } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { CmsDynamicZoneTemplate, CmsModel, CmsModelField } from \"~/types.js\";\nimport { CmsEntryError } from \"~/features/contentEntry/CmsEntryError.js\";\n\ninterface ApiFieldError {\n fieldId: string;\n error: string;\n parents?: (string | number)[];\n}\n\nconst isApiFieldError = (value: unknown): value is ApiFieldError => {\n const item = value as ApiFieldError;\n return typeof item?.fieldId === \"string\" && typeof item.error === \"string\";\n};\n\nconst isIndex = (value: unknown): boolean => /^\\d+$/.test(String(value));\n\n/**\n * The API lists the parents of a nested field as `fieldId`, then the item index for list\n * fields, then the template's `gqlTypeName` for dynamic zones. Form paths have no template\n * segment, so walk the model to drop it: `blocks.0.PageHero.title` → `blocks.0.title`.\n */\nconst toFieldPath = (model: CmsModel, error: ApiFieldError): string => {\n const parents = error.parents ?? [];\n const path: string[] = [];\n let fields: CmsModelField[] = model.fields;\n\n for (let i = 0; i < parents.length; i++) {\n const field = fields.find(f => f.fieldId === String(parents[i]));\n if (!field) {\n return [...parents, error.fieldId].join(\".\");\n }\n path.push(field.fieldId);\n\n if (field.list && isIndex(parents[i + 1])) {\n path.push(String(parents[++i]));\n }\n\n if (field.type === \"dynamicZone\") {\n const templates = (field.settings?.templates ?? []) as CmsDynamicZoneTemplate[];\n const template = templates.find(t => t.gqlTypeName === String(parents[i + 1]));\n i++;\n fields = template?.fields ?? [];\n } else {\n fields = (field.settings?.fields ?? []) as CmsModelField[];\n }\n }\n\n return [...path, error.fieldId].join(\".\");\n};\n\n/**\n * Converts a rejected save into form errors: per-field validation errors from the API are\n * placed on their fields, anything else is shown as a form-level error.\n */\nexport const toFormErrors = (error: unknown, model: CmsModel): FormModel.FormError[] => {\n if (error instanceof CmsEntryError && Array.isArray(error.data)) {\n const fieldErrors = error.data.filter(isApiFieldError);\n if (fieldErrors.length > 0) {\n return fieldErrors.map(item => ({\n path: toFieldPath(model, item),\n message: item.error\n }));\n }\n }\n\n const message = error instanceof Error ? error.message : \"Could not save the entry.\";\n return [{ path: \"\", message }];\n};\n"],"names":["isApiFieldError","value","item","isIndex","String","toFieldPath","model","error","parents","path","fields","i","field","f","templates","template","t","toFormErrors","CmsEntryError","Array","fieldErrors","message","Error"],"mappings":";AAUA,MAAMA,kBAAkB,CAACC;IACrB,MAAMC,OAAOD;IACb,OAAO,AAAyB,YAAzB,OAAOC,MAAM,WAAwB,AAAsB,YAAtB,OAAOA,KAAK,KAAK;AACjE;AAEA,MAAMC,UAAU,CAACF,QAA4B,QAAQ,IAAI,CAACG,OAAOH;AAOjE,MAAMI,cAAc,CAACC,OAAiBC;IAClC,MAAMC,UAAUD,MAAM,OAAO,IAAI,EAAE;IACnC,MAAME,OAAiB,EAAE;IACzB,IAAIC,SAA0BJ,MAAM,MAAM;IAE1C,IAAK,IAAIK,IAAI,GAAGA,IAAIH,QAAQ,MAAM,EAAEG,IAAK;QACrC,MAAMC,QAAQF,OAAO,IAAI,CAACG,CAAAA,IAAKA,EAAE,OAAO,KAAKT,OAAOI,OAAO,CAACG,EAAE;QAC9D,IAAI,CAACC,OACD,OAAO;eAAIJ;YAASD,MAAM,OAAO;SAAC,CAAC,IAAI,CAAC;QAE5CE,KAAK,IAAI,CAACG,MAAM,OAAO;QAEvB,IAAIA,MAAM,IAAI,IAAIT,QAAQK,OAAO,CAACG,IAAI,EAAE,GACpCF,KAAK,IAAI,CAACL,OAAOI,OAAO,CAAC,EAAEG,EAAE;QAGjC,IAAIC,AAAe,kBAAfA,MAAM,IAAI,EAAoB;YAC9B,MAAME,YAAaF,MAAM,QAAQ,EAAE,aAAa,EAAE;YAClD,MAAMG,WAAWD,UAAU,IAAI,CAACE,CAAAA,IAAKA,EAAE,WAAW,KAAKZ,OAAOI,OAAO,CAACG,IAAI,EAAE;YAC5EA;YACAD,SAASK,UAAU,UAAU,EAAE;QACnC,OACIL,SAAUE,MAAM,QAAQ,EAAE,UAAU,EAAE;IAE9C;IAEA,OAAO;WAAIH;QAAMF,MAAM,OAAO;KAAC,CAAC,IAAI,CAAC;AACzC;AAMO,MAAMU,eAAe,CAACV,OAAgBD;IACzC,IAAIC,iBAAiBW,iBAAiBC,MAAM,OAAO,CAACZ,MAAM,IAAI,GAAG;QAC7D,MAAMa,cAAcb,MAAM,IAAI,CAAC,MAAM,CAACP;QACtC,IAAIoB,YAAY,MAAM,GAAG,GACrB,OAAOA,YAAY,GAAG,CAAClB,CAAAA,OAAS;gBAC5B,MAAMG,YAAYC,OAAOJ;gBACzB,SAASA,KAAK,KAAK;YACvB;IAER;IAEA,MAAMmB,UAAUd,iBAAiBe,QAAQf,MAAM,OAAO,GAAG;IACzD,OAAO;QAAC;YAAE,MAAM;YAAIc;QAAQ;KAAE;AAClC"}