@unopsitg/ux 21.0.2 → 21.0.19

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.
@@ -3,23 +3,23 @@ import SoftBase from '@primeuix/themes/aura';
3
3
  import CrispBase from '@primeuix/themes/lara';
4
4
  import ContrastBase from '@primeuix/themes/nora';
5
5
  import * as i0 from '@angular/core';
6
- import { InjectionToken, signal, inject, computed, effect, Injectable, Component, PLATFORM_ID, model, booleanAttribute, Input, input, HostListener, ViewChild, ElementRef, ChangeDetectionStrategy } from '@angular/core';
6
+ import { InjectionToken, signal, inject, computed, effect, Injectable, Component, PLATFORM_ID, model, booleanAttribute, Input, input, ChangeDetectionStrategy, HostListener, ViewChild, ElementRef, output, DestroyRef, Directive, ContentChildren } from '@angular/core';
7
7
  import * as i1 from '@angular/router';
8
8
  import { Router, NavigationEnd, RouterModule } from '@angular/router';
9
9
  import * as i1$1 from '@angular/common';
10
- import { CommonModule, isPlatformBrowser } from '@angular/common';
10
+ import { CommonModule, isPlatformBrowser, NgTemplateOutlet, NgClass } from '@angular/common';
11
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
11
12
  import { BehaviorSubject, filter, Subject, takeUntil } from 'rxjs';
12
- import * as i5 from '@angular/forms';
13
+ import * as i1$2 from '@angular/forms';
13
14
  import { FormsModule } from '@angular/forms';
14
15
  import { PrimeNG } from 'primeng/config';
15
16
  import * as i4 from 'primeng/drawer';
16
17
  import { DrawerModule } from 'primeng/drawer';
17
- import * as i5$1 from 'primeng/radiobutton';
18
+ import * as i5 from 'primeng/radiobutton';
18
19
  import { RadioButtonModule } from 'primeng/radiobutton';
19
20
  import * as i3 from 'primeng/selectbutton';
20
21
  import { SelectButtonModule } from 'primeng/selectbutton';
21
- import { ToggleSwitchModule } from 'primeng/toggleswitch';
22
- import * as i6 from 'primeng/button';
22
+ import * as i2$1 from 'primeng/button';
23
23
  import { ButtonModule } from 'primeng/button';
24
24
  import * as i2 from 'primeng/divider';
25
25
  import { DividerModule } from 'primeng/divider';
@@ -29,7 +29,7 @@ import * as i3$1 from 'primeng/select';
29
29
  import { SelectModule } from 'primeng/select';
30
30
  import * as i3$2 from 'primeng/autofocus';
31
31
  import { AutoFocusModule } from 'primeng/autofocus';
32
- import * as i1$2 from 'primeng/dialog';
32
+ import * as i1$3 from 'primeng/dialog';
33
33
  import { DialogModule } from 'primeng/dialog';
34
34
  import * as i4$2 from 'primeng/ripple';
35
35
  import { RippleModule } from 'primeng/ripple';
@@ -39,14 +39,34 @@ import * as i10 from 'primeng/avatar';
39
39
  import { AvatarModule } from 'primeng/avatar';
40
40
  import * as i8 from 'primeng/badge';
41
41
  import { BadgeModule } from 'primeng/badge';
42
- import * as i5$2 from 'primeng/iconfield';
42
+ import * as i5$1 from 'primeng/iconfield';
43
43
  import { IconFieldModule } from 'primeng/iconfield';
44
- import * as i6$1 from 'primeng/inputicon';
44
+ import * as i6 from 'primeng/inputicon';
45
45
  import { InputIconModule } from 'primeng/inputicon';
46
46
  import * as i9 from 'primeng/overlaybadge';
47
47
  import { OverlayBadgeModule } from 'primeng/overlaybadge';
48
48
  import * as i3$4 from 'primeng/styleclass';
49
49
  import { StyleClassModule } from 'primeng/styleclass';
50
+ import * as i2$2 from 'primeng/paginator';
51
+ import { PaginatorModule } from 'primeng/paginator';
52
+ import * as i4$3 from 'primeng/tabs';
53
+ import { TabsModule } from 'primeng/tabs';
54
+ import * as i3$5 from 'primeng/fileupload';
55
+ import { FileUploadModule } from 'primeng/fileupload';
56
+ import * as i7 from 'primeng/menu';
57
+ import { MenuModule } from 'primeng/menu';
58
+ import * as i10$1 from 'primeng/panel';
59
+ import { PanelModule } from 'primeng/panel';
60
+ import * as i8$1 from 'primeng/table';
61
+ import { TableModule } from 'primeng/table';
62
+ import * as i9$1 from 'primeng/tag';
63
+ import { TagModule } from 'primeng/tag';
64
+ import * as i8$2 from 'primeng/autocomplete';
65
+ import { AutoCompleteModule } from 'primeng/autocomplete';
66
+ import * as i7$1 from 'primeng/datepicker';
67
+ import { DatePickerModule } from 'primeng/datepicker';
68
+ import * as i5$2 from 'primeng/textarea';
69
+ import { TextareaModule } from 'primeng/textarea';
50
70
 
51
71
  /**
52
72
  * Brand theme presets built on top of PrimeUIX base presets.
@@ -57,8 +77,8 @@ import { StyleClassModule } from 'primeng/styleclass';
57
77
  * Contrast <- @primeuix/themes/nora
58
78
  */
59
79
  const brandPrimitives = {
60
- deepsea: { 50: '#F1F5F9', 100: '#E2E8F0', 200: '#CBD5E1', 300: '#94A3B8', 400: '#64748B', 500: '#475569', 600: '#334155', 700: '#1E293B', 800: '#0F172A', 900: '#0A101D', 950: '#03080c' },
61
- gray: { 50: '#f0f3f4', 100: '#ECF0F5', 200: '#d8dadf', 300: '#b9bac1', 400: '#858c99', 500: '#808393', 600: '#808284', 700: '#6a6b6d', 800: '#535455', 900: '#3c3d3e', 950: '#262627' },
80
+ deepsea: { 50: '#c3c7cb', 100: '#9ea5ac', 200: '#7a838d', 300: '#56626d', 400: '#31404e', 500: '#0d1e2f', 600: '#0b1a28', 700: '#091521', 800: '#07111a', 900: '#050c13', 950: '#03080c' },
81
+ gray: { 50: '#f8fafc', 100: '#f1f5f9', 200: '#e2e8f0', 300: '#cbd5e1', 400: '#94a3b8', 500: '#64748b', 600: '#475569', 700: '#334155', 800: '#1e293b', 900: '#0f172a', 950: '#020617' },
62
82
  red: { 50: '#f6cac6', 100: '#f0a9a4', 200: '#eb8982', 300: '#e56960', 400: '#e0493e', 500: '#da291c', 600: '#b92318', 700: '#991d14', 800: '#78170f', 900: '#57100b', 950: '#370a07' },
63
83
  orange: { 50: '#f9d6c3', 100: '#f6be9f', 200: '#f2a57a', 300: '#ef8d56', 400: '#eb7432', 500: '#e85c0e', 600: '#c54e0c', 700: '#a2400a', 800: '#803308', 900: '#5d2506', 950: '#3a1704' },
64
84
  yellow: { 50: '#fff0c5', 100: '#ffe7a1', 200: '#ffdd7e', 300: '#ffd45b', 400: '#ffcb38', 500: '#ffc215', 600: '#d9a512', 700: '#b3880f', 800: '#8c6b0c', 900: '#664e08', 950: '#403105' },
@@ -72,7 +92,8 @@ const brandPrimitives = {
72
92
  blue: { 50: '#DEEFFF', 100: '#C9E8FF', 150: '#99D3ED', 200: '#73c3e6', 300: '#4db3df', 400: '#26a2d8', 500: '#0092d1', 600: '#007cb2', 700: '#006692', 800: '#005073', 900: '#003a54', 950: '#002534' },
73
93
  darkblue: { 50: '#D0EEFF', 100: '#B7E2F9', 200: '#73abc7', 300: '#4d94b8', 400: '#267da9', 500: '#00669a', 600: '#005783', 700: '#00476c', 800: '#003855', 900: '#00293e', 950: '#001a27' },
74
94
  midnight: { 50: '#bfd2dd', 100: '#99b6c8', 200: '#739bb4', 300: '#4d809f', 400: '#26648b', 500: '#004976', 600: '#003e64', 700: '#003353', 800: '#002841', 850: '#001E31', 900: '#001d2f', 950: '#00121e' },
75
- cherry: { 50: '#e6c7d9', 100: '#d6a5c2', 200: '#c783ab', 300: '#b86294', 400: '#a8407d', 500: '#991e66', 600: '#821a57', 700: '#6b1547', 800: '#541138', 900: '#3d0c29', 950: '#26081a' }
95
+ cherry: { 50: '#e6c7d9', 100: '#d6a5c2', 200: '#c783ab', 300: '#b86294', 400: '#a8407d', 500: '#991e66', 600: '#821a57', 700: '#6b1547', 800: '#541138', 900: '#3d0c29', 950: '#26081a' },
96
+ ai: { 50: '#f0edff', 100: '#e4deff', 200: '#cdc2ff', 300: '#b0a0ff', 400: '#9378ff', 500: '#7c5cfc', 600: '#6a44f0', 700: '#5835d4', 800: '#482dac', 900: '#3b278a', 950: '#24185e' }
76
97
  };
77
98
  const brandOverrides = {
78
99
  primitive: {
@@ -111,17 +132,17 @@ const brandOverrides = {
111
132
  light: {
112
133
  surface: {
113
134
  0: '#ffffff',
114
- 50: '{deepsea.50}',
115
- 100: '{deepsea.100}',
116
- 200: '{deepsea.200}',
117
- 300: '{deepsea.300}',
118
- 400: '{deepsea.400}',
119
- 500: '{deepsea.500}',
120
- 600: '{deepsea.600}',
121
- 700: '{deepsea.700}',
122
- 800: '{deepsea.800}',
123
- 900: '{deepsea.900}',
124
- 950: '{deepsea.950}'
135
+ 50: '{gray.50}',
136
+ 100: '{gray.100}',
137
+ 200: '{gray.200}',
138
+ 300: '{gray.300}',
139
+ 400: '{gray.400}',
140
+ 500: '{gray.500}',
141
+ 600: '{gray.600}',
142
+ 700: '{gray.700}',
143
+ 800: '{gray.800}',
144
+ 900: '{gray.900}',
145
+ 950: '{gray.950}'
125
146
  }
126
147
  },
127
148
  dark: {
@@ -130,12 +151,12 @@ const brandOverrides = {
130
151
  50: '{darkblue.50}',
131
152
  100: '{darkblue.100}',
132
153
  200: '{darkblue.200}',
133
- 300: '{darkblue.200}',
134
- 400: '{darkblue.300}',
135
- 500: '{darkblue.400}',
136
- 600: '{darkblue.500}',
137
- 700: '{darkblue.600}',
138
- 800: '{darkblue.700}',
154
+ 300: '{darkblue.300}',
155
+ 400: '{blue.400}',
156
+ 500: '{blue.500}',
157
+ 600: '{darkblue.600}',
158
+ 700: '{darkblue.700}',
159
+ 800: '{darkblue.800}',
139
160
  900: '{darkblue.900}',
140
161
  950: '{darkblue.950}'
141
162
  }
@@ -143,6 +164,12 @@ const brandOverrides = {
143
164
  }
144
165
  },
145
166
  components: {
167
+ accordion: {
168
+ root: { transitionDuration: '{transition.duration}' },
169
+ panel: { borderWidth: '0' },
170
+ header: { background: 'transparent', padding: '0 0 0 0' },
171
+ content: { background: 'transparent' }
172
+ },
146
173
  button: {
147
174
  colorScheme: {
148
175
  light: {
@@ -171,11 +198,106 @@ const brandOverrides = {
171
198
  }
172
199
  }
173
200
  },
201
+ // Tab active/hover backgrounds are set in _tabs.scss via sidebar
202
+ // `--d-menuitem-*` tokens (not expressible in the preset). Only structural
203
+ // properties that _tabs.scss does NOT override belong here.
204
+ tabs: {
205
+ tablist: {
206
+ background: 'transparent',
207
+ borderWidth: '0 0 1px 0'
208
+ },
209
+ tab: {
210
+ background: 'transparent',
211
+ borderColor: 'transparent',
212
+ activeBorderColor: 'transparent',
213
+ borderWidth: '0',
214
+ padding: '0.5rem 1rem',
215
+ margin: '0 0 0.5rem 0'
216
+ },
217
+ tabpanel: {
218
+ background: 'transparent',
219
+ padding: '0'
220
+ }
221
+ },
222
+ dataview: {
223
+ header: {
224
+ background: 'transparent',
225
+ borderWidth: '0',
226
+ padding: '0'
227
+ },
228
+ content: {
229
+ background: 'transparent',
230
+ padding: '0'
231
+ }
232
+ },
174
233
  paginator: {
175
234
  root: {
176
235
  background: 'transparent'
177
236
  }
178
237
  },
238
+ fileupload: {
239
+ root: {
240
+ background: 'transparent'
241
+ }
242
+ },
243
+ panel: {
244
+ root: {
245
+ background: 'transparent',
246
+ borderColor: 'transparent',
247
+ borderRadius: '0'
248
+ },
249
+ header: {
250
+ background: 'transparent',
251
+ padding: '0',
252
+ borderWidth: '0'
253
+ },
254
+ toggleableHeader: {
255
+ padding: '0'
256
+ },
257
+ title: {
258
+ fontWeight: '700'
259
+ },
260
+ content: {
261
+ padding: '0'
262
+ }
263
+ },
264
+ card: {
265
+ root: {
266
+ shadow: 'none',
267
+ borderRadius: '{border.radius.xl}'
268
+ },
269
+ body: {
270
+ padding: '1.25rem',
271
+ gap: '1rem'
272
+ }
273
+ },
274
+ avatar: {
275
+ root: {
276
+ borderRadius: '9999px',
277
+ fontSize: '0.875rem'
278
+ }
279
+ },
280
+ drawer: {
281
+ content: { padding: '1.25rem' }
282
+ },
283
+ inputtext: {
284
+ root: {
285
+ borderRadius: '{border.radius.xl}',
286
+ paddingY: '0.5rem'
287
+ }
288
+ },
289
+ divider: {
290
+ horizontal: {
291
+ margin: '0',
292
+ padding: '0',
293
+ content: { padding: '0 0.5rem' }
294
+ },
295
+ vertical: {
296
+ margin: '0',
297
+ padding: '0',
298
+ content: { padding: '0.5rem 0' }
299
+ }
300
+ },
179
301
  tag: {
180
302
  root: {
181
303
  padding: '0.25rem 0.5rem',
@@ -185,22 +307,47 @@ const brandOverrides = {
185
307
  light: {
186
308
  secondary: { color: '{surface.800}' },
187
309
  success: { color: '{green.800}' },
188
- active: { color: '{green.800}' },
189
- inactive: { color: '{gray.800}' },
190
310
  info: { color: '{blue.800}' },
191
311
  warn: { color: '{orange.800}' },
192
- error: { color: '{red.800}' }
312
+ danger: { color: '{red.800}' }
193
313
  },
194
314
  dark: {
195
315
  secondary: { color: '{surface.100}' },
196
316
  success: { color: '{green.100}' },
197
- active: { color: '{green.100}' },
198
317
  info: { color: '{blue.100}' },
199
- inactive: { color: '{gray.100}' },
200
318
  warn: { color: '{orange.100}' },
201
- error: { color: '{red.100}' }
319
+ danger: { color: '{red.100}' }
202
320
  }
203
321
  }
322
+ },
323
+ toolbar: {
324
+ root: {
325
+ background: 'transparent',
326
+ borderColor: 'transparent',
327
+ borderRadius: '0',
328
+ padding: '0',
329
+ gap: '0.5rem'
330
+ }
331
+ },
332
+ progressbar: {
333
+ root: {
334
+ borderRadius: '{border.radius.lg}',
335
+ height: '0.75rem'
336
+ }
337
+ },
338
+ message: {
339
+ root: {
340
+ borderRadius: '{border.radius.lg}',
341
+ borderWidth: '0 0 0 4px'
342
+ },
343
+ content: {
344
+ padding: '0.75rem 1rem',
345
+ gap: '0.5rem'
346
+ },
347
+ text: {
348
+ fontSize: '0.875rem',
349
+ fontWeight: '500'
350
+ }
204
351
  }
205
352
  }
206
353
  };
@@ -390,6 +537,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
390
537
  }]
391
538
  }], ctorParameters: () => [] });
392
539
 
540
+ class FooterService {
541
+ content = signal(null, ...(ngDevMode ? [{ debugName: "content" }] : []));
542
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: FooterService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
543
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: FooterService, providedIn: 'root' });
544
+ }
545
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: FooterService, decorators: [{
546
+ type: Injectable,
547
+ args: [{ providedIn: 'root' }]
548
+ }] });
549
+
393
550
  class AppBreadcrumb {
394
551
  router;
395
552
  _breadcrumbs$ = new BehaviorSubject([]);
@@ -462,11 +619,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
462
619
  class AppConfigurator {
463
620
  simple = false;
464
621
  location = 'app';
465
- router = inject(Router);
466
622
  config = inject(PrimeNG);
467
623
  layoutService = inject(LayoutService);
468
624
  platformId = inject(PLATFORM_ID);
469
- primeng = inject(PrimeNG);
470
625
  presetKeys = Object.keys(brandPresets);
471
626
  themeOptions = [
472
627
  { name: 'Light', value: false },
@@ -494,42 +649,10 @@ class AppConfigurator {
494
649
  ];
495
650
  }
496
651
  }
497
- surfaces = [
498
- {
499
- name: 'gray',
500
- palette: {
501
- 0: '#ffffff',
502
- 50: '#e5e6e6',
503
- 100: '#d5d6d7',
504
- 200: '#c6c7c8',
505
- 300: '#b6b8b9',
506
- 400: '#a7a8aa',
507
- 500: '#97999b',
508
- 600: '#808284',
509
- 700: '#6a6b6d',
510
- 800: '#535455',
511
- 900: '#3c3d3e',
512
- 950: '#262627'
513
- }
514
- },
515
- {
516
- name: 'darkblue',
517
- palette: {
518
- 0: '#ffffff',
519
- 50: '#D0EEFF',
520
- 100: '#B7E2F9',
521
- 200: '#73abc7',
522
- 300: '#73abc7',
523
- 400: '#4d94b8',
524
- 500: '#267da9',
525
- 600: '#00669a',
526
- 700: '#005783',
527
- 800: '#00476c',
528
- 900: '#00293e',
529
- 950: '#001a27'
530
- }
531
- }
532
- ];
652
+ surfaces = ['gray', 'darkblue'].map(name => ({
653
+ name,
654
+ palette: { 0: '#ffffff', ...brandPrimitives[name] }
655
+ }));
533
656
  selectedPrimaryColor = computed(() => {
534
657
  return this.layoutService.layoutConfig().primary;
535
658
  }, ...(ngDevMode ? [{ debugName: "selectedPrimaryColor" }] : []));
@@ -795,12 +918,12 @@ class AppConfigurator {
795
918
  </div>
796
919
  <div *ngIf="!simple && location === 'app'" class="flex flex-col gap-2">
797
920
  <span class="text-lg text-muted-color font-semibold">Card Style</span>
798
- <p-selectbutton [ngModel]="cardStyle()" (ngModelChange)="onCardStyleChange($event)" [options]="cardStyleOptions" optionLabel="name" optionValue="value" [allowEmpty]="false" [allowEmpty]="false" />
921
+ <p-selectbutton [ngModel]="cardStyle()" (ngModelChange)="onCardStyleChange($event)" [options]="cardStyleOptions" optionLabel="name" optionValue="value" [allowEmpty]="false" />
799
922
  </div>
800
923
 
801
924
  <div *ngIf="!simple && location === 'app'" class="flex flex-col gap-2">
802
925
  <span class="text-lg text-muted-color font-semibold">Menu Theme</span>
803
- <p-selectbutton [ngModel]="menuTheme()" (ngModelChange)="onMenuThemeChange($event)" [options]="menuThemeOptions" optionLabel="name" optionValue="value" [allowEmpty]="false" [allowEmpty]="false" />
926
+ <p-selectbutton [ngModel]="menuTheme()" (ngModelChange)="onMenuThemeChange($event)" [options]="menuThemeOptions" optionLabel="name" optionValue="value" [allowEmpty]="false" />
804
927
  </div>
805
928
 
806
929
  <div *ngIf="!simple && location === 'app'">
@@ -818,30 +941,16 @@ class AppConfigurator {
818
941
  </div>
819
942
  </div>
820
943
  <div class="flex">
821
- <div class="flex items-center gap-2 w-6/12">
822
- <p-radiobutton name="menuMode" value="overlay" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('overlay')" inputId="overlay"></p-radiobutton>
823
- <label for="overlay">Overlay</label>
824
- </div>
825
944
  <div class="flex items-center gap-2 w-6/12">
826
945
  <p-radiobutton name="menuMode" value="slim" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('slim')" inputId="slim"></p-radiobutton>
827
946
  <label for="slim">Slim</label>
828
947
  </div>
829
- </div>
830
- <div class="flex">
831
948
  <div class="flex items-center gap-2 w-6/12">
832
949
  <p-radiobutton name="menuMode" value="compact" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('compact')" inputId="compact"></p-radiobutton>
833
950
  <label for="compact">Compact</label>
834
951
  </div>
835
- <div class="flex items-center gap-2 w-6/12">
836
- <p-radiobutton name="menuMode" value="reveal" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('reveal')" inputId="reveal"></p-radiobutton>
837
- <label for="reveal">Reveal</label>
838
- </div>
839
952
  </div>
840
953
  <div class="flex">
841
- <div class="flex items-center gap-2 w-6/12">
842
- <p-radiobutton name="menuMode" value="drawer" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('drawer')" inputId="drawer"></p-radiobutton>
843
- <label for="drawer">Drawer</label>
844
- </div>
845
954
  <div class="flex items-center gap-2 w-6/12">
846
955
  <p-radiobutton name="menuMode" value="horizontal" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('horizontal')" inputId="horizontal"></p-radiobutton>
847
956
  <label for="horizontal">Horizontal</label>
@@ -852,13 +961,13 @@ class AppConfigurator {
852
961
  </div>
853
962
  </div>
854
963
  </p-drawer>
855
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i3.SelectButton, selector: "p-selectButton, p-selectbutton, p-select-button", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "styleClass", "ariaLabelledBy", "dataKey", "autofocus", "size", "fluid"], outputs: ["onOptionClick", "onChange"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i4.Drawer, selector: "p-drawer", inputs: ["appendTo", "motionOptions", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: ToggleSwitchModule }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i5$1.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "styleClass", "autofocus", "binary", "variant", "size"], outputs: ["onClick", "onFocus", "onBlur"] }] });
964
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SelectButtonModule }, { kind: "component", type: i3.SelectButton, selector: "p-selectButton, p-selectbutton, p-select-button", inputs: ["options", "optionLabel", "optionValue", "optionDisabled", "unselectable", "tabindex", "multiple", "allowEmpty", "styleClass", "ariaLabelledBy", "dataKey", "autofocus", "size", "fluid"], outputs: ["onOptionClick", "onChange"] }, { kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i4.Drawer, selector: "p-drawer", inputs: ["appendTo", "motionOptions", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i5.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "styleClass", "autofocus", "binary", "variant", "size"], outputs: ["onClick", "onFocus", "onBlur"] }] });
856
965
  }
