@topconsultnpm/sdkui-react 6.20.0-dev1.3 → 6.20.0-dev1.30

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.
Files changed (64) hide show
  1. package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +3 -3
  2. package/lib/components/NewComponents/ContextMenu/hooks.d.ts +1 -0
  3. package/lib/components/NewComponents/ContextMenu/hooks.js +8 -4
  4. package/lib/components/NewComponents/ContextMenu/styles.d.ts +4 -1
  5. package/lib/components/NewComponents/ContextMenu/styles.js +41 -8
  6. package/lib/components/NewComponents/ContextMenu/types.d.ts +1 -0
  7. package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +38 -30
  8. package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +8 -0
  9. package/lib/components/NewComponents/FloatingMenuBar/styles.js +30 -19
  10. package/lib/components/base/TMAccordion.js +2 -2
  11. package/lib/components/base/TMCustomButton.js +41 -5
  12. package/lib/components/base/TMDataGrid.d.ts +2 -2
  13. package/lib/components/base/TMDataGrid.js +16 -5
  14. package/lib/components/editors/TMHtmlEditor.js +1 -1
  15. package/lib/components/editors/TMMetadataValues.js +20 -2
  16. package/lib/components/features/documents/TMDcmtBlog.d.ts +1 -7
  17. package/lib/components/features/documents/TMDcmtBlog.js +29 -2
  18. package/lib/components/features/documents/TMDcmtForm.js +268 -168
  19. package/lib/components/features/documents/TMDcmtPreview.js +37 -66
  20. package/lib/components/features/search/TMDcmtCheckoutInfoForm.d.ts +8 -0
  21. package/lib/components/features/search/{TMSearchResultCheckoutInfoForm.js → TMDcmtCheckoutInfoForm.js} +6 -11
  22. package/lib/components/features/search/TMSearchQueryPanel.js +13 -12
  23. package/lib/components/features/search/TMSearchResult.js +74 -111
  24. package/lib/components/features/search/TMSearchResultsMenuItems.d.ts +1 -1
  25. package/lib/components/features/search/TMSearchResultsMenuItems.js +26 -43
  26. package/lib/components/features/search/TMSignatureInfoContent.d.ts +6 -0
  27. package/lib/components/features/search/TMSignatureInfoContent.js +140 -0
  28. package/lib/components/forms/Login/LoginValidatorService.d.ts +2 -0
  29. package/lib/components/forms/Login/LoginValidatorService.js +7 -2
  30. package/lib/components/forms/Login/TMLoginForm.js +34 -6
  31. package/lib/css/tm-sdkui.css +1 -1
  32. package/lib/helper/SDKUI_Globals.d.ts +13 -14
  33. package/lib/helper/SDKUI_Globals.js +9 -0
  34. package/lib/helper/SDKUI_Localizator.d.ts +8 -0
  35. package/lib/helper/SDKUI_Localizator.js +98 -0
  36. package/lib/helper/TMPdfViewer.d.ts +8 -0
  37. package/lib/helper/TMPdfViewer.js +187 -0
  38. package/lib/helper/TMUtils.d.ts +3 -1
  39. package/lib/helper/TMUtils.js +51 -0
  40. package/lib/helper/checkinCheckoutManager.d.ts +85 -0
  41. package/lib/helper/checkinCheckoutManager.js +348 -0
  42. package/lib/helper/devextremeCustomMessages.d.ts +30 -0
  43. package/lib/helper/devextremeCustomMessages.js +30 -0
  44. package/lib/helper/helpers.js +7 -1
  45. package/lib/helper/index.d.ts +2 -0
  46. package/lib/helper/index.js +2 -0
  47. package/lib/helper/queryHelper.js +29 -0
  48. package/lib/hooks/useCheckInOutOperations.d.ts +28 -0
  49. package/lib/hooks/useCheckInOutOperations.js +223 -0
  50. package/lib/services/platform_services.d.ts +1 -1
  51. package/package.json +5 -2
  52. package/lib/components/NewComponents/Notification/Notification.d.ts +0 -4
  53. package/lib/components/NewComponents/Notification/Notification.js +0 -60
  54. package/lib/components/NewComponents/Notification/NotificationContainer.d.ts +0 -8
  55. package/lib/components/NewComponents/Notification/NotificationContainer.js +0 -33
  56. package/lib/components/NewComponents/Notification/index.d.ts +0 -2
  57. package/lib/components/NewComponents/Notification/index.js +0 -2
  58. package/lib/components/NewComponents/Notification/styles.d.ts +0 -21
  59. package/lib/components/NewComponents/Notification/styles.js +0 -180
  60. package/lib/components/NewComponents/Notification/types.d.ts +0 -18
  61. package/lib/components/NewComponents/Notification/types.js +0 -1
  62. package/lib/components/features/search/TMSearchResultCheckoutInfoForm.d.ts +0 -8
  63. package/lib/helper/cicoHelper.d.ts +0 -31
  64. package/lib/helper/cicoHelper.js +0 -155
