@webiny/app-headless-cms 6.5.0-beta.3 → 6.5.0-beta.5

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 (154) hide show
  1. package/ContentEntriesModule.js +9 -2
  2. package/ContentEntriesModule.js.map +1 -1
  3. package/admin/components/ContentEntries/BulkActions/ActionDelete.d.ts +2 -2
  4. package/admin/components/ContentEntries/BulkActions/ActionDelete.js.map +1 -1
  5. package/admin/components/ContentEntries/BulkActions/ActionMove.d.ts +2 -2
  6. package/admin/components/ContentEntries/BulkActions/ActionMove.js.map +1 -1
  7. package/admin/components/ContentEntries/BulkActions/ActionPublish.d.ts +2 -2
  8. package/admin/components/ContentEntries/BulkActions/ActionPublish.js.map +1 -1
  9. package/admin/components/ContentEntries/BulkActions/ActionUnpublish.d.ts +2 -2
  10. package/admin/components/ContentEntries/BulkActions/ActionUnpublish.js.map +1 -1
  11. package/admin/components/ContentEntries/Table/Actions/DuplicateEntry.d.ts +2 -0
  12. package/admin/components/ContentEntries/Table/Actions/DuplicateEntry.js +34 -0
  13. package/admin/components/ContentEntries/Table/Actions/DuplicateEntry.js.map +1 -0
  14. package/admin/components/ContentEntries/Table/Actions/index.d.ts +1 -0
  15. package/admin/components/ContentEntries/Table/Actions/index.js +1 -0
  16. package/admin/components/ContentModelEditor/ContentModelEditor.js +1 -1
  17. package/admin/components/ContentModelEditor/ContentModelEditor.js.map +1 -1
  18. package/admin/components/Dialogs/DuplicateEntryConfirmDialog.d.ts +2 -0
  19. package/admin/components/Dialogs/DuplicateEntryConfirmDialog.js +22 -0
  20. package/admin/components/Dialogs/DuplicateEntryConfirmDialog.js.map +1 -0
  21. package/admin/components/FieldEditor/EditFieldDialog.d.ts +2 -2
  22. package/admin/components/FieldEditor/Field.js +4 -4
  23. package/admin/components/FieldEditor/Field.js.map +1 -1
  24. package/features/contentEntry/CmsEntryError.d.ts +10 -0
  25. package/features/contentEntry/CmsEntryError.js +11 -0
  26. package/features/contentEntry/CmsEntryError.js.map +1 -0
  27. package/features/contentEntry/createEntry/CreateEntryGateway.d.ts +0 -1
  28. package/features/contentEntry/createEntry/CreateEntryGateway.js +3 -10
  29. package/features/contentEntry/createEntry/CreateEntryGateway.js.map +1 -1
  30. package/features/contentEntry/createRevisionFrom/CreateRevisionFromGateway.d.ts +3 -1
  31. package/features/contentEntry/createRevisionFrom/CreateRevisionFromGateway.js +8 -3
  32. package/features/contentEntry/createRevisionFrom/CreateRevisionFromGateway.js.map +1 -1
  33. package/features/contentEntry/duplicateEntry/DuplicateEntryGateway.d.ts +14 -0
  34. package/features/contentEntry/duplicateEntry/DuplicateEntryGateway.js +43 -0
  35. package/features/contentEntry/duplicateEntry/DuplicateEntryGateway.js.map +1 -0
  36. package/features/contentEntry/duplicateEntry/DuplicateEntryRepository.d.ts +15 -0
  37. package/features/contentEntry/duplicateEntry/DuplicateEntryRepository.js +34 -0
  38. package/features/contentEntry/duplicateEntry/DuplicateEntryRepository.js.map +1 -0
  39. package/features/contentEntry/duplicateEntry/DuplicateEntryUseCase.d.ts +11 -0
  40. package/features/contentEntry/duplicateEntry/DuplicateEntryUseCase.js +18 -0
  41. package/features/contentEntry/duplicateEntry/DuplicateEntryUseCase.js.map +1 -0
  42. package/features/contentEntry/duplicateEntry/abstractions.d.ts +26 -0
  43. package/features/contentEntry/duplicateEntry/abstractions.js +7 -0
  44. package/features/contentEntry/duplicateEntry/abstractions.js.map +1 -0
  45. package/features/contentEntry/duplicateEntry/feature.d.ts +3 -0
  46. package/features/contentEntry/duplicateEntry/feature.js +21 -0
  47. package/features/contentEntry/duplicateEntry/feature.js.map +1 -0
  48. package/features/contentEntry/entryValueGateways.test.d.ts +1 -0
  49. package/features/contentEntry/entryValueGateways.test.js +106 -0
  50. package/features/contentEntry/entryValueGateways.test.js.map +1 -0
  51. package/features/contentEntry/feature.js +2 -0
  52. package/features/contentEntry/feature.js.map +1 -1
  53. package/features/contentEntry/singletonEntry/UpdateSingletonEntryGateway.d.ts +0 -1
  54. package/features/contentEntry/singletonEntry/UpdateSingletonEntryGateway.js +3 -10
  55. package/features/contentEntry/singletonEntry/UpdateSingletonEntryGateway.js.map +1 -1
  56. package/features/contentEntry/updateEntry/UpdateEntryGateway.d.ts +0 -1
  57. package/features/contentEntry/updateEntry/UpdateEntryGateway.js +3 -10
  58. package/features/contentEntry/updateEntry/UpdateEntryGateway.js.map +1 -1
  59. package/features/contentEntry/valueTransformers/EntryDataPreparer.d.ts +9 -0
  60. package/features/contentEntry/valueTransformers/EntryDataPreparer.js +8 -0
  61. package/features/contentEntry/valueTransformers/EntryDataPreparer.js.map +1 -1
  62. package/features/contentEntry/valueTransformers/EntryDataPreparer.test.d.ts +1 -0
  63. package/features/contentEntry/valueTransformers/EntryDataPreparer.test.js +113 -0
  64. package/features/contentEntry/valueTransformers/EntryDataPreparer.test.js.map +1 -0
  65. package/features/contentEntry/valueTransformers/entryValueFixtures.d.ts +146 -0
  66. package/features/contentEntry/valueTransformers/entryValueFixtures.js +171 -0
  67. package/features/contentEntry/valueTransformers/entryValueFixtures.js.map +1 -0
  68. package/features/formModel/CmsFormModelBuilder.d.ts +8 -0
  69. package/features/formModel/CmsFormModelBuilder.js +25 -2
  70. package/features/formModel/CmsFormModelBuilder.js.map +1 -1
  71. package/features/formModel/CmsFormModelBuilder.test.d.ts +1 -0
  72. package/features/formModel/CmsFormModelBuilder.test.js +111 -0
  73. package/features/formModel/CmsFormModelBuilder.test.js.map +1 -0
  74. package/features/formModel/mappers/DynamicZoneFieldMapper.js +1 -0
  75. package/features/formModel/mappers/DynamicZoneFieldMapper.js.map +1 -1
  76. package/features/model/exportModels/ExportModelsGateway.d.ts +1 -1
  77. package/features/model/exportModels/ExportModelsGateway.js.map +1 -1
  78. package/features/model/exportModels/ExportModelsUseCase.js +7 -1
  79. package/features/model/exportModels/ExportModelsUseCase.js.map +1 -1
  80. package/features/model/exportModels/abstractions.d.ts +8 -2
  81. package/features/model/exportModels/abstractions.js.map +1 -1
  82. package/package.json +26 -26
  83. package/presentation/contentEntries/form/ContentEntryFormPresenter.js +3 -1
  84. package/presentation/contentEntries/form/ContentEntryFormPresenter.js.map +1 -1
  85. package/presentation/contentEntries/form/toFormErrors.d.ts +7 -0
  86. package/presentation/contentEntries/form/toFormErrors.js +49 -0
  87. package/presentation/contentEntries/form/toFormErrors.js.map +1 -0
  88. package/presentation/contentEntries/form/toFormErrors.test.d.ts +1 -0
  89. package/presentation/contentEntries/form/toFormErrors.test.js +175 -0
  90. package/presentation/contentEntries/form/toFormErrors.test.js.map +1 -0
  91. package/presentation/contentEntries/list/ContentEntriesPresenter.d.ts +6 -2
  92. package/presentation/contentEntries/list/ContentEntriesPresenter.js +27 -2
  93. package/presentation/contentEntries/list/ContentEntriesPresenter.js.map +1 -1
  94. package/presentation/contentEntries/list/ContentEntriesPresenter.test.js +58 -1
  95. package/presentation/contentEntries/list/ContentEntriesPresenter.test.js.map +1 -1
  96. package/presentation/contentEntries/list/abstractions.d.ts +12 -0
  97. package/presentation/contentEntries/list/abstractions.js.map +1 -1
  98. package/presentation/contentEntries/singleEntry/SingleEntryPresenter.js +3 -1
  99. package/presentation/contentEntries/singleEntry/SingleEntryPresenter.js.map +1 -1
  100. package/presentation/contentEntries/trashBin/CmsTrashBin.d.ts +2 -2
  101. package/presentation/contentEntries/views/CmsAdvancedSearch.d.ts +2 -2
  102. package/presentation/contentEntries/views/ContentEntriesListLayout.d.ts +2 -2
  103. package/presentation/contentEntries/views/ContentEntryFormView.d.ts +2 -2
  104. package/presentation/contentEntries/views/OlderRevisionAlert.d.ts +2 -2
  105. package/presentation/contentEntries/views/RevisionDrawer.d.ts +2 -2
  106. package/presentation/contentEntries/views/RevisionsList.d.ts +2 -2
  107. package/presentation/contentEntries/views/SingleEntryLayout.d.ts +2 -2
  108. package/presentation/contentEntries/views/Table/Table.d.ts +2 -2
  109. package/presentation/contentEntries/views/actions/CreateNewRevisionButton.d.ts +2 -2
  110. package/presentation/contentEntries/views/actions/DeleteEntryMenuItem.d.ts +2 -2
  111. package/presentation/contentEntries/views/actions/PublishOnlyButton.d.ts +2 -2
  112. package/presentation/contentEntries/views/actions/RevisionSelector.d.ts +2 -2
  113. package/presentation/contentEntries/views/actions/SaveAndPublishButton.d.ts +2 -2
  114. package/presentation/contentEntries/views/actions/SaveContentButton.d.ts +2 -2
  115. package/presentation/contentEntries/views/actions/ShowRevisionListMenuItem.d.ts +2 -2
  116. package/presentation/contentEntries/views/layout/ContentEntryForm.d.ts +12 -12
  117. package/presentation/contentModels/ContentModelsPresenter.d.ts +5 -5
  118. package/presentation/contentModels/ContentModelsPresenter.js +1 -1
  119. package/presentation/contentModels/ContentModelsPresenter.js.map +1 -1
  120. package/presentation/contentModels/abstractions.d.ts +7 -2
  121. package/presentation/contentModels/abstractions.js.map +1 -1
  122. package/presentation/contentModels/components/CloneContentModelDialog.d.ts +2 -2
  123. package/presentation/contentModels/components/ContentModels.d.ts +2 -2
  124. package/presentation/contentModels/components/ContentModelsDataList.d.ts +2 -2
  125. package/presentation/contentModels/components/ContentModelsDataList.js +6 -5
  126. package/presentation/contentModels/components/ContentModelsDataList.js.map +1 -1
  127. package/presentation/contentModels/components/NewContentModelDialog.d.ts +2 -2
  128. package/presentation/contentModels/components/fullDelete/dialog/Content.js +2 -2
  129. package/presentation/contentModels/components/fullDelete/dialog/Content.js.map +1 -1
  130. package/presentation/contentModels/components/fullDelete/types.js.map +1 -1
  131. package/presentation/fieldRenderers/ref/components/RefFieldOptions.d.ts +2 -2
  132. package/presentation/fieldRenderers/ref/components/RefFieldOptions.js +3 -2
  133. package/presentation/fieldRenderers/ref/components/RefFieldOptions.js.map +1 -1
  134. package/presentation/importContentModels/ImportContentModelsDialog.d.ts +2 -2
  135. package/presentation/importContentModels/ImportContentModelsPresenter.js +1 -1
  136. package/presentation/importContentModels/ImportContentModelsPresenter.js.map +1 -1
  137. package/presentation/importContentModels/components/DataList.d.ts +2 -2
  138. package/presentation/importContentModels/components/FileUpload.d.ts +2 -2
  139. package/presentation/importContentModels/components/FileUpload.js +1 -1
  140. package/presentation/importContentModels/components/FileUpload.js.map +1 -1
  141. package/presentation/importContentModels/components/Model/DataListModelItem.js +3 -3
  142. package/presentation/importContentModels/components/Model/DataListModelItem.js.map +1 -1
  143. package/presentation/importContentModels/components/Model/DataListModelItemInfo.js +2 -2
  144. package/presentation/importContentModels/components/Model/DataListModelItemInfo.js.map +1 -1
  145. package/presentation/importContentModels/components/Model/DataListModels.d.ts +2 -2
  146. package/presentation/menus/ContentGroupsMenuItems.d.ts +2 -2
  147. package/presentation/modelGroup/ModelGroupPresenter.js +1 -2
  148. package/presentation/modelGroup/ModelGroupPresenter.js.map +1 -1
  149. package/presentation/modelGroup/components/ContentModelGroups.d.ts +2 -2
  150. package/presentation/modelGroup/components/ContentModelGroupsDataList.d.ts +6 -2
  151. package/presentation/modelGroup/components/ContentModelGroupsForm.d.ts +8 -2
  152. package/utils/normalizeIcon.d.ts +4 -0
  153. package/utils/normalizeIcon.js +9 -6
  154. package/utils/normalizeIcon.js.map +1 -1
