ichec-angular-core 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,7 @@
1
- import { map, catchError, tap, mergeMap, throwError, BehaviorSubject, mergeAll, merge, debounceTime, distinctUntilChanged, Subscription, of, finalize } from 'rxjs';
1
+ import { map, catchError, tap, mergeMap, throwError, BehaviorSubject, mergeAll, of, merge, debounceTime, distinctUntilChanged, Subscription, finalize } from 'rxjs';
2
2
  import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
3
3
  import * as i0 from '@angular/core';
4
4
  import { InjectionToken, inject, Injectable, input, signal, Component, viewChild, computed, output } from '@angular/core';
5
- import { NgIf, Location, NgTemplateOutlet, TitleCasePipe } from '@angular/common';
6
5
  import * as i1 from '@angular/router';
7
6
  import { RouterModule, RouterOutlet, Router, ActivatedRoute } from '@angular/router';
8
7
  import * as i2 from '@angular/material/toolbar';
@@ -17,6 +16,7 @@ import * as i1$1 from '@angular/material/sidenav';
17
16
  import { MatSidenavContent, MatSidenavModule } from '@angular/material/sidenav';
18
17
  import * as i2$2 from '@angular/material/list';
19
18
  import { MatListModule } from '@angular/material/list';
19
+ import { NgIf, Location, NgTemplateOutlet, TitleCasePipe } from '@angular/common';
20
20
  import * as i1$2 from '@angular/forms';
21
21
  import { FormsModule, FormControl, ReactiveFormsModule, FormBuilder } from '@angular/forms';
22
22
  import * as i3$2 from '@angular/material/input';
@@ -53,76 +53,43 @@ class Paginated {
53
53
  results = [];
54
54
  }
55
55
  class Permission {
56
- id = 0;
57
- url = "";
58
- codename = "";
59
56
  static typename = "permission";
60
57
  static plural = "permissions";
61
- constructor(params = {}) {
62
- Object.assign(this, params);
63
- }
64
58
  }
59
+
65
60
  class PortalMember {
66
61
  static typename = "member";
67
62
  static plural = "members";
68
- id = 0;
69
- url = "";
70
- username = "";
71
- email = "";
72
- first_name = "";
73
- last_name = "";
74
- phone = "";
75
- organization = "";
76
- profile_url = "";
77
- all_permissions = [];
78
- constructor(params = {}) {
79
- Object.assign(this, params);
63
+ static getInitials(member) {
64
+ let first_initial = "";
65
+ let second_initial = "";
66
+ if (member.first_name) {
67
+ first_initial = member.first_name[0];
68
+ }
69
+ else {
70
+ first_initial = member.username[0];
71
+ }
72
+ if (member.first_name) {
73
+ second_initial = member.last_name[0];
74
+ }
75
+ const combined = first_initial + second_initial;
76
+ return combined.toUpperCase();
80
77
  }
81
78
  }
79
+
82
80
  class Group {
83
81
  static typename = "group";
84
82
  static plural = "groups";
85
- name = "";
86
- url = "";
87
- id = 0;
88
- constructor(params = {}) {
89
- Object.assign(this, params);
90
- }
91
83
  }
92
84
 
93
- class Address {
94
- static typename = "address";
95
- static plural = "addresses";
96
- id = 0;
97
- url = "";
98
- line1 = "";
99
- line2 = "";
100
- line3 = "";
101
- city = "";
102
- region = "";
103
- postcode = "";
104
- country = "";
105
- country_name = "";
106
- country_flag = "";
107
- members = [];
108
- constructor(params = {}) {
109
- Object.assign(this, params);
110
- }
111
- }
112
85
  class Organization {
113
86
  static typename = "organization";
114
87
  static plural = "organizations";
115
- id = 0;
116
- url = "";
117
- name = "";
118
- acronym = "";
119
- description = "";
120
- address = "";
121
- website = "";
122
- members = [];
123
- constructor(params = {}) {
124
- Object.assign(this, params);
125
- }
88
+ }
89
+
90
+ class Address {
91
+ static typename = "address";
92
+ static plural = "addresses";
126
93
  }
127
94
 
128
95
  class ItemQuery {
@@ -261,7 +228,8 @@ class RestService {
261
228
  }
262
229
 
263
230
  class ItemService extends RestService {
264
- itemType = "";
231
+ typenamePlural = "";
232
+ typename = "";
265
233
  canEdit() {
266
234
  return false;
267
235
  }
@@ -274,11 +242,8 @@ class ItemService extends RestService {
274
242
  canView() {
275
243
  return false;
276
244
  }
277
- instantiateType(_) {
278
- throw new Error('Not Implemented');
279
- }
280
- getItem(id) {
281
- return super.getItem(id).pipe(map(x => this.instantiateType(x)));
245
+ typePlural() {
246
+ return this.typenamePlural ? this.typenamePlural : this.typename + "s";
282
247
  }
283
248
  }
284
249
 
@@ -294,11 +259,8 @@ class UserService extends ItemService {
294
259
  */
295
260
  loggedInUser = new BehaviorSubject(null);
296
261
  _url = PortalMember.plural;
297
- itemType = PortalMember.plural;
262
+ typename = PortalMember.typename;
298
263
  permissions = new Map();
299
- instantiateType(item) {
300
- return new PortalMember(item);
301
- }
302
264
  login(username, password) {
303
265
  console.log("Attempting login");
304
266
  const body = new URLSearchParams();
@@ -392,12 +354,51 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImpor
392
354
  }]
393
355
  }] });
394
356
 
395
- class AddressService extends ItemService {
396
- _url = Address.plural;
397
- itemType = Address.plural;
398
- instantiateType(item) {
399
- return new Address(item);
357
+ class ItemWithUserService extends ItemService {
358
+ userService = inject(UserService);
359
+ userItems = new BehaviorSubject([]);
360
+ canCreate() {
361
+ return this.userService.hasAddPermission(this.permissionName());
362
+ }
363
+ canView() {
364
+ return this.userService.hasViewPermission(this.permissionName());
365
+ }
366
+ canEdit() {
367
+ return this.userService.hasEditPermission(this.permissionName());
368
+ }
369
+ canDelete() {
370
+ return this.userService.hasDeletePermission(this.permissionName());
371
+ }
372
+ permissionName() {
373
+ return this.typename.replace("_", "");
374
+ }
375
+ getUserItems() {
376
+ if (this.userService.loggedInUser.value) {
377
+ return this.getForUser(this.userService.loggedInUser.value);
378
+ }
379
+ return of(new Paginated());
380
+ }
381
+ refreshUserItems(user) {
382
+ if (user) {
383
+ this.getUserItems().subscribe(items => this.userItems.next(items.results));
384
+ }
385
+ else {
386
+ this.userItems.next([]);
387
+ }
400
388
  }
389
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: ItemWithUserService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
390
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: ItemWithUserService, providedIn: 'root' });
391
+ }
392
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: ItemWithUserService, decorators: [{
393
+ type: Injectable,
394
+ args: [{
395
+ providedIn: 'root'
396
+ }]
397
+ }] });
398
+
399
+ class AddressService extends ItemWithUserService {
400
+ _url = Address.plural;
401
+ typename = Address.typename;
401
402
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: AddressService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
402
403
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: AddressService, providedIn: 'root' });
403
404
  }
@@ -408,26 +409,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImpor
408
409
  }]
409
410
  }] });
410
411
 
411
- class GroupService extends ItemService {
412
- userService = inject(UserService);
413
- userItems = new BehaviorSubject([]);
412
+ class GroupService extends ItemWithUserService {
414
413
  _url = Group.plural;
415
- itemType = Group.plural;
414
+ typename = Group.typename;
416
415
  constructor() {
417
416
  super();
418
417
  this.userService.loggedInUser.subscribe(user => this.refreshUserItems(user));
419
418
  }
420
- refreshUserItems(user) {
421
- if (user) {
422
- this.getForUser(user).subscribe(items => this.userItems.next(items.results));
423
- }
424
- else {
425
- this.userItems.next([]);
426
- }
427
- }
428
- instantiateType(item) {
429
- return new Group(item);
430
- }
431
419
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: GroupService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
432
420
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: GroupService, providedIn: 'root' });
433
421
  }
@@ -438,25 +426,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImpor
438
426
  }]
439
427
  }], ctorParameters: () => [] });
440
428
 
441
- class OrganizationService extends ItemService {
429
+ class OrganizationService extends ItemWithUserService {
442
430
  _url = Organization.plural;
443
- itemType = Organization.plural;
444
- userService = inject(UserService);
445
- instantiateType(item) {
446
- return new Organization(item);
447
- }
448
- canCreate() {
449
- return this.userService.hasAddPermission(Organization.typename);
450
- }
451
- canView() {
452
- return this.userService.hasViewPermission(Organization.typename);
453
- }
454
- canEdit() {
455
- return this.userService.hasEditPermission(Organization.typename);
456
- }
457
- canDelete() {
458
- return this.userService.hasDeletePermission(Organization.typename);
459
- }
431
+ typename = Organization.typename;
460
432
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: OrganizationService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
461
433
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: OrganizationService, providedIn: 'root' });
462
434
  }
@@ -504,6 +476,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImpor
504
476
  }]
505
477
  }], ctorParameters: () => [] });
506
478
 
479
+ class MockItemService {
480
+ canEdit() {
481
+ return false;
482
+ }
483
+ canDelete() {
484
+ return false;
485
+ }
486
+ canCreate() {
487
+ return false;
488
+ }
489
+ typePlural() {
490
+ return "mock_plurals";
491
+ }
492
+ }
493
+
507
494
  class TopBarComponent {
508
495
  title = input(...(ngDevMode ? [undefined, { debugName: "title" }] : []));
509
496
  user = signal(null, ...(ngDevMode ? [{ debugName: "user" }] : []));
@@ -517,12 +504,23 @@ class TopBarComponent {
517
504
  onLogout() {
518
505
  this.userService.logout();
519
506
  }
507
+ getInitials() {
508
+ const user = this.user();
509
+ if (user) {
510
+ return PortalMember.getInitials(user);
511
+ }
512
+ return "";
513
+ }
520
514
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: TopBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
521
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: TopBarComponent, isStandalone: true, selector: "lib-top-bar", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n@if(user(); as user){\n<mat-toolbar style=\"height:60px\">\n <a mat-icon-button aria-label=\"Home\" [routerLink]=\"['home']\">\n <mat-icon>home</mat-icon>\n </a>\n <span>{{title()}}</span>\n <span class=\"topbar-spacer\"></span>\n <span>{{user.username}}</span>\n \n <button mat-icon-button aria-label=\"User Profile\" [matMenuTriggerFor]=\"profile_menu\">\n <mat-icon>person</mat-icon>\n </button>\n\n <mat-menu #profile_menu=\"matMenu\">\n <button mat-menu-item (click)=\"onLogout()\">Log Out</button>\n </mat-menu>\n</mat-toolbar>\n}\n", styles: [".topbar-spacer{flex:1 1 auto}\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"] }, { kind: "ngmodule", type: MatToolbarModule }, { kind: "component", type: i2.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i5.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i5.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i5.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }] });
515
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: TopBarComponent, isStandalone: true, selector: "lib-top-bar", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "\n@if(user(); as user){\n<mat-toolbar style=\"height:60px\">\n <a mat-icon-button aria-label=\"Home\" [routerLink]=\"['home']\">\n <mat-icon>home</mat-icon>\n </a>\n <span>{{title()}}</span>\n <span class=\"topbar-spacer\"></span>\n <span>{{user.username}}</span>\n \n <button mat-icon-button style=\"margin: 5px\" aria-label=\"User Profile\" [matMenuTriggerFor]=\"profile_menu\">\n <mat-icon>person</mat-icon>\n </button>\n\n <mat-menu #profile_menu=\"matMenu\">\n <a mat-menu-item aria-label=\"My Profile\" [routerLink]=\"['/members/detail', user.id]\">\n <mat-icon>manage_accounts</mat-icon>\n <span>My Profile</span>\n </a> \n <a mat-menu-item aria-label=\"Get Support\" [routerLink]=\"['/feedback']\">\n <mat-icon>contact_support</mat-icon>\n <span>Get Support</span>\n </a> \n <button mat-menu-item (click)=\"onLogout()\">\n <mat-icon>logout</mat-icon>\n <span>Log Out</span>\n </button>\n\n </mat-menu>\n</mat-toolbar>\n}\n", styles: [".topbar-spacer{flex:1 1 auto}\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"] }, { kind: "ngmodule", type: MatToolbarModule }, { kind: "component", type: i2.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i5.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i5.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i5.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }] });
522
516
  }
523
517
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: TopBarComponent, decorators: [{
524
518
  type: Component,
525
- args: [{ selector: 'lib-top-bar', imports: [NgIf, RouterModule, MatToolbarModule, MatIconModule, MatButtonModule, MatMenuModule], template: "\n@if(user(); as user){\n<mat-toolbar style=\"height:60px\">\n <a mat-icon-button aria-label=\"Home\" [routerLink]=\"['home']\">\n <mat-icon>home</mat-icon>\n </a>\n <span>{{title()}}</span>\n <span class=\"topbar-spacer\"></span>\n <span>{{user.username}}</span>\n \n <button mat-icon-button aria-label=\"User Profile\" [matMenuTriggerFor]=\"profile_menu\">\n <mat-icon>person</mat-icon>\n </button>\n\n <mat-menu #profile_menu=\"matMenu\">\n <button mat-menu-item (click)=\"onLogout()\">Log Out</button>\n </mat-menu>\n</mat-toolbar>\n}\n", styles: [".topbar-spacer{flex:1 1 auto}\n"] }]
519
+ args: [{ selector: 'lib-top-bar', imports: [RouterModule,
520
+ MatToolbarModule,
521
+ MatIconModule,
522
+ MatButtonModule,
523
+ MatMenuModule], template: "\n@if(user(); as user){\n<mat-toolbar style=\"height:60px\">\n <a mat-icon-button aria-label=\"Home\" [routerLink]=\"['home']\">\n <mat-icon>home</mat-icon>\n </a>\n <span>{{title()}}</span>\n <span class=\"topbar-spacer\"></span>\n <span>{{user.username}}</span>\n \n <button mat-icon-button style=\"margin: 5px\" aria-label=\"User Profile\" [matMenuTriggerFor]=\"profile_menu\">\n <mat-icon>person</mat-icon>\n </button>\n\n <mat-menu #profile_menu=\"matMenu\">\n <a mat-menu-item aria-label=\"My Profile\" [routerLink]=\"['/members/detail', user.id]\">\n <mat-icon>manage_accounts</mat-icon>\n <span>My Profile</span>\n </a> \n <a mat-menu-item aria-label=\"Get Support\" [routerLink]=\"['/feedback']\">\n <mat-icon>contact_support</mat-icon>\n <span>Get Support</span>\n </a> \n <button mat-menu-item (click)=\"onLogout()\">\n <mat-icon>logout</mat-icon>\n <span>Log Out</span>\n </button>\n\n </mat-menu>\n</mat-toolbar>\n}\n", styles: [".topbar-spacer{flex:1 1 auto}\n"] }]
526
524
  }] });
