@webiny/app-file-manager 6.4.12-beta.4 → 6.5.0-beta.0

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 (210) hide show
  1. package/app.js +21 -2
  2. package/app.js.map +1 -1
  3. package/domain/tagsHelpers.d.ts +2 -2
  4. package/domain/types.d.ts +0 -8
  5. package/features/permissions/abstractions.d.ts +2 -2
  6. package/features/permissions/feature.d.ts +2 -2
  7. package/features/shared/FILE_FIELDS.js +4 -0
  8. package/features/shared/FILE_FIELDS.js.map +1 -1
  9. package/modules/AiEnrichment/AiImageEnrichmentEventHandler.d.ts +18 -0
  10. package/modules/AiEnrichment/AiImageEnrichmentEventHandler.js +33 -0
  11. package/modules/AiEnrichment/AiImageEnrichmentEventHandler.js.map +1 -0
  12. package/modules/AiEnrichment/feature.d.ts +1 -0
  13. package/modules/AiEnrichment/feature.js +11 -0
  14. package/modules/AiEnrichment/feature.js.map +1 -0
  15. package/modules/AiEnrichment/index.d.ts +2 -0
  16. package/modules/AiEnrichment/index.js +9 -0
  17. package/modules/AiEnrichment/index.js.map +1 -0
  18. package/modules/HeadlessCms/fieldType/AssetFieldSettingsModifier.d.ts +26 -0
  19. package/modules/HeadlessCms/fieldType/AssetFieldSettingsModifier.js +39 -0
  20. package/modules/HeadlessCms/fieldType/AssetFieldSettingsModifier.js.map +1 -0
  21. package/modules/HeadlessCms/fieldType/AssetFieldType.d.ts +34 -0
  22. package/modules/HeadlessCms/fieldType/AssetFieldType.js +42 -0
  23. package/modules/HeadlessCms/fieldType/AssetFieldType.js.map +1 -0
  24. package/modules/HeadlessCms/fieldType/AssetFormFieldType.d.ts +21 -0
  25. package/modules/HeadlessCms/fieldType/AssetFormFieldType.js +42 -0
  26. package/modules/HeadlessCms/fieldType/AssetFormFieldType.js.map +1 -0
  27. package/modules/HeadlessCms/fieldType/AssetInputRenderer.d.ts +15 -0
  28. package/modules/HeadlessCms/fieldType/AssetInputRenderer.js +20 -0
  29. package/modules/HeadlessCms/fieldType/AssetInputRenderer.js.map +1 -0
  30. package/modules/HeadlessCms/fieldType/AssetInputsRenderer.d.ts +15 -0
  31. package/modules/HeadlessCms/fieldType/AssetInputsRenderer.js +20 -0
  32. package/modules/HeadlessCms/fieldType/AssetInputsRenderer.js.map +1 -0
  33. package/modules/HeadlessCms/fieldType/FileFieldSettingsModifier.d.ts +16 -0
  34. package/modules/HeadlessCms/fieldType/FileFieldSettingsModifier.js +33 -0
  35. package/modules/HeadlessCms/fieldType/FileFieldSettingsModifier.js.map +1 -0
  36. package/modules/HeadlessCms/fieldType/FileFieldType.d.ts +25 -0
  37. package/modules/HeadlessCms/fieldType/FileFieldType.js +38 -0
  38. package/modules/HeadlessCms/fieldType/FileFieldType.js.map +1 -0
  39. package/modules/HeadlessCms/fieldType/FileInputRenderer.d.ts +15 -0
  40. package/modules/HeadlessCms/fieldType/FileInputRenderer.js +19 -0
  41. package/modules/HeadlessCms/fieldType/FileInputRenderer.js.map +1 -0
  42. package/modules/HeadlessCms/fieldType/FileInputsRenderer.d.ts +15 -0
  43. package/modules/HeadlessCms/fieldType/FileInputsRenderer.js +19 -0
  44. package/modules/HeadlessCms/fieldType/FileInputsRenderer.js.map +1 -0
  45. package/modules/HeadlessCms/fieldType/feature.d.ts +1 -0
  46. package/modules/HeadlessCms/fieldType/feature.js +27 -0
  47. package/modules/HeadlessCms/fieldType/feature.js.map +1 -0
  48. package/modules/HeadlessCms/fieldType/isAssetField.d.ts +2 -0
  49. package/modules/HeadlessCms/fieldType/isAssetField.js +4 -0
  50. package/modules/HeadlessCms/fieldType/isAssetField.js.map +1 -0
  51. package/package.json +25 -35
  52. package/presentation/FileActions/FileDetails/EditImage.d.ts +2 -0
  53. package/presentation/FileActions/FileDetails/EditImage.js +39 -0
  54. package/presentation/FileActions/FileDetails/EditImage.js.map +1 -0
  55. package/presentation/FileActions/index.js +4 -0
  56. package/presentation/FileActions/index.js.map +1 -1
  57. package/presentation/FileDetails/FileDetailsPresenter.d.ts +2 -0
  58. package/presentation/FileDetails/FileDetailsPresenter.js +5 -0
  59. package/presentation/FileDetails/FileDetailsPresenter.js.map +1 -1
  60. package/presentation/FileDetails/FileDetailsPresenter.test.js +1 -0
  61. package/presentation/FileDetails/FileDetailsPresenter.test.js.map +1 -1
  62. package/presentation/FileDetails/abstractions.d.ts +2 -0
  63. package/presentation/FileDetails/abstractions.js.map +1 -1
  64. package/presentation/FileDetails/components/Content.d.ts +5 -4
  65. package/presentation/FileDetails/components/FileDetailsDrawer.d.ts +2 -2
  66. package/presentation/FileList/FileListDataSource.d.ts +8 -12
  67. package/presentation/FileList/FileListDataSource.js +47 -87
  68. package/presentation/FileList/FileListDataSource.js.map +1 -1
  69. package/presentation/FileList/FileListPresenter.test.js +9 -3
  70. package/presentation/FileList/FileListPresenter.test.js.map +1 -1
  71. package/presentation/FileList/FileManagerPresenter.d.ts +1 -0
  72. package/presentation/FileList/FileManagerPresenter.js +15 -5
  73. package/presentation/FileList/FileManagerPresenter.js.map +1 -1
  74. package/presentation/FileList/abstractions.d.ts +3 -0
  75. package/presentation/FileList/abstractions.js.map +1 -1
  76. package/presentation/FileList/components/BulkActions/BulkActionBar.d.ts +2 -2
  77. package/presentation/FileList/components/BulkActions/BulkActionDelete.d.ts +2 -2
  78. package/presentation/FileList/components/BulkActions/BulkActionMove.d.ts +2 -2
  79. package/presentation/FileList/components/BulkActions/index.d.ts +0 -1
  80. package/presentation/FileList/components/BulkActions/index.js +0 -1
  81. package/presentation/FileList/components/BulkActions/useBulkActionWorker.d.ts +9 -8
  82. package/presentation/FileList/components/BulkActions/useBulkActionWorker.js +3 -9
  83. package/presentation/FileList/components/BulkActions/useBulkActionWorker.js.map +1 -1
  84. package/presentation/FileList/components/Filters/SubFoldersToggle.d.ts +2 -2
  85. package/presentation/FileList/components/Filters/TagsFilter.d.ts +2 -2
  86. package/presentation/FileList/components/Filters/TypeFilter.d.ts +2 -2
  87. package/presentation/FileList/components/Grid/FileGrid.d.ts +2 -2
  88. package/presentation/FileList/components/Grid/FileGrid.js +1 -1
  89. package/presentation/FileList/components/Grid/FileGrid.js.map +1 -1
  90. package/presentation/FileList/components/Grid/FileGrid.test.js +18 -7
  91. package/presentation/FileList/components/Grid/FileGrid.test.js.map +1 -1
  92. package/presentation/FileList/components/Header/FileManagerHeader.d.ts +2 -2
  93. package/presentation/FileList/components/Search/SearchBar.d.ts +2 -2
  94. package/presentation/FileList/components/Table/FileTable.d.ts +2 -2
  95. package/presentation/FileList/components/Table/FileTable.js +1 -1
  96. package/presentation/FileList/components/Table/FileTable.js.map +1 -1
  97. package/presentation/FileList/components/Table/FileTable.test.js +9 -4
  98. package/presentation/FileList/components/Table/FileTable.test.js.map +1 -1
  99. package/presentation/FileList/components/Upload/UploadProgress.d.ts +2 -2
  100. package/presentation/FileManager/FileManagerView.js +4 -2
  101. package/presentation/FileManager/FileManagerView.js.map +1 -1
  102. package/presentation/FileManager/RouteParamsSync.d.ts +2 -0
  103. package/presentation/FileManager/RouteParamsSync.js +30 -0
  104. package/presentation/FileManager/RouteParamsSync.js.map +1 -0
  105. package/presentation/Settings/components/SettingsView.d.ts +2 -2
  106. package/presentation/config/DefaultFileManagerConfig.js +1 -4
  107. package/presentation/config/DefaultFileManagerConfig.js.map +1 -1
  108. package/presentation/config/FileManagerViewConfig.d.ts +19 -25
  109. package/presentation/config/configComponents/Browser/BulkAction.d.ts +10 -8
  110. package/presentation/config/configComponents/Browser/BulkAction.js +3 -9
  111. package/presentation/config/configComponents/Browser/BulkAction.js.map +1 -1
  112. package/presentation/config/configComponents/Browser/FileAction.d.ts +2 -1
  113. package/presentation/config/configComponents/Browser/FolderAction.d.ts +2 -1
  114. package/presentation/config/configComponents/Browser/Table/Column.d.ts +9 -8
  115. package/presentation/config/configComponents/Browser/Table/index.d.ts +9 -9
  116. package/presentation/config/configComponents/Browser/index.d.ts +20 -20
  117. package/presentation/config/configComponents/Browser/index.js +0 -8
  118. package/presentation/config/configComponents/Browser/index.js.map +1 -1
  119. package/presentation/config/configComponents/FileDetails/index.d.ts +4 -2
  120. package/presentation/config/getThumbnailRenderer.js +1 -1
  121. package/presentation/config/getThumbnailRenderer.js.map +1 -1
  122. package/presentation/config/thumbnailRenderers/CroppedFileImage.d.ts +18 -0
  123. package/presentation/config/thumbnailRenderers/CroppedFileImage.js +66 -0
  124. package/presentation/config/thumbnailRenderers/CroppedFileImage.js.map +1 -0
  125. package/presentation/config/thumbnailRenderers/FilePreviewImageRenderer.js +6 -9
  126. package/presentation/config/thumbnailRenderers/FilePreviewImageRenderer.js.map +1 -1
  127. package/presentation/config/thumbnailRenderers/GridItemImageRenderer.js +6 -9
  128. package/presentation/config/thumbnailRenderers/GridItemImageRenderer.js.map +1 -1
  129. package/presentation/config/thumbnailRenderers/TableItemImageRenderer.js +6 -9
  130. package/presentation/config/thumbnailRenderers/TableItemImageRenderer.js.map +1 -1
  131. package/presentation/fieldRenderers/CmsAssetPickerRenderer.d.ts +22 -0
  132. package/presentation/fieldRenderers/CmsAssetPickerRenderer.js +56 -0
  133. package/presentation/fieldRenderers/CmsAssetPickerRenderer.js.map +1 -0
  134. package/presentation/fieldRenderers/CmsFilePickerRenderer.d.ts +13 -0
  135. package/presentation/fieldRenderers/CmsFilePickerRenderer.js +36 -0
  136. package/presentation/fieldRenderers/CmsFilePickerRenderer.js.map +1 -0
  137. package/presentation/fieldRenderers/CmsImageEditorDialog.d.ts +7 -0
  138. package/presentation/fieldRenderers/CmsImageEditorDialog.js +32 -0
  139. package/presentation/fieldRenderers/CmsImageEditorDialog.js.map +1 -0
  140. package/presentation/fieldRenderers/CmsMultiAssetPickerRenderer.d.ts +18 -0
  141. package/presentation/fieldRenderers/CmsMultiAssetPickerRenderer.js +88 -0
  142. package/presentation/fieldRenderers/CmsMultiAssetPickerRenderer.js.map +1 -0
  143. package/presentation/fieldRenderers/CmsMultiFilePickerRenderer.d.ts +13 -0
  144. package/presentation/fieldRenderers/CmsMultiFilePickerRenderer.js +60 -0
  145. package/presentation/fieldRenderers/CmsMultiFilePickerRenderer.js.map +1 -0
  146. package/presentation/fieldRenderers/CroppedAssetThumb.d.ts +15 -0
  147. package/presentation/fieldRenderers/CroppedAssetThumb.js +25 -0
  148. package/presentation/fieldRenderers/CroppedAssetThumb.js.map +1 -0
  149. package/presentation/fieldRenderers/assetValue.d.ts +10 -0
  150. package/presentation/fieldRenderers/assetValue.js +88 -0
  151. package/presentation/fieldRenderers/assetValue.js.map +1 -0
  152. package/presentation/hooks/useCmsImageEditor.d.ts +10 -0
  153. package/presentation/hooks/useCmsImageEditor.js +76 -0
  154. package/presentation/hooks/useCmsImageEditor.js.map +1 -0
  155. package/presentation/hooks/useEditImage.d.ts +14 -0
  156. package/presentation/hooks/useEditImage.js +67 -0
  157. package/presentation/hooks/useEditImage.js.map +1 -0
  158. package/routes.d.ts +1 -0
  159. package/routes.js +2 -1
  160. package/routes.js.map +1 -1
  161. package/modules/HeadlessCms/fileField.d.ts +0 -2
  162. package/modules/HeadlessCms/fileField.js +0 -44
  163. package/modules/HeadlessCms/fileField.js.map +0 -1
  164. package/modules/HeadlessCms/fileRenderer/fileField.d.ts +0 -2
  165. package/modules/HeadlessCms/fileRenderer/fileField.js +0 -53
  166. package/modules/HeadlessCms/fileRenderer/fileField.js.map +0 -1
  167. package/modules/HeadlessCms/fileRenderer/fileFields.d.ts +0 -2
  168. package/modules/HeadlessCms/fileRenderer/fileFields.js +0 -73
  169. package/modules/HeadlessCms/fileRenderer/fileFields.js.map +0 -1
  170. package/modules/HeadlessCms/fileRenderer/utils.d.ts +0 -2
  171. package/modules/HeadlessCms/fileRenderer/utils.js +0 -15
  172. package/modules/HeadlessCms/fileRenderer/utils.js.map +0 -1
  173. package/modules/HeadlessCms/index.d.ts +0 -1
  174. package/modules/HeadlessCms/index.js +0 -14
  175. package/modules/HeadlessCms/index.js.map +0 -1
  176. package/modules/Settings/graphql.d.ts +0 -12
  177. package/modules/Settings/graphql.js +0 -36
  178. package/modules/Settings/graphql.js.map +0 -1
  179. package/modules/Settings/views/FileManagerSettings.d.ts +0 -2
  180. package/modules/Settings/views/FileManagerSettings.js +0 -134
  181. package/modules/Settings/views/FileManagerSettings.js.map +0 -1
  182. package/presentation/FileList/components/BulkActions/BatchEditorDialog/AddOperation.d.ts +0 -7
  183. package/presentation/FileList/components/BulkActions/BatchEditorDialog/AddOperation.js +0 -15
  184. package/presentation/FileList/components/BulkActions/BatchEditorDialog/AddOperation.js.map +0 -1
  185. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditor.d.ts +0 -15
  186. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditor.js +0 -44
  187. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditor.js.map +0 -1
  188. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialog.d.ts +0 -15
  189. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialog.js +0 -52
  190. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialog.js.map +0 -1
  191. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.d.ts +0 -61
  192. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.js +0 -111
  193. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.js.map +0 -1
  194. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.test.d.ts +0 -1
  195. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.test.js +0 -372
  196. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.test.js.map +0 -1
  197. package/presentation/FileList/components/BulkActions/BatchEditorDialog/FieldRenderer.d.ts +0 -8
  198. package/presentation/FileList/components/BulkActions/BatchEditorDialog/FieldRenderer.js +0 -31
  199. package/presentation/FileList/components/BulkActions/BatchEditorDialog/FieldRenderer.js.map +0 -1
  200. package/presentation/FileList/components/BulkActions/BatchEditorDialog/Operation.d.ts +0 -11
  201. package/presentation/FileList/components/BulkActions/BatchEditorDialog/Operation.js +0 -33
  202. package/presentation/FileList/components/BulkActions/BatchEditorDialog/Operation.js.map +0 -1
  203. package/presentation/FileList/components/BulkActions/BatchEditorDialog/index.d.ts +0 -1
  204. package/presentation/FileList/components/BulkActions/BatchEditorDialog/index.js +0 -1
  205. package/presentation/FileList/components/BulkActions/BulkActionEdit.d.ts +0 -4
  206. package/presentation/FileList/components/BulkActions/BulkActionEdit.js +0 -105
  207. package/presentation/FileList/components/BulkActions/BulkActionEdit.js.map +0 -1
  208. package/presentation/config/configComponents/Browser/FolderFieldDecorator.d.ts +0 -7
  209. package/presentation/config/configComponents/Browser/FolderFieldDecorator.js +0 -11
  210. package/presentation/config/configComponents/Browser/FolderFieldDecorator.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/FileDetails/FileDetailsPresenter.test.js","sources":["../../../src/presentation/FileDetails/FileDetailsPresenter.test.ts"],"sourcesContent":["// @vitest-environment jsdom\nimport { describe, it, expect, vi, beforeEach } from \"vitest\";\nimport { Container } from \"@webiny/di\";\nimport { GetFileUseCase } from \"../../features/getFile/abstractions.js\";\nimport { UpdateFileUseCase } from \"../../features/updateFile/abstractions.js\";\nimport { DeleteFileUseCase } from \"../../features/deleteFile/abstractions.js\";\nimport { FormModelFactory } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { IFormModel } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { IFormVM } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport { FileManagerPermissions } from \"../../features/permissions/abstractions.js\";\nimport { GetSettingsRepository } from \"../../features/settings/abstractions.js\";\nimport { FileDetailsPresenter as Abstraction, type IFileDetailsPresenter } from \"./abstractions.js\";\nimport { FileDetailsPresenter } from \"./FileDetailsPresenter.js\";\nimport { WcpService } from \"@webiny/app-admin/features/wcp/abstractions.js\";\nimport type { FmFile } from \"../../features/shared/types.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction createMockFormModel(overrides?: Partial<IFormVM>): IFormModel {\n const vm: IFormVM = {\n layout: [],\n errors: [],\n isDirty: false,\n isValid: true,\n submitCount: 0,\n focusField: vi.fn(),\n getData: vi.fn(() => ({})),\n setData: vi.fn(),\n ...overrides\n };\n\n const data: Record<string, unknown> = {};\n\n return {\n field: vi.fn().mockReturnValue({ setDisabled: vi.fn() }) as any,\n fields: vi.fn() as any,\n layout: vi.fn() as any,\n getData: vi.fn(() => ({ ...data })),\n setData: vi.fn((incoming: Record<string, unknown>) => {\n Object.assign(data, incoming);\n }),\n reset: vi.fn(),\n validate: vi.fn().mockResolvedValue(true),\n submit: vi.fn().mockResolvedValue({ ...data }),\n get isDirty() {\n return vm.isDirty;\n },\n get isValid() {\n return vm.isValid;\n },\n get errors() {\n return vm.errors;\n },\n get vm() {\n return vm;\n }\n } as unknown as IFormModel;\n}\n\nfunction createMockFormModelFactory(): FormModelFactory.Interface {\n const mockForm = createMockFormModel();\n return {\n create: vi.fn().mockReturnValue(mockForm)\n };\n}\n\nfunction createMockGetFileUseCase(): GetFileUseCase.Interface {\n return {\n execute: vi.fn().mockResolvedValue({ success: true, file: createTestFile() })\n };\n}\n\nfunction createMockUpdateFileUseCase(): UpdateFileUseCase.Interface {\n return {\n execute: vi.fn().mockResolvedValue({ success: true, file: createTestFile() })\n };\n}\n\nfunction createMockDeleteFileUseCase(): DeleteFileUseCase.Interface {\n return {\n execute: vi.fn().mockResolvedValue({ success: true })\n };\n}\n\nfunction createMockPermissions(): FileManagerPermissions.Interface {\n return {\n canAccess: vi.fn().mockReturnValue(true),\n canRead: vi.fn().mockReturnValue(true),\n canCreate: vi.fn().mockReturnValue(true),\n canEdit: vi.fn().mockReturnValue(true),\n canDelete: vi.fn().mockReturnValue(false),\n canAction: vi.fn().mockReturnValue(true)\n } as unknown as FileManagerPermissions.Interface;\n}\n\nfunction createMockSettingsRepository(): GetSettingsRepository.Interface {\n return {\n execute: vi.fn().mockResolvedValue({\n uploadMinFileSize: \"0\",\n uploadMaxFileSize: \"10485760\",\n srcPrefix: \"https://cdn.example.com/\"\n }),\n save: vi.fn().mockImplementation(async (data: Record<string, unknown>) => data),\n settings: {\n uploadMinFileSize: \"0\",\n uploadMaxFileSize: \"10485760\",\n srcPrefix: \"https://cdn.example.com/\"\n }\n };\n}\n\nfunction createMockWcpService(canUsePrivateFiles = true): WcpService.Interface {\n return {\n getProject: vi.fn().mockReturnValue({\n canUsePrivateFiles: vi.fn().mockReturnValue(canUsePrivateFiles)\n }),\n isLoaded: vi.fn().mockReturnValue(true),\n canUseFeature: vi.fn().mockReturnValue(false),\n loadProject: vi.fn().mockResolvedValue(undefined)\n } as unknown as WcpService.Interface;\n}\n\nfunction createTestFile(overrides?: Partial<FmFile>): FmFile {\n return {\n id: \"file-1\",\n name: \"photo.jpg\",\n key: \"files/photo.jpg\",\n src: \"https://cdn.example.com/files/photo.jpg\",\n type: \"image/jpeg\",\n size: 12345,\n metadata: {},\n tags: [\"nature\", \"landscape\"],\n createdOn: \"2025-01-01T00:00:00Z\",\n savedOn: \"2025-01-01T00:00:00Z\",\n createdBy: { id: \"user-1\", displayName: \"Test User\", type: \"admin\" },\n savedBy: { id: \"user-1\", displayName: \"Test User\", type: \"admin\" },\n location: { folderId: \"root\" },\n accessControl: { type: \"public\" },\n ...overrides\n };\n}\n\n// ---------------------------------------------------------------------------\n// Container setup\n// ---------------------------------------------------------------------------\n\ninterface Mocks {\n getFileUseCase: GetFileUseCase.Interface;\n updateFileUseCase: UpdateFileUseCase.Interface;\n deleteFileUseCase: DeleteFileUseCase.Interface;\n formModelFactory: FormModelFactory.Interface;\n permissions: FileManagerPermissions.Interface;\n settingsRepository: GetSettingsRepository.Interface;\n wcpService: WcpService.Interface;\n}\n\nfunction createMocks(canUsePrivateFiles = true): Mocks {\n return {\n getFileUseCase: createMockGetFileUseCase(),\n updateFileUseCase: createMockUpdateFileUseCase(),\n deleteFileUseCase: createMockDeleteFileUseCase(),\n formModelFactory: createMockFormModelFactory(),\n permissions: createMockPermissions(),\n settingsRepository: createMockSettingsRepository(),\n wcpService: createMockWcpService(canUsePrivateFiles)\n };\n}\n\nfunction createContainer(mocks: Mocks) {\n const container = new Container();\n\n container.registerInstance(GetFileUseCase, mocks.getFileUseCase);\n container.registerInstance(UpdateFileUseCase, mocks.updateFileUseCase);\n container.registerInstance(DeleteFileUseCase, mocks.deleteFileUseCase);\n container.registerInstance(FormModelFactory, mocks.formModelFactory);\n container.registerInstance(FileManagerPermissions, mocks.permissions);\n container.registerInstance(GetSettingsRepository, mocks.settingsRepository);\n container.registerInstance(WcpService, mocks.wcpService);\n\n // Register the real FileDetailsPresenter implementation.\n container.register(FileDetailsPresenter).inSingletonScope();\n\n return container;\n}\n\n// ---------------------------------------------------------------------------\n// Tests\n// ---------------------------------------------------------------------------\n\ndescribe(\"FileDetailsPresenter\", () => {\n let mocks: Mocks;\n let presenter: IFileDetailsPresenter;\n\n beforeEach(() => {\n mocks = createMocks();\n const container = createContainer(mocks);\n presenter = container.resolve(Abstraction);\n });\n\n // -----------------------------------------------------------------------\n // Initial state.\n // -----------------------------------------------------------------------\n\n it(\"should have null file and loading=null before loadFile is called\", () => {\n expect(presenter.vm.file).toBeNull();\n expect(presenter.vm.loading).toBeNull();\n });\n\n it(\"should have a form vm with layout and isDirty=false initially\", () => {\n expect(presenter.vm.form).toBeDefined();\n expect(presenter.vm.form.layout).toBeDefined();\n expect(presenter.vm.form.isDirty).toBe(false);\n });\n\n it(\"should have null previewUrl when no file is loaded\", () => {\n expect(presenter.vm.previewUrl).toBeNull();\n });\n\n // -----------------------------------------------------------------------\n // loadFile populates vm with file data and FormModel.\n // -----------------------------------------------------------------------\n\n it(\"should populate vm.file after loadFile\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.file).not.toBeNull();\n expect(presenter.vm.file!.id).toBe(\"file-1\");\n expect(presenter.vm.file!.name).toBe(\"photo.jpg\");\n });\n\n it(\"should set loading=null after loadFile completes\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.loading).toBeNull();\n });\n\n it(\"should call GetFileUseCase.execute with the file id\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(mocks.getFileUseCase.execute).toHaveBeenCalledWith({ id: \"file-1\" });\n });\n\n it(\"should rebuild the form and call setData after loadFile\", async () => {\n await presenter.loadFile(\"file-1\");\n\n // FormModelFactory.create should be called twice: once in constructor, once in loadFile.\n expect(mocks.formModelFactory.create).toHaveBeenCalledTimes(2);\n });\n\n it(\"should have form.isDirty=false after loadFile\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.form.isDirty).toBe(false);\n });\n\n // -----------------------------------------------------------------------\n // Form submission triggers UpdateFileUseCase.\n // -----------------------------------------------------------------------\n\n it(\"should call UpdateFileUseCase.execute when saveFile is called\", async () => {\n await presenter.loadFile(\"file-1\");\n\n // Mock the form submit to return data.\n const mockForm = (mocks.formModelFactory.create as ReturnType<typeof vi.fn>).mock.results[1]\n .value as IFormModel;\n (mockForm.submit as ReturnType<typeof vi.fn>).mockResolvedValue({\n name: \"updated.jpg\",\n tags: [\"updated\"],\n accessControl: \"private-authenticated\"\n });\n\n await presenter.saveFile();\n\n expect(mocks.updateFileUseCase.execute).toHaveBeenCalledWith({\n id: \"file-1\",\n data: {\n name: \"updated.jpg\",\n tags: [\"updated\"],\n accessControl: { type: \"private-authenticated\" }\n }\n });\n });\n\n it(\"should not call UpdateFileUseCase when no file is loaded\", async () => {\n await presenter.saveFile();\n\n expect(mocks.updateFileUseCase.execute).not.toHaveBeenCalled();\n });\n\n it(\"should not call UpdateFileUseCase when form submit returns false\", async () => {\n await presenter.loadFile(\"file-1\");\n\n // Mock the form submit to return false (validation failure).\n const mockForm = (mocks.formModelFactory.create as ReturnType<typeof vi.fn>).mock.results[1]\n .value as IFormModel;\n (mockForm.submit as ReturnType<typeof vi.fn>).mockResolvedValue(false);\n\n await presenter.saveFile();\n\n expect(mocks.updateFileUseCase.execute).not.toHaveBeenCalled();\n });\n\n // -----------------------------------------------------------------------\n // Permission flags control action visibility.\n // -----------------------------------------------------------------------\n\n it(\"should expose permission flags in vm.permissions\", () => {\n expect(presenter.vm.permissions.canEdit).toBe(true);\n expect(presenter.vm.permissions.canDelete).toBe(false);\n });\n\n it(\"should reflect updated permission mock values\", () => {\n // Override permissions to both false.\n (mocks.permissions.canEdit as ReturnType<typeof vi.fn>).mockReturnValue(false);\n (mocks.permissions.canDelete as ReturnType<typeof vi.fn>).mockReturnValue(true);\n\n expect(presenter.vm.permissions.canEdit).toBe(false);\n expect(presenter.vm.permissions.canDelete).toBe(true);\n });\n\n // -----------------------------------------------------------------------\n // previewUrl construction from srcPrefix + key.\n // -----------------------------------------------------------------------\n\n it(\"should construct previewUrl from srcPrefix + file.key after loadFile\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.previewUrl).toBe(\"https://cdn.example.com/files/photo.jpg\");\n });\n\n it(\"should return null previewUrl when file has no key\", async () => {\n (mocks.getFileUseCase.execute as ReturnType<typeof vi.fn>).mockResolvedValue({\n success: true,\n file: createTestFile({ key: undefined })\n });\n\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.previewUrl).toBeNull();\n });\n\n // -----------------------------------------------------------------------\n // Loading state transitions.\n // -----------------------------------------------------------------------\n\n it(\"should set loading=true during loadFile and false after\", async () => {\n // Capture loading state during execution.\n let loadingDuringExecution: string | null = null;\n\n (mocks.getFileUseCase.execute as ReturnType<typeof vi.fn>).mockImplementation(async () => {\n loadingDuringExecution = presenter.vm.loading;\n return { success: true, file: createTestFile() };\n });\n\n await presenter.loadFile(\"file-1\");\n\n expect(loadingDuringExecution).toBe(\"Loading file...\");\n expect(presenter.vm.loading).toBeNull();\n });\n\n // -------------------------------------------------------------------\n // When Private Files is NOT licensed.\n // -------------------------------------------------------------------\n\n describe(\"when Private Files is NOT licensed\", () => {\n let unlicensedMocks: Mocks;\n let unlicensedPresenter: IFileDetailsPresenter;\n\n beforeEach(() => {\n unlicensedMocks = createMocks(false);\n const container = createContainer(unlicensedMocks);\n unlicensedPresenter = container.resolve(Abstraction);\n });\n\n it(\"should NOT include accessControl in the save payload\", async () => {\n await unlicensedPresenter.loadFile(\"file-1\");\n\n const mockForm = (unlicensedMocks.formModelFactory.create as ReturnType<typeof vi.fn>)\n .mock.results[1].value as IFormModel;\n (mockForm.submit as ReturnType<typeof vi.fn>).mockResolvedValue({\n name: \"updated.jpg\",\n description: \"\",\n tags: [\"updated\"]\n });\n\n await unlicensedPresenter.saveFile();\n\n const callArgs = (unlicensedMocks.updateFileUseCase.execute as ReturnType<typeof vi.fn>)\n .mock.calls[0][0];\n expect(callArgs.data).not.toHaveProperty(\"accessControl\");\n });\n\n it(\"should NOT set accessControl in form data during loadFile\", async () => {\n await unlicensedPresenter.loadFile(\"file-1\");\n\n const mockForm = (unlicensedMocks.formModelFactory.create as ReturnType<typeof vi.fn>)\n .mock.results[1].value as IFormModel;\n const setDataCall = (mockForm.setData as ReturnType<typeof vi.fn>).mock.calls[0][0];\n expect(setDataCall).not.toHaveProperty(\"accessControl\");\n });\n\n it(\"should NOT disable accessControl field when user lacks edit permission\", async () => {\n (unlicensedMocks.permissions.canEdit as ReturnType<typeof vi.fn>).mockReturnValue(\n false\n );\n\n await unlicensedPresenter.loadFile(\"file-1\");\n\n const mockForm = (unlicensedMocks.formModelFactory.create as ReturnType<typeof vi.fn>)\n .mock.results[1].value as IFormModel;\n expect(mockForm.field).toHaveBeenCalledTimes(3);\n expect(mockForm.field).not.toHaveBeenCalledWith(\"accessControl\");\n });\n });\n});\n"],"names":["createMockFormModel","overrides","vm","vi","data","incoming","Object","createMockFormModelFactory","mockForm","createMockGetFileUseCase","createTestFile","createMockUpdateFileUseCase","createMockDeleteFileUseCase","createMockPermissions","createMockSettingsRepository","createMockWcpService","canUsePrivateFiles","undefined","createMocks","createContainer","mocks","container","Container","GetFileUseCase","UpdateFileUseCase","DeleteFileUseCase","FormModelFactory","FileManagerPermissions","GetSettingsRepository","WcpService","FileDetailsPresenter","describe","presenter","beforeEach","Abstraction","it","expect","loadingDuringExecution","unlicensedMocks","unlicensedPresenter","callArgs","setDataCall"],"mappings":";;;;;;;;;;;AAoBA,SAASA,oBAAoBC,SAA4B;IACrD,MAAMC,KAAc;QAChB,QAAQ,EAAE;QACV,QAAQ,EAAE;QACV,SAAS;QACT,SAAS;QACT,aAAa;QACb,YAAYC,GAAG,EAAE;QACjB,SAASA,GAAG,EAAE,CAAC,IAAO,EAAC;QACvB,SAASA,GAAG,EAAE;QACd,GAAGF,SAAS;IAChB;IAEA,MAAMG,OAAgC,CAAC;IAEvC,OAAO;QACH,OAAOD,GAAG,EAAE,GAAG,eAAe,CAAC;YAAE,aAAaA,GAAG,EAAE;QAAG;QACtD,QAAQA,GAAG,EAAE;QACb,QAAQA,GAAG,EAAE;QACb,SAASA,GAAG,EAAE,CAAC,IAAO;gBAAE,GAAGC,IAAI;YAAC;QAChC,SAASD,GAAG,EAAE,CAAC,CAACE;YACZC,OAAO,MAAM,CAACF,MAAMC;QACxB;QACA,OAAOF,GAAG,EAAE;QACZ,UAAUA,GAAG,EAAE,GAAG,iBAAiB,CAAC;QACpC,QAAQA,GAAG,EAAE,GAAG,iBAAiB,CAAC;YAAE,GAAGC,IAAI;QAAC;QAC5C,IAAI;YACA,OAAOF,GAAG,OAAO;QACrB;QACA,IAAI;YACA,OAAOA,GAAG,OAAO;QACrB;QACA,IAAI;YACA,OAAOA,GAAG,MAAM;QACpB;QACA,IAAI;YACA,OAAOA;QACX;IACJ;AACJ;AAEA,SAASK;IACL,MAAMC,WAAWR;IACjB,OAAO;QACH,QAAQG,GAAG,EAAE,GAAG,eAAe,CAACK;IACpC;AACJ;AAEA,SAASC;IACL,OAAO;QACH,SAASN,GAAG,EAAE,GAAG,iBAAiB,CAAC;YAAE,SAAS;YAAM,MAAMO;QAAiB;IAC/E;AACJ;AAEA,SAASC;IACL,OAAO;QACH,SAASR,GAAG,EAAE,GAAG,iBAAiB,CAAC;YAAE,SAAS;YAAM,MAAMO;QAAiB;IAC/E;AACJ;AAEA,SAASE;IACL,OAAO;QACH,SAAST,GAAG,EAAE,GAAG,iBAAiB,CAAC;YAAE,SAAS;QAAK;IACvD;AACJ;AAEA,SAASU;IACL,OAAO;QACH,WAAWV,GAAG,EAAE,GAAG,eAAe,CAAC;QACnC,SAASA,GAAG,EAAE,GAAG,eAAe,CAAC;QACjC,WAAWA,GAAG,EAAE,GAAG,eAAe,CAAC;QACnC,SAASA,GAAG,EAAE,GAAG,eAAe,CAAC;QACjC,WAAWA,GAAG,EAAE,GAAG,eAAe,CAAC;QACnC,WAAWA,GAAG,EAAE,GAAG,eAAe,CAAC;IACvC;AACJ;AAEA,SAASW;IACL,OAAO;QACH,SAASX,GAAG,EAAE,GAAG,iBAAiB,CAAC;YAC/B,mBAAmB;YACnB,mBAAmB;YACnB,WAAW;QACf;QACA,MAAMA,GAAG,EAAE,GAAG,kBAAkB,CAAC,OAAOC,OAAkCA;QAC1E,UAAU;YACN,mBAAmB;YACnB,mBAAmB;YACnB,WAAW;QACf;IACJ;AACJ;AAEA,SAASW,qBAAqBC,qBAAqB,IAAI;IACnD,OAAO;QACH,YAAYb,GAAG,EAAE,GAAG,eAAe,CAAC;YAChC,oBAAoBA,GAAG,EAAE,GAAG,eAAe,CAACa;QAChD;QACA,UAAUb,GAAG,EAAE,GAAG,eAAe,CAAC;QAClC,eAAeA,GAAG,EAAE,GAAG,eAAe,CAAC;QACvC,aAAaA,GAAG,EAAE,GAAG,iBAAiB,CAACc;IAC3C;AACJ;AAEA,SAASP,eAAeT,SAA2B;IAC/C,OAAO;QACH,IAAI;QACJ,MAAM;QACN,KAAK;QACL,KAAK;QACL,MAAM;QACN,MAAM;QACN,UAAU,CAAC;QACX,MAAM;YAAC;YAAU;SAAY;QAC7B,WAAW;QACX,SAAS;QACT,WAAW;YAAE,IAAI;YAAU,aAAa;YAAa,MAAM;QAAQ;QACnE,SAAS;YAAE,IAAI;YAAU,aAAa;YAAa,MAAM;QAAQ;QACjE,UAAU;YAAE,UAAU;QAAO;QAC7B,eAAe;YAAE,MAAM;QAAS;QAChC,GAAGA,SAAS;IAChB;AACJ;AAgBA,SAASiB,YAAYF,qBAAqB,IAAI;IAC1C,OAAO;QACH,gBAAgBP;QAChB,mBAAmBE;QACnB,mBAAmBC;QACnB,kBAAkBL;QAClB,aAAaM;QACb,oBAAoBC;QACpB,YAAYC,qBAAqBC;IACrC;AACJ;AAEA,SAASG,gBAAgBC,KAAY;IACjC,MAAMC,YAAY,IAAIC;IAEtBD,UAAU,gBAAgB,CAACE,gBAAgBH,MAAM,cAAc;IAC/DC,UAAU,gBAAgB,CAACG,mBAAmBJ,MAAM,iBAAiB;IACrEC,UAAU,gBAAgB,CAACI,mBAAmBL,MAAM,iBAAiB;IACrEC,UAAU,gBAAgB,CAACK,kBAAkBN,MAAM,gBAAgB;IACnEC,UAAU,gBAAgB,CAACM,wBAAwBP,MAAM,WAAW;IACpEC,UAAU,gBAAgB,CAACO,uBAAuBR,MAAM,kBAAkB;IAC1EC,UAAU,gBAAgB,CAACQ,YAAYT,MAAM,UAAU;IAGvDC,UAAU,QAAQ,CAACS,uDAAsB,gBAAgB;IAEzD,OAAOT;AACX;AAMAU,SAAS,wBAAwB;IAC7B,IAAIX;IACJ,IAAIY;IAEJC,WAAW;QACPb,QAAQF;QACR,MAAMG,YAAYF,gBAAgBC;QAClCY,YAAYX,UAAU,OAAO,CAACa;IAClC;IAMAC,GAAG,oEAAoE;QACnEC,OAAOJ,UAAU,EAAE,CAAC,IAAI,EAAE,QAAQ;QAClCI,OAAOJ,UAAU,EAAE,CAAC,OAAO,EAAE,QAAQ;IACzC;IAEAG,GAAG,iEAAiE;QAChEC,OAAOJ,UAAU,EAAE,CAAC,IAAI,EAAE,WAAW;QACrCI,OAAOJ,UAAU,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW;QAC5CI,OAAOJ,UAAU,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC;IAC3C;IAEAG,GAAG,sDAAsD;QACrDC,OAAOJ,UAAU,EAAE,CAAC,UAAU,EAAE,QAAQ;IAC5C;IAMAG,GAAG,0CAA0C;QACzC,MAAMH,UAAU,QAAQ,CAAC;QAEzBI,OAAOJ,UAAU,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC,QAAQ;QACtCI,OAAOJ,UAAU,EAAE,CAAC,IAAI,CAAE,EAAE,EAAE,IAAI,CAAC;QACnCI,OAAOJ,UAAU,EAAE,CAAC,IAAI,CAAE,IAAI,EAAE,IAAI,CAAC;IACzC;IAEAG,GAAG,oDAAoD;QACnD,MAAMH,UAAU,QAAQ,CAAC;QAEzBI,OAAOJ,UAAU,EAAE,CAAC,OAAO,EAAE,QAAQ;IACzC;IAEAG,GAAG,uDAAuD;QACtD,MAAMH,UAAU,QAAQ,CAAC;QAEzBI,OAAOhB,MAAM,cAAc,CAAC,OAAO,EAAE,oBAAoB,CAAC;YAAE,IAAI;QAAS;IAC7E;IAEAe,GAAG,2DAA2D;QAC1D,MAAMH,UAAU,QAAQ,CAAC;QAGzBI,OAAOhB,MAAM,gBAAgB,CAAC,MAAM,EAAE,qBAAqB,CAAC;IAChE;IAEAe,GAAG,iDAAiD;QAChD,MAAMH,UAAU,QAAQ,CAAC;QAEzBI,OAAOJ,UAAU,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC;IAC3C;IAMAG,GAAG,iEAAiE;QAChE,MAAMH,UAAU,QAAQ,CAAC;QAGzB,MAAMxB,WAAYY,MAAM,gBAAgB,CAAC,MAAM,CAA8B,IAAI,CAAC,OAAO,CAAC,EAAE,CACvF,KAAK;QACTZ,SAAS,MAAM,CAA8B,iBAAiB,CAAC;YAC5D,MAAM;YACN,MAAM;gBAAC;aAAU;YACjB,eAAe;QACnB;QAEA,MAAMwB,UAAU,QAAQ;QAExBI,OAAOhB,MAAM,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,CAAC;YACzD,IAAI;YACJ,MAAM;gBACF,MAAM;gBACN,MAAM;oBAAC;iBAAU;gBACjB,eAAe;oBAAE,MAAM;gBAAwB;YACnD;QACJ;IACJ;IAEAe,GAAG,4DAA4D;QAC3D,MAAMH,UAAU,QAAQ;QAExBI,OAAOhB,MAAM,iBAAiB,CAAC,OAAO,EAAE,GAAG,CAAC,gBAAgB;IAChE;IAEAe,GAAG,oEAAoE;QACnE,MAAMH,UAAU,QAAQ,CAAC;QAGzB,MAAMxB,WAAYY,MAAM,gBAAgB,CAAC,MAAM,CAA8B,IAAI,CAAC,OAAO,CAAC,EAAE,CACvF,KAAK;QACTZ,SAAS,MAAM,CAA8B,iBAAiB,CAAC;QAEhE,MAAMwB,UAAU,QAAQ;QAExBI,OAAOhB,MAAM,iBAAiB,CAAC,OAAO,EAAE,GAAG,CAAC,gBAAgB;IAChE;IAMAe,GAAG,oDAAoD;QACnDC,OAAOJ,UAAU,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC;QAC9CI,OAAOJ,UAAU,EAAE,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC;IACpD;IAEAG,GAAG,iDAAiD;QAE/Cf,MAAM,WAAW,CAAC,OAAO,CAA8B,eAAe,CAAC;QACvEA,MAAM,WAAW,CAAC,SAAS,CAA8B,eAAe,CAAC;QAE1EgB,OAAOJ,UAAU,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC;QAC9CI,OAAOJ,UAAU,EAAE,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC;IACpD;IAMAG,GAAG,wEAAwE;QACvE,MAAMH,UAAU,QAAQ,CAAC;QAEzBI,OAAOJ,UAAU,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC;IACzC;IAEAG,GAAG,sDAAsD;QACpDf,MAAM,cAAc,CAAC,OAAO,CAA8B,iBAAiB,CAAC;YACzE,SAAS;YACT,MAAMV,eAAe;gBAAE,KAAKO;YAAU;QAC1C;QAEA,MAAMe,UAAU,QAAQ,CAAC;QAEzBI,OAAOJ,UAAU,EAAE,CAAC,UAAU,EAAE,QAAQ;IAC5C;IAMAG,GAAG,2DAA2D;QAE1D,IAAIE,yBAAwC;QAE3CjB,MAAM,cAAc,CAAC,OAAO,CAA8B,kBAAkB,CAAC;YAC1EiB,yBAAyBL,UAAU,EAAE,CAAC,OAAO;YAC7C,OAAO;gBAAE,SAAS;gBAAM,MAAMtB;YAAiB;QACnD;QAEA,MAAMsB,UAAU,QAAQ,CAAC;QAEzBI,OAAOC,wBAAwB,IAAI,CAAC;QACpCD,OAAOJ,UAAU,EAAE,CAAC,OAAO,EAAE,QAAQ;IACzC;IAMAD,SAAS,sCAAsC;QAC3C,IAAIO;QACJ,IAAIC;QAEJN,WAAW;YACPK,kBAAkBpB,YAAY;YAC9B,MAAMG,YAAYF,gBAAgBmB;YAClCC,sBAAsBlB,UAAU,OAAO,CAACa;QAC5C;QAEAC,GAAG,wDAAwD;YACvD,MAAMI,oBAAoB,QAAQ,CAAC;YAEnC,MAAM/B,WAAY8B,gBAAgB,gBAAgB,CAAC,MAAM,CACpD,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK;YACzB9B,SAAS,MAAM,CAA8B,iBAAiB,CAAC;gBAC5D,MAAM;gBACN,aAAa;gBACb,MAAM;oBAAC;iBAAU;YACrB;YAEA,MAAM+B,oBAAoB,QAAQ;YAElC,MAAMC,WAAYF,gBAAgB,iBAAiB,CAAC,OAAO,CACtD,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE;YACrBF,OAAOI,SAAS,IAAI,EAAE,GAAG,CAAC,cAAc,CAAC;QAC7C;QAEAL,GAAG,6DAA6D;YAC5D,MAAMI,oBAAoB,QAAQ,CAAC;YAEnC,MAAM/B,WAAY8B,gBAAgB,gBAAgB,CAAC,MAAM,CACpD,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK;YAC1B,MAAMG,cAAejC,SAAS,OAAO,CAA8B,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE;YACnF4B,OAAOK,aAAa,GAAG,CAAC,cAAc,CAAC;QAC3C;QAEAN,GAAG,0EAA0E;YACxEG,gBAAgB,WAAW,CAAC,OAAO,CAA8B,eAAe,CAC7E;YAGJ,MAAMC,oBAAoB,QAAQ,CAAC;YAEnC,MAAM/B,WAAY8B,gBAAgB,gBAAgB,CAAC,MAAM,CACpD,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK;YAC1BF,OAAO5B,SAAS,KAAK,EAAE,qBAAqB,CAAC;YAC7C4B,OAAO5B,SAAS,KAAK,EAAE,GAAG,CAAC,oBAAoB,CAAC;QACpD;IACJ;AACJ"}
