@fhss-web-team/frontend-utils 2.4.0 → 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, linkedSignal, effect, Injector, model, 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,7 +25,7 @@ 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
@@ -37,8 +37,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
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);
@@ -103,26 +109,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", 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
125
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ByuHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
121
- 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: 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"] }] });
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
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) {
@@ -326,6 +331,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
326
331
  class ConfirmationDialog {
327
332
  dialogRef = inject((MatDialogRef));
328
333
  data = inject(MAT_DIALOG_DATA);
334
+ static open(config, afterClosed) {
335
+ const dialogRef = inject(MatDialog).open(ConfirmationDialog, {
336
+ data: config,
337
+ });
338
+ dialogRef.afterClosed().subscribe(afterClosed);
339
+ }
329
340
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ConfirmationDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
330
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
  }
@@ -334,8 +345,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
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
403
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AuthErrorPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
399
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", 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. 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 .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" }] });
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
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. 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 .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
412
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ForbiddenPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
408
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", 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" }] });
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
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
421
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NotFoundPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
417
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", 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" }] });
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
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
430
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ServerErrorPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
426
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", 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" }] });
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
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, 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 {\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. 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 { 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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fhss-web-team/frontend-utils",
3
- "version": "2.4.0",
3
+ "version": "3.0.0",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^19.2.0",
6
6
  "@angular/core": "^19.2.0"