527
525
 
528
526
  class LeftNavComponent {
@@ -582,6 +580,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImpor
582
580
  args: [{ selector: 'lib-landing', imports: [FormsModule, MatCardModule, MatFormFieldModule, MatInputModule, MatButtonModule, MatIconModule, NgIf], template: "\n @if(message(); as message){\n <p>{{message}}</p>\n }\n \n <mat-card style=\"text-align:center;\">\n <form class=\"base-form\" #loginForm=\"ngForm\" (ngSubmit)=\"login()\">\n <mat-card-content>\n <mat-form-field class=\"form-field\">\n <input matInput\n placeholder=\"Username\"\n type=\"text\"\n [(ngModel)]=\"loginUser.name\"\n name=\"username\"\n required>\n </mat-form-field>\n <mat-form-field class=\"form-field\">\n <input matInput\n placeholder=\"Password\"\n type=\"password\"\n [(ngModel)]=\"loginUser.password\"\n name=\"password\"\n required>\n </mat-form-field>\n </mat-card-content>\n <div *ngIf=\"loginError\" class=\"error\">\n <mat-icon>error</mat-icon><p>{{loginError()}}</p>\n </div>\n <button mat-flat-button\n type=\"submit\"\n [disabled]=\"!loginForm.form.valid\">Log In</button>\n </form>\n </mat-card>\n\n\n \n", styles: [":host{display:flex;align-items:center;flex-grow:1;background-color:#a5b3c9;flex-direction:column}.base-form{padding:10px}.form-field{display:flex}\n"] }]
583
581
  }] });
584
582
 