1
+ {"version":3,"file":"presentation/FileDetails/FileDetailsPresenter.test.js","sources":["../../../src/presentation/FileDetails/FileDetailsPresenter.test.ts"],"sourcesContent":["// @vitest-environment jsdom\nimport { describe, it, expect, vi, beforeEach } from \"vitest\";\nimport { Container } from \"@webiny/di\";\nimport { GetFileUseCase } from \"../../features/getFile/abstractions.js\";\nimport { UpdateFileUseCase } from \"../../features/updateFile/abstractions.js\";\nimport { DeleteFileUseCase } from \"../../features/deleteFile/abstractions.js\";\nimport { FormModelFactory } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { IFormModel } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { IFormVM } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport { FileManagerPermissions } from \"../../features/permissions/abstractions.js\";\nimport { GetSettingsRepository } from \"../../features/settings/abstractions.js\";\nimport { FileDetailsPresenter as Abstraction, type IFileDetailsPresenter } from \"./abstractions.js\";\nimport { FileDetailsPresenter } from \"./FileDetailsPresenter.js\";\nimport { WcpService } from \"@webiny/app-admin/features/wcp/abstractions.js\";\nimport type { FmFile } from \"../../features/shared/types.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction createMockFormModel(overrides?: Partial<IFormVM>): IFormModel {\n const vm: IFormVM = {\n layout: [],\n errors: [],\n hasErrors: false,\n isDirty: false,\n isValid: true,\n submitCount: 0,\n focusField: vi.fn(),\n getData: vi.fn(() => ({})),\n setData: vi.fn(),\n ...overrides\n };\n\n const data: Record<string, unknown> = {};\n\n return {\n field: vi.fn().mockReturnValue({ setDisabled: vi.fn() }) as any,\n fields: vi.fn() as any,\n layout: vi.fn() as any,\n getData: vi.fn(() => ({ ...data })),\n setData: vi.fn((incoming: Record<string, unknown>) => {\n Object.assign(data, incoming);\n }),\n reset: vi.fn(),\n validate: vi.fn().mockResolvedValue(true),\n submit: vi.fn().mockResolvedValue({ ...data }),\n get isDirty() {\n return vm.isDirty;\n },\n get isValid() {\n return vm.isValid;\n },\n get errors() {\n return vm.errors;\n },\n get vm() {\n return vm;\n }\n } as unknown as IFormModel;\n}\n\nfunction createMockFormModelFactory(): FormModelFactory.Interface {\n const mockForm = createMockFormModel();\n return {\n create: vi.fn().mockReturnValue(mockForm)\n };\n}\n\nfunction createMockGetFileUseCase(): GetFileUseCase.Interface {\n return {\n execute: vi.fn().mockResolvedValue({ success: true, file: createTestFile() })\n };\n}\n\nfunction createMockUpdateFileUseCase(): UpdateFileUseCase.Interface {\n return {\n execute: vi.fn().mockResolvedValue({ success: true, file: createTestFile() })\n };\n}\n\nfunction createMockDeleteFileUseCase(): DeleteFileUseCase.Interface {\n return {\n execute: vi.fn().mockResolvedValue({ success: true })\n };\n}\n\nfunction createMockPermissions(): FileManagerPermissions.Interface {\n return {\n canAccess: vi.fn().mockReturnValue(true),\n canRead: vi.fn().mockReturnValue(true),\n canCreate: vi.fn().mockReturnValue(true),\n canEdit: vi.fn().mockReturnValue(true),\n canDelete: vi.fn().mockReturnValue(false),\n canAction: vi.fn().mockReturnValue(true)\n } as unknown as FileManagerPermissions.Interface;\n}\n\nfunction createMockSettingsRepository(): GetSettingsRepository.Interface {\n return {\n execute: vi.fn().mockResolvedValue({\n uploadMinFileSize: \"0\",\n uploadMaxFileSize: \"10485760\",\n srcPrefix: \"https://cdn.example.com/\"\n }),\n save: vi.fn().mockImplementation(async (data: Record<string, unknown>) => data),\n settings: {\n uploadMinFileSize: \"0\",\n uploadMaxFileSize: \"10485760\",\n srcPrefix: \"https://cdn.example.com/\"\n }\n };\n}\n\nfunction createMockWcpService(canUsePrivateFiles = true): WcpService.Interface {\n return {\n getProject: vi.fn().mockReturnValue({\n canUsePrivateFiles: vi.fn().mockReturnValue(canUsePrivateFiles)\n }),\n isLoaded: vi.fn().mockReturnValue(true),\n canUseFeature: vi.fn().mockReturnValue(false),\n loadProject: vi.fn().mockResolvedValue(undefined)\n } as unknown as WcpService.Interface;\n}\n\nfunction createTestFile(overrides?: Partial<FmFile>): FmFile {\n return {\n id: \"file-1\",\n name: \"photo.jpg\",\n key: \"files/photo.jpg\",\n src: \"https://cdn.example.com/files/photo.jpg\",\n type: \"image/jpeg\",\n size: 12345,\n metadata: {},\n tags: [\"nature\", \"landscape\"],\n createdOn: \"2025-01-01T00:00:00Z\",\n savedOn: \"2025-01-01T00:00:00Z\",\n createdBy: { id: \"user-1\", displayName: \"Test User\", type: \"admin\" },\n savedBy: { id: \"user-1\", displayName: \"Test User\", type: \"admin\" },\n location: { folderId: \"root\" },\n accessControl: { type: \"public\" },\n ...overrides\n };\n}\n\n// ---------------------------------------------------------------------------\n// Container setup\n// ---------------------------------------------------------------------------\n\ninterface Mocks {\n getFileUseCase: GetFileUseCase.Interface;\n updateFileUseCase: UpdateFileUseCase.Interface;\n deleteFileUseCase: DeleteFileUseCase.Interface;\n formModelFactory: FormModelFactory.Interface;\n permissions: FileManagerPermissions.Interface;\n settingsRepository: GetSettingsRepository.Interface;\n wcpService: WcpService.Interface;\n}\n\nfunction createMocks(canUsePrivateFiles = true): Mocks {\n return {\n getFileUseCase: createMockGetFileUseCase(),\n updateFileUseCase: createMockUpdateFileUseCase(),\n deleteFileUseCase: createMockDeleteFileUseCase(),\n formModelFactory: createMockFormModelFactory(),\n permissions: createMockPermissions(),\n settingsRepository: createMockSettingsRepository(),\n wcpService: createMockWcpService(canUsePrivateFiles)\n };\n}\n\nfunction createContainer(mocks: Mocks) {\n const container = new Container();\n\n container.registerInstance(GetFileUseCase, mocks.getFileUseCase);\n container.registerInstance(UpdateFileUseCase, mocks.updateFileUseCase);\n container.registerInstance(DeleteFileUseCase, mocks.deleteFileUseCase);\n container.registerInstance(FormModelFactory, mocks.formModelFactory);\n container.registerInstance(FileManagerPermissions, mocks.permissions);\n container.registerInstance(GetSettingsRepository, mocks.settingsRepository);\n container.registerInstance(WcpService, mocks.wcpService);\n\n // Register the real FileDetailsPresenter implementation.\n container.register(FileDetailsPresenter).inSingletonScope();\n\n return container;\n}\n\n// ---------------------------------------------------------------------------\n// Tests\n// ---------------------------------------------------------------------------\n\ndescribe(\"FileDetailsPresenter\", () => {\n let mocks: Mocks;\n let presenter: IFileDetailsPresenter;\n\n beforeEach(() => {\n mocks = createMocks();\n const container = createContainer(mocks);\n presenter = container.resolve(Abstraction);\n });\n\n // -----------------------------------------------------------------------\n // Initial state.\n // -----------------------------------------------------------------------\n\n it(\"should have null file and loading=null before loadFile is called\", () => {\n expect(presenter.vm.file).toBeNull();\n expect(presenter.vm.loading).toBeNull();\n });\n\n it(\"should have a form vm with layout and isDirty=false initially\", () => {\n expect(presenter.vm.form).toBeDefined();\n expect(presenter.vm.form.layout).toBeDefined();\n expect(presenter.vm.form.isDirty).toBe(false);\n });\n\n it(\"should have null previewUrl when no file is loaded\", () => {\n expect(presenter.vm.previewUrl).toBeNull();\n });\n\n // -----------------------------------------------------------------------\n // loadFile populates vm with file data and FormModel.\n // -----------------------------------------------------------------------\n\n it(\"should populate vm.file after loadFile\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.file).not.toBeNull();\n expect(presenter.vm.file!.id).toBe(\"file-1\");\n expect(presenter.vm.file!.name).toBe(\"photo.jpg\");\n });\n\n it(\"should set loading=null after loadFile completes\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.loading).toBeNull();\n });\n\n it(\"should call GetFileUseCase.execute with the file id\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(mocks.getFileUseCase.execute).toHaveBeenCalledWith({ id: \"file-1\" });\n });\n\n it(\"should rebuild the form and call setData after loadFile\", async () => {\n await presenter.loadFile(\"file-1\");\n\n // FormModelFactory.create should be called twice: once in constructor, once in loadFile.\n expect(mocks.formModelFactory.create).toHaveBeenCalledTimes(2);\n });\n\n it(\"should have form.isDirty=false after loadFile\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.form.isDirty).toBe(false);\n });\n\n // -----------------------------------------------------------------------\n // Form submission triggers UpdateFileUseCase.\n // -----------------------------------------------------------------------\n\n it(\"should call UpdateFileUseCase.execute when saveFile is called\", async () => {\n await presenter.loadFile(\"file-1\");\n\n // Mock the form submit to return data.\n const mockForm = (mocks.formModelFactory.create as ReturnType<typeof vi.fn>).mock.results[1]\n .value as IFormModel;\n (mockForm.submit as ReturnType<typeof vi.fn>).mockResolvedValue({\n name: \"updated.jpg\",\n tags: [\"updated\"],\n accessControl: \"private-authenticated\"\n });\n\n await presenter.saveFile();\n\n expect(mocks.updateFileUseCase.execute).toHaveBeenCalledWith({\n id: \"file-1\",\n data: {\n name: \"updated.jpg\",\n tags: [\"updated\"],\n accessControl: { type: \"private-authenticated\" }\n }\n });\n });\n\n it(\"should not call UpdateFileUseCase when no file is loaded\", async () => {\n await presenter.saveFile();\n\n expect(mocks.updateFileUseCase.execute).not.toHaveBeenCalled();\n });\n\n it(\"should not call UpdateFileUseCase when form submit returns false\", async () => {\n await presenter.loadFile(\"file-1\");\n\n // Mock the form submit to return false (validation failure).\n const mockForm = (mocks.formModelFactory.create as ReturnType<typeof vi.fn>).mock.results[1]\n .value as IFormModel;\n (mockForm.submit as ReturnType<typeof vi.fn>).mockResolvedValue(false);\n\n await presenter.saveFile();\n\n expect(mocks.updateFileUseCase.execute).not.toHaveBeenCalled();\n });\n\n // -----------------------------------------------------------------------\n // Permission flags control action visibility.\n // -----------------------------------------------------------------------\n\n it(\"should expose permission flags in vm.permissions\", () => {\n expect(presenter.vm.permissions.canEdit).toBe(true);\n expect(presenter.vm.permissions.canDelete).toBe(false);\n });\n\n it(\"should reflect updated permission mock values\", () => {\n // Override permissions to both false.\n (mocks.permissions.canEdit as ReturnType<typeof vi.fn>).mockReturnValue(false);\n (mocks.permissions.canDelete as ReturnType<typeof vi.fn>).mockReturnValue(true);\n\n expect(presenter.vm.permissions.canEdit).toBe(false);\n expect(presenter.vm.permissions.canDelete).toBe(true);\n });\n\n // -----------------------------------------------------------------------\n // previewUrl construction from srcPrefix + key.\n // -----------------------------------------------------------------------\n\n it(\"should construct previewUrl from srcPrefix + file.key after loadFile\", async () => {\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.previewUrl).toBe(\"https://cdn.example.com/files/photo.jpg\");\n });\n\n it(\"should return null previewUrl when file has no key\", async () => {\n (mocks.getFileUseCase.execute as ReturnType<typeof vi.fn>).mockResolvedValue({\n success: true,\n file: createTestFile({ key: undefined })\n });\n\n await presenter.loadFile(\"file-1\");\n\n expect(presenter.vm.previewUrl).toBeNull();\n });\n\n // -----------------------------------------------------------------------\n // Loading state transitions.\n // -----------------------------------------------------------------------\n\n it(\"should set loading=true during loadFile and false after\", async () => {\n // Capture loading state during execution.\n let loadingDuringExecution: string | null = null;\n\n (mocks.getFileUseCase.execute as ReturnType<typeof vi.fn>).mockImplementation(async () => {\n loadingDuringExecution = presenter.vm.loading;\n return { success: true, file: createTestFile() };\n });\n\n await presenter.loadFile(\"file-1\");\n\n expect(loadingDuringExecution).toBe(\"Loading file...\");\n expect(presenter.vm.loading).toBeNull();\n });\n\n // -------------------------------------------------------------------\n // When Private Files is NOT licensed.\n // -------------------------------------------------------------------\n\n describe(\"when Private Files is NOT licensed\", () => {\n let unlicensedMocks: Mocks;\n let unlicensedPresenter: IFileDetailsPresenter;\n\n beforeEach(() => {\n unlicensedMocks = createMocks(false);\n const container = createContainer(unlicensedMocks);\n unlicensedPresenter = container.resolve(Abstraction);\n });\n\n it(\"should NOT include accessControl in the save payload\", async () => {\n await unlicensedPresenter.loadFile(\"file-1\");\n\n const mockForm = (unlicensedMocks.formModelFactory.create as ReturnType<typeof vi.fn>)\n .mock.results[1].value as IFormModel;\n (mockForm.submit as ReturnType<typeof vi.fn>).mockResolvedValue({\n name: \"updated.jpg\",\n description: \"\",\n tags: [\"updated\"]\n });\n\n await unlicensedPresenter.saveFile();\n\n const callArgs = (unlicensedMocks.updateFileUseCase.execute as ReturnType<typeof vi.fn>)\n .mock.calls[0][0];\n expect(callArgs.data).not.toHaveProperty(\"accessControl\");\n });\n\n it(\"should NOT set accessControl in form data during loadFile\", async () => {\n await unlicensedPresenter.loadFile(\"file-1\");\n\n const mockForm = (unlicensedMocks.formModelFactory.create as ReturnType<typeof vi.fn>)\n .mock.results[1].value as IFormModel;\n const setDataCall = (mockForm.setData as ReturnType<typeof vi.fn>).mock.calls[0][0];\n expect(setDataCall).not.toHaveProperty(\"accessControl\");\n });\n\n it(\"should NOT disable accessControl field when user lacks edit permission\", async () => {\n (unlicensedMocks.permissions.canEdit as ReturnType<typeof vi.fn>).mockReturnValue(\n false\n );\n\n await unlicensedPresenter.loadFile(\"file-1\");\n\n const mockForm = (unlicensedMocks.formModelFactory.create as ReturnType<typeof vi.fn>)\n .mock.results[1].value as IFormModel;\n expect(mockForm.field).toHaveBeenCalledTimes(3);\n expect(mockForm.field).not.toHaveBeenCalledWith(\"accessControl\");\n });\n });\n});\n"],"names":["createMockFormModel","overrides","vm","vi","data","incoming","Object","createMockFormModelFactory","mockForm","createMockGetFileUseCase","createTestFile","createMockUpdateFileUseCase","createMockDeleteFileUseCase","createMockPermissions","createMockSettingsRepository","createMockWcpService","canUsePrivateFiles","undefined","createMocks","createContainer","mocks","container","Container","GetFileUseCase","UpdateFileUseCase","DeleteFileUseCase","FormModelFactory","FileManagerPermissions","GetSettingsRepository","WcpService","FileDetailsPresenter","describe","presenter","beforeEach","Abstraction","it","expect","loadingDuringExecution","unlicensedMocks","unlicensedPresenter","callArgs","setDataCall"],"mappings":";;;;;;;;;;;AAoBA,SAASA,oBAAoBC,SAA4B;IACrD,MAAMC,KAAc;QAChB,QAAQ,EAAE;QACV,QAAQ,EAAE;QACV,WAAW;QACX,SAAS;QACT,SAAS;QACT,aAAa;QACb,YAAYC,GAAG,EAAE;QACjB,SAASA,GAAG,EAAE,CAAC,IAAO,EAAC;QACvB,SAASA,GAAG,EAAE;QACd,GAAGF,SAAS;IAChB;IAEA,MAAMG,OAAgC,CAAC;IAEvC,OAAO;QACH,OAAOD,GAAG,EAAE,GAAG,eAAe,CAAC;YAAE,aAAaA,GAAG,EAAE;QAAG;QACtD,QAAQA,GAAG,EAAE;QACb,QAAQA,GAAG,EAAE;QACb,SAASA,GAAG,EAAE,CAAC,IAAO;gBAAE,GAAGC,IAAI;YAAC;QAChC,SAASD,GAAG,EAAE,CAAC,CAACE;YACZC,OAAO,MAAM,CAACF,MAAMC;QACxB;QACA,OAAOF,GAAG,EAAE;QACZ,UAAUA,GAAG,EAAE,GAAG,iBAAiB,CAAC;QACpC,QAAQA,GAAG,EAAE,GAAG,iBAAiB,CAAC;YAAE,GAAGC,IAAI;QAAC;QAC5C,IAAI,WAAU;YACV,OAAOF,GAAG,OAAO;QACrB;QACA,IAAI,WAAU;YACV,OAAOA,GAAG,OAAO;QACrB;QACA,IAAI,UAAS;YACT,OAAOA,GAAG,MAAM;QACpB;QACA,IAAI,MAAK;YACL,OAAOA;QACX;IACJ;AACJ;AAEA,SAASK;IACL,MAAMC,WAAWR;IACjB,OAAO;QACH,QAAQG,GAAG,EAAE,GAAG,eAAe,CAACK;IACpC;AACJ;AAEA,SAASC;IACL,OAAO;QACH,SAASN,GAAG,EAAE,GAAG,iBAAiB,CAAC;YAAE,SAAS;YAAM,MAAMO;QAAiB;IAC/E;AACJ;AAEA,SAASC;IACL,OAAO;QACH,SAASR,GAAG,EAAE,GAAG,iBAAiB,CAAC;YAAE,SAAS;YAAM,MAAMO;QAAiB;IAC/E;AACJ;AAEA,SAASE;IACL,OAAO;QACH,SAAST,GAAG,EAAE,GAAG,iBAAiB,CAAC;YAAE,SAAS;QAAK;IACvD;AACJ;AAEA,SAASU;IACL,OAAO;QACH,WAAWV,GAAG,EAAE,GAAG,eAAe,CAAC;QACnC,SAASA,GAAG,EAAE,GAAG,eAAe,CAAC;QACjC,WAAWA,GAAG,EAAE,GAAG,eAAe,CAAC;QACnC,SAASA,GAAG,EAAE,GAAG,eAAe,CAAC;QACjC,WAAWA,GAAG,EAAE,GAAG,eAAe,CAAC;QACnC,WAAWA,GAAG,EAAE,GAAG,eAAe,CAAC;IACvC;AACJ;AAEA,SAASW;IACL,OAAO;QACH,SAASX,GAAG,EAAE,GAAG,iBAAiB,CAAC;YAC/B,mBAAmB;YACnB,mBAAmB;YACnB,WAAW;QACf;QACA,MAAMA,GAAG,EAAE,GAAG,kBAAkB,CAAC,OAAOC,OAAkCA;QAC1E,UAAU;YACN,mBAAmB;YACnB,mBAAmB;YACnB,WAAW;QACf;IACJ;AACJ;AAEA,SAASW,qBAAqBC,qBAAqB,IAAI;IACnD,OAAO;QACH,YAAYb,GAAG,EAAE,GAAG,eAAe,CAAC;YAChC,oBAAoBA,GAAG,EAAE,GAAG,eAAe,CAACa;QAChD;QACA,UAAUb,GAAG,EAAE,GAAG,eAAe,CAAC;QAClC,eAAeA,GAAG,EAAE,GAAG,eAAe,CAAC;QACvC,aAAaA,GAAG,EAAE,GAAG,iBAAiB,CAACc;IAC3C;AACJ;AAEA,SAASP,eAAeT,SAA2B;IAC/C,OAAO;QACH,IAAI;QACJ,MAAM;QACN,KAAK;QACL,KAAK;QACL,MAAM;QACN,MAAM;QACN,UAAU,CAAC;QACX,MAAM;YAAC;YAAU;SAAY;QAC7B,WAAW;QACX,SAAS;QACT,WAAW;YAAE,IAAI;YAAU,aAAa;YAAa,MAAM;QAAQ;QACnE,SAAS;YAAE,IAAI;YAAU,aAAa;YAAa,MAAM;QAAQ;QACjE,UAAU;YAAE,UAAU;QAAO;QAC7B,eAAe;YAAE,MAAM;QAAS;QAChC,GAAGA,SAAS;IAChB;AACJ;AAgBA,SAASiB,YAAYF,qBAAqB,IAAI;IAC1C,OAAO;QACH,gBAAgBP;QAChB,mBAAmBE;QACnB,mBAAmBC;QACnB,kBAAkBL;QAClB,aAAaM;QACb,oBAAoBC;QACpB,YAAYC,qBAAqBC;IACrC;AACJ;AAEA,SAASG,gBAAgBC,KAAY;IACjC,MAAMC,YAAY,IAAIC;IAEtBD,UAAU,gBAAgB,CAACE,gBAAgBH,MAAM,cAAc;IAC/DC,UAAU,gBAAgB,CAACG,mBAAmBJ,MAAM,iBAAiB;IACrEC,UAAU,gBAAgB,CAACI,mBAAmBL,MAAM,iBAAiB;IACrEC,UAAU,gBAAgB,CAACK,kBAAkBN,MAAM,gBAAgB;IACnEC,UAAU,gBAAgB,CAACM,wBAAwBP,MAAM,WAAW;IACpEC,UAAU,gBAAgB,CAACO,uBAAuBR,MAAM,kBAAkB;IAC1EC,UAAU,gBAAgB,CAACQ,YAAYT,MAAM,UAAU;IAGvDC,UAAU,QAAQ,CAACS,uDAAsB,gBAAgB;IAEzD,OAAOT;AACX;AAMAU,SAAS,wBAAwB;IAC7B,IAAIX;IACJ,IAAIY;IAEJC,WAAW;QACPb,QAAQF;QACR,MAAMG,YAAYF,gBAAgBC;QAClCY,YAAYX,UAAU,OAAO,CAACa;IAClC;IAMAC,GAAG,oEAAoE;QACnEC,OAAOJ,UAAU,EAAE,CAAC,IAAI,EAAE,QAAQ;QAClCI,OAAOJ,UAAU,EAAE,CAAC,OAAO,EAAE,QAAQ;IACzC;IAEAG,GAAG,iEAAiE;QAChEC,OAAOJ,UAAU,EAAE,CAAC,IAAI,EAAE,WAAW;QACrCI,OAAOJ,UAAU,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW;QAC5CI,OAAOJ,UAAU,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC;IAC3C;IAEAG,GAAG,sDAAsD;QACrDC,OAAOJ,UAAU,EAAE,CAAC,UAAU,EAAE,QAAQ;IAC5C;IAMAG,GAAG,0CAA0C;QACzC,MAAMH,UAAU,QAAQ,CAAC;QAEzBI,OAAOJ,UAAU,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC,QAAQ;QACtCI,OAAOJ,UAAU,EAAE,CAAC,IAAI,CAAE,EAAE,EAAE,IAAI,CAAC;QACnCI,OAAOJ,UAAU,EAAE,CAAC,IAAI,CAAE,IAAI,EAAE,IAAI,CAAC;IACzC;IAEAG,GAAG,oDAAoD;QACnD,MAAMH,UAAU,QAAQ,CAAC;QAEzBI,OAAOJ,UAAU,EAAE,CAAC,OAAO,EAAE,QAAQ;IACzC;IAEAG,GAAG,uDAAuD;QACtD,MAAMH,UAAU,QAAQ,CAAC;QAEzBI,OAAOhB,MAAM,cAAc,CAAC,OAAO,EAAE,oBAAoB,CAAC;YAAE,IAAI;QAAS;IAC7E;IAEAe,GAAG,2DAA2D;QAC1D,MAAMH,UAAU,QAAQ,CAAC;QAGzBI,OAAOhB,MAAM,gBAAgB,CAAC,MAAM,EAAE,qBAAqB,CAAC;IAChE;IAEAe,GAAG,iDAAiD;QAChD,MAAMH,UAAU,QAAQ,CAAC;QAEzBI,OAAOJ,UAAU,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC;IAC3C;IAMAG,GAAG,iEAAiE;QAChE,MAAMH,UAAU,QAAQ,CAAC;QAGzB,MAAMxB,WAAYY,MAAM,gBAAgB,CAAC,MAAM,CAA8B,IAAI,CAAC,OAAO,CAAC,EAAE,CACvF,KAAK;QACTZ,SAAS,MAAM,CAA8B,iBAAiB,CAAC;YAC5D,MAAM;YACN,MAAM;gBAAC;aAAU;YACjB,eAAe;QACnB;QAEA,MAAMwB,UAAU,QAAQ;QAExBI,OAAOhB,MAAM,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,CAAC;YACzD,IAAI;YACJ,MAAM;gBACF,MAAM;gBACN,MAAM;oBAAC;iBAAU;gBACjB,eAAe;oBAAE,MAAM;gBAAwB;YACnD;QACJ;IACJ;IAEAe,GAAG,4DAA4D;QAC3D,MAAMH,UAAU,QAAQ;QAExBI,OAAOhB,MAAM,iBAAiB,CAAC,OAAO,EAAE,GAAG,CAAC,gBAAgB;IAChE;IAEAe,GAAG,oEAAoE;QACnE,MAAMH,UAAU,QAAQ,CAAC;QAGzB,MAAMxB,WAAYY,MAAM,gBAAgB,CAAC,MAAM,CAA8B,IAAI,CAAC,OAAO,CAAC,EAAE,CACvF,KAAK;QACTZ,SAAS,MAAM,CAA8B,iBAAiB,CAAC;QAEhE,MAAMwB,UAAU,QAAQ;QAExBI,OAAOhB,MAAM,iBAAiB,CAAC,OAAO,EAAE,GAAG,CAAC,gBAAgB;IAChE;IAMAe,GAAG,oDAAoD;QACnDC,OAAOJ,UAAU,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC;QAC9CI,OAAOJ,UAAU,EAAE,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC;IACpD;IAEAG,GAAG,iDAAiD;QAE/Cf,MAAM,WAAW,CAAC,OAAO,CAA8B,eAAe,CAAC;QACvEA,MAAM,WAAW,CAAC,SAAS,CAA8B,eAAe,CAAC;QAE1EgB,OAAOJ,UAAU,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC;QAC9CI,OAAOJ,UAAU,EAAE,CAAC,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC;IACpD;IAMAG,GAAG,wEAAwE;QACvE,MAAMH,UAAU,QAAQ,CAAC;QAEzBI,OAAOJ,UAAU,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC;IACzC;IAEAG,GAAG,sDAAsD;QACpDf,MAAM,cAAc,CAAC,OAAO,CAA8B,iBAAiB,CAAC;YACzE,SAAS;YACT,MAAMV,eAAe;gBAAE,KAAKO;YAAU;QAC1C;QAEA,MAAMe,UAAU,QAAQ,CAAC;QAEzBI,OAAOJ,UAAU,EAAE,CAAC,UAAU,EAAE,QAAQ;IAC5C;IAMAG,GAAG,2DAA2D;QAE1D,IAAIE,yBAAwC;QAE3CjB,MAAM,cAAc,CAAC,OAAO,CAA8B,kBAAkB,CAAC;YAC1EiB,yBAAyBL,UAAU,EAAE,CAAC,OAAO;YAC7C,OAAO;gBAAE,SAAS;gBAAM,MAAMtB;YAAiB;QACnD;QAEA,MAAMsB,UAAU,QAAQ,CAAC;QAEzBI,OAAOC,wBAAwB,IAAI,CAAC;QACpCD,OAAOJ,UAAU,EAAE,CAAC,OAAO,EAAE,QAAQ;IACzC;IAMAD,SAAS,sCAAsC;QAC3C,IAAIO;QACJ,IAAIC;QAEJN,WAAW;YACPK,kBAAkBpB,YAAY;YAC9B,MAAMG,YAAYF,gBAAgBmB;YAClCC,sBAAsBlB,UAAU,OAAO,CAACa;QAC5C;QAEAC,GAAG,wDAAwD;YACvD,MAAMI,oBAAoB,QAAQ,CAAC;YAEnC,MAAM/B,WAAY8B,gBAAgB,gBAAgB,CAAC,MAAM,CACpD,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK;YACzB9B,SAAS,MAAM,CAA8B,iBAAiB,CAAC;gBAC5D,MAAM;gBACN,aAAa;gBACb,MAAM;oBAAC;iBAAU;YACrB;YAEA,MAAM+B,oBAAoB,QAAQ;YAElC,MAAMC,WAAYF,gBAAgB,iBAAiB,CAAC,OAAO,CACtD,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE;YACrBF,OAAOI,SAAS,IAAI,EAAE,GAAG,CAAC,cAAc,CAAC;QAC7C;QAEAL,GAAG,6DAA6D;YAC5D,MAAMI,oBAAoB,QAAQ,CAAC;YAEnC,MAAM/B,WAAY8B,gBAAgB,gBAAgB,CAAC,MAAM,CACpD,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK;YAC1B,MAAMG,cAAejC,SAAS,OAAO,CAA8B,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE;YACnF4B,OAAOK,aAAa,GAAG,CAAC,cAAc,CAAC;QAC3C;QAEAN,GAAG,0EAA0E;YACxEG,gBAAgB,WAAW,CAAC,OAAO,CAA8B,eAAe,CAC7E;YAGJ,MAAMC,oBAAoB,QAAQ,CAAC;YAEnC,MAAM/B,WAAY8B,gBAAgB,gBAAgB,CAAC,MAAM,CACpD,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK;YAC1BF,OAAO5B,SAAS,KAAK,EAAE,qBAAqB,CAAC;YAC7C4B,OAAO5B,SAAS,KAAK,EAAE,GAAG,CAAC,oBAAoB,CAAC;QACpD;IACJ;AACJ"}
@@ -14,6 +14,8 @@ export interface IFileDetailsPresenter {
14
14
  vm: IFileDetailsViewModel;
15
15
  loadFile(id: string): Promise<void>;
16
16
  saveFile(): Promise<boolean>;
17
+ /** Replace the currently displayed file (e.g. after an out-of-band update). */
18
+ setFile(file: FmFile): void;
17
19
  }
