@fhss-web-team/frontend-utils 2.3.4 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,6 +15,7 @@ npm install @fhss-web-team/frontend-utils
15
15
 
16
16
  - Components
17
17
  - [BYU Header](./src/lib/components/byu-header/byu-header.md)
18
+ - [FHSS Table](./src/lib/components/fhss-table/fhss-table.md)
18
19
  - Pages
19
20
  - Services
20
21
  - Guards
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Component, inject, signal, computed, Injectable, input, effect, Injector, model, linkedSignal, InjectionToken, untracked } from '@angular/core';
2
+ import { Component, InjectionToken, inject, signal, computed, Injectable, input, linkedSignal, effect, Injector, model, untracked } from '@angular/core';
3
3
  import * as i1 from '@angular/router';
4
4
  import { Router, RouterModule, RedirectCommand } from '@angular/router';
5
5
  import { isTRPCClientError } from '@trpc/client';
@@ -25,20 +25,23 @@ import * as i10 from '@angular/material/checkbox';
25
25
  import { MatCheckboxModule } from '@angular/material/checkbox';
26
26
  import { SelectionModel } from '@angular/cdk/collections';
27
27
  import * as i1$3 from '@angular/material/dialog';
28
- import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
28
+ import { MatDialogRef, MAT_DIALOG_DATA, MatDialog, MatDialogModule } from '@angular/material/dialog';
29
29
 
30
30
  class ByuFooterComponent {
31
31
  currentYear = new Date().getFullYear(); // Automatically updates the year
32
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: ByuFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
33
- 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"] });
32
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ByuFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
33
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", 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"] });
34
34
  }
35
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: ByuFooterComponent, decorators: [{
35
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ByuFooterComponent, decorators: [{
36
36
  type: Component,
37
37
  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"] }]
38
38
  }] });
39
39
 
40
+ const FHSS_CONFIG = new InjectionToken('FHSS_CONFIG');
41
+
40
42
  class AuthService {
41
43
  router = inject(Router);
44
+ FhssConfig = inject(FHSS_CONFIG);
42
45
  constructor() {
43
46
  this.whoAmI();
44
47
  }
@@ -46,8 +49,11 @@ class AuthService {
46
49
  userId = signal(undefined);
47
50
  preferredFirstName = signal(undefined);
48
51
  preferredLastName = signal(undefined);
49
- roles = signal(undefined);
50
- permissions = signal(undefined);
52
+ roles = signal([]);
53
+ permissions = signal([]);
54
+ effectivePermissions = computed(() => new Set(this.roles()
55
+ .flatMap((role) => this.FhssConfig.rolePermissionMap[role])
56
+ .concat(this.permissions())));
51
57
  preferredName = computed(() => {
52
58
  const prefLast = this.preferredLastName();
53
59
  return `${this.preferredFirstName() ?? ''}${prefLast ? ` ${prefLast}` : ''}`;
@@ -57,8 +63,8 @@ class AuthService {
57
63
  this.userId.set(undefined);
58
64
  this.preferredFirstName.set(undefined);
59
65
  this.preferredLastName.set(undefined);
60
- this.roles.set(undefined);
61
- this.permissions.set(undefined);
66
+ this.roles.set([]);
67
+ this.permissions.set([]);
62
68
  }
63
69
  setUser(user) {
64
70
  this.authenticated.set(true);
@@ -91,10 +97,10 @@ class AuthService {
91
97
  return null;
92
98
  }
93
99
  }
94
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
95
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AuthService, providedIn: 'root' });
100
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
101
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AuthService, providedIn: 'root' });
96
102
  }
97
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AuthService, decorators: [{
103
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AuthService, decorators: [{
98
104
  type: Injectable,
99
105
  args: [{
100
106
  providedIn: 'root',
@@ -103,31 +109,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImpo
103
109
 
104
110
  class ByuHeaderComponent {
105
111
  auth = inject(AuthService);
106
- config = input();
107
- isHeaderLink(item) {
112
+ config = input.required();
113
+ openDropdown = null;
114
+ isMenuLink(item) {
108
115
  return 'path' in item;
109
116
  }
110
- // Track which dropdown is open (null means none are open)
111
- openDropdownText = null;
112
- // Toggle function — if clicking the same dropdown, close it; otherwise open it
113
- toggleDropdown(text) {
114
- this.openDropdownText = this.openDropdownText === text ? null : text;
117
+ isMenuItemVisible(item) {
118
+ return item.requiredPermissions !== undefined
119
+ ? item.requiredPermissions.some((permission) => this.auth.effectivePermissions().has(permission))
120
+ : true;
115
121
  }
116
- // Check if a given dropdown is currently open
117
- isOpen(text) {
118
- return this.openDropdownText === text;
122
+ toggleDropdown(dropdown) {
123
+ this.openDropdown = this.openDropdown === dropdown ? null : dropdown;
119
124
  }
120
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: ByuHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
121
- 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.preferredName() }}</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"] }] });
125
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ByuHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
126
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: ByuHeaderComponent, isStandalone: true, selector: "byu-header", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, 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.preferredName() }}</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 (isMenuItemVisible(menuItem)) {\n @if (isMenuLink(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)\">\n <div class=\"nav-item-content\" >{{ menuItem.text }}</div>\n @if (openDropdown === menuItem) {\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 }\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"] }] });
122
127
  }
123
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: ByuHeaderComponent, decorators: [{
128
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ByuHeaderComponent, decorators: [{
124
129
  type: Component,
125
- 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.preferredName() }}</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"] }]
130
+ 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.preferredName() }}</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 (isMenuItemVisible(menuItem)) {\n @if (isMenuLink(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)\">\n <div class=\"nav-item-content\" >{{ menuItem.text }}</div>\n @if (openDropdown === menuItem) {\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 }\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"] }]
126
131
  }] });
127
132
 
128
133
  function trpcResource(procedure, input, options) {
129
134
  const currentInput = computed(input);
130
- const value = signal(options?.initialValue ?? options?.defaultValue, { equal: options?.equal });
135
+ const value = linkedSignal(options?.valueComputation ?? (() => options?.initialValue ?? options?.defaultValue), { equal: options?.equal });
131
136
  const error = signal(undefined);
132
137
  const isLoading = signal(false);
133
138
  if (options?.autoRefresh) {
@@ -304,10 +309,10 @@ class FhssTableComponent {
304
309
  return `${this.selection.isSelected(row) ? 'deselect' : 'select'} row ${row.position + 1}`;
305
310
  }
306
311
  isBoolean = (val) => typeof val === 'boolean';
307
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: FhssTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
308
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: FhssTableComponent, isStandalone: true, selector: "fhss-table", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, miscParams: { classPropertyName: "miscParams", publicName: "miscParams", isSignal: true, isRequired: false, transformFunction: null }, selectedValues: { classPropertyName: "selectedValues", publicName: "selectedValues", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedValues: "selectedValuesChange" }, ngImport: i0, template: "<div class=\"table-container\">\n @if (dataResource.isLoading() || dataResource.error()) {\n <div class=\"shade\">\n @if (dataResource.isLoading()) {\n <mat-spinner />\n }\n @if (dataResource.error()) {\n <div class=\"msg-bkgd\">\n <div class=\"error-msg\">There was an error retrieving this data</div>\n <button mat-button (click)=\"this.dataResource.refresh()\">\n Retry\n </button>\n </div>\n }\n </div>\n }\n\n @if (config().showSearch) {\n <mat-form-field class=\"search\" subscriptSizing=\"dynamic\">\n <input matInput [(ngModel)]=\"search\" placeholder=\"Find...\" />\n @if (search()) {\n <button\n matSuffix\n mat-icon-button\n aria-label=\"Clear\"\n (click)=\"search.set('')\"\n >\n <mat-icon>close</mat-icon>\n </button>\n }\n </mat-form-field>\n }\n\n <div class=\"other-controls\">\n <ng-content />\n </div>\n\n <table\n mat-table\n [dataSource]=\"dataResource.value()?.data ?? []\"\n matSort\n [matSortActive]=\"sortBy()\"\n [matSortDirection]=\"sortDirection()\"\n (matSortChange)=\"onSortChange($event)\"\n matSortDisableClear\n >\n @if (selection) {\n <ng-container matColumnDef=\"table-checkbox-column\">\n <mat-header-cell *matHeaderCellDef>\n @if (selection.isMultipleSelection()) {\n <mat-checkbox\n (change)=\"$event ? toggleAllRows() : null\"\n [checked]=\"selection.hasValue() && isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n [aria-label]=\"checkboxLabel()\"\n />\n }\n </mat-header-cell>\n <mat-cell *matCellDef=\"let row\">\n <mat-checkbox\n (click)=\"$event.stopPropagation()\"\n (change)=\"$event ? onRowClick(row) : null\"\n [checked]=\"selection.isSelected(row)\"\n [aria-label]=\"checkboxLabel(row)\"\n />\n </mat-cell>\n </ng-container>\n }\n\n @for (key of columnKeys; track key) {\n <ng-container [matColumnDef]=\"key\">\n <mat-header-cell\n *matHeaderCellDef\n mat-sort-header\n [disabled]=\"!config().columns[key].allowSort\"\n >\n <div class=\"header\">\n {{ config().columns[key].header || key }}\n @if (config().columns[key].individualFilter) {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <input\n matInput\n (click)=\"$event.stopPropagation()\"\n [ngModel]=\"filters()[key] || ''\"\n (ngModelChange)=\"onFilterChange(key, $event)\"\n placeholder=\"Filter...\"\n />\n @if (filters()[key]) {\n <button\n matSuffix\n mat-icon-button\n aria-label=\"Clear\"\n (click)=\"onFilterChange(key, '')\"\n >\n <mat-icon>close</mat-icon>\n </button>\n }\n </mat-form-field>\n }\n </div>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let row\">\n @if (isBoolean(row[key])) {\n <mat-checkbox [checked]=\"row[key]\" [disabled]=\"true\" />\n } @else {\n {{ row[key] }}\n }\n </mat-cell>\n </ng-container>\n }\n\n <mat-header-row *matHeaderRowDef=\"columnsToDisplay\"></mat-header-row>\n @if (config().interaction) {\n <mat-row\n matRipple\n (click)=\"onRowClick(row)\"\n *matRowDef=\"let row; columns: columnsToDisplay\"\n ></mat-row>\n } @else {\n <mat-row *matRowDef=\"let row; columns: columnsToDisplay\"></mat-row>\n }\n\n <tr class=\"mat-row\" *matNoDataRow>\n <td class=\"mat-cell\">\n <div class=\"no-data\">\n No data found\n <button mat-button (click)=\"this.dataResource.refresh()\">\n Retry\n </button>\n </div>\n </td>\n </tr>\n </table>\n\n @if (!config().pagination.hideControls) {\n <mat-paginator\n [length]=\"dataResource.value()?.totalCount\"\n [showFirstLastButtons]=\"true\"\n [pageSize]=\"config().pagination.pageSize\"\n [hidePageSize]=\"true\"\n [pageIndex]=\"pageIndex()\"\n [disabled]=\"dataResource.isLoading()\"\n (page)=\"onPaginationChange($event)\"\n />\n }\n</div>\n", styles: [".table-container{position:relative}.shade{position:absolute;inset:0;background:#00000026;z-index:99;display:flex;align-items:center;justify-content:center}.msg-bkgd{background:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:15px 15px 5px}.search{background:#faf9fd;width:100%}.other-controls:not(:empty){background:#faf9fd;padding:10px 0}.mat-column-table-checkbox-column{width:fit-content}.no-data{width:fit-content;margin:auto}\n"], 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: "directive", type: i1$1.MatNoDataRow, selector: "ng-template[matNoDataRow]" }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i2.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { 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: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.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: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i5.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i1$2.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i7.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i7.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatRippleModule }, { kind: "directive", type: i9.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i10.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }] });
312
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FhssTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
313
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: FhssTableComponent, isStandalone: true, selector: "fhss-table", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, miscParams: { classPropertyName: "miscParams", publicName: "miscParams", isSignal: true, isRequired: false, transformFunction: null }, selectedValues: { classPropertyName: "selectedValues", publicName: "selectedValues", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedValues: "selectedValuesChange" }, ngImport: i0, template: "<div class=\"table-container\">\n @if (dataResource.isLoading() || dataResource.error()) {\n <div class=\"shade\">\n @if (dataResource.isLoading()) {\n <mat-spinner />\n }\n @if (dataResource.error()) {\n <div class=\"msg-bkgd\">\n <div class=\"error-msg\">There was an error retrieving this data</div>\n <button mat-button (click)=\"this.dataResource.refresh()\">\n Retry\n </button>\n </div>\n }\n </div>\n }\n\n @if (config().showSearch) {\n <mat-form-field class=\"search\" subscriptSizing=\"dynamic\">\n <input matInput [(ngModel)]=\"search\" placeholder=\"Find...\" />\n @if (search()) {\n <button\n matSuffix\n mat-icon-button\n aria-label=\"Clear\"\n (click)=\"search.set('')\"\n >\n <mat-icon>close</mat-icon>\n </button>\n }\n </mat-form-field>\n }\n\n <div class=\"other-controls\">\n <ng-content />\n </div>\n\n <table\n mat-table\n [dataSource]=\"dataResource.value()?.data ?? []\"\n matSort\n [matSortActive]=\"sortBy()\"\n [matSortDirection]=\"sortDirection()\"\n (matSortChange)=\"onSortChange($event)\"\n matSortDisableClear\n >\n @if (selection) {\n <ng-container matColumnDef=\"table-checkbox-column\">\n <mat-header-cell *matHeaderCellDef>\n @if (selection.isMultipleSelection()) {\n <mat-checkbox\n (change)=\"$event ? toggleAllRows() : null\"\n [checked]=\"selection.hasValue() && isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n [aria-label]=\"checkboxLabel()\"\n />\n }\n </mat-header-cell>\n <mat-cell *matCellDef=\"let row\">\n <mat-checkbox\n (click)=\"$event.stopPropagation()\"\n (change)=\"$event ? onRowClick(row) : null\"\n [checked]=\"selection.isSelected(row)\"\n [aria-label]=\"checkboxLabel(row)\"\n />\n </mat-cell>\n </ng-container>\n }\n\n @for (key of columnKeys; track key) {\n <ng-container [matColumnDef]=\"key\">\n <mat-header-cell\n *matHeaderCellDef\n mat-sort-header\n [disabled]=\"!config().columns[key].allowSort\"\n >\n <div class=\"header\">\n {{ config().columns[key].header || key }}\n @if (config().columns[key].individualFilter) {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <input\n matInput\n (click)=\"$event.stopPropagation()\"\n [ngModel]=\"filters()[key] || ''\"\n (ngModelChange)=\"onFilterChange(key, $event)\"\n placeholder=\"Filter...\"\n />\n @if (filters()[key]) {\n <button\n matSuffix\n mat-icon-button\n aria-label=\"Clear\"\n (click)=\"onFilterChange(key, '')\"\n >\n <mat-icon>close</mat-icon>\n </button>\n }\n </mat-form-field>\n }\n </div>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let row\">\n @if (isBoolean(row[key])) {\n <mat-checkbox [checked]=\"row[key]\" [disabled]=\"true\" />\n } @else {\n {{ row[key] }}\n }\n </mat-cell>\n </ng-container>\n }\n\n <mat-header-row *matHeaderRowDef=\"columnsToDisplay\"></mat-header-row>\n @if (config().interaction) {\n <mat-row\n matRipple\n (click)=\"onRowClick(row)\"\n *matRowDef=\"let row; columns: columnsToDisplay\"\n ></mat-row>\n } @else {\n <mat-row *matRowDef=\"let row; columns: columnsToDisplay\"></mat-row>\n }\n\n <tr class=\"mat-row\" *matNoDataRow>\n <td class=\"mat-cell\">\n <div class=\"no-data\">\n No data found\n <button mat-button (click)=\"this.dataResource.refresh()\">\n Retry\n </button>\n </div>\n </td>\n </tr>\n </table>\n\n @if (!config().pagination.hideControls) {\n <mat-paginator\n [length]=\"dataResource.value()?.totalCount\"\n [showFirstLastButtons]=\"true\"\n [pageSize]=\"config().pagination.pageSize\"\n [hidePageSize]=\"true\"\n [pageIndex]=\"pageIndex()\"\n [disabled]=\"dataResource.isLoading()\"\n (page)=\"onPaginationChange($event)\"\n />\n }\n</div>\n", styles: [".table-container{position:relative}.shade{position:absolute;inset:0;background:#00000026;z-index:99;display:flex;align-items:center;justify-content:center}.msg-bkgd{background:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:15px 15px 5px}.search{background:#faf9fd;width:100%}.other-controls:not(:empty){background:#faf9fd;padding:10px 0}.mat-column-table-checkbox-column{width:fit-content}.no-data{width:fit-content;margin:auto}\n"], 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: "directive", type: i1$1.MatNoDataRow, selector: "ng-template[matNoDataRow]" }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i2.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { 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: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.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: i4.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i5.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i5.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i5.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i1$2.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i7.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i7.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatRippleModule }, { kind: "directive", type: i9.MatRipple, selector: "[mat-ripple], [matRipple]", inputs: ["matRippleColor", "matRippleUnbounded", "matRippleCentered", "matRippleRadius", "matRippleAnimation", "matRippleDisabled", "matRippleTrigger"], exportAs: ["matRipple"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i10.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }] });
309
314
  }
