kindof working

This commit is contained in:
Daniel 2024-11-05 10:05:45 -05:00
parent c891a94c7b
commit 6e0aade9d2
22 changed files with 320 additions and 88 deletions

View file

@ -8,9 +8,8 @@ describe('AlertsComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AlertsComponent]
})
.compileComponents();
imports: [AlertsComponent],
}).compileComponents();
fixture = TestBed.createComponent(AlertsComponent);
component = fixture.componentInstance;

View file

@ -5,8 +5,6 @@ import { Component } from '@angular/core';
standalone: true,
imports: [],
templateUrl: './alerts.component.html',
styleUrl: './alerts.component.css'
styleUrl: './alerts.component.css',
})
export class AlertsComponent {
}
export class AlertsComponent {}

View file

@ -1,12 +1,26 @@
<!-- Navbar -->
<nav class="navbar justify-between bg-base-300">
<!-- Logo -->
<a class="btn btn-ghost text-lg">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M15.362 5.214A8.252 8.252 0 0 1 12 21 8.25 8.25 0 0 1 6.038 7.047 8.287 8.287 0 0 0 9 9.601a8.983 8.983 0 0 1 3.361-6.867 8.21 8.21 0 0 0 3 2.48Z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M12 18a3.75 3.75 0 0 0 .495-7.468 5.99 5.99 0 0 0-1.925 3.547 5.975 5.975 0 0 1-2.133-1.001A3.75 3.75 0 0 0 12 18Z" />
<a class="btn btn-ghost text-lg btn-square">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-6"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M15.362 5.214A8.252 8.252 0 0 1 12 21 8.25 8.25 0 0 1 6.038 7.047 8.287 8.287 0 0 0 9 9.601a8.983 8.983 0 0 1 3.361-6.867 8.21 8.21 0 0 0 3 2.48Z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M12 18a3.75 3.75 0 0 0 .495-7.468 5.99 5.99 0 0 0-1.925 3.547 5.975 5.975 0 0 1-2.133-1.001A3.75 3.75 0 0 0 12 18Z"
/>
</svg>
</a>
<!-- Menu for mobile -->
@ -15,7 +29,10 @@
<i class="fa-solid fa-bars text-lg"></i>
</button>
<ul tabindex="0" class="dropdown-content menu z-[1] bg-base-200 p-6 rounded-box shadow w-56 gap-2">
<ul
tabindex="0"
class="dropdown-content menu z-[1] bg-base-200 p-6 rounded-box shadow w-56 gap-2"
>
<li><a>Item</a></li>
<a class="btn btn-sm btn-primary">Do</a>
</ul>
@ -24,31 +41,60 @@
<!-- Menu for desktop -->
<ul class="hidden menu sm:menu-horizontal gap-2">
<li><a>Item</a></li>
<a class="btn btn-sm btn-primary">Do</a>
@if (auth.loggedIn) {
<a (click)="logout()" class="btn btn-sm btn-primary">Logout</a>
}
</ul>
</nav>
<div class="flex overflow-hidden relative">
<!-- Sidebar -->
<aside class="h-screen sticky top-0 flex flex-col overflow-y-auto gap-2 py-6 px-2 bg-base-200">
<a class="btn btn-square btn-ghost btn-secondary text-xl" title="Home" routerLink="/home" routerLinkActive="btn-active">
<aside
class="h-screen sticky top-0 flex flex-col overflow-y-auto gap-2 py-6 px-2 bg-base-200"
>
<a
class="btn btn-square btn-ghost btn-secondary text-xl"
title="Home"
routerLink="/home"
routerLinkActive="btn-active"
>
<ng-icon name="ionHome"></ng-icon>
</a>
<a routerLink="/calls" routerLinkActive="btn-active" class="btn btn-ghost btn-secondary text-xl" title="Calls">
<a
routerLink="/calls"
routerLinkActive="btn-active"
class="btn btn-ghost btn-secondary text-xl"
title="Calls"
>
<ng-icon name="ionMegaphoneOutline"></ng-icon>
</a>
<a class="btn btn-square btn-ghost text-xl" title="Talkgroups" routerLink="/talkgroups" routerLinkActive="btn-active">
<a
class="btn btn-square btn-ghost text-xl"
title="Talkgroups"
routerLink="/talkgroups"
routerLinkActive="btn-active"
>
<ng-icon name="ionChatbubbles"></ng-icon>
</a>
<div class="divider my-0"></div>
<a class="btn btn-square btn-ghost text-xl" title="Incidents" routerLink="/incidents" routerLinkActive="btn-active">
<a
class="btn btn-square btn-ghost text-xl"
title="Incidents"
routerLink="/incidents"
routerLinkActive="btn-active"
>
<ng-icon name="ionNewspaperOutline"></ng-icon>
</a>
<a class="btn btn-square btn-ghost text-xl" title="Alerts" routerLink="/alerts" routerLinkActive="btn-active">
<a
class="btn btn-square btn-ghost text-xl"
title="Alerts"
routerLink="/alerts"
routerLinkActive="btn-active"
>
<ng-icon name="ionAlertCircleOutline"></ng-icon>
</a>
<div class="divider my-0"></div>
@ -57,7 +103,7 @@
<ng-icon name="ionRadioOutline"></ng-icon>
</a>
</aside>
<div class="container mx-auto px-4">
<div class="container mx-auto px-4 flex overflow-auto">
<router-outlet />
</div>
</div>

