@fhss-web-team/frontend-utils 1.4.1 → 1.4.2

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.
@@ -22,19 +22,15 @@ import { MatButtonModule } from '@angular/material/button';
22
22
 
23
23
  class ByuFooterComponent {
24
24
  currentYear = new Date().getFullYear(); // Automatically updates the year
25
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: ByuFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
26
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.8", type: ByuFooterComponent, isStandalone: true, selector: "byu-footer", ngImport: i0, template: "<footer>\n <p class=\"title\"><a href=\"https://www.byu.edu/\">BRIGHAM YOUNG UNIVERSITY</a></p>\n <p>Provo, UT 84602, USA | \u00A9 {{ currentYear }} All rights reserved.</p>\n <p>\n <a href=\"https://privacy.byu.edu/privacy-notice\">Privacy Notice</a> |\n <a href=\"https://privacy.byu.edu/cookie-prefs\">Cookie Preferences</a>\n </p>\n</footer>\n \n\n", styles: ["footer{font-family:HCo Ringside Narrow SSm,Open Sans,Helvetica,Arial,sans-serif;font-weight:400;font-size:14px;color:#fff;background-color:#002e5d;text-align:center;padding:10px 20px}p{margin:8px 0}a{text-decoration:none;color:inherit}a:hover{text-decoration:underline}.title{font-family:HCo Ringside Narrow SSm Bold,Open Sans,Helvetica,Arial,sans-serif;font-weight:700;font-size:20px;letter-spacing:5px;margin-bottom:18px}.title a:hover{text-decoration:none}\n"] });
25
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: ByuFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
26
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.10", type: ByuFooterComponent, isStandalone: true, selector: "byu-footer", ngImport: i0, template: "<footer>\n <p class=\"title\"><a href=\"https://www.byu.edu/\">BRIGHAM YOUNG UNIVERSITY</a></p>\n <p>Provo, UT 84602, USA | \u00A9 {{ currentYear }} All rights reserved.</p>\n <p>\n <a href=\"https://privacy.byu.edu/privacy-notice\">Privacy Notice</a> |\n <a href=\"https://privacy.byu.edu/cookie-prefs\">Cookie Preferences</a>\n </p>\n</footer>\n \n\n", styles: ["footer{font-family:HCo Ringside Narrow SSm,Open Sans,Helvetica,Arial,sans-serif;font-weight:400;font-size:14px;color:#fff;background-color:#002e5d;text-align:center;padding:10px 20px}p{margin:8px 0}a{text-decoration:none;color:inherit}a:hover{text-decoration:underline}.title{font-family:HCo Ringside Narrow SSm Bold,Open Sans,Helvetica,Arial,sans-serif;font-weight:700;font-size:20px;letter-spacing:5px;margin-bottom:18px}.title a:hover{text-decoration:none}\n"] });
27
27
  }
28
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: ByuFooterComponent, decorators: [{
28
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: ByuFooterComponent, decorators: [{
29
29
  type: Component,
30
30
  args: [{ selector: 'byu-footer', imports: [], template: "<footer>\n <p class=\"title\"><a href=\"https://www.byu.edu/\">BRIGHAM YOUNG UNIVERSITY</a></p>\n <p>Provo, UT 84602, USA | \u00A9 {{ currentYear }} All rights reserved.</p>\n <p>\n <a href=\"https://privacy.byu.edu/privacy-notice\">Privacy Notice</a> |\n <a href=\"https://privacy.byu.edu/cookie-prefs\">Cookie Preferences</a>\n </p>\n</footer>\n \n\n", styles: ["footer{font-family:HCo Ringside Narrow SSm,Open Sans,Helvetica,Arial,sans-serif;font-weight:400;font-size:14px;color:#fff;background-color:#002e5d;text-align:center;padding:10px 20px}p{margin:8px 0}a{text-decoration:none;color:inherit}a:hover{text-decoration:underline}.title{font-family:HCo Ringside Narrow SSm Bold,Open Sans,Helvetica,Arial,sans-serif;font-weight:700;font-size:20px;letter-spacing:5px;margin-bottom:18px}.title a:hover{text-decoration:none}\n"] }]
31
31
  }] });
32
32
 
33
33
  const FHSS_CONFIG = new InjectionToken('FHSS_CONFIG');
34
- const provideFhss = (config) => ({
35
- provide: FHSS_CONFIG,
36
- useValue: config,
37
- });
38
34
 
