@excellore/ui 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,64 +1,64 @@
1
- # Ui
2
-
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.2.0.
4
-
5
- ## Code scaffolding
6
-
7
- Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
8
-
9
- ```bash
10
- ng generate component component-name
11
- ```
12
-
13
- For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
14
-
15
- ```bash
16
- ng generate --help
17
- ```
18
-
19
- ## Building
20
-
21
- To build the library, run:
22
-
23
- ```bash
24
- ng build ui
25
- ```
26
-
27
- This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
28
-
29
- ### Publishing the Library
30
-
31
- Once the project is built, you can publish your library by following these steps:
32
-
33
- 1. Navigate to the `dist` directory:
34
-
35
- ```bash
36
- cd dist/ui
37
- ```
38
-
39
- 2. Run the `npm publish` command to publish your library to the npm registry:
40
- ```bash
41
- npm publish
42
- ```
43
-
44
- ## Running unit tests
45
-
46
- To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
47
-
48
- ```bash
49
- ng test
50
- ```
51
-
52
- ## Running end-to-end tests
53
-
54
- For end-to-end (e2e) testing, run:
55
-
56
- ```bash
57
- ng e2e
58
- ```
59
-
60
- Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
61
-
62
- ## Additional Resources
63
-
64
- For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
1
+ # Ui
2
+
3
+ This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.2.0.
4
+
5
+ ## Code scaffolding
6
+
7
+ Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
8
+
9
+ ```bash
10
+ ng generate component component-name
11
+ ```
12
+
13
+ For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
14
+
15
+ ```bash
16
+ ng generate --help
17
+ ```
18
+
19
+ ## Building
20
+
21
+ To build the library, run:
22
+
23
+ ```bash
24
+ ng build ui
25
+ ```
26
+
27
+ This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
28
+
29
+ ### Publishing the Library
30
+
31
+ Once the project is built, you can publish your library by following these steps:
32
+
33
+ 1. Navigate to the `dist` directory:
34
+
35
+ ```bash
36
+ cd dist/ui
37
+ ```
38
+
39
+ 2. Run the `npm publish` command to publish your library to the npm registry:
40
+ ```bash
41
+ npm publish
42
+ ```
43
+
44
+ ## Running unit tests
45
+
46
+ To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
47
+
48
+ ```bash
49
+ ng test
50
+ ```
51
+
52
+ ## Running end-to-end tests
53
+
54
+ For end-to-end (e2e) testing, run:
55
+
56
+ ```bash
57
+ ng e2e
58
+ ```
59
+
60
+ Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
61
+
62
+ ## Additional Resources
63
+
64
+ For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
@@ -153,11 +153,11 @@ class AppSwitcherComponent {
153
153
  }
154
154
  }
155
155
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AppSwitcherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
156
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: AppSwitcherComponent, isStandalone: true, selector: "lib-app-switcher", inputs: { applications: { classPropertyName: "applications", publicName: "applications", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { applicationSelected: "applicationSelected" }, host: { listeners: { "document:click": "onOutsideClick($event)" } }, ngImport: i0, template: "<div class=\"app-switcher-wrapper relative inline-block text-left\">\n <button\n type=\"button\"\n class=\"grid h-9 w-9 place-items-center rounded-md border border-transparent text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\n (click)=\"toggleSwitcher()\"\n aria-haspopup=\"true\"\n [aria-expanded]=\"isOpen()\"\n aria-label=\"Toggle application switcher\"\n >\n <lucide-icon [name]=\"gridIcon\" class=\"h-4.5 w-4.5\" />\n </button>\n\n @if (isOpen()) {\n <div class=\"app-switcher-dropdown absolute right-0 mt-2 w-[320px] origin-top-right rounded-lg border border-[var(--border)] bg-[var(--background)] p-3 shadow-lg focus:outline-none z-50\">\n <div class=\"mb-2.5 flex items-center justify-between border-b border-[var(--border)] pb-2\">\n <h3 class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Excellore Apps</h3>\n </div>\n \n @if (applications().length === 0) {\n <div class=\"py-6 text-center text-xs text-[var(--secondary)]\">\n No other apps available\n </div>\n } @else {\n <div class=\"grid grid-cols-3 gap-1.5\">\n @for (app of applications(); track app.id) {\n @if (app.available !== false) {\n <a\n [href]=\"app.url\"\n (click)=\"onSelect(app)\"\n class=\"flex flex-col items-center gap-1.5 rounded-lg p-2 text-center no-underline transition hover:bg-[rgba(var(--foreground),0.05)] border border-transparent hover:border-[var(--border)] cursor-pointer\"\n >\n <div class=\"flex h-10 w-10 items-center justify-center rounded-lg bg-[rgba(var(--primary),0.08)] text-[var(--primary)] text-sm font-bold border border-[rgba(var(--primary),0.12)]\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <span class=\"text-xs font-medium text-[var(--foreground)] truncate w-full\" [title]=\"app.name\">{{ app.name }}</span>\n </a>\n } @else {\n <div class=\"flex flex-col items-center gap-1.5 rounded-lg p-2 text-center opacity-40 select-none\">\n <div class=\"flex h-10 w-10 items-center justify-center rounded-lg bg-[rgba(var(--foreground),0.04)] text-[var(--secondary)] text-sm font-bold border border-dashed border-[var(--border)]\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"flex flex-col items-center w-full\">\n <span class=\"text-xs font-medium text-[var(--foreground)] truncate w-full\">{{ app.name }}</span>\n <span class=\"text-[9px] font-medium tracking-wider text-[var(--secondary)] uppercase\">Soon</span>\n </div>\n </div>\n }\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host{display:inline-block}.app-switcher-dropdown{box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;animation:switcherEnter .15s cubic-bezier(.16,1,.3,1)}@keyframes switcherEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
156
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: AppSwitcherComponent, isStandalone: true, selector: "lib-app-switcher", inputs: { applications: { classPropertyName: "applications", publicName: "applications", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { applicationSelected: "applicationSelected" }, host: { listeners: { "document:click": "onOutsideClick($event)" } }, ngImport: i0, template: "<div class=\"app-switcher-wrapper relative inline-block text-left\">\r\n <button\n type=\"button\"\n class=\"app-switcher-trigger\"\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Toggle application switcher\"\r\n >\r\n <lucide-icon [name]=\"gridIcon\" class=\"h-4.5 w-4.5\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"app-switcher-dropdown fixed left-2 right-2 top-14 mt-2 max-h-[calc(100dvh-4.5rem)] origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-2.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-[360px] sm:max-w-[calc(100vw-1rem)] sm:origin-top-right\">\n <div class=\"mb-1.5 flex items-center justify-between border-b border-[var(--border)] px-1 pb-2.5\">\n <div>\n <h3 class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Excellore Apps</h3>\n <p class=\"mt-0.5 text-[11px] text-[var(--secondary)]\">Switch between available workspaces</p>\n </div>\n </div>\n \n @if (applications().length === 0) {\n <div class=\"py-6 text-center text-xs text-[var(--secondary)]\">\n No other apps available\n </div>\n } @else {\n <div class=\"max-h-[calc(100dvh-10rem)] overflow-y-auto py-1 sm:max-h-[420px]\">\n @for (app of applications(); track app.id) {\n @if (app.available !== false) {\n <a\n [href]=\"app.url\"\n (click)=\"onSelect(app)\"\n class=\"app-switcher-item app-switcher-link group flex items-center gap-3 rounded-lg border border-transparent px-2.5 py-2.5 no-underline transition focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\"\n >\n <div class=\"app-switcher-app-mark\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\" [title]=\"app.name\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"app-switcher-open-pill shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide opacity-0 transition group-hover:opacity-100\">\n Open\n </span>\n </a>\n } @else {\n <div class=\"app-switcher-item flex items-center gap-3 rounded-lg px-2.5 py-2.5 opacity-55 select-none\">\n <div class=\"app-switcher-app-mark unavailable\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"shrink-0 rounded-full border border-[var(--border)] px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-[var(--secondary)]\">\n Soon\n </span>\n </div>\n }\n }\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.app-switcher-trigger{display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease}.app-switcher-trigger:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.app-switcher-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.app-switcher-dropdown{box-shadow:0 12px 30px #0f172a24;animation:switcherEnter .15s cubic-bezier(.16,1,.3,1)}.app-switcher-item+.app-switcher-item{margin-top:.125rem}.app-switcher-link:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 34%,transparent)}.app-switcher-app-mark{display:flex;width:2.5rem;height:2.5rem;align-items:center;justify-content:center;flex-shrink:0;border-radius:.7rem;color:var(--primary);background:color-mix(in srgb,var(--primary) 9%,transparent);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--primary) 15%,transparent);font-size:.875rem;font-weight:800}.app-switcher-app-mark.unavailable{border:1px dashed var(--border);color:var(--secondary);background:color-mix(in srgb,var(--border) 32%,transparent);box-shadow:none}.app-switcher-open-pill{color:var(--primary);background:color-mix(in srgb,var(--primary) 9%,transparent)}.app-switcher-dropdown ::-webkit-scrollbar{width:.45rem}.app-switcher-dropdown ::-webkit-scrollbar-track{background:transparent}.app-switcher-dropdown ::-webkit-scrollbar-thumb{background:var(--border);border-radius:999px}@keyframes switcherEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
157
157
  }
158
158
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AppSwitcherComponent, decorators: [{
159
159
  type: Component,
160
- args: [{ selector: 'lib-app-switcher', standalone: true, imports: [CommonModule, LucideAngularModule], template: "<div class=\"app-switcher-wrapper relative inline-block text-left\">\n <button\n type=\"button\"\n class=\"grid h-9 w-9 place-items-center rounded-md border border-transparent text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\n (click)=\"toggleSwitcher()\"\n aria-haspopup=\"true\"\n [aria-expanded]=\"isOpen()\"\n aria-label=\"Toggle application switcher\"\n >\n <lucide-icon [name]=\"gridIcon\" class=\"h-4.5 w-4.5\" />\n </button>\n\n @if (isOpen()) {\n <div class=\"app-switcher-dropdown absolute right-0 mt-2 w-[320px] origin-top-right rounded-lg border border-[var(--border)] bg-[var(--background)] p-3 shadow-lg focus:outline-none z-50\">\n <div class=\"mb-2.5 flex items-center justify-between border-b border-[var(--border)] pb-2\">\n <h3 class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Excellore Apps</h3>\n </div>\n \n @if (applications().length === 0) {\n <div class=\"py-6 text-center text-xs text-[var(--secondary)]\">\n No other apps available\n </div>\n } @else {\n <div class=\"grid grid-cols-3 gap-1.5\">\n @for (app of applications(); track app.id) {\n @if (app.available !== false) {\n <a\n [href]=\"app.url\"\n (click)=\"onSelect(app)\"\n class=\"flex flex-col items-center gap-1.5 rounded-lg p-2 text-center no-underline transition hover:bg-[rgba(var(--foreground),0.05)] border border-transparent hover:border-[var(--border)] cursor-pointer\"\n >\n <div class=\"flex h-10 w-10 items-center justify-center rounded-lg bg-[rgba(var(--primary),0.08)] text-[var(--primary)] text-sm font-bold border border-[rgba(var(--primary),0.12)]\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <span class=\"text-xs font-medium text-[var(--foreground)] truncate w-full\" [title]=\"app.name\">{{ app.name }}</span>\n </a>\n } @else {\n <div class=\"flex flex-col items-center gap-1.5 rounded-lg p-2 text-center opacity-40 select-none\">\n <div class=\"flex h-10 w-10 items-center justify-center rounded-lg bg-[rgba(var(--foreground),0.04)] text-[var(--secondary)] text-sm font-bold border border-dashed border-[var(--border)]\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"flex flex-col items-center w-full\">\n <span class=\"text-xs font-medium text-[var(--foreground)] truncate w-full\">{{ app.name }}</span>\n <span class=\"text-[9px] font-medium tracking-wider text-[var(--secondary)] uppercase\">Soon</span>\n </div>\n </div>\n }\n }\n </div>\n }\n </div>\n }\n</div>\n", styles: [":host{display:inline-block}.app-switcher-dropdown{box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;animation:switcherEnter .15s cubic-bezier(.16,1,.3,1)}@keyframes switcherEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"] }]
160
+ args: [{ selector: 'lib-app-switcher', standalone: true, imports: [CommonModule, LucideAngularModule], template: "<div class=\"app-switcher-wrapper relative inline-block text-left\">\r\n <button\n type=\"button\"\n class=\"app-switcher-trigger\"\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Toggle application switcher\"\r\n >\r\n <lucide-icon [name]=\"gridIcon\" class=\"h-4.5 w-4.5\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"app-switcher-dropdown fixed left-2 right-2 top-14 mt-2 max-h-[calc(100dvh-4.5rem)] origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-2.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-[360px] sm:max-w-[calc(100vw-1rem)] sm:origin-top-right\">\n <div class=\"mb-1.5 flex items-center justify-between border-b border-[var(--border)] px-1 pb-2.5\">\n <div>\n <h3 class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Excellore Apps</h3>\n <p class=\"mt-0.5 text-[11px] text-[var(--secondary)]\">Switch between available workspaces</p>\n </div>\n </div>\n \n @if (applications().length === 0) {\n <div class=\"py-6 text-center text-xs text-[var(--secondary)]\">\n No other apps available\n </div>\n } @else {\n <div class=\"max-h-[calc(100dvh-10rem)] overflow-y-auto py-1 sm:max-h-[420px]\">\n @for (app of applications(); track app.id) {\n @if (app.available !== false) {\n <a\n [href]=\"app.url\"\n (click)=\"onSelect(app)\"\n class=\"app-switcher-item app-switcher-link group flex items-center gap-3 rounded-lg border border-transparent px-2.5 py-2.5 no-underline transition focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\"\n >\n <div class=\"app-switcher-app-mark\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\" [title]=\"app.name\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"app-switcher-open-pill shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide opacity-0 transition group-hover:opacity-100\">\n Open\n </span>\n </a>\n } @else {\n <div class=\"app-switcher-item flex items-center gap-3 rounded-lg px-2.5 py-2.5 opacity-55 select-none\">\n <div class=\"app-switcher-app-mark unavailable\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"shrink-0 rounded-full border border-[var(--border)] px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-[var(--secondary)]\">\n Soon\n </span>\n </div>\n }\n }\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.app-switcher-trigger{display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease}.app-switcher-trigger:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.app-switcher-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.app-switcher-dropdown{box-shadow:0 12px 30px #0f172a24;animation:switcherEnter .15s cubic-bezier(.16,1,.3,1)}.app-switcher-item+.app-switcher-item{margin-top:.125rem}.app-switcher-link:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 34%,transparent)}.app-switcher-app-mark{display:flex;width:2.5rem;height:2.5rem;align-items:center;justify-content:center;flex-shrink:0;border-radius:.7rem;color:var(--primary);background:color-mix(in srgb,var(--primary) 9%,transparent);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--primary) 15%,transparent);font-size:.875rem;font-weight:800}.app-switcher-app-mark.unavailable{border:1px dashed var(--border);color:var(--secondary);background:color-mix(in srgb,var(--border) 32%,transparent);box-shadow:none}.app-switcher-open-pill{color:var(--primary);background:color-mix(in srgb,var(--primary) 9%,transparent)}.app-switcher-dropdown ::-webkit-scrollbar{width:.45rem}.app-switcher-dropdown ::-webkit-scrollbar-track{background:transparent}.app-switcher-dropdown ::-webkit-scrollbar-thumb{background:var(--border);border-radius:999px}@keyframes switcherEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"] }]
161
161
  }], propDecorators: { applications: [{ type: i0.Input, args: [{ isSignal: true, alias: "applications", required: false }] }], applicationSelected: [{ type: i0.Output, args: ["applicationSelected"] }], onOutsideClick: [{
162
162
  type: HostListener,
163
163
  args: ['document:click', ['$event']]
@@ -188,11 +188,11 @@ class OrganizationSwitcherComponent {
188
188
  }
189
189
  }
190
190
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: OrganizationSwitcherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
191
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: OrganizationSwitcherComponent, isStandalone: true, selector: "lib-organization-switcher", inputs: { organizations: { classPropertyName: "organizations", publicName: "organizations", isSignal: true, isRequired: false, transformFunction: null }, selectedOrganization: { classPropertyName: "selectedOrganization", publicName: "selectedOrganization", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { organizationChanged: "organizationChanged" }, host: { listeners: { "document:click": "onOutsideClick($event)" } }, ngImport: i0, template: "<div class=\"org-switcher-wrapper relative inline-block text-left\">\n <button\n type=\"button\"\n class=\"flex items-center gap-1.5 rounded-md border border-[var(--border)] bg-[var(--background)] px-2.5 py-1.5 text-xs font-medium text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\n (click)=\"toggleSwitcher()\"\n aria-haspopup=\"true\"\n [aria-expanded]=\"isOpen()\"\n aria-label=\"Switch organization\"\n >\n <lucide-icon [name]=\"buildingIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)]\" />\n <span class=\"truncate max-w-[120px]\">{{ selectedOrganization()?.name || 'Select Tenant' }}</span>\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3 w-3 text-[var(--secondary)] transition-transform duration-200\" [class.rotate-180]=\"isOpen()\" />\n </button>\n\n @if (isOpen()) {\n <div class=\"org-switcher-dropdown absolute left-0 mt-2 w-52 origin-top-left rounded-lg border border-[var(--border)] bg-[var(--background)] p-1 shadow-lg focus:outline-none z-50\">\n <div class=\"px-2 py-1.5 border-b border-[var(--border)] mb-1\">\n <span class=\"text-[9px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Switch Organization</span>\n </div>\n\n <div class=\"py-0.5 space-y-0.5 max-h-48 overflow-y-auto\">\n @for (org of organizations(); track org.id) {\n <button\n type=\"button\"\n class=\"flex w-full items-center justify-between rounded-md px-2 py-1.5 text-xs text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\n (click)=\"onSelect(org)\"\n >\n <span class=\"truncate pr-2\" [class.font-semibold]=\"org.id === selectedOrganization()?.id\">{{ org.name }}</span>\n @if (org.id === selectedOrganization()?.id) {\n <lucide-icon [name]=\"checkIcon\" class=\"h-3.5 w-3.5 text-[var(--primary)] flex-shrink-0\" />\n }\n </button>\n }\n </div>\n </div>\n }\n</div>\n", styles: [":host{display:inline-block}.org-switcher-dropdown{box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;animation:orgEnter .15s cubic-bezier(.16,1,.3,1)}@keyframes orgEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
191
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: OrganizationSwitcherComponent, isStandalone: true, selector: "lib-organization-switcher", inputs: { organizations: { classPropertyName: "organizations", publicName: "organizations", isSignal: true, isRequired: false, transformFunction: null }, selectedOrganization: { classPropertyName: "selectedOrganization", publicName: "selectedOrganization", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { organizationChanged: "organizationChanged" }, host: { listeners: { "document:click": "onOutsideClick($event)" } }, ngImport: i0, template: "<div class=\"org-switcher-wrapper relative inline-block text-left\">\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center gap-1.5 rounded-md border border-[var(--border)] bg-[var(--background)] px-2.5 py-1.5 text-xs font-medium text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\r\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Switch organization\"\r\n >\r\n <lucide-icon [name]=\"buildingIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)]\" />\r\n <span class=\"truncate max-w-[120px]\">{{ selectedOrganization()?.name || 'Select Tenant' }}</span>\r\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3 w-3 text-[var(--secondary)] transition-transform duration-200\" [class.rotate-180]=\"isOpen()\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"org-switcher-dropdown absolute left-0 mt-2 w-52 origin-top-left rounded-lg border border-[var(--border)] bg-[var(--background)] p-1 shadow-lg focus:outline-none z-50\">\r\n <div class=\"px-2 py-1.5 border-b border-[var(--border)] mb-1\">\r\n <span class=\"text-[9px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Switch Organization</span>\r\n </div>\r\n\r\n <div class=\"py-0.5 space-y-0.5 max-h-48 overflow-y-auto\">\r\n @for (org of organizations(); track org.id) {\r\n <button\r\n type=\"button\"\r\n class=\"flex w-full items-center justify-between rounded-md px-2 py-1.5 text-xs text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\r\n (click)=\"onSelect(org)\"\r\n >\r\n <span class=\"truncate pr-2\" [class.font-semibold]=\"org.id === selectedOrganization()?.id\">{{ org.name }}</span>\r\n @if (org.id === selectedOrganization()?.id) {\r\n <lucide-icon [name]=\"checkIcon\" class=\"h-3.5 w-3.5 text-[var(--primary)] flex-shrink-0\" />\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.org-switcher-dropdown{box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;animation:orgEnter .15s cubic-bezier(.16,1,.3,1)}@keyframes orgEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
192
192
  }
193
193
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: OrganizationSwitcherComponent, decorators: [{
194
194
  type: Component,
195
- args: [{ selector: 'lib-organization-switcher', standalone: true, imports: [CommonModule, LucideAngularModule], template: "<div class=\"org-switcher-wrapper relative inline-block text-left\">\n <button\n type=\"button\"\n class=\"flex items-center gap-1.5 rounded-md border border-[var(--border)] bg-[var(--background)] px-2.5 py-1.5 text-xs font-medium text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\n (click)=\"toggleSwitcher()\"\n aria-haspopup=\"true\"\n [aria-expanded]=\"isOpen()\"\n aria-label=\"Switch organization\"\n >\n <lucide-icon [name]=\"buildingIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)]\" />\n <span class=\"truncate max-w-[120px]\">{{ selectedOrganization()?.name || 'Select Tenant' }}</span>\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3 w-3 text-[var(--secondary)] transition-transform duration-200\" [class.rotate-180]=\"isOpen()\" />\n </button>\n\n @if (isOpen()) {\n <div class=\"org-switcher-dropdown absolute left-0 mt-2 w-52 origin-top-left rounded-lg border border-[var(--border)] bg-[var(--background)] p-1 shadow-lg focus:outline-none z-50\">\n <div class=\"px-2 py-1.5 border-b border-[var(--border)] mb-1\">\n <span class=\"text-[9px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Switch Organization</span>\n </div>\n\n <div class=\"py-0.5 space-y-0.5 max-h-48 overflow-y-auto\">\n @for (org of organizations(); track org.id) {\n <button\n type=\"button\"\n class=\"flex w-full items-center justify-between rounded-md px-2 py-1.5 text-xs text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\n (click)=\"onSelect(org)\"\n >\n <span class=\"truncate pr-2\" [class.font-semibold]=\"org.id === selectedOrganization()?.id\">{{ org.name }}</span>\n @if (org.id === selectedOrganization()?.id) {\n <lucide-icon [name]=\"checkIcon\" class=\"h-3.5 w-3.5 text-[var(--primary)] flex-shrink-0\" />\n }\n </button>\n }\n </div>\n </div>\n }\n</div>\n", styles: [":host{display:inline-block}.org-switcher-dropdown{box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;animation:orgEnter .15s cubic-bezier(.16,1,.3,1)}@keyframes orgEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"] }]
195
+ args: [{ selector: 'lib-organization-switcher', standalone: true, imports: [CommonModule, LucideAngularModule], template: "<div class=\"org-switcher-wrapper relative inline-block text-left\">\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center gap-1.5 rounded-md border border-[var(--border)] bg-[var(--background)] px-2.5 py-1.5 text-xs font-medium text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\r\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Switch organization\"\r\n >\r\n <lucide-icon [name]=\"buildingIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)]\" />\r\n <span class=\"truncate max-w-[120px]\">{{ selectedOrganization()?.name || 'Select Tenant' }}</span>\r\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3 w-3 text-[var(--secondary)] transition-transform duration-200\" [class.rotate-180]=\"isOpen()\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"org-switcher-dropdown absolute left-0 mt-2 w-52 origin-top-left rounded-lg border border-[var(--border)] bg-[var(--background)] p-1 shadow-lg focus:outline-none z-50\">\r\n <div class=\"px-2 py-1.5 border-b border-[var(--border)] mb-1\">\r\n <span class=\"text-[9px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Switch Organization</span>\r\n </div>\r\n\r\n <div class=\"py-0.5 space-y-0.5 max-h-48 overflow-y-auto\">\r\n @for (org of organizations(); track org.id) {\r\n <button\r\n type=\"button\"\r\n class=\"flex w-full items-center justify-between rounded-md px-2 py-1.5 text-xs text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\r\n (click)=\"onSelect(org)\"\r\n >\r\n <span class=\"truncate pr-2\" [class.font-semibold]=\"org.id === selectedOrganization()?.id\">{{ org.name }}</span>\r\n @if (org.id === selectedOrganization()?.id) {\r\n <lucide-icon [name]=\"checkIcon\" class=\"h-3.5 w-3.5 text-[var(--primary)] flex-shrink-0\" />\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.org-switcher-dropdown{box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;animation:orgEnter .15s cubic-bezier(.16,1,.3,1)}@keyframes orgEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"] }]
196
196
  }], propDecorators: { organizations: [{ type: i0.Input, args: [{ isSignal: true, alias: "organizations", required: false }] }], selectedOrganization: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedOrganization", required: false }] }], organizationChanged: [{ type: i0.Output, args: ["organizationChanged"] }], onOutsideClick: [{
197
197
  type: HostListener,
198
198
  args: ['document:click', ['$event']]
@@ -236,11 +236,11 @@ class NotificationPanelComponent {
236
236
  return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
237
237
  }
238
238
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: NotificationPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
239
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: NotificationPanelComponent, isStandalone: true, selector: "lib-notification-panel", inputs: { notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { notificationClick: "notificationClick", markRead: "markRead", clearAll: "clearAll" }, ngImport: i0, template: "<div class=\"notification-panel-inner flex flex-col h-full max-h-[380px] w-full text-left\">\n <!-- Panel Header -->\n <div class=\"flex items-center justify-between border-b border-[var(--border)] px-3.5 py-2\">\n <span class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Notifications</span>\n @if (notifications().length > 0) {\n <button\n type=\"button\"\n class=\"flex items-center gap-1 text-[9px] font-bold text-[var(--secondary)] hover:text-[var(--danger,#ef4444)] hover:underline cursor-pointer\"\n (click)=\"onClearAll($event)\"\n >\n <lucide-icon [name]=\"trashIcon\" class=\"h-3 w-3\" />\n <span>Clear All</span>\n </button>\n }\n </div>\n\n <!-- Panel Body -->\n <div class=\"flex-1 overflow-y-auto min-h-0 py-0.5 divide-y divide-[var(--border)]\">\n @if (notifications().length === 0) {\n <div class=\"flex flex-col items-center justify-center py-10 px-4 text-center select-none\">\n <div class=\"flex h-10 w-10 items-center justify-center rounded-full bg-[rgba(var(--secondary),0.06)] text-[var(--secondary)] mb-2.5\">\n <lucide-icon [name]=\"bellOffIcon\" class=\"h-4.5 w-4.5\" />\n </div>\n <p class=\"text-xs font-semibold text-[var(--foreground)]\">All caught up</p>\n <p class=\"text-[10px] text-[var(--secondary)] mt-0.5\">No new notifications to display.</p>\n </div>\n } @else {\n @for (item of notifications(); track item.id) {\n <div\n (click)=\"onNotificationClick(item)\"\n class=\"notification-item flex gap-2.5 p-3 transition hover:bg-[rgba(var(--foreground),0.02)] cursor-pointer relative\"\n [class.bg-[rgba(var(--primary),0.015)]]=\"!item.read\"\n >\n <!-- Read status dot -->\n <div class=\"flex-shrink-0 pt-1\">\n @if (!item.read) {\n <span class=\"block h-1.5 w-1.5 rounded-full bg-[var(--primary)] animate-pulse\"></span>\n } @else {\n <span class=\"block h-1.5 w-1.5 rounded-full bg-transparent\"></span>\n }\n </div>\n\n <!-- Notification content -->\n <div class=\"flex-1 min-w-0\">\n <h4 class=\"text-xs font-semibold text-[var(--foreground)] truncate\" [class.font-bold]=\"!item.read\">\n {{ item.title }}\n </h4>\n <p class=\"text-[10.5px] text-[var(--secondary)] mt-0.5 leading-snug break-words\">\n {{ item.message }}\n </p>\n <span class=\"block text-[9px] text-[var(--secondary)] mt-1 font-medium\">\n {{ formatTime(item.createdAt) }}\n </span>\n </div>\n\n <!-- Actions -->\n @if (!item.read) {\n <div class=\"flex items-center flex-shrink-0\">\n <button\n type=\"button\"\n class=\"mark-read-btn grid h-6 w-6 place-items-center rounded-full text-[var(--secondary)] hover:text-[var(--primary)] hover:bg-[rgba(var(--foreground),0.05)] cursor-pointer\"\n [title]=\"'Mark as read'\"\n (click)=\"onMarkRead(item.id, $event)\"\n >\n <lucide-icon [name]=\"checkCheckIcon\" class=\"h-3.5 w-3.5\" />\n </button>\n </div>\n }\n </div>\n }\n }\n </div>\n</div>\n", styles: [":host{display:block;width:100%}.notification-item{border-left:2px solid transparent}.notification-item:hover{border-left-color:var(--primary)}.mark-read-btn{opacity:.5;transition:opacity .2s,background-color .2s}.notification-item:hover .mark-read-btn{opacity:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
239
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: NotificationPanelComponent, isStandalone: true, selector: "lib-notification-panel", inputs: { notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { notificationClick: "notificationClick", markRead: "markRead", clearAll: "clearAll" }, ngImport: i0, template: "<div class=\"notification-panel-inner flex flex-col h-full max-h-[380px] w-full text-left\">\r\n <!-- Panel Header -->\r\n <div class=\"flex items-center justify-between border-b border-[var(--border)] px-3.5 py-2\">\r\n <span class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Notifications</span>\r\n @if (notifications().length > 0) {\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center gap-1 text-[9px] font-bold text-[var(--secondary)] hover:text-[var(--danger,#ef4444)] hover:underline cursor-pointer\"\r\n (click)=\"onClearAll($event)\"\r\n >\r\n <lucide-icon [name]=\"trashIcon\" class=\"h-3 w-3\" />\r\n <span>Clear All</span>\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Panel Body -->\r\n <div class=\"flex-1 overflow-y-auto min-h-0 py-0.5 divide-y divide-[var(--border)]\">\r\n @if (notifications().length === 0) {\r\n <div class=\"flex flex-col items-center justify-center py-10 px-4 text-center select-none\">\r\n <div class=\"flex h-10 w-10 items-center justify-center rounded-full bg-[rgba(var(--secondary),0.06)] text-[var(--secondary)] mb-2.5\">\r\n <lucide-icon [name]=\"bellOffIcon\" class=\"h-4.5 w-4.5\" />\r\n </div>\r\n <p class=\"text-xs font-semibold text-[var(--foreground)]\">All caught up</p>\r\n <p class=\"text-[10px] text-[var(--secondary)] mt-0.5\">No new notifications to display.</p>\r\n </div>\r\n } @else {\r\n @for (item of notifications(); track item.id) {\r\n <div\r\n (click)=\"onNotificationClick(item)\"\r\n class=\"notification-item flex gap-2.5 p-3 transition hover:bg-[rgba(var(--foreground),0.02)] cursor-pointer relative\"\r\n [class.bg-[rgba(var(--primary),0.015)]]=\"!item.read\"\r\n >\r\n <!-- Read status dot -->\r\n <div class=\"flex-shrink-0 pt-1\">\r\n @if (!item.read) {\r\n <span class=\"block h-1.5 w-1.5 rounded-full bg-[var(--primary)] animate-pulse\"></span>\r\n } @else {\r\n <span class=\"block h-1.5 w-1.5 rounded-full bg-transparent\"></span>\r\n }\r\n </div>\r\n\r\n <!-- Notification content -->\r\n <div class=\"flex-1 min-w-0\">\r\n <h4 class=\"text-xs font-semibold text-[var(--foreground)] truncate\" [class.font-bold]=\"!item.read\">\r\n {{ item.title }}\r\n </h4>\r\n <p class=\"text-[10.5px] text-[var(--secondary)] mt-0.5 leading-snug break-words\">\r\n {{ item.message }}\r\n </p>\r\n <span class=\"block text-[9px] text-[var(--secondary)] mt-1 font-medium\">\r\n {{ formatTime(item.createdAt) }}\r\n </span>\r\n </div>\r\n\r\n <!-- Actions -->\r\n @if (!item.read) {\r\n <div class=\"flex items-center flex-shrink-0\">\r\n <button\r\n type=\"button\"\r\n class=\"mark-read-btn grid h-6 w-6 place-items-center rounded-full text-[var(--secondary)] hover:text-[var(--primary)] hover:bg-[rgba(var(--foreground),0.05)] cursor-pointer\"\r\n [title]=\"'Mark as read'\"\r\n (click)=\"onMarkRead(item.id, $event)\"\r\n >\r\n <lucide-icon [name]=\"checkCheckIcon\" class=\"h-3.5 w-3.5\" />\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n</div>\r\n", styles: [":host{display:block;width:100%}.notification-item{border-left:2px solid transparent}.notification-item:hover{border-left-color:var(--primary)}.mark-read-btn{opacity:.5;transition:opacity .2s,background-color .2s}.notification-item:hover .mark-read-btn{opacity:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
240
240
  }
241
241
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: NotificationPanelComponent, decorators: [{
242
242
  type: Component,
243
- args: [{ selector: 'lib-notification-panel', standalone: true, imports: [CommonModule, LucideAngularModule], template: "<div class=\"notification-panel-inner flex flex-col h-full max-h-[380px] w-full text-left\">\n <!-- Panel Header -->\n <div class=\"flex items-center justify-between border-b border-[var(--border)] px-3.5 py-2\">\n <span class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Notifications</span>\n @if (notifications().length > 0) {\n <button\n type=\"button\"\n class=\"flex items-center gap-1 text-[9px] font-bold text-[var(--secondary)] hover:text-[var(--danger,#ef4444)] hover:underline cursor-pointer\"\n (click)=\"onClearAll($event)\"\n >\n <lucide-icon [name]=\"trashIcon\" class=\"h-3 w-3\" />\n <span>Clear All</span>\n </button>\n }\n </div>\n\n <!-- Panel Body -->\n <div class=\"flex-1 overflow-y-auto min-h-0 py-0.5 divide-y divide-[var(--border)]\">\n @if (notifications().length === 0) {\n <div class=\"flex flex-col items-center justify-center py-10 px-4 text-center select-none\">\n <div class=\"flex h-10 w-10 items-center justify-center rounded-full bg-[rgba(var(--secondary),0.06)] text-[var(--secondary)] mb-2.5\">\n <lucide-icon [name]=\"bellOffIcon\" class=\"h-4.5 w-4.5\" />\n </div>\n <p class=\"text-xs font-semibold text-[var(--foreground)]\">All caught up</p>\n <p class=\"text-[10px] text-[var(--secondary)] mt-0.5\">No new notifications to display.</p>\n </div>\n } @else {\n @for (item of notifications(); track item.id) {\n <div\n (click)=\"onNotificationClick(item)\"\n class=\"notification-item flex gap-2.5 p-3 transition hover:bg-[rgba(var(--foreground),0.02)] cursor-pointer relative\"\n [class.bg-[rgba(var(--primary),0.015)]]=\"!item.read\"\n >\n <!-- Read status dot -->\n <div class=\"flex-shrink-0 pt-1\">\n @if (!item.read) {\n <span class=\"block h-1.5 w-1.5 rounded-full bg-[var(--primary)] animate-pulse\"></span>\n } @else {\n <span class=\"block h-1.5 w-1.5 rounded-full bg-transparent\"></span>\n }\n </div>\n\n <!-- Notification content -->\n <div class=\"flex-1 min-w-0\">\n <h4 class=\"text-xs font-semibold text-[var(--foreground)] truncate\" [class.font-bold]=\"!item.read\">\n {{ item.title }}\n </h4>\n <p class=\"text-[10.5px] text-[var(--secondary)] mt-0.5 leading-snug break-words\">\n {{ item.message }}\n </p>\n <span class=\"block text-[9px] text-[var(--secondary)] mt-1 font-medium\">\n {{ formatTime(item.createdAt) }}\n </span>\n </div>\n\n <!-- Actions -->\n @if (!item.read) {\n <div class=\"flex items-center flex-shrink-0\">\n <button\n type=\"button\"\n class=\"mark-read-btn grid h-6 w-6 place-items-center rounded-full text-[var(--secondary)] hover:text-[var(--primary)] hover:bg-[rgba(var(--foreground),0.05)] cursor-pointer\"\n [title]=\"'Mark as read'\"\n (click)=\"onMarkRead(item.id, $event)\"\n >\n <lucide-icon [name]=\"checkCheckIcon\" class=\"h-3.5 w-3.5\" />\n </button>\n </div>\n }\n </div>\n }\n }\n </div>\n</div>\n", styles: [":host{display:block;width:100%}.notification-item{border-left:2px solid transparent}.notification-item:hover{border-left-color:var(--primary)}.mark-read-btn{opacity:.5;transition:opacity .2s,background-color .2s}.notification-item:hover .mark-read-btn{opacity:1}\n"] }]
243
+ args: [{ selector: 'lib-notification-panel', standalone: true, imports: [CommonModule, LucideAngularModule], template: "<div class=\"notification-panel-inner flex flex-col h-full max-h-[380px] w-full text-left\">\r\n <!-- Panel Header -->\r\n <div class=\"flex items-center justify-between border-b border-[var(--border)] px-3.5 py-2\">\r\n <span class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Notifications</span>\r\n @if (notifications().length > 0) {\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center gap-1 text-[9px] font-bold text-[var(--secondary)] hover:text-[var(--danger,#ef4444)] hover:underline cursor-pointer\"\r\n (click)=\"onClearAll($event)\"\r\n >\r\n <lucide-icon [name]=\"trashIcon\" class=\"h-3 w-3\" />\r\n <span>Clear All</span>\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Panel Body -->\r\n <div class=\"flex-1 overflow-y-auto min-h-0 py-0.5 divide-y divide-[var(--border)]\">\r\n @if (notifications().length === 0) {\r\n <div class=\"flex flex-col items-center justify-center py-10 px-4 text-center select-none\">\r\n <div class=\"flex h-10 w-10 items-center justify-center rounded-full bg-[rgba(var(--secondary),0.06)] text-[var(--secondary)] mb-2.5\">\r\n <lucide-icon [name]=\"bellOffIcon\" class=\"h-4.5 w-4.5\" />\r\n </div>\r\n <p class=\"text-xs font-semibold text-[var(--foreground)]\">All caught up</p>\r\n <p class=\"text-[10px] text-[var(--secondary)] mt-0.5\">No new notifications to display.</p>\r\n </div>\r\n } @else {\r\n @for (item of notifications(); track item.id) {\r\n <div\r\n (click)=\"onNotificationClick(item)\"\r\n class=\"notification-item flex gap-2.5 p-3 transition hover:bg-[rgba(var(--foreground),0.02)] cursor-pointer relative\"\r\n [class.bg-[rgba(var(--primary),0.015)]]=\"!item.read\"\r\n >\r\n <!-- Read status dot -->\r\n <div class=\"flex-shrink-0 pt-1\">\r\n @if (!item.read) {\r\n <span class=\"block h-1.5 w-1.5 rounded-full bg-[var(--primary)] animate-pulse\"></span>\r\n } @else {\r\n <span class=\"block h-1.5 w-1.5 rounded-full bg-transparent\"></span>\r\n }\r\n </div>\r\n\r\n <!-- Notification content -->\r\n <div class=\"flex-1 min-w-0\">\r\n <h4 class=\"text-xs font-semibold text-[var(--foreground)] truncate\" [class.font-bold]=\"!item.read\">\r\n {{ item.title }}\r\n </h4>\r\n <p class=\"text-[10.5px] text-[var(--secondary)] mt-0.5 leading-snug break-words\">\r\n {{ item.message }}\r\n </p>\r\n <span class=\"block text-[9px] text-[var(--secondary)] mt-1 font-medium\">\r\n {{ formatTime(item.createdAt) }}\r\n </span>\r\n </div>\r\n\r\n <!-- Actions -->\r\n @if (!item.read) {\r\n <div class=\"flex items-center flex-shrink-0\">\r\n <button\r\n type=\"button\"\r\n class=\"mark-read-btn grid h-6 w-6 place-items-center rounded-full text-[var(--secondary)] hover:text-[var(--primary)] hover:bg-[rgba(var(--foreground),0.05)] cursor-pointer\"\r\n [title]=\"'Mark as read'\"\r\n (click)=\"onMarkRead(item.id, $event)\"\r\n >\r\n <lucide-icon [name]=\"checkCheckIcon\" class=\"h-3.5 w-3.5\" />\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n</div>\r\n", styles: [":host{display:block;width:100%}.notification-item{border-left:2px solid transparent}.notification-item:hover{border-left-color:var(--primary)}.mark-read-btn{opacity:.5;transition:opacity .2s,background-color .2s}.notification-item:hover .mark-read-btn{opacity:1}\n"] }]
244
244
  }], propDecorators: { notifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "notifications", required: false }] }], notificationClick: [{ type: i0.Output, args: ["notificationClick"] }], markRead: [{ type: i0.Output, args: ["markRead"] }], clearAll: [{ type: i0.Output, args: ["clearAll"] }] } });
245
245
 
246
246
  class NotificationBellComponent {
@@ -274,11 +274,11 @@ class NotificationBellComponent {
274
274
  }
275
275
  }
276
276
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: NotificationBellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
277
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: NotificationBellComponent, isStandalone: true, selector: "lib-notification-bell", inputs: { unreadCount: { classPropertyName: "unreadCount", publicName: "unreadCount", isSignal: true, isRequired: false, transformFunction: null }, notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { notificationClick: "notificationClick", markRead: "markRead", clearAll: "clearAll" }, host: { listeners: { "document:click": "onOutsideClick($event)" } }, ngImport: i0, template: "<div class=\"notification-bell-wrapper relative inline-block text-left\">\n <button\n type=\"button\"\n class=\"relative grid h-9 w-9 place-items-center rounded-md border border-transparent text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\n (click)=\"togglePanel()\"\n aria-haspopup=\"true\"\n [aria-expanded]=\"isOpen()\"\n aria-label=\"View notifications\"\n >\n <lucide-icon [name]=\"bellIcon\" class=\"h-4.5 w-4.5\" />\n \n @if (unreadCount() > 0) {\n <span class=\"absolute top-1.5 right-1.5 flex h-2 w-2\">\n <span class=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--danger,#ef4444)] opacity-75\"></span>\n <span class=\"relative inline-flex rounded-full h-2 w-2 bg-[var(--danger,#ef4444)]\"></span>\n </span>\n }\n </button>\n\n @if (isOpen()) {\n <div class=\"notification-dropdown absolute right-0 mt-2 w-80 origin-top-right rounded-lg border border-[var(--border)] bg-[var(--background)] p-0 shadow-lg focus:outline-none z-50\">\n <lib-notification-panel\n [notifications]=\"notifications()\"\n (notificationClick)=\"onNotificationClick($event)\"\n (markRead)=\"onMarkRead($event)\"\n (clearAll)=\"onClearAll()\"\n />\n </div>\n }\n</div>\n", styles: [":host{display:inline-block}.notification-dropdown{box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;animation:bellEnter .15s cubic-bezier(.16,1,.3,1);overflow:hidden}@keyframes bellEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: NotificationPanelComponent, selector: "lib-notification-panel", inputs: ["notifications"], outputs: ["notificationClick", "markRead", "clearAll"] }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
277
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: NotificationBellComponent, isStandalone: true, selector: "lib-notification-bell", inputs: { unreadCount: { classPropertyName: "unreadCount", publicName: "unreadCount", isSignal: true, isRequired: false, transformFunction: null }, notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { notificationClick: "notificationClick", markRead: "markRead", clearAll: "clearAll" }, host: { listeners: { "document:click": "onOutsideClick($event)" } }, ngImport: i0, template: "<div class=\"notification-bell-wrapper relative inline-block text-left\">\r\n <button\n type=\"button\"\n class=\"notification-trigger\"\n (click)=\"togglePanel()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"View notifications\"\r\n >\r\n <lucide-icon [name]=\"bellIcon\" class=\"h-4.5 w-4.5\" />\r\n \r\n @if (unreadCount() > 0) {\r\n <span class=\"absolute top-1.5 right-1.5 flex h-2 w-2\">\r\n <span class=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--danger,#ef4444)] opacity-75\"></span>\r\n <span class=\"relative inline-flex rounded-full h-2 w-2 bg-[var(--danger,#ef4444)]\"></span>\r\n </span>\r\n }\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"notification-dropdown fixed left-2 right-2 top-14 mt-2 origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-0 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-80 sm:origin-top-right\">\n <lib-notification-panel\r\n [notifications]=\"notifications()\"\r\n (notificationClick)=\"onNotificationClick($event)\"\r\n (markRead)=\"onMarkRead($event)\"\r\n (clearAll)=\"onClearAll()\"\r\n />\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.notification-trigger{position:relative;display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease}.notification-trigger:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.notification-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.notification-dropdown{box-shadow:0 12px 30px #0f172a24;animation:bellEnter .15s cubic-bezier(.16,1,.3,1);overflow:hidden}@keyframes bellEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: NotificationPanelComponent, selector: "lib-notification-panel", inputs: ["notifications"], outputs: ["notificationClick", "markRead", "clearAll"] }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
278
278
  }
279
279
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: NotificationBellComponent, decorators: [{
280
280
  type: Component,
281
- args: [{ selector: 'lib-notification-bell', standalone: true, imports: [CommonModule, NotificationPanelComponent, LucideAngularModule], template: "<div class=\"notification-bell-wrapper relative inline-block text-left\">\n <button\n type=\"button\"\n class=\"relative grid h-9 w-9 place-items-center rounded-md border border-transparent text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\n (click)=\"togglePanel()\"\n aria-haspopup=\"true\"\n [aria-expanded]=\"isOpen()\"\n aria-label=\"View notifications\"\n >\n <lucide-icon [name]=\"bellIcon\" class=\"h-4.5 w-4.5\" />\n \n @if (unreadCount() > 0) {\n <span class=\"absolute top-1.5 right-1.5 flex h-2 w-2\">\n <span class=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--danger,#ef4444)] opacity-75\"></span>\n <span class=\"relative inline-flex rounded-full h-2 w-2 bg-[var(--danger,#ef4444)]\"></span>\n </span>\n }\n </button>\n\n @if (isOpen()) {\n <div class=\"notification-dropdown absolute right-0 mt-2 w-80 origin-top-right rounded-lg border border-[var(--border)] bg-[var(--background)] p-0 shadow-lg focus:outline-none z-50\">\n <lib-notification-panel\n [notifications]=\"notifications()\"\n (notificationClick)=\"onNotificationClick($event)\"\n (markRead)=\"onMarkRead($event)\"\n (clearAll)=\"onClearAll()\"\n />\n </div>\n }\n</div>\n", styles: [":host{display:inline-block}.notification-dropdown{box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;animation:bellEnter .15s cubic-bezier(.16,1,.3,1);overflow:hidden}@keyframes bellEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"] }]
281
+ args: [{ selector: 'lib-notification-bell', standalone: true, imports: [CommonModule, NotificationPanelComponent, LucideAngularModule], template: "<div class=\"notification-bell-wrapper relative inline-block text-left\">\r\n <button\n type=\"button\"\n class=\"notification-trigger\"\n (click)=\"togglePanel()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"View notifications\"\r\n >\r\n <lucide-icon [name]=\"bellIcon\" class=\"h-4.5 w-4.5\" />\r\n \r\n @if (unreadCount() > 0) {\r\n <span class=\"absolute top-1.5 right-1.5 flex h-2 w-2\">\r\n <span class=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--danger,#ef4444)] opacity-75\"></span>\r\n <span class=\"relative inline-flex rounded-full h-2 w-2 bg-[var(--danger,#ef4444)]\"></span>\r\n </span>\r\n }\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"notification-dropdown fixed left-2 right-2 top-14 mt-2 origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-0 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-80 sm:origin-top-right\">\n <lib-notification-panel\r\n [notifications]=\"notifications()\"\r\n (notificationClick)=\"onNotificationClick($event)\"\r\n (markRead)=\"onMarkRead($event)\"\r\n (clearAll)=\"onClearAll()\"\r\n />\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:inline-block}.notification-trigger{position:relative;display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease}.notification-trigger:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.notification-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.notification-dropdown{box-shadow:0 12px 30px #0f172a24;animation:bellEnter .15s cubic-bezier(.16,1,.3,1);overflow:hidden}@keyframes bellEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"] }]
282
282
  }], propDecorators: { unreadCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "unreadCount", required: false }] }], notifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "notifications", required: false }] }], notificationClick: [{ type: i0.Output, args: ["notificationClick"] }], markRead: [{ type: i0.Output, args: ["markRead"] }], clearAll: [{ type: i0.Output, args: ["clearAll"] }], onOutsideClick: [{
283
283
  type: HostListener,
284
284
  args: ['document:click', ['$event']]
@@ -306,11 +306,11 @@ class UserAvatarComponent {
306
306
  this.imageError = true;
307
307
  }
308
308
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: UserAvatarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
309
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: UserAvatarComponent, isStandalone: true, selector: "lib-user-avatar", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"avatar-container flex h-full w-full items-center justify-center rounded-full border border-[var(--border)] select-none overflow-hidden transition-all duration-200\">\n @if (user()?.avatar && !imageError) {\n <img [src]=\"user()?.avatar\" [alt]=\"user()?.name || 'User avatar'\" class=\"avatar-img h-full w-full object-cover\" (error)=\"onImageError()\" />\n } @else {\n <span class=\"avatar-initials text-xs font-semibold uppercase leading-none\">{{ initials() }}</span>\n }\n</div>\n", styles: [":host{display:block;width:inherit;height:inherit}.avatar-container{background:linear-gradient(135deg,var(--primary, #0067b8) 0%,var(--secondary, #005a9e) 100%);color:var(--background, #ffffff);box-shadow:0 1px 3px #0000000d}:host:hover .avatar-container{transform:scale(1.02);box-shadow:0 4px 6px -1px #00000014,0 2px 4px -1px #0000000a}.avatar-initials{text-shadow:0 1px 1px rgba(0,0,0,.15)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
309
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: UserAvatarComponent, isStandalone: true, selector: "lib-user-avatar", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"avatar-container flex h-full w-full items-center justify-center rounded-full border border-[var(--border)] select-none overflow-hidden transition-all duration-200\">\r\n @if (user()?.avatar && !imageError) {\r\n <img [src]=\"user()?.avatar\" [alt]=\"user()?.name || 'User avatar'\" class=\"avatar-img h-full w-full object-cover\" (error)=\"onImageError()\" />\r\n } @else {\r\n <span class=\"avatar-initials text-xs font-semibold uppercase leading-none\">{{ initials() }}</span>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:inherit;height:inherit}.avatar-container{background:linear-gradient(135deg,var(--primary, #0067b8) 0%,var(--secondary, #005a9e) 100%);color:var(--background, #ffffff);box-shadow:0 1px 3px #0000000d}:host:hover .avatar-container{transform:scale(1.02);box-shadow:0 4px 6px -1px #00000014,0 2px 4px -1px #0000000a}.avatar-initials{text-shadow:0 1px 1px rgba(0,0,0,.15)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
310
310
  }
311
311
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: UserAvatarComponent, decorators: [{
312
312
  type: Component,
313
- args: [{ selector: 'lib-user-avatar', standalone: true, imports: [CommonModule], template: "<div class=\"avatar-container flex h-full w-full items-center justify-center rounded-full border border-[var(--border)] select-none overflow-hidden transition-all duration-200\">\n @if (user()?.avatar && !imageError) {\n <img [src]=\"user()?.avatar\" [alt]=\"user()?.name || 'User avatar'\" class=\"avatar-img h-full w-full object-cover\" (error)=\"onImageError()\" />\n } @else {\n <span class=\"avatar-initials text-xs font-semibold uppercase leading-none\">{{ initials() }}</span>\n }\n</div>\n", styles: [":host{display:block;width:inherit;height:inherit}.avatar-container{background:linear-gradient(135deg,var(--primary, #0067b8) 0%,var(--secondary, #005a9e) 100%);color:var(--background, #ffffff);box-shadow:0 1px 3px #0000000d}:host:hover .avatar-container{transform:scale(1.02);box-shadow:0 4px 6px -1px #00000014,0 2px 4px -1px #0000000a}.avatar-initials{text-shadow:0 1px 1px rgba(0,0,0,.15)}\n"] }]
313
+ args: [{ selector: 'lib-user-avatar', standalone: true, imports: [CommonModule], template: "<div class=\"avatar-container flex h-full w-full items-center justify-center rounded-full border border-[var(--border)] select-none overflow-hidden transition-all duration-200\">\r\n @if (user()?.avatar && !imageError) {\r\n <img [src]=\"user()?.avatar\" [alt]=\"user()?.name || 'User avatar'\" class=\"avatar-img h-full w-full object-cover\" (error)=\"onImageError()\" />\r\n } @else {\r\n <span class=\"avatar-initials text-xs font-semibold uppercase leading-none\">{{ initials() }}</span>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:inherit;height:inherit}.avatar-container{background:linear-gradient(135deg,var(--primary, #0067b8) 0%,var(--secondary, #005a9e) 100%);color:var(--background, #ffffff);box-shadow:0 1px 3px #0000000d}:host:hover .avatar-container{transform:scale(1.02);box-shadow:0 4px 6px -1px #00000014,0 2px 4px -1px #0000000a}.avatar-initials{text-shadow:0 1px 1px rgba(0,0,0,.15)}\n"] }]
314
314
  }], propDecorators: { user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: false }] }] } });
