@unopsitg/ux 21.0.5 → 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, output, DestroyRef, Directive, ContentChildren } 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, NgClass } 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,18 +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$1 from 'primeng/paginator';
50
+ import * as i2$2 from 'primeng/paginator';
51
51
  import { PaginatorModule } from 'primeng/paginator';
52
- import * as i2$2 from 'primeng/tabs';
52
+ import * as i4$3 from 'primeng/tabs';
53
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';
54
70
 
55
71
  /**
56
72
  * Brand theme presets built on top of PrimeUIX base presets.
@@ -61,8 +77,8 @@ import { TabsModule } from 'primeng/tabs';
61
77
  * Contrast <- @primeuix/themes/nora
62
78
  */
63
79
  const brandPrimitives = {
64
- deepsea: { 50: '#F1F5F9', 100: '#E2E8F0', 200: '#CBD5E1', 300: '#94A3B8', 400: '#64748B', 500: '#475569', 600: '#334155', 700: '#1E293B', 800: '#0F172A', 900: '#0A101D', 950: '#03080c' },
65
- 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' },
66
82
  red: { 50: '#f6cac6', 100: '#f0a9a4', 200: '#eb8982', 300: '#e56960', 400: '#e0493e', 500: '#da291c', 600: '#b92318', 700: '#991d14', 800: '#78170f', 900: '#57100b', 950: '#370a07' },
67
83
  orange: { 50: '#f9d6c3', 100: '#f6be9f', 200: '#f2a57a', 300: '#ef8d56', 400: '#eb7432', 500: '#e85c0e', 600: '#c54e0c', 700: '#a2400a', 800: '#803308', 900: '#5d2506', 950: '#3a1704' },
68
84
  yellow: { 50: '#fff0c5', 100: '#ffe7a1', 200: '#ffdd7e', 300: '#ffd45b', 400: '#ffcb38', 500: '#ffc215', 600: '#d9a512', 700: '#b3880f', 800: '#8c6b0c', 900: '#664e08', 950: '#403105' },
@@ -76,7 +92,8 @@ const brandPrimitives = {
76
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' },
77
93
  darkblue: { 50: '#D0EEFF', 100: '#B7E2F9', 200: '#73abc7', 300: '#4d94b8', 400: '#267da9', 500: '#00669a', 600: '#005783', 700: '#00476c', 800: '#003855', 900: '#00293e', 950: '#001a27' },
78
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' },
79
- 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' }
80
97
  };
81
98
  const brandOverrides = {
82
99
  primitive: {
@@ -115,17 +132,17 @@ const brandOverrides = {
115
132
  light: {
116
133
  surface: {
117
134
  0: '#ffffff',
118
- 50: '{deepsea.50}',
119
- 100: '{deepsea.100}',
120
- 200: '{deepsea.200}',
121
- 300: '{deepsea.300}',
122
- 400: '{deepsea.400}',
123
- 500: '{deepsea.500}',
124
- 600: '{deepsea.600}',
125
- 700: '{deepsea.700}',
126
- 800: '{deepsea.800}',
127
- 900: '{deepsea.900}',
128
- 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}'
129
146
  }
130
147
  },
131
148
  dark: {
@@ -135,8 +152,8 @@ const brandOverrides = {
135
152
  100: '{darkblue.100}',
136
153
  200: '{darkblue.200}',
137
154
  300: '{darkblue.300}',
138
- 400: '{darkblue.400}',
139
- 500: '{darkblue.500}',
155
+ 400: '{blue.400}',
156
+ 500: '{blue.500}',
140
157
  600: '{darkblue.600}',
141
158
  700: '{darkblue.700}',
142
159
  800: '{darkblue.800}',
@@ -147,6 +164,12 @@ const brandOverrides = {
147
164
  }
148
165
  },
149
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
+ },
150
173
  button: {
151
174
  colorScheme: {
152
175
  light: {
@@ -175,34 +198,36 @@ const brandOverrides = {
175
198
  }
176
199
  }
177
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.
178
204
  tabs: {
179
205
  tablist: {
180
- background: 'transparent'
206
+ background: 'transparent',
207
+ borderWidth: '0 0 1px 0'
181
208
  },
182
209
  tab: {
183
210
  background: 'transparent',
184
- hoverBackground: '{primary.50}',
185
- hoverColor: '{primary.700}',
186
- activeBackground: '{primary.100}',
187
- activeColor: '{primary.900}',
188
211
  borderColor: 'transparent',
189
- activeBorderColor: '{primary.200}',
190
- padding: '0.5rem 1rem'
212
+ activeBorderColor: 'transparent',
213
+ borderWidth: '0',
214
+ padding: '0.5rem 1rem',
215
+ margin: '0 0 0.5rem 0'
191
216
  },
192
217
  tabpanel: {
193
218
  background: 'transparent',
194
219
  padding: '0'
220
+ }
221
+ },
222
+ dataview: {
223
+ header: {
224
+ background: 'transparent',
225
+ borderWidth: '0',
226
+ padding: '0'
195
227
  },
196
- colorScheme: {
197
- dark: {
198
- tab: {
199
- hoverBackground: '{primary.900}',
200
- hoverColor: '{primary.200}',
201
- activeBackground: '{surface.800}',
202
- activeColor: '{primary.200}',
203
- activeBorderColor: '{primary.400}'
204
- }
205
- }
228
+ content: {
229
+ background: 'transparent',
230
+ padding: '0'
206
231
  }
207
232
  },
208
233
  paginator: {
@@ -215,6 +240,64 @@ const brandOverrides = {
215
240
  background: 'transparent'
216
241
  }
217
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
+ },
218
301
  tag: {
219
302
  root: {
220
303
  padding: '0.25rem 0.5rem',
@@ -224,22 +307,47 @@ const brandOverrides = {
224
307
  light: {
225
308
  secondary: { color: '{surface.800}' },
226
309
  success: { color: '{green.800}' },
227
- active: { color: '{green.800}' },
228
- inactive: { color: '{gray.800}' },
229
310
  info: { color: '{blue.800}' },
230
311
  warn: { color: '{orange.800}' },
231
- error: { color: '{red.800}' }
312
+ danger: { color: '{red.800}' }
232
313
  },
233
314
  dark: {
234
315
  secondary: { color: '{surface.100}' },
235
316
  success: { color: '{green.100}' },
236
- active: { color: '{green.100}' },
237
317
  info: { color: '{blue.100}' },
238
- inactive: { color: '{gray.100}' },
239
318
  warn: { color: '{orange.100}' },
240
- error: { color: '{red.100}' }
319
+ danger: { color: '{red.100}' }
241
320
  }
242
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
+ }
243
351
  }
244
352
  }
245
353
  };
@@ -429,6 +537,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
429
537
  }]
430
538
  }], ctorParameters: () => [] });
