Paginate, new table

This commit is contained in:
Daniel Ponte 2024-11-26 09:19:31 -05:00
parent a4fc8f7cca
commit 20104c59af
8 changed files with 253 additions and 131 deletions

View file

@ -163,37 +163,6 @@
</svg> </svg>
</button> </button>
<!-- End Navigation Toggle --> <!-- End Navigation Toggle -->
<!-- Breadcrumb -->
<ol class="ms-3 flex items-center whitespace-nowrap">
<li
class="flex items-center text-sm text-gray-800 dark:text-neutral-400"
>
Application Layout
<svg
class="shrink-0 mx-3 overflow-visible size-2.5 text-gray-400 dark:text-neutral-500"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5 1L10.6869 7.16086C10.8637 7.35239 10.8637 7.64761 10.6869 7.83914L5 14"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
/>
</svg>
</li>
<li
class="text-sm font-semibold text-gray-800 truncate dark:text-neutral-400"
aria-current="page"
>
Dashboard
</li>
</ol>
<!-- End Breadcrumb -->
</div> </div>
</div> </div>
<!-- End Breadcrumb --> <!-- End Breadcrumb -->
@ -289,14 +258,16 @@
<!-- Content --> <!-- Content -->
@if (auth.loggedIn) { @if (auth.loggedIn) {
<div class="w-full lg:ps-64"> <div class="container w-full lg:ps-64">
<div class="p-4 sm:p-6 space-y-4 sm:space-y-6"> <div
class="object-contain object-scale-down p-4 sm:p-6 space-y-4 sm:space-y-6"
>
<router-outlet /> <router-outlet />
</div> </div>
</div> </div>
} @else { } @else {
<div class="w-full"> <div class="w-full">
<div class="p-4 sm:p-6 space-y-4 sm:space-y-6"> <div class="object-contain p-4 sm:p-6 space-y-4 sm:space-y-6">
<router-outlet /> <router-outlet />
</div> </div>
</div> </div>

View file

@ -1,5 +1,5 @@
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter, } from '@angular/router'; import { provideRouter } from '@angular/router';
import { environment } from './../environments/environment'; import { environment } from './../environments/environment';
import { import {
HttpRequest, HttpRequest,

View file

@ -34,16 +34,16 @@ export interface Metadata {
icon: string | null; icon: string | null;
} }
export interface IconMap { export interface IconMap {
[name: string]: string; [name: string]: string;
} }
export const iconMapping: IconMap = { export const iconMapping: IconMap = {
'police': 'matLocalPoliceOutline', police: 'matLocalPoliceOutline',
'fire': 'matFireTruckOutline', fire: 'matFireTruckOutline',
'ems': 'matEmergencyOutline', ems: 'matEmergencyOutline',
'bus': 'matDirectionsBusOutline', bus: 'matDirectionsBusOutline',
'': 'matGroupWorkOutline', '': 'matGroupWorkOutline',
}; };
export class Talkgroup { export class Talkgroup {

View file

@ -60,14 +60,14 @@
<alert-rule-builder [rules]="tg.alert_config" /> <alert-rule-builder [rules]="tg.alert_config" />
<div> <div>
<label for="icon">Icon: </label> <label for="icon">Icon: </label>
<select <select class="select" name="icon" id="icon" formControlName="icon">
class="select" @for (opt of iconMapping | keyvalue; track opt) {
name="icon" <option
id="icon" value="{{ opt.key }}"
formControlName="icon" [selected]="opt.key == tg.metadata?.icon"
> >
@for(opt of iconMapping | keyvalue; track opt) { {{ opt.key | titlecase }}
<option value="{{ opt.key }}" [selected]="opt.key == tg.metadata?.icon">{{ opt.key | titlecase }}</option> </option>
} }
</select> </select>
</div> </div>

View file

@ -1,5 +1,10 @@
import { Component, inject } from '@angular/core'; import { Component, inject } from '@angular/core';
import { Talkgroup, TalkgroupUpdate, IconMap, iconMapping } from '../../talkgroup'; import {
Talkgroup,
TalkgroupUpdate,
IconMap,
iconMapping,
} from '../../talkgroup';
import { TalkgroupService } from '../talkgroups.service'; import { TalkgroupService } from '../talkgroups.service';
import { AlertRuleBuilderComponent } from './alert-rule-builder/alert-rule-builder.component'; import { AlertRuleBuilderComponent } from './alert-rule-builder/alert-rule-builder.component';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
@ -40,19 +45,17 @@ export class TalkgroupRecordComponent {
.getTalkgroup(Number(sysId), Number(tgId)) .getTalkgroup(Number(sysId), Number(tgId))
.subscribe((data: Talkgroup) => { .subscribe((data: Talkgroup) => {
this.tg = data; this.tg = data;
this.form = new FormGroup({ this.form = new FormGroup({
name: new FormControl(this.tg.name), name: new FormControl(this.tg.name),
alpha_tag: new FormControl(this.tg.alpha_tag), alpha_tag: new FormControl(this.tg.alpha_tag),
tg_group: new FormControl(this.tg.tg_group), tg_group: new FormControl(this.tg.tg_group),
frequency: new FormControl(this.tg.frequency), frequency: new FormControl(this.tg.frequency),
alert: new FormControl(this.tg.alert), alert: new FormControl(this.tg.alert),
weight: new FormControl(this.tg.weight), weight: new FormControl(this.tg.weight),
icon: new FormControl(this.tg.icon), icon: new FormControl(this.tg.icon),
}); });
console.log(this.tg.icon); console.log(this.tg.icon);
}); });
} }
submit() { submit() {
@ -84,9 +87,11 @@ export class TalkgroupRecordComponent {
tgu.metadata = {}; tgu.metadata = {};
} }
if (this.tg.icon == null || this.tg.icon == '') { if (this.tg.icon == null || this.tg.icon == '') {
tgu.metadata = Object.assign(tgu.metadata, {icon: undefined}); tgu.metadata = Object.assign(tgu.metadata, { icon: undefined });
} else { } else {
tgu.metadata = Object.assign(tgu.metadata!, { icon: this.form.controls['icon'] }); tgu.metadata = Object.assign(tgu.metadata!, {
icon: this.form.controls['icon'],
});
} }
} }
this.tgService this.tgService

View file

@ -1,2 +1,2 @@
.tgIcon { .tgIcon {
} }

View file

@ -1,45 +1,179 @@
<a href="#" class="btn btn-primary" routerLink="/talkgroups/import">Import</a>
<div class="w-100 justify-center overflow-x-auto"> <div class="w-100 justify-center overflow-x-auto">
<table class="table"> <a href="#" class="btn btn-primary" routerLink="/talkgroups/import">Import</a>
<thead> <div class="rounded-lg border border-gray-200 dark:border-gray-700">
<tr> <div class="overflow-x-auto rounded-t-lg">
<th></th> <table
<th>Sys</th> class="min-w-full divide-y-2 divide-gray-200 bg-white text-sm dark:divide-gray-700 dark:bg-gray-900"
<th>Sys ID</th> >
<th>Group</th> <thead class="ltr:text-left">
<th>Name</th> <tr>
<th>Alpha</th> <th
<th>TG ID</th> class="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-white"
<th>Learned</th> ></th>
<th></th> <th
</tr> class="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-white"
</thead> >
<tbody> Sys
@let tgs = talkgroups$ | async; </th>
@for (tg of tgs?.talkgroups ; track tg.id) { <th
<tr> class="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-white"
<!-- <td class="tgIcon" [innerHTML]="(tg | iconify).iconSvg! | sanitizeHtml"></td> --> >
<td class="tgIcon"><ng-icon [name]="(tg | iconify).iconSvg!"></ng-icon></td> Sys ID
<td>{{ tg.system?.name }}</td> </th>
<td>{{ tg.system?.id }}</td> <th
<td>{{ tg.tg_group }}</td> class="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-white"
<td>{{ tg.name }}</td> >
<td>{{ tg.alpha_tag }}</td> Group
<td>{{ tg.tgid }}</td> </th>
<td>{{ tg?.learned ? "Y" : "" }}</td> <th
<td> class="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-white"
<a routerLink="/talkgroups/{{ tg.system?.id }}/{{ tg.tgid }}" >
><ng-icon name="ionCreateOutline"></ng-icon Name
></a> </th>
</td> <th
</tr> class="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-white"
} >
</tbody> Alpha Tag
</table> </th>
<div class="w-100"> <th
Total: {{ tgs?.count }} class="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-white"
<button class="btn btn-primary" (click)="prevPage()" [class.btn-ghost]="page == 1"><ng-icon name="ionChevronBack"></ng-icon></button> >
Page {{ page }} of {{ totalPages }} TG ID
<button class="btn btn-primary" (click)="nextPage()" [class.btn-ghost]="page == totalPages"><ng-icon name="ionChevronForward"></ng-icon></button> </th>
<th
class="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-white"
>
Learned
</th>
<th
class="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-white"
></th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-700">
@let tgs = talkgroups$ | async;
@for (tg of tgs?.talkgroups; track tg.id) {
<tr>
<!-- <td class="tgIcon" [innerHTML]="(tg | iconify).iconSvg! | sanitizeHtml"></td> -->
<td class="tgIcon">
<ng-icon [name]="(tg | iconify).iconSvg!"></ng-icon>
</td>
<td
class="whitespace-nowrap px-4 py-2 font-medium text-gray-900 dark:text-white"
>
{{ tg.system?.name }}
</td>
<td
class="whitespace-nowrap px-4 py-2 text-gray-700 dark:text-gray-200"
>
{{ tg.system?.id }}
</td>
<td
class="whitespace-nowrap px-4 py-2 text-gray-700 dark:text-gray-200"
>
{{ tg.tg_group }}
</td>
<td
class="whitespace-nowrap px-4 py-2 text-gray-700 dark:text-gray-200"
>
{{ tg.name }}
</td>
<td
class="whitespace-nowrap px-4 py-2 text-gray-700 dark:text-gray-200"
>
{{ tg.alpha_tag }}
</td>
<td
class="whitespace-nowrap px-4 py-2 text-gray-700 dark:text-gray-200"
>
{{ tg.tgid }}
</td>
<td
class="whitespace-nowrap px-4 py-2 text-gray-700 dark:text-gray-200"
>
{{ tg?.learned ? "Y" : "" }}
</td>
<td
class="whitespace-nowrap px-4 py-2 text-gray-700 dark:text-gray-200"
>
<a routerLink="/talkgroups/{{ tg.system?.id }}/{{ tg.tgid }}"
><ng-icon name="ionCreateOutline"></ng-icon
></a>
</td>
</tr>
}
</tbody>
</table>
</div>
<div
class="rounded-b-lg border-t border-gray-200 px-4 py-2 dark:border-gray-700"
>
<ol class="flex justify-end gap-1 text-xs font-medium">
<li>
<button
(click)="prevPage()"
[class.btn-ghost]="page == 1"
class="inline-flex size-8 items-center justify-center rounded border border-gray-100 bg-white text-gray-900 rtl:rotate-180 dark:border-gray-800 dark:bg-gray-900 dark:text-white"
>
<span class="sr-only">Prev Page</span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="size-3"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
clip-rule="evenodd"
/>
</svg>
</button>
</li>
@for (pgn of [].constructor(totalPages); track i; let i = $index) {
@if (i != page) {
<li>
<button
(click)="setPage(i + 1)"
class="block size-8 rounded border border-gray-100 bg-white text-center leading-8 text-gray-900 dark:border-gray-800 dark:bg-gray-900 dark:text-white"
>
{{ i + 1 }}
</button>
</li>
} @else {
<li
class="block size-8 rounded border-blue-600 bg-blue-600 text-center leading-8 dark:text-white"
>
{{ i + 1 }}
</li>
}
}
<li>
<button
(click)="nextPage()"
[class.btn-ghost]="page == totalPages"
class="inline-flex size-8 items-center justify-center rounded border border-gray-100 bg-white text-gray-900 rtl:rotate-180 dark:border-gray-800 dark:bg-gray-900 dark:text-white"
>
<span class="sr-only">Next Page</span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="size-3"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd"
/>
</svg>
</button>
</li>
</ol>
</div>
</div> </div>
</div> </div>

View file

@ -1,7 +1,11 @@
import { Component, inject, Pipe, PipeTransform } from '@angular/core'; import { Component, inject, Pipe, PipeTransform } from '@angular/core';
import { TalkgroupService, TalkgroupsPaginated } from './talkgroups.service'; import { TalkgroupService, TalkgroupsPaginated } from './talkgroups.service';
import { NgIconComponent, provideIcons } from '@ng-icons/core'; import { NgIconComponent, provideIcons } from '@ng-icons/core';
import { ionCreateOutline, ionChevronBack, ionChevronForward } from '@ng-icons/ionicons'; import {
ionCreateOutline,
ionChevronBack,
ionChevronForward,
} from '@ng-icons/ionicons';
import { import {
matFireTruckOutline, matFireTruckOutline,
matLocalPoliceOutline, matLocalPoliceOutline,
@ -17,7 +21,6 @@ import { RouterModule, RouterOutlet, RouterLink } from '@angular/router';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
@Pipe({ @Pipe({
standalone: true, standalone: true,
name: 'iconify', name: 'iconify',
@ -35,19 +38,16 @@ export class IconifyPipe implements PipeTransform {
@Pipe({ @Pipe({
standalone: true, standalone: true,
name: 'sanitizeHtml' name: 'sanitizeHtml',
}) })
export class SanitizeHtmlPipe implements PipeTransform { export class SanitizeHtmlPipe implements PipeTransform {
constructor(private _sanitizer: DomSanitizer) {}
constructor(private _sanitizer:DomSanitizer) { transform(v: string): SafeHtml {
}
transform(v:string):SafeHtml {
return this._sanitizer.bypassSecurityTrustHtml(v); return this._sanitizer.bypassSecurityTrustHtml(v);
} }
} }
@Component({ @Component({
selector: 'talkgroups', selector: 'talkgroups',
standalone: true, standalone: true,
@ -62,15 +62,18 @@ export class SanitizeHtmlPipe implements PipeTransform {
], ],
templateUrl: './talkgroups.component.html', templateUrl: './talkgroups.component.html',
styleUrl: './talkgroups.component.css', styleUrl: './talkgroups.component.css',
providers: [provideIcons({ ionCreateOutline, providers: [
matFireTruckOutline, provideIcons({
matLocalPoliceOutline, ionCreateOutline,
matEmergencyOutline, matFireTruckOutline,
matDirectionsBusOutline, matLocalPoliceOutline,
matGroupWorkOutline, matEmergencyOutline,
ionChevronBack, matDirectionsBusOutline,
ionChevronForward, matGroupWorkOutline,
})], ionChevronBack,
ionChevronForward,
}),
],
}) })
export class TalkgroupsComponent { export class TalkgroupsComponent {
selectedSys: number = 0; selectedSys: number = 0;
@ -97,16 +100,25 @@ export class TalkgroupsComponent {
} }
} }
setPage(p: number) {
if (p <= this.totalPages && p > 0) {
this.page = p;
this.fetchTGs();
}
}
fetchTGs() { fetchTGs() {
this.talkgroups$ = this.route.paramMap.pipe( this.talkgroups$ = this.route.paramMap.pipe(
switchMap((params) => { switchMap((params) => {
this.selectedSys = Number(params.get('sys')); this.selectedSys = Number(params.get('sys'));
this.selectedId = Number(params.get('tg')); this.selectedId = Number(params.get('tg'));
return this.tgService.getTalkgroupsPag({page: this.page, perPage: this.perPage}).pipe( return this.tgService
tap((event) => { .getTalkgroupsPag({ page: this.page, perPage: this.perPage })
this.totalPages = Math.ceil(event.count/this.perPage); .pipe(
}) tap((event) => {
); this.totalPages = Math.ceil(event.count / this.perPage);
}),
);
}), }),
); );
} }