315
315
 
316
316
  class UserMenuComponent {
@@ -341,11 +341,11 @@ class UserMenuComponent {
341
341
  }
342
342
  }
343
343
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: UserMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
344
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: UserMenuComponent, isStandalone: true, selector: "lib-user-menu", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { menuAction: "menuAction" }, host: { listeners: { "document:click": "onOutsideClick($event)" } }, ngImport: i0, template: "<div class=\"user-menu-wrapper relative inline-block text-left\">\n <button\n type=\"button\"\n class=\"flex items-center gap-1.5 rounded-full border border-[var(--border)] p-0.5 pr-2 transition-all hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] focus:ring-offset-2 cursor-pointer\"\n (click)=\"toggleMenu()\"\n aria-haspopup=\"true\"\n [aria-expanded]=\"isOpen()\"\n aria-label=\"User profile menu\"\n >\n <div class=\"h-7 w-7 rounded-full overflow-hidden\">\n <lib-user-avatar [user]=\"user()\" />\n </div>\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)] transition-transform duration-200\" [class.rotate-180]=\"isOpen()\" />\n </button>\n\n @if (isOpen()) {\n <div class=\"user-dropdown-panel absolute right-0 mt-2 w-56 origin-top-right rounded-lg border border-[var(--border)] bg-[var(--background)] p-1.5 shadow-lg focus:outline-none z-50\">\n @if (user(); as u) {\n <div class=\"px-3 py-2 border-b border-[var(--border)] mb-1\">\n <p class=\"text-[10px] font-normal text-[var(--secondary)] uppercase tracking-wider\">Signed in as</p>\n <p class=\"text-sm font-semibold truncate text-[var(--foreground)]\" [title]=\"u.name\">{{ u.name }}</p>\n <p class=\"text-xs truncate text-[var(--secondary)] mt-0.5\" [title]=\"u.email\">{{ u.email }}</p>\n </div>\n }\n\n <div class=\"py-0.5 space-y-0.5\">\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\n (click)=\"onAction('profile')\"\n >\n <lucide-icon [name]=\"userIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\n <span>My Profile</span>\n </button>\n\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\n (click)=\"onAction('settings')\"\n >\n <lucide-icon [name]=\"settingsIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\n <span>Settings</span>\n </button>\n\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\n (click)=\"onAction('billing')\"\n >\n <lucide-icon [name]=\"creditCardIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\n <span>Billing</span>\n </button>\n </div>\n\n <div class=\"border-t border-[var(--border)] mt-1 pt-1\">\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm text-[var(--danger,#ef4444)] transition hover:bg-[rgba(239,68,68,0.08)] text-left cursor-pointer\"\n (click)=\"onAction('logout')\"\n >\n <lucide-icon [name]=\"logOutIcon\" class=\"h-4 w-4 text-[var(--danger,#ef4444)]\" />\n <span>Sign Out</span>\n </button>\n </div>\n </div>\n }\n</div>\n", styles: [":host{display:inline-block}.user-dropdown-panel{box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;animation:dropdownEnter .15s cubic-bezier(.16,1,.3,1)}@keyframes dropdownEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: UserAvatarComponent, selector: "lib-user-avatar", inputs: ["user"] }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
344
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: UserMenuComponent, isStandalone: true, selector: "lib-user-menu", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { menuAction: "menuAction" }, host: { listeners: { "document:click": "onOutsideClick($event)" } }, ngImport: i0, template: "<div class=\"user-menu-wrapper relative inline-block text-left\">\r\n <button type=\"button\" class=\"user-menu-trigger\" (click)=\"toggleMenu()\" aria-haspopup=\"true\" [aria-expanded]=\"isOpen()\"\r\n aria-label=\"User profile menu\">\r\n <div class=\"h-7 w-7 rounded-full overflow-hidden\">\r\n <lib-user-avatar [user]=\"user()\" />\r\n </div>\r\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)] transition-transform duration-200\"\r\n [class.rotate-180]=\"isOpen()\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div\r\n class=\"user-dropdown-panel fixed left-2 right-2 top-14 mt-2 origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-1.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-56 sm:origin-top-right\">\r\n @if (user(); as u) {\r\n <div class=\"px-3 py-2 border-b border-[var(--border)] mb-1\">\r\n <p class=\"text-sm font-semibold truncate text-[var(--foreground)]\" [title]=\"u.name\">{{ u.name }}</p>\r\n <p class=\"text-xs truncate text-[var(--secondary)] mt-0.5\" [title]=\"u.email\">{{ u.email }}</p>\r\n </div>\r\n }\r\n\r\n <div class=\"py-0.5 space-y-0.5\">\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('profile')\">\r\n <lucide-icon [name]=\"userIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\r\n <span>My Profile</span>\r\n </button>\r\n\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('settings')\">\r\n <lucide-icon [name]=\"settingsIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\r\n <span>Settings</span>\r\n </button>\r\n\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('billing')\">\r\n <lucide-icon [name]=\"creditCardIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\r\n <span>Billing</span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"border-t border-[var(--border)] mt-1 pt-1\">\r\n <button type=\"button\" class=\"user-menu-item danger\" (click)=\"onAction('logout')\">\r\n <lucide-icon [name]=\"logOutIcon\" class=\"h-4 w-4 text-[var(--danger,#ef4444)]\" />\r\n <span>Sign Out</span>\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n</div>", styles: [":host{display:inline-block}.user-menu-trigger{display:flex;align-items:center;gap:.375rem;border:1px solid var(--border);border-radius:999px;color:var(--foreground);background:var(--background);padding:.125rem .45rem .125rem .125rem;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease}.user-menu-trigger:hover{background:color-mix(in srgb,var(--border) 28%,transparent)}.user-menu-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.user-dropdown-panel{box-shadow:0 12px 30px #0f172a24;animation:dropdownEnter .15s cubic-bezier(.16,1,.3,1)}.user-menu-item{display:flex;width:100%;align-items:center;gap:.625rem;border:0;border-radius:.5rem;color:var(--foreground);background:transparent;padding:.55rem .75rem;font-size:.875rem;text-align:left;cursor:pointer;transition:background-color .15s ease,color .15s ease}.user-menu-item:hover{background:color-mix(in srgb,var(--border) 32%,transparent)}.user-menu-item.danger{color:var(--danger, #ef4444)}.user-menu-item.danger:hover{background:color-mix(in srgb,var(--danger, #ef4444) 10%,transparent)}@keyframes dropdownEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: UserAvatarComponent, selector: "lib-user-avatar", inputs: ["user"] }, { kind: "ngmodule", type: LucideAngularModule }, { kind: "component", type: i1.LucideAngularComponent, selector: "lucide-angular, lucide-icon, i-lucide, span-lucide", inputs: ["class", "name", "img", "color", "absoluteStrokeWidth", "size", "strokeWidth"] }] });
345
345
  }
346
346
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: UserMenuComponent, decorators: [{
347
347
  type: Component,
348
- args: [{ selector: 'lib-user-menu', standalone: true, imports: [CommonModule, UserAvatarComponent, LucideAngularModule], template: "<div class=\"user-menu-wrapper relative inline-block text-left\">\n <button\n type=\"button\"\n class=\"flex items-center gap-1.5 rounded-full border border-[var(--border)] p-0.5 pr-2 transition-all hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] focus:ring-offset-2 cursor-pointer\"\n (click)=\"toggleMenu()\"\n aria-haspopup=\"true\"\n [aria-expanded]=\"isOpen()\"\n aria-label=\"User profile menu\"\n >\n <div class=\"h-7 w-7 rounded-full overflow-hidden\">\n <lib-user-avatar [user]=\"user()\" />\n </div>\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)] transition-transform duration-200\" [class.rotate-180]=\"isOpen()\" />\n </button>\n\n @if (isOpen()) {\n <div class=\"user-dropdown-panel absolute right-0 mt-2 w-56 origin-top-right rounded-lg border border-[var(--border)] bg-[var(--background)] p-1.5 shadow-lg focus:outline-none z-50\">\n @if (user(); as u) {\n <div class=\"px-3 py-2 border-b border-[var(--border)] mb-1\">\n <p class=\"text-[10px] font-normal text-[var(--secondary)] uppercase tracking-wider\">Signed in as</p>\n <p class=\"text-sm font-semibold truncate text-[var(--foreground)]\" [title]=\"u.name\">{{ u.name }}</p>\n <p class=\"text-xs truncate text-[var(--secondary)] mt-0.5\" [title]=\"u.email\">{{ u.email }}</p>\n </div>\n }\n\n <div class=\"py-0.5 space-y-0.5\">\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\n (click)=\"onAction('profile')\"\n >\n <lucide-icon [name]=\"userIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\n <span>My Profile</span>\n </button>\n\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\n (click)=\"onAction('settings')\"\n >\n <lucide-icon [name]=\"settingsIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\n <span>Settings</span>\n </button>\n\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\n (click)=\"onAction('billing')\"\n >\n <lucide-icon [name]=\"creditCardIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\n <span>Billing</span>\n </button>\n </div>\n\n <div class=\"border-t border-[var(--border)] mt-1 pt-1\">\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm text-[var(--danger,#ef4444)] transition hover:bg-[rgba(239,68,68,0.08)] text-left cursor-pointer\"\n (click)=\"onAction('logout')\"\n >\n <lucide-icon [name]=\"logOutIcon\" class=\"h-4 w-4 text-[var(--danger,#ef4444)]\" />\n <span>Sign Out</span>\n </button>\n </div>\n </div>\n }\n</div>\n", styles: [":host{display:inline-block}.user-dropdown-panel{box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;animation:dropdownEnter .15s cubic-bezier(.16,1,.3,1)}@keyframes dropdownEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"] }]
348
+ args: [{ selector: 'lib-user-menu', standalone: true, imports: [CommonModule, UserAvatarComponent, LucideAngularModule], template: "<div class=\"user-menu-wrapper relative inline-block text-left\">\r\n <button type=\"button\" class=\"user-menu-trigger\" (click)=\"toggleMenu()\" aria-haspopup=\"true\" [aria-expanded]=\"isOpen()\"\r\n aria-label=\"User profile menu\">\r\n <div class=\"h-7 w-7 rounded-full overflow-hidden\">\r\n <lib-user-avatar [user]=\"user()\" />\r\n </div>\r\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)] transition-transform duration-200\"\r\n [class.rotate-180]=\"isOpen()\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div\r\n class=\"user-dropdown-panel fixed left-2 right-2 top-14 mt-2 origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-1.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-56 sm:origin-top-right\">\r\n @if (user(); as u) {\r\n <div class=\"px-3 py-2 border-b border-[var(--border)] mb-1\">\r\n <p class=\"text-sm font-semibold truncate text-[var(--foreground)]\" [title]=\"u.name\">{{ u.name }}</p>\r\n <p class=\"text-xs truncate text-[var(--secondary)] mt-0.5\" [title]=\"u.email\">{{ u.email }}</p>\r\n </div>\r\n }\r\n\r\n <div class=\"py-0.5 space-y-0.5\">\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('profile')\">\r\n <lucide-icon [name]=\"userIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\r\n <span>My Profile</span>\r\n </button>\r\n\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('settings')\">\r\n <lucide-icon [name]=\"settingsIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\r\n <span>Settings</span>\r\n </button>\r\n\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('billing')\">\r\n <lucide-icon [name]=\"creditCardIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\r\n <span>Billing</span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"border-t border-[var(--border)] mt-1 pt-1\">\r\n <button type=\"button\" class=\"user-menu-item danger\" (click)=\"onAction('logout')\">\r\n <lucide-icon [name]=\"logOutIcon\" class=\"h-4 w-4 text-[var(--danger,#ef4444)]\" />\r\n <span>Sign Out</span>\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n</div>", styles: [":host{display:inline-block}.user-menu-trigger{display:flex;align-items:center;gap:.375rem;border:1px solid var(--border);border-radius:999px;color:var(--foreground);background:var(--background);padding:.125rem .45rem .125rem .125rem;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,box-shadow .15s ease}.user-menu-trigger:hover{background:color-mix(in srgb,var(--border) 28%,transparent)}.user-menu-trigger:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.user-dropdown-panel{box-shadow:0 12px 30px #0f172a24;animation:dropdownEnter .15s cubic-bezier(.16,1,.3,1)}.user-menu-item{display:flex;width:100%;align-items:center;gap:.625rem;border:0;border-radius:.5rem;color:var(--foreground);background:transparent;padding:.55rem .75rem;font-size:.875rem;text-align:left;cursor:pointer;transition:background-color .15s ease,color .15s ease}.user-menu-item:hover{background:color-mix(in srgb,var(--border) 32%,transparent)}.user-menu-item.danger{color:var(--danger, #ef4444)}.user-menu-item.danger:hover{background:color-mix(in srgb,var(--danger, #ef4444) 10%,transparent)}@keyframes dropdownEnter{0%{opacity:0;transform:scale(.95) translateY(-4px)}to{opacity:1;transform:scale(1) translateY(0)}}\n"] }]
349
349
  }], propDecorators: { user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: false }] }], menuAction: [{ type: i0.Output, args: ["menuAction"] }], onOutsideClick: [{
350
350
  type: HostListener,
351
351
  args: ['document:click', ['$event']]
@@ -409,10 +409,17 @@ class HeaderComponent {
409
409
  applications = input([], ...(ngDevMode ? [{ debugName: "applications" }] : /* istanbul ignore next */ []));
410
410
  notifications = input([], ...(ngDevMode ? [{ debugName: "notifications" }] : /* istanbul ignore next */ []));
411
411
  notificationCount = input(0, ...(ngDevMode ? [{ debugName: "notificationCount" }] : /* istanbul ignore next */ []));
412
- logoUrl = input('https://cdn.jsdelivr.net/gh/Excellore/excellore-static-assets@main/images/logo_origin_tp.PNG', ...(ngDevMode ? [{ debugName: "logoUrl" }] : /* istanbul ignore next */ []));
412
+ logoUrl = input('https://cdn.jsdelivr.net/gh/Excellore/excellore-static-assets@main/images/excellore-logo-origin.png', ...(ngDevMode ? [{ debugName: "logoUrl" }] : /* istanbul ignore next */ []));
413
413
  appName = input('Excellore', ...(ngDevMode ? [{ debugName: "appName" }] : /* istanbul ignore next */ []));
414
+ currentAppName = input('', ...(ngDevMode ? [{ debugName: "currentAppName" }] : /* istanbul ignore next */ []));
415
+ showSearch = input(true, ...(ngDevMode ? [{ debugName: "showSearch" }] : /* istanbul ignore next */ []));
416
+ showNotifications = input(true, ...(ngDevMode ? [{ debugName: "showNotifications" }] : /* istanbul ignore next */ []));
417
+ showAppSwitcher = input(true, ...(ngDevMode ? [{ debugName: "showAppSwitcher" }] : /* istanbul ignore next */ []));
418
+ showThemeSwitcher = input(true, ...(ngDevMode ? [{ debugName: "showThemeSwitcher" }] : /* istanbul ignore next */ []));
419
+ showSignIn = input(true, ...(ngDevMode ? [{ debugName: "showSignIn" }] : /* istanbul ignore next */ []));
414
420
  // Outputs as decided in the architecture plan
415
421
  search = output();
422
+ signIn = output();
416
423
  applicationSelected = output();
417
424
  notificationClick = output();
418
425
  markRead = output();
@@ -442,6 +449,7 @@ class HeaderComponent {
442
449
  return overrideCount;
443
450
  return this.notifications().filter((n) => !n.read).length;
444
451
  }, ...(ngDevMode ? [{ debugName: "unreadCount" }] : /* istanbul ignore next */ []));
452
+ hasMobileMenuContent = computed(() => this.showSearch() || this.navigationItems().some((item) => this.hasPermission(item)), ...(ngDevMode ? [{ debugName: "hasMobileMenuContent" }] : /* istanbul ignore next */ []));
445
453
  // Determines if an item's internal route is active
446
454
  isActive(item) {
447
455
  if (!item.route)
@@ -467,15 +475,6 @@ class HeaderComponent {
467
475
  onSearch(query) {
468
476
  this.search.emit(query);
469
477
  }
470
- getOrganizationInitials(organization) {
471
- return organization.name
472
- .split(/\s+/)
473
- .filter(Boolean)
474
- .slice(0, 2)
475
- .map((word) => word[0])
476
- .join('')
477
- .toUpperCase();
478
- }
479
478
  onApplicationSelect(app) {
480
479
  this.applicationSelected.emit(app);
481
480
  }
@@ -492,7 +491,7 @@ class HeaderComponent {
492
491
  this.menuAction.emit(action);
493
492
  }
494
493
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: HeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
495
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: HeaderComponent, isStandalone: true, selector: "lib-header", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null }, permissions: { classPropertyName: "permissions", publicName: "permissions", isSignal: true, isRequired: false, transformFunction: null }, organization: { classPropertyName: "organization", publicName: "organization", isSignal: true, isRequired: false, transformFunction: null }, navigationItems: { classPropertyName: "navigationItems", publicName: "navigationItems", isSignal: true, isRequired: false, transformFunction: null }, applications: { classPropertyName: "applications", publicName: "applications", isSignal: true, isRequired: false, transformFunction: null }, notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null }, notificationCount: { classPropertyName: "notificationCount", publicName: "notificationCount", isSignal: true, isRequired: false, transformFunction: null }, logoUrl: { classPropertyName: "logoUrl", publicName: "logoUrl", isSignal: true, isRequired: false, transformFunction: null }, appName: { classPropertyName: "appName", publicName: "appName", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { search: "search", applicationSelected: "applicationSelected", notificationClick: "notificationClick", markRead: "markRead", clearAll: "clearAll", menuAction: "menuAction" }, ngImport: i0, template: "<header\n class=\"header-container fixed top-0 inset-x-0 z-40 flex h-14 items-center justify-between border-b border-[var(--border)] bg-[var(--background)]/80 px-4 text-[var(--foreground)] backdrop-blur-md\">\n\n <!-- Left Side: Organization & Desktop Links -->\n <div class=\"flex items-center gap-3.5\">\n\n <!-- Excellore brand logo/title hidden while the organization is the primary header identity.\n <a routerLink=\"/\"\n class=\"flex items-center gap-2 text-sm font-bold tracking-tight text-[var(--foreground)] no-underline hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-[var(--primary)] rounded\">\n <img [src]=\"logoUrl()\" [alt]=\"appName() + ' logo'\" width=\"26\" height=\"26\" class=\"h-6.5 w-6.5 select-none\" />\n <span class=\"hidden sm:inline-block\">{{ appName() }}</span>\n </a>\n -->\n\n <!-- Organization Identity -->\n @if (organization(); as organization) {\n <div class=\"h-8 flex items-center\">\n <div class=\"flex items-center gap-2.5\" [attr.aria-label]=\"'Organization: ' + organization.name\">\n @if (organization.logoUrl) {\n <img [src]=\"organization.logoUrl\" [alt]=\"organization.name + ' logo'\"\n class=\"h-8 w-8 rounded-md object-contain bg-white\" />\n } @else {\n <span\n class=\"grid h-8 w-8 place-items-center rounded-md bg-[var(--primary)] text-[11px] font-bold text-white\"\n aria-hidden=\"true\">\n {{ getOrganizationInitials(organization) }}\n </span>\n }\n <span class=\"hidden max-w-[180px] truncate text-sm font-semibold text-[var(--foreground)] sm:inline\">\n {{ organization.name }}\n </span>\n </div>\n </div>\n }\n\n <!-- Desktop Navigation Links -->\n <nav class=\"ml-2 hidden lg:flex items-center gap-5.5\" aria-label=\"Desktop primary navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{exact: true}\"\n class=\"nav-link text-xs font-medium text-[var(--secondary)] transition hover:text-[var(--foreground)] no-underline py-1 relative focus:outline-none focus:ring-2 focus:ring-[var(--primary)] rounded\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\"\n class=\"nav-link text-xs font-medium text-[var(--secondary)] transition hover:text-[var(--foreground)] no-underline py-1 relative focus:outline-none focus:ring-2 focus:ring-[var(--primary)] rounded\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n </div>\n\n <!-- Middle: Search Input (Desktop) -->\n <div class=\"hidden md:flex flex-1 max-w-[260px] mx-4\">\n <div class=\"relative w-full\">\n <!-- <lucide-icon [name]=\"searchIcon\"\n class=\"absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" /> -->\n <lib-icon name=\"search\" [size]=\"14\"\n class=\"absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-[var(--secondary)]\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #searchInput\n (keyup.enter)=\"onSearch(searchInput.value); searchInput.value = ''\"\n class=\"w-full h-8 pl-8 pr-3 text-xs bg-[rgba(var(--foreground),0.02)] border border-[var(--border)] rounded-md focus:outline-none focus:bg-[var(--background)] focus:ring-2 focus:ring-[var(--primary)] text-[var(--foreground)] transition-all placeholder:text-[var(--secondary)]/60\"\n autocomplete=\"off\" aria-label=\"Search\" />\n </div>\n </div>\n\n <!-- Right Side Actions -->\n <div class=\"flex items-center gap-1 sm:gap-1.5\">\n\n <!-- Mobile Search Trigger -->\n <button type=\"button\"\n class=\"grid md:hidden h-8.5 w-8.5 place-items-center rounded-md border border-transparent hover:bg-[rgba(var(--foreground),0.03)] text-[var(--foreground)] cursor-pointer focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\"\n (click)=\"toggleMobileMenu()\" aria-label=\"Open mobile menu\" aria-expanded=\"isMobileMenuOpen()\" #menuToggle>\n <!-- <lucide-icon [name]=\"searchIcon\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" /> -->\n <lib-icon name=\"search\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n\n <!-- Theme Switcher -->\n <button type=\"button\"\n class=\"grid h-8.5 w-8.5 place-items-center rounded-md border border-transparent text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\n (click)=\"themeService.toggleTheme()\" aria-label=\"Toggle theme\">\n @switch (themeService.theme()) {\n @case ('light') {\n <!-- <lucide-icon [name]=\"sunIcon\" class=\"h-4 w-4 text-[var(--primary)]\" aria-hidden=\"true\" /> -->\n <lib-icon name=\"sun\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @case ('dark') {\n <!-- <lucide-icon [name]=\"moonIcon\" class=\"h-4 w-4 text-[var(--primary)]\" aria-hidden=\"true\" /> -->\n <lib-icon name=\"moon\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @default {\n <!-- <lucide-icon [name]=\"monitorIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" aria-hidden=\"true\" /> -->\n <lib-icon name=\"monitor\" [size]=\"16\" class=\"text-[var(--secondary)]\" />\n }\n }\n </button>\n\n <!-- App Switcher -->\n @if (applications().length > 0) {\n <lib-app-switcher [applications]=\"applications()\" (applicationSelected)=\"onApplicationSelect($event)\" />\n }\n\n <!-- Notifications -->\n <lib-notification-bell [unreadCount]=\"unreadCount()\" [notifications]=\"notifications()\"\n (notificationClick)=\"onNotificationClick($event)\" (markRead)=\"onMarkRead($event)\" (clearAll)=\"onClearAll()\" />\n\n <!-- User Menu -->\n @if (user()) {\n <div class=\"h-6 border-l border-[var(--border)] pl-1.5 sm:pl-2 flex items-center ml-0.5\">\n <lib-user-menu [user]=\"user()\" (menuAction)=\"onMenuAction($event)\" />\n </div>\n }\n\n <!-- Mobile Menu Toggle (Hamburger) -->\n <button type=\"button\"\n class=\"grid lg:hidden h-8.5 w-8.5 place-items-center rounded-md border border-transparent text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\n (click)=\"toggleMobileMenu()\" aria-label=\"Toggle navigation menu\" aria-expanded=\"isMobileMenuOpen()\"\n #mobileMenuToggle>\n <!-- <lucide-icon [name]=\"isMobileMenuOpen() ? xIcon : menuIcon\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" /> -->\n <lib-icon [name]=\"isMobileMenuOpen() ? 'x' : 'menu'\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n </div>\n</header>\n\n<!-- Mobile Menu Backdrop -->\n@if (isMobileMenuOpen()) {\n<div class=\"fixed inset-0 z-30 bg-black/20 lg:hidden\" (click)=\"closeMobileMenu()\" (keydown.escape)=\"closeMobileMenu()\"\n tabindex=\"-1\" aria-hidden=\"true\"></div>\n\n<!-- Mobile Navigation Drawer -->\n<div\n class=\"mobile-drawer fixed top-14 inset-x-0 bottom-0 z-40 flex flex-col bg-[var(--background)] border-t border-[var(--border)] p-4 lg:hidden\"\n role=\"dialog\" aria-modal=\"true\" aria-label=\"Mobile navigation\" (keydown.escape)=\"closeMobileMenu()\">\n <!-- Mobile Search -->\n <div class=\"relative w-full mb-4\">\n <!-- <lucide-icon [name]=\"searchIcon\"\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" /> -->\n\n <lib-icon name=\"search\" [size]=\"16\"\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #mobileSearchInput\n (keyup.enter)=\"onSearch(mobileSearchInput.value); mobileSearchInput.value = ''; closeMobileMenu()\"\n class=\"w-full h-10 pl-9 pr-3 text-sm bg-[rgba(var(--foreground),0.02)] border border-[var(--border)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--primary)] text-[var(--foreground)] placeholder:text-[var(--secondary)]/60\"\n autocomplete=\"off\" aria-label=\"Search\" />\n </div>\n\n <!-- Mobile Nav Links -->\n <nav class=\"flex flex-col gap-1 overflow-y-auto max-h-[70vh]\" role=\"navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"mobile-active\" [routerLinkActiveOptions]=\"{exact: true}\"\n (click)=\"closeMobileMenu()\"\n class=\"flex items-center px-3 py-2.5 rounded-lg text-sm font-semibold text-[var(--secondary)] hover:text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] no-underline focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" (click)=\"closeMobileMenu()\"\n class=\"flex items-center px-3 py-2.5 rounded-lg text-sm font-semibold text-[var(--secondary)] hover:text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] no-underline focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n</div>\n}\n", styles: [":host{display:block}.header-container{box-shadow:0 1px 2px #00000008;will-change:transform}.nav-link{transition:color .15s ease-in-out}.nav-link.active{color:var(--foreground)!important;font-weight:600}.nav-link.active:after{content:\"\";position:absolute;bottom:-14px;left:0;right:0;height:2px;background-color:rgb(var(--primary));border-radius:9999px}.mobile-drawer{box-shadow:0 10px 15px -3px #0000000d,0 4px 6px -2px #00000008;animation:slideDown .2s cubic-bezier(.16,1,.3,1)}.mobile-active{color:var(--foreground)!important;background-color:rgba(var(--foreground),.03);font-weight:700}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}:focus-visible{outline:2px solid rgb(var(--primary));outline-offset:2px}:focus:not(:focus-visible){outline:none}@supports not (backdrop-filter: blur(12px)){.header-container{background-color:rgb(var(--background))}}lucide-icon{display:inline-flex;vertical-align:middle}*{transition-property:background-color,border-color,color;transition-duration:.15s;transition-timing-function:ease-in-out}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: AppSwitcherComponent, selector: "lib-app-switcher", inputs: ["applications"], outputs: ["applicationSelected"] }, { kind: "component", type: NotificationBellComponent, selector: "lib-notification-bell", inputs: ["unreadCount", "notifications"], outputs: ["notificationClick", "markRead", "clearAll"] }, { kind: "component", type: UserMenuComponent, selector: "lib-user-menu", inputs: ["user"], outputs: ["menuAction"] }, { kind: "component", type: IconComponent, selector: "lib-icon", inputs: ["name", "size", "color", "strokeWidth"] }] });
494
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: HeaderComponent, isStandalone: true, selector: "lib-header", inputs: { user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: false, transformFunction: null }, permissions: { classPropertyName: "permissions", publicName: "permissions", isSignal: true, isRequired: false, transformFunction: null }, organization: { classPropertyName: "organization", publicName: "organization", isSignal: true, isRequired: false, transformFunction: null }, navigationItems: { classPropertyName: "navigationItems", publicName: "navigationItems", isSignal: true, isRequired: false, transformFunction: null }, applications: { classPropertyName: "applications", publicName: "applications", isSignal: true, isRequired: false, transformFunction: null }, notifications: { classPropertyName: "notifications", publicName: "notifications", isSignal: true, isRequired: false, transformFunction: null }, notificationCount: { classPropertyName: "notificationCount", publicName: "notificationCount", isSignal: true, isRequired: false, transformFunction: null }, logoUrl: { classPropertyName: "logoUrl", publicName: "logoUrl", isSignal: true, isRequired: false, transformFunction: null }, appName: { classPropertyName: "appName", publicName: "appName", isSignal: true, isRequired: false, transformFunction: null }, currentAppName: { classPropertyName: "currentAppName", publicName: "currentAppName", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, showNotifications: { classPropertyName: "showNotifications", publicName: "showNotifications", isSignal: true, isRequired: false, transformFunction: null }, showAppSwitcher: { classPropertyName: "showAppSwitcher", publicName: "showAppSwitcher", isSignal: true, isRequired: false, transformFunction: null }, showThemeSwitcher: { classPropertyName: "showThemeSwitcher", publicName: "showThemeSwitcher", isSignal: true, isRequired: false, transformFunction: null }, showSignIn: { classPropertyName: "showSignIn", publicName: "showSignIn", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { search: "search", signIn: "signIn", applicationSelected: "applicationSelected", notificationClick: "notificationClick", markRead: "markRead", clearAll: "clearAll", menuAction: "menuAction" }, ngImport: i0, template: "<header\n class=\"header-container fixed top-0 inset-x-0 z-40 flex h-[3.75rem] items-center justify-between px-3 text-[var(--foreground)] sm:px-4 lg:px-6\">\n\n <!-- Left Side: Brand, Current App & Desktop Links -->\n <div class=\"flex min-w-0 items-center gap-3\">\n\n <a routerLink=\"/\" class=\"brand-identity\">\n <img [src]=\"logoUrl()\" [alt]=\"appName() + ' logo'\" width=\"26\" height=\"26\" class=\"brand-logo\" />\n <span class=\"brand-name\">{{ appName() }}</span>\n </a>\n\n @if (currentAppName()) {\n <div class=\"current-app-identity\" [attr.aria-label]=\"'Current app: ' + currentAppName()\">\n <span class=\"brand-separator\" aria-hidden=\"true\">|</span>\n <span class=\"current-app-name\">{{ currentAppName() }}</span>\n </div>\n }\n\n <!-- Desktop Navigation Links -->\n <nav class=\"desktop-nav ml-1 hidden items-center lg:flex\" aria-label=\"Desktop primary navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{exact: true}\" class=\"nav-link\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" class=\"nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n </div>\n\n <!-- Middle: Search Input (Desktop) -->\n @if (showSearch()) {\n <div class=\"hidden flex-1 justify-center px-4 md:flex\">\n <div class=\"header-search-shell\">\n <lib-icon name=\"search\" [size]=\"14\"\n class=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-[var(--secondary)]\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #searchInput\n (keyup.enter)=\"onSearch(searchInput.value); searchInput.value = ''\" class=\"header-search\" autocomplete=\"off\"\n aria-label=\"Search\" />\n </div>\n </div>\n } @else {\n <div class=\"hidden flex-1 md:block\"></div>\n }\n\n <!-- Right Side Actions -->\n <div class=\"header-actions\">\n\n <!-- Mobile Search Trigger -->\n @if (showSearch()) {\n <button type=\"button\" class=\"header-action mobile-search-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Open mobile menu\" aria-expanded=\"isMobileMenuOpen()\" #menuToggle>\n <lib-icon name=\"search\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n\n <!-- Theme Switcher -->\n @if (showThemeSwitcher()) {\n <button type=\"button\" class=\"header-action\" (click)=\"themeService.toggleTheme()\" aria-label=\"Toggle theme\">\n @switch (themeService.theme()) {\n @case ('light') {\n <lib-icon name=\"sun\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @case ('dark') {\n <lib-icon name=\"moon\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @default {\n <lib-icon name=\"monitor\" [size]=\"16\" class=\"text-[var(--secondary)]\" />\n }\n }\n </button>\n }\n\n <!-- App Switcher -->\n @if (showAppSwitcher() && applications().length > 0) {\n <lib-app-switcher [applications]=\"applications()\" (applicationSelected)=\"onApplicationSelect($event)\" />\n }\n\n <!-- Notifications -->\n @if (showNotifications()) {\n <lib-notification-bell [unreadCount]=\"unreadCount()\" [notifications]=\"notifications()\"\n (notificationClick)=\"onNotificationClick($event)\" (markRead)=\"onMarkRead($event)\" (clearAll)=\"onClearAll()\" />\n }\n\n <!-- User Menu -->\n @if (user()) {\n <div class=\"user-menu-divider\">\n <lib-user-menu [user]=\"user()\" (menuAction)=\"onMenuAction($event)\" />\n </div>\n } @else if (showSignIn()) {\n <button type=\"button\" class=\"sign-in-button\" (click)=\"signIn.emit()\">\n Sign in\n </button>\n }\n\n <!-- Mobile Menu Toggle (Hamburger) -->\n @if (hasMobileMenuContent()) {\n <button type=\"button\" class=\"header-action mobile-menu-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Toggle navigation menu\" aria-expanded=\"isMobileMenuOpen()\" #mobileMenuToggle>\n <lib-icon [name]=\"isMobileMenuOpen() ? 'x' : 'menu'\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n </div>\n</header>\n\n<!-- Mobile Menu Backdrop -->\n@if (isMobileMenuOpen() && hasMobileMenuContent()) {\n<div class=\"mobile-backdrop fixed inset-0 z-30 lg:hidden\" (click)=\"closeMobileMenu()\"\n (keydown.escape)=\"closeMobileMenu()\" tabindex=\"-1\" aria-hidden=\"true\"></div>\n\n<!-- Mobile Navigation Drawer -->\n<div\n class=\"mobile-drawer fixed inset-x-2 top-16 z-40 flex max-h-[calc(100dvh-4.5rem)] flex-col rounded-2xl border border-[var(--border)] bg-[var(--background)] p-3 lg:hidden\"\n role=\"dialog\" aria-modal=\"true\" aria-label=\"Mobile navigation\" (keydown.escape)=\"closeMobileMenu()\">\n <!-- Mobile Search -->\n @if (showSearch()) {\n <div class=\"relative mb-3 w-full\">\n <lib-icon name=\"search\" [size]=\"16\"\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #mobileSearchInput\n (keyup.enter)=\"onSearch(mobileSearchInput.value); mobileSearchInput.value = ''; closeMobileMenu()\"\n class=\"header-search mobile\" autocomplete=\"off\" aria-label=\"Search\" />\n </div>\n }\n\n <!-- Mobile Nav Links -->\n <nav class=\"flex flex-col gap-1 overflow-y-auto\" role=\"navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"mobile-active\" [routerLinkActiveOptions]=\"{exact: true}\"\n (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\" [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n</div>\n}\n", styles: [":host{display:block}.header-container{border-bottom:1px solid var(--border);background:var(--background);box-shadow:0 1px 2px #0f172a0a;will-change:transform}.brand-identity{display:inline-flex;min-width:0;align-items:center;gap:.5rem;border-radius:.5rem;color:var(--foreground);text-decoration:none}.brand-identity:hover{opacity:.9}.brand-logo{width:1.625rem;height:1.625rem;flex-shrink:0;-webkit-user-select:none;user-select:none}.brand-name{display:none;color:var(--foreground);font-size:.95rem;font-weight:750;letter-spacing:-.01em;white-space:nowrap}.current-app-identity{display:inline-flex;min-width:0;align-items:center;gap:.5rem}.brand-separator{color:var(--border);font-size:1rem;font-weight:500}.current-app-name{max-width:11rem;overflow:hidden;color:var(--foreground);font-size:.875rem;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.desktop-nav{gap:1.25rem;padding:0;background:transparent}.nav-link{position:relative;display:inline-flex;align-items:center;min-height:2rem;color:var(--secondary);padding:0;font-size:.78rem;font-weight:600;line-height:1;text-decoration:none;transition:color .15s ease,opacity .15s ease}.nav-link:hover{color:var(--foreground)}.nav-link.active{color:var(--foreground)!important;font-weight:700}.nav-link.active:after{content:\"\";position:absolute;right:0;bottom:-.85rem;left:0;height:.125rem;background-color:var(--primary);border-radius:9999px}.header-search-shell{position:relative;width:min(100%,20rem)}.header-search{width:100%;height:2.25rem;border:1px solid var(--border);border-radius:.5rem;outline:0;color:var(--foreground);background:var(--background);padding:0 .9rem 0 2.25rem;font-size:.78rem;transition:border-color .15s ease,box-shadow .15s ease}.header-search::placeholder{color:var(--secondary)}.header-search:focus{border-color:var(--primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--primary) 16%,transparent)}.header-search.mobile{width:100%;height:2.6rem;border-radius:.625rem;font-size:.875rem}.header-search.mobile:focus{width:100%}.header-actions{display:flex;align-items:center;gap:.25rem;background:transparent}.header-action{display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,color .15s ease}.header-action:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.user-menu-divider{display:flex;height:1.75rem;align-items:center;margin-left:.2rem;border-left:1px solid var(--border);padding-left:.35rem}.sign-in-button{height:2.15rem;border:1px solid var(--primary);border-radius:.5rem;color:#fff;background:var(--primary);padding:0 .85rem;font-size:.825rem;font-weight:700;cursor:pointer;transition:opacity .15s ease,box-shadow .15s ease}.sign-in-button:hover{opacity:.92;box-shadow:0 4px 10px color-mix(in srgb,var(--primary) 18%,transparent)}.sign-in-button:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.mobile-backdrop{background:#0f172a29}.mobile-drawer{box-shadow:0 12px 30px #0f172a24;animation:slideDown .2s cubic-bezier(.16,1,.3,1)}.mobile-nav-link{display:flex;align-items:center;min-height:2.75rem;border-radius:.625rem;color:var(--secondary);padding:0 .85rem;font-size:.925rem;font-weight:700;text-decoration:none;transition:background-color .15s ease,color .15s ease,transform .15s ease}.mobile-nav-link:hover{color:var(--foreground);background:color-mix(in srgb,var(--border) 35%,transparent)}.mobile-active{color:var(--foreground)!important;background:color-mix(in srgb,var(--primary) 10%,transparent);font-weight:700}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}:focus-visible{outline:2px solid var(--primary);outline-offset:2px}:focus:not(:focus-visible){outline:none}lucide-icon{display:inline-flex;vertical-align:middle}*{transition-property:background-color,border-color,color;transition-duration:.15s;transition-timing-function:ease-in-out}@media(min-width:640px){.brand-name{display:inline-block}.header-actions{gap:.25rem}.user-menu-divider{padding-left:.5rem}}@media(min-width:768px){.mobile-search-action{display:none}}@media(min-width:1024px){.mobile-menu-action{display:none}}@media(max-width:420px){.current-app-name{max-width:8rem}.header-actions{gap:0;padding:0;background:transparent}.header-action{width:2rem;height:2rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: AppSwitcherComponent, selector: "lib-app-switcher", inputs: ["applications"], outputs: ["applicationSelected"] }, { kind: "component", type: NotificationBellComponent, selector: "lib-notification-bell", inputs: ["unreadCount", "notifications"], outputs: ["notificationClick", "markRead", "clearAll"] }, { kind: "component", type: UserMenuComponent, selector: "lib-user-menu", inputs: ["user"], outputs: ["menuAction"] }, { kind: "component", type: IconComponent, selector: "lib-icon", inputs: ["name", "size", "color", "strokeWidth"] }] });
496
495
  }
497
496
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: HeaderComponent, decorators: [{
498
497
  type: Component,
@@ -504,8 +503,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
504
503
  NotificationBellComponent,
505
504
  UserMenuComponent,
506
505
  IconComponent,
507
- ], template: "<header\n class=\"header-container fixed top-0 inset-x-0 z-40 flex h-14 items-center justify-between border-b border-[var(--border)] bg-[var(--background)]/80 px-4 text-[var(--foreground)] backdrop-blur-md\">\n\n <!-- Left Side: Organization & Desktop Links -->\n <div class=\"flex items-center gap-3.5\">\n\n <!-- Excellore brand logo/title hidden while the organization is the primary header identity.\n <a routerLink=\"/\"\n class=\"flex items-center gap-2 text-sm font-bold tracking-tight text-[var(--foreground)] no-underline hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-[var(--primary)] rounded\">\n <img [src]=\"logoUrl()\" [alt]=\"appName() + ' logo'\" width=\"26\" height=\"26\" class=\"h-6.5 w-6.5 select-none\" />\n <span class=\"hidden sm:inline-block\">{{ appName() }}</span>\n </a>\n -->\n\n <!-- Organization Identity -->\n @if (organization(); as organization) {\n <div class=\"h-8 flex items-center\">\n <div class=\"flex items-center gap-2.5\" [attr.aria-label]=\"'Organization: ' + organization.name\">\n @if (organization.logoUrl) {\n <img [src]=\"organization.logoUrl\" [alt]=\"organization.name + ' logo'\"\n class=\"h-8 w-8 rounded-md object-contain bg-white\" />\n } @else {\n <span\n class=\"grid h-8 w-8 place-items-center rounded-md bg-[var(--primary)] text-[11px] font-bold text-white\"\n aria-hidden=\"true\">\n {{ getOrganizationInitials(organization) }}\n </span>\n }\n <span class=\"hidden max-w-[180px] truncate text-sm font-semibold text-[var(--foreground)] sm:inline\">\n {{ organization.name }}\n </span>\n </div>\n </div>\n }\n\n <!-- Desktop Navigation Links -->\n <nav class=\"ml-2 hidden lg:flex items-center gap-5.5\" aria-label=\"Desktop primary navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{exact: true}\"\n class=\"nav-link text-xs font-medium text-[var(--secondary)] transition hover:text-[var(--foreground)] no-underline py-1 relative focus:outline-none focus:ring-2 focus:ring-[var(--primary)] rounded\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\"\n class=\"nav-link text-xs font-medium text-[var(--secondary)] transition hover:text-[var(--foreground)] no-underline py-1 relative focus:outline-none focus:ring-2 focus:ring-[var(--primary)] rounded\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n </div>\n\n <!-- Middle: Search Input (Desktop) -->\n <div class=\"hidden md:flex flex-1 max-w-[260px] mx-4\">\n <div class=\"relative w-full\">\n <!-- <lucide-icon [name]=\"searchIcon\"\n class=\"absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" /> -->\n <lib-icon name=\"search\" [size]=\"14\"\n class=\"absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-[var(--secondary)]\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #searchInput\n (keyup.enter)=\"onSearch(searchInput.value); searchInput.value = ''\"\n class=\"w-full h-8 pl-8 pr-3 text-xs bg-[rgba(var(--foreground),0.02)] border border-[var(--border)] rounded-md focus:outline-none focus:bg-[var(--background)] focus:ring-2 focus:ring-[var(--primary)] text-[var(--foreground)] transition-all placeholder:text-[var(--secondary)]/60\"\n autocomplete=\"off\" aria-label=\"Search\" />\n </div>\n </div>\n\n <!-- Right Side Actions -->\n <div class=\"flex items-center gap-1 sm:gap-1.5\">\n\n <!-- Mobile Search Trigger -->\n <button type=\"button\"\n class=\"grid md:hidden h-8.5 w-8.5 place-items-center rounded-md border border-transparent hover:bg-[rgba(var(--foreground),0.03)] text-[var(--foreground)] cursor-pointer focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\"\n (click)=\"toggleMobileMenu()\" aria-label=\"Open mobile menu\" aria-expanded=\"isMobileMenuOpen()\" #menuToggle>\n <!-- <lucide-icon [name]=\"searchIcon\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" /> -->\n <lib-icon name=\"search\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n\n <!-- Theme Switcher -->\n <button type=\"button\"\n class=\"grid h-8.5 w-8.5 place-items-center rounded-md border border-transparent text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\n (click)=\"themeService.toggleTheme()\" aria-label=\"Toggle theme\">\n @switch (themeService.theme()) {\n @case ('light') {\n <!-- <lucide-icon [name]=\"sunIcon\" class=\"h-4 w-4 text-[var(--primary)]\" aria-hidden=\"true\" /> -->\n <lib-icon name=\"sun\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @case ('dark') {\n <!-- <lucide-icon [name]=\"moonIcon\" class=\"h-4 w-4 text-[var(--primary)]\" aria-hidden=\"true\" /> -->\n <lib-icon name=\"moon\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @default {\n <!-- <lucide-icon [name]=\"monitorIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" aria-hidden=\"true\" /> -->\n <lib-icon name=\"monitor\" [size]=\"16\" class=\"text-[var(--secondary)]\" />\n }\n }\n </button>\n\n <!-- App Switcher -->\n @if (applications().length > 0) {\n <lib-app-switcher [applications]=\"applications()\" (applicationSelected)=\"onApplicationSelect($event)\" />\n }\n\n <!-- Notifications -->\n <lib-notification-bell [unreadCount]=\"unreadCount()\" [notifications]=\"notifications()\"\n (notificationClick)=\"onNotificationClick($event)\" (markRead)=\"onMarkRead($event)\" (clearAll)=\"onClearAll()\" />\n\n <!-- User Menu -->\n @if (user()) {\n <div class=\"h-6 border-l border-[var(--border)] pl-1.5 sm:pl-2 flex items-center ml-0.5\">\n <lib-user-menu [user]=\"user()\" (menuAction)=\"onMenuAction($event)\" />\n </div>\n }\n\n <!-- Mobile Menu Toggle (Hamburger) -->\n <button type=\"button\"\n class=\"grid lg:hidden h-8.5 w-8.5 place-items-center rounded-md border border-transparent text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\n (click)=\"toggleMobileMenu()\" aria-label=\"Toggle navigation menu\" aria-expanded=\"isMobileMenuOpen()\"\n #mobileMenuToggle>\n <!-- <lucide-icon [name]=\"isMobileMenuOpen() ? xIcon : menuIcon\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" /> -->\n <lib-icon [name]=\"isMobileMenuOpen() ? 'x' : 'menu'\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n </div>\n</header>\n\n<!-- Mobile Menu Backdrop -->\n@if (isMobileMenuOpen()) {\n<div class=\"fixed inset-0 z-30 bg-black/20 lg:hidden\" (click)=\"closeMobileMenu()\" (keydown.escape)=\"closeMobileMenu()\"\n tabindex=\"-1\" aria-hidden=\"true\"></div>\n\n<!-- Mobile Navigation Drawer -->\n<div\n class=\"mobile-drawer fixed top-14 inset-x-0 bottom-0 z-40 flex flex-col bg-[var(--background)] border-t border-[var(--border)] p-4 lg:hidden\"\n role=\"dialog\" aria-modal=\"true\" aria-label=\"Mobile navigation\" (keydown.escape)=\"closeMobileMenu()\">\n <!-- Mobile Search -->\n <div class=\"relative w-full mb-4\">\n <!-- <lucide-icon [name]=\"searchIcon\"\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" /> -->\n\n <lib-icon name=\"search\" [size]=\"16\"\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #mobileSearchInput\n (keyup.enter)=\"onSearch(mobileSearchInput.value); mobileSearchInput.value = ''; closeMobileMenu()\"\n class=\"w-full h-10 pl-9 pr-3 text-sm bg-[rgba(var(--foreground),0.02)] border border-[var(--border)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--primary)] text-[var(--foreground)] placeholder:text-[var(--secondary)]/60\"\n autocomplete=\"off\" aria-label=\"Search\" />\n </div>\n\n <!-- Mobile Nav Links -->\n <nav class=\"flex flex-col gap-1 overflow-y-auto max-h-[70vh]\" role=\"navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"mobile-active\" [routerLinkActiveOptions]=\"{exact: true}\"\n (click)=\"closeMobileMenu()\"\n class=\"flex items-center px-3 py-2.5 rounded-lg text-sm font-semibold text-[var(--secondary)] hover:text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] no-underline focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" (click)=\"closeMobileMenu()\"\n class=\"flex items-center px-3 py-2.5 rounded-lg text-sm font-semibold text-[var(--secondary)] hover:text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] no-underline focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n</div>\n}\n", styles: [":host{display:block}.header-container{box-shadow:0 1px 2px #00000008;will-change:transform}.nav-link{transition:color .15s ease-in-out}.nav-link.active{color:var(--foreground)!important;font-weight:600}.nav-link.active:after{content:\"\";position:absolute;bottom:-14px;left:0;right:0;height:2px;background-color:rgb(var(--primary));border-radius:9999px}.mobile-drawer{box-shadow:0 10px 15px -3px #0000000d,0 4px 6px -2px #00000008;animation:slideDown .2s cubic-bezier(.16,1,.3,1)}.mobile-active{color:var(--foreground)!important;background-color:rgba(var(--foreground),.03);font-weight:700}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}:focus-visible{outline:2px solid rgb(var(--primary));outline-offset:2px}:focus:not(:focus-visible){outline:none}@supports not (backdrop-filter: blur(12px)){.header-container{background-color:rgb(var(--background))}}lucide-icon{display:inline-flex;vertical-align:middle}*{transition-property:background-color,border-color,color;transition-duration:.15s;transition-timing-function:ease-in-out}\n"] }]
508
- }], propDecorators: { user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: false }] }], permissions: [{ type: i0.Input, args: [{ isSignal: true, alias: "permissions", required: false }] }], organization: [{ type: i0.Input, args: [{ isSignal: true, alias: "organization", required: false }] }], navigationItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "navigationItems", required: false }] }], applications: [{ type: i0.Input, args: [{ isSignal: true, alias: "applications", required: false }] }], notifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "notifications", required: false }] }], notificationCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "notificationCount", required: false }] }], logoUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "logoUrl", required: false }] }], appName: [{ type: i0.Input, args: [{ isSignal: true, alias: "appName", required: false }] }], search: [{ type: i0.Output, args: ["search"] }], applicationSelected: [{ type: i0.Output, args: ["applicationSelected"] }], notificationClick: [{ type: i0.Output, args: ["notificationClick"] }], markRead: [{ type: i0.Output, args: ["markRead"] }], clearAll: [{ type: i0.Output, args: ["clearAll"] }], menuAction: [{ type: i0.Output, args: ["menuAction"] }] } });
506
+ ], template: "<header\n class=\"header-container fixed top-0 inset-x-0 z-40 flex h-[3.75rem] items-center justify-between px-3 text-[var(--foreground)] sm:px-4 lg:px-6\">\n\n <!-- Left Side: Brand, Current App & Desktop Links -->\n <div class=\"flex min-w-0 items-center gap-3\">\n\n <a routerLink=\"/\" class=\"brand-identity\">\n <img [src]=\"logoUrl()\" [alt]=\"appName() + ' logo'\" width=\"26\" height=\"26\" class=\"brand-logo\" />\n <span class=\"brand-name\">{{ appName() }}</span>\n </a>\n\n @if (currentAppName()) {\n <div class=\"current-app-identity\" [attr.aria-label]=\"'Current app: ' + currentAppName()\">\n <span class=\"brand-separator\" aria-hidden=\"true\">|</span>\n <span class=\"current-app-name\">{{ currentAppName() }}</span>\n </div>\n }\n\n <!-- Desktop Navigation Links -->\n <nav class=\"desktop-nav ml-1 hidden items-center lg:flex\" aria-label=\"Desktop primary navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{exact: true}\" class=\"nav-link\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" class=\"nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n </div>\n\n <!-- Middle: Search Input (Desktop) -->\n @if (showSearch()) {\n <div class=\"hidden flex-1 justify-center px-4 md:flex\">\n <div class=\"header-search-shell\">\n <lib-icon name=\"search\" [size]=\"14\"\n class=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-[var(--secondary)]\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #searchInput\n (keyup.enter)=\"onSearch(searchInput.value); searchInput.value = ''\" class=\"header-search\" autocomplete=\"off\"\n aria-label=\"Search\" />\n </div>\n </div>\n } @else {\n <div class=\"hidden flex-1 md:block\"></div>\n }\n\n <!-- Right Side Actions -->\n <div class=\"header-actions\">\n\n <!-- Mobile Search Trigger -->\n @if (showSearch()) {\n <button type=\"button\" class=\"header-action mobile-search-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Open mobile menu\" aria-expanded=\"isMobileMenuOpen()\" #menuToggle>\n <lib-icon name=\"search\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n\n <!-- Theme Switcher -->\n @if (showThemeSwitcher()) {\n <button type=\"button\" class=\"header-action\" (click)=\"themeService.toggleTheme()\" aria-label=\"Toggle theme\">\n @switch (themeService.theme()) {\n @case ('light') {\n <lib-icon name=\"sun\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @case ('dark') {\n <lib-icon name=\"moon\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @default {\n <lib-icon name=\"monitor\" [size]=\"16\" class=\"text-[var(--secondary)]\" />\n }\n }\n </button>\n }\n\n <!-- App Switcher -->\n @if (showAppSwitcher() && applications().length > 0) {\n <lib-app-switcher [applications]=\"applications()\" (applicationSelected)=\"onApplicationSelect($event)\" />\n }\n\n <!-- Notifications -->\n @if (showNotifications()) {\n <lib-notification-bell [unreadCount]=\"unreadCount()\" [notifications]=\"notifications()\"\n (notificationClick)=\"onNotificationClick($event)\" (markRead)=\"onMarkRead($event)\" (clearAll)=\"onClearAll()\" />\n }\n\n <!-- User Menu -->\n @if (user()) {\n <div class=\"user-menu-divider\">\n <lib-user-menu [user]=\"user()\" (menuAction)=\"onMenuAction($event)\" />\n </div>\n } @else if (showSignIn()) {\n <button type=\"button\" class=\"sign-in-button\" (click)=\"signIn.emit()\">\n Sign in\n </button>\n }\n\n <!-- Mobile Menu Toggle (Hamburger) -->\n @if (hasMobileMenuContent()) {\n <button type=\"button\" class=\"header-action mobile-menu-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Toggle navigation menu\" aria-expanded=\"isMobileMenuOpen()\" #mobileMenuToggle>\n <lib-icon [name]=\"isMobileMenuOpen() ? 'x' : 'menu'\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n </div>\n</header>\n\n<!-- Mobile Menu Backdrop -->\n@if (isMobileMenuOpen() && hasMobileMenuContent()) {\n<div class=\"mobile-backdrop fixed inset-0 z-30 lg:hidden\" (click)=\"closeMobileMenu()\"\n (keydown.escape)=\"closeMobileMenu()\" tabindex=\"-1\" aria-hidden=\"true\"></div>\n\n<!-- Mobile Navigation Drawer -->\n<div\n class=\"mobile-drawer fixed inset-x-2 top-16 z-40 flex max-h-[calc(100dvh-4.5rem)] flex-col rounded-2xl border border-[var(--border)] bg-[var(--background)] p-3 lg:hidden\"\n role=\"dialog\" aria-modal=\"true\" aria-label=\"Mobile navigation\" (keydown.escape)=\"closeMobileMenu()\">\n <!-- Mobile Search -->\n @if (showSearch()) {\n <div class=\"relative mb-3 w-full\">\n <lib-icon name=\"search\" [size]=\"16\"\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #mobileSearchInput\n (keyup.enter)=\"onSearch(mobileSearchInput.value); mobileSearchInput.value = ''; closeMobileMenu()\"\n class=\"header-search mobile\" autocomplete=\"off\" aria-label=\"Search\" />\n </div>\n }\n\n <!-- Mobile Nav Links -->\n <nav class=\"flex flex-col gap-1 overflow-y-auto\" role=\"navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"mobile-active\" [routerLinkActiveOptions]=\"{exact: true}\"\n (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\" [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n</div>\n}\n", styles: [":host{display:block}.header-container{border-bottom:1px solid var(--border);background:var(--background);box-shadow:0 1px 2px #0f172a0a;will-change:transform}.brand-identity{display:inline-flex;min-width:0;align-items:center;gap:.5rem;border-radius:.5rem;color:var(--foreground);text-decoration:none}.brand-identity:hover{opacity:.9}.brand-logo{width:1.625rem;height:1.625rem;flex-shrink:0;-webkit-user-select:none;user-select:none}.brand-name{display:none;color:var(--foreground);font-size:.95rem;font-weight:750;letter-spacing:-.01em;white-space:nowrap}.current-app-identity{display:inline-flex;min-width:0;align-items:center;gap:.5rem}.brand-separator{color:var(--border);font-size:1rem;font-weight:500}.current-app-name{max-width:11rem;overflow:hidden;color:var(--foreground);font-size:.875rem;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.desktop-nav{gap:1.25rem;padding:0;background:transparent}.nav-link{position:relative;display:inline-flex;align-items:center;min-height:2rem;color:var(--secondary);padding:0;font-size:.78rem;font-weight:600;line-height:1;text-decoration:none;transition:color .15s ease,opacity .15s ease}.nav-link:hover{color:var(--foreground)}.nav-link.active{color:var(--foreground)!important;font-weight:700}.nav-link.active:after{content:\"\";position:absolute;right:0;bottom:-.85rem;left:0;height:.125rem;background-color:var(--primary);border-radius:9999px}.header-search-shell{position:relative;width:min(100%,20rem)}.header-search{width:100%;height:2.25rem;border:1px solid var(--border);border-radius:.5rem;outline:0;color:var(--foreground);background:var(--background);padding:0 .9rem 0 2.25rem;font-size:.78rem;transition:border-color .15s ease,box-shadow .15s ease}.header-search::placeholder{color:var(--secondary)}.header-search:focus{border-color:var(--primary);box-shadow:0 0 0 2px color-mix(in srgb,var(--primary) 16%,transparent)}.header-search.mobile{width:100%;height:2.6rem;border-radius:.625rem;font-size:.875rem}.header-search.mobile:focus{width:100%}.header-actions{display:flex;align-items:center;gap:.25rem;background:transparent}.header-action{display:grid;width:2.15rem;height:2.15rem;place-items:center;border:1px solid transparent;border-radius:.5rem;color:var(--foreground);background:transparent;cursor:pointer;transition:background-color .15s ease,border-color .15s ease,color .15s ease}.header-action:hover{border-color:var(--border);background:color-mix(in srgb,var(--border) 35%,transparent)}.user-menu-divider{display:flex;height:1.75rem;align-items:center;margin-left:.2rem;border-left:1px solid var(--border);padding-left:.35rem}.sign-in-button{height:2.15rem;border:1px solid var(--primary);border-radius:.5rem;color:#fff;background:var(--primary);padding:0 .85rem;font-size:.825rem;font-weight:700;cursor:pointer;transition:opacity .15s ease,box-shadow .15s ease}.sign-in-button:hover{opacity:.92;box-shadow:0 4px 10px color-mix(in srgb,var(--primary) 18%,transparent)}.sign-in-button:focus-visible{outline:2px solid var(--primary);outline-offset:2px}.mobile-backdrop{background:#0f172a29}.mobile-drawer{box-shadow:0 12px 30px #0f172a24;animation:slideDown .2s cubic-bezier(.16,1,.3,1)}.mobile-nav-link{display:flex;align-items:center;min-height:2.75rem;border-radius:.625rem;color:var(--secondary);padding:0 .85rem;font-size:.925rem;font-weight:700;text-decoration:none;transition:background-color .15s ease,color .15s ease,transform .15s ease}.mobile-nav-link:hover{color:var(--foreground);background:color-mix(in srgb,var(--border) 35%,transparent)}.mobile-active{color:var(--foreground)!important;background:color-mix(in srgb,var(--primary) 10%,transparent);font-weight:700}@keyframes slideDown{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}:focus-visible{outline:2px solid var(--primary);outline-offset:2px}:focus:not(:focus-visible){outline:none}lucide-icon{display:inline-flex;vertical-align:middle}*{transition-property:background-color,border-color,color;transition-duration:.15s;transition-timing-function:ease-in-out}@media(min-width:640px){.brand-name{display:inline-block}.header-actions{gap:.25rem}.user-menu-divider{padding-left:.5rem}}@media(min-width:768px){.mobile-search-action{display:none}}@media(min-width:1024px){.mobile-menu-action{display:none}}@media(max-width:420px){.current-app-name{max-width:8rem}.header-actions{gap:0;padding:0;background:transparent}.header-action{width:2rem;height:2rem}}\n"] }]
507
+ }], propDecorators: { user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: false }] }], permissions: [{ type: i0.Input, args: [{ isSignal: true, alias: "permissions", required: false }] }], organization: [{ type: i0.Input, args: [{ isSignal: true, alias: "organization", required: false }] }], navigationItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "navigationItems", required: false }] }], applications: [{ type: i0.Input, args: [{ isSignal: true, alias: "applications", required: false }] }], notifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "notifications", required: false }] }], notificationCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "notificationCount", required: false }] }], logoUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "logoUrl", required: false }] }], appName: [{ type: i0.Input, args: [{ isSignal: true, alias: "appName", required: false }] }], currentAppName: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentAppName", required: false }] }], showSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSearch", required: false }] }], showNotifications: [{ type: i0.Input, args: [{ isSignal: true, alias: "showNotifications", required: false }] }], showAppSwitcher: [{ type: i0.Input, args: [{ isSignal: true, alias: "showAppSwitcher", required: false }] }], showThemeSwitcher: [{ type: i0.Input, args: [{ isSignal: true, alias: "showThemeSwitcher", required: false }] }], showSignIn: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSignIn", required: false }] }], search: [{ type: i0.Output, args: ["search"] }], signIn: [{ type: i0.Output, args: ["signIn"] }], applicationSelected: [{ type: i0.Output, args: ["applicationSelected"] }], notificationClick: [{ type: i0.Output, args: ["notificationClick"] }], markRead: [{ type: i0.Output, args: ["markRead"] }], clearAll: [{ type: i0.Output, args: ["clearAll"] }], menuAction: [{ type: i0.Output, args: ["menuAction"] }] } });
509
508
 
