@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.
@@ -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";
@@ -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>;
@@ -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
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react-beta",
3
- "version": "6.14.36",
3
+ "version": "6.14.38",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",