@topconsultnpm/sdkui-react-beta 6.14.36 → 6.14.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/base/TMFileManagerUtils.d.ts +17 -0
- package/lib/components/base/TMFileManagerUtils.js +123 -0
- package/lib/components/features/wg/TMWGsCopyMoveForm.d.ts +24 -0
- package/lib/components/features/wg/TMWGsCopyMoveForm.js +329 -0
- package/lib/helper/SDKUI_Localizator.d.ts +4 -0
- package/lib/helper/SDKUI_Localizator.js +40 -0
- package/lib/helper/TMUtils.d.ts +5 -0
- package/lib/helper/TMUtils.js +20 -0
- package/package.json +1 -1
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
import { SearchResultDescriptor, UserDescriptor, WGTreeDescriptor } from "@topconsultnpm/sdk-ts-beta";
|
|
1
2
|
import { FileItem, TMFileManagerTreeViewDirectory } from "./TMFileManager";
|
|
2
3
|
export declare const findFileItems: (items: Array<FileItem>, id: number) => FileItem | undefined;
|
|
3
4
|
export declare const setFolderTreeViewItems: (items: Array<FileItem>) => Array<TMFileManagerTreeViewDirectory>;
|
|
5
|
+
export declare const buildFolderHierarchy: (fileSystemTree: WGTreeDescriptor | undefined, draftsFile: SearchResultDescriptor | undefined, archivedDocuments: Array<SearchResultDescriptor> | undefined, participants: Array<UserDescriptor> | undefined) => {
|
|
6
|
+
folderMap: Map<number, FileItem>;
|
|
7
|
+
draftInfoMap: Map<number, {
|
|
8
|
+
latestVersion: number;
|
|
9
|
+
folderId: number;
|
|
10
|
+
folderName: string;
|
|
11
|
+
fileExt: string;
|
|
12
|
+
fileSize: string;
|
|
13
|
+
}>;
|
|
14
|
+
archivedDocumentMap: Map<number, {
|
|
15
|
+
tid: number;
|
|
16
|
+
did: number;
|
|
17
|
+
fileExt: string;
|
|
18
|
+
fileSize: string;
|
|
19
|
+
}>;
|
|
20
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { associateColumnsToRows } from "../../helper";
|
|
1
2
|
// Function to find a specific file or folder based on its ID (used for finding nested items)
|
|
2
3
|
export const findFileItems = (items, id) => {
|
|
3
4
|
for (let item of items) {
|
|
@@ -29,3 +30,125 @@ export const setFolderTreeViewItems = (items) => {
|
|
|
29
30
|
return el;
|
|
30
31
|
});
|
|
31
32
|
};
|
|
33
|
+
// Function to process and build the folder hierarchy
|
|
34
|
+
export const buildFolderHierarchy = (fileSystemTree, draftsFile, archivedDocuments, participants) => {
|
|
35
|
+
// Initialize a map to hold folder data
|
|
36
|
+
const folderMap = new Map();
|
|
37
|
+
// Initialize a map to hold information draft
|
|
38
|
+
const draftInfoMap = new Map();
|
|
39
|
+
// Initialize a map to hold archived document
|
|
40
|
+
const archivedDocumentMap = new Map();
|
|
41
|
+
// Set to track IDs of folders that are children
|
|
42
|
+
const childFolderIds = new Set();
|
|
43
|
+
// Extract folders from the file system tree
|
|
44
|
+
const folders = fileSystemTree?.folders ?? [];
|
|
45
|
+
// Step 1: Populate the folderMap with folder data
|
|
46
|
+
folders.forEach((folder) => {
|
|
47
|
+
if (folder.id) {
|
|
48
|
+
folderMap.set(folder.id, { id: folder.id, name: `${folder.name ?? '-'}`, isDirectory: true, items: [] });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// Step 2: Process the draftsFile if it is provided, and populate items for corresponding folders
|
|
52
|
+
if (draftsFile !== undefined && draftsFile.dtdResult !== undefined) {
|
|
53
|
+
// Process the columns and rows to associate them with folder IDs
|
|
54
|
+
const data = associateColumnsToRows(draftsFile.dtdResult.columns, draftsFile.dtdResult.rows);
|
|
55
|
+
data.forEach(row => {
|
|
56
|
+
if (row.FolderID) {
|
|
57
|
+
// Ensure FolderID is a number, and retrieve the folder from the folderMap
|
|
58
|
+
const folderId = Number(row.FolderID);
|
|
59
|
+
const folder = folderMap.get(folderId);
|
|
60
|
+
const draftID = Number(row.DraftID);
|
|
61
|
+
const version = row.Versione ? Number(row.Versione) : 0;
|
|
62
|
+
let checkOutUserName = '';
|
|
63
|
+
let updaterName = '';
|
|
64
|
+
if (participants) {
|
|
65
|
+
const checkOutUserParticipant = participants.find(participant => participant.id === Number(row.CheckOutUserID)) || null;
|
|
66
|
+
if (checkOutUserParticipant) {
|
|
67
|
+
checkOutUserName = checkOutUserParticipant.name ?? '-';
|
|
68
|
+
}
|
|
69
|
+
const updaterUserParticipant = participants.find(participant => participant.id === Number(row.UpdaterID)) || null;
|
|
70
|
+
if (updaterUserParticipant) {
|
|
71
|
+
updaterName = updaterUserParticipant.name ?? '-';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const fileItem = {
|
|
75
|
+
id: draftID,
|
|
76
|
+
name: (row.Nome ?? '-'),
|
|
77
|
+
tid: Number(row.TID),
|
|
78
|
+
did: Number(row.DID),
|
|
79
|
+
isDirectory: false,
|
|
80
|
+
items: [],
|
|
81
|
+
size: row.FileSize ? Number(row.FileSize) : 0,
|
|
82
|
+
ext: row.FileExt,
|
|
83
|
+
creationTime: new Date(row.CreationTime),
|
|
84
|
+
lastUpdateTime: new Date(row.LastUpdateTime),
|
|
85
|
+
updaterID: Number(row.UpdaterID),
|
|
86
|
+
updaterName: updaterName,
|
|
87
|
+
description: row.Descrizione,
|
|
88
|
+
checkOutUserID: Number(row.CheckOutUserID),
|
|
89
|
+
checkOutUserName: checkOutUserName,
|
|
90
|
+
checkoutDate: row.CheckOutDate ? new Date(row.CheckOutDate) : null,
|
|
91
|
+
version,
|
|
92
|
+
};
|
|
93
|
+
if (draftInfoMap.has(draftID)) {
|
|
94
|
+
// Get the current version in the map
|
|
95
|
+
const currentDraftInfo = draftInfoMap.get(draftID);
|
|
96
|
+
// Update only if the new version is greater than the current version
|
|
97
|
+
if (currentDraftInfo && version > currentDraftInfo.latestVersion) {
|
|
98
|
+
draftInfoMap.set(draftID, { latestVersion: version, folderId: folderId, folderName: folder ? folder.name : '', fileExt: row.FileExt, fileSize: row.FileSize });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// If DraftID doesn't exist in the map, add it
|
|
103
|
+
draftInfoMap.set(draftID, { latestVersion: version, folderId: folderId, folderName: folder ? folder.name : '', fileExt: row.FileExt, fileSize: row.FileSize });
|
|
104
|
+
}
|
|
105
|
+
if (folder) {
|
|
106
|
+
// Add the item to the folder's items if the folder exists
|
|
107
|
+
folder.items.push(fileItem);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
folderMap.set(draftID, fileItem);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
// Step 3: Process the archived document if it is provided, and populate items for corresponding files
|
|
116
|
+
if (archivedDocuments !== undefined && archivedDocuments.length > 0) {
|
|
117
|
+
archivedDocuments.forEach((archivedDocument) => {
|
|
118
|
+
if (archivedDocument.dtdResult?.columns && archivedDocument.dtdResult.rows) {
|
|
119
|
+
const data = associateColumnsToRows(archivedDocument.dtdResult.columns, archivedDocument.dtdResult.rows);
|
|
120
|
+
data.forEach((row) => {
|
|
121
|
+
const did = Number(row.DID);
|
|
122
|
+
const tid = Number(row.TID);
|
|
123
|
+
if (did && tid) {
|
|
124
|
+
if (archivedDocumentMap.has(did)) {
|
|
125
|
+
const currentArchivedDocumentInfo = draftInfoMap.get(did);
|
|
126
|
+
if (currentArchivedDocumentInfo) {
|
|
127
|
+
archivedDocumentMap.set(did, { tid, did, fileExt: row.FileExt, fileSize: row.FileSize });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
archivedDocumentMap.set(did, { tid, did, fileExt: row.FileExt, fileSize: row.FileSize });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// Step 4: Build parent-child relationships based on folder structure
|
|
139
|
+
folders.forEach((folder) => {
|
|
140
|
+
if (folder.parentID !== undefined && folder.id !== undefined) {
|
|
141
|
+
const parent = folderMap.get(folder.parentID);
|
|
142
|
+
const child = folderMap.get(folder.id);
|
|
143
|
+
if (parent && child) {
|
|
144
|
+
parent.items.push(child); // Attach the child folder to the parent's items
|
|
145
|
+
childFolderIds.add(folder.id); // Mark folder ID as a child
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
// Step 4: Remove child folders from the root-level folder map
|
|
150
|
+
childFolderIds.forEach((id) => {
|
|
151
|
+
folderMap.delete(id);
|
|
152
|
+
});
|
|
153
|
+
return { folderMap, draftInfoMap, archivedDocumentMap };
|
|
154
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { WorkingGroupDescriptor } from "@topconsultnpm/sdk-ts-beta";
|
|
2
|
+
import { FileItem } from '../../base/TMFileManager';
|
|
3
|
+
interface TMWGsCopyMoveFormProps {
|
|
4
|
+
mode: 'copy' | 'move';
|
|
5
|
+
currentWorkingGroup: WorkingGroupDescriptor;
|
|
6
|
+
folder: FileItem;
|
|
7
|
+
selectedDrafts: Array<FileItem>;
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
refreshCallback: () => void;
|
|
10
|
+
workingGroups?: Array<WorkingGroupDescriptor>;
|
|
11
|
+
}
|
|
12
|
+
declare const TMWGsCopyMoveForm: (props: TMWGsCopyMoveFormProps) => import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export default TMWGsCopyMoveForm;
|
|
14
|
+
interface StepItem {
|
|
15
|
+
id: number | string;
|
|
16
|
+
title: string;
|
|
17
|
+
subtitle?: string;
|
|
18
|
+
}
|
|
19
|
+
interface TMStepNavigatorProps {
|
|
20
|
+
steps: Array<StepItem>;
|
|
21
|
+
currentStep: number;
|
|
22
|
+
onStepChange: (stepIndex: number) => void;
|
|
23
|
+
}
|
|
24
|
+
export declare const TMStepNavigator: (props: TMStepNavigatorProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { ResultTypes, SDK_Globals, WorkingGroupCacheService, WorkingGroupEngine } from "@topconsultnpm/sdk-ts-beta";
|
|
4
|
+
import { TreeView } from 'devextreme-react';
|
|
5
|
+
import styled from 'styled-components';
|
|
6
|
+
import { buildFolderHierarchy, findFileItems, setFolderTreeViewItems } from '../../base/TMFileManagerUtils';
|
|
7
|
+
import TMSpinner from '../../base/TMSpinner';
|
|
8
|
+
import { TMExceptionBoxManager } from '../../base/TMPopUp';
|
|
9
|
+
import { useDeviceType } from '../../base/TMDeviceProvider';
|
|
10
|
+
import { SDKUI_Localizator, getExceptionMessage, Globalization, IconUserGroup, IconFolder, calcResponsiveSizes } from '../../../helper';
|
|
11
|
+
import { useSaveForm, SaveFormOptions } from '../../../hooks/useForm';
|
|
12
|
+
import { FormModes } from '../../../ts';
|
|
13
|
+
import { TMColors } from '../../../utils/theme';
|
|
14
|
+
import TMDataGrid from '../../base/TMDataGrid';
|
|
15
|
+
import TMTooltip from '../../base/TMTooltip';
|
|
16
|
+
import { TMLayoutWaitingContainer } from '../../base/TMWaitPanel';
|
|
17
|
+
import { TMResultManager } from '../../forms/TMResultDialog';
|
|
18
|
+
import TMSaveForm from '../../forms/TMSaveForm';
|
|
19
|
+
let abortController = new AbortController();
|
|
20
|
+
const TMWGsCopyMoveForm = (props) => {
|
|
21
|
+
const { mode, workingGroups, currentWorkingGroup, folder, selectedDrafts, onClose, refreshCallback } = props;
|
|
22
|
+
// Get the current device type (e.g., mobile, tablet, desktop) using a custom hook.
|
|
23
|
+
const deviceType = useDeviceType();
|
|
24
|
+
// Stepper state: manages the current step in a multi-step UI
|
|
25
|
+
const [currentStep, setCurrentStep] = useState(0);
|
|
26
|
+
// State to store the list of working groups
|
|
27
|
+
const [wgs, setWgs] = useState([]);
|
|
28
|
+
// State to store the currently selected working group, or undefined if none is selected
|
|
29
|
+
const [selectedWg, setSelectedWg] = useState(undefined);
|
|
30
|
+
// State hook to store the currently focused row key, initially set to undefined
|
|
31
|
+
const [focusedRowKey, setFocusedRowKey] = useState(undefined);
|
|
32
|
+
// State to store selected row keys
|
|
33
|
+
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
|
34
|
+
// State to manage the file system hierarchy
|
|
35
|
+
const [treeFs, setTreeFs] = useState(undefined);
|
|
36
|
+
// State to store transformed directory data for file manager
|
|
37
|
+
const [treeViewData, setTreeViewData] = useState([]);
|
|
38
|
+
// State to manage the selected file
|
|
39
|
+
const [selectedFolder, setSelectedFolder] = useState(undefined);
|
|
40
|
+
// State variable to control the visibility of the wait panel
|
|
41
|
+
const [showWaitPanel, setShowWaitPanel] = useState(false);
|
|
42
|
+
// State variable to store the title of the wait panel
|
|
43
|
+
const [waitPanelTitle, setWaitPanelTitle] = useState('');
|
|
44
|
+
// State variable to control the visibility of the primary section of the wait panel
|
|
45
|
+
const [showPrimary, setShowPrimary] = useState(false);
|
|
46
|
+
// State variable to store the primary text of the wait panel
|
|
47
|
+
const [waitPanelTextPrimary, setWaitPanelTextPrimary] = useState('');
|
|
48
|
+
// State variable to track the current value of the primary progress indicator in the wait panel
|
|
49
|
+
const [waitPanelValuePrimary, setWaitPanelValuePrimary] = useState(0);
|
|
50
|
+
// State variable to define the maximum value for the primary progress indicator in the wait panel
|
|
51
|
+
const [waitPanelMaxValuePrimary, setWaitPanelMaxValuePrimary] = useState(0);
|
|
52
|
+
// Disable if currentWorkingGroup or its id, selectedWg, or selectedFolder are undefined; recomputed only when dependencies change
|
|
53
|
+
const isDisabled = useMemo(() => {
|
|
54
|
+
return currentWorkingGroup === undefined || currentWorkingGroup.id === undefined || selectedWg === undefined || selectedFolder === undefined;
|
|
55
|
+
}, [currentWorkingGroup, selectedWg, selectedFolder]);
|
|
56
|
+
// Sort working groups and set focused row to current and select current working group and go to step
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!currentWorkingGroup)
|
|
59
|
+
return;
|
|
60
|
+
const loadWorkingGroups = async () => {
|
|
61
|
+
let groupsToUse = workingGroups;
|
|
62
|
+
if (!groupsToUse) {
|
|
63
|
+
try {
|
|
64
|
+
TMSpinner.show({ description: SDKUI_Localizator.LoadingWorkGroups });
|
|
65
|
+
// Clear cache and fetch working groups
|
|
66
|
+
WorkingGroupCacheService.RemoveAll();
|
|
67
|
+
const fetchedGroups = await WorkingGroupCacheService.GetAllAsync();
|
|
68
|
+
groupsToUse = fetchedGroups;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
TMExceptionBoxManager.show({ exception: error });
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
TMSpinner.hide();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (groupsToUse) {
|
|
78
|
+
const sortedGroups = [...groupsToUse].sort((a, b) => {
|
|
79
|
+
if (!a.name && !b.name)
|
|
80
|
+
return 0;
|
|
81
|
+
if (!a.name)
|
|
82
|
+
return 1;
|
|
83
|
+
if (!b.name)
|
|
84
|
+
return -1;
|
|
85
|
+
return a.name.localeCompare(b.name);
|
|
86
|
+
});
|
|
87
|
+
setWgs(sortedGroups);
|
|
88
|
+
setFocusedRowKey(currentWorkingGroup.id);
|
|
89
|
+
setSelectedWg(currentWorkingGroup);
|
|
90
|
+
setCurrentStep(1);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
loadWorkingGroups();
|
|
94
|
+
}, [workingGroups, currentWorkingGroup]);
|
|
95
|
+
// Fetch the file system tree for the selected working group when it or the list of WGs changes
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
const fetchWgTree = async () => {
|
|
98
|
+
if (!selectedWg || !selectedWg.id)
|
|
99
|
+
return;
|
|
100
|
+
try {
|
|
101
|
+
const workingGroupEngine = new WorkingGroupEngine(SDK_Globals.tmSession);
|
|
102
|
+
const wgTreeFileSystem = await workingGroupEngine.DraftsGetTreeAsync(selectedWg.id);
|
|
103
|
+
const { folderMap } = buildFolderHierarchy(wgTreeFileSystem, undefined, undefined, undefined);
|
|
104
|
+
const rootFolder = { id: -1, name: SDKUI_Localizator.Drafts, isDirectory: true, items: Array.from(folderMap.values()), };
|
|
105
|
+
const rootTreeViewItem = { id: -1, text: rootFolder.name, subFileFolderCount: rootFolder.items.filter(item => !item.isDirectory).length, expanded: true, items: setFolderTreeViewItems(rootFolder.items), };
|
|
106
|
+
setTreeFs(rootFolder);
|
|
107
|
+
setTreeViewData([rootTreeViewItem]);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
TMExceptionBoxManager.show({ exception: error });
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
if (selectedWg && wgs.length) {
|
|
114
|
+
fetchWgTree();
|
|
115
|
+
}
|
|
116
|
+
}, [selectedWg, wgs]);
|
|
117
|
+
// When the current step resets to the first step (index 0), clear the selected folder by setting it to undefined
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
if (currentStep === 0) {
|
|
120
|
+
setSelectedFolder(undefined);
|
|
121
|
+
}
|
|
122
|
+
}, [currentStep]);
|
|
123
|
+
// Single selection mode for data grid
|
|
124
|
+
const selection = useMemo(() => {
|
|
125
|
+
return { mode: 'single' };
|
|
126
|
+
}, []);
|
|
127
|
+
// Handles focus change in the data grid
|
|
128
|
+
const onFocusedRowChanged = useCallback((e) => {
|
|
129
|
+
setFocusedRowKey(e.row?.key);
|
|
130
|
+
}, []);
|
|
131
|
+
// Handles selection change in the data grid
|
|
132
|
+
const onSelectionChanged = useCallback((e) => {
|
|
133
|
+
const selectedKeys = e.component.getSelectedRowKeys() ?? [];
|
|
134
|
+
setSelectedRowKeys(selectedKeys);
|
|
135
|
+
}, []);
|
|
136
|
+
const onRowDblClick = useCallback((e) => {
|
|
137
|
+
if (e.data) {
|
|
138
|
+
setSelectedWg(e.data);
|
|
139
|
+
setCurrentStep(1);
|
|
140
|
+
}
|
|
141
|
+
}, []);
|
|
142
|
+
// Handle selected folder
|
|
143
|
+
const handleSelectedFolder = (folderItem) => {
|
|
144
|
+
setSelectedFolder(folderItem);
|
|
145
|
+
};
|
|
146
|
+
// Function to handle item click event in the TreeView
|
|
147
|
+
const handleTreeViewItemClick = (e) => {
|
|
148
|
+
if (!e || !treeFs)
|
|
149
|
+
return;
|
|
150
|
+
const clickedItemId = e.itemData.id;
|
|
151
|
+
const item = clickedItemId === treeFs.id ? treeFs : findFileItems(treeFs.items, clickedItemId);
|
|
152
|
+
handleSelectedFolder(item);
|
|
153
|
+
};
|
|
154
|
+
const validator = async () => {
|
|
155
|
+
let vil = [];
|
|
156
|
+
return vil;
|
|
157
|
+
};
|
|
158
|
+
// Returns true if one row and folder are selected and either differs from the current group or folder
|
|
159
|
+
const isLocalSelectionModified = () => {
|
|
160
|
+
// Ensure a single row is selected and a folder is selected
|
|
161
|
+
if (selectedWg !== undefined && selectedFolder !== undefined && currentStep === 1) {
|
|
162
|
+
// Check if the selected row and folder differ from the current state
|
|
163
|
+
const isGroupChanged = selectedWg.id !== currentWorkingGroup.id;
|
|
164
|
+
const isFolderChanged = selectedFolder.id !== folder.id;
|
|
165
|
+
return isGroupChanged || isFolderChanged;
|
|
166
|
+
}
|
|
167
|
+
// Default to unmodified
|
|
168
|
+
return false;
|
|
169
|
+
};
|
|
170
|
+
const { validationItems, exception } = useSaveForm(FormModes.Update, 0, new SaveFormOptions(), validator);
|
|
171
|
+
const onSaveAsync = async () => {
|
|
172
|
+
if (isDisabled)
|
|
173
|
+
return;
|
|
174
|
+
try {
|
|
175
|
+
// If the currentWorkingGroup or its id is not defined, exit early by resolving the promise
|
|
176
|
+
if (currentWorkingGroup === undefined || currentWorkingGroup.id === undefined || selectedWg === undefined || selectedFolder === undefined)
|
|
177
|
+
return Promise.resolve();
|
|
178
|
+
setWaitPanelTitle(mode === 'copy' ? SDKUI_Localizator.Copy : SDKUI_Localizator.Move);
|
|
179
|
+
setShowWaitPanel(true);
|
|
180
|
+
setShowPrimary(true);
|
|
181
|
+
// Initialize an empty array to hold the result information
|
|
182
|
+
abortController = new AbortController();
|
|
183
|
+
let result = [];
|
|
184
|
+
let i = 0;
|
|
185
|
+
setWaitPanelMaxValuePrimary(selectedDrafts.length);
|
|
186
|
+
for (const draft of selectedDrafts) {
|
|
187
|
+
i++;
|
|
188
|
+
setWaitPanelValuePrimary(i);
|
|
189
|
+
if (abortController.signal.aborted) {
|
|
190
|
+
result.push({ rowIndex: i, id1: i, id2: i, resultType: ResultTypes.WARNING, description: SDKUI_Localizator.OperationFilesInterrupted.replaceParams(i) });
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
setWaitPanelTextPrimary(`${i}/${selectedDrafts.length}`);
|
|
195
|
+
try {
|
|
196
|
+
const workingGroupEngine = new WorkingGroupEngine(SDK_Globals.tmSession);
|
|
197
|
+
if (currentWorkingGroup.id && selectedWg && selectedWg.id && selectedFolder && draft.did) {
|
|
198
|
+
const selectedFolderId = selectedFolder.id === -1 ? 0 : selectedFolder.id;
|
|
199
|
+
if (mode === 'copy') {
|
|
200
|
+
await workingGroupEngine.DraftsCopyAsync(currentWorkingGroup.id, draft.did, selectedWg.id, selectedFolderId);
|
|
201
|
+
}
|
|
202
|
+
else if (mode === 'move') {
|
|
203
|
+
await workingGroupEngine.DraftsMoveAsync(currentWorkingGroup.id, draft.did, selectedWg.id, selectedFolderId);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
result.push({ rowIndex: i, id1: i, id2: i, description: SDKUI_Localizator.UpdateCompletedSuccessfully, resultType: ResultTypes.SUCCESS });
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
result.push({ rowIndex: i, id1: i, id2: i, resultType: ResultTypes.ERROR, description: getExceptionMessage(err) });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
setWaitPanelTextPrimary('');
|
|
214
|
+
setWaitPanelMaxValuePrimary(0);
|
|
215
|
+
setWaitPanelValuePrimary(0);
|
|
216
|
+
setShowWaitPanel(false);
|
|
217
|
+
TMResultManager.show(result, mode === 'copy' ? SDKUI_Localizator.Copy : SDKUI_Localizator.Move, "ID", undefined);
|
|
218
|
+
refreshCallback();
|
|
219
|
+
onClose();
|
|
220
|
+
}
|
|
221
|
+
catch (e) {
|
|
222
|
+
// If any error occurs during the try block execution, display the exception in an error box
|
|
223
|
+
TMExceptionBoxManager.show({ exception: e });
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
const cellIconRender = useCallback((cellData) => {
|
|
227
|
+
const data = cellData.data;
|
|
228
|
+
const tooltipContent = (_jsxs("div", { style: { textAlign: 'left' }, children: [_jsx("div", { children: _jsx("strong", { children: SDKUI_Localizator.WorkGroup }) }), _jsx("hr", {}), _jsxs("div", { children: [_jsx("span", { style: { fontWeight: 'bold' }, children: "ID:" }), " ", data.id] }), _jsxs("div", { children: [_jsxs("span", { style: { fontWeight: 'bold' }, children: [SDKUI_Localizator.Name, ":"] }), " ", data.name ?? '-'] }), _jsxs("div", { children: [_jsxs("span", { style: { fontWeight: 'bold' }, children: [SDKUI_Localizator.OwnerName, ":"] }), " ", data.ownerName ?? '-'] }), _jsxs("div", { children: [_jsxs("span", { style: { fontWeight: 'bold' }, children: [SDKUI_Localizator.CreationTime, ":"] }), " ", Globalization.getDateTimeDisplayValue(data.creationTime)] })] }));
|
|
229
|
+
return _jsx(TMTooltip, { content: tooltipContent, children: _jsx(IconUserGroup, { color: "#009700" }) });
|
|
230
|
+
}, []);
|
|
231
|
+
const cellDafaultRender = useCallback((cellData) => {
|
|
232
|
+
return _jsx("span", { children: cellData.value });
|
|
233
|
+
}, []);
|
|
234
|
+
const cellNameRender = useCallback((cellData) => {
|
|
235
|
+
const data = cellData.data;
|
|
236
|
+
return _jsx("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: _jsx("span", { children: data.name }) });
|
|
237
|
+
}, []);
|
|
238
|
+
const dataColumns = useMemo(() => {
|
|
239
|
+
return ([
|
|
240
|
+
{ dataField: 'id', caption: "ID", dataType: 'string', visible: false, cellRender: cellDafaultRender },
|
|
241
|
+
{ caption: "", dataType: 'string', cellRender: cellIconRender, width: "30px", allowFiltering: false },
|
|
242
|
+
{ dataField: 'name', caption: SDKUI_Localizator.Name, dataType: 'string', cellRender: cellNameRender },
|
|
243
|
+
{ dataField: 'description', caption: SDKUI_Localizator.Description, dataType: 'string', cellRender: cellDafaultRender },
|
|
244
|
+
{ dataField: "ownerName", caption: SDKUI_Localizator.OwnerName, dataType: 'string', cellRender: cellDafaultRender },
|
|
245
|
+
]);
|
|
246
|
+
}, []);
|
|
247
|
+
// Render each TreeView item (directories) with custom styling/icons
|
|
248
|
+
const renderTreeViewItem = (itemData) => {
|
|
249
|
+
const isSelected = selectedFolder && selectedFolder.id === itemData.id;
|
|
250
|
+
const tooltipContent = _jsxs("div", { style: { textAlign: "center" }, children: [_jsx("span", { style: { fontWeight: 'bold' }, children: "ID" }), ": ", itemData.id] });
|
|
251
|
+
return (_jsxs("div", { style: { whiteSpace: "nowrap", display: "flex", alignItems: "center" }, className: isSelected ? 'treeview-selected-item-manager' : '', children: [_jsx(TMTooltip, { content: tooltipContent, children: _jsx(IconFolder, { style: { marginRight: 5, color: '#e6c200' } }) }), itemData.text] }));
|
|
252
|
+
};
|
|
253
|
+
return _jsx(TMSaveForm, { title: (mode === 'copy' ? SDKUI_Localizator.Copy : SDKUI_Localizator.Move) + (selectedDrafts.length === 1
|
|
254
|
+
? ': ' + selectedDrafts[0].name
|
|
255
|
+
: ' (' + selectedDrafts.length + ')'), showErrorCount: false, showUndoButton: false, hasNavigation: false, skipIsModifiedCheck: true, isModal: true, width: calcResponsiveSizes(deviceType, '800px', '800px', '95%'), height: '550px', validationItems: validationItems, exception: exception, isModified: isLocalSelectionModified(), showToolbar: false, showSaveButton: false, onClose: onClose, children: _jsxs(TMLayoutWaitingContainer, { direction: 'vertical', showWaitPanel: showWaitPanel, showWaitPanelPrimary: showPrimary, waitPanelTitle: waitPanelTitle, waitPanelTextPrimary: waitPanelTextPrimary, waitPanelValuePrimary: waitPanelValuePrimary, waitPanelMaxValuePrimary: waitPanelMaxValuePrimary, isCancelable: true, abortController: abortController, children: [_jsx("div", { style: { width: "100%", height: '48px' }, children: _jsx(TMStepNavigator, { steps: [
|
|
256
|
+
{ id: 0, title: SDKUI_Localizator.ChooseGroup, subtitle: selectedWg ? selectedWg.name : undefined },
|
|
257
|
+
{ id: 1, title: SDKUI_Localizator.ChooseFolder, subtitle: selectedFolder ? selectedFolder.name : undefined }
|
|
258
|
+
], currentStep: currentStep, onStepChange: (stepIndex) => { setCurrentStep(stepIndex); } }) }), _jsxs("div", { style: {
|
|
259
|
+
marginTop: '10px',
|
|
260
|
+
padding: '10px',
|
|
261
|
+
borderRadius: '8px',
|
|
262
|
+
boxShadow: '3px 0 5px #D3D3D3BF, -3px 0 5px #D3D3D3BF, 0 3px 5px #D3D3D3BF, 0 -3px 5px #D3D3D3BF',
|
|
263
|
+
border: '1px solid rgba(0,0,0,0.15)',
|
|
264
|
+
width: '100%',
|
|
265
|
+
height: 'calc(100% - 113px)',
|
|
266
|
+
overflow: 'auto',
|
|
267
|
+
}, children: [(currentStep === 0 && wgs.length > 0) &&
|
|
268
|
+
_jsx(TMDataGrid, { dataColumns: dataColumns, dataSource: wgs, selection: selection, focusedRowKey: focusedRowKey, onFocusedRowChanged: onFocusedRowChanged, selectedRowKeys: [...selectedRowKeys], onSelectionChanged: onSelectionChanged, onRowDblClick: onRowDblClick, autoNavigateToFocusedRow: true, showColumnHeaders: false }), (currentStep === 1) &&
|
|
269
|
+
_jsx(TreeView, { dataSource: treeViewData, displayExpr: "text", itemRender: renderTreeViewItem, onItemClick: handleTreeViewItemClick })] }), _jsxs("div", { style: {
|
|
270
|
+
width: "100%",
|
|
271
|
+
height: '45px',
|
|
272
|
+
marginTop: "10px",
|
|
273
|
+
display: 'flex',
|
|
274
|
+
justifyContent: 'flex-end',
|
|
275
|
+
alignItems: 'center',
|
|
276
|
+
gap: '10px'
|
|
277
|
+
}, children: [_jsx("button", { onClick: () => onSaveAsync(), disabled: isDisabled, style: {
|
|
278
|
+
background: 'none',
|
|
279
|
+
border: 'none',
|
|
280
|
+
color: isDisabled ? '#a0a0a0' : TMColors.primary,
|
|
281
|
+
cursor: isDisabled ? 'not-allowed' : 'pointer',
|
|
282
|
+
padding: '6px 14px',
|
|
283
|
+
borderRadius: '6px',
|
|
284
|
+
transition: 'background-color 0.3s, color 0.3s',
|
|
285
|
+
}, onMouseEnter: e => {
|
|
286
|
+
if (!isDisabled) {
|
|
287
|
+
e.currentTarget.style.backgroundColor = '#e6f0ff';
|
|
288
|
+
e.currentTarget.style.color = '#004a99';
|
|
289
|
+
}
|
|
290
|
+
}, onMouseLeave: e => {
|
|
291
|
+
if (!isDisabled) {
|
|
292
|
+
e.currentTarget.style.backgroundColor = 'transparent';
|
|
293
|
+
e.currentTarget.style.color = TMColors.primary;
|
|
294
|
+
}
|
|
295
|
+
}, children: mode === 'copy' ? SDKUI_Localizator.Copy : SDKUI_Localizator.Move }), _jsx("button", { onClick: () => onClose(), style: {
|
|
296
|
+
background: 'none',
|
|
297
|
+
border: 'none',
|
|
298
|
+
color: TMColors.primary,
|
|
299
|
+
cursor: 'pointer',
|
|
300
|
+
padding: '6px 14px',
|
|
301
|
+
borderRadius: '6px',
|
|
302
|
+
transition: 'background-color 0.3s, color 0.3s',
|
|
303
|
+
}, onMouseEnter: e => {
|
|
304
|
+
e.currentTarget.style.backgroundColor = '#e6f0ff';
|
|
305
|
+
e.currentTarget.style.color = '#004a99';
|
|
306
|
+
}, onMouseLeave: e => {
|
|
307
|
+
e.currentTarget.style.backgroundColor = 'transparent';
|
|
308
|
+
e.currentTarget.style.color = TMColors.primary;
|
|
309
|
+
}, children: SDKUI_Localizator.Cancel })] })] }) });
|
|
310
|
+
};
|
|
311
|
+
export default TMWGsCopyMoveForm;
|
|
312
|
+
const StepSpan = styled.span `
|
|
313
|
+
color: ${({ $isCurrent, $isPast }) => ($isCurrent ? TMColors.primary : $isPast ? '#555' : '#888')};
|
|
314
|
+
font-weight: ${({ $isCurrent }) => ($isCurrent ? 600 : 400)};
|
|
315
|
+
cursor: ${({ $isPast }) => ($isPast ? 'pointer' : 'default')};
|
|
316
|
+
transition: color 0.3s ease;
|
|
317
|
+
&:hover {
|
|
318
|
+
color: ${({ $isPast }) => ($isPast ? '#007bff' : undefined)};
|
|
319
|
+
text-decoration: ${({ $isPast }) => ($isPast ? 'underline' : 'none')};
|
|
320
|
+
}
|
|
321
|
+
`;
|
|
322
|
+
export const TMStepNavigator = (props) => {
|
|
323
|
+
const { steps, currentStep, onStepChange } = props;
|
|
324
|
+
return (_jsx("div", { style: { display: 'flex', justifyContent: 'center' }, children: _jsx("div", { style: { display: 'flex', alignItems: 'center', backgroundColor: '#f9f9f9', padding: '6px 16px', borderRadius: '999px', boxShadow: '0 1px 4px rgba(0, 0, 0, 0.05)' }, children: steps.map((step, index) => {
|
|
325
|
+
const isPastStep = index < currentStep;
|
|
326
|
+
const isCurrent = currentStep === index;
|
|
327
|
+
return _jsxs("div", { style: { display: 'flex', alignItems: 'center' }, children: [_jsx(StepSpan, { "$isCurrent": isCurrent, "$isPast": isPastStep, onClick: isPastStep ? () => onStepChange(index) : undefined, children: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center' }, children: [_jsx("span", { children: step.title }), step.subtitle && _jsxs("span", { style: { fontSize: '0.90em', fontStyle: "italic" }, children: ["\"", step.subtitle, "\""] })] }) }), index < steps.length - 1 && (_jsx("span", { style: { margin: '0 8px', color: '#ddd' }, children: _jsx("i", { className: 'dx-icon-greater' }) }))] }, step.id);
|
|
328
|
+
}) }) }));
|
|
329
|
+
};
|
|
@@ -60,6 +60,8 @@ export declare class SDKUI_Localizator {
|
|
|
60
60
|
static get ChangePassword(): "Kennwort ändern" | "Change password" | "Cambiar la contraseña" | "Changer le mot de passe" | "Alterar a senha" | "Cambia password";
|
|
61
61
|
static get CheckIn(): "Check in" | "Enregistrement";
|
|
62
62
|
static get CheckOut(): "Check out" | "Extraction";
|
|
63
|
+
static get ChooseFolder(): string;
|
|
64
|
+
static get ChooseGroup(): string;
|
|
63
65
|
static get ChronologyDelete(): "Physische Löschung der Chronologie" | "Physical deletion of chronology" | "Eliminación física de la cronología" | "Suppression physique de la chronologie" | "Exclusão física da cronologia" | "Cancellazione fisica della cronologia";
|
|
64
66
|
static get Clear(): "Bereinigen" | "Clear" | "Limpiar" | "Efface" | "Limpar" | "Pulisci";
|
|
65
67
|
static get ClearOTP(): "OTP löschen" | "Clear OTP" | "Borrar OTP" | "Effacer l'OTP" | "Limpar OTP" | "Cancella OTP";
|
|
@@ -200,6 +202,7 @@ export declare class SDKUI_Localizator {
|
|
|
200
202
|
static get List_Hide(): "Liste ausblenden" | "Hide list" | "Ocultar lista" | "Masquer la liste" | "Nascondi la lista";
|
|
201
203
|
static get List_Show(): "liste anzeigen" | "Show list" | "Ver lista" | "Afficher la liste" | "Visualizza la lista";
|
|
202
204
|
static get Loading(): "Laden" | "Loading" | "Carga" | "Chargement" | "Caricamento";
|
|
205
|
+
static get LoadingWorkGroups(): string;
|
|
203
206
|
static get Login(): string;
|
|
204
207
|
static get LogDelete(): "Löschen der Logik" | "Logical delete" | "Cancelación lógica" | "Suppression logique" | "Lógica de cancelamento" | "Cancellazione logica";
|
|
205
208
|
static get MakeEditable(): "Bearbeitbar machen" | "Make editable" | "Hacer editable" | "Rendre modifiable" | "Faça editável" | "Rendi editabile";
|
|
@@ -256,6 +259,7 @@ export declare class SDKUI_Localizator {
|
|
|
256
259
|
static get Operations(): "Operationen" | "Operations" | "Operaciones" | "Opérations" | "Operações" | "Operazioni";
|
|
257
260
|
static get OnBehalfOf(): "im Auftrag von" | "on behalf of" | "a nombre de" | "de la part de" | "em nome de" | "per conto di";
|
|
258
261
|
static get OneMore(): "andere" | "more" | "otro" | "autre" | "outro" | "altro";
|
|
262
|
+
static get OperationFilesInterrupted(): string;
|
|
259
263
|
static get OperationResult(): "Operationsergebnis" | "Operation result" | "Resultado de la operación" | "Résultat de l'opération" | "Resultado da operação" | "Esito operazione";
|
|
260
264
|
static get OperationSuccess(): "Vorgang erfolgreich abgeschlossen" | "Operation completed successfully" | "Operación completada con éxito" | "Opération terminée avec succès" | "Operação concluída com sucesso" | "Operazione completata con successo";
|
|
261
265
|
static get Options(): "Optionen" | "Options" | "Opciones" | "Opções" | "Opzioni";
|
|
@@ -549,6 +549,26 @@ export class SDKUI_Localizator {
|
|
|
549
549
|
default: return "Check out";
|
|
550
550
|
}
|
|
551
551
|
}
|
|
552
|
+
static get ChooseFolder() {
|
|
553
|
+
switch (this._cultureID) {
|
|
554
|
+
case CultureIDs.De_DE: return "Ordner auswählen";
|
|
555
|
+
case CultureIDs.En_US: return "Choose the folder";
|
|
556
|
+
case CultureIDs.Es_ES: return "Elegir la carpeta";
|
|
557
|
+
case CultureIDs.Fr_FR: return "Choisir le dossier";
|
|
558
|
+
case CultureIDs.Pt_PT: return "Escolher a pasta";
|
|
559
|
+
default: return "Scegli la cartella";
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
static get ChooseGroup() {
|
|
563
|
+
switch (this._cultureID) {
|
|
564
|
+
case CultureIDs.De_DE: return "Gruppe auswählen";
|
|
565
|
+
case CultureIDs.En_US: return "Choose the group";
|
|
566
|
+
case CultureIDs.Es_ES: return "Elegir el grupo";
|
|
567
|
+
case CultureIDs.Fr_FR: return "Choisir le groupe";
|
|
568
|
+
case CultureIDs.Pt_PT: return "Escolher o grupo";
|
|
569
|
+
default: return "Scegli il gruppo";
|
|
570
|
+
}
|
|
571
|
+
}
|
|
552
572
|
static get ChronologyDelete() {
|
|
553
573
|
switch (this._cultureID) {
|
|
554
574
|
case CultureIDs.De_DE: return "Physische Löschung der Chronologie";
|
|
@@ -1960,6 +1980,16 @@ export class SDKUI_Localizator {
|
|
|
1960
1980
|
default: return "Caricamento";
|
|
1961
1981
|
}
|
|
1962
1982
|
}
|
|
1983
|
+
static get LoadingWorkGroups() {
|
|
1984
|
+
switch (this._cultureID) {
|
|
1985
|
+
case CultureIDs.De_DE: return "Laden von Arbeitsgruppen";
|
|
1986
|
+
case CultureIDs.En_US: return "Loading Work Groups";
|
|
1987
|
+
case CultureIDs.Es_ES: return "Cargando Grupos de Trabajo";
|
|
1988
|
+
case CultureIDs.Fr_FR: return "Chargement des Groupes de Travail";
|
|
1989
|
+
case CultureIDs.Pt_PT: return "Carregando Grupos de Trabalho";
|
|
1990
|
+
default: return "Caricamento dei Gruppi di lavoro";
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1963
1993
|
static get Login() {
|
|
1964
1994
|
switch (this._cultureID) {
|
|
1965
1995
|
case CultureIDs.De_DE: return "Anmeldung";
|
|
@@ -2511,6 +2541,16 @@ export class SDKUI_Localizator {
|
|
|
2511
2541
|
default: return "altro";
|
|
2512
2542
|
}
|
|
2513
2543
|
}
|
|
2544
|
+
static get OperationFilesInterrupted() {
|
|
2545
|
+
switch (this._cultureID) {
|
|
2546
|
+
case CultureIDs.De_DE: return `Vorgang unterbrochen. {{0}} Dateien verarbeitet`;
|
|
2547
|
+
case CultureIDs.En_US: return `Operation interrupted. Processed {{0}} files`;
|
|
2548
|
+
case CultureIDs.Es_ES: return `Operación interrumpida. Procesados {{0}} archivos`;
|
|
2549
|
+
case CultureIDs.Fr_FR: return `Opération interrompue. {{0}} fichiers traités`;
|
|
2550
|
+
case CultureIDs.Pt_PT: return `Operação interrompida. Processados {{0}} arquivos`;
|
|
2551
|
+
default: return `Operazione interrotta. Elaborati {{0}} files`;
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2514
2554
|
static get OperationResult() {
|
|
2515
2555
|
switch (this._cultureID) {
|
|
2516
2556
|
case CultureIDs.De_DE: return "Operationsergebnis";
|
package/lib/helper/TMUtils.d.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
+
import { DataColumnDescriptor } from '@topconsultnpm/sdk-ts-beta';
|
|
1
2
|
export declare const getFileIcon: (fileExtension: string | undefined, fileCount: number | undefined, tooltipContent?: JSX.Element | string) => import("react/jsx-runtime").JSX.Element;
|
|
2
3
|
export declare function formatBytes(bytes: number | undefined, decimalPlaces?: number): string;
|
|
4
|
+
export interface RowData {
|
|
5
|
+
[key: string]: string | number | null;
|
|
6
|
+
}
|
|
7
|
+
export declare const associateColumnsToRows: (columns: Array<DataColumnDescriptor> | undefined, rows: Array<Array<string>> | undefined) => Array<RowData>;
|
package/lib/helper/TMUtils.js
CHANGED
|
@@ -95,3 +95,23 @@ export function formatBytes(bytes, decimalPlaces = 2) {
|
|
|
95
95
|
const value = parseFloat((bytes / Math.pow(k, i)).toFixed(decimalPlaces));
|
|
96
96
|
return `${value} ${units[i]}`;
|
|
97
97
|
}
|
|
98
|
+
// Function to associate columns to rows and return an array of RowData objects
|
|
99
|
+
export const associateColumnsToRows = (columns, rows) => {
|
|
100
|
+
if (columns === undefined || rows === undefined)
|
|
101
|
+
return [];
|
|
102
|
+
// Map over each row and associate each value with the corresponding column's caption
|
|
103
|
+
return rows.map(row => {
|
|
104
|
+
// Create an empty object to hold the key-value pairs for each row
|
|
105
|
+
const rowObject = {};
|
|
106
|
+
// Loop through each cell in the row and map its value to the corresponding column's caption
|
|
107
|
+
row.forEach((value, index) => {
|
|
108
|
+
const column = columns[index]; // Get the column corresponding to the current index
|
|
109
|
+
// If a column exists at this index, add the value to the rowObject with the column's caption as the key
|
|
110
|
+
if (column && column.caption) {
|
|
111
|
+
rowObject[column.caption] = value;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
// Return the populated rowObject, which represents the row with columns as keys
|
|
115
|
+
return rowObject;
|
|
116
|
+
});
|
|
117
|
+
};
|