857
966
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppConfigurator, decorators: [{
858
967
  type: Component,
859
968
  args: [{
860
969
  selector: 'app-configurator',
861
- imports: [CommonModule, FormsModule, SelectButtonModule, DrawerModule, ToggleSwitchModule, RadioButtonModule],
970
+ imports: [CommonModule, FormsModule, SelectButtonModule, DrawerModule, RadioButtonModule],
862
971
  template: `
863
972
  <p-drawer [(visible)]="configSidebarVisible" position="right" [transitionOptions]="'.3s cubic-bezier(0, 0, 0.2, 1)'" styleClass="layout-config-sidebar w-80" header="Settings" appendTo="body">
864
973
  <div class="flex flex-col gap-6">
@@ -913,12 +1022,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
913
1022
  </div>
914
1023
  <div *ngIf="!simple && location === 'app'" class="flex flex-col gap-2">
915
1024
  <span class="text-lg text-muted-color font-semibold">Card Style</span>
916
- <p-selectbutton [ngModel]="cardStyle()" (ngModelChange)="onCardStyleChange($event)" [options]="cardStyleOptions" optionLabel="name" optionValue="value" [allowEmpty]="false" [allowEmpty]="false" />
1025
+ <p-selectbutton [ngModel]="cardStyle()" (ngModelChange)="onCardStyleChange($event)" [options]="cardStyleOptions" optionLabel="name" optionValue="value" [allowEmpty]="false" />
917
1026
  </div>
918
1027
 
919
1028
  <div *ngIf="!simple && location === 'app'" class="flex flex-col gap-2">
920
1029
  <span class="text-lg text-muted-color font-semibold">Menu Theme</span>
921
- <p-selectbutton [ngModel]="menuTheme()" (ngModelChange)="onMenuThemeChange($event)" [options]="menuThemeOptions" optionLabel="name" optionValue="value" [allowEmpty]="false" [allowEmpty]="false" />
1030
+ <p-selectbutton [ngModel]="menuTheme()" (ngModelChange)="onMenuThemeChange($event)" [options]="menuThemeOptions" optionLabel="name" optionValue="value" [allowEmpty]="false" />
922
1031
  </div>
923
1032
 
924
1033
  <div *ngIf="!simple && location === 'app'">
@@ -936,30 +1045,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
936
1045
  </div>
937
1046
  </div>
938
1047
  <div class="flex">
939
- <div class="flex items-center gap-2 w-6/12">
940
- <p-radiobutton name="menuMode" value="overlay" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('overlay')" inputId="overlay"></p-radiobutton>
941
- <label for="overlay">Overlay</label>
942
- </div>
943
1048
  <div class="flex items-center gap-2 w-6/12">
944
1049
  <p-radiobutton name="menuMode" value="slim" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('slim')" inputId="slim"></p-radiobutton>
945
1050
  <label for="slim">Slim</label>
946
1051
  </div>
947
- </div>
948
- <div class="flex">
949
1052
  <div class="flex items-center gap-2 w-6/12">
950
1053
  <p-radiobutton name="menuMode" value="compact" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('compact')" inputId="compact"></p-radiobutton>
951
1054
  <label for="compact">Compact</label>
952
1055
  </div>
953
- <div class="flex items-center gap-2 w-6/12">
954
- <p-radiobutton name="menuMode" value="reveal" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('reveal')" inputId="reveal"></p-radiobutton>
955
- <label for="reveal">Reveal</label>
956
- </div>
957
1056
  </div>
958
1057
  <div class="flex">
959
- <div class="flex items-center gap-2 w-6/12">
960
- <p-radiobutton name="menuMode" value="drawer" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('drawer')" inputId="drawer"></p-radiobutton>
961
- <label for="drawer">Drawer</label>
962
- </div>
963
1058
  <div class="flex items-center gap-2 w-6/12">
964
1059
  <p-radiobutton name="menuMode" value="horizontal" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('horizontal')" inputId="horizontal"></p-radiobutton>
965
1060
  <label for="horizontal">Horizontal</label>
@@ -979,26 +1074,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
979
1074
  type: Input
980
1075
  }], cardStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "cardStyle", required: false }] }, { type: i0.Output, args: ["cardStyleChange"] }] } });
981
1076
 
982
- class AppFooter {
983
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppFooter, deps: [], target: i0.ɵɵFactoryTarget.Component });
984
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: AppFooter, isStandalone: true, selector: "[app-footer]", ngImport: i0, template: `
985
- <footer class="layout-footer">
986
- <span class="footer-copyright">&#169; UNOPS 2026</span>
987
- </footer>
988
- `, isInline: true });
989
- }
990
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppFooter, decorators: [{
991
- type: Component,
992
- args: [{
993
- selector: '[app-footer]',
994
- template: `
995
- <footer class="layout-footer">
996
- <span class="footer-copyright">&#169; UNOPS 2026</span>
997
- </footer>
998
- `
999
- }]
1000
- }] });
1001
-
1002
1077
  class AppRightMenu {
1003
1078
  layoutService = inject(LayoutService);
1004
1079
  cards = [
@@ -1020,7 +1095,7 @@ class AppRightMenu {
1020
1095
  <div class="flex flex-col mt-7">
1021
1096
  <div class="flex gap-6">
1022
1097
  <div class="flex flex-col items-center">
1023
- <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05);]">
1098
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-subtle">
1024
1099
  <i class="pi pi-dollar text-blue-600 text-2xl!"></i>
1025
1100
  </span>
1026
1101
  <span class="min-h-14 w-px bg-(--surface-border)"></span>
@@ -1032,7 +1107,7 @@ class AppRightMenu {
1032
1107
  </div>
1033
1108
  <div class="flex gap-6">
1034
1109
  <div class="flex flex-col items-center">
1035
- <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05);]">
1110
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-subtle">
1036
1111
  <i class="pi pi-download text-orange-600 text-2xl!"></i>
1037
1112
  </span>
1038
1113
  <span class="min-h-14 w-px bg-(--surface-border)"></span>
@@ -1044,7 +1119,7 @@ class AppRightMenu {
1044
1119
  </div>
1045
1120
  <div class="flex gap-6">
1046
1121
  <div class="flex flex-col items-center">
1047
- <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05);]">
1122
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-subtle">
1048
1123
  <i class="pi pi-question-circle text-violet-600 text-2xl!"></i>
1049
1124
  </span>
1050
1125
  <span class="min-h-14 w-px bg-(--surface-border)"></span>
@@ -1056,7 +1131,7 @@ class AppRightMenu {
1056
1131
  </div>
1057
1132
  <div class="flex gap-6">
1058
1133
  <div class="flex flex-col items-center">
1059
- <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05);]">
1134
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-subtle">
1060
1135
  <i class="pi pi-comment text-blue-600 text-2xl!"></i>
1061
1136
  </span>
1062
1137
  </div>
@@ -1082,7 +1157,7 @@ class AppRightMenu {
1082
1157
  <p class="body-small mt-1 text-left">Track your ongoing shipments to customers.</p>
1083
1158
  <img class="w-full h-full max-h-60 object-cover border border-surface rounded-2xl mt-4" src="layout/images/sidebar-right/staticmap.png" alt="unops-ng_ux" />
1084
1159
  </div>
1085
- </p-drawer>`, isInline: true, dependencies: [{ kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i4.Drawer, selector: "p-drawer", inputs: ["appendTo", "motionOptions", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: DividerModule }, { kind: "component", type: i2.Divider, selector: "p-divider", inputs: ["styleClass", "layout", "type", "align"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i3$1.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i5.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: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i6.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }] });
1160
+ </p-drawer>`, isInline: true, dependencies: [{ kind: "ngmodule", type: DrawerModule }, { kind: "component", type: i4.Drawer, selector: "p-drawer", inputs: ["appendTo", "motionOptions", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: DividerModule }, { kind: "component", type: i2.Divider, selector: "p-divider", inputs: ["styleClass", "layout", "type", "align"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i3$1.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.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$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$1.ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "pButtonPT", "pButtonUnstyled", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }] });
1086
1161
  }
1087
1162
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppRightMenu, decorators: [{
1088
1163
  type: Component,
@@ -1095,7 +1170,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1095
1170
  <div class="flex flex-col mt-7">
1096
1171
  <div class="flex gap-6">
1097
1172
  <div class="flex flex-col items-center">
1098
- <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05);]">
1173
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-subtle">
1099
1174
  <i class="pi pi-dollar text-blue-600 text-2xl!"></i>
1100
1175
  </span>
1101
1176
  <span class="min-h-14 w-px bg-(--surface-border)"></span>
@@ -1107,7 +1182,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1107
1182
  </div>
1108
1183
  <div class="flex gap-6">
1109
1184
  <div class="flex flex-col items-center">
1110
- <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05);]">
1185
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-subtle">
1111
1186
  <i class="pi pi-download text-orange-600 text-2xl!"></i>
1112
1187
  </span>
1113
1188
  <span class="min-h-14 w-px bg-(--surface-border)"></span>
@@ -1119,7 +1194,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1119
1194
  </div>
1120
1195
  <div class="flex gap-6">
1121
1196
  <div class="flex flex-col items-center">
1122
- <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05);]">
1197
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-subtle">
1123
1198
  <i class="pi pi-question-circle text-violet-600 text-2xl!"></i>
1124
1199
  </span>
1125
1200
  <span class="min-h-14 w-px bg-(--surface-border)"></span>
@@ -1131,7 +1206,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1131
1206
  </div>
1132
1207
  <div class="flex gap-6">
1133
1208
  <div class="flex flex-col items-center">
1134
- <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05);]">
1209
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-subtle">
1135
1210
  <i class="pi pi-comment text-blue-600 text-2xl!"></i>
1136
1211
  </span>
1137
1212
  </div>
@@ -1173,21 +1248,21 @@ class AppSearch {
1173
1248
  this.layoutService.layoutState.update((prev) => ({ ...prev, searchBarActive: _val }));
1174
1249
  }
1175
1250
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppSearch, deps: [], target: i0.ɵɵFactoryTarget.Component });
1176
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: AppSearch, isStandalone: true, selector: "[app-search]", ngImport: i0, template: ` <p-dialog [(visible)]="searchBarActive" [breakpoints]="{ '992px': '75vw', '576px': '90vw' }" modal dismissableMask styleClass="w-1/2 search-container">
1251
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: AppSearch, isStandalone: true, selector: "[app-search]", ngImport: i0, template: ` <p-dialog [(visible)]="searchBarActive" [breakpoints]="{ '1024px': '75vw', '780px': '90vw' }" modal dismissableMask styleClass="w-1/2 search-container">
1177
1252
  <ng-template #headless>
1178
1253
  <div class="w-full">
1179
1254
  <i class="pi pi-search"></i>
1180
1255
  <input pInputText type="text" [pAutoFocus]="true" class="p-inputtext search-input" placeholder="Search" (keydown.enter)="toggleSearchBar()" />
1181
1256
  </div>
1182
1257
  </ng-template>
1183
- </p-dialog>`, isInline: true, dependencies: [{ kind: "ngmodule", type: DialogModule }, { kind: "component", type: i1$2.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: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: AutoFocusModule }, { kind: "directive", type: i3$2.AutoFocus, selector: "[pAutoFocus]", inputs: ["pAutoFocus"] }] });
1258
+ </p-dialog>`, isInline: true, dependencies: [{ kind: "ngmodule", type: DialogModule }, { kind: "component", type: i1$3.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: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: AutoFocusModule }, { kind: "directive", type: i3$2.AutoFocus, selector: "[pAutoFocus]", inputs: ["pAutoFocus"] }] });
1184
1259
  }
1185
1260
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppSearch, decorators: [{
1186
1261
  type: Component,
1187
1262
  args: [{
1188
1263
  selector: '[app-search]',
1189
1264
  imports: [DialogModule, InputTextModule, AutoFocusModule],
1190
- template: ` <p-dialog [(visible)]="searchBarActive" [breakpoints]="{ '992px': '75vw', '576px': '90vw' }" modal dismissableMask styleClass="w-1/2 search-container">
1265
+ template: ` <p-dialog [(visible)]="searchBarActive" [breakpoints]="{ '1024px': '75vw', '780px': '90vw' }" modal dismissableMask styleClass="w-1/2 search-container">
1191
1266
  <ng-template #headless>
1192
1267
  <div class="w-full">
1193
1268
  <i class="pi pi-search"></i>
@@ -1198,6 +1273,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1198
1273
  }]
1199
1274
  }] });
1200
1275
 
1276
+ class FooterMainComponent {
1277
+ /** When true, always shows the default copyright — ignores FooterService content. */
1278
+ copyrightOnly = input(false, ...(ngDevMode ? [{ debugName: "copyrightOnly" }] : []));
1279
+ footerService = inject(FooterService);
1280
+ copyrightYear = new Date().getFullYear();
1281
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: FooterMainComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1282
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: FooterMainComponent, isStandalone: true, selector: "ux-footer-main", inputs: { copyrightOnly: { classPropertyName: "copyrightOnly", publicName: "copyrightOnly", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "ux-footer-main" }, ngImport: i0, template: `
1283
+ <div class="footer-inner">
1284
+ @if (copyrightOnly()) {
1285
+ <span>&#169; UNOPS {{ copyrightYear }}</span>
1286
+ } @else if (footerService.content(); as tpl) {
1287
+ <ng-container [ngTemplateOutlet]="tpl" />
1288
+ }
1289
+ </div>
1290
+ `, isInline: true, styles: [":host{flex-shrink:0;display:flex;align-items:center;height:3rem;background:var(--p-primary-50);font-size:var(--font-size-xs, .75rem);line-height:1.2;color:var(--p-text-color)}:host-context(:root[class*=\"app-dark\"]){background:var(--p-primary-950);color:var(--p-surface-100)}:host(.footer-sticky){position:sticky;bottom:0;z-index:10}.footer-inner{width:100%;max-width:1540px;margin-inline:auto;padding:1rem}@media screen and (min-width:780px){.footer-inner{padding:1rem 3rem}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1291
+ }
1292
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: FooterMainComponent, decorators: [{
1293
+ type: Component,
1294
+ args: [{ selector: 'ux-footer-main', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet], host: { class: 'ux-footer-main' }, template: `
1295
+ <div class="footer-inner">
1296
+ @if (copyrightOnly()) {
1297
+ <span>&#169; UNOPS {{ copyrightYear }}</span>
1298
+ } @else if (footerService.content(); as tpl) {
1299
+ <ng-container [ngTemplateOutlet]="tpl" />
1300
+ }
1301
+ </div>
1302
+ `, styles: [":host{flex-shrink:0;display:flex;align-items:center;height:3rem;background:var(--p-primary-50);font-size:var(--font-size-xs, .75rem);line-height:1.2;color:var(--p-text-color)}:host-context(:root[class*=\"app-dark\"]){background:var(--p-primary-950);color:var(--p-surface-100)}:host(.footer-sticky){position:sticky;bottom:0;z-index:10}.footer-inner{width:100%;max-width:1540px;margin-inline:auto;padding:1rem}@media screen and (min-width:780px){.footer-inner{padding:1rem 3rem}}\n"] }]
1303
+ }], propDecorators: { copyrightOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "copyrightOnly", required: false }] }] } });
1304
+
1201
1305
  class AppMenuitem {
1202
1306
  layoutService = inject(LayoutService);
1203
1307
  router = inject(Router);
@@ -1677,19 +1781,24 @@ class AppTopbar {
1677
1781
  <i class="pi pi-bars"></i>
1678
1782
  </button>
1679
1783
  <div class="topbar-left">
1680
- <button
1681
- type="button"
1682
- class="topbar-menu-toggle"
1683
- [class.active]="isSidebarPinned()"
1684
- [attr.aria-label]="isSidebarPinned() ? 'Collapse sidebar' : 'Expand sidebar'"
1685
- (click)="toggleSidebarPin()"
1686
- >
1687
- <i class="pi pi-bars"></i>
1688
- </button>
1689
- <a class="topbar-logo" [routerLink]="['/']">
1690
- <img [src]="desktopLogo()" [attr.alt]="mobileLogoConfig.alt" />
1691
- </a>
1692
- <span class="topbar-logo-separator"></span>
1784
+ <div class="topbar-sidebar-section">
1785
+ <button
1786
+ type="button"
1787
+ class="topbar-menu-toggle"
1788
+ [class.active]="isSidebarPinned()"
1789
+ [attr.aria-label]="isSidebarPinned() ? 'Collapse sidebar' : 'Expand sidebar'"
1790
+ (click)="toggleSidebarPin()"
1791
+ >
1792
+ <i class="pi pi-bars"></i>
1793
+ </button>
1794
+ <a class="topbar-logo" [routerLink]="['/']">
1795
+ <img [src]="desktopLogo()" [attr.alt]="mobileLogoConfig.alt" />
1796
+ </a>
1797
+ <span class="topbar-logo-separator"></span>
1798
+ </div>
1799
+ </div>
1800
+
1801
+ <div class="topbar-main">
1693
1802
  <div app-breadcrumb></div>
1694
1803
  @if (searchActive()) {
1695
1804
  <div class="flex items-center gap-2 ml-auto">
@@ -1702,14 +1811,8 @@ class AppTopbar {
1702
1811
  </button>
1703
1812
  </div>
1704
1813
  }
1705
- </div>
1706
-
1707
- <a class="mobile-logo" [routerLink]="['/landing']">
1708
- <img [src]="mobileLogo()" [attr.alt]="mobileLogoConfig.alt" />
1709
- </a>
1710
-
1711
- <div class="topbar-right">
1712
- <ul class="topbar-menu">
1814
+ <div class="topbar-right">
1815
+ <ul class="topbar-menu">
1713
1816
  <li class="right-sidebar-item" [class.hidden]="searchActive()">
1714
1817
  <a class="right-sidebar-button" aria-label="Open search" (click)="openSearch()">
1715
1818
  <i class="pi pi-search"></i>
@@ -1730,11 +1833,11 @@ class AppTopbar {
1730
1833
  <i class="pi pi-bell"></i>
1731
1834
  </a>
1732
1835
  <div
1733
- class="list-none m-0 rounded-2xl border border-surface fixed sm:absolute bg-surface-0 dark:bg-surface-900 overflow-hidden hidden origin-top w-[calc(100vw-2rem)] sm:w-88 mt-2 z-50 top-auto left-4 sm:left-auto sm:right-0 shadow-[0px_56px_16px_0px_rgba(0,0,0,0.00),0px_36px_14px_0px_rgba(0,0,0,0.01),0px_20px_12px_0px_rgba(0,0,0,0.02),0px_9px_9px_0px_rgba(0,0,0,0.03),0px_2px_5px_0px_rgba(0,0,0,0.04)]"
1836
+ class="list-none m-0 rounded-2xl border border-surface fixed sm:absolute bg-surface-0 dark:bg-surface-900 overflow-hidden hidden origin-top w-[calc(100vw-2rem)] sm:w-88 mt-2 z-50 top-auto left-4 sm:left-auto sm:right-0 shadow-flyout"
1734
1837
  >
1735
1838
  <div class="p-4 flex items-center justify-between border-b border-surface">
1736
1839
  <span class="label-small text-surface-950 dark:text-surface-0">Notifications</span>
1737
- <button pRipple class="py-1 px-2 text-surface-950 dark:text-surface-0 label-x-small hover:bg-emphasis border border-surface rounded-lg shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05)] transition-all">Mark all as read</button>
1840
+ <button pRipple class="py-1 px-2 text-surface-950 dark:text-surface-0 label-x-small hover:bg-emphasis border border-surface rounded-lg shadow-subtle transition-all">Mark all as read</button>
1738
1841
  </div>
1739
1842
  <div class="flex items-center border-b border-surface">
1740
1843
  @for (item of notificationsBars(); track item.id; let i = $index) {
@@ -1784,7 +1887,7 @@ class AppTopbar {
1784
1887
  </svg>
1785
1888
  </a>
1786
1889
  <div
1787
- class="list-none p-2 m-0 rounded-2xl border border-surface overflow-hidden fixed sm:absolute bg-surface-0 dark:bg-surface-900 hidden origin-top w-44 mt-2 right-4 sm:right-0 z-999 top-auto shadow-[0px_56px_16px_0px_rgba(0,0,0,0.00),0px_36px_14px_0px_rgba(0,0,0,0.01),0px_20px_12px_0px_rgba(0,0,0,0.02),0px_9px_9px_0px_rgba(0,0,0,0.03),0px_2px_5px_0px_rgba(0,0,0,0.04)]"
1890
+ class="list-none p-2 m-0 rounded-2xl border border-surface overflow-hidden fixed sm:absolute bg-surface-0 dark:bg-surface-900 hidden origin-top w-44 mt-2 right-4 sm:right-0 z-999 top-auto shadow-flyout"
1788
1891
  >
1789
1892
  <ul class="flex flex-col gap-1">
1790
1893
  @for (lang of languages(); track lang.code) {
@@ -1810,7 +1913,7 @@ class AppTopbar {
1810
1913
  </a>
1811
1914
  <div
1812
1915
  #profilePanel
1813
- class="list-none p-2 m-0 rounded-2xl border border-surface overflow-hidden fixed sm:absolute bg-surface-0 dark:bg-surface-900 origin-top w-52 mt-2 right-4 sm:right-0 z-999 top-auto shadow-[0px_56px_16px_0px_rgba(0,0,0,0.00),0px_36px_14px_0px_rgba(0,0,0,0.01),0px_20px_12px_0px_rgba(0,0,0,0.02),0px_9px_9px_0px_rgba(0,0,0,0.03),0px_2px_5px_0px_rgba(0,0,0,0.04)]"
1916
+ class="list-none p-2 m-0 rounded-2xl border border-surface overflow-hidden fixed sm:absolute bg-surface-0 dark:bg-surface-900 origin-top w-52 mt-2 right-4 sm:right-0 z-999 top-auto shadow-flyout"
1814
1917
  [class.hidden]="!profileMenuOpen()"
1815
1918
  [class.animate-scalein]="profileMenuOpen()"
1816
1919
  >
@@ -1861,7 +1964,7 @@ class AppTopbar {
1861
1964
  </a>
1862
1965
  </li>
1863
1966
  <li>
1864
- <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false)">
1967
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false); onConfigButtonClick()">
1865
1968
  <i class="pi pi-cog"></i>
1866
1969
  <span>Settings</span>
1867
1970
  </a>
@@ -1887,9 +1990,14 @@ class AppTopbar {
1887
1990
  </ul>
1888
1991
  </div>
1889
1992
  </li>
1890
- </ul>
1993
+ </ul>
1994
+ </div>
1891
1995
  </div>
1892
- </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i3$4.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "component", type: AppBreadcrumb, selector: "[app-breadcrumb]" }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: IconFieldModule }, { kind: "component", type: i5$2.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "ngmodule", type: InputIconModule }, { kind: "component", type: i6$1.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "ngmodule", type: RippleModule }, { kind: "directive", type: i4$2.Ripple, selector: "[pRipple]" }, { kind: "ngmodule", type: BadgeModule }, { kind: "component", type: i8.Badge, selector: "p-badge", inputs: ["styleClass", "badgeSize", "size", "severity", "value", "badgeDisabled"] }, { kind: "ngmodule", type: OverlayBadgeModule }, { kind: "component", type: i9.OverlayBadge, selector: "p-overlayBadge, p-overlay-badge, p-overlaybadge", inputs: ["styleClass", "style", "badgeSize", "severity", "value", "badgeDisabled", "size"] }, { kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i10.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }] });
1996
+
1997
+ <a class="mobile-logo" [routerLink]="['/']">
1998
+ <img [src]="mobileLogo()" [attr.alt]="mobileLogoConfig.alt" />
1999
+ </a>
2000
+ </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: StyleClassModule }, { kind: "directive", type: i3$4.StyleClass, selector: "[pStyleClass]", inputs: ["pStyleClass", "enterFromClass", "enterActiveClass", "enterToClass", "leaveFromClass", "leaveActiveClass", "leaveToClass", "hideOnOutsideClick", "toggleClass", "hideOnEscape", "hideOnResize", "resizeSelector"] }, { kind: "component", type: AppBreadcrumb, selector: "[app-breadcrumb]" }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: IconFieldModule }, { kind: "component", type: i5$1.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "ngmodule", type: InputIconModule }, { kind: "component", type: i6.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "ngmodule", type: RippleModule }, { kind: "directive", type: i4$2.Ripple, selector: "[pRipple]" }, { kind: "ngmodule", type: BadgeModule }, { kind: "component", type: i8.Badge, selector: "p-badge", inputs: ["styleClass", "badgeSize", "size", "severity", "value", "badgeDisabled"] }, { kind: "ngmodule", type: OverlayBadgeModule }, { kind: "component", type: i9.OverlayBadge, selector: "p-overlayBadge, p-overlay-badge, p-overlaybadge", inputs: ["styleClass", "style", "badgeSize", "severity", "value", "badgeDisabled", "size"] }, { kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i10.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }] });
1893
2001
  }
1894
2002
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppTopbar, decorators: [{
1895
2003
  type: Component,
@@ -1901,19 +2009,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1901
2009
  <i class="pi pi-bars"></i>
1902
2010
  </button>
1903
2011
  <div class="topbar-left">
1904
- <button
1905
- type="button"
1906
- class="topbar-menu-toggle"
1907
- [class.active]="isSidebarPinned()"
1908
- [attr.aria-label]="isSidebarPinned() ? 'Collapse sidebar' : 'Expand sidebar'"
1909
- (click)="toggleSidebarPin()"
1910
- >
1911
- <i class="pi pi-bars"></i>
1912
- </button>
1913
- <a class="topbar-logo" [routerLink]="['/']">
1914
- <img [src]="desktopLogo()" [attr.alt]="mobileLogoConfig.alt" />
1915
- </a>
1916
- <span class="topbar-logo-separator"></span>
2012
+ <div class="topbar-sidebar-section">
2013
+ <button
2014
+ type="button"
2015
+ class="topbar-menu-toggle"
2016
+ [class.active]="isSidebarPinned()"
2017
+ [attr.aria-label]="isSidebarPinned() ? 'Collapse sidebar' : 'Expand sidebar'"
2018
+ (click)="toggleSidebarPin()"
2019
+ >
2020
+ <i class="pi pi-bars"></i>
2021
+ </button>
2022
+ <a class="topbar-logo" [routerLink]="['/']">
2023
+ <img [src]="desktopLogo()" [attr.alt]="mobileLogoConfig.alt" />
2024
+ </a>
2025
+ <span class="topbar-logo-separator"></span>
2026
+ </div>
2027
+ </div>
2028
+
2029
+ <div class="topbar-main">
1917
2030
  <div app-breadcrumb></div>
1918
2031
  @if (searchActive()) {
1919
2032
  <div class="flex items-center gap-2 ml-auto">
@@ -1926,14 +2039,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1926
2039
  </button>
1927
2040
  </div>
1928
2041
  }
1929
- </div>
1930
-
1931
- <a class="mobile-logo" [routerLink]="['/landing']">
1932
- <img [src]="mobileLogo()" [attr.alt]="mobileLogoConfig.alt" />
1933
- </a>
1934
-
1935
- <div class="topbar-right">
1936
- <ul class="topbar-menu">
2042
+ <div class="topbar-right">
2043
+ <ul class="topbar-menu">
1937
2044
  <li class="right-sidebar-item" [class.hidden]="searchActive()">
1938
2045
  <a class="right-sidebar-button" aria-label="Open search" (click)="openSearch()">
1939
2046
  <i class="pi pi-search"></i>
@@ -1954,11 +2061,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1954
2061
  <i class="pi pi-bell"></i>
1955
2062
  </a>
1956
2063
  <div
1957
- class="list-none m-0 rounded-2xl border border-surface fixed sm:absolute bg-surface-0 dark:bg-surface-900 overflow-hidden hidden origin-top w-[calc(100vw-2rem)] sm:w-88 mt-2 z-50 top-auto left-4 sm:left-auto sm:right-0 shadow-[0px_56px_16px_0px_rgba(0,0,0,0.00),0px_36px_14px_0px_rgba(0,0,0,0.01),0px_20px_12px_0px_rgba(0,0,0,0.02),0px_9px_9px_0px_rgba(0,0,0,0.03),0px_2px_5px_0px_rgba(0,0,0,0.04)]"
2064
+ class="list-none m-0 rounded-2xl border border-surface fixed sm:absolute bg-surface-0 dark:bg-surface-900 overflow-hidden hidden origin-top w-[calc(100vw-2rem)] sm:w-88 mt-2 z-50 top-auto left-4 sm:left-auto sm:right-0 shadow-flyout"
1958
2065
  >
1959
2066
  <div class="p-4 flex items-center justify-between border-b border-surface">
1960
2067
  <span class="label-small text-surface-950 dark:text-surface-0">Notifications</span>
1961
- <button pRipple class="py-1 px-2 text-surface-950 dark:text-surface-0 label-x-small hover:bg-emphasis border border-surface rounded-lg shadow-[0px_1px_2px_0px_rgba(18,18,23,0.05)] transition-all">Mark all as read</button>
2068
+ <button pRipple class="py-1 px-2 text-surface-950 dark:text-surface-0 label-x-small hover:bg-emphasis border border-surface rounded-lg shadow-subtle transition-all">Mark all as read</button>
1962
2069
  </div>
1963
2070
  <div class="flex items-center border-b border-surface">
1964
2071
  @for (item of notificationsBars(); track item.id; let i = $index) {
@@ -2008,7 +2115,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2008
2115
  </svg>
2009
2116
  </a>
2010
2117
  <div
2011
- class="list-none p-2 m-0 rounded-2xl border border-surface overflow-hidden fixed sm:absolute bg-surface-0 dark:bg-surface-900 hidden origin-top w-44 mt-2 right-4 sm:right-0 z-999 top-auto shadow-[0px_56px_16px_0px_rgba(0,0,0,0.00),0px_36px_14px_0px_rgba(0,0,0,0.01),0px_20px_12px_0px_rgba(0,0,0,0.02),0px_9px_9px_0px_rgba(0,0,0,0.03),0px_2px_5px_0px_rgba(0,0,0,0.04)]"
2118
+ class="list-none p-2 m-0 rounded-2xl border border-surface overflow-hidden fixed sm:absolute bg-surface-0 dark:bg-surface-900 hidden origin-top w-44 mt-2 right-4 sm:right-0 z-999 top-auto shadow-flyout"
2012
2119
  >
2013
2120
  <ul class="flex flex-col gap-1">
2014
2121
  @for (lang of languages(); track lang.code) {
@@ -2034,7 +2141,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2034
2141
  </a>
2035
2142
  <div
2036
2143
  #profilePanel
2037
- class="list-none p-2 m-0 rounded-2xl border border-surface overflow-hidden fixed sm:absolute bg-surface-0 dark:bg-surface-900 origin-top w-52 mt-2 right-4 sm:right-0 z-999 top-auto shadow-[0px_56px_16px_0px_rgba(0,0,0,0.00),0px_36px_14px_0px_rgba(0,0,0,0.01),0px_20px_12px_0px_rgba(0,0,0,0.02),0px_9px_9px_0px_rgba(0,0,0,0.03),0px_2px_5px_0px_rgba(0,0,0,0.04)]"
2144
+ class="list-none p-2 m-0 rounded-2xl border border-surface overflow-hidden fixed sm:absolute bg-surface-0 dark:bg-surface-900 origin-top w-52 mt-2 right-4 sm:right-0 z-999 top-auto shadow-flyout"
2038
2145
  [class.hidden]="!profileMenuOpen()"
2039
2146
  [class.animate-scalein]="profileMenuOpen()"
2040
2147
  >
@@ -2085,7 +2192,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2085
2192
  </a>
2086
2193
  </li>
2087
2194
  <li>
2088
- <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false)">
2195
+ <a class="label-small dark:text-surface-400 flex gap-2 py-2 px-2.5 rounded-lg items-center hover:bg-emphasis transition-colors duration-150 cursor-pointer" (click)="profileMenuOpen.set(false); onConfigButtonClick()">
2089
2196
  <i class="pi pi-cog"></i>
2090
2197
  <span>Settings</span>
2091
2198
  </a>
@@ -2111,8 +2218,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2111
2218
  </ul>
2112
2219
  </div>
2113
2220
  </li>
2114
- </ul>
2221
+ </ul>
2222
+ </div>
2115
2223
  </div>
2224
+
2225
+ <a class="mobile-logo" [routerLink]="['/']">
2226
+ <img [src]="mobileLogo()" [attr.alt]="mobileLogoConfig.alt" />
2227
+ </a>
2116
2228
  </div>`
2117
2229
  }]