39
35
  /**
40
36
  * AuthService provides a wrapper around the Keycloak JavaScript adapter, exposing its properties and methods
@@ -205,10 +201,10 @@ class AuthService {
205
201
  this.keycloakSignal();
206
202
  return this.keycloak.responseType;
207
203
  });
208
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
209
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AuthService, providedIn: 'root' });
204
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
205
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AuthService, providedIn: 'root' });
210
206
  }
211
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AuthService, decorators: [{
207
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AuthService, decorators: [{
212
208
  type: Injectable,
213
209
  args: [{
214
210
  providedIn: 'root',
@@ -231,10 +227,10 @@ class ByuHeaderComponent {
231
227
  isOpen(text) {
232
228
  return this.openDropdownText === text;
233
229
  }
234
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: ByuHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
235
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.8", type: ByuHeaderComponent, isStandalone: true, selector: "byu-header", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<header>\n <div class=\"top\" >\n <img class=\"logo\" src=\"/BYU_monogram_white@2x.png\" alt=\"BYU\">\n <div class=\"titles\"> \n <div class=\"breadcrumbs\">\n @for (breadcrumb of config()?.breadcrumbs; track breadcrumb.text){\n <a [href]=\"breadcrumb.path\" >{{ breadcrumb.text }}</a>\n }\n </div>\n <a [routerLink]=\"config()?.title?.path\" class=\"title\">{{ config()?.title?.text }}</a>\n <a [routerLink]=\"config()?.subtitle?.path\" class=\"subtitle\">{{ config()?.subtitle?.text }}</a>\n </div>\n <div class=\"signin\">\n <p>{{ this.auth.tokenParsed()?.['given_name'] ?? '' }}</p>\n <svg class=\"signin-icon\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\">\n <path fill=\"currentcolor\" d=\"M50 95c-26 0-34-18-34-18 3-12 8-18 17-18 5 5 10 7 17 7s12-2 17-7c9 0 14 6 17 18 0 0-7 18-34 18z\"></path>\n <circle cx=\"50\" cy=\"50\" r=\"45\" fill=\"none\" stroke=\"currentcolor\" stroke-width=\"10\"></circle>\n <circle fill=\"currentcolor\" cx=\"50\" cy=\"40\" r=\"20\"></circle>\n </svg>\n @if (auth.authenticated()) {\n <a class=\"signin-link\" (click)=\"auth.logout()\">Sign Out</a>\n } @else {\n <a class=\"signin-link\" (click)=\"auth.login()\">Sign In</a>\n }\n </div>\n </div>\n <div class=\"bottom\">\n <nav>\n @for (menuItem of config()?.menu; track menuItem.text){>\n @if (isHeaderLink(menuItem)) {\n <li class = \"nav-item\">\n <a class=\"nav-item-content\" [routerLink]=\"menuItem.path\">{{ menuItem.text }}</a>\n </li>\n } @else {\n <li class=\"nav-item dropdown\" (click)=\"toggleDropdown(menuItem.text)\">\n <div class=\"nav-item-content\" >{{ menuItem.text }}</div>\n @if (openDropdownText === menuItem.text) {\n <ul class = \"dropdown-item-menu\"> \n @for(dropItem of menuItem.items; track dropItem.text){\n <li class = \"dropdown-item\">\n <a class=\"dropdown-item-content\" [routerLink]=\"dropItem.path\">{{ dropItem.text }}</a>\n </li>\n }\n </ul>\n }\n </li>\n }\n }\n </nav>\n </div>\n</header>\n", styles: ["header{font-family:HCo Ringside Narrow SSm,Open Sans,Helvetica,Arial,sans-serif;color:#fff}header .top{background-color:#002e5d;display:flex;align-items:center;padding:13px 16px;gap:16px}header .top .logo{width:100px}header .top .titles{display:flex;flex-direction:column;gap:8px;padding-left:30px;border-left:1px solid rgba(255,255,255,.25)}header .top .titles .breadcrumbs{display:flex;flex-direction:row}header .top .titles .breadcrumbs a{color:#a6abb1;text-decoration:none;font-size:16px}header .top .titles .breadcrumbs a:not(:first-child){padding-left:10px}header .top .titles .breadcrumbs a:not(:last-child){padding-right:10px;border-right:1px solid rgba(255,255,255,.25)}header .top .titles .breadcrumbs a:hover{color:#fff}header .top .titles .title{color:inherit;font-size:24px;font-weight:500;text-decoration:none}header .top .titles .subtitle{color:inherit;font-size:16px;font-weight:500;text-decoration:none}header .top .signin{display:flex;align-items:center;gap:10px;font-size:16px;margin-left:auto}header .top .signin .signin-icon{display:flex;margin-left:auto;margin-right:0;align-items:center;height:20px;width:20px}header .top .signin .signin-link{color:#fff;text-decoration:none;cursor:pointer}header .bottom{box-shadow:0 3px 10px #ccc5c580}header .bottom nav{display:flex;flex-direction:row;padding-left:124px}header .bottom nav .nav-item{display:block;list-style:none;transition:all .25s;text-decoration:none}header .bottom nav .nav-item:hover{box-shadow:inset 0 -5px #002e5d;background-color:#f1f1f1}header .bottom nav .nav-item .nav-item-content{margin:5px;display:inline-block;padding:11px 22px;text-decoration:none;color:#002e5d}header .bottom nav .nav-item.dropdown{position:relative}header .bottom nav .nav-item.dropdown .dropdown-item-menu{position:absolute;background:#fff;z-index:1000;top:100%;width:min-content;margin:-5px;padding:0;box-shadow:0 3px 3px #ccc5c5bf}header .bottom nav .nav-item.dropdown .dropdown-item-menu .dropdown-item:hover{background-color:#f1f1f1}header .bottom nav .nav-item.dropdown .dropdown-item-menu .dropdown-item .dropdown-item-content{margin:10px;display:inline-block;text-decoration:none;text-wrap:nowrap;color:#002e5d;padding:11px 22px}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] });
230
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: ByuHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
231
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: ByuHeaderComponent, isStandalone: true, selector: "byu-header", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<header>\n <div class=\"top\" >\n <img class=\"logo\" src=\"/BYU_monogram_white@2x.png\" alt=\"BYU\">\n <div class=\"titles\"> \n <div class=\"breadcrumbs\">\n @for (breadcrumb of config()?.breadcrumbs; track breadcrumb.text){\n <a [href]=\"breadcrumb.path\" >{{ breadcrumb.text }}</a>\n }\n </div>\n <a [routerLink]=\"config()?.title?.path\" class=\"title\">{{ config()?.title?.text }}</a>\n <a [routerLink]=\"config()?.subtitle?.path\" class=\"subtitle\">{{ config()?.subtitle?.text }}</a>\n </div>\n <div class=\"signin\">\n <p>{{ this.auth.tokenParsed()?.['given_name'] ?? '' }}</p>\n <svg class=\"signin-icon\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\">\n <path fill=\"currentcolor\" d=\"M50 95c-26 0-34-18-34-18 3-12 8-18 17-18 5 5 10 7 17 7s12-2 17-7c9 0 14 6 17 18 0 0-7 18-34 18z\"></path>\n <circle cx=\"50\" cy=\"50\" r=\"45\" fill=\"none\" stroke=\"currentcolor\" stroke-width=\"10\"></circle>\n <circle fill=\"currentcolor\" cx=\"50\" cy=\"40\" r=\"20\"></circle>\n </svg>\n @if (auth.authenticated()) {\n <a class=\"signin-link\" (click)=\"auth.logout()\">Sign Out</a>\n } @else {\n <a class=\"signin-link\" (click)=\"auth.login()\">Sign In</a>\n }\n </div>\n </div>\n <div class=\"bottom\">\n <nav>\n @for (menuItem of config()?.menu; track menuItem.text){>\n @if (isHeaderLink(menuItem)) {\n <li class = \"nav-item\">\n <a class=\"nav-item-content\" [routerLink]=\"menuItem.path\">{{ menuItem.text }}</a>\n </li>\n } @else {\n <li class=\"nav-item dropdown\" (click)=\"toggleDropdown(menuItem.text)\">\n <div class=\"nav-item-content\" >{{ menuItem.text }}</div>\n @if (openDropdownText === menuItem.text) {\n <ul class = \"dropdown-item-menu\"> \n @for(dropItem of menuItem.items; track dropItem.text){\n <li class = \"dropdown-item\">\n <a class=\"dropdown-item-content\" [routerLink]=\"dropItem.path\">{{ dropItem.text }}</a>\n </li>\n }\n </ul>\n }\n </li>\n }\n }\n </nav>\n </div>\n</header>\n", styles: ["header{font-family:HCo Ringside Narrow SSm,Open Sans,Helvetica,Arial,sans-serif;color:#fff}header .top{background-color:#002e5d;display:flex;align-items:center;padding:13px 16px;gap:16px}header .top .logo{width:100px}header .top .titles{display:flex;flex-direction:column;gap:8px;padding-left:30px;border-left:1px solid rgba(255,255,255,.25)}header .top .titles .breadcrumbs{display:flex;flex-direction:row}header .top .titles .breadcrumbs a{color:#a6abb1;text-decoration:none;font-size:16px}header .top .titles .breadcrumbs a:not(:first-child){padding-left:10px}header .top .titles .breadcrumbs a:not(:last-child){padding-right:10px;border-right:1px solid rgba(255,255,255,.25)}header .top .titles .breadcrumbs a:hover{color:#fff}header .top .titles .title{color:inherit;font-size:24px;font-weight:500;text-decoration:none}header .top .titles .subtitle{color:inherit;font-size:16px;font-weight:500;text-decoration:none}header .top .signin{display:flex;align-items:center;gap:10px;font-size:16px;margin-left:auto}header .top .signin .signin-icon{display:flex;margin-left:auto;margin-right:0;align-items:center;height:20px;width:20px}header .top .signin .signin-link{color:#fff;text-decoration:none;cursor:pointer}header .bottom{box-shadow:0 3px 10px #ccc5c580}header .bottom nav{display:flex;flex-direction:row;padding-left:124px}header .bottom nav .nav-item{display:block;list-style:none;transition:all .25s;text-decoration:none}header .bottom nav .nav-item:hover{box-shadow:inset 0 -5px #002e5d;background-color:#f1f1f1}header .bottom nav .nav-item .nav-item-content{margin:5px;display:inline-block;padding:11px 22px;text-decoration:none;color:#002e5d}header .bottom nav .nav-item.dropdown{position:relative}header .bottom nav .nav-item.dropdown .dropdown-item-menu{position:absolute;background:#fff;z-index:1000;top:100%;width:min-content;margin:-5px;padding:0;box-shadow:0 3px 3px #ccc5c5bf}header .bottom nav .nav-item.dropdown .dropdown-item-menu .dropdown-item:hover{background-color:#f1f1f1}header .bottom nav .nav-item.dropdown .dropdown-item-menu .dropdown-item .dropdown-item-content{margin:10px;display:inline-block;text-decoration:none;text-wrap:nowrap;color:#002e5d;padding:11px 22px}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] });
236
232
  }
237
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: ByuHeaderComponent, decorators: [{
233
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: ByuHeaderComponent, decorators: [{
238
234
  type: Component,
239
235
  args: [{ selector: 'byu-header', imports: [RouterModule], template: "<header>\n <div class=\"top\" >\n <img class=\"logo\" src=\"/BYU_monogram_white@2x.png\" alt=\"BYU\">\n <div class=\"titles\"> \n <div class=\"breadcrumbs\">\n @for (breadcrumb of config()?.breadcrumbs; track breadcrumb.text){\n <a [href]=\"breadcrumb.path\" >{{ breadcrumb.text }}</a>\n }\n </div>\n <a [routerLink]=\"config()?.title?.path\" class=\"title\">{{ config()?.title?.text }}</a>\n <a [routerLink]=\"config()?.subtitle?.path\" class=\"subtitle\">{{ config()?.subtitle?.text }}</a>\n </div>\n <div class=\"signin\">\n <p>{{ this.auth.tokenParsed()?.['given_name'] ?? '' }}</p>\n <svg class=\"signin-icon\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\">\n <path fill=\"currentcolor\" d=\"M50 95c-26 0-34-18-34-18 3-12 8-18 17-18 5 5 10 7 17 7s12-2 17-7c9 0 14 6 17 18 0 0-7 18-34 18z\"></path>\n <circle cx=\"50\" cy=\"50\" r=\"45\" fill=\"none\" stroke=\"currentcolor\" stroke-width=\"10\"></circle>\n <circle fill=\"currentcolor\" cx=\"50\" cy=\"40\" r=\"20\"></circle>\n </svg>\n @if (auth.authenticated()) {\n <a class=\"signin-link\" (click)=\"auth.logout()\">Sign Out</a>\n } @else {\n <a class=\"signin-link\" (click)=\"auth.login()\">Sign In</a>\n }\n </div>\n </div>\n <div class=\"bottom\">\n <nav>\n @for (menuItem of config()?.menu; track menuItem.text){>\n @if (isHeaderLink(menuItem)) {\n <li class = \"nav-item\">\n <a class=\"nav-item-content\" [routerLink]=\"menuItem.path\">{{ menuItem.text }}</a>\n </li>\n } @else {\n <li class=\"nav-item dropdown\" (click)=\"toggleDropdown(menuItem.text)\">\n <div class=\"nav-item-content\" >{{ menuItem.text }}</div>\n @if (openDropdownText === menuItem.text) {\n <ul class = \"dropdown-item-menu\"> \n @for(dropItem of menuItem.items; track dropItem.text){\n <li class = \"dropdown-item\">\n <a class=\"dropdown-item-content\" [routerLink]=\"dropItem.path\">{{ dropItem.text }}</a>\n </li>\n }\n </ul>\n }\n </li>\n }\n }\n </nav>\n </div>\n</header>\n", styles: ["header{font-family:HCo Ringside Narrow SSm,Open Sans,Helvetica,Arial,sans-serif;color:#fff}header .top{background-color:#002e5d;display:flex;align-items:center;padding:13px 16px;gap:16px}header .top .logo{width:100px}header .top .titles{display:flex;flex-direction:column;gap:8px;padding-left:30px;border-left:1px solid rgba(255,255,255,.25)}header .top .titles .breadcrumbs{display:flex;flex-direction:row}header .top .titles .breadcrumbs a{color:#a6abb1;text-decoration:none;font-size:16px}header .top .titles .breadcrumbs a:not(:first-child){padding-left:10px}header .top .titles .breadcrumbs a:not(:last-child){padding-right:10px;border-right:1px solid rgba(255,255,255,.25)}header .top .titles .breadcrumbs a:hover{color:#fff}header .top .titles .title{color:inherit;font-size:24px;font-weight:500;text-decoration:none}header .top .titles .subtitle{color:inherit;font-size:16px;font-weight:500;text-decoration:none}header .top .signin{display:flex;align-items:center;gap:10px;font-size:16px;margin-left:auto}header .top .signin .signin-icon{display:flex;margin-left:auto;margin-right:0;align-items:center;height:20px;width:20px}header .top .signin .signin-link{color:#fff;text-decoration:none;cursor:pointer}header .bottom{box-shadow:0 3px 10px #ccc5c580}header .bottom nav{display:flex;flex-direction:row;padding-left:124px}header .bottom nav .nav-item{display:block;list-style:none;transition:all .25s;text-decoration:none}header .bottom nav .nav-item:hover{box-shadow:inset 0 -5px #002e5d;background-color:#f1f1f1}header .bottom nav .nav-item .nav-item-content{margin:5px;display:inline-block;padding:11px 22px;text-decoration:none;color:#002e5d}header .bottom nav .nav-item.dropdown{position:relative}header .bottom nav .nav-item.dropdown .dropdown-item-menu{position:absolute;background:#fff;z-index:1000;top:100%;width:min-content;margin:-5px;padding:0;box-shadow:0 3px 3px #ccc5c5bf}header .bottom nav .nav-item.dropdown .dropdown-item-menu .dropdown-item:hover{background-color:#f1f1f1}header .bottom nav .nav-item.dropdown .dropdown-item-menu .dropdown-item .dropdown-item-content{margin:10px;display:inline-block;text-decoration:none;text-wrap:nowrap;color:#002e5d;padding:11px 22px}\n"] }]
240
236
  }] });
@@ -447,10 +443,10 @@ class UserManagementService {
447
443
  }
448
444
  }), true);
449
445
  }
450
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: UserManagementService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
451
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: UserManagementService, providedIn: 'root' });
446
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: UserManagementService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
447
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: UserManagementService, providedIn: 'root' });
452
448
  }
453
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: UserManagementService, decorators: [{
449
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: UserManagementService, decorators: [{
454
450
  type: Injectable,
455
451
  args: [{
456
452
  providedIn: 'root'
@@ -483,10 +479,10 @@ class UserManagementComponent {
483
479
  this.pageCount.set(pageSize);
484
480
  });
485
481
  }
486
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: UserManagementComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
487
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.8", type: UserManagementComponent, isStandalone: true, selector: "app-user-management", viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true }], ngImport: i0, template: "<mat-form-field>\n <mat-label>Search for User</mat-label>\n <input matInput [(ngModel)]=\"search\">\n</mat-form-field>\n<mat-form-field>\n <mat-label>Account Type</mat-label>\n <mat-select multiple [(ngModel)]=\"accountTypes\">\n @for (accountType of accountTypeOptions; track accountType) {\n <mat-option [value]=\"accountType\">{{accountType}}</mat-option>\n }\n </mat-select>\n</mat-form-field>\n\n<table mat-table [dataSource]=\"getUsers.persistentValue()?.data ?? []\" matSort matSortActive=\"netId\" matSortDisableClear matSortDirection=\"asc\">\n <!-- NetID Column -->\n <ng-container matColumnDef=\"netId\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>NetID</th>\n <td mat-cell *matCellDef=\"let user\">{{user.netId}}</td>\n </ng-container>\n\n <!-- First Name Column -->\n <ng-container matColumnDef=\"preferredFirstName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>First Name</th>\n <td mat-cell *matCellDef=\"let user\">{{user.preferredFirstName}}</td>\n </ng-container>\n\n <!-- Last Name Column -->\n <ng-container matColumnDef=\"preferredLastName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>Last Name</th>\n <td mat-cell *matCellDef=\"let user\">{{user.preferredLastName}}</td>\n </ng-container>\n\n <!-- Roles Column -->\n <ng-container matColumnDef=\"roles\">\n <th mat-header-cell *matHeaderCellDef>Roles</th>\n <td mat-cell *matCellDef=\"let user\">{{user.roles.join(', ')}}</td>\n </ng-container>\n\n <!-- Account Type Column -->\n <ng-container matColumnDef=\"accountType\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>Account Type</th>\n <td mat-cell *matCellDef=\"let user\">{{user.accountType}}</td>\n </ng-container>\n\n <!-- Created Column -->\n <ng-container matColumnDef=\"created\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n Created\n </th>\n <td mat-cell *matCellDef=\"let user\">{{user.created | date}}</td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n</table>\n\n<mat-paginator [length]=\"getUsers.persistentValue()?.totalCount\" [pageSize]=\"pageCount()\" [pageSizeOptions]=\"[5, 10, 20]\" showFirstLastButtons></mat-paginator>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i1$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i1$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i1$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i1$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i1$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i1$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i1$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i1$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i1$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i1$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i2.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i2.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }] });
482
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: UserManagementComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
483
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: UserManagementComponent, isStandalone: true, selector: "app-user-management", viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true }], ngImport: i0, template: "<mat-form-field>\n <mat-label>Search for User</mat-label>\n <input matInput [(ngModel)]=\"search\">\n</mat-form-field>\n<mat-form-field>\n <mat-label>Account Type</mat-label>\n <mat-select multiple [(ngModel)]=\"accountTypes\">\n @for (accountType of accountTypeOptions; track accountType) {\n <mat-option [value]=\"accountType\">{{accountType}}</mat-option>\n }\n </mat-select>\n</mat-form-field>\n\n<table mat-table [dataSource]=\"getUsers.persistentValue()?.data ?? []\" matSort matSortActive=\"netId\" matSortDisableClear matSortDirection=\"asc\">\n <!-- NetID Column -->\n <ng-container matColumnDef=\"netId\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>NetID</th>\n <td mat-cell *matCellDef=\"let user\">{{user.netId}}</td>\n </ng-container>\n\n <!-- First Name Column -->\n <ng-container matColumnDef=\"preferredFirstName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>First Name</th>\n <td mat-cell *matCellDef=\"let user\">{{user.preferredFirstName}}</td>\n </ng-container>\n\n <!-- Last Name Column -->\n <ng-container matColumnDef=\"preferredLastName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>Last Name</th>\n <td mat-cell *matCellDef=\"let user\">{{user.preferredLastName}}</td>\n </ng-container>\n\n <!-- Roles Column -->\n <ng-container matColumnDef=\"roles\">\n <th mat-header-cell *matHeaderCellDef>Roles</th>\n <td mat-cell *matCellDef=\"let user\">{{user.roles.join(', ')}}</td>\n </ng-container>\n\n <!-- Account Type Column -->\n <ng-container matColumnDef=\"accountType\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>Account Type</th>\n <td mat-cell *matCellDef=\"let user\">{{user.accountType}}</td>\n </ng-container>\n\n <!-- Created Column -->\n <ng-container matColumnDef=\"created\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n Created\n </th>\n <td mat-cell *matCellDef=\"let user\">{{user.created | date}}</td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n</table>\n\n<mat-paginator [length]=\"getUsers.persistentValue()?.totalCount\" [pageSize]=\"pageCount()\" [pageSizeOptions]=\"[5, 10, 20]\" showFirstLastButtons></mat-paginator>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i1$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i1$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i1$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i1$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i1$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i1$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i1$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i1$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i1$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i1$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i2.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i2.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }] });
488
484
  }
489
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: UserManagementComponent, decorators: [{
485
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: UserManagementComponent, decorators: [{
490
486
  type: Component,
491
487
  args: [{ selector: 'app-user-management', imports: [MatTableModule, MatSortModule, MatPaginatorModule, DatePipe, FormsModule, MatInputModule, MatSelectModule], template: "<mat-form-field>\n <mat-label>Search for User</mat-label>\n <input matInput [(ngModel)]=\"search\">\n</mat-form-field>\n<mat-form-field>\n <mat-label>Account Type</mat-label>\n <mat-select multiple [(ngModel)]=\"accountTypes\">\n @for (accountType of accountTypeOptions; track accountType) {\n <mat-option [value]=\"accountType\">{{accountType}}</mat-option>\n }\n </mat-select>\n</mat-form-field>\n\n<table mat-table [dataSource]=\"getUsers.persistentValue()?.data ?? []\" matSort matSortActive=\"netId\" matSortDisableClear matSortDirection=\"asc\">\n <!-- NetID Column -->\n <ng-container matColumnDef=\"netId\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>NetID</th>\n <td mat-cell *matCellDef=\"let user\">{{user.netId}}</td>\n </ng-container>\n\n <!-- First Name Column -->\n <ng-container matColumnDef=\"preferredFirstName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>First Name</th>\n <td mat-cell *matCellDef=\"let user\">{{user.preferredFirstName}}</td>\n </ng-container>\n\n <!-- Last Name Column -->\n <ng-container matColumnDef=\"preferredLastName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>Last Name</th>\n <td mat-cell *matCellDef=\"let user\">{{user.preferredLastName}}</td>\n </ng-container>\n\n <!-- Roles Column -->\n <ng-container matColumnDef=\"roles\">\n <th mat-header-cell *matHeaderCellDef>Roles</th>\n <td mat-cell *matCellDef=\"let user\">{{user.roles.join(', ')}}</td>\n </ng-container>\n\n <!-- Account Type Column -->\n <ng-container matColumnDef=\"accountType\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>Account Type</th>\n <td mat-cell *matCellDef=\"let user\">{{user.accountType}}</td>\n </ng-container>\n\n <!-- Created Column -->\n <ng-container matColumnDef=\"created\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n Created\n </th>\n <td mat-cell *matCellDef=\"let user\">{{user.created | date}}</td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n</table>\n\n<mat-paginator [length]=\"getUsers.persistentValue()?.totalCount\" [pageSize]=\"pageCount()\" [pageSizeOptions]=\"[5, 10, 20]\" showFirstLastButtons></mat-paginator>\n" }]
492
488
  }], propDecorators: { paginator: [{
@@ -497,6 +493,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImpor
497
493
  args: [MatSort]
498
494
  }] } });
499
495
 
496
+ const provideFhss = (config) => ({
497
+ provide: FHSS_CONFIG,
498
+ useValue: config,
499
+ });
500
+ const enumToRecord = (enumInput) => {
501
+ return Object.fromEntries(Object.entries(enumInput).filter(([key, value]) => typeof value === 'number'));
502
+ };
503
+
500
504
  /**
501
505
  * This guard checks if the client is authenticated, redirecting to login if not.
502
506
  */