18
20
  export declare const FileDetailsPresenter: import("@webiny/di").Abstraction<IFileDetailsPresenter>;
19
21
  export declare namespace FileDetailsPresenter {
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/FileDetails/abstractions.js","sources":["../../../src/presentation/FileDetails/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport type { IFormVM } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { FmFile } from \"../../features/shared/types.js\";\n\n// ---------------------------------------------------------------------------\n// FileDetailsViewModel\n// ---------------------------------------------------------------------------\n\nexport interface IFileDetailsViewModel {\n file: FmFile | null;\n loading: string | null;\n form: IFormVM;\n previewUrl: string | null;\n permissions: {\n canEdit: boolean;\n canDelete: boolean;\n };\n}\n\n// ---------------------------------------------------------------------------\n// IFileDetailsPresenter\n// ---------------------------------------------------------------------------\n\nexport interface IFileDetailsPresenter {\n vm: IFileDetailsViewModel;\n loadFile(id: string): Promise<void>;\n saveFile(): Promise<boolean>;\n}\n\nexport const FileDetailsPresenter =\n createAbstraction<IFileDetailsPresenter>(\"FileDetailsPresenter\");\n\nexport namespace FileDetailsPresenter {\n export type Interface = IFileDetailsPresenter;\n export type ViewModel = IFileDetailsViewModel;\n}\n"],"names":["FileDetailsPresenter","createAbstraction"],"mappings":";AA6BO,MAAMA,uBACTC,kBAAyC"}
1
+ {"version":3,"file":"presentation/FileDetails/abstractions.js","sources":["../../../src/presentation/FileDetails/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport type { IFormVM } from \"@webiny/app-admin/features/formModel/abstractions.js\";\nimport type { FmFile } from \"../../features/shared/types.js\";\n\n// ---------------------------------------------------------------------------\n// FileDetailsViewModel\n// ---------------------------------------------------------------------------\n\nexport interface IFileDetailsViewModel {\n file: FmFile | null;\n loading: string | null;\n form: IFormVM;\n previewUrl: string | null;\n permissions: {\n canEdit: boolean;\n canDelete: boolean;\n };\n}\n\n// ---------------------------------------------------------------------------\n// IFileDetailsPresenter\n// ---------------------------------------------------------------------------\n\nexport interface IFileDetailsPresenter {\n vm: IFileDetailsViewModel;\n loadFile(id: string): Promise<void>;\n saveFile(): Promise<boolean>;\n /** Replace the currently displayed file (e.g. after an out-of-band update). */\n setFile(file: FmFile): void;\n}\n\nexport const FileDetailsPresenter =\n createAbstraction<IFileDetailsPresenter>(\"FileDetailsPresenter\");\n\nexport namespace FileDetailsPresenter {\n export type Interface = IFileDetailsPresenter;\n export type ViewModel = IFileDetailsViewModel;\n}\n"],"names":["FileDetailsPresenter","createAbstraction"],"mappings":";AA+BO,MAAMA,uBACTC,kBAAyC"}
@@ -2,13 +2,14 @@ import React from "react";
2
2
  interface ContentProps {
3
3
  children: React.ReactNode;
4
4
  }
