wuic-framework-lib 1.1.0 → 1.2.1

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 (16) hide show
  1. package/fesm2022/{wuic-framework-lib-chart-list.component-C9_EDkGu.mjs → wuic-framework-lib-chart-list.component-B1V8Q-Qq.mjs} +2 -2
  2. package/fesm2022/{wuic-framework-lib-code-editor.component-D_p7LGSQ.mjs → wuic-framework-lib-code-editor.component-4N95qt3D.mjs} +24 -3
  3. package/fesm2022/{wuic-framework-lib-designer.component-Dx6RraAn.mjs → wuic-framework-lib-designer.component-pW1YK-oY.mjs} +2 -2
  4. package/fesm2022/{wuic-framework-lib-first-run-wizard.component-Cp8IKDb9.mjs → wuic-framework-lib-first-run-wizard.component-CaGePYLi.mjs} +12 -2
  5. package/fesm2022/{wuic-framework-lib-framework-docs.component-CxckBsIX.mjs → wuic-framework-lib-framework-docs.component-m0IlS0Sl.mjs} +3 -3
  6. package/fesm2022/{wuic-framework-lib-pivot-builder.component-C-ceJi48.mjs → wuic-framework-lib-pivot-builder.component-VIs0CIH_.mjs} +11 -8
  7. package/fesm2022/{wuic-framework-lib-prompt-dialog-fallback-host.component-DPZCgxOB.mjs → wuic-framework-lib-prompt-dialog-fallback-host.component-Dm9GTZ07.mjs} +2 -2
  8. package/fesm2022/{wuic-framework-lib-report-designer.component-3-hUYft8.mjs → wuic-framework-lib-report-designer.component-FiAwNkPM.mjs} +2 -2
  9. package/fesm2022/{wuic-framework-lib-report-viewer.component-D4QD_wPl.mjs → wuic-framework-lib-report-viewer.component-DY0dGMrt.mjs} +2 -2
  10. package/fesm2022/{wuic-framework-lib-scheduler-list.component-C0nY8Wd6.mjs → wuic-framework-lib-scheduler-list.component-3E5m-9Qd.mjs} +31 -5
  11. package/fesm2022/{wuic-framework-lib-spreadsheet-list-sf.component-C9sWgq61.mjs → wuic-framework-lib-spreadsheet-list-sf.component-D9P9WbDl.mjs} +42 -22
  12. package/fesm2022/{wuic-framework-lib-workflow-designer.component-8mv_Rbz9.mjs → wuic-framework-lib-workflow-designer.component-4RC2EkGA.mjs} +23 -3
  13. package/fesm2022/{wuic-framework-lib-wuic-framework-lib-DJZAYFw8.mjs → wuic-framework-lib-wuic-framework-lib-BSyGhpM1.mjs} +251 -39
  14. package/fesm2022/wuic-framework-lib.mjs +1 -1
  15. package/package.json +1 -1
  16. package/types/wuic-framework-lib.d.ts +56 -1
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, ApplicationRef, EnvironmentInjector, createComponent, NgModule, Component, createNgModule, inject, NgZone, Input, HostBinding, ChangeDetectionStrategy, Pipe, ViewChild, EventEmitter, Output, forwardRef, HostListener, Optional, ViewChildren, signal, computed, ViewContainerRef, Directive, ElementRef, ViewEncapsulation } from '@angular/core';
2
+ import { Injectable, ApplicationRef, EnvironmentInjector, createComponent, NgModule, Component, createNgModule, inject, NgZone, ElementRef, Input, HostBinding, ChangeDetectionStrategy, Pipe, ViewChild, EventEmitter, Output, forwardRef, HostListener, Optional, ViewChildren, signal, computed, ViewContainerRef, Directive, ViewEncapsulation } from '@angular/core';
3
3
  import { BehaviorSubject, firstValueFrom, Subject, combineLatest, catchError, EMPTY, throwError, Subscription } from 'rxjs';
4
4
  import * as i1$2 from '@angular/router';
5
5
  import { Router, NavigationEnd, NavigationStart, RouterModule, RouterLink } from '@angular/router';