310
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: FhssTableComponent, decorators: [{
315
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FhssTableComponent, decorators: [{
311
316
  type: Component,
312
317
  args: [{ selector: 'fhss-table', imports: [
313
318
  MatTableModule,
@@ -326,16 +331,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImpo
326
331
  class ConfirmationDialog {
327
332
  dialogRef = inject((MatDialogRef));
328
333
  data = inject(MAT_DIALOG_DATA);
329
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: ConfirmationDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
330
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: ConfirmationDialog, isStandalone: true, selector: "fhss-confirmation", ngImport: i0, template: "<h2 mat-dialog-title>{{ data.title ?? \"Are you sure?\"}}</h2>\n\n<mat-dialog-content>\n @if (data.description) {\n <p>{{ data.description }}</p>\n } @else {\n <p>Are you sure you would like to {{ data.action ?? 'continue' }}?</p>\n }</mat-dialog-content\n>\n\n<mat-dialog-actions>\n @if (data.buttons) { @for (name of data.buttons; track name) {\n <button mat-button [mat-dialog-close]=\"name\">{{name}}</button>\n } } @else {\n <button mat-button [mat-dialog-close]=\"false\">No</button>\n <button mat-button [mat-dialog-close]=\"true\">Yes</button>\n }\n</mat-dialog-actions>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1$3.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1$3.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1$3.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1$3.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }] });
334
+ static open(config, afterClosed) {
335
+ const dialogRef = inject(MatDialog).open(ConfirmationDialog, {
336
+ data: config,
337
+ });
338
+ dialogRef.afterClosed().subscribe(afterClosed);
339
+ }
340
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ConfirmationDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
341
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.14", type: ConfirmationDialog, isStandalone: true, selector: "fhss-confirmation", ngImport: i0, template: "<h2 mat-dialog-title>{{ data.title ?? \"Are you sure?\"}}</h2>\n\n<mat-dialog-content>\n @if (data.description) {\n <p>{{ data.description }}</p>\n } @else {\n <p>Are you sure you would like to {{ data.action ?? 'continue' }}?</p>\n }</mat-dialog-content\n>\n\n<mat-dialog-actions>\n @if (data.buttons) { @for (name of data.buttons; track name) {\n <button mat-button [mat-dialog-close]=\"name\">{{name}}</button>\n } } @else {\n <button mat-button [mat-dialog-close]=\"false\">No</button>\n <button mat-button [mat-dialog-close]=\"true\">Yes</button>\n }\n</mat-dialog-actions>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1$3.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1$3.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1$3.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1$3.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }] });
331
342
  }
332
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: ConfirmationDialog, decorators: [{
343
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ConfirmationDialog, decorators: [{
333
344
  type: Component,
334
345
  args: [{ selector: 'fhss-confirmation', imports: [MatDialogModule, MatButtonModule], template: "<h2 mat-dialog-title>{{ data.title ?? \"Are you sure?\"}}</h2>\n\n<mat-dialog-content>\n @if (data.description) {\n <p>{{ data.description }}</p>\n } @else {\n <p>Are you sure you would like to {{ data.action ?? 'continue' }}?</p>\n }</mat-dialog-content\n>\n\n<mat-dialog-actions>\n @if (data.buttons) { @for (name of data.buttons; track name) {\n <button mat-button [mat-dialog-close]=\"name\">{{name}}</button>\n } } @else {\n <button mat-button [mat-dialog-close]=\"false\">No</button>\n <button mat-button [mat-dialog-close]=\"true\">Yes</button>\n }\n</mat-dialog-actions>\n" }]
335
346
  }] });
336
347
 
337
- const FHSS_CONFIG = new InjectionToken('FHSS_CONFIG');
338
-
339
348
  const provideFhss = (config) => ({
340
349
  provide: FHSS_CONFIG,
341
350
  useValue: config,
@@ -380,54 +389,50 @@ const permissionGuardFactory = (reqPerms, haveAll) => {
380
389
  return async (_route, state) => {
381
390
  const router = inject(Router);
382
391
  const authService = inject(AuthService);
383
- const { rolePermissionMap } = inject(FHSS_CONFIG);
384
- const usr = await authService.whoAmI();
385
- if (!usr) {
392
+ const user = await authService.whoAmI();
393
+ if (!user) {
386
394
  authService.login(state.url);
387
395
  return false;
388
396
  }
389
- const userPermissions = new Set(usr.roles
390
- .flatMap((role) => rolePermissionMap[role])
391
- .concat(usr.permissions));
392
- const hasAccess = reqPerms[haveAll ? 'every' : 'some']((role) => userPermissions.has(role));
397
+ const hasAccess = reqPerms[haveAll ? 'every' : 'some']((role) => authService.effectivePermissions().has(role));
393
398
  return hasAccess || new RedirectCommand(router.parseUrl('/forbidden'));
394
399
  };
395
400
  };
396
401
 
397
402
  class AuthErrorPage {
398
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AuthErrorPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
399
- 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" }] });
403
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AuthErrorPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
404
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AuthErrorPage, isStandalone: true, selector: "fhss-auth-error", ngImport: i0, template: "<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. If the issue persists, contact the web team at <a href=\"mailto:fhss-web@byu.edu\">fhss-web&#64;byu.edu</a></p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>", 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"] }] });
400
405
  }
401
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AuthErrorPage, decorators: [{
406
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AuthErrorPage, decorators: [{
402
407
  type: Component,
403
- 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"] }]
408
+ args: [{ selector: 'fhss-auth-error', imports: [MatButtonModule], template: "<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. If the issue persists, contact the web team at <a href=\"mailto:fhss-web@byu.edu\">fhss-web&#64;byu.edu</a></p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>", 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"] }]
404
409
  }] });
405
410
 
406
411
  class ForbiddenPage {
407
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: ForbiddenPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
408
- 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" }] });
412
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ForbiddenPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
413
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: ForbiddenPage, isStandalone: true, selector: "fhss-forbidden", ngImport: i0, template: "<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>", 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"] }] });
409
414
  }
410
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: ForbiddenPage, decorators: [{
415
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ForbiddenPage, decorators: [{
411
416
  type: Component,
412
- 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"] }]
417
+ args: [{ selector: 'fhss-forbidden', imports: [MatButtonModule], template: "<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>", 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"] }]
413
418
  }] });
414
419
 
415
420
  class NotFoundPage {
416
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: NotFoundPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
417
- 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" }] });
421
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NotFoundPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
422
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: NotFoundPage, isStandalone: true, selector: "fhss-not-found", ngImport: i0, template: "<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>", 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"] }] });
418
423
  }
419
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: NotFoundPage, decorators: [{
424
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NotFoundPage, decorators: [{
420
425
  type: Component,
421
- 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"] }]
426
+ args: [{ selector: 'fhss-not-found', imports: [MatButtonModule], template: "<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>", 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"] }]
422
427
  }] });
423
428
 
424
429
  class ServerErrorPage {
425
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: ServerErrorPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
426
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.10", type: ServerErrorPage, isStandalone: true, selector: "fhss-server-error", ngImport: i0, template: "<byu-header />\n\n<div class=\"container\">\n <div class=\"server-error\">\n <img src=\"/error-duck.png\" alt=\"\" />\n <h1>500 - Internal Server Error</h1>\n <p>We ran into an unexpected error! If the issue persists, contact the web team at <a href=\"mailto:fhss-web@byu.edu\">fhss-web&#64;byu.edu</a></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 .server-error{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" }] });
430
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ServerErrorPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
431
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: ServerErrorPage, isStandalone: true, selector: "fhss-server-error", ngImport: i0, template: "<div class=\"container\">\n <div class=\"server-error\">\n <img src=\"/error-duck.png\" alt=\"\" />\n <h1>500 - Internal Server Error</h1>\n <p>We ran into an unexpected error! If the issue persists, contact the web team at <a href=\"mailto:fhss-web@byu.edu\">fhss-web&#64;byu.edu</a></p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>", styles: [":host{height:100vh;display:flex;flex-direction:column}.container{flex:1;display:flex;align-items:center;justify-content:center}.container .server-error{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"] }] });
427
432
  }
428
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: ServerErrorPage, decorators: [{
433
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ServerErrorPage, decorators: [{
429
434
  type: Component,
430
- args: [{ selector: 'fhss-server-error', imports: [MatButtonModule, ByuHeaderComponent, ByuFooterComponent], template: "<byu-header />\n\n<div class=\"container\">\n <div class=\"server-error\">\n <img src=\"/error-duck.png\" alt=\"\" />\n <h1>500 - Internal Server Error</h1>\n <p>We ran into an unexpected error! If the issue persists, contact the web team at <a href=\"mailto:fhss-web@byu.edu\">fhss-web&#64;byu.edu</a></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 .server-error{text-align:center;padding-bottom:3em}\n"] }]
435
+ args: [{ selector: 'fhss-server-error', imports: [MatButtonModule], template: "<div class=\"container\">\n <div class=\"server-error\">\n <img src=\"/error-duck.png\" alt=\"\" />\n <h1>500 - Internal Server Error</h1>\n <p>We ran into an unexpected error! If the issue persists, contact the web team at <a href=\"mailto:fhss-web@byu.edu\">fhss-web&#64;byu.edu</a></p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>", styles: [":host{height:100vh;display:flex;flex-direction:column}.container{flex:1;display:flex;align-items:center;justify-content:center}.container .server-error{text-align:center;padding-bottom:3em}\n"] }]
431
436
  }] });
432
437
 
433
438
  /**
@@ -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/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/trpcResource/trpcResource.ts","../../../projects/frontend-utils/src/lib/signals/debounced/debounced.ts","../../../projects/frontend-utils/src/lib/components/fhss-table/fhss-table.types.ts","../../../projects/frontend-utils/src/lib/components/fhss-table/fhss-table.component.ts","../../../projects/frontend-utils/src/lib/components/fhss-table/fhss-table.component.html","../../../projects/frontend-utils/src/lib/components/confirmation/confirmation.dialog.ts","../../../projects/frontend-utils/src/lib/components/confirmation/confirmation.dialog.html","../../../projects/frontend-utils/src/lib/config/lib.config.ts","../../../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/permission/permission.guard.ts","../../../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/lib/pages/server-error/server-error.page.ts","../../../projects/frontend-utils/src/lib/pages/server-error/server-error.page.html","../../../projects/frontend-utils/src/lib/signals/fetch-signal/fetch-signal.ts","../../../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 { computed, inject, Injectable, signal } from '@angular/core';\nimport { Router } from '@angular/router';\n\ntype WhoAmI = {\n id: string;\n preferredFirstName: string;\n preferredLastName: string;\n roles: string[];\n permissions: string[];\n};\n\n@Injectable({\n providedIn: 'root',\n})\nexport class AuthService {\n readonly router = inject(Router);\n\n constructor() {\n this.whoAmI();\n }\n\n authenticated = signal(false);\n userId = signal<string | undefined>(undefined);\n preferredFirstName = signal<string | undefined>(undefined);\n preferredLastName = signal<string | undefined>(undefined);\n roles = signal<string[] | undefined>(undefined);\n permissions = signal<string[] | undefined>(undefined);\n\n preferredName = computed(() => {\n const prefLast = this.preferredLastName();\n return `${this.preferredFirstName() ?? ''}${prefLast ? ` ${prefLast}` : ''}`;\n });\n\n private resetAuthState() {\n this.authenticated.set(false);\n this.userId.set(undefined);\n this.preferredFirstName.set(undefined);\n this.preferredLastName.set(undefined);\n this.roles.set(undefined);\n this.permissions.set(undefined);\n }\n\n private setUser(user: WhoAmI) {\n this.authenticated.set(true);\n this.userId.set(user.id);\n this.preferredFirstName.set(user.preferredFirstName);\n this.preferredLastName.set(user.preferredLastName);\n this.roles.set(user.roles);\n this.permissions.set(user.permissions);\n }\n\n login(nextUri?: string) {\n window.location.href = `/login${nextUri ? `?next_uri=${nextUri}` : ''}`;\n }\n\n logout() {\n this.resetAuthState();\n window.location.href = '/logout';\n }\n\n async whoAmI(): Promise<WhoAmI | null> {\n try {\n const res = await fetch('/sys/who-am-i');\n const user = await res.json();\n if (!user) {\n this.resetAuthState();\n return null;\n }\n this.setUser(user);\n return user;\n } catch (error) {\n console.error(error);\n return null;\n }\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.preferredName() }}</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 { computed, effect, inject, Injector, signal } from \"@angular/core\";\nimport { isTRPCClientError, Resolver } from \"@trpc/client\";\nimport { ResolverDef, TrpcResource ,TrpcResourceOptions } from \"./trpcResource.types\";\n\nfunction trpcResource<TDef extends ResolverDef>(procedure: Resolver<TDef>, input: () => TDef['input'], options?: TrpcResourceOptions<TDef['output']>): TrpcResource<TDef> {\n const currentInput = computed(input);\n\n const value = signal<TDef['output'] | undefined>(options?.initialValue ?? options?.defaultValue, { equal: options?.equal });\n const error = signal<TDef['errorShape'] | undefined>(undefined);\n const isLoading = signal<boolean>(false);\n\n if (options?.autoRefresh) {\n effect((onCleanup) => {\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 reads currentInput which triggers the effect\n refresh(controller.signal, true);\n }, { injector: options?.injector || inject(Injector)});\n }\n\n const refresh = async (\n abortSignal?: AbortSignal,\n keepLoadingThroughAbort: boolean = true,\n ) => {\n // Reset signals for a fresh request.\n isLoading.set(true);\n error.set(undefined);\n\n try {\n value.set(await procedure(currentInput(), {\n signal: abortSignal\n }));\n error.set(undefined)\n } catch (err) {\n if (isTRPCClientError(err)) {\n // if the trpc request was aborted\n // we check if we would like to continue loading (the next request)\n // if so then we just leave this refresh in an undefined state\n // else we error as usual\n if (err.cause?.name === 'AbortError' && keepLoadingThroughAbort) {\n return;\n }\n error.set(err);\n } else {\n // Fallback for non-Error values\n console.error(\"A non-tRPC error has occured on this trpcResource: \", String(err));\n }\n value.set(options?.defaultValue);\n }\n isLoading.set(false);\n }\n\n return {\n value,\n error,\n isLoading,\n refresh\n };\n}\n\nfunction debugTrpcResource<TDef extends ResolverDef>(_trpcResource: TrpcResource<TDef>) {\n return {\n value: _trpcResource.value(),\n error: _trpcResource.error(),\n isLoading: _trpcResource.isLoading(),\n }\n}\n\nexport { debugTrpcResource, trpcResource };","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 { Resolver } from '@trpc/client';\nimport { ResolverDef } from '../../signals/trpcResource/trpcResource.types';\nimport { trpcResource } from '../../signals/trpcResource/trpcResource';\n\nexport type FhssTableConfig<TDef extends ResolverDef> = {\n procedure: Resolver<TDef>;\n columns: {\n [K in keyof TDef['output']['data'][number]]: {\n hide?: boolean;\n header?: string;\n allowSort?: boolean;\n individualFilter?: boolean;\n };\n };\n showSearch?: boolean;\n sorting: {\n defaultSortBy: Extract<keyof TDef['output']['data'][number], string>;\n defaultSortDirection?: 'asc' | 'desc';\n };\n pagination: {\n pageSize: number;\n hideControls?: boolean;\n };\n interaction?:\n | {\n type: 'click';\n onClick: (obj: TDef['output']['data'][number]) => void;\n }\n | {\n type: 'select';\n multi?: boolean;\n };\n};\n\nexport const makeTableConfig = <TDef extends ResolverDef>(\n config: FhssTableConfig<TDef>,\n) => config;\n\nexport type AnyResolver = {\n input: any;\n output: { totalCount: number; data: any[] };\n transformer: any;\n errorShape: {\n code: any;\n message: string;\n data: any;\n };\n};\n\nexport type AnyResource = ReturnType<typeof trpcResource<AnyResolver>>;\n","import {\n Component,\n inject,\n Injector,\n input,\n linkedSignal,\n model,\n OnInit,\n signal,\n} from '@angular/core';\nimport { trpcResource } from '../../signals/trpcResource/trpcResource';\nimport { debounced } from '../../signals/debounced/debounced';\nimport { FhssTableConfig, AnyResolver, AnyResource } from './fhss-table.types';\nexport { makeTableConfig } from './fhss-table.types';\n\nimport { MatTableModule } from '@angular/material/table';\nimport { MatProgressSpinnerModule } from '@angular/material/progress-spinner';\nimport { MatPaginatorModule, PageEvent } from '@angular/material/paginator';\nimport { MatInputModule } from '@angular/material/input';\nimport { FormsModule } from '@angular/forms';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatSortModule, Sort } from '@angular/material/sort';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatRippleModule } from '@angular/material/core';\nimport { MatCheckboxModule } from '@angular/material/checkbox';\nimport { SelectionModel } from '@angular/cdk/collections';\n\n@Component({\n selector: 'fhss-table',\n imports: [\n MatTableModule,\n MatProgressSpinnerModule,\n MatPaginatorModule,\n MatInputModule,\n FormsModule,\n MatButtonModule,\n MatSortModule,\n MatIconModule,\n MatRippleModule,\n MatCheckboxModule,\n ],\n templateUrl: './fhss-table.component.html',\n styleUrl: './fhss-table.component.scss',\n})\nexport class FhssTableComponent implements OnInit {\n private injector = inject(Injector);\n\n config = input.required<FhssTableConfig<AnyResolver>>();\n miscParams = input<Record<string, any>>({});\n\n columnKeys: string[] = [];\n columnsToDisplay: string[] = [];\n selection: SelectionModel<any> | undefined;\n selectedValues = model<any[]>([]);\n\n dataResource!: AnyResource;\n refresh() {\n this.dataResource.refresh()\n }\n\n ngOnInit() {\n const cfg = this.config();\n\n this.columnKeys = Object.keys(cfg.columns);\n this.columnsToDisplay = this.columnKeys.filter((k) => !cfg.columns[k].hide);\n\n if (cfg.interaction?.type === 'select') {\n this.columnsToDisplay.unshift('table-checkbox-column');\n this.selection = new SelectionModel<any>(\n cfg.interaction.multi,\n undefined,\n undefined,\n (o1, o2) => o1.id === o2.id,\n );\n }\n\n this.dataResource = trpcResource(\n this.config().procedure,\n () => ({\n search: this.debouncedSearch(),\n filters: this.debouncedFilters(),\n sort: {\n property: this.sortBy(),\n direction: this.sortDirection(),\n },\n page: {\n size: this.config().pagination.pageSize,\n index: this.pageIndex(),\n },\n ...this.miscParams(),\n }),\n {\n autoRefresh: true,\n injector: this.injector,\n },\n );\n }\n\n search = signal('');\n debouncedSearch = debounced(this.search);\n filters = signal<Record<string, string>>({});\n debouncedFilters = debounced(this.filters);\n\n sortBy = linkedSignal(() => this.config().sorting.defaultSortBy);\n sortDirection = linkedSignal<'asc' | 'desc'>(\n () => this.config().sorting.defaultSortDirection ?? 'asc',\n );\n\n pageIndex = linkedSignal(() => {\n this.search();\n return 0;\n });\n\n onFilterChange(col: string, val: string) {\n this.filters.update((f) => ({ ...f, [col]: val }));\n }\n onPaginationChange(e: PageEvent) {\n this.pageIndex.set(e.pageIndex);\n }\n onSortChange(sort: Sort) {\n this.sortBy.set(sort.active || this.config().sorting.defaultSortBy);\n this.sortDirection.set(\n sort.direction || this.config().sorting.defaultSortDirection || 'asc',\n );\n this.pageIndex.set(0);\n }\n onSelectionChange() {\n this.selectedValues.set(this.selection?.selected ?? []);\n }\n\n onRowClick(row: any) {\n const interaction = this.config().interaction;\n if (!interaction) return;\n\n if (interaction.type === 'click') {\n interaction.onClick(row);\n } else if (interaction.type === 'select') {\n this.selection?.toggle(row);\n }\n this.onSelectionChange();\n }\n\n isAllSelected() {\n if (!this.selection) return false;\n const numSelected = this.selection.selected.length;\n const numRows = this.dataResource.value()?.data?.length ?? 0;\n return numSelected === numRows;\n }\n\n toggleAllRows() {\n if (!this.selection) return;\n if (this.isAllSelected()) {\n this.selection.clear();\n } else {\n this.selection.select(...(this.dataResource.value()?.data ?? []));\n }\n this.onSelectionChange();\n }\n\n checkboxLabel(row?: any): string {\n if (!this.selection) return '';\n if (!row) {\n return `${this.isAllSelected() ? 'deselect' : 'select'} all`;\n }\n return `${this.selection.isSelected(row) ? 'deselect' : 'select'} row ${row.position + 1}`;\n }\n\n protected isBoolean = (val: unknown) => typeof val === 'boolean';\n}\n","<div class=\"table-container\">\n @if (dataResource.isLoading() || dataResource.error()) {\n <div class=\"shade\">\n @if (dataResource.isLoading()) {\n <mat-spinner />\n }\n @if (dataResource.error()) {\n <div class=\"msg-bkgd\">\n <div class=\"error-msg\">There was an error retrieving this data</div>\n <button mat-button (click)=\"this.dataResource.refresh()\">\n Retry\n </button>\n </div>\n }\n </div>\n }\n\n @if (config().showSearch) {\n <mat-form-field class=\"search\" subscriptSizing=\"dynamic\">\n <input matInput [(ngModel)]=\"search\" placeholder=\"Find...\" />\n @if (search()) {\n <button\n matSuffix\n mat-icon-button\n aria-label=\"Clear\"\n (click)=\"search.set('')\"\n >\n <mat-icon>close</mat-icon>\n </button>\n }\n </mat-form-field>\n }\n\n <div class=\"other-controls\">\n <ng-content />\n </div>\n\n <table\n mat-table\n [dataSource]=\"dataResource.value()?.data ?? []\"\n matSort\n [matSortActive]=\"sortBy()\"\n [matSortDirection]=\"sortDirection()\"\n (matSortChange)=\"onSortChange($event)\"\n matSortDisableClear\n >\n @if (selection) {\n <ng-container matColumnDef=\"table-checkbox-column\">\n <mat-header-cell *matHeaderCellDef>\n @if (selection.isMultipleSelection()) {\n <mat-checkbox\n (change)=\"$event ? toggleAllRows() : null\"\n [checked]=\"selection.hasValue() && isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n [aria-label]=\"checkboxLabel()\"\n />\n }\n </mat-header-cell>\n <mat-cell *matCellDef=\"let row\">\n <mat-checkbox\n (click)=\"$event.stopPropagation()\"\n (change)=\"$event ? onRowClick(row) : null\"\n [checked]=\"selection.isSelected(row)\"\n [aria-label]=\"checkboxLabel(row)\"\n />\n </mat-cell>\n </ng-container>\n }\n\n @for (key of columnKeys; track key) {\n <ng-container [matColumnDef]=\"key\">\n <mat-header-cell\n *matHeaderCellDef\n mat-sort-header\n [disabled]=\"!config().columns[key].allowSort\"\n >\n <div class=\"header\">\n {{ config().columns[key].header || key }}\n @if (config().columns[key].individualFilter) {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <input\n matInput\n (click)=\"$event.stopPropagation()\"\n [ngModel]=\"filters()[key] || ''\"\n (ngModelChange)=\"onFilterChange(key, $event)\"\n placeholder=\"Filter...\"\n />\n @if (filters()[key]) {\n <button\n matSuffix\n mat-icon-button\n aria-label=\"Clear\"\n (click)=\"onFilterChange(key, '')\"\n >\n <mat-icon>close</mat-icon>\n </button>\n }\n </mat-form-field>\n }\n </div>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let row\">\n @if (isBoolean(row[key])) {\n <mat-checkbox [checked]=\"row[key]\" [disabled]=\"true\" />\n } @else {\n {{ row[key] }}\n }\n </mat-cell>\n </ng-container>\n }\n\n <mat-header-row *matHeaderRowDef=\"columnsToDisplay\"></mat-header-row>\n @if (config().interaction) {\n <mat-row\n matRipple\n (click)=\"onRowClick(row)\"\n *matRowDef=\"let row; columns: columnsToDisplay\"\n ></mat-row>\n } @else {\n <mat-row *matRowDef=\"let row; columns: columnsToDisplay\"></mat-row>\n }\n\n <tr class=\"mat-row\" *matNoDataRow>\n <td class=\"mat-cell\">\n <div class=\"no-data\">\n No data found\n <button mat-button (click)=\"this.dataResource.refresh()\">\n Retry\n </button>\n </div>\n </td>\n </tr>\n </table>\n\n @if (!config().pagination.hideControls) {\n <mat-paginator\n [length]=\"dataResource.value()?.totalCount\"\n [showFirstLastButtons]=\"true\"\n [pageSize]=\"config().pagination.pageSize\"\n [hidePageSize]=\"true\"\n [pageIndex]=\"pageIndex()\"\n [disabled]=\"dataResource.isLoading()\"\n (page)=\"onPaginationChange($event)\"\n />\n }\n</div>\n","import { Component, inject } from '@angular/core';\nimport { MatButtonModule } from '@angular/material/button';\nimport {\n MatDialogRef,\n MatDialogModule,\n MAT_DIALOG_DATA,\n} from '@angular/material/dialog';\n\n@Component({\n selector: 'fhss-confirmation',\n imports: [MatDialogModule, MatButtonModule],\n templateUrl: './confirmation.dialog.html',\n styleUrl: './confirmation.dialog.scss',\n})\nexport class ConfirmationDialog {\n readonly dialogRef = inject(MatDialogRef<ConfirmationDialog>);\n readonly data: {\n action?: string;\n title?: string;\n description?: string;\n buttons?: string[];\n } = inject(MAT_DIALOG_DATA);\n}\n","<h2 mat-dialog-title>{{ data.title ?? \"Are you sure?\"}}</h2>\n\n<mat-dialog-content>\n @if (data.description) {\n <p>{{ data.description }}</p>\n } @else {\n <p>Are you sure you would like to {{ data.action ?? 'continue' }}?</p>\n }</mat-dialog-content\n>\n\n<mat-dialog-actions>\n @if (data.buttons) { @for (name of data.buttons; track name) {\n <button mat-button [mat-dialog-close]=\"name\">{{name}}</button>\n } } @else {\n <button mat-button [mat-dialog-close]=\"false\">No</button>\n <button mat-button [mat-dialog-close]=\"true\">Yes</button>\n }\n</mat-dialog-actions>\n","import { InjectionToken } from '@angular/core';\n\nexport const FHSS_CONFIG = new InjectionToken<FhssConfig>('FHSS_CONFIG');\n\nexport interface FhssConfig {\n rolePermissionMap: Record<string, string[]>;\n}\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","import { inject } from '@angular/core';\nimport { CanActivateFn } from '@angular/router';\nimport { AuthService } from '../../services/auth/auth.service';\n\n/**\n * This guard checks if the client is authenticated, redirecting to login if not.\n */\nexport const authGuard: CanActivateFn = async (route, state) => {\n const authService = inject(AuthService);\n const usr = await authService.whoAmI();\n if (!usr) {\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 * Factory function to create a permission-based route guard.\n *\n * This guard checks if the current user has the required permissions to access a route.\n * If the user is not authenticated, they are redirected to the login page.\n * If the user lacks the necessary permissions, they are redirected to the `/forbidden` page.\n *\n * @param requiredPermissions - An array of permission strings that the user must have.\n * @param haveAll - If `true`, the user must have all required permissions. If `false` or omitted, the user must have at least one of the required permissions.\n * @returns A `CanActivateFn` function to be used as a route guard.\n * \n * @example\n * ```typescript\n * const routes: Routes = [\n * {\n * path: 'admin',\n * component: AdminPage,\n * canActivate: [permissionGuardFactory(['manage-users', 'see-data'], true)],\n * },\n * ];\n * ```\n */\nexport const permissionGuardFactory: <Permission extends string>(\n requiredPermissions: Permission[],\n haveAll?: boolean,\n) => CanActivateFn = (reqPerms, haveAll) => {\n return async (_route, state) => {\n const router = inject(Router);\n const authService = inject(AuthService);\n const { rolePermissionMap } = inject(FHSS_CONFIG);\n\n const usr = await authService.whoAmI();\n if (!usr) {\n authService.login(state.url);\n return false;\n }\n\n const userPermissions = new Set(\n usr.roles\n .flatMap((role) => rolePermissionMap[role])\n .concat(usr.permissions),\n );\n\n const hasAccess = reqPerms[haveAll ? 'every' : 'some']((role) =>\n userPermissions.has(role),\n );\n\n return hasAccess || new RedirectCommand(router.parseUrl('/forbidden'));\n };\n};\n","import { Component } from '@angular/core';\nimport { ByuFooterComponent } from '../../components/byu-footer/byu-footer.component';\nimport { ByuHeaderComponent } from '../../components/byu-header/byu-header.component';\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 } from '../../components/byu-footer/byu-footer.component';\nimport { ByuHeaderComponent } from '../../components/byu-header/byu-header.component';\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 } from '../../components/byu-footer/byu-footer.component';\nimport { ByuHeaderComponent } from '../../components/byu-header/byu-header.component';\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","import { Component } from '@angular/core';\nimport { MatButtonModule } from '@angular/material/button';\nimport { ByuFooterComponent } from '../../components/byu-footer/byu-footer.component';\nimport { ByuHeaderComponent } from '../../components/byu-header/byu-header.component';\n\n@Component({\n selector: 'fhss-server-error',\n imports: [MatButtonModule, ByuHeaderComponent, ByuFooterComponent],\n templateUrl: './server-error.page.html',\n styleUrl: './server-error.page.scss',\n})\nexport class ServerErrorPage {}\n","<byu-header />\n\n<div class=\"container\">\n <div class=\"server-error\">\n <img src=\"/error-duck.png\" alt=\"\" />\n <h1>500 - Internal Server Error</h1>\n <p>We ran into an unexpected error! If the issue persists, contact the web team at <a href=\"mailto:fhss-web@byu.edu\">fhss-web&#64;byu.edu</a></p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>\n\n<byu-footer />\n","import { computed, effect, EffectRef, inject, Injector, signal, untracked } from '@angular/core';\nimport { FetchSignal, FetchSignalFactory, FetchSignalOptions, FetchSignalRequest, FetchSignalStatus, HttpMethod, Json, ResponseTransformer } from './fetch-signal.types';\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, ErrorResponse, RawResponse>(\n request: () => FetchSignalRequest,\n method: HttpMethod,\n transform: ResponseTransformer<Response>,\n options?: FetchSignalOptions<Response, RawResponse>,\n): FetchSignal<Response, ErrorResponse> {\n // Use a computed signal so that any changes to Signals in the request object trigger updates.\n const currentRequest = computed<FetchSignalRequest>(request);\n\n const value = signal<Response | undefined>(options?.defaultValue, { equal: options?.equal });\n const errorResponse = signal<ErrorResponse | undefined>(undefined);\n const isLoading = signal<boolean>(false);\n const statusCode = signal<number | undefined>(undefined);\n const headers = signal<Record<string, string> | undefined>(undefined);\n\n const status = signal<FetchSignalStatus>('idle');\n const error = signal<Error | undefined>(undefined);\n\n const injector = inject(Injector);\n let effectRef: EffectRef | undefined = undefined;\n if (options?.autoRefresh) {\n effectRef = effect((onCleanup) => {\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, true);\n }, { injector: options?.injector || injector});\n }\n\n const refresh = async (\n abortSignal?: AbortSignal,\n keepLoadingThroughAbort?: boolean,\n ) => {\n // if the fetchSignal has been destroyed, do nothing\n if (untracked(status) === 'destroyed') return;\n\n // Reset signals for a fresh request.\n isLoading.set(true);\n errorResponse.set(undefined);\n statusCode.set(undefined);\n headers.set(undefined);\n status.set('loading');\n error.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)\n searchParams.append(key, String(params[key]));\n }\n uri += (uri.includes('?') ? '&' : '?') + searchParams.toString();\n }\n \n // Filter out undefined header values\n const filteredHeaders = requestHeaders\n ? Object.fromEntries(\n Object.entries(requestHeaders).filter(\n ([, value]) => value !== undefined\n ) as [string, string][]\n )\n : undefined;\n \n try {\n // send the request\n const response = await fetch(uri, {\n method,\n headers: filteredHeaders,\n // Only include a body if one is provided.\n body: body,\n signal: abortSignal,\n });\n\n // set the status code\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 status.set('resolved');\n } else {\n // try to parse the error as json\n try {\n errorResponse.set(await response.json());\n value.set(undefined);\n status.set('resolved');\n } catch {\n throw new Error('Unable to parse error response.')\n }\n }\n } catch (err: any) {\n if (err instanceof Error) {\n // if the fetch request was aborted\n // we check if we would like to continue loading (the next request)\n // if so then we just leave this refresh in an undefined state\n // else we error as usual\n if (err.name === 'AbortError' && keepLoadingThroughAbort) {\n return;\n }\n error.set(err);\n } else {\n // Fallback for non-Error values\n error.set(new Error(String(err)));\n }\n value.set(undefined);\n status.set('error');\n }\n isLoading.set(false);\n };\n\n const destroy = () => {\n // if the fetchSignal has been destroyed, do nothing\n if (status() === 'destroyed') return;\n\n if (effectRef) {\n effectRef.destroy();\n }\n status.set('destroyed');\n value.set(undefined);\n errorResponse.set(undefined);\n isLoading.set(false);\n statusCode.set(undefined);\n headers.set(undefined);\n error.set(undefined);\n }\n\n return {\n value,\n errorResponse,\n isLoading,\n statusCode,\n headers,\n status,\n error,\n refresh,\n destroy\n };\n}\n\n//\n// Helpers for attaching response transforms.\n//\nconst createHelper = <Response, Error>(method: HttpMethod, transform: ResponseTransformer<Response>) =>\n (request: () => FetchSignalRequest, options?: FetchSignalOptions<Response, any>): FetchSignal<Response, Error> =>\n createFetchSignal<Response, Error, any>(request, method, transform, options);\n\n// Transforms\nconst jsonTransformer: ResponseTransformer<any> = (response: Response) => response.json();\nconst textTransformer: ResponseTransformer<string> = (response: Response) => response.text();\nconst blobTransformer: ResponseTransformer<Blob> = (response: Response) => response.blob();\nconst arrayBufferTransformer: ResponseTransformer<ArrayBuffer> = (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(\n 'GET',\n jsonTransformer,\n) 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(\n 'POST',\n jsonTransformer,\n) 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(\n 'PUT',\n jsonTransformer,\n) 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(\n 'PATCH',\n jsonTransformer,\n) 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(\n 'DELETE',\n jsonTransformer,\n) 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 };\nexport type { Json };\n\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/fhss-table/fhss-table.component'\nexport * from './lib/components/confirmation/confirmation.dialog'\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/permission/permission.guard'\n\n/**\n * Pages\n */\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'\nexport * from './lib/pages/server-error/server-error.page'\n\n/**\n * Services\n */\nexport * from './lib/services/auth/auth.service'\n\n/**\n * Signals\n */\nexport * from './lib/signals/trpcResource/trpcResource'\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":["i6","i2","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/trpcResource/trpcResource.ts","../../../projects/frontend-utils/src/lib/signals/debounced/debounced.ts","../../../projects/frontend-utils/src/lib/components/fhss-table/fhss-table.types.ts","../../../projects/frontend-utils/src/lib/components/fhss-table/fhss-table.component.ts","../../../projects/frontend-utils/src/lib/components/fhss-table/fhss-table.component.html","../../../projects/frontend-utils/src/lib/components/confirmation/confirmation.dialog.ts","../../../projects/frontend-utils/src/lib/components/confirmation/confirmation.dialog.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/permission/permission.guard.ts","../../../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/lib/pages/server-error/server-error.page.ts","../../../projects/frontend-utils/src/lib/pages/server-error/server-error.page.html","../../../projects/frontend-utils/src/lib/signals/fetch-signal/fetch-signal.ts","../../../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 rolePermissionMap: Record<string, string[]>;\n}\n","import { computed, inject, Injectable, signal } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { FHSS_CONFIG } from '../../config/lib.config';\n\ntype WhoAmI = {\n id: string;\n preferredFirstName: string;\n preferredLastName: string;\n roles: string[];\n permissions: string[];\n};\n\n@Injectable({\n providedIn: 'root',\n})\nexport class AuthService {\n readonly router = inject(Router);\n readonly FhssConfig = inject(FHSS_CONFIG);\n\n constructor() {\n this.whoAmI();\n }\n\n authenticated = signal(false);\n userId = signal<string | undefined>(undefined);\n preferredFirstName = signal<string | undefined>(undefined);\n preferredLastName = signal<string | undefined>(undefined);\n roles = signal<string[]>([]);\n permissions = signal<string[]>([]);\n\n effectivePermissions = computed(() => \n new Set(\n this.roles()\n .flatMap((role) => this.FhssConfig.rolePermissionMap[role])\n .concat(this.permissions())\n )\n );\n\n preferredName = computed(() => {\n const prefLast = this.preferredLastName();\n return `${this.preferredFirstName() ?? ''}${prefLast ? ` ${prefLast}` : ''}`;\n });\n\n\n private resetAuthState() {\n this.authenticated.set(false);\n this.userId.set(undefined);\n this.preferredFirstName.set(undefined);\n this.preferredLastName.set(undefined);\n this.roles.set([]);\n this.permissions.set([]);\n }\n\n private setUser(user: WhoAmI) {\n this.authenticated.set(true);\n this.userId.set(user.id);\n this.preferredFirstName.set(user.preferredFirstName);\n this.preferredLastName.set(user.preferredLastName);\n this.roles.set(user.roles);\n this.permissions.set(user.permissions);\n }\n\n login(nextUri?: string) {\n window.location.href = `/login${nextUri ? `?next_uri=${nextUri}` : ''}`;\n }\n\n logout() {\n this.resetAuthState();\n window.location.href = '/logout';\n }\n\n async whoAmI(): Promise<WhoAmI | null> {\n try {\n const res = await fetch('/sys/who-am-i');\n const user = await res.json();\n if (!user) {\n this.resetAuthState();\n return null;\n }\n this.setUser(user);\n return user;\n } catch (error) {\n console.error(error);\n return null;\n }\n }\n}\n","import { Component, input, inject } from '@angular/core';\nimport { RouterModule } from '@angular/router';\nimport { AuthService } from '../../services/auth/auth.service';\n\ntype MenuItemBase<T extends string> = {\n text: string;\n requiredPermissions?: T[];\n}\n\ntype HeaderLink = {\n text: string;\n path: string;\n};\n\ntype MenuLink<T extends string> = MenuItemBase<T> & {\n path: string;\n};\n\ntype MenuDropdown<T extends string> = MenuItemBase<T> & {\n items: MenuLink<T>[];\n}\n\ntype HeaderMenu<T extends string> = MenuLink<T> | MenuDropdown<T>\n\nexport type HeaderConfig<Permission extends string> = {\n title: HeaderLink;\n subtitle?: HeaderLink;\n breadcrumbs?: HeaderLink[];\n menu?: HeaderMenu<Permission>[];\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.required<HeaderConfig<string>>();\n\n openDropdown: MenuDropdown<string> | null = null;\n\n isMenuLink(item: HeaderMenu<string>): item is MenuLink<string> {\n return 'path' in item;\n }\n\n isMenuItemVisible(item: HeaderMenu<string>) {\n \n return item.requiredPermissions !== undefined\n ? item.requiredPermissions.some((permission) => this.auth.effectivePermissions().has(permission))\n : true;\n }\n \n toggleDropdown(dropdown: MenuDropdown<string>) {\n this.openDropdown = this.openDropdown === dropdown ? null : dropdown;\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.preferredName() }}</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 (isMenuItemVisible(menuItem)) {\n @if (isMenuLink(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)\">\n <div class=\"nav-item-content\" >{{ menuItem.text }}</div>\n @if (openDropdown === menuItem) {\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 }\n </nav>\n </div>\n</header>\n","import { computed, effect, inject, Injector, linkedSignal, signal } from \"@angular/core\";\nimport { isTRPCClientError, Resolver } from \"@trpc/client\";\nimport { ResolverDef, TrpcResource, TrpcResourceOptions } from \"./trpcResource.types\";\n\nfunction trpcResource<TDef extends ResolverDef>(procedure: Resolver<TDef>, input: () => TDef['input'], options?: TrpcResourceOptions<TDef['output']>): TrpcResource<TDef> {\n const currentInput = computed(input);\n\n const value = linkedSignal<TDef['output'] | undefined>(options?.valueComputation ?? (() => options?.initialValue ?? options?.defaultValue), { equal: options?.equal });\n const error = signal<TDef['errorShape'] | undefined>(undefined);\n const isLoading = signal<boolean>(false);\n\n if (options?.autoRefresh) {\n effect((onCleanup) => {\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 reads currentInput which triggers the effect\n refresh(controller.signal, true);\n }, { injector: options?.injector || inject(Injector)});\n }\n\n const refresh = async (\n abortSignal?: AbortSignal,\n keepLoadingThroughAbort: boolean = true,\n ) => {\n // Reset signals for a fresh request.\n isLoading.set(true);\n error.set(undefined);\n\n try {\n value.set(await procedure(currentInput(), {\n signal: abortSignal\n }));\n error.set(undefined)\n } catch (err) {\n if (isTRPCClientError(err)) {\n // if the trpc request was aborted\n // we check if we would like to continue loading (the next request)\n // if so then we just leave this refresh in an undefined state\n // else we error as usual\n if (err.cause?.name === 'AbortError' && keepLoadingThroughAbort) {\n return;\n }\n error.set(err);\n } else {\n // Fallback for non-Error values\n console.error(\"A non-tRPC error has occured on this trpcResource: \", String(err));\n }\n value.set(options?.defaultValue);\n }\n isLoading.set(false);\n }\n\n return {\n value,\n error,\n isLoading,\n refresh\n };\n}\n\nfunction debugTrpcResource<TDef extends ResolverDef>(_trpcResource: TrpcResource<TDef>) {\n return {\n value: _trpcResource.value(),\n error: _trpcResource.error(),\n isLoading: _trpcResource.isLoading(),\n }\n}\n\nexport { debugTrpcResource, trpcResource };","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 { Resolver } from '@trpc/client';\nimport { ResolverDef } from '../../signals/trpcResource/trpcResource.types';\nimport { trpcResource } from '../../signals/trpcResource/trpcResource';\n\nexport type FhssTableConfig<TDef extends ResolverDef> = {\n procedure: Resolver<TDef>;\n columns: {\n [K in keyof TDef['output']['data'][number]]: {\n hide?: boolean;\n header?: string;\n allowSort?: boolean;\n individualFilter?: boolean;\n };\n };\n showSearch?: boolean;\n sorting: {\n defaultSortBy: Extract<keyof TDef['output']['data'][number], string>;\n defaultSortDirection?: 'asc' | 'desc';\n };\n pagination: {\n pageSize: number;\n hideControls?: boolean;\n };\n interaction?:\n | {\n type: 'click';\n onClick: (obj: TDef['output']['data'][number]) => void;\n }\n | {\n type: 'select';\n multi?: boolean;\n };\n};\n\nexport const makeTableConfig = <TDef extends ResolverDef>(\n config: FhssTableConfig<TDef>,\n) => config;\n\nexport type AnyResolver = {\n input: any;\n output: { totalCount: number; data: any[] };\n transformer: any;\n errorShape: {\n code: any;\n message: string;\n data: any;\n };\n};\n\nexport type AnyResource = ReturnType<typeof trpcResource<AnyResolver>>;\n","import {\n Component,\n inject,\n Injector,\n input,\n linkedSignal,\n model,\n OnInit,\n signal,\n} from '@angular/core';\nimport { trpcResource } from '../../signals/trpcResource/trpcResource';\nimport { debounced } from '../../signals/debounced/debounced';\nimport { FhssTableConfig, AnyResolver, AnyResource } from './fhss-table.types';\nexport { makeTableConfig } from './fhss-table.types';\n\nimport { MatTableModule } from '@angular/material/table';\nimport { MatProgressSpinnerModule } from '@angular/material/progress-spinner';\nimport { MatPaginatorModule, PageEvent } from '@angular/material/paginator';\nimport { MatInputModule } from '@angular/material/input';\nimport { FormsModule } from '@angular/forms';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatSortModule, Sort } from '@angular/material/sort';\nimport { MatIconModule } from '@angular/material/icon';\nimport { MatRippleModule } from '@angular/material/core';\nimport { MatCheckboxModule } from '@angular/material/checkbox';\nimport { SelectionModel } from '@angular/cdk/collections';\n\n@Component({\n selector: 'fhss-table',\n imports: [\n MatTableModule,\n MatProgressSpinnerModule,\n MatPaginatorModule,\n MatInputModule,\n FormsModule,\n MatButtonModule,\n MatSortModule,\n MatIconModule,\n MatRippleModule,\n MatCheckboxModule,\n ],\n templateUrl: './fhss-table.component.html',\n styleUrl: './fhss-table.component.scss',\n})\nexport class FhssTableComponent implements OnInit {\n private injector = inject(Injector);\n\n config = input.required<FhssTableConfig<AnyResolver>>();\n miscParams = input<Record<string, any>>({});\n\n columnKeys: string[] = [];\n columnsToDisplay: string[] = [];\n selection: SelectionModel<any> | undefined;\n selectedValues = model<any[]>([]);\n\n dataResource!: AnyResource;\n refresh() {\n this.dataResource.refresh()\n }\n\n ngOnInit() {\n const cfg = this.config();\n\n this.columnKeys = Object.keys(cfg.columns);\n this.columnsToDisplay = this.columnKeys.filter((k) => !cfg.columns[k].hide);\n\n if (cfg.interaction?.type === 'select') {\n this.columnsToDisplay.unshift('table-checkbox-column');\n this.selection = new SelectionModel<any>(\n cfg.interaction.multi,\n undefined,\n undefined,\n (o1, o2) => o1.id === o2.id,\n );\n }\n\n this.dataResource = trpcResource(\n this.config().procedure,\n () => ({\n search: this.debouncedSearch(),\n filters: this.debouncedFilters(),\n sort: {\n property: this.sortBy(),\n direction: this.sortDirection(),\n },\n page: {\n size: this.config().pagination.pageSize,\n index: this.pageIndex(),\n },\n ...this.miscParams(),\n }),\n {\n autoRefresh: true,\n injector: this.injector,\n },\n );\n }\n\n search = signal('');\n debouncedSearch = debounced(this.search);\n filters = signal<Record<string, string>>({});\n debouncedFilters = debounced(this.filters);\n\n sortBy = linkedSignal(() => this.config().sorting.defaultSortBy);\n sortDirection = linkedSignal<'asc' | 'desc'>(\n () => this.config().sorting.defaultSortDirection ?? 'asc',\n );\n\n pageIndex = linkedSignal(() => {\n this.search();\n return 0;\n });\n\n onFilterChange(col: string, val: string) {\n this.filters.update((f) => ({ ...f, [col]: val }));\n }\n onPaginationChange(e: PageEvent) {\n this.pageIndex.set(e.pageIndex);\n }\n onSortChange(sort: Sort) {\n this.sortBy.set(sort.active || this.config().sorting.defaultSortBy);\n this.sortDirection.set(\n sort.direction || this.config().sorting.defaultSortDirection || 'asc',\n );\n this.pageIndex.set(0);\n }\n onSelectionChange() {\n this.selectedValues.set(this.selection?.selected ?? []);\n }\n\n onRowClick(row: any) {\n const interaction = this.config().interaction;\n if (!interaction) return;\n\n if (interaction.type === 'click') {\n interaction.onClick(row);\n } else if (interaction.type === 'select') {\n this.selection?.toggle(row);\n }\n this.onSelectionChange();\n }\n\n isAllSelected() {\n if (!this.selection) return false;\n const numSelected = this.selection.selected.length;\n const numRows = this.dataResource.value()?.data?.length ?? 0;\n return numSelected === numRows;\n }\n\n toggleAllRows() {\n if (!this.selection) return;\n if (this.isAllSelected()) {\n this.selection.clear();\n } else {\n this.selection.select(...(this.dataResource.value()?.data ?? []));\n }\n this.onSelectionChange();\n }\n\n checkboxLabel(row?: any): string {\n if (!this.selection) return '';\n if (!row) {\n return `${this.isAllSelected() ? 'deselect' : 'select'} all`;\n }\n return `${this.selection.isSelected(row) ? 'deselect' : 'select'} row ${row.position + 1}`;\n }\n\n protected isBoolean = (val: unknown) => typeof val === 'boolean';\n}\n","<div class=\"table-container\">\n @if (dataResource.isLoading() || dataResource.error()) {\n <div class=\"shade\">\n @if (dataResource.isLoading()) {\n <mat-spinner />\n }\n @if (dataResource.error()) {\n <div class=\"msg-bkgd\">\n <div class=\"error-msg\">There was an error retrieving this data</div>\n <button mat-button (click)=\"this.dataResource.refresh()\">\n Retry\n </button>\n </div>\n }\n </div>\n }\n\n @if (config().showSearch) {\n <mat-form-field class=\"search\" subscriptSizing=\"dynamic\">\n <input matInput [(ngModel)]=\"search\" placeholder=\"Find...\" />\n @if (search()) {\n <button\n matSuffix\n mat-icon-button\n aria-label=\"Clear\"\n (click)=\"search.set('')\"\n >\n <mat-icon>close</mat-icon>\n </button>\n }\n </mat-form-field>\n }\n\n <div class=\"other-controls\">\n <ng-content />\n </div>\n\n <table\n mat-table\n [dataSource]=\"dataResource.value()?.data ?? []\"\n matSort\n [matSortActive]=\"sortBy()\"\n [matSortDirection]=\"sortDirection()\"\n (matSortChange)=\"onSortChange($event)\"\n matSortDisableClear\n >\n @if (selection) {\n <ng-container matColumnDef=\"table-checkbox-column\">\n <mat-header-cell *matHeaderCellDef>\n @if (selection.isMultipleSelection()) {\n <mat-checkbox\n (change)=\"$event ? toggleAllRows() : null\"\n [checked]=\"selection.hasValue() && isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n [aria-label]=\"checkboxLabel()\"\n />\n }\n </mat-header-cell>\n <mat-cell *matCellDef=\"let row\">\n <mat-checkbox\n (click)=\"$event.stopPropagation()\"\n (change)=\"$event ? onRowClick(row) : null\"\n [checked]=\"selection.isSelected(row)\"\n [aria-label]=\"checkboxLabel(row)\"\n />\n </mat-cell>\n </ng-container>\n }\n\n @for (key of columnKeys; track key) {\n <ng-container [matColumnDef]=\"key\">\n <mat-header-cell\n *matHeaderCellDef\n mat-sort-header\n [disabled]=\"!config().columns[key].allowSort\"\n >\n <div class=\"header\">\n {{ config().columns[key].header || key }}\n @if (config().columns[key].individualFilter) {\n <mat-form-field appearance=\"outline\" subscriptSizing=\"dynamic\">\n <input\n matInput\n (click)=\"$event.stopPropagation()\"\n [ngModel]=\"filters()[key] || ''\"\n (ngModelChange)=\"onFilterChange(key, $event)\"\n placeholder=\"Filter...\"\n />\n @if (filters()[key]) {\n <button\n matSuffix\n mat-icon-button\n aria-label=\"Clear\"\n (click)=\"onFilterChange(key, '')\"\n >\n <mat-icon>close</mat-icon>\n </button>\n }\n </mat-form-field>\n }\n </div>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let row\">\n @if (isBoolean(row[key])) {\n <mat-checkbox [checked]=\"row[key]\" [disabled]=\"true\" />\n } @else {\n {{ row[key] }}\n }\n </mat-cell>\n </ng-container>\n }\n\n <mat-header-row *matHeaderRowDef=\"columnsToDisplay\"></mat-header-row>\n @if (config().interaction) {\n <mat-row\n matRipple\n (click)=\"onRowClick(row)\"\n *matRowDef=\"let row; columns: columnsToDisplay\"\n ></mat-row>\n } @else {\n <mat-row *matRowDef=\"let row; columns: columnsToDisplay\"></mat-row>\n }\n\n <tr class=\"mat-row\" *matNoDataRow>\n <td class=\"mat-cell\">\n <div class=\"no-data\">\n No data found\n <button mat-button (click)=\"this.dataResource.refresh()\">\n Retry\n </button>\n </div>\n </td>\n </tr>\n </table>\n\n @if (!config().pagination.hideControls) {\n <mat-paginator\n [length]=\"dataResource.value()?.totalCount\"\n [showFirstLastButtons]=\"true\"\n [pageSize]=\"config().pagination.pageSize\"\n [hidePageSize]=\"true\"\n [pageIndex]=\"pageIndex()\"\n [disabled]=\"dataResource.isLoading()\"\n (page)=\"onPaginationChange($event)\"\n />\n }\n</div>\n","import { Component, inject } from '@angular/core';\nimport { MatButtonModule } from '@angular/material/button';\nimport { MatDialogRef, MatDialogModule, MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog';\n\ntype ConfirmationConfig = {\n action?: string;\n title?: string;\n description?: string;\n buttons?: string[];\n};\n\n@Component({\n selector: 'fhss-confirmation',\n imports: [MatDialogModule, MatButtonModule],\n templateUrl: './confirmation.dialog.html',\n styleUrl: './confirmation.dialog.scss',\n})\nexport class ConfirmationDialog {\n readonly dialogRef = inject(MatDialogRef<ConfirmationDialog>);\n readonly data: ConfirmationConfig = inject(MAT_DIALOG_DATA);\n\n static open(config: ConfirmationConfig, afterClosed: (result: any) => void) {\n const dialogRef = inject(MatDialog).open(ConfirmationDialog, {\n data: config,\n });\n dialogRef.afterClosed().subscribe(afterClosed);\n }\n}\n","<h2 mat-dialog-title>{{ data.title ?? \"Are you sure?\"}}</h2>\n\n<mat-dialog-content>\n @if (data.description) {\n <p>{{ data.description }}</p>\n } @else {\n <p>Are you sure you would like to {{ data.action ?? 'continue' }}?</p>\n }</mat-dialog-content\n>\n\n<mat-dialog-actions>\n @if (data.buttons) { @for (name of data.buttons; track name) {\n <button mat-button [mat-dialog-close]=\"name\">{{name}}</button>\n } } @else {\n <button mat-button [mat-dialog-close]=\"false\">No</button>\n <button mat-button [mat-dialog-close]=\"true\">Yes</button>\n }\n</mat-dialog-actions>\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","import { inject } from '@angular/core';\nimport { CanActivateFn } from '@angular/router';\nimport { AuthService } from '../../services/auth/auth.service';\n\n/**\n * This guard checks if the client is authenticated, redirecting to login if not.\n */\nexport const authGuard: CanActivateFn = async (route, state) => {\n const authService = inject(AuthService);\n const usr = await authService.whoAmI();\n if (!usr) {\n authService.login(state.url);\n return false;\n }\n return true;\n};\n","import { CanActivateFn, RedirectCommand, Router } from '@angular/router';\nimport { AuthService } from '../../services/auth/auth.service';\nimport { inject } from '@angular/core';\n\n/**\n * Factory function to create a permission-based route guard.\n *\n * This guard checks if the current user has the required permissions to access a route.\n * If the user is not authenticated, they are redirected to the login page.\n * If the user lacks the necessary permissions, they are redirected to the `/forbidden` page.\n *\n * @param requiredPermissions - An array of permission strings that the user must have.\n * @param haveAll - If `true`, the user must have all required permissions. If `false` or omitted, the user must have at least one of the required permissions.\n * @returns A `CanActivateFn` function to be used as a route guard.\n * \n * @example\n * ```typescript\n * const routes: Routes = [\n * {\n * path: 'admin',\n * component: AdminPage,\n * canActivate: [permissionGuardFactory(['manage-users', 'see-data'], true)],\n * },\n * ];\n * ```\n */\nexport const permissionGuardFactory: <Permission extends string>(\n requiredPermissions: Permission[],\n haveAll?: boolean,\n) => CanActivateFn = (reqPerms, haveAll) => {\n return async (_route, state) => {\n const router = inject(Router);\n const authService = inject(AuthService);\n\n const user = await authService.whoAmI();\n if (!user) {\n authService.login(state.url);\n return false;\n }\n\n const hasAccess = reqPerms[haveAll ? 'every' : 'some']((role) =>\n authService.effectivePermissions().has(role),\n );\n\n return hasAccess || new RedirectCommand(router.parseUrl('/forbidden'));\n };\n};\n","import { Component } from '@angular/core';\nimport { MatButtonModule } from '@angular/material/button';\n\n@Component({\n selector: 'fhss-auth-error',\n imports: [MatButtonModule],\n templateUrl: './auth-error.page.html',\n styleUrl: './auth-error.page.scss'\n})\nexport class AuthErrorPage {}\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. If the issue persists, contact the web team at <a href=\"mailto:fhss-web@byu.edu\">fhss-web&#64;byu.edu</a></p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>","import { Component } from '@angular/core';\nimport { MatButtonModule } from '@angular/material/button';\n\n@Component({\n selector: 'fhss-forbidden',\n imports: [MatButtonModule],\n templateUrl: './forbidden.page.html',\n styleUrl: './forbidden.page.scss',\n})\nexport class ForbiddenPage {}\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>","import { Component } from '@angular/core';\nimport { MatButtonModule } from '@angular/material/button';\n\n@Component({\n selector: 'fhss-not-found',\n imports: [MatButtonModule],\n templateUrl: './not-found.page.html',\n styleUrl: './not-found.page.scss',\n})\nexport class NotFoundPage {}\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>","import { Component } from '@angular/core';\nimport { MatButtonModule } from '@angular/material/button';\n\n@Component({\n selector: 'fhss-server-error',\n imports: [MatButtonModule],\n templateUrl: './server-error.page.html',\n styleUrl: './server-error.page.scss',\n})\nexport class ServerErrorPage {}\n","<div class=\"container\">\n <div class=\"server-error\">\n <img src=\"/error-duck.png\" alt=\"\" />\n <h1>500 - Internal Server Error</h1>\n <p>We ran into an unexpected error! If the issue persists, contact the web team at <a href=\"mailto:fhss-web@byu.edu\">fhss-web&#64;byu.edu</a></p>\n <a mat-flat-button href=\"/\">Go back to home</a>\n </div>\n</div>","import { computed, effect, EffectRef, inject, Injector, signal, untracked } from '@angular/core';\nimport { FetchSignal, FetchSignalFactory, FetchSignalOptions, FetchSignalRequest, FetchSignalStatus, HttpMethod, Json, ResponseTransformer } from './fetch-signal.types';\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, ErrorResponse, RawResponse>(\n request: () => FetchSignalRequest,\n method: HttpMethod,\n transform: ResponseTransformer<Response>,\n options?: FetchSignalOptions<Response, RawResponse>,\n): FetchSignal<Response, ErrorResponse> {\n // Use a computed signal so that any changes to Signals in the request object trigger updates.\n const currentRequest = computed<FetchSignalRequest>(request);\n\n const value = signal<Response | undefined>(options?.defaultValue, { equal: options?.equal });\n const errorResponse = signal<ErrorResponse | undefined>(undefined);\n const isLoading = signal<boolean>(false);\n const statusCode = signal<number | undefined>(undefined);\n const headers = signal<Record<string, string> | undefined>(undefined);\n\n const status = signal<FetchSignalStatus>('idle');\n const error = signal<Error | undefined>(undefined);\n\n const injector = inject(Injector);\n let effectRef: EffectRef | undefined = undefined;\n if (options?.autoRefresh) {\n effectRef = effect((onCleanup) => {\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, true);\n }, { injector: options?.injector || injector});\n }\n\n const refresh = async (\n abortSignal?: AbortSignal,\n keepLoadingThroughAbort?: boolean,\n ) => {\n // if the fetchSignal has been destroyed, do nothing\n if (untracked(status) === 'destroyed') return;\n\n // Reset signals for a fresh request.\n isLoading.set(true);\n errorResponse.set(undefined);\n statusCode.set(undefined);\n headers.set(undefined);\n status.set('loading');\n error.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)\n searchParams.append(key, String(params[key]));\n }\n uri += (uri.includes('?') ? '&' : '?') + searchParams.toString();\n }\n \n // Filter out undefined header values\n const filteredHeaders = requestHeaders\n ? Object.fromEntries(\n Object.entries(requestHeaders).filter(\n ([, value]) => value !== undefined\n ) as [string, string][]\n )\n : undefined;\n \n try {\n // send the request\n const response = await fetch(uri, {\n method,\n headers: filteredHeaders,\n // Only include a body if one is provided.\n body: body,\n signal: abortSignal,\n });\n\n // set the status code\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 status.set('resolved');\n } else {\n // try to parse the error as json\n try {\n errorResponse.set(await response.json());\n value.set(undefined);\n status.set('resolved');\n } catch {\n throw new Error('Unable to parse error response.')\n }\n }\n } catch (err: any) {\n if (err instanceof Error) {\n // if the fetch request was aborted\n // we check if we would like to continue loading (the next request)\n // if so then we just leave this refresh in an undefined state\n // else we error as usual\n if (err.name === 'AbortError' && keepLoadingThroughAbort) {\n return;\n }\n error.set(err);\n } else {\n // Fallback for non-Error values\n error.set(new Error(String(err)));\n }\n value.set(undefined);\n status.set('error');\n }\n isLoading.set(false);\n };\n\n const destroy = () => {\n // if the fetchSignal has been destroyed, do nothing\n if (status() === 'destroyed') return;\n\n if (effectRef) {\n effectRef.destroy();\n }\n status.set('destroyed');\n value.set(undefined);\n errorResponse.set(undefined);\n isLoading.set(false);\n statusCode.set(undefined);\n headers.set(undefined);\n error.set(undefined);\n }\n\n return {\n value,\n errorResponse,\n isLoading,\n statusCode,\n headers,\n status,\n error,\n refresh,\n destroy\n };\n}\n\n//\n// Helpers for attaching response transforms.\n//\nconst createHelper = <Response, Error>(method: HttpMethod, transform: ResponseTransformer<Response>) =>\n (request: () => FetchSignalRequest, options?: FetchSignalOptions<Response, any>): FetchSignal<Response, Error> =>\n createFetchSignal<Response, Error, any>(request, method, transform, options);\n\n// Transforms\nconst jsonTransformer: ResponseTransformer<any> = (response: Response) => response.json();\nconst textTransformer: ResponseTransformer<string> = (response: Response) => response.text();\nconst blobTransformer: ResponseTransformer<Blob> = (response: Response) => response.blob();\nconst arrayBufferTransformer: ResponseTransformer<ArrayBuffer> = (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(\n 'GET',\n jsonTransformer,\n) 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(\n 'POST',\n jsonTransformer,\n) 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(\n 'PUT',\n jsonTransformer,\n) 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(\n 'PATCH',\n jsonTransformer,\n) 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(\n 'DELETE',\n jsonTransformer,\n) 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 };\nexport type { Json };\n\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/fhss-table/fhss-table.component'\nexport * from './lib/components/confirmation/confirmation.dialog'\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/permission/permission.guard'\n\n/**\n * Pages\n */\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'\nexport * from './lib/pages/server-error/server-error.page'\n\n/**\n * Services\n */\nexport * from './lib/services/auth/auth.service'\n\n/**\n * Signals\n */\nexport * from './lib/signals/trpcResource/trpcResource'\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":["i6","i2","i1"],"mappings":"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"}
@@ -1,27 +1,34 @@
1
1
  import { AuthService } from '../../services/auth/auth.service';
2
2
  import * as i0 from "@angular/core";
3
+ type MenuItemBase<T extends string> = {
4
+ text: string;
5
+ requiredPermissions?: T[];
6
+ };
3
7
  type HeaderLink = {
4
8
  text: string;
5
9
  path: string;
6
10
  };
7
- type HeaderMenu = HeaderLink | {
8
- text: string;
9
- items: HeaderLink[];
11
+ type MenuLink<T extends string> = MenuItemBase<T> & {
12
+ path: string;
13
+ };
14
+ type MenuDropdown<T extends string> = MenuItemBase<T> & {
15
+ items: MenuLink<T>[];
10
16
  };
11
- export type HeaderConfig = {
17
+ type HeaderMenu<T extends string> = MenuLink<T> | MenuDropdown<T>;
18
+ export type HeaderConfig<Permission extends string> = {
12
19
  title: HeaderLink;
13
20
  subtitle?: HeaderLink;
14
21
  breadcrumbs?: HeaderLink[];
15
- menu?: HeaderMenu[];
22
+ menu?: HeaderMenu<Permission>[];
16
23
  };
17
24
  export declare class ByuHeaderComponent {
18
25
  auth: AuthService;
19
- config: import("@angular/core").InputSignal<HeaderConfig | undefined>;
20
- isHeaderLink(item: HeaderMenu): item is HeaderLink;
21
- openDropdownText: string | null;
22
- toggleDropdown(text: string): void;
23
- isOpen(text: string): boolean;
26
+ config: import("@angular/core").InputSignal<HeaderConfig<string>>;
27
+ openDropdown: MenuDropdown<string> | null;
28
+ isMenuLink(item: HeaderMenu<string>): item is MenuLink<string>;
29
+ isMenuItemVisible(item: HeaderMenu<string>): boolean;
30
+ toggleDropdown(dropdown: MenuDropdown<string>): void;
24
31
  static ɵfac: i0.ɵɵFactoryDeclaration<ByuHeaderComponent, never>;
25
- static ɵcmp: i0.ɵɵComponentDeclaration<ByuHeaderComponent, "byu-header", never, { "config": { "alias": "config"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
32
+ static ɵcmp: i0.ɵɵComponentDeclaration<ByuHeaderComponent, "byu-header", never, { "config": { "alias": "config"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
26
33
  }
27
34
  export {};
@@ -1,13 +1,16 @@
1
1
  import { MatDialogRef } from '@angular/material/dialog';
2
2
  import * as i0 from "@angular/core";
3
+ type ConfirmationConfig = {
4
+ action?: string;
5
+ title?: string;
6
+ description?: string;
7
+ buttons?: string[];
8
+ };
3
9
  export declare class ConfirmationDialog {
4
10
  readonly dialogRef: MatDialogRef<any, any>;
5
- readonly data: {
6
- action?: string;
7
- title?: string;
8
- description?: string;
9
- buttons?: string[];
10
- };
11
+ readonly data: ConfirmationConfig;
12
+ static open(config: ConfirmationConfig, afterClosed: (result: any) => void): void;
11
13
  static ɵfac: i0.ɵɵFactoryDeclaration<ConfirmationDialog, never>;
12
14
  static ɵcmp: i0.ɵɵComponentDeclaration<ConfirmationDialog, "fhss-confirmation", never, {}, {}, never, never, true, never>;
13
15
  }
16
+ export {};
@@ -9,13 +9,15 @@ type WhoAmI = {
9
9
  };
10
10
  export declare class AuthService {
11
11
  readonly router: Router;
12
+ readonly FhssConfig: import("@fhss-web-team/frontend-utils").FhssConfig;
12
13
  constructor();
13
14
  authenticated: import("@angular/core").WritableSignal<boolean>;
14
15
  userId: import("@angular/core").WritableSignal<string | undefined>;
15
16
  preferredFirstName: import("@angular/core").WritableSignal<string | undefined>;
16
17
  preferredLastName: import("@angular/core").WritableSignal<string | undefined>;
17
- roles: import("@angular/core").WritableSignal<string[] | undefined>;
18
- permissions: import("@angular/core").WritableSignal<string[] | undefined>;
18
+ roles: import("@angular/core").WritableSignal<string[]>;
19
+ permissions: import("@angular/core").WritableSignal<string[]>;
20
+ effectivePermissions: import("@angular/core").Signal<Set<string>>;
19
21
  preferredName: import("@angular/core").Signal<string>;
20
22
  private resetAuthState;
21
23
  private setUser;
@@ -10,6 +10,10 @@ export type TrpcResourceOptions<TOutput> = {
10
10
  * Whether or not the request should be fetched reactively when the request updates.
11
11
  */
12
12
  autoRefresh?: boolean;
13
+ /**
14
+ * Optional computation that will be used in a `linkedSignal` definition of the `trpcResource` value.
15
+ */
16
+ valueComputation?: () => NoInfer<TOutput>;
13
17
  /**
14
18
  * Value that the `trpcResource` will take when in the Idle or Error states.
15
19
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fhss-web-team/frontend-utils",
3
- "version": "2.3.4",
3
+ "version": "3.0.0",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^19.2.0",
6
6
  "@angular/core": "^19.2.0"