5
- export declare const Content: {
6
- ({ children }: ContentProps): React.JSX.Element;
7
- Panel: ({ flex, children, className }: PanelProps) => React.JSX.Element;
8
- };
5
+ export declare function Content({ children }: ContentProps): React.JSX.Element;
6
+ export declare namespace Content {
7
+ export { Panel };
8
+ }
9
9
  interface PanelProps {
10
10
  flex?: number;
11
11
  children: React.ReactNode;
12
12
  className?: string;
13
13
  }
14
+ declare const Panel: ({ flex, children, className }: PanelProps) => React.JSX.Element;
14
15
  export {};
@@ -1,4 +1,4 @@
1
1
  import React from "react";
2
- export declare const FileDetailsDrawer: (() => React.JSX.Element | null) & React.NamedExoticComponent<unknown> & {
3
- readonly type: () => React.JSX.Element | null;
2
+ export declare const FileDetailsDrawer: (() => React.JSX.Element | null) & {
3
+ displayName: string;
4
4
  };
@@ -1,20 +1,16 @@
1
- import type { IDataSource, IDataSourceQuery, IDataSourceMeta } from "@webiny/app-admin/presentation/listPresenter/abstractions.js";
2
- import type { IListFilesUseCase } from "../../features/listFiles/abstractions.js";
1
+ import { FolderAwareDataSource } from "@webiny/app-admin/presentation/listPresenter/FolderAwareDataSource.js";
2
+ import type { FetchParams, FetchResult } from "@webiny/app-admin/presentation/listPresenter/FolderAwareDataSource.js";
3
3
  import type { IGetDescendantFoldersUseCase } from "@webiny/app-aco/features/folders/getDescendantFolders/abstractions.js";
4
4
  import type { IListCache } from "@webiny/app-admin/features/listCache/index.js";
5
+ import type { IListFilesUseCase } from "../../features/listFiles/index.js";
5
6
  import type { FmFile } from "../../features/shared/types.js";
6
- export declare class FileListDataSource implements IDataSource<FmFile> {
7
+ export declare class FileListDataSource extends FolderAwareDataSource<FmFile> {
7
8
  private listFilesUseCase;
8
9
  private cache;
9
- private getDescendantFoldersUseCase?;
10
10
  private scope?;
11
- private _meta;
12
- private _loading;
13
- constructor(listFilesUseCase: IListFilesUseCase, cache: IListCache<FmFile>, getDescendantFoldersUseCase?: IGetDescendantFoldersUseCase | undefined, scope?: string | undefined);
11
+ constructor(listFilesUseCase: IListFilesUseCase, cache: IListCache<FmFile>, getDescendantFoldersUseCase?: IGetDescendantFoldersUseCase, scope?: string | undefined);
14
12
  get rows(): FmFile[];
15
- get meta(): IDataSourceMeta;
16
- get loading(): boolean;
17
- query(params: IDataSourceQuery): Promise<void>;
18
- loadMore(params: IDataSourceQuery): Promise<void>;
19
- private buildWhere;
13
+ fetch(params: FetchParams): Promise<FetchResult<FmFile>>;
14
+ protected shouldExpandFolders(filters: Record<string, unknown>): boolean;
15
+ protected customizeWhere(where: Record<string, unknown>, filters: Record<string, unknown>): void;
20
16
  }
@@ -1,86 +1,41 @@
1
- import { computed, makeAutoObservable, runInAction } from "mobx";
1
+ import { FolderAwareDataSource } from "@webiny/app-admin/presentation/listPresenter/FolderAwareDataSource.js";
2
2
  import { DEFAULT_SCOPE } from "../../domain/constants.js";
3
- class FileListDataSource {
3
+ class FileListDataSource extends FolderAwareDataSource {
4
4
  constructor(listFilesUseCase, cache, getDescendantFoldersUseCase, scope){
5
- this.listFilesUseCase = listFilesUseCase;
6
- this.cache = cache;
7
- this.getDescendantFoldersUseCase = getDescendantFoldersUseCase;
8
- this.scope = scope;
9
- this._meta = {
10
- cursor: null,
11
- hasMoreItems: false,
12
- totalCount: 0
13
- };
14
- this._loading = false;
15
- makeAutoObservable(this, {
16
- listFilesUseCase: false,
17
- getDescendantFoldersUseCase: false,
18
- rows: computed
19
- });
5
+ super({
6
+ keyField: "id",
7
+ getDescendantFolders: getDescendantFoldersUseCase,
8
+ localFilters: {
9
+ accept: (item, value)=>{
10
+ const mimeTypes = value;
11
+ if (!mimeTypes || 0 === mimeTypes.length) return true;
12
+ return mimeTypes.some((pattern)=>{
13
+ if (pattern.endsWith("/*")) return item.type.startsWith(pattern.slice(0, -1));
14
+ return item.type === pattern;
15
+ });
16
+ }
17
+ }
18
+ }), this.listFilesUseCase = listFilesUseCase, this.cache = cache, this.scope = scope;
20
19
  }
21
20
  get rows() {
22
- return this.cache.getItems();
23
- }
24
- get meta() {
25
- return this._meta;
21
+ return this.queryMatcher.filter(this.cache.getItems());
26
22
  }
27
- get loading() {
28
- return this._loading;
29
- }
30
- async query(params) {
31
- this._loading = true;
32
- this.cache.clear();
33
- const where = this.buildWhere(params);
34
- const sort = params.sort ? [
35
- `${params.sort.field}_${params.sort.direction}`
36
- ] : void 0;
37
- const result = await this.listFilesUseCase.execute({
23
+ async fetch(params) {
24
+ return this.listFilesUseCase.execute({
38
25
  search: params.search,
39
- where,
40
- sort,
26
+ where: params.where,
27
+ sort: params.sort,
41
28
  limit: params.limit,
42
- after: params.cursor
43
- });
44
- runInAction(()=>{
45
- this.cache.addItems(result.data);
46
- this._meta = {
47
- cursor: result.meta.cursor,
48
- hasMoreItems: result.meta.hasMoreItems,
49
- totalCount: result.meta.totalCount
50
- };
51
- this._loading = false;
29
+ after: params.after
52
30
  });
53
31
  }
54
- async loadMore(params) {
55
- if (!this._meta.hasMoreItems || this._loading) return;
56
- this._loading = true;
57
- const where = this.buildWhere(params);
58
- const sort = params.sort ? [
59
- `${params.sort.field}_${params.sort.direction}`
60
- ] : void 0;
61
- const result = await this.listFilesUseCase.execute({
62
- search: params.search,
63
- where,
64
- sort,
65
- limit: params.limit,
66
- after: params.cursor
67
- });
68
- runInAction(()=>{
69
- this.cache.addItems(result.data);
70
- this._meta = {
71
- cursor: result.meta.cursor,
72
- hasMoreItems: result.meta.hasMoreItems,
73
- totalCount: result.meta.totalCount
74
- };
75
- this._loading = false;
76
- });
32
+ shouldExpandFolders(filters) {
33
+ return true === filters["includeSubFolders"];
77
34
  }
78
- buildWhere(params) {
79
- const where = {
80
- ...params.filters
81
- };
82
- const includeSubFolders = true === where["includeSubFolders"];
35
+ customizeWhere(where, filters) {
83
36
  delete where["includeSubFolders"];
37
+ const accept = where["accept"];
38
+ delete where["accept"];
84
39
  const tags = where["tags"];
85
40
  const tagsRule = where["tags_rule"] || "OR";
86
41
  delete where["tags"];
@@ -99,22 +54,27 @@ class FileListDataSource {
99
54
  })));
100
55
  where["AND"] = andConditions;
101
56
  }
102
- if (where["folderId"]) {
103
- const currentFolderId = where["folderId"];
104
- const isRoot = "root" === currentFolderId;
105
- if (params.search && isRoot) ;
106
- else if ((params.search || includeSubFolders) && this.getDescendantFoldersUseCase) {
107
- const descendants = this.getDescendantFoldersUseCase.execute(currentFolderId);
108
- const folderIds = descendants.map((f)=>f.id);
109
- where["location"] = {
110
- folderId_in: folderIds
111
- };
112
- } else where["location"] = {
113
- folderId: currentFolderId
114
- };
115
- delete where["folderId"];
57
+ if (accept && accept.length > 0) {
58
+ const exact = [];
59
+ const prefixes = [];
60
+ for (const mime of accept)if (mime.endsWith("/*")) prefixes.push(mime.slice(0, -1));
61
+ else exact.push(mime);
62
+ if (0 === prefixes.length) where["type_in"] = exact;
63
+ else if (0 === exact.length && 1 === prefixes.length) where["type_startsWith"] = prefixes[0];
64
+ else {
65
+ const orConditions = [];
66
+ if (exact.length > 0) orConditions.push({
67
+ type_in: exact
68
+ });
69
+ for (const prefix of prefixes)orConditions.push({
70
+ type_startsWith: prefix
71
+ });
72
+ where["OR"] = [
73
+ ...where["OR"] ?? [],
74
+ ...orConditions
75
+ ];
76
+ }
116
77
  }
117
- return where;
118
78
  }
119
79
  }
120
80
  export { FileListDataSource };
@@ -1 +1 @@
1
- {"version":3,"file":"presentation/FileList/FileListDataSource.js","sources":["../../../src/presentation/FileList/FileListDataSource.ts"],"sourcesContent":["import { makeAutoObservable, runInAction, computed } from \"mobx\";\nimport type {\n IDataSource,\n IDataSourceQuery,\n IDataSourceMeta\n} from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport type { IListFilesUseCase } from \"../../features/listFiles/abstractions.js\";\nimport type { IGetDescendantFoldersUseCase } from \"@webiny/app-aco/features/folders/getDescendantFolders/abstractions.js\";\nimport type { IListCache } from \"@webiny/app-admin/features/listCache/index.js\";\nimport type { FmFile } from \"../../features/shared/types.js\";\nimport { DEFAULT_SCOPE } from \"~/domain/constants.js\";\n\nexport class FileListDataSource implements IDataSource<FmFile> {\n private _meta: IDataSourceMeta = { cursor: null, hasMoreItems: false, totalCount: 0 };\n private _loading = false;\n\n constructor(\n private listFilesUseCase: IListFilesUseCase,\n private cache: IListCache<FmFile>,\n private getDescendantFoldersUseCase?: IGetDescendantFoldersUseCase,\n private scope?: string\n ) {\n makeAutoObservable<FileListDataSource, \"listFilesUseCase\" | \"getDescendantFoldersUseCase\">(\n this,\n {\n listFilesUseCase: false,\n getDescendantFoldersUseCase: false,\n rows: computed\n }\n );\n }\n\n get rows(): FmFile[] {\n return this.cache.getItems();\n }\n\n get meta(): IDataSourceMeta {\n return this._meta;\n }\n\n get loading(): boolean {\n return this._loading;\n }\n\n async query(params: IDataSourceQuery): Promise<void> {\n this._loading = true;\n this.cache.clear();\n\n const where = this.buildWhere(params);\n const sort = params.sort ? [`${params.sort.field}_${params.sort.direction}`] : undefined;\n\n const result = await this.listFilesUseCase.execute({\n search: params.search,\n where,\n sort,\n limit: params.limit,\n after: params.cursor\n });\n\n runInAction(() => {\n this.cache.addItems(result.data);\n this._meta = {\n cursor: result.meta.cursor,\n hasMoreItems: result.meta.hasMoreItems,\n totalCount: result.meta.totalCount\n };\n this._loading = false;\n });\n }\n\n async loadMore(params: IDataSourceQuery): Promise<void> {\n if (!this._meta.hasMoreItems || this._loading) {\n return;\n }\n this._loading = true;\n\n const where = this.buildWhere(params);\n const sort = params.sort ? [`${params.sort.field}_${params.sort.direction}`] : undefined;\n\n const result = await this.listFilesUseCase.execute({\n search: params.search,\n where,\n sort,\n limit: params.limit,\n after: params.cursor\n });\n\n runInAction(() => {\n this.cache.addItems(result.data);\n this._meta = {\n cursor: result.meta.cursor,\n hasMoreItems: result.meta.hasMoreItems,\n totalCount: result.meta.totalCount\n };\n this._loading = false;\n });\n }\n\n private buildWhere(params: IDataSourceQuery): Record<string, unknown> {\n const where: Record<string, unknown> = { ...params.filters };\n\n const includeSubFolders = where[\"includeSubFolders\"] === true;\n delete where[\"includeSubFolders\"];\n\n const tags = where[\"tags\"] as string[] | undefined;\n const tagsRule = (where[\"tags_rule\"] as string) || \"OR\";\n delete where[\"tags\"];\n delete where[\"tags_rule\"];\n\n if (this.scope) {\n where[\"tags_startsWith\"] = this.scope;\n } else {\n where[\"tags_not_startsWith\"] = DEFAULT_SCOPE;\n }\n\n if (tags && tags.length > 0) {\n const andConditions: Record<string, unknown>[] = [];\n if (tagsRule === \"OR\") {\n andConditions.push({ tags_in: tags });\n } else {\n andConditions.push(...tags.map(tag => ({ tags_in: [tag] })));\n }\n where[\"AND\"] = andConditions;\n }\n\n if (where[\"folderId\"]) {\n const currentFolderId = where[\"folderId\"] as string;\n const isRoot = currentFolderId === \"root\";\n\n if (params.search && isRoot) {\n // Search from root: no location filter — search all folders.\n } else if ((params.search || includeSubFolders) && this.getDescendantFoldersUseCase) {\n const descendants = this.getDescendantFoldersUseCase.execute(currentFolderId);\n const folderIds = descendants.map(f => f.id);\n where[\"location\"] = { folderId_in: folderIds };\n } else {\n where[\"location\"] = { folderId: currentFolderId };\n }\n\n delete where[\"folderId\"];\n }\n\n return where;\n }\n}\n"],"names":["FileListDataSource","listFilesUseCase","cache","getDescendantFoldersUseCase","scope","makeAutoObservable","computed","params","where","sort","undefined","result","runInAction","includeSubFolders","tags","tagsRule","DEFAULT_SCOPE","andConditions","tag","currentFolderId","isRoot","descendants","folderIds","f"],"mappings":"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"}
1
+ {"version":3,"file":"presentation/FileList/FileListDataSource.js","sources":["../../../src/presentation/FileList/FileListDataSource.ts"],"sourcesContent":["import { FolderAwareDataSource } from \"@webiny/app-admin/presentation/listPresenter/FolderAwareDataSource.js\";\nimport type {\n FetchParams,\n FetchResult\n} from \"@webiny/app-admin/presentation/listPresenter/FolderAwareDataSource.js\";\nimport type { IGetDescendantFoldersUseCase } from \"@webiny/app-aco/features/folders/getDescendantFolders/abstractions.js\";\nimport type { IListCache } from \"@webiny/app-admin/features/listCache/index.js\";\nimport { DEFAULT_SCOPE } from \"~/domain/constants.js\";\nimport type { IListFilesUseCase } from \"~/features/listFiles/index.js\";\nimport type { FmFile } from \"~/features/shared/types.js\";\n\nexport class FileListDataSource extends FolderAwareDataSource<FmFile> {\n constructor(\n private listFilesUseCase: IListFilesUseCase,\n private cache: IListCache<FmFile>,\n getDescendantFoldersUseCase?: IGetDescendantFoldersUseCase,\n private scope?: string\n ) {\n super({\n keyField: \"id\",\n getDescendantFolders: getDescendantFoldersUseCase,\n localFilters: {\n accept: (item, value) => {\n const mimeTypes = value as string[];\n if (!mimeTypes || mimeTypes.length === 0) {\n return true;\n }\n return mimeTypes.some(pattern => {\n if (pattern.endsWith(\"/*\")) {\n return item.type.startsWith(pattern.slice(0, -1));\n }\n return item.type === pattern;\n });\n }\n }\n });\n }\n\n get rows(): FmFile[] {\n return this.queryMatcher.filter(this.cache.getItems());\n }\n\n async fetch(params: FetchParams): Promise<FetchResult<FmFile>> {\n return this.listFilesUseCase.execute({\n search: params.search,\n where: params.where,\n sort: params.sort,\n limit: params.limit,\n after: params.after\n });\n }\n\n protected override shouldExpandFolders(filters: Record<string, unknown>): boolean {\n return filters[\"includeSubFolders\"] === true;\n }\n\n protected override customizeWhere(\n where: Record<string, unknown>,\n filters: Record<string, unknown>\n ): void {\n delete where[\"includeSubFolders\"];\n\n const accept = where[\"accept\"] as string[] | undefined;\n delete where[\"accept\"];\n\n const tags = where[\"tags\"] as string[] | undefined;\n const tagsRule = (where[\"tags_rule\"] as string) || \"OR\";\n delete where[\"tags\"];\n delete where[\"tags_rule\"];\n\n if (this.scope) {\n where[\"tags_startsWith\"] = this.scope;\n } else {\n where[\"tags_not_startsWith\"] = DEFAULT_SCOPE;\n }\n\n if (tags && tags.length > 0) {\n const andConditions: Record<string, unknown>[] = [];\n if (tagsRule === \"OR\") {\n andConditions.push({ tags_in: tags });\n } else {\n andConditions.push(...tags.map(tag => ({ tags_in: [tag] })));\n }\n where[\"AND\"] = andConditions;\n }\n\n if (accept && accept.length > 0) {\n const exact: string[] = [];\n const prefixes: string[] = [];\n for (const mime of accept) {\n if (mime.endsWith(\"/*\")) {\n prefixes.push(mime.slice(0, -1));\n } else {\n exact.push(mime);\n }\n }\n\n if (prefixes.length === 0) {\n where[\"type_in\"] = exact;\n } else if (exact.length === 0 && prefixes.length === 1) {\n where[\"type_startsWith\"] = prefixes[0];\n } else {\n const orConditions: Record<string, unknown>[] = [];\n if (exact.length > 0) {\n orConditions.push({ type_in: exact });\n }\n for (const prefix of prefixes) {\n orConditions.push({ type_startsWith: prefix });\n }\n where[\"OR\"] = [\n ...((where[\"OR\"] as Record<string, unknown>[]) ?? []),\n ...orConditions\n ];\n }\n }\n }\n}\n"],"names":["FileListDataSource","FolderAwareDataSource","listFilesUseCase","cache","getDescendantFoldersUseCase","scope","item","value","mimeTypes","pattern","params","filters","where","accept","tags","tagsRule","DEFAULT_SCOPE","andConditions","tag","exact","prefixes","mime","orConditions","prefix"],"mappings":"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"}
@@ -33,8 +33,10 @@ function createMockListPresenter() {
33
33
  selection: {
34
34
  selectedIds: new Set(),
35
35
  selectedCount: 0,
36
- allSelected: false
36
+ allSelected: false,
37
+ label: ""
37
38
  },
39
+ showingFilters: false,
38
40
  empty: true,
39
41
  emptyWithFilters: false,
40
42
  error: null
@@ -55,7 +57,9 @@ function createMockListPresenter() {
55
57
  filter: {
56
58
  set: vi.fn(),
57
59
  clear: vi.fn(),
58
- clearAll: vi.fn()
60
+ clearAll: vi.fn(),
61
+ show: vi.fn(),
62
+ hide: vi.fn()
59
63
  },
60
64
  selection: {
61
65
  toggle: vi.fn(),
@@ -194,6 +198,7 @@ function createMockFileDetailsPresenter() {
194
198
  form: {
195
199
  layout: [],
196
200
  errors: [],
201
+ hasErrors: false,
197
202
  isDirty: false,
198
203
  isValid: null,
199
204
  submitCount: 0,
@@ -208,7 +213,8 @@ function createMockFileDetailsPresenter() {
208
213
  }
209
214
  },
210
215
  loadFile: vi.fn().mockResolvedValue(void 0),
211
- saveFile: vi.fn().mockResolvedValue(void 0)
216
+ saveFile: vi.fn().mockResolvedValue(void 0),
217
+ setFile: vi.fn()
212
218
  };
213
219
  }
214
220
  function createMocks() {