431
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
+
432
550
  class AppBreadcrumb {
433
551
  router;
434
552
  _breadcrumbs$ = new BehaviorSubject([]);
@@ -501,11 +619,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
501
619
  class AppConfigurator {
502
620
  simple = false;
503
621
  location = 'app';
504
- router = inject(Router);
505
622
  config = inject(PrimeNG);
506
623
  layoutService = inject(LayoutService);
507
624
  platformId = inject(PLATFORM_ID);
508
- primeng = inject(PrimeNG);
509
625
  presetKeys = Object.keys(brandPresets);
510
626
  themeOptions = [
511
627
  { name: 'Light', value: false },
@@ -533,42 +649,10 @@ class AppConfigurator {
533
649
  ];
534
650
  }
535
651
  }
536
- surfaces = [
537
- {
538
- name: 'gray',
539
- palette: {
540
- 0: '#ffffff',
541
- 50: '#e5e6e6',
542
- 100: '#d5d6d7',
543
- 200: '#c6c7c8',
544
- 300: '#b6b8b9',
545
- 400: '#a7a8aa',
546
- 500: '#97999b',
547
- 600: '#808284',
548
- 700: '#6a6b6d',
549
- 800: '#535455',
550
- 900: '#3c3d3e',
551
- 950: '#262627'
552
- }
553
- },
554
- {
555
- name: 'darkblue',
556
- palette: {
557
- 0: '#ffffff',
558
- 50: '#D0EEFF',
559
- 100: '#B7E2F9',
560
- 200: '#73abc7',
561
- 300: '#73abc7',
562
- 400: '#4d94b8',
563
- 500: '#267da9',
564
- 600: '#00669a',
565
- 700: '#005783',
566
- 800: '#00476c',
567
- 900: '#00293e',
568
- 950: '#001a27'
569
- }
570
- }
571
- ];
652
+ surfaces = ['gray', 'darkblue'].map(name => ({
653
+ name,
654
+ palette: { 0: '#ffffff', ...brandPrimitives[name] }
655
+ }));
572
656
  selectedPrimaryColor = computed(() => {
573
657
  return this.layoutService.layoutConfig().primary;
574
658
  }, ...(ngDevMode ? [{ debugName: "selectedPrimaryColor" }] : []));
@@ -834,12 +918,12 @@ class AppConfigurator {
834
918
  </div>
835
919
  <div *ngIf="!simple && location === 'app'" class="flex flex-col gap-2">
836
920
  <span class="text-lg text-muted-color font-semibold">Card Style</span>
837
- <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" />
838
922
  </div>
839
923
 
840
924
  <div *ngIf="!simple && location === 'app'" class="flex flex-col gap-2">
841
925
  <span class="text-lg text-muted-color font-semibold">Menu Theme</span>
842
- <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" />
843
927
  </div>
844
928
 
845
929
  <div *ngIf="!simple && location === 'app'">
@@ -857,30 +941,16 @@ class AppConfigurator {
857
941
  </div>
858
942
  </div>
859
943
  <div class="flex">
860
- <div class="flex items-center gap-2 w-6/12">
861
- <p-radiobutton name="menuMode" value="overlay" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('overlay')" inputId="overlay"></p-radiobutton>
862
- <label for="overlay">Overlay</label>
863
- </div>
864
944
  <div class="flex items-center gap-2 w-6/12">
865
945
  <p-radiobutton name="menuMode" value="slim" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('slim')" inputId="slim"></p-radiobutton>
866
946
  <label for="slim">Slim</label>
867
947
  </div>
868
- </div>
869
- <div class="flex">
870
948
  <div class="flex items-center gap-2 w-6/12">
871
949
  <p-radiobutton name="menuMode" value="compact" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('compact')" inputId="compact"></p-radiobutton>
872
950
  <label for="compact">Compact</label>
873
951
  </div>
874
- <div class="flex items-center gap-2 w-6/12">
875
- <p-radiobutton name="menuMode" value="reveal" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('reveal')" inputId="reveal"></p-radiobutton>
876
- <label for="reveal">Reveal</label>
877
- </div>
878
952
  </div>
879
953
  <div class="flex">
880
- <div class="flex items-center gap-2 w-6/12">
881
- <p-radiobutton name="menuMode" value="drawer" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('drawer')" inputId="drawer"></p-radiobutton>
882
- <label for="drawer">Drawer</label>
883
- </div>
884
954
  <div class="flex items-center gap-2 w-6/12">
885
955
  <p-radiobutton name="menuMode" value="horizontal" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('horizontal')" inputId="horizontal"></p-radiobutton>
886
956
  <label for="horizontal">Horizontal</label>
@@ -891,13 +961,13 @@ class AppConfigurator {
891
961
  </div>
892
962
  </div>
893
963
  </p-drawer>
894
- `, 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"] }] });
895
965
  }
896
966
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppConfigurator, decorators: [{
897
967
  type: Component,
898
968
  args: [{
899
969
  selector: 'app-configurator',
900
- imports: [CommonModule, FormsModule, SelectButtonModule, DrawerModule, ToggleSwitchModule, RadioButtonModule],
970
+ imports: [CommonModule, FormsModule, SelectButtonModule, DrawerModule, RadioButtonModule],
901
971
  template: `
902
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">
903
973
  <div class="flex flex-col gap-6">
@@ -952,12 +1022,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
952
1022
  </div>
953
1023
  <div *ngIf="!simple && location === 'app'" class="flex flex-col gap-2">
954
1024
  <span class="text-lg text-muted-color font-semibold">Card Style</span>
955
- <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" />
956
1026
  </div>
957
1027
 
958
1028
  <div *ngIf="!simple && location === 'app'" class="flex flex-col gap-2">
959
1029
  <span class="text-lg text-muted-color font-semibold">Menu Theme</span>
960
- <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" />
961
1031
  </div>
962
1032
 
963
1033
  <div *ngIf="!simple && location === 'app'">
@@ -975,30 +1045,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
975
1045
  </div>
976
1046
  </div>
977
1047
  <div class="flex">
978
- <div class="flex items-center gap-2 w-6/12">
979
- <p-radiobutton name="menuMode" value="overlay" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('overlay')" inputId="overlay"></p-radiobutton>
980
- <label for="overlay">Overlay</label>
981
- </div>
982
1048
  <div class="flex items-center gap-2 w-6/12">
983
1049
  <p-radiobutton name="menuMode" value="slim" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('slim')" inputId="slim"></p-radiobutton>
984
1050
  <label for="slim">Slim</label>
985
1051
  </div>
986
- </div>
987
- <div class="flex">
988
1052
  <div class="flex items-center gap-2 w-6/12">
989
1053
  <p-radiobutton name="menuMode" value="compact" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('compact')" inputId="compact"></p-radiobutton>
990
1054
  <label for="compact">Compact</label>
991
1055
  </div>
992
- <div class="flex items-center gap-2 w-6/12">
993
- <p-radiobutton name="menuMode" value="reveal" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('reveal')" inputId="reveal"></p-radiobutton>
994
- <label for="reveal">Reveal</label>
995
- </div>
996
1056
  </div>
997
1057
  <div class="flex">
998
- <div class="flex items-center gap-2 w-6/12">
999
- <p-radiobutton name="menuMode" value="drawer" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('drawer')" inputId="drawer"></p-radiobutton>
1000
- <label for="drawer">Drawer</label>
1001
- </div>
1002
1058
  <div class="flex items-center gap-2 w-6/12">
1003
1059
  <p-radiobutton name="menuMode" value="horizontal" [ngModel]="menuMode()" (ngModelChange)="setMenuMode('horizontal')" inputId="horizontal"></p-radiobutton>
1004
1060
  <label for="horizontal">Horizontal</label>
@@ -1018,26 +1074,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1018
1074
  type: Input
1019
1075
  }], cardStyle: [{ type: i0.Input, args: [{ isSignal: true, alias: "cardStyle", required: false }] }, { type: i0.Output, args: ["cardStyleChange"] }] } });
