From 56878c977c5bafbeee970eb705e8e3a69b512848 Mon Sep 17 00:00:00 2001 From: Sosthene Date: Fri, 12 Sep 2025 12:59:43 +0200 Subject: [PATCH] Add helper function to secure handling of processId in the code --- .../DefaultDeedTypeDashboard/index.tsx | 32 +++++++----- .../DeedTypes/DeedTypesCreate/index.tsx | 3 +- .../Layouts/DeedTypes/DeedTypesEdit/index.tsx | 5 +- .../DeedTypes/DeedTypesInformations/index.tsx | 32 +++++++----- .../DocumentTypesCreate/index.tsx | 3 +- .../NoClientView/AddClientSection/index.tsx | 3 +- .../Layouts/Folder/SendDocuments/index.tsx | 10 ++-- src/front/Components/Layouts/Folder/index.tsx | 3 +- src/front/Utils/ProcessIdUtils.ts | 52 +++++++++++++++++++ 9 files changed, 107 insertions(+), 36 deletions(-) create mode 100644 src/front/Utils/ProcessIdUtils.ts diff --git a/src/front/Components/LayoutTemplates/DefaultDeedTypeDashboard/index.tsx b/src/front/Components/LayoutTemplates/DefaultDeedTypeDashboard/index.tsx index f8440245..375ac2f9 100644 --- a/src/front/Components/LayoutTemplates/DefaultDeedTypeDashboard/index.tsx +++ b/src/front/Components/LayoutTemplates/DefaultDeedTypeDashboard/index.tsx @@ -7,6 +7,7 @@ import DefaultDashboardWithList, { IPropsDashboardWithList } from "../DefaultDas import { DeedType } from "le-coffre-resources/dist/Notary"; import DeedTypeService from "src/common/Api/LeCoffreApi/sdk/DeedTypeService"; +import { idAsUrl } from "@Front/Utils/ProcessIdUtils"; type IProps = IPropsDashboardWithList; @@ -17,13 +18,13 @@ export default function DefaultDeedTypeDashboard(props: IProps) { const [deedTypes, setDeedTypes] = React.useState(null); useEffect(() => { - DeedTypeService.getDeedTypes((processes: any[]) => { - if (processes.length > 0) { - let deedTypes = processes.map((process: any) => process.processData); - - // FilterBy archived_at = null or not defined - deedTypes = deedTypes.filter((deedType: any) => !deedType.archived_at); - + DeedTypeService.getDeedTypes((processes: Record) => { + const deedTypes = Object.entries(processes).map(([processId, processData]) => ({ + ...processData, + processId: processId + })); + if (deedTypes.length > 0) { + deedTypes.filter((deedType: any) => !deedType.archived_at); // OrderBy name asc deedTypes.sort((a: any, b: any) => a.name.localeCompare(b.name)); @@ -35,7 +36,9 @@ export default function DefaultDeedTypeDashboard(props: IProps) { }, []); const onSelectedBlock = (block: IBlock) => { - router.push(Module.getInstance().get().modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", block.id)); + // Remove ':0' suffix from processId for URL navigation + const urlId = idAsUrl(block.id as string); + router.push(Module.getInstance().get().modules.pages.DeedTypes.pages.DeedTypesInformations.props.path.replace("[uid]", urlId)); }; return ( @@ -44,11 +47,14 @@ export default function DefaultDeedTypeDashboard(props: IProps) { onSelectedBlock={onSelectedBlock} blocks={ deedTypes - ? deedTypes.map((deedTypes) => ({ - id: deedTypes.uid!, - primaryText: deedTypes.name, - isActive: deedTypes.uid === deedTypeUid, - })) + ? deedTypes.map((deedType: any) => { + const urlId = idAsUrl(deedType.processId); + return { + id: deedType.processId, // Keep full processId for internal use + primaryText: deedType.name, + isActive: urlId === deedTypeUid, // Compare without ':0' suffix + }; + }) : [] } bottomButton={{ diff --git a/src/front/Components/Layouts/DeedTypes/DeedTypesCreate/index.tsx b/src/front/Components/Layouts/DeedTypes/DeedTypesCreate/index.tsx index 85f0ca62..524874d3 100644 --- a/src/front/Components/Layouts/DeedTypes/DeedTypesCreate/index.tsx +++ b/src/front/Components/Layouts/DeedTypes/DeedTypesCreate/index.tsx @@ -19,6 +19,7 @@ import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import DeedTypeService from "src/common/Api/LeCoffreApi/sdk/DeedTypeService"; import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants"; import Auth from "@Front/Api/Auth/IdNot"; +import { idAsUrl } from "@Front/Utils/ProcessIdUtils"; type IProps = {}; export default function DeedTypesCreate(props: IProps) { @@ -80,7 +81,7 @@ export default function DeedTypesCreate(props: IProps) { }); const deedTypeUid = processCreated.processId; // Remove ':0' suffix from processId for URL navigation - const urlId = deedTypeUid.replace(/:0$/, ''); + const urlId = idAsUrl(deedTypeUid); router.push( Module.getInstance() .get() diff --git a/src/front/Components/Layouts/DeedTypes/DeedTypesEdit/index.tsx b/src/front/Components/Layouts/DeedTypes/DeedTypesEdit/index.tsx index c7c680b0..c3ae8cec 100644 --- a/src/front/Components/Layouts/DeedTypes/DeedTypesEdit/index.tsx +++ b/src/front/Components/Layouts/DeedTypes/DeedTypesEdit/index.tsx @@ -17,6 +17,7 @@ import classes from "./classes.module.scss"; import DeedTypeService from "src/common/Api/LeCoffreApi/sdk/DeedTypeService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import MessageBus from "src/sdk/MessageBus"; +import { idAsProcessId } from "@Front/Utils/ProcessIdUtils"; export default function DeedTypesEdit() { const router = useRouter(); @@ -33,7 +34,7 @@ export default function DeedTypesEdit() { if (!deedTypeUid) return; LoaderService.getInstance().show(); // deedTypeUid comes from URL without ':0' suffix, add it back for API calls - const processId = (deedTypeUid as string) + ':0'; + const processId = idAsProcessId(deedTypeUid as string); MessageBus.getInstance().getProcessData(processId).then((processData: any) => { if (processData) { setDeedTypeSelected(processData); @@ -66,7 +67,7 @@ export default function DeedTypesEdit() { try { LoaderService.getInstance().show(); // deedTypeUid comes from URL without ':0' suffix, add it back for API calls - const processId = (deedTypeUid as string) + ':0'; + const processId = idAsProcessId(deedTypeUid as string); const process = await MessageBus.getInstance().getProcessData(processId); if (process) { diff --git a/src/front/Components/Layouts/DeedTypes/DeedTypesInformations/index.tsx b/src/front/Components/Layouts/DeedTypes/DeedTypesInformations/index.tsx index 7a472e84..d630a727 100644 --- a/src/front/Components/Layouts/DeedTypes/DeedTypesInformations/index.tsx +++ b/src/front/Components/Layouts/DeedTypes/DeedTypesInformations/index.tsx @@ -22,6 +22,7 @@ import DeedTypeService from "src/common/Api/LeCoffreApi/sdk/DeedTypeService"; import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; import MessageBus from "src/sdk/MessageBus"; +import { idAsProcessId } from "@Front/Utils/ProcessIdUtils"; type IProps = {}; export default function DeedTypesInformations(props: IProps) { @@ -51,12 +52,14 @@ export default function DeedTypesInformations(props: IProps) { setIsSaveModalOpened(false); }, []); - const deleteDeedType = useCallback(async () => { + const deleteDeedType = async () => { LoaderService.getInstance().show(); - // deedTypeUid comes from URL without ':0' suffix, add it back for API calls - const processId = (deedTypeUid as string) + ':0'; - MessageBus.getInstance().getProcessData(processId).then(async (process: any) => { - if (process) { + try { + // deedTypeUid comes from URL without ':0' suffix, add it back for API calls + const processId = idAsProcessId(deedTypeUid as string); + const process = await MessageBus.getInstance().getProcessData(processId); + + if (process && process[processId]) { // New data const newData: any = { isDeleted: 'true', @@ -64,19 +67,22 @@ export default function DeedTypesInformations(props: IProps) { }; // Merge process data with new data & update process - process.processData.isDeleted = newData.isDeleted; - process.processData.archived_at = newData.archived_at; - await DeedTypeService.updateDeedType(process, newData); + process[processId].isDeleted = newData.isDeleted; + process[processId].archived_at = newData.archived_at; + await DeedTypeService.updateDeedType(processId, newData); router.push( Module.getInstance() .get() .modules.pages.DeedTypes.props.path ); - LoaderService.getInstance().hide(); } - }); - }, [deedTypeUid, router]); + } catch (error) { + console.error('Error deleting deed type:', error); + } finally { + LoaderService.getInstance().hide(); + } + }; useEffect(() => { async function getDeedType() { @@ -84,7 +90,7 @@ export default function DeedTypesInformations(props: IProps) { setSelectedDocuments([]); // deedTypeUid comes from URL without ':0' suffix, add it back for API calls - const processId = (deedTypeUid as string) + ':0'; + const processId = idAsProcessId(deedTypeUid as string); MessageBus.getInstance().getProcessData(processId).then((process: any) => { if (process) { console.log('[DeedTypesInformations] process', process); @@ -131,7 +137,7 @@ export default function DeedTypesInformations(props: IProps) { const saveDocumentTypes = useCallback(async () => { LoaderService.getInstance().show(); // deedTypeUid comes from URL without ':0' suffix, add it back for API calls - const processId = (deedTypeUid as string) + ':0'; + const processId = idAsProcessId(deedTypeUid as string); console.log('[DeedTypesInformations] processId', processId); const deedType = (await MessageBus.getInstance().getProcessData(processId))[processId]; if (deedType) { diff --git a/src/front/Components/Layouts/DocumentTypes/DocumentTypesCreate/index.tsx b/src/front/Components/Layouts/DocumentTypes/DocumentTypesCreate/index.tsx index 81b65a47..4b670663 100644 --- a/src/front/Components/Layouts/DocumentTypes/DocumentTypesCreate/index.tsx +++ b/src/front/Components/Layouts/DocumentTypes/DocumentTypesCreate/index.tsx @@ -16,6 +16,7 @@ import classes from "./classes.module.scss"; import DocumentTypeService from "src/common/Api/LeCoffreApi/sdk/DocumentTypeService"; import LoaderService from "src/common/Api/LeCoffreApi/sdk/Loader/LoaderService"; +import { idAsUrl } from "@Front/Utils/ProcessIdUtils"; import UserStore from "@Front/Stores/UserStore"; import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants"; @@ -68,7 +69,7 @@ export default function DocumentTypesCreate(props: IProps) { title: "Succès !", description: "Type de document créé avec succès" }); - const documentTypeUid = processCreated.processId.split(':')[0]; + const documentTypeUid = idAsUrl(processCreated.processId); if (!documentTypeUid) { console.error("DocumentTypesCreate: documentTypeUid is undefined - processCreated.processId is missing"); return; diff --git a/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/AddClientSection/index.tsx b/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/AddClientSection/index.tsx index 09421703..6188f715 100644 --- a/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/AddClientSection/index.tsx +++ b/src/front/Components/Layouts/Folder/FolderInformation/NoClientView/AddClientSection/index.tsx @@ -1,6 +1,7 @@ import Button, { EButtonSize, EButtonstyletype, EButtonVariant } from "@Front/Components/DesignSystem/Button"; import EmptyAlert from "@Front/Components/DesignSystem/EmptyAlert"; import Module from "@Front/Config/Module"; +import { idAsUrl } from "@Front/Utils/ProcessIdUtils"; import { UserPlusIcon } from "@heroicons/react/24/outline"; import Link from "next/link"; import React, { useMemo } from "react"; @@ -14,7 +15,7 @@ export default function AddClientSection(props: IProps) { const addClientPath = useMemo(() => { if (!folderUid) return ""; - return Module.getInstance().get().modules.pages.Folder.pages.AddClient.props.path.replace("[folderUid]", folderUid); + return Module.getInstance().get().modules.pages.Folder.pages.AddClient.props.path.replace("[folderUid]", idAsUrl(folderUid)); }, [folderUid]); return ( diff --git a/src/front/Components/Layouts/Folder/SendDocuments/index.tsx b/src/front/Components/Layouts/Folder/SendDocuments/index.tsx index d3cae195..f0842fee 100644 --- a/src/front/Components/Layouts/Folder/SendDocuments/index.tsx +++ b/src/front/Components/Layouts/Folder/SendDocuments/index.tsx @@ -24,6 +24,8 @@ import DocumentService from "src/common/Api/LeCoffreApi/sdk/DocumentService"; import FileService from "src/common/Api/LeCoffreApi/sdk/FileService"; import CustomerService from "src/common/Api/LeCoffreApi/sdk/CustomerService"; import { DEFAULT_VALIDATOR_ID } from "@Front/Config/AppConstants"; +import { idAsProcessId } from "@Front/Utils/ProcessIdUtils"; +import MessageBus from "src/sdk/MessageBus"; enum EClientSelection { ALL_CLIENTS = "all_clients", @@ -70,7 +72,7 @@ export default function SendDocuments() { const customer: any = await new Promise((resolve: (customer: any) => void) => { CustomerService.getCustomerByUid(selectedClient as string).then((process: any) => { if (process) { - const customer: any = process.processData; + const customer: any = process; resolve(customer); } }); @@ -100,7 +102,7 @@ export default function SendDocuments() { }; FileService.createFile(fileData, DEFAULT_VALIDATOR_ID).then((processCreated: any) => { - const fileUid: string = processCreated.processData.uid; + const fileUid: string = processCreated.uid; const documentData: any = { folder: { @@ -173,9 +175,9 @@ export default function SendDocuments() { const fetchFolder = useCallback(async () => { LoaderService.getInstance().show(); - FolderService.getFolderByUid(folderUid as string).then((process: any) => { + MessageBus.getInstance().getProcessData(idAsProcessId(folderUid as string)).then((process: any) => { if (process) { - const folder: any = process.processData; + const folder: any = process; setFolder(folder); LoaderService.getInstance().hide(); } diff --git a/src/front/Components/Layouts/Folder/index.tsx b/src/front/Components/Layouts/Folder/index.tsx index 0879e8f1..0c1ddbb2 100644 --- a/src/front/Components/Layouts/Folder/index.tsx +++ b/src/front/Components/Layouts/Folder/index.tsx @@ -16,6 +16,7 @@ import { useEffect, useState } from "react"; import classes from "./classes.module.scss"; import FolderService from "src/common/Api/LeCoffreApi/sdk/FolderService"; +import { idAsUrl } from "@Front/Utils/ProcessIdUtils"; export default function Folder() { const [_isArchivedModalOpen, _setIsArchivedModalOpen] = useState(true); @@ -39,7 +40,7 @@ export default function Folder() { folders = folders.sort((a: any, b: any) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); if (folders.length > 0) { - const folderUid = folders[0]?.processId; + const folderUid = idAsUrl(folders[0]?.processId); if (!folderUid) { return; diff --git a/src/front/Utils/ProcessIdUtils.ts b/src/front/Utils/ProcessIdUtils.ts new file mode 100644 index 00000000..612d8798 --- /dev/null +++ b/src/front/Utils/ProcessIdUtils.ts @@ -0,0 +1,52 @@ +/** + * Utility functions to safely handle conversion between URL-friendly IDs and process IDs. + * + * Process IDs in the system use a ":0" suffix for API calls, but URLs use the clean ID without the suffix. + * These functions provide safe conversion between the two formats to avoid error-prone string manipulation. + */ + +/** + * Converts a process ID to URL-friendly format by removing the ":0" suffix if present. + * Safe to call multiple times - won't remove ":0" from IDs that don't end with it. + * + * @param processId - The process ID that may or may not have ":0" suffix + * @returns The ID without ":0" suffix, suitable for use in URLs. Returns empty string for null/undefined. + * + * @example + * idAsUrl("abc123:0") // returns "abc123" + * idAsUrl("abc123") // returns "abc123" (no change) + * idAsUrl("abc:123:0") // returns "abc:123" (only removes trailing ":0") + * idAsUrl(null) // returns "" + * idAsUrl(undefined) // returns "" + */ +export function idAsUrl(processId: string | null | undefined): string { + if (!processId || typeof processId !== 'string') { + return ''; + } + + // Only remove ":0" if it's at the very end of the string + return processId.replace(/:0$/, ''); +} + +/** + * Converts a URL-friendly ID to process ID format by adding ":0" suffix if not already present. + * Safe to call multiple times - won't add ":0" if it's already there. + * + * @param urlId - The URL-friendly ID that may or may not have ":0" suffix + * @returns The ID with ":0" suffix, suitable for use in API calls. Returns ":0" for null/undefined. + * + * @example + * idAsProcessId("abc123") // returns "abc123:0" + * idAsProcessId("abc123:0") // returns "abc123:0" (no change) + * idAsProcessId("abc:123") // returns "abc:123:0" + * idAsProcessId(null) // returns "" + * idAsProcessId(undefined) // returns "" + */ +export function idAsProcessId(urlId: string | null | undefined): string { + if (!urlId || typeof urlId !== 'string') { + return ''; + } + + // Only add ":0" if it's not already at the end + return urlId.endsWith(':0') ? urlId : `${urlId}:0`; +}