From d15bfe2d27cf27a99bafa548c8bceea08e813f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Mon, 19 Oct 2020 11:24:35 +0200 Subject: [PATCH 001/151] Show create button when if required in the packages table --- i18n/en.pot | 4 ++-- .../ModulePackageListTable.tsx | 5 ++++- .../package-list-table/PackageListTable.tsx | 7 +++++-- .../module-package-list/ModulePackageListPage.tsx | 12 ++++++++---- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 8f7b175fd..43a2f8217 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-06T10:10:16.836Z\n" -"PO-Revision-Date: 2020-10-06T10:10:16.836Z\n" +"POT-Creation-Date: 2020-10-19T08:31:48.050Z\n" +"PO-Revision-Date: 2020-10-19T08:31:48.050Z\n" msgid "Field {{field}} cannot be blank" msgstr "" diff --git a/src/presentation/react/components/module-package-list-table/ModulePackageListTable.tsx b/src/presentation/react/components/module-package-list-table/ModulePackageListTable.tsx index 7414b6b69..a6610fde2 100644 --- a/src/presentation/react/components/module-package-list-table/ModulePackageListTable.tsx +++ b/src/presentation/react/components/module-package-list-table/ModulePackageListTable.tsx @@ -98,7 +98,10 @@ export const ModulePackageListTable: React.FC = Rea paginationOptions={paginationOptions} openSyncSummary={openSyncSummary} onActionButtonClick={ - viewSelector.value === "modules" && !selectedInstance ? onCreate : undefined + (viewSelector.value === "modules" && !selectedInstance) || + (viewSelector.value === "packages" && selectedInstance) + ? onCreate + : undefined } /> ); diff --git a/src/presentation/react/components/package-list-table/PackageListTable.tsx b/src/presentation/react/components/package-list-table/PackageListTable.tsx index bd48897b0..b1dec12e9 100644 --- a/src/presentation/react/components/package-list-table/PackageListTable.tsx +++ b/src/presentation/react/components/package-list-table/PackageListTable.tsx @@ -230,6 +230,8 @@ export const PackagesListTable: React.FC = ({ [] ); + const canImport = presentation === "app" && isRemoteInstance && appConfigurator; + const actions: TableAction[] = useMemo( () => [ { @@ -278,7 +280,7 @@ export const PackagesListTable: React.FC = ({ multiple: false, onClick: importPackage, icon: arrow_downward, - isActive: () => presentation === "app" && isRemoteInstance && appConfigurator, + isActive: () => canImport, }, ], [ @@ -291,6 +293,7 @@ export const PackagesListTable: React.FC = ({ presentation, publishPackage, showStore, + canImport, ] ); @@ -355,7 +358,7 @@ export const PackagesListTable: React.FC = ({ columns={columns} details={details} actions={actions} - onActionButtonClick={onActionButtonClick} + onActionButtonClick={canImport ? onActionButtonClick : undefined} forceSelectionColumn={presentation === "app"} filterComponents={filterComponents} selection={selection} diff --git a/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx b/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx index dd5fa1ca8..895959b8a 100644 --- a/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx +++ b/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx @@ -34,9 +34,13 @@ export const ModulePackageListPage: React.FC = () => { history.push("/"); }, [history]); - const createModule = useCallback(() => { - history.push(`/modules/new`); - }, [history]); + const create = useCallback(() => { + if (tableOption === "modules") { + history.push(`/modules/new`); + } else { + console.log("show wizard"); + } + }, [history, tableOption]); const setTableOption = useCallback( (option: ViewOption) => { @@ -61,7 +65,7 @@ export const ModulePackageListPage: React.FC = () => { Date: Mon, 19 Oct 2020 13:01:08 +0200 Subject: [PATCH 002/151] Create package import dialog --- i18n/en.pot | 13 +++-- i18n/es.po | 12 +++-- i18n/fr.po | 11 ++-- i18n/pt.po | 11 ++-- .../PackageImportDialog.tsx | 51 +++++++++++++++++++ .../ModulePackageListPage.tsx | 9 +++- 6 files changed, 89 insertions(+), 18 deletions(-) create mode 100644 src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx diff --git a/i18n/en.pot b/i18n/en.pot index 43a2f8217..f1b617dbb 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-19T08:31:48.050Z\n" -"PO-Revision-Date: 2020-10-19T08:31:48.050Z\n" +"POT-Creation-Date: 2020-10-19T11:00:32.305Z\n" +"PO-Revision-Date: 2020-10-19T11:00:32.305Z\n" msgid "Field {{field}} cannot be blank" msgstr "" @@ -523,6 +523,12 @@ msgstr "" msgid "[Pull Request by {{name}}] {{subject}}" msgstr "" +msgid "Packages Import" +msgstr "" + +msgid "Import" +msgstr "" + msgid "Invalid package" msgstr "" @@ -594,9 +600,6 @@ msgstr "" msgid "Cannot get data from remote instance" msgstr "" -msgid "Import" -msgstr "" - msgid "objects" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index 438b56715..4703b3383 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-06T10:10:16.836Z\n" +"POT-Creation-Date: 2020-10-19T11:00:30.607Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -524,6 +524,13 @@ msgstr "" msgid "[Pull Request by {{name}}] {{subject}}" msgstr "" +#, fuzzy +msgid "Packages Import" +msgstr "Paquetes" + +msgid "Import" +msgstr "" + msgid "Invalid package" msgstr "" @@ -595,9 +602,6 @@ msgstr "" msgid "Cannot get data from remote instance" msgstr "" -msgid "Import" -msgstr "" - msgid "objects" msgstr "" diff --git a/i18n/fr.po b/i18n/fr.po index c22bfe293..51dbbefe2 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-06T10:10:16.836Z\n" +"POT-Creation-Date: 2020-10-19T11:00:30.607Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -524,6 +524,12 @@ msgstr "" msgid "[Pull Request by {{name}}] {{subject}}" msgstr "" +msgid "Packages Import" +msgstr "" + +msgid "Import" +msgstr "" + msgid "Invalid package" msgstr "" @@ -595,9 +601,6 @@ msgstr "" msgid "Cannot get data from remote instance" msgstr "" -msgid "Import" -msgstr "" - msgid "objects" msgstr "" diff --git a/i18n/pt.po b/i18n/pt.po index c22bfe293..51dbbefe2 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-06T10:10:16.836Z\n" +"POT-Creation-Date: 2020-10-19T11:00:30.607Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -524,6 +524,12 @@ msgstr "" msgid "[Pull Request by {{name}}] {{subject}}" msgstr "" +msgid "Packages Import" +msgstr "" + +msgid "Import" +msgstr "" + msgid "Invalid package" msgstr "" @@ -595,9 +601,6 @@ msgstr "" msgid "Cannot get data from remote instance" msgstr "" -msgid "Import" -msgstr "" - msgid "objects" msgstr "" diff --git a/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx b/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx new file mode 100644 index 000000000..3fe7c7c16 --- /dev/null +++ b/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx @@ -0,0 +1,51 @@ +import DialogContent from "@material-ui/core/DialogContent"; +import { ConfirmationDialog } from "d2-ui-components"; +import React, { useState } from "react"; +import i18n from "../../../../locales"; + +interface PackageImportDialogProps { + isOpen: boolean; + //syncRule: SyncRule; + //task: (syncRule: SyncRule) => void; + //onChange(syncRule: SyncRule): void; + onClose: (importResponse?: any) => void; +} + +const PackageImportDialog: React.FC = ({ + isOpen, + //syncRule, + //onChange, + onClose, + //task, +}) => { + const [enableImport] = useState(false); + + // useEffect(() => { + // syncRule.isValid().then(setEnableImport); + // }, [syncRule]); + + return ( + task(syncRule)} + onCancel={onClose} + saveText={i18n.t("Import")} + maxWidth={"lg"} + fullWidth={true} + disableSave={!enableImport} + > + + {/* */} + Wizard + + + ); +}; + +export default PackageImportDialog; diff --git a/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx b/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx index 895959b8a..a70e50423 100644 --- a/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx +++ b/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx @@ -9,6 +9,7 @@ import { PresentationOption, ViewOption, } from "../../../react/components/module-package-list-table/ModulePackageListTable"; +import PackageImportDialog from "../../../react/components/package-import-dialog/PackageImportDialog"; import PageHeader from "../../../react/components/page-header/PageHeader"; import SyncSummary from "../../../react/components/sync-summary/SyncSummary"; @@ -26,6 +27,7 @@ export interface ModulePackageListPageProps { export const ModulePackageListPage: React.FC = () => { const history = useHistory(); const [syncReport, setSyncReport] = useState(); + const [openImportPackageDialog, setOpenImportPackageDialog] = useState(false); const { list: tableOption = "modules" } = useParams<{ list: ViewOption }>(); const title = buildTitle(tableOption); @@ -38,7 +40,7 @@ export const ModulePackageListPage: React.FC = () => { if (tableOption === "modules") { history.push(`/modules/new`); } else { - console.log("show wizard"); + setOpenImportPackageDialog(true); } }, [history, tableOption]); @@ -75,6 +77,11 @@ export const ModulePackageListPage: React.FC = () => { {!!syncReport && ( setSyncReport(undefined)} /> )} + + setOpenImportPackageDialog(false)} + /> ); }; From 832e3734e6ca2ec9d4711e389fb85dbf1c3a3c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Mon, 19 Oct 2020 16:40:23 +0200 Subject: [PATCH 003/151] Create PackageImportWizard with empty steps --- i18n/en.pot | 7 +- i18n/es.po | 6 +- i18n/fr.po | 5 +- i18n/pt.po | 5 +- .../PackageImportDialog.tsx | 3 +- .../PackageImportWizard.tsx | 76 +++++++++++++++++++ 6 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 src/presentation/react/components/package-import-wizard/PackageImportWizard.tsx diff --git a/i18n/en.pot b/i18n/en.pot index f1b617dbb..3e9463313 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-19T11:00:32.305Z\n" -"PO-Revision-Date: 2020-10-19T11:00:32.305Z\n" +"POT-Creation-Date: 2020-10-19T14:17:23.213Z\n" +"PO-Revision-Date: 2020-10-19T14:17:23.213Z\n" msgid "Field {{field}} cannot be blank" msgstr "" @@ -529,6 +529,9 @@ msgstr "" msgid "Import" msgstr "" +msgid "Packages mapping" +msgstr "" + msgid "Invalid package" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index 4703b3383..ca528a605 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-19T11:00:30.607Z\n" +"POT-Creation-Date: 2020-10-19T13:55:56.440Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -531,6 +531,10 @@ msgstr "Paquetes" msgid "Import" msgstr "" +#, fuzzy +msgid "Packages mapping" +msgstr "Paquetes" + msgid "Invalid package" msgstr "" diff --git a/i18n/fr.po b/i18n/fr.po index 51dbbefe2..406fe6dcf 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-19T11:00:30.607Z\n" +"POT-Creation-Date: 2020-10-19T13:55:56.440Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -530,6 +530,9 @@ msgstr "" msgid "Import" msgstr "" +msgid "Packages mapping" +msgstr "" + msgid "Invalid package" msgstr "" diff --git a/i18n/pt.po b/i18n/pt.po index 51dbbefe2..406fe6dcf 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-19T11:00:30.607Z\n" +"POT-Creation-Date: 2020-10-19T13:55:56.440Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -530,6 +530,9 @@ msgstr "" msgid "Import" msgstr "" +msgid "Packages mapping" +msgstr "" + msgid "Invalid package" msgstr "" diff --git a/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx b/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx index 3fe7c7c16..1afd01c96 100644 --- a/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx +++ b/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx @@ -2,6 +2,7 @@ import DialogContent from "@material-ui/core/DialogContent"; import { ConfirmationDialog } from "d2-ui-components"; import React, { useState } from "react"; import i18n from "../../../../locales"; +import { PackageImportWizard } from "../package-import-wizard/PackageImportWizard"; interface PackageImportDialogProps { isOpen: boolean; @@ -42,7 +43,7 @@ const PackageImportDialog: React.FC = ({ onChange={onChange} onCancel={onClose} /> */} - Wizard + ); diff --git a/src/presentation/react/components/package-import-wizard/PackageImportWizard.tsx b/src/presentation/react/components/package-import-wizard/PackageImportWizard.tsx new file mode 100644 index 000000000..8f2646c5e --- /dev/null +++ b/src/presentation/react/components/package-import-wizard/PackageImportWizard.tsx @@ -0,0 +1,76 @@ +import { Wizard, WizardStep } from "d2-ui-components"; +import React from "react"; +import { useLocation } from "react-router-dom"; +import i18n from "../../../../locales"; + +const GeneralInfoStep = () =>
General info
; +const PackagesStep = () =>
Packages
; +const PackagesMetadataStep = () =>
Packages
; +const SummaryStep = () =>
Summary
; + +export const stepsBaseInfo = [ + { + key: "general-info", + label: i18n.t("General info"), + component: GeneralInfoStep, + validationKeys: ["name", "department"], + }, + { + key: "packages", + label: i18n.t("Packages"), + component: PackagesStep, + validationKeys: ["name", "department"], + }, + { + key: "package-mapping", + label: i18n.t("Packages mapping"), + component: PackagesMetadataStep, + validationKeys: ["name", "department"], + }, + { + key: "summary", + label: i18n.t("Summary"), + component: SummaryStep, + validationKeys: [], + showOnSyncDialog: true, + }, +]; + +export interface PackageImportWizardProps { + // isEdit: boolean; + // onCancel: () => void; + // onClose: () => void; + // module: Module; + // onChange: (module: Module) => void; +} + +export const PackageImportWizard: React.FC = () => { + const location = useLocation(); + + //const props: ModuleWizardStepProps = { module, onChange, onCancel, onClose, isEdit }; + //const steps = metadataModuleSteps.map(step => ({ ...step, props })); + const steps = stepsBaseInfo; //.map(step => ({ ...step, props })); + + const onStepChangeRequest = async (_currentStep: WizardStep, _newStep: WizardStep) => { + // const index = _(steps).findIndex(step => step.key === newStep.key); + // return _.take(steps, index).flatMap(({ validationKeys }) => + // module.validate(validationKeys).map(({ description }) => description) + // ); + return undefined; + }; + + const urlHash = location.hash.slice(1); + const stepExists = steps.find(step => step.key === urlHash); + const firstStepKey = steps.map(step => step.key)[0]; + const initialStepKey = stepExists ? urlHash : firstStepKey; + + return ( + + ); +}; From 5af0cb740efe8098e42bca8feff7ea80d38c4a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Tue, 20 Oct 2020 10:57:22 +0200 Subject: [PATCH 004/151] Create PackageImportRule domain entitiy --- .../entities/PackageImportRule.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/domain/package-import/entities/PackageImportRule.ts diff --git a/src/domain/package-import/entities/PackageImportRule.ts b/src/domain/package-import/entities/PackageImportRule.ts new file mode 100644 index 000000000..f8802b113 --- /dev/null +++ b/src/domain/package-import/entities/PackageImportRule.ts @@ -0,0 +1,39 @@ +import { ModelValidation, validateModel, ValidationError } from "../../common/entities/Validations"; +import { Instance } from "../../instance/entities/Instance"; + +interface PackageImportRuleData { + instance: Instance; + packageIds: string[]; +} + +export class PackageImportRule { + public readonly instance: Instance; + public readonly packageIds: string[]; + + constructor(private data: PackageImportRuleData) { + this.instance = data.instance; + this.packageIds = data.packageIds; + } + + static create(instance: Instance): PackageImportRule { + return new PackageImportRule({ instance, packageIds: [] }); + } + + public updatePackageIds(packageIds: string[]): PackageImportRule { + return new PackageImportRule({ ...this.data, packageIds }); + } + + public validate(filter?: string[]): ValidationError[] { + return validateModel(this, this.validations()).filter( + ({ property }) => filter?.includes(property) ?? true + ); + } + + private validations = (): ModelValidation[] => [ + { + property: "packageIds", + validation: "hasItems", + alias: "package element", + }, + ]; +} From fc110e776c8c390fd5ac872a044a016caff96de7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Tue, 20 Oct 2020 10:58:50 +0200 Subject: [PATCH 005/151] Create PackageSelectionStep --- i18n/en.pot | 4 +- i18n/es.po | 2 +- i18n/fr.po | 2 +- i18n/pt.po | 2 +- .../ModulePackageListTable.tsx | 8 ++- .../PackageImportDialog.tsx | 36 +++++++------- .../PackageImportWizard.tsx | 49 ++++++++++++------- .../steps/PackageSelectionStep.tsx | 31 ++++++++++++ .../package-list-table/PackageListTable.tsx | 38 +++++++++++--- .../ModulePackageListPage.tsx | 30 ++++++++++-- 10 files changed, 150 insertions(+), 52 deletions(-) create mode 100644 src/presentation/react/components/package-import-wizard/steps/PackageSelectionStep.tsx diff --git a/i18n/en.pot b/i18n/en.pot index 3e9463313..76c38085b 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-19T14:17:23.213Z\n" -"PO-Revision-Date: 2020-10-19T14:17:23.213Z\n" +"POT-Creation-Date: 2020-10-20T08:55:38.811Z\n" +"PO-Revision-Date: 2020-10-20T08:55:38.811Z\n" msgid "Field {{field}} cannot be blank" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index ca528a605..2a92ad692 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-19T13:55:56.440Z\n" +"POT-Creation-Date: 2020-10-19T15:55:37.566Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/i18n/fr.po b/i18n/fr.po index 406fe6dcf..24ecc5a59 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-19T13:55:56.440Z\n" +"POT-Creation-Date: 2020-10-19T15:55:37.566Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/i18n/pt.po b/i18n/pt.po index 406fe6dcf..24ecc5a59 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-19T13:55:56.440Z\n" +"POT-Creation-Date: 2020-10-19T15:55:37.566Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/src/presentation/react/components/module-package-list-table/ModulePackageListTable.tsx b/src/presentation/react/components/module-package-list-table/ModulePackageListTable.tsx index a6610fde2..0659345c4 100644 --- a/src/presentation/react/components/module-package-list-table/ModulePackageListTable.tsx +++ b/src/presentation/react/components/module-package-list-table/ModulePackageListTable.tsx @@ -21,6 +21,7 @@ export interface ModulePackageListTableProps { showSelector: ViewSelectorConfig; showInstances: InstanceSelectionConfig; openSyncSummary?: (syncReport: SyncReport) => void; + onInstanceChange?: (instance?: Instance) => void; } export type ViewOption = "modules" | "packages"; @@ -35,6 +36,7 @@ export const ModulePackageListTable: React.FC = Rea showSelector, showInstances, openSyncSummary, + onInstanceChange, }) => { const [selectedInstance, setSelectedInstance] = useState(); const [showStore, setShowStore] = useState(false); @@ -53,8 +55,12 @@ export const ModulePackageListTable: React.FC = Rea (type: InstanceSelectionOption, instance?: Instance) => { setShowStore(type === "store"); setSelectedInstance(instance); + + if (onInstanceChange) { + onInstanceChange(instance); + } }, - [] + [onInstanceChange] ); const filters = useMemo( diff --git a/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx b/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx index 1afd01c96..0f77d3507 100644 --- a/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx +++ b/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx @@ -1,35 +1,36 @@ import DialogContent from "@material-ui/core/DialogContent"; import { ConfirmationDialog } from "d2-ui-components"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; +import { PackageImportRule } from "../../../../domain/package-import/entities/PackageImportRule"; import i18n from "../../../../locales"; import { PackageImportWizard } from "../package-import-wizard/PackageImportWizard"; interface PackageImportDialogProps { isOpen: boolean; - //syncRule: SyncRule; - //task: (syncRule: SyncRule) => void; - //onChange(syncRule: SyncRule): void; - onClose: (importResponse?: any) => void; + packageImportRule: PackageImportRule; + onChange: (packageImportRule: PackageImportRule) => void; + onClose: () => void; + executeImport: (packageImportRule: PackageImportRule) => void; } const PackageImportDialog: React.FC = ({ isOpen, - //syncRule, - //onChange, + packageImportRule, + onChange, onClose, - //task, + executeImport, }) => { - const [enableImport] = useState(false); + const [enableImport, setEnableImport] = useState(false); - // useEffect(() => { - // syncRule.isValid().then(setEnableImport); - // }, [syncRule]); + useEffect(() => { + setEnableImport(packageImportRule.validate().length === 0); + }, [packageImportRule]); return ( task(syncRule)} + onSave={() => executeImport(packageImportRule)} onCancel={onClose} saveText={i18n.t("Import")} maxWidth={"lg"} @@ -37,13 +38,12 @@ const PackageImportDialog: React.FC = ({ disableSave={!enableImport} > - {/* */} - + onClose={onClose} + /> ); diff --git a/src/presentation/react/components/package-import-wizard/PackageImportWizard.tsx b/src/presentation/react/components/package-import-wizard/PackageImportWizard.tsx index 8f2646c5e..7dea4cc8e 100644 --- a/src/presentation/react/components/package-import-wizard/PackageImportWizard.tsx +++ b/src/presentation/react/components/package-import-wizard/PackageImportWizard.tsx @@ -1,10 +1,24 @@ import { Wizard, WizardStep } from "d2-ui-components"; import React from "react"; import { useLocation } from "react-router-dom"; +import { PackageImportRule } from "../../../../domain/package-import/entities/PackageImportRule"; import i18n from "../../../../locales"; +import { PackageSelectionStep } from "./steps/PackageSelectionStep"; + +export interface PackageImportWizardStep extends WizardStep { + validationKeys: string[]; + showOnSyncDialog?: boolean; +} + +export interface PackageImportWizardStepProps { + packageImportRule: PackageImportRule; + onChange: (packageImportRule: PackageImportRule) => void; + onCancel: () => void; + onClose: () => void; +} const GeneralInfoStep = () =>
General info
; -const PackagesStep = () =>
Packages
; +//const PackagesStep = () =>
Packages
; const PackagesMetadataStep = () =>
Packages
; const SummaryStep = () =>
Summary
; @@ -18,8 +32,8 @@ export const stepsBaseInfo = [ { key: "packages", label: i18n.t("Packages"), - component: PackagesStep, - validationKeys: ["name", "department"], + component: PackageSelectionStep, + validationKeys: ["packageIds"], }, { key: "package-mapping", @@ -37,26 +51,25 @@ export const stepsBaseInfo = [ ]; export interface PackageImportWizardProps { - // isEdit: boolean; - // onCancel: () => void; - // onClose: () => void; - // module: Module; - // onChange: (module: Module) => void; + packageImportRule: PackageImportRule; + onChange: (packageImportRule: PackageImportRule) => void; + onCancel: () => void; + onClose: () => void; } -export const PackageImportWizard: React.FC = () => { +export const PackageImportWizard: React.FC = props => { const location = useLocation(); - //const props: ModuleWizardStepProps = { module, onChange, onCancel, onClose, isEdit }; - //const steps = metadataModuleSteps.map(step => ({ ...step, props })); - const steps = stepsBaseInfo; //.map(step => ({ ...step, props })); + const steps = stepsBaseInfo.map(step => ({ ...step, props })); + + const onStepChangeRequest = async (currentStep: WizardStep, _newStep: WizardStep) => { + const step = currentStep as PackageImportWizardStep; + + const errors = props.packageImportRule + .validate(step.validationKeys) + .map(({ description }) => description); - const onStepChangeRequest = async (_currentStep: WizardStep, _newStep: WizardStep) => { - // const index = _(steps).findIndex(step => step.key === newStep.key); - // return _.take(steps, index).flatMap(({ validationKeys }) => - // module.validate(validationKeys).map(({ description }) => description) - // ); - return undefined; + return errors; }; const urlHash = location.hash.slice(1); diff --git a/src/presentation/react/components/package-import-wizard/steps/PackageSelectionStep.tsx b/src/presentation/react/components/package-import-wizard/steps/PackageSelectionStep.tsx new file mode 100644 index 000000000..b357d3377 --- /dev/null +++ b/src/presentation/react/components/package-import-wizard/steps/PackageSelectionStep.tsx @@ -0,0 +1,31 @@ +import { PaginationOptions } from "d2-ui-components"; +import React from "react"; +import { PackagesListTable } from "../../package-list-table/PackageListTable"; +import { PackageImportWizardProps } from "../PackageImportWizard"; + +export const PackageSelectionStep: React.FC = ({ + packageImportRule, + onChange, +}) => { + const handleSelectionChange = (ids: string[]) => { + onChange(packageImportRule.updatePackageIds(ids)); + }; + + return ( + + ); +}; + +const paginationOptions: PaginationOptions = { + pageSizeOptions: [10], + pageSizeInitialValue: 10, +}; diff --git a/src/presentation/react/components/package-list-table/PackageListTable.tsx b/src/presentation/react/components/package-list-table/PackageListTable.tsx index b1dec12e9..bf18be9e7 100644 --- a/src/presentation/react/components/package-list-table/PackageListTable.tsx +++ b/src/presentation/react/components/package-list-table/PackageListTable.tsx @@ -24,7 +24,13 @@ import { useAppContext } from "../../contexts/AppContext"; type ListPackage = Omit; -export const PackagesListTable: React.FC = ({ +interface PackagesListTableProps extends ModulePackageListPageProps { + isImportDialog?: boolean; + onSelectionChange?: (ids: string[]) => void; + selectedIds?: string[]; +} + +export const PackagesListTable: React.FC = ({ remoteInstance, showStore, onActionButtonClick, @@ -32,6 +38,9 @@ export const PackagesListTable: React.FC = ({ externalComponents, openSyncSummary = _.noop, paginationOptions, + isImportDialog = false, + onSelectionChange, + selectedIds, }) => { const { api, compositionRoot } = useAppContext(); const snackbar = useSnackbar(); @@ -42,7 +51,9 @@ export const PackagesListTable: React.FC = ({ const rows = showStore ? storePackages : instancePackages; const [resetKey, setResetKey] = useState(Math.random()); - const [selection, updateSelection] = useState([]); + const [selection, updateSelection] = useState( + selectedIds?.map(id => ({ id })) || [] + ); const [dialogProps, updateDialog] = useState(null); const [packageToDiff, setPackageToDiff] = useState(null); const [moduleFilter, setModuleFilter] = useState(""); @@ -68,8 +79,13 @@ export const PackagesListTable: React.FC = ({ const updateTable = useCallback( ({ selection }: TableState) => { updateSelection(selection); + + if (onSelectionChange) { + const selectedIds = selection.map(selection => selection.id); + onSelectionChange(selectedIds); + } }, - [updateSelection] + [updateSelection, onSelectionChange] ); const downloadPackage = useCallback( @@ -230,7 +246,8 @@ export const PackagesListTable: React.FC = ({ [] ); - const canImport = presentation === "app" && isRemoteInstance && appConfigurator; + const canImport = + !isImportDialog && presentation === "app" && isRemoteInstance && appConfigurator; const actions: TableAction[] = useMemo( () => [ @@ -247,7 +264,11 @@ export const PackagesListTable: React.FC = ({ onClick: deletePackages, icon: delete, isActive: () => - presentation === "app" && !isRemoteInstance && !showStore && appConfigurator, + !isImportDialog && + presentation === "app" && + !isRemoteInstance && + !showStore && + appConfigurator, }, { name: "download", @@ -263,7 +284,11 @@ export const PackagesListTable: React.FC = ({ onClick: publishPackage, icon: publish, isActive: () => - presentation === "app" && !isRemoteInstance && !showStore && appConfigurator, + !isImportDialog && + presentation === "app" && + !isRemoteInstance && + !showStore && + appConfigurator, }, { name: "compare-with-local", @@ -294,6 +319,7 @@ export const PackagesListTable: React.FC = ({ publishPackage, showStore, canImport, + isImportDialog, ] ); diff --git a/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx b/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx index a70e50423..e8823727d 100644 --- a/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx +++ b/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx @@ -2,6 +2,7 @@ import { PaginationOptions } from "d2-ui-components"; import React, { ReactNode, useCallback, useMemo, useState } from "react"; import { useHistory, useParams } from "react-router-dom"; import { Instance } from "../../../../domain/instance/entities/Instance"; +import { PackageImportRule } from "../../../../domain/package-import/entities/PackageImportRule"; import i18n from "../../../../locales"; import SyncReport from "../../../../models/syncReport"; import { @@ -28,6 +29,9 @@ export const ModulePackageListPage: React.FC = () => { const history = useHistory(); const [syncReport, setSyncReport] = useState(); const [openImportPackageDialog, setOpenImportPackageDialog] = useState(false); + const [packageImportRule, setPackageImportRule] = useState( + undefined + ); const { list: tableOption = "modules" } = useParams<{ list: ViewOption }>(); const title = buildTitle(tableOption); @@ -60,6 +64,18 @@ export const ModulePackageListPage: React.FC = () => { [tableOption] ); + const handleInstanceChange = (instance?: Instance) => { + setPackageImportRule(instance ? PackageImportRule.create(instance) : undefined); + }; + + const handlePackageImportRuleChange = (packageImportRule: PackageImportRule) => { + setPackageImportRule(packageImportRule); + }; + + const handleExecuteImport = (packageImportRule: PackageImportRule) => { + console.log({ packageImportRule }, "Execute Import"); + }; + return ( @@ -72,16 +88,22 @@ export const ModulePackageListPage: React.FC = () => { onViewChange={setTableOption} presentation={"app"} openSyncSummary={setSyncReport} + onInstanceChange={handleInstanceChange} /> {!!syncReport && ( setSyncReport(undefined)} /> )} - setOpenImportPackageDialog(false)} - /> + {packageImportRule && ( + setOpenImportPackageDialog(false)} + packageImportRule={packageImportRule} + onChange={handlePackageImportRuleChange} + executeImport={handleExecuteImport} + /> + )} ); }; From f860b9320b7c87a9339f3d9b869edb666092ce2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Tue, 20 Oct 2020 11:48:36 +0200 Subject: [PATCH 006/151] Create SummaryStep --- i18n/en.pot | 7 +- i18n/es.po | 5 +- i18n/fr.po | 5 +- i18n/pt.po | 5 +- .../PackageImportWizard.tsx | 9 +-- .../steps/SummaryStep.tsx | 66 +++++++++++++++++++ 6 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 src/presentation/react/components/package-import-wizard/steps/SummaryStep.tsx diff --git a/i18n/en.pot b/i18n/en.pot index 76c38085b..8c98ba13e 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-20T08:55:38.811Z\n" -"PO-Revision-Date: 2020-10-20T08:55:38.811Z\n" +"POT-Creation-Date: 2020-10-20T09:47:06.029Z\n" +"PO-Revision-Date: 2020-10-20T09:47:06.029Z\n" msgid "Field {{field}} cannot be blank" msgstr "" @@ -532,6 +532,9 @@ msgstr "" msgid "Packages mapping" msgstr "" +msgid "Instance" +msgstr "" + msgid "Invalid package" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index 2a92ad692..08c33b324 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-19T15:55:37.566Z\n" +"POT-Creation-Date: 2020-10-20T09:26:11.585Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -535,6 +535,9 @@ msgstr "" msgid "Packages mapping" msgstr "Paquetes" +msgid "Instance" +msgstr "" + msgid "Invalid package" msgstr "" diff --git a/i18n/fr.po b/i18n/fr.po index 24ecc5a59..a03b660c9 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-19T15:55:37.566Z\n" +"POT-Creation-Date: 2020-10-20T09:26:11.585Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -533,6 +533,9 @@ msgstr "" msgid "Packages mapping" msgstr "" +msgid "Instance" +msgstr "" + msgid "Invalid package" msgstr "" diff --git a/i18n/pt.po b/i18n/pt.po index 24ecc5a59..a03b660c9 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-19T15:55:37.566Z\n" +"POT-Creation-Date: 2020-10-20T09:26:11.585Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -533,6 +533,9 @@ msgstr "" msgid "Packages mapping" msgstr "" +msgid "Instance" +msgstr "" + msgid "Invalid package" msgstr "" diff --git a/src/presentation/react/components/package-import-wizard/PackageImportWizard.tsx b/src/presentation/react/components/package-import-wizard/PackageImportWizard.tsx index 7dea4cc8e..48b085c48 100644 --- a/src/presentation/react/components/package-import-wizard/PackageImportWizard.tsx +++ b/src/presentation/react/components/package-import-wizard/PackageImportWizard.tsx @@ -4,10 +4,10 @@ import { useLocation } from "react-router-dom"; import { PackageImportRule } from "../../../../domain/package-import/entities/PackageImportRule"; import i18n from "../../../../locales"; import { PackageSelectionStep } from "./steps/PackageSelectionStep"; +import { SummaryStep } from "./steps/SummaryStep"; export interface PackageImportWizardStep extends WizardStep { validationKeys: string[]; - showOnSyncDialog?: boolean; } export interface PackageImportWizardStepProps { @@ -18,16 +18,14 @@ export interface PackageImportWizardStepProps { } const GeneralInfoStep = () =>
General info
; -//const PackagesStep = () =>
Packages
; const PackagesMetadataStep = () =>
Packages
; -const SummaryStep = () =>
Summary
; export const stepsBaseInfo = [ { key: "general-info", label: i18n.t("General info"), component: GeneralInfoStep, - validationKeys: ["name", "department"], + validationKeys: [], }, { key: "packages", @@ -39,14 +37,13 @@ export const stepsBaseInfo = [ key: "package-mapping", label: i18n.t("Packages mapping"), component: PackagesMetadataStep, - validationKeys: ["name", "department"], + validationKeys: [], }, { key: "summary", label: i18n.t("Summary"), component: SummaryStep, validationKeys: [], - showOnSyncDialog: true, }, ]; diff --git a/src/presentation/react/components/package-import-wizard/steps/SummaryStep.tsx b/src/presentation/react/components/package-import-wizard/steps/SummaryStep.tsx new file mode 100644 index 000000000..d24c01ba4 --- /dev/null +++ b/src/presentation/react/components/package-import-wizard/steps/SummaryStep.tsx @@ -0,0 +1,66 @@ +import { useSnackbar } from "d2-ui-components"; +import _ from "lodash"; +import React, { ReactNode, useEffect, useState } from "react"; +import { ListPackage } from "../../../../../domain/packages/entities/Package"; +import i18n from "../../../../../locales"; +import { isGlobalAdmin } from "../../../../../utils/permissions"; +import { useAppContext } from "../../../contexts/AppContext"; +import { PackageImportWizardProps } from "../PackageImportWizard"; + +export const SummaryStep: React.FC = ({ packageImportRule }) => { + const { api, compositionRoot } = useAppContext(); + const snackbar = useSnackbar(); + + const packageList = compositionRoot.packages.list; + + const [globalAdmin, setGlobalAdmin] = useState(false); + const [instancePackages, setInstancePackages] = useState([]); + //const [storePackages, setStorePackages] = useState([]); + + useEffect(() => { + isGlobalAdmin(api).then(setGlobalAdmin); + }, [api]); + + useEffect(() => { + packageList(globalAdmin, packageImportRule.instance) + .then(setInstancePackages) + .catch((error: Error) => { + snackbar.error(error.message); + setInstancePackages([]); + }); + }, [packageList, packageImportRule, globalAdmin, snackbar]); + + return ( + +
    + + +
      + {packageImportRule.packageIds.map(id => { + const instancePackage = instancePackages.find(pkg => pkg.id === id); + return ; + })} +
    +
    +
+
+ ); +}; + +interface Entry { + label: string; + value?: string | number; + children?: ReactNode; + hide?: boolean; +} + +const LiEntry = ({ label, value, children, hide = false }: Entry) => { + if (hide) return null; + + return ( +
  • + {_.compact([label, value]).join(": ")} + {children} +
  • + ); +}; From 0c793ca9f514dd47788d35d3e0e815dcf6583ba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Tue, 20 Oct 2020 12:21:26 +0200 Subject: [PATCH 007/151] Implement import for the first package rule --- i18n/en.pot | 4 +- .../ModulePackageListPage.tsx | 42 +++++++++++++++++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 8c98ba13e..97a61457f 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-20T09:47:06.029Z\n" -"PO-Revision-Date: 2020-10-20T09:47:06.029Z\n" +"POT-Creation-Date: 2020-10-20T10:17:34.025Z\n" +"PO-Revision-Date: 2020-10-20T10:17:34.025Z\n" msgid "Field {{field}} cannot be blank" msgstr "" diff --git a/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx b/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx index e8823727d..280fb1977 100644 --- a/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx +++ b/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx @@ -1,4 +1,4 @@ -import { PaginationOptions } from "d2-ui-components"; +import { PaginationOptions, useLoading, useSnackbar } from "d2-ui-components"; import React, { ReactNode, useCallback, useMemo, useState } from "react"; import { useHistory, useParams } from "react-router-dom"; import { Instance } from "../../../../domain/instance/entities/Instance"; @@ -13,6 +13,7 @@ import { import PackageImportDialog from "../../../react/components/package-import-dialog/PackageImportDialog"; import PageHeader from "../../../react/components/page-header/PageHeader"; import SyncSummary from "../../../react/components/sync-summary/SyncSummary"; +import { useAppContext } from "../../../react/contexts/AppContext"; export interface ModulePackageListPageProps { remoteInstance?: Instance; @@ -27,6 +28,9 @@ export interface ModulePackageListPageProps { export const ModulePackageListPage: React.FC = () => { const history = useHistory(); + const snackbar = useSnackbar(); + const loading = useLoading(); + const { compositionRoot, api } = useAppContext(); const [syncReport, setSyncReport] = useState(); const [openImportPackageDialog, setOpenImportPackageDialog] = useState(false); const [packageImportRule, setPackageImportRule] = useState( @@ -72,8 +76,40 @@ export const ModulePackageListPage: React.FC = () => { setPackageImportRule(packageImportRule); }; - const handleExecuteImport = (packageImportRule: PackageImportRule) => { - console.log({ packageImportRule }, "Execute Import"); + const handleExecuteImport = async (packageImportRule: PackageImportRule) => { + const result = await compositionRoot.packages.get( + packageImportRule.packageIds[0], + packageImportRule.instance + ); + result.match({ + success: async ({ name, contents }) => { + try { + loading.show(true, i18n.t("Importing package {{name}}", { name })); + const result = await compositionRoot.metadata.import(contents); + + const report = SyncReport.create("metadata"); + report.setStatus( + result.status === "ERROR" || result.status === "NETWORK ERROR" + ? "FAILURE" + : "DONE" + ); + report.addSyncResult({ + ...result, + origin: packageImportRule.instance.toPublicObject(), + }); + await report.save(api); + + setSyncReport(report); + } catch (error) { + snackbar.error(error.message); + } + loading.reset(); + setOpenImportPackageDialog(false); + }, + error: async () => { + snackbar.error(i18n.t("Couldn't load package")); + }, + }); }; return ( From e5cf1a5d946b56ee8ac90a4e0d7e2e89998b7073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Tue, 20 Oct 2020 12:36:57 +0200 Subject: [PATCH 008/151] Move import handler to PackageImportDialog --- i18n/en.pot | 16 ++--- i18n/es.po | 14 ++-- i18n/fr.po | 14 ++-- i18n/pt.po | 14 ++-- .../PackageImportDialog.tsx | 70 +++++++++++++++---- .../PackageImportWizard.tsx | 2 +- .../ModulePackageListPage.tsx | 65 +++-------------- 7 files changed, 97 insertions(+), 98 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 97a61457f..6ad68b086 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-20T10:17:34.025Z\n" -"PO-Revision-Date: 2020-10-20T10:17:34.025Z\n" +"POT-Creation-Date: 2020-10-20T10:35:03.005Z\n" +"PO-Revision-Date: 2020-10-20T10:35:03.005Z\n" msgid "Field {{field}} cannot be blank" msgstr "" @@ -523,6 +523,12 @@ msgstr "" msgid "[Pull Request by {{name}}] {{subject}}" msgstr "" +msgid "Importing package {{name}}" +msgstr "" + +msgid "Couldn't load package" +msgstr "" + msgid "Packages Import" msgstr "" @@ -573,12 +579,6 @@ msgstr "" msgid "Couldn't create new branch on store" msgstr "" -msgid "Importing package {{name}}" -msgstr "" - -msgid "Couldn't load package" -msgstr "" - msgid "Version" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index 08c33b324..2665ae187 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-20T09:26:11.585Z\n" +"POT-Creation-Date: 2020-10-20T10:33:16.288Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -524,6 +524,12 @@ msgstr "" msgid "[Pull Request by {{name}}] {{subject}}" msgstr "" +msgid "Importing package {{name}}" +msgstr "" + +msgid "Couldn't load package" +msgstr "" + #, fuzzy msgid "Packages Import" msgstr "Paquetes" @@ -576,12 +582,6 @@ msgstr "" msgid "Couldn't create new branch on store" msgstr "" -msgid "Importing package {{name}}" -msgstr "" - -msgid "Couldn't load package" -msgstr "" - msgid "Version" msgstr "" diff --git a/i18n/fr.po b/i18n/fr.po index a03b660c9..115f50b57 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-20T09:26:11.585Z\n" +"POT-Creation-Date: 2020-10-20T10:33:16.288Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -524,6 +524,12 @@ msgstr "" msgid "[Pull Request by {{name}}] {{subject}}" msgstr "" +msgid "Importing package {{name}}" +msgstr "" + +msgid "Couldn't load package" +msgstr "" + msgid "Packages Import" msgstr "" @@ -574,12 +580,6 @@ msgstr "" msgid "Couldn't create new branch on store" msgstr "" -msgid "Importing package {{name}}" -msgstr "" - -msgid "Couldn't load package" -msgstr "" - msgid "Version" msgstr "" diff --git a/i18n/pt.po b/i18n/pt.po index a03b660c9..115f50b57 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-20T09:26:11.585Z\n" +"POT-Creation-Date: 2020-10-20T10:33:16.288Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -524,6 +524,12 @@ msgstr "" msgid "[Pull Request by {{name}}] {{subject}}" msgstr "" +msgid "Importing package {{name}}" +msgstr "" + +msgid "Couldn't load package" +msgstr "" + msgid "Packages Import" msgstr "" @@ -574,12 +580,6 @@ msgstr "" msgid "Couldn't create new branch on store" msgstr "" -msgid "Importing package {{name}}" -msgstr "" - -msgid "Couldn't load package" -msgstr "" - msgid "Version" msgstr "" diff --git a/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx b/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx index 0f77d3507..d0875e791 100644 --- a/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx +++ b/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx @@ -1,36 +1,82 @@ import DialogContent from "@material-ui/core/DialogContent"; -import { ConfirmationDialog } from "d2-ui-components"; -import React, { useEffect, useState } from "react"; +import { ConfirmationDialog, useLoading, useSnackbar } from "d2-ui-components"; +import React, { useState } from "react"; +import { Instance } from "../../../../domain/instance/entities/Instance"; import { PackageImportRule } from "../../../../domain/package-import/entities/PackageImportRule"; import i18n from "../../../../locales"; +import SyncReport from "../../../../models/syncReport"; +import { useAppContext } from "../../contexts/AppContext"; import { PackageImportWizard } from "../package-import-wizard/PackageImportWizard"; interface PackageImportDialogProps { isOpen: boolean; - packageImportRule: PackageImportRule; - onChange: (packageImportRule: PackageImportRule) => void; + instance: Instance; onClose: () => void; - executeImport: (packageImportRule: PackageImportRule) => void; + openSyncSummary?: (result: SyncReport) => void; } const PackageImportDialog: React.FC = ({ isOpen, - packageImportRule, - onChange, + instance, onClose, - executeImport, + openSyncSummary, }) => { const [enableImport, setEnableImport] = useState(false); + const snackbar = useSnackbar(); + const loading = useLoading(); + const { compositionRoot, api } = useAppContext(); - useEffect(() => { + const [packageImportRule, setPackageImportRule] = useState( + PackageImportRule.create(instance) + ); + + const handlePackageImportRuleChange = (packageImportRule: PackageImportRule) => { setEnableImport(packageImportRule.validate().length === 0); - }, [packageImportRule]); + setPackageImportRule(packageImportRule); + }; + + const handleExecuteImport = async () => { + const result = await compositionRoot.packages.get( + packageImportRule.packageIds[0], + packageImportRule.instance + ); + result.match({ + success: async ({ name, contents }) => { + try { + loading.show(true, i18n.t("Importing package {{name}}", { name })); + const result = await compositionRoot.metadata.import(contents); + + const report = SyncReport.create("metadata"); + report.setStatus( + result.status === "ERROR" || result.status === "NETWORK ERROR" + ? "FAILURE" + : "DONE" + ); + report.addSyncResult({ + ...result, + origin: packageImportRule.instance.toPublicObject(), + }); + await report.save(api); + + if (openSyncSummary) { + openSyncSummary(report); + } + } catch (error) { + snackbar.error(error.message); + } + loading.reset(); + }, + error: async () => { + snackbar.error(i18n.t("Couldn't load package")); + }, + }); + }; return ( executeImport(packageImportRule)} + onSave={() => handleExecuteImport()} onCancel={onClose} saveText={i18n.t("Import")} maxWidth={"lg"} @@ -40,7 +86,7 @@ const PackageImportDialog: React.FC = ({ diff --git a/src/presentation/react/components/package-import-wizard/PackageImportWizard.tsx b/src/presentation/react/components/package-import-wizard/PackageImportWizard.tsx index 48b085c48..ed882a04c 100644 --- a/src/presentation/react/components/package-import-wizard/PackageImportWizard.tsx +++ b/src/presentation/react/components/package-import-wizard/PackageImportWizard.tsx @@ -18,7 +18,7 @@ export interface PackageImportWizardStepProps { } const GeneralInfoStep = () =>
    General info
    ; -const PackagesMetadataStep = () =>
    Packages
    ; +const PackagesMetadataStep = () =>
    Packages Mapping
    ; export const stepsBaseInfo = [ { diff --git a/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx b/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx index 280fb1977..2adbb5baf 100644 --- a/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx +++ b/src/presentation/webapp/pages/module-package-list/ModulePackageListPage.tsx @@ -1,8 +1,7 @@ -import { PaginationOptions, useLoading, useSnackbar } from "d2-ui-components"; +import { PaginationOptions } from "d2-ui-components"; import React, { ReactNode, useCallback, useMemo, useState } from "react"; import { useHistory, useParams } from "react-router-dom"; import { Instance } from "../../../../domain/instance/entities/Instance"; -import { PackageImportRule } from "../../../../domain/package-import/entities/PackageImportRule"; import i18n from "../../../../locales"; import SyncReport from "../../../../models/syncReport"; import { @@ -13,7 +12,6 @@ import { import PackageImportDialog from "../../../react/components/package-import-dialog/PackageImportDialog"; import PageHeader from "../../../react/components/page-header/PageHeader"; import SyncSummary from "../../../react/components/sync-summary/SyncSummary"; -import { useAppContext } from "../../../react/contexts/AppContext"; export interface ModulePackageListPageProps { remoteInstance?: Instance; @@ -28,14 +26,9 @@ export interface ModulePackageListPageProps { export const ModulePackageListPage: React.FC = () => { const history = useHistory(); - const snackbar = useSnackbar(); - const loading = useLoading(); - const { compositionRoot, api } = useAppContext(); const [syncReport, setSyncReport] = useState(); const [openImportPackageDialog, setOpenImportPackageDialog] = useState(false); - const [packageImportRule, setPackageImportRule] = useState( - undefined - ); + const [selectedInstance, setSelectedInstance] = useState(); const { list: tableOption = "modules" } = useParams<{ list: ViewOption }>(); const title = buildTitle(tableOption); @@ -68,48 +61,9 @@ export const ModulePackageListPage: React.FC = () => { [tableOption] ); - const handleInstanceChange = (instance?: Instance) => { - setPackageImportRule(instance ? PackageImportRule.create(instance) : undefined); - }; - - const handlePackageImportRuleChange = (packageImportRule: PackageImportRule) => { - setPackageImportRule(packageImportRule); - }; - - const handleExecuteImport = async (packageImportRule: PackageImportRule) => { - const result = await compositionRoot.packages.get( - packageImportRule.packageIds[0], - packageImportRule.instance - ); - result.match({ - success: async ({ name, contents }) => { - try { - loading.show(true, i18n.t("Importing package {{name}}", { name })); - const result = await compositionRoot.metadata.import(contents); - - const report = SyncReport.create("metadata"); - report.setStatus( - result.status === "ERROR" || result.status === "NETWORK ERROR" - ? "FAILURE" - : "DONE" - ); - report.addSyncResult({ - ...result, - origin: packageImportRule.instance.toPublicObject(), - }); - await report.save(api); - - setSyncReport(report); - } catch (error) { - snackbar.error(error.message); - } - loading.reset(); - setOpenImportPackageDialog(false); - }, - error: async () => { - snackbar.error(i18n.t("Couldn't load package")); - }, - }); + const handleOpenSyncSummaryFromDialog = (syncReport: SyncReport) => { + setOpenImportPackageDialog(false); + setSyncReport(syncReport); }; return ( @@ -124,20 +78,19 @@ export const ModulePackageListPage: React.FC = () => { onViewChange={setTableOption} presentation={"app"} openSyncSummary={setSyncReport} - onInstanceChange={handleInstanceChange} + onInstanceChange={setSelectedInstance} /> {!!syncReport && ( setSyncReport(undefined)} /> )} - {packageImportRule && ( + {selectedInstance && ( setOpenImportPackageDialog(false)} - packageImportRule={packageImportRule} - onChange={handlePackageImportRuleChange} - executeImport={handleExecuteImport} + instance={selectedInstance} + openSyncSummary={handleOpenSyncSummaryFromDialog} /> )} From 02ae975603454be2477ccd1cf48f0e31d5deac24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Tue, 20 Oct 2020 13:33:49 +0200 Subject: [PATCH 009/151] Add package info to sync result, report and summary --- i18n/en.pot | 7 +++++-- i18n/es.po | 5 ++++- i18n/fr.po | 5 ++++- i18n/pt.po | 5 ++++- .../entities/SynchronizationResult.ts | 2 ++ src/models/syncReport.ts | 2 +- .../react/components/sync-summary/SyncSummary.tsx | 15 ++++++++++++++- 7 files changed, 34 insertions(+), 7 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 6ad68b086..b425dac59 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-20T10:35:03.005Z\n" -"PO-Revision-Date: 2020-10-20T10:35:03.005Z\n" +"POT-Creation-Date: 2020-10-20T11:32:21.869Z\n" +"PO-Revision-Date: 2020-10-20T11:32:21.869Z\n" msgid "Field {{field}} cannot be blank" msgstr "" @@ -744,6 +744,9 @@ msgstr "" msgid "Origin instance" msgstr "" +msgid "Origin package" +msgstr "" + msgid "Destination instance" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index 2665ae187..4532e9344 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-20T10:33:16.288Z\n" +"POT-Creation-Date: 2020-10-20T11:32:21.869Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -747,6 +747,9 @@ msgstr "" msgid "Origin instance" msgstr "" +msgid "Origin package" +msgstr "" + msgid "Destination instance" msgstr "" diff --git a/i18n/fr.po b/i18n/fr.po index 115f50b57..0030f2720 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-20T10:33:16.288Z\n" +"POT-Creation-Date: 2020-10-20T11:32:21.869Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -745,6 +745,9 @@ msgstr "" msgid "Origin instance" msgstr "" +msgid "Origin package" +msgstr "" + msgid "Destination instance" msgstr "" diff --git a/i18n/pt.po b/i18n/pt.po index 115f50b57..0030f2720 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-20T10:33:16.288Z\n" +"POT-Creation-Date: 2020-10-20T11:32:21.869Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -745,6 +745,9 @@ msgstr "" msgid "Origin instance" msgstr "" +msgid "Origin package" +msgstr "" + msgid "Destination instance" msgstr "" diff --git a/src/domain/synchronization/entities/SynchronizationResult.ts b/src/domain/synchronization/entities/SynchronizationResult.ts index 48807fac1..e7e16b8a0 100644 --- a/src/domain/synchronization/entities/SynchronizationResult.ts +++ b/src/domain/synchronization/entities/SynchronizationResult.ts @@ -1,4 +1,5 @@ import { PublicInstance } from "../../instance/entities/Instance"; +import { Package } from "../../packages/entities/Package"; import { SynchronizationType } from "./SynchronizationType"; export type SynchronizationStatus = "PENDING" | "SUCCESS" | "WARNING" | "ERROR" | "NETWORK ERROR"; @@ -23,6 +24,7 @@ export interface SynchronizationResult { status: SynchronizationStatus; origin?: PublicInstance; instance: PublicInstance; + originPackage?: Package; date: Date; type: SynchronizationType; message?: string; diff --git a/src/models/syncReport.ts b/src/models/syncReport.ts index 33e8925e5..a7eefe2b8 100644 --- a/src/models/syncReport.ts +++ b/src/models/syncReport.ts @@ -116,7 +116,7 @@ export default class SyncReport { this.results = _.unionBy( [...result], this.results, - ({ instance, type }) => `${instance.id}-${type}` + ({ instance, type, originPackage }) => `${instance.id}-${type}-${originPackage?.id}` ); } diff --git a/src/presentation/react/components/sync-summary/SyncSummary.tsx b/src/presentation/react/components/sync-summary/SyncSummary.tsx index 53443785f..a17daf85a 100644 --- a/src/presentation/react/components/sync-summary/SyncSummary.tsx +++ b/src/presentation/react/components/sync-summary/SyncSummary.tsx @@ -196,7 +196,17 @@ const SyncSummary = ({ response, onClose }: SyncSummaryProps) => { {results.map( ( - { origin, instance, status, typeStats = [], stats, message, errors, type }, + { + origin, + instance, + status, + typeStats = [], + stats, + message, + errors, + type, + originPackage, + }, i ) => ( {
    {origin && `${i18n.t("Origin instance")}: ${origin.name}`} {origin &&
    } + {originPackage && + `${i18n.t("Origin package")}: ${originPackage.name}`} + {originPackage &&
    } {`${i18n.t("Destination instance")}: ${instance.name}`} From 888f4de3afa883468e1b1f137a21c5dc720d41ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Tue, 20 Oct 2020 13:34:17 +0200 Subject: [PATCH 010/151] Execute import for all packages selected in wizard --- .../PackageImportDialog.tsx | 78 +++++++++++-------- 1 file changed, 47 insertions(+), 31 deletions(-) diff --git a/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx b/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx index d0875e791..e8b0613d9 100644 --- a/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx +++ b/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx @@ -36,40 +36,56 @@ const PackageImportDialog: React.FC = ({ }; const handleExecuteImport = async () => { - const result = await compositionRoot.packages.get( - packageImportRule.packageIds[0], - packageImportRule.instance - ); - result.match({ - success: async ({ name, contents }) => { - try { - loading.show(true, i18n.t("Importing package {{name}}", { name })); - const result = await compositionRoot.metadata.import(contents); + const report = SyncReport.create("metadata"); - const report = SyncReport.create("metadata"); - report.setStatus( - result.status === "ERROR" || result.status === "NETWORK ERROR" - ? "FAILURE" - : "DONE" - ); - report.addSyncResult({ - ...result, - origin: packageImportRule.instance.toPublicObject(), - }); - await report.save(api); + const executePackageImport = async (packageId: string) => { + const result = await compositionRoot.packages.get( + packageId, + packageImportRule.instance + ); - if (openSyncSummary) { - openSyncSummary(report); + result.match({ + success: async originPackage => { + try { + loading.show( + true, + i18n.t("Importing package {{name}}", { name: originPackage.name }) + ); + const result = await compositionRoot.metadata.import( + originPackage.contents + ); + + report.setStatus( + result.status === "ERROR" || result.status === "NETWORK ERROR" + ? "FAILURE" + : "DONE" + ); + report.addSyncResult({ + ...result, + originPackage, + origin: packageImportRule.instance.toPublicObject(), + }); + loading.reset(); + } catch (error) { + snackbar.error(error.message); } - } catch (error) { - snackbar.error(error.message); - } - loading.reset(); - }, - error: async () => { - snackbar.error(i18n.t("Couldn't load package")); - }, - }); + }, + error: async () => { + loading.reset(); + snackbar.error(i18n.t("Couldn't load package")); + }, + }); + }; + + for (const id of packageImportRule.packageIds) { + await executePackageImport(id); + } + + await report.save(api); + + if (openSyncSummary) { + openSyncSummary(report); + } }; return ( From d1f3c02c3fdf5f4915d0f5e5dca02182299660c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Wed, 21 Oct 2020 12:16:38 +0200 Subject: [PATCH 011/151] Save in the history only nameRef for origin package --- src/domain/packages/entities/Package.ts | 4 ++++ src/domain/synchronization/entities/SynchronizationResult.ts | 4 ++-- .../components/package-import-dialog/PackageImportDialog.tsx | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/domain/packages/entities/Package.ts b/src/domain/packages/entities/Package.ts index a644a127a..059ad8aa2 100644 --- a/src/domain/packages/entities/Package.ts +++ b/src/domain/packages/entities/Package.ts @@ -133,4 +133,8 @@ export class Package implements BasePackage { return []; } + + toRef(): NamedRef { + return { id: this.id, name: this.name }; + } } diff --git a/src/domain/synchronization/entities/SynchronizationResult.ts b/src/domain/synchronization/entities/SynchronizationResult.ts index e7e16b8a0..a39a71a38 100644 --- a/src/domain/synchronization/entities/SynchronizationResult.ts +++ b/src/domain/synchronization/entities/SynchronizationResult.ts @@ -1,5 +1,5 @@ +import { NamedRef } from "../../common/entities/Ref"; import { PublicInstance } from "../../instance/entities/Instance"; -import { Package } from "../../packages/entities/Package"; import { SynchronizationType } from "./SynchronizationType"; export type SynchronizationStatus = "PENDING" | "SUCCESS" | "WARNING" | "ERROR" | "NETWORK ERROR"; @@ -24,7 +24,7 @@ export interface SynchronizationResult { status: SynchronizationStatus; origin?: PublicInstance; instance: PublicInstance; - originPackage?: Package; + originPackage?: NamedRef; date: Date; type: SynchronizationType; message?: string; diff --git a/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx b/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx index e8b0613d9..d5b814037 100644 --- a/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx +++ b/src/presentation/react/components/package-import-dialog/PackageImportDialog.tsx @@ -36,6 +36,8 @@ const PackageImportDialog: React.FC = ({ }; const handleExecuteImport = async () => { + // TODO: this coordination to import several packages and save the result + // should be in the domain layer, may be a new use case? const report = SyncReport.create("metadata"); const executePackageImport = async (packageId: string) => { @@ -62,7 +64,7 @@ const PackageImportDialog: React.FC = ({ ); report.addSyncResult({ ...result, - originPackage, + originPackage: originPackage.toRef(), origin: packageImportRule.instance.toPublicObject(), }); loading.reset(); From 7c1b31c8e7efb6d6397f733fca103f9efb049122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Wed, 21 Oct 2020 12:17:16 +0200 Subject: [PATCH 012/151] Rename get package function variable --- .../components/package-import-wizard/steps/SummaryStep.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/presentation/react/components/package-import-wizard/steps/SummaryStep.tsx b/src/presentation/react/components/package-import-wizard/steps/SummaryStep.tsx index d24c01ba4..535adbffe 100644 --- a/src/presentation/react/components/package-import-wizard/steps/SummaryStep.tsx +++ b/src/presentation/react/components/package-import-wizard/steps/SummaryStep.tsx @@ -11,7 +11,7 @@ export const SummaryStep: React.FC = ({ packageImportR const { api, compositionRoot } = useAppContext(); const snackbar = useSnackbar(); - const packageList = compositionRoot.packages.list; + const getPackages = compositionRoot.packages.list; const [globalAdmin, setGlobalAdmin] = useState(false); const [instancePackages, setInstancePackages] = useState([]); @@ -22,13 +22,13 @@ export const SummaryStep: React.FC = ({ packageImportR }, [api]); useEffect(() => { - packageList(globalAdmin, packageImportRule.instance) + getPackages(globalAdmin, packageImportRule.instance) .then(setInstancePackages) .catch((error: Error) => { snackbar.error(error.message); setInstancePackages([]); }); - }, [packageList, packageImportRule, globalAdmin, snackbar]); + }, [getPackages, packageImportRule, globalAdmin, snackbar]); return ( From 98bf20a5b3236d0dafb0a611fc1541d6e2ab7c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Wed, 21 Oct 2020 13:22:55 +0200 Subject: [PATCH 013/151] Create initial PackageMappingStep --- i18n/en.pot | 7 +- i18n/es.po | 6 +- i18n/fr.po | 5 +- i18n/pt.po | 5 +- .../entities/PackageImportRule.ts | 12 +- .../components/mapping-table/MappingTable.tsx | 5 +- .../metadata-table/MetadataTable.tsx | 4 + .../PackageImportWizard.tsx | 5 +- .../steps/PackageMappingStep.tsx | 121 ++++++++++++++++++ 9 files changed, 161 insertions(+), 9 deletions(-) create mode 100644 src/presentation/react/components/package-import-wizard/steps/PackageMappingStep.tsx diff --git a/i18n/en.pot b/i18n/en.pot index b425dac59..8a96717f6 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-20T11:32:21.869Z\n" -"PO-Revision-Date: 2020-10-20T11:32:21.869Z\n" +"POT-Creation-Date: 2020-10-21T11:17:42.482Z\n" +"PO-Revision-Date: 2020-10-21T11:17:42.482Z\n" msgid "Field {{field}} cannot be blank" msgstr "" @@ -538,6 +538,9 @@ msgstr "" msgid "Packages mapping" msgstr "" +msgid "Package" +msgstr "" + msgid "Instance" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index 4532e9344..7ffa4a5fa 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-20T11:32:21.869Z\n" +"POT-Creation-Date: 2020-10-21T11:17:42.482Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -541,6 +541,10 @@ msgstr "" msgid "Packages mapping" msgstr "Paquetes" +#, fuzzy +msgid "Package" +msgstr "Paquetes" + msgid "Instance" msgstr "" diff --git a/i18n/fr.po b/i18n/fr.po index 0030f2720..aeeca3fae 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-20T11:32:21.869Z\n" +"POT-Creation-Date: 2020-10-21T11:17:42.482Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -539,6 +539,9 @@ msgstr "" msgid "Packages mapping" msgstr "" +msgid "Package" +msgstr "" + msgid "Instance" msgstr "" diff --git a/i18n/pt.po b/i18n/pt.po index 0030f2720..aeeca3fae 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-20T11:32:21.869Z\n" +"POT-Creation-Date: 2020-10-21T11:17:42.482Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -539,6 +539,9 @@ msgstr "" msgid "Packages mapping" msgstr "" +msgid "Package" +msgstr "" + msgid "Instance" msgstr "" diff --git a/src/domain/package-import/entities/PackageImportRule.ts b/src/domain/package-import/entities/PackageImportRule.ts index f8802b113..385748ebe 100644 --- a/src/domain/package-import/entities/PackageImportRule.ts +++ b/src/domain/package-import/entities/PackageImportRule.ts @@ -1,28 +1,38 @@ import { ModelValidation, validateModel, ValidationError } from "../../common/entities/Validations"; import { Instance } from "../../instance/entities/Instance"; +import { MetadataMappingDictionary } from "../../instance/entities/MetadataMapping"; interface PackageImportRuleData { instance: Instance; packageIds: string[]; + mappingByPackageId: Record; } export class PackageImportRule { public readonly instance: Instance; public readonly packageIds: string[]; + public readonly mappingByPackageId: Record; constructor(private data: PackageImportRuleData) { this.instance = data.instance; this.packageIds = data.packageIds; + this.mappingByPackageId = data.mappingByPackageId; } static create(instance: Instance): PackageImportRule { - return new PackageImportRule({ instance, packageIds: [] }); + return new PackageImportRule({ instance, packageIds: [], mappingByPackageId: {} }); } public updatePackageIds(packageIds: string[]): PackageImportRule { return new PackageImportRule({ ...this.data, packageIds }); } + public updateMappingsByPackageId( + mappingByPackageId: Record + ): PackageImportRule { + return new PackageImportRule({ ...this.data, mappingByPackageId }); + } + public validate(filter?: string[]): ValidationError[] { return validateModel(this, this.validations()).filter( ({ property }) => filter?.includes(property) ?? true diff --git a/src/presentation/react/components/mapping-table/MappingTable.tsx b/src/presentation/react/components/mapping-table/MappingTable.tsx index 510a76b9b..98493df35 100644 --- a/src/presentation/react/components/mapping-table/MappingTable.tsx +++ b/src/presentation/react/components/mapping-table/MappingTable.tsx @@ -9,7 +9,7 @@ import { useSnackbar, } from "d2-ui-components"; import _ from "lodash"; -import React, { useCallback, useMemo, useState } from "react"; +import React, { ReactNode, useCallback, useMemo, useState } from "react"; import { Instance } from "../../../../domain/instance/entities/Instance"; import { MetadataMapping, @@ -73,6 +73,7 @@ export interface MappingTableProps { onApplyGlobalMapping(type: string, id: string, mapping: MetadataMapping): Promise; isChildrenMapping?: boolean; mappingPath?: string[]; + externalFilterComponents?: ReactNode; } export default function MappingTable({ @@ -86,6 +87,7 @@ export default function MappingTable({ onApplyGlobalMapping, isChildrenMapping = false, mappingPath, + externalFilterComponents, }: MappingTableProps) { const { api, compositionRoot } = useAppContext(); const classes = useStyles(); @@ -872,6 +874,7 @@ export default function MappingTable({ globalActions={globalActions} childrenKeys={!isChildrenMapping ? model.getChildrenKeys() : undefined} rowConfig={rowConfig} + externalFilterComponets={externalFilterComponents} /> ); diff --git a/src/presentation/react/components/metadata-table/MetadataTable.tsx b/src/presentation/react/components/metadata-table/MetadataTable.tsx index 56325524f..dd5f601f6 100644 --- a/src/presentation/react/components/metadata-table/MetadataTable.tsx +++ b/src/presentation/react/components/metadata-table/MetadataTable.tsx @@ -49,6 +49,7 @@ interface MetadataTableProps extends Omit, "rows notifyRowsChange?(rows: MetadataType[]): void; allowChangingResponsible?: boolean; showOnlySelectedFilter?: boolean; + externalFilterComponets?: ReactNode; } const useStyles = makeStyles({ @@ -111,6 +112,7 @@ const MetadataTable: React.FC = ({ showIndeterminateSelection = false, allowChangingResponsible = false, showOnlySelectedFilter = true, + externalFilterComponets: externalFilterComponents, ...rest }) => { const { compositionRoot } = useAppContext(); @@ -235,6 +237,8 @@ const MetadataTable: React.FC = ({ const filterComponents = ( + {externalFilterComponents} + {models.length > 1 && (
    General info
    ; -const PackagesMetadataStep = () =>
    Packages Mapping
    ; +//const PackagesMetadataStep = () =>
    Packages Mapping
    ; export const stepsBaseInfo = [ { @@ -36,7 +37,7 @@ export const stepsBaseInfo = [ { key: "package-mapping", label: i18n.t("Packages mapping"), - component: PackagesMetadataStep, + component: PackageMappingStep, validationKeys: [], }, { diff --git a/src/presentation/react/components/package-import-wizard/steps/PackageMappingStep.tsx b/src/presentation/react/components/package-import-wizard/steps/PackageMappingStep.tsx new file mode 100644 index 000000000..10d560be9 --- /dev/null +++ b/src/presentation/react/components/package-import-wizard/steps/PackageMappingStep.tsx @@ -0,0 +1,121 @@ +import { useSnackbar } from "d2-ui-components"; +import React, { useEffect, useState } from "react"; +import { + MetadataMapping, + MetadataMappingDictionary, +} from "../../../../../domain/instance/entities/MetadataMapping"; +import { ListPackage } from "../../../../../domain/packages/entities/Package"; +import i18n from "../../../../../locales"; +import { + AggregatedDataElementModel, + EventProgramWithDataElementsModel, + EventProgramWithIndicatorsModel, + IndicatorMappedModel, + OrganisationUnitMappedModel, +} from "../../../../../models/dhis/mapping"; +import { isGlobalAdmin } from "../../../../../utils/permissions"; +import { useAppContext } from "../../../contexts/AppContext"; +import Dropdown from "../../dropdown/Dropdown"; +import MappingTable from "../../mapping-table/MappingTable"; +import { PackageImportWizardProps } from "../PackageImportWizard"; + +export const PackageMappingStep: React.FC = ({ + packageImportRule, + onChange, +}) => { + const [packageFilter, setPackageFilter] = useState(packageImportRule.packageIds[0]); + const [currentMetadataMapping, setCurrentMetadataMapping] = useState( + {} + ); + const { compositionRoot, api } = useAppContext(); + const snackbar = useSnackbar(); + const getPackages = compositionRoot.packages.list; + + const [globalAdmin, setGlobalAdmin] = useState(false); + const [packages, setPackages] = useState([]); + + const models = [ + AggregatedDataElementModel, + IndicatorMappedModel, + EventProgramWithDataElementsModel, + EventProgramWithIndicatorsModel, + OrganisationUnitMappedModel, + ]; + + useEffect(() => { + isGlobalAdmin(api).then(setGlobalAdmin); + }, [api]); + + useEffect(() => { + getPackages(globalAdmin, packageImportRule.instance) + .then(packages => { + const importPackages = packages.filter(pkg => + packageImportRule.packageIds.includes(pkg.id) + ); + + setPackages(importPackages); + }) + .catch((error: Error) => { + snackbar.error(error.message); + setPackages([]); + }); + }, [getPackages, packageImportRule, globalAdmin, snackbar]); + + const onChangePackageFilter = (selectedPackageId: string) => { + const metadataMapping = packageImportRule.mappingByPackageId[selectedPackageId] || {}; + + setCurrentMetadataMapping(metadataMapping); + setPackageFilter(selectedPackageId); + }; + + const onChangeMapping = async (metadataMapping: MetadataMappingDictionary) => { + setCurrentMetadataMapping(metadataMapping); + + const metadataMappingsByPackageId = { + ...packageImportRule.mappingByPackageId, + [packageFilter]: metadataMapping, + }; + onChange(packageImportRule.updateMappingsByPackageId(metadataMappingsByPackageId)); + + // if (!instance) return; + // const newInstance = instance.update({ metadataMapping }); + // await compositionRoot.instances.save(newInstance); + // setInstance(newInstance); + }; + + const onApplyGlobalMapping = async ( + _type: string, + _id: string, + _subMapping: MetadataMapping + ) => { + // if (!instance) return; + // const newMapping = _.clone(instance.metadataMapping); + // _.set(newMapping, [type, id], { ...subMapping, global: true }); + // await onChangeMapping(newMapping); + }; + + const packageFilterComponent = ( + + ); + + return ( + + + + ); +}; From 501c3353fe3e0ec278e50dd02a236b6b554ce5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Thu, 22 Oct 2020 09:25:18 +0200 Subject: [PATCH 014/151] Create migration to allow multiple stores --- src/migrations/tasks/05.multiple-stores.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/migrations/tasks/05.multiple-stores.ts diff --git a/src/migrations/tasks/05.multiple-stores.ts b/src/migrations/tasks/05.multiple-stores.ts new file mode 100644 index 000000000..7ca7ab9ff --- /dev/null +++ b/src/migrations/tasks/05.multiple-stores.ts @@ -0,0 +1,19 @@ +import { generateUid } from "d2/uid"; +import { Store } from "../../domain/packages/entities/Store"; +import { deleteDataStore, saveDataStore } from "../../models/dataStore"; +import { D2Api } from "../../types/d2-api"; + +export default async function migrate(api: D2Api): Promise { + const oldKey = "store"; + const newKey = "stores"; + + const dataStore = api.dataStore("metadata-synchronization"); + const oldContents = await dataStore.get(oldKey).getData(); + + if (oldContents) { + const newContents = [{ ...oldContents, id: generateUid(), default: true }]; + + await saveDataStore(api, newKey, newContents); + await deleteDataStore(api, oldKey); + } +} From ffc7cccb26fb6eb739cbbec7bb201df9b6c795af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Thu, 22 Oct 2020 10:34:46 +0200 Subject: [PATCH 015/151] update migration tasks --- src/migrations/tasks/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/migrations/tasks/index.ts b/src/migrations/tasks/index.ts index c24495f88..fb07812bf 100644 --- a/src/migrations/tasks/index.ts +++ b/src/migrations/tasks/index.ts @@ -3,10 +3,12 @@ import Migration01 from "./01.instances-by-id"; import Migration02 from "./02.rules-by-id"; import Migration03 from "./03.sync-reports"; import Migration04 from "./04.history-notifications"; +import Migration05 from "./05.multiple-stores"; export const migrationTasks: Migration[] = [ { version: 1, name: "01.instances-by-id", fn: Migration01 }, { version: 2, name: "02.rules-by-id", fn: Migration02 }, { version: 3, name: "03.sync-reports", fn: Migration03 }, { version: 4, name: "04.history-notifications", fn: Migration04 }, + { version: 5, name: "05.multiple-stores", fn: Migration05 }, ]; From b9657efd16bcd241f2f8536772ad2827c75f0c63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Thu, 22 Oct 2020 10:39:37 +0200 Subject: [PATCH 016/151] Create store list page --- i18n/en.pot | 36 +++- i18n/es.po | 34 +++- i18n/fr.po | 34 +++- i18n/pt.po | 34 +++- src/domain/packages/entities/Store.ts | 2 + .../packages/usecases/GetStoreUseCase.ts | 2 + .../packages/usecases/ListStoresUseCase.ts | 16 ++ src/domain/storage/Namespaces.ts | 2 + src/presentation/CompositionRoot.ts | 2 + src/presentation/webapp/pages/Root.jsx | 3 + .../webapp/pages/home/HomePage.tsx | 1 + .../webapp/pages/store-list/StoreListPage.tsx | 179 ++++++++++++++++++ 12 files changed, 340 insertions(+), 5 deletions(-) create mode 100644 src/domain/packages/usecases/ListStoresUseCase.ts create mode 100644 src/presentation/webapp/pages/store-list/StoreListPage.tsx diff --git a/i18n/en.pot b/i18n/en.pot index 8f7b175fd..351ed024f 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-06T10:10:16.836Z\n" -"PO-Revision-Date: 2020-10-06T10:10:16.836Z\n" +"POT-Creation-Date: 2020-10-22T08:37:35.777Z\n" +"PO-Revision-Date: 2020-10-22T08:37:35.777Z\n" msgid "Field {{field}} cannot be blank" msgstr "" @@ -1481,6 +1481,38 @@ msgstr "" msgid "Reset" msgstr "" +msgid "An unexpected error has ocurred deleting import rules. " +msgstr "" + +msgid "Successfully delete {{deleteCount}} import rules" +msgstr "" + +msgid "Account" +msgstr "" + +msgid "Repository" +msgstr "" + +msgid "Token" +msgstr "" + +msgid "Default" +msgstr "" + +msgid "Set as default" +msgstr "" + +msgid "Stores" +msgstr "" + +msgid "Delete Stores?" +msgstr "" + +msgid "Are you sure you want to delete {{count}} stores?" +msgid_plural "Are you sure you want to delete {{count}} stores?" +msgstr[0] "" +msgstr[1] "" + msgid "New {{type}} synchronization rule" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index 438b56715..7d20b189b 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-06T10:10:16.836Z\n" +"POT-Creation-Date: 2020-10-22T08:37:35.777Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1484,6 +1484,38 @@ msgstr "" msgid "Reset" msgstr "" +msgid "An unexpected error has ocurred deleting import rules. " +msgstr "" + +msgid "Successfully delete {{deleteCount}} import rules" +msgstr "" + +msgid "Account" +msgstr "" + +msgid "Repository" +msgstr "" + +msgid "Token" +msgstr "" + +msgid "Default" +msgstr "" + +msgid "Set as default" +msgstr "" + +msgid "Stores" +msgstr "" + +msgid "Delete Stores?" +msgstr "" + +msgid "Are you sure you want to delete {{count}} stores?" +msgid_plural "Are you sure you want to delete {{count}} stores?" +msgstr[0] "" +msgstr[1] "" + msgid "New {{type}} synchronization rule" msgstr "" diff --git a/i18n/fr.po b/i18n/fr.po index c22bfe293..64fcb50bb 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-06T10:10:16.836Z\n" +"POT-Creation-Date: 2020-10-22T08:37:35.777Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1484,6 +1484,38 @@ msgstr "" msgid "Reset" msgstr "" +msgid "An unexpected error has ocurred deleting import rules. " +msgstr "" + +msgid "Successfully delete {{deleteCount}} import rules" +msgstr "" + +msgid "Account" +msgstr "" + +msgid "Repository" +msgstr "" + +msgid "Token" +msgstr "" + +msgid "Default" +msgstr "" + +msgid "Set as default" +msgstr "" + +msgid "Stores" +msgstr "" + +msgid "Delete Stores?" +msgstr "" + +msgid "Are you sure you want to delete {{count}} stores?" +msgid_plural "Are you sure you want to delete {{count}} stores?" +msgstr[0] "" +msgstr[1] "" + msgid "New {{type}} synchronization rule" msgstr "" diff --git a/i18n/pt.po b/i18n/pt.po index c22bfe293..64fcb50bb 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-06T10:10:16.836Z\n" +"POT-Creation-Date: 2020-10-22T08:37:35.777Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1484,6 +1484,38 @@ msgstr "" msgid "Reset" msgstr "" +msgid "An unexpected error has ocurred deleting import rules. " +msgstr "" + +msgid "Successfully delete {{deleteCount}} import rules" +msgstr "" + +msgid "Account" +msgstr "" + +msgid "Repository" +msgstr "" + +msgid "Token" +msgstr "" + +msgid "Default" +msgstr "" + +msgid "Set as default" +msgstr "" + +msgid "Stores" +msgstr "" + +msgid "Delete Stores?" +msgstr "" + +msgid "Are you sure you want to delete {{count}} stores?" +msgid_plural "Are you sure you want to delete {{count}} stores?" +msgstr[0] "" +msgstr[1] "" + msgid "New {{type}} synchronization rule" msgstr "" diff --git a/src/domain/packages/entities/Store.ts b/src/domain/packages/entities/Store.ts index 4b8931a44..7db261317 100644 --- a/src/domain/packages/entities/Store.ts +++ b/src/domain/packages/entities/Store.ts @@ -1,7 +1,9 @@ export interface Store { + id: string; token: string; account: string; repository: string; + default: boolean; branch?: string; basePath?: string; } diff --git a/src/domain/packages/usecases/GetStoreUseCase.ts b/src/domain/packages/usecases/GetStoreUseCase.ts index 5361c41a2..8341bda5d 100644 --- a/src/domain/packages/usecases/GetStoreUseCase.ts +++ b/src/domain/packages/usecases/GetStoreUseCase.ts @@ -8,9 +8,11 @@ export class GetStoreUseCase implements UseCase { public async execute(): Promise { return await this.storageRepository.getOrCreateObject(Namespace.STORE, { + id: "", token: "", account: "", repository: "", + default: false, }); } } diff --git a/src/domain/packages/usecases/ListStoresUseCase.ts b/src/domain/packages/usecases/ListStoresUseCase.ts new file mode 100644 index 000000000..e94826348 --- /dev/null +++ b/src/domain/packages/usecases/ListStoresUseCase.ts @@ -0,0 +1,16 @@ +import { UseCase } from "../../common/entities/UseCase"; +import { Namespace } from "../../storage/Namespaces"; +import { StorageRepository } from "../../storage/repositories/StorageRepository"; +import { Store } from "../entities/Store"; + +export class ListStoresUseCase implements UseCase { + constructor(private storageRepository: StorageRepository) {} + + public async execute(): Promise { + const stores = await this.storageRepository.listObjectsInCollection( + Namespace.STORES + ); + + return stores; + } +} diff --git a/src/domain/storage/Namespaces.ts b/src/domain/storage/Namespaces.ts index 15d1d2aad..1a7b9174b 100644 --- a/src/domain/storage/Namespaces.ts +++ b/src/domain/storage/Namespaces.ts @@ -9,6 +9,7 @@ export const Namespace = { NOTIFICATIONS: "notifications", CONFIG: "config", STORE: "store", + STORES: "stores", RESPONSIBLES: "responsibles", }; @@ -21,5 +22,6 @@ export const NamespaceProperties: Record = { [Namespace.NOTIFICATIONS]: ["payload"], [Namespace.CONFIG]: [], [Namespace.STORE]: [], + [Namespace.STORES]: [], [Namespace.RESPONSIBLES]: [], }; diff --git a/src/presentation/CompositionRoot.ts b/src/presentation/CompositionRoot.ts index 6f4fa36f6..4bf54c3c3 100644 --- a/src/presentation/CompositionRoot.ts +++ b/src/presentation/CompositionRoot.ts @@ -47,6 +47,7 @@ import { GetPackageUseCase } from "../domain/packages/usecases/GetPackageUseCase import { GetStoreUseCase } from "../domain/packages/usecases/GetStoreUseCase"; import { ListPackagesUseCase } from "../domain/packages/usecases/ListPackagesUseCase"; import { ListStorePackagesUseCase } from "../domain/packages/usecases/ListStorePackagesUseCase"; +import { ListStoresUseCase } from "../domain/packages/usecases/ListStoresUseCase"; import { PublishStorePackageUseCase } from "../domain/packages/usecases/PublishStorePackageUseCase"; import { SaveStoreUseCase } from "../domain/packages/usecases/SaveStoreUseCase"; import { ValidateStoreUseCase } from "../domain/packages/usecases/ValidateStoreUseCase"; @@ -148,6 +149,7 @@ export class CompositionRoot { get: new GetStoreUseCase(storage), update: new SaveStoreUseCase(github, storage), validate: new ValidateStoreUseCase(github), + list: new ListStoresUseCase(storage), }); } diff --git a/src/presentation/webapp/pages/Root.jsx b/src/presentation/webapp/pages/Root.jsx index f8b2345ce..360b56153 100644 --- a/src/presentation/webapp/pages/Root.jsx +++ b/src/presentation/webapp/pages/Root.jsx @@ -16,6 +16,7 @@ import ModuleCreationPage from "./modules-creation/ModuleCreationPage"; import NotificationsListPage from "./notifications-list/NotificationsListPage"; import ResponsiblesListPage from "./responsibles-list/ResponsiblesListPage"; import StoreConfigPage from "./store-config/StoreConfigPage"; +import StoreListPage from "./store-list/StoreListPage"; import SyncRulesCreationPage from "./sync-rules-creation/SyncRulesCreationPage"; import SyncRulesPage from "./sync-rules-list/SyncRulesListPage"; @@ -77,6 +78,8 @@ function Root() { render={props => } /> + } /> + } diff --git a/src/presentation/webapp/pages/home/HomePage.tsx b/src/presentation/webapp/pages/home/HomePage.tsx index 87b25a8af..d88ba4333 100644 --- a/src/presentation/webapp/pages/home/HomePage.tsx +++ b/src/presentation/webapp/pages/home/HomePage.tsx @@ -177,6 +177,7 @@ const LandingPage: React.FC = () => { description: i18n.t("Configure connections to metadata package stores."), isVisible: appConfigurator, addAction: () => history.push("/modules/config"), + listAction: () => history.push("/stores"), }, ]), }, diff --git a/src/presentation/webapp/pages/store-list/StoreListPage.tsx b/src/presentation/webapp/pages/store-list/StoreListPage.tsx new file mode 100644 index 000000000..53cd22274 --- /dev/null +++ b/src/presentation/webapp/pages/store-list/StoreListPage.tsx @@ -0,0 +1,179 @@ +import { Checkbox, Icon } from "@material-ui/core"; +import { + ConfirmationDialog, + ObjectsTable, + ObjectsTableDetailField, + TableAction, + TableColumn, + TableSelection, + TableState, + useLoading, +} from "d2-ui-components"; +import React, { useCallback, useEffect, useState } from "react"; +import { useHistory } from "react-router-dom"; +import { Store } from "../../../../domain/packages/entities/Store"; +import i18n from "../../../../locales"; +import PageHeader from "../../../react/components/page-header/PageHeader"; +import { useAppContext } from "../../../react/contexts/AppContext"; + +export const StoreListPage: React.FC = () => { + const history = useHistory(); + const [selection, updateSelection] = useState([]); + const [rows, setRows] = useState([]); + const [toDelete, setToDelete] = useState([]); + const loading = useLoading(); + const { compositionRoot } = useAppContext(); + const getStores = compositionRoot.store.list; + + useEffect(() => { + getStores().then(setRows); + }, [getStores]); + + const backHome = useCallback(() => { + history.push("/"); + }, [history]); + + const createStore = useCallback(() => { + history.push(`/stores/new`); + }, [history]); + + const editStore = useCallback( + (id: string) => { + history.push(`/stores/edit/${id}`); + }, + [history] + ); + + const setStoreAsDefault = useCallback((_id: string) => {}, []); + + const updateTable = useCallback( + ({ selection }: TableState) => { + updateSelection(selection); + }, + [updateSelection] + ); + + const confirmDelete = async () => { + loading.show(true, "Deleting packages"); + + // const handleFailure = (failure: DeleteImportRulesByIdError): string => { + // switch (failure.kind) { + // case "UnexpectedError": + // return ( + // i18n.t("An unexpected error has ocurred deleting import rules. ") + + // failure.error.message + // ); + // } + // }; + + // const results = await importRules.delete.execute(state.toDelete); + + // results.fold( + // error => snackbar.error(handleFailure(error)), + // () => + // snackbar.success( + // i18n.t("Successfully delete {{deleteCount}} import rules", { + // deleteCount: state.toDelete.length, + // }) + // ) + // ); + + loading.reset(); + // setState({ + // ...state, + // isDeleting: false, + // toDelete: [], + // selection: [], + // objectsTableKey: new Date().getTime(), + // }); + }; + + const columns: TableColumn[] = [ + { name: "id", text: i18n.t("Id"), sortable: true, hidden: true }, + { name: "account", text: i18n.t("Account"), sortable: true }, + { name: "repository", text: i18n.t("Repository"), sortable: true }, + { name: "token", text: i18n.t("Token"), sortable: true, hidden: true }, + { + name: "default", + text: i18n.t("Default"), + sortable: true, + getValue: store => { + return ; + }, + }, + ]; + + const details: ObjectsTableDetailField[] = [ + { name: "id", text: i18n.t("ID") }, + { name: "account", text: i18n.t("Account") }, + { name: "repository", text: i18n.t("Repository") }, + { name: "token", text: i18n.t("Token") }, + { name: "default", text: i18n.t("Default") }, + ]; + + const actions: TableAction[] = [ + { + name: "details", + text: i18n.t("Details"), + multiple: false, + }, + { + name: "edit", + text: i18n.t("Edit"), + multiple: false, + onClick: (ids: string[]) => editStore(ids[0]), + primary: true, + icon: edit, + }, + { + name: "delete", + text: i18n.t("Delete"), + multiple: true, + onClick: setToDelete, + icon: delete, + }, + { + name: "setAdDefault", + text: i18n.t("Set as default"), + multiple: false, + onClick: (ids: string[]) => setStoreAsDefault(ids[0]), + icon: cloud_download, + }, + ]; + + return ( + + + + + rows={rows} + columns={columns} + details={details} + actions={actions} + onActionButtonClick={createStore} + forceSelectionColumn={true} + selection={selection} + onChange={updateTable} + /> + + {toDelete.length > 0 && ( + setToDelete([])} + title={i18n.t("Delete Stores?")} + description={ + toDelete + ? i18n.t("Are you sure you want to delete {{count}} stores?", { + count: toDelete.length, + }) + : "" + } + saveText={i18n.t("Ok")} + /> + )} + + ); +}; + +export default StoreListPage; From 3e1d13a2dbb19c771c12c5bc2f8fd898aca13a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Thu, 22 Oct 2020 11:21:58 +0200 Subject: [PATCH 017/151] Implement delete store action --- i18n/en.pot | 10 +--- i18n/es.po | 8 +-- i18n/fr.po | 8 +-- i18n/pt.po | 8 +-- src/domain/packages/entities/Store.ts | 3 +- .../packages/usecases/DeleteStoreUseCase.ts | 28 +++++++++++ .../packages/usecases/ListStoresUseCase.ts | 2 +- src/presentation/CompositionRoot.ts | 2 + .../webapp/pages/store-list/StoreListPage.tsx | 49 ++++++------------- 9 files changed, 53 insertions(+), 65 deletions(-) create mode 100644 src/domain/packages/usecases/DeleteStoreUseCase.ts diff --git a/i18n/en.pot b/i18n/en.pot index 351ed024f..15b1f1b56 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-22T08:37:35.777Z\n" -"PO-Revision-Date: 2020-10-22T08:37:35.777Z\n" +"POT-Creation-Date: 2020-10-22T09:21:03.775Z\n" +"PO-Revision-Date: 2020-10-22T09:21:03.775Z\n" msgid "Field {{field}} cannot be blank" msgstr "" @@ -1481,12 +1481,6 @@ msgstr "" msgid "Reset" msgstr "" -msgid "An unexpected error has ocurred deleting import rules. " -msgstr "" - -msgid "Successfully delete {{deleteCount}} import rules" -msgstr "" - msgid "Account" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index 7d20b189b..c382eeea3 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-22T08:37:35.777Z\n" +"POT-Creation-Date: 2020-10-22T08:53:01.560Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1484,12 +1484,6 @@ msgstr "" msgid "Reset" msgstr "" -msgid "An unexpected error has ocurred deleting import rules. " -msgstr "" - -msgid "Successfully delete {{deleteCount}} import rules" -msgstr "" - msgid "Account" msgstr "" diff --git a/i18n/fr.po b/i18n/fr.po index 64fcb50bb..0021acbf5 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-22T08:37:35.777Z\n" +"POT-Creation-Date: 2020-10-22T08:53:01.560Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1484,12 +1484,6 @@ msgstr "" msgid "Reset" msgstr "" -msgid "An unexpected error has ocurred deleting import rules. " -msgstr "" - -msgid "Successfully delete {{deleteCount}} import rules" -msgstr "" - msgid "Account" msgstr "" diff --git a/i18n/pt.po b/i18n/pt.po index 64fcb50bb..0021acbf5 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-22T08:37:35.777Z\n" +"POT-Creation-Date: 2020-10-22T08:53:01.560Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1484,12 +1484,6 @@ msgstr "" msgid "Reset" msgstr "" -msgid "An unexpected error has ocurred deleting import rules. " -msgstr "" - -msgid "Successfully delete {{deleteCount}} import rules" -msgstr "" - msgid "Account" msgstr "" diff --git a/src/domain/packages/entities/Store.ts b/src/domain/packages/entities/Store.ts index 7db261317..7c3bd2d81 100644 --- a/src/domain/packages/entities/Store.ts +++ b/src/domain/packages/entities/Store.ts @@ -3,7 +3,8 @@ export interface Store { token: string; account: string; repository: string; - default: boolean; branch?: string; basePath?: string; + default: boolean; + deleted?: boolean; } diff --git a/src/domain/packages/usecases/DeleteStoreUseCase.ts b/src/domain/packages/usecases/DeleteStoreUseCase.ts new file mode 100644 index 000000000..75084eedf --- /dev/null +++ b/src/domain/packages/usecases/DeleteStoreUseCase.ts @@ -0,0 +1,28 @@ +import { UseCase } from "../../common/entities/UseCase"; +import { Namespace } from "../../storage/Namespaces"; +import { StorageRepository } from "../../storage/repositories/StorageRepository"; +import { Store } from "../entities/Store"; + +export class DeleteStoreUseCase implements UseCase { + constructor(private storageRepository: StorageRepository) {} + + public async execute(id: string): Promise { + const store = await this.storageRepository.getObjectInCollection( + Namespace.STORES, + id + ); + + try { + if (!store) return false; + + await this.storageRepository.saveObjectInCollection(Namespace.STORES, { + ...store, + deleted: true, + }); + } catch (error) { + return false; + } + + return true; + } +} diff --git a/src/domain/packages/usecases/ListStoresUseCase.ts b/src/domain/packages/usecases/ListStoresUseCase.ts index e94826348..cac0ef121 100644 --- a/src/domain/packages/usecases/ListStoresUseCase.ts +++ b/src/domain/packages/usecases/ListStoresUseCase.ts @@ -11,6 +11,6 @@ export class ListStoresUseCase implements UseCase { Namespace.STORES ); - return stores; + return stores.filter(store => !store.deleted); } } diff --git a/src/presentation/CompositionRoot.ts b/src/presentation/CompositionRoot.ts index 4bf54c3c3..1c89bdbf0 100644 --- a/src/presentation/CompositionRoot.ts +++ b/src/presentation/CompositionRoot.ts @@ -41,6 +41,7 @@ import { MarkReadNotificationsUseCase } from "../domain/notifications/usecases/M import { UpdatePullRequestStatusUseCase } from "../domain/notifications/usecases/UpdatePullRequestStatusUseCase"; import { CreatePackageUseCase } from "../domain/packages/usecases/CreatePackageUseCase"; import { DeletePackageUseCase } from "../domain/packages/usecases/DeletePackageUseCase"; +import { DeleteStoreUseCase } from "../domain/packages/usecases/DeleteStoreUseCase"; import { DiffPackageUseCase } from "../domain/packages/usecases/DiffPackageUseCase"; import { DownloadPackageUseCase } from "../domain/packages/usecases/DownloadPackageUseCase"; import { GetPackageUseCase } from "../domain/packages/usecases/GetPackageUseCase"; @@ -150,6 +151,7 @@ export class CompositionRoot { update: new SaveStoreUseCase(github, storage), validate: new ValidateStoreUseCase(github), list: new ListStoresUseCase(storage), + delete: new DeleteStoreUseCase(storage), }); } diff --git a/src/presentation/webapp/pages/store-list/StoreListPage.tsx b/src/presentation/webapp/pages/store-list/StoreListPage.tsx index 53cd22274..2b2bca4f6 100644 --- a/src/presentation/webapp/pages/store-list/StoreListPage.tsx +++ b/src/presentation/webapp/pages/store-list/StoreListPage.tsx @@ -18,16 +18,19 @@ import { useAppContext } from "../../../react/contexts/AppContext"; export const StoreListPage: React.FC = () => { const history = useHistory(); - const [selection, updateSelection] = useState([]); + const [selection, setSelection] = useState([]); const [rows, setRows] = useState([]); const [toDelete, setToDelete] = useState([]); + const [objectsTableKey, setObjectsTableKey] = useState(Math.random()); + const loading = useLoading(); const { compositionRoot } = useAppContext(); const getStores = compositionRoot.store.list; + const deleteStore = compositionRoot.store.delete; useEffect(() => { getStores().then(setRows); - }, [getStores]); + }, [getStores, objectsTableKey]); const backHome = useCallback(() => { history.push("/"); @@ -48,44 +51,22 @@ export const StoreListPage: React.FC = () => { const updateTable = useCallback( ({ selection }: TableState) => { - updateSelection(selection); + setSelection(selection); }, - [updateSelection] + [setSelection] ); const confirmDelete = async () => { - loading.show(true, "Deleting packages"); - - // const handleFailure = (failure: DeleteImportRulesByIdError): string => { - // switch (failure.kind) { - // case "UnexpectedError": - // return ( - // i18n.t("An unexpected error has ocurred deleting import rules. ") + - // failure.error.message - // ); - // } - // }; - - // const results = await importRules.delete.execute(state.toDelete); - - // results.fold( - // error => snackbar.error(handleFailure(error)), - // () => - // snackbar.success( - // i18n.t("Successfully delete {{deleteCount}} import rules", { - // deleteCount: state.toDelete.length, - // }) - // ) - // ); + loading.show(true, "Deleting stores"); + + for (const id of toDelete) { + await deleteStore(id); + } + setObjectsTableKey(Math.random()); + setSelection([]); + setToDelete([]); loading.reset(); - // setState({ - // ...state, - // isDeleting: false, - // toDelete: [], - // selection: [], - // objectsTableKey: new Date().getTime(), - // }); }; const columns: TableColumn[] = [ From bf6856ee626577699d2a0a77f4423a7f5c739db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Thu, 22 Oct 2020 11:51:18 +0200 Subject: [PATCH 018/151] Implement set store as default action --- i18n/en.pot | 7 +++-- i18n/es.po | 5 ++- i18n/fr.po | 5 ++- i18n/pt.po | 5 ++- .../usecases/SetStoreAsDefaultUseCase.ts | 31 +++++++++++++++++++ src/presentation/CompositionRoot.ts | 2 ++ .../webapp/pages/store-list/StoreListPage.tsx | 26 ++++++++++++---- 7 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 src/domain/packages/usecases/SetStoreAsDefaultUseCase.ts diff --git a/i18n/en.pot b/i18n/en.pot index 15b1f1b56..1cbdb7a81 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-22T09:21:03.775Z\n" -"PO-Revision-Date: 2020-10-22T09:21:03.775Z\n" +"POT-Creation-Date: 2020-10-22T09:50:02.552Z\n" +"PO-Revision-Date: 2020-10-22T09:50:02.552Z\n" msgid "Field {{field}} cannot be blank" msgstr "" @@ -1481,6 +1481,9 @@ msgstr "" msgid "Reset" msgstr "" +msgid "An error has occurred setting store as default" +msgstr "" + msgid "Account" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index c382eeea3..0661374b7 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-22T08:53:01.560Z\n" +"POT-Creation-Date: 2020-10-22T09:40:30.776Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1484,6 +1484,9 @@ msgstr "" msgid "Reset" msgstr "" +msgid "An error has occurred setting store as default" +msgstr "" + msgid "Account" msgstr "" diff --git a/i18n/fr.po b/i18n/fr.po index 0021acbf5..682201313 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-22T08:53:01.560Z\n" +"POT-Creation-Date: 2020-10-22T09:40:30.776Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1484,6 +1484,9 @@ msgstr "" msgid "Reset" msgstr "" +msgid "An error has occurred setting store as default" +msgstr "" + msgid "Account" msgstr "" diff --git a/i18n/pt.po b/i18n/pt.po index 0021acbf5..682201313 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-22T08:53:01.560Z\n" +"POT-Creation-Date: 2020-10-22T09:40:30.776Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1484,6 +1484,9 @@ msgstr "" msgid "Reset" msgstr "" +msgid "An error has occurred setting store as default" +msgstr "" + msgid "Account" msgstr "" diff --git a/src/domain/packages/usecases/SetStoreAsDefaultUseCase.ts b/src/domain/packages/usecases/SetStoreAsDefaultUseCase.ts new file mode 100644 index 000000000..b34016681 --- /dev/null +++ b/src/domain/packages/usecases/SetStoreAsDefaultUseCase.ts @@ -0,0 +1,31 @@ +import { Either } from "../../common/entities/Either"; +import { UseCase } from "../../common/entities/UseCase"; +import { Namespace } from "../../storage/Namespaces"; +import { StorageRepository } from "../../storage/repositories/StorageRepository"; +import { Store } from "../entities/Store"; + +type SetStoreAsDefaultError = { + kind: "SetStoreAsDefaultError"; +}; + +export class SetStoreAsDefaultUseCase implements UseCase { + constructor(private storageRepository: StorageRepository) {} + + public async execute(id: string): Promise> { + try { + const stores = await this.storageRepository.listObjectsInCollection( + Namespace.STORES + ); + + const newStores = stores.map(store => ({ ...store, default: store.id === id })); + + await this.storageRepository.saveObject(Namespace.STORES, newStores); + + return Either.success(undefined); + } catch { + return Either.error({ + kind: "SetStoreAsDefaultError", + }); + } + } +} diff --git a/src/presentation/CompositionRoot.ts b/src/presentation/CompositionRoot.ts index 1c89bdbf0..b5c92456d 100644 --- a/src/presentation/CompositionRoot.ts +++ b/src/presentation/CompositionRoot.ts @@ -51,6 +51,7 @@ import { ListStorePackagesUseCase } from "../domain/packages/usecases/ListStoreP import { ListStoresUseCase } from "../domain/packages/usecases/ListStoresUseCase"; import { PublishStorePackageUseCase } from "../domain/packages/usecases/PublishStorePackageUseCase"; import { SaveStoreUseCase } from "../domain/packages/usecases/SaveStoreUseCase"; +import { SetStoreAsDefaultUseCase } from "../domain/packages/usecases/SetStoreAsDefaultUseCase"; import { ValidateStoreUseCase } from "../domain/packages/usecases/ValidateStoreUseCase"; import { Repositories } from "../domain/Repositories"; import { DownloadFileUseCase } from "../domain/storage/usecases/DownloadFileUseCase"; @@ -152,6 +153,7 @@ export class CompositionRoot { validate: new ValidateStoreUseCase(github), list: new ListStoresUseCase(storage), delete: new DeleteStoreUseCase(storage), + setAsDefault: new SetStoreAsDefaultUseCase(storage), }); } diff --git a/src/presentation/webapp/pages/store-list/StoreListPage.tsx b/src/presentation/webapp/pages/store-list/StoreListPage.tsx index 2b2bca4f6..b61f93d7a 100644 --- a/src/presentation/webapp/pages/store-list/StoreListPage.tsx +++ b/src/presentation/webapp/pages/store-list/StoreListPage.tsx @@ -8,6 +8,7 @@ import { TableSelection, TableState, useLoading, + useSnackbar, } from "d2-ui-components"; import React, { useCallback, useEffect, useState } from "react"; import { useHistory } from "react-router-dom"; @@ -18,6 +19,7 @@ import { useAppContext } from "../../../react/contexts/AppContext"; export const StoreListPage: React.FC = () => { const history = useHistory(); + const snackbar = useSnackbar(); const [selection, setSelection] = useState([]); const [rows, setRows] = useState([]); const [toDelete, setToDelete] = useState([]); @@ -27,6 +29,7 @@ export const StoreListPage: React.FC = () => { const { compositionRoot } = useAppContext(); const getStores = compositionRoot.store.list; const deleteStore = compositionRoot.store.delete; + const setStoreAsDefault = compositionRoot.store.setAsDefault; useEffect(() => { getStores().then(setRows); @@ -36,18 +39,29 @@ export const StoreListPage: React.FC = () => { history.push("/"); }, [history]); - const createStore = useCallback(() => { + const handleCreateStore = useCallback(() => { history.push(`/stores/new`); }, [history]); - const editStore = useCallback( + const handleEditStore = useCallback( (id: string) => { history.push(`/stores/edit/${id}`); }, [history] ); - const setStoreAsDefault = useCallback((_id: string) => {}, []); + const handleSetStoreAsDefault = useCallback( + async (id: string) => { + const result = await setStoreAsDefault(id); + result.match({ + error: () => { + snackbar.error(i18n.t("An error has occurred setting store as default")); + }, + success: () => setObjectsTableKey(Math.random()), + }); + }, + [setStoreAsDefault, snackbar] + ); const updateTable = useCallback( ({ selection }: TableState) => { @@ -102,7 +116,7 @@ export const StoreListPage: React.FC = () => { name: "edit", text: i18n.t("Edit"), multiple: false, - onClick: (ids: string[]) => editStore(ids[0]), + onClick: (ids: string[]) => handleEditStore(ids[0]), primary: true, icon: edit, }, @@ -117,7 +131,7 @@ export const StoreListPage: React.FC = () => { name: "setAdDefault", text: i18n.t("Set as default"), multiple: false, - onClick: (ids: string[]) => setStoreAsDefault(ids[0]), + onClick: (ids: string[]) => handleSetStoreAsDefault(ids[0]), icon: cloud_download, }, ]; @@ -131,7 +145,7 @@ export const StoreListPage: React.FC = () => { columns={columns} details={details} actions={actions} - onActionButtonClick={createStore} + onActionButtonClick={handleCreateStore} forceSelectionColumn={true} selection={selection} onChange={updateTable} From f4eae79fa742828abd847f7aa53ed020653f1b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Thu, 22 Oct 2020 12:10:00 +0200 Subject: [PATCH 019/151] Refactor publish to store to use default store --- i18n/en.pot | 8 ++++---- i18n/es.po | 6 +++--- i18n/fr.po | 6 +++--- i18n/pt.po | 6 +++--- .../packages/usecases/PublishStorePackageUseCase.ts | 11 ++++++----- .../package-list-table/PackageListTable.tsx | 6 +++--- 6 files changed, 22 insertions(+), 21 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 1cbdb7a81..663840d93 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-22T09:50:02.552Z\n" -"PO-Revision-Date: 2020-10-22T09:50:02.552Z\n" +"POT-Creation-Date: 2020-10-22T10:08:06.021Z\n" +"PO-Revision-Date: 2020-10-22T10:08:06.021Z\n" msgid "Field {{field}} cannot be blank" msgstr "" @@ -529,10 +529,10 @@ msgstr "" msgid "Publishing package to Store" msgstr "" -msgid "Package published to store" +msgid "Package published to default store" msgstr "" -msgid "Store is not properly configured" +msgid "Default store is not properly configured" msgstr "" msgid "Could not read package" diff --git a/i18n/es.po b/i18n/es.po index 0661374b7..865f8ab77 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-22T09:40:30.776Z\n" +"POT-Creation-Date: 2020-10-22T10:08:06.021Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -530,10 +530,10 @@ msgstr "" msgid "Publishing package to Store" msgstr "" -msgid "Package published to store" +msgid "Package published to default store" msgstr "" -msgid "Store is not properly configured" +msgid "Default store is not properly configured" msgstr "" msgid "Could not read package" diff --git a/i18n/fr.po b/i18n/fr.po index 682201313..8469f7bfa 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-22T09:40:30.776Z\n" +"POT-Creation-Date: 2020-10-22T10:08:06.021Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -530,10 +530,10 @@ msgstr "" msgid "Publishing package to Store" msgstr "" -msgid "Package published to store" +msgid "Package published to default store" msgstr "" -msgid "Store is not properly configured" +msgid "Default store is not properly configured" msgstr "" msgid "Could not read package" diff --git a/i18n/pt.po b/i18n/pt.po index 682201313..8469f7bfa 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-22T09:40:30.776Z\n" +"POT-Creation-Date: 2020-10-22T10:08:06.021Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -530,10 +530,10 @@ msgstr "" msgid "Publishing package to Store" msgstr "" -msgid "Package published to store" +msgid "Package published to default store" msgstr "" -msgid "Store is not properly configured" +msgid "Default store is not properly configured" msgstr "" msgid "Could not read package" diff --git a/src/domain/packages/usecases/PublishStorePackageUseCase.ts b/src/domain/packages/usecases/PublishStorePackageUseCase.ts index 20644bc7c..87e5af282 100644 --- a/src/domain/packages/usecases/PublishStorePackageUseCase.ts +++ b/src/domain/packages/usecases/PublishStorePackageUseCase.ts @@ -14,7 +14,7 @@ import { GitHubRepositoryConstructor } from "../repositories/GitHubRepository"; export type PublishStorePackageError = | GitHubError - | "STORE_NOT_FOUND" + | "DEFAULT_STORE_NOT_FOUND" | "PACKAGE_NOT_FOUND" | "ALREADY_PUBLISHED"; @@ -25,10 +25,11 @@ export class PublishStorePackageUseCase implements UseCase { packageId: string, force = false ): Promise> { - const store = await this.storageRepository(this.localInstance).getObject( - Namespace.STORE - ); - if (!store) return Either.error("STORE_NOT_FOUND"); + const store = ( + await this.storageRepository(this.localInstance).getObject(Namespace.STORES) + )?.find(store => store.default); + + if (!store) return Either.error("DEFAULT_STORE_NOT_FOUND"); const storedPackage = await this.storageRepository( this.localInstance diff --git a/src/presentation/react/components/package-list-table/PackageListTable.tsx b/src/presentation/react/components/package-list-table/PackageListTable.tsx index bd48897b0..9356a3677 100644 --- a/src/presentation/react/components/package-list-table/PackageListTable.tsx +++ b/src/presentation/react/components/package-list-table/PackageListTable.tsx @@ -90,15 +90,15 @@ export const PackagesListTable: React.FC = ({ validation.match({ success: () => { loading.reset(); - snackbar.success(i18n.t("Package published to store")); + snackbar.success(i18n.t("Package published to default store")); }, error: code => { loading.reset(); switch (code) { case "BAD_CREDENTIALS": case "NO_TOKEN": - case "STORE_NOT_FOUND": - snackbar.error(i18n.t("Store is not properly configured")); + case "DEFAULT_STORE_NOT_FOUND": + snackbar.error(i18n.t("Default store is not properly configured")); return; case "PACKAGE_NOT_FOUND": snackbar.error(i18n.t("Could not read package")); From 4302bbb5066370a2e07e09c70f40c4a45c8389ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Thu, 22 Oct 2020 12:53:45 +0200 Subject: [PATCH 020/151] Refactor StoreConfigPage to StoreCreationPage --- i18n/en.pot | 10 ++++- i18n/es.po | 8 +++- i18n/fr.po | 8 +++- i18n/pt.po | 8 +++- .../packages/usecases/GetStoreUseCase.ts | 12 ++---- .../packages/usecases/SaveStoreUseCase.ts | 5 ++- src/presentation/webapp/pages/Root.jsx | 10 ++--- .../StoreCreationPage.tsx} | 39 ++++++++++++------- 8 files changed, 68 insertions(+), 32 deletions(-) rename src/presentation/webapp/pages/{store-config/StoreConfigPage.tsx => store-creation/StoreCreationPage.tsx} (90%) diff --git a/i18n/en.pot b/i18n/en.pot index 663840d93..a2e0ca1e9 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-22T10:08:06.021Z\n" -"PO-Revision-Date: 2020-10-22T10:08:06.021Z\n" +"POT-Creation-Date: 2020-10-22T10:51:05.832Z\n" +"PO-Revision-Date: 2020-10-22T10:51:05.832Z\n" msgid "Field {{field}} cannot be blank" msgstr "" @@ -1429,6 +1429,12 @@ msgstr "" msgid "Refresh" msgstr "" +msgid "New store" +msgstr "" + +msgid "Edit store" +msgstr "" + msgid "The token is empty" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index 865f8ab77..709c96d1d 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-22T10:08:06.021Z\n" +"POT-Creation-Date: 2020-10-22T10:21:06.220Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1431,6 +1431,12 @@ msgstr "" msgid "Refresh" msgstr "" +msgid "New store" +msgstr "" + +msgid "Edit store" +msgstr "" + msgid "The token is empty" msgstr "" diff --git a/i18n/fr.po b/i18n/fr.po index 8469f7bfa..fd484fd92 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-22T10:08:06.021Z\n" +"POT-Creation-Date: 2020-10-22T10:21:06.220Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1431,6 +1431,12 @@ msgstr "" msgid "Refresh" msgstr "" +msgid "New store" +msgstr "" + +msgid "Edit store" +msgstr "" + msgid "The token is empty" msgstr "" diff --git a/i18n/pt.po b/i18n/pt.po index 8469f7bfa..fd484fd92 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-22T10:08:06.021Z\n" +"POT-Creation-Date: 2020-10-22T10:21:06.220Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1431,6 +1431,12 @@ msgstr "" msgid "Refresh" msgstr "" +msgid "New store" +msgstr "" + +msgid "Edit store" +msgstr "" + msgid "The token is empty" msgstr "" diff --git a/src/domain/packages/usecases/GetStoreUseCase.ts b/src/domain/packages/usecases/GetStoreUseCase.ts index 8341bda5d..1be33b4fa 100644 --- a/src/domain/packages/usecases/GetStoreUseCase.ts +++ b/src/domain/packages/usecases/GetStoreUseCase.ts @@ -6,13 +6,9 @@ import { Store } from "../entities/Store"; export class GetStoreUseCase implements UseCase { constructor(private storageRepository: StorageRepository) {} - public async execute(): Promise { - return await this.storageRepository.getOrCreateObject(Namespace.STORE, { - id: "", - token: "", - account: "", - repository: "", - default: false, - }); + public async execute(id: string): Promise { + const store = this.storageRepository.getObjectInCollection(Namespace.STORES, id); + + return store; } } diff --git a/src/domain/packages/usecases/SaveStoreUseCase.ts b/src/domain/packages/usecases/SaveStoreUseCase.ts index 648583dd8..5d458db64 100644 --- a/src/domain/packages/usecases/SaveStoreUseCase.ts +++ b/src/domain/packages/usecases/SaveStoreUseCase.ts @@ -1,3 +1,4 @@ +import { generateUid } from "d2/uid"; import { Either } from "../../common/entities/Either"; import { UseCase } from "../../common/entities/UseCase"; import { Namespace } from "../../storage/Namespaces"; @@ -18,7 +19,9 @@ export class SaveStoreUseCase implements UseCase { if (validation.isError()) return Either.error(validation.value.error ?? "UNKNOWN"); } - await this.storageRepository.saveObject(Namespace.STORE, store); + const storeToSave = !store.id ? { ...store, id: generateUid() } : store; + + await this.storageRepository.saveObjectInCollection(Namespace.STORES, storeToSave); return Either.success(undefined); } } diff --git a/src/presentation/webapp/pages/Root.jsx b/src/presentation/webapp/pages/Root.jsx index 360b56153..1b098b124 100644 --- a/src/presentation/webapp/pages/Root.jsx +++ b/src/presentation/webapp/pages/Root.jsx @@ -15,7 +15,7 @@ import ModulePackageListPage from "./module-package-list/ModulePackageListPage"; import ModuleCreationPage from "./modules-creation/ModuleCreationPage"; import NotificationsListPage from "./notifications-list/NotificationsListPage"; import ResponsiblesListPage from "./responsibles-list/ResponsiblesListPage"; -import StoreConfigPage from "./store-config/StoreConfigPage"; +import StoreCreationPage from "./store-creation/StoreCreationPage"; import StoreListPage from "./store-list/StoreListPage"; import SyncRulesCreationPage from "./sync-rules-creation/SyncRulesCreationPage"; import SyncRulesPage from "./sync-rules-list/SyncRulesListPage"; @@ -78,13 +78,13 @@ function Root() { render={props => } /> - } /> - } + path={"/stores/:action(new|edit)/:id?"} + render={props => } /> + } /> + } diff --git a/src/presentation/webapp/pages/store-config/StoreConfigPage.tsx b/src/presentation/webapp/pages/store-creation/StoreCreationPage.tsx similarity index 90% rename from src/presentation/webapp/pages/store-config/StoreConfigPage.tsx rename to src/presentation/webapp/pages/store-creation/StoreCreationPage.tsx index 43eb3770b..3d9f4081f 100644 --- a/src/presentation/webapp/pages/store-config/StoreConfigPage.tsx +++ b/src/presentation/webapp/pages/store-creation/StoreCreationPage.tsx @@ -8,7 +8,7 @@ import { } from "d2-ui-components"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import Linkify from "react-linkify"; -import { useHistory } from "react-router-dom"; +import { useHistory, useParams } from "react-router-dom"; import { GitHubError } from "../../../../domain/packages/entities/Errors"; import { Store } from "../../../../domain/packages/entities/Store"; import i18n from "../../../../locales"; @@ -16,19 +16,37 @@ import { useAppContext } from "../../../react/contexts/AppContext"; import PageHeader from "../../../react/components/page-header/PageHeader"; import helpStoreGithub from "../../../../assets/img/help-store-github.png"; -const ModulesConfigPage: React.FC = () => { +const StoreCreationPage: React.FC = () => { const { compositionRoot } = useAppContext(); const history = useHistory(); const classes = useStyles(); const snackbar = useSnackbar(); const loading = useLoading(); - const [state, setState] = useState>({}); + const { id, action } = useParams<{ id: string; action: "edit" | "new" }>(); + + const [state, setState] = useState({ + id: "", + token: "", + account: "", + repository: "", + default: false, + }); const [dialogProps, updateDialog] = useState(null); + const isEdit = action === "edit" && (!!module || !!id); + const title = !isEdit ? i18n.t(`New store`) : i18n.t(`Edit store`); + useEffect(() => { - compositionRoot.store.get().then(setState); - }, [compositionRoot]); + if (id) + compositionRoot.store.get(id).then(store => { + if (store) { + setState(store); + } else { + snackbar.error(i18n.t("Store not found: " + id)); + } + }); + }, [compositionRoot, id, snackbar]); const onChangeField = (field: keyof Store) => { return (event: React.ChangeEvent) => { @@ -38,7 +56,7 @@ const ModulesConfigPage: React.FC = () => { }; const close = useCallback(() => { - history.push("/"); + history.goBack(); }, [history]); const validateError = useCallback((error?: GitHubError): string => { @@ -155,12 +173,7 @@ const ModulesConfigPage: React.FC = () => { {dialogProps && } - + Date: Fri, 23 Oct 2020 07:23:07 +0200 Subject: [PATCH 021/151] Use default store to list, download and diff packages --- i18n/en.pot | 4 ++-- src/domain/packages/usecases/DiffPackageUseCase.ts | 7 ++++--- .../packages/usecases/DownloadPackageUseCase.ts | 7 ++++--- .../packages/usecases/ListStorePackagesUseCase.ts | 11 ++++++----- src/domain/storage/Namespaces.ts | 2 -- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index a2e0ca1e9..f8b6d4ae5 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-22T10:51:05.832Z\n" -"PO-Revision-Date: 2020-10-22T10:51:05.832Z\n" +"POT-Creation-Date: 2020-10-23T05:14:55.521Z\n" +"PO-Revision-Date: 2020-10-23T05:14:55.521Z\n" msgid "Field {{field}} cannot be blank" msgstr "" diff --git a/src/domain/packages/usecases/DiffPackageUseCase.ts b/src/domain/packages/usecases/DiffPackageUseCase.ts index 4001bc95e..020cc390e 100644 --- a/src/domain/packages/usecases/DiffPackageUseCase.ts +++ b/src/domain/packages/usecases/DiffPackageUseCase.ts @@ -61,9 +61,10 @@ export class DiffPackageUseCase implements UseCase { } private async getStorePackage(url: string) { - const store = await this.storageRepository(this.localInstance).getObject( - Namespace.STORE - ); + const store = ( + await this.storageRepository(this.localInstance).getObject(Namespace.STORES) + )?.find(store => store.default); + if (!store) return undefined; const { encoding, content } = await this.downloadRepository().fetch<{ diff --git a/src/domain/packages/usecases/DownloadPackageUseCase.ts b/src/domain/packages/usecases/DownloadPackageUseCase.ts index ea5b84d82..1875d94b0 100644 --- a/src/domain/packages/usecases/DownloadPackageUseCase.ts +++ b/src/domain/packages/usecases/DownloadPackageUseCase.ts @@ -38,9 +38,10 @@ export class DownloadPackageUseCase implements UseCase { } private async getStorePackage(url: string) { - const store = await this.storageRepository(this.localInstance).getObject( - Namespace.STORE - ); + const store = ( + await this.storageRepository(this.localInstance).getObject(Namespace.STORES) + )?.find(store => store.default); + if (!store) return undefined; const { encoding, content } = await this.downloadRepository().fetch<{ diff --git a/src/domain/packages/usecases/ListStorePackagesUseCase.ts b/src/domain/packages/usecases/ListStorePackagesUseCase.ts index 6800cf3f2..7cb6d4a07 100644 --- a/src/domain/packages/usecases/ListStorePackagesUseCase.ts +++ b/src/domain/packages/usecases/ListStorePackagesUseCase.ts @@ -17,16 +17,17 @@ import { Package } from "../entities/Package"; import { Store } from "../entities/Store"; import { GitHubRepositoryConstructor } from "../repositories/GitHubRepository"; -export type ListStorePackagesError = GitHubError | "STORE_NOT_FOUND"; +export type ListStorePackagesError = GitHubError | "DEFAULT_STORE_NOT_FOUND"; export class ListStorePackagesUseCase implements UseCase { constructor(private repositoryFactory: RepositoryFactory, private localInstance: Instance) {} public async execute(): Promise> { - const store = await this.storageRepository(this.localInstance).getObject( - Namespace.STORE - ); - if (!store) return Either.error("STORE_NOT_FOUND"); + const store = ( + await this.storageRepository(this.localInstance).getObject(Namespace.STORES) + )?.find(store => store.default); + + if (!store) return Either.error("DEFAULT_STORE_NOT_FOUND"); const userGroups = await this.instanceRepository(this.localInstance).getUserGroups(); const validation = await this.gitRepository().listBranches(store); diff --git a/src/domain/storage/Namespaces.ts b/src/domain/storage/Namespaces.ts index 1a7b9174b..b25d80b27 100644 --- a/src/domain/storage/Namespaces.ts +++ b/src/domain/storage/Namespaces.ts @@ -8,7 +8,6 @@ export const Namespace = { HISTORY: "history", NOTIFICATIONS: "notifications", CONFIG: "config", - STORE: "store", STORES: "stores", RESPONSIBLES: "responsibles", }; @@ -21,7 +20,6 @@ export const NamespaceProperties: Record = { [Namespace.HISTORY]: [], [Namespace.NOTIFICATIONS]: ["payload"], [Namespace.CONFIG]: [], - [Namespace.STORE]: [], [Namespace.STORES]: [], [Namespace.RESPONSIBLES]: [], }; From 6745c226b997d19586e950a9b7df9764d0c6e9c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Fri, 23 Oct 2020 09:11:55 +0200 Subject: [PATCH 022/151] Show button to start package import wizard for store --- i18n/en.pot | 4 ++-- .../ModulePackageListTable.tsx | 2 +- .../package-list-table/PackageListTable.tsx | 18 ++++++++++++------ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 8a96717f6..176765bff 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-21T11:17:42.482Z\n" -"PO-Revision-Date: 2020-10-21T11:17:42.482Z\n" +"POT-Creation-Date: 2020-10-23T07:09:15.216Z\n" +"PO-Revision-Date: 2020-10-23T07:09:15.216Z\n" msgid "Field {{field}} cannot be blank" msgstr "" diff --git a/src/presentation/react/components/module-package-list-table/ModulePackageListTable.tsx b/src/presentation/react/components/module-package-list-table/ModulePackageListTable.tsx index 0659345c4..67e0a2353 100644 --- a/src/presentation/react/components/module-package-list-table/ModulePackageListTable.tsx +++ b/src/presentation/react/components/module-package-list-table/ModulePackageListTable.tsx @@ -105,7 +105,7 @@ export const ModulePackageListTable: React.FC = Rea openSyncSummary={openSyncSummary} onActionButtonClick={ (viewSelector.value === "modules" && !selectedInstance) || - (viewSelector.value === "packages" && selectedInstance) + (viewSelector.value === "packages" && (selectedInstance || showStore)) ? onCreate : undefined } diff --git a/src/presentation/react/components/package-list-table/PackageListTable.tsx b/src/presentation/react/components/package-list-table/PackageListTable.tsx index bf18be9e7..0201479fb 100644 --- a/src/presentation/react/components/package-list-table/PackageListTable.tsx +++ b/src/presentation/react/components/package-list-table/PackageListTable.tsx @@ -246,9 +246,6 @@ export const PackagesListTable: React.FC = ({ [] ); - const canImport = - !isImportDialog && presentation === "app" && isRemoteInstance && appConfigurator; - const actions: TableAction[] = useMemo( () => [ { @@ -305,7 +302,11 @@ export const PackagesListTable: React.FC = ({ multiple: false, onClick: importPackage, icon: arrow_downward, - isActive: () => canImport, + isActive: () => + !isImportDialog && + presentation === "app" && + isRemoteInstance && + appConfigurator, }, ], [ @@ -318,7 +319,6 @@ export const PackagesListTable: React.FC = ({ presentation, publishPackage, showStore, - canImport, isImportDialog, ] ); @@ -377,6 +377,12 @@ export const PackagesListTable: React.FC = ({ isGlobalAdmin(api).then(setGlobalAdmin); }, [api]); + const showImportFromWizardButton = + !isImportDialog && + presentation === "app" && + (isRemoteInstance || showStore) && + appConfigurator; + return ( @@ -384,7 +390,7 @@ export const PackagesListTable: React.FC = ({ columns={columns} details={details} actions={actions} - onActionButtonClick={canImport ? onActionButtonClick : undefined} + onActionButtonClick={showImportFromWizardButton ? onActionButtonClick : undefined} forceSelectionColumn={presentation === "app"} filterComponents={filterComponents} selection={selection} From ac9ba80d8a2a1461c1acf1d86563b364e23e93ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Fri, 23 Oct 2020 10:52:49 +0200 Subject: [PATCH 023/151] Not allow empty fields to save store --- i18n/en.pot | 10 +++- i18n/es.po | 8 ++- i18n/fr.po | 8 ++- i18n/pt.po | 8 ++- src/data/packages/GitHubOctokitRepository.ts | 2 + src/domain/packages/entities/Errors.ts | 2 + .../store-creation/StoreCreationPage.tsx | 52 ++++++++++++------- 7 files changed, 66 insertions(+), 24 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index f8b6d4ae5..480d64dc0 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-23T05:14:55.521Z\n" -"PO-Revision-Date: 2020-10-23T05:14:55.521Z\n" +"POT-Creation-Date: 2020-10-23T08:51:17.118Z\n" +"PO-Revision-Date: 2020-10-23T08:51:17.118Z\n" msgid "Field {{field}} cannot be blank" msgstr "" @@ -1438,6 +1438,12 @@ msgstr "" msgid "The token is empty" msgstr "" +msgid "The account is empty" +msgstr "" + +msgid "The repository is empty" +msgstr "" + msgid "The token is invalid" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index 709c96d1d..3102926e0 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-22T10:21:06.220Z\n" +"POT-Creation-Date: 2020-10-23T08:51:17.118Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1440,6 +1440,12 @@ msgstr "" msgid "The token is empty" msgstr "" +msgid "The account is empty" +msgstr "" + +msgid "The repository is empty" +msgstr "" + msgid "The token is invalid" msgstr "" diff --git a/i18n/fr.po b/i18n/fr.po index fd484fd92..433efa945 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-22T10:21:06.220Z\n" +"POT-Creation-Date: 2020-10-23T08:51:17.118Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1440,6 +1440,12 @@ msgstr "" msgid "The token is empty" msgstr "" +msgid "The account is empty" +msgstr "" + +msgid "The repository is empty" +msgstr "" + msgid "The token is invalid" msgstr "" diff --git a/i18n/pt.po b/i18n/pt.po index fd484fd92..433efa945 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-22T10:21:06.220Z\n" +"POT-Creation-Date: 2020-10-23T08:51:17.118Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1440,6 +1440,12 @@ msgstr "" msgid "The token is empty" msgstr "" +msgid "The account is empty" +msgstr "" + +msgid "The repository is empty" +msgstr "" + msgid "The token is invalid" msgstr "" diff --git a/src/data/packages/GitHubOctokitRepository.ts b/src/data/packages/GitHubOctokitRepository.ts index c4023893c..53d3c33f7 100644 --- a/src/data/packages/GitHubOctokitRepository.ts +++ b/src/data/packages/GitHubOctokitRepository.ts @@ -186,6 +186,8 @@ export class GitHubOctokitRepository implements GitHubRepository { try { const { token, account, repository } = store; if (!token?.trim()) return Either.error("NO_TOKEN"); + if (!account?.trim()) return Either.error("NO_ACCOUNT"); + if (!repository?.trim()) return Either.error("NO_REPOSITORY"); const octokit = await this.getOctoKit(token); const { login: username } = await this.getCurrentUser(store); diff --git a/src/domain/packages/entities/Errors.ts b/src/domain/packages/entities/Errors.ts index 848fe89d6..502cd1466 100644 --- a/src/domain/packages/entities/Errors.ts +++ b/src/domain/packages/entities/Errors.ts @@ -1,5 +1,7 @@ export type GitHubError = | "NOT_FOUND" + | "NO_ACCOUNT" + | "NO_REPOSITORY" | "NO_TOKEN" | "BAD_CREDENTIALS" | "WRITE_PERMISSIONS" diff --git a/src/presentation/webapp/pages/store-creation/StoreCreationPage.tsx b/src/presentation/webapp/pages/store-creation/StoreCreationPage.tsx index 3d9f4081f..76152ac63 100644 --- a/src/presentation/webapp/pages/store-creation/StoreCreationPage.tsx +++ b/src/presentation/webapp/pages/store-creation/StoreCreationPage.tsx @@ -63,6 +63,10 @@ const StoreCreationPage: React.FC = () => { switch (error) { case "NO_TOKEN": return i18n.t("The token is empty"); + case "NO_ACCOUNT": + return i18n.t("The account is empty"); + case "NO_REPOSITORY": + return i18n.t("The repository is empty"); case "BAD_CREDENTIALS": return i18n.t("The token is invalid"); case "NOT_FOUND": @@ -111,31 +115,41 @@ const StoreCreationPage: React.FC = () => { const save = useCallback(async () => { loading.show(true, i18n.t("Saving store connection")); + const handleError = (error: GitHubError) => { + switch (error) { + case "NO_TOKEN": + case "NO_ACCOUNT": + case "NO_REPOSITORY": + return snackbar.error(validateError(error)); + default: { + updateDialog({ + title: validateError(error), + description: i18n.t( + "There are issues with the connection details you provided.\nDo you want to proceed?" + ), + onCancel: () => { + updateDialog(null); + }, + onSave: async () => { + await compositionRoot.store.update(state as Store, false); + updateDialog(null); + close(); + }, + cancelText: i18n.t("Cancel"), + saveText: i18n.t("Proceed"), + }); + } + } + }; + const validation = await compositionRoot.store.update(state as Store); validation.match({ - error: error => { - updateDialog({ - title: validateError(error), - description: i18n.t( - "There are issues with the connection details you provided.\nDo you want to proceed?" - ), - onCancel: () => { - updateDialog(null); - }, - onSave: async () => { - await compositionRoot.store.update(state as Store, false); - updateDialog(null); - close(); - }, - cancelText: i18n.t("Cancel"), - saveText: i18n.t("Proceed"), - }); - }, + error: error => handleError(error), success: close, }); loading.reset(); - }, [compositionRoot, state, validateError, close, loading]); + }, [compositionRoot, state, validateError, close, loading, snackbar]); const helpContainer = useMemo( () => ( From b5699e93a8619b6c0b460fe8936ec808a2092cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sa=CC=81nchez?= Date: Fri, 23 Oct 2020 10:58:52 +0200 Subject: [PATCH 024/151] Delete reset button in the store page --- i18n/en.pot | 15 ++--------- i18n/es.po | 13 +--------- i18n/fr.po | 13 +--------- i18n/pt.po | 13 +--------- .../store-creation/StoreCreationPage.tsx | 26 ------------------- 5 files changed, 5 insertions(+), 75 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 480d64dc0..5816536d5 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2020-10-23T08:51:17.118Z\n" -"PO-Revision-Date: 2020-10-23T08:51:17.118Z\n" +"POT-Creation-Date: 2020-10-23T08:57:00.268Z\n" +"PO-Revision-Date: 2020-10-23T08:57:00.268Z\n" msgid "Field {{field}} cannot be blank" msgstr "" @@ -1456,14 +1456,6 @@ msgstr "" msgid "Connected successfully" msgstr "" -msgid "Reset store configuration" -msgstr "" - -msgid "" -"You will clear the existing configuration for all users in this instance.\n" -"Do you want to proceed?" -msgstr "" - msgid "Saving store connection" msgstr "" @@ -1490,9 +1482,6 @@ msgstr "" msgid "GitHub personal access token (*)" msgstr "" -msgid "Reset" -msgstr "" - msgid "An error has occurred setting store as default" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index 3102926e0..a359adc8e 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-23T08:51:17.118Z\n" +"POT-Creation-Date: 2020-10-23T08:53:45.330Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1458,14 +1458,6 @@ msgstr "" msgid "Connected successfully" msgstr "" -msgid "Reset store configuration" -msgstr "" - -msgid "" -"You will clear the existing configuration for all users in this instance.\n" -"Do you want to proceed?" -msgstr "" - msgid "Saving store connection" msgstr "" @@ -1493,9 +1485,6 @@ msgstr "" msgid "GitHub personal access token (*)" msgstr "" -msgid "Reset" -msgstr "" - msgid "An error has occurred setting store as default" msgstr "" diff --git a/i18n/fr.po b/i18n/fr.po index 433efa945..0f3298e23 100644 --- a/i18n/fr.po +++ b/i18n/fr.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-23T08:51:17.118Z\n" +"POT-Creation-Date: 2020-10-23T08:53:45.330Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1458,14 +1458,6 @@ msgstr "" msgid "Connected successfully" msgstr "" -msgid "Reset store configuration" -msgstr "" - -msgid "" -"You will clear the existing configuration for all users in this instance.\n" -"Do you want to proceed?" -msgstr "" - msgid "Saving store connection" msgstr "" @@ -1493,9 +1485,6 @@ msgstr "" msgid "GitHub personal access token (*)" msgstr "" -msgid "Reset" -msgstr "" - msgid "An error has occurred setting store as default" msgstr "" diff --git a/i18n/pt.po b/i18n/pt.po index 433efa945..0f3298e23 100644 --- a/i18n/pt.po +++ b/i18n/pt.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2020-10-23T08:51:17.118Z\n" +"POT-Creation-Date: 2020-10-23T08:53:45.330Z\n" "PO-Revision-Date: 2020-07-10T06:53:30.625Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -1458,14 +1458,6 @@ msgstr "" msgid "Connected successfully" msgstr "" -msgid "Reset store configuration" -msgstr "" - -msgid "" -"You will clear the existing configuration for all users in this instance.\n" -"Do you want to proceed?" -msgstr "" - msgid "Saving store connection" msgstr "" @@ -1493,9 +1485,6 @@ msgstr "" msgid "GitHub personal access token (*)" msgstr "" -msgid "Reset" -msgstr "" - msgid "An error has occurred setting store as default" msgstr "" diff --git a/src/presentation/webapp/pages/store-creation/StoreCreationPage.tsx b/src/presentation/webapp/pages/store-creation/StoreCreationPage.tsx index 76152ac63..9eb18ea4d 100644 --- a/src/presentation/webapp/pages/store-creation/StoreCreationPage.tsx +++ b/src/presentation/webapp/pages/store-creation/StoreCreationPage.tsx @@ -93,25 +93,6 @@ const StoreCreationPage: React.FC = () => { loading.reset(); }, [compositionRoot, state, validateError, snackbar, loading]); - const reset = useCallback(async () => { - updateDialog({ - title: i18n.t("Reset store configuration"), - description: i18n.t( - "You will clear the existing configuration for all users in this instance.\nDo you want to proceed?" - ), - onCancel: () => { - updateDialog(null); - }, - onSave: async () => { - await compositionRoot.store.update({} as Store, false); - updateDialog(null); - setState({} as Store); - }, - cancelText: i18n.t("Cancel"), - saveText: i18n.t("Proceed"), - }); - }, [compositionRoot]); - const save = useCallback(async () => { loading.show(true, i18n.t("Saving store connection")); @@ -228,13 +209,6 @@ const StoreCreationPage: React.FC = () => {
    -