This commit is contained in:
Daniel Ponte 2024-12-19 23:53:00 -05:00
parent b39069c893
commit 4bf6efa8cc
6 changed files with 163 additions and 112 deletions

View file

@ -1,9 +1,9 @@
export interface CallRecord { export interface CallRecord {
id: string; id: string;
call_date: Date; call_date: Date;
duration: number; duration: number;
system_id: number; system_id: number;
tgid: number; tgid: number;
system_name: string; system_name: string;
tg_name: string; tg_name: string;
}; }

View file

@ -1,50 +1,60 @@
<div class="toolbar"> <div class="toolbar">
<table mat-table [dataSource]="dataSource"> <mat-form-field subscriptSizing="dynamic" class="filterBox">
<ng-container matColumnDef="play"> <mat-label>Start</mat-label>
<th mat-header-cell *matHeaderCellDef><mat-icon>play_arrow</mat-icon></th> <input
<td mat-cell *matCellDef="let call"> matInput
<!-- player here --> type="datetime-local"
</td> name="start"
</ng-container> placeholder="Start date"
<ng-container matColumnDef="date"> [formControl]="start"
<th mat-header-cell *matHeaderCellDef>Date</th> />
<td mat-cell *matCellDef="let call"> </mat-form-field>
{{ call.call_date | date }} </div>
</td> <table mat-table [dataSource]="dataSource">
</ng-container> <ng-container matColumnDef="play">
<ng-container matColumnDef="time"> <th mat-header-cell *matHeaderCellDef><mat-icon>play_arrow</mat-icon></th>
<th mat-header-cell *matHeaderCellDef>Time</th> <td mat-cell *matCellDef="let call">
<td mat-cell *matCellDef="let call"> <a (click)="playAudio($event, call)"><mat-icon>play_arrow</mat-icon></a>
{{ call.call_date | time }} </td>
</td> </ng-container>
</ng-container> <ng-container matColumnDef="date">
<ng-container matColumnDef="talkgroup"> <th mat-header-cell *matHeaderCellDef>Date</th>
<th mat-header-cell *matHeaderCellDef>Talkgroup</th> <td mat-cell *matCellDef="let call">
<td mat-cell *matCellDef="let call"> {{ call.call_date | date }}
{{ call | alphaTag | async }} </td>
</td> </ng-container>
</ng-container> <ng-container matColumnDef="time">
<ng-container matColumnDef="duration"> <th mat-header-cell *matHeaderCellDef>Time</th>
<th mat-header-cell *matHeaderCellDef>Duration</th> <td mat-cell *matCellDef="let call">
<td mat-cell *matCellDef="let call"> {{ call.call_date | time }}
{{ call | duration }} </td>
</td> </ng-container>
</ng-container> <ng-container matColumnDef="talkgroup">
<tr mat-header-row *matHeaderRowDef="columns; sticky: true"></tr> <th mat-header-cell *matHeaderCellDef>Talkgroup</th>
<tr mat-row *matRowDef="let myRowData; columns: columns"></tr> <td mat-cell *matCellDef="let call">
</table> {{ call | alphaTag | async }}
<div class="pagFoot"> </td>
<mat-paginator </ng-container>
#paginator <ng-container matColumnDef="duration">
class="paginator" <th mat-header-cell *matHeaderCellDef class="durationHdr">Duration</th>
(page)="setPage($event)" <td mat-cell *matCellDef="let call" class="duration">
[length]="count" {{ call | duration }}
showFirstLastButtons="true" </td>
[pageSize]="curPage.pageSize" </ng-container>
[pageSizeOptions]="pageSizeOptions" <tr mat-header-row *matHeaderRowDef="columns; sticky: true"></tr>
[pageIndex]="curPage.pageIndex" <tr mat-row *matRowDef="let myRowData; columns: columns"></tr>
aria-label="Select page" </table>
> <div class="pagFoot">
</mat-paginator> <mat-paginator
</div> #paginator
</div> class="paginator"
(page)="setPage($event)"
[length]="count"
showFirstLastButtons="true"
[pageSize]="curPage.pageSize"
[pageSizeOptions]="pageSizeOptions"
[pageIndex]="curPage.pageIndex"
aria-label="Select page"
>
</mat-paginator>
</div>

View file

@ -0,0 +1,13 @@
.filterBox {
width: 240px;
}
.duration {
text-align: right;
padding-right: 4em;
}
.durationHdr,
.duration {
max-width: 3em;
}

