diff --git a/client/admin/package-lock.json b/client/admin/package-lock.json index 9943063..c4ca8ae 100644 --- a/client/admin/package-lock.json +++ b/client/admin/package-lock.json @@ -18,6 +18,7 @@ "@angular/router": "^18.2.0", "@ng-icons/core": "^29.6.1", "@ng-icons/ionicons": "^29.6.1", + "@ng-icons/material-icons": "^29.10.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.10" @@ -3624,6 +3625,15 @@ "tslib": "^2.3.0" } }, + "node_modules/@ng-icons/material-icons": { + "version": "29.10.0", + "resolved": "https://registry.npmjs.org/@ng-icons/material-icons/-/material-icons-29.10.0.tgz", + "integrity": "sha512-zmOJQeDpCoISxmmlzYbBMwHrmMz4RnT0UNOxWbfSSlgU8+XTHUUbVomS72ltFkkgY/rt5VF8rnp2ILmmDeZZWA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + } + }, "node_modules/@ngtools/webpack": { "version": "18.2.12", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.12.tgz", diff --git a/client/admin/package.json b/client/admin/package.json index 2519db1..9e7fb36 100644 --- a/client/admin/package.json +++ b/client/admin/package.json @@ -20,6 +20,7 @@ "@angular/router": "^18.2.0", "@ng-icons/core": "^29.6.1", "@ng-icons/ionicons": "^29.6.1", + "@ng-icons/material-icons": "^29.10.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.14.10" diff --git a/client/admin/src/app/app.config.ts b/client/admin/src/app/app.config.ts index dd9ab72..280f53e 100644 --- a/client/admin/src/app/app.config.ts +++ b/client/admin/src/app/app.config.ts @@ -1,5 +1,5 @@ import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; -import { provideRouter, withDebugTracing } from '@angular/router'; +import { provideRouter, } from '@angular/router'; import { environment } from './../environments/environment'; import { HttpRequest, @@ -43,7 +43,7 @@ export function authIntercept( export const appConfig: ApplicationConfig = { providers: [ provideZoneChangeDetection({ eventCoalescing: true }), - provideRouter(routes, withDebugTracing()), + provideRouter(routes), provideHttpClient(withInterceptors([apiBaseInterceptor, authIntercept])), ], }; diff --git a/client/admin/src/app/talkgroup.ts b/client/admin/src/app/talkgroup.ts index c402bdf..5ed74fe 100644 --- a/client/admin/src/app/talkgroup.ts +++ b/client/admin/src/app/talkgroup.ts @@ -31,23 +31,61 @@ export interface System { export interface Metadata { encrypted: boolean | null; + icon: string | null; } -export interface Talkgroup { - id: number; - system_id: number; - tgid: number; - name: string; - alpha_tag: string; - tg_group: string; - frequency: number; - metadata: Metadata | null; - tags: string[]; - alert: boolean; + export interface IconMap { + [name: string]: string; + } + +export const iconMapping: IconMap = { + 'police': 'matLocalPoliceOutline', + 'fire': 'matFireTruckOutline', + 'ems': 'matEmergencyOutline', + 'bus': 'matDirectionsBusOutline', + '': 'matGroupWorkOutline', +}; + +export class Talkgroup { + id!: number; + system_id!: number; + tgid!: number; + name!: string; + alpha_tag!: string; + tg_group!: string; + frequency!: number; + metadata!: Metadata | null; + tags!: string[]; + alert!: boolean; system?: System; - alert_config: AlertRule[]; - weight: number; + alert_config!: AlertRule[]; + weight!: number; learned?: boolean; + icon?: string; + iconSvg?: string; + constructor( + id: number, + system_id: number, + tgid: number, + name: string, + alpha_tag: string, + tg_group: string, + frequency: number, + metadata: Metadata | null, + tags: string[], + alert: boolean, + alert_config: AlertRule[], + weight: number, + system?: System, + learned?: boolean, + icon?: string, + ) { + this.iconSvg = this.iconMap(this.metadata?.icon!); + } + + iconMap(icon: string): string { + return iconMapping[icon]!; + } } export interface TalkgroupUI extends Talkgroup { diff --git a/client/admin/src/app/talkgroups/talkgroup-record/talkgroup-record.component.html b/client/admin/src/app/talkgroups/talkgroup-record/talkgroup-record.component.html index 6066d07..8dff380 100644 --- a/client/admin/src/app/talkgroups/talkgroup-record/talkgroup-record.component.html +++ b/client/admin/src/app/talkgroups/talkgroup-record/talkgroup-record.component.html @@ -1,5 +1,5 @@
-
+
@@ -17,7 +16,6 @@ type="text" class="w-full input input-bordered" formControlName="alpha_tag" - [(ngModel)]="tg.alpha_tag" />
@@ -27,7 +25,6 @@ type="text" class="w-full input input-bordered" formControlName="tg_group" - [(ngModel)]="tg.tg_group" />
@@ -37,7 +34,6 @@ type="text" class="w-full input input-bordered" formControlName="frequency" - [(ngModel)]="tg.frequency" />
@@ -47,7 +43,6 @@ id="alert" type="checkbox" formControlName="alert" - [(ngModel)]="tg.alert" class="checkbox" />
@@ -59,10 +54,23 @@ type="text" class="w-full input input-bordered" formControlName="weight" - [(ngModel)]="tg.weight" />
+
Rules:
+
+ + +
diff --git a/client/admin/src/app/talkgroups/talkgroup-record/talkgroup-record.component.ts b/client/admin/src/app/talkgroups/talkgroup-record/talkgroup-record.component.ts index 3614150..b8384a9 100644 --- a/client/admin/src/app/talkgroups/talkgroup-record/talkgroup-record.component.ts +++ b/client/admin/src/app/talkgroups/talkgroup-record/talkgroup-record.component.ts @@ -1,5 +1,5 @@ import { Component, inject } from '@angular/core'; -import { Talkgroup, TalkgroupUpdate } from '../../talkgroup'; +import { Talkgroup, TalkgroupUpdate, IconMap, iconMapping } from '../../talkgroup'; import { TalkgroupService } from '../talkgroups.service'; import { AlertRuleBuilderComponent } from './alert-rule-builder/alert-rule-builder.component'; import { CommonModule } from '@angular/common'; @@ -22,6 +22,7 @@ import { Observable } from 'rxjs'; }) export class TalkgroupRecordComponent { tg!: Talkgroup; + iconMapping: IconMap = iconMapping; tgService: TalkgroupService = inject(TalkgroupService); form!: FormGroup; @@ -39,16 +40,19 @@ export class TalkgroupRecordComponent { .getTalkgroup(Number(sysId), Number(tgId)) .subscribe((data: Talkgroup) => { this.tg = data; + this.form = new FormGroup({ + name: new FormControl(this.tg.name), + alpha_tag: new FormControl(this.tg.alpha_tag), + tg_group: new FormControl(this.tg.tg_group), + frequency: new FormControl(this.tg.frequency), + alert: new FormControl(this.tg.alert), + weight: new FormControl(this.tg.weight), + icon: new FormControl(this.tg.icon), + }); + console.log(this.tg.icon); }); - this.form = new FormGroup({ - name: new FormControl(''), - alpha_tag: new FormControl(''), - tg_group: new FormControl(''), - frequency: new FormControl(0), - alert: new FormControl(true), - weight: new FormControl(1.0), - }); + } submit() { @@ -58,22 +62,32 @@ export class TalkgroupRecordComponent { id: this.tg.id, }; if (this.form.controls['name'].dirty) { - tgu.name = this.tg.name; + tgu.name = this.form.controls['name'].value; } if (this.form.controls['alpha_tag'].dirty) { - tgu.alpha_tag = this.tg.alpha_tag; + tgu.alpha_tag = this.form.controls['alpha_tag'].value; } if (this.form.controls['tg_group'].dirty) { - tgu.tg_group = this.tg.tg_group; + tgu.tg_group = this.form.controls['tg_group'].value; } if (this.form.controls['frequency'].dirty) { - tgu.frequency = this.tg.frequency; + tgu.frequency = this.form.controls['frequency'].value; } if (this.form.controls['alert'].dirty) { - tgu.alert = this.tg.alert; + tgu.alert = this.form.controls['alert'].value; } if (this.form.controls['weight'].dirty) { - tgu.weight = Number(this.tg.weight); + tgu.weight = Number(this.form.controls['weight'].value); + } + if (this.form.controls['icon'].dirty) { + if (tgu.metadata == null) { + tgu.metadata = {}; + } + if (this.tg.icon == null || this.tg.icon == '') { + tgu.metadata = Object.assign(tgu.metadata, {icon: undefined}); + } else { + tgu.metadata = Object.assign(tgu.metadata!, { icon: this.form.controls['icon'] }); + } } this.tgService .putTalkgroup(tgu) diff --git a/client/admin/src/app/talkgroups/talkgroups.component.css b/client/admin/src/app/talkgroups/talkgroups.component.css index e69de29..c4d0414 100644 --- a/client/admin/src/app/talkgroups/talkgroups.component.css +++ b/client/admin/src/app/talkgroups/talkgroups.component.css @@ -0,0 +1,2 @@ +.tgIcon { +} \ No newline at end of file diff --git a/client/admin/src/app/talkgroups/talkgroups.component.html b/client/admin/src/app/talkgroups/talkgroups.component.html index fa3f08c..c41bd40 100644 --- a/client/admin/src/app/talkgroups/talkgroups.component.html +++ b/client/admin/src/app/talkgroups/talkgroups.component.html @@ -3,20 +3,27 @@ + + + - @for (tg of talkgroups$ | async; track tg.id) { + @for (tg of talkgroups$ | async ; track tg.id) { + + + +
Sys Sys IDGroup NameAlpha TG ID Learned
{{ tg.system?.name }} {{ tg.system?.id }}{{ tg.tg_group }} {{ tg.name }}{{ tg.alpha_tag }} {{ tg.tgid }} {{ tg?.learned ? "Y" : "" }} diff --git a/client/admin/src/app/talkgroups/talkgroups.component.ts b/client/admin/src/app/talkgroups/talkgroups.component.ts index 41fcdaa..eeb5705 100644 --- a/client/admin/src/app/talkgroups/talkgroups.component.ts +++ b/client/admin/src/app/talkgroups/talkgroups.component.ts @@ -1,13 +1,52 @@ -import { Component, inject } from '@angular/core'; +import { Component, inject, Pipe, PipeTransform } from '@angular/core'; import { TalkgroupService } from './talkgroups.service'; -import { Talkgroup } from '../talkgroup'; import { NgIconComponent, provideIcons } from '@ng-icons/core'; import { ionCreateOutline } from '@ng-icons/ionicons'; +import { + matFireTruckOutline, + matLocalPoliceOutline, + matEmergencyOutline, + matDirectionsBusOutline, + matGroupWorkOutline, +} from '@ng-icons/material-icons/outline'; +import { Talkgroup, iconMapping } from '../talkgroup'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { switchMap } from 'rxjs/operators'; import { RouterModule, RouterOutlet, RouterLink } from '@angular/router'; import { CommonModule } from '@angular/common'; +import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; + + +@Pipe({ + standalone: true, + name: 'iconify', +}) +export class IconifyPipe implements PipeTransform { + transform(value: Talkgroup): Talkgroup { + if (value?.metadata?.icon != null) { + value.iconSvg = iconMapping[value?.metadata?.icon]; + } else if (value?.metadata?.icon == undefined) { + value.iconSvg = iconMapping['']; + } + return value; + } +} + +@Pipe({ + standalone: true, + name: 'sanitizeHtml' +}) +export class SanitizeHtmlPipe implements PipeTransform { + + constructor(private _sanitizer:DomSanitizer) { + } + + transform(v:string):SafeHtml { + return this._sanitizer.bypassSecurityTrustHtml(v); + } +} + @Component({ selector: 'talkgroups', @@ -18,10 +57,18 @@ import { CommonModule } from '@angular/common'; RouterModule, RouterLink, CommonModule, + IconifyPipe, + SanitizeHtmlPipe, ], templateUrl: './talkgroups.component.html', styleUrl: './talkgroups.component.css', - providers: [provideIcons({ ionCreateOutline })], + providers: [provideIcons({ ionCreateOutline, + matFireTruckOutline, + matLocalPoliceOutline, + matEmergencyOutline, + matDirectionsBusOutline, + matGroupWorkOutline, +})], }) export class TalkgroupsComponent { selectedSys: number = 0;