@topconsultnpm/sdkui-react 6.22.0-dev2.45 → 6.22.0-dev2.47

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,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React, { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
3
- import styled, { css } from 'styled-components';
3
+ import styled, { createGlobalStyle, css } from 'styled-components';
4
4
  import DataGrid, { Column, HeaderFilter, Selection, Scrolling, LoadPanel, SearchPanel, Pager, Sorting, Paging, FilterPanel, ColumnChooser, Grouping, GroupPanel, Summary, Editing, FilterRow, StateStoring, RowDragging, MasterDetail, Position, ColumnChooserSearch, ColumnChooserSelection } from 'devextreme-react/data-grid';
5
5
  import DataSource from 'devextreme/data/data_source';
6
6
  import { IconAll, IconSelected, IconVisible, SDKUI_Globals, SDKUI_Localizator } from '../../helper';
@@ -382,7 +382,7 @@ const TMDataGrid = React.forwardRef((props, ref) => {
382
382
  return pager;
383
383
  return { ...pager, displayMode: pager.displayMode ?? 'compact' };
384
384
  }, [pager, compactPager]);
385
- return _jsxs(StyledDataGridContainer, { "$compactPager": compactPager, style: { width: "100%", height: "100%" }, children: [_jsx("div", { ref: gridContainerRef, style: { width: "100%", height: counterConfig.show ? "calc(100% - 25px)" : "100%" }, children: _jsxs(DataGrid, { ref: internalRef, id: id, className: `tm-datagrid ${hasFilters ? 'has-filters' : ''}`,
385
+ return _jsxs(StyledDataGridContainer, { "$compactPager": compactPager, style: { width: "100%", height: "100%" }, children: [_jsx(HeaderFilterMenuGlobalStyle, {}), _jsx("div", { ref: gridContainerRef, style: { width: "100%", height: counterConfig.show ? "calc(100% - 25px)" : "100%" }, children: _jsxs(DataGrid, { ref: internalRef, id: id, className: `tm-datagrid ${hasFilters ? 'has-filters' : ''}`,
386
386
  // main properties
387
387
  keyExpr: keyExpr, dataSource: dataSource, selectedRowKeys: selectedRowKeys, focusedRowEnabled: focusedRowEnabled, hoverStateEnabled: hoverStateEnabled,
388
388
  // events and callbacks
@@ -451,6 +451,185 @@ const StyledDataGridContainer = styled.div `
451
451
  }
452
452
  `}
453
453
  `;
454
+ // Stile del pannello del filtro d'intestazione (ricerca, elenco valori, OK/Annulla).
455
+ // Il popup è in portale sul body, fuori da StyledDataGridContainer: le regole sono globali
456
+ // ma tutte sotto .dx-header-filter-menu, classe che DevExtreme applica solo a questo popup.
457
+ const HeaderFilterMenuGlobalStyle = createGlobalStyle `
458
+ .dx-header-filter-menu {
459
+
460
+ &.dx-popup-wrapper > .dx-overlay-content.dx-popup-normal {
461
+ border: 1px solid #E4E8EE;
462
+ border-radius: 8px;
463
+ box-shadow: 0 2px 6px rgba(18, 44, 76, .08), 0 12px 28px rgba(18, 44, 76, .14);
464
+ overflow: hidden;
465
+ }
466
+
467
+ &.dx-popup-wrapper .dx-popup-content {
468
+ padding: 10px 10px 4px;
469
+ }
470
+
471
+ /* Campo di ricerca: TextBox mode "search" resa da List o da TreeView (filtri gerarchici) */
472
+ .dx-searchbox.dx-list-search,
473
+ .dx-searchbox.dx-treeview-search {
474
+ margin-bottom: 8px;
475
+ border-radius: 6px;
476
+ border: 1px solid #CDD2D8;
477
+ background-color: #F4F6F9;
478
+ box-shadow: none;
479
+ transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease;
480
+
481
+ /* fluent disegna qui la barretta di focus, sostituita da bordo + alone */
482
+ &::before,
483
+ &::after {
484
+ display: none;
485
+ }
486
+
487
+ &.dx-state-hover:not(.dx-state-focused) {
488
+ border-color: #AEB6C1;
489
+ background-color: #EEF2F7;
490
+ }
491
+
492
+ &.dx-state-focused {
493
+ border-color: #2559A5;
494
+ background-color: #FFFFFF;
495
+ box-shadow: 0 0 0 2px rgba(37, 89, 165, .16);
496
+ }
497
+
498
+ .dx-icon-search {
499
+ width: 28px;
500
+ color: #8A929B;
501
+ transition: color .15s ease;
502
+ }
503
+
504
+ &.dx-state-focused .dx-icon-search {
505
+ color: #2559A5;
506
+ }
507
+
508
+ .dx-texteditor-input {
509
+ min-height: 28px;
510
+ padding-inline-start: 28px;
511
+ padding-inline-end: 8px;
512
+ font-size: 12px;
513
+ background-color: transparent;
514
+ }
515
+
516
+ .dx-placeholder {
517
+ color: #9AA3AD;
518
+ font-size: 12px;
519
+
520
+ &::before {
521
+ padding-inline-start: 28px;
522
+ padding-inline-end: 8px;
523
+ }
524
+ }
525
+
526
+ .dx-clear-button-area .dx-icon-clear {
527
+ color: #8A929B;
528
+ transition: color .15s ease;
529
+ }
530
+
531
+ .dx-clear-button-area:hover .dx-icon-clear {
532
+ color: #2559A5;
533
+ }
534
+ }
535
+
536
+ .dx-list-item {
537
+ border-radius: 4px;
538
+ transition: background-color .12s ease;
539
+ }
540
+
541
+ .dx-list-item-content {
542
+ padding: 4px 6px;
543
+ }
544
+
545
+ .dx-list-item.dx-state-hover,
546
+ .dx-list-item.dx-state-focused {
547
+ background-color: #F1F6FD;
548
+ color: #122C4C;
549
+ }
550
+
551
+ .dx-list-select-all {
552
+ padding: 2px 0 6px;
553
+ margin-bottom: 4px;
554
+ border-bottom: 1px solid #ECEFF3;
555
+ }
556
+
557
+ .dx-list-select-all-label {
558
+ font-weight: 600;
559
+ color: #122C4C;
560
+ }
561
+
562
+ .dx-checkbox-checked .dx-checkbox-icon,
563
+ .dx-checkbox-indeterminate .dx-checkbox-icon {
564
+ border-color: #2559A5;
565
+ background-color: #2559A5;
566
+ }
567
+
568
+ .dx-checkbox.dx-state-hover:not(.dx-checkbox-checked) .dx-checkbox-icon {
569
+ border-color: #2559A5;
570
+ }
571
+
572
+ /* il :not() lascia intatto il fade-out nativo della scrollbar */
573
+ .dx-scrollable-scroll:not(.dx-state-invisible) .dx-scrollable-scroll-content {
574
+ background-color: rgba(18, 44, 76, .28);
575
+ border-radius: 3px;
576
+ }
577
+
578
+ &.dx-popup-wrapper .dx-popup-bottom.dx-toolbar {
579
+ padding: 8px 10px 10px;
580
+ border-top: 1px solid #ECEFF3;
581
+ }
582
+
583
+ .dx-popup-bottom .dx-button {
584
+ border-radius: 5px;
585
+ min-width: 76px;
586
+ margin: 0;
587
+ }
588
+
589
+ .dx-popup-bottom .dx-toolbar-button + .dx-toolbar-button {
590
+ margin-inline-start: 6px;
591
+
592
+ .dx-button {
593
+ margin: 0;
594
+ }
595
+ }
596
+
597
+ /* DevExtreme aggiunge prima OK e poi Annulla: individuarli per posizione li rende indipendenti dalla lingua */
598
+ .dx-popup-bottom .dx-toolbar-after > .dx-toolbar-button:first-child .dx-button {
599
+ border-color: transparent;
600
+ background-color: #2559A5;
601
+
602
+ .dx-button-text {
603
+ color: #FFFFFF;
604
+ }
605
+
606
+ &.dx-state-hover {
607
+ background-color: #1E4A8C;
608
+ }
609
+
610
+ &.dx-state-active {
611
+ background-color: #1A3F78;
612
+ }
613
+ }
614
+
615
+ .dx-popup-bottom .dx-toolbar-after > .dx-toolbar-button + .dx-toolbar-button .dx-button {
616
+ border-color: transparent;
617
+ background-color: transparent;
618
+
619
+ .dx-button-text {
620
+ color: #44506B;
621
+ }
622
+
623
+ &.dx-state-hover {
624
+ background-color: #F0F3F7;
625
+ }
626
+
627
+ &.dx-state-active {
628
+ background-color: #E6EBF2;
629
+ }
630
+ }
631
+ }
632
+ `;
454
633
  const getRecordCount = (dataSource) => {
455
634
  if (Array.isArray(dataSource)) {
456
635
  return dataSource.length; // For array
@@ -7,9 +7,8 @@ import { DeviceType, useDeviceType } from './TMDeviceProvider';
7
7
  const HIDE_DELAY_MS = 400;
8
8
  const TMTooltip = ({ children, position, content, hideAfterDelay, parentStyle, childStyle, allowContentInteraction = false }) => {
9
9
  const [showTooltip, setShowTooltip] = useState(false);
10
- const [id, setID] = useState('');
10
+ const [id] = useState(() => genUniqueId());
11
11
  const hideTimerRef = useRef();
12
- useEffect(() => { setID(genUniqueId()); }, [children]);
13
12
  useEffect(() => {
14
13
  const hide = () => setShowTooltip(false);
15
14
  window.addEventListener('click', hide);
@@ -13,6 +13,15 @@ import { TMColors } from '../../../utils/theme';
13
13
  import styled from 'styled-components';
14
14
  import { useOutsideClick } from '../../../hooks/useOutsideClick';
15
15
  import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
16
+ const TMTreeDcmtTypeCell = ({ tid, isMobile }) => {
17
+ const [dtd, setDtd] = useState();
18
+ const loadDTD = useCallback(async () => {
19
+ if (isMobile || dtd || !tid)
20
+ return;
21
+ setDtd(await DcmtTypeListCacheService.GetAsync(tid));
22
+ }, [isMobile, dtd, tid]);
23
+ return (_jsxs(StyledTreeDcmtTypeRow, { onMouseEnter: loadDTD, children: [_jsx("span", { style: { cursor: 'pointer', flex: 1, minWidth: 0 }, children: _jsx(TMTidViewer, { tid: tid, showIcon: false }) }), !isMobile && (_jsx("span", { className: 'info-icon', children: _jsx(TMDcmtTypeTooltip, { dtd: dtd, children: _jsx(IconInfo, { color: TMColors.info }) }) }))] }));
24
+ };
16
25
  const TMTreeSelector = ({ layoutMode = LayoutModes.Update, isVisible, onSelectedTIDChanged, onClosePanel, allowMaximize = true, onMaximizePanel }) => {
17
26
  const [trees, setTrees] = useState([]);
18
27
  const [treeItems, setTreeItems] = useState([]);
@@ -66,36 +75,9 @@ const TMTreeSelector = ({ layoutMode = LayoutModes.Update, isVisible, onSelected
66
75
  }, [selectedTreeId, trees]);
67
76
  const renderCell = useCallback((data) => {
68
77
  const treeItem = data.data;
69
- const [showInfo, setShowInfo] = useState(false);
70
78
  // Documento (DcmtType)
71
79
  if (treeItem.type === TreeItemTypes.DcmtType) {
72
- return (_jsxs(StyledDivHorizontal, { style: {
73
- alignItems: 'center',
74
- minWidth: 0,
75
- width: '100%',
76
- gap: 2,
77
- position: 'relative'
78
- }, onMouseEnter: async () => {
79
- if (!isMobile) {
80
- setShowInfo(true);
81
- let dtd = await DcmtTypeListCacheService.GetAsync(treeItem.tid);
82
- setInfoDTD(dtd);
83
- }
84
- }, onMouseLeave: () => {
85
- if (!isMobile) {
86
- setShowInfo(false);
87
- setInfoDTD(undefined);
88
- }
89
- }, children: [_jsx("span", { style: { cursor: 'pointer', flex: 1, minWidth: 0 }, children: _jsx(TMTidViewer, { tid: treeItem.tid, showIcon: false }) }), !isMobile && (_jsx("span", { style: {
90
- opacity: showInfo ? 1 : 0,
91
- transition: 'opacity 0.2s',
92
- pointerEvents: showInfo ? 'auto' : 'none',
93
- cursor: 'pointer',
94
- display: 'flex',
95
- alignItems: 'center',
96
- marginLeft: 'auto',
97
- zIndex: 2
98
- }, children: _jsx(TMDcmtTypeTooltip, { dtd: infoDTD, children: _jsx(IconInfo, { color: TMColors.info }) }) }))] }));
80
+ return _jsx(TMTreeDcmtTypeCell, { tid: treeItem.tid, isMobile: isMobile });
99
81
  }
100
82
  // Folder (altri tipi)
101
83
  return (_jsxs(StyledDivHorizontal, { style: { gap: 5, alignItems: 'center', minWidth: 0 }, children: [_jsx("span", { style: {
@@ -113,7 +95,7 @@ const TMTreeSelector = ({ layoutMode = LayoutModes.Update, isVisible, onSelected
113
95
  whiteSpace: 'nowrap',
114
96
  flex: 1,
115
97
  }, children: treeItem.nameLoc })] }));
116
- }, [isMobile, infoDTD, setInfoDTD]);
98
+ }, [isMobile]);
117
99
  // Handles selection change in the data grid
118
100
  const onSelectionChanged = useCallback((e) => {
119
101
  const selectedKeys = e.component.getSelectedRowKeys() ?? [];
@@ -214,6 +196,29 @@ const TMTreeSelector = ({ layoutMode = LayoutModes.Update, isVisible, onSelected
214
196
  : _jsx("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', width: '100%', fontSize: '1.5rem' }, children: SDKUI_Localizator.TreesNoAvailable }) }));
215
197
  };
216
198
  export default TMTreeSelector;
199
+ const StyledTreeDcmtTypeRow = styled(StyledDivHorizontal) `
200
+ align-items: center;
201
+ min-width: 0;
202
+ width: 100%;
203
+ gap: 2px;
204
+ position: relative;
205
+
206
+ .info-icon {
207
+ display: flex;
208
+ align-items: center;
209
+ margin-left: auto;
210
+ z-index: 2;
211
+ cursor: pointer;
212
+ opacity: 0;
213
+ pointer-events: none;
214
+ transition: opacity 0.2s;
215
+ }
216
+
217
+ &:hover .info-icon {
218
+ opacity: 1;
219
+ pointer-events: auto;
220
+ }
221
+ `;
217
222
  const StyledTreeListWrapper = styled.div `
218
223
  width: 100%;
219
224
  height: 100%;
@@ -86,6 +86,8 @@ interface TMBlogsPostProps {
86
86
  addTaskCallback?: (task: TaskDescriptor) => Promise<void>;
87
87
  handleNavigateToWGs?: (value: HomeBlogPost | number) => Promise<void>;
88
88
  handleNavigateToDossiers?: (value: HomeBlogPost | number) => Promise<void>;
89
+ /** Apre la pratica CaseFlow di una notizia con `classID === 'CF'` (bacheca Home V6). */
90
+ handleNavigateToCaseflows?: (value: HomeBlogPost | number) => Promise<void>;
89
91
  }
90
92
  declare const TMBlogsPost: (props: TMBlogsPostProps) => import("react/jsx-runtime").JSX.Element;
91
93
  export default TMBlogsPost;
@@ -5,6 +5,7 @@ import { ContextMenu as TMContextMenu } from "../NewComponents/ContextMenu";
5
5
  import { SDKUI_Localizator, Globalization, getExceptionMessage, TMConditionalWrapper, getMoreInfoTasksForDocument, taskModalSizes } from "../../helper";
6
6
  import TMToppyMessage from "../../helper/TMToppyMessage";
7
7
  import { useDcmtOperations } from "../../hooks/useDcmtOperations";
8
+ import { CaseFlowService } from "../../services/caseflow/CaseFlowService";
8
9
  import { DownloadTypes, FormModes } from "../../ts";
9
10
  import { TMColors } from "../../utils/theme";
10
11
  import ShowAlert from "../base/TMAlert";
@@ -32,7 +33,7 @@ const TMBlogsPost = (props) => {
32
33
  isRestoreEnabled: false,
33
34
  isRefreshEnabled: false,
34
35
  isCreateContextualTask: false,
35
- }, showFloatingCommentButton = false, showCommentFormCallback, refreshCallback, showId, setShowId, refreshHomePageNews, markBlogAsRead, externalBlogPost, resetExternalBlogPost, visible = true, allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, afterTaskSaved, handleNavigateToWGs, handleNavigateToDossiers, } = props;
36
+ }, showFloatingCommentButton = false, showCommentFormCallback, refreshCallback, showId, setShowId, refreshHomePageNews, markBlogAsRead, externalBlogPost, resetExternalBlogPost, visible = true, allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, afterTaskSaved, handleNavigateToWGs, handleNavigateToDossiers, handleNavigateToCaseflows, } = props;
36
37
  const { abortController, showWaitPanel, waitPanelTitle, showPrimary, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, downloadDcmtsAsync } = useDcmtOperations();
37
38
  // Get the current device type (e.g., mobile, tablet, desktop) using a custom hook.
38
39
  const deviceType = useDeviceType();
@@ -292,6 +293,8 @@ const TMBlogsPost = (props) => {
292
293
  handleNavigateToWGs(blogPost);
293
294
  if (handleNavigateToDossiers && id && blogPost.classID === 'DS')
294
295
  handleNavigateToDossiers(blogPost);
296
+ if (handleNavigateToCaseflows && id && blogPost.classID === 'CF')
297
+ handleNavigateToCaseflows(blogPost);
295
298
  };
296
299
  const handleKeyDown = (event) => {
297
300
  if (!selectedItem.length)
@@ -324,6 +327,9 @@ const TMBlogsPost = (props) => {
324
327
  if (handleNavigateToDossiers && item.id && classItemID === 'DS') {
325
328
  handleNavigateToDossiers(item);
326
329
  }
330
+ if (handleNavigateToCaseflows && item.id && classItemID === 'CF') {
331
+ handleNavigateToCaseflows(item);
332
+ }
327
333
  break;
328
334
  default:
329
335
  break;
@@ -367,6 +373,17 @@ const TMBlogsPost = (props) => {
367
373
  result.push({ rowIndex: currentBlog.id, id1: currentBlog.id, id2: currentBlog.id, resultType: ResultTypes.ERROR, description: getExceptionMessage(err) });
368
374
  });
369
375
  break;
376
+ case 'CF':
377
+ // La notizia porta il solo CFIID: il template (cftid) che l'API
378
+ // richiede lo risolve CaseFlowService.setFollow.
379
+ await CaseFlowService.setFollow(currentBlog.id, false).then(async () => {
380
+ result.push({ rowIndex: currentBlog.id, id1: currentBlog.id, id2: currentBlog.id, description: SDKUI_Localizator.OperationSuccess, resultType: ResultTypes.SUCCESS });
381
+ refreshHomePageNews?.();
382
+ })
383
+ .catch((err) => {
384
+ result.push({ rowIndex: currentBlog.id, id1: currentBlog.id, id2: currentBlog.id, resultType: ResultTypes.ERROR, description: getExceptionMessage(err) });
385
+ });
386
+ break;
370
387
  default:
371
388
  ShowAlert({ message: 'TODO', mode: 'warning', title: SDKUI_Localizator.Unfollow, duration: 3000 });
372
389
  break;
@@ -570,7 +587,7 @@ const TMBlogsPost = (props) => {
570
587
  beginGroup: true
571
588
  });
572
589
  }
573
- if (targetItem && Boolean(classId && (classId === 'WG' || classId === 'DS'))) {
590
+ if (targetItem && Boolean(classId && (classId === 'WG' || classId === 'DS' || classId === 'CF'))) {
574
591
  menuItems.push({
575
592
  icon: _jsx("span", { className: "dx-icon-eyeclose" }),
576
593
  name: SDKUI_Localizator.Unfollow,
@@ -632,6 +649,12 @@ const TMBlogsPost = (props) => {
632
649
  else
633
650
  console.log("Working Group reference missing objID");
634
651
  break;
652
+ case ObjectClasses.CaseFlowInstance:
653
+ if (ref.objID)
654
+ handleNavigateToCaseflows?.(ref.objID);
655
+ else
656
+ console.log("CaseFlow instance reference missing objID");
657
+ break;
635
658
  // Handle other object types as needed
636
659
  default:
637
660
  console.warn(`Unhandled object type: ${ref.objClass}`);
@@ -687,7 +710,7 @@ const TMBlogsPost = (props) => {
687
710
  textDecoration: blogPost.isDel ? 'line-through' : 'none',
688
711
  boxShadow: isFocused ? "0 4px 12px rgba(19, 85, 150, 0.6)" : "none",
689
712
  cursor: 'pointer',
690
- }, children: [_jsx(BlogPostTitle, { displayMode: displayMode, layoutMode: layoutMode, blogPost: blogPost, isSelected: isSelected, isOwnComment: isOwnComment, searchText: searchText, isSys: isSys, isHomeBlogPost: isHomeBlogPost, showId: localShowId, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }), isNew && _jsx(NewBadge, { layoutMode: layoutMode }), _jsx("div", { style: { fontSize: '1rem', color: "#000", marginTop: "10px", overflow: "hidden" }, children: _jsx(TMHtmlContentDisplay, { markup: blogPost.description ?? '-', searchText: searchText, isSelected: isSelected }) }), showExtendedAttachments && blogPost.attachments && blogPost.attachments.length > 0 && (_jsx(TMBlogAttachments, { attachments: blogPost.attachments, layoutMode: layoutMode, isSelected: isSelected, isLoading: !descriptorsLoaded, searchText: searchText, dcmtTypeDescriptors: dcmtTypeDescriptors, treeFs: treeFs, draftLatestInfoMap: draftLatestInfoMap, archivedDocumentMap: archivedDocumentMap, handleAttachmentFocus: handleFocusedAttachment, openDcmtForm: openDcmtForm }))] }, `${id}-blogpost-${blogPost.id}`) })] }, "blog-post-wrapper-" + id + "-" + blogPost.id);
713
+ }, children: [_jsx(BlogPostTitle, { displayMode: displayMode, layoutMode: layoutMode, blogPost: blogPost, isSelected: isSelected, isOwnComment: isOwnComment, searchText: searchText, isSys: isSys, isHomeBlogPost: isHomeBlogPost, showId: localShowId, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, handleNavigateToCaseflows: handleNavigateToCaseflows }), isNew && _jsx(NewBadge, { layoutMode: layoutMode }), _jsx("div", { style: { fontSize: '1rem', color: "#000", marginTop: "10px", overflow: "hidden" }, children: _jsx(TMHtmlContentDisplay, { markup: blogPost.description ?? '-', searchText: searchText, isSelected: isSelected }) }), showExtendedAttachments && blogPost.attachments && blogPost.attachments.length > 0 && (_jsx(TMBlogAttachments, { attachments: blogPost.attachments, layoutMode: layoutMode, isSelected: isSelected, isLoading: !descriptorsLoaded, searchText: searchText, dcmtTypeDescriptors: dcmtTypeDescriptors, treeFs: treeFs, draftLatestInfoMap: draftLatestInfoMap, archivedDocumentMap: archivedDocumentMap, handleAttachmentFocus: handleFocusedAttachment, openDcmtForm: openDcmtForm }))] }, `${id}-blogpost-${blogPost.id}`) })] }, "blog-post-wrapper-" + id + "-" + blogPost.id);
691
714
  }), _jsx("div", { ref: bottomRef })] }), _jsx(TMContextMenu, { items: menuItems, target: `#${id}-blogs-wrapper`, externalControl: {
692
715
  visible: menuVisible,
693
716
  position: menuPosition,
@@ -109,6 +109,7 @@ interface BlogPostTitleProps {
109
109
  showId: boolean;
110
110
  handleNavigateToWGs?: (blogPost: BlogPost | HomeBlogPost) => void;
111
111
  handleNavigateToDossiers?: (blogPost: BlogPost | HomeBlogPost) => void;
112
+ handleNavigateToCaseflows?: (blogPost: BlogPost | HomeBlogPost) => void;
112
113
  }
113
114
  export declare const BlogPostTitle: (props: BlogPostTitleProps) => import("react/jsx-runtime").JSX.Element;
114
115
  export declare const stripHtml: (html: string) => string;
@@ -162,8 +162,16 @@ export const getDcmtTypeDescriptor = async (blogPosts) => {
162
162
  return dcmtTypeMap;
163
163
  };
164
164
  export const BlogPostHomeHeader = (header, classId, isSelected, searchText, headerClickCallback) => {
165
+ // 'CF' = istanza di CaseFlow (bacheca Home V6). Attenzione: nel mondo dei
166
+ // task PdGs.CF indica invece la pratica-dossier; qui il dominio e' il
167
+ // ClassID di HomeBlogPost, dove 'DS' e' la pratica e 'CF' la pratica CaseFlow.
165
168
  const isWorkGroup = classId === 'WG';
166
- const iconColor = isSelected ? '#ffffff' : isWorkGroup ? PDGS_COLORS.WORKING_GROUP : PDGS_COLORS.DOSSIER;
169
+ const isCaseFlow = classId === 'CF';
170
+ const iconColor = isSelected
171
+ ? '#ffffff'
172
+ : isWorkGroup ? PDGS_COLORS.WORKING_GROUP
173
+ : isCaseFlow ? PDGS_COLORS.CASEFLOW
174
+ : PDGS_COLORS.DOSSIER;
167
175
  return (_jsxs("div", { style: {
168
176
  display: "flex",
169
177
  alignItems: "center",
@@ -171,7 +179,7 @@ export const BlogPostHomeHeader = (header, classId, isSelected, searchText, head
171
179
  fontWeight: "bold",
172
180
  color: isSelected ? "#fff" : TMColors.primary,
173
181
  gap: "4px",
174
- }, children: [_jsx(TMTooltip, { content: isWorkGroup ? SDKUI_Localizator.WorkGroup : SDKUI_Localizator.Dossier, children: isWorkGroup ? (_jsx(IconMenuCAWorkingGroups, { color: iconColor, fontSize: 28, onClick: headerClickCallback })) : (_jsx(IconCADossier, { color: iconColor, fontSize: 28, onClick: headerClickCallback })) }), _jsx("div", { onClick: headerClickCallback, style: {
182
+ }, children: [_jsx(TMTooltip, { content: isWorkGroup ? SDKUI_Localizator.WorkGroup : isCaseFlow ? "CaseFlow" : SDKUI_Localizator.Dossier, children: isWorkGroup ? (_jsx(IconMenuCAWorkingGroups, { color: iconColor, fontSize: 28, onClick: headerClickCallback })) : isCaseFlow ? (_jsx(IconCADossier, { color: iconColor, fontSize: 28, onClick: headerClickCallback })) : (_jsx(IconCADossier, { color: iconColor, fontSize: 28, onClick: headerClickCallback })) }), _jsx("div", { onClick: headerClickCallback, style: {
175
183
  whiteSpace: "nowrap",
176
184
  overflow: "hidden",
177
185
  textOverflow: "ellipsis",
@@ -218,7 +226,7 @@ export const removeFileExtension = (filename) => {
218
226
  return filename.includes('.') ? filename.substring(0, filename.lastIndexOf('.')) : filename;
219
227
  };
220
228
  export const BlogPostTitle = (props) => {
221
- const { displayMode, layoutMode, blogPost, isSelected, isOwnComment, searchText, isSys, isHomeBlogPost, showId, handleNavigateToWGs, handleNavigateToDossiers } = props;
229
+ const { displayMode, layoutMode, blogPost, isSelected, isOwnComment, searchText, isSys, isHomeBlogPost, showId, handleNavigateToWGs, handleNavigateToDossiers, handleNavigateToCaseflows } = props;
222
230
  const showOwnerBadge = (displayMode === "stacked" || !isOwnComment) && layoutMode === "extended";
223
231
  let subtitle = "";
224
232
  if (isHomeBlogPost) {
@@ -239,6 +247,9 @@ export const BlogPostTitle = (props) => {
239
247
  if (handleNavigateToDossiers && blogPost.id && blogPost.classID === 'DS') {
240
248
  handleNavigateToDossiers(blogPost);
241
249
  }
250
+ if (handleNavigateToCaseflows && blogPost.id && blogPost.classID === 'CF') {
251
+ handleNavigateToCaseflows(blogPost);
252
+ }
242
253
  };
243
254
  return _jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [showOwnerBadge && (_jsx("div", { style: { width: "40px", height: "40px", flexShrink: 0 }, children: OwnerInitialsBadge(blogPost) })), _jsxs("div", { style: {
244
255
  display: "flex",
@@ -6,6 +6,7 @@ import TMSpinner from '../base/TMSpinner';
6
6
  import { StyledDivHorizontal, StyledTooltipContainer, StyledTooltipItem, StyledTooltipSeparatorItem } from '../base/Styled';
7
7
  import TMTooltip from '../base/TMTooltip';
8
8
  import { TMColors } from '../../utils/theme';
9
+ import styled, { keyframes } from 'styled-components';
9
10
  export var ImageIDList;
10
11
  (function (ImageIDList) {
11
12
  ImageIDList["arrow_001"] = "arrow_001";
@@ -261,13 +262,54 @@ export const TMDcmtTypeIcon = ({ dtd }) => {
261
262
  export const TMDcmtTypeTooltip = ({ dtd, children }) => {
262
263
  return (_jsx("div", { style: { pointerEvents: 'all' }, children: _jsx(TMTooltip, { allowContentInteraction: true, content: renderDTDTooltipContent(dtd), children: children }) }));
263
264
  };
265
+ /** Legge il tipo documento dalle cache dell'SDK senza passare dal mutex. Serve perché
266
+ * l'espansione di una cartella nell'albero ricrea tutte le celle: senza una lettura sincrona
267
+ * ogni riga tornerebbe a mostrare il testo di "nessuna selezione" fino alla fine delle
268
+ * richieste (una per TID, serializzate dal mutex).
269
+ * GetCache restituisce le mappe vive usate dall'SDK, quindi le invalidazioni
270
+ * (RemoveAllWithMetadata, RemoveAllWithoutMetadata, logout) hanno effetto immediato. */
271
+ const getCachedDTD = (tid, tmSession) => {
272
+ if (!tid || tid <= 0)
273
+ return undefined;
274
+ const accessToken = (tmSession ?? SDK_Globals.tmSession)?.SessionDescr?.accessToken;
275
+ if (!accessToken)
276
+ return undefined;
277
+ // La cache senza metadati è popolata dalla login: copre già i tipi documento autorizzati
278
+ return DcmtTypeListCacheService.GetCache(false).get(accessToken)?.find(o => o.id === tid)
279
+ ?? DcmtTypeListCacheService.GetCache(true).get(accessToken)?.find(o => o.id === tid);
280
+ };
281
+ const shimmer = keyframes `
282
+ 0% { background-position: 200% 0; }
283
+ 100% { background-position: -200% 0; }
284
+ `;
285
+ /** Segnaposto animato mostrato al posto del nome durante il caricamento.
286
+ * Grigio semitrasparente: leggibile sia su sfondo chiaro che scuro. */
287
+ const StyledTidSkeleton = styled.span `
288
+ display: inline-block;
289
+ flex: 0 0 auto;
290
+ width: 90px;
291
+ height: 0.9em;
292
+ border-radius: 4px;
293
+ background-image: linear-gradient(90deg, rgba(127, 127, 127, 0.12) 25%, rgba(127, 127, 127, 0.28) 50%, rgba(127, 127, 127, 0.12) 75%);
294
+ background-size: 200% 100%;
295
+ animation: ${shimmer} 1.4s ease-in-out infinite;
296
+
297
+ @media (prefers-reduced-motion: reduce) { animation: none; }
298
+ `;
264
299
  const TMTidViewer = ({ tmSession, tid, did, showIcon = false, color, showId = false, noneSelectionText = `<${SDKUI_Localizator.NoneSelection}>` }) => {
265
- const [dtd, setDtd] = useState();
300
+ const accessToken = (tmSession ?? SDK_Globals.tmSession)?.SessionDescr?.accessToken;
301
+ const [dtd, setDtd] = useState(() => getCachedDTD(tid, tmSession));
302
+ const [isLoading, setIsLoading] = useState(() => !!tid && tid > 0 && !getCachedDTD(tid, tmSession));
266
303
  const fetchData = async (tid, did) => {
267
304
  if (!tid || tid <= 0) {
268
305
  setDtd(undefined);
306
+ setIsLoading(false);
269
307
  return;
270
308
  }
309
+ const cached = getCachedDTD(tid, tmSession);
310
+ if (cached)
311
+ setDtd(cached);
312
+ setIsLoading(!cached);
271
313
  let count = DcmtTypeListCacheService.CacheCount(true, 'all', tmSession);
272
314
  if (count <= 0)
273
315
  TMSpinner.show({ description: `${SDKUI_Localizator.Loading} - ${SDK_Localizator.ListDcmtTypeOrView} ...` });
@@ -281,11 +323,14 @@ const TMTidViewer = ({ tmSession, tid, did, showIcon = false, color, showId = fa
281
323
  finally {
282
324
  if (count <= 0)
283
325
  TMSpinner.hide();
326
+ setIsLoading(false);
284
327
  }
285
328
  };
329
+ // accessToken e non tmSession: la sessione è un oggetto e un'identità nuova a ogni render
330
+ // del chiamante rifarebbe la richiesta senza motivo
286
331
  useEffect(() => {
287
332
  fetchData(tid, did);
288
- }, [tid, did]);
333
+ }, [tid, did, accessToken]);
289
334
  const displayName = () => {
290
335
  if (!dtd)
291
336
  return noneSelectionText;
@@ -295,16 +340,18 @@ const TMTidViewer = ({ tmSession, tid, did, showIcon = false, color, showId = fa
295
340
  whiteSpace: 'nowrap',
296
341
  overflow: 'hidden',
297
342
  textOverflow: 'ellipsis'
298
- }, children: [showIcon && dtd && _jsx(TMDcmtTypeIcon, { dtd: dtd }), _jsx("p", { title: displayName(), style: {
299
- textAlign: 'left',
300
- margin: 0,
301
- marginLeft: showIcon ? '5px' : '',
302
- color: dtd?.isView ? 'red' : (color ?? TMColors.primary),
303
- whiteSpace: 'nowrap',
304
- overflow: 'hidden',
305
- textOverflow: 'ellipsis',
306
- // paddingRight: '10px'
307
- }, children: displayName() }), showId && _jsx("p", { style: { padding: '0px 3px', margin: 0 }, children: `(TID: ${tid})` })] }));
343
+ }, children: [showIcon && dtd && _jsx(TMDcmtTypeIcon, { dtd: dtd }), isLoading && !dtd
344
+ ? _jsx(StyledTidSkeleton, { title: `${SDKUI_Localizator.Loading} ...`, style: { marginLeft: showIcon ? '5px' : undefined } })
345
+ : _jsx("p", { title: displayName(), style: {
346
+ textAlign: 'left',
347
+ margin: 0,
348
+ marginLeft: showIcon ? '5px' : '',
349
+ color: dtd?.isView ? 'red' : (color ?? TMColors.primary),
350
+ whiteSpace: 'nowrap',
351
+ overflow: 'hidden',
352
+ textOverflow: 'ellipsis',
353
+ // paddingRight: '10px'
354
+ }, children: displayName() }), showId && _jsx("p", { style: { padding: '0px 3px', margin: 0 }, children: `(TID: ${tid})` })] }));
308
355
  };
309
356
  export default TMTidViewer;
310
357
  export const cellRenderTID = (data, noneSelectionText) => {
@@ -8,110 +8,110 @@ import { SDKUI_Globals } from './SDKUI_Globals';
8
8
  import { SDKUI_Localizator } from './SDKUI_Localizator';
9
9
  import { IconUpdate, IconOpenInNew } from './TMIcons';
10
10
  import { getAppModuleButtonColors } from './TMUtils';
11
- const Content = styled.div `
12
- display: flex;
13
- flex-direction: column;
14
- width: 100%;
15
- background-color: #ffffff;
11
+ const Content = styled.div `
12
+ display: flex;
13
+ flex-direction: column;
14
+ width: 100%;
15
+ background-color: #ffffff;
16
16
  `;
17
- const Header = styled.div `
18
- display: flex;
19
- flex-direction: column;
20
- align-items: center;
21
- gap: 14px;
22
- padding: 32px 30px 26px 30px;
23
- background-color: ${({ $backgroundColor }) => $backgroundColor};
24
- color: #ffffff;
25
- text-align: center;
17
+ const Header = styled.div `
18
+ display: flex;
19
+ flex-direction: column;
20
+ align-items: center;
21
+ gap: 14px;
22
+ padding: 32px 30px 26px 30px;
23
+ background-color: ${({ $backgroundColor }) => $backgroundColor};
24
+ color: #ffffff;
25
+ text-align: center;
26
26
  `;
27
- const HeaderIcon = styled.div `
28
- display: flex;
29
- align-items: center;
30
- justify-content: center;
31
- width: 68px;
32
- height: 68px;
33
- border-radius: 50%;
34
- background-color: rgba(255, 255, 255, 0.18);
35
- border: 1px solid rgba(255, 255, 255, 0.35);
36
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
27
+ const HeaderIcon = styled.div `
28
+ display: flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ width: 68px;
32
+ height: 68px;
33
+ border-radius: 50%;
34
+ background-color: rgba(255, 255, 255, 0.18);
35
+ border: 1px solid rgba(255, 255, 255, 0.35);
36
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
37
37
  `;
38
- const HeaderTitle = styled.h2 `
39
- margin: 0;
40
- font-size: 22px;
41
- font-weight: 600;
42
- letter-spacing: 0.2px;
38
+ const HeaderTitle = styled.h2 `
39
+ margin: 0;
40
+ font-size: 22px;
41
+ font-weight: 600;
42
+ letter-spacing: 0.2px;
43
43
  `;
44
- const VersionBadge = styled.span `
45
- display: inline-block;
46
- padding: 4px 12px;
47
- border-radius: 999px;
48
- background-color: rgba(255, 255, 255, 0.22);
49
- border: 1px solid rgba(255, 255, 255, 0.4);
50
- font-size: 13px;
51
- font-weight: 600;
52
- letter-spacing: 0.3px;
44
+ const VersionBadge = styled.span `
45
+ display: inline-block;
46
+ padding: 4px 12px;
47
+ border-radius: 999px;
48
+ background-color: rgba(255, 255, 255, 0.22);
49
+ border: 1px solid rgba(255, 255, 255, 0.4);
50
+ font-size: 13px;
51
+ font-weight: 600;
52
+ letter-spacing: 0.3px;
53
53
  `;
54
- const Body = styled.div `
55
- display: flex;
56
- flex-direction: column;
57
- gap: 10px;
58
- padding: 26px 30px 22px 30px;
59
- text-align: center;
54
+ const Body = styled.div `
55
+ display: flex;
56
+ flex-direction: column;
57
+ gap: 10px;
58
+ padding: 26px 30px 22px 30px;
59
+ text-align: center;
60
60
  `;
61
- const Message = styled.p `
62
- margin: 0;
63
- font-size: 15px;
64
- line-height: 1.6;
65
- color: #242424;
61
+ const Message = styled.p `
62
+ margin: 0;
63
+ font-size: 15px;
64
+ line-height: 1.6;
65
+ color: #242424;
66
66
  `;
67
- const SubMessage = styled.p `
68
- margin: 0;
69
- font-size: 13px;
70
- line-height: 1.6;
71
- color: #6b6b6b;
67
+ const SubMessage = styled.p `
68
+ margin: 0;
69
+ font-size: 13px;
70
+ line-height: 1.6;
71
+ color: #6b6b6b;
72
72
  `;
73
- const Footer = styled.div `
74
- display: flex;
75
- flex-direction: row;
76
- justify-content: center;
77
- align-items: center;
78
- flex-wrap: wrap;
79
- gap: 12px;
80
- padding: 0 30px 28px 30px;
73
+ const Footer = styled.div `
74
+ display: flex;
75
+ flex-direction: row;
76
+ justify-content: center;
77
+ align-items: center;
78
+ flex-wrap: wrap;
79
+ gap: 12px;
80
+ padding: 0 30px 28px 30px;
81
81
  `;
82
- const BaseButton = styled.button `
83
- display: inline-flex;
84
- align-items: center;
85
- justify-content: center;
86
- gap: 8px;
87
- min-width: 150px;
88
- padding: 10px 18px;
89
- border-radius: 8px;
90
- font-size: 14px;
91
- font-weight: 600;
92
- cursor: pointer;
93
- transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
82
+ const BaseButton = styled.button `
83
+ display: inline-flex;
84
+ align-items: center;
85
+ justify-content: center;
86
+ gap: 8px;
87
+ min-width: 150px;
88
+ padding: 10px 18px;
89
+ border-radius: 8px;
90
+ font-size: 14px;
91
+ font-weight: 600;
92
+ cursor: pointer;
93
+ transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
94
94
  `;
95
- const PrimaryButton = styled(BaseButton) `
96
- border: 1px solid ${({ $base }) => $base};
97
- background-color: ${({ $base }) => $base};
98
- color: #ffffff;
99
-
100
- &:hover {
101
- background-color: ${({ $hover }) => $hover};
102
- border-color: ${({ $hover }) => $hover};
103
- box-shadow: 0 2px 8px ${({ $shadow }) => $shadow};
104
- }
95
+ const PrimaryButton = styled(BaseButton) `
96
+ border: 1px solid ${({ $base }) => $base};
97
+ background-color: ${({ $base }) => $base};
98
+ color: #ffffff;
99
+
100
+ &:hover {
101
+ background-color: ${({ $hover }) => $hover};
102
+ border-color: ${({ $hover }) => $hover};
103
+ box-shadow: 0 2px 8px ${({ $shadow }) => $shadow};
104
+ }
105
105
  `;
106
- const SecondaryButton = styled(BaseButton) `
107
- border: 1px solid #d5d5d5;
108
- background-color: #ffffff;
109
- color: #444444;
110
-
111
- &:hover {
112
- background-color: #f3f3f3;
113
- border-color: #bdbdbd;
114
- }
106
+ const SecondaryButton = styled(BaseButton) `
107
+ border: 1px solid #d5d5d5;
108
+ background-color: #ffffff;
109
+ color: #444444;
110
+
111
+ &:hover {
112
+ background-color: #f3f3f3;
113
+ border-color: #bdbdbd;
114
+ }
115
115
  `;
116
116
  export const TMAppUpdatedModal = () => {
117
117
  const deviceType = useDeviceType();
@@ -53,6 +53,8 @@ export declare const PDGS_COLORS: {
53
53
  DOSSIER: string;
54
54
  DOCUMENT: string;
55
55
  WORKING_GROUP: string;
56
+ /** Istanza di CaseFlow: viola, per non confonderla ne' con la pratica (arancio) ne' con il gruppo di lavoro (verde). */
57
+ CASEFLOW: string;
56
58
  };
57
59
  export declare const getPdgsIconMap: (fontSize?: number) => Map<PdGs, JSX.Element>;
58
60
  export declare const convertSearchResultDescriptorToFileItems: (documents: Array<SearchResultDescriptor>) => Array<FileItem>;
@@ -270,6 +270,8 @@ export const PDGS_COLORS = {
270
270
  DOSSIER: '#e65b00',
271
271
  DOCUMENT: '#b38600',
272
272
  WORKING_GROUP: '#009700',
273
+ /** Istanza di CaseFlow: viola, per non confonderla ne' con la pratica (arancio) ne' con il gruppo di lavoro (verde). */
274
+ CASEFLOW: '#6a3fb5',
273
275
  };
274
276
  export const getPdgsIconMap = (fontSize = 20) => {
275
277
  return new Map([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react",
3
- "version": "6.22.0-dev2.45",
3
+ "version": "6.22.0-dev2.47",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",