diff --git a/client/stillbox/src/app/app.routes.ts b/client/stillbox/src/app/app.routes.ts index d1a67e2..6bd9e8d 100644 --- a/client/stillbox/src/app/app.routes.ts +++ b/client/stillbox/src/app/app.routes.ts @@ -65,6 +65,14 @@ export const routes: Routes = [ ), data: { title: 'Incidents' }, }, + { + path: 'incidents/:id', + loadComponent: () => + import('./incidents/incident/incident.component').then( + (m) => m.IncidentComponent, + ), + data: { title: 'View Incident' }, + }, { path: 'alerts', loadComponent: () => diff --git a/client/stillbox/src/app/incidents/incident/incident.component.html b/client/stillbox/src/app/incidents/incident/incident.component.html index 5afe3dc..78c2992 100644 --- a/client/stillbox/src/app/incidents/incident/incident.component.html +++ b/client/stillbox/src/app/incidents/incident/incident.component.html @@ -1 +1,15 @@ -

incident works!

+@let inc = inc$ | async; + +

{{ inc?.name }}

+
+
Start
+
+ {{ inc?.startTime | fmtDate }} +
+
End
+
{{ inc?.endTime | fmtDate }}
+
+
+ {{ inc?.description }} +
+
diff --git a/client/stillbox/src/app/incidents/incident/incident.component.scss b/client/stillbox/src/app/incidents/incident/incident.component.scss index e69de29..9c7d43f 100644 --- a/client/stillbox/src/app/incidents/incident/incident.component.scss +++ b/client/stillbox/src/app/incidents/incident/incident.component.scss @@ -0,0 +1,32 @@ +.incident { + margin: 50px 50px 50px 50px; + padding: 50px 50px 50px 50px; + display: flex; + flex-flow: column; + width: 50%; + margin-left: auto; + margin-right: auto; +} + +.inc-heading, +.inc-description { + display: flex; + flex-flow: row wrap; + flex: 1 1; +} + +.inc-heading, +.inc-description { + margin-bottom: 30px; +} + +.field { + flex: 1 1; +} + +.field-label { + font-weight: bolder; +} +.field-label::after { + content: ":"; +} diff --git a/client/stillbox/src/app/incidents/incident/incident.component.ts b/client/stillbox/src/app/incidents/incident/incident.component.ts index 27d0508..8893e4c 100644 --- a/client/stillbox/src/app/incidents/incident/incident.component.ts +++ b/client/stillbox/src/app/incidents/incident/incident.component.ts @@ -1,9 +1,73 @@ -import { Component } from '@angular/core'; +import { Component, computed, inject, ViewChild } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; +import { debounceTime } from 'rxjs/operators'; +import { + Talkgroup, + TalkgroupUpdate, + IconMap, + iconMapping, +} from '../../talkgroup'; +import { COMMA, ENTER } from '@angular/cdk/keycodes'; +import { TalkgroupService } from '../../talkgroups/talkgroups.service'; +import { + MatAutocomplete, + MatAutocompleteModule, + MatAutocompleteSelectedEvent, + MatAutocompleteActivatedEvent, +} from '@angular/material/autocomplete'; +import { CommonModule, DatePipe } from '@angular/common'; +import { BehaviorSubject, catchError, of, Subscription } from 'rxjs'; +import { shareReplay } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { + ReactiveFormsModule, + FormGroup, + FormControl, + FormsModule, +} from '@angular/forms'; +import { Router, ActivatedRoute } from '@angular/router'; +import { MatInputModule } from '@angular/material/input'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatIconModule } from '@angular/material/icon'; +import { IncidentsService } from '../incidents.service'; +import { IncidentRecord } from '../../incidents'; +import { MatCardModule } from '@angular/material/card'; +import { FmtDatePipe } from '../incidents.component'; @Component({ selector: 'app-incident', - imports: [], + imports: [ + CommonModule, + ReactiveFormsModule, + FormsModule, + MatInputModule, + MatFormFieldModule, + MatCheckboxModule, + MatIconModule, + MatCardModule, + FmtDatePipe, + ], templateUrl: './incident.component.html', styleUrl: './incident.component.scss', }) -export class IncidentComponent {} +export class IncidentComponent { + inc$!: Observable; + subscriptions: Subscription = new Subscription(); + + constructor( + private route: ActivatedRoute, + private incSvc: IncidentsService, + ) {} + + saveIncName(ev: Event) {} + + ngOnInit() { + const incID = this.route.snapshot.paramMap.get('id')!; + this.inc$ = this.incSvc.getIncident(incID); + } + + ngOnDestroy() { + this.subscriptions.unsubscribe(); + } +} diff --git a/client/stillbox/src/app/incidents/incidents.component.ts b/client/stillbox/src/app/incidents/incidents.component.ts index 06624aa..8795eeb 100644 --- a/client/stillbox/src/app/incidents/incidents.component.ts +++ b/client/stillbox/src/app/incidents/incidents.component.ts @@ -34,12 +34,17 @@ import { ToolbarContextService } from '../navigation/toolbar-context.service'; standalone: true, pure: true, }) -export class DatePipe implements PipeTransform { - transform(ts: string, args?: any): string { +export class FmtDatePipe implements PipeTransform { + transform(ts: string | Date | null | undefined, args?: any): string { if (!ts) { return '\u2014'; } - const timestamp = new Date(ts); + let timestamp: Date; + if (ts instanceof Date) { + timestamp = ts; + } else { + timestamp = new Date(ts); + } return ( timestamp.getMonth() + 1 + @@ -61,7 +66,7 @@ const reqPageSize = 200; selector: 'app-incidents', imports: [ MatIconModule, - DatePipe, + FmtDatePipe, MatPaginatorModule, MatTableModule, AsyncPipe, diff --git a/client/stillbox/src/app/incidents/incidents.service.ts b/client/stillbox/src/app/incidents/incidents.service.ts index e432914..6244052 100644 --- a/client/stillbox/src/app/incidents/incidents.service.ts +++ b/client/stillbox/src/app/incidents/incidents.service.ts @@ -47,4 +47,8 @@ export class IncidentsService { updateIncident(id: string, inp: IncidentRecord): Observable { return this.http.patch('/api/incident/' + id, inp); } + + getIncident(id: string): Observable { + return this.http.get('/api/incident/' + id); + } } diff --git a/client/stillbox/src/app/talkgroups/talkgroup-record/talkgroup-record.component.ts b/client/stillbox/src/app/talkgroups/talkgroup-record/talkgroup-record.component.ts index 27ea769..3469bd0 100644 --- a/client/stillbox/src/app/talkgroups/talkgroup-record/talkgroup-record.component.ts +++ b/client/stillbox/src/app/talkgroups/talkgroup-record/talkgroup-record.component.ts @@ -17,7 +17,7 @@ import { MatAutocompleteActivatedEvent, } from '@angular/material/autocomplete'; import { CommonModule } from '@angular/common'; -import { BehaviorSubject, catchError, of, Subscription } from 'rxjs'; +import { catchError, of, Subscription } from 'rxjs'; import { shareReplay } from 'rxjs/operators'; import { Observable } from 'rxjs'; import { diff --git a/client/stillbox/src/app/talkgroups/talkgroups.service.ts b/client/stillbox/src/app/talkgroups/talkgroups.service.ts index 0c8d7d2..d50bba4 100644 --- a/client/stillbox/src/app/talkgroups/talkgroups.service.ts +++ b/client/stillbox/src/app/talkgroups/talkgroups.service.ts @@ -26,6 +26,7 @@ export interface TalkgroupsPaginated { providedIn: 'root', }) export class TalkgroupService { + private readonly _getTalkgroup = new Map>(); private tgs$: Observable; private tags$!: Observable; private fetchAll = new BehaviorSubject<'fetch'>('fetch'); @@ -45,13 +46,19 @@ export class TalkgroupService { } getTalkgroups(): Observable { - return this.http.get('/api/talkgroup/'); + return this.http.get('/api/talkgroup/').pipe(shareReplay()); } getTalkgroup(sys: number, tg: number): Observable { - return this.tgs$.pipe( - switchMap((tgs) => tgs.filter(t => t.system_id === sys && t.tgid === tg)) - ); + const key = this.tgKey(sys, tg); + if (!this._getTalkgroup.get(key)) { + return this.tgs$.pipe( + switchMap((talkg) => + talkg.filter((tgv) => tgv.tgid == tg && tgv.system_id == sys), + ), + ); + } + return this._getTalkgroup.get(key)!; } putTalkgroup(tu: TalkgroupUpdate): Observable {