583
+ class FeedbackComponent {
584
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: FeedbackComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
585
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.1", type: FeedbackComponent, isStandalone: true, selector: "lib-feedback", ngImport: i0, template: "<p>feedback works!</p>\n", styles: [""] });
586
+ }
587
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: FeedbackComponent, decorators: [{
588
+ type: Component,
589
+ args: [{ selector: 'lib-feedback', imports: [], template: "<p>feedback works!</p>\n" }]
590
+ }] });
591
+
592
+ class AvatarComponent {
593
+ text = input("", ...(ngDevMode ? [{ debugName: "text" }] : []));
594
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: AvatarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
595
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "20.1.1", type: AvatarComponent, isStandalone: true, selector: "lib-avatar", inputs: { text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"container\"> <span>{{text()}}</span> </div>", styles: [".container{width:36px;height:36px;border-radius:100%;text-align:center;justify-content:center;align-content:center;background-color:#d3d3d3}.container span{line-height:8px;color:#000}\n"] });
596
+ }
597
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: AvatarComponent, decorators: [{
598
+ type: Component,
599
+ args: [{ selector: 'lib-avatar', imports: [], template: "<div class=\"container\"> <span>{{text()}}</span> </div>", styles: [".container{width:36px;height:36px;border-radius:100%;text-align:center;justify-content:center;align-content:center;background-color:#d3d3d3}.container span{line-height:8px;color:#000}\n"] }]
600
+ }] });
601
+
585
602
  class DetailView {
586
603
  item = signal(null, ...(ngDevMode ? [{ debugName: "item" }] : []));
587
604
  route = inject(ActivatedRoute);
@@ -593,6 +610,12 @@ class DetailView {
593
610
  onInit() {
594
611
  this.getItem();
595
612
  }
613
+ title() {
614
+ if (this.itemService.typename) {
615
+ return this.itemService.typename.replace("_", " ");
616
+ }
617
+ return "";
618
+ }
596
619
  onItemAvailable(item) {
597
620
  this.item.set(item);
598
621
  this.userService.loggedInUser.subscribe(user => { if (user) {
@@ -621,64 +644,74 @@ class DetailView {
621
644
  }
622
645
 
623
646
  class EditView {
624
- typename = "";
625
647
  createMode = signal(false, ...(ngDevMode ? [{ debugName: "createMode" }] : []));
626
- heading = computed(() => { const prefix = this.createMode() ? "New " : "Edit "; return prefix + this.typename; }, ...(ngDevMode ? [{ debugName: "heading" }] : []));
648
+ heading = computed(() => { const prefix = this.createMode() ? "New " : "Edit "; return prefix + this.getTitle(); }, ...(ngDevMode ? [{ debugName: "heading" }] : []));
627
649
  item = signal(null, ...(ngDevMode ? [{ debugName: "item" }] : []));
628
650
  _route = inject(ActivatedRoute);
629
651
  _location = inject(Location);
630
652
  _userService = inject(UserService);
631
- _restService;
632
- constructor(_restService) {
633
- this._restService = _restService;
653
+ form;
654
+ itemService;
655
+ constructor(itemService, form) {
656
+ this.itemService = itemService;
657
+ this.form = form;
634
658
  }
635
659
  onInit() {
636
660
  this.getItem();
637
661
  }
638
- onItemAvailable() {
662
+ onItemAvailable(item) {
639
663
  this._userService.loggedInUser.subscribe(user => { if (user) {
640
- this.onItemAndUserAvailable(user);
664
+ this.onItemAndUserAvailable(item, user);
641
665
  } });
642
666
  }
643
- onItemAndUserAvailable(_) {
667
+ onItemAndUserAvailable(_item, _user) {
644
668
  }
645
669
  goBack() {
646
670
  this._location.back();
647
671
  }
648
672
  submit() {
649
- if (this.item()) {
650
- this.save();
651
- }
673
+ this.save();
652
674
  }
653
675
  save() {
654
676
  if (this.createMode()) {
655
- this.createItem();
677
+ this.postItem();
656
678
  }
657
679
  else {
658
- this.updateItem();
680
+ const item = this.item();
681
+ {
682
+ if (item) {
683
+ this.putItem(item);
684
+ }
685
+ }
659
686
  }
660
687
  }
661
688
  cancel() {
662
689
  this.goBack();
663
690
  }
691
+ getTitle() {
692
+ if (this.itemService && this.itemService.typename) {
693
+ return this.itemService.typename.replace("_", " ");
694
+ }
695
+ return "";
696
+ }
664
697
  fetchRestOptions() {
665
- this._restService.getOptions().subscribe(options => this.onOptions(options));
698
+ this.itemService.getOptions().subscribe(options => this.onOptions(options));
666
699
  }
667
700
  onUploadedFileReady(_file) {
668
701
  }
669
702
  onFileRead(_content) {
670
703
  }
671
704
  createItem() {
672
- const item = this.item();
673
- if (item) {
674
- this._restService.postItem(item).subscribe(item => this.saveFiles(item));
675
- }
705
+ return this.form.createItem();
676
706
  }
677
- updateItem() {
678
- const item = this.item();
679
- if (item) {
680
- this._restService.putItem(item).subscribe(item => this.saveFiles(item));
681
- }
707
+ updateItem(item) {
708
+ return this.form.updateItem(item);
709
+ }
710
+ postItem() {
711
+ this.itemService.postItem(this.createItem()).subscribe(item => this.saveFiles(item));
712
+ }
713
+ putItem(item) {
714
+ this.itemService.putItem(this.updateItem(item)).subscribe(item => this.saveFiles(item));
682
715
  }
683
716
  saveFiles(item) {
684
717
  this.onItemUpdated(item);
@@ -697,16 +730,14 @@ class EditView {
697
730
  }
698
731
  getItem() {
699
732
  if (this.isCreateRoute()) {
700
- this.item.set(this.getTemplateItem());
701
733
  this.createMode.set(true);
702
- this.onItemAvailable();
703
734
  }
704
735
  else {
705
736
  const id_str = this._route.snapshot.paramMap.get('id');
706
- this._restService.getItem(Number(id_str))
737
+ this.itemService.getItem(Number(id_str))
707
738
  .subscribe(item => {
708
739
  this.item.set(item);
709
- this.onItemAvailable();
740
+ this.onItemAvailable(item);
710
741
  });
711
742
  }
712
743
  }
@@ -726,23 +757,6 @@ class EditView {
726
757
  }
727
758
  }
728
759
 
729
- class ListView {
730
- _route = inject(ActivatedRoute);
731
- userService = inject(UserService);
732
- user = signal(null, ...(ngDevMode ? [{ debugName: "user" }] : []));
733
- getUser() {
734
- this.userService.loggedInUser.subscribe(user => {
735
- if (user) {
736
- this.user.set(user);
737
- }
738
- });
739
- }
740
- isSelfList() {
741
- const url_segments = this._route.snapshot.url;
742
- return (url_segments.length == 2) && (url_segments[1].path == "self");
743
- }
744
- }
745
-
746
760
  class SelectTableComponent {
747
761
  itemType = input("", ...(ngDevMode ? [{ debugName: "itemType" }] : []));
748
762
  selected = input([], ...(ngDevMode ? [{ debugName: "selected" }] : []));
@@ -751,7 +765,7 @@ class SelectTableComponent {
751
765
  itemAdded = output();
752
766
  itemRemoved = output();
753
767
  searchChanged = output();
754
- columnNames = computed(() => this.columns().map(c => c.name), ...(ngDevMode ? [{ debugName: "columnNames" }] : []));
768
+ columnNames = computed(() => { const names = this.columns().map(c => c.name); names.push("remove"); return names; }, ...(ngDevMode ? [{ debugName: "columnNames" }] : []));
755
769
  searchControl = new FormControl('');
756
770
  table = viewChild(MatTable, ...(ngDevMode ? [{ debugName: "table" }] : []));
757
771
  ngOnInit() {
@@ -765,6 +779,7 @@ class SelectTableComponent {
765
779
  if (this.searchControl.value) {
766
780
  this.itemAdded.emit(this.searchControl.value);
767
781
  }
782
+ this.searchControl.reset();
768
783
  this.table()?.renderRows();
769
784
  }
770
785
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: SelectTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -782,6 +797,50 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImpor
782
797
  MatInputModule], template: "<div class='select-container'>\n <div>\n <mat-form-field>\n <mat-label>Add {{itemType()}}</mat-label>\n <input \n type=\"text\"\n aria-label=\"Selected item\"\n matInput\n [formControl]=\"searchControl\" \n [matAutocomplete]=\"auto\">\n\n <mat-autocomplete #auto=\"matAutocomplete\">\n @for(item of options(); track item.item.id){\n <mat-option [value]=\"item.title\">{{item.title}}\n </mat-option>\n }\n </mat-autocomplete>\n </mat-form-field>\n\n <button mat-mini-fab type=\"button\" class=\"form_action_button\" (click)=\"add()\">\n <mat-icon>add\n </mat-icon>\n </button>\n </div>\n\n @if(selected().length > 0){\n <table mat-table [dataSource]=\"selected()\" class=\"mat-elevation-z8\">\n @for (column of columns(); track column.name) {\n <ng-container matColumnDef=\"{{ column.name }}\">\n <th mat-header-cell mat-sort-header *matHeaderCellDef>\n {{ column.title }}\n </th>\n <td mat-cell *matCellDef=\"let element\">{{ element[column.name] }}</td>\n </ng-container>\n }\n\n <ng-container matColumnDef=\"remove\">\n <th mat-header-cell *matHeaderCellDef>Remove\n </th>\n <td mat-cell *matCellDef=\"let element\">\n <button mat-icon-button\n color=\"primary\"\n aria-label=\"Remove an item\"\n (click)=\"remove(element.id)\">\n <mat-icon>remove\n </mat-icon>\n </button>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"columnNames()\">\n </tr>\n <tr mat-row *matRowDef=\"let row; columns: columnNames();\">\n </tr>\n </table>\n }\n</div>", styles: [".select-container,.table_container{display:flex;text-align:center;justify-content:center;flex-direction:column}.form_action_button{padding:5px;margin:5px}\n"] }]
783
798
  }] });
784
799
 
800
+ const USER_TABLE_FULL = [
801
+ { name: 'first_name', title: 'First Name', element_type: 'string' },
802
+ { name: 'last_name', title: 'Surname', element_type: 'string' },
803
+ { name: 'username', title: 'Username', element_type: 'string' },
804
+ { name: 'email', title: 'Email', element_type: 'email' }
805
+ ];
806
+
807
+ class SelectionManager {
808
+ candidates = signal([], ...(ngDevMode ? [{ debugName: "candidates" }] : []));
809
+ selected = signal([], ...(ngDevMode ? [{ debugName: "selected" }] : []));
810
+ columns = USER_TABLE_FULL;
811
+ userService = inject(UserService);
812
+ fetchCandidates(item, id) {
813
+ const queries = new Map([[item, id.toString()]]);
814
+ const query = new ItemQuery({ queries: queries });
815
+ this.userService.get(query).subscribe(users => this.selected.set(users.results));
816
+ this.userService.get().subscribe(users => this.updateCandidates(users));
817
+ }
818
+ updateCandidates(users) {
819
+ this.candidates.set(users.results.map(user => { return { title: user.username, item: user }; }));
820
+ }
821
+ onRemoved(id) {
822
+ const url = this.selected().find(m => m.id == id).url;
823
+ this.selected.update(members => members.filter(m => m.id != id));
824
+ return url;
825
+ }
826
+ onAdded(title) {
827
+ const selected = this.candidates().find(m => m.title == title);
828
+ if (selected) {
829
+ this.selected.update(members => { members.push(selected.item); return members; });
830
+ }
831
+ this.candidates.update(members => members.filter(m => m.title != title));
832
+ return this.selected().find(m => m.username == title).url;
833
+ }
834
+ onSearchChanged(searchTerm) {
835
+ if (searchTerm) {
836
+ this.userService.get(new ItemQuery({ filter: searchTerm })).subscribe(users => this.updateCandidates(users));
837
+ }
838
+ else {
839
+ this.userService.get().subscribe(users => this.updateCandidates(users));
840
+ }
841
+ }
842
+ }
843
+
785
844
  /* eslint-disable @typescript-eslint/no-explicit-any */
786
845
  class ListTableViewComponent {
787
846
  itemType = input("", ...(ngDevMode ? [{ debugName: "itemType" }] : []));
@@ -793,12 +852,6 @@ class ListTableViewComponent {
793
852
  columnNames = computed(() => this.columns().map(c => c.name), ...(ngDevMode ? [{ debugName: "columnNames" }] : []));
794
853
  sort = viewChild(MatSort, ...(ngDevMode ? [{ debugName: "sort" }] : []));
795
854
  paginator = viewChild(MatPaginator, ...(ngDevMode ? [{ debugName: "paginator" }] : []));
796
- ngOnInit() {
797
- const data_source = this.dataSource();
798
- if (data_source) {
799
- data_source.fetch(new ItemQuery({ page_size: this.initialPageSize }));
800
- }
801
- }
802
855
  ngAfterViewInit() {
803
856
  const paginator = this.paginator();
804
857
  const sort = this.sort();
@@ -855,9 +908,6 @@ class ListScrollViewComponent {
855
908
  pageSize = input(20, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
856
909
  dataSource = input(...(ngDevMode ? [undefined, { debugName: "dataSource" }] : []));
857
910
  listItemTemplate = input(null, ...(ngDevMode ? [{ debugName: "listItemTemplate" }] : []));
858
- ngOnInit() {
859
- this.reset();
860
- }
861
911
  reset() {
862
912
  const data_source = this.dataSource();
863
913
  if (!data_source) {
@@ -926,6 +976,8 @@ class ListDataSource extends DataSource {
926
976
  loading = signal(true, ...(ngDevMode ? [{ debugName: "loading" }] : []));
927
977
  searchTerm = signal("", ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
928
978
  pageSize = signal(20, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
979
+ hasFetched = signal(false, ...(ngDevMode ? [{ debugName: "hasFetched" }] : []));
980
+ sourceIsEmpty = computed(() => this.length() == 0 && this.hasFetched() && !this.searchTerm(), ...(ngDevMode ? [{ debugName: "sourceIsEmpty" }] : []));
929
981
  consumerType;
930
982
  items = new BehaviorSubject([]);
931
983
  itemService;
@@ -976,6 +1028,7 @@ class ListDataSource extends DataSource {
976
1028
  }
977
1029
  onResponse(response) {
978
1030
  this.length.set(response.count);
1031
+ this.hasFetched.set(true);
979
1032
  if (this.hasTableConsumer()) {
980
1033
  this.items.next(response.results);
981
1034
  }
@@ -1032,10 +1085,13 @@ class ListViewComponent {
1032
1085
  itemType() {
1033
1086
  const item_service = this.itemService();
1034
1087
  if (item_service) {
1035
- return item_service.itemType;
1088
+ return item_service.typePlural();
1036
1089
  }
1037
1090
  return "";
1038
1091
  }
1092
+ title() {
1093
+ return this.itemType().replace("_", " ");
1094
+ }
1039
1095
  isTableView() {
1040
1096
  return this.selectedViewType() === "table";
1041
1097
  }
@@ -1061,7 +1117,7 @@ class ListViewComponent {
1061
1117
  return (url_segments.length == 2) && (url_segments[1].path == "self");
1062
1118
  }
1063
1119
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: ListViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1064
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: ListViewComponent, isStandalone: true, selector: "lib-list-view", inputs: { viewType: { classPropertyName: "viewType", publicName: "viewType", isSignal: true, isRequired: false, transformFunction: null }, itemService: { classPropertyName: "itemService", publicName: "itemService", isSignal: true, isRequired: false, transformFunction: null }, itemDetailTemplate: { classPropertyName: "itemDetailTemplate", publicName: "itemDetailTemplate", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, sortFields: { classPropertyName: "sortFields", publicName: "sortFields", isSignal: true, isRequired: false, transformFunction: null }, noSelfItemsMessage: { classPropertyName: "noSelfItemsMessage", publicName: "noSelfItemsMessage", isSignal: true, isRequired: false, transformFunction: null }, noItemsCanCreateMessage: { classPropertyName: "noItemsCanCreateMessage", publicName: "noItemsCanCreateMessage", isSignal: true, isRequired: false, transformFunction: null }, noItemsMessage: { classPropertyName: "noItemsMessage", publicName: "noItemsMessage", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "sort", first: true, predicate: MatSort, descendants: true, isSignal: true }], ngImport: i0, template: "<lib-back-button></lib-back-button>\n\n<div class=\"container\">\n <div class=\"header\">\n <h1 class=\"title\">{{ itemType() | titlecase }}</h1>\n\n @if (canCreate()) {\n <div class=\"button-container\">\n <a mat-fab class=\"padded-button\" aria-label=\"Add new item\" matTooltip=\"Add new item\" [routerLink]=\"['create']\">\n <mat-icon>add</mat-icon></a>\n </div>\n }\n </div>\n\n <div class=\"controls\">\n <lib-search-bar [itemType]=\"itemType()\" [sortFields]=\"sortFields()\"\n (searchChanged)=\"onSearchChange($event)\"></lib-search-bar>\n\n @if(itemDetailTemplate()){\n <div style=\"margin: 10px\">\n <mat-button-toggle-group name=\"viewType\" aria-label=\"View Type\" [value]=\"selectedViewType()\"\n (change)=\"viewChanged($event)\">\n\n <mat-button-toggle value=\"table\" matTooltip=\"Show as table\">\n <mat-icon>table_view</mat-icon>\n </mat-button-toggle>\n\n <mat-button-toggle value=\"scroll\" matTooltip=\"Show as list\">\n <mat-icon>list</mat-icon>\n </mat-button-toggle>\n\n </mat-button-toggle-group>\n </div>\n }\n </div>\n\n @if(dataSource)\n {\n @if (dataSource.loading()) {\n <div class=\"spinner-container\">\n <mat-spinner></mat-spinner>\n </div>\n }\n @else{\n @if(dataSource.length() === 0)\n {\n @if(isSelfList())\n {\n @if(noSelfItemsMessage())\n {\n <p>{{noSelfItemsMessage()}}</p>\n }\n @else {\n <p>You do not have any {{itemType()}}.</p> \n }\n\n }\n @else{\n @if(canCreate()){\n @if(noItemsCanCreateMessage())\n {\n <p>{{noItemsCanCreateMessage()}}</p>\n }\n @else{\n <p>There are currently no {{itemType()}}, click above to add one.</p>\n }\n }\n @else{\n @if(noItemsMessage())\n {\n <p>{{noItemsMessage()}}</p>\n }\n @else\n {\n <p>There are currently no {{itemType()}}.</p>\n }\n }\n }\n }\n @else{\n @if(isTableView()) {\n <lib-list-table-view [itemType]=\"itemType()\" [columns]=\"columns()\" [dataSource]=\"dataSource\">\n </lib-list-table-view>\n } \n @else {\n <lib-list-scroll-view [listItemTemplate]=\"itemDetailTemplate()\" [dataSource]=\"dataSource\"></lib-list-scroll-view>\n }\n }\n }\n }\n</div>", styles: [":host{flex-grow:1}.container{display:flex;padding:10px;flex-direction:column;justify-content:center;text-align:center;align-items:center}.header{display:flex;flex-direction:row;align-items:center;justify-content:center}.title{text-align:center;padding:10px}.controls{display:flex;align-items:center;justify-content:center;flex-wrap:wrap}\n"], dependencies: [{ kind: "component", type: ListTableViewComponent, selector: "lib-list-table-view", inputs: ["itemType", "columns", "dataSource", "searchFilter"] }, { kind: "component", type: ListScrollViewComponent, selector: "lib-list-scroll-view", inputs: ["searchTerm", "pageSize", "dataSource", "listItemTemplate"] }, { kind: "component", type: BackButtonComponent, selector: "lib-back-button" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i1$5.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i1$5.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i2$5.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i4.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: SearchBarComponent, selector: "lib-search-bar", inputs: ["itemType", "sortFields"], outputs: ["searchChanged"] }, { kind: "pipe", type: TitleCasePipe, name: "titlecase" }] });
1120
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: ListViewComponent, isStandalone: true, selector: "lib-list-view", inputs: { viewType: { classPropertyName: "viewType", publicName: "viewType", isSignal: true, isRequired: false, transformFunction: null }, itemService: { classPropertyName: "itemService", publicName: "itemService", isSignal: true, isRequired: false, transformFunction: null }, itemDetailTemplate: { classPropertyName: "itemDetailTemplate", publicName: "itemDetailTemplate", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, sortFields: { classPropertyName: "sortFields", publicName: "sortFields", isSignal: true, isRequired: false, transformFunction: null }, noSelfItemsMessage: { classPropertyName: "noSelfItemsMessage", publicName: "noSelfItemsMessage", isSignal: true, isRequired: false, transformFunction: null }, noItemsCanCreateMessage: { classPropertyName: "noItemsCanCreateMessage", publicName: "noItemsCanCreateMessage", isSignal: true, isRequired: false, transformFunction: null }, noItemsMessage: { classPropertyName: "noItemsMessage", publicName: "noItemsMessage", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "sort", first: true, predicate: MatSort, descendants: true, isSignal: true }], ngImport: i0, template: "<lib-back-button></lib-back-button>\n\n<div class=\"container\">\n <div class=\"header\">\n <h1 class=\"title\">{{ title() | titlecase }}</h1>\n\n @if (canCreate()) {\n <div class=\"button-container\">\n <a mat-fab class=\"padded-button\" aria-label=\"Add new item\" matTooltip=\"Add new item\" [routerLink]=\"['create']\">\n <mat-icon>add</mat-icon></a>\n </div>\n }\n </div>\n\n @if(dataSource)\n {\n <div class=\"controls\" [hidden]=\"dataSource.sourceIsEmpty()\">\n <lib-search-bar [itemType]=\"title()\" [sortFields]=\"sortFields()\"\n (searchChanged)=\"onSearchChange($event)\"></lib-search-bar>\n\n @if(itemDetailTemplate()){\n <div style=\"margin: 10px\">\n <mat-button-toggle-group name=\"viewType\" aria-label=\"View Type\" [value]=\"selectedViewType()\"\n (change)=\"viewChanged($event)\">\n\n <mat-button-toggle value=\"table\" matTooltip=\"Show as table\">\n <mat-icon>table_view</mat-icon>\n </mat-button-toggle>\n\n <mat-button-toggle value=\"scroll\" matTooltip=\"Show as list\">\n <mat-icon>list</mat-icon>\n </mat-button-toggle>\n\n </mat-button-toggle-group>\n </div>\n }\n </div>\n\n @if (dataSource.loading()) {\n <div class=\"spinner-container\">\n <mat-spinner></mat-spinner>\n </div>\n }\n\n <div [hidden]=\"!dataSource.sourceIsEmpty()\">\n @if(isSelfList())\n {\n @if(noSelfItemsMessage())\n {\n <p>{{noSelfItemsMessage()}}</p>\n }\n @else {\n <p>You do not have any {{itemType()}}.</p> \n }\n }\n @else{\n @if(canCreate()){\n @if(noItemsCanCreateMessage())\n {\n <p>{{noItemsCanCreateMessage()}}</p>\n }\n @else{\n <p>There are currently no {{itemType()}}, click above to add one.</p>\n }\n }\n @else{\n @if(noItemsMessage())\n {\n <p>{{noItemsMessage()}}</p>\n }\n @else\n {\n <p>There are currently no {{itemType()}}.</p>\n }\n }\n }\n </div>\n\n <div [hidden]=\"dataSource.sourceIsEmpty()\">\n @if(isTableView()) {\n <lib-list-table-view [itemType]=\"itemType()\" [columns]=\"columns()\" [dataSource]=\"dataSource\">\n </lib-list-table-view>\n } \n @else {\n <lib-list-scroll-view [listItemTemplate]=\"itemDetailTemplate()\" [dataSource]=\"dataSource\"></lib-list-scroll-view>\n }\n\n </div>\n }\n</div>", styles: [":host{flex-grow:1}.container{display:flex;padding:10px;flex-direction:column;justify-content:center;text-align:center;align-items:center}.header{display:flex;flex-direction:row;align-items:center;justify-content:center}.title{text-align:center;padding:10px}.controls{display:flex;align-items:center;justify-content:center;flex-wrap:wrap}.hide-element{display:none}\n"], dependencies: [{ kind: "component", type: ListTableViewComponent, selector: "lib-list-table-view", inputs: ["itemType", "columns", "dataSource", "searchFilter"] }, { kind: "component", type: ListScrollViewComponent, selector: "lib-list-scroll-view", inputs: ["searchTerm", "pageSize", "dataSource", "listItemTemplate"] }, { kind: "component", type: BackButtonComponent, selector: "lib-back-button" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i1$5.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i1$5.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i2$5.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i4.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: SearchBarComponent, selector: "lib-search-bar", inputs: ["itemType", "sortFields"], outputs: ["searchChanged"] }, { kind: "pipe", type: TitleCasePipe, name: "titlecase" }] });
1065
1121
  }
1066
1122
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: ListViewComponent, decorators: [{
1067
1123
  type: Component,
@@ -1078,7 +1134,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImpor
1078
1134
  MatInputModule,
1079
1135
  MatTooltipModule,
1080
1136
  MatButtonModule,
1081
- SearchBarComponent], template: "<lib-back-button></lib-back-button>\n\n<div class=\"container\">\n <div class=\"header\">\n <h1 class=\"title\">{{ itemType() | titlecase }}</h1>\n\n @if (canCreate()) {\n <div class=\"button-container\">\n <a mat-fab class=\"padded-button\" aria-label=\"Add new item\" matTooltip=\"Add new item\" [routerLink]=\"['create']\">\n <mat-icon>add</mat-icon></a>\n </div>\n }\n </div>\n\n <div class=\"controls\">\n <lib-search-bar [itemType]=\"itemType()\" [sortFields]=\"sortFields()\"\n (searchChanged)=\"onSearchChange($event)\"></lib-search-bar>\n\n @if(itemDetailTemplate()){\n <div style=\"margin: 10px\">\n <mat-button-toggle-group name=\"viewType\" aria-label=\"View Type\" [value]=\"selectedViewType()\"\n (change)=\"viewChanged($event)\">\n\n <mat-button-toggle value=\"table\" matTooltip=\"Show as table\">\n <mat-icon>table_view</mat-icon>\n </mat-button-toggle>\n\n <mat-button-toggle value=\"scroll\" matTooltip=\"Show as list\">\n <mat-icon>list</mat-icon>\n </mat-button-toggle>\n\n </mat-button-toggle-group>\n </div>\n }\n </div>\n\n @if(dataSource)\n {\n @if (dataSource.loading()) {\n <div class=\"spinner-container\">\n <mat-spinner></mat-spinner>\n </div>\n }\n @else{\n @if(dataSource.length() === 0)\n {\n @if(isSelfList())\n {\n @if(noSelfItemsMessage())\n {\n <p>{{noSelfItemsMessage()}}</p>\n }\n @else {\n <p>You do not have any {{itemType()}}.</p> \n }\n\n }\n @else{\n @if(canCreate()){\n @if(noItemsCanCreateMessage())\n {\n <p>{{noItemsCanCreateMessage()}}</p>\n }\n @else{\n <p>There are currently no {{itemType()}}, click above to add one.</p>\n }\n }\n @else{\n @if(noItemsMessage())\n {\n <p>{{noItemsMessage()}}</p>\n }\n @else\n {\n <p>There are currently no {{itemType()}}.</p>\n }\n }\n }\n }\n @else{\n @if(isTableView()) {\n <lib-list-table-view [itemType]=\"itemType()\" [columns]=\"columns()\" [dataSource]=\"dataSource\">\n </lib-list-table-view>\n } \n @else {\n <lib-list-scroll-view [listItemTemplate]=\"itemDetailTemplate()\" [dataSource]=\"dataSource\"></lib-list-scroll-view>\n }\n }\n }\n }\n</div>", styles: [":host{flex-grow:1}.container{display:flex;padding:10px;flex-direction:column;justify-content:center;text-align:center;align-items:center}.header{display:flex;flex-direction:row;align-items:center;justify-content:center}.title{text-align:center;padding:10px}.controls{display:flex;align-items:center;justify-content:center;flex-wrap:wrap}\n"] }]
1137
+ SearchBarComponent], template: "<lib-back-button></lib-back-button>\n\n<div class=\"container\">\n <div class=\"header\">\n <h1 class=\"title\">{{ title() | titlecase }}</h1>\n\n @if (canCreate()) {\n <div class=\"button-container\">\n <a mat-fab class=\"padded-button\" aria-label=\"Add new item\" matTooltip=\"Add new item\" [routerLink]=\"['create']\">\n <mat-icon>add</mat-icon></a>\n </div>\n }\n </div>\n\n @if(dataSource)\n {\n <div class=\"controls\" [hidden]=\"dataSource.sourceIsEmpty()\">\n <lib-search-bar [itemType]=\"title()\" [sortFields]=\"sortFields()\"\n (searchChanged)=\"onSearchChange($event)\"></lib-search-bar>\n\n @if(itemDetailTemplate()){\n <div style=\"margin: 10px\">\n <mat-button-toggle-group name=\"viewType\" aria-label=\"View Type\" [value]=\"selectedViewType()\"\n (change)=\"viewChanged($event)\">\n\n <mat-button-toggle value=\"table\" matTooltip=\"Show as table\">\n <mat-icon>table_view</mat-icon>\n </mat-button-toggle>\n\n <mat-button-toggle value=\"scroll\" matTooltip=\"Show as list\">\n <mat-icon>list</mat-icon>\n </mat-button-toggle>\n\n </mat-button-toggle-group>\n </div>\n }\n </div>\n\n @if (dataSource.loading()) {\n <div class=\"spinner-container\">\n <mat-spinner></mat-spinner>\n </div>\n }\n\n <div [hidden]=\"!dataSource.sourceIsEmpty()\">\n @if(isSelfList())\n {\n @if(noSelfItemsMessage())\n {\n <p>{{noSelfItemsMessage()}}</p>\n }\n @else {\n <p>You do not have any {{itemType()}}.</p> \n }\n }\n @else{\n @if(canCreate()){\n @if(noItemsCanCreateMessage())\n {\n <p>{{noItemsCanCreateMessage()}}</p>\n }\n @else{\n <p>There are currently no {{itemType()}}, click above to add one.</p>\n }\n }\n @else{\n @if(noItemsMessage())\n {\n <p>{{noItemsMessage()}}</p>\n }\n @else\n {\n <p>There are currently no {{itemType()}}.</p>\n }\n }\n }\n </div>\n\n <div [hidden]=\"dataSource.sourceIsEmpty()\">\n @if(isTableView()) {\n <lib-list-table-view [itemType]=\"itemType()\" [columns]=\"columns()\" [dataSource]=\"dataSource\">\n </lib-list-table-view>\n } \n @else {\n <lib-list-scroll-view [listItemTemplate]=\"itemDetailTemplate()\" [dataSource]=\"dataSource\"></lib-list-scroll-view>\n }\n\n </div>\n }\n</div>", styles: [":host{flex-grow:1}.container{display:flex;padding:10px;flex-direction:column;justify-content:center;text-align:center;align-items:center}.header{display:flex;flex-direction:row;align-items:center;justify-content:center}.title{text-align:center;padding:10px}.controls{display:flex;align-items:center;justify-content:center;flex-wrap:wrap}.hide-element{display:none}\n"] }]
1082
1138
  }] });
1083
1139
 
1084
1140
  class DetailHeaderComponent {
@@ -1088,7 +1144,7 @@ class DetailHeaderComponent {
1088
1144
  canEdit = input(false, ...(ngDevMode ? [{ debugName: "canEdit" }] : []));
1089
1145
  canDelete = input(false, ...(ngDevMode ? [{ debugName: "canDelete" }] : []));
1090
1146
  deleteClicked = output();
1091
- fullRoute = computed(() => "/" + this.route() + "s/edit/", ...(ngDevMode ? [{ debugName: "fullRoute" }] : []));
1147
+ fullRoute = computed(() => "/" + this.route() + "/edit/", ...(ngDevMode ? [{ debugName: "fullRoute" }] : []));
1092
1148
  deleteClick() {
1093
1149
  this.deleteClicked.emit();
1094
1150
  }
@@ -1106,6 +1162,7 @@ class FileUploadComponent {
1106
1162
  files = input([], ...(ngDevMode ? [{ debugName: "files" }] : []));
1107
1163
  previewLoaded = output();
1108
1164
  fileUploaded = output();
1165
+ clearLocal = output();
1109
1166
  onPreviewRead(name, content) {
1110
1167
  this.previewLoaded.emit({ name: name, content: content });
1111
1168
  }
@@ -1133,12 +1190,15 @@ class FileUploadComponent {
1133
1190
  ;
1134
1191
  };
1135
1192
  }
1193
+ onClearLocal() {
1194
+ this.clearLocal.emit();
1195
+ }
1136
1196
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: FileUploadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1137
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: FileUploadComponent, isStandalone: true, selector: "lib-file-upload", inputs: { files: { classPropertyName: "files", publicName: "files", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { previewLoaded: "previewLoaded", fileUploaded: "fileUploaded" }, ngImport: i0, template: "<div class=\"container\">\n @for( file of files(); track file.name)\n {\n @if (file.local)\n {\n <div class=\"image_holder\">\n <p>Update {{file.display_name}}</p>\n <img alt=\"{{file.display_name}}\" src=\"{{file.local}}\" style=\"height:120px;\">\n </div> \n }\n @else\n {\n @if (file.remote){\n <div class=\"image_holder\">\n <p>Update {{file.display_name}}</p>\n <img alt=\"{{file.display_name}}\" src=\"{{file.remote}}\" style=\"height:120px;\">\n </div>\n }\n @else {\n <div>\n <span>Upload a {{file.display_name}}</span>\n </div> \n }\n \n }\n\n <div class=\"button-row\">\n <button mat-mini-fab (click)=\"fileInput.click()\">\n <mat-icon>attachment</mat-icon>\n </button>\n <input hidden type=\"file\" #fileInput (change)=\"onFileUpload(file.name, $event)\"/>\n </div> \n } \n</div>\n", styles: [".container{padding:10px;display:flex;justify-content:center;text-align:center;flex-direction:column;max-width:300px}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }] });
1197
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: FileUploadComponent, isStandalone: true, selector: "lib-file-upload", inputs: { files: { classPropertyName: "files", publicName: "files", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { previewLoaded: "previewLoaded", fileUploaded: "fileUploaded", clearLocal: "clearLocal" }, ngImport: i0, template: "<div class=\"container\">\n @for( file of files(); track file.name)\n {\n @if (file.local)\n {\n <div class=\"image_holder\">\n <p>Update {{file.display_name}}</p>\n <img alt=\"{{file.display_name}}\" src=\"{{file.local}}\" style=\"height:120px;\">\n <button mat-mini-fab (click)=\"onClearLocal()\">\n <mat-icon>clear</mat-icon>\n </button>\n </div> \n }\n @else\n {\n @if (file.remote){\n <div class=\"image_holder\">\n <p>Update {{file.display_name}}</p>\n <img alt=\"{{file.display_name}}\" src=\"{{file.remote}}\" style=\"height:120px;\">\n </div>\n }\n @else {\n <div>\n <span>Upload a {{file.display_name}}</span>\n </div> \n }\n \n }\n\n <div class=\"button-row\">\n <button mat-mini-fab (click)=\"fileInput.click()\">\n <mat-icon>attachment</mat-icon>\n </button>\n <input hidden type=\"file\" #fileInput (change)=\"onFileUpload(file.name, $event)\"/>\n </div> \n } \n</div>\n", styles: [".container{padding:10px;display:flex;justify-content:center;text-align:center;flex-direction:column;max-width:300px}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatMiniFabButton, selector: "button[mat-mini-fab], a[mat-mini-fab], button[matMiniFab], a[matMiniFab]", exportAs: ["matButton", "matAnchor"] }] });
1138
1198
  }
1139
1199
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: FileUploadComponent, decorators: [{
1140
1200
  type: Component,
1141
- args: [{ selector: 'lib-file-upload', imports: [MatIconModule, MatButtonModule], template: "<div class=\"container\">\n @for( file of files(); track file.name)\n {\n @if (file.local)\n {\n <div class=\"image_holder\">\n <p>Update {{file.display_name}}</p>\n <img alt=\"{{file.display_name}}\" src=\"{{file.local}}\" style=\"height:120px;\">\n </div> \n }\n @else\n {\n @if (file.remote){\n <div class=\"image_holder\">\n <p>Update {{file.display_name}}</p>\n <img alt=\"{{file.display_name}}\" src=\"{{file.remote}}\" style=\"height:120px;\">\n </div>\n }\n @else {\n <div>\n <span>Upload a {{file.display_name}}</span>\n </div> \n }\n \n }\n\n <div class=\"button-row\">\n <button mat-mini-fab (click)=\"fileInput.click()\">\n <mat-icon>attachment</mat-icon>\n </button>\n <input hidden type=\"file\" #fileInput (change)=\"onFileUpload(file.name, $event)\"/>\n </div> \n } \n</div>\n", styles: [".container{padding:10px;display:flex;justify-content:center;text-align:center;flex-direction:column;max-width:300px}\n"] }]
1201
+ args: [{ selector: 'lib-file-upload', imports: [MatIconModule, MatButtonModule], template: "<div class=\"container\">\n @for( file of files(); track file.name)\n {\n @if (file.local)\n {\n <div class=\"image_holder\">\n <p>Update {{file.display_name}}</p>\n <img alt=\"{{file.display_name}}\" src=\"{{file.local}}\" style=\"height:120px;\">\n <button mat-mini-fab (click)=\"onClearLocal()\">\n <mat-icon>clear</mat-icon>\n </button>\n </div> \n }\n @else\n {\n @if (file.remote){\n <div class=\"image_holder\">\n <p>Update {{file.display_name}}</p>\n <img alt=\"{{file.display_name}}\" src=\"{{file.remote}}\" style=\"height:120px;\">\n </div>\n }\n @else {\n <div>\n <span>Upload a {{file.display_name}}</span>\n </div> \n }\n \n }\n\n <div class=\"button-row\">\n <button mat-mini-fab (click)=\"fileInput.click()\">\n <mat-icon>attachment</mat-icon>\n </button>\n <input hidden type=\"file\" #fileInput (change)=\"onFileUpload(file.name, $event)\"/>\n </div> \n } \n</div>\n", styles: [".container{padding:10px;display:flex;justify-content:center;text-align:center;flex-direction:column;max-width:300px}\n"] }]
1142
1202
  }] });
1143
1203
 
1144
1204
  class FileRecord {
@@ -1154,7 +1214,6 @@ class FileRecord {
1154
1214
  }
1155
1215
 
1156
1216
  class UserDetailComponent extends DetailView {
1157
- typename = "user";
1158
1217
  constructor() {
1159
1218
  super(inject(UserService));
1160
1219
  }
@@ -1162,20 +1221,15 @@ class UserDetailComponent extends DetailView {
1162
1221
  this.onInit();
1163
1222
  }
1164
1223
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: UserDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1165
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: UserDetailComponent, isStandalone: true, selector: "lib-user-detail", usesInheritance: true, ngImport: i0, template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n @if(item(); as item){\n <div class=\"item-detail-container\">\n\n <lib-detail-header\n [text]=\"item.username\"\n [id]=\"item.id\"\n [route]=\"typename\"\n [canEdit]=\"canEdit()\"\n [canDelete]=\"canDelete()\"\n (onDelete)=\"onDelete\"></lib-detail-header>\n\n @if(item.profile_url){\n <div class=\"image-holder\">\n <img alt=\"User Profile Image\" src=\"{{item.url}}{{item.profile_url}}\" style=\"height:120px;\">\n </div> \n }\n \n <div class=\"item-field\">\n <span><b>Email: </b></span>\n {{item.email}}\n </div>\n \n <div class=\"item-field\">\n <span><b>First Name: </b></span>\n {{item.first_name}}\n </div>\n \n <div class=\"item-field\">\n <span><b>Last Name: </b></span>\n {{item.last_name}}\n </div>\n \n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}\n"], dependencies: [{ kind: "component", type: BackButtonComponent, selector: "lib-back-button" }, { kind: "component", type: DetailHeaderComponent, selector: "lib-detail-header", inputs: ["id", "text", "route", "canEdit", "canDelete"], outputs: ["deleteClicked"] }] });
1224
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: UserDetailComponent, isStandalone: true, selector: "lib-user-detail", usesInheritance: true, ngImport: i0, template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n @if(item(); as item){\n <div class=\"item-detail-container\">\n\n <lib-detail-header\n [text]=\"item.username\"\n [id]=\"item.id\"\n [route]=\"itemService.typePlural()\"\n [canEdit]=\"canEdit()\"\n [canDelete]=\"canDelete()\"\n (onDelete)=\"onDelete()\"></lib-detail-header>\n\n @if(item.profile_url){\n <div class=\"image-holder\">\n <img alt=\"User Profile Image\" src=\"{{item.url}}{{item.profile_url}}\" style=\"height:120px;\">\n </div> \n }\n \n <div class=\"item-field\">\n <span><b>Email: </b></span>\n {{item.email}}\n </div>\n \n <div class=\"item-field\">\n <span><b>First Name: </b></span>\n {{item.first_name}}\n </div>\n \n <div class=\"item-field\">\n <span><b>Last Name: </b></span>\n {{item.last_name}}\n </div>\n \n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}\n"], dependencies: [{ kind: "component", type: BackButtonComponent, selector: "lib-back-button" }, { kind: "component", type: DetailHeaderComponent, selector: "lib-detail-header", inputs: ["id", "text", "route", "canEdit", "canDelete"], outputs: ["deleteClicked"] }] });
1166
1225
  }
1167
1226
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: UserDetailComponent, decorators: [{
1168
1227
  type: Component,
1169
- args: [{ selector: 'lib-user-detail', imports: [NgIf,
1170
- BackButtonComponent,
1171
- DetailHeaderComponent], template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n @if(item(); as item){\n <div class=\"item-detail-container\">\n\n <lib-detail-header\n [text]=\"item.username\"\n [id]=\"item.id\"\n [route]=\"typename\"\n [canEdit]=\"canEdit()\"\n [canDelete]=\"canDelete()\"\n (onDelete)=\"onDelete\"></lib-detail-header>\n\n @if(item.profile_url){\n <div class=\"image-holder\">\n <img alt=\"User Profile Image\" src=\"{{item.url}}{{item.profile_url}}\" style=\"height:120px;\">\n </div> \n }\n \n <div class=\"item-field\">\n <span><b>Email: </b></span>\n {{item.email}}\n </div>\n \n <div class=\"item-field\">\n <span><b>First Name: </b></span>\n {{item.first_name}}\n </div>\n \n <div class=\"item-field\">\n <span><b>Last Name: </b></span>\n {{item.last_name}}\n </div>\n \n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}\n"] }]
1228
+ args: [{ selector: 'lib-user-detail', imports: [BackButtonComponent,
1229
+ DetailHeaderComponent], template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n @if(item(); as item){\n <div class=\"item-detail-container\">\n\n <lib-detail-header\n [text]=\"item.username\"\n [id]=\"item.id\"\n [route]=\"itemService.typePlural()\"\n [canEdit]=\"canEdit()\"\n [canDelete]=\"canDelete()\"\n (onDelete)=\"onDelete()\"></lib-detail-header>\n\n @if(item.profile_url){\n <div class=\"image-holder\">\n <img alt=\"User Profile Image\" src=\"{{item.url}}{{item.profile_url}}\" style=\"height:120px;\">\n </div> \n }\n \n <div class=\"item-field\">\n <span><b>Email: </b></span>\n {{item.email}}\n </div>\n \n <div class=\"item-field\">\n <span><b>First Name: </b></span>\n {{item.first_name}}\n </div>\n \n <div class=\"item-field\">\n <span><b>Last Name: </b></span>\n {{item.last_name}}\n </div>\n \n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}\n"] }]
1172
1230
  }], ctorParameters: () => [] });
1173
1231
 
1174
- class UserEditComponent extends EditView {
1175
- typename = "User";
1176
- files = signal([new FileRecord({ name: "image",
1177
- display_name: "Profile Image",
1178
- file_type: "image" })], ...(ngDevMode ? [{ debugName: "files" }] : []));
1232
+ class UserForm {
1179
1233
  formBuilder = inject(FormBuilder);
1180
1234
  form = this.formBuilder.group({
1181
1235
  username: [''],
@@ -1184,17 +1238,7 @@ class UserEditComponent extends EditView {
1184
1238
  last_name: [''],
1185
1239
  phone: ['']
1186
1240
  });
1187
- constructor() {
1188
- super(inject(UserService));
1189
- }
1190
- ngOnInit() {
1191
- this.onInit();
1192
- }
1193
- onItemAvailable() {
1194
- const item = this.item();
1195
- if (!item) {
1196
- return;
1197
- }
1241
+ setValue(item) {
1198
1242
  this.form.setValue({
1199
1243
  username: item.username,
1200
1244
  email: item.email,
@@ -1202,31 +1246,55 @@ class UserEditComponent extends EditView {
1202
1246
  last_name: item.last_name,
1203
1247
  phone: item.phone
1204
1248
  });
1249
+ }
1250
+ createItem() {
1251
+ const value = this.form.value;
1252
+ return {
1253
+ username: value.username || "",
1254
+ email: value.email || "",
1255
+ first_name: value.first_name || "",
1256
+ last_name: value.last_name || "",
1257
+ phone: value.phone || ""
1258
+ };
1259
+ }
1260
+ updateItem(item) {
1261
+ const value = this.form.value;
1262
+ item.username = value.username || "";
1263
+ item.email = value.email || "";
1264
+ item.first_name = value.first_name || "";
1265
+ item.last_name = value.last_name || "";
1266
+ item.phone = value.phone || "";
1267
+ return item;
1268
+ }
1269
+ }
1270
+
1271
+ class UserEditComponent extends EditView {
1272
+ files = signal([new FileRecord({
1273
+ name: "image",
1274
+ display_name: "Profile Image",
1275
+ file_type: "image"
1276
+ })], ...(ngDevMode ? [{ debugName: "files" }] : []));
1277
+ constructor() {
1278
+ super(inject(UserService), new UserForm());
1279
+ }
1280
+ ngOnInit() {
1281
+ this.onInit();
1282
+ }
1283
+ onItemAvailable(item) {
1284
+ this.form.setValue(item);
1205
1285
  if (item.profile_url) {
1206
1286
  this.files.update(files => { const file = files.find(f => f.name === "image"); if (file) {
1207
1287
  file.remote = item.url + item.profile_url;
1208
1288
  } ; return files; });
1209
1289
  }
1210
- super.onItemAvailable();
1290
+ super.onItemAvailable(item);
1211
1291
  }
1212
1292
  submit() {
1213
1293
  this.item.update((item) => { if (item) {
1214
- item = this.updateFromForm(item);
1294
+ item = this.form.updateItem(item);
1215
1295
  } return item; });
1216
- console.log(this.item());
1217
1296
  super.submit();
1218
1297
  }
1219
- updateFromForm(item) {
1220
- item.username = this.form.value.username || "";
1221
- item.email = this.form.value.email || "";
1222
- item.first_name = this.form.value.first_name || "";
1223
- item.last_name = this.form.value.last_name || "";
1224
- item.phone = this.form.value.phone || "";
1225
- return item;
1226
- }
1227
- getTemplateItem() {
1228
- return new PortalMember();
1229
- }
1230
1298
  onPreviewLoaded(preview) {
1231
1299
  this.files.update(files => { const file = files.find(f => f.name === preview.name); if (file) {
1232
1300
  file.local = preview.content;
@@ -1239,23 +1307,24 @@ class UserEditComponent extends EditView {
1239
1307
  }
1240
1308
  saveFiles(item) {
1241
1309
  this.files().forEach(record => { if (record.file) {
1242
- this._restService.postFile(item.id, record.name, record.file).subscribe(item => this.onItemUpdated(item));
1310
+ this.itemService.postFile(item.id, record.name, record.file).subscribe(item => this.onItemUpdated(item));
1243
1311
  } ; });
1244
1312
  }
1245
1313
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: UserEditComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1246
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: UserEditComponent, isStandalone: true, selector: "lib-user-edit", usesInheritance: true, ngImport: i0, template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n\n @if(item(); as item){\n <div class=\"item-edit-container\">\n\n <h1 class=\"item-edit-header\">{{heading()}}</h1>\n\n <form class=\"form-card\" [formGroup]=\"form\" (ngSubmit)=\"submit()\">\n <mat-form-field class=\"form-field-wide\">\n <mat-label>Username</mat-label>\n <input matInput\n placeholder=\"Username\"\n type=\"text\"\n formControlName=\"username\"\n name=\"username\">\n </mat-form-field>\n \n <mat-form-field class=\"form-field-wide\">\n <mat-label>Email</mat-label>\n <input matInput\n placeholder=\"Email\"\n type=\"text\"\n formControlName=\"email\"\n name=\"email\">\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label>First Name</mat-label>\n <input matInput\n placeholder=\"First Name\"\n type=\"text\"\n formControlName=\"first_name\"\n name=\"first_name\">\n </mat-form-field>\n \n <mat-form-field class=\"form-field\">\n <mat-label>Last Name</mat-label>\n <input matInput\n placeholder=\"Last Name\"\n type=\"text\"\n formControlName=\"last_name\"\n name=\"last_name\">\n </mat-form-field>\n\n <lib-file-upload [files]=\"files()\"\n (previewLoaded)=\"onPreviewLoaded($event)\"\n (fileUploaded)=\"onFileUploaded($event)\"></lib-file-upload>\n \n <div class=\"button_group\">\n <button mat-fab\n class=\"form_action_button\"\n type=\"submit\"\n [disabled]=\"!form.valid\">\n <mat-icon>save</mat-icon></button>\n \n <button mat-fab\n class=\"form_action_button\"\n (click)=\"cancel()\">\n <mat-icon>cancel</mat-icon></button>\n </div>\n \n </form>\n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}.item_view{display:flex;justify-content:left;align-items:left;flex-direction:column}.checkbox_group{justify-content:left;display:flex;flex-direction:column}.button_group{margin:10px;display:flex;flex-direction:row;justify-content:center}.btn-block{padding:5px}.form_action_button{padding:5px;margin:5px}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.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: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: BackButtonComponent, selector: "lib-back-button" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3$2.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: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: FileUploadComponent, selector: "lib-file-upload", inputs: ["files"], outputs: ["previewLoaded", "fileUploaded"] }] });
1314
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: UserEditComponent, isStandalone: true, selector: "lib-user-edit", usesInheritance: true, ngImport: i0, template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n\n @if(item(); as item){\n <div class=\"item-edit-container\">\n\n <h1 class=\"item-edit-header\">{{heading() | titlecase}}</h1>\n\n <form class=\"form-card\" [formGroup]=\"form.form\" (ngSubmit)=\"submit()\">\n <mat-form-field class=\"form-field-wide\">\n <mat-label>Username</mat-label>\n <input matInput\n placeholder=\"Username\"\n type=\"text\"\n formControlName=\"username\"\n name=\"username\">\n </mat-form-field>\n \n <mat-form-field class=\"form-field-wide\">\n <mat-label>Email</mat-label>\n <input matInput\n placeholder=\"Email\"\n type=\"text\"\n formControlName=\"email\"\n name=\"email\">\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label>First Name</mat-label>\n <input matInput\n placeholder=\"First Name\"\n type=\"text\"\n formControlName=\"first_name\"\n name=\"first_name\">\n </mat-form-field>\n \n <mat-form-field class=\"form-field\">\n <mat-label>Last Name</mat-label>\n <input matInput\n placeholder=\"Last Name\"\n type=\"text\"\n formControlName=\"last_name\"\n name=\"last_name\">\n </mat-form-field>\n\n <lib-file-upload [files]=\"files()\"\n (previewLoaded)=\"onPreviewLoaded($event)\"\n (fileUploaded)=\"onFileUploaded($event)\"></lib-file-upload>\n </form>\n\n <div class=\"button_group\">\n <button mat-fab\n class=\"form_action_button\"\n type=\"submit\"\n [disabled]=\"!form.form.valid\">\n <mat-icon>save</mat-icon></button>\n \n <button mat-fab\n class=\"form_action_button\"\n (click)=\"cancel()\">\n <mat-icon>cancel</mat-icon></button>\n </div>\n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}.item_view{display:flex;justify-content:left;align-items:left;flex-direction:column}.checkbox_group{justify-content:left;display:flex;flex-direction:column}.button_group{margin:10px;display:flex;flex-direction:row;justify-content:center}.btn-block{padding:5px}.form_action_button{padding:5px;margin:5px}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.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: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: BackButtonComponent, selector: "lib-back-button" }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3$2.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: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: FileUploadComponent, selector: "lib-file-upload", inputs: ["files"], outputs: ["previewLoaded", "fileUploaded", "clearLocal"] }, { kind: "pipe", type: TitleCasePipe, name: "titlecase" }] });
1247
1315
  }
1248
1316
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: UserEditComponent, decorators: [{
1249
1317
  type: Component,
1250
- args: [{ selector: 'lib-user-edit', imports: [NgIf,
1318
+ args: [{ selector: 'lib-user-edit', imports: [
1251
1319
  ReactiveFormsModule,
1252
1320
  MatButtonModule,
1253
1321
  BackButtonComponent,
1254
1322
  MatInputModule,
1255
1323
  MatFormFieldModule,
1256
1324
  MatIconModule,
1257
- FileUploadComponent
1258
- ], template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n\n @if(item(); as item){\n <div class=\"item-edit-container\">\n\n <h1 class=\"item-edit-header\">{{heading()}}</h1>\n\n <form class=\"form-card\" [formGroup]=\"form\" (ngSubmit)=\"submit()\">\n <mat-form-field class=\"form-field-wide\">\n <mat-label>Username</mat-label>\n <input matInput\n placeholder=\"Username\"\n type=\"text\"\n formControlName=\"username\"\n name=\"username\">\n </mat-form-field>\n \n <mat-form-field class=\"form-field-wide\">\n <mat-label>Email</mat-label>\n <input matInput\n placeholder=\"Email\"\n type=\"text\"\n formControlName=\"email\"\n name=\"email\">\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label>First Name</mat-label>\n <input matInput\n placeholder=\"First Name\"\n type=\"text\"\n formControlName=\"first_name\"\n name=\"first_name\">\n </mat-form-field>\n \n <mat-form-field class=\"form-field\">\n <mat-label>Last Name</mat-label>\n <input matInput\n placeholder=\"Last Name\"\n type=\"text\"\n formControlName=\"last_name\"\n name=\"last_name\">\n </mat-form-field>\n\n <lib-file-upload [files]=\"files()\"\n (previewLoaded)=\"onPreviewLoaded($event)\"\n (fileUploaded)=\"onFileUploaded($event)\"></lib-file-upload>\n \n <div class=\"button_group\">\n <button mat-fab\n class=\"form_action_button\"\n type=\"submit\"\n [disabled]=\"!form.valid\">\n <mat-icon>save</mat-icon></button>\n \n <button mat-fab\n class=\"form_action_button\"\n (click)=\"cancel()\">\n <mat-icon>cancel</mat-icon></button>\n </div>\n \n </form>\n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}.item_view{display:flex;justify-content:left;align-items:left;flex-direction:column}.checkbox_group{justify-content:left;display:flex;flex-direction:column}.button_group{margin:10px;display:flex;flex-direction:row;justify-content:center}.btn-block{padding:5px}.form_action_button{padding:5px;margin:5px}\n"] }]
1325
+ FileUploadComponent,
1326
+ TitleCasePipe
1327
+ ], template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n\n @if(item(); as item){\n <div class=\"item-edit-container\">\n\n <h1 class=\"item-edit-header\">{{heading() | titlecase}}</h1>\n\n <form class=\"form-card\" [formGroup]=\"form.form\" (ngSubmit)=\"submit()\">\n <mat-form-field class=\"form-field-wide\">\n <mat-label>Username</mat-label>\n <input matInput\n placeholder=\"Username\"\n type=\"text\"\n formControlName=\"username\"\n name=\"username\">\n </mat-form-field>\n \n <mat-form-field class=\"form-field-wide\">\n <mat-label>Email</mat-label>\n <input matInput\n placeholder=\"Email\"\n type=\"text\"\n formControlName=\"email\"\n name=\"email\">\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label>First Name</mat-label>\n <input matInput\n placeholder=\"First Name\"\n type=\"text\"\n formControlName=\"first_name\"\n name=\"first_name\">\n </mat-form-field>\n \n <mat-form-field class=\"form-field\">\n <mat-label>Last Name</mat-label>\n <input matInput\n placeholder=\"Last Name\"\n type=\"text\"\n formControlName=\"last_name\"\n name=\"last_name\">\n </mat-form-field>\n\n <lib-file-upload [files]=\"files()\"\n (previewLoaded)=\"onPreviewLoaded($event)\"\n (fileUploaded)=\"onFileUploaded($event)\"></lib-file-upload>\n </form>\n\n <div class=\"button_group\">\n <button mat-fab\n class=\"form_action_button\"\n type=\"submit\"\n [disabled]=\"!form.form.valid\">\n <mat-icon>save</mat-icon></button>\n \n <button mat-fab\n class=\"form_action_button\"\n (click)=\"cancel()\">\n <mat-icon>cancel</mat-icon></button>\n </div>\n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}.item_view{display:flex;justify-content:left;align-items:left;flex-direction:column}.checkbox_group{justify-content:left;display:flex;flex-direction:column}.button_group{margin:10px;display:flex;flex-direction:row;justify-content:center}.btn-block{padding:5px}.form_action_button{padding:5px;margin:5px}\n"] }]
1259
1328
  }], ctorParameters: () => [] });
1260
1329
 
1261
1330
  class UserListDetailComponent {
@@ -1272,17 +1341,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImpor
1272
1341
  class UserComponent {
1273
1342
  viewType = signal("list", ...(ngDevMode ? [{ debugName: "viewType" }] : []));
1274
1343
  itemService = inject(UserService);
1275
- columns = [{ name: 'username', title: 'Name', element_type: 'string' }];
1344
+ columns = USER_TABLE_FULL;
1276
1345
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: UserComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1277
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.1", type: UserComponent, isStandalone: true, selector: "lib-user", ngImport: i0, template: "<lib-list-view\n [itemService]=\"itemService\"\n [itemDetailTemplate]=\"listItemTemplate\"\n [columns]=\"columns\"\n [viewType]=\"viewType()\"\n >\n <ng-template #listItemTemplate let-item=\"item\">\n <lib-user-list-detail [item]=\"item\"\n [routerLink]=\"['/' + itemService.itemType + '/detail/', item.id]\"\n [routerLinkActive]=\"['is-active']\">\n </lib-user-list-detail>\n </ng-template>\n</lib-list-view>\n", styles: [":host{flex-grow:1}\n"], dependencies: [{ kind: "component", type: ListViewComponent, selector: "lib-list-view", inputs: ["viewType", "itemService", "itemDetailTemplate", "columns", "sortFields", "noSelfItemsMessage", "noItemsCanCreateMessage", "noItemsMessage"] }, { kind: "component", type: UserListDetailComponent, selector: "lib-user-list-detail", inputs: ["item"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }] });
1346
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.1", type: UserComponent, isStandalone: true, selector: "lib-user", ngImport: i0, template: "<lib-list-view\n [itemService]=\"itemService\"\n [itemDetailTemplate]=\"listItemTemplate\"\n [columns]=\"columns\"\n [viewType]=\"viewType()\"\n >\n <ng-template #listItemTemplate let-item=\"item\">\n <lib-user-list-detail [item]=\"item\"\n [routerLink]=\"['/' + itemService.typePlural() + '/detail/', item.id]\"\n [routerLinkActive]=\"['is-active']\">\n </lib-user-list-detail>\n </ng-template>\n</lib-list-view>\n", styles: [":host{flex-grow:1}\n"], dependencies: [{ kind: "component", type: ListViewComponent, selector: "lib-list-view", inputs: ["viewType", "itemService", "itemDetailTemplate", "columns", "sortFields", "noSelfItemsMessage", "noItemsCanCreateMessage", "noItemsMessage"] }, { kind: "component", type: UserListDetailComponent, selector: "lib-user-list-detail", inputs: ["item"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }] });
1278
1347
  }
1279
1348
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: UserComponent, decorators: [{
1280
1349
  type: Component,
1281
- args: [{ selector: 'lib-user', imports: [ListViewComponent, UserListDetailComponent, RouterModule], template: "<lib-list-view\n [itemService]=\"itemService\"\n [itemDetailTemplate]=\"listItemTemplate\"\n [columns]=\"columns\"\n [viewType]=\"viewType()\"\n >\n <ng-template #listItemTemplate let-item=\"item\">\n <lib-user-list-detail [item]=\"item\"\n [routerLink]=\"['/' + itemService.itemType + '/detail/', item.id]\"\n [routerLinkActive]=\"['is-active']\">\n </lib-user-list-detail>\n </ng-template>\n</lib-list-view>\n", styles: [":host{flex-grow:1}\n"] }]
1350
+ args: [{ selector: 'lib-user', imports: [ListViewComponent, UserListDetailComponent, RouterModule], template: "<lib-list-view\n [itemService]=\"itemService\"\n [itemDetailTemplate]=\"listItemTemplate\"\n [columns]=\"columns\"\n [viewType]=\"viewType()\"\n >\n <ng-template #listItemTemplate let-item=\"item\">\n <lib-user-list-detail [item]=\"item\"\n [routerLink]=\"['/' + itemService.typePlural() + '/detail/', item.id]\"\n [routerLinkActive]=\"['is-active']\">\n </lib-user-list-detail>\n </ng-template>\n</lib-list-view>\n", styles: [":host{flex-grow:1}\n"] }]
1282
1351
  }] });
1283
1352
 
1284
1353
  class GroupDetailComponent extends DetailView {
1285
- typename = "group";
1286
1354
  constructor() {
1287
1355
  super(inject(GroupService));
1288
1356
  }
@@ -1290,13 +1358,12 @@ class GroupDetailComponent extends DetailView {
1290
1358
  this.onInit();
1291
1359
  }
1292
1360
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: GroupDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1293
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: GroupDetailComponent, isStandalone: true, selector: "lib-group-detail", usesInheritance: true, ngImport: i0, template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n @if(item(); as item) {\n <div class=\"item-detail-container\">\n\n <lib-detail-header\n [text]=\"item.name\"\n [id]=\"item.id\"\n [route]=\"typename\"></lib-detail-header>\n \n <div class=\"item-field\">{{item.name}}</div>\n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}\n"], dependencies: [{ kind: "component", type: BackButtonComponent, selector: "lib-back-button" }, { kind: "component", type: DetailHeaderComponent, selector: "lib-detail-header", inputs: ["id", "text", "route", "canEdit", "canDelete"], outputs: ["deleteClicked"] }] });
1361
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: GroupDetailComponent, isStandalone: true, selector: "lib-group-detail", usesInheritance: true, ngImport: i0, template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n @if(item(); as item) {\n <div class=\"item-detail-container\">\n\n <lib-detail-header\n [text]=\"item.name\"\n [id]=\"item.id\"\n [canEdit]=\"itemService.canEdit()\"\n [canDelete]=\"itemService.canDelete()\"\n [route]=\"itemService.typePlural()\"></lib-detail-header>\n \n <div class=\"item-field\">{{item.name}}</div>\n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}\n"], dependencies: [{ kind: "component", type: BackButtonComponent, selector: "lib-back-button" }, { kind: "component", type: DetailHeaderComponent, selector: "lib-detail-header", inputs: ["id", "text", "route", "canEdit", "canDelete"], outputs: ["deleteClicked"] }] });
1294
1362
  }
1295
1363
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: GroupDetailComponent, decorators: [{
1296
1364
  type: Component,
1297
- args: [{ selector: 'lib-group-detail', imports: [NgIf,
1298
- BackButtonComponent,
1299
- DetailHeaderComponent], template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n @if(item(); as item) {\n <div class=\"item-detail-container\">\n\n <lib-detail-header\n [text]=\"item.name\"\n [id]=\"item.id\"\n [route]=\"typename\"></lib-detail-header>\n \n <div class=\"item-field\">{{item.name}}</div>\n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}\n"] }]
1365
+ args: [{ selector: 'lib-group-detail', imports: [BackButtonComponent,
1366
+ DetailHeaderComponent], template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n @if(item(); as item) {\n <div class=\"item-detail-container\">\n\n <lib-detail-header\n [text]=\"item.name\"\n [id]=\"item.id\"\n [canEdit]=\"itemService.canEdit()\"\n [canDelete]=\"itemService.canDelete()\"\n [route]=\"itemService.typePlural()\"></lib-detail-header>\n \n <div class=\"item-field\">{{item.name}}</div>\n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}\n"] }]
1300
1367
  }], ctorParameters: () => [] });
1301
1368
 
1302
1369
  class GroupComponent {
@@ -1314,7 +1381,7 @@ class AddressEditComponent {
1314
1381
  countryOptions = input([], ...(ngDevMode ? [{ debugName: "countryOptions" }] : []));
1315
1382
  form = input(...(ngDevMode ? [undefined, { debugName: "form" }] : []));
1316
1383
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: AddressEditComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1317
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: AddressEditComponent, isStandalone: true, selector: "lib-address-edit", inputs: { countryOptions: { classPropertyName: "countryOptions", publicName: "countryOptions", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if(form(); as form){\n<div [formGroup]=\"form\" style=\"display: flex; flex-direction: column; width:100%\">\n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"line1\">Line 1</mat-label>\n <input matInput placeholder=\"Address Line 1\" type=\"text\" id=\"line1\" formControlName=\"line1\" name=\"line1\"\n required>\n </mat-form-field>\n\n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"line2\">Line 2</mat-label>\n <input matInput placeholder=\"Address Line 2\" type=\"text\" id=\"line2\" formControlName=\"line2\" name=\"line1\" required>\n </mat-form-field>\n\n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"line3\">Line 3</mat-label>\n <input matInput placeholder=\"Address Line 3\" type=\"text\" id=\"line3\" formControlName=\"line3\" name=\"line3\" required>\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label for=\"city\">City</mat-label>\n <input matInput placeholder=\"City\" type=\"text\" id=\"city\" formControlName=\"city\" name=\"city\" required>\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label for=\"region\">Region</mat-label>\n <input matInput placeholder=\"Region\" type=\"text\" id=\"region\" formControlName=\"region\" name=\"region\" required>\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label for=\"line1\">Post Code</mat-label>\n <input matInput placeholder=\"Post Code\" type=\"text\" id=\"postcode\" formControlName=\"postcode\" name=\"postcode\" required>\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label for=\"country\">Country</mat-label>\n <mat-select formControlName=\"country\" name=\"country\" id=\"country\" required>\n @for(country of countryOptions(); track $index){\n <mat-option [value]=\"country.code\">{{country.name}}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n}", styles: [""], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.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"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3$2.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: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.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: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
1384
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: AddressEditComponent, isStandalone: true, selector: "lib-address-edit", inputs: { countryOptions: { classPropertyName: "countryOptions", publicName: "countryOptions", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if(form(); as form){\n<div [formGroup]=\"form\" style=\"display: flex; flex-direction: column; width:100%\">\n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"line1\">Line 1</mat-label>\n <input matInput placeholder=\"Address Line 1\" type=\"text\" id=\"line1\" formControlName=\"line1\" name=\"line1\"\n required>\n </mat-form-field>\n\n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"line2\">Line 2</mat-label>\n <input matInput placeholder=\"Address Line 2\" type=\"text\" id=\"line2\" formControlName=\"line2\" name=\"line1\">\n </mat-form-field>\n\n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"line3\">Line 3</mat-label>\n <input matInput placeholder=\"Address Line 3\" type=\"text\" id=\"line3\" formControlName=\"line3\" name=\"line3\">\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label for=\"city\">City</mat-label>\n <input matInput placeholder=\"City\" type=\"text\" id=\"city\" formControlName=\"city\" name=\"city\">\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label for=\"region\">Region</mat-label>\n <input matInput placeholder=\"Region\" type=\"text\" id=\"region\" formControlName=\"region\" name=\"region\" required>\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label for=\"line1\">Post Code</mat-label>\n <input matInput placeholder=\"Post Code\" type=\"text\" id=\"postcode\" formControlName=\"postcode\" name=\"postcode\">\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label for=\"country\">Country</mat-label>\n <mat-select formControlName=\"country\" name=\"country\" id=\"country\" required>\n @for(country of countryOptions(); track $index){\n <mat-option [value]=\"country.code\">{{country.name}}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n}", styles: [""], dependencies: [{ kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.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"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3$2.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: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.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: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
1318
1385
  }
1319
1386
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: AddressEditComponent, decorators: [{
1320
1387
  type: Component,
@@ -1322,7 +1389,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImpor
1322
1389
  MatSelectModule,
1323
1390
  MatInputModule,
1324
1391
  ReactiveFormsModule
1325
- ], template: "@if(form(); as form){\n<div [formGroup]=\"form\" style=\"display: flex; flex-direction: column; width:100%\">\n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"line1\">Line 1</mat-label>\n <input matInput placeholder=\"Address Line 1\" type=\"text\" id=\"line1\" formControlName=\"line1\" name=\"line1\"\n required>\n </mat-form-field>\n\n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"line2\">Line 2</mat-label>\n <input matInput placeholder=\"Address Line 2\" type=\"text\" id=\"line2\" formControlName=\"line2\" name=\"line1\" required>\n </mat-form-field>\n\n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"line3\">Line 3</mat-label>\n <input matInput placeholder=\"Address Line 3\" type=\"text\" id=\"line3\" formControlName=\"line3\" name=\"line3\" required>\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label for=\"city\">City</mat-label>\n <input matInput placeholder=\"City\" type=\"text\" id=\"city\" formControlName=\"city\" name=\"city\" required>\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label for=\"region\">Region</mat-label>\n <input matInput placeholder=\"Region\" type=\"text\" id=\"region\" formControlName=\"region\" name=\"region\" required>\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label for=\"line1\">Post Code</mat-label>\n <input matInput placeholder=\"Post Code\" type=\"text\" id=\"postcode\" formControlName=\"postcode\" name=\"postcode\" required>\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label for=\"country\">Country</mat-label>\n <mat-select formControlName=\"country\" name=\"country\" id=\"country\" required>\n @for(country of countryOptions(); track $index){\n <mat-option [value]=\"country.code\">{{country.name}}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n}" }]
1392
+ ], template: "@if(form(); as form){\n<div [formGroup]=\"form\" style=\"display: flex; flex-direction: column; width:100%\">\n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"line1\">Line 1</mat-label>\n <input matInput placeholder=\"Address Line 1\" type=\"text\" id=\"line1\" formControlName=\"line1\" name=\"line1\"\n required>\n </mat-form-field>\n\n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"line2\">Line 2</mat-label>\n <input matInput placeholder=\"Address Line 2\" type=\"text\" id=\"line2\" formControlName=\"line2\" name=\"line1\">\n </mat-form-field>\n\n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"line3\">Line 3</mat-label>\n <input matInput placeholder=\"Address Line 3\" type=\"text\" id=\"line3\" formControlName=\"line3\" name=\"line3\">\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label for=\"city\">City</mat-label>\n <input matInput placeholder=\"City\" type=\"text\" id=\"city\" formControlName=\"city\" name=\"city\">\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label for=\"region\">Region</mat-label>\n <input matInput placeholder=\"Region\" type=\"text\" id=\"region\" formControlName=\"region\" name=\"region\" required>\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label for=\"line1\">Post Code</mat-label>\n <input matInput placeholder=\"Post Code\" type=\"text\" id=\"postcode\" formControlName=\"postcode\" name=\"postcode\">\n </mat-form-field>\n\n <mat-form-field class=\"form-field\">\n <mat-label for=\"country\">Country</mat-label>\n <mat-select formControlName=\"country\" name=\"country\" id=\"country\" required>\n @for(country of countryOptions(); track $index){\n <mat-option [value]=\"country.code\">{{country.name}}</mat-option>\n }\n </mat-select>\n </mat-form-field>\n</div>\n}" }]
1326
1393
  }] });
1327
1394
 
1328
1395
  class AddressDetailComponent {
@@ -1349,22 +1416,32 @@ class AddressForm {
1349
1416
  setValue(item) {
1350
1417
  this.form.setValue({
1351
1418
  line1: item.line1,
1352
- line2: item.line2,
1353
- line3: item.line3,
1354
- city: item.city,
1419
+ line2: item.line2 || "",
1420
+ line3: item.line3 || "",
1421
+ city: item.city || "",
1355
1422
  region: item.region,
1356
- postcode: item.postcode,
1423
+ postcode: item.postcode || "",
1357
1424
  country: item.country
1358
1425
  });
1359
1426
  }
1427
+ createItem() {
1428
+ const value = this.form.value;
1429
+ return { line1: value.line1 || "",
1430
+ line2: value.line2 || null,
1431
+ line3: value.line3 || null,
1432
+ city: value.city || null,
1433
+ region: value.region || "",
1434
+ postcode: value.postcode || null,
1435
+ country: value.country || "" };
1436
+ }
1360
1437
  updateItem(item) {
1361
1438
  const value = this.form.value;
1362
1439
  item.line1 = value.line1 || "";
1363
- item.line2 = value.line2 || "";
1364
- item.line3 = value.line3 || "";
1365
- item.city = value.city || "";
1440
+ item.line2 = value.line2 || null;
1441
+ item.line3 = value.line3 || null;
1442
+ item.city = value.city || null;
1366
1443
  item.region = value.region || "";
1367
- item.postcode = value.postcode || "";
1444
+ item.postcode = value.postcode || null;
1368
1445
  item.country = value.country || "";
1369
1446
  return item;
1370
1447
  }
@@ -1386,25 +1463,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImpor
1386
1463
  }] });
1387
1464
 
1388
1465
  class OrganizationDetailComponent extends DetailView {
1389
- typename = "organization";
1390
- address = signal(null, ...(ngDevMode ? [{ debugName: "address" }] : []));
1391
- addressService = inject(AddressService);
1392
1466
  constructor() {
1393
1467
  super(inject(OrganizationService));
1394
1468
  }
1395
1469
  ngOnInit() {
1396
1470
  this.onInit();
1397
1471
  }
1398
- onItemAndUserAvailable(item, _user) {
1399
- this.addressService.getUrl(item.address).subscribe(address => this.address.set(address));
1400
- }
1401
1472
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: OrganizationDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1402
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: OrganizationDetailComponent, isStandalone: true, selector: "lib-organization-detail", usesInheritance: true, ngImport: i0, template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n\n @if(item(); as item){\n <div class=\"item-detail-container\">\n\n <lib-detail-header\n [text]=\"item.name\"\n [id]=\"item.id\"\n [canEdit]=\"itemService.canEdit()\"\n [canDelete]=\"itemService.canDelete()\"\n [route]=\"typename\"></lib-detail-header>\n\n <div class=\"item-field\">\n {{item.description}}\n </div>\n \n @if(address(); as address)\n {\n <h3>Address</h3>\n <lib-address-detail [address]=\"address\"></lib-address-detail>\n }\n <div class=\"item-field\">\n <h3>Website</h3>{{item.website}}\n </div>\n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}.padded_button{padding:5px}\n"], dependencies: [{ kind: "component", type: BackButtonComponent, selector: "lib-back-button" }, { kind: "component", type: DetailHeaderComponent, selector: "lib-detail-header", inputs: ["id", "text", "route", "canEdit", "canDelete"], outputs: ["deleteClicked"] }, { kind: "component", type: AddressDetailComponent, selector: "lib-address-detail", inputs: ["address"] }] });
1473
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: OrganizationDetailComponent, isStandalone: true, selector: "lib-organization-detail", usesInheritance: true, ngImport: i0, template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n\n @if(item(); as item){\n <div class=\"item-detail-container\">\n\n <lib-detail-header\n [text]=\"item.name\"\n [id]=\"item.id\"\n [canEdit]=\"itemService.canEdit()\"\n [canDelete]=\"itemService.canDelete()\"\n [route]=\"itemService.typePlural()\"></lib-detail-header>\n\n <div class=\"item-field\">\n {{item.description}}\n </div>\n \n <h3>Address</h3>\n <lib-address-detail [address]=\"item.address\"></lib-address-detail>\n\n <div class=\"item-field\">\n <h3>Website</h3>{{item.website}}\n </div>\n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}.padded_button{padding:5px}\n"], dependencies: [{ kind: "component", type: BackButtonComponent, selector: "lib-back-button" }, { kind: "component", type: DetailHeaderComponent, selector: "lib-detail-header", inputs: ["id", "text", "route", "canEdit", "canDelete"], outputs: ["deleteClicked"] }, { kind: "component", type: AddressDetailComponent, selector: "lib-address-detail", inputs: ["address"] }] });
1403
1474
  }
1404
1475
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: OrganizationDetailComponent, decorators: [{
1405
1476
  type: Component,
1406
1477
  args: [{ selector: 'lib-organization-detail', imports: [BackButtonComponent,
1407
- DetailHeaderComponent, AddressDetailComponent], template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n\n @if(item(); as item){\n <div class=\"item-detail-container\">\n\n <lib-detail-header\n [text]=\"item.name\"\n [id]=\"item.id\"\n [canEdit]=\"itemService.canEdit()\"\n [canDelete]=\"itemService.canDelete()\"\n [route]=\"typename\"></lib-detail-header>\n\n <div class=\"item-field\">\n {{item.description}}\n </div>\n \n @if(address(); as address)\n {\n <h3>Address</h3>\n <lib-address-detail [address]=\"address\"></lib-address-detail>\n }\n <div class=\"item-field\">\n <h3>Website</h3>{{item.website}}\n </div>\n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}.padded_button{padding:5px}\n"] }]
1478
+ DetailHeaderComponent, AddressDetailComponent], template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n\n @if(item(); as item){\n <div class=\"item-detail-container\">\n\n <lib-detail-header\n [text]=\"item.name\"\n [id]=\"item.id\"\n [canEdit]=\"itemService.canEdit()\"\n [canDelete]=\"itemService.canDelete()\"\n [route]=\"itemService.typePlural()\"></lib-detail-header>\n\n <div class=\"item-field\">\n {{item.description}}\n </div>\n \n <h3>Address</h3>\n <lib-address-detail [address]=\"item.address\"></lib-address-detail>\n\n <div class=\"item-field\">\n <h3>Website</h3>{{item.website}}\n </div>\n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}.padded_button{padding:5px}\n"] }]
1408
1479
  }], ctorParameters: () => [] });