@@ -0,0 +1,187 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState, useRef, useCallback } from "react";
3
+ import styled from "styled-components";
4
+ import { LoadIndicator } from 'devextreme-react/load-indicator';
5
+ import { IconCloseOutline } from "./TMIcons";
6
+ import { SDKUI_Localizator } from "./SDKUI_Localizator";
7
+ import { TMColors } from "../utils/theme";
8
+ // Dynamic imports for optional dependencies
9
+ let pdfjs;
10
+ let Document;
11
+ let Page;
12
+ let isReactPdfAvailable = false;
13
+ try {
14
+ const reactPdf = require('react-pdf');
15
+ pdfjs = reactPdf.pdfjs;
16
+ Document = reactPdf.Document;
17
+ Page = reactPdf.Page;
18
+ // Configura il worker PDF.js PRIMA del rendering
19
+ pdfjs.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
20
+ isReactPdfAvailable = true;
21
+ }
22
+ catch (error) {
23
+ // react-pdf not available - will use iframe fallback
24
+ // console.warn('react-pdf is not installed. TMPdfViewer will use iframe fallback.');
25
+ }
26
+ const PDFViewerContainer = styled.div `
27
+ width: 100%;
28
+ height: 100%;
29
+ overflow-y: auto;
30
+ overflow-x: hidden;
31
+ background-color: #f5f5f5;
32
+ position: relative;
33
+
34
+ .react-pdf__Document {
35
+ display: flex;
36
+ flex-direction: column;
37
+ align-items: center;
38
+ gap: 10px;
39
+ padding: 10px 0;
40
+ }
41
+
42
+ .react-pdf__Page {
43
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
44
+ margin: 0 auto;
45
+ }
46
+
47
+ .react-pdf__Page__canvas {
48
+ max-width: 100%;
49
+ height: auto !important;
50
+ }
51
+ `;
52
+ const LoadingOverlay = styled.div `
53
+ position: absolute;
54
+ top: 0;
55
+ left: 0;
56
+ width: 100%;
57
+ height: 100%;
58
+ background-color: rgba(245, 245, 245, 0.95);
59
+ display: flex;
60
+ justify-content: center;
61
+ align-items: center;
62
+ z-index: 1000;
63
+ `;
64
+ const TMPdfViewer = (props) => {
65
+ const { pdfBlob, title = "Anteprima PDF", isResizingActive, enableFitToWidth = false } = props;
66
+ const [isMobile, setIsMobile] = useState(false);
67
+ const [totalPagesNumber, setTotalPagesNumber] = useState(0);
68
+ const [loadedPagesNumber, setLoadedPagesNumber] = useState(0);
69
+ const [visiblePages, setVisiblePages] = useState(new Set([1]));
70
+ const [pdfUrl, setPdfUrl] = useState("");
71
+ const observerRef = useRef(null);
72
+ useEffect(() => {
73
+ const checkIsMobile = () => {
74
+ const userAgent = navigator.userAgent || navigator.vendor || window.opera;
75
+ // Detect actual mobile/tablet devices
76
+ const isMobileDevice =
77
+ // User agent detection (phones and tablets)
78
+ /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent) ||
79
+ // Media query for touch devices with coarse pointer (more reliable than screen width)
80
+ (window.matchMedia?.('(hover: none) and (pointer: coarse)').matches ?? false) ||
81
+ // Touch-capable devices excluding desktop OS
82
+ (navigator.maxTouchPoints > 1 && !/Win|Mac|Linux x86_64/i.test(userAgent));
83
+ setIsMobile(isMobileDevice);
84
+ };
85
+ checkIsMobile();
86
+ // Create URL for iframe
87
+ const url = URL.createObjectURL(pdfBlob);
88
+ setPdfUrl(url);
89
+ return () => {
90
+ if (url) {
91
+ URL.revokeObjectURL(url);
92
+ }
93
+ };
94
+ }, [pdfBlob]);
95
+ /**
96
+ * Callback per l'Intersection Observer che gestisce il lazy loading delle pagine PDF.
97
+ * Viene chiamato ogni volta che una pagina entra o esce dal viewport (area visibile).
98
+ *
99
+ * - Monitora quando gli elementi (pagine) diventano visibili
100
+ * - Aggiunge il numero di pagina al Set delle pagine visibili quando entra nel viewport
101
+ * - Il rootMargin di 500px carica le pagine prima che siano effettivamente visibili (preloading)
102
+ */
103
+ const pageObserverCallback = useCallback((entries) => {
104
+ entries.forEach(entry => {
105
+ const pageNum = parseInt(entry.target.getAttribute('data-page-number') || '0');
106
+ if (entry.isIntersecting) {
107
+ // Aggiunge la pagina al Set di quelle da renderizzare
108
+ setVisiblePages(prev => new Set([...prev, pageNum]));
109
+ }
110
+ });
111
+ }, []);
112
+ /**
113
+ * Crea e configura l'Intersection Observer per il lazy loading.
114
+ *
115
+ * Configurazione:
116
+ * - root: null → osserva rispetto al viewport del browser
117
+ * - rootMargin: '500px' → inizia il caricamento 500px prima che la pagina sia visibile (buffer)
118
+ * - threshold: 0.01 → trigger quando anche solo l'1% dell'elemento è visibile
119
+ *
120
+ * Benefici:
121
+ * - Non blocca l'UI durante il rendering
122
+ * - Carica solo le pagine necessarie
123
+ * - Migliora le performance su PDF con molte pagine
124
+ */
125
+ useEffect(() => {
126
+ observerRef.current = new IntersectionObserver(pageObserverCallback, {
127
+ root: null,
128
+ rootMargin: '500px',
129
+ threshold: 0.01
130
+ });
131
+ return () => {
132
+ // Cleanup: disconnette l'observer quando il componente viene smontato
133
+ observerRef.current?.disconnect();
134
+ };
135
+ }, [pageObserverCallback]);
136
+ // Use iframe se react-pdf non è disponibile O se è desktop
137
+ if (!isReactPdfAvailable || (!isMobile && pdfUrl)) {
138
+ return (_jsx(PDFViewerContainer, { children: _jsx("iframe", { src: `${pdfUrl}#${enableFitToWidth ? 'view=FitH&' : ''}scrollbar=1`, title: title, style: {
139
+ width: '100%',
140
+ height: '100%',
141
+ border: 'none',
142
+ zIndex: 0,
143
+ pointerEvents: isResizingActive === true ? "none" : "auto"
144
+ } }, pdfUrl) }));
145
+ }
146
+ // Usa react-pdf solo per mobile e solo se disponibile
147
+ return _jsxs(PDFViewerContainer, { children: [loadedPagesNumber === 0 && totalPagesNumber > 0 && _jsx(LoadingOverlay, { children: _jsxs("div", { style: {
148
+ display: 'flex',
149
+ justifyContent: 'center',
150
+ alignItems: 'center',
151
+ flexDirection: 'column',
152
+ gap: '10px'
153
+ }, children: [_jsx(LoadIndicator, { height: 60, width: 60 }), _jsxs("div", { children: [SDKUI_Localizator.Loading, "..."] })] }) }), _jsx("div", { style: { display: loadedPagesNumber > 0 && totalPagesNumber > 0 ? 'block' : 'none' }, children: _jsx(Document, { file: pdfBlob, onLoadSuccess: ({ numPages }) => {
154
+ setTotalPagesNumber(numPages);
155
+ setLoadedPagesNumber(0);
156
+ }, loading: _jsxs("div", { style: {
157
+ display: 'flex',
158
+ justifyContent: 'center',
159
+ alignItems: 'center',
160
+ height: '100%',
161
+ flexDirection: 'column',
162
+ gap: '10px'
163
+ }, children: [_jsx(LoadIndicator, { height: 60, width: 60 }), _jsxs("div", { children: [SDKUI_Localizator.Loading, "..."] })] }), error: _jsxs("div", { style: {
164
+ display: 'flex',
165
+ justifyContent: 'center',
166
+ alignItems: 'center',
167
+ height: '100%',
168
+ flexDirection: 'column',
169
+ gap: '10px'
170
+ }, children: [_jsx(IconCloseOutline, { fontSize: 64, color: TMColors.error }), _jsx("div", { children: "Errore nel caricamento del PDF" })] }), children: Array.from(new Array(totalPagesNumber), (el, index) => {
171
+ const pageNumber = index + 1;
172
+ const shouldRender = visiblePages.has(pageNumber);
173
+ return (_jsx("div", { "data-page-number": pageNumber, ref: (el) => {
174
+ if (el && observerRef.current) {
175
+ observerRef.current.observe(el);
176
+ }
177
+ }, style: {
178
+ minHeight: shouldRender ? 'auto' : '1000px',
179
+ display: 'flex',
180
+ justifyContent: 'center',
181
+ alignItems: 'center'
182
+ }, children: shouldRender && (_jsx(Page, { pageNumber: pageNumber, renderTextLayer: false, renderAnnotationLayer: false, width: Math.min(window.innerWidth - 40, 1200), loading: _jsx("div", { style: { padding: '20px' }, children: _jsx(LoadIndicator, { height: 40, width: 40 }) }), onLoadSuccess: () => {
183
+ setLoadedPagesNumber(prev => prev + 1);
184
+ } })) }, `page_${pageNumber}`));
185
+ }) }) })] });
186
+ };
187
+ export default TMPdfViewer;
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
- import { DataColumnDescriptor, PdGs } from '@topconsultnpm/sdk-ts';
2
+ import { FileItem } from '../components';
3
+ import { DataColumnDescriptor, PdGs, SearchResultDescriptor } from '@topconsultnpm/sdk-ts';
3
4
  export declare const getFileIcon: (fileExtension: string | undefined, fileCount: number | undefined, tooltipContent?: JSX.Element | string) => import("react/jsx-runtime").JSX.Element;