View file

@ -3,17 +3,47 @@ import { RouterModule, RouterOutlet, RouterLink } from '@angular/router';
import { CommonModule } from '@angular/common';
import { AuthService } from './login/auth.service';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
import { ionMenuOutline, ionChatbubbles, ionNewspaperOutline, ionAlertCircleOutline, ionRadioOutline, ionHome, ionMegaphoneOutline } from '@ng-icons/ionicons';
import {
ionMenuOutline,
ionChatbubbles,
ionNewspaperOutline,
ionAlertCircleOutline,
ionRadioOutline,
ionHome,
ionMegaphoneOutline,
ionCreateOutline,
} from '@ng-icons/ionicons';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet, RouterModule, RouterLink, NgIconComponent],
imports: [
CommonModule,
RouterOutlet,
RouterModule,
RouterLink,
NgIconComponent,
],
templateUrl: './app.component.html',
styleUrl: './app.component.css',
providers: [ provideIcons({ ionMenuOutline, ionChatbubbles, ionNewspaperOutline, ionAlertCircleOutline, ionRadioOutline, ionHome, ionMegaphoneOutline })],
providers: [
provideIcons({
ionMenuOutline,
ionChatbubbles,
ionNewspaperOutline,
ionAlertCircleOutline,
ionRadioOutline,
ionHome,
ionMegaphoneOutline,
ionCreateOutline,
}),
],
})
export class AppComponent {
auth: AuthService = inject(AuthService);
title = 'admin';
logout() {
this.auth.logout();
}
}

View file

@ -7,7 +7,8 @@ import {
withInterceptors,
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { isDevMode } from '@angular/core';
import { isDevMode, inject } from '@angular/core';
import { AuthService } from './login/auth.service';
import { routes } from './app.routes';
import { provideHttpClient } from '@angular/common/http';
@ -26,10 +27,28 @@ export function apiBaseInterceptor(
return next(apiReq);
}
export function authIntercept(
req: HttpRequest<unknown>,
next: HttpHandlerFn,
): Observable<HttpEvent<unknown>> {
let authSvc: AuthService = inject(AuthService);
if (authSvc.loggedIn) {
req = req.clone({
setHeaders: {
'Content-Type': 'application/json; charset=utf-8',
Accept: 'application/json',
Authorization: `Bearer ${authSvc.getToken()}`,
},
});
}
return next(req);
}
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient(withInterceptors([apiBaseInterceptor])),
provideHttpClient(withInterceptors([apiBaseInterceptor, authIntercept])),
],
};

View file

@ -8,9 +8,8 @@ describe('CallsComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CallsComponent]
})
.compileComponents();
imports: [CallsComponent],
}).compileComponents();
fixture = TestBed.createComponent(CallsComponent);
component = fixture.componentInstance;

View file

@ -5,8 +5,6 @@ import { Component } from '@angular/core';
standalone: true,
imports: [],
templateUrl: './calls.component.html',
styleUrl: './calls.component.css'
styleUrl: './calls.component.css',
})
export class CallsComponent {
}
export class CallsComponent {}

View file

@ -1,3 +1 @@
<p>
This will be a dashboard someday.
</p>
<p>This will be a dashboard someday.</p>

View file

@ -7,5 +7,4 @@ import { Component } from '@angular/core';
templateUrl: './home.component.html',
styleUrl: './home.component.css',
})
export class HomeComponent {
}
export class HomeComponent {}

View file

@ -8,9 +8,8 @@ describe('IncidentsComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [IncidentsComponent]
})
.compileComponents();
imports: [IncidentsComponent],
}).compileComponents();
fixture = TestBed.createComponent(IncidentsComponent);
component = fixture.componentInstance;

