From 0a660357a772b95ff399a6186aa1327896a6855b Mon Sep 17 00:00:00 2001 From: Emmanuel Date: Fri, 23 Aug 2024 09:59:33 +0100 Subject: [PATCH 1/7] feat: improve dataset deletion --- src/packages/model/Dataset.ts | 6 ++++ src/packages/model/ServerSideError.ts | 4 +++ src/packages/modules-classifications/hooks.js | 4 +-- .../item/edition/index.js | 2 +- .../visualization/home-container.js | 5 +-- .../code-sliding-panel-menu.js | 5 ++- .../codelist-detail/codes-panel-add-button.js | 5 ++- .../components/codelist-detail/edit.js | 5 ++- .../components/codelist-detail/menu.js | 5 ++- .../codelist-partial-detail/edit.js | 5 ++- src/packages/modules-codelists/menu/index.js | 5 ++- .../collections/export/home-container.js | 2 +- .../modules-concepts/collections/menu.js | 5 ++- .../collections/validation/home-container.js | 5 ++- .../collections/visualization/controls.js | 7 ++-- .../modules-concepts/export/home-container.js | 2 +- src/packages/modules-concepts/menu.js | 5 ++- .../validation/home-container.js | 5 ++- .../visualization/home-container.js | 5 +-- .../modules-datasets/api/datasets-api.js | 31 ---------------- .../modules-datasets/api/distributions-api.js | 31 ---------------- .../modules-datasets/{config.js => config.ts} | 0 .../{hooks.js => datasets.ts} | 17 +++++---- .../modules-datasets/datasets/edit/edit.js | 16 ++++----- .../datasets/edit/tabs/internal-management.js | 4 +-- .../modules-datasets/datasets/home/home.tsx | 2 +- .../modules-datasets/datasets/view/menu.js | 7 ++-- .../modules-datasets/datasets/view/view.js | 29 ++++++++++----- .../modules-datasets/distributions/edit.js | 10 +++--- .../distributions/home/home.tsx | 2 +- .../distributions/view/menu.js | 5 ++- .../distributions/view/view.js | 12 +++---- src/packages/modules-datasets/i18n.ts | 20 +++++++++++ .../components/component-detail/edit.js | 5 ++- .../components/component-detail/menu.js | 5 ++- .../structure-visualization/controls.js | 5 ++- .../modules-structures/edition/component.js | 5 ++- src/packages/redux/hooks/usePermission.ts | 6 ++++ src/packages/sdk/datasets-api.ts | 36 +++++++++++++++++++ src/packages/sdk/distributions-api.ts | 36 +++++++++++++++++++ src/packages/sdk/index.ts | 2 ++ 41 files changed, 207 insertions(+), 166 deletions(-) create mode 100644 src/packages/model/Dataset.ts create mode 100644 src/packages/model/ServerSideError.ts delete mode 100644 src/packages/modules-datasets/api/datasets-api.js delete mode 100644 src/packages/modules-datasets/api/distributions-api.js rename src/packages/modules-datasets/{config.js => config.ts} (100%) rename src/packages/modules-datasets/{hooks.js => datasets.ts} (56%) create mode 100644 src/packages/modules-datasets/i18n.ts create mode 100644 src/packages/redux/hooks/usePermission.ts create mode 100644 src/packages/sdk/datasets-api.ts create mode 100644 src/packages/sdk/distributions-api.ts diff --git a/src/packages/model/Dataset.ts b/src/packages/model/Dataset.ts new file mode 100644 index 000000000..b2fbc85d0 --- /dev/null +++ b/src/packages/model/Dataset.ts @@ -0,0 +1,6 @@ +export type Dataset = { + id?: string; +}; +export type Distribution = { + id?: string; +}; diff --git a/src/packages/model/ServerSideError.ts b/src/packages/model/ServerSideError.ts new file mode 100644 index 000000000..859f03370 --- /dev/null +++ b/src/packages/model/ServerSideError.ts @@ -0,0 +1,4 @@ +export type ServerSideError = { + code: number; + message: string; +}; diff --git a/src/packages/modules-classifications/hooks.js b/src/packages/modules-classifications/hooks.js index eb4e2fcfb..c363b7de5 100644 --- a/src/packages/modules-classifications/hooks.js +++ b/src/packages/modules-classifications/hooks.js @@ -19,7 +19,7 @@ export const useClassification = (id) => { export const usePublishClassification = (id) => { const queryClient = useQueryClient(); const { - isLoading: isPublishing, + isPending: isPublishing, mutate: publish, error, } = useMutation({ @@ -37,7 +37,7 @@ export const usePublishClassification = (id) => { export const useUpdateClassification = (id) => { const queryClient = useQueryClient(); const { - isLoading: isSaving, + isPending: isSaving, mutate: save, error, isSuccess: isSavingSuccess, diff --git a/src/packages/modules-classifications/item/edition/index.js b/src/packages/modules-classifications/item/edition/index.js index cada77ede..dd70ff1dd 100644 --- a/src/packages/modules-classifications/item/edition/index.js +++ b/src/packages/modules-classifications/item/edition/index.js @@ -68,7 +68,7 @@ const ClassificationItemEdition = () => { true ); - const { data: previousLevels = [], isLoading: isPreviousLevelsLoading } = + const { data: previousLevels = [], isPending: isPreviousLevelsLoading } = useQuery({ queryKey: ['classification-parent-levels', classificationId, itemId], queryFn: () => { diff --git a/src/packages/modules-classifications/visualization/home-container.js b/src/packages/modules-classifications/visualization/home-container.js index 3ada172a0..bef44d09e 100644 --- a/src/packages/modules-classifications/visualization/home-container.js +++ b/src/packages/modules-classifications/visualization/home-container.js @@ -3,14 +3,15 @@ import { useSelector } from 'react-redux'; import ClassificationVisualization from './home'; import { Loading } from '../../components'; import { useClassification, usePublishClassification } from '../hooks'; -import { getLocales, getPermission } from '../../redux/selectors'; +import { getLocales } from '../../redux/selectors'; import { getSecondLang } from '../../redux/second-lang'; +import { usePermission } from '../../redux/hooks/usePermission'; const ClassificationVisualizationContainer = () => { const { id } = useParams(); const langs = useSelector((state) => getLocales(state)); const secondLang = useSelector((state) => getSecondLang(state)); - const permission = useSelector((state) => getPermission(state)); + const permission = usePermission(); const { isLoading, classification } = useClassification(id); const { isPublishing, publish, error } = usePublishClassification(); diff --git a/src/packages/modules-codelists/components/codelist-detail/code-sliding-panel-menu.js b/src/packages/modules-codelists/components/codelist-detail/code-sliding-panel-menu.js index 9400d9106..32460a1bf 100644 --- a/src/packages/modules-codelists/components/codelist-detail/code-sliding-panel-menu.js +++ b/src/packages/modules-codelists/components/codelist-detail/code-sliding-panel-menu.js @@ -1,19 +1,18 @@ -import { useSelector } from 'react-redux'; import { ActionToolbar, ReturnButton, SaveButton, UpdateButton, } from '@inseefr/wilco'; -import { getPermission } from '../../../redux/selectors'; import { ADMIN, CODELIST_CONTRIBUTOR } from '../../../auth/roles'; +import { usePermission } from '../../../redux/hooks/usePermission'; export const CodeSlidingPanelMenu = ({ codelist, handleSubmit, handleBack, creation, }) => { - const permission = useSelector(getPermission); + const permission = usePermission(); const hasRightsBasedOnStamp = permission?.stamp === codelist?.contributor && diff --git a/src/packages/modules-codelists/components/codelist-detail/codes-panel-add-button.js b/src/packages/modules-codelists/components/codelist-detail/codes-panel-add-button.js index 5cef5eaa0..814756cf0 100644 --- a/src/packages/modules-codelists/components/codelist-detail/codes-panel-add-button.js +++ b/src/packages/modules-codelists/components/codelist-detail/codes-panel-add-button.js @@ -1,10 +1,9 @@ import D from '../../i18n/build-dictionary'; -import { useSelector } from 'react-redux'; import { ADMIN, CODELIST_CONTRIBUTOR } from '../../../auth/roles'; -import { getPermission } from '../../../redux/selectors'; +import { usePermission } from '../../../redux/hooks/usePermission'; export const CodesPanelAddButton = ({ codelist, onHandlePanel }) => { - const permission = useSelector(getPermission); + const permission = usePermission(); if (!codelist.lastCodeUriSegment) { return null; diff --git a/src/packages/modules-codelists/components/codelist-detail/edit.js b/src/packages/modules-codelists/components/codelist-detail/edit.js index f2524ffc3..53f0834ca 100644 --- a/src/packages/modules-codelists/components/codelist-detail/edit.js +++ b/src/packages/modules-codelists/components/codelist-detail/edit.js @@ -11,7 +11,6 @@ import D, { D1, D2 } from '../../i18n/build-dictionary'; import './edit.scss'; import MainDictionary from '../../../deprecated-locales/build-dictionary'; import { CodesCollapsiblePanel } from './codes-panel'; -import { useSelector } from 'react-redux'; import { TextInput, Row, @@ -23,8 +22,8 @@ import { Select, } from '../../../components'; import { useTitle } from '../../../utils/hooks/useTitle'; -import { getPermission } from '../../../redux/selectors'; import { ADMIN, CODELIST_CONTRIBUTOR } from '../../../auth/roles'; +import { usePermission } from '../../../redux/hooks/usePermission'; const defaultCodelist = { created: dayjs(), @@ -43,7 +42,7 @@ export const DumbCodelistDetailEdit = ({ useTitle(D.codelistsTitle, codelist?.labelLg1 || D.codelistsCreateTitle); - const permission = useSelector(getPermission); + const permission = usePermission(); const stamp = permission?.stamp; const isContributor = permission?.roles?.includes(CODELIST_CONTRIBUTOR) && diff --git a/src/packages/modules-codelists/components/codelist-detail/menu.js b/src/packages/modules-codelists/components/codelist-detail/menu.js index b16b74d0e..9e2637113 100644 --- a/src/packages/modules-codelists/components/codelist-detail/menu.js +++ b/src/packages/modules-codelists/components/codelist-detail/menu.js @@ -4,11 +4,10 @@ import { ReturnButton, UpdateButton, } from '@inseefr/wilco'; -import { useSelector } from 'react-redux'; import { UNPUBLISHED } from '../../..//model/ValidationState'; import { ValidationButton } from '../../../components'; import { ADMIN, CODELIST_CONTRIBUTOR } from '../../../auth/roles'; -import { getPermission } from '../../../redux/selectors'; +import { usePermission } from '../../../redux/hooks/usePermission'; export const ViewMenu = ({ col, handleUpdate, @@ -19,7 +18,7 @@ export const ViewMenu = ({ updatable, deletable, }) => { - const permission = useSelector(getPermission); + const permission = usePermission(); const hasRightsBasedOnStamp = permission?.stamp === codelist?.contributor && diff --git a/src/packages/modules-codelists/components/codelist-partial-detail/edit.js b/src/packages/modules-codelists/components/codelist-partial-detail/edit.js index ec10ef80c..dba85fa66 100644 --- a/src/packages/modules-codelists/components/codelist-partial-detail/edit.js +++ b/src/packages/modules-codelists/components/codelist-partial-detail/edit.js @@ -11,7 +11,6 @@ import { validatePartialCodelist, partialInGlobalCodes } from '../../utils'; import D, { D1, D2 } from '../../i18n/build-dictionary'; import '../codelist-detail/edit.scss'; import MainDictionary from '../../../deprecated-locales/build-dictionary'; -import { useSelector } from 'react-redux'; import { TextInput, Row, @@ -24,8 +23,8 @@ import { } from '../../../components'; import { CodeListApi } from '../../../sdk'; import { useTitle } from '../../../utils/hooks/useTitle'; -import { getPermission } from '../../../redux/selectors'; import { ADMIN, CODELIST_CONTRIBUTOR } from '../../../auth/roles'; +import { usePermission } from '../../../redux/hooks/usePermission'; const defaultCodelist = { created: dayjs(), @@ -77,7 +76,7 @@ export const DumbCodelistPartialDetailEdit = ({ [codelist, handleParentCode, globalCodeListOptions] ); - const permission = useSelector(getPermission); + const permission = usePermission(); const stamp = permission?.stamp; const isContributor = permission?.roles?.includes(CODELIST_CONTRIBUTOR) && diff --git a/src/packages/modules-codelists/menu/index.js b/src/packages/modules-codelists/menu/index.js index 53def6813..a55452d55 100644 --- a/src/packages/modules-codelists/menu/index.js +++ b/src/packages/modules-codelists/menu/index.js @@ -1,15 +1,14 @@ import { Menu } from '@inseefr/wilco'; import D from '../i18n/build-dictionary'; -import { useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; -import { getPermission } from '../../redux/selectors'; import { ADMIN } from '../../auth/roles'; +import { usePermission } from '../../redux/hooks/usePermission'; const defaultAttrs = { 'aria-current': 'page' }; const MenuCodelists = () => { const location = useLocation(); - const permission = useSelector((state) => getPermission(state)); + const permission = usePermission(); const activePath = location.pathname; if (activePath === '/') return null; diff --git a/src/packages/modules-concepts/collections/export/home-container.js b/src/packages/modules-concepts/collections/export/home-container.js index 79a501705..f82bc0cb5 100644 --- a/src/packages/modules-concepts/collections/export/home-container.js +++ b/src/packages/modules-concepts/collections/export/home-container.js @@ -13,7 +13,7 @@ const CollectionsToExportContainer = () => { const [ids, setIds] = useState([]); const { data: collections, isLoading } = useCollections(); - const { mutate: exportCollection, isLoading: isExporting } = + const { mutate: exportCollection, isPending: isExporting } = useCollectionExporter(); if (isExporting) return ; diff --git a/src/packages/modules-concepts/collections/menu.js b/src/packages/modules-concepts/collections/menu.js index 14868104d..af403154b 100644 --- a/src/packages/modules-concepts/collections/menu.js +++ b/src/packages/modules-concepts/collections/menu.js @@ -1,12 +1,11 @@ import D from '../../deprecated-locales/build-dictionary'; import check from '../../auth/auth'; import { Button, VerticalMenu, ExportButton } from '@inseefr/wilco'; -import { useSelector } from 'react-redux'; import { FeminineButton } from '../../components'; -import { getPermission } from '../../redux/selectors'; +import { usePermission } from '../../redux/hooks/usePermission'; export const Menu = () => { - const { authType, roles } = useSelector((state) => getPermission(state)); + const { authType, roles } = usePermission(); const authImpl = check(authType); const adminOrCreator = authImpl.isAdminOrCollectionCreator(roles); diff --git a/src/packages/modules-concepts/collections/validation/home-container.js b/src/packages/modules-concepts/collections/validation/home-container.js index d558898b2..ce86ec4db 100644 --- a/src/packages/modules-concepts/collections/validation/home-container.js +++ b/src/packages/modules-concepts/collections/validation/home-container.js @@ -1,17 +1,16 @@ import { useEffect, useState } from 'react'; -import { useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; import CollectionsToValidate from './home'; import { Loading } from '../../../components'; import D from '../../../deprecated-locales'; import { ConceptsApi } from '../../../sdk'; import { useTitle } from '../../../utils/hooks/useTitle'; -import { getPermission } from '../../../redux/selectors'; +import { usePermission } from '../../../redux/hooks/usePermission'; const CollectionsToValidateContainer = () => { useTitle(D.collectionsTitle, D.btnValid); - const permission = useSelector((state) => getPermission(state)); + const permission = usePermission(); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [collections, setCollections] = useState([]); diff --git a/src/packages/modules-concepts/collections/visualization/controls.js b/src/packages/modules-concepts/collections/visualization/controls.js index a33f36f73..f62974509 100644 --- a/src/packages/modules-concepts/collections/visualization/controls.js +++ b/src/packages/modules-concepts/collections/visualization/controls.js @@ -3,10 +3,9 @@ import { ActionToolbar, Button } from '@inseefr/wilco'; import check from '../../../auth/auth'; import D from '../../../deprecated-locales'; import { CollectionExportModal } from '../modal'; -import { useSelector } from 'react-redux'; import { saveFileFromHttpResponse } from '../../../utils/files'; import { CollectionApi } from '../../../sdk/collection-api'; -import { getPermission } from '../../../redux/selectors'; +import { usePermission } from '../../../redux/hooks/usePermission'; const CollectionVisualizationControls = ({ isValidated, @@ -15,9 +14,7 @@ const CollectionVisualizationControls = ({ handleValidation, setExporting, }) => { - const { authType, roles, stamp } = useSelector((state) => - getPermission(state) - ); + const { authType, roles, stamp } = usePermission(); const [displayModal, setDisplayModal] = useState(false); diff --git a/src/packages/modules-concepts/export/home-container.js b/src/packages/modules-concepts/export/home-container.js index bf595b5a7..b61d8a02f 100644 --- a/src/packages/modules-concepts/export/home-container.js +++ b/src/packages/modules-concepts/export/home-container.js @@ -9,7 +9,7 @@ const ConceptsToExportContainer = () => { useTitle(D.conceptsTitle, D.exportTitle); const [ids, setIds] = useState([]); - const { mutate: exportConcept, isLoading: isExporting } = + const { mutate: exportConcept, isPending: isExporting } = useConceptExporter(); const { isLoading, data: concepts } = useConcepts(); diff --git a/src/packages/modules-concepts/menu.js b/src/packages/modules-concepts/menu.js index 64891a691..69462e01f 100644 --- a/src/packages/modules-concepts/menu.js +++ b/src/packages/modules-concepts/menu.js @@ -1,13 +1,12 @@ import { ExportButton, PublishButton, VerticalMenu } from '@inseefr/wilco'; -import { useSelector } from 'react-redux'; import check from '../auth/auth'; import { MasculineButton } from '../components'; import { ADMIN } from '../auth/roles'; -import { getPermission } from '../redux/selectors'; import Auth from '../auth/components/auth'; +import { usePermission } from '../redux/hooks/usePermission'; export const Menu = () => { - const permission = useSelector((state) => getPermission(state)); + const permission = usePermission(); const { authType, roles } = permission; const authImpl = check(authType); const adminOrCreator = authImpl.isAdminOrConceptCreator(roles); diff --git a/src/packages/modules-concepts/validation/home-container.js b/src/packages/modules-concepts/validation/home-container.js index b2be40bc8..6b3206587 100644 --- a/src/packages/modules-concepts/validation/home-container.js +++ b/src/packages/modules-concepts/validation/home-container.js @@ -1,5 +1,4 @@ import { useEffect, useState } from 'react'; -import { useSelector } from 'react-redux'; import { Redirect } from 'react-router-dom'; import ConceptsToValidate from './home'; import { Loading } from '../../components'; @@ -8,11 +7,11 @@ import D from '../../deprecated-locales'; import { ConceptsApi } from '../../sdk'; import { useTitle } from '../../utils/hooks/useTitle'; import { sortArrayByLabel } from '../../utils/array-utils'; -import { getPermission } from '../../redux/selectors'; +import { usePermission } from '../../redux/hooks/usePermission'; const ConceptsToValidateContainer = () => { useTitle(D.conceptsTitle, D.btnValid); - const permission = useSelector((state) => getPermission(state)); + const permission = usePermission(); const [loading, setLoading] = useState(true); const [exporting, setExporting] = useState(); const [concepts, setConcepts] = useState([]); diff --git a/src/packages/modules-concepts/visualization/home-container.js b/src/packages/modules-concepts/visualization/home-container.js index 2da560e72..48cc7d5c8 100644 --- a/src/packages/modules-concepts/visualization/home-container.js +++ b/src/packages/modules-concepts/visualization/home-container.js @@ -6,9 +6,10 @@ import ConceptVisualization from './home'; import { LoadingProvider } from './loading'; import { ConceptsApi } from '../../sdk'; import { rmesHtmlToRawHtml } from '../../utils/html-utils'; -import { getLocales, getPermission } from '../../redux/selectors'; +import { getLocales } from '../../redux/selectors'; import { getSecondLang } from '../../redux/second-lang'; import { emptyNotes } from '../utils/notes'; +import { usePermission } from '../../redux/hooks/usePermission'; const formatNotes = (notes) => { return Object.assign( @@ -25,7 +26,7 @@ const ConceptVisualizationContainer = () => { const history = useHistory(); const langs = useSelector((state) => getLocales(state)); - const permission = useSelector((state) => getPermission(state)); + const permission = usePermission(); const secondLang = useSelector((state) => getSecondLang(state)); const [loading, setLoading] = useState('loading'); diff --git a/src/packages/modules-datasets/api/datasets-api.js b/src/packages/modules-datasets/api/datasets-api.js deleted file mode 100644 index 77ffa6579..000000000 --- a/src/packages/modules-datasets/api/datasets-api.js +++ /dev/null @@ -1,31 +0,0 @@ -import { buildApi } from '../..//sdk'; - -const api = { - getAll: () => [''], - getArchivageUnits: () => ['archivageUnits'], - getById: (id) => [id], - publish: (id) => [`${id}/validate`, { method: 'PUT' }, (res) => res.text()], - putDataset: (dataset) => [ - dataset.id, - { - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(dataset), - }, - () => Promise.resolve(dataset.id), - ], - postDataset: (dataset) => [ - '', - { - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(dataset), - }, - (res) => res.text(), - ], - deleteDataset: (id) => [`${id}`, (res) => res.text()], -}; - -export default buildApi('datasets', api); diff --git a/src/packages/modules-datasets/api/distributions-api.js b/src/packages/modules-datasets/api/distributions-api.js deleted file mode 100644 index 3c63a62e5..000000000 --- a/src/packages/modules-datasets/api/distributions-api.js +++ /dev/null @@ -1,31 +0,0 @@ -import { buildApi } from '../..//sdk'; - -const api = { - getAll: () => [''], - getById: (id) => [id], - getDatasets: () => ['datasets'], - publish: (id) => [`${id}/validate`, { method: 'PUT' }, (res) => res.text()], - putDistribution: (distribution) => [ - distribution.id, - { - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(distribution), - }, - () => Promise.resolve(distribution.id), - ], - postDistribution: (distribution) => [ - '', - { - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(distribution), - }, - (res) => res.text(), - ], - deleteDistribution: (id) => [`${id}`, (res) => res.text()], -}; - -export default buildApi('distribution', api); diff --git a/src/packages/modules-datasets/config.js b/src/packages/modules-datasets/config.ts similarity index 100% rename from src/packages/modules-datasets/config.js rename to src/packages/modules-datasets/config.ts diff --git a/src/packages/modules-datasets/hooks.js b/src/packages/modules-datasets/datasets.ts similarity index 56% rename from src/packages/modules-datasets/hooks.js rename to src/packages/modules-datasets/datasets.ts index 23d98b6a1..97e9e600c 100644 --- a/src/packages/modules-datasets/hooks.js +++ b/src/packages/modules-datasets/datasets.ts @@ -1,40 +1,39 @@ import { useQuery } from '@tanstack/react-query'; -import datasetApi from './api/datasets-api'; -import distributionApi from './api/distributions-api'; +import { DatasetsApi, DistributionApi } from '../sdk'; export const useDatasets = () => { return useQuery({ - queryFn: () => datasetApi.getAll(), + queryFn: () => DatasetsApi.getAll(), queryKey: ['datasets'], }); }; export const useDatasetsForDistributions = () => { return useQuery({ - queryFn: () => distributionApi.getDatasets(), + queryFn: () => DistributionApi.getDatasets(), queryKey: ['datasets-distributions'], }); }; export const useDistributions = () => { return useQuery({ - queryFn: () => distributionApi.getAll(), + queryFn: () => DistributionApi.getAll(), queryKey: ['distributions'], }); }; -export const useDistribution = (id) => { +export const useDistribution = (id: string) => { return useQuery({ enabled: !!id, queryKey: ['distributions', id], - queryFn: () => distributionApi.getById(id), + queryFn: () => DistributionApi.getById(id), }); }; -export const useDataset = (id) => { +export const useDataset = (id: string) => { return useQuery({ enabled: !!id, queryKey: ['datasets', id], - queryFn: () => datasetApi.getById(id), + queryFn: () => DatasetsApi.getById(id), }); }; diff --git a/src/packages/modules-datasets/datasets/edit/edit.js b/src/packages/modules-datasets/datasets/edit/edit.js index a7b6cac2c..1610c3f46 100644 --- a/src/packages/modules-datasets/datasets/edit/edit.js +++ b/src/packages/modules-datasets/datasets/edit/edit.js @@ -1,10 +1,8 @@ import D from '../../../deprecated-locales'; import { useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; -import { useSelector } from 'react-redux'; import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { useDataset } from '../../hooks'; -import api from '../../api/datasets-api'; +import { useDataset } from '../../datasets'; import { Loading, GlobalClientSideErrorBloc, @@ -21,8 +19,9 @@ import { validate } from './validation'; import './edit.scss'; import { useGoBack } from '../../../utils/hooks/useGoBack'; import { useTitle } from '../../../utils/hooks/useTitle'; -import { getPermission } from '../../../redux/selectors'; import { ADMIN, DATASET_CONTRIBUTOR } from '../../../auth/roles'; +import { usePermission } from '../../../redux/hooks/usePermission'; +import { DatasetsApi } from '../../../sdk'; export const DatasetEdit = (props) => { const { id } = useParams(); @@ -125,7 +124,8 @@ export const DatasetEdit = (props) => { const { data: dataset, status } = useDataset(id); - const permission = useSelector(getPermission); + const permission = usePermission(); + const stamp = permission?.stamp; const isContributor = permission?.roles?.includes(DATASET_CONTRIBUTOR) && @@ -145,13 +145,13 @@ export const DatasetEdit = (props) => { const queryClient = useQueryClient(); - const { isLoading: isSaving, mutate: save } = useMutation({ + const { isPending: isSaving, mutate: save } = useMutation({ mutationFn: () => { const formattedDataset = { themes: [], ...editingDataset }; if (isEditing) { - return api.putDataset(formattedDataset); + return DatasetsApi.putDataset(formattedDataset); } - return api.postDataset(formattedDataset); + return DatasetsApi.postDataset(formattedDataset); }, onSuccess: (id) => { diff --git a/src/packages/modules-datasets/datasets/edit/tabs/internal-management.js b/src/packages/modules-datasets/datasets/edit/tabs/internal-management.js index d2c7c603e..5b894ac35 100644 --- a/src/packages/modules-datasets/datasets/edit/tabs/internal-management.js +++ b/src/packages/modules-datasets/datasets/edit/tabs/internal-management.js @@ -1,7 +1,6 @@ import { D1 } from '../../../../deprecated-locales'; import { useEffect, useState } from 'react'; import { withCodesLists } from '../../../../utils/hoc/withCodesLists'; -import api from '../../../api/datasets-api'; import { LabelRequired } from '@inseefr/wilco'; import { useStampsOptions } from '../../../../utils/hooks/stamps'; import { useSeriesOperationsOptions } from './useSeriesOperationsOptions'; @@ -14,6 +13,7 @@ import { Select, } from '../../../../components'; import { convertCodesListsToSelectOption } from '../../../../modules-datasets/utils/codelist-to-select-options'; +import { DatasetsApi } from '../../../../sdk'; const InternalManagementTab = ({ editingDataset, @@ -40,7 +40,7 @@ const InternalManagementTab = ({ const [archivageUnits, setArchivageUnits] = useState([]); useEffect(() => { - api.getArchivageUnits().then(setArchivageUnits); + DatasetsApi.getArchivageUnits().then(setArchivageUnits); }, []); return ( diff --git a/src/packages/modules-datasets/datasets/home/home.tsx b/src/packages/modules-datasets/datasets/home/home.tsx index 761fca87e..0e932804b 100644 --- a/src/packages/modules-datasets/datasets/home/home.tsx +++ b/src/packages/modules-datasets/datasets/home/home.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { PageTitle } from '@inseefr/wilco'; import D from '../../../deprecated-locales/build-dictionary'; -import { useDatasets } from '../../hooks'; +import { useDatasets } from '../../datasets'; import { HomePageMenu } from './menu'; import { Loading, Row, SearchableList } from '../../../components'; import { useTitle } from '../../../utils/hooks/useTitle'; diff --git a/src/packages/modules-datasets/datasets/view/menu.js b/src/packages/modules-datasets/datasets/view/menu.js index 7cd7a79f3..66acab67a 100644 --- a/src/packages/modules-datasets/datasets/view/menu.js +++ b/src/packages/modules-datasets/datasets/view/menu.js @@ -5,17 +5,16 @@ import { ReturnButton, } from '@inseefr/wilco'; import D from '../../../deprecated-locales/build-dictionary'; -import { useSelector } from 'react-redux'; import { UNPUBLISHED } from '../../../model/ValidationState'; import { ValidationButton } from '../../../components'; import { useGoBack } from '../../../utils/hooks/useGoBack'; -import { getPermission } from '../../../redux/selectors'; import { ADMIN, DATASET_CONTRIBUTOR } from '../../../auth/roles'; +import { usePermission } from '../../../redux/hooks/usePermission'; -export const ViewMenu = ({ dataset, onPublish, onDelete, ...props }) => { +export const ViewMenu = ({ dataset, onPublish, onDelete }) => { const goBack = useGoBack(); - const permission = useSelector(getPermission); + const permission = usePermission(); const hasDatasetRightsBasedOnStamp = permission?.stamp === dataset?.catalogRecord?.contributor && diff --git a/src/packages/modules-datasets/datasets/view/view.js b/src/packages/modules-datasets/datasets/view/view.js index f7cd064ca..20e8b66ac 100644 --- a/src/packages/modules-datasets/datasets/view/view.js +++ b/src/packages/modules-datasets/datasets/view/view.js @@ -4,11 +4,10 @@ import { renderMarkdownElement } from '../../../utils/html-utils'; import { useEffect, useState } from 'react'; import { Note } from '@inseefr/wilco'; import D, { D1, D2 } from '../../../deprecated-locales/build-dictionary'; -import api from '../../api/datasets-api'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useThemes } from '../useThemes'; import { withCodesLists } from '../../../utils/hoc/withCodesLists'; -import { useDataset } from '../../hooks'; +import { useDataset } from '../../datasets'; import { ViewMenu } from './menu'; import { Deleting, @@ -22,6 +21,7 @@ import { DisseminationStatusVisualisation, PageTitleBlock, CheckSecondLang, + ErrorBloc, } from '../../../components'; import { CL_FREQ } from '../../../redux/actions/constants/codeList'; import { useOrganizations } from '../../../utils/hooks/organizations'; @@ -31,8 +31,12 @@ import { useTitle } from '../../../utils/hooks/useTitle'; import { stringToDate } from '../../../utils/date-utils'; import { getSecondLang } from '../../../redux/second-lang'; import { useStructures } from '../../../utils/hooks/structures'; +import { DatasetsApi } from '../../../sdk'; + +import { D as DatasetDictionary } from '../../i18n'; const Dataset = (props) => { + const [serverSideError, setServerSideError] = useState(); const { id } = useParams(); const history = useHistory(); const { data: structures } = useStructures(); @@ -40,7 +44,7 @@ const Dataset = (props) => { const [archivageUnits, setArchivageUnits] = useState([]); useEffect(() => { - api.getArchivageUnits().then(setArchivageUnits); + DatasetsApi.getArchivageUnits().then(setArchivageUnits); }, []); const { data: organisations } = useOrganizations(); @@ -53,9 +57,9 @@ const Dataset = (props) => { const secondLang = useSelector((state) => getSecondLang(state)); const queryClient = useQueryClient(); - const { isLoading: isPublishing, mutate: publish } = useMutation({ + const { isPending: isPublishing, mutate: publish } = useMutation({ mutationFn: () => { - return api.publish(id); + return DatasetsApi.publish(id); }, onSuccess: (id) => { @@ -63,14 +67,17 @@ const Dataset = (props) => { }, }); - const { isLoading: isDeleting, mutate: remove } = useMutation({ + const { isPending: isDeleting, mutate: remove } = useMutation({ mutationFn: () => { - return api.deleteDataset(id); + return DatasetsApi.deleteDataset(id); + }, + onError: (error) => { + setServerSideError(error); }, onSuccess: (id) => { return Promise.all([ - queryClient.invalidateQueries(['dataset', id]), - queryClient.invalidateQueries(['dataset']), + queryClient.invalidateQueries(['datasets', id]), + queryClient.invalidateQueries(['datasets']), ]).then(() => history.push('/datasets')); }, }); @@ -95,6 +102,10 @@ const Dataset = (props) => { onPublish={publish} onDelete={remove} /> + {serverSideError && ( + + )} + diff --git a/src/packages/modules-datasets/distributions/edit.js b/src/packages/modules-datasets/distributions/edit.js index 5c8cf5e30..6c7ff3c84 100644 --- a/src/packages/modules-datasets/distributions/edit.js +++ b/src/packages/modules-datasets/distributions/edit.js @@ -7,11 +7,10 @@ import { import { useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import { useMutation, useQueryClient } from '@tanstack/react-query'; -import api from '../api/distributions-api'; import { D1, D2 } from '../../deprecated-locales'; import { default as ReactSelect } from 'react-select'; import D from '../../deprecated-locales/build-dictionary'; -import { useDatasetsForDistributions, useDistribution } from '../hooks'; +import { useDatasetsForDistributions, useDistribution } from '../datasets'; import { validate } from './validation'; import { TextInput, @@ -24,6 +23,7 @@ import { } from '../../components'; import { useGoBack } from '../../utils/hooks/useGoBack'; import { useTitle } from '../../utils/hooks/useTitle'; +import { DistributionApi } from '../../sdk'; export const DistributionEdit = (props) => { const { id } = useParams(); @@ -52,12 +52,12 @@ export const DistributionEdit = (props) => { const queryClient = useQueryClient(); - const { isLoading: isSaving, mutate: save } = useMutation({ + const { isPending: isSaving, mutate: save } = useMutation({ mutationFn: () => { if (isEditing) { - return api.putDistribution(editingDistribution); + return DistributionApi.putDistribution(editingDistribution); } - return api.postDistribution(editingDistribution); + return DistributionApi.postDistribution(editingDistribution); }, onSuccess: (id) => { diff --git a/src/packages/modules-datasets/distributions/home/home.tsx b/src/packages/modules-datasets/distributions/home/home.tsx index 91dab8fb2..a3177ea7a 100644 --- a/src/packages/modules-datasets/distributions/home/home.tsx +++ b/src/packages/modules-datasets/distributions/home/home.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { PageTitle } from '@inseefr/wilco'; import D from '../../../deprecated-locales/build-dictionary'; -import { useDistributions } from '../../hooks'; +import { useDistributions } from '../../datasets'; import { HomePageMenu } from './menu'; import { Loading, Row, SearchableList } from '../../../components'; import { useTitle } from '../../../utils/hooks/useTitle'; diff --git a/src/packages/modules-datasets/distributions/view/menu.js b/src/packages/modules-datasets/distributions/view/menu.js index 38f6e778a..e5667a6ea 100644 --- a/src/packages/modules-datasets/distributions/view/menu.js +++ b/src/packages/modules-datasets/distributions/view/menu.js @@ -4,13 +4,12 @@ import { DeleteButton, ReturnButton, } from '@inseefr/wilco'; -import { useSelector } from 'react-redux'; import D from '../../../deprecated-locales/build-dictionary'; import { UNPUBLISHED } from '../../../model/ValidationState'; import { ValidationButton } from '../../../components'; import { useGoBack } from '../../../utils/hooks/useGoBack'; import { ADMIN, DATASET_CONTRIBUTOR } from '../../../auth/roles'; -import { getPermission } from '../../../redux/selectors'; +import { usePermission } from '../../../redux/hooks/usePermission'; export const ViewMenu = ({ distribution, @@ -21,7 +20,7 @@ export const ViewMenu = ({ }) => { const goBack = useGoBack(); - const permission = useSelector(getPermission); + const permission = usePermission(); const hasDatasetRightsBasedOnStamp = permission?.stamp === dataset?.catalogRecord?.contributor && diff --git a/src/packages/modules-datasets/distributions/view/view.js b/src/packages/modules-datasets/distributions/view/view.js index fa60bd272..631752e53 100644 --- a/src/packages/modules-datasets/distributions/view/view.js +++ b/src/packages/modules-datasets/distributions/view/view.js @@ -9,14 +9,14 @@ import { import { renderMarkdownElement } from '../../../utils/html-utils'; import { Note } from '@inseefr/wilco'; import D, { D1, D2 } from '../../../deprecated-locales/build-dictionary'; -import { useDataset, useDistribution } from '../../hooks'; +import { useDataset, useDistribution } from '../../datasets'; import { ViewMenu } from './menu'; import { useMutation, useQueryClient } from '@tanstack/react-query'; -import distributionApi from '../../api/distributions-api'; import { getLocales } from '../../../redux/selectors'; import { useTitle } from '../../../utils/hooks/useTitle'; import { stringToDate } from '../../../utils/date-utils'; import { getSecondLang } from '../../../redux/second-lang'; +import { DistributionApi } from '../../../sdk'; export const DistributionView = (props) => { const { id } = useParams(); @@ -33,9 +33,9 @@ export const DistributionView = (props) => { const queryClient = useQueryClient(); - const { isLoading: isPublishing, mutate: publish } = useMutation({ + const { isPending: isPublishing, mutate: publish } = useMutation({ mutationFn: () => { - return distributionApi.publish(id); + return DistributionApi.publish(id); }, onSuccess: (id) => { @@ -43,9 +43,9 @@ export const DistributionView = (props) => { }, }); - const { isLoading: isDeleting, mutate: remove } = useMutation({ + const { isPending: isDeleting, mutate: remove } = useMutation({ mutationFn: () => { - return distributionApi.deleteDistribution(id); + return DistributionApi.deleteDistribution(id); }, onSuccess: (id) => { diff --git a/src/packages/modules-datasets/i18n.ts b/src/packages/modules-datasets/i18n.ts new file mode 100644 index 000000000..7e8c65b64 --- /dev/null +++ b/src/packages/modules-datasets/i18n.ts @@ -0,0 +1,20 @@ +import { createAllDictionary } from '../utils/dictionnary'; + +export const { D, D1, D2 } = createAllDictionary({ + errors: { + 1203: { + fr: () => 'Seules les datasets non publiées peuvent être supprimées', + en: () => 'Only unpublished datasets can be deleted', + }, + 1204: { + fr: () => + 'Seules les datasets sans distributions associées peuvent être supprimées', + en: () => 'Only dataset without any distribution can be deleted', + }, + 1205: { + fr: () => + 'Seules les datasets sans distributions dérivées peuvent être supprimées', + en: () => 'Only dataset without any derived dataset can be deleted', + }, + }, +}); diff --git a/src/packages/modules-structures/components/component-detail/edit.js b/src/packages/modules-structures/components/component-detail/edit.js index 17154339f..ebf0951d2 100644 --- a/src/packages/modules-structures/components/component-detail/edit.js +++ b/src/packages/modules-structures/components/component-detail/edit.js @@ -23,7 +23,6 @@ import D, { D1, D2 } from '../../i18n/build-dictionary'; import './edit.scss'; import { CodesListPanel } from '../codes-list-panel/codes-list-panel'; import { API } from '../../../modules-codelists/apis'; -import { useSelector } from 'react-redux'; import { convertToArrayIfDefined, sortArray } from '../../../utils/array-utils'; import { TextInput, @@ -38,8 +37,8 @@ import { } from '../../../components'; import { useTitle } from '../../../utils/hooks/useTitle'; import { AppContext } from '../../../application/app-context'; -import { getPermission } from '../../../redux/selectors'; import { ADMIN, STRUCTURE_CONTRIBUTOR } from '../../../auth/roles'; +import { usePermission } from '../../../redux/hooks/usePermission'; const linkedAttributeLabelMapping = { [XSD_INTEGER]: D.insertIntValue, @@ -184,7 +183,7 @@ export const DumbComponentDetailEdit = ({ const { lg1, lg2 } = useContext(AppContext); useTitle(D.componentTitle, component?.labelLg1 || D.componentsCreateTitle); - const permission = useSelector(getPermission); + const permission = usePermission(); const stamp = permission?.stamp; const isContributor = permission?.roles?.includes(STRUCTURE_CONTRIBUTOR) && diff --git a/src/packages/modules-structures/components/component-detail/menu.js b/src/packages/modules-structures/components/component-detail/menu.js index 4dc9c4a06..7814b5e77 100644 --- a/src/packages/modules-structures/components/component-detail/menu.js +++ b/src/packages/modules-structures/components/component-detail/menu.js @@ -4,11 +4,10 @@ import { ReturnButton, UpdateButton, } from '@inseefr/wilco'; -import { useSelector } from 'react-redux'; import { UNPUBLISHED } from '../../../model/ValidationState'; import { ValidationButton } from '../../../components'; import { ADMIN, STRUCTURE_CONTRIBUTOR } from '../../../auth/roles'; -import { getPermission } from '../../../redux/selectors'; +import { usePermission } from '../../../redux/hooks/usePermission'; const canBeDeleted = (component) => { const withoutStructuresUsingThisComponent = !component.structures || component.structures?.length === 0; @@ -24,7 +23,7 @@ export const ViewMenu = ({ updatable, col, }) => { - const permission = useSelector(getPermission); + const permission = usePermission(); const hasRightsBasedOnStamp = permission?.stamp === component?.contributor && diff --git a/src/packages/modules-structures/components/structure-visualization/controls.js b/src/packages/modules-structures/components/structure-visualization/controls.js index ba6306f50..1bfe9071b 100644 --- a/src/packages/modules-structures/components/structure-visualization/controls.js +++ b/src/packages/modules-structures/components/structure-visualization/controls.js @@ -7,14 +7,13 @@ import { DeleteButton, DuplicateButton, } from '@inseefr/wilco'; -import { useSelector } from 'react-redux'; import { UNPUBLISHED } from '../../../model/ValidationState'; import { ValidationButton } from '../../../components'; import { ADMIN, STRUCTURE_CONTRIBUTOR } from '../../../auth/roles'; -import { getPermission } from '../../../redux/selectors'; import { StructureApi } from '../../../sdk'; +import { usePermission } from '../../../redux/hooks/usePermission'; const Controls = ({ structure, publish }) => { - const permission = useSelector(getPermission); + const permission = usePermission(); const { id } = structure; let history = useHistory(); diff --git a/src/packages/modules-structures/edition/component.js b/src/packages/modules-structures/edition/component.js index 403b3ba96..b88a977c0 100644 --- a/src/packages/modules-structures/edition/component.js +++ b/src/packages/modules-structures/edition/component.js @@ -17,13 +17,12 @@ import Controls from './controls'; import Components from './components'; import { DISSEMINATION_STATUS } from '../utils/constants'; import D, { D1, D2 } from '../../deprecated-locales'; -import { useSelector } from 'react-redux'; import { validate } from './validation'; import { useStampsOptions } from '../../utils/hooks/stamps'; import { AppContext } from '../../application/app-context'; -import { getPermission } from '../../redux/selectors'; import { ADMIN, STRUCTURE_CONTRIBUTOR } from '../../auth/roles'; import { StructureApi } from '../../sdk'; +import { usePermission } from '../../redux/hooks/usePermission'; const defaultDSD = { identifiant: '', @@ -69,7 +68,7 @@ const Edition = ({ creation, initialStructure }) => { disseminationStatus, } = structure; - const permission = useSelector(getPermission); + const permission = usePermission(); const stamp = permission?.stamp; const isContributor = permission?.roles?.includes(STRUCTURE_CONTRIBUTOR) && diff --git a/src/packages/redux/hooks/usePermission.ts b/src/packages/redux/hooks/usePermission.ts new file mode 100644 index 000000000..47dcec24f --- /dev/null +++ b/src/packages/redux/hooks/usePermission.ts @@ -0,0 +1,6 @@ +import { useSelector } from 'react-redux'; +import { getPermission } from '../selectors'; + +export const usePermission = () => { + return useSelector(getPermission); +}; diff --git a/src/packages/sdk/datasets-api.ts b/src/packages/sdk/datasets-api.ts new file mode 100644 index 000000000..7a5416c28 --- /dev/null +++ b/src/packages/sdk/datasets-api.ts @@ -0,0 +1,36 @@ +import { buildApi } from './build-api'; +import { Dataset } from '../model/Dataset'; + +const api = { + getAll: () => [''], + getArchivageUnits: () => ['archivageUnits'], + getById: (id: string) => [id], + publish: (id: string) => [ + `${id}/validate`, + { method: 'PUT' }, + (res: Response) => res.text(), + ], + putDataset: (dataset: Dataset) => [ + dataset.id, + { + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(dataset), + }, + () => Promise.resolve(dataset.id), + ], + postDataset: (dataset: Dataset) => [ + '', + { + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(dataset), + }, + (res: Response) => res.text(), + ], + deleteDataset: (id: string) => [`${id}`, {}, () => Promise.resolve(id)], +}; + +export const DatasetsApi = buildApi('datasets', api) as any; diff --git a/src/packages/sdk/distributions-api.ts b/src/packages/sdk/distributions-api.ts new file mode 100644 index 000000000..10021b856 --- /dev/null +++ b/src/packages/sdk/distributions-api.ts @@ -0,0 +1,36 @@ +import { buildApi } from './build-api'; +import { Distribution } from '../model/Dataset'; + +const api = { + getAll: () => [''], + getById: (id: string) => [id], + getDatasets: () => ['datasets'], + publish: (id: string) => [ + `${id}/validate`, + { method: 'PUT' }, + (res: Response) => res.text(), + ], + putDistribution: (distribution: Distribution) => [ + distribution.id, + { + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(distribution), + }, + () => Promise.resolve(distribution.id), + ], + postDistribution: (distribution: Distribution) => [ + '', + { + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(distribution), + }, + (res: Response) => res.text(), + ], + deleteDistribution: (id: string) => [`${id}`, {}, () => Promise.resolve(id)], +}; + +export const DistributionApi = buildApi('distribution', api) as any; diff --git a/src/packages/sdk/index.ts b/src/packages/sdk/index.ts index 80943fe94..f8b9bf105 100644 --- a/src/packages/sdk/index.ts +++ b/src/packages/sdk/index.ts @@ -1,5 +1,7 @@ export * from './codes-list-api'; export * from './concepts-api'; +export * from './datasets-api'; +export * from './distributions-api'; export * from './organisations-api'; export * from './stamps-api'; export * from './structure-api'; From c2f42c9996907f352d986f5183d3f6ecd5ef3b7a Mon Sep 17 00:00:00 2001 From: Emmanuel Date: Fri, 23 Aug 2024 10:09:52 +0100 Subject: [PATCH 2/7] fix: remove 2 sonar issues --- src/packages/modules-datasets/datasets/home/home.tsx | 2 +- src/packages/modules-datasets/distributions/home/home.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/modules-datasets/datasets/home/home.tsx b/src/packages/modules-datasets/datasets/home/home.tsx index 0e932804b..384e212fe 100644 --- a/src/packages/modules-datasets/datasets/home/home.tsx +++ b/src/packages/modules-datasets/datasets/home/home.tsx @@ -23,7 +23,7 @@ export const DatasetHome = () => {
{
dataset.labelLg1} From 633cab7f8c33ddb4bd5fc25719edc8b3d20575fe Mon Sep 17 00:00:00 2001 From: Emmanuel Date: Sat, 24 Aug 2024 13:21:41 +0100 Subject: [PATCH 3/7] feat: migrate 2 files and remove sonar issues --- src/index.tsx | 4 +-- src/packages/components/errors-bloc/index.tsx | 26 +++++++++++-------- src/packages/model/Classification.ts | 14 ++++++++++ src/packages/modules-classifications/hooks.js | 2 +- .../item/edition/index.js | 5 +--- .../{home-container.js => home-container.tsx} | 14 +++++----- .../visualization/{home.js => home.tsx} | 26 ++++++++++++------- 7 files changed, 56 insertions(+), 35 deletions(-) create mode 100644 src/packages/model/Classification.ts rename src/packages/modules-classifications/visualization/{home-container.js => home-container.tsx} (74%) rename src/packages/modules-classifications/visualization/{home.js => home.tsx} (78%) diff --git a/src/index.tsx b/src/index.tsx index a511206ec..ca270d077 100755 --- a/src/index.tsx +++ b/src/index.tsx @@ -26,7 +26,7 @@ const queryClient = new QueryClient({ }, }); -const Error = () => { +const ErrorBlock = () => { return (
@@ -41,7 +41,7 @@ GeneralApi.getInit() .then( (res: any) => (res.ok ? res.json() : Promise.reject(res.statusText)), (err: any) => { - renderApp(Error, {}, { home: true }); + renderApp(ErrorBlock, {}, { home: true }); return Promise.reject(err.toString()); } ) diff --git a/src/packages/components/errors-bloc/index.tsx b/src/packages/components/errors-bloc/index.tsx index 4b2ed7199..bc39dddad 100644 --- a/src/packages/components/errors-bloc/index.tsx +++ b/src/packages/components/errors-bloc/index.tsx @@ -61,15 +61,19 @@ export const ErrorBloc = ({ return errorMsg; }); - return formattedErrors.map((e, index) => ( -
- {
|| ( - - )} -
- )); + return ( + <> + {formattedErrors.map((e, index) => ( +
+ {
|| ( + + )} +
+ ))} + + ); }; diff --git a/src/packages/model/Classification.ts b/src/packages/model/Classification.ts new file mode 100644 index 000000000..4405bc8bc --- /dev/null +++ b/src/packages/model/Classification.ts @@ -0,0 +1,14 @@ +export type Classification = { + general: { + id: string; + prefLabelLg1: string; + prefLabelLg2: string; + scopeNoteLg1: string; + scopeNoteLg2: string; + changeNoteLg1: string; + changeNoteLg2: string; + descriptionLg1: string; + descriptionLg2: string; + }; + levels: unknown[]; +}; diff --git a/src/packages/modules-classifications/hooks.js b/src/packages/modules-classifications/hooks.js index c363b7de5..f9b65904a 100644 --- a/src/packages/modules-classifications/hooks.js +++ b/src/packages/modules-classifications/hooks.js @@ -23,7 +23,7 @@ export const usePublishClassification = (id) => { mutate: publish, error, } = useMutation({ - mutationFn: (id) => { + mutationFn: () => { return ClassificationsApi.publishClassification(id); }, diff --git a/src/packages/modules-classifications/item/edition/index.js b/src/packages/modules-classifications/item/edition/index.js index dd70ff1dd..ecdf09f71 100644 --- a/src/packages/modules-classifications/item/edition/index.js +++ b/src/packages/modules-classifications/item/edition/index.js @@ -117,10 +117,7 @@ const ClassificationItemEdition = () => { }); Object.entries(value).forEach(([key]) => { - if ( - key.indexOf('altLabelsLg1_') === 0 || - key.indexOf('altLabelsLg2_') === 0 - ) { + if (key.startsWith('altLabelsLg1_') || key.startsWith('altLabelsLg2_')) { delete value[key]; } }); diff --git a/src/packages/modules-classifications/visualization/home-container.js b/src/packages/modules-classifications/visualization/home-container.tsx similarity index 74% rename from src/packages/modules-classifications/visualization/home-container.js rename to src/packages/modules-classifications/visualization/home-container.tsx index bef44d09e..d6e3b8a2d 100644 --- a/src/packages/modules-classifications/visualization/home-container.js +++ b/src/packages/modules-classifications/visualization/home-container.tsx @@ -5,16 +5,15 @@ import { Loading } from '../../components'; import { useClassification, usePublishClassification } from '../hooks'; import { getLocales } from '../../redux/selectors'; import { getSecondLang } from '../../redux/second-lang'; -import { usePermission } from '../../redux/hooks/usePermission'; +import { ReduxModel } from '../../redux/model'; const ClassificationVisualizationContainer = () => { - const { id } = useParams(); - const langs = useSelector((state) => getLocales(state)); - const secondLang = useSelector((state) => getSecondLang(state)); - const permission = usePermission(); + const { id } = useParams<{ id: string }>(); + const langs = useSelector((state: ReduxModel) => getLocales(state)); + const secondLang = useSelector((state: ReduxModel) => getSecondLang(state)); const { isLoading, classification } = useClassification(id); - const { isPublishing, publish, error } = usePublishClassification(); + const { isPublishing, publish, error } = usePublishClassification(id); if (isLoading) { return ; @@ -29,8 +28,7 @@ const ClassificationVisualizationContainer = () => { classificationId={id} secondLang={secondLang} langs={langs} - permission={permission} - publish={() => publish(id)} + publish={publish} serverSideError={error} /> ); diff --git a/src/packages/modules-classifications/visualization/home.js b/src/packages/modules-classifications/visualization/home.tsx similarity index 78% rename from src/packages/modules-classifications/visualization/home.js rename to src/packages/modules-classifications/visualization/home.tsx index 43186d693..2fba866c8 100644 --- a/src/packages/modules-classifications/visualization/home.js +++ b/src/packages/modules-classifications/visualization/home.tsx @@ -7,16 +7,24 @@ import Notes from './notes'; import Levels from './levels'; import D from '../../deprecated-locales'; import { useTitle } from '../../utils/hooks/useTitle'; +import { Classification } from '../../model/Classification'; -const ClassificationVisualization = (props) => { - const { - classification: { general, levels }, - classificationId, - secondLang, - langs, - publish, - serverSideError, - } = props; +type ClassificationVisualizationTypes = { + classification: Classification; + classificationId: string; + secondLang?: boolean; + langs: { lg1: string; lg2: string }; + publish: () => void; + serverSideError?: any; +}; +const ClassificationVisualization = ({ + classification: { general, levels }, + classificationId, + secondLang, + langs, + publish, + serverSideError, +}: ClassificationVisualizationTypes) => { useTitle(D.classificationsTitle, general?.prefLabelLg1); const notes = { From 589f300b07c207d91a4e268e19ce72800ed20378 Mon Sep 17 00:00:00 2001 From: Emmanuel Date: Sun, 25 Aug 2024 14:09:13 +0100 Subject: [PATCH 4/7] fix: solve sonar issue --- src/packages/components/explanatory-note/index.tsx | 1 + src/packages/components/tabs/tabs.tsx | 4 ++-- src/packages/modules-concepts/visualization/home.js | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/packages/components/explanatory-note/index.tsx b/src/packages/components/explanatory-note/index.tsx index 38e848020..39bcedb77 100644 --- a/src/packages/components/explanatory-note/index.tsx +++ b/src/packages/components/explanatory-note/index.tsx @@ -22,6 +22,7 @@ export const ExplanatoryNote = ({ /href="http:\/\/.+?\/codes\/(.+?)\/.+?\/(.+?)"/g, `href="${window.location.origin}/classifications/classification/$1/item/$2"` ); + return ( setActiveTab(index)} + onSelect={setActiveTab} justified > {tabs.map((t, i) => ( Date: Sun, 25 Aug 2024 14:19:56 +0100 Subject: [PATCH 5/7] feat: migrate few TS files --- .../i18n/{build-dictionary.js => build-dictionary.ts} | 0 .../modules-structures/i18n/{component.js => component.ts} | 0 .../modules-structures/i18n/{dictionary.js => dictionary.ts} | 0 src/packages/modules-structures/i18n/{errors.js => errors.ts} | 4 ++-- 4 files changed, 2 insertions(+), 2 deletions(-) rename src/packages/modules-structures/i18n/{build-dictionary.js => build-dictionary.ts} (100%) rename src/packages/modules-structures/i18n/{component.js => component.ts} (100%) rename src/packages/modules-structures/i18n/{dictionary.js => dictionary.ts} (100%) rename src/packages/modules-structures/i18n/{errors.js => errors.ts} (86%) diff --git a/src/packages/modules-structures/i18n/build-dictionary.js b/src/packages/modules-structures/i18n/build-dictionary.ts similarity index 100% rename from src/packages/modules-structures/i18n/build-dictionary.js rename to src/packages/modules-structures/i18n/build-dictionary.ts diff --git a/src/packages/modules-structures/i18n/component.js b/src/packages/modules-structures/i18n/component.ts similarity index 100% rename from src/packages/modules-structures/i18n/component.js rename to src/packages/modules-structures/i18n/component.ts diff --git a/src/packages/modules-structures/i18n/dictionary.js b/src/packages/modules-structures/i18n/dictionary.ts similarity index 100% rename from src/packages/modules-structures/i18n/dictionary.js rename to src/packages/modules-structures/i18n/dictionary.ts diff --git a/src/packages/modules-structures/i18n/errors.js b/src/packages/modules-structures/i18n/errors.ts similarity index 86% rename from src/packages/modules-structures/i18n/errors.js rename to src/packages/modules-structures/i18n/errors.ts index ee9ebf870..cb661e73d 100644 --- a/src/packages/modules-structures/i18n/errors.js +++ b/src/packages/modules-structures/i18n/errors.ts @@ -7,9 +7,9 @@ const messages = { }, //TODO find a solution in order to avoid this duplicated key mandatoryProperty: { - fr: (propertyName) => + fr: (propertyName: string) => `La propriété ${propertyName} est obligatoire.`, - en: (propertyName) => + en: (propertyName: string) => `The property ${propertyName} is required.`, }, }; From 89eff5ca8ed5fb8c6b0f9b04a5c1a9cb7e605b70 Mon Sep 17 00:00:00 2001 From: Emmanuel Date: Sun, 25 Aug 2024 15:04:04 +0100 Subject: [PATCH 6/7] feat: add unit test for document menu component --- .../modules-operations/document/home.js | 36 +++---------- .../modules-operations/document/menu.spec.tsx | 54 +++++++++++++++++++ .../modules-operations/document/menu.tsx | 25 +++++++++ 3 files changed, 85 insertions(+), 30 deletions(-) create mode 100644 src/packages/modules-operations/document/menu.spec.tsx create mode 100644 src/packages/modules-operations/document/menu.tsx diff --git a/src/packages/modules-operations/document/home.js b/src/packages/modules-operations/document/home.js index b5b7d87b0..f5c268bb1 100644 --- a/src/packages/modules-operations/document/home.js +++ b/src/packages/modules-operations/document/home.js @@ -1,23 +1,13 @@ import { useState, useCallback, useEffect } from 'react'; -import { VerticalMenu, filterKeyDeburr, nbResults } from '@inseefr/wilco'; -import { - PageTitle, - TextInput, - MasculineButton, - Pagination, -} from '../../components'; +import { filterKeyDeburr, nbResults } from '@inseefr/wilco'; +import { PageTitle, TextInput, Pagination, Row } from '../../components'; import D from '../../deprecated-locales'; import { BOTH, DOCUMENT, LINK, isLink, isDocument } from './utils'; import { Link, useHistory, useLocation } from 'react-router-dom'; import FilterToggleButtons from '../../components/filter-toggle-buttons'; import { useTitle } from '../../utils/hooks/useTitle'; -import Auth from '../../auth/components/auth'; -import { - ADMIN, - INDICATOR_CONTRIBUTOR, - SERIES_CONTRIBUTOR, -} from '../../auth/roles'; +import { Menu } from './menu'; const formatter = (content, label) => { const extraInformations = []; @@ -130,22 +120,8 @@ function DocumentHome({ documents }) { return (
-
- - - {[ - ['/operations/document/create', D.document], - ['/operations/link/create', D.link], - ].map(([url, title], index) => ( - - ))} - - + +
@@ -170,7 +146,7 @@ function DocumentHome({ documents }) { autoFocus={true} />
-
+
); } diff --git a/src/packages/modules-operations/document/menu.spec.tsx b/src/packages/modules-operations/document/menu.spec.tsx new file mode 100644 index 000000000..af4a4a23d --- /dev/null +++ b/src/packages/modules-operations/document/menu.spec.tsx @@ -0,0 +1,54 @@ +import { render, screen } from '@testing-library/react'; +import { Menu } from './menu'; +import { RBACMock } from '../../tests-utils/rbac'; +import { + ADMIN, + INDICATOR_CONTRIBUTOR, + SERIES_CONTRIBUTOR, +} from '../../auth/roles'; + +describe('Structures Home Page Menu', () => { + it('an admin can create a new structure if he does not have the Gestionnaire_structures_RMESGNCS role', () => { + render( + + + + ); + + screen.getByText('New Link'); + screen.getByText('New Document'); + }); + + it('a user with INDICATOR_CONTRIBUTOR role can create a structure', () => { + render( + + + + ); + + screen.getByText('New Link'); + screen.getByText('New Document'); + }); + + it('a user with SERIES_CONTRIBUTOR role can create a structure', () => { + render( + + + + ); + + screen.getByText('New Link'); + screen.getByText('New Document'); + }); + + it('a user without Admin or INDICATOR_CONTRIBUTOR or SERIES_CONTRIBUTOR role cannot create a structure', () => { + render( + + + + ); + + expect(screen.queryByText('New Link')).toBeNull(); + expect(screen.queryByText('New Document')).toBeNull(); + }); +}); diff --git a/src/packages/modules-operations/document/menu.tsx b/src/packages/modules-operations/document/menu.tsx new file mode 100644 index 000000000..8f11062ec --- /dev/null +++ b/src/packages/modules-operations/document/menu.tsx @@ -0,0 +1,25 @@ +import { + ADMIN, + INDICATOR_CONTRIBUTOR, + SERIES_CONTRIBUTOR, +} from '../../auth/roles'; +import { VerticalMenu } from '@inseefr/wilco'; +import D from '../../deprecated-locales/build-dictionary'; +import { MasculineButton } from '../../components'; +import Auth from '../../auth/components/auth'; + +const routes = [ + ['/operations/document/create', D.document], + ['/operations/link/create', D.link], +]; +export const Menu = () => { + return ( + + + {routes.map(([url, title]) => ( + + ))} + + + ); +}; From a64fe83407e694dbcdb1bbe3c6daa6f8e8a18cc6 Mon Sep 17 00:00:00 2001 From: Emmanuel Date: Mon, 26 Aug 2024 09:08:22 +0100 Subject: [PATCH 7/7] feat: migrate document visualization menu --- src/packages/auth/components/auth.ts | 4 +- src/packages/model/Sims.ts | 4 + src/packages/model/operations/document.ts | 6 ++ .../modules-operations/document/menu.spec.tsx | 8 +- .../document/visualization/Menu.spec.tsx | 102 ++++++++++++++++++ .../document/visualization/Menu.tsx | 61 +++++++++++ .../document/visualization/index.js | 52 +-------- 7 files changed, 182 insertions(+), 55 deletions(-) create mode 100644 src/packages/model/operations/document.ts create mode 100644 src/packages/modules-operations/document/visualization/Menu.spec.tsx create mode 100644 src/packages/modules-operations/document/visualization/Menu.tsx diff --git a/src/packages/auth/components/auth.ts b/src/packages/auth/components/auth.ts index 50facc154..7f7a835a7 100644 --- a/src/packages/auth/components/auth.ts +++ b/src/packages/auth/components/auth.ts @@ -19,7 +19,7 @@ const mapDispatchToProps = { type AuthDumbTypes = { userRoles?: string[]; userStamp?: string; - roles: string[]; + roles: Array boolean]>; fallback?: any; complementaryCheck?: boolean; loadUserStamp?: any; @@ -49,7 +49,7 @@ export function AuthDumb({ const isAuthorized = !!roles.find((role) => { if (Array.isArray(role)) { const [r, check] = role; - return userRoles?.includes(r) && check(userStamp); + return userRoles?.includes(r) && check(userStamp!); } return userRoles?.includes(role); }); diff --git a/src/packages/model/Sims.ts b/src/packages/model/Sims.ts index a609e3c30..d141ff43d 100644 --- a/src/packages/model/Sims.ts +++ b/src/packages/model/Sims.ts @@ -1,3 +1,7 @@ export type Rubric = { idAttribute: string; }; + +export type Sims = { + creators: string[]; +}; diff --git a/src/packages/model/operations/document.ts b/src/packages/model/operations/document.ts new file mode 100644 index 000000000..d5a367b0c --- /dev/null +++ b/src/packages/model/operations/document.ts @@ -0,0 +1,6 @@ +import { Sims } from '../Sims'; + +export type Document = { + id: string; + sims: Sims[]; +}; diff --git a/src/packages/modules-operations/document/menu.spec.tsx b/src/packages/modules-operations/document/menu.spec.tsx index af4a4a23d..bf76f25e5 100644 --- a/src/packages/modules-operations/document/menu.spec.tsx +++ b/src/packages/modules-operations/document/menu.spec.tsx @@ -7,7 +7,7 @@ import { SERIES_CONTRIBUTOR, } from '../../auth/roles'; -describe('Structures Home Page Menu', () => { +describe('Document Home Page Menu', () => { it('an admin can create a new structure if he does not have the Gestionnaire_structures_RMESGNCS role', () => { render( @@ -19,7 +19,7 @@ describe('Structures Home Page Menu', () => { screen.getByText('New Document'); }); - it('a user with INDICATOR_CONTRIBUTOR role can create a structure', () => { + it('a user with INDICATOR_CONTRIBUTOR role can create a document', () => { render( @@ -30,7 +30,7 @@ describe('Structures Home Page Menu', () => { screen.getByText('New Document'); }); - it('a user with SERIES_CONTRIBUTOR role can create a structure', () => { + it('a user with SERIES_CONTRIBUTOR role can create a document', () => { render( @@ -41,7 +41,7 @@ describe('Structures Home Page Menu', () => { screen.getByText('New Document'); }); - it('a user without Admin or INDICATOR_CONTRIBUTOR or SERIES_CONTRIBUTOR role cannot create a structure', () => { + it('a user without Admin or INDICATOR_CONTRIBUTOR or SERIES_CONTRIBUTOR role cannot create a document', () => { render( diff --git a/src/packages/modules-operations/document/visualization/Menu.spec.tsx b/src/packages/modules-operations/document/visualization/Menu.spec.tsx new file mode 100644 index 000000000..295ef4b76 --- /dev/null +++ b/src/packages/modules-operations/document/visualization/Menu.spec.tsx @@ -0,0 +1,102 @@ +import { render, screen } from '@testing-library/react'; +import { Menu } from './Menu'; +import { RBACMock } from '../../../tests-utils/rbac'; +import { + ADMIN, + INDICATOR_CONTRIBUTOR, + SERIES_CONTRIBUTOR, +} from '../../../auth/roles'; +import { Document } from '../../../model/operations/document'; +describe('Document Visualization Page Menu', () => { + it('an admin can create a new structure if he does not have the Gestionnaire_structures_RMESGNCS role', () => { + render( + + + + ); + + screen.getByText('Update'); + }); + + it('a user with INDICATOR_CONTRIBUTOR role can update a document if the stamp match', () => { + render( + + + + ); + + screen.getByText('Update'); + }); + + it('a user with INDICATOR_CONTRIBUTOR role cannot update a document if the stamp does not match', () => { + render( + + + + ); + + expect(screen.queryByText('Update')).toBeNull(); + }); + + it('a user with SERIES_CONTRIBUTOR role can update a document if the stamp match', () => { + render( + + + + ); + + screen.getByText('Update'); + }); + + it('a user with SERIES_CONTRIBUTOR role cannot update a document if the stamp does not match', () => { + render( + + + + ); + + expect(screen.queryByText('Update')).toBeNull(); + }); + + it('a user without Admin or INDICATOR_CONTRIBUTOR or SERIES_CONTRIBUTOR role cannot create a document', () => { + render( + + + + ); + + expect(screen.queryByText('Update')).toBeNull(); + }); +}); diff --git a/src/packages/modules-operations/document/visualization/Menu.tsx b/src/packages/modules-operations/document/visualization/Menu.tsx new file mode 100644 index 000000000..844cd5aac --- /dev/null +++ b/src/packages/modules-operations/document/visualization/Menu.tsx @@ -0,0 +1,61 @@ +import { ActionToolbar, Button, ReturnButton } from '@inseefr/wilco'; +import Auth from '../../../auth/components/auth'; +import { + ADMIN, + INDICATOR_CONTRIBUTOR, + SERIES_CONTRIBUTOR, +} from '../../../auth/roles'; +import D from '../../../deprecated-locales/build-dictionary'; +import { useGoBack } from '../../../utils/hooks/useGoBack'; +import { Document } from '../../../model/operations/document'; + +type MenuTypes = { + document: Document; + type: string; +}; + +const checkContributorRight = (document: Document) => { + return (stamp: string) => { + const sims = document.sims; + if (sims?.length === 0) { + return true; + } + const stamps = sims.map(({ creators }) => creators); + for (let i = 1; i < stamps.length; i++) { + // we first check if all stamps array have the same size. + if (stamps[i - 1].length !== stamps[i].length) { + return false; + } + if ( + stamps[i - 1].length > 0 && + stamps[i - 1].filter((s) => stamps[i].includes(s)).length === 0 + ) { + return false; + } + } + return stamps[0].includes(stamp); + }; +}; + +export const Menu = ({ document, type }: Readonly) => { + const goBack = useGoBack(); + + return ( + + goBack('/operations/documents')} /> + + +