@@ -547,37 +551,37 @@ class AuthCallbackPage {
547
551
  goHome() {
548
552
  this.router.navigate(['/']);
549
553
  }
550
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AuthCallbackPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
551
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.8", type: AuthCallbackPage, isStandalone: true, selector: "app-auth-callback", ngImport: i0, template: "<div class=\"container\">\n <p>Logging you in...</p>\n</div>", styles: [":host{height:100vh;display:flex;flex-direction:column}.container{flex:1;display:flex;align-items:center;justify-content:center;text-align:center;padding-bottom:3em}\n"] });
554
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AuthCallbackPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
555
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.10", type: AuthCallbackPage, isStandalone: true, selector: "app-auth-callback", ngImport: i0, template: "<div class=\"container\">\n <p>Logging you in...</p>\n</div>", styles: [":host{height:100vh;display:flex;flex-direction:column}.container{flex:1;display:flex;align-items:center;justify-content:center;text-align:center;padding-bottom:3em}\n"] });
552
556
  }
553
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AuthCallbackPage, decorators: [{
557
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AuthCallbackPage, decorators: [{
554
558
  type: Component,
555
559
  args: [{ selector: 'app-auth-callback', imports: [], template: "<div class=\"container\">\n <p>Logging you in...</p>\n</div>", styles: [":host{height:100vh;display:flex;flex-direction:column}.container{flex:1;display:flex;align-items:center;justify-content:center;text-align:center;padding-bottom:3em}\n"] }]
556
560
  }], ctorParameters: () => [] });
557
561
 
558
562
  class AuthErrorPage {
559
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AuthErrorPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
560
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.8", type: AuthErrorPage, isStandalone: true, selector: "fhss-auth-error", ngImport: i0, template: "<byu-header />\n\n<div class=\"container\">\n <div class=\"not-found\">\n <img src=\"/sad-duck.jpg\" alt=\"sad duck\" />\n <h1>Login Failed</h1>\n <p>Something went wrong while trying to log you in.</p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>\n\n<byu-footer />\n", styles: [":host{height:100vh;display:flex;flex-direction:column}.container{flex:1;display:flex;align-items:center;justify-content:center}.container .not-found{text-align:center;padding-bottom:3em}.container .not-found img{width:25%}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatAnchor, selector: "a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: ByuHeaderComponent, selector: "byu-header", inputs: ["config"] }, { kind: "component", type: ByuFooterComponent, selector: "byu-footer" }] });
563
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AuthErrorPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
564
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.10", type: AuthErrorPage, isStandalone: true, selector: "fhss-auth-error", ngImport: i0, template: "<byu-header />\n\n<div class=\"container\">\n <div class=\"not-found\">\n <img src=\"/sad-duck.jpg\" alt=\"sad duck\" />\n <h1>Login Failed</h1>\n <p>Something went wrong while trying to log you in.</p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>\n\n<byu-footer />\n", styles: [":host{height:100vh;display:flex;flex-direction:column}.container{flex:1;display:flex;align-items:center;justify-content:center}.container .not-found{text-align:center;padding-bottom:3em}.container .not-found img{width:25%}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatAnchor, selector: "a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: ByuHeaderComponent, selector: "byu-header", inputs: ["config"] }, { kind: "component", type: ByuFooterComponent, selector: "byu-footer" }] });
561
565
  }
562
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AuthErrorPage, decorators: [{
566
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AuthErrorPage, decorators: [{
563
567
  type: Component,
564
568
  args: [{ selector: 'fhss-auth-error', imports: [MatButtonModule, ByuHeaderComponent, ByuFooterComponent], template: "<byu-header />\n\n<div class=\"container\">\n <div class=\"not-found\">\n <img src=\"/sad-duck.jpg\" alt=\"sad duck\" />\n <h1>Login Failed</h1>\n <p>Something went wrong while trying to log you in.</p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>\n\n<byu-footer />\n", styles: [":host{height:100vh;display:flex;flex-direction:column}.container{flex:1;display:flex;align-items:center;justify-content:center}.container .not-found{text-align:center;padding-bottom:3em}.container .not-found img{width:25%}\n"] }]
565
569
  }] });
566
570
 
567
571
  class ForbiddenPage {
568
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: ForbiddenPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
569
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.8", type: ForbiddenPage, isStandalone: true, selector: "fhss-forbidden", ngImport: i0, template: "<byu-header />\n\n<div class=\"container\">\n <div class=\"not-found\">\n <img src=\"/police-duck.jpg\" alt=\"\" />\n <h1>403 - Forbidden</h1>\n <p>You don't have permission to access this page.</p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>\n\n<byu-footer />\n", styles: [":host{height:100vh;display:flex;flex-direction:column}.container{flex:1;display:flex;align-items:center;justify-content:center}.container .not-found{text-align:center;padding-bottom:3em}.container .not-found img{width:25%}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatAnchor, selector: "a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: ByuHeaderComponent, selector: "byu-header", inputs: ["config"] }, { kind: "component", type: ByuFooterComponent, selector: "byu-footer" }] });
572
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: ForbiddenPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
573
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.10", type: ForbiddenPage, isStandalone: true, selector: "fhss-forbidden", ngImport: i0, template: "<byu-header />\n\n<div class=\"container\">\n <div class=\"not-found\">\n <img src=\"/police-duck.jpg\" alt=\"\" />\n <h1>403 - Forbidden</h1>\n <p>You don't have permission to access this page.</p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>\n\n<byu-footer />\n", styles: [":host{height:100vh;display:flex;flex-direction:column}.container{flex:1;display:flex;align-items:center;justify-content:center}.container .not-found{text-align:center;padding-bottom:3em}.container .not-found img{width:25%}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatAnchor, selector: "a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: ByuHeaderComponent, selector: "byu-header", inputs: ["config"] }, { kind: "component", type: ByuFooterComponent, selector: "byu-footer" }] });
570
574
  }
571
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: ForbiddenPage, decorators: [{
575
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: ForbiddenPage, decorators: [{
572
576
  type: Component,
573
577
  args: [{ selector: 'fhss-forbidden', imports: [MatButtonModule, ByuHeaderComponent, ByuFooterComponent], template: "<byu-header />\n\n<div class=\"container\">\n <div class=\"not-found\">\n <img src=\"/police-duck.jpg\" alt=\"\" />\n <h1>403 - Forbidden</h1>\n <p>You don't have permission to access this page.</p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>\n\n<byu-footer />\n", styles: [":host{height:100vh;display:flex;flex-direction:column}.container{flex:1;display:flex;align-items:center;justify-content:center}.container .not-found{text-align:center;padding-bottom:3em}.container .not-found img{width:25%}\n"] }]
574
578
  }] });
575
579
 
576
580
  class NotFoundPage {
577
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: NotFoundPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
578
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.8", type: NotFoundPage, isStandalone: true, selector: "fhss-not-found", ngImport: i0, template: "<byu-header />\n\n<div class=\"container\">\n <div class=\"not-found\">\n <img src=\"/confused-duck.png\" alt=\"\" />\n <h1>404 - Page not found</h1>\n <p>The page you are looking for doesn't exist or it may have moved.</p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>\n\n<byu-footer />\n", styles: [":host{height:100vh;display:flex;flex-direction:column}.container{flex:1;display:flex;align-items:center;justify-content:center}.container .not-found{text-align:center;padding-bottom:3em}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatAnchor, selector: "a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: ByuHeaderComponent, selector: "byu-header", inputs: ["config"] }, { kind: "component", type: ByuFooterComponent, selector: "byu-footer" }] });
581
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: NotFoundPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
582
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.10", type: NotFoundPage, isStandalone: true, selector: "fhss-not-found", ngImport: i0, template: "<byu-header />\n\n<div class=\"container\">\n <div class=\"not-found\">\n <img src=\"/confused-duck.png\" alt=\"\" />\n <h1>404 - Page not found</h1>\n <p>The page you are looking for doesn't exist or it may have moved.</p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>\n\n<byu-footer />\n", styles: [":host{height:100vh;display:flex;flex-direction:column}.container{flex:1;display:flex;align-items:center;justify-content:center}.container .not-found{text-align:center;padding-bottom:3em}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatAnchor, selector: "a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: ByuHeaderComponent, selector: "byu-header", inputs: ["config"] }, { kind: "component", type: ByuFooterComponent, selector: "byu-footer" }] });
579
583
  }
580
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: NotFoundPage, decorators: [{
584
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: NotFoundPage, decorators: [{
581
585
  type: Component,
582
586
  args: [{ selector: 'fhss-not-found', imports: [MatButtonModule, ByuHeaderComponent, ByuFooterComponent], template: "<byu-header />\n\n<div class=\"container\">\n <div class=\"not-found\">\n <img src=\"/confused-duck.png\" alt=\"\" />\n <h1>404 - Page not found</h1>\n <p>The page you are looking for doesn't exist or it may have moved.</p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>\n\n<byu-footer />\n", styles: [":host{height:100vh;display:flex;flex-direction:column}.container{flex:1;display:flex;align-items:center;justify-content:center}.container .not-found{text-align:center;padding-bottom:3em}\n"] }]
583
587
  }] });
@@ -590,5 +594,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImpor
590
594
  * Generated bundle index. Do not edit.
591
595
  */
592
596
 
