@fhss-web-team/frontend-utils 1.6.1 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/fhss-web-team-frontend-utils.mjs +146 -56
- package/fesm2022/fhss-web-team-frontend-utils.mjs.map +1 -1
- package/lib/components/user-management/user-management.component.d.ts +1 -1
- package/lib/components/user-management/user-management.service.d.ts +1 -1
- package/lib/signals/fetch-signal/fetch-signal.d.ts +2 -350
- package/lib/signals/fetch-signal/fetch-signal.types.d.ts +460 -0
- package/lib/signals/trpcResource/trpcResource.d.ts +11 -0
- package/lib/signals/trpcResource/trpcResource.types.d.ts +65 -0
- package/package.json +1 -1
- package/public-api.d.ts +1 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Component, InjectionToken, inject, Injector, signal, afterNextRender, effect, computed, Injectable, input, ViewChild } from '@angular/core';
|
|
2
|
+
import { Component, InjectionToken, inject, Injector, signal, afterNextRender, effect, computed, Injectable, input, untracked, ViewChild } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/router';
|
|
4
4
|
import { Router, RouterModule, RedirectCommand } from '@angular/router';
|
|
5
5
|
import { KEYCLOAK_EVENT_SIGNAL, KeycloakEventType, typeEventArgs } from 'keycloak-angular';
|
|
@@ -19,6 +19,7 @@ import * as i6 from '@angular/material/select';
|
|
|
19
19
|
import { MatSelectModule } from '@angular/material/select';
|
|
20
20
|
import * as i1$2 from '@angular/material/button';
|
|
21
21
|
import { MatButtonModule } from '@angular/material/button';
|
|
22
|
+
import { isTRPCClientError } from '@trpc/client';
|
|
22
23
|
|
|
23
24
|
class ByuFooterComponent {
|
|
24
25
|
currentYear = new Date().getFullYear(); // Automatically updates the year
|
|
@@ -271,42 +272,43 @@ const debounce = (callback, wait) => {
|
|
|
271
272
|
};
|
|
272
273
|
};
|
|
273
274
|
|
|
274
|
-
/**
|
|
275
|
-
* Reads the underlying value from a MaybeSignal.
|
|
276
|
-
*
|
|
277
|
-
* This function is designed for use with reactive APIs (like fetchSignal) where it's important
|
|
278
|
-
* that Angular's computed methods register the dependency. If the input is a Signal, invoking it
|
|
279
|
-
* not only returns its current value but also tracks the signal for reactivity. If the input is
|
|
280
|
-
* a plain value, it simply returns that value.
|
|
281
|
-
*
|
|
282
|
-
* @template T The type of the value.
|
|
283
|
-
* @param value A plain value or a Signal that yields the value.
|
|
284
|
-
* @returns The current value, with dependency tracking enabled if the input is a Signal.
|
|
285
|
-
*/
|
|
286
|
-
function readMaybeSignal(value) {
|
|
287
|
-
return typeof value === 'function' ? value() : value;
|
|
288
|
-
}
|
|
289
275
|
/**
|
|
290
276
|
* Creates a reactive fetch signal.
|
|
291
277
|
*
|
|
292
278
|
* The request function can include Signals for properties. These are unwrapped in the refresh function.
|
|
293
279
|
*/
|
|
294
|
-
function createFetchSignal(request, method, transform,
|
|
280
|
+
function createFetchSignal(request, method, transform, options) {
|
|
295
281
|
// Use a computed signal so that any changes to Signals in the request object trigger updates.
|
|
296
282
|
const currentRequest = computed(request);
|
|
297
|
-
const value = signal(
|
|
298
|
-
const
|
|
283
|
+
const value = signal(options?.defaultValue, { equal: options?.equal });
|
|
284
|
+
const errorResponse = signal(undefined);
|
|
299
285
|
const isLoading = signal(false);
|
|
300
|
-
const error = signal(undefined);
|
|
301
286
|
const statusCode = signal(undefined);
|
|
302
287
|
const headers = signal(undefined);
|
|
288
|
+
const status = signal('idle');
|
|
289
|
+
const error = signal(undefined);
|
|
290
|
+
const injector = inject(Injector);
|
|
291
|
+
let effectRef = undefined;
|
|
292
|
+
if (options?.autoRefresh) {
|
|
293
|
+
effectRef = effect((onCleanup) => {
|
|
294
|
+
// pass abort signal to refresh on cleanup of effect
|
|
295
|
+
const controller = new AbortController();
|
|
296
|
+
onCleanup(() => controller.abort());
|
|
297
|
+
// call refresh with this abort controller
|
|
298
|
+
refresh(controller.signal, true);
|
|
299
|
+
}, { injector: options?.injector || injector });
|
|
300
|
+
}
|
|
303
301
|
const refresh = async (abortSignal, keepLoadingThroughAbort) => {
|
|
302
|
+
// if the fetchSignal has been destroyed, do nothing
|
|
303
|
+
if (untracked(status) === 'destroyed')
|
|
304
|
+
return;
|
|
304
305
|
// Reset signals for a fresh request.
|
|
305
|
-
value.set(undefined);
|
|
306
306
|
isLoading.set(true);
|
|
307
|
-
|
|
307
|
+
errorResponse.set(undefined);
|
|
308
308
|
statusCode.set(undefined);
|
|
309
309
|
headers.set(undefined);
|
|
310
|
+
status.set('loading');
|
|
311
|
+
error.set(undefined);
|
|
310
312
|
// Unwrap the current request.
|
|
311
313
|
const req = currentRequest();
|
|
312
314
|
const url = req.url;
|
|
@@ -323,14 +325,20 @@ function createFetchSignal(request, method, transform, autoRefresh) {
|
|
|
323
325
|
}
|
|
324
326
|
uri += (uri.includes('?') ? '&' : '?') + searchParams.toString();
|
|
325
327
|
}
|
|
328
|
+
// Filter out undefined header values
|
|
329
|
+
const filteredHeaders = requestHeaders
|
|
330
|
+
? Object.fromEntries(Object.entries(requestHeaders).filter(([, value]) => value !== undefined))
|
|
331
|
+
: undefined;
|
|
326
332
|
try {
|
|
333
|
+
// send the request
|
|
327
334
|
const response = await fetch(uri, {
|
|
328
335
|
method,
|
|
329
|
-
headers:
|
|
336
|
+
headers: filteredHeaders,
|
|
330
337
|
// Only include a body if one is provided.
|
|
331
|
-
body: body
|
|
338
|
+
body: body,
|
|
332
339
|
signal: abortSignal,
|
|
333
340
|
});
|
|
341
|
+
// set the status code
|
|
334
342
|
statusCode.set(response.status);
|
|
335
343
|
// Extract response headers.
|
|
336
344
|
const headersObj = {};
|
|
@@ -341,57 +349,71 @@ function createFetchSignal(request, method, transform, autoRefresh) {
|
|
|
341
349
|
// if the response is ok, transform the body
|
|
342
350
|
if (response.ok) {
|
|
343
351
|
value.set(await transform(response));
|
|
344
|
-
|
|
352
|
+
status.set('resolved');
|
|
345
353
|
}
|
|
346
354
|
else {
|
|
347
355
|
// try to parse the error as json
|
|
348
|
-
// set the error to null if this fails
|
|
349
356
|
try {
|
|
350
|
-
|
|
357
|
+
errorResponse.set(await response.json());
|
|
358
|
+
value.set(undefined);
|
|
359
|
+
status.set('resolved');
|
|
351
360
|
}
|
|
352
361
|
catch {
|
|
353
|
-
error.
|
|
354
|
-
}
|
|
355
|
-
finally {
|
|
356
|
-
value.set(null);
|
|
362
|
+
throw new Error('Unable to parse error response.');
|
|
357
363
|
}
|
|
358
364
|
}
|
|
359
365
|
}
|
|
360
366
|
catch (err) {
|
|
361
|
-
if (err
|
|
362
|
-
|
|
367
|
+
if (err instanceof Error) {
|
|
368
|
+
// if the fetch request was aborted
|
|
369
|
+
// we check if we would like to continue loading (the next request)
|
|
370
|
+
// if so then we just leave this refresh in an undefined state
|
|
371
|
+
// else we error as usual
|
|
372
|
+
if (err.name === 'AbortError' && keepLoadingThroughAbort) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
error.set(err);
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
// Fallback for non-Error values
|
|
379
|
+
error.set(new Error(String(err)));
|
|
363
380
|
}
|
|
364
|
-
|
|
365
|
-
|
|
381
|
+
value.set(undefined);
|
|
382
|
+
status.set('error');
|
|
366
383
|
}
|
|
367
|
-
persistentValue.set(value());
|
|
368
384
|
isLoading.set(false);
|
|
369
385
|
};
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
386
|
+
const destroy = () => {
|
|
387
|
+
// if the fetchSignal has been destroyed, do nothing
|
|
388
|
+
if (status() === 'destroyed')
|
|
389
|
+
return;
|
|
390
|
+
if (effectRef) {
|
|
391
|
+
effectRef.destroy();
|
|
392
|
+
}
|
|
393
|
+
status.set('destroyed');
|
|
394
|
+
value.set(undefined);
|
|
395
|
+
errorResponse.set(undefined);
|
|
396
|
+
isLoading.set(false);
|
|
397
|
+
statusCode.set(undefined);
|
|
398
|
+
headers.set(undefined);
|
|
399
|
+
error.set(undefined);
|
|
400
|
+
};
|
|
381
401
|
return {
|
|
382
402
|
value,
|
|
383
|
-
|
|
403
|
+
errorResponse,
|
|
384
404
|
isLoading,
|
|
385
|
-
error,
|
|
386
405
|
statusCode,
|
|
387
406
|
headers,
|
|
407
|
+
status,
|
|
408
|
+
error,
|
|
388
409
|
refresh,
|
|
410
|
+
destroy
|
|
389
411
|
};
|
|
390
412
|
}
|
|
391
413
|
//
|
|
392
|
-
// Helpers for attaching response transforms
|
|
414
|
+
// Helpers for attaching response transforms.
|
|
393
415
|
//
|
|
394
|
-
const createHelper = (method, transform) => (request,
|
|
416
|
+
const createHelper = (method, transform) => (request, options) => createFetchSignal(request, method, transform, options);
|
|
395
417
|
// Transforms
|
|
396
418
|
const jsonTransformer = (response) => response.json();
|
|
397
419
|
const textTransformer = (response) => response.text();
|
|
@@ -462,9 +484,9 @@ class UserManagementService {
|
|
|
462
484
|
created_before: createdBefore && createdBefore().toISOString(),
|
|
463
485
|
},
|
|
464
486
|
headers: {
|
|
465
|
-
Authorization: this.auth.bearerToken()
|
|
487
|
+
Authorization: this.auth.bearerToken()
|
|
466
488
|
}
|
|
467
|
-
}), true);
|
|
489
|
+
}), { autoRefresh: true });
|
|
468
490
|
}
|
|
469
491
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: UserManagementService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
470
492
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: UserManagementService, providedIn: 'root' });
|
|
@@ -503,11 +525,11 @@ class UserManagementComponent {
|
|
|
503
525
|
});
|
|
504
526
|
}
|
|
505
527
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: UserManagementComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
506
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: UserManagementComponent, isStandalone: true, selector: "app-user-management", viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true }], ngImport: i0, template: "<mat-form-field>\n <mat-label>Search for User</mat-label>\n <input matInput [(ngModel)]=\"search\">\n</mat-form-field>\n<mat-form-field>\n <mat-label>Account Type</mat-label>\n <mat-select multiple [(ngModel)]=\"accountTypes\">\n @for (accountType of accountTypeOptions; track accountType) {\n <mat-option [value]=\"accountType\">{{accountType}}</mat-option>\n }\n </mat-select>\n</mat-form-field>\n\n<table mat-table [dataSource]=\"getUsers.
|
|
528
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.10", type: UserManagementComponent, isStandalone: true, selector: "app-user-management", viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true }], ngImport: i0, template: "<mat-form-field>\n <mat-label>Search for User</mat-label>\n <input matInput [(ngModel)]=\"search\">\n</mat-form-field>\n<mat-form-field>\n <mat-label>Account Type</mat-label>\n <mat-select multiple [(ngModel)]=\"accountTypes\">\n @for (accountType of accountTypeOptions; track accountType) {\n <mat-option [value]=\"accountType\">{{accountType}}</mat-option>\n }\n </mat-select>\n</mat-form-field>\n\n<table mat-table [dataSource]=\"getUsers.value()?.data ?? []\" matSort matSortActive=\"netId\" matSortDisableClear matSortDirection=\"asc\">\n <!-- NetID Column -->\n <ng-container matColumnDef=\"netId\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>NetID</th>\n <td mat-cell *matCellDef=\"let user\">{{user.netId}}</td>\n </ng-container>\n\n <!-- First Name Column -->\n <ng-container matColumnDef=\"preferredFirstName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>First Name</th>\n <td mat-cell *matCellDef=\"let user\">{{user.preferredFirstName}}</td>\n </ng-container>\n\n <!-- Last Name Column -->\n <ng-container matColumnDef=\"preferredLastName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>Last Name</th>\n <td mat-cell *matCellDef=\"let user\">{{user.preferredLastName}}</td>\n </ng-container>\n\n <!-- Roles Column -->\n <ng-container matColumnDef=\"roles\">\n <th mat-header-cell *matHeaderCellDef>Roles</th>\n <td mat-cell *matCellDef=\"let user\">{{user.roles.join(', ')}}</td>\n </ng-container>\n\n <!-- Account Type Column -->\n <ng-container matColumnDef=\"accountType\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>Account Type</th>\n <td mat-cell *matCellDef=\"let user\">{{user.accountType}}</td>\n </ng-container>\n\n <!-- Created Column -->\n <ng-container matColumnDef=\"created\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n Created\n </th>\n <td mat-cell *matCellDef=\"let user\">{{user.created | date}}</td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n</table>\n\n<mat-paginator [length]=\"getUsers.value()?.totalCount\" [pageSize]=\"pageCount()\" [pageSizeOptions]=\"[5, 10, 20]\" showFirstLastButtons></mat-paginator>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i1$1.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i1$1.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i1$1.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i1$1.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i1$1.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i1$1.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i1$1.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i1$1.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i1$1.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i1$1.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i2.MatSort, selector: "[matSort]", inputs: ["matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear", "matSortDisabled"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i2.MatSortHeader, selector: "[mat-sort-header]", inputs: ["mat-sort-header", "arrowPosition", "start", "disabled", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }] });
|
|
507
529
|
}
|
|
508
530
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: UserManagementComponent, decorators: [{
|
|
509
531
|
type: Component,
|
|
510
|
-
args: [{ selector: 'app-user-management', imports: [MatTableModule, MatSortModule, MatPaginatorModule, DatePipe, FormsModule, MatInputModule, MatSelectModule], template: "<mat-form-field>\n <mat-label>Search for User</mat-label>\n <input matInput [(ngModel)]=\"search\">\n</mat-form-field>\n<mat-form-field>\n <mat-label>Account Type</mat-label>\n <mat-select multiple [(ngModel)]=\"accountTypes\">\n @for (accountType of accountTypeOptions; track accountType) {\n <mat-option [value]=\"accountType\">{{accountType}}</mat-option>\n }\n </mat-select>\n</mat-form-field>\n\n<table mat-table [dataSource]=\"getUsers.
|
|
532
|
+
args: [{ selector: 'app-user-management', imports: [MatTableModule, MatSortModule, MatPaginatorModule, DatePipe, FormsModule, MatInputModule, MatSelectModule], template: "<mat-form-field>\n <mat-label>Search for User</mat-label>\n <input matInput [(ngModel)]=\"search\">\n</mat-form-field>\n<mat-form-field>\n <mat-label>Account Type</mat-label>\n <mat-select multiple [(ngModel)]=\"accountTypes\">\n @for (accountType of accountTypeOptions; track accountType) {\n <mat-option [value]=\"accountType\">{{accountType}}</mat-option>\n }\n </mat-select>\n</mat-form-field>\n\n<table mat-table [dataSource]=\"getUsers.value()?.data ?? []\" matSort matSortActive=\"netId\" matSortDisableClear matSortDirection=\"asc\">\n <!-- NetID Column -->\n <ng-container matColumnDef=\"netId\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>NetID</th>\n <td mat-cell *matCellDef=\"let user\">{{user.netId}}</td>\n </ng-container>\n\n <!-- First Name Column -->\n <ng-container matColumnDef=\"preferredFirstName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>First Name</th>\n <td mat-cell *matCellDef=\"let user\">{{user.preferredFirstName}}</td>\n </ng-container>\n\n <!-- Last Name Column -->\n <ng-container matColumnDef=\"preferredLastName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>Last Name</th>\n <td mat-cell *matCellDef=\"let user\">{{user.preferredLastName}}</td>\n </ng-container>\n\n <!-- Roles Column -->\n <ng-container matColumnDef=\"roles\">\n <th mat-header-cell *matHeaderCellDef>Roles</th>\n <td mat-cell *matCellDef=\"let user\">{{user.roles.join(', ')}}</td>\n </ng-container>\n\n <!-- Account Type Column -->\n <ng-container matColumnDef=\"accountType\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>Account Type</th>\n <td mat-cell *matCellDef=\"let user\">{{user.accountType}}</td>\n </ng-container>\n\n <!-- Created Column -->\n <ng-container matColumnDef=\"created\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header>\n Created\n </th>\n <td mat-cell *matCellDef=\"let user\">{{user.created | date}}</td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n</table>\n\n<mat-paginator [length]=\"getUsers.value()?.totalCount\" [pageSize]=\"pageCount()\" [pageSizeOptions]=\"[5, 10, 20]\" showFirstLastButtons></mat-paginator>\n" }]
|
|
511
533
|
}], propDecorators: { paginator: [{
|
|
512
534
|
type: ViewChild,
|
|
513
535
|
args: [MatPaginator]
|
|
@@ -605,6 +627,74 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImpo
|
|
|
605
627
|
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"] }]
|
|
606
628
|
}] });
|
|
607
629
|
|
|
630
|
+
function trpcResource(procedure, input, options) {
|
|
631
|
+
const currentInput = computed(input);
|
|
632
|
+
const value = signal(options?.defaultValue, { equal: options?.equal });
|
|
633
|
+
const error = signal(undefined);
|
|
634
|
+
const isLoading = signal(false);
|
|
635
|
+
const resourceError = signal(undefined);
|
|
636
|
+
const injector = inject(Injector);
|
|
637
|
+
let effectRef = undefined;
|
|
638
|
+
if (options?.autoRefresh) {
|
|
639
|
+
effectRef = effect((onCleanup) => {
|
|
640
|
+
// pass abort signal to refresh on cleanup of effect
|
|
641
|
+
const controller = new AbortController();
|
|
642
|
+
onCleanup(() => controller.abort());
|
|
643
|
+
// call refresh with this abort controller
|
|
644
|
+
// refresh reads currentInput which triggers the effect
|
|
645
|
+
refresh(controller.signal, true);
|
|
646
|
+
}, { injector: options?.injector || injector });
|
|
647
|
+
}
|
|
648
|
+
const refresh = async (abortSignal, keepLoadingThroughAbort = true) => {
|
|
649
|
+
// Reset signals for a fresh request.
|
|
650
|
+
isLoading.set(true);
|
|
651
|
+
error.set(undefined);
|
|
652
|
+
resourceError.set(undefined);
|
|
653
|
+
try {
|
|
654
|
+
value.set(await procedure(currentInput(), {
|
|
655
|
+
signal: abortSignal
|
|
656
|
+
}));
|
|
657
|
+
error.set(undefined);
|
|
658
|
+
}
|
|
659
|
+
catch (err) {
|
|
660
|
+
if (isTRPCClientError(err)) {
|
|
661
|
+
// if the trpc request was aborted
|
|
662
|
+
// we check if we would like to continue loading (the next request)
|
|
663
|
+
// if so then we just leave this refresh in an undefined state
|
|
664
|
+
// else we error as usual
|
|
665
|
+
if (err.cause?.name === 'AbortError' && keepLoadingThroughAbort) {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
error.set(err);
|
|
669
|
+
}
|
|
670
|
+
else if (err instanceof Error) {
|
|
671
|
+
resourceError.set(err);
|
|
672
|
+
}
|
|
673
|
+
else {
|
|
674
|
+
// Fallback for non-Error values
|
|
675
|
+
resourceError.set(new Error(String(err)));
|
|
676
|
+
}
|
|
677
|
+
value.set(options?.defaultValue);
|
|
678
|
+
}
|
|
679
|
+
isLoading.set(false);
|
|
680
|
+
};
|
|
681
|
+
return {
|
|
682
|
+
value,
|
|
683
|
+
error,
|
|
684
|
+
isLoading,
|
|
685
|
+
resourceError,
|
|
686
|
+
refresh
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
function debugTrpcResource(_trpcResource) {
|
|
690
|
+
return {
|
|
691
|
+
value: _trpcResource.value(),
|
|
692
|
+
error: _trpcResource.error(),
|
|
693
|
+
isLoading: _trpcResource.isLoading(),
|
|
694
|
+
resourceError: _trpcResource.resourceError(),
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
|
|
608
698
|
/**
|
|
609
699
|
* Components
|
|
610
700
|
*/
|
|
@@ -613,5 +703,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImpo
|
|
|
613
703
|
* Generated bundle index. Do not edit.
|
|
614
704
|
*/
|
|
615
705
|
|
|
616
|
-
export { AuthCallbackPage, AuthErrorPage, AuthService, ByuFooterComponent, ByuHeaderComponent, FHSS_CONFIG, ForbiddenPage, NotFoundPage, UserManagementComponent, authGuard, debounced, enumToRecord, fetchSignal, provideFhss,
|
|
706
|
+
export { AuthCallbackPage, AuthErrorPage, AuthService, ByuFooterComponent, ByuHeaderComponent, FHSS_CONFIG, ForbiddenPage, NotFoundPage, UserManagementComponent, authGuard, debounced, debugTrpcResource, enumToRecord, fetchSignal, provideFhss, roleGuardFactory };
|
|
617
707
|
//# sourceMappingURL=fhss-web-team-frontend-utils.mjs.map
|