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 {