Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi Process Export (New MS) #115

Merged
merged 28 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
895ae9e
select box styling, table categories button added, functionality tba
winniel24 Aug 23, 2023
ed9f3df
Merge branch 'main' into modeler-toolbar-zustand-exp
MaxiLein Aug 23, 2023
400e3dd
select bar layout wip
winniel24 Aug 23, 2023
51e0738
Merge branch 'modeler-toolbar-zustand-exp' of https://github.com/PROC…
MaxiLein Aug 23, 2023
b81caa1
Merge branch 'main' into modeler-toolbar-zustand-exp
MaxiLein Aug 24, 2023
a3c477d
added first version of a breadcrumb
MaxiLein Aug 24, 2023
b1a277c
styling wip
winniel24 Aug 24, 2023
f4ca92f
Breadcrumb version and process select
MaxiLein Aug 24, 2023
09765e6
fuzzy search, dropdown for columns, deselect for multirow
MaxiLein Aug 24, 2023
c383cb7
add comment for fuzzy search
MaxiLein Aug 28, 2023
e645f7b
Merge branch 'main' into modeler-toolbar-zustand-exp
MaxiLein Aug 28, 2023
9e00506
changed colour breadcrumb, added latest changes to modeler (jj), Menu…
MaxiLein Aug 28, 2023
ee44b66
Merge branch 'main' into modeler-toolbar-zustand-exp
MaxiLein Aug 29, 2023
232c295
Merge branch 'main' into modeler-toolbar-zustand-exp
MaxiLein Aug 30, 2023
4051761
Adjusted Modeler height, added Preview-Viewer Co-authored-by: winniel…
MaxiLein Aug 31, 2023
b940034
Made previewer height adjustable
MaxiLein Sep 1, 2023
3b8515f
Merge branch 'main' into modeler-toolbar-zustand-exp
MaxiLein Sep 1, 2023
0a3f6a7
Added export of a single process as bpmn, svg or pdf from the process…
jjoderis Sep 7, 2023
698f1d3
Added the process export modal to the processes view in addition to t…
jjoderis Sep 7, 2023
697da26
Merge branch 'main' of github.com:PROCEED-Labs/proceed into ms2/proce…
jjoderis Sep 7, 2023
37fb59f
Merge branch 'main' of github.com:PROCEED-Labs/proceed into ms2/proce…
jjoderis Sep 8, 2023
3bc7865
Ran prettier
jjoderis Sep 8, 2023
3c27e63
Merge branch 'main' of github.com:PROCEED-Labs/proceed into ms2/proce…
jjoderis Oct 3, 2023
0c42950
Removed unnecessary code
jjoderis Oct 3, 2023
c98738d
Merge branch 'main' of github.com:PROCEED-Labs/proceed into ms2/proce…
jjoderis Oct 5, 2023
1801e6f
Added jszip as a dependency
jjoderis Oct 5, 2023
073fd06
Added option to export multiple processes at once
jjoderis Oct 6, 2023
4770307
Merge branch 'main' of github.com:PROCEED-Labs/proceed into ms2/multi…
jjoderis Oct 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 8 additions & 15 deletions src/management-system-v2/components/modeler-toolbar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';

import type ElementRegistry from 'diagram-js/lib/core/ElementRegistry';

Expand All @@ -20,13 +20,12 @@ import { SvgXML, SvgShare } from '@/components/svg';
import PropertiesPanel from './properties-panel';

import useModelerStateStore from '@/lib/use-modeler-state-store';
import { useParams } from 'next/navigation';
import { useParams, useSearchParams } from 'next/navigation';

import ProcessExportModal from './process-export';

import { createNewProcessVersion } from '@/lib/helpers';
import VersionCreationButton from './version-creation-button';
import { useGetAsset } from '@/lib/fetch-data';