2118
2230
  }], propDecorators: { menuButton: [{
@@ -2132,7 +2244,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2132
2244
  args: ['document:click', ['$event']]
2133
2245
  }] } });
2134
2246
 
2135
- const BREAKPOINT = 992;
2247
+ const BREAKPOINT = 1024;
2136
2248
  class AppSidebar {
2137
2249
  layoutService = inject(LayoutService);
2138
2250
  router = inject(Router);
@@ -2339,22 +2451,28 @@ class AppSidebar {
2339
2451
  }
2340
2452
  }
2341
2453
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppSidebar, deps: [], target: i0.ɵɵFactoryTarget.Component });
2342
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: AppSidebar, isStandalone: true, selector: "[app-sidebar]", viewQueries: [{ propertyName: "menuContainer", first: true, predicate: ["menuContainer"], descendants: true }, { propertyName: "sidebarRef", first: true, predicate: ["sidebarRef"], descendants: true }], ngImport: i0, template: `<nav class="layout-sidebar" aria-label="Main navigation" (mouseleave)="onMouseLeave()">
2343
- <div #menuContainer class="layout-menu-container" (scroll)="onMenuScroll()">
2454
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: AppSidebar, isStandalone: true, selector: "[app-sidebar]", viewQueries: [{ propertyName: "menuContainer", first: true, predicate: ["menuContainer"], descendants: true }, { propertyName: "sidebarRef", first: true, predicate: ["sidebarRef"], descendants: true }], ngImport: i0, template: `<nav class="layout-sidebar" aria-label="Main navigation" (mouseleave)="onMouseLeave()">
2455
+ <div #menuContainer class="layout-menu-container min-h-0 grow" (scroll)="onMenuScroll()">
2344
2456
  <div app-menu></div>
2345
2457
  </div>
2458
+ @if (!isHorizontal()) {
2459
+ <ux-footer-main [copyrightOnly]="true" role="contentinfo" />
2460
+ }
2346
2461
  <div app-topbar *ngIf="isHorizontal()"></div>
2347
- </nav>`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: AppMenu, selector: "[app-menu]" }, { kind: "ngmodule", type: RouterModule }, { kind: "component", type: AppTopbar, selector: "[app-topbar]", inputs: ["selectedNotificationBar"], outputs: ["selectedNotificationBarChange"] }] });
2462
+ </nav>`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: AppMenu, selector: "[app-menu]" }, { kind: "ngmodule", type: RouterModule }, { kind: "component", type: AppTopbar, selector: "[app-topbar]", inputs: ["selectedNotificationBar"], outputs: ["selectedNotificationBarChange"] }, { kind: "component", type: FooterMainComponent, selector: "ux-footer-main", inputs: ["copyrightOnly"] }] });
2348
2463
  }