1020
1076
 
1021
- class AppFooter {
1022
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppFooter, deps: [], target: i0.ɵɵFactoryTarget.Component });
1023
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: AppFooter, isStandalone: true, selector: "[app-footer]", ngImport: i0, template: `
1024
- <footer class="layout-footer">
1025
- <span class="footer-copyright">&#169; UNOPS 2026</span>
1026
- </footer>
1027
- `, isInline: true });
1028
- }
1029
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppFooter, decorators: [{
1030
- type: Component,
1031
- args: [{
1032
- selector: '[app-footer]',
1033
- template: `
1034
- <footer class="layout-footer">
1035
- <span class="footer-copyright">&#169; UNOPS 2026</span>
1036
- </footer>
1037
- `
1038
- }]
1039
- }] });
1040
-
1041
1077
  class AppRightMenu {
1042
1078
  layoutService = inject(LayoutService);
1043
1079
  cards = [
@@ -1059,7 +1095,7 @@ class AppRightMenu {
1059
1095
  <div class="flex flex-col mt-7">
1060
1096
  <div class="flex gap-6">
1061
1097
  <div class="flex flex-col items-center">
1062
- <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">
1063
1099
  <i class="pi pi-dollar text-blue-600 text-2xl!"></i>
1064
1100
  </span>
1065
1101
  <span class="min-h-14 w-px bg-(--surface-border)"></span>
@@ -1071,7 +1107,7 @@ class AppRightMenu {
1071
1107
  </div>
1072
1108
  <div class="flex gap-6">
1073
1109
  <div class="flex flex-col items-center">
1074
- <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">
1075
1111
  <i class="pi pi-download text-orange-600 text-2xl!"></i>
1076
1112
  </span>
1077
1113
  <span class="min-h-14 w-px bg-(--surface-border)"></span>
@@ -1083,7 +1119,7 @@ class AppRightMenu {
1083
1119
  </div>
1084
1120
  <div class="flex gap-6">
1085
1121
  <div class="flex flex-col items-center">
1086
- <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">
1087
1123
  <i class="pi pi-question-circle text-violet-600 text-2xl!"></i>
1088
1124
  </span>
1089
1125
  <span class="min-h-14 w-px bg-(--surface-border)"></span>
@@ -1095,7 +1131,7 @@ class AppRightMenu {
1095
1131
  </div>
1096
1132
  <div class="flex gap-6">
1097
1133
  <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);]">
1134
+ <span class="w-14 h-14 flex items-center justify-center border border-surface rounded-xl shadow-subtle">
1099
1135
  <i class="pi pi-comment text-blue-600 text-2xl!"></i>
1100
1136
  </span>
1101
1137
  </div>
@@ -1121,7 +1157,7 @@ class AppRightMenu {
1121
1157
  <p class="body-small mt-1 text-left">Track your ongoing shipments to customers.</p>
1122
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" />
1123
1159
  </div>
1124
- </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"] }] });
1125
1161
  }
1126
1162
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppRightMenu, decorators: [{
1127
1163
  type: Component,
@@ -1134,7 +1170,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1134
1170
  <div class="flex flex-col mt-7">
1135
1171
  <div class="flex gap-6">
1136
1172
  <div class="flex flex-col items-center">
1137
- <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">
1138
1174
  <i class="pi pi-dollar text-blue-600 text-2xl!"></i>
1139
1175
  </span>
1140
1176
  <span class="min-h-14 w-px bg-(--surface-border)"></span>
@@ -1146,7 +1182,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1146
1182
  </div>
1147
1183
  <div class="flex gap-6">
1148
1184
  <div class="flex flex-col items-center">
1149
- <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">
1150
1186
  <i class="pi pi-download text-orange-600 text-2xl!"></i>
1151
1187
  </span>
1152
1188
  <span class="min-h-14 w-px bg-(--surface-border)"></span>
@@ -1158,7 +1194,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1158
1194
  </div>
1159
1195
  <div class="flex gap-6">
1160
1196
  <div class="flex flex-col items-center">
1161
- <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">
1162
1198
  <i class="pi pi-question-circle text-violet-600 text-2xl!"></i>
1163
1199
  </span>
1164
1200
  <span class="min-h-14 w-px bg-(--surface-border)"></span>
@@ -1170,7 +1206,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1170
1206
  </div>
1171
1207
  <div class="flex gap-6">
1172
1208
  <div class="flex flex-col items-center">
1173
- <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">
1174
1210
  <i class="pi pi-comment text-blue-600 text-2xl!"></i>
1175
1211
  </span>
1176
1212
  </div>
@@ -1212,21 +1248,21 @@ class AppSearch {
1212
1248
  this.layoutService.layoutState.update((prev) => ({ ...prev, searchBarActive: _val }));
1213
1249
  }
1214
1250
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppSearch, deps: [], target: i0.ɵɵFactoryTarget.Component });
1215
- 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">
1216
1252
  <ng-template #headless>
1217
1253
  <div class="w-full">
1218
1254
  <i class="pi pi-search"></i>
1219
1255
  <input pInputText type="text" [pAutoFocus]="true" class="p-inputtext search-input" placeholder="Search" (keydown.enter)="toggleSearchBar()" />
1220
1256
  </div>
1221
1257
  </ng-template>
1222
- </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"] }] });
1223
1259
  }
1224
1260
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppSearch, decorators: [{
1225
1261
  type: Component,
1226
1262
  args: [{
1227
1263
  selector: '[app-search]',
1228
1264
  imports: [DialogModule, InputTextModule, AutoFocusModule],
1229
- 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">
1230
1266
  <ng-template #headless>
1231
1267
  <div class="w-full">
1232
1268
  <i class="pi pi-search"></i>
@@ -1237,6 +1273,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1237
1273
  }]
1238
1274
  }] });
1239
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
+
1240
1305
  class AppMenuitem {
1241
1306
  layoutService = inject(LayoutService);
1242
1307
  router = inject(Router);
@@ -1716,19 +1781,24 @@ class AppTopbar {
1716
1781
  <i class="pi pi-bars"></i>
1717
1782
  </button>
1718
1783
  <div class="topbar-left">
1719
- <button
1720
- type="button"
1721
- class="topbar-menu-toggle"
1722
- [class.active]="isSidebarPinned()"
1723
- [attr.aria-label]="isSidebarPinned() ? 'Collapse sidebar' : 'Expand sidebar'"
1724
- (click)="toggleSidebarPin()"
1725
- >
1726
- <i class="pi pi-bars"></i>
1727
- </button>
1728
- <a class="topbar-logo" [routerLink]="['/']">
1729
- <img [src]="desktopLogo()" [attr.alt]="mobileLogoConfig.alt" />
1730
- </a>
1731
- <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">
1732
1802
  <div app-breadcrumb></div>
1733
1803
  @if (searchActive()) {
1734
1804
  <div class="flex items-center gap-2 ml-auto">
@@ -1741,14 +1811,8 @@ class AppTopbar {
1741
1811
  </button>
1742
1812
  </div>
1743
1813
  }
1744
- </div>
1745
-
1746
- <a class="mobile-logo" [routerLink]="['/landing']">
1747
- <img [src]="mobileLogo()" [attr.alt]="mobileLogoConfig.alt" />
1748
- </a>
1749
-
1750
- <div class="topbar-right">
1751
- <ul class="topbar-menu">
1814
+ <div class="topbar-right">
1815
+ <ul class="topbar-menu">
1752
1816
  <li class="right-sidebar-item" [class.hidden]="searchActive()">
1753
1817
  <a class="right-sidebar-button" aria-label="Open search" (click)="openSearch()">
1754
1818
  <i class="pi pi-search"></i>
@@ -1769,11 +1833,11 @@ class AppTopbar {
1769
1833
  <i class="pi pi-bell"></i>
1770
1834
  </a>
1771
1835
  <div
1772
- 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"
1773
1837
  >
1774
1838
  <div class="p-4 flex items-center justify-between border-b border-surface">
1775
1839
  <span class="label-small text-surface-950 dark:text-surface-0">Notifications</span>
1776
- <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>
1777
1841
  </div>
1778
1842
  <div class="flex items-center border-b border-surface">
1779
1843
  @for (item of notificationsBars(); track item.id; let i = $index) {
@@ -1823,7 +1887,7 @@ class AppTopbar {
1823
1887
  </svg>
1824
1888
  </a>
1825
1889
  <div
1826
- 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"
1827
1891
  >
1828
1892
  <ul class="flex flex-col gap-1">
1829
1893
  @for (lang of languages(); track lang.code) {
@@ -1849,7 +1913,7 @@ class AppTopbar {
1849
1913
  </a>
1850
1914
  <div
1851
1915
  #profilePanel
1852
- 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"
1853
1917
  [class.hidden]="!profileMenuOpen()"
1854
1918
  [class.animate-scalein]="profileMenuOpen()"
1855
1919
  >
@@ -1900,7 +1964,7 @@ class AppTopbar {
1900
1964
  </a>
1901
1965
  </li>
1902
1966
  <li>
1903
- <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()">
1904
1968
  <i class="pi pi-cog"></i>
1905
1969
  <span>Settings</span>
1906
1970
  </a>
@@ -1926,9 +1990,14 @@ class AppTopbar {
1926
1990
  </ul>
1927
1991
  </div>
1928
1992
  </li>
1929
- </ul>
1993
+ </ul>
1994
+ </div>
1930
1995
  </div>
1931
- </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"] }] });
1932
2001
  }
1933
2002
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppTopbar, decorators: [{
1934
2003
  type: Component,
@@ -1940,19 +2009,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1940
2009
  <i class="pi pi-bars"></i>
1941
2010
  </button>
1942
2011
  <div class="topbar-left">
1943
- <button
1944
- type="button"
1945
- class="topbar-menu-toggle"
1946
- [class.active]="isSidebarPinned()"
1947
- [attr.aria-label]="isSidebarPinned() ? 'Collapse sidebar' : 'Expand sidebar'"
1948
- (click)="toggleSidebarPin()"
1949
- >
1950
- <i class="pi pi-bars"></i>
1951
- </button>
1952
- <a class="topbar-logo" [routerLink]="['/']">
1953
- <img [src]="desktopLogo()" [attr.alt]="mobileLogoConfig.alt" />
1954
- </a>
1955
- <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">
1956
2030
  <div app-breadcrumb></div>
1957
2031
  @if (searchActive()) {
1958
2032
  <div class="flex items-center gap-2 ml-auto">
@@ -1965,14 +2039,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1965
2039
  </button>
1966
2040
  </div>
1967
2041
  }
1968
- </div>
1969
-
1970
- <a class="mobile-logo" [routerLink]="['/landing']">
1971
- <img [src]="mobileLogo()" [attr.alt]="mobileLogoConfig.alt" />
1972
- </a>
1973
-
1974
- <div class="topbar-right">
1975
- <ul class="topbar-menu">
2042
+ <div class="topbar-right">
2043
+ <ul class="topbar-menu">
1976
2044
  <li class="right-sidebar-item" [class.hidden]="searchActive()">
1977
2045
  <a class="right-sidebar-button" aria-label="Open search" (click)="openSearch()">
1978
2046
  <i class="pi pi-search"></i>
@@ -1993,11 +2061,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1993
2061
  <i class="pi pi-bell"></i>
1994
2062
  </a>
1995
2063
  <div
1996
- 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"
1997
2065
  >
1998
2066
  <div class="p-4 flex items-center justify-between border-b border-surface">
1999
2067
  <span class="label-small text-surface-950 dark:text-surface-0">Notifications</span>
2000
- <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>
2001
2069
  </div>
2002
2070
  <div class="flex items-center border-b border-surface">
2003
2071
  @for (item of notificationsBars(); track item.id; let i = $index) {
@@ -2047,7 +2115,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2047
2115
  </svg>
2048
2116
  </a>
2049
2117
  <div
2050
- 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"
2051
2119
  >
2052
2120
  <ul class="flex flex-col gap-1">
2053
2121
  @for (lang of languages(); track lang.code) {
@@ -2073,7 +2141,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2073
2141
  </a>
2074
2142
  <div
2075
2143
  #profilePanel
2076
- 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"
2077
2145
  [class.hidden]="!profileMenuOpen()"
2078
2146
  [class.animate-scalein]="profileMenuOpen()"
2079
2147
  >
@@ -2124,7 +2192,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2124
2192
  </a>
2125
2193
  </li>
2126
2194
  <li>
2127
- <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()">
2128
2196
  <i class="pi pi-cog"></i>
2129
2197
  <span>Settings</span>
2130
2198
  </a>
@@ -2150,8 +2218,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2150
2218
  </ul>
2151
2219
  </div>
2152
2220
  </li>
2153
- </ul>
2221
+ </ul>
2222
+ </div>
2154
2223
  </div>
2224
+
2225
+ <a class="mobile-logo" [routerLink]="['/']">
2226
+ <img [src]="mobileLogo()" [attr.alt]="mobileLogoConfig.alt" />
2227
+ </a>
2155
2228
  </div>`
2156
2229
  }]
2157
2230
  }], propDecorators: { menuButton: [{
@@ -2171,7 +2244,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2171
2244
  args: ['document:click', ['$event']]
2172
2245
  }] } });
2173
2246
 
2174
- const BREAKPOINT = 992;
2247
+ const BREAKPOINT = 1024;
2175
2248
  class AppSidebar {
2176
2249
  layoutService = inject(LayoutService);
2177
2250
  router = inject(Router);
@@ -2378,22 +2451,28 @@ class AppSidebar {
2378
2451
  }
2379
2452
  }
2380
2453
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppSidebar, deps: [], target: i0.ɵɵFactoryTarget.Component });
2381
- 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()">
2382
- <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()">
2383
2456
  <div app-menu></div>
2384
2457
  </div>
2458
+ @if (!isHorizontal()) {
2459
+ <ux-footer-main [copyrightOnly]="true" role="contentinfo" />
2460
+ }
2385
2461
  <div app-topbar *ngIf="isHorizontal()"></div>
2386
- </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"] }] });
2387
2463
  }
2388
2464
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppSidebar, decorators: [{
2389
2465
  type: Component,
2390
2466
  args: [{
2391
2467
  selector: '[app-sidebar]',
2392
- imports: [CommonModule, AppMenu, RouterModule, AppTopbar],
2468
+ imports: [CommonModule, AppMenu, RouterModule, AppTopbar, FooterMainComponent],
2393
2469
  template: `<nav class="layout-sidebar" aria-label="Main navigation" (mouseleave)="onMouseLeave()">
2394
- <div #menuContainer class="layout-menu-container" (scroll)="onMenuScroll()">
2470
+ <div #menuContainer class="layout-menu-container min-h-0 grow" (scroll)="onMenuScroll()">
2395
2471
  <div app-menu></div>
2396
2472
  </div>
2473
+ @if (!isHorizontal()) {
2474
+ <ux-footer-main [copyrightOnly]="true" role="contentinfo" />
2475
+ }
2397
2476
  <div app-topbar *ngIf="isHorizontal()"></div>
2398
2477
  </nav>`
2399
2478
  }]
@@ -2407,6 +2486,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2407
2486
 
2408
2487
  class AppLayout {
2409
2488
  layoutService = inject(LayoutService);
2489
+ elRef = inject(ElementRef);
2410
2490
  constructor() {
2411
2491
  effect(() => {
2412
2492
  const state = this.layoutService.layoutState();
@@ -2417,6 +2497,11 @@ class AppLayout {
2417
2497
  document.body.classList.remove('blocked-scroll');
2418
2498
  }
2419
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
+ });
2420
2505
  }
2421
2506
  containerClass = computed(() => {
2422
2507
  const layoutConfig = this.layoutService.layoutConfig();
@@ -2449,21 +2534,21 @@ class AppLayout {
2449
2534
  <div app-breadcrumb></div>
2450
2535
  <router-outlet></router-outlet>
2451
2536
  </main>
2452
- <div app-footer></div>
2453
2537
  </div>
2538
+ <ux-footer-main class="footer-sticky" />
2454
2539
  </div>
2455
2540
  </div>
2456
2541
  <app-configurator />
2457
2542
  <div app-search></div>
2458
2543
  <div app-rightmenu></div>
2459
2544
  <div class="layout-mask"></div>
2460
- </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]" }] });
2461
2546
  }
2462
2547
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AppLayout, decorators: [{
2463
2548
  type: Component,
2464
2549
  args: [{
2465
2550
  selector: 'app-layout',
2466
- imports: [CommonModule, AppTopbar, AppSidebar, RouterModule, AppConfigurator, AppBreadcrumb, AppFooter, AppSearch, AppRightMenu],
2551
+ imports: [CommonModule, AppTopbar, AppSidebar, RouterModule, AppConfigurator, AppBreadcrumb, FooterMainComponent, AppSearch, AppRightMenu],
2467
2552
  template: `<div class="layout-wrapper" [ngClass]="containerClass()">
2468
2553
  <div app-topbar></div>
2469
2554
  <div class="layout-body">
@@ -2474,8 +2559,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2474
2559
  <div app-breadcrumb></div>
2475
2560
  <router-outlet></router-outlet>
2476
2561
  </main>
2477
- <div app-footer></div>
2478
2562
  </div>
2563
+ <ux-footer-main class="footer-sticky" />
2479
2564
  </div>
2480
2565
  </div>
2481
2566
  <app-configurator />
@@ -2486,6 +2571,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2486
2571
  }]
2487
2572
  }], ctorParameters: () => [] });
2488
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
+
2489
2600
  class AuthLayout {
2490
2601
  layoutService = inject(LayoutService);
2491
2602
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AuthLayout, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -2571,7 +2682,7 @@ class AiCardBgComponent {
2571
2682
  />
2572
2683
  </svg>
2573
2684
  <ng-content />
2574
- `, 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 });
2575
2686
  }
2576
2687
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AiCardBgComponent, decorators: [{
2577
2688
  type: Component,
@@ -2620,7 +2731,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2620
2731
  />
2621
2732
  </svg>
2622
2733
  <ng-content />
2623
- `, 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"] }]
2624
2735
  }] });
2625
2736
 
2626
2737
  class AiInsightsCardComponent {
@@ -2658,27 +2769,25 @@ class AiInsightsCardComponent {
2658
2769
  return Math.max(1, Math.floor(available / insightCardHeight));
2659
2770
  }
2660
2771
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AiInsightsCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2661
- 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.h-[calc(100dvh-12rem)": "expanded()" }, classAttribute: "ux-ai-insights-card block border border-[#e0e7ff] dark:border-[#2d3a5c] rounded-2xl shadow-sm overflow-hidden transition-all duration-300 flex flex-col max-h-[calc(100dvh-12rem)]" }, ngImport: i0, template: `
2662
- <ux-ai-card-bg class="flex flex-col flex-1 min-h-0 p-4">
2663
- <div class="motion-safe:animate-enter-liquid [animation-delay:80ms] flex flex-col flex-1 min-h-0">
2664
- <div class="flex items-center justify-between cursor-pointer shrink-0" (click)="expanded.set(!expanded())">
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())">
2665
2776
  <div class="flex items-center gap-3">
2666
2777
  <div class="w-[34px] h-[34px] rounded-[10px] flex items-center justify-center shrink-0">
2667
- <i class="pi pi-sparkles text-blue-800 dark:text-blue-300"></i>
2778
+ <i class="pi pi-sparkles text-ai-600 dark:text-ai-300"></i>
2668
2779
  </div>
2669
2780
  <div class="flex flex-col">
2670
2781
  <h4 class="title-h4 text-left text-deepsea-500 dark:text-surface-0">{{ title() }}</h4>
2671
2782
  <span class="text-midnight-700 dark:text-surface-100 text-sm font-medium leading-tight">{{ insights().length }} insights available for your review</span>
2672
2783
  </div>
2673
2784
  </div>
2674
- <button class="w-[30px] h-[30px] rounded-full bg-white/85 dark:bg-transparent border border-white dark:border-surface-300 shadow-sm flex items-center justify-center cursor-pointer hover:bg-white dark:hover:bg-white/10 transition-colors">
2675
- <i class="pi text-xs text-darkblue-500 dark:text-surface-0" [ngClass]="expanded() ? 'pi-chevron-up' : 'pi-chevron-down'"></i>
2676
- </button>
2785
+ <i class="pi text-sm text-darkblue-500 dark:text-surface-0" [ngClass]="expanded() ? 'pi-chevron-up' : 'pi-chevron-down'"></i>
2677
2786
  </div>
2678
2787
 
2679
2788
  <div class="expand-body" [class.expand-body--open]="expanded()">
2680
2789
  <div class="expand-body__inner">
2681
- <div class="flex flex-col gap-4 mt-4 flex-1 min-h-0">
2790
+ <div class="flex flex-col gap-4 mt-4">
2682
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">
2683
2792
  <i class="pi pi-search text-surface-500 dark:text-surface-300 text-sm"></i>
2684
2793
  <input
@@ -2690,14 +2799,14 @@ class AiInsightsCardComponent {
2690
2799
  />
2691
2800
  </div>
2692
2801
 
2693
- <div class="flex flex-col gap-3 flex-1 min-h-0 overflow-y-auto overscroll-y-contain pr-0.5">
2802
+ <div class="flex flex-col gap-3">
2694
2803
  @for (insight of paginatedInsights(); track insight.id) {
2695
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">
2696
2805
  <i class="pi mt-0.5" [ngClass]="[insight.icon, insight.iconColor]"></i>
2697
2806
  <div class="flex flex-col gap-2 flex-1 min-w-0">
2698
2807
  <div class="flex flex-col gap-1">
2699
2808
  <span class="text-midnight-500 dark:text-surface-0 text-sm font-bold leading-[21px]">{{ insight.title }}</span>
2700
- <p class="text-[#2b638b] dark:text-surface-300 text-sm leading-normal">{{ insight.description }}</p>
2809
+ <p class="text-midnight-400 dark:text-surface-300 text-sm leading-normal">{{ insight.description }}</p>
2701
2810
  </div>
2702
2811
  <button
2703
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"
@@ -2718,8 +2827,8 @@ class AiInsightsCardComponent {
2718
2827
  [first]="first()"
2719
2828
  (onPageChange)="page.set($event.page ?? 0)"
2720
2829
  [pageLinkSize]="3"
2721
- styleClass="w-full border-none! bg-transparent!"
2722
- [pt]="{ root: { class: 'bg-transparent! relative! w-full! justify-center!' } }"
2830
+ styleClass="w-full"
2831
+ [pt]="{ root: { class: 'justify-center' } }"
2723
2832
  />
2724
2833
  </div>
2725
2834
  </div>
@@ -2727,34 +2836,32 @@ class AiInsightsCardComponent {
2727
2836
  </div>
2728
2837
  </div>
2729
2838
  </ux-ai-card-bg>
2730
- `, isInline: true, styles: [":host{display:flex}\n"], dependencies: [{ kind: "component", type: AiCardBgComponent, selector: "ux-ai-card-bg" }, { 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: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i2$1.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 });
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 });
2731
2840
  }
2732
2841
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: AiInsightsCardComponent, decorators: [{
2733
2842
  type: Component,
2734
2843
  args: [{ selector: 'ux-ai-insights-card', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AiCardBgComponent, FormsModule, NgClass, PaginatorModule], host: {
2735
- class: 'ux-ai-insights-card block border border-[#e0e7ff] dark:border-[#2d3a5c] rounded-2xl shadow-sm overflow-hidden transition-all duration-300 flex flex-col max-h-[calc(100dvh-12rem)]',
2736
- '[class.h-[calc(100dvh-12rem)]]': 'expanded()'
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()'
2737
2846
  }, template: `
2738
- <ux-ai-card-bg class="flex flex-col flex-1 min-h-0 p-4">
2739
- <div class="motion-safe:animate-enter-liquid [animation-delay:80ms] flex flex-col flex-1 min-h-0">
2740
- <div class="flex items-center justify-between cursor-pointer shrink-0" (click)="expanded.set(!expanded())">
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())">
2741
2850
  <div class="flex items-center gap-3">
2742
2851
  <div class="w-[34px] h-[34px] rounded-[10px] flex items-center justify-center shrink-0">
2743
- <i class="pi pi-sparkles text-blue-800 dark:text-blue-300"></i>
2852
+ <i class="pi pi-sparkles text-ai-600 dark:text-ai-300"></i>
2744
2853
  </div>
2745
2854
  <div class="flex flex-col">
2746
2855
  <h4 class="title-h4 text-left text-deepsea-500 dark:text-surface-0">{{ title() }}</h4>
2747
2856
  <span class="text-midnight-700 dark:text-surface-100 text-sm font-medium leading-tight">{{ insights().length }} insights available for your review</span>
2748
2857
  </div>
2749
2858
  </div>
2750
- <button class="w-[30px] h-[30px] rounded-full bg-white/85 dark:bg-transparent border border-white dark:border-surface-300 shadow-sm flex items-center justify-center cursor-pointer hover:bg-white dark:hover:bg-white/10 transition-colors">
2751
- <i class="pi text-xs text-darkblue-500 dark:text-surface-0" [ngClass]="expanded() ? 'pi-chevron-up' : 'pi-chevron-down'"></i>
2752
- </button>
2859
+ <i class="pi text-sm text-darkblue-500 dark:text-surface-0" [ngClass]="expanded() ? 'pi-chevron-up' : 'pi-chevron-down'"></i>
2753
2860
  </div>
2754
2861
 
2755
2862
  <div class="expand-body" [class.expand-body--open]="expanded()">
2756
2863
  <div class="expand-body__inner">
2757
- <div class="flex flex-col gap-4 mt-4 flex-1 min-h-0">
2864
+ <div class="flex flex-col gap-4 mt-4">
2758
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">
2759
2866
  <i class="pi pi-search text-surface-500 dark:text-surface-300 text-sm"></i>
2760
2867
  <input
@@ -2766,14 +2873,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2766
2873
  />
2767
2874
  </div>
2768
2875
 
2769
- <div class="flex flex-col gap-3 flex-1 min-h-0 overflow-y-auto overscroll-y-contain pr-0.5">
2876
+ <div class="flex flex-col gap-3">
2770
2877
  @for (insight of paginatedInsights(); track insight.id) {
2771
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">
2772
2879
  <i class="pi mt-0.5" [ngClass]="[insight.icon, insight.iconColor]"></i>
2773
2880
  <div class="flex flex-col gap-2 flex-1 min-w-0">
2774
2881
  <div class="flex flex-col gap-1">
2775
2882
  <span class="text-midnight-500 dark:text-surface-0 text-sm font-bold leading-[21px]">{{ insight.title }}</span>
2776
- <p class="text-[#2b638b] dark:text-surface-300 text-sm leading-normal">{{ insight.description }}</p>
2883
+ <p class="text-midnight-400 dark:text-surface-300 text-sm leading-normal">{{ insight.description }}</p>
2777
2884
  </div>
2778
2885
  <button
2779
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"
@@ -2794,8 +2901,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2794
2901
  [first]="first()"
2795
2902
  (onPageChange)="page.set($event.page ?? 0)"
2796
2903
  [pageLinkSize]="3"
2797
- styleClass="w-full border-none! bg-transparent!"
2798
- [pt]="{ root: { class: 'bg-transparent! relative! w-full! justify-center!' } }"
2904
+ styleClass="w-full"
2905
+ [pt]="{ root: { class: 'justify-center' } }"
2799
2906
  />
2800
2907
  </div>
2801
2908
  </div>
@@ -2806,6 +2913,153 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2806
2913
  `, styles: [":host{display:flex}\n"] }]
2807
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"] }] } });
2808
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
+
2809
3063
  /**
2810
3064
  * Structural directive that marks a template as content for a specific tab
2811
3065
  * inside `<ux-detail-layout>`.
@@ -2843,12 +3097,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2843
3097
  * <ng-template uxDetailTab="scope">...tab 2...</ng-template>
2844
3098
  *
2845
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. -->
2846
3102
  * ...right sidebar (AI card, documents, etc.)...
2847
3103
  * </ng-container>
2848
3104
  *
2849
- * <ng-container ux-detail-footer>
2850
- * ...audit metadata row...
2851
- * </ng-container>
2852
3105
  * </ux-detail-layout>
2853
3106
  * ```
2854
3107
  */
@@ -2857,8 +3110,23 @@ class DetailLayoutComponent {
2857
3110
  tabs = input.required(...(ngDevMode ? [{ debugName: "tabs" }] : []));
2858
3111
  /** Currently active tab value (two-way bindable). */
2859
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" }] : []));
2860
3119
  /** True once the scrollable body has been scrolled past the threshold. */
2861
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
+ }
2862
3130
  /** Tab content templates provided by the consumer. */
2863
3131
  tabTemplates;
2864
3132
  getTabTemplate(value) {
@@ -2870,8 +3138,8 @@ class DetailLayoutComponent {
2870
3138
  this.scrolled.set(el.scrollTop > 10);
2871
3139
  }
2872
3140
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: DetailLayoutComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2873
- 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 block" }, queries: [{ propertyName: "tabTemplates", predicate: DetailTabDirective }], ngImport: i0, template: `
2874
- <div class="flex flex-col overflow-hidden" [style.height]="'calc(100vh - 64px)'">
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">
2875
3143
 
2876
3144
  <!-- Sticky header (projected) -->
2877
3145
  <div class="ux-dl__header flex-shrink-0 z-10">
@@ -2883,15 +3151,32 @@ class DetailLayoutComponent {
2883
3151
  </div>
2884
3152
  </div>
2885
3153
 
2886
- <!-- Scrollable body -->
2887
- <div class="flex flex-col flex-1 min-h-0 overflow-y-auto overflow-x-hidden ux-dl__scroll"
2888
- (scroll)="onScroll($event)">
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
+ }
2889
3171
 
2890
- <!-- Full-width tab bar -->
2891
- <p-tabs [value]="activeTab()" (valueChange)="activeTab.set($event + '')">
2892
- <p-tablist>
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
+ >
2893
3178
  @for (tab of tabs(); track tab.value) {
2894
- <p-tab [value]="tab.value">
3179
+ <p-tab [value]="tab.value" [pt]="{ root: { class: 'flex-1 justify-center' } }">
2895
3180
  @if (tab.icon) {
2896
3181
  <i [class]="tab.icon" class="mr-2 text-sm"></i>
2897
3182
  }
@@ -2899,12 +3184,17 @@ class DetailLayoutComponent {
2899
3184
  </p-tab>
2900
3185
  }
2901
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)">
2902
3192
 
2903
- <!-- Content + Sidebar row below tab bar -->
2904
- <div class="flex flex-col lg:flex-row items-start gap-6 w-full py-4 sm:py-6">
3193
+ <!-- Content + Sidebar row -->
3194
+ <div class="flex flex-col lg:flex-row items-start gap-6 w-full py-4 lg:py-6">
2905
3195
 
2906
3196
  <!-- Main column: tab panels -->
2907
- <div class="flex-1 min-w-0 flex flex-col gap-6">
3197
+ <div class="w-full flex-1 min-w-0 flex flex-col gap-6">
2908
3198
  <p-tabpanels>
2909
3199
  @for (tab of tabs(); track tab.value) {
2910
3200
  <p-tabpanel [value]="tab.value">
@@ -2916,27 +3206,27 @@ class DetailLayoutComponent {
2916
3206
  </p-tabpanel>
2917
3207
  }
2918
3208
  </p-tabpanels>
2919
-
2920
- <!-- Footer below tab content -->
2921
- <ng-content select="[ux-detail-footer]" />
2922
3209
  </div>
2923
3210
 
2924
3211
  <!-- Sidebar -->
2925
- <aside class="w-full lg:w-[380px] shrink-0 flex flex-col lg:sticky lg:top-4 lg:self-start pb-8">
3212
+ <aside class="w-full lg:w-[380px] shrink-0 flex flex-col lg:sticky lg:top-4 lg:self-start lg:pb-8">
2926
3213
  <div class="ux-dl__sidebar-inner w-full">
2927
3214
  <ng-content select="[ux-detail-sidebar]" />
2928
3215
  </div>
2929
3216
  </aside>
2930
3217
  </div>
2931
- </p-tabs>
2932
- </div>
3218
+
3219
+ </div>
3220
+
3221
+ </p-tabs>
3222
+
2933
3223
  </div>
2934
- `, isInline: true, styles: [":host{display:block;min-height:calc(100vh - 4rem);font-family:var(--p-font-family, \"Noto Sans\", sans-serif);background:transparent;color:var(--p-text-color)}.ux-dl__header{background:transparent;border-bottom:1px solid var(--p-content-border-color)}.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,.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}:host :deep p-tablist{position:sticky;top:0;z-index:5;background:var(--p-content-background, var(--p-primary-400))}:host :deep p-tablist .p-tablist-content{width:100%}:host :deep p-tablist .p-tablist-tab-list{width:100%}:host :deep p-tab{flex:1;justify-content:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: TabsModule }, { kind: "component", type: i2$2.Tabs, selector: "p-tabs", inputs: ["value", "scrollable", "lazy", "selectOnFocus", "showNavigators", "tabindex"], outputs: ["valueChange"] }, { kind: "component", type: i2$2.TabPanels, selector: "p-tabpanels" }, { kind: "component", type: i2$2.TabPanel, selector: "p-tabpanel", inputs: ["lazy", "value"], outputs: ["valueChange"] }, { kind: "component", type: i2$2.TabList, selector: "p-tablist" }, { kind: "component", type: i2$2.Tab, selector: "p-tab", inputs: ["value", "disabled"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
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 });
2935
3225
  }
2936
3226
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: DetailLayoutComponent, decorators: [{
2937
3227
  type: Component,
2938
- args: [{ selector: 'ux-detail-layout', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, TabsModule], host: { class: 'ux-detail-layout block' }, template: `
2939
- <div class="flex flex-col overflow-hidden" [style.height]="'calc(100vh - 64px)'">
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">
2940
3230
 
2941
3231
  <!-- Sticky header (projected) -->
2942
3232
  <div class="ux-dl__header flex-shrink-0 z-10">
@@ -2948,15 +3238,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2948
3238
  </div>
2949
3239
  </div>
2950
3240
 
2951
- <!-- Scrollable body -->
2952
- <div class="flex flex-col flex-1 min-h-0 overflow-y-auto overflow-x-hidden ux-dl__scroll"
2953
- (scroll)="onScroll($event)">
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
+ }
2954
3258
 
2955
- <!-- Full-width tab bar -->
2956
- <p-tabs [value]="activeTab()" (valueChange)="activeTab.set($event + '')">
2957
- <p-tablist>
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
+ >
2958
3265
  @for (tab of tabs(); track tab.value) {
2959
- <p-tab [value]="tab.value">
3266
+ <p-tab [value]="tab.value" [pt]="{ root: { class: 'flex-1 justify-center' } }">
2960
3267
  @if (tab.icon) {
2961
3268
  <i [class]="tab.icon" class="mr-2 text-sm"></i>
2962
3269
  }
@@ -2964,12 +3271,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2964
3271
  </p-tab>
2965
3272
  }
2966
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)">
2967
3279
 
2968
- <!-- Content + Sidebar row below tab bar -->
2969
- <div class="flex flex-col lg:flex-row items-start gap-6 w-full py-4 sm:py-6">
3280
+ <!-- Content + Sidebar row -->
3281
+ <div class="flex flex-col lg:flex-row items-start gap-6 w-full py-4 lg:py-6">
2970
3282
 
2971
3283
  <!-- Main column: tab panels -->
2972
- <div class="flex-1 min-w-0 flex flex-col gap-6">
3284
+ <div class="w-full flex-1 min-w-0 flex flex-col gap-6">
2973
3285
  <p-tabpanels>
2974
3286
  @for (tab of tabs(); track tab.value) {
2975
3287
  <p-tabpanel [value]="tab.value">
@@ -2981,27 +3293,658 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
2981
3293
  </p-tabpanel>
2982
3294
  }
2983
3295
  </p-tabpanels>
2984
-
2985
- <!-- Footer below tab content -->
2986
- <ng-content select="[ux-detail-footer]" />
2987
3296
  </div>
2988
3297
 
2989
3298
  <!-- Sidebar -->
2990
- <aside class="w-full lg:w-[380px] shrink-0 flex flex-col lg:sticky lg:top-4 lg:self-start pb-8">
3299
+ <aside class="w-full lg:w-[380px] shrink-0 flex flex-col lg:sticky lg:top-4 lg:self-start lg:pb-8">
2991
3300
  <div class="ux-dl__sidebar-inner w-full">
2992
3301
  <ng-content select="[ux-detail-sidebar]" />
2993
3302
  </div>
2994
3303
  </aside>
2995
3304
  </div>
2996
- </p-tabs>
2997
- </div>
3305
+
3306
+ </div>
3307
+
3308
+ </p-tabs>
3309
+
2998
3310
  </div>
2999
- `, styles: [":host{display:block;min-height:calc(100vh - 4rem);font-family:var(--p-font-family, \"Noto Sans\", sans-serif);background:transparent;color:var(--p-text-color)}.ux-dl__header{background:transparent;border-bottom:1px solid var(--p-content-border-color)}.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,.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}:host :deep p-tablist{position:sticky;top:0;z-index:5;background:var(--p-content-background, var(--p-primary-400))}:host :deep p-tablist .p-tablist-content{width:100%}:host :deep p-tablist .p-tablist-tab-list{width:100%}:host :deep p-tab{flex:1;justify-content:center}\n"] }]
3000
- }], 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: [{
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: [{
3001
3313
  type: ContentChildren,
3002
3314
  args: [DetailTabDirective]
3003
3315
  }] } });
3004
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
+
3005
3948
  /*
3006
3949
  * Public API Surface of @unopsitg/ux
3007
3950
  */
@@ -3010,5 +3953,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
3010
3953
  * Generated bundle index. Do not edit.
3011
3954
  */
3012
3955
 
3013
- export { AiCardBgComponent, AiInsightsCardComponent, AppBreadcrumb, AppConfigurator, AppFooter, AppLayout, AppMenu, AppMenuitem, AppRightMenu, AppSearch, AppSidebar, AppTopbar, AuthLayout, BrandContrast, BrandCrisp, BrandSoft, DetailLayoutComponent, DetailTabDirective, 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 };
3014
3957
  //# sourceMappingURL=unopsitg-ux.mjs.map