View file

@ -5,8 +5,6 @@ import { Component } from '@angular/core';
standalone: true,
imports: [],
templateUrl: './incidents.component.html',
styleUrl: './incidents.component.css'
styleUrl: './incidents.component.css',
})
export class IncidentsComponent {
}
export class IncidentsComponent {}

View file

@ -41,4 +41,14 @@ export class AuthService {
}),
);
}
getToken(): string | null {
return sessionStorage.getItem('jwt');
}
logout() {
sessionStorage.removeItem('jwt');
this.loggedIn = false;
this._router.navigateByUrl('/login');
}
}

View file

@ -0,0 +1,30 @@
export interface TGID {
sys: number;
tg: number;
}
export interface AlertRule {
times: string[];
mult: number;
}
export interface System {
id: number;
name: string;
}
export interface Talkgroup {
id: number;
system_id: number;
tgid: number;
name: string;
alpha_tag: string;
tg_group: string;
frequency: number;
metadata: Object;
tags: string[];
alert: boolean;
alert_config: Map<TGID, AlertRule[]>;
system: System;
weight: number;
}

View file

@ -0,0 +1 @@
<p>TG comp</p>

View file

@ -0,0 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TalkgroupRecordComponent } from './talkgroup-record.component';
describe('TalkgroupRecordComponent', () => {
let component: TalkgroupRecordComponent;
let fixture: ComponentFixture<TalkgroupRecordComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TalkgroupRecordComponent],
}).compileComponents();
fixture = TestBed.createComponent(TalkgroupRecordComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View file

@ -0,0 +1,16 @@
import { Component } from '@angular/core';
import { Talkgroup } from '../../talkgroup';
@Component({
selector: 'talkgroup-record',
standalone: true,
imports: [],
templateUrl: './talkgroup-record.component.html',
styleUrl: './talkgroup-record.component.css',
})
export class TalkgroupRecordComponent {
tg: Talkgroup;
constructor(tg: Talkgroup) {
this.tg = tg;
}
}

View file

@ -1 +1,26 @@
<p>talkgroups works!</p>
<div class="w-3/5 justify-center overflow-x-auto">
<table class="table">
<!-- head -->
<thead>
<tr>
<th>Sys</th>
<th>Sys ID</th>
<th>Name</th>
<th>TG ID</th>
<th></th>
</tr>
</thead>
<tbody>
<!-- row 1 -->
@for (tg of tgs; track tg.id) {
<tr>
<td>{{ tg.system.name }}</td>
<td>{{ tg.system.id }}</td>
<td>{{ tg.name }}</td>
<td>{{ tg.tgid }}</td>
<td><ng-icon name="ionCreateOutline"></ng-icon></td>
</tr>
}
</tbody>
</table>
</div>

View file

@ -8,9 +8,8 @@ describe('TalkgroupsComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TalkgroupsComponent]
})
.compileComponents();
imports: [TalkgroupsComponent],
}).compileComponents();
fixture = TestBed.createComponent(TalkgroupsComponent);
component = fixture.componentInstance;

View file

@ -1,12 +1,26 @@
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
import { TalkgroupService } from './talkgroups.service';
import { Talkgroup } from '../talkgroup';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
import { ionCreateOutline } from '@ng-icons/ionicons';
@Component({
selector: 'app-talkgroups',
selector: 'talkgroups',
standalone: true,
imports: [],
imports: [NgIconComponent],
templateUrl: './talkgroups.component.html',
styleUrl: './talkgroups.component.css'
styleUrl: './talkgroups.component.css',
providers: [provideIcons({ ionCreateOutline })],
})
export class TalkgroupsComponent {
tgs: Talkgroup[] = [];
tgService: TalkgroupService = inject(TalkgroupService);
ngOnInit() {
this.getTalkgroups();
}
getTalkgroups() {
this.tgService.getTalkgroups().subscribe((tgs) => (this.tgs = tgs));
}
}

View file

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { APIService } from './api.service';
describe('APIService', () => {
let service: APIService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(APIService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View file

@ -0,0 +1,16 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Talkgroup } from '../talkgroup';
@Injectable({
providedIn: 'root',
})
export class TalkgroupService {
loggedIn: boolean = false;
constructor(private http: HttpClient) {}
getTalkgroups(): Observable<Talkgroup[]> {
return this.http.get<Talkgroup[]>('/api/talkgroup/');
}
}