@topconsultnpm/sdkui-react 6.22.0-dev2.47 → 6.22.0-dev2.49
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/TMPanel.d.ts +4 -0
- package/lib/components/base/TMPanel.js +198 -144
- package/lib/components/features/archive/TMArchive.js +17 -3
- package/lib/components/features/documents/TMDcmtForm.js +39 -1
- package/lib/components/features/search/TMSearch.js +57 -8
- package/lib/components/features/search/TMSearchQueryPanel.d.ts +3 -0
- package/lib/components/features/search/TMSearchQueryPanel.js +310 -251
- package/lib/components/features/search/TMTreeSelector.js +136 -118
- package/lib/helper/TMAppUpdatedModal.js +93 -93
- package/lib/helper/queryHelper.d.ts +4 -1
- package/lib/helper/queryHelper.js +5 -2
- package/package.json +1 -1
|
@@ -24,11 +24,9 @@ import TMToppyMessage from '../../../helper/TMToppyMessage';
|
|
|
24
24
|
import TMMetadataSorterForm from './TMMetadataSorterForm';
|
|
25
25
|
import TMMetadataOutputForm from './TMMetadataOutputForm';
|
|
26
26
|
import TMTooltip from '../../base/TMTooltip';
|
|
27
|
-
const TMSearchQueryPanel = ({ isSearchViewOnScreen = true, fromDTD, showBackToResultButton, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, SQD, inputMids, onSearchCompleted, onSqdSaved, onBack, onClosePanel, allowMaximize = true, onMaximizePanel, onBackToResult, passToArchiveCallback, maxDcmtsToBeReturned }) => {
|
|
27
|
+
const TMSearchQueryPanel = ({ isSearchViewOnScreen = true, fromDTD, showBackToResultButton, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, SQD, inputMids, onSearchCompleted, onSqdSaved, onBack, onClosePanel, allowMaximize = true, onMaximizePanel, onBackToResult, passToArchiveCallback, maxDcmtsToBeReturned, onRefreshRequested, onDcmtTypeReloaded }) => {
|
|
28
28
|
const [confirmQueryParams, ConfirmQueryParamsDialog] = useQueryParametersDialog();
|
|
29
29
|
const [qd, setQd] = useState();
|
|
30
|
-
const [shouldSearch, setShouldSearch] = useState(false);
|
|
31
|
-
const [isQueryPanelActive, setIsQueryPanelActive] = useState(false);
|
|
32
30
|
const [lastQdParams, setLastQdParams] = useState([]);
|
|
33
31
|
const [dcmtTypesList, setDcmtTypesList] = useState([]);
|
|
34
32
|
const [showSqdForm, setShowSqdForm] = useState(false);
|
|
@@ -45,12 +43,19 @@ const TMSearchQueryPanel = ({ isSearchViewOnScreen = true, fromDTD, showBackToRe
|
|
|
45
43
|
const isMobile = deviceType === DeviceType.MOBILE;
|
|
46
44
|
let initialMaxItems = deviceType === DeviceType.MOBILE ? 8 : 12;
|
|
47
45
|
const tmPanelRef = useRef(null);
|
|
46
|
+
// Contenitore del form: l'Invio avvia la ricerca solo se il focus si trova al suo interno
|
|
47
|
+
const contentRef = useRef(null);
|
|
48
48
|
// Passando nella vista risultati si prenota il focus, che verrà ripreso al rientro nella ricerca
|
|
49
49
|
const shouldRefocusOnReturnRef = useRef(false);
|
|
50
50
|
const appliedInputMidsRef = useRef(null);
|
|
51
51
|
const pendingMidsRef = useRef(null);
|
|
52
|
-
//
|
|
53
|
-
const
|
|
52
|
+
// Impedisce ricerche sovrapposte (doppio Invio, o Invio mentre la precedente e' ancora in corso)
|
|
53
|
+
const isSearchingRef = useRef(false);
|
|
54
|
+
// Snapshot letti da runSearch al momento dell'esecuzione: evitano di cercare con dati stantii
|
|
55
|
+
const qdRef = useRef(undefined);
|
|
56
|
+
const showAdvancedSearchRef = useRef(false);
|
|
57
|
+
const searchAsyncRef = useRef();
|
|
58
|
+
const reloadMetadataAsyncRef = useRef();
|
|
54
59
|
// Tornando dalla vista risultati il pannello dei metadati di ricerca riprende il focus,
|
|
55
60
|
// così è lui quello evidenziato: prendendo il focus gli altri pannelli ricevono l'onBlur e si disattivano
|
|
56
61
|
useEffect(() => {
|
|
@@ -135,39 +140,6 @@ const TMSearchQueryPanel = ({ isSearchViewOnScreen = true, fromDTD, showBackToRe
|
|
|
135
140
|
setQd({ ...qd, where: newWhere });
|
|
136
141
|
setShowAllMdWhere(true);
|
|
137
142
|
}, [qd, fromDTD, inputMids]);
|
|
138
|
-
// Eseguire la ricerca quando shouldSearch è true e qd è definito
|
|
139
|
-
useEffect(() => {
|
|
140
|
-
if (shouldSearch && qd) {
|
|
141
|
-
const executeSearch = async () => {
|
|
142
|
-
// Legge e consuma il flag "accoda" impostato dall'azione secondaria del bottone Ricerca
|
|
143
|
-
const append = appendNextSearchRef.current;
|
|
144
|
-
appendNextSearchRef.current = false;
|
|
145
|
-
await searchAsync(qd, showAdvancedSearch, append);
|
|
146
|
-
setShouldSearch(false); // Resetta il trigger dopo la ricerca
|
|
147
|
-
};
|
|
148
|
-
executeSearch();
|
|
149
|
-
}
|
|
150
|
-
}, [shouldSearch, qd, showAdvancedSearch]);
|
|
151
|
-
// Se il pannello è attivo e non ci sono modali aperti, rimuove il focus e avvia la ricerca
|
|
152
|
-
useEffect(() => {
|
|
153
|
-
const handleKeyDown = (e) => {
|
|
154
|
-
if (isQueryPanelActive && !modalIsOpen) {
|
|
155
|
-
if (e.key === 'Enter') {
|
|
156
|
-
e.preventDefault();
|
|
157
|
-
// Disattiva l'elemento attivo per forzare l'onBlur
|
|
158
|
-
if (document.activeElement instanceof HTMLElement) {
|
|
159
|
-
document.activeElement.blur();
|
|
160
|
-
}
|
|
161
|
-
// Attiva il flag di ricerca. L'useEffect sopra gestirà l'attesa di qd.
|
|
162
|
-
setShouldSearch(true);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
window.addEventListener('keydown', handleKeyDown);
|
|
167
|
-
return () => {
|
|
168
|
-
window.removeEventListener('keydown', handleKeyDown);
|
|
169
|
-
};
|
|
170
|
-
}, [isQueryPanelActive, modalIsOpen]);
|
|
171
143
|
const setDataAsync = async (sqd) => {
|
|
172
144
|
let newSqd = (sqd?.id == 1) ? sqd : await SavedQueryCacheService.GetAsync(sqd?.id);
|
|
173
145
|
let newQd = SearchEngine.NormalizeQueryDescriptor(newSqd?.qd);
|
|
@@ -199,11 +171,92 @@ const TMSearchQueryPanel = ({ isSearchViewOnScreen = true, fromDTD, showBackToRe
|
|
|
199
171
|
let results = [];
|
|
200
172
|
if (searchResult?.result)
|
|
201
173
|
results.push(searchResult?.result);
|
|
202
|
-
// Before notifying onSearchComplete, let's set the panel to NOT active
|
|
203
|
-
setIsQueryPanelActive(false);
|
|
204
174
|
onSearchCompleted?.(results, searchResult?.qd, append);
|
|
205
175
|
}
|
|
206
176
|
};
|
|
177
|
+
// Tra l'apertura della ricerca e il click su Ricerca la lista dei metadati del tipo documento può essere
|
|
178
|
+
// cambiata (metadati aggiunti o rimossi lato amministrazione): prima di cercare si rileggono dal server.
|
|
179
|
+
// I valori digitati restano, mentre i filtri sui metadati non più disponibili vengono scartati.
|
|
180
|
+
// Restituisce il qd con cui eseguire la ricerca.
|
|
181
|
+
const reloadMetadataAsync = async (qdInput) => {
|
|
182
|
+
if (!qdInput?.from?.tid)
|
|
183
|
+
return qdInput;
|
|
184
|
+
let dcmtTypes;
|
|
185
|
+
try {
|
|
186
|
+
TMSpinner.show({ description: `${SDKUI_Localizator.Loading} ...`, backgroundColor: 'transparent' });
|
|
187
|
+
dcmtTypes = await getDcmtTypesByQdAsync(qdInput, true);
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
TMSpinner.hide();
|
|
191
|
+
}
|
|
192
|
+
// Rilettura non riuscita: si cerca con i metadati che si hanno, senza scartare nulla
|
|
193
|
+
if (dcmtTypes.length <= 0)
|
|
194
|
+
return qdInput;
|
|
195
|
+
setDcmtTypesList(dcmtTypes);
|
|
196
|
+
const reloadedFromDTD = dcmtTypes.find(o => o.id === qdInput.from?.tid);
|
|
197
|
+
if (reloadedFromDTD)
|
|
198
|
+
onDcmtTypeReloaded?.(reloadedFromDTD);
|
|
199
|
+
const { qd: newQd, removedCount } = dropUnavailableMetadataFromQd(qdInput, dcmtTypes);
|
|
200
|
+
if (removedCount <= 0)
|
|
201
|
+
return qdInput;
|
|
202
|
+
setQd(newQd);
|
|
203
|
+
return newQd;
|
|
204
|
+
};
|
|
205
|
+
// Allineati a ogni render: runSearch li legge quando esegue, non quando viene creata
|
|
206
|
+
qdRef.current = qd;
|
|
207
|
+
showAdvancedSearchRef.current = showAdvancedSearch;
|
|
208
|
+
searchAsyncRef.current = searchAsync;
|
|
209
|
+
reloadMetadataAsyncRef.current = reloadMetadataAsync;
|
|
210
|
+
// Unico punto di avvio della ricerca: bottone Ricerca, Ricerca e accoda e tasto Invio passano tutti da qui
|
|
211
|
+
const runSearch = useCallback((append) => {
|
|
212
|
+
if (isSearchingRef.current)
|
|
213
|
+
return;
|
|
214
|
+
isSearchingRef.current = true;
|
|
215
|
+
// Toglie il focus per far committare il valore del campo in editing
|
|
216
|
+
if (document.activeElement instanceof HTMLElement)
|
|
217
|
+
document.activeElement.blur();
|
|
218
|
+
// Alcuni editor committano il valore in modo asincrono: si attende un tick prima di leggere il qd definitivo
|
|
219
|
+
setTimeout(async () => {
|
|
220
|
+
try {
|
|
221
|
+
// I metadati si rileggono a ogni ricerca: si cerca sempre sulla lista aggiornata
|
|
222
|
+
const qdToSearch = await reloadMetadataAsyncRef.current?.(qdRef.current) ?? qdRef.current;
|
|
223
|
+
await searchAsyncRef.current?.(qdToSearch, showAdvancedSearchRef.current, append);
|
|
224
|
+
}
|
|
225
|
+
catch (ex) {
|
|
226
|
+
TMExceptionBoxManager.show({ exception: ex });
|
|
227
|
+
}
|
|
228
|
+
finally {
|
|
229
|
+
isSearchingRef.current = false;
|
|
230
|
+
}
|
|
231
|
+
}, 0);
|
|
232
|
+
}, []);
|
|
233
|
+
// I dialoghi (TMModal/chooser) escono da contentRef perche' DevExtreme Popup li rende in un portale su document.body:
|
|
234
|
+
// il controllo di contenimento li esclude gia'. Questi flag coprono il caso in cui il focus sia sul contenitore del pannello.
|
|
235
|
+
// NB: si usano solo flag locali affidabili, non modalIsOpen, che resterebbe a true se un chooser venisse smontato senza onClose.
|
|
236
|
+
const isAnyDialogOpen = showFiltersConfig || showOutputConfig || showMetadataSorterForm || showSqdForm || showDistinctValuesPanel;
|
|
237
|
+
// L'Invio avvia SEMPRE la ricerca quando il focus e' nel form di ricerca (o sul pannello che lo contiene).
|
|
238
|
+
// Il criterio e' il contenimento nel DOM, non uno stato di "pannello attivo" che puo' desincronizzarsi.
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
const handleKeyDown = (e) => {
|
|
241
|
+
if (e.key !== 'Enter' || e.repeat)
|
|
242
|
+
return;
|
|
243
|
+
if (isAnyDialogOpen)
|
|
244
|
+
return;
|
|
245
|
+
const content = contentRef.current;
|
|
246
|
+
const target = e.target;
|
|
247
|
+
if (!content || !target)
|
|
248
|
+
return;
|
|
249
|
+
const isInsideForm = content.contains(target);
|
|
250
|
+
// Al rientro dai risultati il focus e' sul contenitore del pannello, che e' antenato del form
|
|
251
|
+
const isPanelContainer = target !== document.body && target !== document.documentElement && target.contains(content);
|
|
252
|
+
if (!isInsideForm && !isPanelContainer)
|
|
253
|
+
return;
|
|
254
|
+
e.preventDefault();
|
|
255
|
+
runSearch(false);
|
|
256
|
+
};
|
|
257
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
258
|
+
return () => { window.removeEventListener('keydown', handleKeyDown); };
|
|
259
|
+
}, [isAnyDialogOpen, runSearch]);
|
|
207
260
|
const updateIsModalOpen = useCallback((isOpen) => {
|
|
208
261
|
setModalIsOpen(isOpen);
|
|
209
262
|
}, []);
|
|
@@ -230,36 +283,15 @@ const TMSearchQueryPanel = ({ isSearchViewOnScreen = true, fromDTD, showBackToRe
|
|
|
230
283
|
}
|
|
231
284
|
}
|
|
232
285
|
};
|
|
233
|
-
const handleSearchButtonClick = useCallback(
|
|
234
|
-
|
|
235
|
-
if (document.activeElement instanceof HTMLElement) {
|
|
236
|
-
document.activeElement.blur();
|
|
237
|
-
}
|
|
238
|
-
// Ricerca normale: azzera l'eventuale flag accoda e avvia
|
|
239
|
-
appendNextSearchRef.current = false;
|
|
240
|
-
setShouldSearch(true);
|
|
241
|
-
}, []);
|
|
242
|
-
const handleSearchAndAppendClick = useCallback(() => {
|
|
243
|
-
// Propagazione blur
|
|
244
|
-
if (document.activeElement instanceof HTMLElement) {
|
|
245
|
-
document.activeElement.blur();
|
|
246
|
-
}
|
|
247
|
-
// Ricerca e accoda: imposta il flag consumato da executeSearch
|
|
248
|
-
appendNextSearchRef.current = true;
|
|
249
|
-
setShouldSearch(true);
|
|
250
|
-
}, []);
|
|
286
|
+
const handleSearchButtonClick = useCallback(() => { runSearch(false); }, [runSearch]);
|
|
287
|
+
const handleSearchAndAppendClick = useCallback(() => { runSearch(true); }, [runSearch]);
|
|
251
288
|
const searchButtonSecondaryActions = useMemo(() => [{
|
|
252
289
|
name: SDKUI_Localizator.SearchAndAppend,
|
|
253
290
|
icon: _jsx(IconSearch, {}),
|
|
254
291
|
onClick: handleSearchAndAppendClick
|
|
255
292
|
}], [handleSearchAndAppendClick]);
|
|
256
293
|
const handleQdChanged = useCallback((newQd) => {
|
|
257
|
-
|
|
258
|
-
setQd(newQd);
|
|
259
|
-
}
|
|
260
|
-
}, []);
|
|
261
|
-
const handlePanelActiveChanged = useCallback((isActive) => {
|
|
262
|
-
setIsQueryPanelActive(isActive);
|
|
294
|
+
setQd(prev => deepCompare(prev, newQd) ? prev : newQd);
|
|
263
295
|
}, []);
|
|
264
296
|
const handleAdvancedMenuClick = useCallback((e) => {
|
|
265
297
|
if (e.button === AdvancedMenuButtons.DistinctValues) {
|
|
@@ -357,10 +389,10 @@ const TMSearchQueryPanel = ({ isSearchViewOnScreen = true, fromDTD, showBackToRe
|
|
|
357
389
|
return false;
|
|
358
390
|
return qd?.orderBy && qd.orderBy.length > 0;
|
|
359
391
|
}, [showAdvancedSearch, qd?.orderBy]);
|
|
360
|
-
return (_jsxs(_Fragment, { children: [_jsxs(TMPanel, { ref: tmPanelRef, title: fromDTD?.nameLoc ?? SDKUI_Localizator.Search_Metadata, allowMaximize: allowMaximize, onMaximize: onMaximizePanel, onHeaderDoubleClick: onMaximizePanel, onBack: onBack,
|
|
361
|
-
_jsx(ContextMenu, { items: contextMenuItems, trigger: "left", children: _jsx(TMButton, { btnStyle: 'icon', caption: 'Altro', icon: _jsx(IconMenuVertical, { color: 'white' }), showTooltip: false
|
|
392
|
+
return (_jsxs(_Fragment, { children: [_jsxs(TMPanel, { ref: tmPanelRef, title: fromDTD?.nameLoc ?? SDKUI_Localizator.Search_Metadata, allowMaximize: allowMaximize, onMaximize: onMaximizePanel, onHeaderDoubleClick: onMaximizePanel, onBack: onBack, onRefresh: (!SQD || showSqdForm) ? undefined : onRefreshRequested, toolbar: _jsx(_Fragment, { children: (SQD && !showSqdForm) ?
|
|
393
|
+
_jsx(ContextMenu, { items: contextMenuItems, trigger: "left", children: _jsx(TMButton, { btnStyle: 'icon', caption: 'Altro', icon: _jsx(IconMenuVertical, { color: 'white' }), showTooltip: false }) })
|
|
362
394
|
: _jsx(_Fragment, {}) }), children: [_jsx(ConfirmQueryParamsDialog, {}), SQD
|
|
363
|
-
? _jsxs("div", { onContextMenu: (e) => e.preventDefault(), style: { height: '100%', width: '100%', position: 'relative', display: showSqdForm ? 'none' : 'flex', flexDirection: 'column', gap: 5 }, children: [showAdvancedSearch
|
|
395
|
+
? _jsxs("div", { ref: contentRef, onContextMenu: (e) => e.preventDefault(), style: { height: '100%', width: '100%', position: 'relative', display: showSqdForm ? 'none' : 'flex', flexDirection: 'column', gap: 5 }, children: [showAdvancedSearch
|
|
364
396
|
? _jsx(TMQueryEditor, { formMode: FormModes.Update, showToolbar: false, inputData: qd, validateSelect: true, showApply: false, onQDChanged: handleQdChanged, updateIsModalOpen: updateIsModalOpen })
|
|
365
397
|
: _jsx(TMSearchQueryEditor, { qd: qd, dcmtTypesList: dcmtTypesList, isExpertMode: isExpertMode, showAllMdWhere: showAllMdWhere, onQdChanged: handleQdChanged, onFocusedMetadataChanged: setFocusedTidMid, onAdvancedMenuClick: handleAdvancedMenuClick, updateIsModalOpen: updateIsModalOpen }), shouldShowOutputBadges && qd?.select && qd.select.length > 0 && (_jsxs(StyledSelectBadgesContainer, { children: [_jsxs(StyledSelectLabel, { children: [SDK_Localizator.QuerySelect, ":"] }), _jsx(StyledSelectBadgesWrapper, { children: (() => {
|
|
366
398
|
const selectItems = qd.select ?? [];
|
|
@@ -418,6 +450,33 @@ const TMSearchQueryPanel = ({ isSearchViewOnScreen = true, fromDTD, showBackToRe
|
|
|
418
450
|
} })] }));
|
|
419
451
|
};
|
|
420
452
|
export default TMSearchQueryPanel;
|
|
453
|
+
/** Scarta dal qd filtri, colonne e ordinamenti sui metadati non più esistenti o non più accessibili, in base
|
|
454
|
+
* alla lista dei metadati appena riletta dal server. I valori digitati su quelli ancora presenti si mantengono. */
|
|
455
|
+
const dropUnavailableMetadataFromQd = (qdInput, dcmtTypes) => {
|
|
456
|
+
const metadataOf = (tid) => dcmtTypes.find(o => o.id === tid)?.metadata;
|
|
457
|
+
// Un tipo documento non riletto non è verificabile: i suoi item si mantengono così come sono.
|
|
458
|
+
// Sui metadati di sistema si verifica solo l'esistenza: i loro permessi non li rendono inutilizzabili.
|
|
459
|
+
const isStillAvailable = (tid, mid, isUsable) => {
|
|
460
|
+
const metadata = metadataOf(tid);
|
|
461
|
+
if (!metadata || metadata.length <= 0 || mid == undefined)
|
|
462
|
+
return true;
|
|
463
|
+
const md = metadata.find(o => o.id === mid);
|
|
464
|
+
if (!md)
|
|
465
|
+
return false;
|
|
466
|
+
return md.isSystem == 1 || isUsable(md);
|
|
467
|
+
};
|
|
468
|
+
const canSearch = (md) => md.perm?.canSearch != AccessLevels.No;
|
|
469
|
+
const canOutput = (md) => !(md.perm?.canView == AccessLevels.No && md.perm?.canUpdate == AccessLevels.No);
|
|
470
|
+
const where = qdInput.where?.filter(o => isStillAvailable(o.tid, o.mid, canSearch)) ?? [];
|
|
471
|
+
const select = qdInput.select?.filter(o => isStillAvailable(o.tid, o.mid, canOutput)) ?? [];
|
|
472
|
+
const orderBy = qdInput.orderBy?.filter(o => isStillAvailable(o.tid, o.mid, canOutput)) ?? [];
|
|
473
|
+
const removedCount = ((qdInput.where?.length ?? 0) - where.length)
|
|
474
|
+
+ ((qdInput.select?.length ?? 0) - select.length)
|
|
475
|
+
+ ((qdInput.orderBy?.length ?? 0) - orderBy.length);
|
|
476
|
+
let qd = new QueryDescriptor();
|
|
477
|
+
qd.init({ ...qdInput, where: where, select: select, orderBy: orderBy });
|
|
478
|
+
return { qd: qd, removedCount: removedCount };
|
|
479
|
+
};
|
|
421
480
|
const searchByQdAsync = async (qdInput, searchParams) => {
|
|
422
481
|
let result;
|
|
423
482
|
let qdSearch;
|
|
@@ -475,205 +534,205 @@ export const refreshLastSearch = async (qd) => {
|
|
|
475
534
|
// =============================================================================
|
|
476
535
|
// STYLED COMPONENTS per i badge degli ordinamenti
|
|
477
536
|
// =============================================================================
|
|
478
|
-
const StyledOrderByBadgesContainer = styled.div `
|
|
479
|
-
display: flex;
|
|
480
|
-
flex-wrap: wrap;
|
|
481
|
-
align-items: center;
|
|
482
|
-
gap: 8px;
|
|
483
|
-
padding: 6px 10px;
|
|
484
|
-
margin: 1px 0;
|
|
485
|
-
background: linear-gradient(135deg, rgba(74, 150, 210, 0.08) 0%, rgba(37, 89, 165, 0.08) 100%);
|
|
486
|
-
border-radius: 8px;
|
|
487
|
-
border: 1px solid rgba(74, 150, 210, 0.2);
|
|
537
|
+
const StyledOrderByBadgesContainer = styled.div `
|
|
538
|
+
display: flex;
|
|
539
|
+
flex-wrap: wrap;
|
|
540
|
+
align-items: center;
|
|
541
|
+
gap: 8px;
|
|
542
|
+
padding: 6px 10px;
|
|
543
|
+
margin: 1px 0;
|
|
544
|
+
background: linear-gradient(135deg, rgba(74, 150, 210, 0.08) 0%, rgba(37, 89, 165, 0.08) 100%);
|
|
545
|
+
border-radius: 8px;
|
|
546
|
+
border: 1px solid rgba(74, 150, 210, 0.2);
|
|
488
547
|
`;
|
|
489
|
-
const StyledOrderByLabel = styled.span `
|
|
490
|
-
font-size: 0.82rem;
|
|
491
|
-
font-weight: 600;
|
|
492
|
-
color: #4A96D2;
|
|
493
|
-
white-space: nowrap;
|
|
548
|
+
const StyledOrderByLabel = styled.span `
|
|
549
|
+
font-size: 0.82rem;
|
|
550
|
+
font-weight: 600;
|
|
551
|
+
color: #4A96D2;
|
|
552
|
+
white-space: nowrap;
|
|
494
553
|
`;
|
|
495
|
-
const StyledOrderByBadgesWrapper = styled.div `
|
|
496
|
-
display: flex;
|
|
497
|
-
flex-wrap: wrap;
|
|
498
|
-
gap: 6px;
|
|
499
|
-
align-items: center;
|
|
500
|
-
flex: 1;
|
|
554
|
+
const StyledOrderByBadgesWrapper = styled.div `
|
|
555
|
+
display: flex;
|
|
556
|
+
flex-wrap: wrap;
|
|
557
|
+
gap: 6px;
|
|
558
|
+
align-items: center;
|
|
559
|
+
flex: 1;
|
|
501
560
|
`;
|
|
502
|
-
const StyledOrderByBadge = styled.div `
|
|
503
|
-
display: inline-flex;
|
|
504
|
-
align-items: center;
|
|
505
|
-
gap: 4px;
|
|
506
|
-
padding: 3px 8px;
|
|
507
|
-
background: white;
|
|
508
|
-
border: 1px solid #d1d9e6;
|
|
509
|
-
border-radius: 16px;
|
|
510
|
-
font-size: 0.82rem;
|
|
511
|
-
color: #333;
|
|
512
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
513
|
-
|
|
514
|
-
.badge-name {
|
|
515
|
-
max-width: 120px;
|
|
516
|
-
overflow: hidden;
|
|
517
|
-
text-overflow: ellipsis;
|
|
518
|
-
white-space: nowrap;
|
|
519
|
-
|
|
520
|
-
@media (max-width: 600px) {
|
|
521
|
-
max-width: 80px;
|
|
522
|
-
}
|
|
523
|
-
}
|
|
561
|
+
const StyledOrderByBadge = styled.div `
|
|
562
|
+
display: inline-flex;
|
|
563
|
+
align-items: center;
|
|
564
|
+
gap: 4px;
|
|
565
|
+
padding: 3px 8px;
|
|
566
|
+
background: white;
|
|
567
|
+
border: 1px solid #d1d9e6;
|
|
568
|
+
border-radius: 16px;
|
|
569
|
+
font-size: 0.82rem;
|
|
570
|
+
color: #333;
|
|
571
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
572
|
+
|
|
573
|
+
.badge-name {
|
|
574
|
+
max-width: 120px;
|
|
575
|
+
overflow: hidden;
|
|
576
|
+
text-overflow: ellipsis;
|
|
577
|
+
white-space: nowrap;
|
|
578
|
+
|
|
579
|
+
@media (max-width: 600px) {
|
|
580
|
+
max-width: 80px;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
524
583
|
`;
|
|
525
|
-
const StyledOrderByBadgeMore = styled.div `
|
|
526
|
-
display: inline-flex;
|
|
527
|
-
align-items: center;
|
|
528
|
-
justify-content: center;
|
|
529
|
-
padding: 3px 10px;
|
|
530
|
-
background: linear-gradient(135deg, #4A96D2 0%, #2559A5 100%);
|
|
531
|
-
border: none;
|
|
532
|
-
border-radius: 16px;
|
|
533
|
-
font-size: 0.82rem;
|
|
534
|
-
font-weight: 600;
|
|
535
|
-
color: white;
|
|
536
|
-
cursor: pointer;
|
|
537
|
-
transition: all 0.2s ease;
|
|
538
|
-
|
|
539
|
-
&:hover {
|
|
540
|
-
transform: scale(1.05);
|
|
541
|
-
box-shadow: 0 2px 6px rgba(74, 150, 210, 0.4);
|
|
542
|
-
}
|
|
584
|
+
const StyledOrderByBadgeMore = styled.div `
|
|
585
|
+
display: inline-flex;
|
|
586
|
+
align-items: center;
|
|
587
|
+
justify-content: center;
|
|
588
|
+
padding: 3px 10px;
|
|
589
|
+
background: linear-gradient(135deg, #4A96D2 0%, #2559A5 100%);
|
|
590
|
+
border: none;
|
|
591
|
+
border-radius: 16px;
|
|
592
|
+
font-size: 0.82rem;
|
|
593
|
+
font-weight: 600;
|
|
594
|
+
color: white;
|
|
595
|
+
cursor: pointer;
|
|
596
|
+
transition: all 0.2s ease;
|
|
597
|
+
|
|
598
|
+
&:hover {
|
|
599
|
+
transform: scale(1.05);
|
|
600
|
+
box-shadow: 0 2px 6px rgba(74, 150, 210, 0.4);
|
|
601
|
+
}
|
|
543
602
|
`;
|
|
544
|
-
const StyledOrderByEditButton = styled.div `
|
|
545
|
-
display: flex;
|
|
546
|
-
align-items: center;
|
|
547
|
-
justify-content: center;
|
|
548
|
-
width: 28px;
|
|
549
|
-
height: 28px;
|
|
550
|
-
border-radius: 4px;
|
|
551
|
-
color: #4A96D2;
|
|
552
|
-
cursor: pointer;
|
|
553
|
-
transition: all 0.2s ease;
|
|
554
|
-
|
|
555
|
-
&:hover {
|
|
556
|
-
background-color: rgba(74, 150, 210, 0.15);
|
|
557
|
-
}
|
|
603
|
+
const StyledOrderByEditButton = styled.div `
|
|
604
|
+
display: flex;
|
|
605
|
+
align-items: center;
|
|
606
|
+
justify-content: center;
|
|
607
|
+
width: 28px;
|
|
608
|
+
height: 28px;
|
|
609
|
+
border-radius: 4px;
|
|
610
|
+
color: #4A96D2;
|
|
611
|
+
cursor: pointer;
|
|
612
|
+
transition: all 0.2s ease;
|
|
613
|
+
|
|
614
|
+
&:hover {
|
|
615
|
+
background-color: rgba(74, 150, 210, 0.15);
|
|
616
|
+
}
|
|
558
617
|
`;
|
|
559
618
|
// =============================================================================
|
|
560
619
|
// STYLED COMPONENTS per i badge degli output (select) - colore verde/teal
|
|
561
620
|
// =============================================================================
|
|
562
|
-
const StyledSelectBadgesContainer = styled.div `
|
|
563
|
-
display: flex;
|
|
564
|
-
flex-wrap: wrap;
|
|
565
|
-
align-items: center;
|
|
566
|
-
gap: 8px;
|
|
567
|
-
padding: 6px 10px;
|
|
568
|
-
margin: 1px 0;
|
|
569
|
-
background: linear-gradient(135deg, rgba(46, 125, 50, 0.08) 0%, rgba(27, 94, 32, 0.08) 100%);
|
|
570
|
-
border-radius: 8px;
|
|
571
|
-
border: 1px solid rgba(46, 125, 50, 0.2);
|
|
621
|
+
const StyledSelectBadgesContainer = styled.div `
|
|
622
|
+
display: flex;
|
|
623
|
+
flex-wrap: wrap;
|
|
624
|
+
align-items: center;
|
|
625
|
+
gap: 8px;
|
|
626
|
+
padding: 6px 10px;
|
|
627
|
+
margin: 1px 0;
|
|
628
|
+
background: linear-gradient(135deg, rgba(46, 125, 50, 0.08) 0%, rgba(27, 94, 32, 0.08) 100%);
|
|
629
|
+
border-radius: 8px;
|
|
630
|
+
border: 1px solid rgba(46, 125, 50, 0.2);
|
|
572
631
|
`;
|
|
573
|
-
const StyledSelectLabel = styled.span `
|
|
574
|
-
font-size: 0.82rem;
|
|
575
|
-
font-weight: 600;
|
|
576
|
-
color: #2E7D32;
|
|
577
|
-
white-space: nowrap;
|
|
632
|
+
const StyledSelectLabel = styled.span `
|
|
633
|
+
font-size: 0.82rem;
|
|
634
|
+
font-weight: 600;
|
|
635
|
+
color: #2E7D32;
|
|
636
|
+
white-space: nowrap;
|
|
578
637
|
`;
|
|
579
|
-
const StyledSelectBadgesWrapper = styled.div `
|
|
580
|
-
display: flex;
|
|
581
|
-
flex-wrap: wrap;
|
|
582
|
-
gap: 6px;
|
|
583
|
-
align-items: center;
|
|
584
|
-
flex: 1;
|
|
638
|
+
const StyledSelectBadgesWrapper = styled.div `
|
|
639
|
+
display: flex;
|
|
640
|
+
flex-wrap: wrap;
|
|
641
|
+
gap: 6px;
|
|
642
|
+
align-items: center;
|
|
643
|
+
flex: 1;
|
|
585
644
|
`;
|
|
586
|
-
const StyledSelectBadge = styled.div `
|
|
587
|
-
display: inline-flex;
|
|
588
|
-
align-items: center;
|
|
589
|
-
gap: 4px;
|
|
590
|
-
padding: 3px 8px;
|
|
591
|
-
background: white;
|
|
592
|
-
border: 1px solid #c8e6c9;
|
|
593
|
-
border-radius: 16px;
|
|
594
|
-
font-size: 0.82rem;
|
|
595
|
-
color: #333;
|
|
596
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
597
|
-
|
|
598
|
-
.badge-name {
|
|
599
|
-
max-width: 120px;
|
|
600
|
-
overflow: hidden;
|
|
601
|
-
text-overflow: ellipsis;
|
|
602
|
-
white-space: nowrap;
|
|
603
|
-
|
|
604
|
-
@media (max-width: 600px) {
|
|
605
|
-
max-width: 80px;
|
|
606
|
-
}
|
|
607
|
-
}
|
|
645
|
+
const StyledSelectBadge = styled.div `
|
|
646
|
+
display: inline-flex;
|
|
647
|
+
align-items: center;
|
|
648
|
+
gap: 4px;
|
|
649
|
+
padding: 3px 8px;
|
|
650
|
+
background: white;
|
|
651
|
+
border: 1px solid #c8e6c9;
|
|
652
|
+
border-radius: 16px;
|
|
653
|
+
font-size: 0.82rem;
|
|
654
|
+
color: #333;
|
|
655
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
656
|
+
|
|
657
|
+
.badge-name {
|
|
658
|
+
max-width: 120px;
|
|
659
|
+
overflow: hidden;
|
|
660
|
+
text-overflow: ellipsis;
|
|
661
|
+
white-space: nowrap;
|
|
662
|
+
|
|
663
|
+
@media (max-width: 600px) {
|
|
664
|
+
max-width: 80px;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
608
667
|
`;
|
|
609
|
-
const StyledSelectBadgeMore = styled.div `
|
|
610
|
-
display: inline-flex;
|
|
611
|
-
align-items: center;
|
|
612
|
-
justify-content: center;
|
|
613
|
-
padding: 3px 10px;
|
|
614
|
-
background: linear-gradient(135deg, #43A047 0%, #2E7D32 100%);
|
|
615
|
-
border: none;
|
|
616
|
-
border-radius: 16px;
|
|
617
|
-
font-size: 0.82rem;
|
|
618
|
-
font-weight: 600;
|
|
619
|
-
color: white;
|
|
620
|
-
cursor: pointer;
|
|
621
|
-
transition: all 0.2s ease;
|
|
622
|
-
|
|
623
|
-
&:hover {
|
|
624
|
-
transform: scale(1.05);
|
|
625
|
-
box-shadow: 0 2px 6px rgba(46, 125, 50, 0.4);
|
|
626
|
-
}
|
|
668
|
+
const StyledSelectBadgeMore = styled.div `
|
|
669
|
+
display: inline-flex;
|
|
670
|
+
align-items: center;
|
|
671
|
+
justify-content: center;
|
|
672
|
+
padding: 3px 10px;
|
|
673
|
+
background: linear-gradient(135deg, #43A047 0%, #2E7D32 100%);
|
|
674
|
+
border: none;
|
|
675
|
+
border-radius: 16px;
|
|
676
|
+
font-size: 0.82rem;
|
|
677
|
+
font-weight: 600;
|
|
678
|
+
color: white;
|
|
679
|
+
cursor: pointer;
|
|
680
|
+
transition: all 0.2s ease;
|
|
681
|
+
|
|
682
|
+
&:hover {
|
|
683
|
+
transform: scale(1.05);
|
|
684
|
+
box-shadow: 0 2px 6px rgba(46, 125, 50, 0.4);
|
|
685
|
+
}
|
|
627
686
|
`;
|
|
628
|
-
const StyledSelectEditButton = styled.div `
|
|
629
|
-
display: flex;
|
|
630
|
-
align-items: center;
|
|
631
|
-
justify-content: center;
|
|
632
|
-
width: 28px;
|
|
633
|
-
height: 28px;
|
|
634
|
-
border-radius: 4px;
|
|
635
|
-
color: #2E7D32;
|
|
636
|
-
cursor: pointer;
|
|
637
|
-
transition: all 0.2s ease;
|
|
638
|
-
|
|
639
|
-
&:hover {
|
|
640
|
-
background-color: rgba(46, 125, 50, 0.15);
|
|
641
|
-
}
|
|
687
|
+
const StyledSelectEditButton = styled.div `
|
|
688
|
+
display: flex;
|
|
689
|
+
align-items: center;
|
|
690
|
+
justify-content: center;
|
|
691
|
+
width: 28px;
|
|
692
|
+
height: 28px;
|
|
693
|
+
border-radius: 4px;
|
|
694
|
+
color: #2E7D32;
|
|
695
|
+
cursor: pointer;
|
|
696
|
+
transition: all 0.2s ease;
|
|
697
|
+
|
|
698
|
+
&:hover {
|
|
699
|
+
background-color: rgba(46, 125, 50, 0.15);
|
|
700
|
+
}
|
|
642
701
|
`;
|
|
643
|
-
export const StyledToppyTextContainer = styled.div `
|
|
644
|
-
padding: 22px 8px;
|
|
645
|
-
width: 100%;
|
|
646
|
-
max-width: 300px;
|
|
647
|
-
border: 1px solid #2559A5;
|
|
648
|
-
border-radius: 30px;
|
|
649
|
-
display: flex;
|
|
650
|
-
align-items: center;
|
|
651
|
-
justify-content: center;
|
|
652
|
-
box-sizing: border-box;
|
|
653
|
-
min-height: 100px;
|
|
702
|
+
export const StyledToppyTextContainer = styled.div `
|
|
703
|
+
padding: 22px 8px;
|
|
704
|
+
width: 100%;
|
|
705
|
+
max-width: 300px;
|
|
706
|
+
border: 1px solid #2559A5;
|
|
707
|
+
border-radius: 30px;
|
|
708
|
+
display: flex;
|
|
709
|
+
align-items: center;
|
|
710
|
+
justify-content: center;
|
|
711
|
+
box-sizing: border-box;
|
|
712
|
+
min-height: 100px;
|
|
654
713
|
`;
|
|
655
|
-
export const StyledToppyText = styled.p `
|
|
656
|
-
text-align: center;
|
|
657
|
-
color: #2559A5;
|
|
658
|
-
font-size: 1rem;
|
|
659
|
-
user-select: none;
|
|
660
|
-
-webkit-touch-callout: none;
|
|
661
|
-
-webkit-user-select: none;
|
|
662
|
-
-moz-user-select: none;
|
|
663
|
-
-ms-user-select: none;
|
|
664
|
-
margin: 0;
|
|
665
|
-
display: -webkit-box;
|
|
666
|
-
-webkit-box-orient: vertical;
|
|
667
|
-
-webkit-line-clamp: 3;
|
|
668
|
-
overflow: hidden;
|
|
669
|
-
text-overflow: ellipsis;
|
|
670
|
-
white-space: normal;
|
|
671
|
-
word-break: break-word;
|
|
672
|
-
line-height: 1.2;
|
|
714
|
+
export const StyledToppyText = styled.p `
|
|
715
|
+
text-align: center;
|
|
716
|
+
color: #2559A5;
|
|
717
|
+
font-size: 1rem;
|
|
718
|
+
user-select: none;
|
|
719
|
+
-webkit-touch-callout: none;
|
|
720
|
+
-webkit-user-select: none;
|
|
721
|
+
-moz-user-select: none;
|
|
722
|
+
-ms-user-select: none;
|
|
723
|
+
margin: 0;
|
|
724
|
+
display: -webkit-box;
|
|
725
|
+
-webkit-box-orient: vertical;
|
|
726
|
+
-webkit-line-clamp: 3;
|
|
727
|
+
overflow: hidden;
|
|
728
|
+
text-overflow: ellipsis;
|
|
729
|
+
white-space: normal;
|
|
730
|
+
word-break: break-word;
|
|
731
|
+
line-height: 1.2;
|
|
673
732
|
`;
|
|
674
|
-
export const StyledToppyImage = styled.img `
|
|
675
|
-
width: 100%;
|
|
676
|
-
max-width: 120px;
|
|
677
|
-
height: auto;
|
|
678
|
-
display: block;
|
|
733
|
+
export const StyledToppyImage = styled.img `
|
|
734
|
+
width: 100%;
|
|
735
|
+
max-width: 120px;
|
|
736
|
+
height: auto;
|
|
737
|
+
display: block;
|
|
679
738
|
`;
|