593
- export { AuthCallbackPage, AuthErrorPage, AuthService, ByuFooterComponent, ByuHeaderComponent, FHSS_CONFIG, ForbiddenPage, NotFoundPage, UserManagementComponent, authGuard, debounced, fetchSignal, provideFhss, readMaybeSignal, roleGuard };
597
+ export { AuthCallbackPage, AuthErrorPage, AuthService, ByuFooterComponent, ByuHeaderComponent, FHSS_CONFIG, ForbiddenPage, NotFoundPage, UserManagementComponent, authGuard, debounced, enumToRecord, fetchSignal, provideFhss, readMaybeSignal, roleGuard };
594
598
  //# sourceMappingURL=fhss-web-team-frontend-utils.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"fhss-web-team-frontend-utils.mjs","sources":["../../../projects/frontend-utils/src/lib/components/byu-footer/byu-footer.component.ts","../../../projects/frontend-utils/src/lib/components/byu-footer/byu-footer.component.html","../../../projects/frontend-utils/src/lib/config/lib.config.ts","../../../projects/frontend-utils/src/lib/services/auth/auth.service.ts","../../../projects/frontend-utils/src/lib/components/byu-header/byu-header.component.ts","../../../projects/frontend-utils/src/lib/components/byu-header/byu-header.component.html","../../../projects/frontend-utils/src/lib/signals/debounced/debounced.ts","../../../projects/frontend-utils/src/lib/signals/fetch-signal/fetch-signal.ts","../../../projects/frontend-utils/src/lib/components/user-management/user-management.service.ts","../../../projects/frontend-utils/src/lib/components/user-management/user-management.component.ts","../../../projects/frontend-utils/src/lib/components/user-management/user-management.component.html","../../../projects/frontend-utils/src/lib/guards/auth/auth.guard.ts","../../../projects/frontend-utils/src/lib/guards/role/role.guard.ts","../../../projects/frontend-utils/src/lib/pages/auth-callback/auth-callback.page.ts","../../../projects/frontend-utils/src/lib/pages/auth-callback/auth-callback.page.html","../../../projects/frontend-utils/src/lib/pages/auth-error/auth-error.page.ts","../../../projects/frontend-utils/src/lib/pages/auth-error/auth-error.page.html","../../../projects/frontend-utils/src/lib/pages/forbidden/forbidden.page.ts","../../../projects/frontend-utils/src/lib/pages/forbidden/forbidden.page.html","../../../projects/frontend-utils/src/lib/pages/not-found/not-found.page.ts","../../../projects/frontend-utils/src/lib/pages/not-found/not-found.page.html","../../../projects/frontend-utils/src/public-api.ts","../../../projects/frontend-utils/src/fhss-web-team-frontend-utils.ts"],"sourcesContent":["import { Component } from '@angular/core';\n\n@Component({\n selector: 'byu-footer',\n imports: [],\n templateUrl: './byu-footer.component.html',\n styleUrl: './byu-footer.component.scss'\n})\nexport class ByuFooterComponent {\n currentYear: number = new Date().getFullYear(); // Automatically updates the year\n}\n","<footer>\n <p class=\"title\"><a href=\"https://www.byu.edu/\">BRIGHAM YOUNG UNIVERSITY</a></p>\n <p>Provo, UT 84602, USA | © {{ currentYear }} All rights reserved.</p>\n <p>\n <a href=\"https://privacy.byu.edu/privacy-notice\">Privacy Notice</a> |\n <a href=\"https://privacy.byu.edu/cookie-prefs\">Cookie Preferences</a>\n </p>\n</footer>\n \n\n","import { InjectionToken, Provider } from '@angular/core';\n\nexport interface FhssConfig {\n roles: Record<string, number>;\n roleHomePages: Record<string, string>;\n}\n\nexport const FHSS_CONFIG = new InjectionToken<FhssConfig>('FHSS_CONFIG');\n\nexport const provideFhss = (config: FhssConfig): Provider => ({\n provide: FHSS_CONFIG,\n useValue: config,\n});\n","import { Injectable, inject, computed } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { KEYCLOAK_EVENT_SIGNAL } from 'keycloak-angular';\nimport Keycloak, {\n KeycloakFlow,\n KeycloakResourceAccess,\n KeycloakResponseMode,\n KeycloakResponseType,\n KeycloakRoles,\n KeycloakTokenParsed,\n} from 'keycloak-js';\nimport { FHSS_CONFIG } from '../../config/lib.config';\n\n@Injectable({\n providedIn: 'root',\n})\n/**\n * AuthService provides a wrapper around the Keycloak JavaScript adapter, exposing its properties and methods\n * as Angular signals for reactive programming. It simplifies authentication, token management, and role-based access control.\n */\nexport class AuthService {\n private keycloak = inject(Keycloak);\n private keycloakSignal = inject(KEYCLOAK_EVENT_SIGNAL);\n private router = inject(Router);\n private config = inject(FHSS_CONFIG);\n\n private readonly nextUriKey = 'nextUri';\n\n /**\n * Initiates the login process. Optionally stores a route to redirect to after login.\n * @param nextUri - The route to navigate to after successful login.\n */\n login = (nextUri?: string) => {\n if (nextUri) {\n sessionStorage.setItem(this.nextUriKey, nextUri);\n }\n this.keycloak.login({\n redirectUri: window.location.origin + '/auth-callback',\n });\n };\n\n /**\n * Gets the user's role home page\n * \n * @returns the user's role home page\n */\n getRoleHomePage = () => {\n if (!this.authenticated()) return '/';\n\n const userRoles = new Set(this.realmAccess()?.roles);\n for (const role in this.config.roleHomePages) {\n if (userRoles.has(role)) {\n return this.config.roleHomePages[role];\n }\n }\n return '/';\n };\n\n /**\n * Handles user provisioning.\n *\n * @returns the uri to which the application should navigate\n */\n handleAuthCallback = async (): Promise<void> => {\n if (!this.authenticated()) {\n sessionStorage.removeItem(this.nextUriKey);\n this.router.navigate(['/']);\n return;\n }\n\n const res = await fetch('/api/auth/callback', {\n headers: { Authorization: this.bearerToken() ?? '' },\n });\n\n if (!res.ok) {\n this.logout('/auth-error');\n return;\n }\n\n const nextRoute = sessionStorage.getItem(this.nextUriKey);\n sessionStorage.removeItem(this.nextUriKey);\n\n this.router.navigate([nextRoute ?? this.getRoleHomePage()]);\n };\n\n /**\n * Logs the user out of the application.\n */\n logout = (nextUri?: string) =>\n this.keycloak.logout({\n redirectUri: window.location.origin + (nextUri ?? '/'),\n });\n\n /**\n * Signal indicating whether the user is authenticated.\n * Returns `true` if authenticated, `false` otherwise.\n */\n authenticated = computed<boolean | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.authenticated;\n });\n\n /**\n * Signal for the base64-encoded token used in the `Authorization` header.\n */\n token = computed<string | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.token;\n });\n\n /**\n * Signal for the Bearer token, prefixed with \"Bearer \".\n */\n bearerToken = computed<string | undefined>(() =>\n this.token() ? 'Bearer ' + this.token() : undefined\n );\n\n /**\n * Signal for the parsed token as a JavaScript object.\n */\n tokenParsed = computed<KeycloakTokenParsed | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.tokenParsed;\n });\n\n /**\n * Signal for the user ID (Keycloak subject).\n */\n userId = computed<string | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.subject;\n });\n\n /**\n * Signal for the base64-encoded ID token.\n */\n idToken = computed<string | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.idToken;\n });\n\n /**\n * Signal for the parsed ID token as a JavaScript object.\n */\n idTokenParsed = computed<KeycloakTokenParsed | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.idTokenParsed;\n });\n\n /**\n * Signal for the realm roles associated with the token.\n */\n realmAccess = computed<KeycloakRoles | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.realmAccess;\n });\n\n /**\n * Signal for the resource roles associated with the token.\n */\n resourceAccess = computed<KeycloakResourceAccess | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.resourceAccess;\n });\n\n /**\n * Signal for the base64-encoded refresh token.\n */\n refreshToken = computed<string | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.refreshToken;\n });\n\n /**\n * Signal for the parsed refresh token as a JavaScript object.\n */\n refreshTokenParsed = computed<KeycloakTokenParsed | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.refreshTokenParsed;\n });\n\n /**\n * Signal for the estimated time difference between the browser and Keycloak server in seconds.\n */\n timeSkew = computed<number | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.timeSkew;\n });\n\n /**\n * Signal for the response mode passed during initialization.\n */\n responseMode = computed<KeycloakResponseMode | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.responseMode;\n });\n\n /**\n * Signal for the flow type used during initialization.\n */\n flow = computed<KeycloakFlow | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.flow;\n });\n\n /**\n * Signal for the response type sent to Keycloak during login requests.\n */\n responseType = computed<KeycloakResponseType | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.responseType;\n });\n}\n","import { Component, input, inject } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { AuthService } from '../../services/auth/auth.service';\n\ntype HeaderLink = {\n text: string;\n path: string;\n};\n\n// A HeaderMenu can be either a simple link with path,\n// OR a menu group with nested items\ntype HeaderMenu = HeaderLink | {\n text: string;\n items: HeaderLink[];\n}\n\nexport type HeaderConfig = {\n title: HeaderLink;\n subtitle?: HeaderLink;\n breadcrumbs?: HeaderLink[];\n menu?: HeaderMenu[];\n}\n\n@Component({\n selector: 'byu-header',\n imports: [RouterModule],\n templateUrl: './byu-header.component.html',\n styleUrl: './byu-header.component.scss'\n})\nexport class ByuHeaderComponent {\n auth = inject(AuthService)\n config = input<HeaderConfig>();\n\n isHeaderLink(item: HeaderMenu): item is HeaderLink {\n return 'path' in item;\n }\n\n // Track which dropdown is open (null means none are open)\n openDropdownText: string | null = null;\n \n // Toggle function — if clicking the same dropdown, close it; otherwise open it\n toggleDropdown(text: string) {\n this.openDropdownText = this.openDropdownText === text ? null : text;\n }\n \n // Check if a given dropdown is currently open\n isOpen(text: string): boolean {\n return this.openDropdownText === text;\n }\n}\n","<header>\n <div class=\"top\" >\n <img class=\"logo\" src=\"/BYU_monogram_white@2x.png\" alt=\"BYU\">\n <div class=\"titles\"> \n <div class=\"breadcrumbs\">\n @for (breadcrumb of config()?.breadcrumbs; track breadcrumb.text){\n <a [href]=\"breadcrumb.path\" >{{ breadcrumb.text }}</a>\n }\n </div>\n <a [routerLink]=\"config()?.title?.path\" class=\"title\">{{ config()?.title?.text }}</a>\n <a [routerLink]=\"config()?.subtitle?.path\" class=\"subtitle\">{{ config()?.subtitle?.text }}</a>\n </div>\n <div class=\"signin\">\n <p>{{ this.auth.tokenParsed()?.['given_name'] ?? '' }}</p>\n <svg class=\"signin-icon\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\">\n <path fill=\"currentcolor\" d=\"M50 95c-26 0-34-18-34-18 3-12 8-18 17-18 5 5 10 7 17 7s12-2 17-7c9 0 14 6 17 18 0 0-7 18-34 18z\"></path>\n <circle cx=\"50\" cy=\"50\" r=\"45\" fill=\"none\" stroke=\"currentcolor\" stroke-width=\"10\"></circle>\n <circle fill=\"currentcolor\" cx=\"50\" cy=\"40\" r=\"20\"></circle>\n </svg>\n @if (auth.authenticated()) {\n <a class=\"signin-link\" (click)=\"auth.logout()\">Sign Out</a>\n } @else {\n <a class=\"signin-link\" (click)=\"auth.login()\">Sign In</a>\n }\n </div>\n </div>\n <div class=\"bottom\">\n <nav>\n @for (menuItem of config()?.menu; track menuItem.text){>\n @if (isHeaderLink(menuItem)) {\n <li class = \"nav-item\">\n <a class=\"nav-item-content\" [routerLink]=\"menuItem.path\">{{ menuItem.text }}</a>\n </li>\n } @else {\n <li class=\"nav-item dropdown\" (click)=\"toggleDropdown(menuItem.text)\">\n <div class=\"nav-item-content\" >{{ menuItem.text }}</div>\n @if (openDropdownText === menuItem.text) {\n <ul class = \"dropdown-item-menu\"> \n @for(dropItem of menuItem.items; track dropItem.text){\n <li class = \"dropdown-item\">\n <a class=\"dropdown-item-content\" [routerLink]=\"dropItem.path\">{{ dropItem.text }}</a>\n </li>\n }\n </ul>\n }\n </li>\n }\n }\n </nav>\n </div>\n</header>\n","import { effect, signal, Signal } from \"@angular/core\";\n\nexport const debounced = <T>(inputSignal: Signal<T>, wait: number = 400) => {\n const debouncedSignal = signal<T>(inputSignal());\n const setSignal = debounce((value) => debouncedSignal.set(value), wait);\n\n effect(() => {\n setSignal(inputSignal())\n })\n\n return debouncedSignal;\n}\n\nconst debounce = (callback: (...args: any[]) => void, wait: number) => {\n let timeoutId: number | undefined;\n return (...args: any[]) => {\n window.clearTimeout(timeoutId);\n timeoutId = window.setTimeout(() => {\n callback(...args);\n }, wait);\n };\n}","import { computed, effect, signal, Signal } from '@angular/core';\n\n//\n// Generic utility types\n//\ntype JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonArray\n | JsonObject;\nexport type JsonObject = { [key: string]: JsonValue }; // this is exported because the builder's typing gets mad if it isn't\ntype JsonArray = JsonValue[];\ntype Json = JsonObject | JsonArray;\nexport type Maybe<T> = T | undefined | null;\ntype HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\ntype DefaultError = { code?: string, message?: string, details?: Json };\n\n/**\n * A value that may be a plain value of type `T` or a reactive `Signal<T>`.\n *\n * This is useful in APIs that accept either static or reactive values,\n * allowing flexibility while maintaining type safety.\n *\n * Use `unwrapSignal` to extract the underlying value in a uniform way.\n *\n * @template T The underlying value type.\n */\nexport type MaybeSignal<T> = T | Signal<T>;\n/**\n * Reads the underlying value from a MaybeSignal.\n *\n * This function is designed for use with reactive APIs (like fetchSignal) where it's important\n * that Angular's computed methods register the dependency. If the input is a Signal, invoking it \n * not only returns its current value but also tracks the signal for reactivity. If the input is \n * a plain value, it simply returns that value.\n *\n * @template T The type of the value.\n * @param value A plain value or a Signal that yields the value.\n * @returns The current value, with dependency tracking enabled if the input is a Signal.\n */\nexport function readMaybeSignal<T>(value: MaybeSignal<T>): T {\n return typeof value === 'function' ? (value as Function)() : value;\n}\n\n//\n// Fetch Request\n//\ntype FetchSignalRequest = {\n url: string;\n body?: unknown;\n params?: Record<string, string | number | boolean | undefined>;\n headers?: Record<string, string>;\n};\n\n//\n// A function that transforms the raw fetch Response to the desired type.\n//\ntype ResponseTransformer<T> = (response: Response) => Promise<T>;\n\n/**\n * Represents the reactive result of an HTTP fetch request.\n */\nexport type FetchSignal<Response, Error = DefaultError> = {\n value: Signal<Maybe<Response>>;\n persistentValue: Signal<Maybe<Response>>;\n isLoading: Signal<boolean>;\n error: Signal<Maybe<Error>>;\n statusCode: Signal<Maybe<number>>;\n headers: Signal<Maybe<Record<string, string>>>;\n refresh: (abortSignal?: AbortSignal) => void;\n};\n\n/**\n * Creates a reactive fetch signal.\n *\n * The request function can include Signals for properties. These are unwrapped in the refresh function.\n */\nfunction createFetchSignal<Response, Error>(\n request: () => (FetchSignalRequest),\n method: HttpMethod,\n transform: ResponseTransformer<Response>,\n autoRefresh: Boolean\n): FetchSignal<Response, Error> {\n // Use a computed signal so that any changes to Signals in the request object trigger updates.\n const currentRequest = computed(request);\n\n const value = signal<Maybe<Response>>(undefined);\n const persistentValue = signal<Maybe<Response>>(undefined);\n const isLoading = signal<boolean>(false);\n const error = signal<Maybe<Error>>(undefined);\n const statusCode = signal<Maybe<number>>(undefined);\n const headers = signal<Maybe<Record<string, string>>>(undefined);\n\n const refresh = async (abortSignal?: AbortSignal) => {\n // Reset signals for a fresh request.\n value.set(undefined);\n isLoading.set(true);\n error.set(undefined);\n statusCode.set(undefined);\n headers.set(undefined);\n\n // Unwrap the current request.\n const req = currentRequest();\n const url = req.url;\n const params = req.params\n const requestHeaders = req.headers;\n const body = req.body;\n\n // Build URL with query parameters.\n let uri = url;\n if (params) {\n const searchParams = new URLSearchParams();\n for (const key in params) {\n if (params[key] !== undefined) searchParams.append(key, String(params[key]));\n }\n uri += (uri.includes('?') ? '&' : '?') + searchParams.toString();\n }\n\n try {\n const response = await fetch(uri, {\n method,\n headers: requestHeaders,\n // Only include a body if one is provided.\n body: body !== undefined ? JSON.stringify(body) : undefined,\n signal: abortSignal\n });\n\n statusCode.set(response.status);\n\n // Extract response headers.\n const headersObj: Record<string, string> = {};\n response.headers.forEach((val, key) => {\n headersObj[key] = val;\n });\n headers.set(headersObj);\n\n // if the response is ok, transform the body\n if (response.ok) {\n value.set(await transform(response));\n error.set(null);\n } else {\n // try to parse the error as json\n // set the error to null if this fails\n try { error.set(await response.json()) }\n catch { error.set(null) }\n finally { value.set(null) }\n }\n } catch (err: any) {\n // if the request is aborted, ignore the error\n if (err.name !== 'AbortError') {\n console.error(err);\n }\n error.set(null);\n value.set(null);\n } finally {\n persistentValue.set(value());\n isLoading.set(false);\n }\n };\n\n if (autoRefresh) {\n effect((onCleanup) => {\n // read the current request to trigger re-run of this effect\n currentRequest();\n\n // pass abort signal to refresh on cleanup of effect\n const controller = new AbortController();\n onCleanup(() => controller.abort());\n\n // call refresh with this abort controller\n refresh(controller.signal);\n })\n }\n\n return { value, persistentValue, isLoading, error, statusCode, headers, refresh };\n}\n\n\n//\n// The chainable API factory type.\n// Note that the default fetchSignal() (and its .json, etc.) are for GET,\n// while .post, .put, .patch require a request with a body.\n// The .delete chain is similar to GET in that no body is expected.\n//\ntype FetchSignalFactory = {\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object containing reactive signals for the response data, loading state, status code, error, headers, and a refresh method.\n */\n <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean): FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration and parses the response as JSON.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the JSON-parsed response.\n */\n json: <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration and parses the response as plain text.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the text response.\n */\n text: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<string, Error>;\n\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration and parses the response as a Blob.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the Blob response.\n */\n blob: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Blob, Error>;\n\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration and parses the response as an ArrayBuffer.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the ArrayBuffer response.\n */\n arrayBuffer: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<ArrayBuffer, Error>;\n\n get: {\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object containing reactive signals for the response data, loading state, status code, error, headers, and a refresh method.\n */\n <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean): FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration and parses the response as JSON.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the JSON-parsed response.\n */\n json: <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration and parses the response as plain text.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the text response.\n */\n text: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<string, Error>;\n\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration and parses the response as a Blob.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the Blob response.\n */\n blob: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Blob, Error>;\n\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration and parses the response as an ArrayBuffer.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the ArrayBuffer response.\n */\n arrayBuffer: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<ArrayBuffer, Error>;\n };\n\n post: {\n /**\n * Initiates a reactive HTTP POST request using the provided request configuration.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP POST request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object containing reactive signals for the response data, loading state, status code, error, headers, and a refresh method.\n */\n <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean): FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP POST request using the provided request configuration and parses the response as JSON.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP POST request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the JSON-parsed response.\n */\n json: <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP POST request using the provided request configuration and parses the response as plain text.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP POST request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the text response.\n */\n text: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<string, Error>;\n\n /**\n * Initiates a reactive HTTP POST request using the provided request configuration and parses the response as a Blob.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP POST request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the Blob response.\n */\n blob: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Blob, Error>;\n\n /**\n * Initiates a reactive HTTP POST request using the provided request configuration and parses the response as an ArrayBuffer.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP POST request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the ArrayBuffer response.\n */\n arrayBuffer: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<ArrayBuffer, Error>;\n };\n\n put: {\n /**\n * Initiates a reactive HTTP PUT request using the provided request configuration.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PUT request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object containing reactive signals for the response data, loading state, status code, error, headers, and a refresh method.\n */\n <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean): FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP PUT request using the provided request configuration and parses the response as JSON.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PUT request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the JSON-parsed response.\n */\n json: <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP PUT request using the provided request configuration and parses the response as plain text.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PUT request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the text response.\n */\n text: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<string, Error>;\n\n /**\n * Initiates a reactive HTTP PUT request using the provided request configuration and parses the response as a Blob.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PUT request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the Blob response.\n */\n blob: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Blob, Error>;\n\n /**\n * Initiates a reactive HTTP PUT request using the provided request configuration and parses the response as an ArrayBuffer.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PUT request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the ArrayBuffer response.\n */\n arrayBuffer: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<ArrayBuffer, Error>;\n };\n\n patch: {\n /**\n * Initiates a reactive HTTP PATCH request using the provided request configuration.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PATCH request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object containing reactive signals for the response data, loading state, status code, error, headers, and a refresh method.\n */\n <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean): FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP PATCH request using the provided request configuration and parses the response as JSON.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PATCH request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the JSON-parsed response.\n */\n json: <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP PATCH request using the provided request configuration and parses the response as plain text.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PATCH request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the text response.\n */\n text: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<string, Error>;\n\n /**\n * Initiates a reactive HTTP PATCH request using the provided request configuration and parses the response as a Blob.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PATCH request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the Blob response.\n */\n blob: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Blob, Error>;\n\n /**\n * Initiates a reactive HTTP PATCH request using the provided request configuration and parses the response as an ArrayBuffer.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PATCH request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the ArrayBuffer response.\n */\n arrayBuffer: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<ArrayBuffer, Error>;\n };\n\n delete: {\n /**\n * Initiates a reactive HTTP DELETE request using the provided request configuration.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP DELETE request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object containing reactive signals for the response data, loading state, status code, error, headers, and a refresh method.\n */\n <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean): FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP DELETE request using the provided request configuration and parses the response as JSON.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP DELETE request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the JSON-parsed response.\n */\n json: <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP DELETE request using the provided request configuration and parses the response as plain text.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP DELETE request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the text response.\n */\n text: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<string, Error>;\n\n /**\n * Initiates a reactive HTTP DELETE request using the provided request configuration and parses the response as a Blob.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP DELETE request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the Blob response.\n */\n blob: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Blob, Error>;\n\n /**\n * Initiates a reactive HTTP DELETE request using the provided request configuration and parses the response as an ArrayBuffer.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP DELETE request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the ArrayBuffer response.\n */\n arrayBuffer: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<ArrayBuffer, Error>;\n };\n};\n\n\n//\n// Helpers for attaching response transforms for GET/DELETE (which don’t include a body).\n//\nconst createHelper = <Response, Error>(\n method: HttpMethod,\n transform: ResponseTransformer<Response>\n) => (request: () => FetchSignalRequest, autoRefresh: boolean = false): FetchSignal<Response, Error> =>\n createFetchSignal<Response, Error>(request, method, transform, autoRefresh);\n\n// Transforms\nconst jsonTransformer = (response: Response) => response.json();\nconst textTransformer = (response: Response) => response.text();\nconst blobTransformer = (response: Response) => response.blob();\nconst arrayBufferTransformer = (response: Response) => response.arrayBuffer();\n\n//\n// Build the defaults - GET chain\n//\nconst fetchSignal = createHelper('GET', jsonTransformer) as FetchSignalFactory;\nfetchSignal.json = createHelper('GET', jsonTransformer);\nfetchSignal.text = createHelper('GET', textTransformer);\nfetchSignal.blob = createHelper('GET', blobTransformer);\nfetchSignal.arrayBuffer = createHelper('GET', arrayBufferTransformer);\n\n//\n// Build the GET chain\n//\nfetchSignal.get = createHelper('GET', jsonTransformer) as FetchSignalFactory['get'];\nfetchSignal.get.json = createHelper('GET', jsonTransformer);\nfetchSignal.get.text = createHelper('GET', textTransformer);\nfetchSignal.get.blob = createHelper('GET', blobTransformer);\nfetchSignal.get.arrayBuffer = createHelper('GET', arrayBufferTransformer);\n\n//\n// Build the POST chain.\n//\nfetchSignal.post = createHelper('POST', jsonTransformer) as FetchSignalFactory['post'];\nfetchSignal.post.json = createHelper('POST', jsonTransformer);\nfetchSignal.post.text = createHelper('POST', textTransformer);\nfetchSignal.post.blob = createHelper('POST', blobTransformer);\nfetchSignal.post.arrayBuffer = createHelper('POST', arrayBufferTransformer);\n\n//\n// Build the PUT chain.\n//\nfetchSignal.put = createHelper('PUT', jsonTransformer) as FetchSignalFactory['put'];\nfetchSignal.put.json = createHelper('PUT', jsonTransformer);\nfetchSignal.put.text = createHelper('PUT', textTransformer);\nfetchSignal.put.blob = createHelper('PUT', blobTransformer);\nfetchSignal.put.arrayBuffer = createHelper('PUT', arrayBufferTransformer);\n\n//\n// Build the PATCH chain.\n//\nfetchSignal.patch = createHelper('PATCH', jsonTransformer) as FetchSignalFactory['patch'];\nfetchSignal.patch.json = createHelper('PATCH', jsonTransformer);\nfetchSignal.patch.text = createHelper('PATCH', textTransformer);\nfetchSignal.patch.blob = createHelper('PATCH', blobTransformer);\nfetchSignal.patch.arrayBuffer = createHelper('PATCH', arrayBufferTransformer);\n\n//\n// Build the DELETE chain\n//\nfetchSignal.delete = createHelper('DELETE', jsonTransformer) as FetchSignalFactory['delete'];\nfetchSignal.delete.json = createHelper('DELETE', jsonTransformer);\nfetchSignal.delete.text = createHelper('DELETE', textTransformer);\nfetchSignal.delete.blob = createHelper('DELETE', blobTransformer);\nfetchSignal.delete.arrayBuffer = createHelper('DELETE', arrayBufferTransformer);\n\nexport { fetchSignal };\n","import { inject, Injectable, Signal } from '@angular/core';\nimport { AuthService } from '../../services/auth/auth.service';\nimport { fetchSignal } from '../../signals/fetch-signal/fetch-signal';\n\nexport type GetUsersResponse = {\n totalCount: number,\n data: {\n id: string;\n netId: string;\n accountType: string;\n preferredFirstName: string;\n preferredLastName: string;\n roles: string[];\n created: string;\n }[]\n}\n\nexport type GetUsersAccountTypes = ('NonBYU' | 'Student' | 'Employee')[]\nexport type GetUsersSortBy = 'netId' | 'accountType' | 'preferredFirstName' | 'preferredLastName' | 'created';\nexport type GetUsersSortDirection = 'asc' | 'desc' | '';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class UserManagementService {\n auth = inject(AuthService);\n\n getUsers(\n search: Signal<string>,\n accountTypes: Signal<GetUsersAccountTypes>,\n sortBy: Signal<GetUsersSortBy>,\n sortDirection: Signal<GetUsersSortDirection>,\n pageCount: Signal<number>,\n pageOffset: Signal<number>,\n createdAfter?: Signal<Date>,\n createdBefore?: Signal<Date>,\n ) {\n return fetchSignal<GetUsersResponse>(() => ({\n url: '/api/user-management',\n params: {\n search: search(),\n account_types: accountTypes().join(','),\n sort_by: sortBy(),\n sort_direction: sortDirection(),\n page_count: pageCount(),\n page_offset: pageOffset(),\n created_after: createdAfter && createdAfter().toISOString(),\n created_before: createdBefore && createdBefore().toISOString(),\n },\n headers: {\n Authorization: this.auth.bearerToken() ?? ''\n }\n }), true)\n }\n}\n","import { Component, inject, signal, ViewChild } from '@angular/core';\nimport { MatTableModule } from '@angular/material/table';\nimport { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';\nimport { MatSort, MatSortModule } from '@angular/material/sort';\nimport { DatePipe } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { MatInputModule } from '@angular/material/input';\nimport { MatSelectModule } from '@angular/material/select';\nimport { debounced } from '../../signals/debounced/debounced';\nimport { UserManagementService } from './user-management.service';\nimport { GetUsersAccountTypes, GetUsersSortBy, GetUsersSortDirection } from './user-management.types';\n\n@Component({\n selector: 'app-user-management',\n imports: [MatTableModule, MatSortModule, MatPaginatorModule, DatePipe, FormsModule, MatInputModule, MatSelectModule],\n templateUrl: './user-management.component.html',\n styleUrl: './user-management.component.scss'\n})\nexport class UserManagementComponent {\n api = inject(UserManagementService);\n\n displayedColumns: string[] = ['netId', 'preferredFirstName', 'preferredLastName', 'roles', 'accountType', 'created'];\n accountTypeOptions = ['NonBYU', 'Student', 'Employee'];\n\n @ViewChild(MatPaginator) paginator!: MatPaginator;\n @ViewChild(MatSort) sort!: MatSort;\n\n search = signal('');\n accountTypes = signal<GetUsersAccountTypes>(['NonBYU', 'Student', 'Employee']);\n sortBy = signal<GetUsersSortBy>('netId');\n sortDirection = signal<GetUsersSortDirection>('asc');\n pageCount = signal(5);\n pageOffset = signal(0);\n\n getUsers = this.api.getUsers(\n debounced(this.search),\n this.accountTypes,\n this.sortBy,\n this.sortDirection,\n this.pageCount,\n this.pageOffset\n )\n\n ngAfterViewInit() {\n // write the observables from the sort and the paging to the signals\n this.sort.sortChange.subscribe(({active, direction}) => {\n this.sortBy.set(active as GetUsersSortBy);\n this.sortDirection.set(direction);\n this.pageOffset.set(0);\n this.paginator.pageIndex = 0;\n });\n this.paginator.page.subscribe(({pageIndex, pageSize}) => {\n this.pageOffset.set(pageIndex * pageSize)\n this.pageCount.set(pageSize)\n });\n }\n}\n","<mat-form-field>\n <mat-label>Search for User</mat-label>\n <input matInput [(ngModel)]=\"search\">\n</mat-form-field>\n<mat-form-field>\n <mat-label>Account Type</mat-label>\n <mat-select multiple [(ngModel)]=\"accountTypes\">\n @for (accountType of accountTypeOptions; track accountType) {\n <mat-option [value]=\"accountType\">{{accountType}}</mat-option>\n }\n </mat-select>\n</mat-form-field>\n\n<table mat-table [dataSource]=\"getUsers.persistentValue()?.data ?? []\" matSort matSortActive=\"netId\" matSortDisableClear matSortDirection=\"asc\">\n <!-- NetID Column -->\n <ng-container matColumnDef=\"netId\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>NetID</th>\n <td mat-cell *matCellDef=\"let user\">{{user.netId}}</td>\n </ng-container>\n\n <!-- First Name Column -->\n <ng-container matColumnDef=\"preferredFirstName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>First Name</th>\n <td mat-cell *matCellDef=\"let user\">{{user.preferredFirstName}}</td>\n </ng-container>\n\n <!-- Last Name Column -->\n <ng-container matColumnDef=\"preferredLastName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>Last Name</th>\n <td mat-cell *matCellDef=\"let user\">{{user.preferredLastName}}</td>\n </ng-container>\n\n <!-- Roles Column -->\n <ng-container matColumnDef=\"roles\">\n <th mat-header-cell *matHeaderCellDef>Roles</th>\n <td mat-cell *matCellDef=\"let user\">{{user.roles.join(', ')}}</td>\n </ng-container>\n\n <!-- Account Type Column -->\n <ng-container matColumnDef=\"accountType\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>Account Type</th>\n <td mat-cell *matCellDef=\"let user\">{{user.accountType}}</td>\n </ng-container>\n\n <!-- Created Column -->\n <ng-container matColumnDef=\"created\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n Created\n </th>\n <td mat-cell *matCellDef=\"let user\">{{user.created | date}}</td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n</table>\n\n<mat-paginator [length]=\"getUsers.persistentValue()?.totalCount\" [pageSize]=\"pageCount()\" [pageSizeOptions]=\"[5, 10, 20]\" showFirstLastButtons></mat-paginator>\n","import { inject } from '@angular/core';\nimport { CanActivateFn } from '@angular/router';\nimport { AuthService } from '../../../public-api';\n\n/**\n * This guard checks if the client is authenticated, redirecting to login if not.\n */\nexport const authGuard: CanActivateFn = (route, state) => {\n const authService = inject(AuthService);\n if(!authService.authenticated()){\n authService.login(state.url);\n return false;\n }\n return true;\n};\n","import { CanActivateFn, RedirectCommand, Router } from '@angular/router';\nimport { FHSS_CONFIG } from '../../config/lib.config';\nimport { AuthService } from '../../services/auth/auth.service';\nimport { inject } from '@angular/core';\n\n/**\n * A guard function to determine if a user has the required roles to access a route.\n * \n * This guard requires the use of the authGuard before it. It also requires the \n * route's `allowedRoles` to be defined. See the example below.\n *\n * @example\n * ```typescript\n * const routes: Routes = [\n * {\n * path: 'admin',\n * component: AdminPage,\n * canActivate: [authGuard, roleGuard],\n * data: { allowedRoles: [Roles.admin] },\n * },\n * ];\n * ```\n */\nexport const roleGuard: CanActivateFn = (route, state) => {\n const roles = inject(FHSS_CONFIG).roles;\n const authService = inject(AuthService);\n const router = inject(Router);\n\n const userRoles = authService.realmAccess()?.roles;\n const allowedRoles: number[] = route.data['allowedRoles'] ?? [];\n const hasRole =\n userRoles?.some((role) => allowedRoles.includes(roles[role])) ?? false;\n\n return hasRole || new RedirectCommand(router.parseUrl('/forbidden'));\n};\n","import { Component, inject, signal } from '@angular/core';\nimport { AuthService } from '../../services/auth/auth.service';\nimport { Router } from '@angular/router';\n\n@Component({\n selector: 'app-auth-callback',\n imports: [],\n templateUrl: './auth-callback.page.html',\n styleUrl: './auth-callback.page.scss',\n})\nexport class AuthCallbackPage {\n auth = inject(AuthService)\n router = inject(Router)\n\n message = signal('Logging you in...')\n\n constructor() {\n this.auth.handleAuthCallback();\n }\n\n goHome() {\n this.router.navigate(['/'])\n }\n}\n","<div class=\"container\">\n <p>Logging you in...</p>\n</div>","import { Component } from '@angular/core';\nimport { ByuFooterComponent, ByuHeaderComponent } from '../../../public-api';\nimport { MatButtonModule } from '@angular/material/button';\n\n@Component({\n selector: 'fhss-auth-error',\n imports: [MatButtonModule, ByuHeaderComponent, ByuFooterComponent],\n templateUrl: './auth-error.page.html',\n styleUrl: './auth-error.page.scss'\n})\nexport class AuthErrorPage {\n\n}\n","<byu-header />\n\n<div class=\"container\">\n <div class=\"not-found\">\n <img src=\"/sad-duck.jpg\" alt=\"sad duck\" />\n <h1>Login Failed</h1>\n <p>Something went wrong while trying to log you in.</p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>\n\n<byu-footer />\n","import { Component } from '@angular/core';\nimport { MatButtonModule } from '@angular/material/button';\nimport { ByuFooterComponent, ByuHeaderComponent } from '../../../public-api';\n\n@Component({\n selector: 'fhss-forbidden',\n imports: [MatButtonModule, ByuHeaderComponent, ByuFooterComponent],\n templateUrl: './forbidden.page.html',\n styleUrl: './forbidden.page.scss',\n})\nexport class ForbiddenPage {}\n","<byu-header />\n\n<div class=\"container\">\n <div class=\"not-found\">\n <img src=\"/police-duck.jpg\" alt=\"\" />\n <h1>403 - Forbidden</h1>\n <p>You don't have permission to access this page.</p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>\n\n<byu-footer />\n","import { Component } from '@angular/core';\nimport { MatButtonModule } from '@angular/material/button';\nimport { ByuFooterComponent, ByuHeaderComponent } from '../../../public-api';\n\n@Component({\n selector: 'fhss-not-found',\n imports: [MatButtonModule, ByuHeaderComponent, ByuFooterComponent],\n templateUrl: './not-found.page.html',\n styleUrl: './not-found.page.scss',\n})\nexport class NotFoundPage {}\n","<byu-header />\n\n<div class=\"container\">\n <div class=\"not-found\">\n <img src=\"/confused-duck.png\" alt=\"\" />\n <h1>404 - Page not found</h1>\n <p>The page you are looking for doesn't exist or it may have moved.</p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>\n\n<byu-footer />\n","/**\n * Components\n */\nexport * from './lib/components/byu-footer/byu-footer.component'\nexport * from './lib/components/byu-header/byu-header.component'\nexport * from './lib/components/user-management/user-management.component'\n\n/**\n * Config\n */\nexport * from './lib/config/lib.config'\n\n/**\n * Guards\n */\nexport * from './lib/guards/auth/auth.guard'\nexport * from './lib/guards/role/role.guard'\n\n/**\n * Pages\n */\nexport * from './lib/pages/auth-callback/auth-callback.page'\nexport * from './lib/pages/auth-error/auth-error.page'\nexport * from './lib/pages/forbidden/forbidden.page'\nexport * from './lib/pages/not-found/not-found.page'\n\n/**\n * Services\n */\nexport * from './lib/services/auth/auth.service'\n\n/**\n * Signals\n */\nexport * from './lib/signals/fetch-signal/fetch-signal'\nexport * from './lib/signals/debounced/debounced'","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1"],"mappings":"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"}