View file

@ -25,50 +25,52 @@ import { MatChipsModule } from '@angular/material/chips';
import { SelectionModel } from '@angular/cdk/collections'; import { SelectionModel } from '@angular/cdk/collections';
import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatCheckboxModule } from '@angular/material/checkbox';
import { Observable, Subscription } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { import { map } from 'rxjs/operators';
map, import { CallsListParams, CallsService } from './calls.service';
} from 'rxjs/operators'; import { CallRecord } from '../calls';
import {
CallsListParams,
CallsService,
} from './calls.service';
import {
CallRecord,
} from '../calls';
import { import { TalkgroupService } from '../talkgroups/talkgroups.service';
TalkgroupService,
} from '../talkgroups/talkgroups.service';
import { Talkgroup } from '../talkgroup'; import { Talkgroup } from '../talkgroup';
import { MatFormField, MatFormFieldModule } from '@angular/material/form-field';
import {
FormGroup,
FormControl,
FormsModule,
ReactiveFormsModule,
} from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
@Pipe({ @Pipe({
name: 'date', name: 'date',
standalone: true, standalone: true,
pure: true pure: true,
}) })
export class DatePipe implements PipeTransform { export class DatePipe implements PipeTransform {
transform(ts: string, args?: any): string { transform(ts: string, args?: any): string {
const timestamp = new Date(ts); const timestamp = new Date(ts);
return (timestamp.getMonth() + 1) +"/"+ (timestamp.getDay()); return timestamp.getMonth() + 1 + '/' + timestamp.getDay();
} }
} }
@Pipe({ @Pipe({
name: 'time', name: 'time',
standalone: true, standalone: true,
pure: true pure: true,
}) })
export class TimePipe implements PipeTransform { export class TimePipe implements PipeTransform {
transform(ts: string, args?: any): string { transform(ts: string, args?: any): string {
const timestamp = new Date(ts); const timestamp = new Date(ts);
return timestamp.toLocaleTimeString(navigator.language, {hour: '2-digit', minute:'2-digit'}); return timestamp.toLocaleTimeString(navigator.language, {
hour: '2-digit',
minute: '2-digit',
});
} }
} }
@Pipe({ @Pipe({
name: 'alphaTag', name: 'alphaTag',
standalone: true, standalone: true,
pure: true pure: true,
}) })
export class AlphaTagPipe implements PipeTransform { export class AlphaTagPipe implements PipeTransform {
constructor(private tgService: TalkgroupService) {} constructor(private tgService: TalkgroupService) {}
@ -77,7 +79,7 @@ export class AlphaTagPipe implements PipeTransform {
return this.tgService.getTalkgroup(call.system_id, call.tgid).pipe( return this.tgService.getTalkgroup(call.system_id, call.tgid).pipe(
map((tg: Talkgroup) => { map((tg: Talkgroup) => {
return tg.alpha_tag; return tg.alpha_tag;
}) }),
); );
} }
} }
@ -85,7 +87,7 @@ export class AlphaTagPipe implements PipeTransform {
@Pipe({ @Pipe({
name: 'duration', name: 'duration',
standalone: true, standalone: true,
pure: true pure: true,
}) })
export class DurationPipe implements PipeTransform { export class DurationPipe implements PipeTransform {
constructor(callsSvc: CallsService) {} constructor(callsSvc: CallsService) {}
@ -108,6 +110,10 @@ export class DurationPipe implements PipeTransform {
MatPaginatorModule, MatPaginatorModule,
MatTableModule, MatTableModule,
AsyncPipe, AsyncPipe,
MatFormFieldModule,
ReactiveFormsModule,
FormsModule,
MatInputModule,
], ],
templateUrl: './calls.component.html', templateUrl: './calls.component.html',
styleUrl: './calls.component.scss', styleUrl: './calls.component.scss',
@ -119,23 +125,30 @@ export class CallsComponent {
page = 0; page = 0;
perPage = 25; perPage = 25;
pageSizeOptions = [25, 50, 75, 100, 200, 500]; pageSizeOptions = [25, 50, 75, 100, 200, 500];
columns = [ columns = ['play', 'date', 'time', 'talkgroup', 'duration'];
'play', curPage = <PageEvent>{ pageIndex: 0, pageSize: 0 };
'date', start = new FormControl(
'time', new Date(new Date().setDate(new Date().getDate() - 7)),
'talkgroup', );
'duration', end = new FormControl(null);
];
curPage = <PageEvent>({pageIndex: 0, pageSize: 0});
constructor(private callsSvc: CallsService) {} constructor(
private callsSvc: CallsService,
private prefsSvc: PrefsService,
) {}
playAudio(ev: Event, call: CallRecord) {
let au = new Audio();
au.src = this.callsSvc.callAudioURL(call.id);
au.load();
au.play();
}
setPage(p: PageEvent) { setPage(p: PageEvent) {
this.curPage = p; this.curPage = p;
const current = new Date(); const current = new Date();
const start: Date = new Date(new Date().setDate(current.getDate() - 7));
const par: CallsListParams = { const par: CallsListParams = {
start: start, start: this.start.value,
page: p.pageIndex, page: p.pageIndex,
perPage: p.pageSize, perPage: p.pageSize,
end: null, end: null,
@ -149,11 +162,20 @@ export class CallsComponent {
}); });
} }
ngOnInit() { zeroPage(): PageEvent {
this.setPage(<PageEvent>({ return <PageEvent>{
pageIndex: 0, pageIndex: 0,
pageSize: 25, pageSize: this.curPage.pageSize,
})); };
} }
ngOnInit() {
this.start.valueChanges.subscribe(() => {
this.setPage(this.zeroPage());
});
this.setPage(<PageEvent>{
pageIndex: 0,
pageSize: 25,
});
}
} }