@@ -0,0 +1,175 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { toFormErrors } from "./toFormErrors.js";
3
+ import { CmsEntryError } from "../../../features/contentEntry/CmsEntryError.js";
4
+ const field = (fieldId, type, extra = {})=>({
5
+ id: fieldId,
6
+ fieldId,
7
+ type,
8
+ label: fieldId,
9
+ ...extra
10
+ });
11
+ const model = {
12
+ modelId: "page",
13
+ fields: [
14
+ field("sku", "text"),
15
+ field("variants", "object", {
16
+ list: true,
17
+ settings: {
18
+ fields: [
19
+ field("code", "text")
20
+ ]
21
+ }
22
+ }),
23
+ field("blocks", "dynamicZone", {
24
+ list: true,
25
+ settings: {
26
+ templates: [
27
+ {
28
+ id: "hero",
29
+ gqlTypeName: "PageHero",
30
+ fields: [
31
+ field("title", "text"),
32
+ field("links", "object", {
33
+ list: true,
34
+ settings: {
35
+ fields: [
36
+ field("url", "text")
37
+ ]
38
+ }
39
+ })
40
+ ]
41
+ }
42
+ ]
43
+ }
44
+ }),
45
+ field("single", "dynamicZone", {
46
+ settings: {
47
+ templates: [
48
+ {
49
+ id: "plain",
50
+ gqlTypeName: "PagePlain",
51
+ fields: [
52
+ field("ctrl", "text")
53
+ ]
54
+ }
55
+ ]
56
+ }
57
+ })
58
+ ]
59
+ };
60
+ const validationError = (data)=>new CmsEntryError({
61
+ message: "Validation failed.",
62
+ code: "Cms/Entry/ValidationError",
63
+ data: data
64
+ }, "Could not update entry");
65
+ describe("toFormErrors", ()=>{
66
+ it("maps a top-level field error to its field", ()=>{
67
+ const error = validationError([
68
+ {
69
+ id: "sku",
70
+ fieldId: "sku",
71
+ error: "Value must be unique.",
72
+ parents: []
73
+ }
74
+ ]);
75
+ expect(toFormErrors(error, model)).toEqual([
76
+ {
77
+ path: "sku",
78
+ message: "Value must be unique."
79
+ }
80
+ ]);
81
+ });
82
+ it("keeps list item indexes in the path", ()=>{
83
+ const error = validationError([
84
+ {
85
+ fieldId: "code",
86
+ error: "Required.",
87
+ parents: [
88
+ "variants",
89
+ "1"
90
+ ]
91
+ }
92
+ ]);
93
+ expect(toFormErrors(error, model)).toEqual([
94
+ {
95
+ path: "variants.1.code",
96
+ message: "Required."
97
+ }
98
+ ]);
99
+ });
100
+ it("drops dynamic zone template names from the path", ()=>{
101
+ const error = validationError([
102
+ {
103
+ fieldId: "title",
104
+ error: "Required.",
105
+ parents: [
106
+ "blocks",
107
+ "0",
108
+ "PageHero"
109
+ ]
110
+ },
111
+ {
112
+ fieldId: "url",
113
+ error: "Invalid URL.",
114
+ parents: [
115
+ "blocks",
116
+ "2",
117
+ "PageHero",
118
+ "links",
119
+ "1"
120
+ ]
121
+ },
122
+ {
123
+ fieldId: "ctrl",
124
+ error: "Required.",
125
+ parents: [
126
+ "single",
127
+ "PagePlain"
128
+ ]
129
+ }
130
+ ]);
131
+ expect(toFormErrors(error, model)).toEqual([
132
+ {
133
+ path: "blocks.0.title",
134
+ message: "Required."
135
+ },
136
+ {
137
+ path: "blocks.2.links.1.url",
138
+ message: "Invalid URL."
139
+ },
140
+ {
141
+ path: "single.ctrl",
142
+ message: "Required."
143
+ }
144
+ ]);
145
+ });
146
+ it("falls back to the error message when there are no field errors", ()=>{
147
+ const error = new CmsEntryError({
148
+ message: "Entry is locked.",
149
+ code: "Cms/Entry/Locked",
150
+ data: null
151
+ }, "Could not update entry");
152
+ expect(toFormErrors(error, model)).toEqual([
153
+ {
154
+ path: "",
155
+ message: "Entry is locked."
156
+ }
157
+ ]);
158
+ });
159
+ it("uses the message of any other error", ()=>{
160
+ expect(toFormErrors(new Error("Network error: Failed to fetch"), model)).toEqual([
161
+ {
162
+ path: "",
163
+ message: "Network error: Failed to fetch"
164
+ }
165
+ ]);
166
+ expect(toFormErrors("boom", model)).toEqual([
167
+ {
168
+ path: "",
169
+ message: "Could not save the entry."
170
+ }
171
+ ]);
172
+ });
173
+ });
174
+
175
+ //# sourceMappingURL=toFormErrors.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presentation/contentEntries/form/toFormErrors.test.js","sources":["../../../../src/presentation/contentEntries/form/toFormErrors.test.ts"],"sourcesContent":["import { describe, it, expect } from \"vitest\";\nimport { toFormErrors } from \"./toFormErrors.js\";\nimport { CmsEntryError } from \"~/features/contentEntry/CmsEntryError.js\";\n\nconst field = (fieldId: string, type: string, extra: Record<string, unknown> = {}) => ({\n id: fieldId,\n fieldId,\n type,\n label: fieldId,\n ...extra\n});\n\nconst model = {\n modelId: \"page\",\n fields: [\n field(\"sku\", \"text\"),\n field(\"variants\", \"object\", {\n list: true,\n settings: { fields: [field(\"code\", \"text\")] }\n }),\n field(\"blocks\", \"dynamicZone\", {\n list: true,\n settings: {\n templates: [\n {\n id: \"hero\",\n gqlTypeName: \"PageHero\",\n fields: [\n field(\"title\", \"text\"),\n field(\"links\", \"object\", {\n list: true,\n settings: { fields: [field(\"url\", \"text\")] }\n })\n ]\n }\n ]\n }\n }),\n field(\"single\", \"dynamicZone\", {\n settings: {\n templates: [\n { id: \"plain\", gqlTypeName: \"PagePlain\", fields: [field(\"ctrl\", \"text\")] }\n ]\n }\n })\n ]\n} as any;\n\nconst validationError = (data: unknown[]) =>\n new CmsEntryError(\n { message: \"Validation failed.\", code: \"Cms/Entry/ValidationError\", data: data as any },\n \"Could not update entry\"\n );\n\ndescribe(\"toFormErrors\", () => {\n it(\"maps a top-level field error to its field\", () => {\n const error = validationError([\n { id: \"sku\", fieldId: \"sku\", error: \"Value must be unique.\", parents: [] }\n ]);\n\n expect(toFormErrors(error, model)).toEqual([\n { path: \"sku\", message: \"Value must be unique.\" }\n ]);\n });\n\n it(\"keeps list item indexes in the path\", () => {\n const error = validationError([\n { fieldId: \"code\", error: \"Required.\", parents: [\"variants\", \"1\"] }\n ]);\n\n expect(toFormErrors(error, model)).toEqual([\n { path: \"variants.1.code\", message: \"Required.\" }\n ]);\n });\n\n it(\"drops dynamic zone template names from the path\", () => {\n const error = validationError([\n { fieldId: \"title\", error: \"Required.\", parents: [\"blocks\", \"0\", \"PageHero\"] },\n {\n fieldId: \"url\",\n error: \"Invalid URL.\",\n parents: [\"blocks\", \"2\", \"PageHero\", \"links\", \"1\"]\n },\n { fieldId: \"ctrl\", error: \"Required.\", parents: [\"single\", \"PagePlain\"] }\n ]);\n\n expect(toFormErrors(error, model)).toEqual([\n { path: \"blocks.0.title\", message: \"Required.\" },\n { path: \"blocks.2.links.1.url\", message: \"Invalid URL.\" },\n { path: \"single.ctrl\", message: \"Required.\" }\n ]);\n });\n\n it(\"falls back to the error message when there are no field errors\", () => {\n const error = new CmsEntryError(\n { message: \"Entry is locked.\", code: \"Cms/Entry/Locked\", data: null },\n \"Could not update entry\"\n );\n\n expect(toFormErrors(error, model)).toEqual([{ path: \"\", message: \"Entry is locked.\" }]);\n });\n\n it(\"uses the message of any other error\", () => {\n expect(toFormErrors(new Error(\"Network error: Failed to fetch\"), model)).toEqual([\n { path: \"\", message: \"Network error: Failed to fetch\" }\n ]);\n expect(toFormErrors(\"boom\", model)).toEqual([\n { path: \"\", message: \"Could not save the entry.\" }\n ]);\n });\n});\n"],"names":["field","fieldId","type","extra","model","validationError","data","CmsEntryError","describe","it","error","expect","toFormErrors","Error"],"mappings":";;;AAIA,MAAMA,QAAQ,CAACC,SAAiBC,MAAcC,QAAiC,CAAC,CAAC,GAAM;QACnF,IAAIF;QACJA;QACAC;QACA,OAAOD;QACP,GAAGE,KAAK;IACZ;AAEA,MAAMC,QAAQ;IACV,SAAS;IACT,QAAQ;QACJJ,MAAM,OAAO;QACbA,MAAM,YAAY,UAAU;YACxB,MAAM;YACN,UAAU;gBAAE,QAAQ;oBAACA,MAAM,QAAQ;iBAAQ;YAAC;QAChD;QACAA,MAAM,UAAU,eAAe;YAC3B,MAAM;YACN,UAAU;gBACN,WAAW;oBACP;wBACI,IAAI;wBACJ,aAAa;wBACb,QAAQ;4BACJA,MAAM,SAAS;4BACfA,MAAM,SAAS,UAAU;gCACrB,MAAM;gCACN,UAAU;oCAAE,QAAQ;wCAACA,MAAM,OAAO;qCAAQ;gCAAC;4BAC/C;yBACH;oBACL;iBACH;YACL;QACJ;QACAA,MAAM,UAAU,eAAe;YAC3B,UAAU;gBACN,WAAW;oBACP;wBAAE,IAAI;wBAAS,aAAa;wBAAa,QAAQ;4BAACA,MAAM,QAAQ;yBAAQ;oBAAC;iBAC5E;YACL;QACJ;KACH;AACL;AAEA,MAAMK,kBAAkB,CAACC,OACrB,IAAIC,cACA;QAAE,SAAS;QAAsB,MAAM;QAA6B,MAAMD;IAAY,GACtF;AAGRE,SAAS,gBAAgB;IACrBC,GAAG,6CAA6C;QAC5C,MAAMC,QAAQL,gBAAgB;YAC1B;gBAAE,IAAI;gBAAO,SAAS;gBAAO,OAAO;gBAAyB,SAAS,EAAE;YAAC;SAC5E;QAEDM,OAAOC,aAAaF,OAAON,QAAQ,OAAO,CAAC;YACvC;gBAAE,MAAM;gBAAO,SAAS;YAAwB;SACnD;IACL;IAEAK,GAAG,uCAAuC;QACtC,MAAMC,QAAQL,gBAAgB;YAC1B;gBAAE,SAAS;gBAAQ,OAAO;gBAAa,SAAS;oBAAC;oBAAY;iBAAI;YAAC;SACrE;QAEDM,OAAOC,aAAaF,OAAON,QAAQ,OAAO,CAAC;YACvC;gBAAE,MAAM;gBAAmB,SAAS;YAAY;SACnD;IACL;IAEAK,GAAG,mDAAmD;QAClD,MAAMC,QAAQL,gBAAgB;YAC1B;gBAAE,SAAS;gBAAS,OAAO;gBAAa,SAAS;oBAAC;oBAAU;oBAAK;iBAAW;YAAC;YAC7E;gBACI,SAAS;gBACT,OAAO;gBACP,SAAS;oBAAC;oBAAU;oBAAK;oBAAY;oBAAS;iBAAI;YACtD;YACA;gBAAE,SAAS;gBAAQ,OAAO;gBAAa,SAAS;oBAAC;oBAAU;iBAAY;YAAC;SAC3E;QAEDM,OAAOC,aAAaF,OAAON,QAAQ,OAAO,CAAC;YACvC;gBAAE,MAAM;gBAAkB,SAAS;YAAY;YAC/C;gBAAE,MAAM;gBAAwB,SAAS;YAAe;YACxD;gBAAE,MAAM;gBAAe,SAAS;YAAY;SAC/C;IACL;IAEAK,GAAG,kEAAkE;QACjE,MAAMC,QAAQ,IAAIH,cACd;YAAE,SAAS;YAAoB,MAAM;YAAoB,MAAM;QAAK,GACpE;QAGJI,OAAOC,aAAaF,OAAON,QAAQ,OAAO,CAAC;YAAC;gBAAE,MAAM;gBAAI,SAAS;YAAmB;SAAE;IAC1F;IAEAK,GAAG,uCAAuC;QACtCE,OAAOC,aAAa,IAAIC,MAAM,mCAAmCT,QAAQ,OAAO,CAAC;YAC7E;gBAAE,MAAM;gBAAI,SAAS;YAAiC;SACzD;QACDO,OAAOC,aAAa,QAAQR,QAAQ,OAAO,CAAC;YACxC;gBAAE,MAAM;gBAAI,SAAS;YAA4B;SACpD;IACL;AACJ"}
@@ -8,12 +8,14 @@ import { DeleteEntryUseCase } from "../../../features/contentEntry/deleteEntry/a
8
8
  import { PublishEntryUseCase } from "../../../features/contentEntry/publishEntry/abstractions.js";