@@ -2707,7 +2707,7 @@ class WtoolboxService {
2707
2707
  const appRef = resolvedInjector.get(ApplicationRef);
2708
2708
  const environmentInjector = resolvedInjector.get(EnvironmentInjector);
2709
2709
  const close$ = new Subject();
2710
- const { PromptDialogFallbackHostComponent } = await import('./wuic-framework-lib-prompt-dialog-fallback-host.component-DPZCgxOB.mjs');
2710
+ const { PromptDialogFallbackHostComponent } = await import('./wuic-framework-lib-prompt-dialog-fallback-host.component-Dm9GTZ07.mjs');
2711
2711
  const hostRef = createComponent(PromptDialogFallbackHostComponent, { environmentInjector });
2712
2712
  hostRef.instance.datasource = dialogConfig?.data?.datasource;
2713
2713
  hostRef.instance.header = String(dialogConfig?.header || '');
@@ -3080,6 +3080,11 @@ class WtoolboxService {
3080
3080
  */
3081
3081
  async suggestColumnMD(record, field, type, routeRef) {
3082
3082
  const explicitRouteRef = String(routeRef || '').trim();
3083
+ // ATTENZIONE: niente trim() sul VALORE — le route system WUIC hanno leading/trailing
3084
+ // space significativi (es. ` metadati tabelle`). Vedi AGENTS.md regola 17:
3085
+ // "le route metadata possono contenere spazi significativi; non applicare trim() o
3086
+ // normalizzazioni che rimuovono/compattano spazi quando leggi/confronti route
3087
+ // metadata". Trim() qui causava server-side route lookup miss → results vuoto.
3083
3088
  const resolveRecordValue = (name) => {
3084
3089
  if (!name || !record) {
3085
3090
  return '';
@@ -3089,13 +3094,13 @@ class WtoolboxService {
3089
3094
  return '';
3090
3095
  }
3091
3096
  if (typeof raw === 'object' && raw && 'value' in raw) {
3092
- return String(raw.value || '').trim();
3097
+ return String(raw.value || '');
3093
3098
  }
3094
- return String(raw || '').trim();
3099
+ return String(raw || '');
3095
3100
  };
3096
3101
  const explicitRoute = explicitRouteRef ? resolveRecordValue(explicitRouteRef) : '';
3097
3102
  const fallbackRoute = resolveRecordValue('md_route_name');
3098
- const routeFromMetadataEditorContext = String(WtoolboxService.metadataEditorContextRouteName || '').trim();
3103
+ const routeFromMetadataEditorContext = String(WtoolboxService.metadataEditorContextRouteName || '');
3099
3104
  const route = explicitRoute || fallbackRoute || routeFromMetadataEditorContext;
3100
3105
  if (!route) {
3101
3106
  WtoolboxService.messageNotificationService.add({ severity: 'info', summary: WtoolboxService.tr('common.not_found_summary', 'Not Found'), detail: WtoolboxService.tr('suggest.not_found_route_for_column', 'No route found for column suggestion.') });
@@ -3933,7 +3938,15 @@ class WtoolboxService {
3933
3938
  usePreview: false
3934
3939
  },
3935
3940
  scheduler: {
3936
- advancedFilter: false
3941
+ advancedFilter: false,
3942
+ // FIX 2026-05-22: allineamento con SchedulerOptions.ts class fields.
3943
+ // Il defaultPropsBag e' la fonte del suggest tree di md_props_bag —
3944
+ // se manca una property qui, l'admin non puo' selezionarla nel suggest.
3945
+ fromField: '',
3946
+ toField: '',
3947
+ titleField: '',
3948
+ itemTemplateString: '',
3949
+ titleFunction: ''
3937
3950
  },
3938
3951
  spreadsheet: {
3939
3952
  advancedFilter: false
@@ -5578,6 +5591,16 @@ class GlobalHandler {
5578
5591
  */
5579
5592
  handleLegacyError(e) {
5580
5593
  let exc;
5594
+ // FirstRun polling endpoints — silent skip dei 503 transient durante
5595
+ // l'app-pool recycle che IIS triggera quando configure_wuic riscrive
5596
+ // appsettings.json (FileSystemWatcher → recycle). Il client fa polling
5597
+ // ogni 500ms su FirstRunProgress + FirstRunStatus durante install, e
5598
+ // i ~30s di shutdown→restart pool generavano 13+ dialog 503 concorrenti.
5599
+ // Questi endpoint sono design-by-retry: il chiamante (first-run-wizard
5600
+ // + waitForBackendReady) ha gia' try/catch silenzioso + retry loop —
5601
+ // il dialog modale del GlobalHandler e' rumore puro.
5602
+ const errUrl = String(e?.url || e?.error?.url || '');
5603
+ const isFirstRunPolling = /\/api\/Meta\/FirstRun(Progress|Status)\b/i.test(errUrl);
5581
5604
  // Skill crash-reporting Commit 9b (Opzione B): generic HTTP/runtime
5582
5605
  // error fallbacks. Coprono errori che NON hanno il typed envelope
5583
5606
  // (chiamate a endpoint non-AsmxProxy: OAuth, file upload custom,
@@ -5587,6 +5610,15 @@ class GlobalHandler {
5587
5610
  const httpStatus = (typeof e?.status === 'number')
5588
5611
  ? e.status
5589
5612
  : (typeof e?.error?.status === 'number' ? e.error.status : undefined);
5613
+ // Silent-suppress firstrun polling errors (any HTTP status — typical
5614
+ // sequence: 503 Shutting Down → connection refused → 503 Starting Up).
5615
+ if (isFirstRunPolling) {
5616
+ try {
5617
+ console.debug('[GlobalHandler] suppress firstrun polling error', httpStatus, errUrl);
5618
+ }
5619
+ catch { /* noop */ }
5620
+ return;
5621
+ }
5590
5622
  const isTimeout = (e?.name === 'TimeoutError') ||
5591
5623
  (typeof e?.message === 'string' && /timeout/i.test(e.message)) ||
5592
5624
  (httpStatus === 0 && typeof e?.message === 'string' && /timeout|aborted/i.test(e.message));
@@ -12660,6 +12692,24 @@ class TranslationManagerService {
12660
12692
  backendRetryTimer;
12661
12693
  firstRunStatusChecked = false;
12662
12694
  firstRunStatusValue = false;
12695
+ /**
12696
+ * Circuit breaker BE-offline: dopo {@link MAX_BACKEND_RETRIES} fallimenti
12697
+ * consecutivi di `loadTranslations`, smettiamo di rischedulare il retry
12698
+ * automatico. Con il vecchio loop a 1s, un BE offline generava ~60 chiamate
12699
+ * fallite/min (vedi screenshot 2026-05-19: 318 errori console + ~190 XHR in
12700
+ * pochi secondi). Ora dopo 5 fallimenti il timer si ferma e l'utente vede
12701
+ * un banner globale "Backend offline" + bottone "Riprova" che chiama
12702
+ * `retryBackendNow()` per resettare il circuito.
12703
+ *
12704
+ * Reset condizioni:
12705
+ * - chiamata esplicita a `ensureTranslationsLoaded(true)` (forceReload);
12706
+ * - chiamata esplicita a `retryBackendNow()` (bottone UI);
12707
+ * - prima chiamata con successo (counter back to 0).
12708
+ */
12709
+ static MAX_BACKEND_RETRIES = 5;
12710
+ consecutiveBackendFailures = 0;
12711
+ /** True quando MAX_BACKEND_RETRIES e' stato raggiunto → niente piu' retry automatici. */
12712
+ backendCircuitOpen$ = new BehaviorSubject(false);
12663
12713
  /**
12664
12714
  * Dedup set per i warning "Missing translation resource". Senza questo
12665
12715
  * Angular ChangeDetection invoca `instant(key)` ad ogni ciclo per ogni
@@ -12753,6 +12803,14 @@ class TranslationManagerService {
12753
12803
  clearTimeout(this.backendRetryTimer);
12754
12804
  this.backendRetryTimer = undefined;
12755
12805
  }
12806
+ // Reset circuit breaker BE-offline: forceReload e' un'azione utente
12807
+ // esplicita (cambio lingua, logout/login, retry manuale), quindi
12808
+ // l'eventuale stato di "BE irraggiungibile" della sessione precedente
12809
+ // va azzerato per dargli una nuova chance.
12810
+ this.consecutiveBackendFailures = 0;
12811
+ if (this.backendCircuitOpen$.value)
12812
+ this.backendCircuitOpen$.next(false);
12813
+ this.firstRunStatusChecked = false;
12756
12814
  }
12757
12815
  if (!this.loadPromise) {
12758
12816
  const currentLoad = this.loadTranslations();
@@ -13071,14 +13129,28 @@ class TranslationManagerService {
13071
13129
  }
13072
13130
  }
13073
13131
  this.translationsLoaded$.next(true);
13132
+ // Successo → reset circuit breaker (anche se non era aperto, resta zero).
13133
+ this.consecutiveBackendFailures = 0;
13134
+ if (this.backendCircuitOpen$.value)
13135
+ this.backendCircuitOpen$.next(false);
13074
13136
  }
13075
13137
  catch (err) {
13076
- // Errore HTTP (network/5xx): mantieni il retry loop originale — un transient
13077
- // glitch (worker recycle, timeout, ecc.) si risolve da solo entro qualche
13078
- // secondo. Usare il bundle qui sarebbe troppo aggressivo: il bundle ha solo
13079
- // 4 chiavi (login form) ma tutta la restante UI mostrerebbe placeholder
13080
- // grezzi. Meglio tenere lo spinner e ri-tentare.
13138
+ // Errore HTTP (network/5xx): retry con limite. Senza il counter qui sotto
13139
+ // un BE offline genera ~60 chiamate fallite/min (1s loop × 2 endpoint:
13140
+ // FirstRunStatus + GetTranslation), e Chrome inonda la console di CORS
13141
+ // errors. Dopo MAX_BACKEND_RETRIES (5) il loop si ferma e l'utente
13142
+ // riprova tramite UI (banner / refresh / `retryBackendNow()`).
13081
13143
  this.translationsLoaded$.next(false);
13144
+ this.consecutiveBackendFailures += 1;
13145
+ if (this.consecutiveBackendFailures >= TranslationManagerService.MAX_BACKEND_RETRIES) {
13146
+ // Circuito aperto: smette di rischedulare. Logga UNA volta per non spammare.
13147
+ if (!this.backendCircuitOpen$.value) {
13148
+ // eslint-disable-next-line no-console
13149
+ console.warn(`[translation-manager] Backend irraggiungibile dopo ${this.consecutiveBackendFailures} tentativi. Retry automatici sospesi. Chiama TranslationManagerService.retryBackendNow() o ricarica la pagina per riprovare.`);
13150
+ this.backendCircuitOpen$.next(true);
13151
+ }
13152
+ return;
13153
+ }
13082
13154
  if (!this.backendRetryTimer) {
13083
13155
  this.backendRetryTimer = setTimeout(() => {
13084
13156
  this.backendRetryTimer = undefined;
@@ -13087,6 +13159,25 @@ class TranslationManagerService {
13087
13159
  }
13088
13160
  }
13089
13161
  }
13162
+ /**
13163
+ * Reset esplicito del circuit breaker BE-offline + retry immediato.
13164
+ * Da chiamare quando l'utente clicca "Riprova" sul banner offline, o
13165
+ * quando un altro servizio rileva che il BE e' tornato online (es. ping
13166
+ * health check, network 'online' event, ecc.). NON usare in retry loops
13167
+ * automatici — il senso del breaker e' proprio fermarli.
13168
+ */
13169
+ retryBackendNow() {
13170
+ this.consecutiveBackendFailures = 0;
13171
+ if (this.backendCircuitOpen$.value)
13172
+ this.backendCircuitOpen$.next(false);
13173
+ if (this.backendRetryTimer) {
13174
+ clearTimeout(this.backendRetryTimer);
13175
+ this.backendRetryTimer = undefined;
13176
+ }
13177
+ this.firstRunStatusChecked = false;
13178
+ this.loadPromise = undefined;
13179
+ void this.ensureTranslationsLoaded();
13180
+ }
13090
13181
  /**
13091
13182
  * Revalidate background: rifetcha la tabella traduzioni dal backend SENZA
13092
13183
  * bloccare l'UI. Confronta size + contenuto (hash leggero su `id,resource,
@@ -13585,6 +13676,18 @@ class DataSourceComponent {
13585
13676
  aRoute;
13586
13677
  userInfo;
13587
13678
  static liveInstances = new Set();
13679
+ /**
13680
+ * Riferimento al DOM host element. Iniettato via `inject()` (Angular 14+ functional
13681
+ * DI) invece che da costruttore per non rompere i call site manuali tipo
13682
+ * `new DataSourceComponent(metaSrv, dataSrv, ...)` in designer.component.ts
13683
+ * e metadata-editor.service.ts (che mantengono i 7 params posizionali storici).
13684
+ * `{ optional: true }` rende il field null fuori da un Angular DI context
13685
+ * (es. unit test che istanziano la classe a mano) — il consumo in ngOnInit
13686
+ * usa `?.nativeElement` per sopportare entrambi i path.
13687
+ * Scopo: esporre `this` su `host.__wuicCmp` per E2E introspection senza
13688
+ * dipendere da `window.ng.getComponent` (tree-shaken in prod build).
13689
+ */
13690
+ elementRef = inject(ElementRef, { optional: true });
13588
13691
  static getLiveInstances() {
13589
13692
  return Array.from(DataSourceComponent.liveInstances);
13590
13693
  }
@@ -14189,6 +14292,18 @@ class DataSourceComponent {
14189
14292
  */
14190
14293
  ngOnInit() {
14191
14294
  DataSourceComponent.liveInstances.add(this);
14295
+ // E2E introspection hook: expose component instance on the host element so
14296
+ // tests can find it via DOM (`host.__wuicCmp`) without depending on
14297
+ // `window.ng.getComponent`, which Angular tree-shakes in production builds
14298
+ // (verified empirically 2026-05-25: VM serves prod bundle, `window.ng` has
14299
+ // only `ɵcompilerFacade`, no `getComponent`). The smart-app contract test
14300
+ // (`smart-app--default.mjs`) and code-editor toggle-monaco test use this
14301
+ // path to assert public method surface (enableClientSideCrud, etc.).
14302
+ // Pattern mirror PrimeNG `__component` / Vuetify `__vueParentComponent`.
14303
+ try {
14304
+ (this.elementRef?.nativeElement).__wuicCmp = this;
14305
+ }
14306
+ catch { /* defensive */ }
14192
14307
  this.lastAction = this.aRoute.snapshot.paramMap.get('action');
14193
14308
  if (this.routeFromRouting) {
14194
14309
  this.routerEventsSubscription?.unsubscribe();
@@ -14268,6 +14383,13 @@ class DataSourceComponent {
14268
14383
  */
14269
14384
  ngOnDestroy() {
14270
14385
  DataSourceComponent.liveInstances.delete(this);
14386
+ // Clear the E2E introspection hook set in ngOnInit. The host element may
14387
+ // outlive the component instance briefly during Angular's tear-down, so
14388
+ // dropping the ref prevents stale-component leaks for tests.
14389
+ try {
14390
+ delete (this.elementRef?.nativeElement).__wuicCmp;
14391
+ }
14392
+ catch { /* defensive */ }
14271
14393
  if (this.interval) {
14272
14394
  clearInterval(this.interval);
14273
14395
  }
@@ -21074,7 +21196,7 @@ class LazySchedulerListComponent {
21074
21196
  * Inizializza il componente preparando stato, sottoscrizioni e primi caricamenti richiesti.
21075
21197
  */
21076
21198
  async ngOnInit() {
21077
- schedulerListComponentPromise ??= import('./wuic-framework-lib-scheduler-list.component-C0nY8Wd6.mjs').then((m) => m.SchedulerListComponent);
21199
+ schedulerListComponentPromise ??= import('./wuic-framework-lib-scheduler-list.component-3E5m-9Qd.mjs').then((m) => m.SchedulerListComponent);
21078
21200
  this.loadedComponent = await schedulerListComponentPromise;
21079
21201
  this.cdr.markForCheck();
21080
21202
  }
@@ -21282,7 +21404,7 @@ class LazySpreadsheetListSfComponent {
21282
21404
  this.activatedRoute = activatedRoute;
21283
21405
  }
21284
21406
  async ngOnInit() {
21285
- spreadsheetListSfComponentPromise ??= import('./wuic-framework-lib-spreadsheet-list-sf.component-C9sWgq61.mjs').then((m) => m.SpreadsheetListSfComponent);
21407
+ spreadsheetListSfComponentPromise ??= import('./wuic-framework-lib-spreadsheet-list-sf.component-D9P9WbDl.mjs').then((m) => m.SpreadsheetListSfComponent);
21286
21408
  this.loadedComponent = await spreadsheetListSfComponentPromise;
21287
21409
  // Quando la promise e' gia' risolta dal cache module-scope (secondo mount
21288
21410
  // post-refresh), il microtask che risolve `await` non scatena sempre CD
@@ -21560,7 +21682,7 @@ class LazyChartListComponent {
21560
21682
  * Inizializza il componente preparando stato, sottoscrizioni e primi caricamenti richiesti.
21561
21683
  */
21562
21684
  async ngOnInit() {
21563
- chartListComponentPromise ??= import('./wuic-framework-lib-chart-list.component-C9_EDkGu.mjs').then((m) => m.ChartListComponent);
21685
+ chartListComponentPromise ??= import('./wuic-framework-lib-chart-list.component-B1V8Q-Qq.mjs').then((m) => m.ChartListComponent);
21564
21686
  this.loadedComponent = await chartListComponentPromise;
21565
21687
  this.cdr.markForCheck();
21566
21688
  }
@@ -41148,13 +41270,33 @@ class TreeListComponent {
41148
41270
  const route = this.metaInfo?.tableMetadata?.md_route_name;
41149
41271
  labelFunction = (record) => WtoolboxService.runUserCallbackSync('archetypes.tree.labelFunction', () => compiledLabelFn(record), [], { archetype: 'tree', route });
41150
41272
  }
41273
+ // FIX 2026-05-22: in server-side ops mode, se leafField ritorna null/undefined
41274
+ // (es. colonna IsLeaf non popolata), PrimeNG cade nel fallback `!children.length`
41275
+ // e tratta il nodo come leaf → niente chevron di espansione → utente non puo'
41276
+ // aprire i child. Default `leaf: false` in lazy mode cosi' il chevron e' sempre
41277
+ // mostrato e il child viene caricato on-demand via onNodeExpand.
41278
+ const isServerSideMode = !!this.metaInfo?.tableMetadata?.md_server_side_operations;
41151
41279
  if (data) {
41152
41280
  data.forEach((record) => {
41153
41281
  const recordKey = this.getRecordPrimaryKeyValue(record);
41154
41282
  const recordLabel = labelFunction ? labelFunction(record) : this.getRecordFieldValue(record, this.treeOptions?.labelField);
41155
41283
  const recordIcon = this.getRecordFieldValue(record, this.treeOptions?.iconField);
41156
- const recordLeaf = this.getRecordFieldValue(record, this.treeOptions?.leafField);
41284
+ const recordLeafRaw = this.getRecordFieldValue(record, this.treeOptions?.leafField);
41157
41285
  const parentKey = this.getRecordFieldValue(record, this.treeOptions?.parentField);
41286
+ // Normalizza leaf flag:
41287
+ // - valore esplicito boolean → usa quello
41288
+ // - null/undefined in server-side mode → leaf=false (chevron + lazy load)
41289
+ // - null/undefined in client-side mode → leaf=undefined (PrimeNG decide su children)
41290
+ let recordLeaf;
41291
+ if (recordLeafRaw === true || recordLeafRaw === 'true' || recordLeafRaw === 1 || recordLeafRaw === '1') {
41292
+ recordLeaf = true;
41293
+ }
41294
+ else if (recordLeafRaw === false || recordLeafRaw === 'false' || recordLeafRaw === 0 || recordLeafRaw === '0') {
41295
+ recordLeaf = false;
41296
+ }
41297
+ else {
41298
+ recordLeaf = isServerSideMode ? false : undefined;
41299
+ }
41158
41300
  let item = {
41159
41301
  key: recordKey,
41160
41302
  label: recordLabel ?? '',
@@ -41221,11 +41363,24 @@ class TreeListComponent {
41221
41363
  ]);
41222
41364
  const res = await this.dataSrv.select(this.datasource.value, true);
41223
41365
  const records = Array.isArray(res?.dato) ? res.dato : [];
41366
+ // FIX 2026-05-22: stessa normalizzazione leaf di _parseDataInternal (sopra)
41367
+ // per i nodi lazy-loaded da onNodeExpand.
41368
+ const isServerSideMode = !!this.metaInfo?.tableMetadata?.md_server_side_operations;
41224
41369
  const children = records.map((record) => {
41225
41370
  const recordKey = this.getRecordPrimaryKeyValue(record);
41226
41371
  const recordLabel = this.getRecordFieldValue(record, this.treeOptions?.labelField);
41227
41372
  const recordIcon = this.getRecordFieldValue(record, this.treeOptions?.iconField);
41228
- const recordLeaf = this.getRecordFieldValue(record, this.treeOptions?.leafField);
41373
+ const recordLeafRaw = this.getRecordFieldValue(record, this.treeOptions?.leafField);
41374
+ let recordLeaf;
41375
+ if (recordLeafRaw === true || recordLeafRaw === 'true' || recordLeafRaw === 1 || recordLeafRaw === '1') {
41376
+ recordLeaf = true;
41377
+ }
41378
+ else if (recordLeafRaw === false || recordLeafRaw === 'false' || recordLeafRaw === 0 || recordLeafRaw === '0') {
41379
+ recordLeaf = false;
41380
+ }
41381
+ else {
41382
+ recordLeaf = isServerSideMode ? false : undefined;
41383
+ }
41229
41384
  return {
41230
41385
  key: recordKey,
41231
41386
  label: recordLabel ?? '',
@@ -43506,6 +43661,13 @@ class MetaMenuComponent {
43506
43661
  * tradotti, che e' un'esperienza confusa.
43507
43662
  */
43508
43663
  translationsLoaded$;
43664
+ /**
43665
+ * Stato circuit breaker BE-offline esposto dal TranslationManagerService.
43666
+ * Quando true → mostriamo il banner "Backend offline" + bottone "Riprova"
43667
+ * al posto dello spinner "Loading…" altrimenti infinito. Il click sul
43668
+ * bottone chiama `retryBackend()` che riarma il circuito e ritenta.
43669
+ */
43670
+ backendCircuitOpen$;
43509
43671
  /**
43510
43672
  * Proprieta di stato del componente per auth return url, usata dalla logica interna e dal template.
43511
43673
  */
@@ -43595,6 +43757,17 @@ class MetaMenuComponent {
43595
43757
  this.elementRef = elementRef;
43596
43758
  this.authState$ = this.authSession.state$;
43597
43759
  this.translationsLoaded$ = this.trslSrv.translationsLoaded$.asObservable();
43760
+ this.backendCircuitOpen$ = this.trslSrv.backendCircuitOpen$.asObservable();
43761
+ }
43762
+ /**
43763
+ * Handler del bottone "Riprova" del banner BE-offline. Resetta il circuit
43764
+ * breaker nel TranslationManagerService e tenta immediatamente una nuova
43765
+ * fetch di `MetaService.GetTranslation`. Se il BE e' di nuovo online il
43766
+ * banner sparisce e parte lo spinner standard; se il BE e' ancora offline,
43767
+ * dopo altri 5 fallimenti consecutivi il banner riappare.
43768
+ */
43769
+ retryBackend() {
43770
+ this.trslSrv.retryBackendNow();
43598
43771
  }
43599
43772
  /**
43600
43773
  * Completa inizializzazione dopo il rendering della view e collega riferimenti UI.
@@ -45068,11 +45241,11 @@ class MetaMenuComponent {
45068
45241
  }
45069
45242
  }
45070
45243
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MetaMenuComponent, deps: [{ token: MetadataProviderService }, { token: AuthSessionService }, { token: TranslationManagerService }, { token: UserInfoService }, { token: WorkflowRuntimeMenuService }, { token: i1$1.HttpClient }, { token: i0.NgZone }, { token: i1$2.Router }, { token: LicenseFeatureService }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
45071
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: MetaMenuComponent, isStandalone: true, selector: "wuic-meta-menu", host: { listeners: { "document:click": "onDocumentClick()" } }, viewQueries: [{ propertyName: "menu", first: true, predicate: ["menubar"], descendants: true }, { propertyName: "menuMetadataDs", first: true, predicate: ["menuMetadataDs"], descendants: true }], ngImport: i0, template: "@if (authState$ | async; as auth) {\n<nav class=\"wuic-meta-menu-landmark\" aria-label=\"Menu principale\">\n @if (auth.sessionExpiring) {\n <div class=\"wuic-session-warning\">\n <span class=\"pi pi-exclamation-triangle\"></span>\n {{ 'auth.session.expiring_in' | translate }} <strong>{{ auth.sessionExpiresInSeconds }}</strong> {{ 'seconds' |\n translate }}.\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"refreshAuthSession()\">{{ 'auth.session.extend' | translate\n }}</button>\n </div>\n }\n @if (auth.initialized && !requiresAuthentication(auth)) {\n <div style=\"display:none\">\n <wuic-data-source #menuMetadataDs [hardcodedRoute]=\"metaMenuRoute\" [autoload]=\"false\"></wuic-data-source>\n </div>\n <p-menubar #menubar [model]=\"items\" [autoHide]=\"false\">\n <ng-template #item let-item>\n @if (item.route) {\n <!-- [href] con path assoluto (NO `#/` hash) cosi' Lighthouse SEO\n `crawlable-anchors` considera il link crawlable: il path\n `/<route>` e' un URL valid + condivisibile + apribile in nuova\n tab via Ctrl+Click. Il click handler `onMenuItemClick` chiama\n `event.preventDefault()` quando il button click e' \"primary\"\n (no Ctrl/middle), cosi' la SPA navigation passa per\n `router.navigateByUrl` (che con `withHashLocation()` aggiunge\n comunque l'hash internamente all'URL) e preserva pending changes\n /query params. Per Ctrl+Click / middle-click il browser segue\n l'href naturalmente \u2192 apre nuova tab con path puro (poi il\n server fa SPA fallback a index.html, e Angular legge la route\n dalla path). -->\n <a class=\"p-menubar-item-link\" [href]=\"getMenuItemHref(item)\" (click)=\"onMenuItemClick(item, $event)\"\n (contextmenu)=\"onMenuContext($event, item, auth, menuCtx)\"\n [attr.draggable]=\"canDragMenu(auth, item) ? true : null\" (dragstart)=\"onMenuDragStart($event, item, auth)\"\n (dragover)=\"onMenuDragOver($event, item, auth)\" (drop)=\"onMenuDrop($event, item, auth)\"\n (dragend)=\"onMenuDragEnd()\">\n <span [class]=\"item.icon\"></span>\n <span class=\"ml-2\">{{ item.label | translate }}</span>\n </a>\n } @else {\n <!-- Parent items (folder con submenu, NO route propria): NON sono `<a>`\n ma `<div>` semantici. Lighthouse non flagga `<div>` come uncrawlable\n (l'audit `crawlable-anchors` controlla solo `<a>`). PrimeNG si\n occupa di toggle del submenu via il (click). -->\n <div class=\"p-menubar-item-link\" (click)=\"onMenuItemClick(item, $event); onFocus($event)\"\n (contextmenu)=\"onMenuContext($event, item, auth, menuCtx)\"\n [attr.draggable]=\"canDragMenu(auth, item) ? true : null\" (dragstart)=\"onMenuDragStart($event, item, auth)\"\n (dragover)=\"onMenuDragOver($event, item, auth)\" (drop)=\"onMenuDrop($event, item, auth)\"\n (dragend)=\"onMenuDragEnd()\">\n <span [class]=\"item.icon\"></span>\n <span class=\"ml-2\">{{ item.label | translate }}</span>\n <span class=\"pi pi-fw pi-angle-down ml-2\"></span>\n </div>\n }\n </ng-template>\n\n <ng-template #end>\n @if (auth.authenticated || auth.legacyAuthenticated) {\n <div class=\"wuic-menubar-extra\">\n <span class=\"wuic-license-badge\" [class.wuic-license-badge-pro]=\"getLicenseTier() === 'professional'\"\n [class.wuic-license-badge-dev]=\"getLicenseTier() === 'developer'\"\n [class.wuic-license-badge-trial]=\"getLicenseTier() === 'trial'\" [attr.title]=\"getLicenseTooltip()\"\n role=\"status\" [attr.aria-label]=\"getLicenseTooltip()\">\n <span [class]=\"getLicenseIconClass()\" aria-hidden=\"true\"></span>\n <span class=\"wuic-license-badge-label\">{{ getLicenseBadgeLabel() }}</span>\n </span>\n\n <div class=\"wuic-menubar-user-wrap\" (click)=\"toggleUserLanguageMenu($event)\">\n <img class=\"wuic-menubar-user-flag\" [src]=\"getCurrentUserLanguageFlagSrc()\" [alt]=\"getCurrentUserLanguageId()\"\n (error)=\"onLanguageFlagLoadError($event)\" />\n <div class=\"wuic-menubar-user\">\n @if (auth.authenticated) {\n <span [pTooltip]=\"auth.name || ('user.default' | translate)\" tooltipPosition=\"bottom\"\n class=\"wuic-menubar-user-name\">{{ auth.name || ('user.default' | translate) }}</span>\n <span class=\"wuic-menubar-user-role\">\n <span [pTooltip]=\"auth.provider || ('auth.provider.google' | translate)\" tooltipPosition=\"bottom\"\n class=\"wuic-role-badge\">{{ auth.provider || ('auth.provider.google' | translate) }}</span>\n </span>\n } @else {\n <span [pTooltip]=\"auth.legacyName || ('user.default' | translate)\" tooltipPosition=\"bottom\"\n class=\"wuic-menubar-user-name\">{{ auth.legacyName || ('user.default' | translate) }}</span>\n <span class=\"wuic-menubar-user-role\">\n <span [pTooltip]=\"auth.legacyRole || ('auth.role.na' | translate)\" tooltipPosition=\"bottom\"\n class=\"wuic-role-badge\" [class.wuic-role-badge-admin]=\"isAdminRole(auth)\">{{ auth.legacyRole ||\n ('auth.role.na' | translate) }}</span>\n </span>\n }\n </div>\n @if (userLanguageSwitchEnabled) {\n <span class=\"pi pi-angle-down wuic-menubar-user-caret\" aria-hidden=\"true\"></span>\n }\n @if (userLanguageSwitchEnabled && userLanguageMenuVisible) {\n <div class=\"wuic-language-menu\" (click)=\"$event.stopPropagation()\">\n @for (lang of userLanguages; track lang.id) {\n <button type=\"button\" class=\"wuic-language-item\" [class.is-active]=\"isCurrentUserLanguage(lang.id)\"\n (click)=\"onSelectUserLanguage(lang.id, $event)\">\n <img class=\"wuic-language-item-flag\" [src]=\"getLanguageFlagSrc(lang.id)\" [alt]=\"lang.id\"\n (error)=\"onLanguageFlagLoadError($event)\" />\n <span>{{ lang.lingua || lang.id }}</span>\n <small>{{ lang.id }}</small>\n </button>\n }\n </div>\n }\n </div>\n\n <button type=\"button\" class=\"wuic-auth-btn wuic-menubar-logout\" (click)=\"logout()\" [disabled]=\"auth.loading\">\n {{ 'logout' | translate }}\n </button>\n </div>\n }\n </ng-template>\n </p-menubar>\n <p-contextMenu #menuCtx [model]=\"contextItems\" appendTo=\"body\" [autoZIndex]=\"true\"\n [baseZIndex]=\"13000\"></p-contextMenu>\n <p-dialog [(visible)]=\"moveDialogVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" [closable]=\"true\"\n [style]=\"{ width: '28rem' }\" [header]=\"'menu.move.title' | translate\" (onHide)=\"cancelPendingMove()\"\n [autoZIndex]=\"true\" [baseZIndex]=\"14000\">\n <p>{{ 'menu.move.drop_target' | translate }}: <strong>{{ pendingMove?.targetLabel || pendingMove?.targetId\n }}</strong></p>\n <p>{{ 'menu.move.choose_position' | translate }}</p>\n <div class=\"wuic-menu-move-actions\">\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"executePendingMove('before')\"\n [disabled]=\"!hasMenuAdminMethod('reorderMenu')\">{{ 'menu.move.before' | translate }}</button>\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"executePendingMove('after')\"\n [disabled]=\"!hasMenuAdminMethod('reorderMenu')\">{{ 'menu.move.after' | translate }}</button>\n <button type=\"button\" class=\"wuic-auth-btn wuic-auth-btn-primary\" (click)=\"executePendingMove('inside')\"\n [disabled]=\"!hasMenuAdminMethod('nestMenu')\">{{ 'menu.move.inside' | translate }}</button>\n </div>\n </p-dialog>\n }\n @if (auth.initialized && showAuthBar(auth)) {\n <div class=\"wuic-auth-stage\" [class.wuic-auth-stage-login]=\"requiresAuthentication(auth)\">\n @if (!(translationsLoaded$ | async)) {\n <!-- Non mostrare il form login finche' le traduzioni non sono caricate: l'utente\n vedrebbe i placeholder grezzi `auth.username` / `auth.password` invece dei\n label tradotti. Meglio uno spinner finche' la tabella traduzioni arriva\n (localStorage o backend). Tipicamente <50ms se cache, ~200-800ms se\n cold-fetch dal backend. -->\n <div class=\"wuic-auth-bar wuic-auth-bar-loading\" [class.wuic-auth-bar-login]=\"requiresAuthentication(auth)\">\n <div class=\"wuic-auth-loading\">\n <i class=\"pi pi-spinner pi-spin\"></i>\n <span>Loading\u2026</span>\n </div>\n </div>\n } @else {\n <div class=\"wuic-auth-bar\" [class.wuic-auth-bar-login]=\"requiresAuthentication(auth)\">\n <div class=\"wuic-auth-status\">\n @if (auth.loading) {\n <span>{{ 'auth.status.loading' | translate }}</span>\n } @else if (auth.authenticated) {\n <span>{{ 'auth.status.active' | translate }}: <strong>{{ auth.name || ('user.default' | translate) }}</strong>\n ({{ auth.provider || ('auth.provider.google' | translate) }})</span>\n } @else if (auth.enabled) {\n <span>{{ 'auth.status.not_authenticated' | translate }} ({{ auth.provider || ('auth.provider.google' |\n translate)\n }})</span>\n } @else if (auth.legacyAuthenticated) {\n <span>{{ 'auth.status.active' | translate }}: <strong>{{ auth.legacyName || ('user.default' | translate)\n }}</strong> ({{ 'auth.provider.credentials' | translate }})</span>\n } @else {\n <span>{{ 'auth.status.login_required' | translate }} ({{ 'auth.provider.credentials' | translate }})</span>\n }\n </div>\n\n <div class=\"wuic-auth-form\" [class.wuic-auth-form-login]=\"requiresAuthentication(auth)\">\n @if (auth.enabled) {\n <label for=\"wuicAuthReturnUrl\">{{ 'auth.return_url' | translate }}</label>\n <input id=\"wuicAuthReturnUrl\" type=\"text\" [(ngModel)]=\"authReturnUrl\" (ngModelChange)=\"onAuthReturnUrlChanged()\"\n placeholder=\"/\" />\n\n <button type=\"button\" class=\"wuic-auth-btn wuic-auth-btn-primary\" (click)=\"loginWithGoogle()\"\n [disabled]=\"auth.authenticated || auth.loading\">\n {{ 'auth.login_google' | translate }}\n </button>\n\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"logout()\" [disabled]=\"!auth.authenticated || auth.loading\">\n {{ 'logout' | translate }}\n </button>\n } @else if (!auth.legacyAuthenticated) {\n\n @if (authView === 'login') {\n <form (ngSubmit)=\"loginLegacy()\" novalidate>\n <label for=\"wuicAuthUser\">{{ 'auth.username' | translate }}</label>\n <input id=\"wuicAuthUser\" type=\"text\" [(ngModel)]=\"loginUsername\" [placeholder]=\"'auth.username' | translate\"\n autocomplete=\"username\" name=\"loginUsername\" />\n\n <label for=\"wuicAuthPwd\">{{ 'auth.password' | translate }}</label>\n <input id=\"wuicAuthPwd\" type=\"password\" [(ngModel)]=\"loginPassword\"\n [placeholder]=\"'auth.password' | translate\" autocomplete=\"current-password\" name=\"loginPassword\" />\n\n @if (captchaEnabled) {\n <div id=\"wuic-captcha-container\" class=\"wuic-captcha\"></div>\n }\n\n <button type=\"submit\" class=\"wuic-auth-btn wuic-auth-btn-primary\"\n [disabled]=\"!loginUsername || !loginPassword || auth.loading || (captchaEnabled && !captchaToken)\">\n {{ 'auth.login' | translate }}\n </button>\n\n <div class=\"wuic-auth-links\">\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('forgotPassword')\">{{\n 'auth.link.forgot_password' | translate }}</button>\n @if (registrationEnabled) {\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('register')\">{{ 'auth.link.register' |\n translate }}</button>\n }\n </div>\n </form>\n } @else if (authView === 'register') {\n <form (ngSubmit)=\"submitRegistration()\" novalidate>\n <label for=\"wuic-auth-register-username\">{{ 'auth.username' | translate }}</label>\n <input id=\"wuic-auth-register-username\" type=\"text\" [(ngModel)]=\"registerUsername\"\n [placeholder]=\"'auth.username' | translate\" autocomplete=\"username\" name=\"registerUsername\" />\n\n <label for=\"wuic-auth-register-email\">{{ 'auth.email' | translate }}</label>\n <input id=\"wuic-auth-register-email\" type=\"email\" [(ngModel)]=\"registerEmail\"\n [placeholder]=\"'auth.email' | translate\" autocomplete=\"email\" name=\"registerEmail\" />\n\n <label for=\"wuic-auth-register-password\">{{ 'auth.password' | translate }}</label>\n <input id=\"wuic-auth-register-password\" type=\"password\" [(ngModel)]=\"registerPassword\"\n [placeholder]=\"'auth.password' | translate\" autocomplete=\"new-password\" name=\"registerPassword\" />\n\n @if (captchaEnabled) {\n <div id=\"wuic-captcha-container\" class=\"wuic-captcha\"></div>\n }\n\n <button type=\"submit\" class=\"wuic-auth-btn wuic-auth-btn-primary\"\n [disabled]=\"!registerUsername || !registerEmail || !registerPassword || (captchaEnabled && !captchaToken)\">\n {{ 'auth.link.register' | translate }}\n </button>\n\n @if (registerMessage) {\n <div class=\"wuic-auth-success\">{{ registerMessage }}</div>\n }\n @if (registerError) {\n <div class=\"wuic-auth-error\">{{ registerError }}</div>\n }\n\n <div class=\"wuic-auth-links\">\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('login')\">{{ 'auth.link.back_to_login'\n | translate }}</button>\n </div>\n </form>\n } @else if (authView === 'forgotPassword') {\n <label for=\"wuic-auth-forgot-email\">{{ 'auth.email' | translate }}</label>\n <input id=\"wuic-auth-forgot-email\" type=\"email\" [(ngModel)]=\"forgotEmail\"\n [placeholder]=\"'auth.forgot.email_placeholder' | translate\" autocomplete=\"email\"\n (keyup.enter)=\"submitForgotPassword()\" />\n\n <button type=\"button\" class=\"wuic-auth-btn wuic-auth-btn-primary\" (click)=\"submitForgotPassword()\"\n [disabled]=\"!forgotEmail\">\n {{ 'auth.forgot.send_reset_link' | translate }}\n </button>\n\n @if (forgotMessage) {\n <div class=\"wuic-auth-success\">{{ forgotMessage }}</div>\n }\n @if (forgotError) {\n <div class=\"wuic-auth-error\">{{ forgotError }}</div>\n }\n\n <div class=\"wuic-auth-links\">\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('login')\">{{ 'auth.link.back_to_login' |\n translate }}</button>\n </div>\n } @else if (authView === 'resetPassword') {\n <form (ngSubmit)=\"submitResetPassword()\" novalidate>\n <label for=\"wuic-auth-reset-new-password\">{{ 'auth.reset.new_password' | translate }}</label>\n <input id=\"wuic-auth-reset-new-password\" type=\"password\" [(ngModel)]=\"resetNewPassword\"\n [placeholder]=\"'auth.reset.new_password' | translate\" autocomplete=\"new-password\" name=\"resetNewPassword\" />\n\n <button type=\"submit\" class=\"wuic-auth-btn wuic-auth-btn-primary\" [disabled]=\"!resetNewPassword\">\n {{ 'auth.reset.submit' | translate }}\n </button>\n\n @if (resetMessage) {\n <div class=\"wuic-auth-success\">{{ resetMessage }}</div>\n }\n @if (resetError) {\n <div class=\"wuic-auth-error\">{{ resetError }}</div>\n }\n\n <div class=\"wuic-auth-links\">\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('login')\">{{ 'auth.link.back_to_login'\n | translate }}</button>\n </div>\n </form>\n }\n\n }\n\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"refreshAuthSession()\" [disabled]=\"auth.loading\">\n {{ 'auth.refresh' | translate }}\n </button>\n </div>\n\n @if (auth.error) {\n <div class=\"wuic-auth-error\">{{ auth.error }}</div>\n }\n </div>\n }\n </div>\n }\n</nav>\n}\n\n<!-- <p-megaMenu *ngIf=\"menuType=='megamenu'\" [model]=\"megaItems\">\n</p-megaMenu> -->\n\n<!-- <p-megaMenu *ngIf=\"menuType=='megamenu'\" [model]=\"megaItems2\"></p-megaMenu>\n</p-megaMenu> -->", styles: [".wuic-session-warning{display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;background:color-mix(in srgb,var(--p-yellow-100, var(--yellow-100, #fef9c3)) 50%,var(--p-surface-card, var(--surface-card, #fff)));border-bottom:2px solid var(--p-yellow-500, var(--yellow-500, #eab308));color:var(--p-yellow-800, var(--yellow-800, #854d0e));font-size:.85rem;font-weight:500;z-index:1000}.wuic-session-warning .pi{font-size:1rem}.wuic-session-warning .wuic-auth-btn{margin-left:auto;padding:.25rem .6rem;font-size:.8rem}.wuic-auth-stage{display:block}.wuic-auth-bar-loading{display:flex;align-items:center;justify-content:center;min-height:48px;padding:.75rem 1rem}.wuic-auth-loading{display:flex;align-items:center;gap:.5rem;color:var(--p-text-color-secondary, var(--text-color-secondary, #64748b));font-size:.9rem}.wuic-auth-loading .pi-spinner{font-size:1.1rem}:host ::ng-deep .p-menubar .p-menubar-item-link[draggable=true]{cursor:grab}:host ::ng-deep .p-menubar .p-menubar-item-link[draggable=true]:active{cursor:grabbing}:host ::ng-deep .p-menubar .p-menubar-item-link:not(.p-disabled):hover{background:var(--p-content-hover-background, var(--surface-hover, #f1f5f9));color:var(--p-primary-color, var(--p-text-color, inherit))}:host-context(.theme-dark) ::ng-deep .p-menubar .p-menubar-item-link:not(.p-disabled):hover{background:color-mix(in srgb,var(--p-content-hover-background, #1f2937) 80%,var(--p-primary-color, #3b82f6) 20%);color:var(--p-primary-color, var(--p-text-color, #f3f4f6))}.wuic-menubar-extra{display:flex;align-items:center;gap:.75rem;min-width:280px;justify-content:flex-end;padding-left:.75rem}.wuic-menubar-user-flag{width:2rem;height:2rem;border-radius:999px;display:block;border:1px solid var(--p-content-border-color, var(--surface-border, #d9e2ec));background:var(--p-content-background, var(--surface-section, #eef3f8));object-fit:cover;flex:0 0 auto}.wuic-menubar-user-wrap{position:relative;display:flex;align-items:center;gap:.55rem;cursor:pointer}.wuic-menubar-user-caret{font-size:.8rem;color:var(--p-text-muted-color, var(--text-color-secondary, #4f6479))}.wuic-menubar-user{display:grid;line-height:1.15;text-align:right;min-width:0}.wuic-language-menu{position:absolute;top:calc(100% + .45rem);right:0;min-width:170px;background:var(--p-surface-card, var(--surface-card, #fff));border:1px solid var(--p-content-border-color, var(--surface-border, #d9e2ec));border-radius:8px;box-shadow:0 10px 28px color-mix(in srgb,var(--p-text-color, #1f2f43) 16%,transparent);z-index:1100;padding:.3rem}.wuic-language-item{width:100%;border:0;background:transparent;display:flex;align-items:center;gap:.8rem;text-align:left;border-radius:6px;padding:.45rem .55rem;cursor:pointer;color:var(--p-text-color, var(--text-color, #1f2f43))}.wuic-language-item-flag{width:1.1rem;height:1.1rem;border-radius:999px;border:1px solid var(--p-content-border-color, var(--surface-border, #d9e2ec));object-fit:cover;flex:0 0 auto}.wuic-language-item span{flex:1 1 auto}.wuic-language-item small{color:var(--p-text-muted-color, var(--text-color-secondary, #5f748a))}.wuic-language-item:hover{background:var(--p-content-hover-background, var(--surface-hover, #f5f8fb))}.wuic-language-item.is-active{background:color-mix(in srgb,var(--p-primary-color, #3b82f6) 14%,transparent);color:var(--p-primary-color, #315fdd)}:host-context(.theme-dark) .wuic-language-menu{background:var(--p-content-background, var(--surface-card, #111827));border-color:var(--p-content-border-color, var(--surface-border, rgba(255, 255, 255, .2)));box-shadow:0 10px 28px color-mix(in srgb,#000 55%,transparent),0 0 0 1px color-mix(in srgb,var(--p-content-border-color, rgba(255, 255, 255, .2)) 65%,transparent) inset}:host-context(.theme-dark) .wuic-language-item{color:var(--p-text-color, var(--text-color, #f3f4f6))}:host-context(.theme-dark) .wuic-language-item small{color:var(--p-text-muted-color, var(--text-color-secondary, #b7c3d0))}:host-context(.theme-dark) .wuic-language-item:hover{background:color-mix(in srgb,var(--p-content-hover-background, #1f2937) 85%,var(--p-primary-color, #3b82f6) 15%)}:host-context(.theme-dark) .wuic-language-item.is-active{background:color-mix(in srgb,var(--p-primary-color, #3b82f6) 30%,transparent);color:var(--p-primary-contrast-color, #e7f0ff)}.wuic-menubar-user-name{font-weight:600;color:var(--p-text-color, var(--text-color, #1f2f43));white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:120px}.wuic-menubar-user-role{font-size:.75rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:220px}.wuic-role-badge{display:inline-flex;align-items:center;max-width:100%;padding:.15rem .5rem;border-radius:999px;border:1px solid var(--p-content-border-color, var(--surface-border, #d4deea));background:var(--p-content-background, var(--surface-section, #f3f7fb));color:var(--p-text-muted-color, var(--text-color-secondary, #4a6077));font-size:.72rem;font-weight:600}.wuic-role-badge-admin{border-color:color-mix(in srgb,var(--p-yellow-500, var(--yellow-500, #f59e0b)) 55%,var(--p-content-border-color, var(--surface-border, #d4deea)));background:color-mix(in srgb,var(--p-yellow-100, var(--yellow-100, #fff6df)) 30%,var(--p-surface-card, var(--surface-card, #fff)));color:color-mix(in srgb,var(--p-yellow-700, var(--yellow-700, #9a5a00)) 80%,var(--p-text-color, var(--text-color, #233242)))}.wuic-menubar-logout{white-space:nowrap;padding-inline:.8rem}.wuic-license-badge{display:inline-flex;align-items:center;gap:.35rem;padding:.25rem .6rem;border-radius:999px;border:1px solid var(--p-content-border-color, var(--surface-border, #d4deea));background:var(--p-content-background, var(--surface-section, #f3f7fb));color:var(--p-text-muted-color, var(--text-color-secondary, #4a6077));font-size:.72rem;font-weight:700;letter-spacing:.02em;cursor:default;white-space:nowrap;-webkit-user-select:none;user-select:none}.wuic-license-badge .pi{font-size:.85rem}.wuic-license-badge-label{line-height:1}.wuic-license-badge-pro{border-color:color-mix(in srgb,var(--p-emerald-500, #10b981) 55%,var(--p-content-border-color, #d4deea));background:color-mix(in srgb,var(--p-emerald-100, #d1fae5) 45%,var(--p-surface-card, #fff));color:color-mix(in srgb,var(--p-emerald-700, #047857) 80%,var(--p-text-color, #233242))}.wuic-license-badge-dev{border-color:color-mix(in srgb,var(--p-blue-500, #3b82f6) 55%,var(--p-content-border-color, #d4deea));background:color-mix(in srgb,var(--p-blue-100, #dbeafe) 45%,var(--p-surface-card, #fff));color:color-mix(in srgb,var(--p-blue-700, #1d4ed8) 80%,var(--p-text-color, #233242))}.wuic-license-badge-trial{border-color:color-mix(in srgb,var(--p-orange-500, #f97316) 55%,var(--p-content-border-color, #d4deea));background:color-mix(in srgb,var(--p-orange-100, #ffedd5) 45%,var(--p-surface-card, #fff));color:color-mix(in srgb,var(--p-orange-700, #c2410c) 80%,var(--p-text-color, #233242))}.wuic-auth-stage-login,.wuic-auth-stage-login *{color-scheme:inherit}.wuic-auth-stage-login{--wuic-surface-ground: var(--p-surface-ground, var(--surface-ground, var(--surface-b, #f7f8fa)));--wuic-surface-card: var(--p-surface-card, var(--surface-card, var(--surface-a, #ffffff)));--wuic-surface-section: var(--p-content-background, var(--surface-section, var(--surface-c, #ffffff)));--wuic-surface-hover: var(--p-content-hover-background, var(--surface-hover, var(--surface-hover-background, #f3f4f6)));--wuic-surface-border: var(--p-content-border-color, var(--surface-border, var(--surface-d, #d7dce3)));--wuic-text: var(--p-text-color, var(--text-color, #1f2937));--wuic-text-muted: var(--p-text-muted-color, var(--text-color-secondary, #6b7280));--wuic-primary: var(--p-primary-color, var(--primary-color, #3b82f6));--wuic-primary-text: var(--p-primary-contrast-color, var(--primary-color-text, #ffffff));--wuic-danger-100: var(--p-red-100, var(--red-100, #fee2e2));--wuic-danger-500: var(--p-red-500, var(--red-500, #ef4444));--wuic-danger-700: var(--p-red-700, var(--red-700, #b91c1c));min-height:calc(100vh - 2rem);display:grid;place-items:center;padding:1rem;color:var(--wuic-text);background-color:var(--wuic-surface-ground)!important;background:radial-gradient(circle at 15% 20%,color-mix(in srgb,var(--wuic-primary) 12%,transparent),transparent 45%),radial-gradient(circle at 85% 15%,color-mix(in srgb,var(--wuic-primary) 8%,transparent),transparent 40%),linear-gradient(180deg,var(--wuic-surface-ground) 0%,var(--wuic-surface-ground) 100%)!important}:host-context(.theme-dark) .wuic-auth-stage-login{--wuic-surface-ground: var(--p-surface-900, var(--p-content-background, var(--surface-b, #0f172a)));--wuic-surface-card: var(--p-content-background, var(--p-surface-800, var(--surface-a, #111827)));--wuic-surface-section: var(--p-content-background, var(--p-surface-800, var(--surface-c, #1f2937)));--wuic-surface-hover: var(--p-content-hover-background, var(--p-surface-700, var(--surface-hover-background, #374151)));--wuic-surface-border: var(--p-content-border-color, var(--surface-d, rgba(255, 255, 255, .28)));--wuic-text: var(--p-content-color, var(--p-text-color, var(--text-color, #f3f4f6)));--wuic-text-muted: var(--p-text-muted-color, var(--text-color-secondary, #d1d5db))}:host-context(.theme-dark) .wuic-auth-bar,:host-context(.theme-dark) .wuic-auth-bar-login{background:var(--wuic-surface-card)!important;color:var(--wuic-text)!important;border-color:var(--wuic-surface-border)!important}:host-context(.theme-dark) .wuic-auth-form label,:host-context(.theme-dark) .wuic-auth-status,:host-context(.theme-dark) .wuic-auth-bar span,:host-context(.theme-dark) .wuic-auth-bar strong{color:var(--wuic-text)!important}:host-context(.theme-dark) .wuic-auth-form input{background:var(--wuic-surface-section)!important;color:var(--wuic-text)!important;border-color:var(--wuic-surface-border)!important}:host-context(.theme-dark) .wuic-auth-form input::placeholder{color:var(--wuic-text-muted)!important}:host-context(.theme-dark) .wuic-auth-btn{background:var(--wuic-surface-section)!important;color:var(--wuic-text)!important;border-color:color-mix(in srgb,var(--wuic-surface-border) 75%,white 25%)!important}:host-context(.theme-dark) .wuic-auth-btn-primary{background:var(--wuic-primary)!important;border-color:var(--wuic-primary)!important;color:var(--wuic-primary-text)!important}.wuic-auth-bar{display:grid;gap:.5rem;padding:.6rem .75rem;border:1px solid var(--wuic-surface-border);border-top:0;background:var(--wuic-surface-card)!important;color:var(--wuic-text)!important}.wuic-auth-bar-login{width:min(420px,92vw);gap:1rem;padding:1.15rem 1.2rem;border-radius:14px;border:1px solid var(--wuic-surface-border)!important;background:var(--wuic-surface-card)!important;box-shadow:0 14px 40px color-mix(in srgb,var(--wuic-text) 12%,transparent),0 0 0 1px color-mix(in srgb,var(--wuic-surface-border) 65%,transparent) inset}.wuic-auth-status{font-size:.9rem;color:var(--wuic-text)!important;margin-bottom:.1rem;line-height:1.35}.wuic-auth-form{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem}.wuic-auth-form-login{flex-direction:column;align-items:stretch;gap:.75rem}.wuic-auth-form-login form{display:grid;gap:.5rem;margin:0}.wuic-auth-form label{font-size:.85rem;color:var(--wuic-text)!important}.wuic-auth-form-login label{font-weight:600;margin-top:0}.wuic-auth-form input{min-width:220px;flex:1 1 320px;border:1px solid var(--wuic-surface-border);border-radius:6px;padding:.45rem .6rem;background:var(--wuic-surface-section)!important;color:var(--wuic-text)!important;box-shadow:0 0 0 1px color-mix(in srgb,var(--wuic-surface-border) 35%,transparent) inset}.wuic-auth-form input::placeholder{color:var(--wuic-text-muted);opacity:1}.wuic-auth-form input:focus{outline:none;border-color:var(--wuic-primary);box-shadow:0 0 0 1px color-mix(in srgb,var(--wuic-primary) 55%,transparent) inset,0 0 0 .15rem color-mix(in srgb,var(--wuic-primary) 22%,transparent)}.wuic-auth-form-login input{min-width:0;width:100%;flex:initial}.wuic-auth-btn{border:1px solid var(--wuic-surface-border);background:var(--wuic-surface-section)!important;color:var(--wuic-text)!important;border-radius:6px;padding:.45rem .75rem;cursor:pointer;transition:background-color .12s ease,border-color .12s ease,color .12s ease,box-shadow .12s ease}.wuic-auth-form-login .wuic-auth-btn{width:100%;justify-content:center}.wuic-auth-form-login>.wuic-auth-btn:not(.wuic-auth-btn-primary){margin-top:.2rem}.wuic-auth-btn:disabled{opacity:.5;cursor:default}.wuic-auth-btn:not(:disabled):hover{background:var(--wuic-surface-hover)!important;border-color:color-mix(in srgb,var(--wuic-primary) 35%,var(--wuic-surface-border))}.wuic-auth-btn:not(:disabled):focus-visible{outline:none;box-shadow:0 0 0 .15rem color-mix(in srgb,var(--wuic-primary) 20%,transparent)}.wuic-auth-btn-primary{background:var(--wuic-primary)!important;border-color:var(--wuic-primary)!important;color:var(--wuic-primary-text)!important}.wuic-auth-btn-primary:not(:disabled):hover{background:color-mix(in srgb,var(--wuic-primary) 88%,black 12%)!important;border-color:color-mix(in srgb,var(--wuic-primary) 88%,black 12%)!important;color:var(--wuic-primary-text)!important}.wuic-captcha{margin:.25rem 0}.wuic-auth-error{color:var(--wuic-danger-700);font-size:.85rem;background:color-mix(in srgb,var(--wuic-danger-100) 22%,var(--wuic-surface-card));border:1px solid color-mix(in srgb,var(--wuic-danger-500) 35%,var(--wuic-surface-border));border-radius:8px;padding:.5rem .65rem}.wuic-auth-success{color:var(--p-green-700, var(--green-700, #15803d));font-size:.85rem;background:color-mix(in srgb,var(--p-green-100, var(--green-100, #dcfce7)) 22%,var(--wuic-surface-card));border:1px solid color-mix(in srgb,var(--p-green-500, var(--green-500, #22c55e)) 35%,var(--wuic-surface-border));border-radius:8px;padding:.5rem .65rem}.wuic-auth-links{display:flex;justify-content:center;gap:1.25rem;margin-top:.1rem}.wuic-auth-link{appearance:none;background:transparent;border:none;padding:0;margin:0;font:inherit;font-size:.82rem;color:color-mix(in srgb,var(--wuic-primary) 60%,#0f172a 40%);cursor:pointer;text-decoration:underline;text-underline-offset:2px}.wuic-auth-link:hover{color:color-mix(in srgb,var(--wuic-primary) 40%,#0f172a 60%)}@media(max-width:768px){.wuic-menubar-extra{min-width:0;gap:.5rem}.wuic-menubar-user-name,.wuic-menubar-user-role{max-width:130px}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list,:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-submenu{overflow-x:hidden!important}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list{max-height:calc(100vh - 13rem);overflow-y:auto!important;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-submenu{width:100%!important;margin-left:0!important;max-height:none;overflow-y:visible!important}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-submenu>.p-menubar-item{width:100%!important;float:none!important}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-item-link{white-space:normal;word-break:break-word}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list>.p-menubar-item>.p-menubar-submenu{display:grid;grid-template-columns:minmax(0,1fr);gap:0}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list>.p-menubar-item>.p-menubar-submenu>.p-menubar-item>.p-menubar-item-link{padding-top:.5rem;padding-bottom:.5rem}}@media(max-width:768px)and (min-width:560px){:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list>.p-menubar-item>.p-menubar-submenu{grid-template-columns:repeat(2,minmax(0,1fr));column-gap:.25rem}}:host ::ng-deep .p-contextmenu{z-index:13000!important}:host ::ng-deep .p-menubar-item-link[draggable=true]{cursor:grab}:host ::ng-deep .p-menubar-item-link[draggable=true]:active{cursor:grabbing}.wuic-menu-move-actions{display:flex;gap:.5rem;justify-content:flex-end;margin-top:.75rem}\n"], dependencies: [{ kind: "ngmodule", type: MenubarModule }, { kind: "component", type: i5$1.Menubar, selector: "p-menubar", inputs: ["model", "styleClass", "autoZIndex", "baseZIndex", "autoDisplay", "autoHide", "breakpoint", "autoHideDelay", "id", "ariaLabel", "ariaLabelledBy"], outputs: ["onFocus", "onBlur"] }, { kind: "ngmodule", type: MegaMenuModule }, { kind: "ngmodule", type: ContextMenuModule }, { kind: "component", type: i14.ContextMenu, selector: "p-contextMenu, p-contextmenu, p-context-menu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "autoZIndex", "baseZIndex", "id", "breakpoint", "ariaLabel", "ariaLabelledBy", "pressDelay", "appendTo", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i2.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "maskMotionOptions", "motionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "component", type: DataSourceComponent, selector: "wuic-data-source", inputs: ["route", "routeFromRouting", "hardcodedRoute", "autoload", "loading", "changeTracking", "parentRecord", "parentMetaInfo", "parentDatasource", "useComboEndpoint", "comboFormulaLookup", "comboMcId", "comboField", "comboExtraFields", "comboCurrentRecord", "componentRef"] }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$4.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: i8.TranslatePipe, name: "translate" }] });
45244
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: MetaMenuComponent, isStandalone: true, selector: "wuic-meta-menu", host: { listeners: { "document:click": "onDocumentClick()" } }, viewQueries: [{ propertyName: "menu", first: true, predicate: ["menubar"], descendants: true }, { propertyName: "menuMetadataDs", first: true, predicate: ["menuMetadataDs"], descendants: true }], ngImport: i0, template: "@if (authState$ | async; as auth) {\n<nav class=\"wuic-meta-menu-landmark\" aria-label=\"Menu principale\">\n @if (auth.sessionExpiring) {\n <div class=\"wuic-session-warning\">\n <span class=\"pi pi-exclamation-triangle\"></span>\n {{ 'auth.session.expiring_in' | translate }} <strong>{{ auth.sessionExpiresInSeconds }}</strong> {{ 'seconds' |\n translate }}.\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"refreshAuthSession()\">{{ 'auth.session.extend' | translate\n }}</button>\n </div>\n }\n @if (auth.initialized && !requiresAuthentication(auth)) {\n <div style=\"display:none\">\n <wuic-data-source #menuMetadataDs [hardcodedRoute]=\"metaMenuRoute\" [autoload]=\"false\"></wuic-data-source>\n </div>\n <p-menubar #menubar [model]=\"items\" [autoHide]=\"false\">\n <ng-template #item let-item>\n @if (item.route) {\n <!-- [href] con path assoluto (NO `#/` hash) cosi' Lighthouse SEO\n `crawlable-anchors` considera il link crawlable: il path\n `/<route>` e' un URL valid + condivisibile + apribile in nuova\n tab via Ctrl+Click. Il click handler `onMenuItemClick` chiama\n `event.preventDefault()` quando il button click e' \"primary\"\n (no Ctrl/middle), cosi' la SPA navigation passa per\n `router.navigateByUrl` (che con `withHashLocation()` aggiunge\n comunque l'hash internamente all'URL) e preserva pending changes\n /query params. Per Ctrl+Click / middle-click il browser segue\n l'href naturalmente \u2192 apre nuova tab con path puro (poi il\n server fa SPA fallback a index.html, e Angular legge la route\n dalla path). -->\n <a class=\"p-menubar-item-link\" [href]=\"getMenuItemHref(item)\" (click)=\"onMenuItemClick(item, $event)\"\n (contextmenu)=\"onMenuContext($event, item, auth, menuCtx)\"\n [attr.draggable]=\"canDragMenu(auth, item) ? true : null\" (dragstart)=\"onMenuDragStart($event, item, auth)\"\n (dragover)=\"onMenuDragOver($event, item, auth)\" (drop)=\"onMenuDrop($event, item, auth)\"\n (dragend)=\"onMenuDragEnd()\">\n <span [class]=\"item.icon\"></span>\n <span class=\"ml-2\">{{ item.label | translate }}</span>\n </a>\n } @else {\n <!-- Parent items (folder con submenu, NO route propria): NON sono `<a>`\n ma `<div>` semantici. Lighthouse non flagga `<div>` come uncrawlable\n (l'audit `crawlable-anchors` controlla solo `<a>`). PrimeNG si\n occupa di toggle del submenu via il (click). -->\n <div class=\"p-menubar-item-link\" (click)=\"onMenuItemClick(item, $event); onFocus($event)\"\n (contextmenu)=\"onMenuContext($event, item, auth, menuCtx)\"\n [attr.draggable]=\"canDragMenu(auth, item) ? true : null\" (dragstart)=\"onMenuDragStart($event, item, auth)\"\n (dragover)=\"onMenuDragOver($event, item, auth)\" (drop)=\"onMenuDrop($event, item, auth)\"\n (dragend)=\"onMenuDragEnd()\">\n <span [class]=\"item.icon\"></span>\n <span class=\"ml-2\">{{ item.label | translate }}</span>\n <span class=\"pi pi-fw pi-angle-down ml-2\"></span>\n </div>\n }\n </ng-template>\n\n <ng-template #end>\n @if (auth.authenticated || auth.legacyAuthenticated) {\n <div class=\"wuic-menubar-extra\">\n <span class=\"wuic-license-badge\" [class.wuic-license-badge-pro]=\"getLicenseTier() === 'professional'\"\n [class.wuic-license-badge-dev]=\"getLicenseTier() === 'developer'\"\n [class.wuic-license-badge-trial]=\"getLicenseTier() === 'trial'\" [attr.title]=\"getLicenseTooltip()\"\n role=\"status\" [attr.aria-label]=\"getLicenseTooltip()\">\n <span [class]=\"getLicenseIconClass()\" aria-hidden=\"true\"></span>\n <span class=\"wuic-license-badge-label\">{{ getLicenseBadgeLabel() }}</span>\n </span>\n\n <div class=\"wuic-menubar-user-wrap\" (click)=\"toggleUserLanguageMenu($event)\">\n <img class=\"wuic-menubar-user-flag\" [src]=\"getCurrentUserLanguageFlagSrc()\" [alt]=\"getCurrentUserLanguageId()\"\n (error)=\"onLanguageFlagLoadError($event)\" />\n <div class=\"wuic-menubar-user\">\n @if (auth.authenticated) {\n <span [pTooltip]=\"auth.name || ('user.default' | translate)\" tooltipPosition=\"bottom\"\n class=\"wuic-menubar-user-name\">{{ auth.name || ('user.default' | translate) }}</span>\n <span class=\"wuic-menubar-user-role\">\n <span [pTooltip]=\"auth.provider || ('auth.provider.google' | translate)\" tooltipPosition=\"bottom\"\n class=\"wuic-role-badge\">{{ auth.provider || ('auth.provider.google' | translate) }}</span>\n </span>\n } @else {\n <span [pTooltip]=\"auth.legacyName || ('user.default' | translate)\" tooltipPosition=\"bottom\"\n class=\"wuic-menubar-user-name\">{{ auth.legacyName || ('user.default' | translate) }}</span>\n <span class=\"wuic-menubar-user-role\">\n <span [pTooltip]=\"auth.legacyRole || ('auth.role.na' | translate)\" tooltipPosition=\"bottom\"\n class=\"wuic-role-badge\" [class.wuic-role-badge-admin]=\"isAdminRole(auth)\">{{ auth.legacyRole ||\n ('auth.role.na' | translate) }}</span>\n </span>\n }\n </div>\n @if (userLanguageSwitchEnabled) {\n <span class=\"pi pi-angle-down wuic-menubar-user-caret\" aria-hidden=\"true\"></span>\n }\n @if (userLanguageSwitchEnabled && userLanguageMenuVisible) {\n <div class=\"wuic-language-menu\" (click)=\"$event.stopPropagation()\">\n @for (lang of userLanguages; track lang.id) {\n <button type=\"button\" class=\"wuic-language-item\" [class.is-active]=\"isCurrentUserLanguage(lang.id)\"\n (click)=\"onSelectUserLanguage(lang.id, $event)\">\n <img class=\"wuic-language-item-flag\" [src]=\"getLanguageFlagSrc(lang.id)\" [alt]=\"lang.id\"\n (error)=\"onLanguageFlagLoadError($event)\" />\n <span>{{ lang.lingua || lang.id }}</span>\n <small>{{ lang.id }}</small>\n </button>\n }\n </div>\n }\n </div>\n\n <button type=\"button\" class=\"wuic-auth-btn wuic-menubar-logout\" (click)=\"logout()\" [disabled]=\"auth.loading\">\n {{ 'logout' | translate }}\n </button>\n </div>\n }\n </ng-template>\n </p-menubar>\n <p-contextMenu #menuCtx [model]=\"contextItems\" appendTo=\"body\" [autoZIndex]=\"true\"\n [baseZIndex]=\"13000\"></p-contextMenu>\n <p-dialog [(visible)]=\"moveDialogVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" [closable]=\"true\"\n [style]=\"{ width: '28rem' }\" [header]=\"'menu.move.title' | translate\" (onHide)=\"cancelPendingMove()\"\n [autoZIndex]=\"true\" [baseZIndex]=\"14000\">\n <p>{{ 'menu.move.drop_target' | translate }}: <strong>{{ pendingMove?.targetLabel || pendingMove?.targetId\n }}</strong></p>\n <p>{{ 'menu.move.choose_position' | translate }}</p>\n <div class=\"wuic-menu-move-actions\">\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"executePendingMove('before')\"\n [disabled]=\"!hasMenuAdminMethod('reorderMenu')\">{{ 'menu.move.before' | translate }}</button>\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"executePendingMove('after')\"\n [disabled]=\"!hasMenuAdminMethod('reorderMenu')\">{{ 'menu.move.after' | translate }}</button>\n <button type=\"button\" class=\"wuic-auth-btn wuic-auth-btn-primary\" (click)=\"executePendingMove('inside')\"\n [disabled]=\"!hasMenuAdminMethod('nestMenu')\">{{ 'menu.move.inside' | translate }}</button>\n </div>\n </p-dialog>\n }\n @if (auth.initialized && showAuthBar(auth)) {\n <div class=\"wuic-auth-stage\" [class.wuic-auth-stage-login]=\"requiresAuthentication(auth)\">\n @if (!(translationsLoaded$ | async)) {\n <!-- Non mostrare il form login finche' le traduzioni non sono caricate: l'utente\n vedrebbe i placeholder grezzi `auth.username` / `auth.password` invece dei\n label tradotti. Meglio uno spinner finche' la tabella traduzioni arriva\n (localStorage o backend). Tipicamente <50ms se cache, ~200-800ms se\n cold-fetch dal backend.\n Eccezione: se il circuit breaker del TranslationManager si e' aperto\n (5 fallimenti consecutivi \u2192 BE offline persistente), lo spinner\n resterebbe infinito. Mostriamo invece un banner con bottone Riprova\n cosi' l'utente puo' riarmare manualmente quando il BE torna su. -->\n @if (backendCircuitOpen$ | async) {\n <div class=\"wuic-auth-bar wuic-auth-bar-loading wuic-be-offline-banner\" [class.wuic-auth-bar-login]=\"requiresAuthentication(auth)\">\n <div class=\"wuic-be-offline-content\">\n <i class=\"pi pi-exclamation-triangle wuic-be-offline-icon\"></i>\n <div class=\"wuic-be-offline-text\">\n <strong>Backend non raggiungibile</strong>\n <span>Impossibile caricare le traduzioni dopo 5 tentativi. Verifica che il server WuicCore sia attivo.</span>\n </div>\n <button type=\"button\" class=\"wuic-be-offline-retry\" (click)=\"retryBackend()\" aria-label=\"Riprova connessione al backend\">\n <i class=\"pi pi-refresh\"></i>\n <span>Riprova</span>\n </button>\n </div>\n </div>\n } @else {\n <div class=\"wuic-auth-bar wuic-auth-bar-loading\" [class.wuic-auth-bar-login]=\"requiresAuthentication(auth)\">\n <div class=\"wuic-auth-loading\">\n <i class=\"pi pi-spinner pi-spin\"></i>\n <span>Loading\u2026</span>\n </div>\n </div>\n }\n } @else {\n <div class=\"wuic-auth-bar\" [class.wuic-auth-bar-login]=\"requiresAuthentication(auth)\">\n <div class=\"wuic-auth-status\">\n @if (auth.loading) {\n <span>{{ 'auth.status.loading' | translate }}</span>\n } @else if (auth.authenticated) {\n <span>{{ 'auth.status.active' | translate }}: <strong>{{ auth.name || ('user.default' | translate) }}</strong>\n ({{ auth.provider || ('auth.provider.google' | translate) }})</span>\n } @else if (auth.enabled) {\n <span>{{ 'auth.status.not_authenticated' | translate }} ({{ auth.provider || ('auth.provider.google' |\n translate)\n }})</span>\n } @else if (auth.legacyAuthenticated) {\n <span>{{ 'auth.status.active' | translate }}: <strong>{{ auth.legacyName || ('user.default' | translate)\n }}</strong> ({{ 'auth.provider.credentials' | translate }})</span>\n } @else {\n <span>{{ 'auth.status.login_required' | translate }} ({{ 'auth.provider.credentials' | translate }})</span>\n }\n </div>\n\n <div class=\"wuic-auth-form\" [class.wuic-auth-form-login]=\"requiresAuthentication(auth)\">\n @if (auth.enabled) {\n <label for=\"wuicAuthReturnUrl\">{{ 'auth.return_url' | translate }}</label>\n <input id=\"wuicAuthReturnUrl\" type=\"text\" [(ngModel)]=\"authReturnUrl\" (ngModelChange)=\"onAuthReturnUrlChanged()\"\n placeholder=\"/\" />\n\n <button type=\"button\" class=\"wuic-auth-btn wuic-auth-btn-primary\" (click)=\"loginWithGoogle()\"\n [disabled]=\"auth.authenticated || auth.loading\">\n {{ 'auth.login_google' | translate }}\n </button>\n\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"logout()\" [disabled]=\"!auth.authenticated || auth.loading\">\n {{ 'logout' | translate }}\n </button>\n } @else if (!auth.legacyAuthenticated) {\n\n @if (authView === 'login') {\n <form (ngSubmit)=\"loginLegacy()\" novalidate>\n <label for=\"wuicAuthUser\">{{ 'auth.username' | translate }}</label>\n <input id=\"wuicAuthUser\" type=\"text\" [(ngModel)]=\"loginUsername\" [placeholder]=\"'auth.username' | translate\"\n autocomplete=\"username\" name=\"loginUsername\" />\n\n <label for=\"wuicAuthPwd\">{{ 'auth.password' | translate }}</label>\n <input id=\"wuicAuthPwd\" type=\"password\" [(ngModel)]=\"loginPassword\"\n [placeholder]=\"'auth.password' | translate\" autocomplete=\"current-password\" name=\"loginPassword\" />\n\n @if (captchaEnabled) {\n <div id=\"wuic-captcha-container\" class=\"wuic-captcha\"></div>\n }\n\n <button type=\"submit\" class=\"wuic-auth-btn wuic-auth-btn-primary\"\n [disabled]=\"!loginUsername || !loginPassword || auth.loading || (captchaEnabled && !captchaToken)\">\n {{ 'auth.login' | translate }}\n </button>\n\n <div class=\"wuic-auth-links\">\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('forgotPassword')\">{{\n 'auth.link.forgot_password' | translate }}</button>\n @if (registrationEnabled) {\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('register')\">{{ 'auth.link.register' |\n translate }}</button>\n }\n </div>\n </form>\n } @else if (authView === 'register') {\n <form (ngSubmit)=\"submitRegistration()\" novalidate>\n <label for=\"wuic-auth-register-username\">{{ 'auth.username' | translate }}</label>\n <input id=\"wuic-auth-register-username\" type=\"text\" [(ngModel)]=\"registerUsername\"\n [placeholder]=\"'auth.username' | translate\" autocomplete=\"username\" name=\"registerUsername\" />\n\n <label for=\"wuic-auth-register-email\">{{ 'auth.email' | translate }}</label>\n <input id=\"wuic-auth-register-email\" type=\"email\" [(ngModel)]=\"registerEmail\"\n [placeholder]=\"'auth.email' | translate\" autocomplete=\"email\" name=\"registerEmail\" />\n\n <label for=\"wuic-auth-register-password\">{{ 'auth.password' | translate }}</label>\n <input id=\"wuic-auth-register-password\" type=\"password\" [(ngModel)]=\"registerPassword\"\n [placeholder]=\"'auth.password' | translate\" autocomplete=\"new-password\" name=\"registerPassword\" />\n\n @if (captchaEnabled) {\n <div id=\"wuic-captcha-container\" class=\"wuic-captcha\"></div>\n }\n\n <button type=\"submit\" class=\"wuic-auth-btn wuic-auth-btn-primary\"\n [disabled]=\"!registerUsername || !registerEmail || !registerPassword || (captchaEnabled && !captchaToken)\">\n {{ 'auth.link.register' | translate }}\n </button>\n\n @if (registerMessage) {\n <div class=\"wuic-auth-success\">{{ registerMessage }}</div>\n }\n @if (registerError) {\n <div class=\"wuic-auth-error\">{{ registerError }}</div>\n }\n\n <div class=\"wuic-auth-links\">\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('login')\">{{ 'auth.link.back_to_login'\n | translate }}</button>\n </div>\n </form>\n } @else if (authView === 'forgotPassword') {\n <label for=\"wuic-auth-forgot-email\">{{ 'auth.email' | translate }}</label>\n <input id=\"wuic-auth-forgot-email\" type=\"email\" [(ngModel)]=\"forgotEmail\"\n [placeholder]=\"'auth.forgot.email_placeholder' | translate\" autocomplete=\"email\"\n (keyup.enter)=\"submitForgotPassword()\" />\n\n <button type=\"button\" class=\"wuic-auth-btn wuic-auth-btn-primary\" (click)=\"submitForgotPassword()\"\n [disabled]=\"!forgotEmail\">\n {{ 'auth.forgot.send_reset_link' | translate }}\n </button>\n\n @if (forgotMessage) {\n <div class=\"wuic-auth-success\">{{ forgotMessage }}</div>\n }\n @if (forgotError) {\n <div class=\"wuic-auth-error\">{{ forgotError }}</div>\n }\n\n <div class=\"wuic-auth-links\">\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('login')\">{{ 'auth.link.back_to_login' |\n translate }}</button>\n </div>\n } @else if (authView === 'resetPassword') {\n <form (ngSubmit)=\"submitResetPassword()\" novalidate>\n <label for=\"wuic-auth-reset-new-password\">{{ 'auth.reset.new_password' | translate }}</label>\n <input id=\"wuic-auth-reset-new-password\" type=\"password\" [(ngModel)]=\"resetNewPassword\"\n [placeholder]=\"'auth.reset.new_password' | translate\" autocomplete=\"new-password\" name=\"resetNewPassword\" />\n\n <button type=\"submit\" class=\"wuic-auth-btn wuic-auth-btn-primary\" [disabled]=\"!resetNewPassword\">\n {{ 'auth.reset.submit' | translate }}\n </button>\n\n @if (resetMessage) {\n <div class=\"wuic-auth-success\">{{ resetMessage }}</div>\n }\n @if (resetError) {\n <div class=\"wuic-auth-error\">{{ resetError }}</div>\n }\n\n <div class=\"wuic-auth-links\">\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('login')\">{{ 'auth.link.back_to_login'\n | translate }}</button>\n </div>\n </form>\n }\n\n }\n\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"refreshAuthSession()\" [disabled]=\"auth.loading\">\n {{ 'auth.refresh' | translate }}\n </button>\n </div>\n\n @if (auth.error) {\n <div class=\"wuic-auth-error\">{{ auth.error }}</div>\n }\n </div>\n }\n </div>\n }\n</nav>\n}\n\n<!-- <p-megaMenu *ngIf=\"menuType=='megamenu'\" [model]=\"megaItems\">\n</p-megaMenu> -->\n\n<!-- <p-megaMenu *ngIf=\"menuType=='megamenu'\" [model]=\"megaItems2\"></p-megaMenu>\n</p-megaMenu> -->", styles: [".wuic-session-warning{display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;background:color-mix(in srgb,var(--p-yellow-100, var(--yellow-100, #fef9c3)) 50%,var(--p-surface-card, var(--surface-card, #fff)));border-bottom:2px solid var(--p-yellow-500, var(--yellow-500, #eab308));color:var(--p-yellow-800, var(--yellow-800, #854d0e));font-size:.85rem;font-weight:500;z-index:1000}.wuic-session-warning .pi{font-size:1rem}.wuic-session-warning .wuic-auth-btn{margin-left:auto;padding:.25rem .6rem;font-size:.8rem}.wuic-auth-stage{display:block}.wuic-auth-bar-loading{display:flex;align-items:center;justify-content:center;min-height:48px;padding:.75rem 1rem}.wuic-auth-loading{display:flex;align-items:center;gap:.5rem;color:var(--p-text-color-secondary, var(--text-color-secondary, #64748b));font-size:.9rem}.wuic-auth-loading .pi-spinner{font-size:1.1rem}:host ::ng-deep .p-menubar .p-menubar-item-link[draggable=true]{cursor:grab}:host ::ng-deep .p-menubar .p-menubar-item-link[draggable=true]:active{cursor:grabbing}:host ::ng-deep .p-menubar .p-menubar-item-link:not(.p-disabled):hover{background:var(--p-content-hover-background, var(--surface-hover, #f1f5f9));color:var(--p-primary-color, var(--p-text-color, inherit))}:host-context(.theme-dark) ::ng-deep .p-menubar .p-menubar-item-link:not(.p-disabled):hover{background:color-mix(in srgb,var(--p-content-hover-background, #1f2937) 80%,var(--p-primary-color, #3b82f6) 20%);color:var(--p-primary-color, var(--p-text-color, #f3f4f6))}.wuic-menubar-extra{display:flex;align-items:center;gap:.75rem;min-width:280px;justify-content:flex-end;padding-left:.75rem}.wuic-menubar-user-flag{width:2rem;height:2rem;border-radius:999px;display:block;border:1px solid var(--p-content-border-color, var(--surface-border, #d9e2ec));background:var(--p-content-background, var(--surface-section, #eef3f8));object-fit:cover;flex:0 0 auto}.wuic-menubar-user-wrap{position:relative;display:flex;align-items:center;gap:.55rem;cursor:pointer}.wuic-menubar-user-caret{font-size:.8rem;color:var(--p-text-muted-color, var(--text-color-secondary, #4f6479))}.wuic-menubar-user{display:grid;line-height:1.15;text-align:right;min-width:0}.wuic-language-menu{position:absolute;top:calc(100% + .45rem);right:0;min-width:170px;background:var(--p-surface-card, var(--surface-card, #fff));border:1px solid var(--p-content-border-color, var(--surface-border, #d9e2ec));border-radius:8px;box-shadow:0 10px 28px color-mix(in srgb,var(--p-text-color, #1f2f43) 16%,transparent);z-index:1100;padding:.3rem}.wuic-language-item{width:100%;border:0;background:transparent;display:flex;align-items:center;gap:.8rem;text-align:left;border-radius:6px;padding:.45rem .55rem;cursor:pointer;color:var(--p-text-color, var(--text-color, #1f2f43))}.wuic-language-item-flag{width:1.1rem;height:1.1rem;border-radius:999px;border:1px solid var(--p-content-border-color, var(--surface-border, #d9e2ec));object-fit:cover;flex:0 0 auto}.wuic-language-item span{flex:1 1 auto}.wuic-language-item small{color:var(--p-text-muted-color, var(--text-color-secondary, #5f748a))}.wuic-language-item:hover{background:var(--p-content-hover-background, var(--surface-hover, #f5f8fb))}.wuic-language-item.is-active{background:color-mix(in srgb,var(--p-primary-color, #3b82f6) 14%,transparent);color:var(--p-primary-color, #315fdd)}:host-context(.theme-dark) .wuic-language-menu{background:var(--p-content-background, var(--surface-card, #111827));border-color:var(--p-content-border-color, var(--surface-border, rgba(255, 255, 255, .2)));box-shadow:0 10px 28px color-mix(in srgb,#000 55%,transparent),0 0 0 1px color-mix(in srgb,var(--p-content-border-color, rgba(255, 255, 255, .2)) 65%,transparent) inset}:host-context(.theme-dark) .wuic-language-item{color:var(--p-text-color, var(--text-color, #f3f4f6))}:host-context(.theme-dark) .wuic-language-item small{color:var(--p-text-muted-color, var(--text-color-secondary, #b7c3d0))}:host-context(.theme-dark) .wuic-language-item:hover{background:color-mix(in srgb,var(--p-content-hover-background, #1f2937) 85%,var(--p-primary-color, #3b82f6) 15%)}:host-context(.theme-dark) .wuic-language-item.is-active{background:color-mix(in srgb,var(--p-primary-color, #3b82f6) 30%,transparent);color:var(--p-primary-contrast-color, #e7f0ff)}.wuic-menubar-user-name{font-weight:600;color:var(--p-text-color, var(--text-color, #1f2f43));white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:120px}.wuic-menubar-user-role{font-size:.75rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:220px}.wuic-role-badge{display:inline-flex;align-items:center;max-width:100%;padding:.15rem .5rem;border-radius:999px;border:1px solid var(--p-content-border-color, var(--surface-border, #d4deea));background:var(--p-content-background, var(--surface-section, #f3f7fb));color:var(--p-text-muted-color, var(--text-color-secondary, #4a6077));font-size:.72rem;font-weight:600}.wuic-role-badge-admin{border-color:color-mix(in srgb,var(--p-yellow-500, var(--yellow-500, #f59e0b)) 55%,var(--p-content-border-color, var(--surface-border, #d4deea)));background:color-mix(in srgb,var(--p-yellow-100, var(--yellow-100, #fff6df)) 30%,var(--p-surface-card, var(--surface-card, #fff)));color:color-mix(in srgb,var(--p-yellow-700, var(--yellow-700, #9a5a00)) 80%,var(--p-text-color, var(--text-color, #233242)))}.wuic-menubar-logout{white-space:nowrap;padding-inline:.8rem}.wuic-license-badge{display:inline-flex;align-items:center;gap:.35rem;padding:.25rem .6rem;border-radius:999px;border:1px solid var(--p-content-border-color, var(--surface-border, #d4deea));background:var(--p-content-background, var(--surface-section, #f3f7fb));color:var(--p-text-muted-color, var(--text-color-secondary, #4a6077));font-size:.72rem;font-weight:700;letter-spacing:.02em;cursor:default;white-space:nowrap;-webkit-user-select:none;user-select:none}.wuic-license-badge .pi{font-size:.85rem}.wuic-license-badge-label{line-height:1}.wuic-license-badge-pro{border-color:color-mix(in srgb,var(--p-emerald-500, #10b981) 55%,var(--p-content-border-color, #d4deea));background:color-mix(in srgb,var(--p-emerald-100, #d1fae5) 45%,var(--p-surface-card, #fff));color:color-mix(in srgb,var(--p-emerald-700, #047857) 80%,var(--p-text-color, #233242))}.wuic-license-badge-dev{border-color:color-mix(in srgb,var(--p-blue-500, #3b82f6) 55%,var(--p-content-border-color, #d4deea));background:color-mix(in srgb,var(--p-blue-100, #dbeafe) 45%,var(--p-surface-card, #fff));color:color-mix(in srgb,var(--p-blue-700, #1d4ed8) 80%,var(--p-text-color, #233242))}.wuic-license-badge-trial{border-color:color-mix(in srgb,var(--p-orange-500, #f97316) 55%,var(--p-content-border-color, #d4deea));background:color-mix(in srgb,var(--p-orange-100, #ffedd5) 45%,var(--p-surface-card, #fff));color:color-mix(in srgb,var(--p-orange-700, #c2410c) 80%,var(--p-text-color, #233242))}.wuic-auth-stage-login,.wuic-auth-stage-login *{color-scheme:inherit}.wuic-auth-stage-login{--wuic-surface-ground: var(--p-surface-ground, var(--surface-ground, var(--surface-b, #f7f8fa)));--wuic-surface-card: var(--p-surface-card, var(--surface-card, var(--surface-a, #ffffff)));--wuic-surface-section: var(--p-content-background, var(--surface-section, var(--surface-c, #ffffff)));--wuic-surface-hover: var(--p-content-hover-background, var(--surface-hover, var(--surface-hover-background, #f3f4f6)));--wuic-surface-border: var(--p-content-border-color, var(--surface-border, var(--surface-d, #d7dce3)));--wuic-text: var(--p-text-color, var(--text-color, #1f2937));--wuic-text-muted: var(--p-text-muted-color, var(--text-color-secondary, #6b7280));--wuic-primary: var(--p-primary-color, var(--primary-color, #3b82f6));--wuic-primary-text: var(--p-primary-contrast-color, var(--primary-color-text, #ffffff));--wuic-danger-100: var(--p-red-100, var(--red-100, #fee2e2));--wuic-danger-500: var(--p-red-500, var(--red-500, #ef4444));--wuic-danger-700: var(--p-red-700, var(--red-700, #b91c1c));min-height:calc(100vh - 2rem);display:grid;place-items:center;padding:1rem;color:var(--wuic-text);background-color:var(--wuic-surface-ground)!important;background:radial-gradient(circle at 15% 20%,color-mix(in srgb,var(--wuic-primary) 12%,transparent),transparent 45%),radial-gradient(circle at 85% 15%,color-mix(in srgb,var(--wuic-primary) 8%,transparent),transparent 40%),linear-gradient(180deg,var(--wuic-surface-ground) 0%,var(--wuic-surface-ground) 100%)!important}:host-context(.theme-dark) .wuic-auth-stage-login{--wuic-surface-ground: var(--p-surface-900, var(--p-content-background, var(--surface-b, #0f172a)));--wuic-surface-card: var(--p-content-background, var(--p-surface-800, var(--surface-a, #111827)));--wuic-surface-section: var(--p-content-background, var(--p-surface-800, var(--surface-c, #1f2937)));--wuic-surface-hover: var(--p-content-hover-background, var(--p-surface-700, var(--surface-hover-background, #374151)));--wuic-surface-border: var(--p-content-border-color, var(--surface-d, rgba(255, 255, 255, .28)));--wuic-text: var(--p-content-color, var(--p-text-color, var(--text-color, #f3f4f6)));--wuic-text-muted: var(--p-text-muted-color, var(--text-color-secondary, #d1d5db))}:host-context(.theme-dark) .wuic-auth-bar,:host-context(.theme-dark) .wuic-auth-bar-login{background:var(--wuic-surface-card)!important;color:var(--wuic-text)!important;border-color:var(--wuic-surface-border)!important}:host-context(.theme-dark) .wuic-auth-form label,:host-context(.theme-dark) .wuic-auth-status,:host-context(.theme-dark) .wuic-auth-bar span,:host-context(.theme-dark) .wuic-auth-bar strong{color:var(--wuic-text)!important}:host-context(.theme-dark) .wuic-auth-form input{background:var(--wuic-surface-section)!important;color:var(--wuic-text)!important;border-color:var(--wuic-surface-border)!important}:host-context(.theme-dark) .wuic-auth-form input::placeholder{color:var(--wuic-text-muted)!important}:host-context(.theme-dark) .wuic-auth-btn{background:var(--wuic-surface-section)!important;color:var(--wuic-text)!important;border-color:color-mix(in srgb,var(--wuic-surface-border) 75%,white 25%)!important}:host-context(.theme-dark) .wuic-auth-btn-primary{background:var(--wuic-primary)!important;border-color:var(--wuic-primary)!important;color:var(--wuic-primary-text)!important}.wuic-auth-bar{display:grid;gap:.5rem;padding:.6rem .75rem;border:1px solid var(--wuic-surface-border);border-top:0;background:var(--wuic-surface-card)!important;color:var(--wuic-text)!important}.wuic-auth-bar-login{width:min(420px,92vw);gap:1rem;padding:1.15rem 1.2rem;border-radius:14px;border:1px solid var(--wuic-surface-border)!important;background:var(--wuic-surface-card)!important;box-shadow:0 14px 40px color-mix(in srgb,var(--wuic-text) 12%,transparent),0 0 0 1px color-mix(in srgb,var(--wuic-surface-border) 65%,transparent) inset}.wuic-auth-status{font-size:.9rem;color:var(--wuic-text)!important;margin-bottom:.1rem;line-height:1.35}.wuic-auth-form{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem}.wuic-auth-form-login{flex-direction:column;align-items:stretch;gap:.75rem}.wuic-auth-form-login form{display:grid;gap:.5rem;margin:0}.wuic-auth-form label{font-size:.85rem;color:var(--wuic-text)!important}.wuic-auth-form-login label{font-weight:600;margin-top:0}.wuic-auth-form input{min-width:220px;flex:1 1 320px;border:1px solid var(--wuic-surface-border);border-radius:6px;padding:.45rem .6rem;background:var(--wuic-surface-section)!important;color:var(--wuic-text)!important;box-shadow:0 0 0 1px color-mix(in srgb,var(--wuic-surface-border) 35%,transparent) inset}.wuic-auth-form input::placeholder{color:var(--wuic-text-muted);opacity:1}.wuic-auth-form input:focus{outline:none;border-color:var(--wuic-primary);box-shadow:0 0 0 1px color-mix(in srgb,var(--wuic-primary) 55%,transparent) inset,0 0 0 .15rem color-mix(in srgb,var(--wuic-primary) 22%,transparent)}.wuic-auth-form-login input{min-width:0;width:100%;flex:initial}.wuic-auth-btn{border:1px solid var(--wuic-surface-border);background:var(--wuic-surface-section)!important;color:var(--wuic-text)!important;border-radius:6px;padding:.45rem .75rem;cursor:pointer;transition:background-color .12s ease,border-color .12s ease,color .12s ease,box-shadow .12s ease}.wuic-auth-form-login .wuic-auth-btn{width:100%;justify-content:center}.wuic-auth-form-login>.wuic-auth-btn:not(.wuic-auth-btn-primary){margin-top:.2rem}.wuic-auth-btn:disabled{opacity:.5;cursor:default}.wuic-auth-btn:not(:disabled):hover{background:var(--wuic-surface-hover)!important;border-color:color-mix(in srgb,var(--wuic-primary) 35%,var(--wuic-surface-border))}.wuic-auth-btn:not(:disabled):focus-visible{outline:none;box-shadow:0 0 0 .15rem color-mix(in srgb,var(--wuic-primary) 20%,transparent)}.wuic-auth-btn-primary{background:var(--wuic-primary)!important;border-color:var(--wuic-primary)!important;color:var(--wuic-primary-text)!important}.wuic-auth-btn-primary:not(:disabled):hover{background:color-mix(in srgb,var(--wuic-primary) 88%,black 12%)!important;border-color:color-mix(in srgb,var(--wuic-primary) 88%,black 12%)!important;color:var(--wuic-primary-text)!important}.wuic-captcha{margin:.25rem 0}.wuic-auth-error{color:var(--wuic-danger-700);font-size:.85rem;background:color-mix(in srgb,var(--wuic-danger-100) 22%,var(--wuic-surface-card));border:1px solid color-mix(in srgb,var(--wuic-danger-500) 35%,var(--wuic-surface-border));border-radius:8px;padding:.5rem .65rem}.wuic-auth-success{color:var(--p-green-700, var(--green-700, #15803d));font-size:.85rem;background:color-mix(in srgb,var(--p-green-100, var(--green-100, #dcfce7)) 22%,var(--wuic-surface-card));border:1px solid color-mix(in srgb,var(--p-green-500, var(--green-500, #22c55e)) 35%,var(--wuic-surface-border));border-radius:8px;padding:.5rem .65rem}.wuic-auth-links{display:flex;justify-content:center;gap:1.25rem;margin-top:.1rem}.wuic-auth-link{appearance:none;background:transparent;border:none;padding:0;margin:0;font:inherit;font-size:.82rem;color:color-mix(in srgb,var(--wuic-primary) 60%,#0f172a 40%);cursor:pointer;text-decoration:underline;text-underline-offset:2px}.wuic-auth-link:hover{color:color-mix(in srgb,var(--wuic-primary) 40%,#0f172a 60%)}@media(max-width:768px){.wuic-menubar-extra{min-width:0;gap:.5rem}.wuic-menubar-user-name,.wuic-menubar-user-role{max-width:130px}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list,:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-submenu{overflow-x:hidden!important}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list{max-height:calc(100vh - 13rem);overflow-y:auto!important;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-submenu{width:100%!important;margin-left:0!important;max-height:none;overflow-y:visible!important}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-submenu>.p-menubar-item{width:100%!important;float:none!important}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-item-link{white-space:normal;word-break:break-word}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list>.p-menubar-item>.p-menubar-submenu{display:grid;grid-template-columns:minmax(0,1fr);gap:0}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list>.p-menubar-item>.p-menubar-submenu>.p-menubar-item>.p-menubar-item-link{padding-top:.5rem;padding-bottom:.5rem}}@media(max-width:768px)and (min-width:560px){:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list>.p-menubar-item>.p-menubar-submenu{grid-template-columns:repeat(2,minmax(0,1fr));column-gap:.25rem}}:host ::ng-deep .p-contextmenu{z-index:13000!important}:host ::ng-deep .p-menubar-item-link[draggable=true]{cursor:grab}:host ::ng-deep .p-menubar-item-link[draggable=true]:active{cursor:grabbing}.wuic-menu-move-actions{display:flex;gap:.5rem;justify-content:flex-end;margin-top:.75rem}.wuic-be-offline-banner{background:#dc354514;border-bottom:2px solid rgba(220,53,69,.6)}.wuic-be-offline-content{display:flex;align-items:center;gap:.75rem;padding:.5rem 1rem;width:100%;max-width:720px;margin:0 auto}.wuic-be-offline-icon{color:#dc3545;font-size:1.5rem;flex-shrink:0}.wuic-be-offline-text{display:flex;flex-direction:column;flex:1 1 auto;min-width:0}.wuic-be-offline-text strong{color:#842029;font-size:.95rem}.wuic-be-offline-text span{color:#58151c;font-size:.8rem;opacity:.85}.wuic-be-offline-retry{display:inline-flex;align-items:center;gap:.4rem;padding:.45rem .9rem;background:#dc3545;color:#fff;border:1px solid #b02a37;border-radius:4px;font-weight:600;font-size:.85rem;cursor:pointer;flex-shrink:0;transition:background-color .12s ease}.wuic-be-offline-retry:hover{background:#bb2d3b}.wuic-be-offline-retry:active{background:#a52834}.wuic-be-offline-retry i{font-size:.9rem}\n"], dependencies: [{ kind: "ngmodule", type: MenubarModule }, { kind: "component", type: i5$1.Menubar, selector: "p-menubar", inputs: ["model", "styleClass", "autoZIndex", "baseZIndex", "autoDisplay", "autoHide", "breakpoint", "autoHideDelay", "id", "ariaLabel", "ariaLabelledBy"], outputs: ["onFocus", "onBlur"] }, { kind: "ngmodule", type: MegaMenuModule }, { kind: "ngmodule", type: ContextMenuModule }, { kind: "component", type: i14.ContextMenu, selector: "p-contextMenu, p-contextmenu, p-context-menu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "autoZIndex", "baseZIndex", "id", "breakpoint", "ariaLabel", "ariaLabelledBy", "pressDelay", "appendTo", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i2.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "maskMotionOptions", "motionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "component", type: DataSourceComponent, selector: "wuic-data-source", inputs: ["route", "routeFromRouting", "hardcodedRoute", "autoload", "loading", "changeTracking", "parentRecord", "parentMetaInfo", "parentDatasource", "useComboEndpoint", "comboFormulaLookup", "comboMcId", "comboField", "comboExtraFields", "comboCurrentRecord", "componentRef"] }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$4.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: i8.TranslatePipe, name: "translate" }] });
45072
45245
  }
45073
45246
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MetaMenuComponent, decorators: [{
45074
45247
  type: Component,
45075
- args: [{ selector: 'wuic-meta-menu', imports: [MenubarModule, MegaMenuModule, ContextMenuModule, DialogModule, DataSourceComponent, RouterModule, FormsModule, AsyncPipe, TranslateModule, TooltipModule], template: "@if (authState$ | async; as auth) {\n<nav class=\"wuic-meta-menu-landmark\" aria-label=\"Menu principale\">\n @if (auth.sessionExpiring) {\n <div class=\"wuic-session-warning\">\n <span class=\"pi pi-exclamation-triangle\"></span>\n {{ 'auth.session.expiring_in' | translate }} <strong>{{ auth.sessionExpiresInSeconds }}</strong> {{ 'seconds' |\n translate }}.\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"refreshAuthSession()\">{{ 'auth.session.extend' | translate\n }}</button>\n </div>\n }\n @if (auth.initialized && !requiresAuthentication(auth)) {\n <div style=\"display:none\">\n <wuic-data-source #menuMetadataDs [hardcodedRoute]=\"metaMenuRoute\" [autoload]=\"false\"></wuic-data-source>\n </div>\n <p-menubar #menubar [model]=\"items\" [autoHide]=\"false\">\n <ng-template #item let-item>\n @if (item.route) {\n <!-- [href] con path assoluto (NO `#/` hash) cosi' Lighthouse SEO\n `crawlable-anchors` considera il link crawlable: il path\n `/<route>` e' un URL valid + condivisibile + apribile in nuova\n tab via Ctrl+Click. Il click handler `onMenuItemClick` chiama\n `event.preventDefault()` quando il button click e' \"primary\"\n (no Ctrl/middle), cosi' la SPA navigation passa per\n `router.navigateByUrl` (che con `withHashLocation()` aggiunge\n comunque l'hash internamente all'URL) e preserva pending changes\n /query params. Per Ctrl+Click / middle-click il browser segue\n l'href naturalmente \u2192 apre nuova tab con path puro (poi il\n server fa SPA fallback a index.html, e Angular legge la route\n dalla path). -->\n <a class=\"p-menubar-item-link\" [href]=\"getMenuItemHref(item)\" (click)=\"onMenuItemClick(item, $event)\"\n (contextmenu)=\"onMenuContext($event, item, auth, menuCtx)\"\n [attr.draggable]=\"canDragMenu(auth, item) ? true : null\" (dragstart)=\"onMenuDragStart($event, item, auth)\"\n (dragover)=\"onMenuDragOver($event, item, auth)\" (drop)=\"onMenuDrop($event, item, auth)\"\n (dragend)=\"onMenuDragEnd()\">\n <span [class]=\"item.icon\"></span>\n <span class=\"ml-2\">{{ item.label | translate }}</span>\n </a>\n } @else {\n <!-- Parent items (folder con submenu, NO route propria): NON sono `<a>`\n ma `<div>` semantici. Lighthouse non flagga `<div>` come uncrawlable\n (l'audit `crawlable-anchors` controlla solo `<a>`). PrimeNG si\n occupa di toggle del submenu via il (click). -->\n <div class=\"p-menubar-item-link\" (click)=\"onMenuItemClick(item, $event); onFocus($event)\"\n (contextmenu)=\"onMenuContext($event, item, auth, menuCtx)\"\n [attr.draggable]=\"canDragMenu(auth, item) ? true : null\" (dragstart)=\"onMenuDragStart($event, item, auth)\"\n (dragover)=\"onMenuDragOver($event, item, auth)\" (drop)=\"onMenuDrop($event, item, auth)\"\n (dragend)=\"onMenuDragEnd()\">\n <span [class]=\"item.icon\"></span>\n <span class=\"ml-2\">{{ item.label | translate }}</span>\n <span class=\"pi pi-fw pi-angle-down ml-2\"></span>\n </div>\n }\n </ng-template>\n\n <ng-template #end>\n @if (auth.authenticated || auth.legacyAuthenticated) {\n <div class=\"wuic-menubar-extra\">\n <span class=\"wuic-license-badge\" [class.wuic-license-badge-pro]=\"getLicenseTier() === 'professional'\"\n [class.wuic-license-badge-dev]=\"getLicenseTier() === 'developer'\"\n [class.wuic-license-badge-trial]=\"getLicenseTier() === 'trial'\" [attr.title]=\"getLicenseTooltip()\"\n role=\"status\" [attr.aria-label]=\"getLicenseTooltip()\">\n <span [class]=\"getLicenseIconClass()\" aria-hidden=\"true\"></span>\n <span class=\"wuic-license-badge-label\">{{ getLicenseBadgeLabel() }}</span>\n </span>\n\n <div class=\"wuic-menubar-user-wrap\" (click)=\"toggleUserLanguageMenu($event)\">\n <img class=\"wuic-menubar-user-flag\" [src]=\"getCurrentUserLanguageFlagSrc()\" [alt]=\"getCurrentUserLanguageId()\"\n (error)=\"onLanguageFlagLoadError($event)\" />\n <div class=\"wuic-menubar-user\">\n @if (auth.authenticated) {\n <span [pTooltip]=\"auth.name || ('user.default' | translate)\" tooltipPosition=\"bottom\"\n class=\"wuic-menubar-user-name\">{{ auth.name || ('user.default' | translate) }}</span>\n <span class=\"wuic-menubar-user-role\">\n <span [pTooltip]=\"auth.provider || ('auth.provider.google' | translate)\" tooltipPosition=\"bottom\"\n class=\"wuic-role-badge\">{{ auth.provider || ('auth.provider.google' | translate) }}</span>\n </span>\n } @else {\n <span [pTooltip]=\"auth.legacyName || ('user.default' | translate)\" tooltipPosition=\"bottom\"\n class=\"wuic-menubar-user-name\">{{ auth.legacyName || ('user.default' | translate) }}</span>\n <span class=\"wuic-menubar-user-role\">\n <span [pTooltip]=\"auth.legacyRole || ('auth.role.na' | translate)\" tooltipPosition=\"bottom\"\n class=\"wuic-role-badge\" [class.wuic-role-badge-admin]=\"isAdminRole(auth)\">{{ auth.legacyRole ||\n ('auth.role.na' | translate) }}</span>\n </span>\n }\n </div>\n @if (userLanguageSwitchEnabled) {\n <span class=\"pi pi-angle-down wuic-menubar-user-caret\" aria-hidden=\"true\"></span>\n }\n @if (userLanguageSwitchEnabled && userLanguageMenuVisible) {\n <div class=\"wuic-language-menu\" (click)=\"$event.stopPropagation()\">\n @for (lang of userLanguages; track lang.id) {\n <button type=\"button\" class=\"wuic-language-item\" [class.is-active]=\"isCurrentUserLanguage(lang.id)\"\n (click)=\"onSelectUserLanguage(lang.id, $event)\">\n <img class=\"wuic-language-item-flag\" [src]=\"getLanguageFlagSrc(lang.id)\" [alt]=\"lang.id\"\n (error)=\"onLanguageFlagLoadError($event)\" />\n <span>{{ lang.lingua || lang.id }}</span>\n <small>{{ lang.id }}</small>\n </button>\n }\n </div>\n }\n </div>\n\n <button type=\"button\" class=\"wuic-auth-btn wuic-menubar-logout\" (click)=\"logout()\" [disabled]=\"auth.loading\">\n {{ 'logout' | translate }}\n </button>\n </div>\n }\n </ng-template>\n </p-menubar>\n <p-contextMenu #menuCtx [model]=\"contextItems\" appendTo=\"body\" [autoZIndex]=\"true\"\n [baseZIndex]=\"13000\"></p-contextMenu>\n <p-dialog [(visible)]=\"moveDialogVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" [closable]=\"true\"\n [style]=\"{ width: '28rem' }\" [header]=\"'menu.move.title' | translate\" (onHide)=\"cancelPendingMove()\"\n [autoZIndex]=\"true\" [baseZIndex]=\"14000\">\n <p>{{ 'menu.move.drop_target' | translate }}: <strong>{{ pendingMove?.targetLabel || pendingMove?.targetId\n }}</strong></p>\n <p>{{ 'menu.move.choose_position' | translate }}</p>\n <div class=\"wuic-menu-move-actions\">\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"executePendingMove('before')\"\n [disabled]=\"!hasMenuAdminMethod('reorderMenu')\">{{ 'menu.move.before' | translate }}</button>\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"executePendingMove('after')\"\n [disabled]=\"!hasMenuAdminMethod('reorderMenu')\">{{ 'menu.move.after' | translate }}</button>\n <button type=\"button\" class=\"wuic-auth-btn wuic-auth-btn-primary\" (click)=\"executePendingMove('inside')\"\n [disabled]=\"!hasMenuAdminMethod('nestMenu')\">{{ 'menu.move.inside' | translate }}</button>\n </div>\n </p-dialog>\n }\n @if (auth.initialized && showAuthBar(auth)) {\n <div class=\"wuic-auth-stage\" [class.wuic-auth-stage-login]=\"requiresAuthentication(auth)\">\n @if (!(translationsLoaded$ | async)) {\n <!-- Non mostrare il form login finche' le traduzioni non sono caricate: l'utente\n vedrebbe i placeholder grezzi `auth.username` / `auth.password` invece dei\n label tradotti. Meglio uno spinner finche' la tabella traduzioni arriva\n (localStorage o backend). Tipicamente <50ms se cache, ~200-800ms se\n cold-fetch dal backend. -->\n <div class=\"wuic-auth-bar wuic-auth-bar-loading\" [class.wuic-auth-bar-login]=\"requiresAuthentication(auth)\">\n <div class=\"wuic-auth-loading\">\n <i class=\"pi pi-spinner pi-spin\"></i>\n <span>Loading\u2026</span>\n </div>\n </div>\n } @else {\n <div class=\"wuic-auth-bar\" [class.wuic-auth-bar-login]=\"requiresAuthentication(auth)\">\n <div class=\"wuic-auth-status\">\n @if (auth.loading) {\n <span>{{ 'auth.status.loading' | translate }}</span>\n } @else if (auth.authenticated) {\n <span>{{ 'auth.status.active' | translate }}: <strong>{{ auth.name || ('user.default' | translate) }}</strong>\n ({{ auth.provider || ('auth.provider.google' | translate) }})</span>\n } @else if (auth.enabled) {\n <span>{{ 'auth.status.not_authenticated' | translate }} ({{ auth.provider || ('auth.provider.google' |\n translate)\n }})</span>\n } @else if (auth.legacyAuthenticated) {\n <span>{{ 'auth.status.active' | translate }}: <strong>{{ auth.legacyName || ('user.default' | translate)\n }}</strong> ({{ 'auth.provider.credentials' | translate }})</span>\n } @else {\n <span>{{ 'auth.status.login_required' | translate }} ({{ 'auth.provider.credentials' | translate }})</span>\n }\n </div>\n\n <div class=\"wuic-auth-form\" [class.wuic-auth-form-login]=\"requiresAuthentication(auth)\">\n @if (auth.enabled) {\n <label for=\"wuicAuthReturnUrl\">{{ 'auth.return_url' | translate }}</label>\n <input id=\"wuicAuthReturnUrl\" type=\"text\" [(ngModel)]=\"authReturnUrl\" (ngModelChange)=\"onAuthReturnUrlChanged()\"\n placeholder=\"/\" />\n\n <button type=\"button\" class=\"wuic-auth-btn wuic-auth-btn-primary\" (click)=\"loginWithGoogle()\"\n [disabled]=\"auth.authenticated || auth.loading\">\n {{ 'auth.login_google' | translate }}\n </button>\n\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"logout()\" [disabled]=\"!auth.authenticated || auth.loading\">\n {{ 'logout' | translate }}\n </button>\n } @else if (!auth.legacyAuthenticated) {\n\n @if (authView === 'login') {\n <form (ngSubmit)=\"loginLegacy()\" novalidate>\n <label for=\"wuicAuthUser\">{{ 'auth.username' | translate }}</label>\n <input id=\"wuicAuthUser\" type=\"text\" [(ngModel)]=\"loginUsername\" [placeholder]=\"'auth.username' | translate\"\n autocomplete=\"username\" name=\"loginUsername\" />\n\n <label for=\"wuicAuthPwd\">{{ 'auth.password' | translate }}</label>\n <input id=\"wuicAuthPwd\" type=\"password\" [(ngModel)]=\"loginPassword\"\n [placeholder]=\"'auth.password' | translate\" autocomplete=\"current-password\" name=\"loginPassword\" />\n\n @if (captchaEnabled) {\n <div id=\"wuic-captcha-container\" class=\"wuic-captcha\"></div>\n }\n\n <button type=\"submit\" class=\"wuic-auth-btn wuic-auth-btn-primary\"\n [disabled]=\"!loginUsername || !loginPassword || auth.loading || (captchaEnabled && !captchaToken)\">\n {{ 'auth.login' | translate }}\n </button>\n\n <div class=\"wuic-auth-links\">\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('forgotPassword')\">{{\n 'auth.link.forgot_password' | translate }}</button>\n @if (registrationEnabled) {\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('register')\">{{ 'auth.link.register' |\n translate }}</button>\n }\n </div>\n </form>\n } @else if (authView === 'register') {\n <form (ngSubmit)=\"submitRegistration()\" novalidate>\n <label for=\"wuic-auth-register-username\">{{ 'auth.username' | translate }}</label>\n <input id=\"wuic-auth-register-username\" type=\"text\" [(ngModel)]=\"registerUsername\"\n [placeholder]=\"'auth.username' | translate\" autocomplete=\"username\" name=\"registerUsername\" />\n\n <label for=\"wuic-auth-register-email\">{{ 'auth.email' | translate }}</label>\n <input id=\"wuic-auth-register-email\" type=\"email\" [(ngModel)]=\"registerEmail\"\n [placeholder]=\"'auth.email' | translate\" autocomplete=\"email\" name=\"registerEmail\" />\n\n <label for=\"wuic-auth-register-password\">{{ 'auth.password' | translate }}</label>\n <input id=\"wuic-auth-register-password\" type=\"password\" [(ngModel)]=\"registerPassword\"\n [placeholder]=\"'auth.password' | translate\" autocomplete=\"new-password\" name=\"registerPassword\" />\n\n @if (captchaEnabled) {\n <div id=\"wuic-captcha-container\" class=\"wuic-captcha\"></div>\n }\n\n <button type=\"submit\" class=\"wuic-auth-btn wuic-auth-btn-primary\"\n [disabled]=\"!registerUsername || !registerEmail || !registerPassword || (captchaEnabled && !captchaToken)\">\n {{ 'auth.link.register' | translate }}\n </button>\n\n @if (registerMessage) {\n <div class=\"wuic-auth-success\">{{ registerMessage }}</div>\n }\n @if (registerError) {\n <div class=\"wuic-auth-error\">{{ registerError }}</div>\n }\n\n <div class=\"wuic-auth-links\">\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('login')\">{{ 'auth.link.back_to_login'\n | translate }}</button>\n </div>\n </form>\n } @else if (authView === 'forgotPassword') {\n <label for=\"wuic-auth-forgot-email\">{{ 'auth.email' | translate }}</label>\n <input id=\"wuic-auth-forgot-email\" type=\"email\" [(ngModel)]=\"forgotEmail\"\n [placeholder]=\"'auth.forgot.email_placeholder' | translate\" autocomplete=\"email\"\n (keyup.enter)=\"submitForgotPassword()\" />\n\n <button type=\"button\" class=\"wuic-auth-btn wuic-auth-btn-primary\" (click)=\"submitForgotPassword()\"\n [disabled]=\"!forgotEmail\">\n {{ 'auth.forgot.send_reset_link' | translate }}\n </button>\n\n @if (forgotMessage) {\n <div class=\"wuic-auth-success\">{{ forgotMessage }}</div>\n }\n @if (forgotError) {\n <div class=\"wuic-auth-error\">{{ forgotError }}</div>\n }\n\n <div class=\"wuic-auth-links\">\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('login')\">{{ 'auth.link.back_to_login' |\n translate }}</button>\n </div>\n } @else if (authView === 'resetPassword') {\n <form (ngSubmit)=\"submitResetPassword()\" novalidate>\n <label for=\"wuic-auth-reset-new-password\">{{ 'auth.reset.new_password' | translate }}</label>\n <input id=\"wuic-auth-reset-new-password\" type=\"password\" [(ngModel)]=\"resetNewPassword\"\n [placeholder]=\"'auth.reset.new_password' | translate\" autocomplete=\"new-password\" name=\"resetNewPassword\" />\n\n <button type=\"submit\" class=\"wuic-auth-btn wuic-auth-btn-primary\" [disabled]=\"!resetNewPassword\">\n {{ 'auth.reset.submit' | translate }}\n </button>\n\n @if (resetMessage) {\n <div class=\"wuic-auth-success\">{{ resetMessage }}</div>\n }\n @if (resetError) {\n <div class=\"wuic-auth-error\">{{ resetError }}</div>\n }\n\n <div class=\"wuic-auth-links\">\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('login')\">{{ 'auth.link.back_to_login'\n | translate }}</button>\n </div>\n </form>\n }\n\n }\n\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"refreshAuthSession()\" [disabled]=\"auth.loading\">\n {{ 'auth.refresh' | translate }}\n </button>\n </div>\n\n @if (auth.error) {\n <div class=\"wuic-auth-error\">{{ auth.error }}</div>\n }\n </div>\n }\n </div>\n }\n</nav>\n}\n\n<!-- <p-megaMenu *ngIf=\"menuType=='megamenu'\" [model]=\"megaItems\">\n</p-megaMenu> -->\n\n<!-- <p-megaMenu *ngIf=\"menuType=='megamenu'\" [model]=\"megaItems2\"></p-megaMenu>\n</p-megaMenu> -->", styles: [".wuic-session-warning{display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;background:color-mix(in srgb,var(--p-yellow-100, var(--yellow-100, #fef9c3)) 50%,var(--p-surface-card, var(--surface-card, #fff)));border-bottom:2px solid var(--p-yellow-500, var(--yellow-500, #eab308));color:var(--p-yellow-800, var(--yellow-800, #854d0e));font-size:.85rem;font-weight:500;z-index:1000}.wuic-session-warning .pi{font-size:1rem}.wuic-session-warning .wuic-auth-btn{margin-left:auto;padding:.25rem .6rem;font-size:.8rem}.wuic-auth-stage{display:block}.wuic-auth-bar-loading{display:flex;align-items:center;justify-content:center;min-height:48px;padding:.75rem 1rem}.wuic-auth-loading{display:flex;align-items:center;gap:.5rem;color:var(--p-text-color-secondary, var(--text-color-secondary, #64748b));font-size:.9rem}.wuic-auth-loading .pi-spinner{font-size:1.1rem}:host ::ng-deep .p-menubar .p-menubar-item-link[draggable=true]{cursor:grab}:host ::ng-deep .p-menubar .p-menubar-item-link[draggable=true]:active{cursor:grabbing}:host ::ng-deep .p-menubar .p-menubar-item-link:not(.p-disabled):hover{background:var(--p-content-hover-background, var(--surface-hover, #f1f5f9));color:var(--p-primary-color, var(--p-text-color, inherit))}:host-context(.theme-dark) ::ng-deep .p-menubar .p-menubar-item-link:not(.p-disabled):hover{background:color-mix(in srgb,var(--p-content-hover-background, #1f2937) 80%,var(--p-primary-color, #3b82f6) 20%);color:var(--p-primary-color, var(--p-text-color, #f3f4f6))}.wuic-menubar-extra{display:flex;align-items:center;gap:.75rem;min-width:280px;justify-content:flex-end;padding-left:.75rem}.wuic-menubar-user-flag{width:2rem;height:2rem;border-radius:999px;display:block;border:1px solid var(--p-content-border-color, var(--surface-border, #d9e2ec));background:var(--p-content-background, var(--surface-section, #eef3f8));object-fit:cover;flex:0 0 auto}.wuic-menubar-user-wrap{position:relative;display:flex;align-items:center;gap:.55rem;cursor:pointer}.wuic-menubar-user-caret{font-size:.8rem;color:var(--p-text-muted-color, var(--text-color-secondary, #4f6479))}.wuic-menubar-user{display:grid;line-height:1.15;text-align:right;min-width:0}.wuic-language-menu{position:absolute;top:calc(100% + .45rem);right:0;min-width:170px;background:var(--p-surface-card, var(--surface-card, #fff));border:1px solid var(--p-content-border-color, var(--surface-border, #d9e2ec));border-radius:8px;box-shadow:0 10px 28px color-mix(in srgb,var(--p-text-color, #1f2f43) 16%,transparent);z-index:1100;padding:.3rem}.wuic-language-item{width:100%;border:0;background:transparent;display:flex;align-items:center;gap:.8rem;text-align:left;border-radius:6px;padding:.45rem .55rem;cursor:pointer;color:var(--p-text-color, var(--text-color, #1f2f43))}.wuic-language-item-flag{width:1.1rem;height:1.1rem;border-radius:999px;border:1px solid var(--p-content-border-color, var(--surface-border, #d9e2ec));object-fit:cover;flex:0 0 auto}.wuic-language-item span{flex:1 1 auto}.wuic-language-item small{color:var(--p-text-muted-color, var(--text-color-secondary, #5f748a))}.wuic-language-item:hover{background:var(--p-content-hover-background, var(--surface-hover, #f5f8fb))}.wuic-language-item.is-active{background:color-mix(in srgb,var(--p-primary-color, #3b82f6) 14%,transparent);color:var(--p-primary-color, #315fdd)}:host-context(.theme-dark) .wuic-language-menu{background:var(--p-content-background, var(--surface-card, #111827));border-color:var(--p-content-border-color, var(--surface-border, rgba(255, 255, 255, .2)));box-shadow:0 10px 28px color-mix(in srgb,#000 55%,transparent),0 0 0 1px color-mix(in srgb,var(--p-content-border-color, rgba(255, 255, 255, .2)) 65%,transparent) inset}:host-context(.theme-dark) .wuic-language-item{color:var(--p-text-color, var(--text-color, #f3f4f6))}:host-context(.theme-dark) .wuic-language-item small{color:var(--p-text-muted-color, var(--text-color-secondary, #b7c3d0))}:host-context(.theme-dark) .wuic-language-item:hover{background:color-mix(in srgb,var(--p-content-hover-background, #1f2937) 85%,var(--p-primary-color, #3b82f6) 15%)}:host-context(.theme-dark) .wuic-language-item.is-active{background:color-mix(in srgb,var(--p-primary-color, #3b82f6) 30%,transparent);color:var(--p-primary-contrast-color, #e7f0ff)}.wuic-menubar-user-name{font-weight:600;color:var(--p-text-color, var(--text-color, #1f2f43));white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:120px}.wuic-menubar-user-role{font-size:.75rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:220px}.wuic-role-badge{display:inline-flex;align-items:center;max-width:100%;padding:.15rem .5rem;border-radius:999px;border:1px solid var(--p-content-border-color, var(--surface-border, #d4deea));background:var(--p-content-background, var(--surface-section, #f3f7fb));color:var(--p-text-muted-color, var(--text-color-secondary, #4a6077));font-size:.72rem;font-weight:600}.wuic-role-badge-admin{border-color:color-mix(in srgb,var(--p-yellow-500, var(--yellow-500, #f59e0b)) 55%,var(--p-content-border-color, var(--surface-border, #d4deea)));background:color-mix(in srgb,var(--p-yellow-100, var(--yellow-100, #fff6df)) 30%,var(--p-surface-card, var(--surface-card, #fff)));color:color-mix(in srgb,var(--p-yellow-700, var(--yellow-700, #9a5a00)) 80%,var(--p-text-color, var(--text-color, #233242)))}.wuic-menubar-logout{white-space:nowrap;padding-inline:.8rem}.wuic-license-badge{display:inline-flex;align-items:center;gap:.35rem;padding:.25rem .6rem;border-radius:999px;border:1px solid var(--p-content-border-color, var(--surface-border, #d4deea));background:var(--p-content-background, var(--surface-section, #f3f7fb));color:var(--p-text-muted-color, var(--text-color-secondary, #4a6077));font-size:.72rem;font-weight:700;letter-spacing:.02em;cursor:default;white-space:nowrap;-webkit-user-select:none;user-select:none}.wuic-license-badge .pi{font-size:.85rem}.wuic-license-badge-label{line-height:1}.wuic-license-badge-pro{border-color:color-mix(in srgb,var(--p-emerald-500, #10b981) 55%,var(--p-content-border-color, #d4deea));background:color-mix(in srgb,var(--p-emerald-100, #d1fae5) 45%,var(--p-surface-card, #fff));color:color-mix(in srgb,var(--p-emerald-700, #047857) 80%,var(--p-text-color, #233242))}.wuic-license-badge-dev{border-color:color-mix(in srgb,var(--p-blue-500, #3b82f6) 55%,var(--p-content-border-color, #d4deea));background:color-mix(in srgb,var(--p-blue-100, #dbeafe) 45%,var(--p-surface-card, #fff));color:color-mix(in srgb,var(--p-blue-700, #1d4ed8) 80%,var(--p-text-color, #233242))}.wuic-license-badge-trial{border-color:color-mix(in srgb,var(--p-orange-500, #f97316) 55%,var(--p-content-border-color, #d4deea));background:color-mix(in srgb,var(--p-orange-100, #ffedd5) 45%,var(--p-surface-card, #fff));color:color-mix(in srgb,var(--p-orange-700, #c2410c) 80%,var(--p-text-color, #233242))}.wuic-auth-stage-login,.wuic-auth-stage-login *{color-scheme:inherit}.wuic-auth-stage-login{--wuic-surface-ground: var(--p-surface-ground, var(--surface-ground, var(--surface-b, #f7f8fa)));--wuic-surface-card: var(--p-surface-card, var(--surface-card, var(--surface-a, #ffffff)));--wuic-surface-section: var(--p-content-background, var(--surface-section, var(--surface-c, #ffffff)));--wuic-surface-hover: var(--p-content-hover-background, var(--surface-hover, var(--surface-hover-background, #f3f4f6)));--wuic-surface-border: var(--p-content-border-color, var(--surface-border, var(--surface-d, #d7dce3)));--wuic-text: var(--p-text-color, var(--text-color, #1f2937));--wuic-text-muted: var(--p-text-muted-color, var(--text-color-secondary, #6b7280));--wuic-primary: var(--p-primary-color, var(--primary-color, #3b82f6));--wuic-primary-text: var(--p-primary-contrast-color, var(--primary-color-text, #ffffff));--wuic-danger-100: var(--p-red-100, var(--red-100, #fee2e2));--wuic-danger-500: var(--p-red-500, var(--red-500, #ef4444));--wuic-danger-700: var(--p-red-700, var(--red-700, #b91c1c));min-height:calc(100vh - 2rem);display:grid;place-items:center;padding:1rem;color:var(--wuic-text);background-color:var(--wuic-surface-ground)!important;background:radial-gradient(circle at 15% 20%,color-mix(in srgb,var(--wuic-primary) 12%,transparent),transparent 45%),radial-gradient(circle at 85% 15%,color-mix(in srgb,var(--wuic-primary) 8%,transparent),transparent 40%),linear-gradient(180deg,var(--wuic-surface-ground) 0%,var(--wuic-surface-ground) 100%)!important}:host-context(.theme-dark) .wuic-auth-stage-login{--wuic-surface-ground: var(--p-surface-900, var(--p-content-background, var(--surface-b, #0f172a)));--wuic-surface-card: var(--p-content-background, var(--p-surface-800, var(--surface-a, #111827)));--wuic-surface-section: var(--p-content-background, var(--p-surface-800, var(--surface-c, #1f2937)));--wuic-surface-hover: var(--p-content-hover-background, var(--p-surface-700, var(--surface-hover-background, #374151)));--wuic-surface-border: var(--p-content-border-color, var(--surface-d, rgba(255, 255, 255, .28)));--wuic-text: var(--p-content-color, var(--p-text-color, var(--text-color, #f3f4f6)));--wuic-text-muted: var(--p-text-muted-color, var(--text-color-secondary, #d1d5db))}:host-context(.theme-dark) .wuic-auth-bar,:host-context(.theme-dark) .wuic-auth-bar-login{background:var(--wuic-surface-card)!important;color:var(--wuic-text)!important;border-color:var(--wuic-surface-border)!important}:host-context(.theme-dark) .wuic-auth-form label,:host-context(.theme-dark) .wuic-auth-status,:host-context(.theme-dark) .wuic-auth-bar span,:host-context(.theme-dark) .wuic-auth-bar strong{color:var(--wuic-text)!important}:host-context(.theme-dark) .wuic-auth-form input{background:var(--wuic-surface-section)!important;color:var(--wuic-text)!important;border-color:var(--wuic-surface-border)!important}:host-context(.theme-dark) .wuic-auth-form input::placeholder{color:var(--wuic-text-muted)!important}:host-context(.theme-dark) .wuic-auth-btn{background:var(--wuic-surface-section)!important;color:var(--wuic-text)!important;border-color:color-mix(in srgb,var(--wuic-surface-border) 75%,white 25%)!important}:host-context(.theme-dark) .wuic-auth-btn-primary{background:var(--wuic-primary)!important;border-color:var(--wuic-primary)!important;color:var(--wuic-primary-text)!important}.wuic-auth-bar{display:grid;gap:.5rem;padding:.6rem .75rem;border:1px solid var(--wuic-surface-border);border-top:0;background:var(--wuic-surface-card)!important;color:var(--wuic-text)!important}.wuic-auth-bar-login{width:min(420px,92vw);gap:1rem;padding:1.15rem 1.2rem;border-radius:14px;border:1px solid var(--wuic-surface-border)!important;background:var(--wuic-surface-card)!important;box-shadow:0 14px 40px color-mix(in srgb,var(--wuic-text) 12%,transparent),0 0 0 1px color-mix(in srgb,var(--wuic-surface-border) 65%,transparent) inset}.wuic-auth-status{font-size:.9rem;color:var(--wuic-text)!important;margin-bottom:.1rem;line-height:1.35}.wuic-auth-form{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem}.wuic-auth-form-login{flex-direction:column;align-items:stretch;gap:.75rem}.wuic-auth-form-login form{display:grid;gap:.5rem;margin:0}.wuic-auth-form label{font-size:.85rem;color:var(--wuic-text)!important}.wuic-auth-form-login label{font-weight:600;margin-top:0}.wuic-auth-form input{min-width:220px;flex:1 1 320px;border:1px solid var(--wuic-surface-border);border-radius:6px;padding:.45rem .6rem;background:var(--wuic-surface-section)!important;color:var(--wuic-text)!important;box-shadow:0 0 0 1px color-mix(in srgb,var(--wuic-surface-border) 35%,transparent) inset}.wuic-auth-form input::placeholder{color:var(--wuic-text-muted);opacity:1}.wuic-auth-form input:focus{outline:none;border-color:var(--wuic-primary);box-shadow:0 0 0 1px color-mix(in srgb,var(--wuic-primary) 55%,transparent) inset,0 0 0 .15rem color-mix(in srgb,var(--wuic-primary) 22%,transparent)}.wuic-auth-form-login input{min-width:0;width:100%;flex:initial}.wuic-auth-btn{border:1px solid var(--wuic-surface-border);background:var(--wuic-surface-section)!important;color:var(--wuic-text)!important;border-radius:6px;padding:.45rem .75rem;cursor:pointer;transition:background-color .12s ease,border-color .12s ease,color .12s ease,box-shadow .12s ease}.wuic-auth-form-login .wuic-auth-btn{width:100%;justify-content:center}.wuic-auth-form-login>.wuic-auth-btn:not(.wuic-auth-btn-primary){margin-top:.2rem}.wuic-auth-btn:disabled{opacity:.5;cursor:default}.wuic-auth-btn:not(:disabled):hover{background:var(--wuic-surface-hover)!important;border-color:color-mix(in srgb,var(--wuic-primary) 35%,var(--wuic-surface-border))}.wuic-auth-btn:not(:disabled):focus-visible{outline:none;box-shadow:0 0 0 .15rem color-mix(in srgb,var(--wuic-primary) 20%,transparent)}.wuic-auth-btn-primary{background:var(--wuic-primary)!important;border-color:var(--wuic-primary)!important;color:var(--wuic-primary-text)!important}.wuic-auth-btn-primary:not(:disabled):hover{background:color-mix(in srgb,var(--wuic-primary) 88%,black 12%)!important;border-color:color-mix(in srgb,var(--wuic-primary) 88%,black 12%)!important;color:var(--wuic-primary-text)!important}.wuic-captcha{margin:.25rem 0}.wuic-auth-error{color:var(--wuic-danger-700);font-size:.85rem;background:color-mix(in srgb,var(--wuic-danger-100) 22%,var(--wuic-surface-card));border:1px solid color-mix(in srgb,var(--wuic-danger-500) 35%,var(--wuic-surface-border));border-radius:8px;padding:.5rem .65rem}.wuic-auth-success{color:var(--p-green-700, var(--green-700, #15803d));font-size:.85rem;background:color-mix(in srgb,var(--p-green-100, var(--green-100, #dcfce7)) 22%,var(--wuic-surface-card));border:1px solid color-mix(in srgb,var(--p-green-500, var(--green-500, #22c55e)) 35%,var(--wuic-surface-border));border-radius:8px;padding:.5rem .65rem}.wuic-auth-links{display:flex;justify-content:center;gap:1.25rem;margin-top:.1rem}.wuic-auth-link{appearance:none;background:transparent;border:none;padding:0;margin:0;font:inherit;font-size:.82rem;color:color-mix(in srgb,var(--wuic-primary) 60%,#0f172a 40%);cursor:pointer;text-decoration:underline;text-underline-offset:2px}.wuic-auth-link:hover{color:color-mix(in srgb,var(--wuic-primary) 40%,#0f172a 60%)}@media(max-width:768px){.wuic-menubar-extra{min-width:0;gap:.5rem}.wuic-menubar-user-name,.wuic-menubar-user-role{max-width:130px}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list,:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-submenu{overflow-x:hidden!important}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list{max-height:calc(100vh - 13rem);overflow-y:auto!important;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-submenu{width:100%!important;margin-left:0!important;max-height:none;overflow-y:visible!important}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-submenu>.p-menubar-item{width:100%!important;float:none!important}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-item-link{white-space:normal;word-break:break-word}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list>.p-menubar-item>.p-menubar-submenu{display:grid;grid-template-columns:minmax(0,1fr);gap:0}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list>.p-menubar-item>.p-menubar-submenu>.p-menubar-item>.p-menubar-item-link{padding-top:.5rem;padding-bottom:.5rem}}@media(max-width:768px)and (min-width:560px){:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list>.p-menubar-item>.p-menubar-submenu{grid-template-columns:repeat(2,minmax(0,1fr));column-gap:.25rem}}:host ::ng-deep .p-contextmenu{z-index:13000!important}:host ::ng-deep .p-menubar-item-link[draggable=true]{cursor:grab}:host ::ng-deep .p-menubar-item-link[draggable=true]:active{cursor:grabbing}.wuic-menu-move-actions{display:flex;gap:.5rem;justify-content:flex-end;margin-top:.75rem}\n"] }]
45248
+ args: [{ selector: 'wuic-meta-menu', imports: [MenubarModule, MegaMenuModule, ContextMenuModule, DialogModule, DataSourceComponent, RouterModule, FormsModule, AsyncPipe, TranslateModule, TooltipModule], template: "@if (authState$ | async; as auth) {\n<nav class=\"wuic-meta-menu-landmark\" aria-label=\"Menu principale\">\n @if (auth.sessionExpiring) {\n <div class=\"wuic-session-warning\">\n <span class=\"pi pi-exclamation-triangle\"></span>\n {{ 'auth.session.expiring_in' | translate }} <strong>{{ auth.sessionExpiresInSeconds }}</strong> {{ 'seconds' |\n translate }}.\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"refreshAuthSession()\">{{ 'auth.session.extend' | translate\n }}</button>\n </div>\n }\n @if (auth.initialized && !requiresAuthentication(auth)) {\n <div style=\"display:none\">\n <wuic-data-source #menuMetadataDs [hardcodedRoute]=\"metaMenuRoute\" [autoload]=\"false\"></wuic-data-source>\n </div>\n <p-menubar #menubar [model]=\"items\" [autoHide]=\"false\">\n <ng-template #item let-item>\n @if (item.route) {\n <!-- [href] con path assoluto (NO `#/` hash) cosi' Lighthouse SEO\n `crawlable-anchors` considera il link crawlable: il path\n `/<route>` e' un URL valid + condivisibile + apribile in nuova\n tab via Ctrl+Click. Il click handler `onMenuItemClick` chiama\n `event.preventDefault()` quando il button click e' \"primary\"\n (no Ctrl/middle), cosi' la SPA navigation passa per\n `router.navigateByUrl` (che con `withHashLocation()` aggiunge\n comunque l'hash internamente all'URL) e preserva pending changes\n /query params. Per Ctrl+Click / middle-click il browser segue\n l'href naturalmente \u2192 apre nuova tab con path puro (poi il\n server fa SPA fallback a index.html, e Angular legge la route\n dalla path). -->\n <a class=\"p-menubar-item-link\" [href]=\"getMenuItemHref(item)\" (click)=\"onMenuItemClick(item, $event)\"\n (contextmenu)=\"onMenuContext($event, item, auth, menuCtx)\"\n [attr.draggable]=\"canDragMenu(auth, item) ? true : null\" (dragstart)=\"onMenuDragStart($event, item, auth)\"\n (dragover)=\"onMenuDragOver($event, item, auth)\" (drop)=\"onMenuDrop($event, item, auth)\"\n (dragend)=\"onMenuDragEnd()\">\n <span [class]=\"item.icon\"></span>\n <span class=\"ml-2\">{{ item.label | translate }}</span>\n </a>\n } @else {\n <!-- Parent items (folder con submenu, NO route propria): NON sono `<a>`\n ma `<div>` semantici. Lighthouse non flagga `<div>` come uncrawlable\n (l'audit `crawlable-anchors` controlla solo `<a>`). PrimeNG si\n occupa di toggle del submenu via il (click). -->\n <div class=\"p-menubar-item-link\" (click)=\"onMenuItemClick(item, $event); onFocus($event)\"\n (contextmenu)=\"onMenuContext($event, item, auth, menuCtx)\"\n [attr.draggable]=\"canDragMenu(auth, item) ? true : null\" (dragstart)=\"onMenuDragStart($event, item, auth)\"\n (dragover)=\"onMenuDragOver($event, item, auth)\" (drop)=\"onMenuDrop($event, item, auth)\"\n (dragend)=\"onMenuDragEnd()\">\n <span [class]=\"item.icon\"></span>\n <span class=\"ml-2\">{{ item.label | translate }}</span>\n <span class=\"pi pi-fw pi-angle-down ml-2\"></span>\n </div>\n }\n </ng-template>\n\n <ng-template #end>\n @if (auth.authenticated || auth.legacyAuthenticated) {\n <div class=\"wuic-menubar-extra\">\n <span class=\"wuic-license-badge\" [class.wuic-license-badge-pro]=\"getLicenseTier() === 'professional'\"\n [class.wuic-license-badge-dev]=\"getLicenseTier() === 'developer'\"\n [class.wuic-license-badge-trial]=\"getLicenseTier() === 'trial'\" [attr.title]=\"getLicenseTooltip()\"\n role=\"status\" [attr.aria-label]=\"getLicenseTooltip()\">\n <span [class]=\"getLicenseIconClass()\" aria-hidden=\"true\"></span>\n <span class=\"wuic-license-badge-label\">{{ getLicenseBadgeLabel() }}</span>\n </span>\n\n <div class=\"wuic-menubar-user-wrap\" (click)=\"toggleUserLanguageMenu($event)\">\n <img class=\"wuic-menubar-user-flag\" [src]=\"getCurrentUserLanguageFlagSrc()\" [alt]=\"getCurrentUserLanguageId()\"\n (error)=\"onLanguageFlagLoadError($event)\" />\n <div class=\"wuic-menubar-user\">\n @if (auth.authenticated) {\n <span [pTooltip]=\"auth.name || ('user.default' | translate)\" tooltipPosition=\"bottom\"\n class=\"wuic-menubar-user-name\">{{ auth.name || ('user.default' | translate) }}</span>\n <span class=\"wuic-menubar-user-role\">\n <span [pTooltip]=\"auth.provider || ('auth.provider.google' | translate)\" tooltipPosition=\"bottom\"\n class=\"wuic-role-badge\">{{ auth.provider || ('auth.provider.google' | translate) }}</span>\n </span>\n } @else {\n <span [pTooltip]=\"auth.legacyName || ('user.default' | translate)\" tooltipPosition=\"bottom\"\n class=\"wuic-menubar-user-name\">{{ auth.legacyName || ('user.default' | translate) }}</span>\n <span class=\"wuic-menubar-user-role\">\n <span [pTooltip]=\"auth.legacyRole || ('auth.role.na' | translate)\" tooltipPosition=\"bottom\"\n class=\"wuic-role-badge\" [class.wuic-role-badge-admin]=\"isAdminRole(auth)\">{{ auth.legacyRole ||\n ('auth.role.na' | translate) }}</span>\n </span>\n }\n </div>\n @if (userLanguageSwitchEnabled) {\n <span class=\"pi pi-angle-down wuic-menubar-user-caret\" aria-hidden=\"true\"></span>\n }\n @if (userLanguageSwitchEnabled && userLanguageMenuVisible) {\n <div class=\"wuic-language-menu\" (click)=\"$event.stopPropagation()\">\n @for (lang of userLanguages; track lang.id) {\n <button type=\"button\" class=\"wuic-language-item\" [class.is-active]=\"isCurrentUserLanguage(lang.id)\"\n (click)=\"onSelectUserLanguage(lang.id, $event)\">\n <img class=\"wuic-language-item-flag\" [src]=\"getLanguageFlagSrc(lang.id)\" [alt]=\"lang.id\"\n (error)=\"onLanguageFlagLoadError($event)\" />\n <span>{{ lang.lingua || lang.id }}</span>\n <small>{{ lang.id }}</small>\n </button>\n }\n </div>\n }\n </div>\n\n <button type=\"button\" class=\"wuic-auth-btn wuic-menubar-logout\" (click)=\"logout()\" [disabled]=\"auth.loading\">\n {{ 'logout' | translate }}\n </button>\n </div>\n }\n </ng-template>\n </p-menubar>\n <p-contextMenu #menuCtx [model]=\"contextItems\" appendTo=\"body\" [autoZIndex]=\"true\"\n [baseZIndex]=\"13000\"></p-contextMenu>\n <p-dialog [(visible)]=\"moveDialogVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" [closable]=\"true\"\n [style]=\"{ width: '28rem' }\" [header]=\"'menu.move.title' | translate\" (onHide)=\"cancelPendingMove()\"\n [autoZIndex]=\"true\" [baseZIndex]=\"14000\">\n <p>{{ 'menu.move.drop_target' | translate }}: <strong>{{ pendingMove?.targetLabel || pendingMove?.targetId\n }}</strong></p>\n <p>{{ 'menu.move.choose_position' | translate }}</p>\n <div class=\"wuic-menu-move-actions\">\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"executePendingMove('before')\"\n [disabled]=\"!hasMenuAdminMethod('reorderMenu')\">{{ 'menu.move.before' | translate }}</button>\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"executePendingMove('after')\"\n [disabled]=\"!hasMenuAdminMethod('reorderMenu')\">{{ 'menu.move.after' | translate }}</button>\n <button type=\"button\" class=\"wuic-auth-btn wuic-auth-btn-primary\" (click)=\"executePendingMove('inside')\"\n [disabled]=\"!hasMenuAdminMethod('nestMenu')\">{{ 'menu.move.inside' | translate }}</button>\n </div>\n </p-dialog>\n }\n @if (auth.initialized && showAuthBar(auth)) {\n <div class=\"wuic-auth-stage\" [class.wuic-auth-stage-login]=\"requiresAuthentication(auth)\">\n @if (!(translationsLoaded$ | async)) {\n <!-- Non mostrare il form login finche' le traduzioni non sono caricate: l'utente\n vedrebbe i placeholder grezzi `auth.username` / `auth.password` invece dei\n label tradotti. Meglio uno spinner finche' la tabella traduzioni arriva\n (localStorage o backend). Tipicamente <50ms se cache, ~200-800ms se\n cold-fetch dal backend.\n Eccezione: se il circuit breaker del TranslationManager si e' aperto\n (5 fallimenti consecutivi \u2192 BE offline persistente), lo spinner\n resterebbe infinito. Mostriamo invece un banner con bottone Riprova\n cosi' l'utente puo' riarmare manualmente quando il BE torna su. -->\n @if (backendCircuitOpen$ | async) {\n <div class=\"wuic-auth-bar wuic-auth-bar-loading wuic-be-offline-banner\" [class.wuic-auth-bar-login]=\"requiresAuthentication(auth)\">\n <div class=\"wuic-be-offline-content\">\n <i class=\"pi pi-exclamation-triangle wuic-be-offline-icon\"></i>\n <div class=\"wuic-be-offline-text\">\n <strong>Backend non raggiungibile</strong>\n <span>Impossibile caricare le traduzioni dopo 5 tentativi. Verifica che il server WuicCore sia attivo.</span>\n </div>\n <button type=\"button\" class=\"wuic-be-offline-retry\" (click)=\"retryBackend()\" aria-label=\"Riprova connessione al backend\">\n <i class=\"pi pi-refresh\"></i>\n <span>Riprova</span>\n </button>\n </div>\n </div>\n } @else {\n <div class=\"wuic-auth-bar wuic-auth-bar-loading\" [class.wuic-auth-bar-login]=\"requiresAuthentication(auth)\">\n <div class=\"wuic-auth-loading\">\n <i class=\"pi pi-spinner pi-spin\"></i>\n <span>Loading\u2026</span>\n </div>\n </div>\n }\n } @else {\n <div class=\"wuic-auth-bar\" [class.wuic-auth-bar-login]=\"requiresAuthentication(auth)\">\n <div class=\"wuic-auth-status\">\n @if (auth.loading) {\n <span>{{ 'auth.status.loading' | translate }}</span>\n } @else if (auth.authenticated) {\n <span>{{ 'auth.status.active' | translate }}: <strong>{{ auth.name || ('user.default' | translate) }}</strong>\n ({{ auth.provider || ('auth.provider.google' | translate) }})</span>\n } @else if (auth.enabled) {\n <span>{{ 'auth.status.not_authenticated' | translate }} ({{ auth.provider || ('auth.provider.google' |\n translate)\n }})</span>\n } @else if (auth.legacyAuthenticated) {\n <span>{{ 'auth.status.active' | translate }}: <strong>{{ auth.legacyName || ('user.default' | translate)\n }}</strong> ({{ 'auth.provider.credentials' | translate }})</span>\n } @else {\n <span>{{ 'auth.status.login_required' | translate }} ({{ 'auth.provider.credentials' | translate }})</span>\n }\n </div>\n\n <div class=\"wuic-auth-form\" [class.wuic-auth-form-login]=\"requiresAuthentication(auth)\">\n @if (auth.enabled) {\n <label for=\"wuicAuthReturnUrl\">{{ 'auth.return_url' | translate }}</label>\n <input id=\"wuicAuthReturnUrl\" type=\"text\" [(ngModel)]=\"authReturnUrl\" (ngModelChange)=\"onAuthReturnUrlChanged()\"\n placeholder=\"/\" />\n\n <button type=\"button\" class=\"wuic-auth-btn wuic-auth-btn-primary\" (click)=\"loginWithGoogle()\"\n [disabled]=\"auth.authenticated || auth.loading\">\n {{ 'auth.login_google' | translate }}\n </button>\n\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"logout()\" [disabled]=\"!auth.authenticated || auth.loading\">\n {{ 'logout' | translate }}\n </button>\n } @else if (!auth.legacyAuthenticated) {\n\n @if (authView === 'login') {\n <form (ngSubmit)=\"loginLegacy()\" novalidate>\n <label for=\"wuicAuthUser\">{{ 'auth.username' | translate }}</label>\n <input id=\"wuicAuthUser\" type=\"text\" [(ngModel)]=\"loginUsername\" [placeholder]=\"'auth.username' | translate\"\n autocomplete=\"username\" name=\"loginUsername\" />\n\n <label for=\"wuicAuthPwd\">{{ 'auth.password' | translate }}</label>\n <input id=\"wuicAuthPwd\" type=\"password\" [(ngModel)]=\"loginPassword\"\n [placeholder]=\"'auth.password' | translate\" autocomplete=\"current-password\" name=\"loginPassword\" />\n\n @if (captchaEnabled) {\n <div id=\"wuic-captcha-container\" class=\"wuic-captcha\"></div>\n }\n\n <button type=\"submit\" class=\"wuic-auth-btn wuic-auth-btn-primary\"\n [disabled]=\"!loginUsername || !loginPassword || auth.loading || (captchaEnabled && !captchaToken)\">\n {{ 'auth.login' | translate }}\n </button>\n\n <div class=\"wuic-auth-links\">\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('forgotPassword')\">{{\n 'auth.link.forgot_password' | translate }}</button>\n @if (registrationEnabled) {\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('register')\">{{ 'auth.link.register' |\n translate }}</button>\n }\n </div>\n </form>\n } @else if (authView === 'register') {\n <form (ngSubmit)=\"submitRegistration()\" novalidate>\n <label for=\"wuic-auth-register-username\">{{ 'auth.username' | translate }}</label>\n <input id=\"wuic-auth-register-username\" type=\"text\" [(ngModel)]=\"registerUsername\"\n [placeholder]=\"'auth.username' | translate\" autocomplete=\"username\" name=\"registerUsername\" />\n\n <label for=\"wuic-auth-register-email\">{{ 'auth.email' | translate }}</label>\n <input id=\"wuic-auth-register-email\" type=\"email\" [(ngModel)]=\"registerEmail\"\n [placeholder]=\"'auth.email' | translate\" autocomplete=\"email\" name=\"registerEmail\" />\n\n <label for=\"wuic-auth-register-password\">{{ 'auth.password' | translate }}</label>\n <input id=\"wuic-auth-register-password\" type=\"password\" [(ngModel)]=\"registerPassword\"\n [placeholder]=\"'auth.password' | translate\" autocomplete=\"new-password\" name=\"registerPassword\" />\n\n @if (captchaEnabled) {\n <div id=\"wuic-captcha-container\" class=\"wuic-captcha\"></div>\n }\n\n <button type=\"submit\" class=\"wuic-auth-btn wuic-auth-btn-primary\"\n [disabled]=\"!registerUsername || !registerEmail || !registerPassword || (captchaEnabled && !captchaToken)\">\n {{ 'auth.link.register' | translate }}\n </button>\n\n @if (registerMessage) {\n <div class=\"wuic-auth-success\">{{ registerMessage }}</div>\n }\n @if (registerError) {\n <div class=\"wuic-auth-error\">{{ registerError }}</div>\n }\n\n <div class=\"wuic-auth-links\">\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('login')\">{{ 'auth.link.back_to_login'\n | translate }}</button>\n </div>\n </form>\n } @else if (authView === 'forgotPassword') {\n <label for=\"wuic-auth-forgot-email\">{{ 'auth.email' | translate }}</label>\n <input id=\"wuic-auth-forgot-email\" type=\"email\" [(ngModel)]=\"forgotEmail\"\n [placeholder]=\"'auth.forgot.email_placeholder' | translate\" autocomplete=\"email\"\n (keyup.enter)=\"submitForgotPassword()\" />\n\n <button type=\"button\" class=\"wuic-auth-btn wuic-auth-btn-primary\" (click)=\"submitForgotPassword()\"\n [disabled]=\"!forgotEmail\">\n {{ 'auth.forgot.send_reset_link' | translate }}\n </button>\n\n @if (forgotMessage) {\n <div class=\"wuic-auth-success\">{{ forgotMessage }}</div>\n }\n @if (forgotError) {\n <div class=\"wuic-auth-error\">{{ forgotError }}</div>\n }\n\n <div class=\"wuic-auth-links\">\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('login')\">{{ 'auth.link.back_to_login' |\n translate }}</button>\n </div>\n } @else if (authView === 'resetPassword') {\n <form (ngSubmit)=\"submitResetPassword()\" novalidate>\n <label for=\"wuic-auth-reset-new-password\">{{ 'auth.reset.new_password' | translate }}</label>\n <input id=\"wuic-auth-reset-new-password\" type=\"password\" [(ngModel)]=\"resetNewPassword\"\n [placeholder]=\"'auth.reset.new_password' | translate\" autocomplete=\"new-password\" name=\"resetNewPassword\" />\n\n <button type=\"submit\" class=\"wuic-auth-btn wuic-auth-btn-primary\" [disabled]=\"!resetNewPassword\">\n {{ 'auth.reset.submit' | translate }}\n </button>\n\n @if (resetMessage) {\n <div class=\"wuic-auth-success\">{{ resetMessage }}</div>\n }\n @if (resetError) {\n <div class=\"wuic-auth-error\">{{ resetError }}</div>\n }\n\n <div class=\"wuic-auth-links\">\n <button type=\"button\" class=\"wuic-auth-link\" (click)=\"switchAuthView('login')\">{{ 'auth.link.back_to_login'\n | translate }}</button>\n </div>\n </form>\n }\n\n }\n\n <button type=\"button\" class=\"wuic-auth-btn\" (click)=\"refreshAuthSession()\" [disabled]=\"auth.loading\">\n {{ 'auth.refresh' | translate }}\n </button>\n </div>\n\n @if (auth.error) {\n <div class=\"wuic-auth-error\">{{ auth.error }}</div>\n }\n </div>\n }\n </div>\n }\n</nav>\n}\n\n<!-- <p-megaMenu *ngIf=\"menuType=='megamenu'\" [model]=\"megaItems\">\n</p-megaMenu> -->\n\n<!-- <p-megaMenu *ngIf=\"menuType=='megamenu'\" [model]=\"megaItems2\"></p-megaMenu>\n</p-megaMenu> -->", styles: [".wuic-session-warning{display:flex;align-items:center;gap:.5rem;padding:.5rem 1rem;background:color-mix(in srgb,var(--p-yellow-100, var(--yellow-100, #fef9c3)) 50%,var(--p-surface-card, var(--surface-card, #fff)));border-bottom:2px solid var(--p-yellow-500, var(--yellow-500, #eab308));color:var(--p-yellow-800, var(--yellow-800, #854d0e));font-size:.85rem;font-weight:500;z-index:1000}.wuic-session-warning .pi{font-size:1rem}.wuic-session-warning .wuic-auth-btn{margin-left:auto;padding:.25rem .6rem;font-size:.8rem}.wuic-auth-stage{display:block}.wuic-auth-bar-loading{display:flex;align-items:center;justify-content:center;min-height:48px;padding:.75rem 1rem}.wuic-auth-loading{display:flex;align-items:center;gap:.5rem;color:var(--p-text-color-secondary, var(--text-color-secondary, #64748b));font-size:.9rem}.wuic-auth-loading .pi-spinner{font-size:1.1rem}:host ::ng-deep .p-menubar .p-menubar-item-link[draggable=true]{cursor:grab}:host ::ng-deep .p-menubar .p-menubar-item-link[draggable=true]:active{cursor:grabbing}:host ::ng-deep .p-menubar .p-menubar-item-link:not(.p-disabled):hover{background:var(--p-content-hover-background, var(--surface-hover, #f1f5f9));color:var(--p-primary-color, var(--p-text-color, inherit))}:host-context(.theme-dark) ::ng-deep .p-menubar .p-menubar-item-link:not(.p-disabled):hover{background:color-mix(in srgb,var(--p-content-hover-background, #1f2937) 80%,var(--p-primary-color, #3b82f6) 20%);color:var(--p-primary-color, var(--p-text-color, #f3f4f6))}.wuic-menubar-extra{display:flex;align-items:center;gap:.75rem;min-width:280px;justify-content:flex-end;padding-left:.75rem}.wuic-menubar-user-flag{width:2rem;height:2rem;border-radius:999px;display:block;border:1px solid var(--p-content-border-color, var(--surface-border, #d9e2ec));background:var(--p-content-background, var(--surface-section, #eef3f8));object-fit:cover;flex:0 0 auto}.wuic-menubar-user-wrap{position:relative;display:flex;align-items:center;gap:.55rem;cursor:pointer}.wuic-menubar-user-caret{font-size:.8rem;color:var(--p-text-muted-color, var(--text-color-secondary, #4f6479))}.wuic-menubar-user{display:grid;line-height:1.15;text-align:right;min-width:0}.wuic-language-menu{position:absolute;top:calc(100% + .45rem);right:0;min-width:170px;background:var(--p-surface-card, var(--surface-card, #fff));border:1px solid var(--p-content-border-color, var(--surface-border, #d9e2ec));border-radius:8px;box-shadow:0 10px 28px color-mix(in srgb,var(--p-text-color, #1f2f43) 16%,transparent);z-index:1100;padding:.3rem}.wuic-language-item{width:100%;border:0;background:transparent;display:flex;align-items:center;gap:.8rem;text-align:left;border-radius:6px;padding:.45rem .55rem;cursor:pointer;color:var(--p-text-color, var(--text-color, #1f2f43))}.wuic-language-item-flag{width:1.1rem;height:1.1rem;border-radius:999px;border:1px solid var(--p-content-border-color, var(--surface-border, #d9e2ec));object-fit:cover;flex:0 0 auto}.wuic-language-item span{flex:1 1 auto}.wuic-language-item small{color:var(--p-text-muted-color, var(--text-color-secondary, #5f748a))}.wuic-language-item:hover{background:var(--p-content-hover-background, var(--surface-hover, #f5f8fb))}.wuic-language-item.is-active{background:color-mix(in srgb,var(--p-primary-color, #3b82f6) 14%,transparent);color:var(--p-primary-color, #315fdd)}:host-context(.theme-dark) .wuic-language-menu{background:var(--p-content-background, var(--surface-card, #111827));border-color:var(--p-content-border-color, var(--surface-border, rgba(255, 255, 255, .2)));box-shadow:0 10px 28px color-mix(in srgb,#000 55%,transparent),0 0 0 1px color-mix(in srgb,var(--p-content-border-color, rgba(255, 255, 255, .2)) 65%,transparent) inset}:host-context(.theme-dark) .wuic-language-item{color:var(--p-text-color, var(--text-color, #f3f4f6))}:host-context(.theme-dark) .wuic-language-item small{color:var(--p-text-muted-color, var(--text-color-secondary, #b7c3d0))}:host-context(.theme-dark) .wuic-language-item:hover{background:color-mix(in srgb,var(--p-content-hover-background, #1f2937) 85%,var(--p-primary-color, #3b82f6) 15%)}:host-context(.theme-dark) .wuic-language-item.is-active{background:color-mix(in srgb,var(--p-primary-color, #3b82f6) 30%,transparent);color:var(--p-primary-contrast-color, #e7f0ff)}.wuic-menubar-user-name{font-weight:600;color:var(--p-text-color, var(--text-color, #1f2f43));white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:120px}.wuic-menubar-user-role{font-size:.75rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:220px}.wuic-role-badge{display:inline-flex;align-items:center;max-width:100%;padding:.15rem .5rem;border-radius:999px;border:1px solid var(--p-content-border-color, var(--surface-border, #d4deea));background:var(--p-content-background, var(--surface-section, #f3f7fb));color:var(--p-text-muted-color, var(--text-color-secondary, #4a6077));font-size:.72rem;font-weight:600}.wuic-role-badge-admin{border-color:color-mix(in srgb,var(--p-yellow-500, var(--yellow-500, #f59e0b)) 55%,var(--p-content-border-color, var(--surface-border, #d4deea)));background:color-mix(in srgb,var(--p-yellow-100, var(--yellow-100, #fff6df)) 30%,var(--p-surface-card, var(--surface-card, #fff)));color:color-mix(in srgb,var(--p-yellow-700, var(--yellow-700, #9a5a00)) 80%,var(--p-text-color, var(--text-color, #233242)))}.wuic-menubar-logout{white-space:nowrap;padding-inline:.8rem}.wuic-license-badge{display:inline-flex;align-items:center;gap:.35rem;padding:.25rem .6rem;border-radius:999px;border:1px solid var(--p-content-border-color, var(--surface-border, #d4deea));background:var(--p-content-background, var(--surface-section, #f3f7fb));color:var(--p-text-muted-color, var(--text-color-secondary, #4a6077));font-size:.72rem;font-weight:700;letter-spacing:.02em;cursor:default;white-space:nowrap;-webkit-user-select:none;user-select:none}.wuic-license-badge .pi{font-size:.85rem}.wuic-license-badge-label{line-height:1}.wuic-license-badge-pro{border-color:color-mix(in srgb,var(--p-emerald-500, #10b981) 55%,var(--p-content-border-color, #d4deea));background:color-mix(in srgb,var(--p-emerald-100, #d1fae5) 45%,var(--p-surface-card, #fff));color:color-mix(in srgb,var(--p-emerald-700, #047857) 80%,var(--p-text-color, #233242))}.wuic-license-badge-dev{border-color:color-mix(in srgb,var(--p-blue-500, #3b82f6) 55%,var(--p-content-border-color, #d4deea));background:color-mix(in srgb,var(--p-blue-100, #dbeafe) 45%,var(--p-surface-card, #fff));color:color-mix(in srgb,var(--p-blue-700, #1d4ed8) 80%,var(--p-text-color, #233242))}.wuic-license-badge-trial{border-color:color-mix(in srgb,var(--p-orange-500, #f97316) 55%,var(--p-content-border-color, #d4deea));background:color-mix(in srgb,var(--p-orange-100, #ffedd5) 45%,var(--p-surface-card, #fff));color:color-mix(in srgb,var(--p-orange-700, #c2410c) 80%,var(--p-text-color, #233242))}.wuic-auth-stage-login,.wuic-auth-stage-login *{color-scheme:inherit}.wuic-auth-stage-login{--wuic-surface-ground: var(--p-surface-ground, var(--surface-ground, var(--surface-b, #f7f8fa)));--wuic-surface-card: var(--p-surface-card, var(--surface-card, var(--surface-a, #ffffff)));--wuic-surface-section: var(--p-content-background, var(--surface-section, var(--surface-c, #ffffff)));--wuic-surface-hover: var(--p-content-hover-background, var(--surface-hover, var(--surface-hover-background, #f3f4f6)));--wuic-surface-border: var(--p-content-border-color, var(--surface-border, var(--surface-d, #d7dce3)));--wuic-text: var(--p-text-color, var(--text-color, #1f2937));--wuic-text-muted: var(--p-text-muted-color, var(--text-color-secondary, #6b7280));--wuic-primary: var(--p-primary-color, var(--primary-color, #3b82f6));--wuic-primary-text: var(--p-primary-contrast-color, var(--primary-color-text, #ffffff));--wuic-danger-100: var(--p-red-100, var(--red-100, #fee2e2));--wuic-danger-500: var(--p-red-500, var(--red-500, #ef4444));--wuic-danger-700: var(--p-red-700, var(--red-700, #b91c1c));min-height:calc(100vh - 2rem);display:grid;place-items:center;padding:1rem;color:var(--wuic-text);background-color:var(--wuic-surface-ground)!important;background:radial-gradient(circle at 15% 20%,color-mix(in srgb,var(--wuic-primary) 12%,transparent),transparent 45%),radial-gradient(circle at 85% 15%,color-mix(in srgb,var(--wuic-primary) 8%,transparent),transparent 40%),linear-gradient(180deg,var(--wuic-surface-ground) 0%,var(--wuic-surface-ground) 100%)!important}:host-context(.theme-dark) .wuic-auth-stage-login{--wuic-surface-ground: var(--p-surface-900, var(--p-content-background, var(--surface-b, #0f172a)));--wuic-surface-card: var(--p-content-background, var(--p-surface-800, var(--surface-a, #111827)));--wuic-surface-section: var(--p-content-background, var(--p-surface-800, var(--surface-c, #1f2937)));--wuic-surface-hover: var(--p-content-hover-background, var(--p-surface-700, var(--surface-hover-background, #374151)));--wuic-surface-border: var(--p-content-border-color, var(--surface-d, rgba(255, 255, 255, .28)));--wuic-text: var(--p-content-color, var(--p-text-color, var(--text-color, #f3f4f6)));--wuic-text-muted: var(--p-text-muted-color, var(--text-color-secondary, #d1d5db))}:host-context(.theme-dark) .wuic-auth-bar,:host-context(.theme-dark) .wuic-auth-bar-login{background:var(--wuic-surface-card)!important;color:var(--wuic-text)!important;border-color:var(--wuic-surface-border)!important}:host-context(.theme-dark) .wuic-auth-form label,:host-context(.theme-dark) .wuic-auth-status,:host-context(.theme-dark) .wuic-auth-bar span,:host-context(.theme-dark) .wuic-auth-bar strong{color:var(--wuic-text)!important}:host-context(.theme-dark) .wuic-auth-form input{background:var(--wuic-surface-section)!important;color:var(--wuic-text)!important;border-color:var(--wuic-surface-border)!important}:host-context(.theme-dark) .wuic-auth-form input::placeholder{color:var(--wuic-text-muted)!important}:host-context(.theme-dark) .wuic-auth-btn{background:var(--wuic-surface-section)!important;color:var(--wuic-text)!important;border-color:color-mix(in srgb,var(--wuic-surface-border) 75%,white 25%)!important}:host-context(.theme-dark) .wuic-auth-btn-primary{background:var(--wuic-primary)!important;border-color:var(--wuic-primary)!important;color:var(--wuic-primary-text)!important}.wuic-auth-bar{display:grid;gap:.5rem;padding:.6rem .75rem;border:1px solid var(--wuic-surface-border);border-top:0;background:var(--wuic-surface-card)!important;color:var(--wuic-text)!important}.wuic-auth-bar-login{width:min(420px,92vw);gap:1rem;padding:1.15rem 1.2rem;border-radius:14px;border:1px solid var(--wuic-surface-border)!important;background:var(--wuic-surface-card)!important;box-shadow:0 14px 40px color-mix(in srgb,var(--wuic-text) 12%,transparent),0 0 0 1px color-mix(in srgb,var(--wuic-surface-border) 65%,transparent) inset}.wuic-auth-status{font-size:.9rem;color:var(--wuic-text)!important;margin-bottom:.1rem;line-height:1.35}.wuic-auth-form{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem}.wuic-auth-form-login{flex-direction:column;align-items:stretch;gap:.75rem}.wuic-auth-form-login form{display:grid;gap:.5rem;margin:0}.wuic-auth-form label{font-size:.85rem;color:var(--wuic-text)!important}.wuic-auth-form-login label{font-weight:600;margin-top:0}.wuic-auth-form input{min-width:220px;flex:1 1 320px;border:1px solid var(--wuic-surface-border);border-radius:6px;padding:.45rem .6rem;background:var(--wuic-surface-section)!important;color:var(--wuic-text)!important;box-shadow:0 0 0 1px color-mix(in srgb,var(--wuic-surface-border) 35%,transparent) inset}.wuic-auth-form input::placeholder{color:var(--wuic-text-muted);opacity:1}.wuic-auth-form input:focus{outline:none;border-color:var(--wuic-primary);box-shadow:0 0 0 1px color-mix(in srgb,var(--wuic-primary) 55%,transparent) inset,0 0 0 .15rem color-mix(in srgb,var(--wuic-primary) 22%,transparent)}.wuic-auth-form-login input{min-width:0;width:100%;flex:initial}.wuic-auth-btn{border:1px solid var(--wuic-surface-border);background:var(--wuic-surface-section)!important;color:var(--wuic-text)!important;border-radius:6px;padding:.45rem .75rem;cursor:pointer;transition:background-color .12s ease,border-color .12s ease,color .12s ease,box-shadow .12s ease}.wuic-auth-form-login .wuic-auth-btn{width:100%;justify-content:center}.wuic-auth-form-login>.wuic-auth-btn:not(.wuic-auth-btn-primary){margin-top:.2rem}.wuic-auth-btn:disabled{opacity:.5;cursor:default}.wuic-auth-btn:not(:disabled):hover{background:var(--wuic-surface-hover)!important;border-color:color-mix(in srgb,var(--wuic-primary) 35%,var(--wuic-surface-border))}.wuic-auth-btn:not(:disabled):focus-visible{outline:none;box-shadow:0 0 0 .15rem color-mix(in srgb,var(--wuic-primary) 20%,transparent)}.wuic-auth-btn-primary{background:var(--wuic-primary)!important;border-color:var(--wuic-primary)!important;color:var(--wuic-primary-text)!important}.wuic-auth-btn-primary:not(:disabled):hover{background:color-mix(in srgb,var(--wuic-primary) 88%,black 12%)!important;border-color:color-mix(in srgb,var(--wuic-primary) 88%,black 12%)!important;color:var(--wuic-primary-text)!important}.wuic-captcha{margin:.25rem 0}.wuic-auth-error{color:var(--wuic-danger-700);font-size:.85rem;background:color-mix(in srgb,var(--wuic-danger-100) 22%,var(--wuic-surface-card));border:1px solid color-mix(in srgb,var(--wuic-danger-500) 35%,var(--wuic-surface-border));border-radius:8px;padding:.5rem .65rem}.wuic-auth-success{color:var(--p-green-700, var(--green-700, #15803d));font-size:.85rem;background:color-mix(in srgb,var(--p-green-100, var(--green-100, #dcfce7)) 22%,var(--wuic-surface-card));border:1px solid color-mix(in srgb,var(--p-green-500, var(--green-500, #22c55e)) 35%,var(--wuic-surface-border));border-radius:8px;padding:.5rem .65rem}.wuic-auth-links{display:flex;justify-content:center;gap:1.25rem;margin-top:.1rem}.wuic-auth-link{appearance:none;background:transparent;border:none;padding:0;margin:0;font:inherit;font-size:.82rem;color:color-mix(in srgb,var(--wuic-primary) 60%,#0f172a 40%);cursor:pointer;text-decoration:underline;text-underline-offset:2px}.wuic-auth-link:hover{color:color-mix(in srgb,var(--wuic-primary) 40%,#0f172a 60%)}@media(max-width:768px){.wuic-menubar-extra{min-width:0;gap:.5rem}.wuic-menubar-user-name,.wuic-menubar-user-role{max-width:130px}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list,:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-submenu{overflow-x:hidden!important}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list{max-height:calc(100vh - 13rem);overflow-y:auto!important;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-submenu{width:100%!important;margin-left:0!important;max-height:none;overflow-y:visible!important}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-submenu>.p-menubar-item{width:100%!important;float:none!important}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-item-link{white-space:normal;word-break:break-word}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list>.p-menubar-item>.p-menubar-submenu{display:grid;grid-template-columns:minmax(0,1fr);gap:0}:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list>.p-menubar-item>.p-menubar-submenu>.p-menubar-item>.p-menubar-item-link{padding-top:.5rem;padding-bottom:.5rem}}@media(max-width:768px)and (min-width:560px){:host ::ng-deep .p-menubar.p-menubar-mobile-active .p-menubar-root-list>.p-menubar-item>.p-menubar-submenu{grid-template-columns:repeat(2,minmax(0,1fr));column-gap:.25rem}}:host ::ng-deep .p-contextmenu{z-index:13000!important}:host ::ng-deep .p-menubar-item-link[draggable=true]{cursor:grab}:host ::ng-deep .p-menubar-item-link[draggable=true]:active{cursor:grabbing}.wuic-menu-move-actions{display:flex;gap:.5rem;justify-content:flex-end;margin-top:.75rem}.wuic-be-offline-banner{background:#dc354514;border-bottom:2px solid rgba(220,53,69,.6)}.wuic-be-offline-content{display:flex;align-items:center;gap:.75rem;padding:.5rem 1rem;width:100%;max-width:720px;margin:0 auto}.wuic-be-offline-icon{color:#dc3545;font-size:1.5rem;flex-shrink:0}.wuic-be-offline-text{display:flex;flex-direction:column;flex:1 1 auto;min-width:0}.wuic-be-offline-text strong{color:#842029;font-size:.95rem}.wuic-be-offline-text span{color:#58151c;font-size:.8rem;opacity:.85}.wuic-be-offline-retry{display:inline-flex;align-items:center;gap:.4rem;padding:.45rem .9rem;background:#dc3545;color:#fff;border:1px solid #b02a37;border-radius:4px;font-weight:600;font-size:.85rem;cursor:pointer;flex-shrink:0;transition:background-color .12s ease}.wuic-be-offline-retry:hover{background:#bb2d3b}.wuic-be-offline-retry:active{background:#a52834}.wuic-be-offline-retry i{font-size:.9rem}\n"] }]
45076
45249
  }], ctorParameters: () => [{ type: MetadataProviderService }, { type: AuthSessionService }, { type: TranslationManagerService }, { type: UserInfoService }, { type: WorkflowRuntimeMenuService }, { type: i1$1.HttpClient }, { type: i0.NgZone }, { type: i1$2.Router }, { type: LicenseFeatureService }, { type: i0.ElementRef }], propDecorators: { menu: [{
45077
45250
  type: ViewChild,
45078
45251
  args: ['menubar']
@@ -45177,7 +45350,7 @@ class LazyFirstRunWizardComponent {
45177
45350
  this.cdr = cdr;
45178
45351
  }
45179
45352
  async ngOnInit() {
45180
- firstRunWizardComponentPromise ??= import('./wuic-framework-lib-first-run-wizard.component-Cp8IKDb9.mjs').then((m) => m.WuicFirstRunWizardComponent);
45353
+ firstRunWizardComponentPromise ??= import('./wuic-framework-lib-first-run-wizard.component-CaGePYLi.mjs').then((m) => m.WuicFirstRunWizardComponent);
45181
45354
  const componentClass = await firstRunWizardComponentPromise;
45182
45355
  this.componentRef = this.vcr.createComponent(componentClass);
45183
45356
  // Forward output del componente reale al wrapper. Type assertion necessaria
@@ -56821,7 +56994,7 @@ class LazyCodeEditorComponent {
56821
56994
  this.cdr = cdr;
56822
56995
  }
56823
56996
  async ngOnInit() {
56824
- codeEditorComponentPromise ??= import('./wuic-framework-lib-code-editor.component-D_p7LGSQ.mjs').then((m) => m.CodeEditorComponent);
56997
+ codeEditorComponentPromise ??= import('./wuic-framework-lib-code-editor.component-4N95qt3D.mjs').then((m) => m.CodeEditorComponent);
56825
56998
  this.loadedComponent = await codeEditorComponentPromise;
56826
56999
  this.cdr.markForCheck();
56827
57000
  }
@@ -58608,7 +58781,7 @@ class DesignerRouteComponent {
58608
58781
  * Inizializza il componente preparando stato, sottoscrizioni e primi caricamenti richiesti.
58609
58782
  */
58610
58783
  async ngOnInit() {
58611
- designerComponentPromise ??= import('./wuic-framework-lib-designer.component-Dx6RraAn.mjs').then((m) => m.DesignerComponent);
58784
+ designerComponentPromise ??= import('./wuic-framework-lib-designer.component-pW1YK-oY.mjs').then((m) => m.DesignerComponent);
58612
58785
  this.loadedComponent = await designerComponentPromise;
58613
58786
  }
58614
58787
  /**
@@ -58685,7 +58858,7 @@ class LazyPivotBuilderComponent {
58685
58858
  this.cdr = cdr;
58686
58859
  }
58687
58860
  async ngOnInit() {
58688
- pivotBuilderComponentPromise ??= import('./wuic-framework-lib-pivot-builder.component-C-ceJi48.mjs').then((m) => m.PivotBuilderComponent);
58861
+ pivotBuilderComponentPromise ??= import('./wuic-framework-lib-pivot-builder.component-VIs0CIH_.mjs').then((m) => m.PivotBuilderComponent);
58689
58862
  this.loadedComponent = await pivotBuilderComponentPromise;
58690
58863
  this.cdr.markForCheck();
58691
58864
  }
@@ -58724,7 +58897,7 @@ class WorkflowDesignerRouteComponent {
58724
58897
  * Inizializza il componente preparando stato, sottoscrizioni e primi caricamenti richiesti.
58725
58898
  */
58726
58899
  async ngOnInit() {
58727
- workflowDesignerComponentPromise ??= import('./wuic-framework-lib-workflow-designer.component-8mv_Rbz9.mjs').then((m) => m.WorkflowDesignerComponent);
58900
+ workflowDesignerComponentPromise ??= import('./wuic-framework-lib-workflow-designer.component-4RC2EkGA.mjs').then((m) => m.WorkflowDesignerComponent);
58728
58901
  this.loadedComponent = await workflowDesignerComponentPromise;
58729
58902
  }
58730
58903
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: WorkflowDesignerRouteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -58781,7 +58954,7 @@ class LazyReportDesignerComponent {
58781
58954
  this.cdr = cdr;
58782
58955
  }
58783
58956
  async ngOnInit() {
58784
- reportDesignerComponentPromise ??= import('./wuic-framework-lib-report-designer.component-3-hUYft8.mjs').then((m) => m.ReportDesignerComponent);
58957
+ reportDesignerComponentPromise ??= import('./wuic-framework-lib-report-designer.component-FiAwNkPM.mjs').then((m) => m.ReportDesignerComponent);
58785
58958
  this.loadedComponent = await reportDesignerComponentPromise;
58786
58959
  this.cdr.markForCheck();
58787
58960
  }
@@ -58836,7 +59009,7 @@ class LazyReportViewerComponent {
58836
59009
  this.cdr = cdr;
58837
59010
  }
58838
59011
  async ngOnInit() {
58839
- reportViewerComponentPromise ??= import('./wuic-framework-lib-report-viewer.component-D4QD_wPl.mjs').then((m) => m.ReportViewerComponent);
59012
+ reportViewerComponentPromise ??= import('./wuic-framework-lib-report-viewer.component-DY0dGMrt.mjs').then((m) => m.ReportViewerComponent);
58840
59013
  this.loadedComponent = await reportViewerComponentPromise;
58841
59014
  this.cdr.markForCheck();
58842
59015
  }
@@ -58864,6 +59037,8 @@ class KanbanListComponent {
58864
59037
  trslSrv;
58865
59038
  dataProviderSrv;
58866
59039
  userInfo;
59040
+ cdr;
59041
+ ngZone;
58867
59042
  hardcodedRoute;
58868
59043
  parentRecord;
58869
59044
  parentMetaInfo;
@@ -58905,11 +59080,13 @@ class KanbanListComponent {
58905
59080
  datasourceReadySubscription;
58906
59081
  fetchInfoSubscription;
58907
59082
  pendingChanges = new Map();
58908
- constructor(router, trslSrv, dataProviderSrv, userInfo) {
59083
+ constructor(router, trslSrv, dataProviderSrv, userInfo, cdr, ngZone) {
58909
59084
  this.router = router;
58910
59085
  this.trslSrv = trslSrv;
58911
59086
  this.dataProviderSrv = dataProviderSrv;
58912
59087
  this.userInfo = userInfo;
59088
+ this.cdr = cdr;
59089
+ this.ngZone = ngZone;
58913
59090
  }
58914
59091
  t(key, fallback) {
58915
59092
  const translated = this.trslSrv?.instant?.(key);
@@ -59126,16 +59303,43 @@ class KanbanListComponent {
59126
59303
  // se lookup non pronta). POI aspetta async lookup + rebuildBoard di nuovo.
59127
59304
  // setTimeout per re-enter Angular zone (ensureLookup completes in microtask
59128
59305
  // outside zone in alcuni scenari Playwright headless → CD non scatta).
59129
- this.rebuildBoard();
59306
+ //
59307
+ // FIX 2026-05-22 (refresh): cdr.detectChanges() + ngZone.run() per forzare
59308
+ // CD quando la Promise risolve fuori NgZone (sintomo: tutte le card in
59309
+ // unassigned, altre colonne compaiono solo al primo evento utente).
59310
+ //
59311
+ // FIX 2026-05-22 (navigation): differenza chiave navigation vs refresh:
59312
+ // - refresh → datasource.value=null all'init → branch datasourceReady
59313
+ // waits async → subscribe FUORI da ngOnInit CD cycle → tutto OK.
59314
+ // - navigation → datasource.value gia' pronto → subscribe SINCRONO
59315
+ // dentro ngOnInit → fetchInfo$ BehaviorSubject emette sync nella
59316
+ // stessa stack frame di ngOnInit → siamo gia' dentro un CD cycle del
59317
+ // parent → cdr.detectChanges() sincrono qui o fallisce con
59318
+ // ExpressionChangedAfterCheckedError (dev) o e' silent-no-op.
59319
+ // Soluzione: differire l'intero handling al prossimo microtask via
59320
+ // `Promise.resolve().then(...)`. Cosi' ngOnInit completa, la CD cycle
59321
+ // termina, poi il nostro callback rebuilda con CD pulita.
59322
+ Promise.resolve().then(() => {
59323
+ this.ngZone.run(() => {
59324
+ this.rebuildBoard();
59325
+ this.cdr.detectChanges();
59326
+ this.onKanbanDataBound.emit({
59327
+ metaInfo: this.metaInfo,
59328
+ columns: this.columns,
59329
+ unassignedCards: this.unassignedCards
59330
+ });
59331
+ });
59332
+ });
59130
59333
  this.ensureLookupStatusColumnsLoaded().then(() => {
59131
- setTimeout(() => {
59334
+ this.ngZone.run(() => {
59132
59335
  this.rebuildBoard();
59336
+ this.cdr.detectChanges();
59133
59337
  this.onKanbanDataBound.emit({
59134
59338
  metaInfo: this.metaInfo,
59135
59339
  columns: this.columns,
59136
59340
  unassignedCards: this.unassignedCards
59137
59341
  });
59138
- }, 0);
59342
+ });
59139
59343
  });
59140
59344
  this.onKanbanDataBound.emit({
59141
59345
  metaInfo: this.metaInfo,
@@ -59720,7 +59924,15 @@ class KanbanListComponent {
59720
59924
  // (Playwright headless con dispatcher applyPatch → invalidateMetadataRuntime
59721
59925
  // → re-bind boundaryRepeater → kanban subscribe nuova chain in cui CD
59722
59926
  // detection async non si attiva senza un macrotask boundary).
59723
- setTimeout(() => this.rebuildBoard(), 0);
59927
+ //
59928
+ // FIX 2026-05-22: setTimeout(0) NON e' sufficiente se il dataProviderSrv
59929
+ // ritorna fuori NgZone (sintomo: su navigazione SPA tutte le card finiscono
59930
+ // in unassigned, e solo dopo evento utente compaiono le altre colonne).
59931
+ // Wrap esplicito in ngZone.run() + cdr.detectChanges() force.
59932
+ this.ngZone.run(() => {
59933
+ this.rebuildBoard();
59934
+ this.cdr.detectChanges();
59935
+ });
59724
59936
  }
59725
59937
  catch (err) {
59726
59938
  this.lookupDerivedStatusColumns = [];
@@ -59731,13 +59943,13 @@ class KanbanListComponent {
59731
59943
  this.lookupColumnsLoading = false;
59732
59944
  }
59733
59945
  }
59734
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: KanbanListComponent, deps: [{ token: i1$2.Router }, { token: TranslationManagerService }, { token: DataProviderService }, { token: UserInfoService }], target: i0.ɵɵFactoryTarget.Component });
59946
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: KanbanListComponent, deps: [{ token: i1$2.Router }, { token: TranslationManagerService }, { token: DataProviderService }, { token: UserInfoService }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
59735
59947
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: KanbanListComponent, isStandalone: true, selector: "wuic-kanban-list", inputs: { hardcodedRoute: "hardcodedRoute", parentRecord: "parentRecord", parentMetaInfo: "parentMetaInfo", datasource: "datasource", hardcodedDatasource: "hardcodedDatasource", hideToolbar: "hideToolbar" }, outputs: { onKanbanDataBound: "onKanbanDataBound", onKanbanCardDrop: "onKanbanCardDrop", onKanbanCardClick: "onKanbanCardClick", onKanbanBatchSave: "onKanbanBatchSave", onKanbanBatchCancel: "onKanbanBatchCancel", onKanbanConfigApplied: "onKanbanConfigApplied" }, ngImport: i0, template: "<div class=\"kanban-root\">\n @if (!hideToolbar) {\n <div class=\"kanban-toolbar\">\n @if (isAdmin) {\n <p-button [label]=\"'kanban.configure' | translate\" icon=\"pi pi-cog\" (onClick)=\"openKanbanConfig()\"></p-button>\n }\n @if (canAddCard) {\n <p-button [label]=\"'add' | translate\" icon=\"pi pi-plus\" (onClick)=\"addRecord()\"></p-button>\n }\n @if (kanbanOptions?.persistMode === 'batch') {\n <p-button [label]=\"'kanban.save_changes' | translate\" icon=\"pi pi-save\" [disabled]=\"!hasPendingChanges || savingBatch\" (onClick)=\"saveBatchChanges()\"></p-button>\n <p-button [label]=\"'kanban.discard_changes' | translate\" icon=\"pi pi-undo\" severity=\"secondary\" [disabled]=\"!hasPendingChanges || savingBatch\" (onClick)=\"cancelBatchChanges()\"></p-button>\n }\n </div>\n }\n\n @if (warningMessage) {\n <div class=\"kanban-warning\">{{ warningMessage }}</div>\n }\n\n @if (canRenderBoard) {\n <div class=\"kanban-board\" cdkDropListGroup>\n <div class=\"kanban-column kanban-column-unassigned\">\n <div class=\"kanban-column-header\">\n <span class=\"kanban-column-title\">{{ 'kanban.unassigned' | translate }}</span>\n <span class=\"kanban-column-count\">{{ unassignedCards.length }}</span>\n </div>\n\n <div class=\"kanban-column-body\" cdkDropList [id]=\"unassignedDropListId\" [cdkDropListData]=\"unassignedCards\"\n [cdkDropListConnectedTo]=\"connectedDropListIds\" (cdkDropListDropped)=\"onDrop($event, null)\">\n @for (card of unassignedCards; track trackCard($index, card)) {\n <div class=\"kanban-card\" cdkDrag (click)=\"onCardClick(card, $event)\">\n <div class=\"kanban-card-actions\" (click)=\"$event.stopPropagation()\">\n <wuic-data-action-button-lazy [data]=\"card.record\" [metaInfo]=\"metaInfo\" [datasource]=\"datasource\">\n </wuic-data-action-button-lazy>\n </div>\n <div class=\"kanban-card-title\">{{ card.title || '-' }}</div>\n @if (card.subtitle) {\n <div class=\"kanban-card-subtitle\">{{ card.subtitle }}</div>\n }\n @if (card.description) {\n <div class=\"kanban-card-description\">{{ card.description }}</div>\n }\n @if (card.assignedUserLabel) {\n <div class=\"kanban-card-subtitle\">{{ 'kanban.assignee' | translate }}: {{ card.assignedUserLabel }}</div>\n }\n </div>\n }\n </div>\n </div>\n\n @for (column of columns; track trackColumn($index, column)) {\n <div class=\"kanban-column\" [ngStyle]=\"getColumnStyle(column)\">\n <div class=\"kanban-column-header\">\n <span class=\"kanban-column-title\">{{ column.label }}</span>\n <span class=\"kanban-column-count\">{{ column.cards.length }}</span>\n </div>\n\n <div class=\"kanban-column-body\" cdkDropList [id]=\"column.id\" [cdkDropListData]=\"column.cards\"\n [cdkDropListConnectedTo]=\"connectedDropListIds\" (cdkDropListDropped)=\"onDrop($event, column)\">\n @for (card of column.cards; track trackCard($index, card)) {\n <div class=\"kanban-card\" cdkDrag (click)=\"onCardClick(card, $event)\">\n <div class=\"kanban-card-actions\" (click)=\"$event.stopPropagation()\">\n <wuic-data-action-button-lazy [data]=\"card.record\" [metaInfo]=\"metaInfo\" [datasource]=\"datasource\">\n </wuic-data-action-button-lazy>\n </div>\n <div class=\"kanban-card-title\">{{ card.title || '-' }}</div>\n @if (card.subtitle) {\n <div class=\"kanban-card-subtitle\">{{ card.subtitle }}</div>\n }\n @if (card.description) {\n <div class=\"kanban-card-description\">{{ card.description }}</div>\n }\n @if (card.assignedUserLabel) {\n <div class=\"kanban-card-subtitle\">{{ 'kanban.assignee' | translate }}: {{ card.assignedUserLabel }}</div>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n }\n</div>\n\n<wuic-archetype-configurator [visible]=\"showConfigDialog\" (visibleChange)=\"showConfigDialog = $event\" [archetype]=\"'kanban'\"\n [metaInfo]=\"metaInfo\" [value]=\"kanbanOptions\" (applyConfig)=\"applyKanbanConfig($event)\">\n</wuic-archetype-configurator>\r\n", styles: [":host{display:block;width:100%;height:100%;min-height:0;--kanban-surface: var(--p-content-background, var(--p-surface-0, #ffffff));--kanban-surface-alt: var(--p-surface-100, #f8fafc);--kanban-card-surface: var(--p-surface-0, #ffffff);--kanban-border: var(--p-content-border-color, var(--p-divider-color, #dcdcdc));--kanban-text: var(--p-text-color, #111827);--kanban-text-muted: var(--p-text-muted-color, #6b7280);--kanban-accent: var(--p-primary-color, #3b82f6);--kanban-header-text: var(--p-primary-contrast-color, #ffffff)}.kanban-root{display:flex;flex-direction:column;gap:.75rem;height:100%;min-height:0}.kanban-toolbar{display:flex;gap:.5rem;align-items:center}.kanban-warning{border:1px solid color-mix(in srgb,var(--kanban-accent) 40%,var(--kanban-border) 60%);background:color-mix(in srgb,var(--kanban-accent) 14%,var(--kanban-surface) 86%);color:var(--kanban-text);border-radius:8px;padding:.6rem .75rem}.kanban-board{display:flex;gap:.75rem;align-items:stretch;flex:1 1 auto;min-height:0;width:100%;overflow-x:auto;overflow-y:hidden;padding-bottom:.35rem}.kanban-column{flex:1 1 0;min-width:0;width:auto;max-width:none;background:var(--kanban-surface-alt);border:1px solid var(--kanban-border);border-radius:10px;display:flex;flex-direction:column;height:100%;min-height:0}.kanban-column-unassigned{border-style:dashed;border-color:color-mix(in srgb,var(--kanban-accent) 35%,var(--kanban-border) 65%)}.kanban-column-header{display:flex;align-items:center;justify-content:space-between;padding:.6rem .65rem;border-bottom:1px solid var(--kanban-border);background:var(--kanban-column-color, color-mix(in srgb, var(--kanban-accent) 16%, var(--kanban-surface) 84%));color:var(--kanban-header-text);border-top-left-radius:10px;border-top-right-radius:10px}.kanban-column-title{font-weight:700}.kanban-column-count{font-size:.82rem;border-radius:999px;padding:.12rem .45rem;background:color-mix(in srgb,var(--kanban-header-text) 22%,transparent);color:inherit}.kanban-column-body{display:flex;flex-direction:column;gap:.55rem;flex:1 1 auto;min-height:0;padding:.6rem;overflow-y:auto;overflow-x:visible}.kanban-card{border:1px solid var(--kanban-border);border-radius:8px;padding:.55rem;background:var(--kanban-card-surface);color:var(--kanban-text);box-shadow:0 1px 2px #0000000f;cursor:pointer}.kanban-card-actions{display:flex;justify-content:flex-end;margin-bottom:.35rem}.kanban-card-title{font-weight:700;margin-bottom:.2rem}.kanban-card-subtitle{font-size:.85rem;color:var(--kanban-text-muted);margin-bottom:.25rem}.kanban-card-description{font-size:.9rem;color:var(--kanban-text)}:host-context(.theme-dark){--kanban-surface: var(--p-surface-900, #0f172a);--kanban-surface-alt: var(--p-surface-800, #1e293b);--kanban-card-surface: var(--p-surface-800, #1f2937);--kanban-border: var(--p-content-border-color, #334155);--kanban-text: var(--p-text-color, #e5e7eb);--kanban-text-muted: var(--p-text-muted-color, #cbd5e1)}:host-context(.theme-dark) .kanban-card{box-shadow:0 2px 8px #00000059}.cdk-drag-preview{box-sizing:border-box;border-radius:8px;box-shadow:0 5px 12px #0003}.cdk-drag-placeholder{opacity:.3}@media(max-width:768px){.kanban-column{flex:0 0 240px;min-width:240px}.kanban-column-body{overflow-y:auto;overflow-x:hidden}.kanban-card{min-width:0;overflow:hidden}.kanban-card,.kanban-card-title,.kanban-card-subtitle,.kanban-card-description{overflow-wrap:anywhere;word-break:break-word;white-space:normal}}\n"], dependencies: [{ kind: "ngmodule", type: DragDropModule }, { kind: "directive", type: i5$4.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i5$4.CdkDropListGroup, selector: "[cdkDropListGroup]", inputs: ["cdkDropListGroupDisabled"], exportAs: ["cdkDropListGroup"] }, { kind: "directive", type: i5$4.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i4.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TranslateModule }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: LazyDataActionButtonComponent, selector: "wuic-data-action-button-lazy", inputs: ["data", "metaInfo", "datasource", "filter", "simplified", "parentField"] }, { kind: "component", type: ArchetypeConfiguratorComponent, selector: "wuic-archetype-configurator", inputs: ["visible", "archetype", "metaInfo", "value"], outputs: ["visibleChange", "applyConfig"] }, { kind: "pipe", type: i8.TranslatePipe, name: "translate" }] });
59736
59948
  }
59737
59949
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: KanbanListComponent, decorators: [{
59738
59950
  type: Component,
59739
59951
  args: [{ selector: 'wuic-kanban-list', imports: [DragDropModule, ButtonModule, TranslateModule, NgStyle, LazyDataActionButtonComponent, ArchetypeConfiguratorComponent], template: "<div class=\"kanban-root\">\n @if (!hideToolbar) {\n <div class=\"kanban-toolbar\">\n @if (isAdmin) {\n <p-button [label]=\"'kanban.configure' | translate\" icon=\"pi pi-cog\" (onClick)=\"openKanbanConfig()\"></p-button>\n }\n @if (canAddCard) {\n <p-button [label]=\"'add' | translate\" icon=\"pi pi-plus\" (onClick)=\"addRecord()\"></p-button>\n }\n @if (kanbanOptions?.persistMode === 'batch') {\n <p-button [label]=\"'kanban.save_changes' | translate\" icon=\"pi pi-save\" [disabled]=\"!hasPendingChanges || savingBatch\" (onClick)=\"saveBatchChanges()\"></p-button>\n <p-button [label]=\"'kanban.discard_changes' | translate\" icon=\"pi pi-undo\" severity=\"secondary\" [disabled]=\"!hasPendingChanges || savingBatch\" (onClick)=\"cancelBatchChanges()\"></p-button>\n }\n </div>\n }\n\n @if (warningMessage) {\n <div class=\"kanban-warning\">{{ warningMessage }}</div>\n }\n\n @if (canRenderBoard) {\n <div class=\"kanban-board\" cdkDropListGroup>\n <div class=\"kanban-column kanban-column-unassigned\">\n <div class=\"kanban-column-header\">\n <span class=\"kanban-column-title\">{{ 'kanban.unassigned' | translate }}</span>\n <span class=\"kanban-column-count\">{{ unassignedCards.length }}</span>\n </div>\n\n <div class=\"kanban-column-body\" cdkDropList [id]=\"unassignedDropListId\" [cdkDropListData]=\"unassignedCards\"\n [cdkDropListConnectedTo]=\"connectedDropListIds\" (cdkDropListDropped)=\"onDrop($event, null)\">\n @for (card of unassignedCards; track trackCard($index, card)) {\n <div class=\"kanban-card\" cdkDrag (click)=\"onCardClick(card, $event)\">\n <div class=\"kanban-card-actions\" (click)=\"$event.stopPropagation()\">\n <wuic-data-action-button-lazy [data]=\"card.record\" [metaInfo]=\"metaInfo\" [datasource]=\"datasource\">\n </wuic-data-action-button-lazy>\n </div>\n <div class=\"kanban-card-title\">{{ card.title || '-' }}</div>\n @if (card.subtitle) {\n <div class=\"kanban-card-subtitle\">{{ card.subtitle }}</div>\n }\n @if (card.description) {\n <div class=\"kanban-card-description\">{{ card.description }}</div>\n }\n @if (card.assignedUserLabel) {\n <div class=\"kanban-card-subtitle\">{{ 'kanban.assignee' | translate }}: {{ card.assignedUserLabel }}</div>\n }\n </div>\n }\n </div>\n </div>\n\n @for (column of columns; track trackColumn($index, column)) {\n <div class=\"kanban-column\" [ngStyle]=\"getColumnStyle(column)\">\n <div class=\"kanban-column-header\">\n <span class=\"kanban-column-title\">{{ column.label }}</span>\n <span class=\"kanban-column-count\">{{ column.cards.length }}</span>\n </div>\n\n <div class=\"kanban-column-body\" cdkDropList [id]=\"column.id\" [cdkDropListData]=\"column.cards\"\n [cdkDropListConnectedTo]=\"connectedDropListIds\" (cdkDropListDropped)=\"onDrop($event, column)\">\n @for (card of column.cards; track trackCard($index, card)) {\n <div class=\"kanban-card\" cdkDrag (click)=\"onCardClick(card, $event)\">\n <div class=\"kanban-card-actions\" (click)=\"$event.stopPropagation()\">\n <wuic-data-action-button-lazy [data]=\"card.record\" [metaInfo]=\"metaInfo\" [datasource]=\"datasource\">\n </wuic-data-action-button-lazy>\n </div>\n <div class=\"kanban-card-title\">{{ card.title || '-' }}</div>\n @if (card.subtitle) {\n <div class=\"kanban-card-subtitle\">{{ card.subtitle }}</div>\n }\n @if (card.description) {\n <div class=\"kanban-card-description\">{{ card.description }}</div>\n }\n @if (card.assignedUserLabel) {\n <div class=\"kanban-card-subtitle\">{{ 'kanban.assignee' | translate }}: {{ card.assignedUserLabel }}</div>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n }\n</div>\n\n<wuic-archetype-configurator [visible]=\"showConfigDialog\" (visibleChange)=\"showConfigDialog = $event\" [archetype]=\"'kanban'\"\n [metaInfo]=\"metaInfo\" [value]=\"kanbanOptions\" (applyConfig)=\"applyKanbanConfig($event)\">\n</wuic-archetype-configurator>\r\n", styles: [":host{display:block;width:100%;height:100%;min-height:0;--kanban-surface: var(--p-content-background, var(--p-surface-0, #ffffff));--kanban-surface-alt: var(--p-surface-100, #f8fafc);--kanban-card-surface: var(--p-surface-0, #ffffff);--kanban-border: var(--p-content-border-color, var(--p-divider-color, #dcdcdc));--kanban-text: var(--p-text-color, #111827);--kanban-text-muted: var(--p-text-muted-color, #6b7280);--kanban-accent: var(--p-primary-color, #3b82f6);--kanban-header-text: var(--p-primary-contrast-color, #ffffff)}.kanban-root{display:flex;flex-direction:column;gap:.75rem;height:100%;min-height:0}.kanban-toolbar{display:flex;gap:.5rem;align-items:center}.kanban-warning{border:1px solid color-mix(in srgb,var(--kanban-accent) 40%,var(--kanban-border) 60%);background:color-mix(in srgb,var(--kanban-accent) 14%,var(--kanban-surface) 86%);color:var(--kanban-text);border-radius:8px;padding:.6rem .75rem}.kanban-board{display:flex;gap:.75rem;align-items:stretch;flex:1 1 auto;min-height:0;width:100%;overflow-x:auto;overflow-y:hidden;padding-bottom:.35rem}.kanban-column{flex:1 1 0;min-width:0;width:auto;max-width:none;background:var(--kanban-surface-alt);border:1px solid var(--kanban-border);border-radius:10px;display:flex;flex-direction:column;height:100%;min-height:0}.kanban-column-unassigned{border-style:dashed;border-color:color-mix(in srgb,var(--kanban-accent) 35%,var(--kanban-border) 65%)}.kanban-column-header{display:flex;align-items:center;justify-content:space-between;padding:.6rem .65rem;border-bottom:1px solid var(--kanban-border);background:var(--kanban-column-color, color-mix(in srgb, var(--kanban-accent) 16%, var(--kanban-surface) 84%));color:var(--kanban-header-text);border-top-left-radius:10px;border-top-right-radius:10px}.kanban-column-title{font-weight:700}.kanban-column-count{font-size:.82rem;border-radius:999px;padding:.12rem .45rem;background:color-mix(in srgb,var(--kanban-header-text) 22%,transparent);color:inherit}.kanban-column-body{display:flex;flex-direction:column;gap:.55rem;flex:1 1 auto;min-height:0;padding:.6rem;overflow-y:auto;overflow-x:visible}.kanban-card{border:1px solid var(--kanban-border);border-radius:8px;padding:.55rem;background:var(--kanban-card-surface);color:var(--kanban-text);box-shadow:0 1px 2px #0000000f;cursor:pointer}.kanban-card-actions{display:flex;justify-content:flex-end;margin-bottom:.35rem}.kanban-card-title{font-weight:700;margin-bottom:.2rem}.kanban-card-subtitle{font-size:.85rem;color:var(--kanban-text-muted);margin-bottom:.25rem}.kanban-card-description{font-size:.9rem;color:var(--kanban-text)}:host-context(.theme-dark){--kanban-surface: var(--p-surface-900, #0f172a);--kanban-surface-alt: var(--p-surface-800, #1e293b);--kanban-card-surface: var(--p-surface-800, #1f2937);--kanban-border: var(--p-content-border-color, #334155);--kanban-text: var(--p-text-color, #e5e7eb);--kanban-text-muted: var(--p-text-muted-color, #cbd5e1)}:host-context(.theme-dark) .kanban-card{box-shadow:0 2px 8px #00000059}.cdk-drag-preview{box-sizing:border-box;border-radius:8px;box-shadow:0 5px 12px #0003}.cdk-drag-placeholder{opacity:.3}@media(max-width:768px){.kanban-column{flex:0 0 240px;min-width:240px}.kanban-column-body{overflow-y:auto;overflow-x:hidden}.kanban-card{min-width:0;overflow:hidden}.kanban-card,.kanban-card-title,.kanban-card-subtitle,.kanban-card-description{overflow-wrap:anywhere;word-break:break-word;white-space:normal}}\n"] }]
59740
- }], ctorParameters: () => [{ type: i1$2.Router }, { type: TranslationManagerService }, { type: DataProviderService }, { type: UserInfoService }], propDecorators: { hardcodedRoute: [{
59952
+ }], ctorParameters: () => [{ type: i1$2.Router }, { type: TranslationManagerService }, { type: DataProviderService }, { type: UserInfoService }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }], propDecorators: { hardcodedRoute: [{
59741
59953
  type: Input
59742
59954
  }], parentRecord: [{
59743
59955
  type: Input
@@ -61808,7 +62020,7 @@ const designerPendingChangesGuard = async (component) => {
61808
62020
  const routes = [
61809
62021
  {
61810
62022
  path: 'framework-docs',
61811
- loadComponent: () => import('./wuic-framework-lib-framework-docs.component-CxckBsIX.mjs').then((m) => m.FrameworkDocsComponent),
62023
+ loadComponent: () => import('./wuic-framework-lib-framework-docs.component-m0IlS0Sl.mjs').then((m) => m.FrameworkDocsComponent),
61812
62024
  canMatch: [menuRouteAccessCanMatchGuard],
61813
62025
  canActivate: [menuRouteAccessCanActivateGuard],
61814
62026
  data: {
@@ -61817,7 +62029,7 @@ const routes = [
61817
62029
  },
61818
62030
  {
61819
62031
  path: 'framework-docs/:slug',
61820
- loadComponent: () => import('./wuic-framework-lib-framework-docs.component-CxckBsIX.mjs').then((m) => m.FrameworkDocsComponent),
62032
+ loadComponent: () => import('./wuic-framework-lib-framework-docs.component-m0IlS0Sl.mjs').then((m) => m.FrameworkDocsComponent),
61821
62033
  canMatch: [menuRouteAccessCanMatchGuard],
61822
62034
  canActivate: [menuRouteAccessCanActivateGuard],
61823
62035
  data: {
@@ -61835,7 +62047,7 @@ const routes = [
61835
62047
  },
61836
62048
  {
61837
62049
  path: ':route/report-designer',
61838
- loadComponent: () => import('./wuic-framework-lib-report-designer.component-3-hUYft8.mjs').then((m) => m.ReportDesignerComponent),
62050
+ loadComponent: () => import('./wuic-framework-lib-report-designer.component-FiAwNkPM.mjs').then((m) => m.ReportDesignerComponent),
61839
62051
  canMatch: [menuRouteAccessCanMatchGuard, featureRouteCanMatchGuard],
61840
62052
  canActivate: [menuRouteAccessCanActivateGuard, featureRouteCanActivateGuard],
61841
62053
  data: {
@@ -61845,7 +62057,7 @@ const routes = [
61845
62057
  },
61846
62058
  {
61847
62059
  path: ':route/report-viewer',
61848
- loadComponent: () => import('./wuic-framework-lib-report-viewer.component-D4QD_wPl.mjs').then((m) => m.ReportViewerComponent),
62060
+ loadComponent: () => import('./wuic-framework-lib-report-viewer.component-DY0dGMrt.mjs').then((m) => m.ReportViewerComponent),
61849
62061
  canMatch: [menuRouteAccessCanMatchGuard],
61850
62062
  canActivate: [menuRouteAccessCanActivateGuard],
61851
62063
  data: {
@@ -61865,7 +62077,7 @@ const routes = [
61865
62077
  },
61866
62078
  {
61867
62079
  path: ':route/pivot-builder',
61868
- loadComponent: () => import('./wuic-framework-lib-pivot-builder.component-C-ceJi48.mjs').then((m) => m.PivotBuilderComponent),
62080
+ loadComponent: () => import('./wuic-framework-lib-pivot-builder.component-VIs0CIH_.mjs').then((m) => m.PivotBuilderComponent),
61869
62081
  canMatch: [menuRouteAccessCanMatchGuard, featureRouteCanMatchGuard],
61870
62082
  canActivate: [menuRouteAccessCanActivateGuard, featureRouteCanActivateGuard],
61871
62083
  data: {
@@ -61875,7 +62087,7 @@ const routes = [
61875
62087
  },
61876
62088
  {
61877
62089
  path: 'pivot-builder',
61878
- loadComponent: () => import('./wuic-framework-lib-pivot-builder.component-C-ceJi48.mjs').then((m) => m.PivotBuilderComponent),
62090
+ loadComponent: () => import('./wuic-framework-lib-pivot-builder.component-VIs0CIH_.mjs').then((m) => m.PivotBuilderComponent),
61879
62091
  canMatch: [menuRouteAccessCanMatchGuard, featureRouteCanMatchGuard],
61880
62092
  canActivate: [menuRouteAccessCanActivateGuard, featureRouteCanActivateGuard],
61881
62093
  data: {
@@ -62134,4 +62346,4 @@ function getThemePrimaryHex(value) {
62134
62346
  */
62135
62347
 
62136
62348
  export { KanbanListComponent as $, ArchetypeConfiguratorComponent as A, BooleanEditorComponent as B, CallbackPipe as C, DynamicGenericTemplateComponent as D, CrashReporterService as E, FilterBarComponent as F, GlobalHandler as G, CustomAttributesService as H, ImportExportButtonComponent as I, CustomException as J, DataActionButtonComponent as K, LazyParametricDialogComponent as L, MetadataProviderService as M, DataProviderOdataService as N, DateEditorComponent as O, PagerComponent as P, DesignerRouteComponent as Q, DictionaryEditorComponent as R, DynamicRowTemplateComponent as S, TranslationManagerService as T, UserInfoService as U, FormatGridViewValuePipe as V, WtoolboxService as W, GetSrcUploadPreviewPipe as X, HtmlEditorComponent as Y, ImageWrapperComponent as Z, IsSelectedRowPipe as _, DynamicCompilerService as a, loadLookupEditorComponent as a$, KanbanOptions as a0, KanbanStatusColumnItem as a1, LazyChartListComponent as a2, LazyCodeEditorComponent as a3, LazyDataActionButtonComponent as a4, LazyDataSourceComponent as a5, LazyFieldEditorComponent as a6, LazyFirstRunWizardComponent as a7, LazyImageWrapperComponent as a8, LazyKanbanListComponent as a9, VisibleFieldListPipe as aA, WidgetDefinition as aB, WorkflowDesignerRouteComponent as aC, WorkflowRunnerComponent as aD, WuicAziendaSwitcherComponent as aE, WuicErrorDialogComponent as aF, WuicErrorInterceptor as aG, WuicFeatureDirective as aH, WuicFrozenColumnDirective as aI, WuicRagChatbotComponent as aJ, WuicRagChatbotFabComponent as aK, WuicRagService as aL, WuicRowTogglerDirective as aM, _PRIMENG_TABLE_DIRECTIVES_NAMES as aN, authExpiredInterceptor as aO, boundedRepeaterPendingChangesGuard as aP, featureRouteCanActivateGuard as aQ, featureRouteCanMatchGuard as aR, getThemePrimaryHex as aS, inferTemplateFieldFromBaseClass as aT, loadBooleanEditorComponent as aU, loadButtonEditorComponent as aV, loadCodeAreaEditorComponent as aW, loadColorEditorComponent as aX, loadDateEditorComponent as aY, loadDictionaryEditorComponent as aZ, loadHtmlEditorComponent as a_, LazyMetaMenuComponent as aa, LazyPivotBuilderComponent as ab, LazyReportDesignerComponent as ac, LazyReportViewerComponent as ad, LazySchedulerListComponent as ae, LazySpreadsheetListSfComponent as af, ListGridComponent as ag, LookupEditorComponent as ah, MapListComponent as ai, MetaMenuComponent as aj, MetadatiTabella as ak, NotificationBellComponent as al, NotificationRealtimeService as am, NumberEditorComponent as an, PRIMARY_PALETTES as ao, PropertyArrayEditorComponent as ap, PropertyObjectEditorComponent as aq, THEME_OPTIONS as ar, TextAreaEditorComponent as as, TextEditorComponent as at, TreeListComponent as au, TreeViewSelectorComponent as av, UploadDialogComponent as aw, UploadEditorComponent as ax, ValidationRule as ay, ValueChangedPayload as az, DataSourceComponent as b, loadNumberEditorComponent as b0, loadPropertyArrayEditorComponent as b1, loadPropertyObjectEditorComponent as b2, loadTextAreaEditorComponent as b3, loadTextEditorComponent as b4, loadTreeViewSelectorComponent as b5, loadUploadEditorComponent as b6, menuRouteAccessCanActivateGuard as b7, menuRouteAccessCanMatchGuard as b8, routes as b9, wuicErrorInterceptor as ba, DataRepeaterComponent as c, ParametricDialogComponent as d, MetadatiColonna as e, DataProviderService as f, MetadataEditorService as g, WorkflowRuntimeMetadataService as h, FieldEditorComponent as i, MetadataEditorComponent as j, WuicClientException as k, WuicErrorCodes as l, MetaInfo as m, AuthSessionService as n, FilterInfo as o, LicenseFeatureService as p, FilterItem as q, getThemeOptions as r, AUTH_REDIRECT_REASON_KEY as s, AppSettingsEditorComponent as t, BoundedRepeaterComponent as u, ButtonEditorComponent as v, CallbackPipe2 as w, CarouselListComponent as x, CodeAreaEditorComponent as y, ColorEditorComponent as z };
62137
- //# sourceMappingURL=wuic-framework-lib-wuic-framework-lib-DJZAYFw8.mjs.map
62349
+ //# sourceMappingURL=wuic-framework-lib-wuic-framework-lib-BSyGhpM1.mjs.map