From bbfc77984927d1f76f7e2f74d7caa9cc5e80f5f8 Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 31 Jan 2025 20:47:06 +0100 Subject: [PATCH 1/8] create template view --- .../bpmn-helper/customSchema.json | 15 +- src/helper-modules/bpmn-helper/index.d.ts | 139 ++++++------------ .../bpmn-helper/src/getters.d.ts | 18 ++- src/helper-modules/bpmn-helper/src/getters.js | 43 +++++- .../bpmn-helper/src/setters.d.ts | 20 +++ src/helper-modules/bpmn-helper/src/setters.js | 26 ++++ .../(dashboard)/[environmentId]/layout.tsx | 2 +- .../[environmentId]/templates/_loading.tsx | 14 ++ .../[folderId]/not-logged-in-fallback.tsx | 20 +++ .../templates/folder/[folderId]/page.tsx | 85 +++++++++++ .../[environmentId]/templates/page.tsx | 3 + .../components/process-creation-button.tsx | 24 ++- .../components/process-modal.tsx | 75 ++++++++-- .../components/processes/index.tsx | 76 +++++++++- .../lib/data/db/folders.ts | 13 +- .../lib/data/db/process.ts | 110 +++++++++----- .../lib/data/process-schema.ts | 4 +- .../lib/data/processes.tsx | 20 ++- .../lib/helpers/processHelpers.ts | 2 + src/management-system-v2/lib/schema.tsx | 22 +++ src/management-system-v2/next.config.js | 1 + .../20250130133848_template/migration.sql | 50 +++++++ .../prisma/migrations/migration_lock.toml | 2 +- src/management-system-v2/prisma/schema.prisma | 46 +++++- 24 files changed, 661 insertions(+), 169 deletions(-) create mode 100644 src/management-system-v2/app/(dashboard)/[environmentId]/templates/_loading.tsx create mode 100644 src/management-system-v2/app/(dashboard)/[environmentId]/templates/folder/[folderId]/not-logged-in-fallback.tsx create mode 100644 src/management-system-v2/app/(dashboard)/[environmentId]/templates/folder/[folderId]/page.tsx create mode 100644 src/management-system-v2/app/(dashboard)/[environmentId]/templates/page.tsx create mode 100644 src/management-system-v2/prisma/migrations/20250130133848_template/migration.sql diff --git a/src/helper-modules/bpmn-helper/customSchema.json b/src/helper-modules/bpmn-helper/customSchema.json index 7f47f7509..9fd67ecaa 100644 --- a/src/helper-modules/bpmn-helper/customSchema.json +++ b/src/helper-modules/bpmn-helper/customSchema.json @@ -327,11 +327,22 @@ ] }, { - "name": "templateId", + "name": "basedOnTemplateId", "extends": ["bpmn:Definitions"], "properties": [ { - "name": "templateId", + "name": "basedOnTemplateId", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "basedOnTemplateVersion", + "extends": ["bpmn:Definitions"], + "properties": [ + { + "name": "basedOnTemplateVersion", "isAttr": true, "type": "String" } diff --git a/src/helper-modules/bpmn-helper/index.d.ts b/src/helper-modules/bpmn-helper/index.d.ts index 453bf272b..2f408041c 100644 --- a/src/helper-modules/bpmn-helper/index.d.ts +++ b/src/helper-modules/bpmn-helper/index.d.ts @@ -147,98 +147,55 @@ declare const _exports: { processId: string; versionId: string; }; - }>; - getStartEvents(bpmn: string | object): Promise; - getAllBpmnFlowElements(bpmn: string | object): Promise; - getAllBpmnFlowNodeIds(bpmn: string | object): Promise; - getAllBpmnFlowElementIds(bpmn: string | object): Promise; - getChildrenFlowElements(bpmn: string | object, elementId: any): Promise; - getElementMachineMapping(bpmn: string | object): Promise<{ - [flowNodeId: string]: { - machineAddress?: string; - machineId?: string; + getMetaData(bpmn: string | object, elId: string): Promise<{ + [key: string]: any; + }>; + getMilestonesFromElement(element: object): { + id: string; + name: string; + description?: string; + }[]; + getMilestonesFromElementById(bpmn: string | object, elementId: string): { + id: string; + name: string; + description?: string; + }[]; + getResourcesFromElement(element: object): { + consumableMaterial: getters.ResourceInfos[]; + tool: getters.ResourceInfos[]; + inspectionInstrument: getters.ResourceInfos[]; }; - }>; - getTaskConstraintMapping(bpmn: string | object): Promise<{ - [bpmnElementIds: string]: { - hardConstraints: any[]; - softConstraints: any[]; + getLocationsFromElement(element: object): { + company: getters.CompanyInfos[]; + factory: getters.FactoryInfos[]; + building: getters.BuildingInfos[]; + area: getters.AreaInfos[]; + workingPlace: getters.WorkingPlaceInfos[]; }; - }>; - getIdentifyingInfos(bpmn: string | object): Promise<{ - id: string; - originalId?: string; - processIds: string[]; - name: string; - description: string; - }>; - getRootFromElement(businessObject: object): object; - getMetaDataFromElement(element: object): { - [key: string]: any; - }; - getMetaData( - bpmn: string | object, - elId: string, - ): Promise<{ - [key: string]: any; - }>; - getMilestonesFromElement(element: object): { - id: string; - name: string; - description?: string; - }[]; - getMilestonesFromElementById( - bpmn: string | object, - elementId: string, - ): { - id: string; - name: string; - description?: string; - }[]; - getResourcesFromElement(element: object): { - consumableMaterial: getters.ResourceInfos[]; - tool: getters.ResourceInfos[]; - inspectionInstrument: getters.ResourceInfos[]; - }; - getLocationsFromElement(element: object): { - company: getters.CompanyInfos[]; - factory: getters.FactoryInfos[]; - building: getters.BuildingInfos[]; - area: getters.AreaInfos[]; - workingPlace: getters.WorkingPlaceInfos[]; - }; - getPerformersFromElement(element: object): any[]; - getPerformersFromElementById(bpmn: string | object, elementId: string): any[]; - parseISODuration(isoDuration: string): { - years: number; - months: number; - days: number; - hours: number; - minutes: number; - seconds: number; - }; - convertISODurationToMiliseconds(isoDuration: string): number; - ensureCorrectProceedNamespace(xml: string): string; - toBpmnObject(xml: string, typename?: string): Promise; - toBpmnXml(obj: any): Promise; - deepCopyElementById(bpmn: string | object, elemId: any): Promise; - getChildren(travObj: object): any[]; - getElementsByTagName(travObj: object, tagname: string): any[]; - getAllElements(travObj: object): any[]; - getElementById(travObj: object, id: string): object; - getElementDI(element: object, definitions?: object): any; - manipulateElementById( - bpmn: string | object, - id: string, - manipFunc: util.manipulationFunction, - ): Promise; - manipulateElementsByTagName( - bpmn: string | object, - tagName: string, - manipFunc: util.manipulationFunction, - ): Promise; - moddle: any; + getPerformersFromElement(element: object): any[]; + getPerformersFromElementById(bpmn: string | object, elementId: string): any[]; + parseISODuration(isoDuration: string): { + years: number; + months: number; + days: number; + hours: number; + minutes: number; + seconds: number; + }; + convertISODurationToMiliseconds(isoDuration: string): number; + ensureCorrectProceedNamespace(xml: string): string; + toBpmnObject(xml: string, typename?: string): Promise; + toBpmnXml(obj: any): Promise; + deepCopyElementById(bpmn: string | object, elemId: any): Promise; + getChildren(travObj: object): any[]; + getElementsByTagName(travObj: object, tagname: string): any[]; + getAllElements(travObj: object): any[]; + getElementById(travObj: object, id: string): object; + getElementDI(element: object, definitions?: object): any; + manipulateElementById(bpmn: string | object, id: string, manipFunc: util.manipulationFunction): Promise; + manipulateElementsByTagName(bpmn: string | object, tagName: string, manipFunc: util.manipulationFunction): Promise; + moddle: any; }; export = _exports; -import getters = require('./src/getters.js'); -import util = require('./src/util.js'); +import getters = require("./src/getters.js"); +import util = require("./src/util.js"); diff --git a/src/helper-modules/bpmn-helper/src/getters.d.ts b/src/helper-modules/bpmn-helper/src/getters.d.ts index 501926d6c..eb5769a5a 100644 --- a/src/helper-modules/bpmn-helper/src/getters.d.ts +++ b/src/helper-modules/bpmn-helper/src/getters.d.ts @@ -11,6 +11,14 @@ export type DefinitionsInfos = { * - definitions original id */ originalId?: string; + /** + * - definitions template id + */ + basedOnTemplateId?: string; + /** + * - definitions template version + */ + basedOnTemplateVersion?: string; /** * - definitions name */ @@ -221,6 +229,8 @@ export function getDefinitionsName(bpmn: string | object): Promise } object containing the identifying information + * @returns { Promise.<{ id: string, originalId?: string, basedOnTemplateId: string, basedOnTemplateVersion: string, processIds: string[], name: string, description: string }> } object containing the identifying information */ export function getIdentifyingInfos(bpmn: string | object): Promise<{ id: string; originalId?: string; + basedOnTemplateId: string; + basedOnTemplateVersion: string; processIds: string[]; name: string; description: string; diff --git a/src/helper-modules/bpmn-helper/src/getters.js b/src/helper-modules/bpmn-helper/src/getters.js index 744acf01b..144ecff72 100644 --- a/src/helper-modules/bpmn-helper/src/getters.js +++ b/src/helper-modules/bpmn-helper/src/getters.js @@ -49,6 +49,30 @@ async function getOriginalDefinitionsId(bpmn) { return bpmnObj.originalId; } +/** + * Returns the value of the basedOnTemplateId attribute in the given process definition + * the basedOnTemplateId is the id the process has which was used as a template + * + * @param {(string|object)} bpmn - the process definition as XML string or BPMN-Moddle Object + * @returns {(Promise.)} The basedOnTemplateId stored in the definitions field of the given bpmn process + */ +async function getDefinitionsTemplateId(bpmn) { + const bpmnObj = typeof bpmn === 'string' ? await toBpmnObject(bpmn) : bpmn; + return bpmnObj.basedOnTemplateId; +} + +/** + * Returns the value of the basedOnTemplateVersion attribute in the given process definition + * the basedOnTemplateVersion is the id the process version has which was used as a template + * + * @param {(string|object)} bpmn - the process definition as XML string or BPMN-Moddle Object + * @returns {(Promise.)} The basedOnTemplateVersion stored in the definitions field of the given bpmn process + */ +async function getDefinitionsTemplateVersion(bpmn) { + const bpmnObj = typeof bpmn === 'string' ? await toBpmnObject(bpmn) : bpmn; + return bpmnObj.basedOnTemplateVersion; +} + /** * Returns the name of the given bpmn process definition * @@ -129,6 +153,8 @@ function getProcessDocumentationByObject(processObject) { * @type {object} * @property {string} id - definitions id * @property {string} [originalId] - definitions original id + * @property {string} [basedOnTemplateId] - definitions template id + * @property {string} [basedOnTemplateVersion] - definitions template version * @property {string} [name] - definitions name * @property {string} [exporter] - definitions exporter * @property {string} [exporterVersion] - definitions exporterVersion @@ -146,6 +172,8 @@ async function getDefinitionsInfos(bpmn) { return { id: await getDefinitionsId(bpmnObj), originalId: await getOriginalDefinitionsId(bpmnObj), + basedOnTemplateId: await getDefinitionsTemplateId(bpmnObj), + basedOnTemplateVersion: await getDefinitionsTemplateVersion(bpmnObj), name: await getDefinitionsName(bpmnObj), exporter: bpmnObj.exporter, exporterVersion: bpmnObj.exporterVersion, @@ -606,12 +634,13 @@ async function getProcessConstraints(bpmn) { * and its name and description for human identification * * @param {(string|object)} bpmn - the process definition as XML string or BPMN-Moddle Object - * @returns { Promise.<{ id: string, originalId?: string, processIds: string[], name: string, description: string }> } object containing the identifying information + * @returns { Promise.<{ id: string, originalId?: string, basedOnTemplateId: string, basedOnTemplateVersion: string, processIds: string[], name: string, description: string }> } object containing the identifying information */ async function getIdentifyingInfos(bpmn) { const bpmnObj = typeof bpmn === 'string' ? await toBpmnObject(bpmn) : bpmn; - const { id, originalId, name } = await getDefinitionsInfos(bpmnObj); + const { id, originalId, name, basedOnTemplateId, basedOnTemplateVersion } = + await getDefinitionsInfos(bpmnObj); const processes = getElementsByTagName(bpmnObj, 'bpmn:Process'); @@ -624,7 +653,15 @@ async function getIdentifyingInfos(bpmn) { description = ''; } - return { id, originalId, processIds, name, description }; + return { + id, + originalId, + basedOnTemplateId, + basedOnTemplateVersion, + processIds, + name, + description, + }; } /** diff --git a/src/helper-modules/bpmn-helper/src/setters.d.ts b/src/helper-modules/bpmn-helper/src/setters.d.ts index a0f083d83..90086de53 100644 --- a/src/helper-modules/bpmn-helper/src/setters.d.ts +++ b/src/helper-modules/bpmn-helper/src/setters.d.ts @@ -17,6 +17,26 @@ export function setDefinitionsId(bpmn: string | object, id: string): Promise} the modified BPMN process as bpmn-moddle object or XML string based on input */ export function setDefinitionsName(bpmn: string | object, name: string): Promise; +/** + * Sets basedOnTemplateId in definitions element to given id, + * @param {(string|object)} bpmn - the process definition as XML string or BPMN-Moddle Object + * @param {string} id - the id we want to set the definitions element to + * @returns {Promise} the modified BPMN process as bpmn-moddle object or XML string based on input + */ +export function setDefinitionsTemplateId( + bpmn: string | object, + id: string, +): Promise; +/** + * Sets basedOnTemplateId in definitions element to given id, + * @param {(string|object)} bpmn - the process definition as XML string or BPMN-Moddle Object + * @param {string} id - the id we want to set the definitions element to + * @returns {Promise} the modified BPMN process as bpmn-moddle object or XML string based on input + */ +export function setDefinitionsTemplateVersion( + bpmn: string | object, + id: string, +): Promise; /** * Will set a version in the definitions element * diff --git a/src/helper-modules/bpmn-helper/src/setters.js b/src/helper-modules/bpmn-helper/src/setters.js index 29eda2b73..1c553fccc 100644 --- a/src/helper-modules/bpmn-helper/src/setters.js +++ b/src/helper-modules/bpmn-helper/src/setters.js @@ -54,6 +54,30 @@ async function setDefinitionsName(bpmn, name) { }); } +/** + * Sets basedOnTemplateId in definitions element to given id, + * @param {(string|object)} bpmn - the process definition as XML string or BPMN-Moddle Object + * @param {string} id - the id we want to set the definitions element to + * @returns {Promise} the modified BPMN process as bpmn-moddle object or XML string based on input + */ +async function setDefinitionsTemplateId(bpmn, id) { + return await manipulateElementsByTagName(bpmn, 'bpmn:Definitions', (definitions) => { + definitions.basedOnTemplateId = id; + }); +} + +/** + * Sets basedOnTemplateId in definitions element to given id, + * @param {(string|object)} bpmn - the process definition as XML string or BPMN-Moddle Object + * @param {string} id - the id we want to set the definitions element to + * @returns {Promise} the modified BPMN process as bpmn-moddle object or XML string based on input + */ +async function setDefinitionsTemplateVersion(bpmn, id) { + return await manipulateElementsByTagName(bpmn, 'bpmn:Definitions', (definitions) => { + definitions.basedOnTemplateVersion = id; + }); +} + /** * Will set a version in the definitions element * @@ -578,6 +602,8 @@ async function removeColorFromAllElements(bpmn) { module.exports = { setDefinitionsId, setDefinitionsName, + setDefinitionsTemplateId, + setDefinitionsTemplateVersion, setDefinitionsVersionInformation, setProcessId, setTemplateId, diff --git a/src/management-system-v2/app/(dashboard)/[environmentId]/layout.tsx b/src/management-system-v2/app/(dashboard)/[environmentId]/layout.tsx index 768777ae6..ad4333d26 100644 --- a/src/management-system-v2/app/(dashboard)/[environmentId]/layout.tsx +++ b/src/management-system-v2/app/(dashboard)/[environmentId]/layout.tsx @@ -72,7 +72,7 @@ const DashboardLayout = async ({ ...children, { key: 'processes-templates', - label: Templates, + label: Templates, icon: , }, ]; diff --git a/src/management-system-v2/app/(dashboard)/[environmentId]/templates/_loading.tsx b/src/management-system-v2/app/(dashboard)/[environmentId]/templates/_loading.tsx new file mode 100644 index 000000000..381455e39 --- /dev/null +++ b/src/management-system-v2/app/(dashboard)/[environmentId]/templates/_loading.tsx @@ -0,0 +1,14 @@ +import Content from '@/components/content'; +import { Skeleton, Space } from 'antd'; + +const TemplatesSkeleton = () => { + return ( + + + + + + ); +}; + +export default TemplatesSkeleton; diff --git a/src/management-system-v2/app/(dashboard)/[environmentId]/templates/folder/[folderId]/not-logged-in-fallback.tsx b/src/management-system-v2/app/(dashboard)/[environmentId]/templates/folder/[folderId]/not-logged-in-fallback.tsx new file mode 100644 index 000000000..c7a2e12a2 --- /dev/null +++ b/src/management-system-v2/app/(dashboard)/[environmentId]/templates/folder/[folderId]/not-logged-in-fallback.tsx @@ -0,0 +1,20 @@ +'use client'; + +import { Button, Result } from 'antd'; +import { signIn } from 'next-auth/react'; +import { LoginOutlined } from '@ant-design/icons'; + +const NotLoggedInFallback = () => ( + } onClick={() => signIn()}> + Login + + } + /> +); + +export default NotLoggedInFallback; diff --git a/src/management-system-v2/app/(dashboard)/[environmentId]/templates/folder/[folderId]/page.tsx b/src/management-system-v2/app/(dashboard)/[environmentId]/templates/folder/[folderId]/page.tsx new file mode 100644 index 000000000..1707856c6 --- /dev/null +++ b/src/management-system-v2/app/(dashboard)/[environmentId]/templates/folder/[folderId]/page.tsx @@ -0,0 +1,85 @@ +import Processes from '@/components/processes'; +import Content from '@/components/content'; +import { Button, Space } from 'antd'; +import { getCurrentEnvironment } from '@/components/auth'; +// This is a workaround to enable the Server Actions in that file to return any +// client components. This is not possible with the current nextjs compiler +// otherwise. It might be possible in the future with turbopack without this +// import. +import '@/lib/data/processes'; +import { getUsersFavourites } from '@/lib/data/users'; +import { ProcessMetadata } from '@/lib/data/process-schema'; +import { Folder } from '@/lib/data/folder-schema'; +import Link from 'next/link'; +import { LeftOutlined } from '@ant-design/icons'; +import EllipsisBreadcrumb from '@/components/ellipsis-breadcrumb'; +import { ComponentProps } from 'react'; +import { spaceURL } from '@/lib/utils'; +import { getFolderById, getRootFolder, getFolderContents } from '@/lib/data/DTOs'; +export type ListItem = ProcessMetadata | (Folder & { type: 'folder' }); + +const TemplatesPage = async ({ + params, +}: { + params: { environmentId: string; folderId?: string }; +}) => { + const { ability, activeEnvironment } = await getCurrentEnvironment(params.environmentId); + + const favs = await getUsersFavourites(); + + const rootFolder = await getRootFolder(activeEnvironment.spaceId, ability); + + const folder = await getFolderById( + params.folderId ? decodeURIComponent(params.folderId) : rootFolder.id, + ); + + const folderContents = await getFolderContents(folder.id, ability); + + const templateProcessesAndFolders = folderContents.filter( + (content) => content.type === 'template' || content.type === 'folder', + ); + + const pathToFolder: ComponentProps['items'] = []; + let currentFolder: Folder | null = folder; + do { + pathToFolder.push({ + title: ( + + {currentFolder.parentId ? currentFolder.name : 'Templates'} + + ), + }); + currentFolder = currentFolder.parentId ? await getFolderById(currentFolder.parentId) : null; + } while (currentFolder); + pathToFolder.reverse(); + + return ( + <> + + {folder.parentId && ( + + + + )} + + + } + > + + + + + + ); +}; + +export default TemplatesPage; diff --git a/src/management-system-v2/app/(dashboard)/[environmentId]/templates/page.tsx b/src/management-system-v2/app/(dashboard)/[environmentId]/templates/page.tsx new file mode 100644 index 000000000..3d21c60dc --- /dev/null +++ b/src/management-system-v2/app/(dashboard)/[environmentId]/templates/page.tsx @@ -0,0 +1,3 @@ +import Page from './folder/[folderId]/page'; + +export default Page; diff --git a/src/management-system-v2/components/process-creation-button.tsx b/src/management-system-v2/components/process-creation-button.tsx index 09f2b6781..bc7f6764f 100644 --- a/src/management-system-v2/components/process-creation-button.tsx +++ b/src/management-system-v2/components/process-creation-button.tsx @@ -12,18 +12,26 @@ export const ProcessCreationModal: React.FC< Partial> & { open: boolean; setOpen: (open: boolean) => void; - customAction?: (values: { name: string; description: string }) => Promise; + templates?: any[]; + customAction?: (values: { + name: string; + description: string; + templateId?: string; + }) => Promise; + type?: 'process' | 'template'; } -> = ({ open, setOpen, customAction, ...props }) => { +> = ({ open, setOpen, customAction, templates = [], type = 'process', ...props }) => { const router = useRouter(); const environment = useEnvironment(); const folderId = useParams<{ folderId: string }>().folderId ?? ''; - const createNewProcess = async (values: { name: string; description: string }[]) => { + const createNewProcess = async ( + values: { name: string; description: string; templateId?: string }[], + ) => { // Invoke the custom handler otherwise use the default server action. const process = await (customAction?.(values[0]) ?? addProcesses( - values.map((value) => ({ ...value, folderId })), + values.map((value) => ({ ...value, folderId, type })), environment.spaceId, ).then((res) => (Array.isArray(res) ? res[0] : res))); @@ -57,20 +65,24 @@ export const ProcessCreationModal: React.FC< return ( setOpen(false)} onSubmit={createNewProcess} + type={type} /> ); }; type ProcessCreationButtonProps = ButtonProps & { + templates?: any[]; customAction?: (values: { name: string; description: string }) => Promise; wrapperElement?: ReactNode; defaultOpen?: boolean; modalProps?: ModalProps; + type?: 'process' | 'template'; }; /** @@ -79,9 +91,11 @@ type ProcessCreationButtonProps = ButtonProps & { */ const ProcessCreationButton: React.FC = ({ wrapperElement, + templates = [], customAction, defaultOpen = false, modalProps, + type, ...props }) => { const [isProcessModalOpen, setIsProcessModalOpen] = useState(defaultOpen); @@ -94,10 +108,12 @@ const ProcessCreationButton: React.FC = ({