View file

@ -1,15 +1,14 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient, HttpResponse } from '@angular/common/http'; import { HttpClient, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { import { CallRecord } from '../calls';
CallRecord, import { environment } from '.././../environments/environment';
} from '../calls';
export interface CallsListParams { export interface CallsListParams {
start: Date|null; start: Date | null;
end: Date|null; end: Date | null;
tagsAny: string[]|null; tagsAny: string[] | null;
tagsNot: string[]|null; tagsNot: string[] | null;
dir: string; dir: string;
page: number; page: number;
perPage: number; perPage: number;
@ -20,16 +19,17 @@ export interface CallsPaginated {
count: number; count: number;
} }
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root',
}) })
export class CallsService { export class CallsService {
constructor(private http: HttpClient) {}
constructor(private http: HttpClient) { }
getCalls(p: CallsListParams): Observable<CallsPaginated> { getCalls(p: CallsListParams): Observable<CallsPaginated> {
return this.http.post<CallsPaginated>('/api/call/', p); return this.http.post<CallsPaginated>('/api/call/', p);
} }
callAudioURL(id: string): string {
return environment.baseUrl + '/api/call/' + id;
}
} }

View file

@ -30,10 +30,14 @@ export class TalkgroupService {
} }
getTalkgroup(sys: number, tg: number): Observable<Talkgroup> { getTalkgroup(sys: number, tg: number): Observable<Talkgroup> {
let tgid = <TGID>({sys: sys, tg: tg}); let tgid = <TGID>{ sys: sys, tg: tg };
if (!this._getTalkgroup.get(tgid)) { if (!this._getTalkgroup.get(tgid)) {
this._getTalkgroup.set(tgid, this.http. this._getTalkgroup.set(
get<Talkgroup>(`/api/talkgroup/${sys}/${tg}`).pipe(shareReplay())); tgid,
this.http
.get<Talkgroup>(`/api/talkgroup/${sys}/${tg}`)
.pipe(shareReplay()),
);
} }
return this._getTalkgroup.get(tgid)!; return this._getTalkgroup.get(tgid)!;
@ -67,11 +71,13 @@ export class TalkgroupService {
} }
putTalkgroup(tu: TalkgroupUpdate): Observable<Talkgroup> { putTalkgroup(tu: TalkgroupUpdate): Observable<Talkgroup> {
let tgid = <TGID>({sys: tu.system_id, tg: tu.tgid}); let tgid = <TGID>{ sys: tu.system_id, tg: tu.tgid };
this._getTalkgroup.set(tgid, this.http.put<Talkgroup>( this._getTalkgroup.set(
`/api/talkgroup/${tu.system_id}/${tu.tgid}`, tgid,
tu, this.http
).pipe(shareReplay())); .put<Talkgroup>(`/api/talkgroup/${tu.system_id}/${tu.tgid}`, tu)
.pipe(shareReplay()),
);
return this._getTalkgroup.get(tgid)!; return this._getTalkgroup.get(tgid)!;
} }