9
9
  import { UnpublishEntryUseCase } from "../../../features/contentEntry/unpublishEntry/abstractions.js";
10
10
  import { MoveEntryUseCase } from "../../../features/contentEntry/moveEntry/abstractions.js";
11
+ import { DuplicateEntryUseCase } from "../../../features/contentEntry/duplicateEntry/abstractions.js";
11
12
  import { UpdateRevisionDescriptionUseCase } from "../../../features/contentEntry/updateRevisionDescription/abstractions.js";
12
13
  import { ContentEntriesCacheProvider, CmsModelContext } from "../../../features/contentEntry/abstractions.js";
13
- import { ContentEntriesPresenter as Abstraction, type IContentEntriesViewModel } from "./abstractions.js";
14
+ import { ContentEntriesPresenter as Abstraction, type IContentEntriesViewModel, type IDuplicateEntryResult } from "./abstractions.js";
14
15
  export declare const TRASH_ENTRY_DIALOG = "trash-entry";
15
16
  export declare const PUBLISH_ENTRY_DIALOG = "publish-entry";
16
17
  export declare const UNPUBLISH_ENTRY_DIALOG = "unpublish-entry";
18
+ export declare const DUPLICATE_ENTRY_DIALOG = "duplicate-entry";
17
19
  declare class ContentEntriesPresenterImpl implements Abstraction.Interface {
18
20
  private listPresenter;
19
21
  private foldersPresenter;
@@ -24,12 +26,13 @@ declare class ContentEntriesPresenterImpl implements Abstraction.Interface {
24
26
  private publishEntryUseCase;
25
27
  private unpublishEntryUseCase;
26
28
  private moveEntryUseCase;
29
+ private duplicateEntryUseCase;
27
30
  private updateRevisionDescriptionUseCase;
28
31
  private cacheProvider;
29
32
  private getDescendantFoldersUseCase;
30
33
  private selectedEntryId;
31
34
  private disposeReaction;
32
- constructor(listPresenter: ListPresenter.Interface<CmsContentEntry>, foldersPresenter: FolderTreePresenter.Interface, confirmation: Confirmation.Interface, modelAccessor: CmsModelContext.Interface, listEntriesUseCase: ListEntriesUseCase.Interface, deleteEntryUseCase: DeleteEntryUseCase.Interface, publishEntryUseCase: PublishEntryUseCase.Interface, unpublishEntryUseCase: UnpublishEntryUseCase.Interface, moveEntryUseCase: MoveEntryUseCase.Interface, updateRevisionDescriptionUseCase: UpdateRevisionDescriptionUseCase.Interface, cacheProvider: ContentEntriesCacheProvider.Interface, getDescendantFoldersUseCase: GetDescendantFoldersUseCase.Interface);
35
+ constructor(listPresenter: ListPresenter.Interface<CmsContentEntry>, foldersPresenter: FolderTreePresenter.Interface, confirmation: Confirmation.Interface, modelAccessor: CmsModelContext.Interface, listEntriesUseCase: ListEntriesUseCase.Interface, deleteEntryUseCase: DeleteEntryUseCase.Interface, publishEntryUseCase: PublishEntryUseCase.Interface, unpublishEntryUseCase: UnpublishEntryUseCase.Interface, moveEntryUseCase: MoveEntryUseCase.Interface, duplicateEntryUseCase: DuplicateEntryUseCase.Interface, updateRevisionDescriptionUseCase: UpdateRevisionDescriptionUseCase.Interface, cacheProvider: ContentEntriesCacheProvider.Interface, getDescendantFoldersUseCase: GetDescendantFoldersUseCase.Interface);
33
36
  private get model();
34
37
  get vm(): IContentEntriesViewModel;
35
38
  init(config?: Abstraction.InitConfig): void;
@@ -42,6 +45,7 @@ declare class ContentEntriesPresenterImpl implements Abstraction.Interface {
42
45
  publishEntry(id: string): Promise<boolean>;
43
46
  unpublishEntry(id: string): Promise<boolean>;
44
47
  moveEntry(id: string, folderId: string): Promise<boolean>;
48
+ duplicateEntry(entry: CmsContentEntry): Promise<IDuplicateEntryResult | false>;
45
49
  dispose(): void;
46
50
  }
47
51
  export declare const ContentEntriesPresenter: typeof ContentEntriesPresenterImpl & {
@@ -9,6 +9,7 @@ import { DeleteEntryUseCase } from "../../../features/contentEntry/deleteEntry/a
9
9
  import { PublishEntryUseCase } from "../../../features/contentEntry/publishEntry/abstractions.js";
10
10
  import { UnpublishEntryUseCase } from "../../../features/contentEntry/unpublishEntry/abstractions.js";
11
11
  import { MoveEntryUseCase } from "../../../features/contentEntry/moveEntry/abstractions.js";
12
+ import { DuplicateEntryUseCase } from "../../../features/contentEntry/duplicateEntry/abstractions.js";
12
13
  import { UpdateRevisionDescriptionUseCase } from "../../../features/contentEntry/updateRevisionDescription/abstractions.js";
13
14
  import { CmsModelContext, ContentEntriesCacheProvider } from "../../../features/contentEntry/abstractions.js";
14
15
  import { ContentEntriesPresenter } from "./abstractions.js";
@@ -16,8 +17,9 @@ import { ContentEntriesDataSource } from "./ContentEntriesDataSource.js";
16
17
  const TRASH_ENTRY_DIALOG = "trash-entry";
17
18
  const PUBLISH_ENTRY_DIALOG = "publish-entry";
18
19
  const UNPUBLISH_ENTRY_DIALOG = "unpublish-entry";
20
+ const DUPLICATE_ENTRY_DIALOG = "duplicate-entry";
19
21
  class ContentEntriesPresenterImpl {
20
- constructor(listPresenter, foldersPresenter, confirmation, modelAccessor, listEntriesUseCase, deleteEntryUseCase, publishEntryUseCase, unpublishEntryUseCase, moveEntryUseCase, updateRevisionDescriptionUseCase, cacheProvider, getDescendantFoldersUseCase){
22
+ constructor(listPresenter, foldersPresenter, confirmation, modelAccessor, listEntriesUseCase, deleteEntryUseCase, publishEntryUseCase, unpublishEntryUseCase, moveEntryUseCase, duplicateEntryUseCase, updateRevisionDescriptionUseCase, cacheProvider, getDescendantFoldersUseCase){
21
23
  this.listPresenter = listPresenter;
22
24
  this.foldersPresenter = foldersPresenter;
23
25
  this.confirmation = confirmation;
@@ -27,6 +29,7 @@ class ContentEntriesPresenterImpl {
27
29
  this.publishEntryUseCase = publishEntryUseCase;
28
30
  this.unpublishEntryUseCase = unpublishEntryUseCase;
29
31
  this.moveEntryUseCase = moveEntryUseCase;
32
+ this.duplicateEntryUseCase = duplicateEntryUseCase;
30
33
  this.updateRevisionDescriptionUseCase = updateRevisionDescriptionUseCase;
31
34
  this.cacheProvider = cacheProvider;
32
35
  this.getDescendantFoldersUseCase = getDescendantFoldersUseCase;
@@ -41,6 +44,7 @@ class ContentEntriesPresenterImpl {
41
44
  publishEntryUseCase: false,
42
45
  unpublishEntryUseCase: false,
43
46
  moveEntryUseCase: false,
47
+ duplicateEntryUseCase: false,
44
48
  updateRevisionDescriptionUseCase: false,
45
49
  cacheProvider: false,
46
50
  getDescendantFoldersUseCase: false,
@@ -153,6 +157,26 @@ class ContentEntriesPresenterImpl {
153
157
  });
154
158
  return true;
155
159
  }
160
+ async duplicateEntry(entry) {
161
+ const model = this.model;
162
+ return this.confirmation.confirm(DUPLICATE_ENTRY_DIALOG, {
163
+ entry
164
+ }, async ()=>{
165
+ try {
166
+ const duplicate = await this.duplicateEntryUseCase.execute({
167
+ model,
168
+ revisionId: entry.id
169
+ });
170
+ return {
171
+ entry: duplicate
172
+ };
173
+ } catch (error) {
174
+ return {
175
+ error: error
176
+ };
177
+ }
178
+ });
179
+ }
156
180
  dispose() {
157
181
  if (this.disposeReaction) {
158
182
  this.disposeReaction();
@@ -172,11 +196,12 @@ const ContentEntriesPresenter_ContentEntriesPresenter = ContentEntriesPresenter.
172
196
  PublishEntryUseCase,
173
197
  UnpublishEntryUseCase,
174
198
  MoveEntryUseCase,
199
+ DuplicateEntryUseCase,
175
200
  UpdateRevisionDescriptionUseCase,
176
201
  ContentEntriesCacheProvider,
177
202
  GetDescendantFoldersUseCase
178
203
  ]
179
204
  });
180
- export { ContentEntriesPresenter_ContentEntriesPresenter as ContentEntriesPresenter, PUBLISH_ENTRY_DIALOG, TRASH_ENTRY_DIALOG, UNPUBLISH_ENTRY_DIALOG };
205
+ export { ContentEntriesPresenter_ContentEntriesPresenter as ContentEntriesPresenter, DUPLICATE_ENTRY_DIALOG, PUBLISH_ENTRY_DIALOG, TRASH_ENTRY_DIALOG, UNPUBLISH_ENTRY_DIALOG };
181
206
 
182
207
  //# sourceMappingURL=ContentEntriesPresenter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/contentEntries/list/ContentEntriesPresenter.js","sources":["../../../../src/presentation/contentEntries/list/ContentEntriesPresenter.ts"],"sourcesContent":["import { computed, makeAutoObservable, reaction } from \"mobx\";\nimport type { IReactionDisposer } from \"mobx\";\nimport { ListPresenter } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport { FolderTreePresenter } from \"@webiny/app-aco/presentation/folderTree/abstractions.js\";\nimport { GetDescendantFoldersUseCase } from \"@webiny/app-aco/features/folders/getDescendantFolders/abstractions.js\";\nimport { sortFolders } from \"@webiny/app-aco\";\nimport { Confirmation } from \"@webiny/app-admin/features/confirmation/abstractions.js\";\nimport type { CmsContentEntry } from \"~/types.js\";\nimport { ListEntriesUseCase } from \"~/features/contentEntry/listEntries/abstractions.js\";\nimport { DeleteEntryUseCase } from \"~/features/contentEntry/deleteEntry/abstractions.js\";\nimport { PublishEntryUseCase } from \"~/features/contentEntry/publishEntry/abstractions.js\";\nimport { UnpublishEntryUseCase } from \"~/features/contentEntry/unpublishEntry/abstractions.js\";\nimport { MoveEntryUseCase } from \"~/features/contentEntry/moveEntry/abstractions.js\";\nimport { UpdateRevisionDescriptionUseCase } from \"~/features/contentEntry/updateRevisionDescription/abstractions.js\";\nimport {\n ContentEntriesCacheProvider,\n CmsModelContext\n} from \"~/features/contentEntry/abstractions.js\";\nimport {\n ContentEntriesPresenter as Abstraction,\n type IContentEntriesViewModel\n} from \"./abstractions.js\";\nimport { ContentEntriesDataSource } from \"./ContentEntriesDataSource.js\";\n\nexport const TRASH_ENTRY_DIALOG = \"trash-entry\";\nexport const PUBLISH_ENTRY_DIALOG = \"publish-entry\";\nexport const UNPUBLISH_ENTRY_DIALOG = \"unpublish-entry\";\n\nclass ContentEntriesPresenterImpl implements Abstraction.Interface {\n private selectedEntryId: string | null = null;\n private disposeReaction: IReactionDisposer | null = null;\n\n constructor(\n private listPresenter: ListPresenter.Interface<CmsContentEntry>,\n private foldersPresenter: FolderTreePresenter.Interface,\n private confirmation: Confirmation.Interface,\n private modelAccessor: CmsModelContext.Interface,\n private listEntriesUseCase: ListEntriesUseCase.Interface,\n private deleteEntryUseCase: DeleteEntryUseCase.Interface,\n private publishEntryUseCase: PublishEntryUseCase.Interface,\n private unpublishEntryUseCase: UnpublishEntryUseCase.Interface,\n private moveEntryUseCase: MoveEntryUseCase.Interface,\n private updateRevisionDescriptionUseCase: UpdateRevisionDescriptionUseCase.Interface,\n private cacheProvider: ContentEntriesCacheProvider.Interface,\n private getDescendantFoldersUseCase: GetDescendantFoldersUseCase.Interface\n ) {\n makeAutoObservable<\n ContentEntriesPresenterImpl,\n | \"disposeReaction\"\n | \"confirmation\"\n | \"modelAccessor\"\n | \"listEntriesUseCase\"\n | \"deleteEntryUseCase\"\n | \"publishEntryUseCase\"\n | \"unpublishEntryUseCase\"\n | \"moveEntryUseCase\"\n | \"updateRevisionDescriptionUseCase\"\n | \"cacheProvider\"\n | \"getDescendantFoldersUseCase\"\n >(this, {\n disposeReaction: false,\n confirmation: false,\n modelAccessor: false,\n listEntriesUseCase: false,\n deleteEntryUseCase: false,\n publishEntryUseCase: false,\n unpublishEntryUseCase: false,\n moveEntryUseCase: false,\n updateRevisionDescriptionUseCase: false,\n cacheProvider: false,\n getDescendantFoldersUseCase: false,\n vm: computed\n });\n }\n\n private get model() {\n return this.modelAccessor.getModel();\n }\n\n get vm(): IContentEntriesViewModel {\n const appliedQuery = this.listPresenter.vm.appliedQuery;\n const hasSearch = !!appliedQuery?.search;\n const hasFilters = Object.keys(this.listPresenter.vm.filters).some(k => k !== \"folderId\");\n const showFolders = !hasSearch && !hasFilters;\n\n const childFolders = showFolders\n ? sortFolders(this.foldersPresenter.vm.childFolders ?? [], appliedQuery?.sort)\n : [];\n\n return {\n model: this.model,\n selectedEntryId: this.selectedEntryId,\n showingEntry: this.selectedEntryId !== null,\n showFolders,\n childFolders\n };\n }\n\n init(config: Abstraction.InitConfig = {}): void {\n const initialFolderId = config.initialFolderId ?? \"root\";\n\n const cache = this.cacheProvider.get(this.model.modelId);\n const dataSource = new ContentEntriesDataSource(\n this.model,\n this.listEntriesUseCase,\n cache,\n this.getDescendantFoldersUseCase,\n config.filterNames\n );\n\n this.listPresenter.init({\n dataSource,\n initialSort: { field: \"savedOn\", direction: \"DESC\" },\n initialFilters: { folderId: initialFolderId },\n initialSearch: config.initialSearch,\n limit: 50,\n itemLabel: { singular: \"entry\", plural: \"entries\" }\n });\n\n if (initialFolderId !== \"root\") {\n this.foldersPresenter.selectFolder(initialFolderId);\n }\n\n this.disposeReaction = reaction(\n () => this.foldersPresenter.vm.currentFolderId,\n folderId => {\n const effectiveFolderId = folderId ?? \"root\";\n this.listPresenter.actions.filter.set(\"folderId\", effectiveFolderId);\n }\n );\n }\n\n get list(): ListPresenter.Interface<CmsContentEntry> {\n return this.listPresenter;\n }\n\n get folders(): FolderTreePresenter.Interface {\n return this.foldersPresenter;\n }\n\n selectEntry(id: string): void {\n this.selectedEntryId = id;\n }\n\n deselectEntry(): void {\n this.selectedEntryId = null;\n }\n\n createEntry(): void {\n this.selectedEntryId = \"new\";\n }\n\n async deleteEntry(id: string): Promise<boolean> {\n const model = this.model;\n const result = await this.confirmation.confirm(TRASH_ENTRY_DIALOG, { entryId: id }, () =>\n this.deleteEntryUseCase.execute({ model, id })\n );\n return result !== false;\n }\n\n async publishEntry(id: string): Promise<boolean> {\n const model = this.model;\n const cache = this.cacheProvider.get(model.modelId);\n const entry = cache.getItem(item => item.id === id);\n if (!entry) {\n return false;\n }\n\n const result = await this.confirmation.confirm<{ revisionDescription: string }>(\n PUBLISH_ENTRY_DIALOG,\n { entry },\n async data => {\n if (data.revisionDescription) {\n await this.updateRevisionDescriptionUseCase.execute({\n model,\n id,\n revisionDescription: data.revisionDescription\n });\n }\n await this.publishEntryUseCase.execute({ model, revisionId: id });\n }\n );\n return result !== false;\n }\n\n async unpublishEntry(id: string): Promise<boolean> {\n const model = this.model;\n const result = await this.confirmation.confirm(\n UNPUBLISH_ENTRY_DIALOG,\n { entryId: id },\n () => this.unpublishEntryUseCase.execute({ model, revisionId: id })\n );\n return result !== false;\n }\n\n async moveEntry(id: string, folderId: string): Promise<boolean> {\n await this.moveEntryUseCase.execute({ model: this.model, id, folderId });\n return true;\n }\n\n dispose(): void {\n if (this.disposeReaction) {\n this.disposeReaction();\n this.disposeReaction = null;\n }\n }\n}\n\nexport const ContentEntriesPresenter = Abstraction.createImplementation({\n implementation: ContentEntriesPresenterImpl,\n dependencies: [\n ListPresenter,\n FolderTreePresenter,\n Confirmation,\n CmsModelContext,\n ListEntriesUseCase,\n DeleteEntryUseCase,\n PublishEntryUseCase,\n UnpublishEntryUseCase,\n MoveEntryUseCase,\n UpdateRevisionDescriptionUseCase,\n ContentEntriesCacheProvider,\n GetDescendantFoldersUseCase\n ]\n});\n"],"names":["TRASH_ENTRY_DIALOG","PUBLISH_ENTRY_DIALOG","UNPUBLISH_ENTRY_DIALOG","ContentEntriesPresenterImpl","listPresenter","foldersPresenter","confirmation","modelAccessor","listEntriesUseCase","deleteEntryUseCase","publishEntryUseCase","unpublishEntryUseCase","moveEntryUseCase","updateRevisionDescriptionUseCase","cacheProvider","getDescendantFoldersUseCase","makeAutoObservable","computed","appliedQuery","hasSearch","hasFilters","Object","k","showFolders","childFolders","sortFolders","config","initialFolderId","cache","dataSource","ContentEntriesDataSource","reaction","folderId","effectiveFolderId","id","model","result","entry","item","data","ContentEntriesPresenter","Abstraction","ListPresenter","FolderTreePresenter","Confirmation","CmsModelContext","ListEntriesUseCase","DeleteEntryUseCase","PublishEntryUseCase","UnpublishEntryUseCase","MoveEntryUseCase","UpdateRevisionDescriptionUseCase","ContentEntriesCacheProvider","GetDescendantFoldersUseCase"],"mappings":"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"}
1
+ {"version":3,"file":"presentation/contentEntries/list/ContentEntriesPresenter.js","sources":["../../../../src/presentation/contentEntries/list/ContentEntriesPresenter.ts"],"sourcesContent":["import { computed, makeAutoObservable, reaction } from \"mobx\";\nimport type { IReactionDisposer } from \"mobx\";\nimport { ListPresenter } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport { FolderTreePresenter } from \"@webiny/app-aco/presentation/folderTree/abstractions.js\";\nimport { GetDescendantFoldersUseCase } from \"@webiny/app-aco/features/folders/getDescendantFolders/abstractions.js\";\nimport { sortFolders } from \"@webiny/app-aco\";\nimport { Confirmation } from \"@webiny/app-admin/features/confirmation/abstractions.js\";\nimport type { CmsContentEntry } from \"~/types.js\";\nimport { ListEntriesUseCase } from \"~/features/contentEntry/listEntries/abstractions.js\";\nimport { DeleteEntryUseCase } from \"~/features/contentEntry/deleteEntry/abstractions.js\";\nimport { PublishEntryUseCase } from \"~/features/contentEntry/publishEntry/abstractions.js\";\nimport { UnpublishEntryUseCase } from \"~/features/contentEntry/unpublishEntry/abstractions.js\";\nimport { MoveEntryUseCase } from \"~/features/contentEntry/moveEntry/abstractions.js\";\nimport { DuplicateEntryUseCase } from \"~/features/contentEntry/duplicateEntry/abstractions.js\";\nimport { UpdateRevisionDescriptionUseCase } from \"~/features/contentEntry/updateRevisionDescription/abstractions.js\";\nimport {\n ContentEntriesCacheProvider,\n CmsModelContext\n} from \"~/features/contentEntry/abstractions.js\";\nimport {\n ContentEntriesPresenter as Abstraction,\n type IContentEntriesViewModel,\n type IDuplicateEntryResult\n} from \"./abstractions.js\";\nimport { ContentEntriesDataSource } from \"./ContentEntriesDataSource.js\";\n\nexport const TRASH_ENTRY_DIALOG = \"trash-entry\";\nexport const PUBLISH_ENTRY_DIALOG = \"publish-entry\";\nexport const UNPUBLISH_ENTRY_DIALOG = \"unpublish-entry\";\nexport const DUPLICATE_ENTRY_DIALOG = \"duplicate-entry\";\n\nclass ContentEntriesPresenterImpl implements Abstraction.Interface {\n private selectedEntryId: string | null = null;\n private disposeReaction: IReactionDisposer | null = null;\n\n constructor(\n private listPresenter: ListPresenter.Interface<CmsContentEntry>,\n private foldersPresenter: FolderTreePresenter.Interface,\n private confirmation: Confirmation.Interface,\n private modelAccessor: CmsModelContext.Interface,\n private listEntriesUseCase: ListEntriesUseCase.Interface,\n private deleteEntryUseCase: DeleteEntryUseCase.Interface,\n private publishEntryUseCase: PublishEntryUseCase.Interface,\n private unpublishEntryUseCase: UnpublishEntryUseCase.Interface,\n private moveEntryUseCase: MoveEntryUseCase.Interface,\n private duplicateEntryUseCase: DuplicateEntryUseCase.Interface,\n private updateRevisionDescriptionUseCase: UpdateRevisionDescriptionUseCase.Interface,\n private cacheProvider: ContentEntriesCacheProvider.Interface,\n private getDescendantFoldersUseCase: GetDescendantFoldersUseCase.Interface\n ) {\n makeAutoObservable<\n ContentEntriesPresenterImpl,\n | \"disposeReaction\"\n | \"confirmation\"\n | \"modelAccessor\"\n | \"listEntriesUseCase\"\n | \"deleteEntryUseCase\"\n | \"publishEntryUseCase\"\n | \"unpublishEntryUseCase\"\n | \"moveEntryUseCase\"\n | \"duplicateEntryUseCase\"\n | \"updateRevisionDescriptionUseCase\"\n | \"cacheProvider\"\n | \"getDescendantFoldersUseCase\"\n >(this, {\n disposeReaction: false,\n confirmation: false,\n modelAccessor: false,\n listEntriesUseCase: false,\n deleteEntryUseCase: false,\n publishEntryUseCase: false,\n unpublishEntryUseCase: false,\n moveEntryUseCase: false,\n duplicateEntryUseCase: false,\n updateRevisionDescriptionUseCase: false,\n cacheProvider: false,\n getDescendantFoldersUseCase: false,\n vm: computed\n });\n }\n\n private get model() {\n return this.modelAccessor.getModel();\n }\n\n get vm(): IContentEntriesViewModel {\n const appliedQuery = this.listPresenter.vm.appliedQuery;\n const hasSearch = !!appliedQuery?.search;\n const hasFilters = Object.keys(this.listPresenter.vm.filters).some(k => k !== \"folderId\");\n const showFolders = !hasSearch && !hasFilters;\n\n const childFolders = showFolders\n ? sortFolders(this.foldersPresenter.vm.childFolders ?? [], appliedQuery?.sort)\n : [];\n\n return {\n model: this.model,\n selectedEntryId: this.selectedEntryId,\n showingEntry: this.selectedEntryId !== null,\n showFolders,\n childFolders\n };\n }\n\n init(config: Abstraction.InitConfig = {}): void {\n const initialFolderId = config.initialFolderId ?? \"root\";\n\n const cache = this.cacheProvider.get(this.model.modelId);\n const dataSource = new ContentEntriesDataSource(\n this.model,\n this.listEntriesUseCase,\n cache,\n this.getDescendantFoldersUseCase,\n config.filterNames\n );\n\n this.listPresenter.init({\n dataSource,\n initialSort: { field: \"savedOn\", direction: \"DESC\" },\n initialFilters: { folderId: initialFolderId },\n initialSearch: config.initialSearch,\n limit: 50,\n itemLabel: { singular: \"entry\", plural: \"entries\" }\n });\n\n if (initialFolderId !== \"root\") {\n this.foldersPresenter.selectFolder(initialFolderId);\n }\n\n this.disposeReaction = reaction(\n () => this.foldersPresenter.vm.currentFolderId,\n folderId => {\n const effectiveFolderId = folderId ?? \"root\";\n this.listPresenter.actions.filter.set(\"folderId\", effectiveFolderId);\n }\n );\n }\n\n get list(): ListPresenter.Interface<CmsContentEntry> {\n return this.listPresenter;\n }\n\n get folders(): FolderTreePresenter.Interface {\n return this.foldersPresenter;\n }\n\n selectEntry(id: string): void {\n this.selectedEntryId = id;\n }\n\n deselectEntry(): void {\n this.selectedEntryId = null;\n }\n\n createEntry(): void {\n this.selectedEntryId = \"new\";\n }\n\n async deleteEntry(id: string): Promise<boolean> {\n const model = this.model;\n const result = await this.confirmation.confirm(TRASH_ENTRY_DIALOG, { entryId: id }, () =>\n this.deleteEntryUseCase.execute({ model, id })\n );\n return result !== false;\n }\n\n async publishEntry(id: string): Promise<boolean> {\n const model = this.model;\n const cache = this.cacheProvider.get(model.modelId);\n const entry = cache.getItem(item => item.id === id);\n if (!entry) {\n return false;\n }\n\n const result = await this.confirmation.confirm<{ revisionDescription: string }>(\n PUBLISH_ENTRY_DIALOG,\n { entry },\n async data => {\n if (data.revisionDescription) {\n await this.updateRevisionDescriptionUseCase.execute({\n model,\n id,\n revisionDescription: data.revisionDescription\n });\n }\n await this.publishEntryUseCase.execute({ model, revisionId: id });\n }\n );\n return result !== false;\n }\n\n async unpublishEntry(id: string): Promise<boolean> {\n const model = this.model;\n const result = await this.confirmation.confirm(\n UNPUBLISH_ENTRY_DIALOG,\n { entryId: id },\n () => this.unpublishEntryUseCase.execute({ model, revisionId: id })\n );\n return result !== false;\n }\n\n async moveEntry(id: string, folderId: string): Promise<boolean> {\n await this.moveEntryUseCase.execute({ model: this.model, id, folderId });\n return true;\n }\n\n async duplicateEntry(entry: CmsContentEntry): Promise<IDuplicateEntryResult | false> {\n const model = this.model;\n return this.confirmation.confirm(\n DUPLICATE_ENTRY_DIALOG,\n { entry },\n async (): Promise<IDuplicateEntryResult> => {\n try {\n const duplicate = await this.duplicateEntryUseCase.execute({\n model,\n revisionId: entry.id\n });\n return { entry: duplicate };\n } catch (error) {\n return { error: error as Error };\n }\n }\n );\n }\n\n dispose(): void {\n if (this.disposeReaction) {\n this.disposeReaction();\n this.disposeReaction = null;\n }\n }\n}\n\nexport const ContentEntriesPresenter = Abstraction.createImplementation({\n implementation: ContentEntriesPresenterImpl,\n dependencies: [\n ListPresenter,\n FolderTreePresenter,\n Confirmation,\n CmsModelContext,\n ListEntriesUseCase,\n DeleteEntryUseCase,\n PublishEntryUseCase,\n UnpublishEntryUseCase,\n MoveEntryUseCase,\n DuplicateEntryUseCase,\n UpdateRevisionDescriptionUseCase,\n ContentEntriesCacheProvider,\n GetDescendantFoldersUseCase\n ]\n});\n"],"names":["TRASH_ENTRY_DIALOG","PUBLISH_ENTRY_DIALOG","UNPUBLISH_ENTRY_DIALOG","DUPLICATE_ENTRY_DIALOG","ContentEntriesPresenterImpl","listPresenter","foldersPresenter","confirmation","modelAccessor","listEntriesUseCase","deleteEntryUseCase","publishEntryUseCase","unpublishEntryUseCase","moveEntryUseCase","duplicateEntryUseCase","updateRevisionDescriptionUseCase","cacheProvider","getDescendantFoldersUseCase","makeAutoObservable","computed","appliedQuery","hasSearch","hasFilters","Object","k","showFolders","childFolders","sortFolders","config","initialFolderId","cache","dataSource","ContentEntriesDataSource","reaction","folderId","effectiveFolderId","id","model","result","entry","item","data","duplicate","error","ContentEntriesPresenter","Abstraction","ListPresenter","FolderTreePresenter","Confirmation","CmsModelContext","ListEntriesUseCase","DeleteEntryUseCase","PublishEntryUseCase","UnpublishEntryUseCase","MoveEntryUseCase","DuplicateEntryUseCase","UpdateRevisionDescriptionUseCase","ContentEntriesCacheProvider","GetDescendantFoldersUseCase"],"mappings":";;;;;;;;;;;;;;;;AA0BO,MAAMA,qBAAqB;AAC3B,MAAMC,uBAAuB;AAC7B,MAAMC,yBAAyB;AAC/B,MAAMC,yBAAyB;AAEtC,MAAMC;IAIF,YACYC,aAAuD,EACvDC,gBAA+C,EAC/CC,YAAoC,EACpCC,aAAwC,EACxCC,kBAAgD,EAChDC,kBAAgD,EAChDC,mBAAkD,EAClDC,qBAAsD,EACtDC,gBAA4C,EAC5CC,qBAAsD,EACtDC,gCAA4E,EAC5EC,aAAoD,EACpDC,2BAAkE,CAC5E;aAbUZ,aAAa,GAAbA;aACAC,gBAAgB,GAAhBA;aACAC,YAAY,GAAZA;aACAC,aAAa,GAAbA;aACAC,kBAAkB,GAAlBA;aACAC,kBAAkB,GAAlBA;aACAC,mBAAmB,GAAnBA;aACAC,qBAAqB,GAArBA;aACAC,gBAAgB,GAAhBA;aACAC,qBAAqB,GAArBA;aACAC,gCAAgC,GAAhCA;aACAC,aAAa,GAAbA;aACAC,2BAA2B,GAA3BA;aAhBJ,eAAe,GAAkB;aACjC,eAAe,GAA6B;QAiBhDC,mBAcE,IAAI,EAAE;YACJ,iBAAiB;YACjB,cAAc;YACd,eAAe;YACf,oBAAoB;YACpB,oBAAoB;YACpB,qBAAqB;YACrB,uBAAuB;YACvB,kBAAkB;YAClB,uBAAuB;YACvB,kCAAkC;YAClC,eAAe;YACf,6BAA6B;YAC7B,IAAIC;QACR;IACJ;IAEA,IAAY,QAAQ;QAChB,OAAO,IAAI,CAAC,aAAa,CAAC,QAAQ;IACtC;IAEA,IAAI,KAA+B;QAC/B,MAAMC,eAAe,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,YAAY;QACvD,MAAMC,YAAY,CAAC,CAACD,cAAc;QAClC,MAAME,aAAaC,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAACC,CAAAA,IAAKA,AAAM,eAANA;QACxE,MAAMC,cAAc,CAACJ,aAAa,CAACC;QAEnC,MAAMI,eAAeD,cACfE,YAAY,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC,YAAY,IAAI,EAAE,EAAEP,cAAc,QACvE,EAAE;QAER,OAAO;YACH,OAAO,IAAI,CAAC,KAAK;YACjB,iBAAiB,IAAI,CAAC,eAAe;YACrC,cAAc,AAAyB,SAAzB,IAAI,CAAC,eAAe;YAClCK;YACAC;QACJ;IACJ;IAEA,KAAKE,SAAiC,CAAC,CAAC,EAAQ;QAC5C,MAAMC,kBAAkBD,OAAO,eAAe,IAAI;QAElD,MAAME,QAAQ,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO;QACvD,MAAMC,aAAa,IAAIC,yBACnB,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,kBAAkB,EACvBF,OACA,IAAI,CAAC,2BAA2B,EAChCF,OAAO,WAAW;QAGtB,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;YACpBG;YACA,aAAa;gBAAE,OAAO;gBAAW,WAAW;YAAO;YACnD,gBAAgB;gBAAE,UAAUF;YAAgB;YAC5C,eAAeD,OAAO,aAAa;YACnC,OAAO;YACP,WAAW;gBAAE,UAAU;gBAAS,QAAQ;YAAU;QACtD;QAEA,IAAIC,AAAoB,WAApBA,iBACA,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAACA;QAGvC,IAAI,CAAC,eAAe,GAAGI,SACnB,IAAM,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC,eAAe,EAC9CC,CAAAA;YACI,MAAMC,oBAAoBD,YAAY;YACtC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,YAAYC;QACtD;IAER;IAEA,IAAI,OAAiD;QACjD,OAAO,IAAI,CAAC,aAAa;IAC7B;IAEA,IAAI,UAAyC;QACzC,OAAO,IAAI,CAAC,gBAAgB;IAChC;IAEA,YAAYC,EAAU,EAAQ;QAC1B,IAAI,CAAC,eAAe,GAAGA;IAC3B;IAEA,gBAAsB;QAClB,IAAI,CAAC,eAAe,GAAG;IAC3B;IAEA,cAAoB;QAChB,IAAI,CAAC,eAAe,GAAG;IAC3B;IAEA,MAAM,YAAYA,EAAU,EAAoB;QAC5C,MAAMC,QAAQ,IAAI,CAAC,KAAK;QACxB,MAAMC,SAAS,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAACtC,oBAAoB;YAAE,SAASoC;QAAG,GAAG,IAChF,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC;gBAAEC;gBAAOD;YAAG;QAEhD,OAAOE,AAAW,UAAXA;IACX;IAEA,MAAM,aAAaF,EAAU,EAAoB;QAC7C,MAAMC,QAAQ,IAAI,CAAC,KAAK;QACxB,MAAMP,QAAQ,IAAI,CAAC,aAAa,CAAC,GAAG,CAACO,MAAM,OAAO;QAClD,MAAME,QAAQT,MAAM,OAAO,CAACU,CAAAA,OAAQA,KAAK,EAAE,KAAKJ;QAChD,IAAI,CAACG,OACD,OAAO;QAGX,MAAMD,SAAS,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAC1CrC,sBACA;YAAEsC;QAAM,GACR,OAAME;YACF,IAAIA,KAAK,mBAAmB,EACxB,MAAM,IAAI,CAAC,gCAAgC,CAAC,OAAO,CAAC;gBAChDJ;gBACAD;gBACA,qBAAqBK,KAAK,mBAAmB;YACjD;YAEJ,MAAM,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC;gBAAEJ;gBAAO,YAAYD;YAAG;QACnE;QAEJ,OAAOE,AAAW,UAAXA;IACX;IAEA,MAAM,eAAeF,EAAU,EAAoB;QAC/C,MAAMC,QAAQ,IAAI,CAAC,KAAK;QACxB,MAAMC,SAAS,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAC1CpC,wBACA;YAAE,SAASkC;QAAG,GACd,IAAM,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC;gBAAEC;gBAAO,YAAYD;YAAG;QAErE,OAAOE,AAAW,UAAXA;IACX;IAEA,MAAM,UAAUF,EAAU,EAAEF,QAAgB,EAAoB;QAC5D,MAAM,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK;YAAEE;YAAIF;QAAS;QACtE,OAAO;IACX;IAEA,MAAM,eAAeK,KAAsB,EAA0C;QACjF,MAAMF,QAAQ,IAAI,CAAC,KAAK;QACxB,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAC5BlC,wBACA;YAAEoC;QAAM,GACR;YACI,IAAI;gBACA,MAAMG,YAAY,MAAM,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC;oBACvDL;oBACA,YAAYE,MAAM,EAAE;gBACxB;gBACA,OAAO;oBAAE,OAAOG;gBAAU;YAC9B,EAAE,OAAOC,OAAO;gBACZ,OAAO;oBAAE,OAAOA;gBAAe;YACnC;QACJ;IAER;IAEA,UAAgB;QACZ,IAAI,IAAI,CAAC,eAAe,EAAE;YACtB,IAAI,CAAC,eAAe;YACpB,IAAI,CAAC,eAAe,GAAG;QAC3B;IACJ;AACJ;AAEO,MAAMC,kDAA0BC,wBAAAA,oBAAgC,CAAC;IACpE,gBAAgBzC;IAChB,cAAc;QACV0C;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;QACAC;KACH;AACL"}
@@ -13,12 +13,14 @@ import { DeleteEntryFeature } from "../../../features/contentEntry/deleteEntry/f
13
13
  import { PublishEntryFeature } from "../../../features/contentEntry/publishEntry/feature.js";
14
14
  import { UnpublishEntryFeature } from "../../../features/contentEntry/unpublishEntry/feature.js";
15
15
  import { MoveEntryFeature } from "../../../features/contentEntry/moveEntry/feature.js";
16
+ import { DuplicateEntryFeature } from "../../../features/contentEntry/duplicateEntry/feature.js";
16
17
  import { UpdateRevisionDescriptionFeature } from "../../../features/contentEntry/updateRevisionDescription/feature.js";
17
18
  import { ListEntriesGateway } from "../../../features/contentEntry/listEntries/abstractions.js";
18
19
  import { DeleteEntryGateway } from "../../../features/contentEntry/deleteEntry/abstractions.js";
19
20
  import { PublishEntryGateway, PublishEntryUseCase } from "../../../features/contentEntry/publishEntry/abstractions.js";
20
21
  import { UnpublishEntryGateway } from "../../../features/contentEntry/unpublishEntry/abstractions.js";
21
22
  import { MoveEntryGateway } from "../../../features/contentEntry/moveEntry/abstractions.js";
23
+ import { DuplicateEntryGateway } from "../../../features/contentEntry/duplicateEntry/abstractions.js";
22
24
  import { UpdateRevisionDescriptionGateway } from "../../../features/contentEntry/updateRevisionDescription/abstractions.js";
23
25
  import { EventPublisher } from "@webiny/app/features/eventPublisher/index.js";
24
26
  import { ContentEntriesPresenter } from "./abstractions.js";
@@ -112,7 +114,7 @@ function setup() {
112
114
  container.registerInstance(FolderTreePresenter, foldersPresenter);
113
115
  container.registerInstance(Confirmation, {
114
116
  confirm: vi.fn(async (_name, _params, execute)=>{
115
- if (execute) await execute(void 0);
117
+ if (execute) return await execute(void 0);
116
118
  return true;
117
119
  })
118
120
  });
@@ -131,6 +133,7 @@ function setup() {
131
133
  PublishEntryFeature.register(container);
132
134
  UnpublishEntryFeature.register(container);
133
135
  MoveEntryFeature.register(container);
136
+ DuplicateEntryFeature.register(container);
134
137
  UpdateRevisionDescriptionFeature.register(container);
135
138
  const listEntriesGateway = {
136
139
  execute: vi.fn()
@@ -144,6 +147,10 @@ function setup() {
144
147
  container.registerInstance(ListEntriesGateway, listEntriesGateway);
145
148
  container.registerInstance(MoveEntryGateway, moveEntryGateway);
146
149
  container.registerInstance(DeleteEntryGateway, deleteEntryGateway);
150
+ const duplicateEntryGateway = {
151
+ execute: vi.fn()
152
+ };
153
+ container.registerInstance(DuplicateEntryGateway, duplicateEntryGateway);
147
154
  const publishEntryGateway = {
148
155
  execute: vi.fn()
149
156
  };
@@ -161,6 +168,7 @@ function setup() {
161
168
  listEntriesGateway,
162
169
  moveEntryGateway,
163
170
  deleteEntryGateway,
171
+ duplicateEntryGateway,
164
172
  getDescendantFolders,
165
173
  foldersPresenter,
166
174
  publishEntryGateway,
@@ -517,6 +525,55 @@ describe("ContentEntriesPresenter", ()=>{
517
525
  });
518
526
  });
519
527
  });
528
+ describe("duplicate entry", ()=>{
529
+ it("should add the duplicated entry to the current view", async ()=>{
530
+ const entries = [
531
+ createEntry("entry-2", "root", {
532
+ savedOn: "2024-01-02T00:00:00.000Z"
533
+ }),
534
+ createEntry("entry-1", "root", {
535
+ savedOn: "2024-01-01T00:00:00.000Z"
536
+ })
537
+ ];
538
+ await initPresenter(t, {
539
+ data: entries
540
+ });
541
+ const duplicate = createEntry("entry-3", "root", {
542
+ savedOn: "2024-01-03T00:00:00.000Z"
543
+ });
544
+ t.duplicateEntryGateway.execute.mockResolvedValueOnce(duplicate);
545
+ const result = await t.presenter.duplicateEntry(entries[1]);
546
+ expect(result).toEqual({
547
+ entry: duplicate
548
+ });
549
+ expect(t.duplicateEntryGateway.execute).toHaveBeenCalledWith({
550
+ model: MODEL,
551
+ revisionId: "entry-1#0001"
552
+ });
553
+ await vi.waitFor(()=>{
554
+ expect(t.presenter.list.vm.rows.map((r)=>r.entryId)).toEqual([
555
+ "entry-3",
556
+ "entry-2",
557
+ "entry-1"
558
+ ]);
559
+ });
560
+ });
561
+ it("should return the error when duplication fails", async ()=>{
562
+ const entries = [
563
+ createEntry("entry-1", "root")
564
+ ];
565
+ await initPresenter(t, {
566
+ data: entries
567
+ });
568
+ const error = new Error("Not allowed.");
569
+ t.duplicateEntryGateway.execute.mockRejectedValueOnce(error);
570
+ const result = await t.presenter.duplicateEntry(entries[0]);
571
+ expect(result).toEqual({
572
+ error
573
+ });
574
+ expect(t.presenter.list.vm.rows).toHaveLength(1);
575
+ });
576
+ });
520
577
  describe("delete entry", ()=>{
521
578
  it("should remove entry from current view after delete", async ()=>{
522
579
  const entries = [