2349
2464
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppSidebar, decorators: [{
2350
2465
  type: Component,
2351
2466
  args: [{
2352
2467
  selector: '[app-sidebar]',
2353
- imports: [CommonModule, AppMenu, RouterModule, AppTopbar],
2468
+ imports: [CommonModule, AppMenu, RouterModule, AppTopbar, FooterMainComponent],
2354
2469
  template: `<nav class="layout-sidebar" aria-label="Main navigation" (mouseleave)="onMouseLeave()">
2355
- <div #menuContainer class="layout-menu-container" (scroll)="onMenuScroll()">
2470
+ <div #menuContainer class="layout-menu-container min-h-0 grow" (scroll)="onMenuScroll()">
2356
2471
  <div app-menu></div>
2357
2472
  </div>
2473
+ @if (!isHorizontal()) {
2474
+ <ux-footer-main [copyrightOnly]="true" role="contentinfo" />
2475
+ }
2358
2476
  <div app-topbar *ngIf="isHorizontal()"></div>
2359
2477
  </nav>`
2360
2478
  }]
@@ -2368,6 +2486,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2368
2486
 
2369
2487
  class AppLayout {
2370
2488
  layoutService = inject(LayoutService);
2489
+ elRef = inject(ElementRef);
2371
2490
  constructor() {
2372
2491
  effect(() => {
2373
2492
  const state = this.layoutService.layoutState();
@@ -2378,6 +2497,11 @@ class AppLayout {
2378
2497
  document.body.classList.remove('blocked-scroll');
2379
2498
  }
2380
2499
  });
2500
+ inject(Router).events.pipe(filter((e) => e instanceof NavigationEnd), takeUntilDestroyed()).subscribe(() => {
2501
+ const wrapper = this.elRef.nativeElement.querySelector('.layout-content-wrapper');
2502
+ if (wrapper)
2503
+ wrapper.scrollTop = 0;
2504
+ });
2381
2505
  }
2382
2506
  containerClass = computed(() => {
2383
2507
  const layoutConfig = this.layoutService.layoutConfig();
@@ -2410,21 +2534,21 @@ class AppLayout {
2410
2534
  <div app-breadcrumb></div>
2411
2535
  <router-outlet></router-outlet>
2412
2536
  </main>
2413
- <div app-footer></div>
2414
2537
  </div>
2538
+ <ux-footer-main class="footer-sticky" />
2415
2539
  </div>
2416
2540
  </div>
2417
2541
  <app-configurator />
2418
2542
  <div app-search></div>
2419
2543
  <div app-rightmenu></div>
2420
2544
  <div class="layout-mask"></div>
2421
- </div> `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: AppTopbar, selector: "[app-topbar]", inputs: ["selectedNotificationBar"], outputs: ["selectedNotificationBarChange"] }, { kind: "component", type: AppSidebar, selector: "[app-sidebar]" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: AppConfigurator, selector: "app-configurator", inputs: ["simple", "location", "cardStyle"], outputs: ["cardStyleChange"] }, { kind: "component", type: AppBreadcrumb, selector: "[app-breadcrumb]" }, { kind: "component", type: AppFooter, selector: "[app-footer]" }, { kind: "component", type: AppSearch, selector: "[app-search]" }, { kind: "component", type: AppRightMenu, selector: "[app-rightmenu]" }] });
2545
+ </div> `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: AppTopbar, selector: "[app-topbar]", inputs: ["selectedNotificationBar"], outputs: ["selectedNotificationBarChange"] }, { kind: "component", type: AppSidebar, selector: "[app-sidebar]" }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: AppConfigurator, selector: "app-configurator", inputs: ["simple", "location", "cardStyle"], outputs: ["cardStyleChange"] }, { kind: "component", type: AppBreadcrumb, selector: "[app-breadcrumb]" }, { kind: "component", type: FooterMainComponent, selector: "ux-footer-main", inputs: ["copyrightOnly"] }, { kind: "component", type: AppSearch, selector: "[app-search]" }, { kind: "component", type: AppRightMenu, selector: "[app-rightmenu]" }] });
2422
2546
  }
