This commit is contained in:
Daniel Ponte 2025-02-14 00:18:49 -05:00
parent dd2ee06f03
commit 48903bb4a2
4 changed files with 298 additions and 6 deletions

View file

@ -6,6 +6,26 @@ import { CallRecord } from '../calls';
import { Share, ShareType } from '../shares';
import { ActivatedRoute, Router } from '@angular/router';
export interface ShareRecord {
id: string;
entityType: string;
entityDate: Date;
owner: string;
entityID: string;
expiration: Date | null;
}
export interface ShareListParams {
page: number | null;
perPage: number | null;
dir: string | null;
}
export interface Shares {
shares: ShareRecord[];
totalCount: number;
}
@Injectable({
providedIn: 'root',
})
@ -28,6 +48,14 @@ export class ShareService {
return this.http.get<Share>(`/share/${id}`);
}
deleteShare(id: string): Observable<void> {
return this.http.delete<void>(`/api/share/${id}`);
}
getShares(p: ShareListParams): Observable<Shares> {
return this.http.post<Shares>('/api/share/', p);
}
getSharedItem(s: Observable<Share>): Observable<ShareType> {
return s.pipe(
map((res) => {

View file

@ -1 +1,82 @@
<p>shares works!</p>
<div class="tabContainer" *ngIf="!isLoading; else spinner">
<table class="sharesTable" mat-table [dataSource]="sharesResult">
<ng-container matColumnDef="select">
<th mat-header-cell *matHeaderCellDef>
<mat-checkbox
(change)="$event ? masterToggle() : null"
[checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()"
>
</mat-checkbox>
</th>
<td mat-cell *matCellDef="let row">
<mat-checkbox
(click)="$event.stopPropagation()"
(change)="$event ? selection.toggle(row) : null"
[checked]="selection.isSelected(row)"
>
</mat-checkbox>
</td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let share">
@switch (share.entityType) {
@case ("incident") {
<mat-icon>newspaper</mat-icon>
}
@case ("call") {
<mat-icon>campaign</mat-icon>
}
}
</td>
</ng-container>
<ng-container matColumnDef="link">
<th mat-header-cell *matHeaderCellDef>Link</th>
<td mat-cell *matCellDef="let share">
<a [href]="'/s/' + share.id"><mat-icon>link</mat-icon></a>
</td>
</ng-container>
<ng-container matColumnDef="date">
<th mat-header-cell *matHeaderCellDef>Date</th>
<td mat-cell *matCellDef="let share">
{{ share.entityDate | fmtDate }}
</td>
</ng-container>
<ng-container matColumnDef="owner">
<th mat-header-cell *matHeaderCellDef>Owner</th>
<td mat-cell *matCellDef="let share">
{{ share.owner }}
</td>
</ng-container>
<ng-container matColumnDef="delete">
<th mat-header-cell *matHeaderCellDef>Delete</th>
<td mat-cell *matCellDef="let share">
<a (click)="deleteShare(share.id)">
<mat-icon>delete</mat-icon>
</a>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="columns; sticky: true"></tr>
<tr mat-row *matRowDef="let myRowData; columns: columns"></tr>
</table>
</div>
<div class="pagFoot">
<mat-paginator
#paginator
class="paginator"
(page)="setPage($event)"
[length]="count"
showFirstLastButtons="true"
[pageSize]="curPage.pageSize"
[pageSizeOptions]="pageSizeOptions"
[pageIndex]="curPage.pageIndex"
aria-label="Select page"
>
</mat-paginator>
</div>
<ng-template #spinner>
<div class="spinner">
<mat-spinner></mat-spinner>
</div>
</ng-template>

View file

@ -1,9 +1,192 @@
import { Component } from '@angular/core';
import { Component, Pipe, PipeTransform, ViewChild } from '@angular/core';
import { CommonModule, AsyncPipe } from '@angular/common';
import { RouterLink } from '@angular/router';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatTableModule } from '@angular/material/table';
import {
MatPaginator,
MatPaginatorModule,
PageEvent,
} from '@angular/material/paginator';
import { PrefsService } from '../prefs/prefs.service';
import { MatIconModule } from '@angular/material/icon';
import { SelectionModel } from '@angular/cdk/collections';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { BehaviorSubject, Subscription } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { TalkgroupService } from '../talkgroups/talkgroups.service';
import { MatFormFieldModule } from '@angular/material/form-field';
import {
FormControl,
FormGroup,
FormsModule,
ReactiveFormsModule,
} from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
import { debounceTime } from 'rxjs/operators';
import { ToolbarContextService } from '../navigation/toolbar-context.service';
import {
ShareListParams,
ShareRecord,
ShareService,
} from '../share/share.service';
import { FmtDatePipe } from '../incidents/incidents.component';
const reqPageSize = 200;
@Component({
selector: 'app-shares',
imports: [],
imports: [
MatIconModule,
MatPaginatorModule,
MatTableModule,
MatFormFieldModule,
ReactiveFormsModule,
FormsModule,
FmtDatePipe,
MatInputModule,
MatCheckboxModule,
CommonModule,
MatProgressSpinnerModule,
],
templateUrl: './shares.component.html',
styleUrl: './shares.component.scss',
})
export class SharesComponent {}
export class SharesComponent {
sharesResult = new BehaviorSubject(new Array<ShareRecord>(0));
selection = new SelectionModel<ShareRecord>(true, []);
@ViewChild('paginator') paginator!: MatPaginator;
count = 0;
curLen = 0;
page = 0;
perPage = 25;
pageSizeOptions = [25, 50, 75, 100, 200];
columns = ['select', 'type', 'link', 'date', 'owner', 'delete'];
curPage = <PageEvent>{ pageIndex: 0, pageSize: 0 };
currentSet!: ShareRecord[];
currentServerPage = 0; // page is never 0, forces load
isLoading = true;
subscriptions = new Subscription();
pageWindow = 0;
fetchIncidents = new BehaviorSubject<ShareListParams>(
this.buildParams(this.curPage, this.curPage.pageIndex),
);
constructor(private sharesSvc: ShareService) {}
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.curLen;
return numSelected === numRows;
}
buildParams(p: PageEvent, serverPage: number): ShareListParams {
const par: ShareListParams = {
page: serverPage,
perPage: reqPageSize,
dir: 'asc',
};
return par;
}
masterToggle() {
this.isAllSelected()
? this.selection.clear()
: this.sharesResult.value.forEach((row) => this.selection.select(row));
}
setPage(p: PageEvent, force?: boolean) {
this.selection.clear();
this.curPage = p;
if (p && p!.pageSize != this.perPage) {
this.perPage = p!.pageSize;
}
this.getShares(p, force);
}
refresh() {
this.selection.clear();
this.getShares(this.curPage, true);
}
getShares(p: PageEvent, force?: boolean) {
const pageStart = p.pageIndex * p.pageSize;
const serverPage = Math.floor(pageStart / reqPageSize) + 1;
this.pageWindow = pageStart % reqPageSize;
if (serverPage == this.currentServerPage && !force && this.currentSet) {
this.sharesResult.next(
this.sharesResult
? this.currentSet.slice(this.pageWindow, this.pageWindow + p.pageSize)
: [],
);
} else {
this.currentServerPage = serverPage;
this.fetchIncidents.next(this.buildParams(p, serverPage));
}
}
zeroPage(): PageEvent {
return <PageEvent>{
pageIndex: 0,
pageSize: this.curPage.pageSize,
};
}
ngOnDestroy() {
this.subscriptions.unsubscribe();
}
ngOnInit() {
let cpp = 25;
this.perPage = cpp;
this.setPage(<PageEvent>{
pageIndex: 0,
pageSize: cpp,
});
this.subscriptions.add(
this.fetchIncidents
.pipe(
switchMap((params) => {
return this.sharesSvc.getShares(params);
}),
)
.subscribe((shares) => {
this.isLoading = false;
this.count = shares.totalCount;
this.currentSet = shares.shares;
this.sharesResult.next(
this.currentSet
? this.currentSet.slice(
this.pageWindow,
this.pageWindow + this.perPage,
)
: [],
);
}),
);
this.subscriptions.add(
this.sharesResult.subscribe((cr) => {
this.curLen = cr.length;
}),
);
}
deleteShare(shareID: string) {
if (confirm('Are you sure you want to delete this share?')) {
this.sharesSvc.deleteShare(shareID).subscribe({
next: () => {
this.fetchIncidents.next(
this.buildParams(this.curPage, this.curPage.pageIndex),
);
},
error: (err) => {
alert(err);
},
});
}
}
}

View file

@ -1,9 +1,9 @@
{
"/api": {
"/api/": {
"target": "http://xenon:3050",
"secure": false
},
"/share": {
"/share/": {
"target": "http://xenon:3050",
"secure": false
}