1
+ {"version":3,"file":"fhss-web-team-frontend-utils.mjs","sources":["../../../projects/frontend-utils/src/lib/components/byu-footer/byu-footer.component.ts","../../../projects/frontend-utils/src/lib/components/byu-footer/byu-footer.component.html","../../../projects/frontend-utils/src/lib/config/lib.config.ts","../../../projects/frontend-utils/src/lib/services/auth/auth.service.ts","../../../projects/frontend-utils/src/lib/components/byu-header/byu-header.component.ts","../../../projects/frontend-utils/src/lib/components/byu-header/byu-header.component.html","../../../projects/frontend-utils/src/lib/signals/debounced/debounced.ts","../../../projects/frontend-utils/src/lib/signals/fetch-signal/fetch-signal.ts","../../../projects/frontend-utils/src/lib/components/user-management/user-management.service.ts","../../../projects/frontend-utils/src/lib/components/user-management/user-management.component.ts","../../../projects/frontend-utils/src/lib/components/user-management/user-management.component.html","../../../projects/frontend-utils/src/lib/config/utils.config.ts","../../../projects/frontend-utils/src/lib/guards/auth/auth.guard.ts","../../../projects/frontend-utils/src/lib/guards/role/role.guard.ts","../../../projects/frontend-utils/src/lib/pages/auth-callback/auth-callback.page.ts","../../../projects/frontend-utils/src/lib/pages/auth-callback/auth-callback.page.html","../../../projects/frontend-utils/src/lib/pages/auth-error/auth-error.page.ts","../../../projects/frontend-utils/src/lib/pages/auth-error/auth-error.page.html","../../../projects/frontend-utils/src/lib/pages/forbidden/forbidden.page.ts","../../../projects/frontend-utils/src/lib/pages/forbidden/forbidden.page.html","../../../projects/frontend-utils/src/lib/pages/not-found/not-found.page.ts","../../../projects/frontend-utils/src/lib/pages/not-found/not-found.page.html","../../../projects/frontend-utils/src/public-api.ts","../../../projects/frontend-utils/src/fhss-web-team-frontend-utils.ts"],"sourcesContent":["import { Component } from '@angular/core';\n\n@Component({\n selector: 'byu-footer',\n imports: [],\n templateUrl: './byu-footer.component.html',\n styleUrl: './byu-footer.component.scss'\n})\nexport class ByuFooterComponent {\n currentYear: number = new Date().getFullYear(); // Automatically updates the year\n}\n","<footer>\n <p class=\"title\"><a href=\"https://www.byu.edu/\">BRIGHAM YOUNG UNIVERSITY</a></p>\n <p>Provo, UT 84602, USA | © {{ currentYear }} All rights reserved.</p>\n <p>\n <a href=\"https://privacy.byu.edu/privacy-notice\">Privacy Notice</a> |\n <a href=\"https://privacy.byu.edu/cookie-prefs\">Cookie Preferences</a>\n </p>\n</footer>\n \n\n","import { InjectionToken } from '@angular/core';\n\nexport const FHSS_CONFIG = new InjectionToken<FhssConfig>('FHSS_CONFIG');\n\nexport interface FhssConfig {\n roles: Record<string, number>;\n roleHomePages: Record<string, string>;\n}\n","import { Injectable, inject, computed } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { KEYCLOAK_EVENT_SIGNAL } from 'keycloak-angular';\nimport Keycloak, {\n KeycloakFlow,\n KeycloakResourceAccess,\n KeycloakResponseMode,\n KeycloakResponseType,\n KeycloakRoles,\n KeycloakTokenParsed,\n} from 'keycloak-js';\nimport { FHSS_CONFIG } from '../../config/lib.config';\n\n@Injectable({\n providedIn: 'root',\n})\n/**\n * AuthService provides a wrapper around the Keycloak JavaScript adapter, exposing its properties and methods\n * as Angular signals for reactive programming. It simplifies authentication, token management, and role-based access control.\n */\nexport class AuthService {\n private keycloak = inject(Keycloak);\n private keycloakSignal = inject(KEYCLOAK_EVENT_SIGNAL);\n private router = inject(Router);\n private config = inject(FHSS_CONFIG);\n\n private readonly nextUriKey = 'nextUri';\n\n /**\n * Initiates the login process. Optionally stores a route to redirect to after login.\n * @param nextUri - The route to navigate to after successful login.\n */\n login = (nextUri?: string) => {\n if (nextUri) {\n sessionStorage.setItem(this.nextUriKey, nextUri);\n }\n this.keycloak.login({\n redirectUri: window.location.origin + '/auth-callback',\n });\n };\n\n /**\n * Gets the user's role home page\n * \n * @returns the user's role home page\n */\n getRoleHomePage = () => {\n if (!this.authenticated()) return '/';\n\n const userRoles = new Set(this.realmAccess()?.roles);\n for (const role in this.config.roleHomePages) {\n if (userRoles.has(role)) {\n return this.config.roleHomePages[role];\n }\n }\n return '/';\n };\n\n /**\n * Handles user provisioning.\n *\n * @returns the uri to which the application should navigate\n */\n handleAuthCallback = async (): Promise<void> => {\n if (!this.authenticated()) {\n sessionStorage.removeItem(this.nextUriKey);\n this.router.navigate(['/']);\n return;\n }\n\n const res = await fetch('/api/auth/callback', {\n headers: { Authorization: this.bearerToken() ?? '' },\n });\n\n if (!res.ok) {\n this.logout('/auth-error');\n return;\n }\n\n const nextRoute = sessionStorage.getItem(this.nextUriKey);\n sessionStorage.removeItem(this.nextUriKey);\n\n this.router.navigate([nextRoute ?? this.getRoleHomePage()]);\n };\n\n /**\n * Logs the user out of the application.\n */\n logout = (nextUri?: string) =>\n this.keycloak.logout({\n redirectUri: window.location.origin + (nextUri ?? '/'),\n });\n\n /**\n * Signal indicating whether the user is authenticated.\n * Returns `true` if authenticated, `false` otherwise.\n */\n authenticated = computed<boolean | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.authenticated;\n });\n\n /**\n * Signal for the base64-encoded token used in the `Authorization` header.\n */\n token = computed<string | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.token;\n });\n\n /**\n * Signal for the Bearer token, prefixed with \"Bearer \".\n */\n bearerToken = computed<string | undefined>(() =>\n this.token() ? 'Bearer ' + this.token() : undefined\n );\n\n /**\n * Signal for the parsed token as a JavaScript object.\n */\n tokenParsed = computed<KeycloakTokenParsed | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.tokenParsed;\n });\n\n /**\n * Signal for the user ID (Keycloak subject).\n */\n userId = computed<string | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.subject;\n });\n\n /**\n * Signal for the base64-encoded ID token.\n */\n idToken = computed<string | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.idToken;\n });\n\n /**\n * Signal for the parsed ID token as a JavaScript object.\n */\n idTokenParsed = computed<KeycloakTokenParsed | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.idTokenParsed;\n });\n\n /**\n * Signal for the realm roles associated with the token.\n */\n realmAccess = computed<KeycloakRoles | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.realmAccess;\n });\n\n /**\n * Signal for the resource roles associated with the token.\n */\n resourceAccess = computed<KeycloakResourceAccess | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.resourceAccess;\n });\n\n /**\n * Signal for the base64-encoded refresh token.\n */\n refreshToken = computed<string | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.refreshToken;\n });\n\n /**\n * Signal for the parsed refresh token as a JavaScript object.\n */\n refreshTokenParsed = computed<KeycloakTokenParsed | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.refreshTokenParsed;\n });\n\n /**\n * Signal for the estimated time difference between the browser and Keycloak server in seconds.\n */\n timeSkew = computed<number | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.timeSkew;\n });\n\n /**\n * Signal for the response mode passed during initialization.\n */\n responseMode = computed<KeycloakResponseMode | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.responseMode;\n });\n\n /**\n * Signal for the flow type used during initialization.\n */\n flow = computed<KeycloakFlow | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.flow;\n });\n\n /**\n * Signal for the response type sent to Keycloak during login requests.\n */\n responseType = computed<KeycloakResponseType | undefined>(() => {\n this.keycloakSignal();\n return this.keycloak.responseType;\n });\n}\n","import { Component, input, inject } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { AuthService } from '../../services/auth/auth.service';\n\ntype HeaderLink = {\n text: string;\n path: string;\n};\n\n// A HeaderMenu can be either a simple link with path,\n// OR a menu group with nested items\ntype HeaderMenu = HeaderLink | {\n text: string;\n items: HeaderLink[];\n}\n\nexport type HeaderConfig = {\n title: HeaderLink;\n subtitle?: HeaderLink;\n breadcrumbs?: HeaderLink[];\n menu?: HeaderMenu[];\n}\n\n@Component({\n selector: 'byu-header',\n imports: [RouterModule],\n templateUrl: './byu-header.component.html',\n styleUrl: './byu-header.component.scss'\n})\nexport class ByuHeaderComponent {\n auth = inject(AuthService)\n config = input<HeaderConfig>();\n\n isHeaderLink(item: HeaderMenu): item is HeaderLink {\n return 'path' in item;\n }\n\n // Track which dropdown is open (null means none are open)\n openDropdownText: string | null = null;\n \n // Toggle function — if clicking the same dropdown, close it; otherwise open it\n toggleDropdown(text: string) {\n this.openDropdownText = this.openDropdownText === text ? null : text;\n }\n \n // Check if a given dropdown is currently open\n isOpen(text: string): boolean {\n return this.openDropdownText === text;\n }\n}\n","<header>\n <div class=\"top\" >\n <img class=\"logo\" src=\"/BYU_monogram_white@2x.png\" alt=\"BYU\">\n <div class=\"titles\"> \n <div class=\"breadcrumbs\">\n @for (breadcrumb of config()?.breadcrumbs; track breadcrumb.text){\n <a [href]=\"breadcrumb.path\" >{{ breadcrumb.text }}</a>\n }\n </div>\n <a [routerLink]=\"config()?.title?.path\" class=\"title\">{{ config()?.title?.text }}</a>\n <a [routerLink]=\"config()?.subtitle?.path\" class=\"subtitle\">{{ config()?.subtitle?.text }}</a>\n </div>\n <div class=\"signin\">\n <p>{{ this.auth.tokenParsed()?.['given_name'] ?? '' }}</p>\n <svg class=\"signin-icon\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\">\n <path fill=\"currentcolor\" d=\"M50 95c-26 0-34-18-34-18 3-12 8-18 17-18 5 5 10 7 17 7s12-2 17-7c9 0 14 6 17 18 0 0-7 18-34 18z\"></path>\n <circle cx=\"50\" cy=\"50\" r=\"45\" fill=\"none\" stroke=\"currentcolor\" stroke-width=\"10\"></circle>\n <circle fill=\"currentcolor\" cx=\"50\" cy=\"40\" r=\"20\"></circle>\n </svg>\n @if (auth.authenticated()) {\n <a class=\"signin-link\" (click)=\"auth.logout()\">Sign Out</a>\n } @else {\n <a class=\"signin-link\" (click)=\"auth.login()\">Sign In</a>\n }\n </div>\n </div>\n <div class=\"bottom\">\n <nav>\n @for (menuItem of config()?.menu; track menuItem.text){>\n @if (isHeaderLink(menuItem)) {\n <li class = \"nav-item\">\n <a class=\"nav-item-content\" [routerLink]=\"menuItem.path\">{{ menuItem.text }}</a>\n </li>\n } @else {\n <li class=\"nav-item dropdown\" (click)=\"toggleDropdown(menuItem.text)\">\n <div class=\"nav-item-content\" >{{ menuItem.text }}</div>\n @if (openDropdownText === menuItem.text) {\n <ul class = \"dropdown-item-menu\"> \n @for(dropItem of menuItem.items; track dropItem.text){\n <li class = \"dropdown-item\">\n <a class=\"dropdown-item-content\" [routerLink]=\"dropItem.path\">{{ dropItem.text }}</a>\n </li>\n }\n </ul>\n }\n </li>\n }\n }\n </nav>\n </div>\n</header>\n","import { effect, signal, Signal } from \"@angular/core\";\n\nexport const debounced = <T>(inputSignal: Signal<T>, wait: number = 400) => {\n const debouncedSignal = signal<T>(inputSignal());\n const setSignal = debounce((value) => debouncedSignal.set(value), wait);\n\n effect(() => {\n setSignal(inputSignal())\n })\n\n return debouncedSignal;\n}\n\nconst debounce = (callback: (...args: any[]) => void, wait: number) => {\n let timeoutId: number | undefined;\n return (...args: any[]) => {\n window.clearTimeout(timeoutId);\n timeoutId = window.setTimeout(() => {\n callback(...args);\n }, wait);\n };\n}","import { computed, effect, signal, Signal } from '@angular/core';\n\n//\n// Generic utility types\n//\ntype JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonArray\n | JsonObject;\nexport type JsonObject = { [key: string]: JsonValue }; // this is exported because the builder's typing gets mad if it isn't\ntype JsonArray = JsonValue[];\ntype Json = JsonObject | JsonArray;\nexport type Maybe<T> = T | undefined | null;\ntype HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\ntype DefaultError = { code?: string, message?: string, details?: Json };\n\n/**\n * A value that may be a plain value of type `T` or a reactive `Signal<T>`.\n *\n * This is useful in APIs that accept either static or reactive values,\n * allowing flexibility while maintaining type safety.\n *\n * Use `unwrapSignal` to extract the underlying value in a uniform way.\n *\n * @template T The underlying value type.\n */\nexport type MaybeSignal<T> = T | Signal<T>;\n/**\n * Reads the underlying value from a MaybeSignal.\n *\n * This function is designed for use with reactive APIs (like fetchSignal) where it's important\n * that Angular's computed methods register the dependency. If the input is a Signal, invoking it \n * not only returns its current value but also tracks the signal for reactivity. If the input is \n * a plain value, it simply returns that value.\n *\n * @template T The type of the value.\n * @param value A plain value or a Signal that yields the value.\n * @returns The current value, with dependency tracking enabled if the input is a Signal.\n */\nexport function readMaybeSignal<T>(value: MaybeSignal<T>): T {\n return typeof value === 'function' ? (value as Function)() : value;\n}\n\n//\n// Fetch Request\n//\ntype FetchSignalRequest = {\n url: string;\n body?: unknown;\n params?: Record<string, string | number | boolean | undefined>;\n headers?: Record<string, string>;\n};\n\n//\n// A function that transforms the raw fetch Response to the desired type.\n//\ntype ResponseTransformer<T> = (response: Response) => Promise<T>;\n\n/**\n * Represents the reactive result of an HTTP fetch request.\n */\nexport type FetchSignal<Response, Error = DefaultError> = {\n value: Signal<Maybe<Response>>;\n persistentValue: Signal<Maybe<Response>>;\n isLoading: Signal<boolean>;\n error: Signal<Maybe<Error>>;\n statusCode: Signal<Maybe<number>>;\n headers: Signal<Maybe<Record<string, string>>>;\n refresh: (abortSignal?: AbortSignal) => void;\n};\n\n/**\n * Creates a reactive fetch signal.\n *\n * The request function can include Signals for properties. These are unwrapped in the refresh function.\n */\nfunction createFetchSignal<Response, Error>(\n request: () => (FetchSignalRequest),\n method: HttpMethod,\n transform: ResponseTransformer<Response>,\n autoRefresh: Boolean\n): FetchSignal<Response, Error> {\n // Use a computed signal so that any changes to Signals in the request object trigger updates.\n const currentRequest = computed(request);\n\n const value = signal<Maybe<Response>>(undefined);\n const persistentValue = signal<Maybe<Response>>(undefined);\n const isLoading = signal<boolean>(false);\n const error = signal<Maybe<Error>>(undefined);\n const statusCode = signal<Maybe<number>>(undefined);\n const headers = signal<Maybe<Record<string, string>>>(undefined);\n\n const refresh = async (abortSignal?: AbortSignal) => {\n // Reset signals for a fresh request.\n value.set(undefined);\n isLoading.set(true);\n error.set(undefined);\n statusCode.set(undefined);\n headers.set(undefined);\n\n // Unwrap the current request.\n const req = currentRequest();\n const url = req.url;\n const params = req.params\n const requestHeaders = req.headers;\n const body = req.body;\n\n // Build URL with query parameters.\n let uri = url;\n if (params) {\n const searchParams = new URLSearchParams();\n for (const key in params) {\n if (params[key] !== undefined) searchParams.append(key, String(params[key]));\n }\n uri += (uri.includes('?') ? '&' : '?') + searchParams.toString();\n }\n\n try {\n const response = await fetch(uri, {\n method,\n headers: requestHeaders,\n // Only include a body if one is provided.\n body: body !== undefined ? JSON.stringify(body) : undefined,\n signal: abortSignal\n });\n\n statusCode.set(response.status);\n\n // Extract response headers.\n const headersObj: Record<string, string> = {};\n response.headers.forEach((val, key) => {\n headersObj[key] = val;\n });\n headers.set(headersObj);\n\n // if the response is ok, transform the body\n if (response.ok) {\n value.set(await transform(response));\n error.set(null);\n } else {\n // try to parse the error as json\n // set the error to null if this fails\n try { error.set(await response.json()) }\n catch { error.set(null) }\n finally { value.set(null) }\n }\n } catch (err: any) {\n // if the request is aborted, ignore the error\n if (err.name !== 'AbortError') {\n console.error(err);\n }\n error.set(null);\n value.set(null);\n } finally {\n persistentValue.set(value());\n isLoading.set(false);\n }\n };\n\n if (autoRefresh) {\n effect((onCleanup) => {\n // read the current request to trigger re-run of this effect\n currentRequest();\n\n // pass abort signal to refresh on cleanup of effect\n const controller = new AbortController();\n onCleanup(() => controller.abort());\n\n // call refresh with this abort controller\n refresh(controller.signal);\n })\n }\n\n return { value, persistentValue, isLoading, error, statusCode, headers, refresh };\n}\n\n\n//\n// The chainable API factory type.\n// Note that the default fetchSignal() (and its .json, etc.) are for GET,\n// while .post, .put, .patch require a request with a body.\n// The .delete chain is similar to GET in that no body is expected.\n//\ntype FetchSignalFactory = {\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object containing reactive signals for the response data, loading state, status code, error, headers, and a refresh method.\n */\n <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean): FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration and parses the response as JSON.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the JSON-parsed response.\n */\n json: <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration and parses the response as plain text.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the text response.\n */\n text: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<string, Error>;\n\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration and parses the response as a Blob.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the Blob response.\n */\n blob: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Blob, Error>;\n\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration and parses the response as an ArrayBuffer.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the ArrayBuffer response.\n */\n arrayBuffer: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<ArrayBuffer, Error>;\n\n get: {\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object containing reactive signals for the response data, loading state, status code, error, headers, and a refresh method.\n */\n <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean): FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration and parses the response as JSON.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the JSON-parsed response.\n */\n json: <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration and parses the response as plain text.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the text response.\n */\n text: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<string, Error>;\n\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration and parses the response as a Blob.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the Blob response.\n */\n blob: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Blob, Error>;\n\n /**\n * Initiates a reactive HTTP GET request using the provided request configuration and parses the response as an ArrayBuffer.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP GET request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the ArrayBuffer response.\n */\n arrayBuffer: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<ArrayBuffer, Error>;\n };\n\n post: {\n /**\n * Initiates a reactive HTTP POST request using the provided request configuration.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP POST request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object containing reactive signals for the response data, loading state, status code, error, headers, and a refresh method.\n */\n <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean): FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP POST request using the provided request configuration and parses the response as JSON.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP POST request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the JSON-parsed response.\n */\n json: <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP POST request using the provided request configuration and parses the response as plain text.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP POST request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the text response.\n */\n text: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<string, Error>;\n\n /**\n * Initiates a reactive HTTP POST request using the provided request configuration and parses the response as a Blob.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP POST request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the Blob response.\n */\n blob: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Blob, Error>;\n\n /**\n * Initiates a reactive HTTP POST request using the provided request configuration and parses the response as an ArrayBuffer.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP POST request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the ArrayBuffer response.\n */\n arrayBuffer: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<ArrayBuffer, Error>;\n };\n\n put: {\n /**\n * Initiates a reactive HTTP PUT request using the provided request configuration.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PUT request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object containing reactive signals for the response data, loading state, status code, error, headers, and a refresh method.\n */\n <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean): FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP PUT request using the provided request configuration and parses the response as JSON.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PUT request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the JSON-parsed response.\n */\n json: <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP PUT request using the provided request configuration and parses the response as plain text.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PUT request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the text response.\n */\n text: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<string, Error>;\n\n /**\n * Initiates a reactive HTTP PUT request using the provided request configuration and parses the response as a Blob.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PUT request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the Blob response.\n */\n blob: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Blob, Error>;\n\n /**\n * Initiates a reactive HTTP PUT request using the provided request configuration and parses the response as an ArrayBuffer.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PUT request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the ArrayBuffer response.\n */\n arrayBuffer: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<ArrayBuffer, Error>;\n };\n\n patch: {\n /**\n * Initiates a reactive HTTP PATCH request using the provided request configuration.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PATCH request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object containing reactive signals for the response data, loading state, status code, error, headers, and a refresh method.\n */\n <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean): FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP PATCH request using the provided request configuration and parses the response as JSON.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PATCH request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the JSON-parsed response.\n */\n json: <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP PATCH request using the provided request configuration and parses the response as plain text.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PATCH request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the text response.\n */\n text: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<string, Error>;\n\n /**\n * Initiates a reactive HTTP PATCH request using the provided request configuration and parses the response as a Blob.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PATCH request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the Blob response.\n */\n blob: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Blob, Error>;\n\n /**\n * Initiates a reactive HTTP PATCH request using the provided request configuration and parses the response as an ArrayBuffer.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP PATCH request, which must include a body.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the ArrayBuffer response.\n */\n arrayBuffer: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<ArrayBuffer, Error>;\n };\n\n delete: {\n /**\n * Initiates a reactive HTTP DELETE request using the provided request configuration.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP DELETE request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object containing reactive signals for the response data, loading state, status code, error, headers, and a refresh method.\n */\n <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean): FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP DELETE request using the provided request configuration and parses the response as JSON.\n *\n * @template Response - The expected response type.\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP DELETE request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the JSON-parsed response.\n */\n json: <Response extends Json, Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Response, Error>;\n\n /**\n * Initiates a reactive HTTP DELETE request using the provided request configuration and parses the response as plain text.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP DELETE request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the text response.\n */\n text: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<string, Error>;\n\n /**\n * Initiates a reactive HTTP DELETE request using the provided request configuration and parses the response as a Blob.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP DELETE request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the Blob response.\n */\n blob: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<Blob, Error>;\n\n /**\n * Initiates a reactive HTTP DELETE request using the provided request configuration and parses the response as an ArrayBuffer.\n *\n * @template Error - The expected error shape (defaults to `DefaultError`).\n * @param request - The configuration object for the HTTP DELETE request.\n * @param autoRefresh - whether or not the request should be automatically refreshed when the request input signals have changed value\n * @returns A `FetchSignal` object with the ArrayBuffer response.\n */\n arrayBuffer: <Error extends Json = DefaultError>(request: () => FetchSignalRequest, autoRefresh?: boolean) => FetchSignal<ArrayBuffer, Error>;\n };\n};\n\n\n//\n// Helpers for attaching response transforms for GET/DELETE (which don’t include a body).\n//\nconst createHelper = <Response, Error>(\n method: HttpMethod,\n transform: ResponseTransformer<Response>\n) => (request: () => FetchSignalRequest, autoRefresh: boolean = false): FetchSignal<Response, Error> =>\n createFetchSignal<Response, Error>(request, method, transform, autoRefresh);\n\n// Transforms\nconst jsonTransformer = (response: Response) => response.json();\nconst textTransformer = (response: Response) => response.text();\nconst blobTransformer = (response: Response) => response.blob();\nconst arrayBufferTransformer = (response: Response) => response.arrayBuffer();\n\n//\n// Build the defaults - GET chain\n//\nconst fetchSignal = createHelper('GET', jsonTransformer) as FetchSignalFactory;\nfetchSignal.json = createHelper('GET', jsonTransformer);\nfetchSignal.text = createHelper('GET', textTransformer);\nfetchSignal.blob = createHelper('GET', blobTransformer);\nfetchSignal.arrayBuffer = createHelper('GET', arrayBufferTransformer);\n\n//\n// Build the GET chain\n//\nfetchSignal.get = createHelper('GET', jsonTransformer) as FetchSignalFactory['get'];\nfetchSignal.get.json = createHelper('GET', jsonTransformer);\nfetchSignal.get.text = createHelper('GET', textTransformer);\nfetchSignal.get.blob = createHelper('GET', blobTransformer);\nfetchSignal.get.arrayBuffer = createHelper('GET', arrayBufferTransformer);\n\n//\n// Build the POST chain.\n//\nfetchSignal.post = createHelper('POST', jsonTransformer) as FetchSignalFactory['post'];\nfetchSignal.post.json = createHelper('POST', jsonTransformer);\nfetchSignal.post.text = createHelper('POST', textTransformer);\nfetchSignal.post.blob = createHelper('POST', blobTransformer);\nfetchSignal.post.arrayBuffer = createHelper('POST', arrayBufferTransformer);\n\n//\n// Build the PUT chain.\n//\nfetchSignal.put = createHelper('PUT', jsonTransformer) as FetchSignalFactory['put'];\nfetchSignal.put.json = createHelper('PUT', jsonTransformer);\nfetchSignal.put.text = createHelper('PUT', textTransformer);\nfetchSignal.put.blob = createHelper('PUT', blobTransformer);\nfetchSignal.put.arrayBuffer = createHelper('PUT', arrayBufferTransformer);\n\n//\n// Build the PATCH chain.\n//\nfetchSignal.patch = createHelper('PATCH', jsonTransformer) as FetchSignalFactory['patch'];\nfetchSignal.patch.json = createHelper('PATCH', jsonTransformer);\nfetchSignal.patch.text = createHelper('PATCH', textTransformer);\nfetchSignal.patch.blob = createHelper('PATCH', blobTransformer);\nfetchSignal.patch.arrayBuffer = createHelper('PATCH', arrayBufferTransformer);\n\n//\n// Build the DELETE chain\n//\nfetchSignal.delete = createHelper('DELETE', jsonTransformer) as FetchSignalFactory['delete'];\nfetchSignal.delete.json = createHelper('DELETE', jsonTransformer);\nfetchSignal.delete.text = createHelper('DELETE', textTransformer);\nfetchSignal.delete.blob = createHelper('DELETE', blobTransformer);\nfetchSignal.delete.arrayBuffer = createHelper('DELETE', arrayBufferTransformer);\n\nexport { fetchSignal };\n","import { inject, Injectable, Signal } from '@angular/core';\nimport { AuthService } from '../../services/auth/auth.service';\nimport { fetchSignal } from '../../signals/fetch-signal/fetch-signal';\n\nexport type GetUsersResponse = {\n totalCount: number,\n data: {\n id: string;\n netId: string;\n accountType: string;\n preferredFirstName: string;\n preferredLastName: string;\n roles: string[];\n created: string;\n }[]\n}\n\nexport type GetUsersAccountTypes = ('NonBYU' | 'Student' | 'Employee')[]\nexport type GetUsersSortBy = 'netId' | 'accountType' | 'preferredFirstName' | 'preferredLastName' | 'created';\nexport type GetUsersSortDirection = 'asc' | 'desc' | '';\n\n@Injectable({\n providedIn: 'root'\n})\nexport class UserManagementService {\n auth = inject(AuthService);\n\n getUsers(\n search: Signal<string>,\n accountTypes: Signal<GetUsersAccountTypes>,\n sortBy: Signal<GetUsersSortBy>,\n sortDirection: Signal<GetUsersSortDirection>,\n pageCount: Signal<number>,\n pageOffset: Signal<number>,\n createdAfter?: Signal<Date>,\n createdBefore?: Signal<Date>,\n ) {\n return fetchSignal<GetUsersResponse>(() => ({\n url: '/api/user-management',\n params: {\n search: search(),\n account_types: accountTypes().join(','),\n sort_by: sortBy(),\n sort_direction: sortDirection(),\n page_count: pageCount(),\n page_offset: pageOffset(),\n created_after: createdAfter && createdAfter().toISOString(),\n created_before: createdBefore && createdBefore().toISOString(),\n },\n headers: {\n Authorization: this.auth.bearerToken() ?? ''\n }\n }), true)\n }\n}\n","import { Component, inject, signal, ViewChild } from '@angular/core';\nimport { MatTableModule } from '@angular/material/table';\nimport { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';\nimport { MatSort, MatSortModule } from '@angular/material/sort';\nimport { DatePipe } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { MatInputModule } from '@angular/material/input';\nimport { MatSelectModule } from '@angular/material/select';\nimport { debounced } from '../../signals/debounced/debounced';\nimport { UserManagementService } from './user-management.service';\nimport { GetUsersAccountTypes, GetUsersSortBy, GetUsersSortDirection } from './user-management.types';\n\n@Component({\n selector: 'app-user-management',\n imports: [MatTableModule, MatSortModule, MatPaginatorModule, DatePipe, FormsModule, MatInputModule, MatSelectModule],\n templateUrl: './user-management.component.html',\n styleUrl: './user-management.component.scss'\n})\nexport class UserManagementComponent {\n api = inject(UserManagementService);\n\n displayedColumns: string[] = ['netId', 'preferredFirstName', 'preferredLastName', 'roles', 'accountType', 'created'];\n accountTypeOptions = ['NonBYU', 'Student', 'Employee'];\n\n @ViewChild(MatPaginator) paginator!: MatPaginator;\n @ViewChild(MatSort) sort!: MatSort;\n\n search = signal('');\n accountTypes = signal<GetUsersAccountTypes>(['NonBYU', 'Student', 'Employee']);\n sortBy = signal<GetUsersSortBy>('netId');\n sortDirection = signal<GetUsersSortDirection>('asc');\n pageCount = signal(5);\n pageOffset = signal(0);\n\n getUsers = this.api.getUsers(\n debounced(this.search),\n this.accountTypes,\n this.sortBy,\n this.sortDirection,\n this.pageCount,\n this.pageOffset\n )\n\n ngAfterViewInit() {\n // write the observables from the sort and the paging to the signals\n this.sort.sortChange.subscribe(({active, direction}) => {\n this.sortBy.set(active as GetUsersSortBy);\n this.sortDirection.set(direction);\n this.pageOffset.set(0);\n this.paginator.pageIndex = 0;\n });\n this.paginator.page.subscribe(({pageIndex, pageSize}) => {\n this.pageOffset.set(pageIndex * pageSize)\n this.pageCount.set(pageSize)\n });\n }\n}\n","<mat-form-field>\n <mat-label>Search for User</mat-label>\n <input matInput [(ngModel)]=\"search\">\n</mat-form-field>\n<mat-form-field>\n <mat-label>Account Type</mat-label>\n <mat-select multiple [(ngModel)]=\"accountTypes\">\n @for (accountType of accountTypeOptions; track accountType) {\n <mat-option [value]=\"accountType\">{{accountType}}</mat-option>\n }\n </mat-select>\n</mat-form-field>\n\n<table mat-table [dataSource]=\"getUsers.persistentValue()?.data ?? []\" matSort matSortActive=\"netId\" matSortDisableClear matSortDirection=\"asc\">\n <!-- NetID Column -->\n <ng-container matColumnDef=\"netId\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>NetID</th>\n <td mat-cell *matCellDef=\"let user\">{{user.netId}}</td>\n </ng-container>\n\n <!-- First Name Column -->\n <ng-container matColumnDef=\"preferredFirstName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>First Name</th>\n <td mat-cell *matCellDef=\"let user\">{{user.preferredFirstName}}</td>\n </ng-container>\n\n <!-- Last Name Column -->\n <ng-container matColumnDef=\"preferredLastName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>Last Name</th>\n <td mat-cell *matCellDef=\"let user\">{{user.preferredLastName}}</td>\n </ng-container>\n\n <!-- Roles Column -->\n <ng-container matColumnDef=\"roles\">\n <th mat-header-cell *matHeaderCellDef>Roles</th>\n <td mat-cell *matCellDef=\"let user\">{{user.roles.join(', ')}}</td>\n </ng-container>\n\n <!-- Account Type Column -->\n <ng-container matColumnDef=\"accountType\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>Account Type</th>\n <td mat-cell *matCellDef=\"let user\">{{user.accountType}}</td>\n </ng-container>\n\n <!-- Created Column -->\n <ng-container matColumnDef=\"created\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n Created\n </th>\n <td mat-cell *matCellDef=\"let user\">{{user.created | date}}</td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n</table>\n\n<mat-paginator [length]=\"getUsers.persistentValue()?.totalCount\" [pageSize]=\"pageCount()\" [pageSizeOptions]=\"[5, 10, 20]\" showFirstLastButtons></mat-paginator>\n","import { Provider } from \"@angular/core\";\nimport { FHSS_CONFIG, FhssConfig } from \"./lib.config\";\n\nexport const provideFhss = (config: FhssConfig): Provider => ({\n provide: FHSS_CONFIG,\n useValue: config,\n});\n\nexport const enumToRecord = <T extends object>(enumInput: T): Record<string, number> => {\n return Object.fromEntries(Object.entries(enumInput).filter(([key, value]) => typeof value === 'number'))\n}\n","import { inject } from '@angular/core';\nimport { CanActivateFn } from '@angular/router';\nimport { AuthService } from '../../../public-api';\n\n/**\n * This guard checks if the client is authenticated, redirecting to login if not.\n */\nexport const authGuard: CanActivateFn = (route, state) => {\n const authService = inject(AuthService);\n if(!authService.authenticated()){\n authService.login(state.url);\n return false;\n }\n return true;\n};\n","import { CanActivateFn, RedirectCommand, Router } from '@angular/router';\nimport { FHSS_CONFIG } from '../../config/lib.config';\nimport { AuthService } from '../../services/auth/auth.service';\nimport { inject } from '@angular/core';\n\n/**\n * A guard function to determine if a user has the required roles to access a route.\n * \n * This guard requires the use of the authGuard before it. It also requires the \n * route's `allowedRoles` to be defined. See the example below.\n *\n * @example\n * ```typescript\n * const routes: Routes = [\n * {\n * path: 'admin',\n * component: AdminPage,\n * canActivate: [authGuard, roleGuard],\n * data: { allowedRoles: [Roles.admin] },\n * },\n * ];\n * ```\n */\nexport const roleGuard: CanActivateFn = (route, state) => {\n const roles = inject(FHSS_CONFIG).roles;\n const authService = inject(AuthService);\n const router = inject(Router);\n\n const userRoles = authService.realmAccess()?.roles;\n const allowedRoles: number[] = route.data['allowedRoles'] ?? [];\n const hasRole =\n userRoles?.some((role) => allowedRoles.includes(roles[role])) ?? false;\n\n return hasRole || new RedirectCommand(router.parseUrl('/forbidden'));\n};\n","import { Component, inject, signal } from '@angular/core';\nimport { AuthService } from '../../services/auth/auth.service';\nimport { Router } from '@angular/router';\n\n@Component({\n selector: 'app-auth-callback',\n imports: [],\n templateUrl: './auth-callback.page.html',\n styleUrl: './auth-callback.page.scss',\n})\nexport class AuthCallbackPage {\n auth = inject(AuthService)\n router = inject(Router)\n\n message = signal('Logging you in...')\n\n constructor() {\n this.auth.handleAuthCallback();\n }\n\n goHome() {\n this.router.navigate(['/'])\n }\n}\n","<div class=\"container\">\n <p>Logging you in...</p>\n</div>","import { Component } from '@angular/core';\nimport { ByuFooterComponent, ByuHeaderComponent } from '../../../public-api';\nimport { MatButtonModule } from '@angular/material/button';\n\n@Component({\n selector: 'fhss-auth-error',\n imports: [MatButtonModule, ByuHeaderComponent, ByuFooterComponent],\n templateUrl: './auth-error.page.html',\n styleUrl: './auth-error.page.scss'\n})\nexport class AuthErrorPage {\n\n}\n","<byu-header />\n\n<div class=\"container\">\n <div class=\"not-found\">\n <img src=\"/sad-duck.jpg\" alt=\"sad duck\" />\n <h1>Login Failed</h1>\n <p>Something went wrong while trying to log you in.</p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>\n\n<byu-footer />\n","import { Component } from '@angular/core';\nimport { MatButtonModule } from '@angular/material/button';\nimport { ByuFooterComponent, ByuHeaderComponent } from '../../../public-api';\n\n@Component({\n selector: 'fhss-forbidden',\n imports: [MatButtonModule, ByuHeaderComponent, ByuFooterComponent],\n templateUrl: './forbidden.page.html',\n styleUrl: './forbidden.page.scss',\n})\nexport class ForbiddenPage {}\n","<byu-header />\n\n<div class=\"container\">\n <div class=\"not-found\">\n <img src=\"/police-duck.jpg\" alt=\"\" />\n <h1>403 - Forbidden</h1>\n <p>You don't have permission to access this page.</p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>\n\n<byu-footer />\n","import { Component } from '@angular/core';\nimport { MatButtonModule } from '@angular/material/button';\nimport { ByuFooterComponent, ByuHeaderComponent } from '../../../public-api';\n\n@Component({\n selector: 'fhss-not-found',\n imports: [MatButtonModule, ByuHeaderComponent, ByuFooterComponent],\n templateUrl: './not-found.page.html',\n styleUrl: './not-found.page.scss',\n})\nexport class NotFoundPage {}\n","<byu-header />\n\n<div class=\"container\">\n <div class=\"not-found\">\n <img src=\"/confused-duck.png\" alt=\"\" />\n <h1>404 - Page not found</h1>\n <p>The page you are looking for doesn't exist or it may have moved.</p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>\n\n<byu-footer />\n","/**\n * Components\n */\nexport * from './lib/components/byu-footer/byu-footer.component'\nexport * from './lib/components/byu-header/byu-header.component'\nexport * from './lib/components/user-management/user-management.component'\n\n/**\n * Config\n */\nexport * from './lib/config/lib.config'\nexport * from './lib/config/utils.config'\n\n/**\n * Guards\n */\nexport * from './lib/guards/auth/auth.guard'\nexport * from './lib/guards/role/role.guard'\n\n/**\n * Pages\n */\nexport * from './lib/pages/auth-callback/auth-callback.page'\nexport * from './lib/pages/auth-error/auth-error.page'\nexport * from './lib/pages/forbidden/forbidden.page'\nexport * from './lib/pages/not-found/not-found.page'\n\n/**\n * Services\n */\nexport * from './lib/services/auth/auth.service'\n\n/**\n * Signals\n */\nexport * from './lib/signals/fetch-signal/fetch-signal'\nexport * from './lib/signals/debounced/debounced'","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":["i1"],"mappings":"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"}
@@ -1,7 +1,6 @@
1
- import { InjectionToken, Provider } from '@angular/core';
1
+ import { InjectionToken } from '@angular/core';
2
+ export declare const FHSS_CONFIG: InjectionToken<FhssConfig>;
2
3
  export interface FhssConfig {
3
4
  roles: Record<string, number>;
4
5
  roleHomePages: Record<string, string>;
5
6
  }
6
- export declare const FHSS_CONFIG: InjectionToken<FhssConfig>;
7
- export declare const provideFhss: (config: FhssConfig) => Provider;
@@ -0,0 +1,4 @@
1
+ import { Provider } from "@angular/core";
2
+ import { FhssConfig } from "./lib.config";
3
+ export declare const provideFhss: (config: FhssConfig) => Provider;
4
+ export declare const enumToRecord: <T extends object>(enumInput: T) => Record<string, number>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fhss-web-team/frontend-utils",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^19.2.0",
6
6
  "@angular/core": "^19.2.0"
package/public-api.d.ts CHANGED
@@ -8,6 +8,7 @@ export * from './lib/components/user-management/user-management.component';
8
8
  * Config
9
9
  */
10
10
  export * from './lib/config/lib.config';
11
+ export * from './lib/config/utils.config';
11
12
  /**
12
13
  * Guards
13
14
  */