2423
2547
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppLayout, decorators: [{
2424
2548
  type: Component,
2425
2549
  args: [{
2426
2550
  selector: 'app-layout',
2427
- imports: [CommonModule, AppTopbar, AppSidebar, RouterModule, AppConfigurator, AppBreadcrumb, AppFooter, AppSearch, AppRightMenu],
2551
+ imports: [CommonModule, AppTopbar, AppSidebar, RouterModule, AppConfigurator, AppBreadcrumb, FooterMainComponent, AppSearch, AppRightMenu],
2428
2552
  template: `<div class="layout-wrapper" [ngClass]="containerClass()">
2429
2553
  <div app-topbar></div>
2430
2554
  <div class="layout-body">
@@ -2435,8 +2559,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2435
2559
  <div app-breadcrumb></div>
2436
2560
  <router-outlet></router-outlet>
2437
2561
  </main>
2438
- <div app-footer></div>
2439
2562
  </div>
2563
+ <ux-footer-main class="footer-sticky" />
2440
2564
  </div>
2441
2565
  </div>
2442
2566
  <app-configurator />
@@ -2447,6 +2571,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2447
2571
  }]
2448
2572
  }], ctorParameters: () => [] });
2449
2573
 
2574
+ class AppFooter {
2575
+ layoutService = inject(LayoutService);
2576
+ copyrightYear = new Date().getFullYear();
2577
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppFooter, deps: [], target: i0.ɵɵFactoryTarget.Component });
2578
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: AppFooter, isStandalone: true, selector: "[app-footer]", ngImport: i0, template: `
2579
+ @if (layoutService.isHorizontal()) {
2580
+ <footer class="layout-footer">
2581
+ <span class="footer-copyright">&#169; UNOPS {{ copyrightYear }}</span>
2582
+ </footer>
2583
+ }
2584
+ `, isInline: true });
2585
+ }
2586
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppFooter, decorators: [{
2587
+ type: Component,
2588
+ args: [{
2589
+ selector: '[app-footer]',
2590
+ template: `
2591
+ @if (layoutService.isHorizontal()) {
2592
+ <footer class="layout-footer">
2593
+ <span class="footer-copyright">&#169; UNOPS {{ copyrightYear }}</span>
2594
+ </footer>
2595
+ }
2596
+ `
2597
+ }]
2598
+ }] });
2599
+
2450
2600
  class AuthLayout {
2451
2601
  layoutService = inject(LayoutService);
2452
2602
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AuthLayout, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -2532,7 +2682,7 @@ class AiCardBgComponent {
2532
2682
  />
2533
2683
  </svg>
2534
2684
  <ng-content />
2535
- `, isInline: true, styles: [":host{--ux-ai-bg-from: #cce5ff;--ux-ai-bg-to:rgb(255, 236, 254);--ux-ai-fg-from:rgb(188, 214, 255);--ux-ai-fg-to:rgb(246, 216, 255);position:relative;overflow:hidden;isolation:isolate;contain:paint;animation:ux-ai-bg-move 5s ease-in-out infinite;will-change:background-color}:host-context([class*=\"app-dark\"]){--ux-ai-bg-from:rgb(0, 32, 69);--ux-ai-bg-to:rgb(47, 18, 116);--ux-ai-fg-from:rgb(0, 20, 47);--ux-ai-fg-to:rgb(76, 1, 71)}:host>:not(svg){position:relative;z-index:1}:host svg.ux-ai-card-bg__svg{position:absolute;inset:0;z-index:0;height:100%;width:100%;max-height:100%;overflow:hidden;pointer-events:none}.ux-ai-fg{will-change:fill;animation:ux-ai-fg-move 6s ease-in-out infinite}.ux-ai-fg--1{animation-delay:0s}.ux-ai-fg--2{animation-delay:2s}.ux-ai-fg--3{animation-delay:4s}@keyframes ux-ai-bg-move{0%,to{background-color:var(--ux-ai-bg-from)}50%{background-color:var(--ux-ai-bg-to)}}@keyframes ux-ai-fg-move{0%,to{fill:var(--ux-ai-fg-from)}50%{fill:var(--ux-ai-fg-to)}}@media(prefers-reduced-motion:reduce){:host{animation:none;background-color:var(--ux-ai-bg-from)}.ux-ai-fg{animation:none;fill:var(--ux-ai-fg-from)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2685
+ `, isInline: true, styles: [":host{--ux-ai-bg-from: var(--color-ai-50);--ux-ai-bg-to: var(--color-cherry-50);--ux-ai-fg-from: var(--color-ai-200);--ux-ai-fg-to: var(--color-cherry-100);position:relative;overflow:hidden;isolation:isolate;contain:paint;animation:ux-ai-bg-move 5s ease-in-out infinite;will-change:background-color}:host-context([class*=\"app-dark\"]){--ux-ai-bg-from: var(--color-ai-950);--ux-ai-bg-to: var(--color-cherry-950);--ux-ai-fg-from: var(--color-ai-900);--ux-ai-fg-to: var(--color-cherry-900)}:host>:not(svg){position:relative;z-index:1}:host svg.ux-ai-card-bg__svg{position:absolute;inset:0;z-index:0;height:100%;width:100%;max-height:100%;overflow:hidden;pointer-events:none}.ux-ai-fg{will-change:fill;animation:ux-ai-fg-move 6s ease-in-out infinite}.ux-ai-fg--1{animation-delay:0s}.ux-ai-fg--2{animation-delay:2s}.ux-ai-fg--3{animation-delay:4s}@keyframes ux-ai-bg-move{0%,to{background-color:var(--ux-ai-bg-from)}50%{background-color:var(--ux-ai-bg-to)}}@keyframes ux-ai-fg-move{0%,to{fill:var(--ux-ai-fg-from)}50%{fill:var(--ux-ai-fg-to)}}@media(prefers-reduced-motion:reduce){:host{animation:none;background-color:var(--ux-ai-bg-from)}.ux-ai-fg{animation:none;fill:var(--ux-ai-fg-from)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2536
2686
  }
2537
2687
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AiCardBgComponent, decorators: [{
2538
2688
  type: Component,
@@ -2581,9 +2731,1220 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2581
2731
  />
2582
2732
  </svg>
2583
2733
  <ng-content />
2584
- `, styles: [":host{--ux-ai-bg-from: #cce5ff;--ux-ai-bg-to:rgb(255, 236, 254);--ux-ai-fg-from:rgb(188, 214, 255);--ux-ai-fg-to:rgb(246, 216, 255);position:relative;overflow:hidden;isolation:isolate;contain:paint;animation:ux-ai-bg-move 5s ease-in-out infinite;will-change:background-color}:host-context([class*=\"app-dark\"]){--ux-ai-bg-from:rgb(0, 32, 69);--ux-ai-bg-to:rgb(47, 18, 116);--ux-ai-fg-from:rgb(0, 20, 47);--ux-ai-fg-to:rgb(76, 1, 71)}:host>:not(svg){position:relative;z-index:1}:host svg.ux-ai-card-bg__svg{position:absolute;inset:0;z-index:0;height:100%;width:100%;max-height:100%;overflow:hidden;pointer-events:none}.ux-ai-fg{will-change:fill;animation:ux-ai-fg-move 6s ease-in-out infinite}.ux-ai-fg--1{animation-delay:0s}.ux-ai-fg--2{animation-delay:2s}.ux-ai-fg--3{animation-delay:4s}@keyframes ux-ai-bg-move{0%,to{background-color:var(--ux-ai-bg-from)}50%{background-color:var(--ux-ai-bg-to)}}@keyframes ux-ai-fg-move{0%,to{fill:var(--ux-ai-fg-from)}50%{fill:var(--ux-ai-fg-to)}}@media(prefers-reduced-motion:reduce){:host{animation:none;background-color:var(--ux-ai-bg-from)}.ux-ai-fg{animation:none;fill:var(--ux-ai-fg-from)}}\n"] }]
2734
+ `, styles: [":host{--ux-ai-bg-from: var(--color-ai-50);--ux-ai-bg-to: var(--color-cherry-50);--ux-ai-fg-from: var(--color-ai-200);--ux-ai-fg-to: var(--color-cherry-100);position:relative;overflow:hidden;isolation:isolate;contain:paint;animation:ux-ai-bg-move 5s ease-in-out infinite;will-change:background-color}:host-context([class*=\"app-dark\"]){--ux-ai-bg-from: var(--color-ai-950);--ux-ai-bg-to: var(--color-cherry-950);--ux-ai-fg-from: var(--color-ai-900);--ux-ai-fg-to: var(--color-cherry-900)}:host>:not(svg){position:relative;z-index:1}:host svg.ux-ai-card-bg__svg{position:absolute;inset:0;z-index:0;height:100%;width:100%;max-height:100%;overflow:hidden;pointer-events:none}.ux-ai-fg{will-change:fill;animation:ux-ai-fg-move 6s ease-in-out infinite}.ux-ai-fg--1{animation-delay:0s}.ux-ai-fg--2{animation-delay:2s}.ux-ai-fg--3{animation-delay:4s}@keyframes ux-ai-bg-move{0%,to{background-color:var(--ux-ai-bg-from)}50%{background-color:var(--ux-ai-bg-to)}}@keyframes ux-ai-fg-move{0%,to{fill:var(--ux-ai-fg-from)}50%{fill:var(--ux-ai-fg-to)}}@media(prefers-reduced-motion:reduce){:host{animation:none;background-color:var(--ux-ai-bg-from)}.ux-ai-fg{animation:none;fill:var(--ux-ai-fg-from)}}\n"] }]
2585
2735
  }] });
2586
2736
 
2737
+ class AiInsightsCardComponent {
2738
+ title = input.required(...(ngDevMode ? [{ debugName: "title" }] : []));
2739
+ insights = input.required(...(ngDevMode ? [{ debugName: "insights" }] : []));
2740
+ searchPlaceholder = input('Search AI insights...', ...(ngDevMode ? [{ debugName: "searchPlaceholder" }] : []));
2741
+ actionClick = output();
2742
+ expanded = signal(false, ...(ngDevMode ? [{ debugName: "expanded" }] : []));
2743
+ searchQuery = signal('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : []));
2744
+ filteredInsights = computed(() => {
2745
+ const query = this.searchQuery().trim().toLowerCase();
2746
+ if (!query)
2747
+ return this.insights();
2748
+ return this.insights().filter(i => i.title.toLowerCase().includes(query) ||
2749
+ i.description.toLowerCase().includes(query));
2750
+ }, ...(ngDevMode ? [{ debugName: "filteredInsights" }] : []));
2751
+ perPage = signal(this.calcPerPage(), ...(ngDevMode ? [{ debugName: "perPage" }] : []));
2752
+ page = signal(0, ...(ngDevMode ? [{ debugName: "page" }] : []));
2753
+ first = computed(() => this.page() * this.perPage(), ...(ngDevMode ? [{ debugName: "first" }] : []));
2754
+ paginatedInsights = computed(() => {
2755
+ const all = this.filteredInsights();
2756
+ return all.slice(this.first(), this.first() + this.perPage());
2757
+ }, ...(ngDevMode ? [{ debugName: "paginatedInsights" }] : []));
2758
+ destroyRef = inject(DestroyRef);
2759
+ ngOnInit() {
2760
+ const onResize = () => this.perPage.set(this.calcPerPage());
2761
+ window.addEventListener('resize', onResize);
2762
+ this.destroyRef.onDestroy(() => window.removeEventListener('resize', onResize));
2763
+ }
2764
+ calcPerPage() {
2765
+ const shellOffset = 12 * 16;
2766
+ const cardChrome = 160 + 72;
2767
+ const insightCardHeight = 150;
2768
+ const available = (typeof window !== 'undefined' ? window.innerHeight : 900) - shellOffset - cardChrome;
2769
+ return Math.max(1, Math.floor(available / insightCardHeight));
2770
+ }
2771
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AiInsightsCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2772
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: AiInsightsCardComponent, isStandalone: true, selector: "ux-ai-insights-card", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, insights: { classPropertyName: "insights", publicName: "insights", isSignal: true, isRequired: true, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { actionClick: "actionClick" }, host: { properties: { "class.ux-ai-expanded": "expanded()" }, classAttribute: "ux-ai-insights-card block border border-blue-100 dark:border-midnight-400 rounded-2xl shadow-sm overflow-hidden transition-all duration-300 flex flex-col" }, ngImport: i0, template: `
2773
+ <ux-ai-card-bg class="flex flex-col flex-1 p-4">
2774
+ <div class="motion-safe:animate-enter-liquid [animation-delay:80ms] flex flex-col flex-1">
2775
+ <div class="flex items-center justify-between cursor-pointer shrink-0 pr-2" (click)="expanded.set(!expanded())">
2776
+ <div class="flex items-center gap-3">
2777
+ <div class="w-[34px] h-[34px] rounded-[10px] flex items-center justify-center shrink-0">
2778
+ <i class="pi pi-sparkles text-ai-600 dark:text-ai-300"></i>
2779
+ </div>
2780
+ <div class="flex flex-col">
2781
+ <h4 class="title-h4 text-left text-deepsea-500 dark:text-surface-0">{{ title() }}</h4>
2782
+ <span class="text-midnight-700 dark:text-surface-100 text-sm font-medium leading-tight">{{ insights().length }} insights available for your review</span>
2783
+ </div>
2784
+ </div>
2785
+ <i class="pi text-sm text-darkblue-500 dark:text-surface-0" [ngClass]="expanded() ? 'pi-chevron-up' : 'pi-chevron-down'"></i>
2786
+ </div>
2787
+
2788
+ <div class="expand-body" [class.expand-body--open]="expanded()">
2789
+ <div class="expand-body__inner">
2790
+ <div class="flex flex-col gap-4 mt-4">
2791
+ <div class="bg-white/60 dark:bg-surface-800/60 border border-white dark:border-surface-700 rounded-[14px] shadow-sm flex items-center gap-4 px-4 py-2.5 shrink-0">
2792
+ <i class="pi pi-search text-surface-500 dark:text-surface-300 text-sm"></i>
2793
+ <input
2794
+ type="text"
2795
+ [ngModel]="searchQuery()"
2796
+ (ngModelChange)="searchQuery.set($event); page.set(0)"
2797
+ [placeholder]="searchPlaceholder()"
2798
+ class="bg-transparent border-none outline-none flex-1 text-sm font-medium text-deepsea-500 dark:text-surface-0 placeholder:text-surface-700 dark:placeholder:text-surface-300"
2799
+ />
2800
+ </div>
2801
+
2802
+ <div class="flex flex-col gap-3">
2803
+ @for (insight of paginatedInsights(); track insight.id) {
2804
+ <div class="bg-white/70 dark:bg-surface-800/70 border border-white/50 dark:border-surface-700/50 rounded-[14px] shadow-sm p-4 flex gap-3 items-start shrink-0">
2805
+ <i class="pi mt-0.5" [ngClass]="[insight.icon, insight.iconColor]"></i>
2806
+ <div class="flex flex-col gap-2 flex-1 min-w-0">
2807
+ <div class="flex flex-col gap-1">
2808
+ <span class="text-midnight-500 dark:text-surface-0 text-sm font-bold leading-[21px]">{{ insight.title }}</span>
2809
+ <p class="text-midnight-400 dark:text-surface-300 text-sm leading-normal">{{ insight.description }}</p>
2810
+ </div>
2811
+ <button
2812
+ class="flex items-center gap-1.5 text-darkblue-500 dark:text-primary-400 text-sm font-semibold cursor-pointer hover:underline bg-transparent border-none p-0 w-fit"
2813
+ (click)="actionClick.emit(insight)"
2814
+ >
2815
+ {{ insight.actionLabel }}
2816
+ <i class="pi pi-arrow-right text-xs"></i>
2817
+ </button>
2818
+ </div>
2819
+ </div>
2820
+ }
2821
+ </div>
2822
+
2823
+ <div class="shrink-0 w-full border-t border-white/50 dark:border-surface-700/50 pt-2 mt-1 relative z-[1] bg-transparent">
2824
+ <p-paginator
2825
+ [rows]="perPage()"
2826
+ [totalRecords]="filteredInsights().length"
2827
+ [first]="first()"
2828
+ (onPageChange)="page.set($event.page ?? 0)"
2829
+ [pageLinkSize]="3"
2830
+ styleClass="w-full"
2831
+ [pt]="{ root: { class: 'justify-center' } }"
2832
+ />
2833
+ </div>
2834
+ </div>
2835
+ </div>
2836
+ </div>
2837
+ </div>
2838
+ </ux-ai-card-bg>
2839
+ `, isInline: true, styles: [":host{display:flex}\n"], dependencies: [{ kind: "component", type: AiCardBgComponent, selector: "ux-ai-card-bg" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.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$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i2$2.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first", "appendTo"], outputs: ["onPageChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2840
+ }
2841
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AiInsightsCardComponent, decorators: [{
2842
+ type: Component,
2843
+ args: [{ selector: 'ux-ai-insights-card', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AiCardBgComponent, FormsModule, NgClass, PaginatorModule], host: {
2844
+ class: 'ux-ai-insights-card block border border-blue-100 dark:border-midnight-400 rounded-2xl shadow-sm overflow-hidden transition-all duration-300 flex flex-col',
2845
+ '[class.ux-ai-expanded]': 'expanded()'
2846
+ }, template: `
2847
+ <ux-ai-card-bg class="flex flex-col flex-1 p-4">
2848
+ <div class="motion-safe:animate-enter-liquid [animation-delay:80ms] flex flex-col flex-1">
2849
+ <div class="flex items-center justify-between cursor-pointer shrink-0 pr-2" (click)="expanded.set(!expanded())">
2850
+ <div class="flex items-center gap-3">
2851
+ <div class="w-[34px] h-[34px] rounded-[10px] flex items-center justify-center shrink-0">
2852
+ <i class="pi pi-sparkles text-ai-600 dark:text-ai-300"></i>
2853
+ </div>
2854
+ <div class="flex flex-col">
2855
+ <h4 class="title-h4 text-left text-deepsea-500 dark:text-surface-0">{{ title() }}</h4>
2856
+ <span class="text-midnight-700 dark:text-surface-100 text-sm font-medium leading-tight">{{ insights().length }} insights available for your review</span>
2857
+ </div>
2858
+ </div>
2859
+ <i class="pi text-sm text-darkblue-500 dark:text-surface-0" [ngClass]="expanded() ? 'pi-chevron-up' : 'pi-chevron-down'"></i>
2860
+ </div>
2861
+
2862
+ <div class="expand-body" [class.expand-body--open]="expanded()">
2863
+ <div class="expand-body__inner">
2864
+ <div class="flex flex-col gap-4 mt-4">
2865
+ <div class="bg-white/60 dark:bg-surface-800/60 border border-white dark:border-surface-700 rounded-[14px] shadow-sm flex items-center gap-4 px-4 py-2.5 shrink-0">
2866
+ <i class="pi pi-search text-surface-500 dark:text-surface-300 text-sm"></i>
2867
+ <input
2868
+ type="text"
2869
+ [ngModel]="searchQuery()"
2870
+ (ngModelChange)="searchQuery.set($event); page.set(0)"
2871
+ [placeholder]="searchPlaceholder()"
2872
+ class="bg-transparent border-none outline-none flex-1 text-sm font-medium text-deepsea-500 dark:text-surface-0 placeholder:text-surface-700 dark:placeholder:text-surface-300"
2873
+ />
2874
+ </div>
2875
+
2876
+ <div class="flex flex-col gap-3">
2877
+ @for (insight of paginatedInsights(); track insight.id) {
2878
+ <div class="bg-white/70 dark:bg-surface-800/70 border border-white/50 dark:border-surface-700/50 rounded-[14px] shadow-sm p-4 flex gap-3 items-start shrink-0">
2879
+ <i class="pi mt-0.5" [ngClass]="[insight.icon, insight.iconColor]"></i>
2880
+ <div class="flex flex-col gap-2 flex-1 min-w-0">
2881
+ <div class="flex flex-col gap-1">
2882
+ <span class="text-midnight-500 dark:text-surface-0 text-sm font-bold leading-[21px]">{{ insight.title }}</span>
2883
+ <p class="text-midnight-400 dark:text-surface-300 text-sm leading-normal">{{ insight.description }}</p>
2884
+ </div>
2885
+ <button
2886
+ class="flex items-center gap-1.5 text-darkblue-500 dark:text-primary-400 text-sm font-semibold cursor-pointer hover:underline bg-transparent border-none p-0 w-fit"
2887
+ (click)="actionClick.emit(insight)"
2888
+ >
2889
+ {{ insight.actionLabel }}
2890
+ <i class="pi pi-arrow-right text-xs"></i>
2891
+ </button>
2892
+ </div>
2893
+ </div>
2894
+ }
2895
+ </div>
2896
+
2897
+ <div class="shrink-0 w-full border-t border-white/50 dark:border-surface-700/50 pt-2 mt-1 relative z-[1] bg-transparent">
2898
+ <p-paginator
2899
+ [rows]="perPage()"
2900
+ [totalRecords]="filteredInsights().length"
2901
+ [first]="first()"
2902
+ (onPageChange)="page.set($event.page ?? 0)"
2903
+ [pageLinkSize]="3"
2904
+ styleClass="w-full"
2905
+ [pt]="{ root: { class: 'justify-center' } }"
2906
+ />
2907
+ </div>
2908
+ </div>
2909
+ </div>
2910
+ </div>
2911
+ </div>
2912
+ </ux-ai-card-bg>
2913
+ `, styles: [":host{display:flex}\n"] }]
2914
+ }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], insights: [{ type: i0.Input, args: [{ isSignal: true, alias: "insights", required: true }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], actionClick: [{ type: i0.Output, args: ["actionClick"] }] } });
2915
+
2916
+ class CompletionStepsComponent {
2917
+ title = input('Completion Steps', ...(ngDevMode ? [{ debugName: "title" }] : []));
2918
+ steps = input([], ...(ngDevMode ? [{ debugName: "steps" }] : []));
2919
+ mandatory = input({ filled: 0, total: 0 }, ...(ngDevMode ? [{ debugName: "mandatory" }] : []));
2920
+ optional = input({ filled: 0, total: 0 }, ...(ngDevMode ? [{ debugName: "optional" }] : []));
2921
+ totalRecords = input(0, ...(ngDevMode ? [{ debugName: "totalRecords" }] : []));
2922
+ interactive = input(false, ...(ngDevMode ? [{ debugName: "interactive" }] : []));
2923
+ emptyTitle = input('No progress tracked yet', ...(ngDevMode ? [{ debugName: "emptyTitle" }] : []));
2924
+ emptyDescription = input('Progress is calculated automatically as you fill in the opportunity sections. Each dot represents a required or optional field — mandatory fields (bordered red) must be completed before submission.', ...(ngDevMode ? [{ debugName: "emptyDescription" }] : []));
2925
+ stepClick = output();
2926
+ dotStyles = {
2927
+ mandatoryFilled: { bg: 'bg-green-200 dark:bg-green-700', text: 'text-green-800 dark:text-green-50', icon: 'pi', iconClass: 'pi-check' },
2928
+ optionalFilled: { bg: 'bg-blue-200 dark:bg-blue-700', text: 'text-blue-800 dark:text-blue-50', icon: 'material', iconClass: 'info_i' },
2929
+ mandatoryMissing: { bg: 'bg-transparent border-2 border-red-400 dark:border-red-500', text: 'text-red-500 dark:text-red-400', icon: 'pi', iconClass: 'pi-plus' },
2930
+ optionalMissing: { bg: 'bg-transparent border-2 border-surface-300 dark:border-surface-600', text: 'text-surface-500 dark:text-surface-400', icon: 'material', iconClass: 'info_i' }
2931
+ };
2932
+ filledTotal = computed(() => this.mandatory().filled + this.optional().filled, ...(ngDevMode ? [{ debugName: "filledTotal" }] : []));
2933
+ empty = computed(() => this.filledTotal() === 0, ...(ngDevMode ? [{ debugName: "empty" }] : []));
2934
+ legendMandatoryBg = computed(() => this.mandatory().filled > 0 ? this.dotStyles.mandatoryFilled.bg : this.dotStyles.mandatoryMissing.bg, ...(ngDevMode ? [{ debugName: "legendMandatoryBg" }] : []));
2935
+ legendOptionalBg = computed(() => this.optional().filled > 0 ? this.dotStyles.optionalFilled.bg : this.dotStyles.optionalMissing.bg, ...(ngDevMode ? [{ debugName: "legendOptionalBg" }] : []));
2936
+ getDotStyle(step) {
2937
+ if (step.filled)
2938
+ return step.type === 'mandatory' ? this.dotStyles.mandatoryFilled : this.dotStyles.optionalFilled;
2939
+ return step.type === 'mandatory' ? this.dotStyles.mandatoryMissing : this.dotStyles.optionalMissing;
2940
+ }
2941
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: CompletionStepsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2942
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: CompletionStepsComponent, isStandalone: true, selector: "ux-completion-steps", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, steps: { classPropertyName: "steps", publicName: "steps", isSignal: true, isRequired: false, transformFunction: null }, mandatory: { classPropertyName: "mandatory", publicName: "mandatory", isSignal: true, isRequired: false, transformFunction: null }, optional: { classPropertyName: "optional", publicName: "optional", isSignal: true, isRequired: false, transformFunction: null }, totalRecords: { classPropertyName: "totalRecords", publicName: "totalRecords", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, emptyTitle: { classPropertyName: "emptyTitle", publicName: "emptyTitle", isSignal: true, isRequired: false, transformFunction: null }, emptyDescription: { classPropertyName: "emptyDescription", publicName: "emptyDescription", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { stepClick: "stepClick" }, host: { classAttribute: "block" }, ngImport: i0, template: `
2943
+ <div class="card flex flex-col gap-4">
2944
+ <div class="flex items-center justify-between">
2945
+ <div class="flex items-center gap-3">
2946
+ <div class="flex flex-col">
2947
+ <span class="text-xs font-semibold text-surface-600 dark:text-surface-300 uppercase tracking-wide">{{ title() }}</span>
2948
+ </div>
2949
+ </div>
2950
+ <span class="text-2xl font-bold" [class]="filledTotal() > 0 ? 'text-surface-900 dark:text-surface-0' : 'text-surface-500 dark:text-surface-400'">{{ filledTotal() }}/{{ totalRecords() }}</span>
2951
+ </div>
2952
+
2953
+ <div class="flex items-center gap-1 flex-wrap">
2954
+ @for (step of steps(); track $index) {
2955
+ <span class="inline-flex items-center justify-center w-6 h-6 rounded-full"
2956
+ [class.cursor-pointer]="interactive()"
2957
+ [class]="getDotStyle(step).bg"
2958
+ [pTooltip]="step.name + (step.filled ? '' : ' (missing)')" tooltipPosition="top"
2959
+ (click)="interactive() && stepClick.emit($index)">
2960
+ @if (getDotStyle(step).icon === 'pi') {
2961
+ <i class="pi text-[3px]" [class]="getDotStyle(step).iconClass + ' ' + getDotStyle(step).text"></i>
2962
+ } @else if (getDotStyle(step).icon === 'material') {
2963
+ <span class="material-symbols-outlined leading-none" style="font-size:20px;transform:scale(0.9)" [class]="getDotStyle(step).text">{{ getDotStyle(step).iconClass }}</span>
2964
+ } @else {
2965
+ <span class="text-sm font-black leading-none" [class]="getDotStyle(step).text">!</span>
2966
+ }
2967
+ </span>
2968
+ }
2969
+ </div>
2970
+
2971
+ <div class="flex flex-wrap items-center gap-x-6 gap-y-2 mt-1">
2972
+ <div class="flex items-center gap-2">
2973
+ <span class="inline-block w-4 h-4 rounded-full shrink-0" [class]="legendMandatoryBg()"></span>
2974
+ <span class="text-sm text-surface-600 dark:text-surface-300">Mandatory:</span>
2975
+ <span class="text-sm font-semibold text-surface-900 dark:text-surface-0">{{ mandatory().filled }}/{{ mandatory().total }}</span>
2976
+ </div>
2977
+ <div class="flex items-center gap-2">
2978
+ <span class="inline-block w-4 h-4 rounded-full shrink-0" [class]="legendOptionalBg()"></span>
2979
+ <span class="text-sm text-surface-600 dark:text-surface-300">Optional:</span>
2980
+ <span class="text-sm font-semibold text-surface-900 dark:text-surface-0">{{ optional().filled }}/{{ optional().total }}</span>
2981
+ </div>
2982
+ <div class="flex items-center gap-2">
2983
+ <span class="text-sm text-surface-600 dark:text-surface-300">Total:</span>
2984
+ <span class="text-sm font-semibold text-surface-900 dark:text-surface-0">{{ totalRecords() }}</span>
2985
+ </div>
2986
+ </div>
2987
+
2988
+ @if (empty()) {
2989
+ <div class="empty-state">
2990
+ <i class="pi pi-chart-bar text-3xl text-surface-500 dark:text-surface-400"></i>
2991
+ <span class="empty-state-title">{{ emptyTitle() }}</span>
2992
+ <span class="empty-state-desc">{{ emptyDescription() }}</span>
2993
+ </div>
2994
+ }
2995
+ </div>
2996
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3$3.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2997
+ }
2998
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: CompletionStepsComponent, decorators: [{
2999
+ type: Component,
3000
+ args: [{
3001
+ selector: 'ux-completion-steps',
3002
+ changeDetection: ChangeDetectionStrategy.OnPush,
3003
+ imports: [TooltipModule],
3004
+ host: { class: 'block' },
3005
+ template: `
3006
+ <div class="card flex flex-col gap-4">
3007
+ <div class="flex items-center justify-between">
3008
+ <div class="flex items-center gap-3">
3009
+ <div class="flex flex-col">
3010
+ <span class="text-xs font-semibold text-surface-600 dark:text-surface-300 uppercase tracking-wide">{{ title() }}</span>
3011
+ </div>
3012
+ </div>
3013
+ <span class="text-2xl font-bold" [class]="filledTotal() > 0 ? 'text-surface-900 dark:text-surface-0' : 'text-surface-500 dark:text-surface-400'">{{ filledTotal() }}/{{ totalRecords() }}</span>
3014
+ </div>
3015
+
3016
+ <div class="flex items-center gap-1 flex-wrap">
3017
+ @for (step of steps(); track $index) {
3018
+ <span class="inline-flex items-center justify-center w-6 h-6 rounded-full"
3019
+ [class.cursor-pointer]="interactive()"
3020
+ [class]="getDotStyle(step).bg"
3021
+ [pTooltip]="step.name + (step.filled ? '' : ' (missing)')" tooltipPosition="top"
3022
+ (click)="interactive() && stepClick.emit($index)">
3023
+ @if (getDotStyle(step).icon === 'pi') {
3024
+ <i class="pi text-[3px]" [class]="getDotStyle(step).iconClass + ' ' + getDotStyle(step).text"></i>
3025
+ } @else if (getDotStyle(step).icon === 'material') {
3026
+ <span class="material-symbols-outlined leading-none" style="font-size:20px;transform:scale(0.9)" [class]="getDotStyle(step).text">{{ getDotStyle(step).iconClass }}</span>
3027
+ } @else {
3028
+ <span class="text-sm font-black leading-none" [class]="getDotStyle(step).text">!</span>
3029
+ }
3030
+ </span>
3031
+ }
3032
+ </div>
3033
+
3034
+ <div class="flex flex-wrap items-center gap-x-6 gap-y-2 mt-1">
3035
+ <div class="flex items-center gap-2">
3036
+ <span class="inline-block w-4 h-4 rounded-full shrink-0" [class]="legendMandatoryBg()"></span>
3037
+ <span class="text-sm text-surface-600 dark:text-surface-300">Mandatory:</span>
3038
+ <span class="text-sm font-semibold text-surface-900 dark:text-surface-0">{{ mandatory().filled }}/{{ mandatory().total }}</span>
3039
+ </div>
3040
+ <div class="flex items-center gap-2">
3041
+ <span class="inline-block w-4 h-4 rounded-full shrink-0" [class]="legendOptionalBg()"></span>
3042
+ <span class="text-sm text-surface-600 dark:text-surface-300">Optional:</span>
3043
+ <span class="text-sm font-semibold text-surface-900 dark:text-surface-0">{{ optional().filled }}/{{ optional().total }}</span>
3044
+ </div>
3045
+ <div class="flex items-center gap-2">
3046
+ <span class="text-sm text-surface-600 dark:text-surface-300">Total:</span>
3047
+ <span class="text-sm font-semibold text-surface-900 dark:text-surface-0">{{ totalRecords() }}</span>
3048
+ </div>
3049
+ </div>
3050
+
3051
+ @if (empty()) {
3052
+ <div class="empty-state">
3053
+ <i class="pi pi-chart-bar text-3xl text-surface-500 dark:text-surface-400"></i>
3054
+ <span class="empty-state-title">{{ emptyTitle() }}</span>
3055
+ <span class="empty-state-desc">{{ emptyDescription() }}</span>
3056
+ </div>
3057
+ }
3058
+ </div>
3059
+ `
3060
+ }]
3061
+ }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], steps: [{ type: i0.Input, args: [{ isSignal: true, alias: "steps", required: false }] }], mandatory: [{ type: i0.Input, args: [{ isSignal: true, alias: "mandatory", required: false }] }], optional: [{ type: i0.Input, args: [{ isSignal: true, alias: "optional", required: false }] }], totalRecords: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalRecords", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], emptyTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyTitle", required: false }] }], emptyDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyDescription", required: false }] }], stepClick: [{ type: i0.Output, args: ["stepClick"] }] } });
3062
+
3063
+ /**
3064
+ * Structural directive that marks a template as content for a specific tab
3065
+ * inside `<ux-detail-layout>`.
3066
+ *
3067
+ * Usage: `<ng-template uxDetailTab="overview">...content...</ng-template>`
3068
+ */
3069
+ class DetailTabDirective {
3070
+ templateRef;
3071
+ uxDetailTab = input.required(...(ngDevMode ? [{ debugName: "uxDetailTab" }] : []));
3072
+ constructor(templateRef) {
3073
+ this.templateRef = templateRef;
3074
+ }
3075
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: DetailTabDirective, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive });
3076
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.0.8", type: DetailTabDirective, isStandalone: true, selector: "[uxDetailTab]", inputs: { uxDetailTab: { classPropertyName: "uxDetailTab", publicName: "uxDetailTab", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
3077
+ }
3078
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: DetailTabDirective, decorators: [{
3079
+ type: Directive,
3080
+ args: [{ selector: '[uxDetailTab]' }]
3081
+ }], ctorParameters: () => [{ type: i0.TemplateRef }], propDecorators: { uxDetailTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "uxDetailTab", required: true }] }] } });
3082
+ /**
3083
+ * Reusable detail-page layout shell: sticky header, tabbed main column,
3084
+ * and a persistent right sidebar (typically for AI insights).
3085
+ *
3086
+ * All styling derives from the active PrimeNG brand preset (BrandSoft / BrandCrisp /
3087
+ * BrandContrast) via `--p-*` CSS variables and Tailwind `surface-*` / `primary-*`
3088
+ * utilities (resolved by `tailwindcss-primeui`). No hardcoded colors.
3089
+ *
3090
+ * ```html
3091
+ * <ux-detail-layout [tabs]="myTabs" [(activeTab)]="currentTab">
3092
+ * <ng-container ux-detail-header>
3093
+ * ...sticky header content...
3094
+ * </ng-container>
3095
+ *
3096
+ * <ng-template uxDetailTab="overview">...tab 1...</ng-template>
3097
+ * <ng-template uxDetailTab="scope">...tab 2...</ng-template>
3098
+ *
3099
+ * <ng-container ux-detail-sidebar>
3100
+ * <!-- Use ng-container so children become direct children of the
3101
+ * library's flex container and inherit the gap spacing. -->
3102
+ * ...right sidebar (AI card, documents, etc.)...
3103
+ * </ng-container>
3104
+ *
3105
+ * </ux-detail-layout>
3106
+ * ```
3107
+ */
3108
+ class DetailLayoutComponent {
3109
+ /** Tab definitions for the main content area. */
3110
+ tabs = input.required(...(ngDevMode ? [{ debugName: "tabs" }] : []));
3111
+ /** Currently active tab value (two-way bindable). */
3112
+ activeTab = model('', ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
3113
+ /** Options for the mobile tab dropdown. */
3114
+ tabOptions = computed(() => this.tabs().map(t => ({ label: t.label, value: t.value })), ...(ngDevMode ? [{ debugName: "tabOptions" }] : []));
3115
+ /** True when there is only a single tab, making the tab bar redundant. */
3116
+ singleTab = computed(() => this.tabs().length <= 1, ...(ngDevMode ? [{ debugName: "singleTab" }] : []));
3117
+ /** True when viewport is below the lg breakpoint (1024px). */
3118
+ isMobile = signal(false, ...(ngDevMode ? [{ debugName: "isMobile" }] : []));
3119
+ /** True once the scrollable body has been scrolled past the threshold. */
3120
+ scrolled = signal(false, ...(ngDevMode ? [{ debugName: "scrolled" }] : []));
3121
+ constructor() {
3122
+ if (isPlatformBrowser(inject(PLATFORM_ID))) {
3123
+ const mql = window.matchMedia('(max-width: 1023px)');
3124
+ this.isMobile.set(mql.matches);
3125
+ const handler = (e) => this.isMobile.set(e.matches);
3126
+ mql.addEventListener('change', handler);
3127
+ inject(DestroyRef).onDestroy(() => mql.removeEventListener('change', handler));
3128
+ }
3129
+ }
3130
+ /** Tab content templates provided by the consumer. */
3131
+ tabTemplates;
3132
+ getTabTemplate(value) {
3133
+ const match = this.tabTemplates?.find(t => t.uxDetailTab() === value);
3134
+ return match?.templateRef ?? null;
3135
+ }
3136
+ onScroll(event) {
3137
+ const el = event.target;
3138
+ this.scrolled.set(el.scrollTop > 10);
3139
+ }
3140
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: DetailLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3141
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: DetailLayoutComponent, isStandalone: true, selector: "ux-detail-layout", inputs: { tabs: { classPropertyName: "tabs", publicName: "tabs", isSignal: true, isRequired: true, transformFunction: null }, activeTab: { classPropertyName: "activeTab", publicName: "activeTab", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeTab: "activeTabChange" }, host: { classAttribute: "ux-detail-layout" }, queries: [{ propertyName: "tabTemplates", predicate: DetailTabDirective }], ngImport: i0, template: `
3142
+ <div class="flex flex-col flex-1 min-h-0">
3143
+
3144
+ <!-- Sticky header (projected) -->
3145
+ <div class="ux-dl__header flex-shrink-0 z-10">
3146
+ <div>
3147
+ <ng-content select="[ux-detail-header]" />
3148
+ </div>
3149
+ <div class="ux-dl__header-meta" [class.ux-dl__header-meta--hidden]="scrolled()">
3150
+ <ng-content select="[ux-detail-header-meta]" />
3151
+ </div>
3152
+ </div>
3153
+
3154
+ <!-- p-tabs wraps tablist + scrollable panels for state binding -->
3155
+ <p-tabs class="flex flex-col flex-1 min-h-0" [value]="activeTab()" (valueChange)="activeTab.set($event + '')">
3156
+
3157
+ @if (!singleTab()) {
3158
+ <!-- Mobile: dropdown selector -->
3159
+ @if (isMobile()) {
3160
+ <div class="ux-dl__mobile-tabs">
3161
+ <p-select
3162
+ [options]="tabOptions()"
3163
+ [ngModel]="activeTab()"
3164
+ (ngModelChange)="activeTab.set($event)"
3165
+ optionLabel="label"
3166
+ optionValue="value"
3167
+ styleClass="w-full ux-dl__mobile-select"
3168
+ />
3169
+ </div>
3170
+ }
3171
+
3172
+ <!-- Desktop: horizontal tab bar (outside scroll → stays fixed below header) -->
3173
+ <p-tablist
3174
+ class="flex-shrink-0 ux-dl__tablist"
3175
+ [style.display]="isMobile() ? 'none' : null"
3176
+ [pt]="{ content: { class: 'w-full' }, tabList: { class: 'w-full pl-8 p-0' } }"
3177
+ >
3178
+ @for (tab of tabs(); track tab.value) {
3179
+ <p-tab [value]="tab.value" [pt]="{ root: { class: 'flex-1 justify-center' } }">
3180
+ @if (tab.icon) {
3181
+ <i [class]="tab.icon" class="mr-2 text-sm"></i>
3182
+ }
3183
+ {{ tab.label }}
3184
+ </p-tab>
3185
+ }
3186
+ </p-tablist>
3187
+ }
3188
+
3189
+ <!-- Scrollable body (only content scrolls, tabs stay above) -->
3190
+ <div class="flex flex-col flex-1 min-h-0 overflow-y-auto overflow-x-hidden ux-dl__scroll"
3191
+ (scroll)="onScroll($event)">
3192
+
3193
+ <!-- Content + Sidebar row -->
3194
+ <div class="flex flex-col lg:flex-row items-start gap-6 w-full py-4 lg:py-6">
3195
+
3196
+ <!-- Main column: tab panels -->
3197
+ <div class="w-full flex-1 min-w-0 flex flex-col gap-6">
3198
+ <p-tabpanels>
3199
+ @for (tab of tabs(); track tab.value) {
3200
+ <p-tabpanel [value]="tab.value">
3201
+ <div class="flex flex-col gap-6">
3202
+ @if (getTabTemplate(tab.value); as tmpl) {
3203
+ <ng-container [ngTemplateOutlet]="tmpl" />
3204
+ }
3205
+ </div>
3206
+ </p-tabpanel>
3207
+ }
3208
+ </p-tabpanels>
3209
+ </div>
3210
+
3211
+ <!-- Sidebar -->
3212
+ <aside class="w-full lg:w-[380px] shrink-0 flex flex-col lg:sticky lg:top-4 lg:self-start lg:pb-8">
3213
+ <div class="ux-dl__sidebar-inner w-full">
3214
+ <ng-content select="[ux-detail-sidebar]" />
3215
+ </div>
3216
+ </aside>
3217
+ </div>
3218
+
3219
+ </div>
3220
+
3221
+ </p-tabs>
3222
+
3223
+ </div>
3224
+ `, isInline: true, styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0;font-family:var(--p-font-family, \"Noto Sans\", sans-serif);background:transparent;color:var(--p-text-color)}.ux-dl__header{background:transparent}.ux-dl__scroll{scrollbar-width:thin;scrollbar-color:color-mix(in srgb,var(--p-primary-color) 25%,transparent) color-mix(in srgb,var(--p-surface-500) 8%,transparent)}.ux-dl__scroll::-webkit-scrollbar{width:10px;height:10px}.ux-dl__scroll::-webkit-scrollbar-track{background:color-mix(in srgb,var(--p-surface-500) 8%,transparent);border-radius:var(--p-content-border-radius, .375rem)}.ux-dl__scroll::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--p-primary-color) 25%,transparent);border-radius:var(--p-content-border-radius, .375rem)}.ux-dl__scroll::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb,var(--p-primary-color) 45%,transparent)}.ux-dl__sidebar-inner{display:flex;flex-direction:column;gap:1.5rem}.ux-dl__header-meta{overflow:hidden;max-height:80px;opacity:1;transition:max-height .25s ease-out,opacity .2s ease-out}.ux-dl__header-meta--hidden{max-height:0;opacity:0}.ux-dl__tablist{display:flex;overflow:hidden;background:var(--p-surface-950);padding-inline:.75rem}@media screen and (min-width:640px){.ux-dl__tablist{padding-inline:1rem}}@media screen and (min-width:1024px){.ux-dl__tablist{padding-inline:1.5rem}}.ux-dl__mobile-tabs{position:sticky;top:0;z-index:5}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i3$1.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: TabsModule }, { kind: "component", type: i4$3.Tabs, selector: "p-tabs", inputs: ["value", "scrollable", "lazy", "selectOnFocus", "showNavigators", "tabindex"], outputs: ["valueChange"] }, { kind: "component", type: i4$3.TabPanels, selector: "p-tabpanels" }, { kind: "component", type: i4$3.TabPanel, selector: "p-tabpanel", inputs: ["lazy", "value"], outputs: ["valueChange"] }, { kind: "component", type: i4$3.TabList, selector: "p-tablist" }, { kind: "component", type: i4$3.Tab, selector: "p-tab", inputs: ["value", "disabled"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3225
+ }
3226
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: DetailLayoutComponent, decorators: [{
3227
+ type: Component,
3228
+ args: [{ selector: 'ux-detail-layout', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, FormsModule, SelectModule, TabsModule], host: { class: 'ux-detail-layout' }, template: `
3229
+ <div class="flex flex-col flex-1 min-h-0">
3230
+
3231
+ <!-- Sticky header (projected) -->
3232
+ <div class="ux-dl__header flex-shrink-0 z-10">
3233
+ <div>
3234
+ <ng-content select="[ux-detail-header]" />
3235
+ </div>
3236
+ <div class="ux-dl__header-meta" [class.ux-dl__header-meta--hidden]="scrolled()">
3237
+ <ng-content select="[ux-detail-header-meta]" />
3238
+ </div>
3239
+ </div>
3240
+
3241
+ <!-- p-tabs wraps tablist + scrollable panels for state binding -->
3242
+ <p-tabs class="flex flex-col flex-1 min-h-0" [value]="activeTab()" (valueChange)="activeTab.set($event + '')">
3243
+
3244
+ @if (!singleTab()) {
3245
+ <!-- Mobile: dropdown selector -->
3246
+ @if (isMobile()) {
3247
+ <div class="ux-dl__mobile-tabs">
3248
+ <p-select
3249
+ [options]="tabOptions()"
3250
+ [ngModel]="activeTab()"
3251
+ (ngModelChange)="activeTab.set($event)"
3252
+ optionLabel="label"
3253
+ optionValue="value"
3254
+ styleClass="w-full ux-dl__mobile-select"
3255
+ />
3256
+ </div>
3257
+ }
3258
+
3259
+ <!-- Desktop: horizontal tab bar (outside scroll → stays fixed below header) -->
3260
+ <p-tablist
3261
+ class="flex-shrink-0 ux-dl__tablist"
3262
+ [style.display]="isMobile() ? 'none' : null"
3263
+ [pt]="{ content: { class: 'w-full' }, tabList: { class: 'w-full pl-8 p-0' } }"
3264
+ >
3265
+ @for (tab of tabs(); track tab.value) {
3266
+ <p-tab [value]="tab.value" [pt]="{ root: { class: 'flex-1 justify-center' } }">
3267
+ @if (tab.icon) {
3268
+ <i [class]="tab.icon" class="mr-2 text-sm"></i>
3269
+ }
3270
+ {{ tab.label }}
3271
+ </p-tab>
3272
+ }
3273
+ </p-tablist>
3274
+ }
3275
+
3276
+ <!-- Scrollable body (only content scrolls, tabs stay above) -->
3277
+ <div class="flex flex-col flex-1 min-h-0 overflow-y-auto overflow-x-hidden ux-dl__scroll"
3278
+ (scroll)="onScroll($event)">
3279
+
3280
+ <!-- Content + Sidebar row -->
3281
+ <div class="flex flex-col lg:flex-row items-start gap-6 w-full py-4 lg:py-6">
3282
+
3283
+ <!-- Main column: tab panels -->
3284
+ <div class="w-full flex-1 min-w-0 flex flex-col gap-6">
3285
+ <p-tabpanels>
3286
+ @for (tab of tabs(); track tab.value) {
3287
+ <p-tabpanel [value]="tab.value">
3288
+ <div class="flex flex-col gap-6">
3289
+ @if (getTabTemplate(tab.value); as tmpl) {
3290
+ <ng-container [ngTemplateOutlet]="tmpl" />
3291
+ }
3292
+ </div>
3293
+ </p-tabpanel>
3294
+ }
3295
+ </p-tabpanels>
3296
+ </div>
3297
+
3298
+ <!-- Sidebar -->
3299
+ <aside class="w-full lg:w-[380px] shrink-0 flex flex-col lg:sticky lg:top-4 lg:self-start lg:pb-8">
3300
+ <div class="ux-dl__sidebar-inner w-full">
3301
+ <ng-content select="[ux-detail-sidebar]" />
3302
+ </div>
3303
+ </aside>
3304
+ </div>
3305
+
3306
+ </div>
3307
+
3308
+ </p-tabs>
3309
+
3310
+ </div>
3311
+ `, styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0;font-family:var(--p-font-family, \"Noto Sans\", sans-serif);background:transparent;color:var(--p-text-color)}.ux-dl__header{background:transparent}.ux-dl__scroll{scrollbar-width:thin;scrollbar-color:color-mix(in srgb,var(--p-primary-color) 25%,transparent) color-mix(in srgb,var(--p-surface-500) 8%,transparent)}.ux-dl__scroll::-webkit-scrollbar{width:10px;height:10px}.ux-dl__scroll::-webkit-scrollbar-track{background:color-mix(in srgb,var(--p-surface-500) 8%,transparent);border-radius:var(--p-content-border-radius, .375rem)}.ux-dl__scroll::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--p-primary-color) 25%,transparent);border-radius:var(--p-content-border-radius, .375rem)}.ux-dl__scroll::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb,var(--p-primary-color) 45%,transparent)}.ux-dl__sidebar-inner{display:flex;flex-direction:column;gap:1.5rem}.ux-dl__header-meta{overflow:hidden;max-height:80px;opacity:1;transition:max-height .25s ease-out,opacity .2s ease-out}.ux-dl__header-meta--hidden{max-height:0;opacity:0}.ux-dl__tablist{display:flex;overflow:hidden;background:var(--p-surface-950);padding-inline:.75rem}@media screen and (min-width:640px){.ux-dl__tablist{padding-inline:1rem}}@media screen and (min-width:1024px){.ux-dl__tablist{padding-inline:1.5rem}}.ux-dl__mobile-tabs{position:sticky;top:0;z-index:5}\n"] }]
3312
+ }], ctorParameters: () => [], propDecorators: { tabs: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabs", required: true }] }], activeTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeTab", required: false }] }, { type: i0.Output, args: ["activeTabChange"] }], tabTemplates: [{
3313
+ type: ContentChildren,
3314
+ args: [DetailTabDirective]
3315
+ }] } });
3316
+
3317
+ /**
3318
+ * Horizontal row of pill-shaped sub-tab buttons. Styling aligns with sidebar menu item
3319
+ * hover/active tokens (`--d-menuitem-*`).
3320
+ */
3321
+ class PillTabsComponent {
3322
+ items = input.required(...(ngDevMode ? [{ debugName: "items" }] : []));
3323
+ activeValue = model('', ...(ngDevMode ? [{ debugName: "activeValue" }] : []));
3324
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: PillTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3325
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: PillTabsComponent, isStandalone: true, selector: "ux-pill-tabs", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, activeValue: { classPropertyName: "activeValue", publicName: "activeValue", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeValue: "activeValueChange" }, host: { classAttribute: "ux-pill-tabs block" }, ngImport: i0, template: `
3326
+ <div class="ux-pill-tabs__row" role="tablist">
3327
+ @for (item of items(); track item.value) {
3328
+ <button
3329
+ type="button"
3330
+ class="ux-pill-tabs__btn"
3331
+ role="tab"
3332
+ [attr.aria-selected]="activeValue() === item.value"
3333
+ [class.ux-pill-tabs__btn--active]="activeValue() === item.value"
3334
+ (click)="activeValue.set(item.value)"
3335
+ >
3336
+ {{ item.label }}
3337
+ </button>
3338
+ }
3339
+ </div>
3340
+ `, isInline: true, styles: [".ux-pill-tabs__row{display:flex;flex-wrap:wrap;gap:.5rem}.ux-pill-tabs__btn{border-radius:9999px;padding:.375rem .875rem;font-size:var(--p-font-size, .875rem);font-weight:600;font-family:inherit;cursor:pointer;transition:all .15s ease;border:1px solid var(--p-primary-800);background:transparent;color:var(--p-text-muted-color)}:host-context(.app-dark) .ux-pill-tabs__btn:not(.ux-pill-tabs__btn--active){border-color:var(--p-surface-700)}.ux-pill-tabs__btn:hover:not(.ux-pill-tabs__btn--active){background:var(--d-menuitem-hover-bg);color:var(--p-primary-950)}:host-context(.app-dark) .ux-pill-tabs__btn:hover:not(.ux-pill-tabs__btn--active){color:var(--p-primary-200)}.ux-pill-tabs__btn--active{background:var(--p-primary-200);border-color:transparent;color:var(--p-primary-950)}:host-context(.app-dark) .ux-pill-tabs__btn--active{color:var(--p-primary-950)}.ux-pill-tabs__btn:focus{outline:none;box-shadow:none}.ux-pill-tabs__btn:focus-visible{outline:none;box-shadow:var(--d-menuitem-focus-shadow)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3341
+ }
3342
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: PillTabsComponent, decorators: [{
3343
+ type: Component,
3344
+ args: [{ selector: 'ux-pill-tabs', changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'ux-pill-tabs block' }, template: `
3345
+ <div class="ux-pill-tabs__row" role="tablist">
3346
+ @for (item of items(); track item.value) {
3347
+ <button
3348
+ type="button"
3349
+ class="ux-pill-tabs__btn"
3350
+ role="tab"
3351
+ [attr.aria-selected]="activeValue() === item.value"
3352
+ [class.ux-pill-tabs__btn--active]="activeValue() === item.value"
3353
+ (click)="activeValue.set(item.value)"
3354
+ >
3355
+ {{ item.label }}
3356
+ </button>
3357
+ }
3358
+ </div>
3359
+ `, styles: [".ux-pill-tabs__row{display:flex;flex-wrap:wrap;gap:.5rem}.ux-pill-tabs__btn{border-radius:9999px;padding:.375rem .875rem;font-size:var(--p-font-size, .875rem);font-weight:600;font-family:inherit;cursor:pointer;transition:all .15s ease;border:1px solid var(--p-primary-800);background:transparent;color:var(--p-text-muted-color)}:host-context(.app-dark) .ux-pill-tabs__btn:not(.ux-pill-tabs__btn--active){border-color:var(--p-surface-700)}.ux-pill-tabs__btn:hover:not(.ux-pill-tabs__btn--active){background:var(--d-menuitem-hover-bg);color:var(--p-primary-950)}:host-context(.app-dark) .ux-pill-tabs__btn:hover:not(.ux-pill-tabs__btn--active){color:var(--p-primary-200)}.ux-pill-tabs__btn--active{background:var(--p-primary-200);border-color:transparent;color:var(--p-primary-950)}:host-context(.app-dark) .ux-pill-tabs__btn--active{color:var(--p-primary-950)}.ux-pill-tabs__btn:focus{outline:none;box-shadow:none}.ux-pill-tabs__btn:focus-visible{outline:none;box-shadow:var(--d-menuitem-focus-shadow)}\n"] }]
3360
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], activeValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeValue", required: false }] }, { type: i0.Output, args: ["activeValueChange"] }] } });
3361
+
3362
+ class DocumentsCardComponent {
3363
+ documents = input([], ...(ngDevMode ? [{ debugName: "documents" }] : []));
3364
+ rows = input(5, ...(ngDevMode ? [{ debugName: "rows" }] : []));
3365
+ expanded = signal(false, ...(ngDevMode ? [{ debugName: "expanded" }] : []));
3366
+ activeFilter = signal('All Files', ...(ngDevMode ? [{ debugName: "activeFilter" }] : []));
3367
+ searchQuery = model('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : []));
3368
+ fileTypes = computed(() => {
3369
+ const types = [...new Set(this.documents().map(d => d.type))];
3370
+ types.sort();
3371
+ return types;
3372
+ }, ...(ngDevMode ? [{ debugName: "fileTypes" }] : []));
3373
+ filterOptions = computed(() => ['All Files', ...this.fileTypes(), 'Other'], ...(ngDevMode ? [{ debugName: "filterOptions" }] : []));
3374
+ pillTabItems = computed(() => this.filterOptions().map(f => ({ value: f, label: f })), ...(ngDevMode ? [{ debugName: "pillTabItems" }] : []));
3375
+ summary = computed(() => {
3376
+ const docs = this.documents();
3377
+ const count = docs.length;
3378
+ if (count === 0)
3379
+ return 'No files attached';
3380
+ const types = this.fileTypes();
3381
+ const fileWord = count === 1 ? 'file' : 'files';
3382
+ if (types.length === 0)
3383
+ return `${count} ${fileWord} attached`;
3384
+ return `${count} ${fileWord} · ${types.join(', ')}`;
3385
+ }, ...(ngDevMode ? [{ debugName: "summary" }] : []));
3386
+ filtered = computed(() => {
3387
+ let docs = this.documents();
3388
+ const query = this.searchQuery().trim().toLowerCase();
3389
+ if (query) {
3390
+ docs = docs.filter(d => d.fileName.toLowerCase().includes(query) || d.owner.toLowerCase().includes(query));
3391
+ }
3392
+ const filter = this.activeFilter();
3393
+ if (filter === 'All Files')
3394
+ return docs;
3395
+ if (filter === 'Other') {
3396
+ const knownTypes = this.fileTypes();
3397
+ return docs.filter(d => !knownTypes.includes(d.type));
3398
+ }
3399
+ return docs.filter(d => d.type === filter);
3400
+ }, ...(ngDevMode ? [{ debugName: "filtered" }] : []));
3401
+ menuItems = [];
3402
+ onMenuToggle(event, _doc, menu) {
3403
+ this.menuItems = [
3404
+ { label: 'Preview', icon: 'pi pi-eye' },
3405
+ { label: 'Share', icon: 'pi pi-share-alt' },
3406
+ { separator: true },
3407
+ { label: 'Delete', icon: 'pi pi-trash', styleClass: 'text-red-500' }
3408
+ ];
3409
+ menu.toggle(event);
3410
+ }
3411
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: DocumentsCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3412
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: DocumentsCardComponent, isStandalone: true, selector: "ux-documents-card", inputs: { documents: { classPropertyName: "documents", publicName: "documents", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, searchQuery: { classPropertyName: "searchQuery", publicName: "searchQuery", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { searchQuery: "searchQueryChange" }, host: { classAttribute: "ux-documents-card block" }, ngImport: i0, template: `
3413
+ <div class="card flex flex-col overflow-hidden">
3414
+ <p-panel [toggleable]="true" [collapsed]="!expanded()" (collapsedChange)="expanded.set(!$event)" toggler="header">
3415
+ <ng-template #headericons let-collapsed>
3416
+ <i class="pi text-sm text-darkblue-500 dark:text-surface-0" [ngClass]="collapsed ? 'pi-chevron-down' : 'pi-chevron-up'"></i>
3417
+ </ng-template>
3418
+ <ng-template #header>
3419
+ <div class="flex items-center gap-3 flex-1">
3420
+ <div class="w-[34px] h-[34px] rounded-[10px] flex items-center justify-center shrink-0">
3421
+ <i class="pi pi-folder text-deepsea-500 dark:text-surface-0"></i>
3422
+ </div>
3423
+ <div class="flex flex-col">
3424
+ <h4 class="title-h4 text-left text-deepsea-500 dark:text-surface-0">Documents</h4>
3425
+ <span class="text-surface-500 dark:text-surface-300 text-sm font-medium leading-tight">{{ summary() }}</span>
3426
+ </div>
3427
+ </div>
3428
+ </ng-template>
3429
+ <div class="flex flex-col gap-4 pt-4">
3430
+ <ux-pill-tabs [items]="pillTabItems()" [(activeValue)]="activeFilter" />
3431
+
3432
+ <p-iconfield>
3433
+ <p-inputicon class="pi pi-search" />
3434
+ <input pInputText [(ngModel)]="searchQuery" placeholder="Search documents" class="w-full" />
3435
+ </p-iconfield>
3436
+
3437
+ @if (filtered().length > 0) {
3438
+ <p-table
3439
+ [value]="filtered()"
3440
+ [paginator]="true"
3441
+ [rows]="rows()"
3442
+ sortMode="multiple"
3443
+ styleClass="flex flex-col rounded-2xl overflow-hidden [&>[data-pc-section=paginatorcontainer]]:border-0! [&>[data-pc-section=paginatorcontainer]]:mt-auto [&_[data-pc-name=pcpaginator]]:rounded-none!"
3444
+ tableStyleClass="w-full table-fixed"
3445
+ paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink"
3446
+ >
3447
+ <ng-template #header>
3448
+ <tr>
3449
+ <th pSortableColumn="fileName" class="w-1/2">File Name <p-sortIcon field="fileName" /></th>
3450
+ <th pSortableColumn="type" class="w-1/4">Type <p-sortIcon field="type" /></th>
3451
+ <th class="w-1/4">Actions</th>
3452
+ </tr>
3453
+ </ng-template>
3454
+ <ng-template #body let-doc>
3455
+ <tr>
3456
+ <td>
3457
+ <div class="flex items-center gap-3 py-1 min-w-0">
3458
+ <i class="pi text-xl text-surface-600 dark:text-surface-300 shrink-0" [ngClass]="doc.icon"></i>
3459
+ <span class="text-surface-700 dark:text-surface-100 text-sm truncate">{{ doc.fileName }}</span>
3460
+ </div>
3461
+ </td>
3462
+ <td>
3463
+ <p-tag [value]="doc.type" styleClass="px-2 py-1" />
3464
+ </td>
3465
+ <td>
3466
+ <div class="flex items-center gap-1">
3467
+ <p-button icon="pi pi-download" [rounded]="true" [text]="true" size="small" severity="secondary" styleClass="cursor-pointer" ariaLabel="Download" />
3468
+ <p-button icon="pi pi-ellipsis-h" [rounded]="true" [text]="true" size="small" severity="secondary" styleClass="cursor-pointer" ariaLabel="More options" (onClick)="onMenuToggle($event, doc, docMenu)" />
3469
+ <p-menu #docMenu [model]="menuItems" [popup]="true" styleClass="w-48" appendTo="body" />
3470
+ </div>
3471
+ </td>
3472
+ </tr>
3473
+ </ng-template>
3474
+ </p-table>
3475
+ } @else {
3476
+ <div class="flex flex-col items-center gap-3 py-8 text-center">
3477
+ <i class="pi pi-folder-open text-3xl text-surface-300 dark:text-surface-500"></i>
3478
+ <span class="text-surface-600 dark:text-surface-300 text-sm">No documents to show</span>
3479
+ </div>
3480
+ }
3481
+
3482
+ <p-fileupload
3483
+ name="documents[]"
3484
+ [multiple]="true"
3485
+ maxFileSize="10000000"
3486
+ mode="advanced"
3487
+ [auto]="false"
3488
+ chooseLabel="Upload File"
3489
+ chooseIcon="pi pi-upload"
3490
+ [showUploadButton]="false"
3491
+ [showCancelButton]="false"
3492
+ [pt]="{ root: { class: 'bg-transparent' }, header: { class: 'bg-transparent' }, content: { class: 'bg-transparent' } }"
3493
+ >
3494
+ <ng-template #header let-chooseCallback="chooseCallback">
3495
+ <div class="flex items-center gap-2 w-full">
3496
+ <p-button icon="pi pi-upload" label="Upload File" (onClick)="chooseCallback()" />
3497
+ <p-button icon="pi pi-link" label="Share Link" [outlined]="true" styleClass="!text-primary-600 !border-primary-600" />
3498
+ </div>
3499
+ </ng-template>
3500
+ <ng-template #empty>
3501
+ <div class="flex flex-col items-center gap-2 py-4">
3502
+ <i class="pi pi-cloud-upload text-2xl text-surface-400 dark:text-surface-300"></i>
3503
+ <span class="text-surface-500 dark:text-surface-100 text-sm">Drag and drop files here</span>
3504
+ </div>
3505
+ </ng-template>
3506
+ </p-fileupload>
3507
+ </div>
3508
+ </p-panel>
3509
+ </div>
3510
+ `, isInline: true, styles: [""], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.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$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.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: FileUploadModule }, { kind: "component", type: i3$5.FileUpload, selector: "p-fileupload, p-fileUpload", inputs: ["name", "url", "method", "multiple", "accept", "disabled", "auto", "withCredentials", "maxFileSize", "invalidFileSizeMessageSummary", "invalidFileSizeMessageDetail", "invalidFileTypeMessageSummary", "invalidFileTypeMessageDetail", "invalidFileLimitMessageDetail", "invalidFileLimitMessageSummary", "style", "styleClass", "previewWidth", "chooseLabel", "uploadLabel", "cancelLabel", "chooseIcon", "uploadIcon", "cancelIcon", "showUploadButton", "showCancelButton", "mode", "headers", "customUpload", "fileLimit", "uploadStyleClass", "cancelStyleClass", "removeStyleClass", "chooseStyleClass", "chooseButtonProps", "uploadButtonProps", "cancelButtonProps", "files"], outputs: ["onBeforeUpload", "onSend", "onUpload", "onError", "onClear", "onRemove", "onSelect", "onProgress", "uploadHandler", "onImageError", "onRemoveUploadedFile"] }, { kind: "ngmodule", type: IconFieldModule }, { kind: "component", type: i5$1.IconField, selector: "p-iconfield, p-iconField, p-icon-field", inputs: ["hostName", "iconPosition", "styleClass"] }, { kind: "ngmodule", type: InputIconModule }, { kind: "component", type: i6.InputIcon, selector: "p-inputicon, p-inputIcon", inputs: ["hostName", "styleClass"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: MenuModule }, { kind: "component", type: i7.Menu, selector: "p-menu", inputs: ["model", "popup", "style", "styleClass", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "ariaLabel", "ariaLabelledBy", "id", "tabindex", "appendTo", "motionOptions"], outputs: ["onShow", "onHide", "onBlur", "onFocus"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i8$1.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i8$1.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "component", type: i8$1.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { kind: "ngmodule", type: TagModule }, { kind: "component", type: i9$1.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "component", type: PillTabsComponent, selector: "ux-pill-tabs", inputs: ["items", "activeValue"], outputs: ["activeValueChange"] }, { kind: "ngmodule", type: PanelModule }, { kind: "component", type: i10$1.Panel, selector: "p-panel", inputs: ["id", "toggleable", "header", "collapsed", "styleClass", "iconPos", "showHeader", "toggler", "transitionOptions", "toggleButtonProps", "motionOptions"], outputs: ["collapsedChange", "onBeforeToggle", "onAfterToggle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3511
+ }
3512
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: DocumentsCardComponent, decorators: [{
3513
+ type: Component,
3514
+ args: [{ selector: 'ux-documents-card', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgClass, FormsModule, ButtonModule, FileUploadModule, IconFieldModule, InputIconModule, InputTextModule, MenuModule, TableModule, TagModule, PillTabsComponent, PanelModule], host: { class: 'ux-documents-card block' }, template: `
3515
+ <div class="card flex flex-col overflow-hidden">
3516
+ <p-panel [toggleable]="true" [collapsed]="!expanded()" (collapsedChange)="expanded.set(!$event)" toggler="header">
3517
+ <ng-template #headericons let-collapsed>
3518
+ <i class="pi text-sm text-darkblue-500 dark:text-surface-0" [ngClass]="collapsed ? 'pi-chevron-down' : 'pi-chevron-up'"></i>
3519
+ </ng-template>
3520
+ <ng-template #header>
3521
+ <div class="flex items-center gap-3 flex-1">
3522
+ <div class="w-[34px] h-[34px] rounded-[10px] flex items-center justify-center shrink-0">
3523
+ <i class="pi pi-folder text-deepsea-500 dark:text-surface-0"></i>
3524
+ </div>
3525
+ <div class="flex flex-col">
3526
+ <h4 class="title-h4 text-left text-deepsea-500 dark:text-surface-0">Documents</h4>
3527
+ <span class="text-surface-500 dark:text-surface-300 text-sm font-medium leading-tight">{{ summary() }}</span>
3528
+ </div>
3529
+ </div>
3530
+ </ng-template>
3531
+ <div class="flex flex-col gap-4 pt-4">
3532
+ <ux-pill-tabs [items]="pillTabItems()" [(activeValue)]="activeFilter" />
3533
+
3534
+ <p-iconfield>
3535
+ <p-inputicon class="pi pi-search" />
3536
+ <input pInputText [(ngModel)]="searchQuery" placeholder="Search documents" class="w-full" />
3537
+ </p-iconfield>
3538
+
3539
+ @if (filtered().length > 0) {
3540
+ <p-table
3541
+ [value]="filtered()"
3542
+ [paginator]="true"
3543
+ [rows]="rows()"
3544
+ sortMode="multiple"
3545
+ styleClass="flex flex-col rounded-2xl overflow-hidden [&>[data-pc-section=paginatorcontainer]]:border-0! [&>[data-pc-section=paginatorcontainer]]:mt-auto [&_[data-pc-name=pcpaginator]]:rounded-none!"
3546
+ tableStyleClass="w-full table-fixed"
3547
+ paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink"
3548
+ >
3549
+ <ng-template #header>
3550
+ <tr>
3551
+ <th pSortableColumn="fileName" class="w-1/2">File Name <p-sortIcon field="fileName" /></th>
3552
+ <th pSortableColumn="type" class="w-1/4">Type <p-sortIcon field="type" /></th>
3553
+ <th class="w-1/4">Actions</th>
3554
+ </tr>
3555
+ </ng-template>
3556
+ <ng-template #body let-doc>
3557
+ <tr>
3558
+ <td>
3559
+ <div class="flex items-center gap-3 py-1 min-w-0">
3560
+ <i class="pi text-xl text-surface-600 dark:text-surface-300 shrink-0" [ngClass]="doc.icon"></i>
3561
+ <span class="text-surface-700 dark:text-surface-100 text-sm truncate">{{ doc.fileName }}</span>
3562
+ </div>
3563
+ </td>
3564
+ <td>
3565
+ <p-tag [value]="doc.type" styleClass="px-2 py-1" />
3566
+ </td>
3567
+ <td>
3568
+ <div class="flex items-center gap-1">
3569
+ <p-button icon="pi pi-download" [rounded]="true" [text]="true" size="small" severity="secondary" styleClass="cursor-pointer" ariaLabel="Download" />
3570
+ <p-button icon="pi pi-ellipsis-h" [rounded]="true" [text]="true" size="small" severity="secondary" styleClass="cursor-pointer" ariaLabel="More options" (onClick)="onMenuToggle($event, doc, docMenu)" />
3571
+ <p-menu #docMenu [model]="menuItems" [popup]="true" styleClass="w-48" appendTo="body" />
3572
+ </div>
3573
+ </td>
3574
+ </tr>
3575
+ </ng-template>
3576
+ </p-table>
3577
+ } @else {
3578
+ <div class="flex flex-col items-center gap-3 py-8 text-center">
3579
+ <i class="pi pi-folder-open text-3xl text-surface-300 dark:text-surface-500"></i>
3580
+ <span class="text-surface-600 dark:text-surface-300 text-sm">No documents to show</span>
3581
+ </div>
3582
+ }
3583
+
3584
+ <p-fileupload
3585
+ name="documents[]"
3586
+ [multiple]="true"
3587
+ maxFileSize="10000000"
3588
+ mode="advanced"
3589
+ [auto]="false"
3590
+ chooseLabel="Upload File"
3591
+ chooseIcon="pi pi-upload"
3592
+ [showUploadButton]="false"
3593
+ [showCancelButton]="false"
3594
+ [pt]="{ root: { class: 'bg-transparent' }, header: { class: 'bg-transparent' }, content: { class: 'bg-transparent' } }"
3595
+ >
3596
+ <ng-template #header let-chooseCallback="chooseCallback">
3597
+ <div class="flex items-center gap-2 w-full">
3598
+ <p-button icon="pi pi-upload" label="Upload File" (onClick)="chooseCallback()" />
3599
+ <p-button icon="pi pi-link" label="Share Link" [outlined]="true" styleClass="!text-primary-600 !border-primary-600" />
3600
+ </div>
3601
+ </ng-template>
3602
+ <ng-template #empty>
3603
+ <div class="flex flex-col items-center gap-2 py-4">
3604
+ <i class="pi pi-cloud-upload text-2xl text-surface-400 dark:text-surface-300"></i>
3605
+ <span class="text-surface-500 dark:text-surface-100 text-sm">Drag and drop files here</span>
3606
+ </div>
3607
+ </ng-template>
3608
+ </p-fileupload>
3609
+ </div>
3610
+ </p-panel>
3611
+ </div>
3612
+ ` }]
3613
+ }], propDecorators: { documents: [{ type: i0.Input, args: [{ isSignal: true, alias: "documents", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], searchQuery: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchQuery", required: false }] }, { type: i0.Output, args: ["searchQueryChange"] }] } });
3614
+
3615
+ class UxSelectComponent {
3616
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : []));
3617
+ optionLabel = input('label', ...(ngDevMode ? [{ debugName: "optionLabel" }] : []));
3618
+ optionValue = input('value', ...(ngDevMode ? [{ debugName: "optionValue" }] : []));
3619
+ optionGroupLabel = input('label', ...(ngDevMode ? [{ debugName: "optionGroupLabel" }] : []));
3620
+ optionGroupChildren = input('items', ...(ngDevMode ? [{ debugName: "optionGroupChildren" }] : []));
3621
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
3622
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
3623
+ filter = input(false, ...(ngDevMode ? [{ debugName: "filter" }] : []));
3624
+ showClear = input(false, ...(ngDevMode ? [{ debugName: "showClear" }] : []));
3625
+ emptyMessage = input('No results found', ...(ngDevMode ? [{ debugName: "emptyMessage" }] : []));
3626
+ group = input(false, ...(ngDevMode ? [{ debugName: "group" }] : []));
3627
+ styleClass = input('', ...(ngDevMode ? [{ debugName: "styleClass" }] : []));
3628
+ value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : []));
3629
+ onChange = output();
3630
+ onFilter = output();
3631
+ resolvedStyleClass = computed(() => {
3632
+ const base = 'ux-select__inner';
3633
+ const extra = this.styleClass();
3634
+ return extra ? `${base} ${extra}` : base;
3635
+ }, ...(ngDevMode ? [{ debugName: "resolvedStyleClass" }] : []));
3636
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: UxSelectComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3637
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.8", type: UxSelectComponent, isStandalone: true, selector: "ux-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: false, transformFunction: null }, optionValue: { classPropertyName: "optionValue", publicName: "optionValue", isSignal: true, isRequired: false, transformFunction: null }, optionGroupLabel: { classPropertyName: "optionGroupLabel", publicName: "optionGroupLabel", isSignal: true, isRequired: false, transformFunction: null }, optionGroupChildren: { classPropertyName: "optionGroupChildren", publicName: "optionGroupChildren", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, filter: { classPropertyName: "filter", publicName: "filter", isSignal: true, isRequired: false, transformFunction: null }, showClear: { classPropertyName: "showClear", publicName: "showClear", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, group: { classPropertyName: "group", publicName: "group", isSignal: true, isRequired: false, transformFunction: null }, styleClass: { classPropertyName: "styleClass", publicName: "styleClass", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", onChange: "onChange", onFilter: "onFilter" }, host: { classAttribute: "ux-select" }, ngImport: i0, template: `
3638
+ <p-select
3639
+ [options]="options()"
3640
+ [optionLabel]="optionLabel()"
3641
+ [optionValue]="optionValue()"
3642
+ [optionGroupLabel]="optionGroupLabel()"
3643
+ [optionGroupChildren]="optionGroupChildren()"
3644
+ [placeholder]="placeholder()"
3645
+ [disabled]="disabled()"
3646
+ [filter]="filter()"
3647
+ [showClear]="showClear()"
3648
+ [emptyMessage]="emptyMessage()"
3649
+ [group]="group()"
3650
+ [ngModel]="value()"
3651
+ (ngModelChange)="value.set($event)"
3652
+ (onChange)="onChange.emit($event)"
3653
+ (onFilter)="onFilter.emit($event)"
3654
+ [styleClass]="resolvedStyleClass()"
3655
+ />
3656
+ `, isInline: true, styles: [":host :deep .p-select{border-radius:var(--p-content-border-radius, .375rem);font-family:var(--p-font-family, \"Noto Sans\", sans-serif);font-size:var(--font-size-sm, .875rem);transition:border-color .15s ease,box-shadow .15s ease;padding:.5rem 1.5rem}:host :deep .p-select:not(.p-disabled):hover{border-color:var(--p-primary-400)}:host :deep .p-select:not(.p-disabled).p-focus{border-color:var(--p-primary-500);box-shadow:0 0 0 2px color-mix(in srgb,var(--p-primary-500) 20%,transparent)}:host :deep .p-select-label{font-size:var(--font-size-sm, .875rem);color:var(--p-text-color)}:host :deep .p-select-label.p-placeholder{color:var(--p-text-muted-color)}:host :deep .p-select-dropdown{color:var(--p-text-muted-color)}:host-context(:root[class*=\"app-dark\"]) :deep .p-select:not(.p-disabled):hover{border-color:var(--p-primary-300)}:host-context(:root[class*=\"app-dark\"]) :deep .p-select:not(.p-disabled).p-focus{border-color:var(--p-primary-400);box-shadow:0 0 0 2px color-mix(in srgb,var(--p-primary-400) 25%,transparent)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i3$1.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3657
+ }
3658
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: UxSelectComponent, decorators: [{
3659
+ type: Component,
3660
+ args: [{ selector: 'ux-select', changeDetection: ChangeDetectionStrategy.OnPush, imports: [FormsModule, SelectModule], host: { class: 'ux-select' }, template: `
3661
+ <p-select
3662
+ [options]="options()"
3663
+ [optionLabel]="optionLabel()"
3664
+ [optionValue]="optionValue()"
3665
+ [optionGroupLabel]="optionGroupLabel()"
3666
+ [optionGroupChildren]="optionGroupChildren()"
3667
+ [placeholder]="placeholder()"
3668
+ [disabled]="disabled()"
3669
+ [filter]="filter()"
3670
+ [showClear]="showClear()"
3671
+ [emptyMessage]="emptyMessage()"
3672
+ [group]="group()"
3673
+ [ngModel]="value()"
3674
+ (ngModelChange)="value.set($event)"
3675
+ (onChange)="onChange.emit($event)"
3676
+ (onFilter)="onFilter.emit($event)"
3677
+ [styleClass]="resolvedStyleClass()"
3678
+ />
3679
+ `, styles: [":host :deep .p-select{border-radius:var(--p-content-border-radius, .375rem);font-family:var(--p-font-family, \"Noto Sans\", sans-serif);font-size:var(--font-size-sm, .875rem);transition:border-color .15s ease,box-shadow .15s ease;padding:.5rem 1.5rem}:host :deep .p-select:not(.p-disabled):hover{border-color:var(--p-primary-400)}:host :deep .p-select:not(.p-disabled).p-focus{border-color:var(--p-primary-500);box-shadow:0 0 0 2px color-mix(in srgb,var(--p-primary-500) 20%,transparent)}:host :deep .p-select-label{font-size:var(--font-size-sm, .875rem);color:var(--p-text-color)}:host :deep .p-select-label.p-placeholder{color:var(--p-text-muted-color)}:host :deep .p-select-dropdown{color:var(--p-text-muted-color)}:host-context(:root[class*=\"app-dark\"]) :deep .p-select:not(.p-disabled):hover{border-color:var(--p-primary-300)}:host-context(:root[class*=\"app-dark\"]) :deep .p-select:not(.p-disabled).p-focus{border-color:var(--p-primary-400);box-shadow:0 0 0 2px color-mix(in srgb,var(--p-primary-400) 25%,transparent)}\n"] }]
3680
+ }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: false }] }], optionValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionValue", required: false }] }], optionGroupLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionGroupLabel", required: false }] }], optionGroupChildren: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionGroupChildren", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], showClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClear", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], group: [{ type: i0.Input, args: [{ isSignal: true, alias: "group", required: false }] }], styleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleClass", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], onChange: [{ type: i0.Output, args: ["onChange"] }], onFilter: [{ type: i0.Output, args: ["onFilter"] }] } });
3681
+
3682
+ class TaskDrawerComponent {
3683
+ visible = model(false, ...(ngDevMode ? [{ debugName: "visible" }] : []));
3684
+ task = input(null, ...(ngDevMode ? [{ debugName: "task" }] : []));
3685
+ mode = input('create', ...(ngDevMode ? [{ debugName: "mode" }] : []));
3686
+ availableMembers = input([
3687
+ { name: 'Amy Elsner', image: 'amyelsner.png' },
3688
+ { name: 'Anna Fali', image: 'annafali.png' },
3689
+ { name: 'Asiya Javayant', image: 'asiyajavayant.png' },
3690
+ { name: 'Bernardo Dominic', image: 'bernardodominic.png' }
3691
+ ], ...(ngDevMode ? [{ debugName: "availableMembers" }] : []));
3692
+ save = output();
3693
+ cancel = output();
3694
+ drawerTitle = computed(() => this.mode() === 'create' ? 'Create New Task' : 'Edit Task', ...(ngDevMode ? [{ debugName: "drawerTitle" }] : []));
3695
+ formData = {
3696
+ id: null,
3697
+ title: '',
3698
+ description: '',
3699
+ status: 'pending',
3700
+ completed: false,
3701
+ startDate: null,
3702
+ endDate: null,
3703
+ members: []
3704
+ };
3705
+ statusOptions = [
3706
+ { label: 'Pending', value: 'pending' },
3707
+ { label: 'In Progress', value: 'in-progress' },
3708
+ { label: 'Completed', value: 'completed' }
3709
+ ];
3710
+ filteredMembers = [];
3711
+ ngOnChanges(changes) {
3712
+ if (changes['task']) {
3713
+ const newTask = changes['task'].currentValue;
3714
+ if (newTask) {
3715
+ this.formData = {
3716
+ id: newTask.id,
3717
+ title: newTask.title || '',
3718
+ description: newTask.description || '',
3719
+ status: newTask.status || 'pending',
3720
+ completed: newTask.completed || false,
3721
+ startDate: newTask.startDate ? this.parseDate(newTask.startDate) : null,
3722
+ endDate: newTask.endDate ? this.parseDate(newTask.endDate) : null,
3723
+ members: newTask.members || []
3724
+ };
3725
+ }
3726
+ else {
3727
+ this.resetForm();
3728
+ }
3729
+ }
3730
+ }
3731
+ parseDate(dateStr) {
3732
+ if (!dateStr)
3733
+ return null;
3734
+ const parts = dateStr.split('.');
3735
+ if (parts.length === 3) {
3736
+ return new Date(parseInt(parts[2]), parseInt(parts[1]) - 1, parseInt(parts[0]));
3737
+ }
3738
+ return null;
3739
+ }
3740
+ resetForm() {
3741
+ this.formData = {
3742
+ id: null,
3743
+ title: '',
3744
+ description: '',
3745
+ status: 'pending',
3746
+ completed: false,
3747
+ startDate: null,
3748
+ endDate: null,
3749
+ members: []
3750
+ };
3751
+ }
3752
+ filterMembers(event) {
3753
+ const members = this.availableMembers();
3754
+ if (!event.query) {
3755
+ this.filteredMembers = members;
3756
+ return;
3757
+ }
3758
+ this.filteredMembers = members.filter(member => member.name?.toLowerCase().includes(event.query.toLowerCase()));
3759
+ }
3760
+ formatDateForSave(date) {
3761
+ if (!date)
3762
+ return null;
3763
+ const d = new Date(date);
3764
+ return `${String(d.getDate()).padStart(2, '0')}.${String(d.getMonth() + 1).padStart(2, '0')}.${d.getFullYear()}`;
3765
+ }
3766
+ handleSave() {
3767
+ const taskData = {
3768
+ id: this.formData.id,
3769
+ title: this.formData.title,
3770
+ description: this.formData.description || null,
3771
+ status: this.formData.status,
3772
+ completed: this.formData.status === 'completed',
3773
+ startDate: this.formatDateForSave(this.formData.startDate),
3774
+ endDate: this.formatDateForSave(this.formData.endDate),
3775
+ members: this.formData.members
3776
+ };
3777
+ this.save.emit(taskData);
3778
+ this.handleCancel();
3779
+ }
3780
+ handleCancel() {
3781
+ this.resetForm();
3782
+ this.cancel.emit();
3783
+ this.visible.set(false);
3784
+ }
3785
+ onHide() {
3786
+ this.handleCancel();
3787
+ }
3788
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: TaskDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3789
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.8", type: TaskDrawerComponent, isStandalone: true, selector: "ux-task-drawer", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: false, transformFunction: null }, task: { classPropertyName: "task", publicName: "task", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, availableMembers: { classPropertyName: "availableMembers", publicName: "availableMembers", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { visible: "visibleChange", save: "save", cancel: "cancel" }, host: { classAttribute: "ux-task-drawer" }, usesOnChanges: true, ngImport: i0, template: `
3790
+ <p-drawer [visible]="visible()" position="right" styleClass="w-full! md:w-[420px]!" (onHide)="onHide()" (visibleChange)="visible.set($event)" appendTo="body">
3791
+ <ng-template #header>
3792
+ <div class="flex items-center gap-3">
3793
+ <i class="pi pi-list-check text-xl text-primary-500"></i>
3794
+ <span class="text-surface-900 dark:text-surface-0 font-semibold text-lg">{{ drawerTitle() }}</span>
3795
+ </div>
3796
+ </ng-template>
3797
+
3798
+ <div class="flex flex-col gap-6 p-1">
3799
+ <div class="flex flex-col gap-2">
3800
+ <label for="task-title" class="text-surface-900 dark:text-surface-0 font-medium text-sm">Task Title</label>
3801
+ <input pInputText id="task-title" [(ngModel)]="formData.title" placeholder="Enter task title..." class="w-full" />
3802
+ </div>
3803
+
3804
+ <div class="flex flex-col gap-2">
3805
+ <label for="task-description" class="text-surface-900 dark:text-surface-0 font-medium text-sm">Description</label>
3806
+ <textarea pTextarea id="task-description" [(ngModel)]="formData.description" placeholder="Enter task description..." [rows]="4" class="w-full"></textarea>
3807
+ </div>
3808
+
3809
+ <div class="flex flex-col gap-2">
3810
+ <label for="task-status" class="text-surface-900 dark:text-surface-0 font-medium text-sm">Status</label>
3811
+ <p-select id="task-status" [(ngModel)]="formData.status" [options]="statusOptions" optionLabel="label" optionValue="value" placeholder="Select status" styleClass="w-full" />
3812
+ </div>
3813
+
3814
+ <p-divider styleClass="my-2" />
3815
+
3816
+ <div class="flex flex-col gap-2">
3817
+ <label for="start-date" class="text-surface-900 dark:text-surface-0 font-medium text-sm">Start Date</label>
3818
+ <p-datepicker id="start-date" [(ngModel)]="formData.startDate" dateFormat="dd.mm.yy" placeholder="Select start date" inputStyleClass="w-full" />
3819
+ </div>
3820
+
3821
+ <div class="flex flex-col gap-2">
3822
+ <label for="end-date" class="text-surface-900 dark:text-surface-0 font-medium text-sm">End Date</label>
3823
+ <p-datepicker id="end-date" [(ngModel)]="formData.endDate" dateFormat="dd.mm.yy" placeholder="Select end date" inputStyleClass="w-full" />
3824
+ </div>
3825
+
3826
+ <p-divider styleClass="my-2" />
3827
+
3828
+ <div class="flex flex-col gap-2">
3829
+ <label for="team-members" class="text-surface-900 dark:text-surface-0 font-medium text-sm">Team Members</label>
3830
+ <p-autocomplete
3831
+ id="team-members"
3832
+ [(ngModel)]="formData.members"
3833
+ [suggestions]="filteredMembers"
3834
+ optionLabel="name"
3835
+ [multiple]="true"
3836
+ placeholder="Search team members..."
3837
+ (completeMethod)="filterMembers($event)"
3838
+ styleClass="w-full"
3839
+ >
3840
+ <ng-template #selecteditem let-value>
3841
+ <div class="flex items-center gap-2 bg-surface-50 dark:bg-surface-900 px-2 py-1 rounded">
3842
+ <p-avatar [image]="'demo/images/avatar/' + value.image" shape="circle" styleClass="w-5 h-5 border border-surface-200 dark:border-surface-700" />
3843
+ </div>
3844
+ </ng-template>
3845
+ <ng-template #item let-option>
3846
+ <div class="flex items-center gap-3">
3847
+ <p-avatar [image]="'demo/images/avatar/' + option.image" shape="circle" styleClass="w-8 h-8 border border-surface-200 dark:border-surface-700" />
3848
+ <span class="text-surface-900 dark:text-surface-0 font-medium">{{ option.name }}</span>
3849
+ </div>
3850
+ </ng-template>
3851
+ </p-autocomplete>
3852
+ </div>
3853
+ </div>
3854
+
3855
+ <ng-template #footer>
3856
+ <div class="flex justify-end gap-3 pt-4 border-t border-surface-200 dark:border-surface-700">
3857
+ <p-button label="Cancel" icon="pi pi-times" [outlined]="true" severity="secondary" (onClick)="handleCancel()" styleClass="flex-1" />
3858
+ <p-button [label]="mode() === 'create' ? 'Create Task' : 'Update Task'" [icon]="mode() === 'create' ? 'pi pi-plus' : 'pi pi-check'" (onClick)="handleSave()" styleClass="flex-1" />
3859
+ </div>
3860
+ </ng-template>
3861
+ </p-drawer>
3862
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$2.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$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.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: DrawerModule }, { kind: "component", type: i4.Drawer, selector: "p-drawer", inputs: ["appendTo", "motionOptions", "blockScroll", "style", "styleClass", "ariaCloseLabel", "autoZIndex", "baseZIndex", "modal", "closeButtonProps", "dismissible", "showCloseIcon", "closeOnEscape", "transitionOptions", "visible", "position", "fullScreen", "header", "maskStyle", "closable"], outputs: ["onShow", "onHide", "visibleChange"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i5$2.Textarea, selector: "[pTextarea], [pInputTextarea]", inputs: ["pTextareaPT", "pTextareaUnstyled", "autoResize", "pSize", "variant", "fluid", "invalid"], outputs: ["onResize"] }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i3$1.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i7$1.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: AutoCompleteModule }, { kind: "component", type: i8$2.AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: DividerModule }, { kind: "component", type: i2.Divider, selector: "p-divider", inputs: ["styleClass", "layout", "type", "align"] }, { kind: "ngmodule", type: AvatarModule }, { kind: "component", type: i10.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3863
+ }
3864
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: TaskDrawerComponent, decorators: [{
3865
+ type: Component,
3866
+ args: [{
3867
+ selector: 'ux-task-drawer',
3868
+ changeDetection: ChangeDetectionStrategy.OnPush,
3869
+ imports: [FormsModule, ButtonModule, DrawerModule, InputTextModule, TextareaModule, SelectModule, DatePickerModule, AutoCompleteModule, DividerModule, AvatarModule],
3870
+ host: { class: 'ux-task-drawer' },
3871
+ template: `
3872
+ <p-drawer [visible]="visible()" position="right" styleClass="w-full! md:w-[420px]!" (onHide)="onHide()" (visibleChange)="visible.set($event)" appendTo="body">
3873
+ <ng-template #header>
3874
+ <div class="flex items-center gap-3">
3875
+ <i class="pi pi-list-check text-xl text-primary-500"></i>
3876
+ <span class="text-surface-900 dark:text-surface-0 font-semibold text-lg">{{ drawerTitle() }}</span>
3877
+ </div>
3878
+ </ng-template>
3879
+
3880
+ <div class="flex flex-col gap-6 p-1">
3881
+ <div class="flex flex-col gap-2">
3882
+ <label for="task-title" class="text-surface-900 dark:text-surface-0 font-medium text-sm">Task Title</label>
3883
+ <input pInputText id="task-title" [(ngModel)]="formData.title" placeholder="Enter task title..." class="w-full" />
3884
+ </div>
3885
+
3886
+ <div class="flex flex-col gap-2">
3887
+ <label for="task-description" class="text-surface-900 dark:text-surface-0 font-medium text-sm">Description</label>
3888
+ <textarea pTextarea id="task-description" [(ngModel)]="formData.description" placeholder="Enter task description..." [rows]="4" class="w-full"></textarea>
3889
+ </div>
3890
+
3891
+ <div class="flex flex-col gap-2">
3892
+ <label for="task-status" class="text-surface-900 dark:text-surface-0 font-medium text-sm">Status</label>
3893
+ <p-select id="task-status" [(ngModel)]="formData.status" [options]="statusOptions" optionLabel="label" optionValue="value" placeholder="Select status" styleClass="w-full" />
3894
+ </div>
3895
+
3896
+ <p-divider styleClass="my-2" />
3897
+
3898
+ <div class="flex flex-col gap-2">
3899
+ <label for="start-date" class="text-surface-900 dark:text-surface-0 font-medium text-sm">Start Date</label>
3900
+ <p-datepicker id="start-date" [(ngModel)]="formData.startDate" dateFormat="dd.mm.yy" placeholder="Select start date" inputStyleClass="w-full" />
3901
+ </div>
3902
+
3903
+ <div class="flex flex-col gap-2">
3904
+ <label for="end-date" class="text-surface-900 dark:text-surface-0 font-medium text-sm">End Date</label>
3905
+ <p-datepicker id="end-date" [(ngModel)]="formData.endDate" dateFormat="dd.mm.yy" placeholder="Select end date" inputStyleClass="w-full" />
3906
+ </div>
3907
+
3908
+ <p-divider styleClass="my-2" />
3909
+
3910
+ <div class="flex flex-col gap-2">
3911
+ <label for="team-members" class="text-surface-900 dark:text-surface-0 font-medium text-sm">Team Members</label>
3912
+ <p-autocomplete
3913
+ id="team-members"
3914
+ [(ngModel)]="formData.members"
3915
+ [suggestions]="filteredMembers"
3916
+ optionLabel="name"
3917
+ [multiple]="true"
3918
+ placeholder="Search team members..."
3919
+ (completeMethod)="filterMembers($event)"
3920
+ styleClass="w-full"
3921
+ >
3922
+ <ng-template #selecteditem let-value>
3923
+ <div class="flex items-center gap-2 bg-surface-50 dark:bg-surface-900 px-2 py-1 rounded">
3924
+ <p-avatar [image]="'demo/images/avatar/' + value.image" shape="circle" styleClass="w-5 h-5 border border-surface-200 dark:border-surface-700" />
3925
+ </div>
3926
+ </ng-template>
3927
+ <ng-template #item let-option>
3928
+ <div class="flex items-center gap-3">
3929
+ <p-avatar [image]="'demo/images/avatar/' + option.image" shape="circle" styleClass="w-8 h-8 border border-surface-200 dark:border-surface-700" />
3930
+ <span class="text-surface-900 dark:text-surface-0 font-medium">{{ option.name }}</span>
3931
+ </div>
3932
+ </ng-template>
3933
+ </p-autocomplete>
3934
+ </div>
3935
+ </div>
3936
+
3937
+ <ng-template #footer>
3938
+ <div class="flex justify-end gap-3 pt-4 border-t border-surface-200 dark:border-surface-700">
3939
+ <p-button label="Cancel" icon="pi pi-times" [outlined]="true" severity="secondary" (onClick)="handleCancel()" styleClass="flex-1" />
3940
+ <p-button [label]="mode() === 'create' ? 'Create Task' : 'Update Task'" [icon]="mode() === 'create' ? 'pi pi-plus' : 'pi pi-check'" (onClick)="handleSave()" styleClass="flex-1" />
3941
+ </div>
3942
+ </ng-template>
3943
+ </p-drawer>
3944
+ `
3945
+ }]
3946
+ }], propDecorators: { visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }, { type: i0.Output, args: ["visibleChange"] }], task: [{ type: i0.Input, args: [{ isSignal: true, alias: "task", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], availableMembers: [{ type: i0.Input, args: [{ isSignal: true, alias: "availableMembers", required: false }] }], save: [{ type: i0.Output, args: ["save"] }], cancel: [{ type: i0.Output, args: ["cancel"] }] } });
3947
+
2587
3948
  /*
2588
3949
  * Public API Surface of @unopsitg/ux
2589
3950
  */
@@ -2592,5 +3953,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2592
3953
  * Generated bundle index. Do not edit.
2593
3954
  */
2594
3955
 
2595
- export { AiCardBgComponent, AppBreadcrumb, AppConfigurator, AppFooter, AppLayout, AppMenu, AppMenuitem, AppRightMenu, AppSearch, AppSidebar, AppTopbar, AuthLayout, BrandContrast, BrandCrisp, BrandSoft, LayoutService, MENU_MODEL, SIDEBAR_LOGO, TOPBAR_MOBILE_LOGO, brandPresets, brandPrimitives };
3956
+ export { AiCardBgComponent, AiInsightsCardComponent, AppBreadcrumb, AppConfigurator, AppFooter, AppLayout, AppMenu, AppMenuitem, AppRightMenu, AppSearch, AppSidebar, AppTopbar, AuthLayout, BrandContrast, BrandCrisp, BrandSoft, CompletionStepsComponent, DetailLayoutComponent, DetailTabDirective, DocumentsCardComponent, FooterMainComponent, FooterService, LayoutService, MENU_MODEL, PillTabsComponent, SIDEBAR_LOGO, TOPBAR_MOBILE_LOGO, TaskDrawerComponent, UxSelectComponent, brandPresets, brandPrimitives };
2596
3957
  //# sourceMappingURL=unopsitg-ux.mjs.map