type ModelerToolbarProps = {
onOpenXmlEditor: () => void;
Expand All @@ -45,14 +44,6 @@ const ModelerToolbar: React.FC<ModelerToolbarProps> = ({ onOpenXmlEditor }) => {
// const [index, setIndex] = useState(0);
const { processId } = useParams();

const {
isSuccess,
data: processData,
refetch: refetchProcess,
} = useGetAsset('/process/{definitionId}', {
params: { path: { definitionId: processId as string } },
});

let selectedElement;

if (modeler) {
Expand All @@ -75,7 +66,6 @@ const ModelerToolbar: React.FC<ModelerToolbarProps> = ({ onOpenXmlEditor }) => {
values.versionName,
values.versionDescription,
);
refetchProcess();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This refetchProcess() was used to show the newly created version immediately in the list of existing versions without having to reload the page. Does this still work if this was removed?

}
};
const handlePropertiesPanelToggle = () => {
Expand All @@ -86,7 +76,7 @@ const ModelerToolbar: React.FC<ModelerToolbarProps> = ({ onOpenXmlEditor }) => {
setShowProcessExportModal(!showProcessExportModal);
};

const selectedVersion = useModelerStateStore((state) => state.selectedVersion);
const selectedVersion = useSearchParams().get('version');

return (
<>
Expand Down Expand Up @@ -140,9 +130,12 @@ const ModelerToolbar: React.FC<ModelerToolbarProps> = ({ onOpenXmlEditor }) => {
<PropertiesPanel selectedElement={selectedElement} setOpen={setShowPropertiesPanel} />
)} */}
<ProcessExportModal
processId={showProcessExportModal ? (processId as string) : undefined}
processes={
showProcessExportModal
? [{ definitionId: processId as string, processVersion: selectedVersion || undefined }]
: []
}
onClose={() => setShowProcessExportModal(false)}
processVersion={selectedVersion || undefined}
/>
</>
);
Expand Down
27 changes: 5 additions & 22 deletions src/management-system-v2/components/process-export.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { useState } from 'react';
import { Modal, Checkbox } from 'antd';
import type { CheckboxValueType } from 'antd/es/checkbox/Group';

import { exportBpmn, exportPDF, exportSVG } from '@/lib/process-export';
import { exportProcesses, exportType } from '@/lib/process-export';