4
5
  export declare function formatBytes(bytes: number | undefined, decimalPlaces?: number): string;
5
6
  export interface RowData {
@@ -64,4 +65,5 @@ export declare const parseSignatureConfiguration: (did: number, informationSign:
64
65
  allowCopyright: boolean;
65
66
  copyrightValue: string;
66
67
  };
68
+ export declare const convertSearchResultDescriptorToFileItems: (documents: Array<SearchResultDescriptor>) => Array<FileItem>;
67
69
  export {};
@@ -382,3 +382,54 @@ export const parseSignatureConfiguration = (did, informationSign, firstName, las
382
382
  throw error;
383
383
  }
384
384
  };
385
+ export const convertSearchResultDescriptorToFileItems = (documents) => {
386
+ const fileItems = [];
387
+ documents.forEach((doc, docIndex) => {
388
+ const columns = doc.dtdResult?.columns || [];
389
+ const rows = doc.dtdResult?.rows || [];
390
+ // Map column captions to their indexes for quick lookup
391
+ const colIndexMap = {};
392
+ columns.forEach((col, i) => {
393
+ if (col.caption !== undefined) {
394
+ colIndexMap[col.caption] = i;
395
+ }
396
+ });
397
+ const getVal = (row, caption) => {
398
+ const idx = colIndexMap[caption];
399
+ return idx !== undefined ? row[idx] : undefined;
400
+ };
401
+ rows.forEach((row, rowIndex) => {
402
+ let baseName = doc.fromName || `Document ${docIndex + 1}`;
403
+ const did = getVal(row, 'DID');
404
+ const fileExt = getVal(row, 'FileExt');
405
+ const tid = getVal(row, 'TID');
406
+ const fileSize = getVal(row, 'FileSize');
407
+ if (did) {
408
+ baseName += ` (DID: ${did})`;
409
+ }
410
+ if (fileExt) {
411
+ baseName += `.${fileExt}`;
412
+ }
413
+ fileItems.push({
414
+ id: did ? Number(did) : Number(`${docIndex}${rowIndex}`),
415
+ name: baseName,
416
+ isDirectory: false,
417
+ items: [],
418
+ tid: tid !== undefined ? Number(tid) : undefined,
419
+ did: did !== undefined ? Number(did) : undefined,
420
+ ext: fileExt ?? undefined,
421
+ size: fileSize !== undefined ? Number(fileSize) : 1,
422
+ creationTime: undefined,
423
+ lastUpdateTime: undefined,
424
+ updaterID: undefined,
425
+ updaterName: undefined,
426
+ description: undefined,
427
+ checkOutUserID: undefined,
428
+ checkOutUserName: undefined,
429
+ checkoutDate: null,
430
+ version: undefined,
431
+ });
432
+ });
433
+ });
434
+ return fileItems;
435
+ };
@@ -0,0 +1,85 @@
1
+ import React from "react";
2
+ import { AccessLevels, DcmtTypeDescriptor, FileDescriptor, UserDescriptor } from "@topconsultnpm/sdk-ts";
3
+ import { DcmtInfo, DownloadModes, DownloadTypes } from "../ts/types";
4
+ import { FileItem } from "../components";
5
+ /**
6
+ * Check-in/Check-out Manager
7
+ * Questo modulo gestisce tutte le operazioni di check-in e check-out
8
+ * dei documenti e delle bozze dei gruppi di lavoro.
9
+ *
10
+ * Operazioni gestite:
11
+ * - Generazione nomi file per download e checkout con timestamp e identificatori univoci
12
+ * - Download di documenti in modalità checkout
13
+ * - Gestione dello storage locale delle informazioni di checkout (aggiunta, aggiornamento, rimozione)
14
+ * - Validazione dei nomi file secondo le convenzioni di naming CICO
15
+ * - Rendering dell'interfaccia utente per conferma check-in con visualizzazione anomalie
16
+ * - Determinazione dello stato di check-out dei documenti (editMode/lockMode)
17
+ * - Verifica permessi e abilitazione funzionalità CICO per tipo documento
18
+ */
19
+ export interface CheckoutInfo {
20
+ DID: string;
21
+ TID: string;
22
+ checkoutFolder: string;
23
+ checkoutName: string;
24
+ }
25
+ interface CheckoutStatusResult {
26
+ isCheckedOut: boolean;
27
+ mode: 'editMode' | 'lockMode' | '';
28
+ version: number;
29
+ icon: React.ReactNode | null;
30
+ editLockTooltipText: React.ReactNode | null;
31
+ }
32
+ export type DownloadSource = {
33
+ type: 'fileItem';
34
+ fileItem: FileItem;
35
+ } | {
36
+ type: 'dcmtInfo';
37
+ dcmtInfo: DcmtInfo;
38
+ originalFileName: string;
39
+ };
40
+ export declare const getCicoDownloadFileName: (source: DownloadSource, checkout: boolean, withTimestampAndExt: boolean) => string;
41
+ export declare const cicoDownloadFilesCallback: (sources: Array<DownloadSource>, checkout: boolean, downloadDcmtsAsync: (inputDcmts: Array<DcmtInfo> | undefined, downloadType?: DownloadTypes, downloadMode?: DownloadModes, onFileDownloaded?: (dcmtFile: File) => void, confirmAttachments?: (list: FileDescriptor[]) => Promise<string[] | undefined>, skipConfirmation?: boolean) => Promise<void>) => Promise<void>;
42
+ export declare const updateCicoCheckoutStorageItem: (item: CheckoutInfo, type: "fileItem" | "dcmtInfo", action?: "addOrUpdate" | "remove") => void;
43
+ export declare const validateCicoFileName: (source: DownloadSource, fileName: string) => FileNameValidation;
44
+ type ValidationResult = {
45
+ expected: string | number | undefined;
46
+ current: string | number | undefined;
47
+ isValid: boolean;
48
+ };
49
+ type FileNameValidationItems = {
50
+ name: ValidationResult;
51
+ archiveID: ValidationResult;
52
+ did: ValidationResult;
53
+ tid: ValidationResult;
54
+ fileExtension: ValidationResult;
55
+ };
56
+ type FileNameValidation = {
57
+ isValid: boolean;
58
+ validationResults?: FileNameValidationItems;
59
+ };
60
+ export declare const renderCicoCheckInContent: (source: DownloadSource, selectedFilename: File, isValid: boolean, validationItems: FileNameValidationItems | undefined, color?: string) => React.ReactNode;
61
+ interface CheckInCheckOutDcmtInfo {
62
+ CICO: number;
63
+ CanCICO: AccessLevels;
64
+ CanDelChronology: AccessLevels;
65
+ UserID_MID: number;
66
+ Date_MID: number;
67
+ Ver_MID: number;
68
+ UserID_CanViewOrUpdate: AccessLevels;
69
+ Date_CanViewOrUpdate: AccessLevels;
70
+ Ver_CanViewOrUpdate: AccessLevels;
71
+ }
72
+ export declare const getDcmtCicoInfo: (dtd: DcmtTypeDescriptor | undefined) => CheckInCheckOutDcmtInfo;
73
+ /**
74
+ * Determina lo stato di check-in/check-out di un documento
75
+ *
76
+ * @param dcmt - Il documento in formato array (MetadataValueDescriptorEx[]) o oggetto piatto
77
+ * @param allUsers - Lista di tutti gli utenti per risolvere i nomi
78
+ * @param dtd - Descrittore del tipo documento contenente le definizioni dei metadati CICO
79
+ * @returns Oggetto con cicoEnabled (se CICO è abilitato) e checkoutStatus (dettagli dello stato)
80
+ */
81
+ export declare const getDcmtCicoStatus: (dcmt: any, allUsers: Array<UserDescriptor>, dtd: DcmtTypeDescriptor | undefined) => {
82
+ cicoEnabled: boolean;
83
+ checkoutStatus: CheckoutStatusResult;
84
+ };
85
+ export {};