510
509
  /*
511
510
  * Public API Surface of ui
@@ -1 +1 @@
1
- {"version":3,"file":"excellore-ui.mjs","sources":["../../../projects/ui/src/lib/services/theme.service.ts","../../../projects/ui/src/lib/navigation/app-switcher/app-switcher.ts","../../../projects/ui/src/lib/navigation/app-switcher/app-switcher.html","../../../projects/ui/src/lib/navigation/organization-switcher/organization-switcher.ts","../../../projects/ui/src/lib/navigation/organization-switcher/organization-switcher.html","../../../projects/ui/src/lib/notifications/notification-panel/notification-panel.ts","../../../projects/ui/src/lib/notifications/notification-panel/notification-panel.html","../../../projects/ui/src/lib/notifications/notification-bell/notification-bell.ts","../../../projects/ui/src/lib/notifications/notification-bell/notification-bell.html","../../../projects/ui/src/lib/user/user-avatar/user-avatar.ts","../../../projects/ui/src/lib/user/user-avatar/user-avatar.html","../../../projects/ui/src/lib/user/user-menu/user-menu.ts","../../../projects/ui/src/lib/user/user-menu/user-menu.html","../../../projects/ui/src/lib/shared/icon/icon.component.ts","../../../projects/ui/src/lib/layout/header/header.ts","../../../projects/ui/src/lib/layout/header/header.html","../../../projects/ui/src/public-api.ts","../../../projects/ui/src/excellore-ui.ts"],"sourcesContent":["import { Injectable, signal, effect, Inject, PLATFORM_ID, inject } from '@angular/core';\nimport { isPlatformBrowser } from '@angular/common';\nimport { Theme } from '../models/models.interface';\n\nconst THEME_STORAGE_KEY = 'excellore-theme';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class ThemeService {\n private readonly platformId = inject(PLATFORM_ID);\n private readonly isBrowser = isPlatformBrowser(this.platformId);\n\n // The active theme preference ('light', 'dark', 'system')\n readonly theme = signal<Theme>('system');\n\n // The currently resolved actual theme applied ('light' | 'dark')\n readonly resolvedTheme = signal<'light' | 'dark'>('light');\n\n private mediaQueryListener?: (e: MediaQueryListEvent) => void;\n\n constructor() {\n this.initializeTheme();\n\n // Effect to reactively update storage, apply document classes, and set up listeners\n effect(() => {\n const activeTheme = this.theme();\n this.saveThemeToStorage(activeTheme);\n this.applyTheme(activeTheme);\n });\n }\n\n setTheme(theme: Theme): void {\n this.theme.set(theme);\n }\n\n getTheme(): Theme {\n return this.theme();\n }\n\n toggleTheme(): void {\n const current = this.theme();\n if (current === 'light') {\n this.setTheme('dark');\n } else if (current === 'dark') {\n this.setTheme('system');\n } else {\n this.setTheme('light');\n }\n }\n\n private initializeTheme(): void {\n if (!this.isBrowser) {\n return;\n }\n\n try {\n const savedTheme = localStorage.getItem(THEME_STORAGE_KEY) as Theme;\n if (savedTheme === 'light' || savedTheme === 'dark' || savedTheme === 'system') {\n this.theme.set(savedTheme);\n } else {\n this.theme.set('system');\n }\n } catch {\n this.theme.set('system');\n }\n }\n\n private saveThemeToStorage(theme: Theme): void {\n if (!this.isBrowser) {\n return;\n }\n\n try {\n localStorage.setItem(THEME_STORAGE_KEY, theme);\n } catch (e) {\n console.warn('Failed to save theme to localStorage:', e);\n }\n }\n\n private applyTheme(theme: Theme): void {\n if (!this.isBrowser) {\n return;\n }\n\n const doc = document.documentElement;\n const mediaQuery = typeof window.matchMedia === 'function' ? window.matchMedia('(prefers-color-scheme: dark)') : null;\n\n // Clean up any previous listener to avoid leaks\n if (this.mediaQueryListener && mediaQuery) {\n mediaQuery.removeEventListener('change', this.mediaQueryListener);\n this.mediaQueryListener = undefined;\n }\n\n let actualTheme: 'light' | 'dark' = 'light';\n\n if (theme === 'system') {\n actualTheme = mediaQuery?.matches ? 'dark' : 'light';\n\n if (mediaQuery) {\n // Watch for system preference changes in real-time\n this.mediaQueryListener = (e: MediaQueryListEvent) => {\n const newResolvedTheme = e.matches ? 'dark' : 'light';\n this.resolvedTheme.set(newResolvedTheme);\n this.updateDocumentClass(newResolvedTheme);\n };\n mediaQuery.addEventListener('change', this.mediaQueryListener);\n }\n } else {\n actualTheme = theme;\n }\n\n this.resolvedTheme.set(actualTheme);\n this.updateDocumentClass(actualTheme);\n }\n\n private updateDocumentClass(resolved: 'light' | 'dark'): void {\n const doc = document.documentElement;\n if (resolved === 'dark') {\n doc.classList.add('dark');\n doc.classList.remove('light');\n } else {\n doc.classList.add('light');\n doc.classList.remove('dark');\n }\n }\n}\n","import { Component, input, output, HostListener, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Application } from '../../models/models.interface';\nimport { LucideAngularModule, Grid, ChevronDown } from 'lucide-angular';\n\n@Component({\n selector: 'lib-app-switcher',\n standalone: true,\n imports: [CommonModule, LucideAngularModule],\n templateUrl: './app-switcher.html',\n styleUrl: './app-switcher.css',\n})\nexport class AppSwitcherComponent {\n readonly applications = input<Application[]>([]);\n readonly applicationSelected = output<Application>();\n\n readonly gridIcon = Grid;\n readonly chevronDownIcon = ChevronDown;\n\n readonly isOpen = signal(false);\n\n toggleSwitcher(): void {\n this.isOpen.update((v) => !v);\n }\n\n closeSwitcher(): void {\n this.isOpen.set(false);\n }\n\n onSelect(app: Application): void {\n if (app.available !== false) {\n this.applicationSelected.emit(app);\n this.closeSwitcher();\n }\n }\n\n getInitials(name: string): string {\n return name\n .split(/[.\\s_-]+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((part) => part[0]?.toUpperCase())\n .join('') || 'AP';\n }\n\n @HostListener('document:click', ['$event'])\n onOutsideClick(event: MouseEvent): void {\n const target = event.target as HTMLElement;\n if (!target.closest('.app-switcher-wrapper')) {\n this.closeSwitcher();\n }\n }\n}\n","<div class=\"app-switcher-wrapper relative inline-block text-left\">\n <button\n type=\"button\"\n class=\"grid h-9 w-9 place-items-center rounded-md border border-transparent text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\n (click)=\"toggleSwitcher()\"\n aria-haspopup=\"true\"\n [aria-expanded]=\"isOpen()\"\n aria-label=\"Toggle application switcher\"\n >\n <lucide-icon [name]=\"gridIcon\" class=\"h-4.5 w-4.5\" />\n </button>\n\n @if (isOpen()) {\n <div class=\"app-switcher-dropdown absolute right-0 mt-2 w-[320px] origin-top-right rounded-lg border border-[var(--border)] bg-[var(--background)] p-3 shadow-lg focus:outline-none z-50\">\n <div class=\"mb-2.5 flex items-center justify-between border-b border-[var(--border)] pb-2\">\n <h3 class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Excellore Apps</h3>\n </div>\n \n @if (applications().length === 0) {\n <div class=\"py-6 text-center text-xs text-[var(--secondary)]\">\n No other apps available\n </div>\n } @else {\n <div class=\"grid grid-cols-3 gap-1.5\">\n @for (app of applications(); track app.id) {\n @if (app.available !== false) {\n <a\n [href]=\"app.url\"\n (click)=\"onSelect(app)\"\n class=\"flex flex-col items-center gap-1.5 rounded-lg p-2 text-center no-underline transition hover:bg-[rgba(var(--foreground),0.05)] border border-transparent hover:border-[var(--border)] cursor-pointer\"\n >\n <div class=\"flex h-10 w-10 items-center justify-center rounded-lg bg-[rgba(var(--primary),0.08)] text-[var(--primary)] text-sm font-bold border border-[rgba(var(--primary),0.12)]\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <span class=\"text-xs font-medium text-[var(--foreground)] truncate w-full\" [title]=\"app.name\">{{ app.name }}</span>\n </a>\n } @else {\n <div class=\"flex flex-col items-center gap-1.5 rounded-lg p-2 text-center opacity-40 select-none\">\n <div class=\"flex h-10 w-10 items-center justify-center rounded-lg bg-[rgba(var(--foreground),0.04)] text-[var(--secondary)] text-sm font-bold border border-dashed border-[var(--border)]\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"flex flex-col items-center w-full\">\n <span class=\"text-xs font-medium text-[var(--foreground)] truncate w-full\">{{ app.name }}</span>\n <span class=\"text-[9px] font-medium tracking-wider text-[var(--secondary)] uppercase\">Soon</span>\n </div>\n </div>\n }\n }\n </div>\n }\n </div>\n }\n</div>\n","import { Component, input, output, HostListener, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Organization } from '../../models/models.interface';\nimport { LucideAngularModule, ChevronDown, Check, Building } from 'lucide-angular';\n\n@Component({\n selector: 'lib-organization-switcher',\n standalone: true,\n imports: [CommonModule, LucideAngularModule],\n templateUrl: './organization-switcher.html',\n styleUrl: './organization-switcher.css',\n})\nexport class OrganizationSwitcherComponent {\n readonly organizations = input<Organization[]>([]);\n readonly selectedOrganization = input<Organization | null>(null);\n readonly organizationChanged = output<Organization>();\n\n readonly chevronDownIcon = ChevronDown;\n readonly checkIcon = Check;\n readonly buildingIcon = Building;\n\n readonly isOpen = signal(false);\n\n toggleSwitcher(): void {\n this.isOpen.update((v) => !v);\n }\n\n closeSwitcher(): void {\n this.isOpen.set(false);\n }\n\n onSelect(org: Organization): void {\n this.organizationChanged.emit(org);\n this.closeSwitcher();\n }\n\n @HostListener('document:click', ['$event'])\n onOutsideClick(event: MouseEvent): void {\n const target = event.target as HTMLElement;\n if (!target.closest('.org-switcher-wrapper')) {\n this.closeSwitcher();\n }\n }\n}\n","<div class=\"org-switcher-wrapper relative inline-block text-left\">\n <button\n type=\"button\"\n class=\"flex items-center gap-1.5 rounded-md border border-[var(--border)] bg-[var(--background)] px-2.5 py-1.5 text-xs font-medium text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\n (click)=\"toggleSwitcher()\"\n aria-haspopup=\"true\"\n [aria-expanded]=\"isOpen()\"\n aria-label=\"Switch organization\"\n >\n <lucide-icon [name]=\"buildingIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)]\" />\n <span class=\"truncate max-w-[120px]\">{{ selectedOrganization()?.name || 'Select Tenant' }}</span>\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3 w-3 text-[var(--secondary)] transition-transform duration-200\" [class.rotate-180]=\"isOpen()\" />\n </button>\n\n @if (isOpen()) {\n <div class=\"org-switcher-dropdown absolute left-0 mt-2 w-52 origin-top-left rounded-lg border border-[var(--border)] bg-[var(--background)] p-1 shadow-lg focus:outline-none z-50\">\n <div class=\"px-2 py-1.5 border-b border-[var(--border)] mb-1\">\n <span class=\"text-[9px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Switch Organization</span>\n </div>\n\n <div class=\"py-0.5 space-y-0.5 max-h-48 overflow-y-auto\">\n @for (org of organizations(); track org.id) {\n <button\n type=\"button\"\n class=\"flex w-full items-center justify-between rounded-md px-2 py-1.5 text-xs text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\n (click)=\"onSelect(org)\"\n >\n <span class=\"truncate pr-2\" [class.font-semibold]=\"org.id === selectedOrganization()?.id\">{{ org.name }}</span>\n @if (org.id === selectedOrganization()?.id) {\n <lucide-icon [name]=\"checkIcon\" class=\"h-3.5 w-3.5 text-[var(--primary)] flex-shrink-0\" />\n }\n </button>\n }\n </div>\n </div>\n }\n</div>\n","import { Component, input, output } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Notification } from '../../models/models.interface';\nimport { LucideAngularModule, CheckCheck, Trash2, BellOff, Circle } from 'lucide-angular';\n\n@Component({\n selector: 'lib-notification-panel',\n standalone: true,\n imports: [CommonModule, LucideAngularModule],\n templateUrl: './notification-panel.html',\n styleUrl: './notification-panel.css',\n})\nexport class NotificationPanelComponent {\n readonly notifications = input<Notification[]>([]);\n readonly notificationClick = output<Notification>();\n readonly markRead = output<string>();\n readonly clearAll = output<void>();\n\n readonly checkCheckIcon = CheckCheck;\n readonly trashIcon = Trash2;\n readonly bellOffIcon = BellOff;\n readonly circleIcon = Circle;\n\n onNotificationClick(notification: Notification): void {\n this.notificationClick.emit(notification);\n }\n\n onMarkRead(id: string, event: MouseEvent): void {\n event.stopPropagation();\n this.markRead.emit(id);\n }\n\n onClearAll(event: MouseEvent): void {\n event.stopPropagation();\n this.clearAll.emit();\n }\n\n formatTime(date: Date | string): string {\n const d = new Date(date);\n const now = new Date();\n const diffMs = now.getTime() - d.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n const diffHours = Math.floor(diffMins / 60);\n const diffDays = Math.floor(diffHours / 24);\n\n if (diffMins < 1) return 'Just now';\n if (diffMins < 60) return `${diffMins}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n if (diffDays === 1) return 'Yesterday';\n return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n }\n}\n","<div class=\"notification-panel-inner flex flex-col h-full max-h-[380px] w-full text-left\">\n <!-- Panel Header -->\n <div class=\"flex items-center justify-between border-b border-[var(--border)] px-3.5 py-2\">\n <span class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Notifications</span>\n @if (notifications().length > 0) {\n <button\n type=\"button\"\n class=\"flex items-center gap-1 text-[9px] font-bold text-[var(--secondary)] hover:text-[var(--danger,#ef4444)] hover:underline cursor-pointer\"\n (click)=\"onClearAll($event)\"\n >\n <lucide-icon [name]=\"trashIcon\" class=\"h-3 w-3\" />\n <span>Clear All</span>\n </button>\n }\n </div>\n\n <!-- Panel Body -->\n <div class=\"flex-1 overflow-y-auto min-h-0 py-0.5 divide-y divide-[var(--border)]\">\n @if (notifications().length === 0) {\n <div class=\"flex flex-col items-center justify-center py-10 px-4 text-center select-none\">\n <div class=\"flex h-10 w-10 items-center justify-center rounded-full bg-[rgba(var(--secondary),0.06)] text-[var(--secondary)] mb-2.5\">\n <lucide-icon [name]=\"bellOffIcon\" class=\"h-4.5 w-4.5\" />\n </div>\n <p class=\"text-xs font-semibold text-[var(--foreground)]\">All caught up</p>\n <p class=\"text-[10px] text-[var(--secondary)] mt-0.5\">No new notifications to display.</p>\n </div>\n } @else {\n @for (item of notifications(); track item.id) {\n <div\n (click)=\"onNotificationClick(item)\"\n class=\"notification-item flex gap-2.5 p-3 transition hover:bg-[rgba(var(--foreground),0.02)] cursor-pointer relative\"\n [class.bg-[rgba(var(--primary),0.015)]]=\"!item.read\"\n >\n <!-- Read status dot -->\n <div class=\"flex-shrink-0 pt-1\">\n @if (!item.read) {\n <span class=\"block h-1.5 w-1.5 rounded-full bg-[var(--primary)] animate-pulse\"></span>\n } @else {\n <span class=\"block h-1.5 w-1.5 rounded-full bg-transparent\"></span>\n }\n </div>\n\n <!-- Notification content -->\n <div class=\"flex-1 min-w-0\">\n <h4 class=\"text-xs font-semibold text-[var(--foreground)] truncate\" [class.font-bold]=\"!item.read\">\n {{ item.title }}\n </h4>\n <p class=\"text-[10.5px] text-[var(--secondary)] mt-0.5 leading-snug break-words\">\n {{ item.message }}\n </p>\n <span class=\"block text-[9px] text-[var(--secondary)] mt-1 font-medium\">\n {{ formatTime(item.createdAt) }}\n </span>\n </div>\n\n <!-- Actions -->\n @if (!item.read) {\n <div class=\"flex items-center flex-shrink-0\">\n <button\n type=\"button\"\n class=\"mark-read-btn grid h-6 w-6 place-items-center rounded-full text-[var(--secondary)] hover:text-[var(--primary)] hover:bg-[rgba(var(--foreground),0.05)] cursor-pointer\"\n [title]=\"'Mark as read'\"\n (click)=\"onMarkRead(item.id, $event)\"\n >\n <lucide-icon [name]=\"checkCheckIcon\" class=\"h-3.5 w-3.5\" />\n </button>\n </div>\n }\n </div>\n }\n }\n </div>\n</div>\n","import { Component, input, output, HostListener, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Notification } from '../../models/models.interface';\nimport { NotificationPanelComponent } from '../notification-panel/notification-panel';\nimport { LucideAngularModule, Bell } from 'lucide-angular';\n\n@Component({\n selector: 'lib-notification-bell',\n standalone: true,\n imports: [CommonModule, NotificationPanelComponent, LucideAngularModule],\n templateUrl: './notification-bell.html',\n styleUrl: './notification-bell.css',\n})\nexport class NotificationBellComponent {\n readonly unreadCount = input<number>(0);\n readonly notifications = input<Notification[]>([]);\n\n readonly notificationClick = output<Notification>();\n readonly markRead = output<string>();\n readonly clearAll = output<void>();\n\n readonly bellIcon = Bell;\n\n readonly isOpen = signal(false);\n\n togglePanel(): void {\n this.isOpen.update((v) => !v);\n }\n\n closePanel(): void {\n this.isOpen.set(false);\n }\n\n onNotificationClick(notification: Notification): void {\n this.notificationClick.emit(notification);\n this.closePanel();\n }\n\n onMarkRead(id: string): void {\n this.markRead.emit(id);\n }\n\n onClearAll(): void {\n this.clearAll.emit();\n }\n\n @HostListener('document:click', ['$event'])\n onOutsideClick(event: MouseEvent): void {\n const target = event.target as HTMLElement;\n if (!target.closest('.notification-bell-wrapper')) {\n this.closePanel();\n }\n }\n}\n","<div class=\"notification-bell-wrapper relative inline-block text-left\">\n <button\n type=\"button\"\n class=\"relative grid h-9 w-9 place-items-center rounded-md border border-transparent text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\n (click)=\"togglePanel()\"\n aria-haspopup=\"true\"\n [aria-expanded]=\"isOpen()\"\n aria-label=\"View notifications\"\n >\n <lucide-icon [name]=\"bellIcon\" class=\"h-4.5 w-4.5\" />\n \n @if (unreadCount() > 0) {\n <span class=\"absolute top-1.5 right-1.5 flex h-2 w-2\">\n <span class=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--danger,#ef4444)] opacity-75\"></span>\n <span class=\"relative inline-flex rounded-full h-2 w-2 bg-[var(--danger,#ef4444)]\"></span>\n </span>\n }\n </button>\n\n @if (isOpen()) {\n <div class=\"notification-dropdown absolute right-0 mt-2 w-80 origin-top-right rounded-lg border border-[var(--border)] bg-[var(--background)] p-0 shadow-lg focus:outline-none z-50\">\n <lib-notification-panel\n [notifications]=\"notifications()\"\n (notificationClick)=\"onNotificationClick($event)\"\n (markRead)=\"onMarkRead($event)\"\n (clearAll)=\"onClearAll()\"\n />\n </div>\n }\n</div>\n","import { Component, input, computed } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { User } from '../../models/models.interface';\n\n@Component({\n selector: 'lib-user-avatar',\n standalone: true,\n imports: [CommonModule],\n templateUrl: './user-avatar.html',\n styleUrl: './user-avatar.css',\n})\nexport class UserAvatarComponent {\n // Input using the modern Angular signal inputs\n readonly user = input<User | null>(null);\n\n // Computes the initials from the user's name or email\n readonly initials = computed(() => {\n const userData = this.user();\n if (!userData) return 'EU';\n\n const source = userData.name || userData.email || 'Excellore User';\n return source\n .split(/[.@\\s_-]+/)\n .filter(Boolean)\n .slice(0, 2)\n .map(part => part[0]?.toUpperCase())\n .join('') || 'EU';\n });\n\n // Check if image loading failed to dynamically switch to initials\n protected imageError = false;\n\n onImageError(): void {\n this.imageError = true;\n }\n}\n","<div class=\"avatar-container flex h-full w-full items-center justify-center rounded-full border border-[var(--border)] select-none overflow-hidden transition-all duration-200\">\n @if (user()?.avatar && !imageError) {\n <img [src]=\"user()?.avatar\" [alt]=\"user()?.name || 'User avatar'\" class=\"avatar-img h-full w-full object-cover\" (error)=\"onImageError()\" />\n } @else {\n <span class=\"avatar-initials text-xs font-semibold uppercase leading-none\">{{ initials() }}</span>\n }\n</div>\n","import { Component, input, output, HostListener, signal } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { User } from '../../models/models.interface';\nimport { UserAvatarComponent } from '../user-avatar/user-avatar';\nimport {\n LucideAngularModule,\n User as UserIcon,\n Settings as SettingsIcon,\n CreditCard as CreditCardIcon,\n LogOut as LogOutIcon,\n ChevronDown as ChevronDownIcon,\n} from 'lucide-angular';\n\n@Component({\n selector: 'lib-user-menu',\n standalone: true,\n imports: [CommonModule, UserAvatarComponent, LucideAngularModule],\n templateUrl: './user-menu.html',\n styleUrl: './user-menu.css',\n})\nexport class UserMenuComponent {\n readonly user = input<User | null>(null);\n readonly menuAction = output<'profile' | 'settings' | 'billing' | 'logout'>();\n\n // Lucide Icons mapping\n readonly userIcon = UserIcon;\n readonly settingsIcon = SettingsIcon;\n readonly creditCardIcon = CreditCardIcon;\n readonly logOutIcon = LogOutIcon;\n readonly chevronDownIcon = ChevronDownIcon;\n\n // Signalled open state\n readonly isOpen = signal(false);\n\n toggleMenu(): void {\n this.isOpen.update((v) => !v);\n }\n\n closeMenu(): void {\n this.isOpen.set(false);\n }\n\n onAction(action: 'profile' | 'settings' | 'billing' | 'logout'): void {\n this.menuAction.emit(action);\n this.closeMenu();\n }\n\n @HostListener('document:click', ['$event'])\n onOutsideClick(event: MouseEvent): void {\n const target = event.target as HTMLElement;\n if (!target.closest('.user-menu-wrapper')) {\n this.closeMenu();\n }\n }\n}\n","<div class=\"user-menu-wrapper relative inline-block text-left\">\n <button\n type=\"button\"\n class=\"flex items-center gap-1.5 rounded-full border border-[var(--border)] p-0.5 pr-2 transition-all hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] focus:ring-offset-2 cursor-pointer\"\n (click)=\"toggleMenu()\"\n aria-haspopup=\"true\"\n [aria-expanded]=\"isOpen()\"\n aria-label=\"User profile menu\"\n >\n <div class=\"h-7 w-7 rounded-full overflow-hidden\">\n <lib-user-avatar [user]=\"user()\" />\n </div>\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)] transition-transform duration-200\" [class.rotate-180]=\"isOpen()\" />\n </button>\n\n @if (isOpen()) {\n <div class=\"user-dropdown-panel absolute right-0 mt-2 w-56 origin-top-right rounded-lg border border-[var(--border)] bg-[var(--background)] p-1.5 shadow-lg focus:outline-none z-50\">\n @if (user(); as u) {\n <div class=\"px-3 py-2 border-b border-[var(--border)] mb-1\">\n <p class=\"text-[10px] font-normal text-[var(--secondary)] uppercase tracking-wider\">Signed in as</p>\n <p class=\"text-sm font-semibold truncate text-[var(--foreground)]\" [title]=\"u.name\">{{ u.name }}</p>\n <p class=\"text-xs truncate text-[var(--secondary)] mt-0.5\" [title]=\"u.email\">{{ u.email }}</p>\n </div>\n }\n\n <div class=\"py-0.5 space-y-0.5\">\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\n (click)=\"onAction('profile')\"\n >\n <lucide-icon [name]=\"userIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\n <span>My Profile</span>\n </button>\n\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\n (click)=\"onAction('settings')\"\n >\n <lucide-icon [name]=\"settingsIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\n <span>Settings</span>\n </button>\n\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\n (click)=\"onAction('billing')\"\n >\n <lucide-icon [name]=\"creditCardIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\n <span>Billing</span>\n </button>\n </div>\n\n <div class=\"border-t border-[var(--border)] mt-1 pt-1\">\n <button\n type=\"button\"\n class=\"flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm text-[var(--danger,#ef4444)] transition hover:bg-[rgba(239,68,68,0.08)] text-left cursor-pointer\"\n (click)=\"onAction('logout')\"\n >\n <lucide-icon [name]=\"logOutIcon\" class=\"h-4 w-4 text-[var(--danger,#ef4444)]\" />\n <span>Sign Out</span>\n </button>\n </div>\n </div>\n }\n</div>\n","// lib/shared/icon/icon.component.ts\r\nimport { Component, input, computed } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport {\r\n LucideAngularModule,\r\n Search,\r\n Menu,\r\n X,\r\n Sun,\r\n Moon,\r\n Monitor,\r\n Bell,\r\n Grid,\r\n ChevronDown,\r\n Building2\r\n} from 'lucide-angular';\r\n\r\nconst ICONS = {\r\n search: Search,\r\n menu: Menu,\r\n x: X,\r\n sun: Sun,\r\n moon: Moon,\r\n monitor: Monitor,\r\n bell: Bell,\r\n grid: Grid,\r\n chevronDown: ChevronDown,\r\n building2: Building2\r\n};\r\n\r\n@Component({\r\n selector: 'lib-icon',\r\n standalone: true,\r\n imports: [CommonModule, LucideAngularModule],\r\n template: `\r\n <lucide-icon \r\n [name]=\"iconName()\" \r\n [size]=\"size()\"\r\n [color]=\"color()\"\r\n [strokeWidth]=\"strokeWidth()\"\r\n class=\"lib-icon\"\r\n />\r\n `,\r\n styles: [`\r\n :host {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n }\r\n .lib-icon {\r\n display: block;\r\n }\r\n `]\r\n})\r\nexport class IconComponent {\r\n readonly name = input.required<string>();\r\n readonly size = input<number>(24);\r\n readonly color = input<string | undefined>(undefined);\r\n readonly strokeWidth = input<number | undefined>(undefined);\r\n\r\n protected readonly iconName = computed(() => {\r\n const name = this.name();\r\n return ICONS[name as keyof typeof ICONS] || Search;\r\n });\r\n}","import { Component, inject, input, output, signal, computed } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Router, RouterLink, RouterLinkActive } from '@angular/router';\nimport { User, Organization, NavigationItem, Notification, Application } from '../../models/models.interface';\nimport { ThemeService } from '../../services/theme.service';\nimport { AppSwitcherComponent } from '../../navigation';\nimport { NotificationBellComponent } from '../../notifications';\nimport { UserMenuComponent } from '../../user';\n\nimport { IconComponent } from '../../shared/icon/icon.component';\n\n\n@Component({\n selector: 'lib-header',\n standalone: true,\n imports: [\n CommonModule,\n RouterLink,\n RouterLinkActive,\n AppSwitcherComponent,\n NotificationBellComponent,\n UserMenuComponent,\n IconComponent,\n ],\n templateUrl: './header.html',\n styleUrl: './header.css',\n})\nexport class HeaderComponent {\n private readonly router = inject(Router);\n protected readonly themeService = inject(ThemeService);\n\n // User & Organization Inputs\n readonly user = input<User | null>(null);\n readonly permissions = input<string[]>([]);\n readonly organization = input<Organization | null>(null);\n\n // Navigation & Integration Inputs\n readonly navigationItems = input<NavigationItem[]>([]);\n readonly applications = input<Application[]>([]);\n readonly notifications = input<Notification[]>([]);\n readonly notificationCount = input<number>(0);\n readonly logoUrl = input<string>('https://cdn.jsdelivr.net/gh/Excellore/excellore-static-assets@main/images/logo_origin_tp.PNG');\n readonly appName = input<string>('Excellore');\n\n // Outputs as decided in the architecture plan\n readonly search = output<string>();\n readonly applicationSelected = output<Application>();\n readonly notificationClick = output<Notification>();\n readonly markRead = output<string>();\n readonly clearAll = output<void>();\n readonly menuAction = output<'profile' | 'settings' | 'billing' | 'logout'>();\n\n // Lucide Icons\n // readonly searchIcon = Search;\n // readonly menuIcon = Menu;\n // readonly xIcon = X;\n // readonly sunIcon = Sun;\n // readonly moonIcon = Moon;\n // readonly monitorIcon = Monitor;\n\n // Header specific UI states\n readonly isMobileMenuOpen = signal(false);\n\n // Checks if the user has permission to see an item\n hasPermission(item: NavigationItem): boolean {\n if (!item.permissions || item.permissions.length === 0) {\n return true;\n }\n const currentPermissions = this.permissions();\n return item.permissions.some((p) => currentPermissions.includes(p));\n }\n\n // Reactive unread count selector\n readonly unreadCount = computed(() => {\n const overrideCount = this.notificationCount();\n if (overrideCount > 0) return overrideCount;\n return this.notifications().filter((n) => !n.read).length;\n });\n\n // Determines if an item's internal route is active\n isActive(item: NavigationItem): boolean {\n if (!item.route) return false;\n try {\n return this.router.isActive(item.route, {\n paths: 'subset',\n queryParams: 'ignored',\n fragment: 'ignored',\n matrixParams: 'ignored',\n });\n } catch {\n return false;\n }\n }\n\n toggleMobileMenu(): void {\n this.isMobileMenuOpen.update((v) => !v);\n }\n\n closeMobileMenu(): void {\n this.isMobileMenuOpen.set(false);\n }\n\n onSearch(query: string): void {\n this.search.emit(query);\n }\n\n getOrganizationInitials(organization: Organization): string {\n return organization.name\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((word) => word[0])\n .join('')\n .toUpperCase();\n }\n\n onApplicationSelect(app: Application): void {\n this.applicationSelected.emit(app);\n }\n\n onNotificationClick(notification: Notification): void {\n this.notificationClick.emit(notification);\n }\n\n onMarkRead(id: string): void {\n this.markRead.emit(id);\n }\n\n onClearAll(): void {\n this.clearAll.emit();\n }\n\n onMenuAction(action: 'profile' | 'settings' | 'billing' | 'logout'): void {\n this.menuAction.emit(action);\n }\n}\n","<header\n class=\"header-container fixed top-0 inset-x-0 z-40 flex h-14 items-center justify-between border-b border-[var(--border)] bg-[var(--background)]/80 px-4 text-[var(--foreground)] backdrop-blur-md\">\n\n <!-- Left Side: Organization & Desktop Links -->\n <div class=\"flex items-center gap-3.5\">\n\n <!-- Excellore brand logo/title hidden while the organization is the primary header identity.\n <a routerLink=\"/\"\n class=\"flex items-center gap-2 text-sm font-bold tracking-tight text-[var(--foreground)] no-underline hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-[var(--primary)] rounded\">\n <img [src]=\"logoUrl()\" [alt]=\"appName() + ' logo'\" width=\"26\" height=\"26\" class=\"h-6.5 w-6.5 select-none\" />\n <span class=\"hidden sm:inline-block\">{{ appName() }}</span>\n </a>\n -->\n\n <!-- Organization Identity -->\n @if (organization(); as organization) {\n <div class=\"h-8 flex items-center\">\n <div class=\"flex items-center gap-2.5\" [attr.aria-label]=\"'Organization: ' + organization.name\">\n @if (organization.logoUrl) {\n <img [src]=\"organization.logoUrl\" [alt]=\"organization.name + ' logo'\"\n class=\"h-8 w-8 rounded-md object-contain bg-white\" />\n } @else {\n <span\n class=\"grid h-8 w-8 place-items-center rounded-md bg-[var(--primary)] text-[11px] font-bold text-white\"\n aria-hidden=\"true\">\n {{ getOrganizationInitials(organization) }}\n </span>\n }\n <span class=\"hidden max-w-[180px] truncate text-sm font-semibold text-[var(--foreground)] sm:inline\">\n {{ organization.name }}\n </span>\n </div>\n </div>\n }\n\n <!-- Desktop Navigation Links -->\n <nav class=\"ml-2 hidden lg:flex items-center gap-5.5\" aria-label=\"Desktop primary navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{exact: true}\"\n class=\"nav-link text-xs font-medium text-[var(--secondary)] transition hover:text-[var(--foreground)] no-underline py-1 relative focus:outline-none focus:ring-2 focus:ring-[var(--primary)] rounded\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\"\n class=\"nav-link text-xs font-medium text-[var(--secondary)] transition hover:text-[var(--foreground)] no-underline py-1 relative focus:outline-none focus:ring-2 focus:ring-[var(--primary)] rounded\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n </div>\n\n <!-- Middle: Search Input (Desktop) -->\n <div class=\"hidden md:flex flex-1 max-w-[260px] mx-4\">\n <div class=\"relative w-full\">\n <!-- <lucide-icon [name]=\"searchIcon\"\n class=\"absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" /> -->\n <lib-icon name=\"search\" [size]=\"14\"\n class=\"absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-[var(--secondary)]\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #searchInput\n (keyup.enter)=\"onSearch(searchInput.value); searchInput.value = ''\"\n class=\"w-full h-8 pl-8 pr-3 text-xs bg-[rgba(var(--foreground),0.02)] border border-[var(--border)] rounded-md focus:outline-none focus:bg-[var(--background)] focus:ring-2 focus:ring-[var(--primary)] text-[var(--foreground)] transition-all placeholder:text-[var(--secondary)]/60\"\n autocomplete=\"off\" aria-label=\"Search\" />\n </div>\n </div>\n\n <!-- Right Side Actions -->\n <div class=\"flex items-center gap-1 sm:gap-1.5\">\n\n <!-- Mobile Search Trigger -->\n <button type=\"button\"\n class=\"grid md:hidden h-8.5 w-8.5 place-items-center rounded-md border border-transparent hover:bg-[rgba(var(--foreground),0.03)] text-[var(--foreground)] cursor-pointer focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\"\n (click)=\"toggleMobileMenu()\" aria-label=\"Open mobile menu\" aria-expanded=\"isMobileMenuOpen()\" #menuToggle>\n <!-- <lucide-icon [name]=\"searchIcon\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" /> -->\n <lib-icon name=\"search\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n\n <!-- Theme Switcher -->\n <button type=\"button\"\n class=\"grid h-8.5 w-8.5 place-items-center rounded-md border border-transparent text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\n (click)=\"themeService.toggleTheme()\" aria-label=\"Toggle theme\">\n @switch (themeService.theme()) {\n @case ('light') {\n <!-- <lucide-icon [name]=\"sunIcon\" class=\"h-4 w-4 text-[var(--primary)]\" aria-hidden=\"true\" /> -->\n <lib-icon name=\"sun\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @case ('dark') {\n <!-- <lucide-icon [name]=\"moonIcon\" class=\"h-4 w-4 text-[var(--primary)]\" aria-hidden=\"true\" /> -->\n <lib-icon name=\"moon\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @default {\n <!-- <lucide-icon [name]=\"monitorIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" aria-hidden=\"true\" /> -->\n <lib-icon name=\"monitor\" [size]=\"16\" class=\"text-[var(--secondary)]\" />\n }\n }\n </button>\n\n <!-- App Switcher -->\n @if (applications().length > 0) {\n <lib-app-switcher [applications]=\"applications()\" (applicationSelected)=\"onApplicationSelect($event)\" />\n }\n\n <!-- Notifications -->\n <lib-notification-bell [unreadCount]=\"unreadCount()\" [notifications]=\"notifications()\"\n (notificationClick)=\"onNotificationClick($event)\" (markRead)=\"onMarkRead($event)\" (clearAll)=\"onClearAll()\" />\n\n <!-- User Menu -->\n @if (user()) {\n <div class=\"h-6 border-l border-[var(--border)] pl-1.5 sm:pl-2 flex items-center ml-0.5\">\n <lib-user-menu [user]=\"user()\" (menuAction)=\"onMenuAction($event)\" />\n </div>\n }\n\n <!-- Mobile Menu Toggle (Hamburger) -->\n <button type=\"button\"\n class=\"grid lg:hidden h-8.5 w-8.5 place-items-center rounded-md border border-transparent text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\n (click)=\"toggleMobileMenu()\" aria-label=\"Toggle navigation menu\" aria-expanded=\"isMobileMenuOpen()\"\n #mobileMenuToggle>\n <!-- <lucide-icon [name]=\"isMobileMenuOpen() ? xIcon : menuIcon\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" /> -->\n <lib-icon [name]=\"isMobileMenuOpen() ? 'x' : 'menu'\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n </div>\n</header>\n\n<!-- Mobile Menu Backdrop -->\n@if (isMobileMenuOpen()) {\n<div class=\"fixed inset-0 z-30 bg-black/20 lg:hidden\" (click)=\"closeMobileMenu()\" (keydown.escape)=\"closeMobileMenu()\"\n tabindex=\"-1\" aria-hidden=\"true\"></div>\n\n<!-- Mobile Navigation Drawer -->\n<div\n class=\"mobile-drawer fixed top-14 inset-x-0 bottom-0 z-40 flex flex-col bg-[var(--background)] border-t border-[var(--border)] p-4 lg:hidden\"\n role=\"dialog\" aria-modal=\"true\" aria-label=\"Mobile navigation\" (keydown.escape)=\"closeMobileMenu()\">\n <!-- Mobile Search -->\n <div class=\"relative w-full mb-4\">\n <!-- <lucide-icon [name]=\"searchIcon\"\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" /> -->\n\n <lib-icon name=\"search\" [size]=\"16\"\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #mobileSearchInput\n (keyup.enter)=\"onSearch(mobileSearchInput.value); mobileSearchInput.value = ''; closeMobileMenu()\"\n class=\"w-full h-10 pl-9 pr-3 text-sm bg-[rgba(var(--foreground),0.02)] border border-[var(--border)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--primary)] text-[var(--foreground)] placeholder:text-[var(--secondary)]/60\"\n autocomplete=\"off\" aria-label=\"Search\" />\n </div>\n\n <!-- Mobile Nav Links -->\n <nav class=\"flex flex-col gap-1 overflow-y-auto max-h-[70vh]\" role=\"navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"mobile-active\" [routerLinkActiveOptions]=\"{exact: true}\"\n (click)=\"closeMobileMenu()\"\n class=\"flex items-center px-3 py-2.5 rounded-lg text-sm font-semibold text-[var(--secondary)] hover:text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] no-underline focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" (click)=\"closeMobileMenu()\"\n class=\"flex items-center px-3 py-2.5 rounded-lg text-sm font-semibold text-[var(--secondary)] hover:text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] no-underline focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n</div>\n}\n","/*\n * Public API Surface of ui\n */\n\nexport * from './lib/models';\nexport * from './lib/services';\nexport * from './lib/layout';\nexport * from './lib/navigation';\nexport * from './lib/user';\nexport * from './lib/notifications';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["UserIcon","SettingsIcon","CreditCardIcon","LogOutIcon","ChevronDownIcon"],"mappings":"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"}
1
+ {"version":3,"file":"excellore-ui.mjs","sources":["../../../projects/ui/src/lib/services/theme.service.ts","../../../projects/ui/src/lib/navigation/app-switcher/app-switcher.ts","../../../projects/ui/src/lib/navigation/app-switcher/app-switcher.html","../../../projects/ui/src/lib/navigation/organization-switcher/organization-switcher.ts","../../../projects/ui/src/lib/navigation/organization-switcher/organization-switcher.html","../../../projects/ui/src/lib/notifications/notification-panel/notification-panel.ts","../../../projects/ui/src/lib/notifications/notification-panel/notification-panel.html","../../../projects/ui/src/lib/notifications/notification-bell/notification-bell.ts","../../../projects/ui/src/lib/notifications/notification-bell/notification-bell.html","../../../projects/ui/src/lib/user/user-avatar/user-avatar.ts","../../../projects/ui/src/lib/user/user-avatar/user-avatar.html","../../../projects/ui/src/lib/user/user-menu/user-menu.ts","../../../projects/ui/src/lib/user/user-menu/user-menu.html","../../../projects/ui/src/lib/shared/icon/icon.component.ts","../../../projects/ui/src/lib/layout/header/header.ts","../../../projects/ui/src/lib/layout/header/header.html","../../../projects/ui/src/public-api.ts","../../../projects/ui/src/excellore-ui.ts"],"sourcesContent":["import { Injectable, signal, effect, Inject, PLATFORM_ID, inject } from '@angular/core';\r\nimport { isPlatformBrowser } from '@angular/common';\r\nimport { Theme } from '../models/models.interface';\r\n\r\nconst THEME_STORAGE_KEY = 'excellore-theme';\r\n\r\n@Injectable({\r\n providedIn: 'root',\r\n})\r\nexport class ThemeService {\r\n private readonly platformId = inject(PLATFORM_ID);\r\n private readonly isBrowser = isPlatformBrowser(this.platformId);\r\n\r\n // The active theme preference ('light', 'dark', 'system')\r\n readonly theme = signal<Theme>('system');\r\n\r\n // The currently resolved actual theme applied ('light' | 'dark')\r\n readonly resolvedTheme = signal<'light' | 'dark'>('light');\r\n\r\n private mediaQueryListener?: (e: MediaQueryListEvent) => void;\r\n\r\n constructor() {\r\n this.initializeTheme();\r\n\r\n // Effect to reactively update storage, apply document classes, and set up listeners\r\n effect(() => {\r\n const activeTheme = this.theme();\r\n this.saveThemeToStorage(activeTheme);\r\n this.applyTheme(activeTheme);\r\n });\r\n }\r\n\r\n setTheme(theme: Theme): void {\r\n this.theme.set(theme);\r\n }\r\n\r\n getTheme(): Theme {\r\n return this.theme();\r\n }\r\n\r\n toggleTheme(): void {\r\n const current = this.theme();\r\n if (current === 'light') {\r\n this.setTheme('dark');\r\n } else if (current === 'dark') {\r\n this.setTheme('system');\r\n } else {\r\n this.setTheme('light');\r\n }\r\n }\r\n\r\n private initializeTheme(): void {\r\n if (!this.isBrowser) {\r\n return;\r\n }\r\n\r\n try {\r\n const savedTheme = localStorage.getItem(THEME_STORAGE_KEY) as Theme;\r\n if (savedTheme === 'light' || savedTheme === 'dark' || savedTheme === 'system') {\r\n this.theme.set(savedTheme);\r\n } else {\r\n this.theme.set('system');\r\n }\r\n } catch {\r\n this.theme.set('system');\r\n }\r\n }\r\n\r\n private saveThemeToStorage(theme: Theme): void {\r\n if (!this.isBrowser) {\r\n return;\r\n }\r\n\r\n try {\r\n localStorage.setItem(THEME_STORAGE_KEY, theme);\r\n } catch (e) {\r\n console.warn('Failed to save theme to localStorage:', e);\r\n }\r\n }\r\n\r\n private applyTheme(theme: Theme): void {\r\n if (!this.isBrowser) {\r\n return;\r\n }\r\n\r\n const doc = document.documentElement;\r\n const mediaQuery = typeof window.matchMedia === 'function' ? window.matchMedia('(prefers-color-scheme: dark)') : null;\r\n\r\n // Clean up any previous listener to avoid leaks\r\n if (this.mediaQueryListener && mediaQuery) {\r\n mediaQuery.removeEventListener('change', this.mediaQueryListener);\r\n this.mediaQueryListener = undefined;\r\n }\r\n\r\n let actualTheme: 'light' | 'dark' = 'light';\r\n\r\n if (theme === 'system') {\r\n actualTheme = mediaQuery?.matches ? 'dark' : 'light';\r\n\r\n if (mediaQuery) {\r\n // Watch for system preference changes in real-time\r\n this.mediaQueryListener = (e: MediaQueryListEvent) => {\r\n const newResolvedTheme = e.matches ? 'dark' : 'light';\r\n this.resolvedTheme.set(newResolvedTheme);\r\n this.updateDocumentClass(newResolvedTheme);\r\n };\r\n mediaQuery.addEventListener('change', this.mediaQueryListener);\r\n }\r\n } else {\r\n actualTheme = theme;\r\n }\r\n\r\n this.resolvedTheme.set(actualTheme);\r\n this.updateDocumentClass(actualTheme);\r\n }\r\n\r\n private updateDocumentClass(resolved: 'light' | 'dark'): void {\r\n const doc = document.documentElement;\r\n if (resolved === 'dark') {\r\n doc.classList.add('dark');\r\n doc.classList.remove('light');\r\n } else {\r\n doc.classList.add('light');\r\n doc.classList.remove('dark');\r\n }\r\n }\r\n}\r\n","import { Component, input, output, HostListener, signal } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { Application } from '../../models/models.interface';\r\nimport { LucideAngularModule, Grid, ChevronDown } from 'lucide-angular';\r\n\r\n@Component({\r\n selector: 'lib-app-switcher',\r\n standalone: true,\r\n imports: [CommonModule, LucideAngularModule],\r\n templateUrl: './app-switcher.html',\r\n styleUrl: './app-switcher.css',\r\n})\r\nexport class AppSwitcherComponent {\r\n readonly applications = input<Application[]>([]);\r\n readonly applicationSelected = output<Application>();\r\n\r\n readonly gridIcon = Grid;\r\n readonly chevronDownIcon = ChevronDown;\r\n\r\n readonly isOpen = signal(false);\r\n\r\n toggleSwitcher(): void {\r\n this.isOpen.update((v) => !v);\r\n }\r\n\r\n closeSwitcher(): void {\r\n this.isOpen.set(false);\r\n }\r\n\r\n onSelect(app: Application): void {\r\n if (app.available !== false) {\r\n this.applicationSelected.emit(app);\r\n this.closeSwitcher();\r\n }\r\n }\r\n\r\n getInitials(name: string): string {\r\n return name\r\n .split(/[.\\s_-]+/)\r\n .filter(Boolean)\r\n .slice(0, 2)\r\n .map((part) => part[0]?.toUpperCase())\r\n .join('') || 'AP';\r\n }\r\n\r\n @HostListener('document:click', ['$event'])\r\n onOutsideClick(event: MouseEvent): void {\r\n const target = event.target as HTMLElement;\r\n if (!target.closest('.app-switcher-wrapper')) {\r\n this.closeSwitcher();\r\n }\r\n }\r\n}\r\n","<div class=\"app-switcher-wrapper relative inline-block text-left\">\r\n <button\n type=\"button\"\n class=\"app-switcher-trigger\"\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Toggle application switcher\"\r\n >\r\n <lucide-icon [name]=\"gridIcon\" class=\"h-4.5 w-4.5\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"app-switcher-dropdown fixed left-2 right-2 top-14 mt-2 max-h-[calc(100dvh-4.5rem)] origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-2.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-[360px] sm:max-w-[calc(100vw-1rem)] sm:origin-top-right\">\n <div class=\"mb-1.5 flex items-center justify-between border-b border-[var(--border)] px-1 pb-2.5\">\n <div>\n <h3 class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Excellore Apps</h3>\n <p class=\"mt-0.5 text-[11px] text-[var(--secondary)]\">Switch between available workspaces</p>\n </div>\n </div>\n \n @if (applications().length === 0) {\n <div class=\"py-6 text-center text-xs text-[var(--secondary)]\">\n No other apps available\n </div>\n } @else {\n <div class=\"max-h-[calc(100dvh-10rem)] overflow-y-auto py-1 sm:max-h-[420px]\">\n @for (app of applications(); track app.id) {\n @if (app.available !== false) {\n <a\n [href]=\"app.url\"\n (click)=\"onSelect(app)\"\n class=\"app-switcher-item app-switcher-link group flex items-center gap-3 rounded-lg border border-transparent px-2.5 py-2.5 no-underline transition focus:outline-none focus:ring-2 focus:ring-[var(--primary)]\"\n >\n <div class=\"app-switcher-app-mark\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\" [title]=\"app.name\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"app-switcher-open-pill shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide opacity-0 transition group-hover:opacity-100\">\n Open\n </span>\n </a>\n } @else {\n <div class=\"app-switcher-item flex items-center gap-3 rounded-lg px-2.5 py-2.5 opacity-55 select-none\">\n <div class=\"app-switcher-app-mark unavailable\">\n {{ app.initials || getInitials(app.name) }}\n </div>\n <div class=\"min-w-0 flex-1\">\n <div class=\"truncate text-sm font-semibold text-[var(--foreground)]\">{{ app.name }}</div>\n <div class=\"mt-0.5 truncate text-[11px] text-[var(--secondary)]\">{{ app.url }}</div>\n </div>\n <span class=\"shrink-0 rounded-full border border-[var(--border)] px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-[var(--secondary)]\">\n Soon\n </span>\n </div>\n }\n }\n </div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n","import { Component, input, output, HostListener, signal } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { Organization } from '../../models/models.interface';\r\nimport { LucideAngularModule, ChevronDown, Check, Building } from 'lucide-angular';\r\n\r\n@Component({\r\n selector: 'lib-organization-switcher',\r\n standalone: true,\r\n imports: [CommonModule, LucideAngularModule],\r\n templateUrl: './organization-switcher.html',\r\n styleUrl: './organization-switcher.css',\r\n})\r\nexport class OrganizationSwitcherComponent {\r\n readonly organizations = input<Organization[]>([]);\r\n readonly selectedOrganization = input<Organization | null>(null);\r\n readonly organizationChanged = output<Organization>();\r\n\r\n readonly chevronDownIcon = ChevronDown;\r\n readonly checkIcon = Check;\r\n readonly buildingIcon = Building;\r\n\r\n readonly isOpen = signal(false);\r\n\r\n toggleSwitcher(): void {\r\n this.isOpen.update((v) => !v);\r\n }\r\n\r\n closeSwitcher(): void {\r\n this.isOpen.set(false);\r\n }\r\n\r\n onSelect(org: Organization): void {\r\n this.organizationChanged.emit(org);\r\n this.closeSwitcher();\r\n }\r\n\r\n @HostListener('document:click', ['$event'])\r\n onOutsideClick(event: MouseEvent): void {\r\n const target = event.target as HTMLElement;\r\n if (!target.closest('.org-switcher-wrapper')) {\r\n this.closeSwitcher();\r\n }\r\n }\r\n}\r\n","<div class=\"org-switcher-wrapper relative inline-block text-left\">\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center gap-1.5 rounded-md border border-[var(--border)] bg-[var(--background)] px-2.5 py-1.5 text-xs font-medium text-[var(--foreground)] hover:bg-[rgba(var(--foreground),0.03)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)] cursor-pointer\"\r\n (click)=\"toggleSwitcher()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"Switch organization\"\r\n >\r\n <lucide-icon [name]=\"buildingIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)]\" />\r\n <span class=\"truncate max-w-[120px]\">{{ selectedOrganization()?.name || 'Select Tenant' }}</span>\r\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3 w-3 text-[var(--secondary)] transition-transform duration-200\" [class.rotate-180]=\"isOpen()\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"org-switcher-dropdown absolute left-0 mt-2 w-52 origin-top-left rounded-lg border border-[var(--border)] bg-[var(--background)] p-1 shadow-lg focus:outline-none z-50\">\r\n <div class=\"px-2 py-1.5 border-b border-[var(--border)] mb-1\">\r\n <span class=\"text-[9px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Switch Organization</span>\r\n </div>\r\n\r\n <div class=\"py-0.5 space-y-0.5 max-h-48 overflow-y-auto\">\r\n @for (org of organizations(); track org.id) {\r\n <button\r\n type=\"button\"\r\n class=\"flex w-full items-center justify-between rounded-md px-2 py-1.5 text-xs text-[var(--foreground)] transition hover:bg-[rgba(var(--foreground),0.05)] text-left cursor-pointer\"\r\n (click)=\"onSelect(org)\"\r\n >\r\n <span class=\"truncate pr-2\" [class.font-semibold]=\"org.id === selectedOrganization()?.id\">{{ org.name }}</span>\r\n @if (org.id === selectedOrganization()?.id) {\r\n <lucide-icon [name]=\"checkIcon\" class=\"h-3.5 w-3.5 text-[var(--primary)] flex-shrink-0\" />\r\n }\r\n </button>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n","import { Component, input, output } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { Notification } from '../../models/models.interface';\r\nimport { LucideAngularModule, CheckCheck, Trash2, BellOff, Circle } from 'lucide-angular';\r\n\r\n@Component({\r\n selector: 'lib-notification-panel',\r\n standalone: true,\r\n imports: [CommonModule, LucideAngularModule],\r\n templateUrl: './notification-panel.html',\r\n styleUrl: './notification-panel.css',\r\n})\r\nexport class NotificationPanelComponent {\r\n readonly notifications = input<Notification[]>([]);\r\n readonly notificationClick = output<Notification>();\r\n readonly markRead = output<string>();\r\n readonly clearAll = output<void>();\r\n\r\n readonly checkCheckIcon = CheckCheck;\r\n readonly trashIcon = Trash2;\r\n readonly bellOffIcon = BellOff;\r\n readonly circleIcon = Circle;\r\n\r\n onNotificationClick(notification: Notification): void {\r\n this.notificationClick.emit(notification);\r\n }\r\n\r\n onMarkRead(id: string, event: MouseEvent): void {\r\n event.stopPropagation();\r\n this.markRead.emit(id);\r\n }\r\n\r\n onClearAll(event: MouseEvent): void {\r\n event.stopPropagation();\r\n this.clearAll.emit();\r\n }\r\n\r\n formatTime(date: Date | string): string {\r\n const d = new Date(date);\r\n const now = new Date();\r\n const diffMs = now.getTime() - d.getTime();\r\n const diffMins = Math.floor(diffMs / 60000);\r\n const diffHours = Math.floor(diffMins / 60);\r\n const diffDays = Math.floor(diffHours / 24);\r\n\r\n if (diffMins < 1) return 'Just now';\r\n if (diffMins < 60) return `${diffMins}m ago`;\r\n if (diffHours < 24) return `${diffHours}h ago`;\r\n if (diffDays === 1) return 'Yesterday';\r\n return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\r\n }\r\n}\r\n","<div class=\"notification-panel-inner flex flex-col h-full max-h-[380px] w-full text-left\">\r\n <!-- Panel Header -->\r\n <div class=\"flex items-center justify-between border-b border-[var(--border)] px-3.5 py-2\">\r\n <span class=\"text-[10px] font-bold uppercase tracking-wider text-[var(--secondary)]\">Notifications</span>\r\n @if (notifications().length > 0) {\r\n <button\r\n type=\"button\"\r\n class=\"flex items-center gap-1 text-[9px] font-bold text-[var(--secondary)] hover:text-[var(--danger,#ef4444)] hover:underline cursor-pointer\"\r\n (click)=\"onClearAll($event)\"\r\n >\r\n <lucide-icon [name]=\"trashIcon\" class=\"h-3 w-3\" />\r\n <span>Clear All</span>\r\n </button>\r\n }\r\n </div>\r\n\r\n <!-- Panel Body -->\r\n <div class=\"flex-1 overflow-y-auto min-h-0 py-0.5 divide-y divide-[var(--border)]\">\r\n @if (notifications().length === 0) {\r\n <div class=\"flex flex-col items-center justify-center py-10 px-4 text-center select-none\">\r\n <div class=\"flex h-10 w-10 items-center justify-center rounded-full bg-[rgba(var(--secondary),0.06)] text-[var(--secondary)] mb-2.5\">\r\n <lucide-icon [name]=\"bellOffIcon\" class=\"h-4.5 w-4.5\" />\r\n </div>\r\n <p class=\"text-xs font-semibold text-[var(--foreground)]\">All caught up</p>\r\n <p class=\"text-[10px] text-[var(--secondary)] mt-0.5\">No new notifications to display.</p>\r\n </div>\r\n } @else {\r\n @for (item of notifications(); track item.id) {\r\n <div\r\n (click)=\"onNotificationClick(item)\"\r\n class=\"notification-item flex gap-2.5 p-3 transition hover:bg-[rgba(var(--foreground),0.02)] cursor-pointer relative\"\r\n [class.bg-[rgba(var(--primary),0.015)]]=\"!item.read\"\r\n >\r\n <!-- Read status dot -->\r\n <div class=\"flex-shrink-0 pt-1\">\r\n @if (!item.read) {\r\n <span class=\"block h-1.5 w-1.5 rounded-full bg-[var(--primary)] animate-pulse\"></span>\r\n } @else {\r\n <span class=\"block h-1.5 w-1.5 rounded-full bg-transparent\"></span>\r\n }\r\n </div>\r\n\r\n <!-- Notification content -->\r\n <div class=\"flex-1 min-w-0\">\r\n <h4 class=\"text-xs font-semibold text-[var(--foreground)] truncate\" [class.font-bold]=\"!item.read\">\r\n {{ item.title }}\r\n </h4>\r\n <p class=\"text-[10.5px] text-[var(--secondary)] mt-0.5 leading-snug break-words\">\r\n {{ item.message }}\r\n </p>\r\n <span class=\"block text-[9px] text-[var(--secondary)] mt-1 font-medium\">\r\n {{ formatTime(item.createdAt) }}\r\n </span>\r\n </div>\r\n\r\n <!-- Actions -->\r\n @if (!item.read) {\r\n <div class=\"flex items-center flex-shrink-0\">\r\n <button\r\n type=\"button\"\r\n class=\"mark-read-btn grid h-6 w-6 place-items-center rounded-full text-[var(--secondary)] hover:text-[var(--primary)] hover:bg-[rgba(var(--foreground),0.05)] cursor-pointer\"\r\n [title]=\"'Mark as read'\"\r\n (click)=\"onMarkRead(item.id, $event)\"\r\n >\r\n <lucide-icon [name]=\"checkCheckIcon\" class=\"h-3.5 w-3.5\" />\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n</div>\r\n","import { Component, input, output, HostListener, signal } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { Notification } from '../../models/models.interface';\r\nimport { NotificationPanelComponent } from '../notification-panel/notification-panel';\r\nimport { LucideAngularModule, Bell } from 'lucide-angular';\r\n\r\n@Component({\r\n selector: 'lib-notification-bell',\r\n standalone: true,\r\n imports: [CommonModule, NotificationPanelComponent, LucideAngularModule],\r\n templateUrl: './notification-bell.html',\r\n styleUrl: './notification-bell.css',\r\n})\r\nexport class NotificationBellComponent {\r\n readonly unreadCount = input<number>(0);\r\n readonly notifications = input<Notification[]>([]);\r\n\r\n readonly notificationClick = output<Notification>();\r\n readonly markRead = output<string>();\r\n readonly clearAll = output<void>();\r\n\r\n readonly bellIcon = Bell;\r\n\r\n readonly isOpen = signal(false);\r\n\r\n togglePanel(): void {\r\n this.isOpen.update((v) => !v);\r\n }\r\n\r\n closePanel(): void {\r\n this.isOpen.set(false);\r\n }\r\n\r\n onNotificationClick(notification: Notification): void {\r\n this.notificationClick.emit(notification);\r\n this.closePanel();\r\n }\r\n\r\n onMarkRead(id: string): void {\r\n this.markRead.emit(id);\r\n }\r\n\r\n onClearAll(): void {\r\n this.clearAll.emit();\r\n }\r\n\r\n @HostListener('document:click', ['$event'])\r\n onOutsideClick(event: MouseEvent): void {\r\n const target = event.target as HTMLElement;\r\n if (!target.closest('.notification-bell-wrapper')) {\r\n this.closePanel();\r\n }\r\n }\r\n}\r\n","<div class=\"notification-bell-wrapper relative inline-block text-left\">\r\n <button\n type=\"button\"\n class=\"notification-trigger\"\n (click)=\"togglePanel()\"\r\n aria-haspopup=\"true\"\r\n [aria-expanded]=\"isOpen()\"\r\n aria-label=\"View notifications\"\r\n >\r\n <lucide-icon [name]=\"bellIcon\" class=\"h-4.5 w-4.5\" />\r\n \r\n @if (unreadCount() > 0) {\r\n <span class=\"absolute top-1.5 right-1.5 flex h-2 w-2\">\r\n <span class=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-[var(--danger,#ef4444)] opacity-75\"></span>\r\n <span class=\"relative inline-flex rounded-full h-2 w-2 bg-[var(--danger,#ef4444)]\"></span>\r\n </span>\r\n }\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div class=\"notification-dropdown fixed left-2 right-2 top-14 mt-2 origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-0 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-80 sm:origin-top-right\">\n <lib-notification-panel\r\n [notifications]=\"notifications()\"\r\n (notificationClick)=\"onNotificationClick($event)\"\r\n (markRead)=\"onMarkRead($event)\"\r\n (clearAll)=\"onClearAll()\"\r\n />\r\n </div>\r\n }\r\n</div>\r\n","import { Component, input, computed } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { User } from '../../models/models.interface';\r\n\r\n@Component({\r\n selector: 'lib-user-avatar',\r\n standalone: true,\r\n imports: [CommonModule],\r\n templateUrl: './user-avatar.html',\r\n styleUrl: './user-avatar.css',\r\n})\r\nexport class UserAvatarComponent {\r\n // Input using the modern Angular signal inputs\r\n readonly user = input<User | null>(null);\r\n\r\n // Computes the initials from the user's name or email\r\n readonly initials = computed(() => {\r\n const userData = this.user();\r\n if (!userData) return 'EU';\r\n\r\n const source = userData.name || userData.email || 'Excellore User';\r\n return source\r\n .split(/[.@\\s_-]+/)\r\n .filter(Boolean)\r\n .slice(0, 2)\r\n .map(part => part[0]?.toUpperCase())\r\n .join('') || 'EU';\r\n });\r\n\r\n // Check if image loading failed to dynamically switch to initials\r\n protected imageError = false;\r\n\r\n onImageError(): void {\r\n this.imageError = true;\r\n }\r\n}\r\n","<div class=\"avatar-container flex h-full w-full items-center justify-center rounded-full border border-[var(--border)] select-none overflow-hidden transition-all duration-200\">\r\n @if (user()?.avatar && !imageError) {\r\n <img [src]=\"user()?.avatar\" [alt]=\"user()?.name || 'User avatar'\" class=\"avatar-img h-full w-full object-cover\" (error)=\"onImageError()\" />\r\n } @else {\r\n <span class=\"avatar-initials text-xs font-semibold uppercase leading-none\">{{ initials() }}</span>\r\n }\r\n</div>\r\n","import { Component, input, output, HostListener, signal } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { User } from '../../models/models.interface';\r\nimport { UserAvatarComponent } from '../user-avatar/user-avatar';\r\nimport {\r\n LucideAngularModule,\r\n User as UserIcon,\r\n Settings as SettingsIcon,\r\n CreditCard as CreditCardIcon,\r\n LogOut as LogOutIcon,\r\n ChevronDown as ChevronDownIcon,\r\n} from 'lucide-angular';\r\n\r\n@Component({\r\n selector: 'lib-user-menu',\r\n standalone: true,\r\n imports: [CommonModule, UserAvatarComponent, LucideAngularModule],\r\n templateUrl: './user-menu.html',\r\n styleUrl: './user-menu.css',\r\n})\r\nexport class UserMenuComponent {\r\n readonly user = input<User | null>(null);\r\n readonly menuAction = output<'profile' | 'settings' | 'billing' | 'logout'>();\r\n\r\n // Lucide Icons mapping\r\n readonly userIcon = UserIcon;\r\n readonly settingsIcon = SettingsIcon;\r\n readonly creditCardIcon = CreditCardIcon;\r\n readonly logOutIcon = LogOutIcon;\r\n readonly chevronDownIcon = ChevronDownIcon;\r\n\r\n // Signalled open state\r\n readonly isOpen = signal(false);\r\n\r\n toggleMenu(): void {\r\n this.isOpen.update((v) => !v);\r\n }\r\n\r\n closeMenu(): void {\r\n this.isOpen.set(false);\r\n }\r\n\r\n onAction(action: 'profile' | 'settings' | 'billing' | 'logout'): void {\r\n this.menuAction.emit(action);\r\n this.closeMenu();\r\n }\r\n\r\n @HostListener('document:click', ['$event'])\r\n onOutsideClick(event: MouseEvent): void {\r\n const target = event.target as HTMLElement;\r\n if (!target.closest('.user-menu-wrapper')) {\r\n this.closeMenu();\r\n }\r\n }\r\n}\r\n","<div class=\"user-menu-wrapper relative inline-block text-left\">\r\n <button type=\"button\" class=\"user-menu-trigger\" (click)=\"toggleMenu()\" aria-haspopup=\"true\" [aria-expanded]=\"isOpen()\"\r\n aria-label=\"User profile menu\">\r\n <div class=\"h-7 w-7 rounded-full overflow-hidden\">\r\n <lib-user-avatar [user]=\"user()\" />\r\n </div>\r\n <lucide-icon [name]=\"chevronDownIcon\" class=\"h-3.5 w-3.5 text-[var(--secondary)] transition-transform duration-200\"\r\n [class.rotate-180]=\"isOpen()\" />\r\n </button>\r\n\r\n @if (isOpen()) {\r\n <div\r\n class=\"user-dropdown-panel fixed left-2 right-2 top-14 mt-2 origin-top rounded-xl border border-[var(--border)] bg-[var(--background)] p-1.5 shadow-lg focus:outline-none z-50 sm:absolute sm:left-auto sm:right-0 sm:top-auto sm:w-56 sm:origin-top-right\">\r\n @if (user(); as u) {\r\n <div class=\"px-3 py-2 border-b border-[var(--border)] mb-1\">\r\n <p class=\"text-sm font-semibold truncate text-[var(--foreground)]\" [title]=\"u.name\">{{ u.name }}</p>\r\n <p class=\"text-xs truncate text-[var(--secondary)] mt-0.5\" [title]=\"u.email\">{{ u.email }}</p>\r\n </div>\r\n }\r\n\r\n <div class=\"py-0.5 space-y-0.5\">\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('profile')\">\r\n <lucide-icon [name]=\"userIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\r\n <span>My Profile</span>\r\n </button>\r\n\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('settings')\">\r\n <lucide-icon [name]=\"settingsIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\r\n <span>Settings</span>\r\n </button>\r\n\r\n <button type=\"button\" class=\"user-menu-item\" (click)=\"onAction('billing')\">\r\n <lucide-icon [name]=\"creditCardIcon\" class=\"h-4 w-4 text-[var(--secondary)]\" />\r\n <span>Billing</span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"border-t border-[var(--border)] mt-1 pt-1\">\r\n <button type=\"button\" class=\"user-menu-item danger\" (click)=\"onAction('logout')\">\r\n <lucide-icon [name]=\"logOutIcon\" class=\"h-4 w-4 text-[var(--danger,#ef4444)]\" />\r\n <span>Sign Out</span>\r\n </button>\r\n </div>\r\n </div>\r\n }\r\n</div>","// lib/shared/icon/icon.component.ts\r\nimport { Component, input, computed } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport {\r\n LucideAngularModule,\r\n Search,\r\n Menu,\r\n X,\r\n Sun,\r\n Moon,\r\n Monitor,\r\n Bell,\r\n Grid,\r\n ChevronDown,\r\n Building2\r\n} from 'lucide-angular';\r\n\r\nconst ICONS = {\r\n search: Search,\r\n menu: Menu,\r\n x: X,\r\n sun: Sun,\r\n moon: Moon,\r\n monitor: Monitor,\r\n bell: Bell,\r\n grid: Grid,\r\n chevronDown: ChevronDown,\r\n building2: Building2\r\n};\r\n\r\n@Component({\r\n selector: 'lib-icon',\r\n standalone: true,\r\n imports: [CommonModule, LucideAngularModule],\r\n template: `\r\n <lucide-icon \r\n [name]=\"iconName()\" \r\n [size]=\"size()\"\r\n [color]=\"color()\"\r\n [strokeWidth]=\"strokeWidth()\"\r\n class=\"lib-icon\"\r\n />\r\n `,\r\n styles: [`\r\n :host {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n }\r\n .lib-icon {\r\n display: block;\r\n }\r\n `]\r\n})\r\nexport class IconComponent {\r\n readonly name = input.required<string>();\r\n readonly size = input<number>(24);\r\n readonly color = input<string | undefined>(undefined);\r\n readonly strokeWidth = input<number | undefined>(undefined);\r\n\r\n protected readonly iconName = computed(() => {\r\n const name = this.name();\r\n return ICONS[name as keyof typeof ICONS] || Search;\r\n });\r\n}","import { Component, inject, input, output, signal, computed } from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { Router, RouterLink, RouterLinkActive } from '@angular/router';\r\nimport { User, Organization, NavigationItem, Notification, Application } from '../../models/models.interface';\r\nimport { ThemeService } from '../../services/theme.service';\r\nimport { AppSwitcherComponent } from '../../navigation';\r\nimport { NotificationBellComponent } from '../../notifications';\r\nimport { UserMenuComponent } from '../../user';\r\n\r\nimport { IconComponent } from '../../shared/icon/icon.component';\r\n\r\n\r\n@Component({\r\n selector: 'lib-header',\r\n standalone: true,\r\n imports: [\r\n CommonModule,\r\n RouterLink,\r\n RouterLinkActive,\r\n AppSwitcherComponent,\r\n NotificationBellComponent,\r\n UserMenuComponent,\r\n IconComponent,\r\n ],\r\n templateUrl: './header.html',\r\n styleUrl: './header.css',\r\n})\r\nexport class HeaderComponent {\r\n private readonly router = inject(Router);\r\n protected readonly themeService = inject(ThemeService);\r\n\r\n // User & Organization Inputs\r\n readonly user = input<User | null>(null);\r\n readonly permissions = input<string[]>([]);\r\n readonly organization = input<Organization | null>(null);\r\n\r\n // Navigation & Integration Inputs\r\n readonly navigationItems = input<NavigationItem[]>([]);\r\n readonly applications = input<Application[]>([]);\r\n readonly notifications = input<Notification[]>([]);\r\n readonly notificationCount = input<number>(0);\r\n readonly logoUrl = input<string>('https://cdn.jsdelivr.net/gh/Excellore/excellore-static-assets@main/images/excellore-logo-origin.png');\n readonly appName = input<string>('Excellore');\n readonly currentAppName = input<string>('');\n readonly showSearch = input<boolean>(true);\n readonly showNotifications = input<boolean>(true);\n readonly showAppSwitcher = input<boolean>(true);\n readonly showThemeSwitcher = input<boolean>(true);\n readonly showSignIn = input<boolean>(true);\n\n // Outputs as decided in the architecture plan\n readonly search = output<string>();\n readonly signIn = output<void>();\n readonly applicationSelected = output<Application>();\n readonly notificationClick = output<Notification>();\r\n readonly markRead = output<string>();\r\n readonly clearAll = output<void>();\r\n readonly menuAction = output<'profile' | 'settings' | 'billing' | 'logout'>();\r\n\r\n // Lucide Icons\r\n // readonly searchIcon = Search;\r\n // readonly menuIcon = Menu;\r\n // readonly xIcon = X;\r\n // readonly sunIcon = Sun;\r\n // readonly moonIcon = Moon;\r\n // readonly monitorIcon = Monitor;\r\n\r\n // Header specific UI states\r\n readonly isMobileMenuOpen = signal(false);\r\n\r\n // Checks if the user has permission to see an item\r\n hasPermission(item: NavigationItem): boolean {\r\n if (!item.permissions || item.permissions.length === 0) {\r\n return true;\r\n }\r\n const currentPermissions = this.permissions();\r\n return item.permissions.some((p) => currentPermissions.includes(p));\r\n }\r\n\r\n // Reactive unread count selector\r\n readonly unreadCount = computed(() => {\n const overrideCount = this.notificationCount();\n if (overrideCount > 0) return overrideCount;\n return this.notifications().filter((n) => !n.read).length;\n });\n\n readonly hasMobileMenuContent = computed(() =>\n this.showSearch() || this.navigationItems().some((item) => this.hasPermission(item))\n );\n\r\n // Determines if an item's internal route is active\r\n isActive(item: NavigationItem): boolean {\r\n if (!item.route) return false;\r\n try {\r\n return this.router.isActive(item.route, {\r\n paths: 'subset',\r\n queryParams: 'ignored',\r\n fragment: 'ignored',\r\n matrixParams: 'ignored',\r\n });\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n toggleMobileMenu(): void {\r\n this.isMobileMenuOpen.update((v) => !v);\r\n }\r\n\r\n closeMobileMenu(): void {\r\n this.isMobileMenuOpen.set(false);\r\n }\r\n\r\n onSearch(query: string): void {\r\n this.search.emit(query);\r\n }\r\n\r\n onApplicationSelect(app: Application): void {\r\n this.applicationSelected.emit(app);\r\n }\r\n\r\n onNotificationClick(notification: Notification): void {\r\n this.notificationClick.emit(notification);\r\n }\r\n\r\n onMarkRead(id: string): void {\r\n this.markRead.emit(id);\r\n }\r\n\r\n onClearAll(): void {\r\n this.clearAll.emit();\r\n }\r\n\r\n onMenuAction(action: 'profile' | 'settings' | 'billing' | 'logout'): void {\r\n this.menuAction.emit(action);\r\n }\r\n}\r\n","<header\n class=\"header-container fixed top-0 inset-x-0 z-40 flex h-[3.75rem] items-center justify-between px-3 text-[var(--foreground)] sm:px-4 lg:px-6\">\n\n <!-- Left Side: Brand, Current App & Desktop Links -->\n <div class=\"flex min-w-0 items-center gap-3\">\n\n <a routerLink=\"/\" class=\"brand-identity\">\n <img [src]=\"logoUrl()\" [alt]=\"appName() + ' logo'\" width=\"26\" height=\"26\" class=\"brand-logo\" />\n <span class=\"brand-name\">{{ appName() }}</span>\n </a>\n\n @if (currentAppName()) {\n <div class=\"current-app-identity\" [attr.aria-label]=\"'Current app: ' + currentAppName()\">\n <span class=\"brand-separator\" aria-hidden=\"true\">|</span>\n <span class=\"current-app-name\">{{ currentAppName() }}</span>\n </div>\n }\n\n <!-- Desktop Navigation Links -->\n <nav class=\"desktop-nav ml-1 hidden items-center lg:flex\" aria-label=\"Desktop primary navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"active\" [routerLinkActiveOptions]=\"{exact: true}\" class=\"nav-link\"\n [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" class=\"nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n </div>\n\n <!-- Middle: Search Input (Desktop) -->\n @if (showSearch()) {\n <div class=\"hidden flex-1 justify-center px-4 md:flex\">\n <div class=\"header-search-shell\">\n <lib-icon name=\"search\" [size]=\"14\"\n class=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-[var(--secondary)]\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #searchInput\n (keyup.enter)=\"onSearch(searchInput.value); searchInput.value = ''\" class=\"header-search\" autocomplete=\"off\"\n aria-label=\"Search\" />\n </div>\n </div>\n } @else {\n <div class=\"hidden flex-1 md:block\"></div>\n }\n\n <!-- Right Side Actions -->\n <div class=\"header-actions\">\n\n <!-- Mobile Search Trigger -->\n @if (showSearch()) {\n <button type=\"button\" class=\"header-action mobile-search-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Open mobile menu\" aria-expanded=\"isMobileMenuOpen()\" #menuToggle>\n <lib-icon name=\"search\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n\n <!-- Theme Switcher -->\n @if (showThemeSwitcher()) {\n <button type=\"button\" class=\"header-action\" (click)=\"themeService.toggleTheme()\" aria-label=\"Toggle theme\">\n @switch (themeService.theme()) {\n @case ('light') {\n <lib-icon name=\"sun\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @case ('dark') {\n <lib-icon name=\"moon\" [size]=\"16\" class=\"text-[var(--primary)]\" />\n }\n @default {\n <lib-icon name=\"monitor\" [size]=\"16\" class=\"text-[var(--secondary)]\" />\n }\n }\n </button>\n }\n\n <!-- App Switcher -->\n @if (showAppSwitcher() && applications().length > 0) {\n <lib-app-switcher [applications]=\"applications()\" (applicationSelected)=\"onApplicationSelect($event)\" />\n }\n\n <!-- Notifications -->\n @if (showNotifications()) {\n <lib-notification-bell [unreadCount]=\"unreadCount()\" [notifications]=\"notifications()\"\n (notificationClick)=\"onNotificationClick($event)\" (markRead)=\"onMarkRead($event)\" (clearAll)=\"onClearAll()\" />\n }\n\n <!-- User Menu -->\n @if (user()) {\n <div class=\"user-menu-divider\">\n <lib-user-menu [user]=\"user()\" (menuAction)=\"onMenuAction($event)\" />\n </div>\n } @else if (showSignIn()) {\n <button type=\"button\" class=\"sign-in-button\" (click)=\"signIn.emit()\">\n Sign in\n </button>\n }\n\n <!-- Mobile Menu Toggle (Hamburger) -->\n @if (hasMobileMenuContent()) {\n <button type=\"button\" class=\"header-action mobile-menu-action\" (click)=\"toggleMobileMenu()\"\n aria-label=\"Toggle navigation menu\" aria-expanded=\"isMobileMenuOpen()\" #mobileMenuToggle>\n <lib-icon [name]=\"isMobileMenuOpen() ? 'x' : 'menu'\" [size]=\"16\" class=\"h-4.5 w-4.5\" aria-hidden=\"true\" />\n </button>\n }\n </div>\n</header>\n\n<!-- Mobile Menu Backdrop -->\n@if (isMobileMenuOpen() && hasMobileMenuContent()) {\n<div class=\"mobile-backdrop fixed inset-0 z-30 lg:hidden\" (click)=\"closeMobileMenu()\"\n (keydown.escape)=\"closeMobileMenu()\" tabindex=\"-1\" aria-hidden=\"true\"></div>\n\n<!-- Mobile Navigation Drawer -->\n<div\n class=\"mobile-drawer fixed inset-x-2 top-16 z-40 flex max-h-[calc(100dvh-4.5rem)] flex-col rounded-2xl border border-[var(--border)] bg-[var(--background)] p-3 lg:hidden\"\n role=\"dialog\" aria-modal=\"true\" aria-label=\"Mobile navigation\" (keydown.escape)=\"closeMobileMenu()\">\n <!-- Mobile Search -->\n @if (showSearch()) {\n <div class=\"relative mb-3 w-full\">\n <lib-icon name=\"search\" [size]=\"16\"\n class=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[var(--secondary)] pointer-events-none\"\n aria-hidden=\"true\" />\n\n <input type=\"search\" placeholder=\"Search Excellore...\" #mobileSearchInput\n (keyup.enter)=\"onSearch(mobileSearchInput.value); mobileSearchInput.value = ''; closeMobileMenu()\"\n class=\"header-search mobile\" autocomplete=\"off\" aria-label=\"Search\" />\n </div>\n }\n\n <!-- Mobile Nav Links -->\n <nav class=\"flex flex-col gap-1 overflow-y-auto\" role=\"navigation\">\n @for (item of navigationItems(); track item.id) {\n @if (hasPermission(item)) {\n @if (item.route) {\n <a [routerLink]=\"item.route\" routerLinkActive=\"mobile-active\" [routerLinkActiveOptions]=\"{exact: true}\"\n (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\" [attr.aria-current]=\"isActive(item) ? 'page' : null\">\n {{ item.label }}\n </a>\n } @else {\n <a [href]=\"item.href\" [target]=\"item.target || '_self'\" (click)=\"closeMobileMenu()\" class=\"mobile-nav-link\">\n {{ item.label }}\n </a>\n }\n }\n }\n </nav>\n</div>\n}\n","/*\r\n * Public API Surface of ui\r\n */\r\n\r\nexport * from './lib/models';\r\nexport * from './lib/services';\r\nexport * from './lib/layout';\r\nexport * from './lib/navigation';\r\nexport * from './lib/user';\r\nexport * from './lib/notifications';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["UserIcon","SettingsIcon","CreditCardIcon","LogOutIcon","ChevronDownIcon"],"mappings":";;;;;;;AAIA,MAAM,iBAAiB,GAAG,iBAAiB;MAK9B,YAAY,CAAA;AACN,IAAA,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC;AAChC,IAAA,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC;;AAGtD,IAAA,KAAK,GAAG,MAAM,CAAQ,QAAQ,4EAAC;;AAG/B,IAAA,aAAa,GAAG,MAAM,CAAmB,OAAO,oFAAC;AAElD,IAAA,kBAAkB;AAE1B,IAAA,WAAA,GAAA;QACE,IAAI,CAAC,eAAe,EAAE;;QAGtB,MAAM,CAAC,MAAK;AACV,YAAA,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,EAAE;AAChC,YAAA,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC;AACpC,YAAA,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;AAC9B,QAAA,CAAC,CAAC;IACJ;AAEA,IAAA,QAAQ,CAAC,KAAY,EAAA;AACnB,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;IACvB;IAEA,QAAQ,GAAA;AACN,QAAA,OAAO,IAAI,CAAC,KAAK,EAAE;IACrB;IAEA,WAAW,GAAA;AACT,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE;AAC5B,QAAA,IAAI,OAAO,KAAK,OAAO,EAAE;AACvB,YAAA,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QACvB;AAAO,aAAA,IAAI,OAAO,KAAK,MAAM,EAAE;AAC7B,YAAA,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACzB;aAAO;AACL,YAAA,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;QACxB;IACF;IAEQ,eAAe,GAAA;AACrB,QAAA,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YACnB;QACF;AAEA,QAAA,IAAI;YACF,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,iBAAiB,CAAU;AACnE,YAAA,IAAI,UAAU,KAAK,OAAO,IAAI,UAAU,KAAK,MAAM,IAAI,UAAU,KAAK,QAAQ,EAAE;AAC9E,gBAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC;YAC5B;iBAAO;AACL,gBAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC1B;QACF;AAAE,QAAA,MAAM;AACN,YAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1B;IACF;AAEQ,IAAA,kBAAkB,CAAC,KAAY,EAAA;AACrC,QAAA,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YACnB;QACF;AAEA,QAAA,IAAI;AACF,YAAA,YAAY,CAAC,OAAO,CAAC,iBAAiB,EAAE,KAAK,CAAC;QAChD;QAAE,OAAO,CAAC,EAAE;AACV,YAAA,OAAO,CAAC,IAAI,CAAC,uCAAuC,EAAE,CAAC,CAAC;QAC1D;IACF;AAEQ,IAAA,UAAU,CAAC,KAAY,EAAA;AAC7B,QAAA,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YACnB;QACF;AAEA,QAAA,MAAM,GAAG,GAAG,QAAQ,CAAC,eAAe;QACpC,MAAM,UAAU,GAAG,OAAO,MAAM,CAAC,UAAU,KAAK,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,8BAA8B,CAAC,GAAG,IAAI;;AAGrH,QAAA,IAAI,IAAI,CAAC,kBAAkB,IAAI,UAAU,EAAE;YACzC,UAAU,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,kBAAkB,CAAC;AACjE,YAAA,IAAI,CAAC,kBAAkB,GAAG,SAAS;QACrC;QAEA,IAAI,WAAW,GAAqB,OAAO;AAE3C,QAAA,IAAI,KAAK,KAAK,QAAQ,EAAE;AACtB,YAAA,WAAW,GAAG,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO;YAEpD,IAAI,UAAU,EAAE;;AAEd,gBAAA,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAsB,KAAI;AACnD,oBAAA,MAAM,gBAAgB,GAAG,CAAC,CAAC,OAAO,GAAG,MAAM,GAAG,OAAO;AACrD,oBAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,gBAAgB,CAAC;AACxC,oBAAA,IAAI,CAAC,mBAAmB,CAAC,gBAAgB,CAAC;AAC5C,gBAAA,CAAC;gBACD,UAAU,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,kBAAkB,CAAC;YAChE;QACF;aAAO;YACL,WAAW,GAAG,KAAK;QACrB;AAEA,QAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC;AACnC,QAAA,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC;IACvC;AAEQ,IAAA,mBAAmB,CAAC,QAA0B,EAAA;AACpD,QAAA,MAAM,GAAG,GAAG,QAAQ,CAAC,eAAe;AACpC,QAAA,IAAI,QAAQ,KAAK,MAAM,EAAE;AACvB,YAAA,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC;AACzB,YAAA,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC;QAC/B;aAAO;AACL,YAAA,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC;AAC1B,YAAA,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC;QAC9B;IACF;wGApHW,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAZ,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,YAAY,cAFX,MAAM,EAAA,CAAA;;4FAEP,YAAY,EAAA,UAAA,EAAA,CAAA;kBAHxB,UAAU;AAAC,YAAA,IAAA,EAAA,CAAA;AACV,oBAAA,UAAU,EAAE,MAAM;AACnB,iBAAA;;;MCIY,oBAAoB,CAAA;AACtB,IAAA,YAAY,GAAG,KAAK,CAAgB,EAAE,mFAAC;IACvC,mBAAmB,GAAG,MAAM,EAAe;IAE3C,QAAQ,GAAG,IAAI;IACf,eAAe,GAAG,WAAW;AAE7B,IAAA,MAAM,GAAG,MAAM,CAAC,KAAK,6EAAC;IAE/B,cAAc,GAAA;AACZ,QAAA,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/B;IAEA,aAAa,GAAA;AACX,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;IACxB;AAEA,IAAA,QAAQ,CAAC,GAAgB,EAAA;AACvB,QAAA,IAAI,GAAG,CAAC,SAAS,KAAK,KAAK,EAAE;AAC3B,YAAA,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC;YAClC,IAAI,CAAC,aAAa,EAAE;QACtB;IACF;AAEA,IAAA,WAAW,CAAC,IAAY,EAAA;AACtB,QAAA,OAAO;aACJ,KAAK,CAAC,UAAU;aAChB,MAAM,CAAC,OAAO;AACd,aAAA,KAAK,CAAC,CAAC,EAAE,CAAC;AACV,aAAA,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE;AACpC,aAAA,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI;IACrB;AAGA,IAAA,cAAc,CAAC,KAAiB,EAAA;AAC9B,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAAqB;QAC1C,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,uBAAuB,CAAC,EAAE;YAC5C,IAAI,CAAC,aAAa,EAAE;QACtB;IACF;wGAvCW,oBAAoB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAApB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,oBAAoB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,EAAA,YAAA,EAAA,EAAA,iBAAA,EAAA,cAAA,EAAA,UAAA,EAAA,cAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,mBAAA,EAAA,qBAAA,EAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,gBAAA,EAAA,wBAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECZjC,s4GAiEA,EAAA,MAAA,EAAA,CAAA,mrDAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDzDY,YAAY,8BAAE,mBAAmB,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,sBAAA,EAAA,QAAA,EAAA,oDAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,KAAA,EAAA,OAAA,EAAA,qBAAA,EAAA,MAAA,EAAA,aAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;4FAIhC,oBAAoB,EAAA,UAAA,EAAA,CAAA;kBAPhC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,kBAAkB,cAChB,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,EAAE,mBAAmB,CAAC,EAAA,QAAA,EAAA,s4GAAA,EAAA,MAAA,EAAA,CAAA,mrDAAA,CAAA,EAAA;;sBAqC3C,YAAY;uBAAC,gBAAgB,EAAE,CAAC,QAAQ,CAAC;;;MEjC/B,6BAA6B,CAAA;AAC/B,IAAA,aAAa,GAAG,KAAK,CAAiB,EAAE,oFAAC;AACzC,IAAA,oBAAoB,GAAG,KAAK,CAAsB,IAAI,2FAAC;IACvD,mBAAmB,GAAG,MAAM,EAAgB;IAE5C,eAAe,GAAG,WAAW;IAC7B,SAAS,GAAG,KAAK;IACjB,YAAY,GAAG,QAAQ;AAEvB,IAAA,MAAM,GAAG,MAAM,CAAC,KAAK,6EAAC;IAE/B,cAAc,GAAA;AACZ,QAAA,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/B;IAEA,aAAa,GAAA;AACX,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;IACxB;AAEA,IAAA,QAAQ,CAAC,GAAiB,EAAA;AACxB,QAAA,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC;QAClC,IAAI,CAAC,aAAa,EAAE;IACtB;AAGA,IAAA,cAAc,CAAC,KAAiB,EAAA;AAC9B,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAAqB;QAC1C,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,uBAAuB,CAAC,EAAE;YAC5C,IAAI,CAAC,aAAa,EAAE;QACtB;IACF;wGA9BW,6BAA6B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA7B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,6BAA6B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,2BAAA,EAAA,MAAA,EAAA,EAAA,aAAA,EAAA,EAAA,iBAAA,EAAA,eAAA,EAAA,UAAA,EAAA,eAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,oBAAA,EAAA,EAAA,iBAAA,EAAA,sBAAA,EAAA,UAAA,EAAA,sBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,mBAAA,EAAA,qBAAA,EAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,gBAAA,EAAA,wBAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECZ1C,woEAqCA,EAAA,MAAA,EAAA,CAAA,4RAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,ED7BY,YAAY,8BAAE,mBAAmB,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,sBAAA,EAAA,QAAA,EAAA,oDAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,KAAA,EAAA,OAAA,EAAA,qBAAA,EAAA,MAAA,EAAA,aAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;4FAIhC,6BAA6B,EAAA,UAAA,EAAA,CAAA;kBAPzC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,2BAA2B,cACzB,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,EAAE,mBAAmB,CAAC,EAAA,QAAA,EAAA,woEAAA,EAAA,MAAA,EAAA,CAAA,4RAAA,CAAA,EAAA;;sBA4B3C,YAAY;uBAAC,gBAAgB,EAAE,CAAC,QAAQ,CAAC;;;MExB/B,0BAA0B,CAAA;AAC5B,IAAA,aAAa,GAAG,KAAK,CAAiB,EAAE,oFAAC;IACzC,iBAAiB,GAAG,MAAM,EAAgB;IAC1C,QAAQ,GAAG,MAAM,EAAU;IAC3B,QAAQ,GAAG,MAAM,EAAQ;IAEzB,cAAc,GAAG,UAAU;IAC3B,SAAS,GAAG,MAAM;IAClB,WAAW,GAAG,OAAO;IACrB,UAAU,GAAG,MAAM;AAE5B,IAAA,mBAAmB,CAAC,YAA0B,EAAA;AAC5C,QAAA,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC;IAC3C;IAEA,UAAU,CAAC,EAAU,EAAE,KAAiB,EAAA;QACtC,KAAK,CAAC,eAAe,EAAE;AACvB,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IACxB;AAEA,IAAA,UAAU,CAAC,KAAiB,EAAA;QAC1B,KAAK,CAAC,eAAe,EAAE;AACvB,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;IACtB;AAEA,IAAA,UAAU,CAAC,IAAmB,EAAA;AAC5B,QAAA,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC;AACxB,QAAA,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE;QACtB,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE;QAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;QAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC;QAE3C,IAAI,QAAQ,GAAG,CAAC;AAAE,YAAA,OAAO,UAAU;QACnC,IAAI,QAAQ,GAAG,EAAE;YAAE,OAAO,CAAA,EAAG,QAAQ,CAAA,KAAA,CAAO;QAC5C,IAAI,SAAS,GAAG,EAAE;YAAE,OAAO,CAAA,EAAG,SAAS,CAAA,KAAA,CAAO;QAC9C,IAAI,QAAQ,KAAK,CAAC;AAAE,YAAA,OAAO,WAAW;AACtC,QAAA,OAAO,CAAC,CAAC,kBAAkB,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;IAC5E;wGAtCW,0BAA0B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAA1B,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,0BAA0B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,wBAAA,EAAA,MAAA,EAAA,EAAA,aAAA,EAAA,EAAA,iBAAA,EAAA,eAAA,EAAA,UAAA,EAAA,eAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,mBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,UAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECZvC,g9GAyEA,EAAA,MAAA,EAAA,CAAA,0QAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDjEY,YAAY,8BAAE,mBAAmB,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,sBAAA,EAAA,QAAA,EAAA,oDAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,KAAA,EAAA,OAAA,EAAA,qBAAA,EAAA,MAAA,EAAA,aAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;4FAIhC,0BAA0B,EAAA,UAAA,EAAA,CAAA;kBAPtC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,wBAAwB,cACtB,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,EAAE,mBAAmB,CAAC,EAAA,QAAA,EAAA,g9GAAA,EAAA,MAAA,EAAA,CAAA,0QAAA,CAAA,EAAA;;;MEKjC,yBAAyB,CAAA;AAC3B,IAAA,WAAW,GAAG,KAAK,CAAS,CAAC,kFAAC;AAC9B,IAAA,aAAa,GAAG,KAAK,CAAiB,EAAE,oFAAC;IAEzC,iBAAiB,GAAG,MAAM,EAAgB;IAC1C,QAAQ,GAAG,MAAM,EAAU;IAC3B,QAAQ,GAAG,MAAM,EAAQ;IAEzB,QAAQ,GAAG,IAAI;AAEf,IAAA,MAAM,GAAG,MAAM,CAAC,KAAK,6EAAC;IAE/B,WAAW,GAAA;AACT,QAAA,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/B;IAEA,UAAU,GAAA;AACR,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;IACxB;AAEA,IAAA,mBAAmB,CAAC,YAA0B,EAAA;AAC5C,QAAA,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC;QACzC,IAAI,CAAC,UAAU,EAAE;IACnB;AAEA,IAAA,UAAU,CAAC,EAAU,EAAA;AACnB,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IACxB;IAEA,UAAU,GAAA;AACR,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;IACtB;AAGA,IAAA,cAAc,CAAC,KAAiB,EAAA;AAC9B,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAAqB;QAC1C,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,4BAA4B,CAAC,EAAE;YACjD,IAAI,CAAC,UAAU,EAAE;QACnB;IACF;wGAvCW,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAzB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,yBAAyB,6hBCbtC,0xCA8BA,EAAA,MAAA,EAAA,CAAA,4uBAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDrBY,YAAY,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,0BAA0B,qJAAE,mBAAmB,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,sBAAA,EAAA,QAAA,EAAA,oDAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,KAAA,EAAA,OAAA,EAAA,qBAAA,EAAA,MAAA,EAAA,aAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;4FAI5D,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBAPrC,SAAS;+BACE,uBAAuB,EAAA,UAAA,EACrB,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,EAAE,0BAA0B,EAAE,mBAAmB,CAAC,EAAA,QAAA,EAAA,0xCAAA,EAAA,MAAA,EAAA,CAAA,4uBAAA,CAAA,EAAA;;sBAqCvE,YAAY;uBAAC,gBAAgB,EAAE,CAAC,QAAQ,CAAC;;;MEnC/B,mBAAmB,CAAA;;AAErB,IAAA,IAAI,GAAG,KAAK,CAAc,IAAI,2EAAC;;AAG/B,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAK;AAChC,QAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE;AAC5B,QAAA,IAAI,CAAC,QAAQ;AAAE,YAAA,OAAO,IAAI;QAE1B,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,KAAK,IAAI,gBAAgB;AAClE,QAAA,OAAO;aACJ,KAAK,CAAC,WAAW;aACjB,MAAM,CAAC,OAAO;AACd,aAAA,KAAK,CAAC,CAAC,EAAE,CAAC;AACV,aAAA,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE;AAClC,aAAA,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI;AACrB,IAAA,CAAC,+EAAC;;IAGQ,UAAU,GAAG,KAAK;IAE5B,YAAY,GAAA;AACV,QAAA,IAAI,CAAC,UAAU,GAAG,IAAI;IACxB;wGAvBW,mBAAmB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;4FAAnB,mBAAmB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECXhC,0gBAOA,EAAA,MAAA,EAAA,CAAA,4YAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDAY,YAAY,EAAA,CAAA,EAAA,CAAA;;4FAIX,mBAAmB,EAAA,UAAA,EAAA,CAAA;kBAP/B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,iBAAiB,EAAA,UAAA,EACf,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,CAAC,EAAA,QAAA,EAAA,0gBAAA,EAAA,MAAA,EAAA,CAAA,4YAAA,CAAA,EAAA;;;MEaZ,iBAAiB,CAAA;AACnB,IAAA,IAAI,GAAG,KAAK,CAAc,IAAI,2EAAC;IAC/B,UAAU,GAAG,MAAM,EAAiD;;IAGpE,QAAQ,GAAGA,IAAQ;IACnB,YAAY,GAAGC,QAAY;IAC3B,cAAc,GAAGC,UAAc;IAC/B,UAAU,GAAGC,MAAU;IACvB,eAAe,GAAGC,WAAe;;AAGjC,IAAA,MAAM,GAAG,MAAM,CAAC,KAAK,6EAAC;IAE/B,UAAU,GAAA;AACR,QAAA,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/B;IAEA,SAAS,GAAA;AACP,QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;IACxB;AAEA,IAAA,QAAQ,CAAC,MAAqD,EAAA;AAC5D,QAAA,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,SAAS,EAAE;IAClB;AAGA,IAAA,cAAc,CAAC,KAAiB,EAAA;AAC9B,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAAqB;QAC1C,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,oBAAoB,CAAC,EAAE;YACzC,IAAI,CAAC,SAAS,EAAE;QAClB;IACF;wGAjCW,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAjB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,iBAAiB,sTCpB9B,mxEA6CM,EAAA,MAAA,EAAA,CAAA,utCAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,ED7BM,YAAY,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,mBAAmB,6EAAE,mBAAmB,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,sBAAA,EAAA,QAAA,EAAA,oDAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,KAAA,EAAA,OAAA,EAAA,qBAAA,EAAA,MAAA,EAAA,aAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;4FAIrD,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAP7B,SAAS;+BACE,eAAe,EAAA,UAAA,EACb,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,CAAC,EAAA,QAAA,EAAA,mxEAAA,EAAA,MAAA,EAAA,CAAA,utCAAA,CAAA,EAAA;;sBA+BhE,YAAY;uBAAC,gBAAgB,EAAE,CAAC,QAAQ,CAAC;;;AE/C5C;AAiBA,MAAM,KAAK,GAAG;AACV,IAAA,MAAM,EAAE,MAAM;AACd,IAAA,IAAI,EAAE,IAAI;AACV,IAAA,CAAC,EAAE,CAAC;AACJ,IAAA,GAAG,EAAE,GAAG;AACR,IAAA,IAAI,EAAE,IAAI;AACV,IAAA,OAAO,EAAE,OAAO;AAChB,IAAA,IAAI,EAAE,IAAI;AACV,IAAA,IAAI,EAAE,IAAI;AACV,IAAA,WAAW,EAAE,WAAW;AACxB,IAAA,SAAS,EAAE;CACd;MA0BY,aAAa,CAAA;AACb,IAAA,IAAI,GAAG,KAAK,CAAC,QAAQ,0EAAU;AAC/B,IAAA,IAAI,GAAG,KAAK,CAAS,EAAE,2EAAC;AACxB,IAAA,KAAK,GAAG,KAAK,CAAqB,SAAS,4EAAC;AAC5C,IAAA,WAAW,GAAG,KAAK,CAAqB,SAAS,kFAAC;AAExC,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAK;AACxC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE;AACxB,QAAA,OAAO,KAAK,CAAC,IAA0B,CAAC,IAAI,MAAM;AACtD,IAAA,CAAC,+EAAC;wGATO,aAAa,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAb,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,aAAa,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EApBZ,CAAA;;;;;;;;GAQX,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,gGAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EATW,YAAY,8BAAE,mBAAmB,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,sBAAA,EAAA,QAAA,EAAA,oDAAA,EAAA,MAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,KAAA,EAAA,OAAA,EAAA,qBAAA,EAAA,MAAA,EAAA,aAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;4FAqBlC,aAAa,EAAA,UAAA,EAAA,CAAA;kBAxBzB,SAAS;+BACI,UAAU,EAAA,UAAA,EACR,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,EAAE,mBAAmB,CAAC,EAAA,QAAA,EAClC,CAAA;;;;;;;;AAQX,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,gGAAA,CAAA,EAAA;;;MCfU,eAAe,CAAA;AACT,IAAA,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AACrB,IAAA,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;;AAG7C,IAAA,IAAI,GAAG,KAAK,CAAc,IAAI,2EAAC;AAC/B,IAAA,WAAW,GAAG,KAAK,CAAW,EAAE,kFAAC;AACjC,IAAA,YAAY,GAAG,KAAK,CAAsB,IAAI,mFAAC;;AAG/C,IAAA,eAAe,GAAG,KAAK,CAAmB,EAAE,sFAAC;AAC7C,IAAA,YAAY,GAAG,KAAK,CAAgB,EAAE,mFAAC;AACvC,IAAA,aAAa,GAAG,KAAK,CAAiB,EAAE,oFAAC;AACzC,IAAA,iBAAiB,GAAG,KAAK,CAAS,CAAC,wFAAC;AACpC,IAAA,OAAO,GAAG,KAAK,CAAS,qGAAqG,8EAAC;AAC9H,IAAA,OAAO,GAAG,KAAK,CAAS,WAAW,8EAAC;AACpC,IAAA,cAAc,GAAG,KAAK,CAAS,EAAE,qFAAC;AAClC,IAAA,UAAU,GAAG,KAAK,CAAU,IAAI,iFAAC;AACjC,IAAA,iBAAiB,GAAG,KAAK,CAAU,IAAI,wFAAC;AACxC,IAAA,eAAe,GAAG,KAAK,CAAU,IAAI,sFAAC;AACtC,IAAA,iBAAiB,GAAG,KAAK,CAAU,IAAI,wFAAC;AACxC,IAAA,UAAU,GAAG,KAAK,CAAU,IAAI,iFAAC;;IAGjC,MAAM,GAAG,MAAM,EAAU;IACzB,MAAM,GAAG,MAAM,EAAQ;IACvB,mBAAmB,GAAG,MAAM,EAAe;IAC3C,iBAAiB,GAAG,MAAM,EAAgB;IAC1C,QAAQ,GAAG,MAAM,EAAU;IAC3B,QAAQ,GAAG,MAAM,EAAQ;IACzB,UAAU,GAAG,MAAM,EAAiD;;;;;;;;;AAWpE,IAAA,gBAAgB,GAAG,MAAM,CAAC,KAAK,uFAAC;;AAGzC,IAAA,aAAa,CAAC,IAAoB,EAAA;AAChC,QAAA,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE;AACtD,YAAA,OAAO,IAAI;QACb;AACA,QAAA,MAAM,kBAAkB,GAAG,IAAI,CAAC,WAAW,EAAE;AAC7C,QAAA,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACrE;;AAGS,IAAA,WAAW,GAAG,QAAQ,CAAC,MAAK;AACnC,QAAA,MAAM,aAAa,GAAG,IAAI,CAAC,iBAAiB,EAAE;QAC9C,IAAI,aAAa,GAAG,CAAC;AAAE,YAAA,OAAO,aAAa;AAC3C,QAAA,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM;AAC3D,IAAA,CAAC,kFAAC;AAEO,IAAA,oBAAoB,GAAG,QAAQ,CAAC,MACvC,IAAI,CAAC,UAAU,EAAE,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,2FACrF;;AAGD,IAAA,QAAQ,CAAC,IAAoB,EAAA;QAC3B,IAAI,CAAC,IAAI,CAAC,KAAK;AAAE,YAAA,OAAO,KAAK;AAC7B,QAAA,IAAI;YACF,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE;AACtC,gBAAA,KAAK,EAAE,QAAQ;AACf,gBAAA,WAAW,EAAE,SAAS;AACtB,gBAAA,QAAQ,EAAE,SAAS;AACnB,gBAAA,YAAY,EAAE,SAAS;AACxB,aAAA,CAAC;QACJ;AAAE,QAAA,MAAM;AACN,YAAA,OAAO,KAAK;QACd;IACF;IAEA,gBAAgB,GAAA;AACd,QAAA,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACzC;IAEA,eAAe,GAAA;AACb,QAAA,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC;IAClC;AAEA,IAAA,QAAQ,CAAC,KAAa,EAAA;AACpB,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;IACzB;AAEA,IAAA,mBAAmB,CAAC,GAAgB,EAAA;AAClC,QAAA,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC;IACpC;AAEA,IAAA,mBAAmB,CAAC,YAA0B,EAAA;AAC5C,QAAA,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC;IAC3C;AAEA,IAAA,UAAU,CAAC,EAAU,EAAA;AACnB,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IACxB;IAEA,UAAU,GAAA;AACR,QAAA,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;IACtB;AAEA,IAAA,YAAY,CAAC,MAAqD,EAAA;AAChE,QAAA,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC;IAC9B;wGA5GW,eAAe,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAf,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,eAAe,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,YAAA,EAAA,MAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,YAAA,EAAA,EAAA,iBAAA,EAAA,cAAA,EAAA,UAAA,EAAA,cAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,eAAA,EAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,YAAA,EAAA,EAAA,iBAAA,EAAA,cAAA,EAAA,UAAA,EAAA,cAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,aAAA,EAAA,EAAA,iBAAA,EAAA,eAAA,EAAA,UAAA,EAAA,eAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,EAAA,iBAAA,EAAA,mBAAA,EAAA,UAAA,EAAA,mBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,cAAA,EAAA,EAAA,iBAAA,EAAA,gBAAA,EAAA,UAAA,EAAA,gBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,EAAA,iBAAA,EAAA,mBAAA,EAAA,UAAA,EAAA,mBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,eAAA,EAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,EAAA,iBAAA,EAAA,mBAAA,EAAA,UAAA,EAAA,mBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,MAAA,EAAA,QAAA,EAAA,MAAA,EAAA,QAAA,EAAA,mBAAA,EAAA,qBAAA,EAAA,iBAAA,EAAA,mBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,UAAA,EAAA,UAAA,EAAA,YAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EC3B5B,qrMA0JA,EAAA,MAAA,EAAA,CAAA,q1IAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,ED1II,YAAY,+BACZ,UAAU,EAAA,QAAA,EAAA,cAAA,EAAA,MAAA,EAAA,CAAA,QAAA,EAAA,aAAA,EAAA,UAAA,EAAA,qBAAA,EAAA,OAAA,EAAA,MAAA,EAAA,YAAA,EAAA,kBAAA,EAAA,oBAAA,EAAA,YAAA,EAAA,YAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACV,gBAAgB,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,yBAAA,EAAA,uBAAA,EAAA,kBAAA,CAAA,EAAA,OAAA,EAAA,CAAA,gBAAA,CAAA,EAAA,QAAA,EAAA,CAAA,kBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAChB,oBAAoB,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,CAAA,cAAA,CAAA,EAAA,OAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACpB,yBAAyB,EAAA,QAAA,EAAA,uBAAA,EAAA,MAAA,EAAA,CAAA,aAAA,EAAA,eAAA,CAAA,EAAA,OAAA,EAAA,CAAA,mBAAA,EAAA,UAAA,EAAA,UAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACzB,iBAAiB,qGACjB,aAAa,EAAA,QAAA,EAAA,UAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,MAAA,EAAA,OAAA,EAAA,aAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;4FAKJ,eAAe,EAAA,UAAA,EAAA,CAAA;kBAf3B,SAAS;+BACE,YAAY,EAAA,UAAA,EACV,IAAI,EAAA,OAAA,EACP;wBACP,YAAY;wBACZ,UAAU;wBACV,gBAAgB;wBAChB,oBAAoB;wBACpB,yBAAyB;wBACzB,iBAAiB;wBACjB,aAAa;AACd,qBAAA,EAAA,QAAA,EAAA,qrMAAA,EAAA,MAAA,EAAA,CAAA,q1IAAA,CAAA,EAAA;;;AEvBH;;AAEG;;ACFH;;AAEG;;;;"}
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@excellore/ui",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
7
  "peerDependencies": {
8
- "@angular/common": "^21.2.0",
9
- "@angular/core": "^21.2.0"
8
+ "@angular/common": "^21.0.0",
9
+ "@angular/core": "^21.0.0"
10
10
  },
11
11
  "dependencies": {
12
12
  "tslib": "^2.3.0"
@@ -69,7 +69,14 @@ declare class HeaderComponent {
69
69
  readonly notificationCount: _angular_core.InputSignal<number>;
70
70
  readonly logoUrl: _angular_core.InputSignal<string>;
71
71
  readonly appName: _angular_core.InputSignal<string>;
72
+ readonly currentAppName: _angular_core.InputSignal<string>;
73
+ readonly showSearch: _angular_core.InputSignal<boolean>;
74
+ readonly showNotifications: _angular_core.InputSignal<boolean>;
75
+ readonly showAppSwitcher: _angular_core.InputSignal<boolean>;
76
+ readonly showThemeSwitcher: _angular_core.InputSignal<boolean>;
77
+ readonly showSignIn: _angular_core.InputSignal<boolean>;
72
78
  readonly search: _angular_core.OutputEmitterRef<string>;
79
+ readonly signIn: _angular_core.OutputEmitterRef<void>;
73
80
  readonly applicationSelected: _angular_core.OutputEmitterRef<Application>;
74
81
  readonly notificationClick: _angular_core.OutputEmitterRef<Notification>;
75
82
  readonly markRead: _angular_core.OutputEmitterRef<string>;
@@ -78,18 +85,18 @@ declare class HeaderComponent {
78
85
  readonly isMobileMenuOpen: _angular_core.WritableSignal<boolean>;
79
86
  hasPermission(item: NavigationItem): boolean;
80
87
  readonly unreadCount: _angular_core.Signal<number>;
88
+ readonly hasMobileMenuContent: _angular_core.Signal<boolean>;
81
89
  isActive(item: NavigationItem): boolean;
82
90
  toggleMobileMenu(): void;
83
91
  closeMobileMenu(): void;
84
92
  onSearch(query: string): void;
85
- getOrganizationInitials(organization: Organization): string;
86
93
  onApplicationSelect(app: Application): void;
87
94
  onNotificationClick(notification: Notification): void;
88
95
  onMarkRead(id: string): void;
89
96
  onClearAll(): void;
90
97
  onMenuAction(action: 'profile' | 'settings' | 'billing' | 'logout'): void;
91
98
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<HeaderComponent, never>;
92
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<HeaderComponent, "lib-header", never, { "user": { "alias": "user"; "required": false; "isSignal": true; }; "permissions": { "alias": "permissions"; "required": false; "isSignal": true; }; "organization": { "alias": "organization"; "required": false; "isSignal": true; }; "navigationItems": { "alias": "navigationItems"; "required": false; "isSignal": true; }; "applications": { "alias": "applications"; "required": false; "isSignal": true; }; "notifications": { "alias": "notifications"; "required": false; "isSignal": true; }; "notificationCount": { "alias": "notificationCount"; "required": false; "isSignal": true; }; "logoUrl": { "alias": "logoUrl"; "required": false; "isSignal": true; }; "appName": { "alias": "appName"; "required": false; "isSignal": true; }; }, { "search": "search"; "applicationSelected": "applicationSelected"; "notificationClick": "notificationClick"; "markRead": "markRead"; "clearAll": "clearAll"; "menuAction": "menuAction"; }, never, never, true, never>;
99
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<HeaderComponent, "lib-header", never, { "user": { "alias": "user"; "required": false; "isSignal": true; }; "permissions": { "alias": "permissions"; "required": false; "isSignal": true; }; "organization": { "alias": "organization"; "required": false; "isSignal": true; }; "navigationItems": { "alias": "navigationItems"; "required": false; "isSignal": true; }; "applications": { "alias": "applications"; "required": false; "isSignal": true; }; "notifications": { "alias": "notifications"; "required": false; "isSignal": true; }; "notificationCount": { "alias": "notificationCount"; "required": false; "isSignal": true; }; "logoUrl": { "alias": "logoUrl"; "required": false; "isSignal": true; }; "appName": { "alias": "appName"; "required": false; "isSignal": true; }; "currentAppName": { "alias": "currentAppName"; "required": false; "isSignal": true; }; "showSearch": { "alias": "showSearch"; "required": false; "isSignal": true; }; "showNotifications": { "alias": "showNotifications"; "required": false; "isSignal": true; }; "showAppSwitcher": { "alias": "showAppSwitcher"; "required": false; "isSignal": true; }; "showThemeSwitcher": { "alias": "showThemeSwitcher"; "required": false; "isSignal": true; }; "showSignIn": { "alias": "showSignIn"; "required": false; "isSignal": true; }; }, { "search": "search"; "signIn": "signIn"; "applicationSelected": "applicationSelected"; "notificationClick": "notificationClick"; "markRead": "markRead"; "clearAll": "clearAll"; "menuAction": "menuAction"; }, never, never, true, never>;
93
100
  }
94
101
 
95
102
  declare class AppSwitcherComponent {