@webiny/app-headless-cms 6.5.0-beta.1 → 6.5.0-beta.11
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.
- package/ContentEntriesModule.js +14 -2
- package/ContentEntriesModule.js.map +1 -1
- package/admin/components/ContentEntries/BulkActions/ActionDelete.d.ts +2 -2
- package/admin/components/ContentEntries/BulkActions/ActionDelete.js.map +1 -1
- package/admin/components/ContentEntries/BulkActions/ActionMove.d.ts +2 -2
- package/admin/components/ContentEntries/BulkActions/ActionMove.js.map +1 -1
- package/admin/components/ContentEntries/BulkActions/ActionPublish.d.ts +2 -2
- package/admin/components/ContentEntries/BulkActions/ActionPublish.js.map +1 -1
- package/admin/components/ContentEntries/BulkActions/ActionUnpublish.d.ts +2 -2
- package/admin/components/ContentEntries/BulkActions/ActionUnpublish.js.map +1 -1
- package/admin/components/ContentEntries/Filters/RefFieldRenderer.d.ts +2 -0
- package/admin/components/ContentEntries/Filters/RefFieldRenderer.js +84 -0
- package/admin/components/ContentEntries/Filters/RefFieldRenderer.js.map +1 -0
- package/admin/components/ContentEntries/Table/Actions/DuplicateEntry.d.ts +2 -0
- package/admin/components/ContentEntries/Table/Actions/DuplicateEntry.js +34 -0
- package/admin/components/ContentEntries/Table/Actions/DuplicateEntry.js.map +1 -0
- package/admin/components/ContentEntries/Table/Actions/index.d.ts +1 -0
- package/admin/components/ContentEntries/Table/Actions/index.js +1 -0
- package/admin/components/ContentModelEditor/ContentModelEditor.js +1 -1
- package/admin/components/ContentModelEditor/ContentModelEditor.js.map +1 -1
- package/admin/components/Dialogs/DuplicateEntryConfirmDialog.d.ts +2 -0
- package/admin/components/Dialogs/DuplicateEntryConfirmDialog.js +22 -0
- package/admin/components/Dialogs/DuplicateEntryConfirmDialog.js.map +1 -0
- package/admin/components/FieldEditor/EditFieldDialog.d.ts +2 -2
- package/admin/components/FieldEditor/Field.js +4 -4
- package/admin/components/FieldEditor/Field.js.map +1 -1
- package/features/contentEntry/CmsEntryError.d.ts +10 -0
- package/features/contentEntry/CmsEntryError.js +11 -0
- package/features/contentEntry/CmsEntryError.js.map +1 -0
- package/features/contentEntry/createEntry/CreateEntryGateway.d.ts +0 -1
- package/features/contentEntry/createEntry/CreateEntryGateway.js +3 -10
- package/features/contentEntry/createEntry/CreateEntryGateway.js.map +1 -1
- package/features/contentEntry/createRevisionFrom/CreateRevisionFromGateway.d.ts +3 -1
- package/features/contentEntry/createRevisionFrom/CreateRevisionFromGateway.js +8 -3
- package/features/contentEntry/createRevisionFrom/CreateRevisionFromGateway.js.map +1 -1
- package/features/contentEntry/duplicateEntry/DuplicateEntryGateway.d.ts +14 -0
- package/features/contentEntry/duplicateEntry/DuplicateEntryGateway.js +43 -0
- package/features/contentEntry/duplicateEntry/DuplicateEntryGateway.js.map +1 -0
- package/features/contentEntry/duplicateEntry/DuplicateEntryRepository.d.ts +15 -0
- package/features/contentEntry/duplicateEntry/DuplicateEntryRepository.js +34 -0
- package/features/contentEntry/duplicateEntry/DuplicateEntryRepository.js.map +1 -0
- package/features/contentEntry/duplicateEntry/DuplicateEntryUseCase.d.ts +11 -0
- package/features/contentEntry/duplicateEntry/DuplicateEntryUseCase.js +18 -0
- package/features/contentEntry/duplicateEntry/DuplicateEntryUseCase.js.map +1 -0
- package/features/contentEntry/duplicateEntry/abstractions.d.ts +26 -0
- package/features/contentEntry/duplicateEntry/abstractions.js +7 -0
- package/features/contentEntry/duplicateEntry/abstractions.js.map +1 -0
- package/features/contentEntry/duplicateEntry/feature.d.ts +3 -0
- package/features/contentEntry/duplicateEntry/feature.js +21 -0
- package/features/contentEntry/duplicateEntry/feature.js.map +1 -0
- package/features/contentEntry/entryValueGateways.test.d.ts +1 -0
- package/features/contentEntry/entryValueGateways.test.js +106 -0
- package/features/contentEntry/entryValueGateways.test.js.map +1 -0
- package/features/contentEntry/feature.js +2 -0
- package/features/contentEntry/feature.js.map +1 -1
- package/features/contentEntry/singletonEntry/UpdateSingletonEntryGateway.d.ts +0 -1
- package/features/contentEntry/singletonEntry/UpdateSingletonEntryGateway.js +3 -10
- package/features/contentEntry/singletonEntry/UpdateSingletonEntryGateway.js.map +1 -1
- package/features/contentEntry/updateEntry/UpdateEntryGateway.d.ts +0 -1
- package/features/contentEntry/updateEntry/UpdateEntryGateway.js +3 -10
- package/features/contentEntry/updateEntry/UpdateEntryGateway.js.map +1 -1
- package/features/contentEntry/valueTransformers/EntryDataPreparer.d.ts +9 -0
- package/features/contentEntry/valueTransformers/EntryDataPreparer.js +8 -0
- package/features/contentEntry/valueTransformers/EntryDataPreparer.js.map +1 -1
- package/features/contentEntry/valueTransformers/EntryDataPreparer.test.d.ts +1 -0
- package/features/contentEntry/valueTransformers/EntryDataPreparer.test.js +113 -0
- package/features/contentEntry/valueTransformers/EntryDataPreparer.test.js.map +1 -0
- package/features/contentEntry/valueTransformers/entryValueFixtures.d.ts +146 -0
- package/features/contentEntry/valueTransformers/entryValueFixtures.js +171 -0
- package/features/contentEntry/valueTransformers/entryValueFixtures.js.map +1 -0
- package/features/formModel/CmsFormModelBuilder.d.ts +9 -1
- package/features/formModel/CmsFormModelBuilder.js +26 -15
- package/features/formModel/CmsFormModelBuilder.js.map +1 -1
- package/features/formModel/CmsFormModelBuilder.test.d.ts +1 -0
- package/features/formModel/CmsFormModelBuilder.test.js +252 -0
- package/features/formModel/CmsFormModelBuilder.test.js.map +1 -0
- package/features/formModel/CmsLayoutMapper.d.ts +8 -1
- package/features/formModel/CmsLayoutMapper.js +23 -1
- package/features/formModel/CmsLayoutMapper.js.map +1 -1
- package/features/formModel/mappers/DynamicZoneFieldMapper.js +4 -0
- package/features/formModel/mappers/DynamicZoneFieldMapper.js.map +1 -1
- package/features/formModel/mappers/ObjectFieldMapper.js +11 -6
- package/features/formModel/mappers/ObjectFieldMapper.js.map +1 -1
- package/features/model/exportModels/ExportModelsGateway.d.ts +1 -1
- package/features/model/exportModels/ExportModelsGateway.js.map +1 -1
- package/features/model/exportModels/ExportModelsUseCase.js +7 -1
- package/features/model/exportModels/ExportModelsUseCase.js.map +1 -1
- package/features/model/exportModels/abstractions.d.ts +8 -2
- package/features/model/exportModels/abstractions.js.map +1 -1
- package/package.json +26 -26
- package/presentation/contentEntries/form/ContentEntryFormPresenter.js +34 -29
- package/presentation/contentEntries/form/ContentEntryFormPresenter.js.map +1 -1
- package/presentation/contentEntries/form/ContentEntryFormPresenter.test.d.ts +1 -0
- package/presentation/contentEntries/form/ContentEntryFormPresenter.test.js +210 -0
- package/presentation/contentEntries/form/ContentEntryFormPresenter.test.js.map +1 -0
- package/presentation/contentEntries/form/toFormErrors.d.ts +7 -0
- package/presentation/contentEntries/form/toFormErrors.js +49 -0
- package/presentation/contentEntries/form/toFormErrors.js.map +1 -0
- package/presentation/contentEntries/form/toFormErrors.test.d.ts +1 -0
- package/presentation/contentEntries/form/toFormErrors.test.js +175 -0
- package/presentation/contentEntries/form/toFormErrors.test.js.map +1 -0
- package/presentation/contentEntries/list/ContentEntriesPresenter.d.ts +6 -2
- package/presentation/contentEntries/list/ContentEntriesPresenter.js +27 -2
- package/presentation/contentEntries/list/ContentEntriesPresenter.js.map +1 -1
- package/presentation/contentEntries/list/ContentEntriesPresenter.test.js +58 -1
- package/presentation/contentEntries/list/ContentEntriesPresenter.test.js.map +1 -1
- package/presentation/contentEntries/list/abstractions.d.ts +12 -0
- package/presentation/contentEntries/list/abstractions.js.map +1 -1
- package/presentation/contentEntries/preview/LivePreviewPresenter.d.ts +12 -1
- package/presentation/contentEntries/preview/LivePreviewPresenter.js +48 -5
- package/presentation/contentEntries/preview/LivePreviewPresenter.js.map +1 -1
- package/presentation/contentEntries/preview/LivePreviewPresenter.test.d.ts +1 -0
- package/presentation/contentEntries/preview/LivePreviewPresenter.test.js +81 -0
- package/presentation/contentEntries/preview/LivePreviewPresenter.test.js.map +1 -0
- package/presentation/contentEntries/preview/ModelEditorComponentDiscovery.js +1 -1
- package/presentation/contentEntries/preview/ModelEditorComponentDiscovery.js.map +1 -1
- package/presentation/contentEntries/preview/PreviewDecorator.js +25 -6
- package/presentation/contentEntries/preview/PreviewDecorator.js.map +1 -1
- package/presentation/contentEntries/preview/PreviewDomainStatus.d.ts +10 -0
- package/presentation/contentEntries/preview/PreviewDomainStatus.js +19 -0
- package/presentation/contentEntries/preview/PreviewDomainStatus.js.map +1 -0
- package/presentation/contentEntries/preview/PreviewPane.d.ts +10 -1
- package/presentation/contentEntries/preview/PreviewPane.js +81 -45
- package/presentation/contentEntries/preview/PreviewPane.js.map +1 -1
- package/presentation/contentEntries/preview/abstractions.d.ts +8 -0
- package/presentation/contentEntries/preview/abstractions.js.map +1 -1
- package/presentation/contentEntries/preview/resolvePreviewUrl.d.ts +21 -0
- package/presentation/contentEntries/preview/resolvePreviewUrl.js +50 -1
- package/presentation/contentEntries/preview/resolvePreviewUrl.js.map +1 -1
- package/presentation/contentEntries/preview/resolvePreviewUrl.test.d.ts +1 -0
- package/presentation/contentEntries/preview/resolvePreviewUrl.test.js +127 -0
- package/presentation/contentEntries/preview/resolvePreviewUrl.test.js.map +1 -0
- package/presentation/contentEntries/singleEntry/SingleEntryPresenter.js +9 -4
- package/presentation/contentEntries/singleEntry/SingleEntryPresenter.js.map +1 -1
- package/presentation/contentEntries/singleEntry/SingleEntryPresenter.test.d.ts +1 -0
- package/presentation/contentEntries/singleEntry/SingleEntryPresenter.test.js +122 -0
- package/presentation/contentEntries/singleEntry/SingleEntryPresenter.test.js.map +1 -0
- package/presentation/contentEntries/trashBin/CmsTrashBin.d.ts +2 -2
- package/presentation/contentEntries/views/CmsAdvancedSearch.d.ts +2 -2
- package/presentation/contentEntries/views/ContentEntriesListLayout.d.ts +2 -2
- package/presentation/contentEntries/views/ContentEntryFormView.d.ts +2 -2
- package/presentation/contentEntries/views/OlderRevisionAlert.d.ts +2 -2
- package/presentation/contentEntries/views/RevisionDrawer.d.ts +2 -2
- package/presentation/contentEntries/views/RevisionsList.d.ts +2 -2
- package/presentation/contentEntries/views/SingleEntryLayout.d.ts +2 -2
- package/presentation/contentEntries/views/Table/Table.d.ts +2 -2
- package/presentation/contentEntries/views/actions/CreateNewRevisionButton.d.ts +2 -2
- package/presentation/contentEntries/views/actions/DeleteEntryMenuItem.d.ts +2 -2
- package/presentation/contentEntries/views/actions/PublishOnlyButton.d.ts +2 -2
- package/presentation/contentEntries/views/actions/RevisionSelector.d.ts +2 -2
- package/presentation/contentEntries/views/actions/SaveAndPublishButton.d.ts +2 -2
- package/presentation/contentEntries/views/actions/SaveContentButton.d.ts +2 -2
- package/presentation/contentEntries/views/actions/ShowRevisionListMenuItem.d.ts +2 -2
- package/presentation/contentEntries/views/layout/ContentEntryForm.d.ts +12 -12
- package/presentation/contentModels/ContentModelsPresenter.d.ts +5 -5
- package/presentation/contentModels/ContentModelsPresenter.js +1 -1
- package/presentation/contentModels/ContentModelsPresenter.js.map +1 -1
- package/presentation/contentModels/abstractions.d.ts +7 -2
- package/presentation/contentModels/abstractions.js.map +1 -1
- package/presentation/contentModels/components/CloneContentModelDialog.d.ts +2 -2
- package/presentation/contentModels/components/ContentModels.d.ts +2 -2
- package/presentation/contentModels/components/ContentModelsDataList.d.ts +2 -2
- package/presentation/contentModels/components/ContentModelsDataList.js +6 -5
- package/presentation/contentModels/components/ContentModelsDataList.js.map +1 -1
- package/presentation/contentModels/components/NewContentModelDialog.d.ts +2 -2
- package/presentation/contentModels/components/fullDelete/dialog/Content.js +2 -2
- package/presentation/contentModels/components/fullDelete/dialog/Content.js.map +1 -1
- package/presentation/contentModels/components/fullDelete/types.js.map +1 -1
- package/presentation/fieldRenderers/ref/autocomplete/BaseAutocompletePresenter.d.ts +1 -0
- package/presentation/fieldRenderers/ref/autocomplete/BaseAutocompletePresenter.js +8 -2
- package/presentation/fieldRenderers/ref/autocomplete/BaseAutocompletePresenter.js.map +1 -1
- package/presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.d.ts +1 -0
- package/presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.js +185 -0
- package/presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.js.map +1 -0
- package/presentation/fieldRenderers/ref/autocomplete/multi/RefMultiAutocompletePresenter.js +1 -1
- package/presentation/fieldRenderers/ref/autocomplete/multi/RefMultiAutocompletePresenter.js.map +1 -1
- package/presentation/fieldRenderers/ref/autocomplete/single/RefSingleAutocompletePresenter.js +1 -1
- package/presentation/fieldRenderers/ref/autocomplete/single/RefSingleAutocompletePresenter.js.map +1 -1
- package/presentation/fieldRenderers/ref/components/RefFieldOptions.d.ts +2 -2
- package/presentation/fieldRenderers/ref/components/RefFieldOptions.js +3 -2
- package/presentation/fieldRenderers/ref/components/RefFieldOptions.js.map +1 -1
- package/presentation/importContentModels/ImportContentModelsDialog.d.ts +2 -2
- package/presentation/importContentModels/ImportContentModelsPresenter.js +1 -1
- package/presentation/importContentModels/ImportContentModelsPresenter.js.map +1 -1
- package/presentation/importContentModels/components/DataList.d.ts +2 -2
- package/presentation/importContentModels/components/FileUpload.d.ts +2 -2
- package/presentation/importContentModels/components/FileUpload.js +1 -1
- package/presentation/importContentModels/components/FileUpload.js.map +1 -1
- package/presentation/importContentModels/components/Model/DataListModelItem.js +3 -3
- package/presentation/importContentModels/components/Model/DataListModelItem.js.map +1 -1
- package/presentation/importContentModels/components/Model/DataListModelItemInfo.js +2 -2
- package/presentation/importContentModels/components/Model/DataListModelItemInfo.js.map +1 -1
- package/presentation/importContentModels/components/Model/DataListModels.d.ts +2 -2
- package/presentation/menus/ContentGroupsMenuItems.d.ts +2 -2
- package/presentation/modelGroup/ModelGroupPresenter.js +1 -2
- package/presentation/modelGroup/ModelGroupPresenter.js.map +1 -1
- package/presentation/modelGroup/components/ContentModelGroups.d.ts +2 -2
- package/presentation/modelGroup/components/ContentModelGroupsDataList.d.ts +6 -2
- package/presentation/modelGroup/components/ContentModelGroupsForm.d.ts +8 -2
- package/utils/normalizeIcon.d.ts +4 -0
- package/utils/normalizeIcon.js +9 -6
- package/utils/normalizeIcon.js.map +1 -1
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import type { IListPresenter } from "@webiny/app-admin/presentation/listPresenter/abstractions.js";
|
|
2
|
-
import type { CmsModel } from "../../types.js";
|
|
2
|
+
import type { CmsGroup, CmsModel } from "../../types.js";
|
|
3
3
|
import type { DeleteModelResult } from "../../features/model/deleteModel/abstractions.js";
|
|
4
4
|
export interface IContentModelsPresenterViewModel {
|
|
5
5
|
loading: boolean;
|
|
6
6
|
models: CmsModel[];
|
|
7
7
|
}
|
|
8
|
+
export interface IContentModelsPresenterExportModelsResponse {
|
|
9
|
+
groups: CmsGroup[];
|
|
10
|
+
models: CmsModel[];
|
|
11
|
+
}
|
|
8
12
|
export interface IContentModelsPresenter {
|
|
9
13
|
readonly vm: IContentModelsPresenterViewModel;
|
|
10
14
|
readonly list: IListPresenter<CmsModel>;
|
|
11
15
|
init(): void;
|
|
12
16
|
deleteModel(modelId: string, confirmation: string): Promise<DeleteModelResult>;
|
|
13
17
|
cancelDelete(modelId: string): Promise<void>;
|
|
14
|
-
exportModels(modelIds?: string[]): Promise<
|
|
18
|
+
exportModels(modelIds?: string[]): Promise<IContentModelsPresenterExportModelsResponse | null>;
|
|
15
19
|
}
|
|
16
20
|
export declare const ContentModelsPresenter: import("@webiny/di").Abstraction<IContentModelsPresenter>;
|
|
17
21
|
export declare namespace ContentModelsPresenter {
|
|
18
22
|
type Interface = IContentModelsPresenter;
|
|
19
23
|
type ViewModel = IContentModelsPresenterViewModel;
|
|
24
|
+
type ExportModelResponse = IContentModelsPresenterExportModelsResponse;
|
|
20
25
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation/contentModels/abstractions.js","sources":["../../../src/presentation/contentModels/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport type { IListPresenter } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport type { CmsModel } from \"~/types.js\";\nimport type { DeleteModelResult } from \"~/features/model/deleteModel/abstractions.js\";\n\nexport interface IContentModelsPresenterViewModel {\n loading: boolean;\n models: CmsModel[];\n}\n\nexport interface IContentModelsPresenter {\n readonly vm: IContentModelsPresenterViewModel;\n readonly list: IListPresenter<CmsModel>;\n init(): void;\n deleteModel(modelId: string, confirmation: string): Promise<DeleteModelResult>;\n cancelDelete(modelId: string): Promise<void>;\n exportModels(modelIds?: string[]): Promise<
|
|
1
|
+
{"version":3,"file":"presentation/contentModels/abstractions.js","sources":["../../../src/presentation/contentModels/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport type { IListPresenter } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport type { CmsGroup, CmsModel } from \"~/types.js\";\nimport type { DeleteModelResult } from \"~/features/model/deleteModel/abstractions.js\";\n\nexport interface IContentModelsPresenterViewModel {\n loading: boolean;\n models: CmsModel[];\n}\n\nexport interface IContentModelsPresenterExportModelsResponse {\n groups: CmsGroup[];\n models: CmsModel[];\n}\n\nexport interface IContentModelsPresenter {\n readonly vm: IContentModelsPresenterViewModel;\n readonly list: IListPresenter<CmsModel>;\n init(): void;\n deleteModel(modelId: string, confirmation: string): Promise<DeleteModelResult>;\n cancelDelete(modelId: string): Promise<void>;\n exportModels(modelIds?: string[]): Promise<IContentModelsPresenterExportModelsResponse | null>;\n}\n\nexport const ContentModelsPresenter = createAbstraction<IContentModelsPresenter>(\n \"CmsContentModels/Presenter\"\n);\n\nexport namespace ContentModelsPresenter {\n export type Interface = IContentModelsPresenter;\n export type ViewModel = IContentModelsPresenterViewModel;\n export type ExportModelResponse = IContentModelsPresenterExportModelsResponse;\n}\n"],"names":["ContentModelsPresenter","createAbstraction"],"mappings":";AAwBO,MAAMA,yBAAyBC,kBAClC"}
|
|
@@ -5,7 +5,7 @@ interface CloneContentModelDialogProps {
|
|
|
5
5
|
contentModel: CmsModel;
|
|
6
6
|
closeModal: () => void;
|
|
7
7
|
}
|
|
8
|
-
export declare const CloneContentModelDialog: (({ onClose, contentModel, closeModal }: CloneContentModelDialogProps) => React.JSX.Element) & {
|
|
9
|
-
|
|
8
|
+
export declare const CloneContentModelDialog: (({ onClose, contentModel, closeModal }: CloneContentModelDialogProps) => React.JSX.Element) & React.NamedExoticComponent<CloneContentModelDialogProps> & {
|
|
9
|
+
readonly type: ({ onClose, contentModel, closeModal }: CloneContentModelDialogProps) => React.JSX.Element;
|
|
10
10
|
};
|
|
11
11
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
declare const ContentModels: (() => React.JSX.Element) & {
|
|
3
|
-
|
|
2
|
+
declare const ContentModels: (() => React.JSX.Element) & React.NamedExoticComponent<unknown> & {
|
|
3
|
+
readonly type: () => React.JSX.Element;
|
|
4
4
|
};
|
|
5
5
|
export default ContentModels;
|
|
@@ -6,7 +6,7 @@ interface ContentModelsDataListProps {
|
|
|
6
6
|
onClone: (contentModel: CmsModel) => void;
|
|
7
7
|
showImportModelModal: () => void;
|
|
8
8
|
}
|
|
9
|
-
declare const ContentModelsDataList: (({ canCreate, onCreate, onClone, showImportModelModal }: ContentModelsDataListProps) => React.JSX.Element) & {
|
|
10
|
-
|
|
9
|
+
declare const ContentModelsDataList: (({ canCreate, onCreate, onClone, showImportModelModal }: ContentModelsDataListProps) => React.JSX.Element) & React.NamedExoticComponent<ContentModelsDataListProps> & {
|
|
10
|
+
readonly type: ({ canCreate, onCreate, onClone, showImportModelModal }: ContentModelsDataListProps) => React.JSX.Element;
|
|
11
11
|
};
|
|
12
12
|
export default ContentModelsDataList;
|
|
@@ -48,7 +48,8 @@ const deserializeSorters = (data)=>{
|
|
|
48
48
|
];
|
|
49
49
|
};
|
|
50
50
|
const DisplayIcon = ({ model })=>{
|
|
51
|
-
|
|
51
|
+
const icon = normalizeIcon(model.icon);
|
|
52
|
+
if (!icon) return null;
|
|
52
53
|
return /*#__PURE__*/ react.createElement("div", {
|
|
53
54
|
className: "text-neutral-muted"
|
|
54
55
|
}, /*#__PURE__*/ react.createElement(Icon, {
|
|
@@ -56,7 +57,7 @@ const DisplayIcon = ({ model })=>{
|
|
|
56
57
|
color: "inherit",
|
|
57
58
|
label: "Content model icon",
|
|
58
59
|
icon: /*#__PURE__*/ react.createElement(FontAwesomeIcon, {
|
|
59
|
-
icon:
|
|
60
|
+
icon: icon
|
|
60
61
|
})
|
|
61
62
|
}));
|
|
62
63
|
};
|
|
@@ -104,10 +105,10 @@ const ContentModelsDataList = observer(({ canCreate, onCreate, onClone, showImpo
|
|
|
104
105
|
(async ()=>{
|
|
105
106
|
try {
|
|
106
107
|
const data = await presenter.exportModels(modelIds);
|
|
107
|
-
if (!data
|
|
108
|
+
if (!data?.models?.length) return void showSnackbar("No data returned from the export query.");
|
|
108
109
|
download(data);
|
|
109
|
-
} catch (
|
|
110
|
-
showSnackbar(
|
|
110
|
+
} catch (ex) {
|
|
111
|
+
showSnackbar(ex.message);
|
|
111
112
|
}
|
|
112
113
|
})();
|
|
113
114
|
}, [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation/contentModels/components/ContentModelsDataList.js","sources":["../../../../src/presentation/contentModels/components/ContentModelsDataList.tsx"],"sourcesContent":["import React, { useCallback, useMemo, useState } from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport orderBy from \"lodash/orderBy.js\";\nimport { TimeAgo } from \"@webiny/admin-ui\";\nimport { SearchUI, useRouter, useSnackbar } from \"@webiny/app-admin\";\nimport { ReactComponent as AddIcon } from \"@webiny/icons/add.svg\";\nimport {\n ReactComponent as DownloadFileIcon,\n ReactComponent as ExportIcon\n} from \"@webiny/icons/file_download.svg\";\nimport { ReactComponent as UploadFileIcon } from \"@webiny/icons/file_upload.svg\";\nimport { ReactComponent as ListIcon } from \"@webiny/icons/list.svg\";\nimport { ReactComponent as EditIcon } from \"@webiny/icons/edit.svg\";\nimport { ReactComponent as MoreVertIcon } from \"@webiny/icons/more_vert.svg\";\nimport { ReactComponent as DeleteIcon } from \"@webiny/icons/delete.svg\";\nimport { ReactComponent as CloneIcon } from \"@webiny/icons/flip_to_front.svg\";\nimport { DataList, DataListModal, List } from \"@webiny/admin-ui\";\nimport { i18n } from \"@webiny/app/i18n/index.js\";\nimport type { CmsModel } from \"~/types.js\";\nimport { usePermission } from \"~/admin/hooks/usePermission.js\";\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport type { IconProp } from \"@fortawesome/fontawesome-svg-core\";\nimport { ModelIsBeingDeleted } from \"./fullDelete/ModelIsBeingDeleted.js\";\nimport { FullyDeleteModelDialog } from \"./fullDelete/FullyDeleteModelDialog.js\";\nimport { Button, DropdownMenu, Icon, IconButton, Select, Tooltip } from \"@webiny/admin-ui\";\nimport { CMS_MODEL_SINGLETON_TAG } from \"@webiny/app-headless-cms-common\";\nimport { Routes } from \"~/routes.js\";\nimport { normalizeIcon } from \"~/utils/normalizeIcon.js\";\nimport { useContentModelsPresenter } from \"../useContentModelsPresenter.js\";\nimport { download } from \"../utils/download.js\";\n\nconst t = i18n.namespace(\"FormsApp.ContentModelsDataList\");\n\nconst SORTERS = [\n { label: \"Newest to oldest\", sorter: \"savedOn_DESC\" },\n { label: \"Oldest to newest\", sorter: \"savedOn_ASC\" },\n { label: \"Name A-Z\", sorter: \"name_ASC\" },\n { label: \"Name Z-A\", sorter: \"name_DESC\" }\n];\n\nconst deserializeSorters = (data: string): [string, \"asc\" | \"desc\"] => {\n const [field, order] = data.split(\"_\");\n return [field, order.toLowerCase() === \"asc\" ? \"asc\" : \"desc\"];\n};\n\ninterface ContentModelsDataListProps {\n canCreate: boolean;\n onCreate: () => void;\n onClone: (contentModel: CmsModel) => void;\n showImportModelModal: () => void;\n}\n\ninterface IconProps {\n model: Pick<CmsModel, \"icon\">;\n}\n\nconst DisplayIcon = ({ model }: IconProps) => {\n if (!model.icon) {\n return null;\n }\n return (\n <div className={\"text-neutral-muted\"}>\n <Icon\n size={\"lg\"}\n color={\"inherit\"}\n label={\"Content model icon\"}\n icon={<FontAwesomeIcon icon={normalizeIcon(model.icon) as IconProp} />}\n />\n </div>\n );\n};\n\nconst ContentModelsDataList = observer(\n ({ canCreate, onCreate, onClone, showImportModelModal }: ContentModelsDataListProps) => {\n const presenter = useContentModelsPresenter();\n const { goToRoute } = useRouter();\n const { showSnackbar } = useSnackbar();\n const { canDelete, canEdit } = usePermission();\n\n const [filter, setFilter] = useState(\"\");\n const [sort, setSort] = useState(SORTERS[0].sorter);\n const [modelToBeDeleted, setModelToBeDeleted] = useState<CmsModel | null>(null);\n\n const models = presenter.vm.models;\n const loading = presenter.vm.loading;\n\n const filteredData = useMemo(() => {\n if (filter === \"\") {\n return models;\n }\n const lc = filter.toLowerCase();\n return models.filter((m: CmsModel) => m.name.toLowerCase().includes(lc));\n }, [models, filter]);\n\n const sortedData = useMemo(() => {\n if (!sort) {\n return filteredData;\n }\n const [key, order] = deserializeSorters(sort);\n return orderBy(filteredData, [key], [order]);\n }, [filteredData, sort]);\n\n const editRecord = (contentModel: CmsModel): void => {\n goToRoute(Routes.ContentModels.Editor, { modelId: contentModel.modelId });\n };\n\n const viewContentEntries = useCallback((contentModel: Pick<CmsModel, \"modelId\">) => {\n return () => {\n goToRoute(Routes.ContentEntries.List, { modelId: contentModel.modelId });\n };\n }, []);\n\n const handleModelsExport = useCallback(\n (modelIds?: string[]) => {\n (async () => {\n try {\n const data = await presenter.exportModels(modelIds);\n if (!data || !data.models || data.models.length === 0) {\n showSnackbar(\"No data returned from the export query.\");\n return;\n }\n download(data);\n } catch (e: any) {\n showSnackbar(e.message);\n }\n })();\n },\n [presenter]\n );\n\n const handleModelExport = useCallback(\n (model: CmsModel) => {\n return () => {\n handleModelsExport([model.modelId]);\n };\n },\n [handleModelsExport]\n );\n\n return (\n <>\n <DataList\n loading={loading}\n data={sortedData}\n title={t`Content Models`}\n actions={\n <div className={\"flex items-center justify-end gap-xs\"}>\n <Tooltip\n content={\"Export all models\"}\n trigger={\n <IconButton\n icon={<DownloadFileIcon />}\n size={\"sm\"}\n variant={\"ghost\"}\n onClick={() => handleModelsExport()}\n />\n }\n />\n <Tooltip\n content={\"Import models\"}\n trigger={\n <IconButton\n icon={<UploadFileIcon />}\n size={\"sm\"}\n variant={\"ghost\"}\n onClick={() => showImportModelModal()}\n />\n }\n />\n {canCreate ? (\n <Button\n data-testid=\"new-record-button\"\n onClick={onCreate}\n text={t`New`}\n icon={<AddIcon />}\n size={\"sm\"}\n className={\"ml-xs\"}\n />\n ) : null}\n </div>\n }\n search={\n <SearchUI\n value={filter}\n onChange={setFilter}\n inputPlaceholder={t`Search content model...`}\n />\n }\n modalOverlay={\n <DataListModal.Content>\n <Select\n value={sort}\n onChange={setSort}\n label={t`Sort by`}\n options={SORTERS.map(({ label, sorter: value }) => ({\n label,\n value\n }))}\n />\n </DataListModal.Content>\n }\n modalOverlayAction={\n <DataListModal.Trigger data-testid={\"default-data-list.filter\"} />\n }\n refresh={null}\n >\n {({ data = [] }: { data: CmsModel[] }) => {\n return (\n <List data-testid=\"default-data-list\">\n {data.map(contentModel => {\n const disableViewContent = contentModel.fields.length === 0;\n const getMessage = () => {\n if (disableViewContent) {\n return \"To view the entries, you first need to add a field and save the model\";\n }\n if (contentModel.tags.includes(CMS_MODEL_SINGLETON_TAG)) {\n return \"View\";\n }\n return \"View entries\";\n };\n\n const canDeleteModel = canDelete(\n contentModel,\n \"cms.contentModel\"\n );\n const canEditModel = canEdit(contentModel, \"cms.contentModel\");\n\n return (\n <List.Item\n key={contentModel.modelId}\n className={\"group/item\"}\n icon={<DisplayIcon model={contentModel} />}\n title={contentModel.name}\n description={t`Last modified: {time}.`({\n time: contentModel.savedOn ? (\n <TimeAgo datetime={contentModel.savedOn} />\n ) : (\n \"N/A\"\n )\n })}\n actions={\n <>\n <ModelIsBeingDeleted model={contentModel}>\n <span\n className={\n \"invisible group-hover/item:visible\"\n }\n >\n <Tooltip\n side={\"top\"}\n content={getMessage()}\n trigger={\n <Button\n text={\"View entries\"}\n icon={<ListIcon />}\n variant={\"secondary\"}\n size={\"sm\"}\n data-testid={\n \"cms-view-content-model-button\"\n }\n onClick={viewContentEntries(\n contentModel\n )}\n disabled={\n disableViewContent\n }\n />\n }\n />\n </span>\n\n {canEditModel && (\n <span\n className={\n \"invisible group-hover/item:visible\"\n }\n >\n <Tooltip\n side={\"top\"}\n content={\n contentModel.plugin\n ? t`Content model is registered via a plugin.`\n : t`Edit content model`\n }\n trigger={\n <Button\n text={\"Edit\"}\n icon={<EditIcon />}\n variant={\"secondary\"}\n size={\"sm\"}\n disabled={\n contentModel.plugin\n }\n onClick={() =>\n editRecord(\n contentModel\n )\n }\n data-testid={\n \"cms-edit-content-model-button\"\n }\n />\n }\n />\n </span>\n )}\n\n <DropdownMenu\n trigger={\n <IconButton\n icon={<MoreVertIcon />}\n size={\"sm\"}\n variant={\"ghost\"}\n />\n }\n >\n {canEditModel && (\n <DropdownMenu.Item\n text={\"Clone\"}\n icon={\n <DropdownMenu.Item.Icon\n label={\"Clone\"}\n element={<CloneIcon />}\n />\n }\n data-testid={\n \"cms-clone-content-model-button\"\n }\n onClick={() =>\n onClone(contentModel)\n }\n />\n )}\n\n <DropdownMenu.Item\n text={\"Export\"}\n icon={\n <DropdownMenu.Item.Icon\n label={\"Export\"}\n element={<ExportIcon />}\n />\n }\n data-testid={\n \"cms-export-content-model-button\"\n }\n onClick={handleModelExport(\n contentModel\n )}\n />\n\n {canDeleteModel && (\n <DropdownMenu.Item\n text={\"Delete\"}\n icon={\n <DropdownMenu.Item.Icon\n label={\"Delete\"}\n element={<DeleteIcon />}\n />\n }\n onClick={() => {\n setModelToBeDeleted(\n contentModel\n );\n }}\n data-testid={\n \"cms-delete-content-model-button\"\n }\n className={\n \"text-destructive-primary! [&_svg]:fill-destructive\"\n }\n />\n )}\n </DropdownMenu>\n </ModelIsBeingDeleted>\n </>\n }\n />\n );\n })}\n </List>\n );\n }}\n </DataList>\n {modelToBeDeleted ? (\n <FullyDeleteModelDialog\n model={modelToBeDeleted}\n onClose={() => {\n setModelToBeDeleted(null);\n }}\n />\n ) : null}\n </>\n );\n }\n);\n\nexport default ContentModelsDataList;\n"],"names":["t","i18n","SORTERS","deserializeSorters","data","field","order","DisplayIcon","model","Icon","FontAwesomeIcon","normalizeIcon","ContentModelsDataList","observer","canCreate","onCreate","onClone","showImportModelModal","presenter","useContentModelsPresenter","goToRoute","useRouter","showSnackbar","useSnackbar","canDelete","canEdit","usePermission","filter","setFilter","useState","sort","setSort","modelToBeDeleted","setModelToBeDeleted","models","loading","filteredData","useMemo","lc","m","sortedData","key","orderBy","editRecord","contentModel","Routes","viewContentEntries","useCallback","handleModelsExport","modelIds","download","e","handleModelExport","DataList","Tooltip","IconButton","DownloadFileIcon","UploadFileIcon","Button","AddIcon","SearchUI","DataListModal","Select","label","value","List","disableViewContent","getMessage","CMS_MODEL_SINGLETON_TAG","canDeleteModel","canEditModel","TimeAgo","ModelIsBeingDeleted","ListIcon","EditIcon","DropdownMenu","MoreVertIcon","CloneIcon","ExportIcon","DeleteIcon","FullyDeleteModelDialog"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"presentation/contentModels/components/ContentModelsDataList.js","sources":["../../../../src/presentation/contentModels/components/ContentModelsDataList.tsx"],"sourcesContent":["import React, { useCallback, useMemo, useState } from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport orderBy from \"lodash/orderBy.js\";\nimport { TimeAgo } from \"@webiny/admin-ui\";\nimport { SearchUI, useRouter, useSnackbar } from \"@webiny/app-admin\";\nimport { ReactComponent as AddIcon } from \"@webiny/icons/add.svg\";\nimport {\n ReactComponent as DownloadFileIcon,\n ReactComponent as ExportIcon\n} from \"@webiny/icons/file_download.svg\";\nimport { ReactComponent as UploadFileIcon } from \"@webiny/icons/file_upload.svg\";\nimport { ReactComponent as ListIcon } from \"@webiny/icons/list.svg\";\nimport { ReactComponent as EditIcon } from \"@webiny/icons/edit.svg\";\nimport { ReactComponent as MoreVertIcon } from \"@webiny/icons/more_vert.svg\";\nimport { ReactComponent as DeleteIcon } from \"@webiny/icons/delete.svg\";\nimport { ReactComponent as CloneIcon } from \"@webiny/icons/flip_to_front.svg\";\nimport { DataList, DataListModal, List } from \"@webiny/admin-ui\";\nimport { i18n } from \"@webiny/app/i18n/index.js\";\nimport type { CmsModel } from \"~/types.js\";\nimport { usePermission } from \"~/admin/hooks/usePermission.js\";\nimport { FontAwesomeIcon } from \"@fortawesome/react-fontawesome\";\nimport { ModelIsBeingDeleted } from \"./fullDelete/ModelIsBeingDeleted.js\";\nimport { FullyDeleteModelDialog } from \"./fullDelete/FullyDeleteModelDialog.js\";\nimport { Button, DropdownMenu, Icon, IconButton, Select, Tooltip } from \"@webiny/admin-ui\";\nimport { CMS_MODEL_SINGLETON_TAG } from \"@webiny/app-headless-cms-common\";\nimport { Routes } from \"~/routes.js\";\nimport { normalizeIcon } from \"~/utils/normalizeIcon.js\";\nimport { useContentModelsPresenter } from \"../useContentModelsPresenter.js\";\nimport { download } from \"../utils/download.js\";\n\nconst t = i18n.namespace(\"FormsApp.ContentModelsDataList\");\n\nconst SORTERS = [\n { label: \"Newest to oldest\", sorter: \"savedOn_DESC\" },\n { label: \"Oldest to newest\", sorter: \"savedOn_ASC\" },\n { label: \"Name A-Z\", sorter: \"name_ASC\" },\n { label: \"Name Z-A\", sorter: \"name_DESC\" }\n];\n\nconst deserializeSorters = (data: string): [string, \"asc\" | \"desc\"] => {\n const [field, order] = data.split(\"_\");\n return [field, order.toLowerCase() === \"asc\" ? \"asc\" : \"desc\"];\n};\n\ninterface ContentModelsDataListProps {\n canCreate: boolean;\n onCreate: () => void;\n onClone: (contentModel: CmsModel) => void;\n showImportModelModal: () => void;\n}\n\ninterface IconProps {\n model: Pick<CmsModel, \"icon\">;\n}\n\nconst DisplayIcon = ({ model }: IconProps) => {\n const icon = normalizeIcon(model.icon);\n if (!icon) {\n return null;\n }\n return (\n <div className={\"text-neutral-muted\"}>\n <Icon\n size={\"lg\"}\n color={\"inherit\"}\n label={\"Content model icon\"}\n icon={<FontAwesomeIcon icon={icon} />}\n />\n </div>\n );\n};\n\nconst ContentModelsDataList = observer(\n ({ canCreate, onCreate, onClone, showImportModelModal }: ContentModelsDataListProps) => {\n const presenter = useContentModelsPresenter();\n const { goToRoute } = useRouter();\n const { showSnackbar } = useSnackbar();\n const { canDelete, canEdit } = usePermission();\n\n const [filter, setFilter] = useState(\"\");\n const [sort, setSort] = useState(SORTERS[0].sorter);\n const [modelToBeDeleted, setModelToBeDeleted] = useState<CmsModel | null>(null);\n\n const models = presenter.vm.models;\n const loading = presenter.vm.loading;\n\n const filteredData = useMemo(() => {\n if (filter === \"\") {\n return models;\n }\n const lc = filter.toLowerCase();\n return models.filter((m: CmsModel) => m.name.toLowerCase().includes(lc));\n }, [models, filter]);\n\n const sortedData = useMemo(() => {\n if (!sort) {\n return filteredData;\n }\n const [key, order] = deserializeSorters(sort);\n return orderBy(filteredData, [key], [order]);\n }, [filteredData, sort]);\n\n const editRecord = (contentModel: CmsModel): void => {\n goToRoute(Routes.ContentModels.Editor, { modelId: contentModel.modelId });\n };\n\n const viewContentEntries = useCallback((contentModel: Pick<CmsModel, \"modelId\">) => {\n return () => {\n goToRoute(Routes.ContentEntries.List, { modelId: contentModel.modelId });\n };\n }, []);\n\n const handleModelsExport = useCallback(\n (modelIds?: string[]) => {\n (async () => {\n try {\n const data = await presenter.exportModels(modelIds);\n if (!data?.models?.length) {\n showSnackbar(\"No data returned from the export query.\");\n return;\n }\n download(data);\n } catch (ex: any) {\n showSnackbar(ex.message);\n }\n })();\n },\n [presenter]\n );\n\n const handleModelExport = useCallback(\n (model: CmsModel) => {\n return () => {\n handleModelsExport([model.modelId]);\n };\n },\n [handleModelsExport]\n );\n\n return (\n <>\n <DataList\n loading={loading}\n data={sortedData}\n title={t`Content Models`}\n actions={\n <div className={\"flex items-center justify-end gap-xs\"}>\n <Tooltip\n content={\"Export all models\"}\n trigger={\n <IconButton\n icon={<DownloadFileIcon />}\n size={\"sm\"}\n variant={\"ghost\"}\n onClick={() => handleModelsExport()}\n />\n }\n />\n <Tooltip\n content={\"Import models\"}\n trigger={\n <IconButton\n icon={<UploadFileIcon />}\n size={\"sm\"}\n variant={\"ghost\"}\n onClick={() => showImportModelModal()}\n />\n }\n />\n {canCreate ? (\n <Button\n data-testid=\"new-record-button\"\n onClick={onCreate}\n text={t`New`}\n icon={<AddIcon />}\n size={\"sm\"}\n className={\"ml-xs\"}\n />\n ) : null}\n </div>\n }\n search={\n <SearchUI\n value={filter}\n onChange={setFilter}\n inputPlaceholder={t`Search content model...`}\n />\n }\n modalOverlay={\n <DataListModal.Content>\n <Select\n value={sort}\n onChange={setSort}\n label={t`Sort by`}\n options={SORTERS.map(({ label, sorter: value }) => ({\n label,\n value\n }))}\n />\n </DataListModal.Content>\n }\n modalOverlayAction={\n <DataListModal.Trigger data-testid={\"default-data-list.filter\"} />\n }\n refresh={null}\n >\n {({ data = [] }: { data: CmsModel[] }) => {\n return (\n <List data-testid=\"default-data-list\">\n {data.map(contentModel => {\n const disableViewContent = contentModel.fields.length === 0;\n const getMessage = () => {\n if (disableViewContent) {\n return \"To view the entries, you first need to add a field and save the model\";\n }\n if (contentModel.tags.includes(CMS_MODEL_SINGLETON_TAG)) {\n return \"View\";\n }\n return \"View entries\";\n };\n\n const canDeleteModel = canDelete(\n contentModel,\n \"cms.contentModel\"\n );\n const canEditModel = canEdit(contentModel, \"cms.contentModel\");\n\n return (\n <List.Item\n key={contentModel.modelId}\n className={\"group/item\"}\n icon={<DisplayIcon model={contentModel} />}\n title={contentModel.name}\n description={t`Last modified: {time}.`({\n time: contentModel.savedOn ? (\n <TimeAgo datetime={contentModel.savedOn} />\n ) : (\n \"N/A\"\n )\n })}\n actions={\n <>\n <ModelIsBeingDeleted model={contentModel}>\n <span\n className={\n \"invisible group-hover/item:visible\"\n }\n >\n <Tooltip\n side={\"top\"}\n content={getMessage()}\n trigger={\n <Button\n text={\"View entries\"}\n icon={<ListIcon />}\n variant={\"secondary\"}\n size={\"sm\"}\n data-testid={\n \"cms-view-content-model-button\"\n }\n onClick={viewContentEntries(\n contentModel\n )}\n disabled={\n disableViewContent\n }\n />\n }\n />\n </span>\n\n {canEditModel && (\n <span\n className={\n \"invisible group-hover/item:visible\"\n }\n >\n <Tooltip\n side={\"top\"}\n content={\n contentModel.plugin\n ? t`Content model is registered via a plugin.`\n : t`Edit content model`\n }\n trigger={\n <Button\n text={\"Edit\"}\n icon={<EditIcon />}\n variant={\"secondary\"}\n size={\"sm\"}\n disabled={\n contentModel.plugin\n }\n onClick={() =>\n editRecord(\n contentModel\n )\n }\n data-testid={\n \"cms-edit-content-model-button\"\n }\n />\n }\n />\n </span>\n )}\n\n <DropdownMenu\n trigger={\n <IconButton\n icon={<MoreVertIcon />}\n size={\"sm\"}\n variant={\"ghost\"}\n />\n }\n >\n {canEditModel && (\n <DropdownMenu.Item\n text={\"Clone\"}\n icon={\n <DropdownMenu.Item.Icon\n label={\"Clone\"}\n element={<CloneIcon />}\n />\n }\n data-testid={\n \"cms-clone-content-model-button\"\n }\n onClick={() =>\n onClone(contentModel)\n }\n />\n )}\n\n <DropdownMenu.Item\n text={\"Export\"}\n icon={\n <DropdownMenu.Item.Icon\n label={\"Export\"}\n element={<ExportIcon />}\n />\n }\n data-testid={\n \"cms-export-content-model-button\"\n }\n onClick={handleModelExport(\n contentModel\n )}\n />\n\n {canDeleteModel && (\n <DropdownMenu.Item\n text={\"Delete\"}\n icon={\n <DropdownMenu.Item.Icon\n label={\"Delete\"}\n element={<DeleteIcon />}\n />\n }\n onClick={() => {\n setModelToBeDeleted(\n contentModel\n );\n }}\n data-testid={\n \"cms-delete-content-model-button\"\n }\n className={\n \"text-destructive-primary! [&_svg]:fill-destructive\"\n }\n />\n )}\n </DropdownMenu>\n </ModelIsBeingDeleted>\n </>\n }\n />\n );\n })}\n </List>\n );\n }}\n </DataList>\n {modelToBeDeleted ? (\n <FullyDeleteModelDialog\n model={modelToBeDeleted}\n onClose={() => {\n setModelToBeDeleted(null);\n }}\n />\n ) : null}\n </>\n );\n }\n);\n\nexport default ContentModelsDataList;\n"],"names":["t","i18n","SORTERS","deserializeSorters","data","field","order","DisplayIcon","model","icon","normalizeIcon","Icon","FontAwesomeIcon","ContentModelsDataList","observer","canCreate","onCreate","onClone","showImportModelModal","presenter","useContentModelsPresenter","goToRoute","useRouter","showSnackbar","useSnackbar","canDelete","canEdit","usePermission","filter","setFilter","useState","sort","setSort","modelToBeDeleted","setModelToBeDeleted","models","loading","filteredData","useMemo","lc","m","sortedData","key","orderBy","editRecord","contentModel","Routes","viewContentEntries","useCallback","handleModelsExport","modelIds","download","ex","handleModelExport","DataList","Tooltip","IconButton","DownloadFileIcon","UploadFileIcon","Button","AddIcon","SearchUI","DataListModal","Select","label","value","List","disableViewContent","getMessage","CMS_MODEL_SINGLETON_TAG","canDeleteModel","canEditModel","TimeAgo","ModelIsBeingDeleted","ListIcon","EditIcon","DropdownMenu","MoreVertIcon","CloneIcon","ExportIcon","DeleteIcon","FullyDeleteModelDialog"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAMA,IAAIC,KAAK,SAAS,CAAC;AAEzB,MAAMC,UAAU;IACZ;QAAE,OAAO;QAAoB,QAAQ;IAAe;IACpD;QAAE,OAAO;QAAoB,QAAQ;IAAc;IACnD;QAAE,OAAO;QAAY,QAAQ;IAAW;IACxC;QAAE,OAAO;QAAY,QAAQ;IAAY;CAC5C;AAED,MAAMC,qBAAqB,CAACC;IACxB,MAAM,CAACC,OAAOC,MAAM,GAAGF,KAAK,KAAK,CAAC;IAClC,OAAO;QAACC;QAA+B,UAAxBC,MAAM,WAAW,KAAe,QAAQ;KAAO;AAClE;AAaA,MAAMC,cAAc,CAAC,EAAEC,KAAK,EAAa;IACrC,MAAMC,OAAOC,cAAcF,MAAM,IAAI;IACrC,IAAI,CAACC,MACD,OAAO;IAEX,OAAO,WAAP,GACI,oBAAC;QAAI,WAAW;qBACZ,oBAACE,MAAIA;QACD,MAAM;QACN,OAAO;QACP,OAAO;QACP,oBAAM,oBAACC,iBAAeA;YAAC,MAAMH;;;AAI7C;AAEA,MAAMI,wBAAwBC,SAC1B,CAAC,EAAEC,SAAS,EAAEC,QAAQ,EAAEC,OAAO,EAAEC,oBAAoB,EAA8B;IAC/E,MAAMC,YAAYC;IAClB,MAAM,EAAEC,SAAS,EAAE,GAAGC;IACtB,MAAM,EAAEC,YAAY,EAAE,GAAGC;IACzB,MAAM,EAAEC,SAAS,EAAEC,OAAO,EAAE,GAAGC;IAE/B,MAAM,CAACC,QAAQC,UAAU,GAAGC,SAAS;IACrC,MAAM,CAACC,MAAMC,QAAQ,GAAGF,SAAS5B,OAAO,CAAC,EAAE,CAAC,MAAM;IAClD,MAAM,CAAC+B,kBAAkBC,oBAAoB,GAAGJ,SAA0B;IAE1E,MAAMK,SAAShB,UAAU,EAAE,CAAC,MAAM;IAClC,MAAMiB,UAAUjB,UAAU,EAAE,CAAC,OAAO;IAEpC,MAAMkB,eAAeC,QAAQ;QACzB,IAAIV,AAAW,OAAXA,QACA,OAAOO;QAEX,MAAMI,KAAKX,OAAO,WAAW;QAC7B,OAAOO,OAAO,MAAM,CAAC,CAACK,IAAgBA,EAAE,IAAI,CAAC,WAAW,GAAG,QAAQ,CAACD;IACxE,GAAG;QAACJ;QAAQP;KAAO;IAEnB,MAAMa,aAAaH,QAAQ;QACvB,IAAI,CAACP,MACD,OAAOM;QAEX,MAAM,CAACK,KAAKpC,MAAM,GAAGH,mBAAmB4B;QACxC,OAAOY,QAAQN,cAAc;YAACK;SAAI,EAAE;YAACpC;SAAM;IAC/C,GAAG;QAAC+B;QAAcN;KAAK;IAEvB,MAAMa,aAAa,CAACC;QAChBxB,UAAUyB,OAAO,aAAa,CAAC,MAAM,EAAE;YAAE,SAASD,aAAa,OAAO;QAAC;IAC3E;IAEA,MAAME,qBAAqBC,YAAY,CAACH,eAC7B;YACHxB,UAAUyB,OAAO,cAAc,CAAC,IAAI,EAAE;gBAAE,SAASD,aAAa,OAAO;YAAC;QAC1E,GACD,EAAE;IAEL,MAAMI,qBAAqBD,YACvB,CAACE;QACI;YACG,IAAI;gBACA,MAAM9C,OAAO,MAAMe,UAAU,YAAY,CAAC+B;gBAC1C,IAAI,CAAC9C,MAAM,QAAQ,QAAQ,YACvBmB,aAAa;gBAGjB4B,SAAS/C;YACb,EAAE,OAAOgD,IAAS;gBACd7B,aAAa6B,GAAG,OAAO;YAC3B;QACJ;IACJ,GACA;QAACjC;KAAU;IAGf,MAAMkC,oBAAoBL,YACtB,CAACxC,QACU;YACHyC,mBAAmB;gBAACzC,MAAM,OAAO;aAAC;QACtC,GAEJ;QAACyC;KAAmB;IAGxB,OAAO,WAAP,GACI,wDACI,oBAACK,UAAQA;QACL,SAASlB;QACT,MAAMK;QACN,OAAOzC,CAAC,CAAC,cAAc,CAAC;QACxB,uBACI,oBAAC;YAAI,WAAW;yBACZ,oBAACuD,SAAOA;YACJ,SAAS;YACT,uBACI,oBAACC,YAAUA;gBACP,oBAAM,oBAACC,kCAAgBA;gBACvB,MAAM;gBACN,SAAS;gBACT,SAAS,IAAMR;;0BAI3B,oBAACM,SAAOA;YACJ,SAAS;YACT,uBACI,oBAACC,YAAUA;gBACP,oBAAM,oBAACE,gCAAcA;gBACrB,MAAM;gBACN,SAAS;gBACT,SAAS,IAAMxC;;YAI1BH,YAAY,WAAZA,GACG,oBAAC4C,QAAMA;YACH,eAAY;YACZ,SAAS3C;YACT,MAAMhB,CAAC,CAAC,GAAG,CAAC;YACZ,oBAAM,oBAAC4D,gBAAOA;YACd,MAAM;YACN,WAAW;aAEf;QAGZ,sBACI,oBAACC,UAAQA;YACL,OAAOjC;YACP,UAAUC;YACV,kBAAkB7B,CAAC,CAAC,uBAAuB,CAAC;;QAGpD,4BACI,oBAAC8D,cAAc,OAAO,sBAClB,oBAACC,QAAMA;YACH,OAAOhC;YACP,UAAUC;YACV,OAAOhC,CAAC,CAAC,OAAO,CAAC;YACjB,SAASE,QAAQ,GAAG,CAAC,CAAC,EAAE8D,KAAK,EAAE,QAAQC,KAAK,EAAE,GAAM;oBAChDD;oBACAC;gBACJ;;QAIZ,kCACI,oBAACH,cAAc,OAAO;YAAC,eAAa;;QAExC,SAAS;OAER,CAAC,EAAE1D,OAAO,EAAE,EAAwB,GAC1B,WAAP,GACI,oBAAC8D,MAAIA;YAAC,eAAY;WACb9D,KAAK,GAAG,CAACyC,CAAAA;YACN,MAAMsB,qBAAqBtB,AAA+B,MAA/BA,aAAa,MAAM,CAAC,MAAM;YACrD,MAAMuB,aAAa;gBACf,IAAID,oBACA,OAAO;gBAEX,IAAItB,aAAa,IAAI,CAAC,QAAQ,CAACwB,0BAC3B,OAAO;gBAEX,OAAO;YACX;YAEA,MAAMC,iBAAiB7C,UACnBoB,cACA;YAEJ,MAAM0B,eAAe7C,QAAQmB,cAAc;YAE3C,OAAO,WAAP,GACI,oBAACqB,KAAK,IAAI;gBACN,KAAKrB,aAAa,OAAO;gBACzB,WAAW;gBACX,oBAAM,oBAACtC,aAAWA;oBAAC,OAAOsC;;gBAC1B,OAAOA,aAAa,IAAI;gBACxB,aAAa7C,CAAC,CAAC,sBAAsB,CAAC,CAAC;oBACnC,MAAM6C,aAAa,OAAO,GAAG,WAAH,GACtB,oBAAC2B,SAAOA;wBAAC,UAAU3B,aAAa,OAAO;yBAEvC;gBAER;gBACA,uBACI,wDACI,oBAAC4B,qBAAmBA;oBAAC,OAAO5B;iCACxB,oBAAC;oBACG,WACI;iCAGJ,oBAACU,SAAOA;oBACJ,MAAM;oBACN,SAASa;oBACT,uBACI,oBAACT,QAAMA;wBACH,MAAM;wBACN,oBAAM,oBAACe,yBAAQA;wBACf,SAAS;wBACT,MAAM;wBACN,eACI;wBAEJ,SAAS3B,mBACLF;wBAEJ,UACIsB;;qBAOnBI,gBAAgB,WAAhBA,GACG,oBAAC;oBACG,WACI;iCAGJ,oBAAChB,SAAOA;oBACJ,MAAM;oBACN,SACIV,aAAa,MAAM,GACb7C,CAAC,CAAC,yCAAyC,CAAC,GAC5CA,CAAC,CAAC,kBAAkB,CAAC;oBAE/B,uBACI,oBAAC2D,QAAMA;wBACH,MAAM;wBACN,oBAAM,oBAACgB,yBAAQA;wBACf,SAAS;wBACT,MAAM;wBACN,UACI9B,aAAa,MAAM;wBAEvB,SAAS,IACLD,WACIC;wBAGR,eACI;;mCAQxB,oBAAC+B,cAAYA;oBACT,uBACI,oBAACpB,YAAUA;wBACP,oBAAM,oBAACqB,8BAAYA;wBACnB,MAAM;wBACN,SAAS;;mBAIhBN,gBAAgB,WAAhBA,GACG,oBAACK,aAAa,IAAI;oBACd,MAAM;oBACN,oBACI,oBAACA,aAAa,IAAI,CAAC,IAAI;wBACnB,OAAO;wBACP,uBAAS,oBAACE,kCAASA;;oBAG3B,eACI;oBAEJ,SAAS,IACL7D,QAAQ4B;kCAKpB,oBAAC+B,aAAa,IAAI;oBACd,MAAM;oBACN,oBACI,oBAACA,aAAa,IAAI,CAAC,IAAI;wBACnB,OAAO;wBACP,uBAAS,oBAACG,kCAAUA;;oBAG5B,eACI;oBAEJ,SAAS1B,kBACLR;oBAIPyB,kBAAkB,WAAlBA,GACG,oBAACM,aAAa,IAAI;oBACd,MAAM;oBACN,oBACI,oBAACA,aAAa,IAAI,CAAC,IAAI;wBACnB,OAAO;wBACP,uBAAS,oBAACI,2BAAUA;;oBAG5B,SAAS;wBACL9C,oBACIW;oBAER;oBACA,eACI;oBAEJ,WACI;;;QAUxC,MAKfZ,mBAAmB,WAAnBA,GACG,oBAACgD,wBAAsBA;QACnB,OAAOhD;QACP,SAAS;YACLC,oBAAoB;QACxB;SAEJ;AAGhB;AAGJ,yCAAerB"}
|
|
@@ -4,7 +4,7 @@ export interface NewContentModelDialogProps {
|
|
|
4
4
|
open: boolean;
|
|
5
5
|
onClose: DialogProps["onClose"];
|
|
6
6
|
}
|
|
7
|
-
declare const NewContentModelDialog: (({ open, onClose }: NewContentModelDialogProps) => React.JSX.Element) & {
|
|
8
|
-
|
|
7
|
+
declare const NewContentModelDialog: (({ open, onClose }: NewContentModelDialogProps) => React.JSX.Element) & React.NamedExoticComponent<NewContentModelDialogProps> & {
|
|
8
|
+
readonly type: ({ open, onClose }: NewContentModelDialogProps) => React.JSX.Element;
|
|
9
9
|
};
|
|
10
10
|
export default NewContentModelDialog;
|
|
@@ -6,10 +6,10 @@ import { OverlayLoader } from "@webiny/admin-ui";
|
|
|
6
6
|
import { Processed } from "./Processed.js";
|
|
7
7
|
const Content = ({ confirmation, setConfirmation, error, model, status })=>{
|
|
8
8
|
if (!model) return /*#__PURE__*/ react.createElement("p", null, "Model not found.");
|
|
9
|
-
if (status === FullyDeleteModelStateStatus.NONE) return /*#__PURE__*/ react.createElement(Information, {
|
|
9
|
+
else if (status === FullyDeleteModelStateStatus.NONE) return /*#__PURE__*/ react.createElement(Information, {
|
|
10
10
|
model: model
|
|
11
11
|
});
|
|
12
|
-
if (status === FullyDeleteModelStateStatus.PROCESSED) return /*#__PURE__*/ react.createElement(Processed, {
|
|
12
|
+
else if (status === FullyDeleteModelStateStatus.PROCESSED) return /*#__PURE__*/ react.createElement(Processed, {
|
|
13
13
|
model: model
|
|
14
14
|
});
|
|
15
15
|
return /*#__PURE__*/ react.createElement(react.Fragment, null, status === FullyDeleteModelStateStatus.CONFIRMED && /*#__PURE__*/ react.createElement(OverlayLoader, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation/contentModels/components/fullDelete/dialog/Content.js","sources":["../../../../../../src/presentation/contentModels/components/fullDelete/dialog/Content.tsx"],"sourcesContent":["import React from \"react\";\nimport type { CmsErrorResponse, CmsModel } from \"~/types.js\";\nimport { FullyDeleteModelStateStatus } from \"../types.js\";\nimport { Information } from \"./Information.js\";\nimport { Confirmation } from \"./Confirmation.js\";\nimport { OverlayLoader } from \"@webiny/admin-ui\";\nimport { Processed } from \"./Processed.js\";\n\nexport interface IContentProps {\n model: CmsModel | null;\n confirmation: string;\n setConfirmation: (value: string) => void;\n error: CmsErrorResponse | null;\n status: FullyDeleteModelStateStatus;\n}\n\nexport const Content = ({ confirmation, setConfirmation, error, model, status }: IContentProps) => {\n if (!model) {\n return <p>Model not found.</p>;\n } else if (status === FullyDeleteModelStateStatus.NONE) {\n return <Information model={model} />;\n } else if (status === FullyDeleteModelStateStatus.PROCESSED) {\n return <Processed model={model} />;\n }\n\n return (\n <>\n {status === FullyDeleteModelStateStatus.CONFIRMED && (\n <OverlayLoader text={\"Starting the deletion process...\"} />\n )}\n <Confirmation\n model={model}\n setConfirmation={setConfirmation}\n confirmation={confirmation}\n error={error}\n />\n </>\n );\n};\n"],"names":["Content","confirmation","setConfirmation","error","model","status","FullyDeleteModelStateStatus","Information","Processed","OverlayLoader","Confirmation"],"mappings":";;;;;;AAgBO,MAAMA,UAAU,CAAC,EAAEC,YAAY,EAAEC,eAAe,EAAEC,KAAK,EAAEC,KAAK,EAAEC,MAAM,EAAiB;IAC1F,IAAI,CAACD,OACD,OAAO,WAAP,GAAO,oBAAC,WAAE;
|
|
1
|
+
{"version":3,"file":"presentation/contentModels/components/fullDelete/dialog/Content.js","sources":["../../../../../../src/presentation/contentModels/components/fullDelete/dialog/Content.tsx"],"sourcesContent":["import React from \"react\";\nimport type { CmsErrorResponse, CmsModel } from \"~/types.js\";\nimport { FullyDeleteModelStateStatus } from \"../types.js\";\nimport { Information } from \"./Information.js\";\nimport { Confirmation } from \"./Confirmation.js\";\nimport { OverlayLoader } from \"@webiny/admin-ui\";\nimport { Processed } from \"./Processed.js\";\n\nexport interface IContentProps {\n model: CmsModel | null;\n confirmation: string;\n setConfirmation: (value: string) => void;\n error: CmsErrorResponse | null;\n status: FullyDeleteModelStateStatus;\n}\n\nexport const Content = ({ confirmation, setConfirmation, error, model, status }: IContentProps) => {\n if (!model) {\n return <p>Model not found.</p>;\n } else if (status === FullyDeleteModelStateStatus.NONE) {\n return <Information model={model} />;\n } else if (status === FullyDeleteModelStateStatus.PROCESSED) {\n return <Processed model={model} />;\n }\n\n return (\n <>\n {status === FullyDeleteModelStateStatus.CONFIRMED && (\n <OverlayLoader text={\"Starting the deletion process...\"} />\n )}\n <Confirmation\n model={model}\n setConfirmation={setConfirmation}\n confirmation={confirmation}\n error={error}\n />\n </>\n );\n};\n"],"names":["Content","confirmation","setConfirmation","error","model","status","FullyDeleteModelStateStatus","Information","Processed","OverlayLoader","Confirmation"],"mappings":";;;;;;AAgBO,MAAMA,UAAU,CAAC,EAAEC,YAAY,EAAEC,eAAe,EAAEC,KAAK,EAAEC,KAAK,EAAEC,MAAM,EAAiB;IAC1F,IAAI,CAACD,OACD,OAAO,WAAP,GAAO,oBAAC,WAAE;SACP,IAAIC,WAAWC,4BAA4B,IAAI,EAClD,OAAO,WAAP,GAAO,oBAACC,aAAWA;QAAC,OAAOH;;SACxB,IAAIC,WAAWC,4BAA4B,SAAS,EACvD,OAAO,WAAP,GAAO,oBAACE,WAASA;QAAC,OAAOJ;;IAG7B,OAAO,WAAP,GACI,0CACKC,WAAWC,4BAA4B,SAAS,IAAI,WAAJ,GAC7C,oBAACG,eAAaA;QAAC,MAAM;sBAEzB,oBAACC,cAAYA;QACT,OAAON;QACP,iBAAiBF;QACjB,cAAcD;QACd,OAAOE;;AAIvB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation/contentModels/components/fullDelete/types.js","sources":["../../../../../src/presentation/contentModels/components/fullDelete/types.ts"],"sourcesContent":["import type { CmsErrorResponse, CmsModel } from \"~/types.js\";\nimport type { IDeleteCmsModelTask } from \"~/admin/viewsGraphql.js\";\n\nexport enum FullyDeleteModelStateStatus {\n NONE = 0,\n UNDERSTOOD = 1,\n CONFIRMED = 2,\n PROCESSED = 3,\n ERROR = 999\n}\n\nexport interface IFullyDeleteModelState {\n status: FullyDeleteModelStateStatus;\n confirmation: string;\n model: CmsModel;\n error: CmsErrorResponse | null;\n task: IDeleteCmsModelTask | null;\n}\n"],"names":["FullyDeleteModelStateStatus"],"mappings":"AAGO,IAAKA,oCAA2BA,WAAAA,GAAAA,SAA3BA,2BAA2B
|
|
1
|
+
{"version":3,"file":"presentation/contentModels/components/fullDelete/types.js","sources":["../../../../../src/presentation/contentModels/components/fullDelete/types.ts"],"sourcesContent":["import type { CmsErrorResponse, CmsModel } from \"~/types.js\";\nimport type { IDeleteCmsModelTask } from \"~/admin/viewsGraphql.js\";\n\nexport enum FullyDeleteModelStateStatus {\n NONE = 0,\n UNDERSTOOD = 1,\n CONFIRMED = 2,\n PROCESSED = 3,\n ERROR = 999\n}\n\nexport interface IFullyDeleteModelState {\n status: FullyDeleteModelStateStatus;\n confirmation: string;\n model: CmsModel;\n error: CmsErrorResponse | null;\n task: IDeleteCmsModelTask | null;\n}\n"],"names":["FullyDeleteModelStateStatus"],"mappings":"AAGO,IAAKA,oCAA2BA,WAAAA,GAAAA,SAA3BA,2BAA2B;sEAC5B;4EACM;2EACD;2EACA;uEACJ;WALAA"}
|
|
@@ -9,6 +9,7 @@ export declare class BaseAutocompletePresenter {
|
|
|
9
9
|
searchOptions: IRefEntryOption[];
|
|
10
10
|
modelIds: string[];
|
|
11
11
|
searchQuery: string;
|
|
12
|
+
private lastSearchId;
|
|
12
13
|
constructor(searchUseCase: SearchContentEntriesUseCase.Interface, getEntriesUseCase: GetContentEntriesUseCase.Interface);
|
|
13
14
|
get activeOptions(): IRefEntryOption[];
|
|
14
15
|
search(query: string): Promise<void>;
|
|
@@ -9,18 +9,22 @@ class BaseAutocompletePresenter {
|
|
|
9
9
|
this.searchOptions = [];
|
|
10
10
|
this.modelIds = [];
|
|
11
11
|
this.searchQuery = "";
|
|
12
|
+
this.lastSearchId = 0;
|
|
12
13
|
makeAutoObservable(this, {
|
|
13
14
|
searchUseCase: false,
|
|
14
|
-
getEntriesUseCase: false
|
|
15
|
+
getEntriesUseCase: false,
|
|
16
|
+
lastSearchId: false
|
|
15
17
|
});
|
|
16
18
|
}
|
|
17
19
|
get activeOptions() {
|
|
18
20
|
return this.searchQuery ? this.searchOptions : this.defaultOptions;
|
|
19
21
|
}
|
|
20
22
|
async search(query) {
|
|
23
|
+
const searchId = ++this.lastSearchId;
|
|
21
24
|
this.searchQuery = query;
|
|
22
25
|
if (!query) {
|
|
23
26
|
this.searchOptions = [];
|
|
27
|
+
this.loading = false;
|
|
24
28
|
return;
|
|
25
29
|
}
|
|
26
30
|
this.loading = true;
|
|
@@ -30,11 +34,12 @@ class BaseAutocompletePresenter {
|
|
|
30
34
|
query,
|
|
31
35
|
limit: 10
|
|
32
36
|
});
|
|
37
|
+
if (searchId !== this.lastSearchId) return;
|
|
33
38
|
runInAction(()=>{
|
|
34
39
|
this.searchOptions = result.data.map(toOption);
|
|
35
40
|
});
|
|
36
41
|
} finally{
|
|
37
|
-
runInAction(()=>{
|
|
42
|
+
if (searchId === this.lastSearchId) runInAction(()=>{
|
|
38
43
|
this.loading = false;
|
|
39
44
|
});
|
|
40
45
|
}
|
|
@@ -77,6 +82,7 @@ class BaseAutocompletePresenter {
|
|
|
77
82
|
}
|
|
78
83
|
}
|
|
79
84
|
clearSearch() {
|
|
85
|
+
this.lastSearchId++;
|
|
80
86
|
this.searchQuery = "";
|
|
81
87
|
this.searchOptions = [];
|
|
82
88
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation/fieldRenderers/ref/autocomplete/BaseAutocompletePresenter.js","sources":["../../../../../src/presentation/fieldRenderers/ref/autocomplete/BaseAutocompletePresenter.ts"],"sourcesContent":["import { makeAutoObservable, runInAction } from \"mobx\";\nimport type { SearchContentEntriesUseCase } from \"~/features/contentEntry/searchContentEntries/abstractions.js\";\nimport type { GetContentEntriesUseCase } from \"~/features/contentEntry/getContentEntries/abstractions.js\";\nimport type { IRefEntryOption } from \"./abstractions.js\";\nimport { toOption } from \"./toOption.js\";\n\nexport class BaseAutocompletePresenter {\n loading = false;\n defaultOptions: IRefEntryOption[] = [];\n searchOptions: IRefEntryOption[] = [];\n modelIds: string[] = [];\n searchQuery = \"\";\n\n constructor(\n private searchUseCase: SearchContentEntriesUseCase.Interface,\n private getEntriesUseCase: GetContentEntriesUseCase.Interface\n ) {\n makeAutoObservable
|
|
1
|
+
{"version":3,"file":"presentation/fieldRenderers/ref/autocomplete/BaseAutocompletePresenter.js","sources":["../../../../../src/presentation/fieldRenderers/ref/autocomplete/BaseAutocompletePresenter.ts"],"sourcesContent":["import { makeAutoObservable, runInAction } from \"mobx\";\nimport type { SearchContentEntriesUseCase } from \"~/features/contentEntry/searchContentEntries/abstractions.js\";\nimport type { GetContentEntriesUseCase } from \"~/features/contentEntry/getContentEntries/abstractions.js\";\nimport type { IRefEntryOption } from \"./abstractions.js\";\nimport { toOption } from \"./toOption.js\";\n\nexport class BaseAutocompletePresenter {\n loading = false;\n defaultOptions: IRefEntryOption[] = [];\n searchOptions: IRefEntryOption[] = [];\n modelIds: string[] = [];\n searchQuery = \"\";\n private lastSearchId = 0;\n\n constructor(\n private searchUseCase: SearchContentEntriesUseCase.Interface,\n private getEntriesUseCase: GetContentEntriesUseCase.Interface\n ) {\n makeAutoObservable<\n BaseAutocompletePresenter,\n \"searchUseCase\" | \"getEntriesUseCase\" | \"lastSearchId\"\n >(this, {\n searchUseCase: false,\n getEntriesUseCase: false,\n lastSearchId: false\n });\n }\n\n get activeOptions(): IRefEntryOption[] {\n return this.searchQuery ? this.searchOptions : this.defaultOptions;\n }\n\n async search(query: string): Promise<void> {\n // Every keystroke starts a search, and responses can arrive out of order.\n // Only the latest search may write its results, or an older, shorter query overwrites them.\n const searchId = ++this.lastSearchId;\n this.searchQuery = query;\n if (!query) {\n this.searchOptions = [];\n this.loading = false;\n return;\n }\n\n this.loading = true;\n try {\n const result = await this.searchUseCase.execute({\n modelIds: this.modelIds,\n query,\n limit: 10\n });\n if (searchId !== this.lastSearchId) {\n return;\n }\n runInAction(() => {\n this.searchOptions = result.data.map(toOption);\n });\n } finally {\n if (searchId === this.lastSearchId) {\n runInAction(() => {\n this.loading = false;\n });\n }\n }\n }\n\n async loadDefaults(): Promise<void> {\n this.loading = true;\n try {\n const result = await this.searchUseCase.execute({\n modelIds: this.modelIds,\n limit: 10\n });\n runInAction(() => {\n this.defaultOptions = result.data.map(toOption);\n });\n } finally {\n runInAction(() => {\n this.loading = false;\n });\n }\n }\n\n async resolveEntries(values: { id: string; modelId: string }[]): Promise<IRefEntryOption[]> {\n this.loading = true;\n try {\n const result = await this.getEntriesUseCase.execute({\n entries: values.map(v => ({ id: v.id, modelId: v.modelId }))\n });\n const options = result.latest.map(toOption);\n runInAction(() => {\n this.loading = false;\n });\n return options;\n } catch (e) {\n runInAction(() => {\n this.loading = false;\n });\n throw e;\n }\n }\n\n clearSearch(): void {\n this.lastSearchId++;\n this.searchQuery = \"\";\n this.searchOptions = [];\n }\n}\n"],"names":["BaseAutocompletePresenter","searchUseCase","getEntriesUseCase","makeAutoObservable","query","searchId","result","runInAction","toOption","values","v","options","e"],"mappings":";;AAMO,MAAMA;IAQT,YACYC,aAAoD,EACpDC,iBAAqD,CAC/D;aAFUD,aAAa,GAAbA;aACAC,iBAAiB,GAAjBA;aATZ,OAAO,GAAG;aACV,cAAc,GAAsB,EAAE;aACtC,aAAa,GAAsB,EAAE;aACrC,QAAQ,GAAa,EAAE;aACvB,WAAW,GAAG;aACN,YAAY,GAAG;QAMnBC,mBAGE,IAAI,EAAE;YACJ,eAAe;YACf,mBAAmB;YACnB,cAAc;QAClB;IACJ;IAEA,IAAI,gBAAmC;QACnC,OAAO,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,cAAc;IACtE;IAEA,MAAM,OAAOC,KAAa,EAAiB;QAGvC,MAAMC,WAAW,EAAE,IAAI,CAAC,YAAY;QACpC,IAAI,CAAC,WAAW,GAAGD;QACnB,IAAI,CAACA,OAAO;YACR,IAAI,CAAC,aAAa,GAAG,EAAE;YACvB,IAAI,CAAC,OAAO,GAAG;YACf;QACJ;QAEA,IAAI,CAAC,OAAO,GAAG;QACf,IAAI;YACA,MAAME,SAAS,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC;gBAC5C,UAAU,IAAI,CAAC,QAAQ;gBACvBF;gBACA,OAAO;YACX;YACA,IAAIC,aAAa,IAAI,CAAC,YAAY,EAC9B;YAEJE,YAAY;gBACR,IAAI,CAAC,aAAa,GAAGD,OAAO,IAAI,CAAC,GAAG,CAACE;YACzC;QACJ,SAAU;YACN,IAAIH,aAAa,IAAI,CAAC,YAAY,EAC9BE,YAAY;gBACR,IAAI,CAAC,OAAO,GAAG;YACnB;QAER;IACJ;IAEA,MAAM,eAA8B;QAChC,IAAI,CAAC,OAAO,GAAG;QACf,IAAI;YACA,MAAMD,SAAS,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC;gBAC5C,UAAU,IAAI,CAAC,QAAQ;gBACvB,OAAO;YACX;YACAC,YAAY;gBACR,IAAI,CAAC,cAAc,GAAGD,OAAO,IAAI,CAAC,GAAG,CAACE;YAC1C;QACJ,SAAU;YACND,YAAY;gBACR,IAAI,CAAC,OAAO,GAAG;YACnB;QACJ;IACJ;IAEA,MAAM,eAAeE,MAAyC,EAA8B;QACxF,IAAI,CAAC,OAAO,GAAG;QACf,IAAI;YACA,MAAMH,SAAS,MAAM,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC;gBAChD,SAASG,OAAO,GAAG,CAACC,CAAAA,IAAM;wBAAE,IAAIA,EAAE,EAAE;wBAAE,SAASA,EAAE,OAAO;oBAAC;YAC7D;YACA,MAAMC,UAAUL,OAAO,MAAM,CAAC,GAAG,CAACE;YAClCD,YAAY;gBACR,IAAI,CAAC,OAAO,GAAG;YACnB;YACA,OAAOI;QACX,EAAE,OAAOC,GAAG;YACRL,YAAY;gBACR,IAAI,CAAC,OAAO,GAAG;YACnB;YACA,MAAMK;QACV;IACJ;IAEA,cAAoB;QAChB,IAAI,CAAC,YAAY;QACjB,IAAI,CAAC,WAAW,GAAG;QACnB,IAAI,CAAC,aAAa,GAAG,EAAE;IAC3B;AACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { Container } from "@webiny/di";
|
|
3
|
+
import { SearchContentEntriesUseCase } from "../../../../features/contentEntry/searchContentEntries/abstractions.js";
|
|
4
|
+
import { GetContentEntriesUseCase } from "../../../../features/contentEntry/getContentEntries/abstractions.js";
|
|
5
|
+
import { RefMultiAutocompletePresenter } from "./multi/abstractions.js";
|
|
6
|
+
import { RefMultiAutocompletePresenter as RefMultiAutocompletePresenter_js_RefMultiAutocompletePresenter } from "./multi/RefMultiAutocompletePresenter.js";
|
|
7
|
+
import { RefSingleAutocompletePresenter } from "./single/abstractions.js";
|
|
8
|
+
import { RefSingleAutocompletePresenter as RefSingleAutocompletePresenter_js_RefSingleAutocompletePresenter } from "./single/RefSingleAutocompletePresenter.js";
|
|
9
|
+
const createEntry = (entryId, title)=>({
|
|
10
|
+
id: `${entryId}#0001`,
|
|
11
|
+
entryId,
|
|
12
|
+
status: "draft",
|
|
13
|
+
title,
|
|
14
|
+
description: null,
|
|
15
|
+
image: null,
|
|
16
|
+
createdOn: "2026-01-01T00:00:00.000Z",
|
|
17
|
+
savedOn: "2026-01-01T00:00:00.000Z",
|
|
18
|
+
createdBy: {
|
|
19
|
+
id: "1",
|
|
20
|
+
type: "admin",
|
|
21
|
+
displayName: "Admin"
|
|
22
|
+
},
|
|
23
|
+
modifiedBy: null,
|
|
24
|
+
model: {
|
|
25
|
+
modelId: "location",
|
|
26
|
+
name: "Location"
|
|
27
|
+
},
|
|
28
|
+
published: null,
|
|
29
|
+
wbyAco_location: null
|
|
30
|
+
});
|
|
31
|
+
const defaultEntry = createEntry("first", "First");
|
|
32
|
+
const searchedEntry = createEntry("second", "Second");
|
|
33
|
+
const createContainer = ()=>{
|
|
34
|
+
const container = new Container();
|
|
35
|
+
container.registerInstance(SearchContentEntriesUseCase, {
|
|
36
|
+
execute: async ({ query })=>({
|
|
37
|
+
data: query ? [
|
|
38
|
+
searchedEntry
|
|
39
|
+
] : [
|
|
40
|
+
defaultEntry
|
|
41
|
+
]
|
|
42
|
+
})
|
|
43
|
+
});
|
|
44
|
+
container.registerInstance(GetContentEntriesUseCase, {
|
|
45
|
+
execute: async ()=>({
|
|
46
|
+
latest: [],
|
|
47
|
+
published: []
|
|
48
|
+
})
|
|
49
|
+
});
|
|
50
|
+
return container;
|
|
51
|
+
};
|
|
52
|
+
describe("RefMultiAutocompletePresenter", ()=>{
|
|
53
|
+
it("keeps an entry picked from the search results", async ()=>{
|
|
54
|
+
const container = createContainer();
|
|
55
|
+
container.register(RefMultiAutocompletePresenter_js_RefMultiAutocompletePresenter);
|
|
56
|
+
const presenter = container.resolve(RefMultiAutocompletePresenter);
|
|
57
|
+
await presenter.init({
|
|
58
|
+
modelIds: [
|
|
59
|
+
"location"
|
|
60
|
+
]
|
|
61
|
+
});
|
|
62
|
+
await presenter.search("sec");
|
|
63
|
+
const refs = presenter.select([
|
|
64
|
+
"second"
|
|
65
|
+
]);
|
|
66
|
+
expect(refs).toEqual([
|
|
67
|
+
{
|
|
68
|
+
id: "second#0001",
|
|
69
|
+
modelId: "location"
|
|
70
|
+
}
|
|
71
|
+
]);
|
|
72
|
+
expect(presenter.vm.values).toEqual([
|
|
73
|
+
"second"
|
|
74
|
+
]);
|
|
75
|
+
expect(presenter.vm.options).toContainEqual({
|
|
76
|
+
label: "Second",
|
|
77
|
+
value: "second"
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
it("keeps earlier picks when another entry is picked from the search results", async ()=>{
|
|
81
|
+
const container = createContainer();
|
|
82
|
+
container.register(RefMultiAutocompletePresenter_js_RefMultiAutocompletePresenter);
|
|
83
|
+
const presenter = container.resolve(RefMultiAutocompletePresenter);
|
|
84
|
+
await presenter.init({
|
|
85
|
+
modelIds: [
|
|
86
|
+
"location"
|
|
87
|
+
]
|
|
88
|
+
});
|
|
89
|
+
presenter.select([
|
|
90
|
+
"first"
|
|
91
|
+
]);
|
|
92
|
+
await presenter.search("sec");
|
|
93
|
+
const refs = presenter.select([
|
|
94
|
+
"first",
|
|
95
|
+
"second"
|
|
96
|
+
]);
|
|
97
|
+
expect(refs).toEqual([
|
|
98
|
+
{
|
|
99
|
+
id: "first#0001",
|
|
100
|
+
modelId: "location"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: "second#0001",
|
|
104
|
+
modelId: "location"
|
|
105
|
+
}
|
|
106
|
+
]);
|
|
107
|
+
expect(presenter.vm.values).toEqual([
|
|
108
|
+
"first",
|
|
109
|
+
"second"
|
|
110
|
+
]);
|
|
111
|
+
});
|
|
112
|
+
it("ignores a search response that arrives after a newer one", async ()=>{
|
|
113
|
+
const boulder = createEntry("boulder", "Boulder");
|
|
114
|
+
const baker = createEntry("baker", "Baker");
|
|
115
|
+
let resolveSlow = ()=>{};
|
|
116
|
+
const slow = new Promise((resolve)=>{
|
|
117
|
+
resolveSlow = resolve;
|
|
118
|
+
});
|
|
119
|
+
const container = new Container();
|
|
120
|
+
container.registerInstance(SearchContentEntriesUseCase, {
|
|
121
|
+
execute: async ({ query })=>{
|
|
122
|
+
if ("B" === query) {
|
|
123
|
+
await slow;
|
|
124
|
+
return {
|
|
125
|
+
data: [
|
|
126
|
+
baker
|
|
127
|
+
]
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
data: query ? [
|
|
132
|
+
boulder
|
|
133
|
+
] : [
|
|
134
|
+
defaultEntry
|
|
135
|
+
]
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
container.registerInstance(GetContentEntriesUseCase, {
|
|
140
|
+
execute: async ()=>({
|
|
141
|
+
latest: [],
|
|
142
|
+
published: []
|
|
143
|
+
})
|
|
144
|
+
});
|
|
145
|
+
container.register(RefMultiAutocompletePresenter_js_RefMultiAutocompletePresenter);
|
|
146
|
+
const presenter = container.resolve(RefMultiAutocompletePresenter);
|
|
147
|
+
await presenter.init({
|
|
148
|
+
modelIds: [
|
|
149
|
+
"location"
|
|
150
|
+
]
|
|
151
|
+
});
|
|
152
|
+
const first = presenter.search("B");
|
|
153
|
+
await presenter.search("Boulder");
|
|
154
|
+
resolveSlow();
|
|
155
|
+
await first;
|
|
156
|
+
expect(presenter.vm.options).toEqual([
|
|
157
|
+
{
|
|
158
|
+
label: "Boulder",
|
|
159
|
+
value: "boulder"
|
|
160
|
+
}
|
|
161
|
+
]);
|
|
162
|
+
expect(presenter.vm.loading).toBe(false);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
describe("RefSingleAutocompletePresenter", ()=>{
|
|
166
|
+
it("keeps an entry picked from the search results", async ()=>{
|
|
167
|
+
const container = createContainer();
|
|
168
|
+
container.register(RefSingleAutocompletePresenter_js_RefSingleAutocompletePresenter);
|
|
169
|
+
const presenter = container.resolve(RefSingleAutocompletePresenter);
|
|
170
|
+
await presenter.init({
|
|
171
|
+
modelIds: [
|
|
172
|
+
"location"
|
|
173
|
+
]
|
|
174
|
+
});
|
|
175
|
+
await presenter.search("sec");
|
|
176
|
+
const ref = presenter.select("second");
|
|
177
|
+
expect(ref).toEqual({
|
|
178
|
+
id: "second#0001",
|
|
179
|
+
modelId: "location"
|
|
180
|
+
});
|
|
181
|
+
expect(presenter.vm.value).toBe("second");
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
//# sourceMappingURL=RefAutocompletePresenters.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.js","sources":["../../../../../src/presentation/fieldRenderers/ref/autocomplete/RefAutocompletePresenters.test.ts"],"sourcesContent":["import { describe, it, expect } from \"vitest\";\nimport { Container } from \"@webiny/di\";\nimport { SearchContentEntriesUseCase } from \"~/features/contentEntry/searchContentEntries/abstractions.js\";\nimport { GetContentEntriesUseCase } from \"~/features/contentEntry/getContentEntries/abstractions.js\";\nimport type { CmsReferenceEntry } from \"~/features/contentEntry/refTypes.js\";\nimport { RefMultiAutocompletePresenter as MultiAbstraction } from \"./multi/abstractions.js\";\nimport { RefMultiAutocompletePresenter } from \"./multi/RefMultiAutocompletePresenter.js\";\nimport { RefSingleAutocompletePresenter as SingleAbstraction } from \"./single/abstractions.js\";\nimport { RefSingleAutocompletePresenter } from \"./single/RefSingleAutocompletePresenter.js\";\n\nconst createEntry = (entryId: string, title: string): CmsReferenceEntry => ({\n id: `${entryId}#0001`,\n entryId,\n status: \"draft\",\n title,\n description: null,\n image: null,\n createdOn: \"2026-01-01T00:00:00.000Z\",\n savedOn: \"2026-01-01T00:00:00.000Z\",\n createdBy: { id: \"1\", type: \"admin\", displayName: \"Admin\" },\n modifiedBy: null,\n model: { modelId: \"location\", name: \"Location\" },\n published: null,\n wbyAco_location: null\n});\n\n// The default list holds only the first entry, so the second one can only be found by searching.\nconst defaultEntry = createEntry(\"first\", \"First\");\nconst searchedEntry = createEntry(\"second\", \"Second\");\n\nconst createContainer = () => {\n const container = new Container();\n container.registerInstance(SearchContentEntriesUseCase, {\n execute: async ({ query }) => ({ data: query ? [searchedEntry] : [defaultEntry] })\n });\n container.registerInstance(GetContentEntriesUseCase, {\n execute: async () => ({ latest: [], published: [] })\n });\n return container;\n};\n\ndescribe(\"RefMultiAutocompletePresenter\", () => {\n it(\"keeps an entry picked from the search results\", async () => {\n const container = createContainer();\n container.register(RefMultiAutocompletePresenter);\n const presenter = container.resolve(MultiAbstraction);\n\n await presenter.init({ modelIds: [\"location\"] });\n await presenter.search(\"sec\");\n\n const refs = presenter.select([\"second\"]);\n\n expect(refs).toEqual([{ id: \"second#0001\", modelId: \"location\" }]);\n expect(presenter.vm.values).toEqual([\"second\"]);\n expect(presenter.vm.options).toContainEqual({ label: \"Second\", value: \"second\" });\n });\n\n it(\"keeps earlier picks when another entry is picked from the search results\", async () => {\n const container = createContainer();\n container.register(RefMultiAutocompletePresenter);\n const presenter = container.resolve(MultiAbstraction);\n\n await presenter.init({ modelIds: [\"location\"] });\n presenter.select([\"first\"]);\n await presenter.search(\"sec\");\n\n const refs = presenter.select([\"first\", \"second\"]);\n\n expect(refs).toEqual([\n { id: \"first#0001\", modelId: \"location\" },\n { id: \"second#0001\", modelId: \"location\" }\n ]);\n expect(presenter.vm.values).toEqual([\"first\", \"second\"]);\n });\n\n it(\"ignores a search response that arrives after a newer one\", async () => {\n const boulder = createEntry(\"boulder\", \"Boulder\");\n const baker = createEntry(\"baker\", \"Baker\");\n // The \"B\" request is slower than the \"Boulder\" one, and its first page does not hold Boulder.\n let resolveSlow: () => void = () => {};\n const slow = new Promise<void>(resolve => {\n resolveSlow = resolve;\n });\n\n const container = new Container();\n container.registerInstance(SearchContentEntriesUseCase, {\n execute: async ({ query }) => {\n if (query === \"B\") {\n await slow;\n return { data: [baker] };\n }\n return { data: query ? [boulder] : [defaultEntry] };\n }\n });\n container.registerInstance(GetContentEntriesUseCase, {\n execute: async () => ({ latest: [], published: [] })\n });\n container.register(RefMultiAutocompletePresenter);\n const presenter = container.resolve(MultiAbstraction);\n\n await presenter.init({ modelIds: [\"location\"] });\n const first = presenter.search(\"B\");\n await presenter.search(\"Boulder\");\n resolveSlow();\n await first;\n\n expect(presenter.vm.options).toEqual([{ label: \"Boulder\", value: \"boulder\" }]);\n expect(presenter.vm.loading).toBe(false);\n });\n});\n\ndescribe(\"RefSingleAutocompletePresenter\", () => {\n it(\"keeps an entry picked from the search results\", async () => {\n const container = createContainer();\n container.register(RefSingleAutocompletePresenter);\n const presenter = container.resolve(SingleAbstraction);\n\n await presenter.init({ modelIds: [\"location\"] });\n await presenter.search(\"sec\");\n\n const ref = presenter.select(\"second\");\n\n expect(ref).toEqual({ id: \"second#0001\", modelId: \"location\" });\n expect(presenter.vm.value).toBe(\"second\");\n });\n});\n"],"names":["createEntry","entryId","title","defaultEntry","searchedEntry","createContainer","container","Container","SearchContentEntriesUseCase","query","GetContentEntriesUseCase","describe","it","RefMultiAutocompletePresenter","presenter","MultiAbstraction","refs","expect","boulder","baker","resolveSlow","slow","Promise","resolve","first","RefSingleAutocompletePresenter","SingleAbstraction","ref"],"mappings":"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"}
|
|
@@ -47,9 +47,9 @@ class RefMultiAutocompletePresenterImpl {
|
|
|
47
47
|
await this.base.search(query);
|
|
48
48
|
}
|
|
49
49
|
select(entryIds) {
|
|
50
|
-
this.base.clearSearch();
|
|
51
50
|
const allOptions = this.getAllKnownOptions();
|
|
52
51
|
const selected = entryIds.map((entryId)=>allOptions.find((o)=>o.entryId === entryId)).filter((o)=>null != o);
|
|
52
|
+
this.base.clearSearch();
|
|
53
53
|
this.resolved = selected;
|
|
54
54
|
return selected.map((opt)=>({
|
|
55
55
|
id: opt.id,
|