1409
1480
 
1410
1481
  class OrganizationForm {
@@ -1420,7 +1491,8 @@ class OrganizationForm {
1420
1491
  address: [this.address.form]
1421
1492
  });
1422
1493
  }
1423
- updateFromItem(item) {
1494
+ setValue(item) {
1495
+ this.address.setValue(item.address);
1424
1496
  this.form.setValue({
1425
1497
  name: item.name,
1426
1498
  acronym: item.acronym,
@@ -1429,87 +1501,51 @@ class OrganizationForm {
1429
1501
  address: this.address.form
1430
1502
  });
1431
1503
  }
1504
+ createItem() {
1505
+ const value = this.form.value;
1506
+ return {
1507
+ name: value.name || "",
1508
+ acronym: value.acronym || "",
1509
+ description: value.description || "",
1510
+ website: value.website || "",
1511
+ address: this.address.createItem(),
1512
+ members: []
1513
+ };
1514
+ }
1432
1515
  updateItem(item) {
1433
1516
  item.name = this.form.value.name || "";
1434
1517
  item.acronym = this.form.value.acronym || "";
1435
1518
  item.description = this.form.value.description || "";
1436
1519
  item.website = this.form.value.website || "";
1520
+ item.address = this.address.updateItem(item.address);
1437
1521
  return item;
1438
1522
  }
1439
1523
  }
1440
1524
 
1441
1525
  class OrganizationEditComponent extends EditView {
1442
- typename = "Organization";
1443
1526
  countryOptions = signal([], ...(ngDevMode ? [{ debugName: "countryOptions" }] : []));
1444
- candidateMembers = signal([], ...(ngDevMode ? [{ debugName: "candidateMembers" }] : []));
1445
- selectedMembers = signal([], ...(ngDevMode ? [{ debugName: "selectedMembers" }] : []));
1446
- columns = [{ name: 'first_name', title: 'First Name', element_type: 'string' },
1447
- { name: 'last_name', title: 'Surname', element_type: 'string' },
1448
- { name: 'username', title: 'Username', element_type: 'string' },
1449
- { name: 'email', title: 'Email', element_type: 'string' }];
1450
- form = new OrganizationForm();
1451
- address = new Address();
1452
- addressService = inject(AddressService);
1527
+ selectionManager = new SelectionManager();
1453
1528
  constructor() {
1454
- super(inject(OrganizationService));
1529
+ super(inject(OrganizationService), new OrganizationForm());
1455
1530
  }
1456
1531
  ngOnInit() {
1457
1532
  this.onInit();
1458
- this.addressService.getOptions().subscribe(options => this.onOptions(options));
1459
- }
1460
- onItemAndUserAvailable(_) {
1461
- const item = this.item();
1462
- if (item) {
1463
- this.fetchAddress(item.address);
1464
- this.form.updateFromItem(item);
1465
- const queries = new Map([["organization", item.id.toString()]]);
1466
- const query = new ItemQuery({ queries: queries });
1467
- this._userService.get(query).subscribe(users => this.selectedMembers.set(users.results));
1468
- this._userService.get().subscribe(users => this.updateCandidates(users));
1469
- }
1470
1533
  }
1471
- updateCandidates(users) {
1472
- this.candidateMembers.set(users.results.map(user => { return { title: user.username, item: user }; }));
1473
- }
1474
- submit() {
1475
- this.syncFromForm();
1476
- if (this.createMode()) {
1477
- this.createAddress().subscribe(_ => super.submit());
1478
- }
1479
- else {
1480
- this.updateAddress();
1481
- super.submit();
1482
- }
1483
- }
1484
- fetchAddress(url) {
1485
- if (url) {
1486
- this.addressService.getUrl(url).subscribe(address => this.onAddressUpdated(address));
1487
- }
1488
- }
1489
- updateAddress() {
1490
- this.addressService.putItem(this.address).subscribe(address => this.onAddressUpdated(address));
1491
- }
1492
- createAddress() {
1493
- return this.addressService.postItem(this.address).pipe(tap(address => { this.onAddressCreated(address); }));
1494
- }
1495
- onAddressUpdated(address) {
1496
- this.address = address;
1497
- this.form.address.setValue(this.address);
1534
+ addressForm() {
1535
+ return this.form.address.form;
1498
1536
  }
1499
- onAddressCreated(address) {
1500
- this.onAddressUpdated(address);
1501
- this.item.update(item => { if (item) {
1502
- item.address = address.url;
1503
- } return item; });
1537
+ createItem() {
1538
+ const item = this.form.createItem();
1539
+ item.members = this.selectionManager.selected().map(m => m.url);
1540
+ return item;
1504
1541
  }
1505
- syncFromForm() {
1506
- this.address = this.form.address.updateItem(this.address);
1507
- this.item.update((item) => { if (item) {
1508
- item = this.form.updateItem(item);
1509
- } return item; });
1542
+ updateItem(item) {
1543
+ item = this.form.updateItem(item);
1544
+ item.members = this.selectionManager.selected().map(m => m.url);
1545
+ return item;
1510
1546
  }
1511
- getTemplateItem() {
1512
- return new Organization();
1547
+ onItemAndUserAvailable(item, _) {
1548
+ this.selectionManager.fetchCandidates(this.itemService.typename, item.id);
1513
1549
  }
1514
1550
  onPostActions(actions) {
1515
1551
  if ('country' in actions && actions['country'].choices) {
@@ -1519,35 +1555,8 @@ class OrganizationEditComponent extends EditView {
1519
1555
  onCountryChoices(choices) {
1520
1556
  this.countryOptions.update(() => choices.map(c => { return { name: c.display_name, code: c.value, flag: "" }; }));
1521
1557
  }
1522
- onMemberRemoved(id) {
1523
- const url = this.selectedMembers().find(m => m.id == id).url;
1524
- this.selectedMembers.update(members => members.filter(m => m.id != id));
1525
- this.item.update(item => { if (item) {
1526
- item.members.filter(m => m != url);
1527
- } return item; });
1528
- }
1529
- onMemberAdded(title) {
1530
- const selected = this.candidateMembers().find(m => m.title == title);
1531
- if (selected) {
1532
- this.selectedMembers.update(members => { members.push(selected.item); return members; });
1533
- }
1534
- this.candidateMembers.update(members => members.filter(m => m.title != title));
1535
- const url = this.selectedMembers().find(m => m.username == title).url;
1536
- this.item.update(item => { if (item) {
1537
- item.members.push(url);
1538
- } return item; });
1539
- }
1540
- onSearchChanged(searchTerm) {
1541
- console.log("Batch requested");
1542
- if (searchTerm) {
1543
- this._userService.get(new ItemQuery({ filter: searchTerm })).subscribe(users => this.updateCandidates(users));
1544
- }
1545
- else {
1546
- this._userService.get().subscribe(users => this.updateCandidates(users));
1547
- }
1548
- }
1549
1558
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: OrganizationEditComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1550
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: OrganizationEditComponent, isStandalone: true, selector: "lib-organization-edit", usesInheritance: true, ngImport: i0, template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n\n @if(item(); as item){\n <div class=\"item-edit-container\">\n \n <h1 class=\"item-edit-header\">{{heading()}}</h1>\n\n <form class=\"form-card\" [formGroup]=\"form.form\" (ngSubmit)=\"submit()\">\n\n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"name\">Name</mat-label>\n <input matInput placeholder=\"Name\"\n type=\"text\"\n id=\"name\"\n formControlName=\"name\"\n name=\"name\"\n required>\n </mat-form-field>\n \n <mat-form-field class=\"form-field\">\n <mat-label for=\"acronym\">Acronym</mat-label> \n <input matInput placeholder=\"Acronym\"\n type=\"text\"\n id=\"acronym\"\n formControlName=\"acronym\"\n name=\"acronym\">\n </mat-form-field>\n \n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"description\">Description</mat-label>\n <textarea matInput placeholder=\"Description\"\n type=\"text\"\n id=\"description\"\n formControlName=\"description\"\n name=\"description\"\n class=\"medium-textarea\"\n required></textarea>\n </mat-form-field>\n\n <h3>Address</h3>\n <lib-address-edit \n [countryOptions]=\"countryOptions()\"\n [form]=\"form.address.form\"\n ></lib-address-edit>\n\n <h3>Members</h3>\n <lib-select-table [selected]=\"selectedMembers()\"\n [itemType]=\"'Members'\"\n [columns]=\"columns\"\n [options]=\"candidateMembers()\"\n (itemAdded)=\"onMemberAdded($event)\"\n (itemRemoved)=\"onMemberRemoved($event)\"\n (searchChanged)=\"onSearchChanged($event)\">\n </lib-select-table>\n\n <div class=\"button_group\">\n <button mat-fab\n class=\"form_action_button\"\n type=\"submit\"\n [disabled]=\"!form.form.valid\">\n <mat-icon>save</mat-icon>\n </button>\n \n <button mat-fab\n class=\"form_action_button\"\n (click)=\"cancel()\">\n <mat-icon>cancel</mat-icon>\n </button>\n </div>\n \n </form>\n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}.medium-textarea{min-height:150px}.checkbox_group{justify-content:left;display:flex;flex-direction:column}.button_group{margin:10px;display:flex;flex-direction:row;justify-content:center}.btn-block{padding:5px}.form_action_button{padding:5px;margin:5px}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.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: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3$2.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: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: BackButtonComponent, selector: "lib-back-button" }, { kind: "component", type: AddressEditComponent, selector: "lib-address-edit", inputs: ["countryOptions", "form"] }, { kind: "component", type: SelectTableComponent, selector: "lib-select-table", inputs: ["itemType", "selected", "options", "columns"], outputs: ["itemAdded", "itemRemoved", "searchChanged"] }] });
1559
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.1", type: OrganizationEditComponent, isStandalone: true, selector: "lib-organization-edit", usesInheritance: true, ngImport: i0, template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n\n @if(item(); as item){\n <div class=\"item-edit-container\">\n \n <h1 class=\"item-edit-header\">{{heading() | titlecase}}</h1>\n\n <form class=\"form-card\" [formGroup]=\"form.form\" (ngSubmit)=\"submit()\">\n\n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"name\">Name</mat-label>\n <input matInput placeholder=\"Name\"\n type=\"text\"\n id=\"name\"\n formControlName=\"name\"\n name=\"name\"\n required>\n </mat-form-field>\n \n <mat-form-field class=\"form-field\">\n <mat-label for=\"acronym\">Acronym</mat-label> \n <input matInput placeholder=\"Acronym\"\n type=\"text\"\n id=\"acronym\"\n formControlName=\"acronym\"\n name=\"acronym\">\n </mat-form-field>\n \n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"description\">Description</mat-label>\n <textarea matInput placeholder=\"Description\"\n type=\"text\"\n id=\"description\"\n formControlName=\"description\"\n name=\"description\"\n class=\"medium-textarea\"\n required></textarea>\n </mat-form-field>\n\n <h3>Address</h3>\n <lib-address-edit \n [countryOptions]=\"countryOptions()\"\n [form]=\"addressForm()\"\n ></lib-address-edit>\n\n <h3>Members</h3>\n <lib-select-table [selected]=\"selectionManager.selected()\"\n [itemType]=\"'Members'\"\n [columns]=\"selectionManager.columns\"\n [options]=\"selectionManager.candidates()\"\n (itemAdded)=\"selectionManager.onAdded($event)\"\n (itemRemoved)=\"selectionManager.onRemoved($event)\"\n (searchChanged)=\"selectionManager.onSearchChanged($event)\">\n </lib-select-table>\n\n </form>\n <div class=\"button_group\">\n <button mat-fab\n class=\"form_action_button\"\n type=\"submit\"\n [disabled]=\"!form.form.valid\">\n <mat-icon>save</mat-icon>\n </button>\n \n <button mat-fab\n class=\"form_action_button\"\n (click)=\"cancel()\">\n <mat-icon>cancel</mat-icon>\n </button>\n </div>\n\n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}.medium-textarea{min-height:150px}.checkbox_group{justify-content:left;display:flex;flex-direction:column}.button_group{margin:10px;display:flex;flex-direction:row;justify-content:center}.btn-block{padding:5px}.form_action_button{padding:5px;margin:5px}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.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: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3$2.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: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatSelectModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: BackButtonComponent, selector: "lib-back-button" }, { kind: "component", type: AddressEditComponent, selector: "lib-address-edit", inputs: ["countryOptions", "form"] }, { kind: "component", type: SelectTableComponent, selector: "lib-select-table", inputs: ["itemType", "selected", "options", "columns"], outputs: ["itemAdded", "itemRemoved", "searchChanged"] }, { kind: "pipe", type: TitleCasePipe, name: "titlecase" }] });
1551
1560
  }
1552
1561
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImport: i0, type: OrganizationEditComponent, decorators: [{
1553
1562
  type: Component,
@@ -1561,8 +1570,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImpor
1561
1570
  MatCheckboxModule,
1562
1571
  BackButtonComponent,
1563
1572
  AddressEditComponent,
1564
- SelectTableComponent
1565
- ], template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n\n @if(item(); as item){\n <div class=\"item-edit-container\">\n \n <h1 class=\"item-edit-header\">{{heading()}}</h1>\n\n <form class=\"form-card\" [formGroup]=\"form.form\" (ngSubmit)=\"submit()\">\n\n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"name\">Name</mat-label>\n <input matInput placeholder=\"Name\"\n type=\"text\"\n id=\"name\"\n formControlName=\"name\"\n name=\"name\"\n required>\n </mat-form-field>\n \n <mat-form-field class=\"form-field\">\n <mat-label for=\"acronym\">Acronym</mat-label> \n <input matInput placeholder=\"Acronym\"\n type=\"text\"\n id=\"acronym\"\n formControlName=\"acronym\"\n name=\"acronym\">\n </mat-form-field>\n \n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"description\">Description</mat-label>\n <textarea matInput placeholder=\"Description\"\n type=\"text\"\n id=\"description\"\n formControlName=\"description\"\n name=\"description\"\n class=\"medium-textarea\"\n required></textarea>\n </mat-form-field>\n\n <h3>Address</h3>\n <lib-address-edit \n [countryOptions]=\"countryOptions()\"\n [form]=\"form.address.form\"\n ></lib-address-edit>\n\n <h3>Members</h3>\n <lib-select-table [selected]=\"selectedMembers()\"\n [itemType]=\"'Members'\"\n [columns]=\"columns\"\n [options]=\"candidateMembers()\"\n (itemAdded)=\"onMemberAdded($event)\"\n (itemRemoved)=\"onMemberRemoved($event)\"\n (searchChanged)=\"onSearchChanged($event)\">\n </lib-select-table>\n\n <div class=\"button_group\">\n <button mat-fab\n class=\"form_action_button\"\n type=\"submit\"\n [disabled]=\"!form.form.valid\">\n <mat-icon>save</mat-icon>\n </button>\n \n <button mat-fab\n class=\"form_action_button\"\n (click)=\"cancel()\">\n <mat-icon>cancel</mat-icon>\n </button>\n </div>\n \n </form>\n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}.medium-textarea{min-height:150px}.checkbox_group{justify-content:left;display:flex;flex-direction:column}.button_group{margin:10px;display:flex;flex-direction:row;justify-content:center}.btn-block{padding:5px}.form_action_button{padding:5px;margin:5px}\n"] }]
1573
+ SelectTableComponent,
1574
+ TitleCasePipe
1575
+ ], template: "<lib-back-button></lib-back-button>\n\n<div class=\"content-container\">\n\n @if(item(); as item){\n <div class=\"item-edit-container\">\n \n <h1 class=\"item-edit-header\">{{heading() | titlecase}}</h1>\n\n <form class=\"form-card\" [formGroup]=\"form.form\" (ngSubmit)=\"submit()\">\n\n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"name\">Name</mat-label>\n <input matInput placeholder=\"Name\"\n type=\"text\"\n id=\"name\"\n formControlName=\"name\"\n name=\"name\"\n required>\n </mat-form-field>\n \n <mat-form-field class=\"form-field\">\n <mat-label for=\"acronym\">Acronym</mat-label> \n <input matInput placeholder=\"Acronym\"\n type=\"text\"\n id=\"acronym\"\n formControlName=\"acronym\"\n name=\"acronym\">\n </mat-form-field>\n \n <mat-form-field class=\"form-field-wide\">\n <mat-label for=\"description\">Description</mat-label>\n <textarea matInput placeholder=\"Description\"\n type=\"text\"\n id=\"description\"\n formControlName=\"description\"\n name=\"description\"\n class=\"medium-textarea\"\n required></textarea>\n </mat-form-field>\n\n <h3>Address</h3>\n <lib-address-edit \n [countryOptions]=\"countryOptions()\"\n [form]=\"addressForm()\"\n ></lib-address-edit>\n\n <h3>Members</h3>\n <lib-select-table [selected]=\"selectionManager.selected()\"\n [itemType]=\"'Members'\"\n [columns]=\"selectionManager.columns\"\n [options]=\"selectionManager.candidates()\"\n (itemAdded)=\"selectionManager.onAdded($event)\"\n (itemRemoved)=\"selectionManager.onRemoved($event)\"\n (searchChanged)=\"selectionManager.onSearchChanged($event)\">\n </lib-select-table>\n\n </form>\n <div class=\"button_group\">\n <button mat-fab\n class=\"form_action_button\"\n type=\"submit\"\n [disabled]=\"!form.form.valid\">\n <mat-icon>save</mat-icon>\n </button>\n \n <button mat-fab\n class=\"form_action_button\"\n (click)=\"cancel()\">\n <mat-icon>cancel</mat-icon>\n </button>\n </div>\n\n </div>\n }\n</div>\n", styles: [":host{flex-grow:1}.medium-textarea{min-height:150px}.checkbox_group{justify-content:left;display:flex;flex-direction:column}.button_group{margin:10px;display:flex;flex-direction:row;justify-content:center}.btn-block{padding:5px}.form_action_button{padding:5px;margin:5px}\n"] }]
1566
1576
  }], ctorParameters: () => [] });
1567
1577
 
1568
1578
  /*
@@ -1573,5 +1583,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.1", ngImpor
1573
1583
  * Generated bundle index. Do not edit.
1574
1584
  */
1575
1585
 
1576
- export { Address, AddressDetailComponent, AddressEditComponent, AddressForm, AddressService, ApiError, BackButtonComponent, DetailHeaderComponent, DetailView, ENDPOINT_URL, EditView, ErrorCode, FileRecord, FileUploadComponent, Group, GroupComponent, GroupDetailComponent, GroupService, ItemQuery, ItemService, LOGIN_USER, LandingComponent, LeftNavComponent, LeftNavService, ListTableViewComponent, ListView, ListViewComponent, Organization, OrganizationComponent, OrganizationDetailComponent, OrganizationEditComponent, OrganizationService, Paginated, Permission, PortalMember, ResolvedPermission, RestService, SearchBarComponent, SelectTableComponent, TopBarComponent, UserComponent, UserDetailComponent, UserEditComponent, UserService };
1586
+ export { Address, AddressDetailComponent, AddressEditComponent, AddressForm, AddressService, ApiError, AvatarComponent, BackButtonComponent, DetailHeaderComponent, DetailView, ENDPOINT_URL, EditView, ErrorCode, FeedbackComponent, FileRecord, FileUploadComponent, Group, GroupComponent, GroupDetailComponent, GroupService, ItemQuery, ItemService, ItemWithUserService, LOGIN_USER, LandingComponent, LeftNavComponent, LeftNavService, ListTableViewComponent, ListViewComponent, MockItemService, Organization, OrganizationComponent, OrganizationDetailComponent, OrganizationEditComponent, OrganizationService, Paginated, Permission, PortalMember, ResolvedPermission, RestService, SearchBarComponent, SelectTableComponent, SelectionManager, TopBarComponent, UserComponent, UserDetailComponent, UserEditComponent, UserService };
1577
1587
  //# sourceMappingURL=ichec-angular-core.mjs.map