const exportTypeOptions = [
{ label: 'BPMN', value: 'bpmn' },
Expand All @@ -12,16 +12,11 @@ const exportTypeOptions = [
];

type ProcessExportModalProps = {
processId?: string; // the id of the process to export; also used to decide if the modal should be opened
processes: { definitionId: string; processVersion?: number | string }[]; // the processes to export; also used to decide if the modal should be opened
onClose: () => void;
processVersion?: number | string;
};

const ProcessExportModal: React.FC<ProcessExportModalProps> = ({
processId,
onClose,
processVersion,
}) => {
const ProcessExportModal: React.FC<ProcessExportModalProps> = ({ processes = [], onClose }) => {
const [selectedTypes, setSelectedTypes] = useState<CheckboxValueType[]>([]);

const handleTypeSelectionChange = (checkedValues: CheckboxValueType[]) => {
Expand All @@ -31,19 +26,7 @@ const ProcessExportModal: React.FC<ProcessExportModalProps> = ({
};

const handleProcessExport = async () => {
switch (selectedTypes[0]) {
case 'bpmn':
await exportBpmn(processId!, processVersion);
break;
case 'pdf':
await exportPDF(processId!, processVersion);
break;
case 'svg':
await exportSVG(processId!, processVersion);
break;
default:
throw 'Unexpected value for process export!';
}
await exportProcesses(processes, selectedTypes[0] as exportType);

onClose();
};
Expand All @@ -52,7 +35,7 @@ const ProcessExportModal: React.FC<ProcessExportModalProps> = ({
<>
<Modal
title="Export selected process"
open={!!processId}
open={!!processes.length}
onOk={handleProcessExport}
onCancel={onClose}
centered
Expand Down
4 changes: 2 additions & 2 deletions src/management-system-v2/components/process-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type ProcessListProps = PropsWithChildren<{
selection: Key[];
setSelection: Dispatch<SetStateAction<Key[]>>;
isLoading?: boolean;
onExportProcess: Dispatch<SetStateAction<string | undefined>>;
onExportProcess: Dispatch<SetStateAction<string[]>>;
}>;

const ColumnHeader = [
Expand Down Expand Up @@ -113,7 +113,7 @@ const ProcessList: FC<ProcessListProps> = ({
<Tooltip placement="top" title={'Export'}>
<ExportOutlined
onClick={() => {
onExportProcess(record.definitionId);
onExportProcess([record.definitionId]);
}}
/>
</Tooltip>
Expand Down
10 changes: 5 additions & 5 deletions src/management-system-v2/components/processes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const Processes: FC = () => {

const [iconView, setIconView] = useState(prefs['icon-view-in-process-list']);

const [exportProcessId, setExportProcessId] = useState<string | undefined>();
const [exportProcessIds, setExportProcessIds] = useState<string[]>([]);

const actionBar = (
<>
Expand All @@ -72,7 +72,7 @@ const Processes: FC = () => {
<Tooltip placement="top" title={'Export'}>
<ExportOutlined
onClick={() => {
setExportProcessId(selectedRowKeys[0].toString());
setExportProcessIds(selectedRowKeys as string[]);
}}
/>
</Tooltip>
Expand Down Expand Up @@ -180,16 +180,16 @@ const Processes: FC = () => {
selection={selectedRowKeys}
setSelection={setSelectedRowKeys}
isLoading={isLoading}
onExportProcess={setExportProcessId}
onExportProcess={setExportProcessIds}
/>
)}
</div>
{/* Meta Data Panel */}
<MetaData data={filteredData} selection={selectedRowKeys} triggerRerender={rerenderLists} />
</div>
<ProcessExportModal
processId={exportProcessId}
onClose={() => setExportProcessId(undefined)}
processes={exportProcessIds.map((definitionId) => ({ definitionId }))}
onClose={() => setExportProcessIds([])}
/>
</>
);
Expand Down
80 changes: 71 additions & 9 deletions src/management-system-v2/lib/process-export.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { v4 } from 'uuid';
import { jsPDF } from 'jspdf';
import jsZip from 'jszip';
import 'svg2pdf.js';

import { fetchProcessVersionBpmn, fetchProcess } from './process-queries';
Expand Down Expand Up @@ -28,12 +29,59 @@ async function getProcessData(processId: string, processVersion?: string | numbe
return data;
}

export async function exportBpmn(processId: string, processVersion?: string | number) {
export type exportType = 'bpmn' | 'svg' | 'pdf';

export async function exportProcesses(
processes: { definitionId: string; processVersion?: number | string }[],
type: exportType,
) {
if (processes.length === 1) {
// generate the file and download it
switch (type) {
case 'bpmn':
await exportBpmn(exportFile, processes[0].definitionId, processes[0].processVersion);
break;
case 'svg':
await exportSVG(exportFile, processes[0].definitionId, processes[0].processVersion);
break;
case 'pdf':
await exportPDF(exportFile, processes[0].definitionId, processes[0].processVersion);
break;
}
} else if (processes.length > 1) {
const zip = new jsZip();

for (const { definitionId, processVersion } of processes) {
// add the file to the zip archive
switch (type) {
case 'bpmn':
await exportBpmn(zip.file.bind(zip), definitionId, processVersion);
break;
case 'svg':
await exportSVG(zip.file.bind(zip), definitionId, processVersion);
break;
case 'pdf':
await exportPDF(zip.file.bind(zip), definitionId, processVersion);
break;
}
}
// download the zip archive to the users device
exportFile('PROCEED_Multiple-Processes_bpmn.zip', await zip.generateAsync({ type: 'blob' }));
} else {
throw new Error('Tried exporting without specifying the processes to export!');
}
}

export async function exportBpmn(
dataHandler: (fileName: string, data: Blob) => void,
processId: string,
processVersion?: string | number,
) {
const process = await getProcessData(processId, processVersion);

const bpmnBlob = new Blob([process.bpmn!], { type: 'application/xml' });

exportFile(`${process.definitionName}.bpmn`, bpmnBlob);
dataHandler(`${process.definitionName}.bpmn`, bpmnBlob);
}

async function getSVGFromBPMN(bpmn: string) {
Expand All @@ -54,7 +102,11 @@ async function getSVGFromBPMN(bpmn: string) {
return svg;
}

export async function exportSVG(processId: string, processVersion?: string | number) {
export async function exportSVG(
dataHandler: (fileName: string, data: Blob) => void,
processId: string,
processVersion?: string | number,
) {
const process = await getProcessData(processId, processVersion);

const svg = await getSVGFromBPMN(process.bpmn!);
Expand All @@ -63,10 +115,14 @@ export async function exportSVG(processId: string, processVersion?: string | num
type: 'image/svg+xml',
});

exportFile(`${process.definitionName}.svg`, svgBlob);
dataHandler(`${process.definitionName}.svg`, svgBlob);
}

export async function exportPDF(processId: string, processVersion?: string | number) {
export async function exportPDF(
dataHandler: (fileName: string, data: Blob) => void,
processId: string,
processVersion?: string | number,
) {
const process = await getProcessData(processId, processVersion);

const svg = await getSVGFromBPMN(process.bpmn!);
Expand Down Expand Up @@ -107,25 +163,31 @@ export async function exportPDF(processId: string, processVersion?: string | num
height: pageHeight,
});

await doc.save(`${process.definitionName}.pdf`);
dataHandler(`${process.definitionName}.pdf`, await doc.output('blob'));
}

function exportFile(processName: string, data: Blob) {
/**
* Downloads the file to export on the users device
*
* @param fileName
* @param data
*/
function exportFile(fileName: string, data: Blob) {
const objectURL = URL.createObjectURL(data);

// Creating Anchor Element to trigger download feature
const aLink = document.createElement('a');

// Setting anchor tag properties
aLink.style.display = 'none';
aLink.download = processName;
aLink.download = fileName;
aLink.href = objectURL;

// Setting anchor tag to DOM
document.body.appendChild(aLink);
aLink.click();
document.body.removeChild(aLink);

// Release Object URL, so browser dont keep reference
// Release Object URL, so the browser doesn't keep the reference
URL.revokeObjectURL(objectURL);
}
21 changes: 11 additions & 10 deletions src/management-system-v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,26 @@
"singleQuote": true
},
"dependencies": {
"jspdf": "^2.5.1",
"svg2pdf.js": "^2.2.2",
"uuid": "^9.0.0",
"@monaco-editor/react": "4.5.2",
"@casl/ability": "6.5.0",
"@monaco-editor/react": "^4.5.2",
"@proceed/bpmn-helper": "1.0.0",
"@tanstack/react-query": "4.35.7",
"antd": "5.9.4",
"bpmn-js": "13.2.0",
"bpmn-js-differ": "2.0.2",
"classnames": "2.3.2",
"fuse.js": "6.6.2",
"immer": "10.0.3",
"jspdf": "^2.5.1",
"jszip": "^3.10.1",
"monaco-editor": "0.43.0",
"next": "13.5.4",
"openapi-fetch": "0.7.8",
"react": "18.2.0",
"react-dom": "18.2.0",
"zustand": "4.4.2",
"immer": "10.0.3",
"fuse.js": "6.6.2",
"@casl/ability": "6.5.0",
"openapi-fetch": "0.7.8",
"@proceed/bpmn-helper": "1.0.0"
"svg2pdf.js": "^2.2.2",
"uuid": "^9.0.0",
"zustand": "4.4.2"
},
"devDependencies": {
"@tanstack/eslint-plugin-query": "4.36.0",
Expand Down
20 changes: 10 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1945,19 +1945,19 @@
resolved "https://registry.yarnpkg.com/@mdi/font/-/font-6.9.96.tgz#c68da7e0895885dd09e60dc08c5ecc0d77f67efb"
integrity sha512-z3QVZStyHVwkDsFR7A7F2PIvZJPWgdSFw4BEEy2Gc9HUN5NfK9mGbjgaYClRcbMWiYEV45srmiYtczmBtCqR8w==

"@monaco-editor/loader@^1.3.3":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.3.3.tgz#7f1742bd3cc21c0362a46a4056317f6e5215cfca"
integrity sha512-6KKF4CTzcJiS8BJwtxtfyYt9shBiEv32ateQ9T4UVogwn4HM/uPo9iJd2Dmbkpz8CM6Y0PDUpjnZzCwC+eYo2Q==
"@monaco-editor/loader@^1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.4.0.tgz#f08227057331ec890fa1e903912a5b711a2ad558"
integrity sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==
dependencies:
state-local "^1.0.6"

"@monaco-editor/[email protected]":
version "4.5.2"
resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.5.2.tgz#e8cc802203f729b423a998ea6fcb466604d61258"
integrity sha512-emcWu6vg1OpXPiYll4aPOaXe8bwYB4UaaNTwtArFLgMoNGBzRZb2Xn0Bra2HMIFM7QLgs7fCGunHO5LkfT2LBA==
"@monaco-editor/react@^4.5.2":
version "4.6.0"
resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.6.0.tgz#bcc68671e358a21c3814566b865a54b191e24119"
integrity sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==
dependencies:
"@monaco-editor/loader" "^1.3.3"
"@monaco-editor/loader" "^1.4.0"

"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"
Expand Down Expand Up @@ -13442,7 +13442,7 @@ jsprim@^1.2.2:
object.assign "^4.1.4"
object.values "^1.1.6"

jszip@^3.0.0, jszip@^3.1.0, jszip@^3.4.0:
jszip@^3.0.0, jszip@^3.1.0, jszip@^3.10.1, jszip@^3.4.0:
version "3.10.1"
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2"
integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==
Expand Down