14 changed files with 266 additions and 8 deletions
@ -0,0 +1,120 @@ |
|||||
|
import { |
||||
|
HttpClient, |
||||
|
HttpErrorResponse, |
||||
|
HttpEvent, |
||||
|
HttpHandler, |
||||
|
HttpInterceptor, |
||||
|
HttpRequest |
||||
|
} from "@angular/common/http"; |
||||
|
import {Component, Injectable, OnInit, ViewContainerRef} from "@angular/core"; |
||||
|
import {catchError, Observable, startWith, throwError} from "rxjs"; |
||||
|
import {MatDialog, MatDialogRef} from "@angular/material/dialog"; |
||||
|
import {FormControl} from "@angular/forms"; |
||||
|
import {NodeMiniDTO} from "../entities/NodeMiniDTO"; |
||||
|
import {MatSnackBar} from "@angular/material/snack-bar"; |
||||
|
|
||||
|
|
||||
|
@Component({ |
||||
|
selector: "app-auth-dialog", |
||||
|
template: ` |
||||
|
<h2 mat-dialog-title>Авторизация</h2> |
||||
|
<div mat-dialog-content> |
||||
|
<mat-tab-group> |
||||
|
<mat-tab label="Через поиск"> |
||||
|
<div> |
||||
|
<div> |
||||
|
<mat-form-field appearance="fill" style="width: 100%"> |
||||
|
<mat-label>Имя ноды</mat-label> |
||||
|
<input |
||||
|
type="text" |
||||
|
matInput |
||||
|
[formControl]="myControl" |
||||
|
[matAutocomplete]="auto" |
||||
|
> |
||||
|
<mat-autocomplete #auto="matAutocomplete" [displayWith]="displayNode"> |
||||
|
<mat-option *ngFor="let node of foundedNodes | async" [value]="node"> |
||||
|
{{ node.long_name }} ({{node.num}}) |
||||
|
</mat-option> |
||||
|
</mat-autocomplete> |
||||
|
</mat-form-field> |
||||
|
|
||||
|
<button mat-button mat-raised-button (click)="sendCodeToNode()">Отправить код на ноду</button> |
||||
|
</div> |
||||
|
<mat-divider></mat-divider> |
||||
|
<div> |
||||
|
<mat-form-field class="example-full-width" appearance="fill"> |
||||
|
<mat-label>Код из сообщения</mat-label> |
||||
|
<input [(ngModel)]="code" matInput maxlength="4" placeholder="Код из сообщения"> |
||||
|
</mat-form-field> |
||||
|
<button mat-button (click)="acceptCode()">Проверить код</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</mat-tab> |
||||
|
<mat-tab label="Написать боту" disabled> |
||||
|
<div></div> |
||||
|
</mat-tab> |
||||
|
</mat-tab-group> |
||||
|
</div> |
||||
|
` |
||||
|
}) |
||||
|
export class AuthDialog implements OnInit { |
||||
|
constructor(private http: HttpClient, |
||||
|
private dialogRef: MatDialogRef<any>, |
||||
|
private snack: MatSnackBar) { |
||||
|
} |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this.myControl.valueChanges.subscribe( |
||||
|
(v) => { |
||||
|
if (typeof v == "string") |
||||
|
this.foundedNodes = this.http.get(`api/nodes/search?name=${v}`) as Observable<NodeMiniDTO[]> |
||||
|
} |
||||
|
) |
||||
|
} |
||||
|
myControl = new FormControl(); |
||||
|
foundedNodes: Observable<NodeMiniDTO[]> = new Observable<NodeMiniDTO[]>() |
||||
|
code!: number |
||||
|
|
||||
|
acceptCode() { |
||||
|
this.http.get(`api/auth/code/check?code=${this.code}`) |
||||
|
// @ts-ignore
|
||||
|
.subscribe((res:{status:boolean}) => { |
||||
|
if (res.status) { |
||||
|
this.dialogRef.close() |
||||
|
} else { |
||||
|
this.snack.open("Неправильный код") |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
displayNode(node: NodeMiniDTO) { |
||||
|
if (!node) return '' |
||||
|
return `${node.long_name} (${node.num})` |
||||
|
} |
||||
|
|
||||
|
sendCodeToNode() { |
||||
|
let node:NodeMiniDTO = this.myControl.value as NodeMiniDTO |
||||
|
this.http.get(`api/auth/code?num=${node.num}`).subscribe( |
||||
|
(res) => this.snack.open(`Код отправлен на ноду ${node.long_name}`) |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Injectable() |
||||
|
export class AuthInterceptor implements HttpInterceptor { |
||||
|
constructor(private dialog: MatDialog) {} |
||||
|
dialogOpened: boolean = false; |
||||
|
|
||||
|
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { |
||||
|
return next.handle(req).pipe( |
||||
|
catchError((error: HttpErrorResponse) => { |
||||
|
if (error.status === 401 && !this.dialogOpened) { |
||||
|
const ref = this.dialog.open(AuthDialog, {disableClose: true, width:"50%"}) |
||||
|
ref.afterClosed().subscribe((res:any) => this.dialogOpened = false) |
||||
|
} |
||||
|
return throwError(() => error); |
||||
|
}) |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
import {Component} from "@angular/core"; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: "app-bot-commands", |
||||
|
template: `` |
||||
|
}) |
||||
|
export class BotCommandsComponent { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
import {Component} from "@angular/core"; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: "app-message-history", |
||||
|
template: `` |
||||
|
}) |
||||
|
export class MessageHistoryComponent { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
import {Component} from "@angular/core"; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: "app-all-nodes", |
||||
|
template: `` |
||||
|
}) |
||||
|
export class AllNodesComponent { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
import {Component, OnInit} from "@angular/core"; |
||||
|
import {HttpClient} from "@angular/common/http"; |
||||
|
import {NodeDTO} from "../../entities/NodeDTO"; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-direct-nodes', |
||||
|
styleUrls: ['nodes.styles.scss'], |
||||
|
template: ` |
||||
|
<!--<p *ngFor="let node of nodes">{{node.long_name}}</p>--> |
||||
|
<div class="card-wrapper"> |
||||
|
<mat-card *ngFor="let node of nodes" style="min-width: 250px"> |
||||
|
<mat-card-header> |
||||
|
<mat-card-title>{{node.long_name}}</mat-card-title> |
||||
|
<mat-card-subtitle>{{node.short_name}} ({{node.num}})</mat-card-subtitle> |
||||
|
</mat-card-header> |
||||
|
<mat-card-content> |
||||
|
<p>график</p> |
||||
|
</mat-card-content> |
||||
|
<mat-card-actions> |
||||
|
<button mat-button *ngIf="node.hops_away > 0">Прыжков: {{node.hops_away}}</button> |
||||
|
<button mat-button>SNR: {{node.snr}}</button> |
||||
|
<button mat-button>{{node.ts * 1000 | date:"hh:mm dd.MM.yyyy"}}</button> |
||||
|
</mat-card-actions> |
||||
|
</mat-card> |
||||
|
</div> |
||||
|
` |
||||
|
}) |
||||
|
export class DirectNodesComponent implements OnInit { |
||||
|
constructor(private http: HttpClient) { |
||||
|
} |
||||
|
|
||||
|
nodes: NodeDTO[] = []; |
||||
|
|
||||
|
ngOnInit(): void { |
||||
|
this.http.get(`api/nodes/direct`).subscribe( |
||||
|
(res) => this.nodes = res as NodeDTO[] |
||||
|
) |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
.card-wrapper { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); |
||||
|
gap: 8px; |
||||
|
} |
||||
|
|
||||
|
.grid-card { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
/* Additional styling as needed */ |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
import {NodeMiniDTO} from "./NodeMiniDTO"; |
||||
|
|
||||
|
export interface NodeDTO extends NodeMiniDTO { |
||||
|
snr: number, |
||||
|
hops_away: number, |
||||
|
ts: number |
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
export interface NodeMiniDTO { |
||||
|
num: number, |
||||
|
long_name: string, |
||||
|
short_name: string |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
{ |
||||
|
"/api": { |
||||
|
"target": "http://192.168.3.2:8680/", |
||||
|
"secure": false |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue