mirror of
https://github.com/MikroWizard/mikrofront.git
synced 2025-06-20 18:05:39 +02:00
New menu/page for configuration sync and cloning (pro feature).
Now displays DHCP server details along with historical lease information(pro feature)). Added a ping status component for monitoring device connectivity. Device pages now include logs, accounting, and authorization data for improved tracking. New component in the device page to display active sessions with the ability to terminate them. Redesigned Device Detail Page Reload Buttons for device details page Tabs Device detail pages now refresh every 30 seconds. Restored the missing device logs action menu on the devices list page. Improved handling of duplicate serial numbers when the license serial is not set, with better registration status details on the dashboard. Various minor stability and performance improvements.
This commit is contained in:
parent
d8aa93f7ec
commit
10d4cff4a4
55 changed files with 2412 additions and 271 deletions
|
@ -84,6 +84,11 @@ const routes: Routes = [
|
|||
loadChildren: () =>
|
||||
import('./views/user_tasks/user_tasks.module').then((m) => m.UserTasksModule)
|
||||
},
|
||||
{
|
||||
path: 'cloner',
|
||||
loadChildren: () =>
|
||||
import('./views/cloner/cloner.module').then((m) => m.ClonerModule)
|
||||
},
|
||||
{
|
||||
path: 'snippets',
|
||||
loadChildren: () =>
|
||||
|
|
|
@ -60,10 +60,17 @@ export const navItems: INavData[] = [
|
|||
icon: 'fa-solid fa-database'
|
||||
},
|
||||
{
|
||||
name: 'snippets',
|
||||
name: 'Snippets',
|
||||
url: '/snippets',
|
||||
icon: 'fa-solid fa-code'
|
||||
},
|
||||
{
|
||||
name: 'Sync and Cloner',
|
||||
url: '/cloner',
|
||||
icon: 'fa-solid fa-rotate',
|
||||
attributes: { 'pro':true }
|
||||
|
||||
},
|
||||
{
|
||||
name: 'Password Vault',
|
||||
url: '/vault',
|
||||
|
|
|
@ -172,6 +172,12 @@ export class dataProvider {
|
|||
}
|
||||
return this.MikroWizardRPC.sendJsonRequest("/api/dev/radio/sensors", data);
|
||||
}
|
||||
get_dev_dhcp_info(id: number){
|
||||
var data={
|
||||
'devid':id,
|
||||
}
|
||||
return this.MikroWizardRPC.sendJsonRequest("/api/dev/dhcp-server/get", data);
|
||||
}
|
||||
get_dev_ifstat(id: number,delta:string="5m",iface:string="ether1",type:string="bps") {
|
||||
var data={
|
||||
'devid':id,
|
||||
|
@ -553,6 +559,48 @@ export class dataProvider {
|
|||
}
|
||||
return this.MikroWizardRPC.sendJsonRequest("/api/sysconfig/apply_update", data);
|
||||
}
|
||||
|
||||
|
||||
get_cloner_list() {
|
||||
return this.MikroWizardRPC.sendJsonRequest("/api/cloner/list", {});
|
||||
}
|
||||
|
||||
Add_cloner(data:any,members:any) {
|
||||
data['members']=members;
|
||||
return this.MikroWizardRPC.sendJsonRequest("/api/cloner/create", data);
|
||||
}
|
||||
|
||||
Delete_cloner(clonerid:number) {
|
||||
var data={
|
||||
'clonerid':clonerid,
|
||||
}
|
||||
return this.MikroWizardRPC.sendJsonRequest("/api/cloner/delete", data);
|
||||
}
|
||||
|
||||
Edit_cloner(data:any,members:any) {
|
||||
data['members']=members;
|
||||
return this.MikroWizardRPC.sendJsonRequest("/api/cloner/edit", data);
|
||||
}
|
||||
|
||||
get_cloner_members(clonerid:number) {
|
||||
var data={
|
||||
'clonerid':clonerid,
|
||||
}
|
||||
return this.MikroWizardRPC.sendJsonRequest("/api/cloner/memberdetails", data);
|
||||
}
|
||||
killSession(devid:number,item:any){
|
||||
var data={
|
||||
'devid':devid,
|
||||
'item':item
|
||||
}
|
||||
return this.MikroWizardRPC.sendJsonRequest("/api/dev/kill_session", data);
|
||||
}
|
||||
getDhcpHistory(item:any){
|
||||
var data={
|
||||
'item':item
|
||||
}
|
||||
return this.MikroWizardRPC.sendJsonRequest("/api/dhcp-history/get", data);
|
||||
}
|
||||
////
|
||||
//// End api funcs
|
||||
////
|
||||
|
|
|
@ -1,10 +1,22 @@
|
|||
<c-row>
|
||||
<c-col xs>
|
||||
<c-card class="mb-4">
|
||||
<c-card class="mb-4" [ngStyle]="component_devid && {'border-top': 'none'}">
|
||||
<c-card-header>
|
||||
<c-row>
|
||||
<c-col xs [lg]="11">
|
||||
Accunting Logs
|
||||
<c-col xs [lg]="11" style="display: flex;flex-direction: column;align-items: flex-start;">
|
||||
<h5>Accunting Logs
|
||||
<a style="cursor: pointer;" (click)="reinitgrid('none','none')"><i
|
||||
*ngIf="devid!=0 && component_devid && !reloading" class="fa-solid fa-arrows-rotate"
|
||||
style="color: #74C0FC;"></i>
|
||||
<i *ngIf="devid!=0 && component_devid && reloading" class="fa-solid fa-arrows-rotate fa-spin"
|
||||
style="color: #74C0FC;"></i>
|
||||
</a>
|
||||
</h5>
|
||||
<c-badge color="warning" *ngIf="devid!=0 && !component_devid">Filtered Result For Device ID
|
||||
{{devid}}</c-badge>
|
||||
<c-alert color="warning" style="padding-top: 5px!important;font-size: 0.8rem;display: inline-block;" *ngIf="!filters['start_time'] && !filters['end_time']">
|
||||
<i class="fa-solid fa-triangle-exclamation mx-1"></i>Showing <strong>last 24 hours logs</strong> by default. Use filters to modify the date and time.
|
||||
</c-alert>
|
||||
</c-col>
|
||||
<c-col xs [lg]="1">
|
||||
<button (click)="toggleCollapse()" cButton class="me-1" color="primary"><i
|
||||
|
|
|
@ -94,4 +94,8 @@ padding: 1rem!important;
|
|||
.mat-mdc-form-field-infix{
|
||||
width:150px;
|
||||
}
|
||||
}
|
||||
.alert{
|
||||
padding: 5px!important;
|
||||
color:#644808!important;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit, ViewEncapsulation } from "@angular/core";
|
||||
import { Component, OnInit, ViewEncapsulation,Input } from "@angular/core";
|
||||
import { dataProvider } from "../../providers/mikrowizard/data";
|
||||
import { Router, ActivatedRoute } from "@angular/router";
|
||||
import { loginChecker } from "../../providers/login_checker";
|
||||
|
@ -19,15 +19,18 @@ import { formatInTimeZone } from "date-fns-tz";
|
|||
|
||||
|
||||
@Component({
|
||||
selector: 'app-acclogs',
|
||||
templateUrl: "acc.component.html",
|
||||
styleUrls: ["acc.component.scss"],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class AccComponent implements OnInit {
|
||||
@Input() component_devid: any=false;
|
||||
public uid: number;
|
||||
public uname: string;
|
||||
public tz: string;
|
||||
public filterText: string;
|
||||
public reloading: boolean = false;
|
||||
public filters: any = {
|
||||
devid: false,
|
||||
ip: "",
|
||||
|
@ -156,7 +159,11 @@ export class AccComponent implements OnInit {
|
|||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.devid = Number(this.route.snapshot.paramMap.get("devid"));
|
||||
if (this.component_devid) {
|
||||
this.devid = this.component_devid;
|
||||
} else{
|
||||
this.devid = Number(this.route.snapshot.paramMap.get("devid"));
|
||||
}
|
||||
if (this.devid > 0) {
|
||||
this.filters["devid"] = this.devid;
|
||||
}
|
||||
|
@ -181,6 +188,8 @@ export class AccComponent implements OnInit {
|
|||
|
||||
initGridTable(): void {
|
||||
var _self = this;
|
||||
if(this.reloading) return;
|
||||
this.reloading = true;
|
||||
this.data_provider.get_account_logs(this.filters).then((res) => {
|
||||
let index = 1;
|
||||
this.source = res.map((d: any) => {
|
||||
|
@ -199,6 +208,7 @@ export class AccComponent implements OnInit {
|
|||
return d;
|
||||
});
|
||||
this.loading = false;
|
||||
this.reloading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ import {
|
|||
FormModule,
|
||||
GridModule,
|
||||
CollapseModule,
|
||||
BadgeModule,
|
||||
AlertModule
|
||||
} from "@coreui/angular";
|
||||
|
||||
import { AccRoutingModule } from "./acc-routing.module";
|
||||
|
@ -36,7 +38,10 @@ import { FormsModule } from "@angular/forms";
|
|||
MatInputModule,
|
||||
MatDatepickerModule,
|
||||
MatSelectModule,
|
||||
BadgeModule,
|
||||
AlertModule
|
||||
],
|
||||
declarations: [AccComponent],
|
||||
exports: [AccComponent],
|
||||
})
|
||||
export class AccModule {}
|
||||
|
|
|
@ -1,10 +1,22 @@
|
|||
<c-row>
|
||||
<c-col xs>
|
||||
<c-card class="mb-4">
|
||||
<c-card class="mb-4" [ngStyle]="component_devid && {'border-top': 'none'}">
|
||||
<c-card-header>
|
||||
<c-row>
|
||||
<c-col xs [lg]="11">
|
||||
Authentication Logs
|
||||
<c-col xs [lg]="11" style="display: flex;flex-direction: column;align-items: flex-start;">
|
||||
<h5>Authentication Logs
|
||||
<a style="cursor: pointer;" (click)="reinitgrid('none','none')"><i
|
||||
*ngIf="devid!=0 && component_devid && !reloading" class="fa-solid fa-arrows-rotate"
|
||||
style="color: #74C0FC;"></i>
|
||||
<i *ngIf="devid!=0 && component_devid && reloading" class="fa-solid fa-arrows-rotate fa-spin"
|
||||
style="color: #74C0FC;"></i>
|
||||
</a>
|
||||
</h5>
|
||||
<c-badge color="warning" *ngIf="devid!=0 && !component_devid">Filtered Result For Device ID
|
||||
{{devid}}</c-badge>
|
||||
<c-alert color="warning" style="padding-top: 5px!important;font-size: 0.8rem;display: inline-block;" *ngIf="!filters['start_time'] && !filters['end_time']">
|
||||
<i class="fa-solid fa-triangle-exclamation mx-1"></i>Showing <strong>last 24 hours logs</strong> by default. Use filters to modify the date and time.
|
||||
</c-alert>
|
||||
</c-col>
|
||||
<c-col xs [lg]="1">
|
||||
<button (click)="toggleCollapse()" cButton class="me-1" color="primary"><i
|
||||
|
@ -14,7 +26,7 @@
|
|||
</c-card-header>
|
||||
<c-card-body>
|
||||
<c-row>
|
||||
<div [visible]="filters_visible" cCollapse>
|
||||
<div [visible]="filters_visible" cCollapse>
|
||||
<c-col xs [lg]="12" class="example-form">
|
||||
<mat-form-field>
|
||||
<mat-label>Start date</mat-label>
|
||||
|
@ -55,7 +67,7 @@
|
|||
Failed
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field >
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>Server</mat-label>
|
||||
<mat-select placeholder="Server" (ngModelChange)="reinitgrid('server',$event)"
|
||||
|
@ -65,7 +77,7 @@
|
|||
{{ac}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field >
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>Device IP</mat-label>
|
||||
<input (ngModelChange)="reinitgrid('devip',$event)" [(ngModel)]="filters['devip']" matInput>
|
||||
|
|
|
@ -34,3 +34,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
.alert{
|
||||
padding: 5px!important;
|
||||
color:#644808!important;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit, ViewEncapsulation } from "@angular/core";
|
||||
import { Component, OnInit, ViewEncapsulation,Input } from "@angular/core";
|
||||
import { dataProvider } from "../../providers/mikrowizard/data";
|
||||
import { Router, ActivatedRoute } from "@angular/router";
|
||||
import { loginChecker } from "../../providers/login_checker";
|
||||
|
@ -30,16 +30,19 @@ interface IUser {
|
|||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-authlogs',
|
||||
templateUrl: "auth.component.html",
|
||||
styleUrls: ["auth.component.scss"],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class AuthComponent implements OnInit {
|
||||
@Input() component_devid: any=false;
|
||||
public uid: number;
|
||||
public uname: string;
|
||||
public tz: string = "UTC";
|
||||
public filterText: string;
|
||||
public devid: number = 0;
|
||||
public reloading: boolean = false;
|
||||
public filters: any = {
|
||||
devid: false,
|
||||
ip: "",
|
||||
|
@ -166,7 +169,11 @@ export class AuthComponent implements OnInit {
|
|||
return str;
|
||||
}
|
||||
ngOnInit(): void {
|
||||
this.devid = Number(this.route.snapshot.paramMap.get("devid"));
|
||||
if (this.component_devid) {
|
||||
this.devid = this.component_devid;
|
||||
} else{
|
||||
this.devid = Number(this.route.snapshot.paramMap.get("devid"));
|
||||
}
|
||||
if (this.devid > 0) {
|
||||
this.filters["devid"] = this.devid;
|
||||
}
|
||||
|
@ -190,6 +197,8 @@ export class AuthComponent implements OnInit {
|
|||
|
||||
initGridTable(): void {
|
||||
var _self = this;
|
||||
if(this.reloading) return;
|
||||
this.reloading = true;
|
||||
this.data_provider.get_auth_logs(this.filters).then((res) => {
|
||||
let index = 1;
|
||||
this.source = res.map((d: any) => {
|
||||
|
@ -217,6 +226,7 @@ export class AuthComponent implements OnInit {
|
|||
return d;
|
||||
});
|
||||
this.loading = false;
|
||||
this.reloading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
GridModule,
|
||||
CollapseModule,
|
||||
BadgeModule,
|
||||
AlertModule,
|
||||
} from '@coreui/angular';
|
||||
import { AuthRoutingModule } from './auth-routing.module';
|
||||
import { AuthComponent } from './auth.component';
|
||||
|
@ -32,9 +33,11 @@ import {MatSelectModule} from '@angular/material/select';
|
|||
MatInputModule,
|
||||
MatDatepickerModule,
|
||||
MatSelectModule,
|
||||
BadgeModule
|
||||
BadgeModule,
|
||||
AlertModule
|
||||
],
|
||||
declarations: [AuthComponent]
|
||||
declarations: [AuthComponent],
|
||||
exports: [AuthComponent],
|
||||
})
|
||||
export class AuthModule {
|
||||
}
|
||||
|
|
21
src/app/views/cloner/cloner-routing.module.ts
Normal file
21
src/app/views/cloner/cloner-routing.module.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { ClonerComponent } from './cloner.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: ClonerComponent,
|
||||
data: {
|
||||
title: $localize`synchronization and cloner`
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class ClonerRoutingModule {
|
||||
}
|
239
src/app/views/cloner/cloner.component.html
Normal file
239
src/app/views/cloner/cloner.component.html
Normal file
|
@ -0,0 +1,239 @@
|
|||
<c-row>
|
||||
<c-col xs>
|
||||
<c-card class="mb-4">
|
||||
<c-card-header>
|
||||
<c-row>
|
||||
<c-col xs [lg]="10">
|
||||
Config synchronization and cloners
|
||||
</c-col>
|
||||
<c-col xs [lg]="2" style="text-align: right;">
|
||||
<button cButton color="primary" (click)="editAddCloner({},'showadd')"><i
|
||||
class="fa-solid fa-plus"></i></button>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</c-card-header>
|
||||
<c-card-body>
|
||||
<gui-grid [autoResizeWidth]="true" [source]="source" [columnMenu]="columnMenu" [sorting]="sorting"
|
||||
[infoPanel]="infoPanel" [autoResizeWidth]=true>
|
||||
<gui-grid-column header="Description" field="description">
|
||||
<ng-template let-value="item.description" let-item="item" let-index="index">
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Members type" field="pair_type">
|
||||
<ng-template let-value="item.pair_type" let-item="item" let-index="index">
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Runtime" field="desc_cron">
|
||||
<ng-template let-value="item.desc_cron" let-item="item" let-index="index">
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Actions" width="120" field="action">
|
||||
<ng-template let-value="item.id" let-item="item" let-index="index">
|
||||
<button cButton color="warning" size="sm" (click)="editAddCloner(item,'edit');"><i
|
||||
class="fa-regular fa-pen-to-square"></i></button>
|
||||
<!-- <button cButton color="info" size="sm" (click)="confirm_run(item);" class="mx-1"><i
|
||||
class="fa-solid fa-bolt"></i></button> -->
|
||||
<button class=" mx-1" cButton color="danger" size="sm" (click)="confirm_delete(item);"><i
|
||||
class="fa-regular fa-trash-can"></i></button>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
</gui-grid>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-col>
|
||||
</c-row>
|
||||
|
||||
<c-modal #EditClonerModal backdrop="static" size="xl" [(visible)]="EditClonerModalVisible" id="EditClonerModal">
|
||||
<c-modal-header>
|
||||
<h5 *ngIf="SelectedCloner['action']=='edit'" cModalTitle>Editing Cloner {{SelectedCloner['name']}}</h5>
|
||||
<h5 *ngIf="SelectedCloner['action']=='add'" cModalTitle>Adding new task</h5>
|
||||
<button [cModalToggle]="EditClonerModal.id" cButtonClose></button>
|
||||
</c-modal-header>
|
||||
<c-modal-body>
|
||||
<label cLabel >General data:</label>
|
||||
<c-input-group class="mb-3">
|
||||
<label cInputGroupText for="floatingInput">Name</label>
|
||||
<input cFormControl id="floatingInput" placeholder="SelectedCloner['name']" [(ngModel)]="SelectedCloner['name']" />
|
||||
<label cInputGroupText for="floatingInput">Description</label>
|
||||
<input cFormControl id="floatingInput" placeholder="SelectedCloner['description']"
|
||||
[(ngModel)]="SelectedCloner['description']" />
|
||||
</c-input-group>
|
||||
<label cLabel >Sync config and Oprating Modes:</label>
|
||||
<c-input-group class="mb-3">
|
||||
<label cInputGroupText for="Direction">
|
||||
Direction
|
||||
</label>
|
||||
<select cSelect id="Direction" [(ngModel)]="SelectedCloner['direction']">
|
||||
<option value="twoway">Two way</option>
|
||||
<option value="oneway">Master mode</option>
|
||||
</select>
|
||||
<label cInputGroupText for="inputGroupSelect01">
|
||||
Live Mode
|
||||
</label>
|
||||
<select cSelect id="inputGroupSelect01" [(ngModel)]="SelectedCloner['live_mode']">
|
||||
<option [ngValue]="false">Deactive</option>
|
||||
<option [ngValue]="true">Active</option>
|
||||
</select>
|
||||
<label *ngIf="SelectedCloner['direction']=='oneway'" cInputGroupText for="inputGroupSelect02">
|
||||
Schedule
|
||||
</label>
|
||||
<select *ngIf="SelectedCloner['direction']=='oneway'" cSelect id="inputGroupSelect02" [(ngModel)]="SelectedCloner['schedule']">
|
||||
<option [ngValue]="false">Deactive</option>
|
||||
<option [ngValue]="true">Active</option>
|
||||
</select>
|
||||
<label cInputGroupText *ngIf="SelectedCloner['schedule'] && SelectedCloner['direction']=='oneway'" for="cron">cron</label>
|
||||
<input cFormControl *ngIf="SelectedCloner['schedule'] && SelectedCloner['direction']=='oneway'" id="cron" placeholder="Cron" [(ngModel)]="SelectedCloner['cron']" />
|
||||
</c-input-group>
|
||||
<label cLabel >Peers Setting:</label>
|
||||
<c-input-group class="mb-3">
|
||||
<label cInputGroupText for="inputGroupSelect03">
|
||||
Peers type
|
||||
</label>
|
||||
<select cSelect id="inputGroupSelect03" (change)="form_changed()" [(ngModel)]="SelectedCloner['pair_type']">
|
||||
<option value="devices">Devices</option>
|
||||
<option value="groups" *ngIf="SelectedCloner['direction']=='oneway'">Groups</option>
|
||||
</select>
|
||||
</c-input-group>
|
||||
|
||||
<c-col xs style="border: 1px solid #ddd; border-radius: 4px; padding: 0;">
|
||||
<div class="nav nav-underline" style="background: #fff; border-bottom: 2px solid #2c3e50;">
|
||||
<div class="nav-item" *ngFor="let tab of tabs; let i = index">
|
||||
<a class="nav-link" [active]="i==0" [cTabContent]="tabContent" [tabPaneIdx]="i">{{ tab.name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
<c-tab-content class="command-sections" style="padding: 10px!important;min-height: 150px;" #tabContent="cTabContent">
|
||||
<c-tab-pane *ngFor="let tab of tabs; let i = index">
|
||||
<div class="section" *ngFor="let section of tab.sections">
|
||||
<h5 class="cloner-sections">{{ section.title }}</h5>
|
||||
<div class="row">
|
||||
<div class="col-4" *ngFor="let command of section.commands">
|
||||
<c-card style="margin-bottom: 5px;">
|
||||
<c-card-body class="p-2">
|
||||
<h6 class="card-title mb-1">{{ command }}</h6>
|
||||
<div class="custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" [checked]="in_active_commands(command)" (click)="activate_command(command)" [id]="command.replace('/', '')">
|
||||
<label class="custom-control-label" [for]="command.replace('/', '')"></label>
|
||||
</div>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</c-tab-pane>
|
||||
</c-tab-content>
|
||||
</c-col>
|
||||
|
||||
<h5>Peers :</h5>
|
||||
<gui-grid [autoResizeWidth]="true" [source]="SelectedMembers" [columnMenu]="columnMenu" [sorting]="sorting"
|
||||
[rowSelection]="rowSelection" [autoResizeWidth]=true [paging]="paging">
|
||||
<gui-grid-column header="Name" field="name">
|
||||
<ng-template let-value="item.name" let-item="item" let-index="index">
|
||||
<i class="fa-solid fa-m" style="color: #ff3300;" *ngIf="SelectedCloner['direction']=='oneway' && item.id==master"></i>
|
||||
{{value}} </ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column *ngIf="SelectedCloner['pair_type']=='devices'" header="MAC" field="mac">
|
||||
<ng-template let-value="item.mac" let-item="item" let-index="index">
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Actions" width="120" field="action">
|
||||
<ng-template let-value="item.id" let-item="item" let-index="index">
|
||||
<button cButton color="danger" size="sm" [cTooltip]="'Delete Member'" (click)="remove_member(item)"><i
|
||||
class="fa-regular fa-trash-can"></i></button>
|
||||
<button *ngIf="SelectedCloner['direction']=='oneway'" cButton color="success" size="sm" style="margin-left: 5px;" [cTooltip]="'Set as Master'" (click)="set_master(item.id)"><i class="fa-regular fa-star"></i></button>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
</gui-grid>
|
||||
<hr />
|
||||
<button cButton color="primary" (click)="show_new_member_form()">+ Add new Members</button>
|
||||
</c-modal-body>
|
||||
<c-modal-footer>
|
||||
<button *ngIf="SelectedCloner['action']=='add'" (click)="submit('add')" cButton color="primary">Add</button>
|
||||
<button *ngIf="SelectedCloner['action']=='edit'" (click)="submit('edit')" cButton color="primary">save</button>
|
||||
<button [cModalToggle]="EditClonerModal.id" cButton color="secondary">
|
||||
Close
|
||||
</button>
|
||||
</c-modal-footer>
|
||||
</c-modal>
|
||||
|
||||
|
||||
<c-modal #NewMemberModal backdrop="static" size="lg" [(visible)]="NewMemberModalVisible" id="NewMemberModal">
|
||||
<c-modal-header>
|
||||
<h5 cModalTitle>Editing Group </h5>
|
||||
<button (click)="NewMemberModalVisible=!NewMemberModalVisible" cButtonClose></button>
|
||||
</c-modal-header>
|
||||
<c-modal-body>
|
||||
<c-input-group class="mb-3">
|
||||
<h5>Group Members :</h5>
|
||||
<gui-grid [autoResizeWidth]="true" *ngIf="NewMemberModalVisible" [searching]="searching"
|
||||
[source]="availbleMembers" [columnMenu]="columnMenu" [sorting]="sorting" [infoPanel]="infoPanel"
|
||||
[rowSelection]="rowSelection" (selectedRows)="onSelectedRowsNewMembers($event)" [autoResizeWidth]=true
|
||||
[paging]="paging">
|
||||
<gui-grid-column header="Member Name" field="name">
|
||||
<ng-template let-value="item.name" let-item="item" let-index="index">
|
||||
{{value}} </ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column *ngIf="SelectedCloner['pair_type']=='devices'" header="IP Address" field="ip">
|
||||
<ng-template let-value="item.ip" let-item="item" let-index="index">
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column *ngIf="SelectedCloner['pair_type']=='devices'" header="MAC Address" field="mac">
|
||||
<ng-template let-value="item.mac" let-item="item" let-index="index">
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
</gui-grid>
|
||||
<br />
|
||||
</c-input-group>
|
||||
<hr />
|
||||
</c-modal-body>
|
||||
|
||||
<c-modal-footer>
|
||||
<button *ngIf="NewMemberRows.length!= 0" (click)="add_new_members()" cButton color="primary">Add {{
|
||||
NewMemberRows.length }}</button>
|
||||
<button (click)="NewMemberModalVisible=!NewMemberModalVisible" cButton color="secondary">
|
||||
Close
|
||||
</button>
|
||||
</c-modal-footer>
|
||||
</c-modal>
|
||||
|
||||
|
||||
<c-modal #DeleteConfirmModal backdrop="static" [(visible)]="DeleteConfirmModalVisible" id="DeleteConfirmModal">
|
||||
<c-modal-header>
|
||||
<h5 cModalTitle>Confirm delete {{ SelectedCloner['name'] }}</h5>
|
||||
<button [cModalToggle]="DeleteConfirmModal.id" cButtonClose></button>
|
||||
</c-modal-header>
|
||||
<c-modal-body>
|
||||
Are you sure that You want to delete following task ?
|
||||
<br />
|
||||
<br />
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td><b>Taks name : </b></td>
|
||||
<td>{{ SelectedCloner['name'] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Description : </b></td>
|
||||
<td>{{ SelectedCloner['description'] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Cron exec : </b></td>
|
||||
<td>{{ SelectedCloner['desc_cron'] }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</c-modal-body>
|
||||
<c-modal-footer>
|
||||
<button (click)="confirm_delete('',true)" cButton color="danger">
|
||||
Yes,Delete!
|
||||
</button>
|
||||
<button [cModalToggle]="DeleteConfirmModal.id" cButton color="info">
|
||||
Close
|
||||
</button>
|
||||
</c-modal-footer>
|
||||
</c-modal>
|
||||
|
||||
<c-toaster position="fixed" placement="top-end"></c-toaster>
|
458
src/app/views/cloner/cloner.component.ts
Normal file
458
src/app/views/cloner/cloner.component.ts
Normal file
|
@ -0,0 +1,458 @@
|
|||
import { Component, OnInit, OnDestroy, ViewChildren ,QueryList } from "@angular/core";
|
||||
import { dataProvider } from "../../providers/mikrowizard/data";
|
||||
import { Router } from "@angular/router";
|
||||
import { loginChecker } from "../../providers/login_checker";
|
||||
import {
|
||||
GuiSelectedRow,
|
||||
GuiSearching,
|
||||
GuiInfoPanel,
|
||||
GuiColumn,
|
||||
GuiColumnMenu,
|
||||
GuiPaging,
|
||||
GuiPagingDisplay,
|
||||
GuiRowSelectionMode,
|
||||
GuiRowSelection,
|
||||
GuiRowSelectionType,
|
||||
} from "@generic-ui/ngx-grid";
|
||||
import { NgxSuperSelectOptions } from "ngx-super-select";
|
||||
import { _getFocusedElementPierceShadowDom } from "@angular/cdk/platform";
|
||||
|
||||
|
||||
import { ToasterComponent } from "@coreui/angular";
|
||||
import { AppToastComponent } from "../toast-simple/toast.component";
|
||||
|
||||
@Component({
|
||||
templateUrl: "cloner.component.html",
|
||||
styleUrls: ["cloner.scss"],
|
||||
|
||||
})
|
||||
export class ClonerComponent implements OnInit {
|
||||
public uid: number;
|
||||
public uname: string;
|
||||
public ispro: boolean = false;
|
||||
|
||||
constructor(
|
||||
private data_provider: dataProvider,
|
||||
private router: Router,
|
||||
private login_checker: loginChecker
|
||||
) {
|
||||
var _self = this;
|
||||
if (!this.login_checker.isLoggedIn()) {
|
||||
setTimeout(function () {
|
||||
_self.router.navigate(["login"]);
|
||||
}, 100);
|
||||
}
|
||||
this.data_provider.getSessionInfo().then((res) => {
|
||||
_self.uid = res.uid;
|
||||
_self.uname = res.name;
|
||||
_self.ispro = res['ISPRO']
|
||||
const userId = _self.uid;
|
||||
|
||||
if (res.role != "admin") {
|
||||
setTimeout(function () {
|
||||
_self.router.navigate(["/user/dashboard"]);
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
//get datagrid data
|
||||
function isNotEmpty(value: any): boolean {
|
||||
return value !== undefined && value !== null && value !== "";
|
||||
}
|
||||
}
|
||||
@ViewChildren(ToasterComponent) viewChildren!: QueryList<ToasterComponent>;
|
||||
|
||||
public source: Array<any> = [];
|
||||
public columns: Array<GuiColumn> = [];
|
||||
public loading: boolean = true;
|
||||
public rows: any = [];
|
||||
public SelectedCloner: any = {};
|
||||
public SelectedClonerItems: any = "";
|
||||
public EditClonerModalVisible: boolean = false;
|
||||
public DeleteConfirmModalVisible: boolean = false;
|
||||
public Members: any = "";
|
||||
public SelectedMembers: any = [];
|
||||
public NewMemberModalVisible: boolean = false;
|
||||
public availbleMembers: any = [];
|
||||
public NewMemberRows: any = [];
|
||||
public SelectedNewMemberRows: any;
|
||||
public master: number = 0;
|
||||
public active_commands:any=[];
|
||||
public tabs:any=[
|
||||
{
|
||||
"name": "Network",
|
||||
"sections": [
|
||||
{
|
||||
"title": "IP Management",
|
||||
"commands": [
|
||||
"/ip address",
|
||||
"/ip cloud",
|
||||
"/ip dhcp-server",
|
||||
"/ip dns",
|
||||
"/ip pool",
|
||||
"/ip route",
|
||||
"/ip vrf"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "IP Services",
|
||||
"commands": [
|
||||
"/ip service"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Security",
|
||||
"sections": [
|
||||
{
|
||||
"title": "Firewall",
|
||||
"commands": [
|
||||
"/ip firewall address-list",
|
||||
"/ip firewall connection",
|
||||
"/ip firewall layer7-protocol",
|
||||
"/ip firewall nat",
|
||||
"/ip firewall service-port",
|
||||
"/ip firewall calea",
|
||||
"/ip firewall filter",
|
||||
"/ip firewall mangle",
|
||||
"/ip firewall raw"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "IPSec",
|
||||
"commands": [
|
||||
"/ip ipsec identity",
|
||||
"/ip ipsec key",
|
||||
"/ip ipsec peer",
|
||||
"/ip ipsec profile",
|
||||
"/ip ipsec settings",
|
||||
"/ip ipsec statistics",
|
||||
"/ip ipsec proposal",
|
||||
"/ip ipsec policy",
|
||||
"/ip ipsec mode-config",
|
||||
"/ip ipsec active-peers",
|
||||
"/ip ipsec installed-sa"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "System",
|
||||
"sections": [
|
||||
{
|
||||
"title": "Scripts & Scheduling",
|
||||
"commands": [
|
||||
"/system script",
|
||||
"/system scheduler"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Time Management",
|
||||
"commands": [
|
||||
"/system ntp client servers",
|
||||
"/system ntp client",
|
||||
"/system ntp server",
|
||||
"/system clock"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "RADIUS",
|
||||
"commands": [
|
||||
"/radius"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "MPLS",
|
||||
"sections": [
|
||||
{
|
||||
"title": "MPLS Configuration",
|
||||
"commands": [
|
||||
"/mpls forwarding-table",
|
||||
"/mpls interface",
|
||||
"/mpls ldp",
|
||||
"/mpls settings",
|
||||
"/mpls traffic-eng"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "VPLS",
|
||||
"commands": [
|
||||
"/interface vpls"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
public sorting = {
|
||||
enabled: true,
|
||||
multiSorting: true,
|
||||
};
|
||||
searching: GuiSearching = {
|
||||
enabled: true,
|
||||
placeholder: "Search Devices",
|
||||
};
|
||||
|
||||
options: Partial<NgxSuperSelectOptions> = {
|
||||
selectionMode: "single",
|
||||
actionsEnabled: false,
|
||||
displayExpr: "name",
|
||||
valueExpr: "id",
|
||||
placeholder: "Snippet",
|
||||
searchEnabled: true,
|
||||
enableDarkMode: false,
|
||||
};
|
||||
|
||||
public paging: GuiPaging = {
|
||||
enabled: true,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
pageSizes: [5, 10, 25, 50],
|
||||
display: GuiPagingDisplay.ADVANCED,
|
||||
};
|
||||
|
||||
public columnMenu: GuiColumnMenu = {
|
||||
enabled: true,
|
||||
sort: true,
|
||||
columnsManager: true,
|
||||
};
|
||||
|
||||
toasterForm = {
|
||||
autohide: true,
|
||||
delay: 3000,
|
||||
position: "fixed",
|
||||
fade: true,
|
||||
closeButton: true,
|
||||
};
|
||||
|
||||
public infoPanel: GuiInfoPanel = {
|
||||
enabled: true,
|
||||
infoDialog: false,
|
||||
columnsManager: true,
|
||||
schemaManager: true,
|
||||
};
|
||||
|
||||
public rowSelection: boolean | GuiRowSelection = {
|
||||
enabled: true,
|
||||
type: GuiRowSelectionType.CHECKBOX,
|
||||
mode: GuiRowSelectionMode.MULTIPLE,
|
||||
};
|
||||
activate_command(command:string){
|
||||
// add to active_commands if it not added before
|
||||
if(!this.active_commands.includes(command)){
|
||||
this.active_commands.push(command);
|
||||
}
|
||||
else{
|
||||
this.active_commands=this.active_commands.filter((x:any)=>x!=command);
|
||||
}
|
||||
}
|
||||
show_toast(title: string, body: string, color: string) {
|
||||
const { ...props } = { ...this.toasterForm, color, title, body };
|
||||
const componentRef = this.viewChildren.first.addToast(
|
||||
AppToastComponent,
|
||||
props,
|
||||
{}
|
||||
);
|
||||
componentRef.instance["closeButton"] = props.closeButton;
|
||||
}
|
||||
show_new_member_form() {
|
||||
this.NewMemberModalVisible = true;
|
||||
var _self = this;
|
||||
_self.availbleMembers = [];
|
||||
this.SelectedNewMemberRows = [];
|
||||
this.NewMemberRows = [];
|
||||
|
||||
var data = {
|
||||
group_id: false,
|
||||
search: false,
|
||||
page: false,
|
||||
size: 10000,
|
||||
};
|
||||
|
||||
if (this.SelectedCloner["pair_type"] == "devices")
|
||||
_self.data_provider.get_dev_list(data).then((res) => {
|
||||
_self.availbleMembers = res.filter(
|
||||
(x: any) => !_self.SelectedClonerItems.includes(x.id)
|
||||
);
|
||||
_self.NewMemberModalVisible = true;
|
||||
});
|
||||
else
|
||||
_self.data_provider.get_devgroup_list().then((res) => {
|
||||
_self.availbleMembers = res.filter(
|
||||
(x: any) => !_self.SelectedClonerItems.includes(x.id)
|
||||
);
|
||||
_self.NewMemberModalVisible = true;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.initGridTable();
|
||||
}
|
||||
|
||||
submit(action: string) {
|
||||
var _self = this;
|
||||
if(_self.master==0 && _self.SelectedCloner['direction']=='oneway'){
|
||||
_self.show_toast(
|
||||
"Error",
|
||||
"Master device is not selected",
|
||||
"danger"
|
||||
);
|
||||
return;
|
||||
}
|
||||
if(_self.SelectedCloner['direction']=='twoway' && _self.SelectedCloner['pair_type']=='groups'){
|
||||
_self.show_toast(
|
||||
"Error",
|
||||
"Using Groups is only allowed with Master Mode",
|
||||
"danger"
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (_self.master!=0 && _self.SelectedCloner['direction']=='oneway'){
|
||||
_self.SelectedCloner["masterid"]=_self.master;
|
||||
}
|
||||
_self.SelectedCloner["active_commands"]=_self.active_commands;
|
||||
if (action == "add") {
|
||||
this.data_provider
|
||||
.Add_cloner(_self.SelectedCloner, _self.SelectedClonerItems)
|
||||
.then((res) => {
|
||||
_self.initGridTable();
|
||||
});
|
||||
} else {
|
||||
this.data_provider
|
||||
.Edit_cloner(_self.SelectedCloner, _self.SelectedClonerItems)
|
||||
.then((res) => {
|
||||
_self.initGridTable();
|
||||
});
|
||||
}
|
||||
this.EditClonerModalVisible = false;
|
||||
}
|
||||
|
||||
onSelectedRowsNewMembers(rows: Array<GuiSelectedRow>): void {
|
||||
this.NewMemberRows = rows;
|
||||
this.SelectedNewMemberRows = rows.map((m: GuiSelectedRow) => m.source);
|
||||
}
|
||||
|
||||
add_new_members() {
|
||||
var _self = this;
|
||||
_self.SelectedMembers = [
|
||||
...new Set(_self.SelectedMembers.concat(_self.SelectedNewMemberRows)),
|
||||
];
|
||||
|
||||
_self.SelectedClonerItems = _self.SelectedMembers.map((x: any) => {
|
||||
return x.id;
|
||||
});
|
||||
|
||||
this.NewMemberModalVisible = false;
|
||||
}
|
||||
|
||||
set_master(id:number){
|
||||
var _self=this;
|
||||
this.master=id;
|
||||
// sort SelectedMembers and put master on top
|
||||
this.SelectedMembers=
|
||||
[
|
||||
...new Set(
|
||||
this.SelectedMembers.sort((a:any,b:any)=>{
|
||||
if(a.id==_self.master) return -1;
|
||||
if(b.id==_self.master) return 1;
|
||||
return 0;
|
||||
})
|
||||
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
editAddCloner(item: any, action: string) {
|
||||
if (action == "showadd") {
|
||||
this.SelectedCloner = {
|
||||
id: 0,
|
||||
name: "",
|
||||
description: "",
|
||||
pair_type: "devices",
|
||||
live_mode: false,
|
||||
schedule: false,
|
||||
cron: "",
|
||||
desc_cron: "",
|
||||
direction: "oneway",
|
||||
members: "",
|
||||
action: "add",
|
||||
};
|
||||
this.SelectedMembers = [];
|
||||
this.SelectedClonerItems = [];
|
||||
this.EditClonerModalVisible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var _self = this;
|
||||
this.SelectedCloner = { ...item };
|
||||
this.active_commands=item['active_commands']?JSON.parse(item['active_commands']):[];
|
||||
if (action != "select_change" ) {
|
||||
this.SelectedCloner["action"] = "edit";
|
||||
this.data_provider.get_cloner_members(_self.SelectedCloner.id).then((res) => {
|
||||
_self.SelectedMembers = res;
|
||||
if(_self.SelectedCloner["master"] && _self.SelectedCloner['direction']=='oneway'){
|
||||
_self.set_master(_self.SelectedCloner["master"]);
|
||||
}
|
||||
_self.EditClonerModalVisible = true;
|
||||
_self.SelectedClonerItems = res.map((x: any) => {
|
||||
return x.id;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
_self.SelectedMembers = [];
|
||||
this.SelectedClonerItems = [];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
in_active_commands(command:string){
|
||||
return this.active_commands.includes(command);
|
||||
}
|
||||
remove_member(item: any) {
|
||||
var _self = this;
|
||||
_self.SelectedMembers = _self.SelectedMembers.filter(
|
||||
(x: any) => x.id != item.id
|
||||
);
|
||||
_self.SelectedClonerItems = _self.SelectedMembers.map((x: any) => {
|
||||
return x.id;
|
||||
});
|
||||
}
|
||||
|
||||
get_member_by_id(id: string) {
|
||||
return this.Members.find((x: any) => x.id == id);
|
||||
}
|
||||
|
||||
confirm_delete(item: any = "", del: boolean = false) {
|
||||
if (!del) {
|
||||
this.SelectedCloner = { ...item };
|
||||
this.DeleteConfirmModalVisible = true;
|
||||
} else {
|
||||
var _self = this;
|
||||
this.data_provider.Delete_cloner(_self.SelectedCloner["id"]).then((res) => {
|
||||
_self.initGridTable();
|
||||
_self.DeleteConfirmModalVisible = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
form_changed() {
|
||||
this.editAddCloner(this.SelectedCloner, "select_change");
|
||||
}
|
||||
|
||||
logger(item: any) {
|
||||
console.dir(item);
|
||||
}
|
||||
|
||||
initGridTable(): void {
|
||||
var _self = this;
|
||||
this.data_provider.get_cloner_list().then((res) => {
|
||||
_self.source = res.map((x: any) => {
|
||||
return x;
|
||||
});
|
||||
_self.loading = false;
|
||||
});
|
||||
}
|
||||
}
|
44
src/app/views/cloner/cloner.module.ts
Normal file
44
src/app/views/cloner/cloner.module.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
import { NgModule } from "@angular/core";
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { FormsModule,ReactiveFormsModule } from "@angular/forms";
|
||||
|
||||
import {
|
||||
ButtonModule,
|
||||
CardModule,
|
||||
FormModule,
|
||||
GridModule,
|
||||
ModalModule,
|
||||
ButtonGroupModule,
|
||||
ToastModule,
|
||||
TooltipModule,
|
||||
NavModule,
|
||||
TabsModule,
|
||||
} from "@coreui/angular";
|
||||
import { ClonerRoutingModule } from "./cloner-routing.module";
|
||||
import { ClonerComponent } from "./cloner.component";
|
||||
import { GuiGridModule } from "@generic-ui/ngx-grid";
|
||||
|
||||
import { NgxSuperSelectModule} from "ngx-super-select";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
ClonerRoutingModule,
|
||||
CardModule,
|
||||
CommonModule,
|
||||
GridModule,
|
||||
FormModule,
|
||||
ButtonModule,
|
||||
ButtonGroupModule,
|
||||
GuiGridModule,
|
||||
ModalModule,
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
NgxSuperSelectModule,
|
||||
ToastModule,
|
||||
TooltipModule,
|
||||
NavModule,
|
||||
TabsModule,
|
||||
],
|
||||
declarations: [ClonerComponent],
|
||||
})
|
||||
export class ClonerModule {}
|
133
src/app/views/cloner/cloner.scss
Normal file
133
src/app/views/cloner/cloner.scss
Normal file
|
@ -0,0 +1,133 @@
|
|||
|
||||
.nav-underline {
|
||||
border-bottom: 2px solid var(--cui-nav-underline-border-color, #c4c9d0)
|
||||
}
|
||||
|
||||
.nav-underline .nav-item {
|
||||
margin-bottom: -2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav-underline .nav-link {
|
||||
color: var(--cui-nav-underline-link-color, #768192);
|
||||
border-style: none none solid!important;
|
||||
border-width:2px;
|
||||
position:relative;
|
||||
bottom:-1px;
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
|
||||
.nav-underline .nav-link:hover,.nav-underline .nav-link:focus {
|
||||
border-color: var(--cui-nav-underline-link-active-border-color, #321fdb)
|
||||
}
|
||||
|
||||
.nav-underline .nav-link.active,.nav-underline .show>.nav-link {
|
||||
color: var(--cui-nav-underline-link-active-color, #321fdb);
|
||||
background: transparent;
|
||||
border-color: var(--cui-nav-underline-link-active-border-color, #321fdb)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.custom-control-label::before, .custom-control-label::after {
|
||||
top: 0.1rem;
|
||||
width: 2rem;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.custom-control-input:checked ~ .custom-control-label::before {
|
||||
color: #fff;
|
||||
border-color: #3498db;
|
||||
background-color: #3498db;
|
||||
}
|
||||
|
||||
.custom-control-input:focus ~ .custom-control-label::before {
|
||||
box-shadow: 0 0 0 0.2rem rgba(52, 152, 219, 0.25);
|
||||
}
|
||||
|
||||
h5.cloner-sections {
|
||||
color: #3498db;
|
||||
margin-bottom: 5px;
|
||||
font-size: 15px;
|
||||
font-weight: 600; }
|
||||
|
||||
.nav-link {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
color: #3498db;
|
||||
border-bottom: 2px solid #3498db;
|
||||
}
|
||||
|
||||
.command-sections c-card-body{
|
||||
display: flex!important;
|
||||
justify-content: space-between!important;
|
||||
padding:5px!important;
|
||||
align-items: center!important;
|
||||
justify-content: space-between!important;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.command-sections c-card-body h6{
|
||||
margin: 0!important;
|
||||
font-size: 12px!important;
|
||||
color: var(--primary-color);
|
||||
white-space: nowrap!important;
|
||||
overflow: hidden!important;
|
||||
text-overflow: ellipsis!important;
|
||||
width: 80%!important;
|
||||
font-weight: bold;
|
||||
|
||||
}
|
||||
.command-sections c-card{
|
||||
border: 1px solid #e0e0e0!important;;
|
||||
border-radius: 4px!important;;
|
||||
}
|
||||
|
||||
/* Checkbox Styling */
|
||||
.custom-switch {
|
||||
position: relative;
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.custom-switch input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.custom-control-label {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 20px;
|
||||
background: #ccc;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.custom-control-label::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 2px;
|
||||
top: 2px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.custom-switch input:checked + .custom-control-label {
|
||||
background: #3498db;
|
||||
}
|
||||
|
||||
.custom-switch input:checked + .custom-control-label::after {
|
||||
left: calc(100% - 18px);
|
||||
}
|
|
@ -139,9 +139,8 @@
|
|||
<span *ngIf="copy_msg" style="color: #fff!important;" class="badge text-bg-success"><i
|
||||
class="fa-solid fa-check"></i>Copy</span>
|
||||
</div>
|
||||
<c-badge color="danger">Not Registred</c-badge>
|
||||
<a class="mx-1" target="_blank" href="http://MikroWizard.com">Learn how to register and get automatic
|
||||
updates!</a>
|
||||
<c-badge *ngIf="stats['username']" color="danger">Not Registred</c-badge>
|
||||
<c-badge *ngIf="!stats['username']" color="danger">License Validation failed</c-badge>
|
||||
</div>
|
||||
<div *ngIf="stats['license']=='connection_error'" class="my-1">
|
||||
<div style="display: inline-block;margin-right: 5px;">
|
||||
|
@ -153,7 +152,7 @@
|
|||
</div>
|
||||
<c-badge class="mx-1" color="danger">Unable connect to server/Check server internet connection</c-badge>
|
||||
</div>
|
||||
<div *ngIf="stats['license']!='connection_error'" class="my-1">
|
||||
<div *ngIf="stats['license']!='connection_error' && stats['license']" class="my-1">
|
||||
<div style="display: inline-block;margin-right: 5px;">
|
||||
<code style="padding: 0!important;">Serial:</code> <small
|
||||
style="background-color: #ccc;padding: 5px;border-radius: 5px;cursor: pointer;" (click)="copy_this()"
|
||||
|
@ -190,6 +189,9 @@
|
|||
class="fa-regular fa-hand-pointer fa-beat-fade"></i> Update availble </button>
|
||||
</span>
|
||||
</div>
|
||||
<p *ngIf="!stats['license'] && !stats['username']" style="color: rgb(0, 119, 255);"><strong>License User name is not set in settings <a style="color: rgb(0, 119, 255);" target="_blank" href="https://mikrowizard.com/docs/register-serial-number/" >read more!</a></strong></p>
|
||||
<p *ngIf="!stats['license'] && stats['username']" style="color: rgb(0, 119, 255);"><strong>Serial number not submited<a style="color: rgb(0, 119, 255);" target="_blank" href="https://mikrowizard.com/docs/register-serial-number/" >read more!</a></strong> </p>
|
||||
|
||||
<!-- <div *ngIf="stats['update_mode']!='auto'" class="my-1">
|
||||
<button cButton color="warning" *ngIf="stats['update_available']" size="sm" style="font-size: 1em;"><i class="fa-regular fa-hand-pointer fa-beat-fade"></i> Update <strong>Mikroman</strong> and reload server</button>
|
||||
<button cButton color="warning" *ngIf="stats['front_update_available']" size="sm" style="font-size: 1em;margin-left: 5px;"><i class="fa-regular fa-hand-pointer fa-beat-fade"></i> Update <strong>MikroFront</strong> and reload Page</button>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { ActiveUsersComponent } from './active-users.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: ActiveUsersComponent,
|
||||
data: {
|
||||
title: 'Widgets'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class ActiveUsersRoutingModule {
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<c-row>
|
||||
<c-col xs>
|
||||
<c-card class="mb-1" style="border: none">
|
||||
<c-card-header>
|
||||
<h6>Active Users</h6>
|
||||
</c-card-header>
|
||||
<c-card-body style="height: 275px;overflow: auto;">
|
||||
<h5 style="display: inline-block;">Current active users : <c-badge color="success">
|
||||
{{active_users.length}}</c-badge></h5>
|
||||
<table cTable small [responsive]="true">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Address</th>
|
||||
<th scope="col">Att</th>
|
||||
<th scope="col">Group</th>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Via</th>
|
||||
<th scope="col">kill</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of active_users; index as i">
|
||||
<td>{{i+1}}</td>
|
||||
<td>{{item['address']}}</td>
|
||||
<td>{{item['when']}}</td>
|
||||
<td>{{item['group']}}</td>
|
||||
<td>{{item['name']}}</td>
|
||||
<td>{{item['via']}}</td>
|
||||
<td><button cButton (click)="killsession(item)" style="padding: 0;" size="sm" color="danger" variant="ghost"><i class="fa-solid fa-skull-crossbones"></i>
|
||||
kill</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-col>
|
||||
</c-row>
|
|
@ -0,0 +1,27 @@
|
|||
import { AfterContentInit, ChangeDetectionStrategy, ChangeDetectorRef, Component,Input } from '@angular/core';
|
||||
import { dataProvider } from "../../../providers/mikrowizard/data";
|
||||
@Component({
|
||||
selector: 'app-active-users',
|
||||
templateUrl: './active-users.component.html',
|
||||
styleUrls: ['./active-users.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.Default
|
||||
})
|
||||
export class ActiveUsersComponent implements AfterContentInit {
|
||||
@Input() active_users: any;
|
||||
@Input() devid: number;
|
||||
|
||||
constructor(
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private data_provider: dataProvider,
|
||||
) {}
|
||||
killsession(item:any){
|
||||
console.log(item);
|
||||
this.data_provider.killSession(this.devid,item).then((res) => {
|
||||
this.active_users = res;
|
||||
});
|
||||
}
|
||||
ngAfterContentInit(): void {
|
||||
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import {
|
||||
ButtonGroupModule,
|
||||
ButtonModule,
|
||||
CardModule,
|
||||
FormModule,
|
||||
GridModule,
|
||||
ProgressModule,
|
||||
NavbarModule,
|
||||
ModalModule,
|
||||
TableModule,
|
||||
UtilitiesModule,
|
||||
BadgeModule,
|
||||
TooltipModule,
|
||||
} from '@coreui/angular';
|
||||
|
||||
// import { WidgetsRoutingModule } from './widgets-routing.module';
|
||||
import { ActiveUsersComponent } from './active-users.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
ActiveUsersComponent,
|
||||
],
|
||||
imports: [
|
||||
CardModule,
|
||||
CommonModule,
|
||||
GridModule,
|
||||
ProgressModule,
|
||||
FormModule,
|
||||
ButtonModule,
|
||||
ButtonGroupModule,
|
||||
NavbarModule,
|
||||
ModalModule,
|
||||
TableModule,
|
||||
UtilitiesModule,
|
||||
BadgeModule,
|
||||
TooltipModule,
|
||||
],
|
||||
exports: [
|
||||
ActiveUsersComponent,
|
||||
]
|
||||
})
|
||||
export class ActiveUsersModule {
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { DeviceInfoComponent } from './device-info.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: DeviceInfoComponent,
|
||||
data: {
|
||||
title: 'Widgets'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class DeviceInfoRoutingModule {
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
|
||||
<c-row>
|
||||
<c-col xs>
|
||||
<c-card class="mb-1" style="border: none;">
|
||||
<c-card-body style="padding: 0;">
|
||||
<c-row style="flex-direction:row">
|
||||
<c-input-group class="mr-0 ml-0 mb-1" *ngIf="!devdata['online']"
|
||||
style="padding-right:unset;width: auto;flex: 1 1 auto;flex-flow: nowrap;flex: unset;">
|
||||
<span
|
||||
style="padding: 0.175rem 0.35rem;background-color:#ed4646;text-transform: capitalize;color:#fff;font-size:0.75rem;border: none;font-weight: bold;"
|
||||
cInputGroupText>Device offline or not accessible for MikroWizard</span>
|
||||
</c-input-group>
|
||||
<c-input-group class="mr-0 ml-0 mb-1" *ngIf="devdata['update_availble']"
|
||||
style="padding-right:unset;width: auto;flex: 1 1 auto;flex-flow: nowrap;flex: unset;">
|
||||
<span
|
||||
style="padding: 0.175rem 0.35rem;background-color:#ed4646;text-transform: capitalize;color:#fff;font-size:0.75rem;border: none;"
|
||||
cInputGroupText>Firmware Upgrade Detected</span>
|
||||
</c-input-group>
|
||||
<c-input-group class="mr-0 ml-0 mb-1" *ngIf="devdata['upgrade_availble']"
|
||||
style="padding-right:unset;width: auto;flex: 1 1 auto;flex-flow: nowrap;flex: unset;">
|
||||
<span
|
||||
style="padding: 0.175rem 0.35rem;background-color:#edd446;text-transform: capitalize;color:#fff;font-size:0.75rem;border: none;"
|
||||
cInputGroupText>Firmware Upgrade Detected</span>
|
||||
</c-input-group>
|
||||
<ng-container *ngFor="let item of devdata | keyvalue; index as i">
|
||||
<c-input-group *ngIf="checkitem(item)" class="mr-0 ml-0 mb-1"
|
||||
style="padding-right:unset;width: auto;flex: 1 1 auto;flex-flow: nowrap;flex: unset;">
|
||||
<span
|
||||
style="padding: 0.175rem 0.35rem;background-color:#4f5d73;text-transform: capitalize;color:#fff;font-size:0.7rem"
|
||||
cInputGroupText>{{item.key}}</span>
|
||||
<span _ngcontent-ng-c666080582="" cinputgrouptext=""
|
||||
style="padding: 0.175rem 0.35rem;color: rgba(44, 56, 74, 0.95);font-size: 0.7rem;background-color: #d8dbe0;border-color: #b1b7c1;"
|
||||
class="input-group-text">{{ item.value }}</span>
|
||||
</c-input-group>
|
||||
</ng-container>
|
||||
</c-row>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-col>
|
||||
</c-row>
|
|
@ -0,0 +1,25 @@
|
|||
import { AfterContentInit, ChangeDetectionStrategy, ChangeDetectorRef, Component,Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-device-info',
|
||||
templateUrl: './device-info.component.html',
|
||||
styleUrls: ['./device-info.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.Default
|
||||
})
|
||||
export class DeviceInfoComponent implements AfterContentInit {
|
||||
@Input() devdata: any;
|
||||
constructor(
|
||||
private changeDetectorRef: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
checkitem(item: any) {
|
||||
if (item.value && !item.key.match("sensors|id|_availble|interfaces|active_users|ping|status|created|online|syslog_configured|port")) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ngAfterContentInit(): void {
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import {
|
||||
ButtonGroupModule,
|
||||
ButtonModule,
|
||||
CardModule,
|
||||
FormModule,
|
||||
GridModule,
|
||||
ProgressModule,
|
||||
NavbarModule,
|
||||
AlertModule,
|
||||
ModalModule,
|
||||
TableModule,
|
||||
UtilitiesModule,
|
||||
BadgeModule,
|
||||
NavModule,
|
||||
TabsModule,
|
||||
} from '@coreui/angular';
|
||||
import { IconModule } from '@coreui/icons-angular';
|
||||
import { ChartjsModule } from '@coreui/angular-chartjs';
|
||||
|
||||
import { WidgetsModule } from "../../widgets/widgets.module";
|
||||
|
||||
// import { WidgetsRoutingModule } from './widgets-routing.module';
|
||||
import { DeviceInfoComponent } from './device-info.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
DeviceInfoComponent,
|
||||
],
|
||||
imports: [
|
||||
CardModule,
|
||||
AlertModule,
|
||||
CommonModule,
|
||||
GridModule,
|
||||
ProgressModule,
|
||||
FormModule,
|
||||
ButtonModule,
|
||||
ButtonGroupModule,
|
||||
ChartjsModule,
|
||||
WidgetsModule,
|
||||
NavbarModule,
|
||||
ModalModule,
|
||||
TableModule,
|
||||
UtilitiesModule,
|
||||
BadgeModule,
|
||||
NavModule,
|
||||
TabsModule,
|
||||
],
|
||||
exports: [
|
||||
DeviceInfoComponent,
|
||||
]
|
||||
})
|
||||
export class DeviceInfoModule {
|
||||
}
|
|
@ -1,234 +1,321 @@
|
|||
<c-alert style="margin-bottom: 5px;margin-top: 5px;" *ngIf="!loading && devdata['update_availble']"
|
||||
(click)="logger(devdata['sensors'])" color="warning">Firmware
|
||||
Update availble For This Device!</c-alert>
|
||||
<c-alert style="margin-bottom: 5px;margin-top: 5px;" *ngIf="!loading && devdata['upgrade_availble']"
|
||||
(click)="logger(devdata['upgrade_availble'])" color="info">Device is updated but needs to upgrade firmware!</c-alert>
|
||||
|
||||
<c-row *ngIf="!loading">
|
||||
<c-col xs>
|
||||
<c-card class="mb-1">
|
||||
<c-card-header>
|
||||
<c-row>
|
||||
<c-col md="6" sm="12" style="display: flex;flex-direction: row;align-items: center;">
|
||||
<h4 style="height: 100%;line-height: 170%;margin: 0;">{{devdata['name'] }}<small style="font-size: 50%;"> ( {{devdata['ip'] }} )</small></h4>
|
||||
</c-col>
|
||||
<c-col md="6" sm="12" class="justify-content-end" style="display: flex;flex-direction: row;align-items: center;">
|
||||
<c-button-group size="sm" aria-label="Upate interval" role="group">
|
||||
<button cButton color="primary" size="sm" (click)="delta='5m';updateData()" [active]="delta=='5m'">5 minute</button>
|
||||
<button cButton color="primary" size="sm" (click)="delta='1h';updateData()" [active]="delta=='1h'">Hourly</button>
|
||||
<button cButton color="primary" size="sm" (click)="delta='daily';updateData()"
|
||||
[active]="delta=='daily'">Daily</button>
|
||||
<button cButton color="primary" (click)="delta='live';updateData()" [active]="delta=='live'">Live</button>
|
||||
</c-button-group>
|
||||
<c-form-check (click)="switch_total()" sizing="xl" class="mx-2" switch>
|
||||
<input cFormCheckInput [checked]="total_type=='bps'" type="checkbox" />
|
||||
<label cFormCheckLabel>
|
||||
<span *ngIf="total_type=='bps'">Total bbs</span>
|
||||
<span *ngIf="total_type!='bps'">Total pps</span>
|
||||
</label>
|
||||
</c-form-check>
|
||||
</c-col>
|
||||
</c-row>
|
||||
|
||||
</c-card-header>
|
||||
|
||||
<c-card-body>
|
||||
<app-widgets-dropdown *ngIf="!loading" [devicedata]=devsensors></app-widgets-dropdown>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-col>
|
||||
</c-row>
|
||||
|
||||
<c-row>
|
||||
<c-col xs>
|
||||
<c-card class="mb-1">
|
||||
<c-card-body>
|
||||
<c-row style="flex-direction:row">
|
||||
<ng-container *ngFor="let item of devdata | keyvalue; index as i">
|
||||
<c-input-group *ngIf="checkitem(item)" class="mr-0 ml-0 mb-1"
|
||||
style="padding-right:unset;width: auto;flex: 1 1 auto;flex-flow: nowrap;flex: unset;">
|
||||
<span
|
||||
style="padding: 0.175rem 0.35rem;background-color:#4f5d73;text-transform: capitalize;color:#fff;font-size:0.7rem"
|
||||
cInputGroupText>{{item.key}}</span>
|
||||
<span _ngcontent-ng-c666080582="" cinputgrouptext=""
|
||||
style="padding: 0.175rem 0.35rem;color: rgba(44, 56, 74, 0.95);font-size: 0.7rem;background-color: #d8dbe0;border-color: #b1b7c1;"
|
||||
class="input-group-text">{{ item.value }}</span>
|
||||
</c-input-group>
|
||||
</ng-container>
|
||||
</c-row>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-col>
|
||||
</c-row>
|
||||
<!-- Mikrotik interfaces table -->
|
||||
<c-row>
|
||||
<c-col xs>
|
||||
<c-card class="mb-1">
|
||||
<c-card-body>
|
||||
<c-row style="flex-direction:row">
|
||||
<gui-grid [source]="interfaces" [columnMenu]="columnMenu" [sorting]="sorting" [infoPanel]="infoPanel"
|
||||
[autoResizeWidth]=true>
|
||||
<gui-grid-column header="Name" field="name">
|
||||
<ng-template let-value="item.name" let-item="item" let-index="index">
|
||||
<c-card class="mb-1">
|
||||
<c-card-header>
|
||||
<c-row *ngIf="!loading">
|
||||
<c-col md="6" sm="12" style="display: flex; flex-direction: row; align-items: center">
|
||||
<h4 style="height: 100%; line-height: 170%; margin: 0">
|
||||
{{ devdata["name"] }}<small style="font-size: 50%"> ( {{ devdata["ip"] }} )</small>
|
||||
</h4>
|
||||
</c-col>
|
||||
<app-device-info [devdata]="devdata"> </app-device-info>
|
||||
</c-row>
|
||||
</c-card-header>
|
||||
</c-card>
|
||||
|
||||
{{value}} - {{item['default-name']}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<c-col xs style="border: 1px solid #ddd; border-radius: 4px; padding: 0;width: 100%;">
|
||||
<div class="nav nav-underline" style="background: #fff; border-bottom: 2px solid #2c3e50;width: 100%;">
|
||||
<div class="nav-item" (click)="actice_tab_index = 0">
|
||||
<a class="nav-link" [active]="true" [cTabContent]="tabContent" [tabPaneIdx]="0" style="display: inline-block;padding-right: 0;">Device Details</a>
|
||||
<a class="nav-link" style="display: inline-block;" *ngIf="actice_tab_index==0" (click)="reload_device()">
|
||||
<i class="fa-solid fa-arrows-rotate" style="color: #74C0FC;" *ngIf="!reloading"></i>
|
||||
<i class="fa-solid fa-arrows-rotate fa-spin" *ngIf="reloading" style="color: #74C0FC;"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item" [ngStyle]="{ display: !is_radio ? 'none' : 'block' }" (click)="actice_tab_index = 1">
|
||||
<a class="nav-link" [active]="false" [cTabContent]="tabContent" [tabPaneIdx]="1">Radio Info</a>
|
||||
</div>
|
||||
<div class="nav-item" [ngStyle]="{ display: !dhcp_server_available ? 'none' : 'block' }" (click)="actice_tab_index = 2">
|
||||
<a class="nav-link" [active]="false" [cTabContent]="tabContent" [tabPaneIdx]="2" style="display: inline-block;padding-right: 0;">DHCP server</a>
|
||||
<a class="nav-link" style="display: inline-block;" *ngIf="actice_tab_index==2" (click)="reload_dhcp_server()">
|
||||
<i class="fa-solid fa-arrows-rotate" style="color: #74C0FC;" *ngIf="!reloading"></i>
|
||||
<i class="fa-solid fa-arrows-rotate fa-spin" *ngIf="reloading" style="color: #74C0FC;"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="nav-item" (click)="actice_tab_index = 3">
|
||||
<a class="nav-link" [active]="false" (click)="show_dev_logs = true" [cTabContent]="tabContent"
|
||||
[tabPaneIdx]="3">Device Events</a>
|
||||
</div>
|
||||
<div class="nav-item" (click)="actice_tab_index = 4">
|
||||
<a class="nav-link" [active]="false" (click)="show_auth_logs = true" [cTabContent]="tabContent"
|
||||
[tabPaneIdx]="4">Authentication</a>
|
||||
</div>
|
||||
<div class="nav-item" (click)="actice_tab_index = 5">
|
||||
<a class="nav-link" [active]="false" (click)="show_acc_logs = true" [cTabContent]="tabContent"
|
||||
[tabPaneIdx]="5">Accounting</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<gui-grid-column header="MAC" field="mac-address">
|
||||
<ng-template let-value="item['mac-address']" let-item="item" let-index="index">
|
||||
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="rx" field="rx-byte">
|
||||
<ng-template let-value="item['rx-byte']" let-item="item" let-index="index">
|
||||
|
||||
<div>{{convert_bw_human(value,'rx')}}</div>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="tx" field="tx-byte">
|
||||
<ng-template let-value="item['tx-byte']" let-item="item" let-index="index">
|
||||
|
||||
{{convert_bw_human(value,'tx')}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="l2mtu" field="l2mtu">
|
||||
<ng-template let-value="item.l2mtu" let-item="item" let-index="index">
|
||||
|
||||
curr:{{value}}<br />
|
||||
max : {{item['max-l2mtu']}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="rx/s" field="rx-bits-per-second" [enabled]="false">
|
||||
<ng-template let-value="item['rx-bits-per-second']" let-item="item" let-index="index">
|
||||
|
||||
{{convert_bw_human(value,'rx')}}
|
||||
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="tx/s" field="tx-bits-per-second" [enabled]="false">
|
||||
<ng-template let-value="item['tx-bits-per-second']" let-item="item" let-index="index">
|
||||
|
||||
{{convert_bw_human(value,'tx')}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Created" field="created" [enabled]="false">
|
||||
<ng-template let-value="item.created" let-item="item.id" let-index="index">
|
||||
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Last Up" field="last-link-up-time">
|
||||
<ng-template let-value="item['last-link-up-time']" let-item="item" let-index="index">
|
||||
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Created" field="created" [enabled]="false">
|
||||
<ng-template let-value="item.created" let-item="item.id" let-index="index">
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Actions" field="action" width="60" align="center">
|
||||
<ng-template let-value="item.id" let-item="item" let-index="index">
|
||||
<button cButton color="info" size="sm" (click)="show_interface_rate(item['default-name'])"
|
||||
class="mx-1"><i class="fa-solid fa-chart-line"></i></button>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
</gui-grid>
|
||||
</c-row>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-col>
|
||||
</c-row>
|
||||
|
||||
<c-row *ngIf="is_radio && !radio_loading">
|
||||
<c-col xs md="12" *ngFor="let raddata of radio_devsensors | keyvalue">
|
||||
<c-card class="mb-1">
|
||||
<c-card-header><h6>Radio data</h6></c-card-header>
|
||||
<c-card-body>
|
||||
<h6>{{raddata.key}}</h6>
|
||||
<app-widgets-dropdown [devicedata]=raddata.value></app-widgets-dropdown>
|
||||
<c-tab-content class="command-sections" style="width: 100%;" #tabContent="cTabContent">
|
||||
<h5 *ngIf="loading" style="text-align: center;height: 100vh;line-height: 33vh;"><c-spinner color="info" variant="grow"></c-spinner>Loading...</h5>
|
||||
<c-tab-pane *ngIf="!loading">
|
||||
<c-card class="mb-1">
|
||||
<c-card-body>
|
||||
<c-row>
|
||||
<c-col md="12" sm="12" class="justify-content-end"
|
||||
style="display: flex; flex-direction: row; align-items: center;padding-bottom: 5px;">
|
||||
<c-button-group size="sm" aria-label="Upate interval" role="group">
|
||||
<button cButton color="primary" size="sm" (click)="delta = '5m'; updateData()"
|
||||
[active]="delta == '5m'">
|
||||
5 minute
|
||||
</button>
|
||||
<button cButton color="primary" size="sm" (click)="delta = '1h'; updateData()"
|
||||
[active]="delta == '1h'">
|
||||
Hourly
|
||||
</button>
|
||||
<button cButton color="primary" size="sm" (click)="delta = 'daily'; updateData()"
|
||||
[active]="delta == 'daily'">
|
||||
Daily
|
||||
</button>
|
||||
<button cButton color="primary" (click)="delta = 'live'; updateData()" [active]="delta == 'live'">
|
||||
Live
|
||||
</button>
|
||||
</c-button-group>
|
||||
<c-form-check (click)="switch_total()" sizing="xl" class="mx-2" switch>
|
||||
<input cFormCheckInput [checked]="total_type == 'bps'" type="checkbox" />
|
||||
<label cFormCheckLabel>
|
||||
<span *ngIf="total_type == 'bps'">Total bbs</span>
|
||||
<span *ngIf="total_type != 'bps'">Total pps</span>
|
||||
</label>
|
||||
</c-form-check>
|
||||
</c-col>
|
||||
</c-row>
|
||||
<app-widgets-dropdown *ngIf="!loading" [devicedata]="devsensors"></app-widgets-dropdown>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
<c-row>
|
||||
<c-col md="3">
|
||||
<table style="word-break: break-word" small stripedColumns cTable>
|
||||
<tbody>
|
||||
<ng-container *ngFor="let d of raddata.value['data'] | keyvalue ; let i=index">
|
||||
<tr *ngIf="i<objectlen(raddata.value['data'])/4">
|
||||
<th style="width: 20%;text-wrap: nowrap;">{{d.key}}</th>
|
||||
<td scope="row">{{d.value}}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</tbody>
|
||||
</table>
|
||||
<c-col sm="12" lg="6">
|
||||
<c-card class="mb-1">
|
||||
<c-card-body style="padding: 0;">
|
||||
<app-ping-stats [ping]="devdata['ping']"></app-ping-stats>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-col>
|
||||
<c-col md="3">
|
||||
<c-col sm="12" lg="6">
|
||||
<c-card class="mb-1">
|
||||
<c-card-body style="padding: 0;">
|
||||
<app-active-users [active_users]="devdata['active_users']" [devid]="devid"></app-active-users>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-col>
|
||||
</c-row>
|
||||
<!-- Mikrotik interfaces table -->
|
||||
<c-row class="interfaces">
|
||||
<c-col xs>
|
||||
<c-card class="mb-1">
|
||||
<c-card-body>
|
||||
<c-row style="flex-direction: row">
|
||||
<gui-grid [source]="interfaces" [columnMenu]="columnMenu" [sorting]="sorting" [infoPanel]="infoPanel"
|
||||
[autoResizeWidth]="true">
|
||||
<gui-grid-column header="Name" field="name">
|
||||
<ng-template let-value="item.name" let-item="item" let-index="index">
|
||||
{{ value }} - {{ item["default-name"] }}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
|
||||
<table style="word-break: break-word" small stripedColumns cTable>
|
||||
<tbody>
|
||||
<ng-container *ngFor="let d of raddata.value['data'] | keyvalue ; let i=index">
|
||||
<tr *ngIf="i>=objectlen(raddata.value['data'])/4 && i<(objectlen(raddata.value['data'])/4)*2">
|
||||
<th style="width: 20%;text-wrap: nowrap;">{{d.key}}</th>
|
||||
<td scope="row">{{d.value}}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-col>
|
||||
<c-col md="3">
|
||||
<table style="word-break: break-word" small stripedColumns cTable>
|
||||
<tbody>
|
||||
<ng-container *ngFor="let d of raddata.value['data'] | keyvalue ; let i=index">
|
||||
<tr *ngIf="i>=(objectlen(raddata.value['data'])/4)*2 && i<(objectlen(raddata.value['data'])/4)*3">
|
||||
<th style="width: 20%;text-wrap: nowrap;">{{d.key}}</th>
|
||||
<td scope="row">{{d.value}}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-col>
|
||||
<c-col md="3">
|
||||
<table small stripedColumns cTable>
|
||||
<tbody>
|
||||
<ng-container *ngFor="let d of raddata.value['data'] | keyvalue ; let i=index">
|
||||
<tr *ngIf="i>=(objectlen(raddata.value['data'])/4)*3">
|
||||
<th>{{d.key}}</th>
|
||||
<td scope="row">{{d.value}}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-col>
|
||||
<gui-grid-column header="MAC" field="mac-address">
|
||||
<ng-template let-value="item['mac-address']" let-item="item" let-index="index">
|
||||
{{ value }}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="rx" field="rx-byte">
|
||||
<ng-template let-value="item['rx-byte']" let-item="item" let-index="index">
|
||||
<div>{{ convert_bw_human(value, "rx") }}</div>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="tx" field="tx-byte">
|
||||
<ng-template let-value="item['tx-byte']" let-item="item" let-index="index">
|
||||
{{ convert_bw_human(value, "tx") }}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="l2mtu" field="l2mtu">
|
||||
<ng-template let-value="item.l2mtu" let-item="item" let-index="index">
|
||||
curr:{{ value }}<br />
|
||||
max : {{ item["max-l2mtu"] }}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="rx/s" field="rx-bits-per-second" [enabled]="false">
|
||||
<ng-template let-value="item['rx-bits-per-second']" let-item="item" let-index="index">
|
||||
{{ convert_bw_human(value, "rx") }}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="tx/s" field="tx-bits-per-second" [enabled]="false">
|
||||
<ng-template let-value="item['tx-bits-per-second']" let-item="item" let-index="index">
|
||||
{{ convert_bw_human(value, "tx") }}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Created" field="created" [enabled]="false">
|
||||
<ng-template let-value="item.created" let-item="item.id" let-index="index">
|
||||
{{ value }}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Last Up" field="last-link-up-time">
|
||||
<ng-template let-value="item['last-link-up-time']" let-item="item" let-index="index">
|
||||
{{ value }}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Created" field="created" [enabled]="false">
|
||||
<ng-template let-value="item.created" let-item="item.id" let-index="index">
|
||||
{{ value }}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Actions" field="action" width="60" align="center">
|
||||
<ng-template let-value="item.id" let-item="item" let-index="index">
|
||||
<button cButton color="info" size="sm" (click)="show_interface_rate(item['default-name'])"
|
||||
class="mx-1">
|
||||
<i class="fa-solid fa-chart-line"></i>
|
||||
</button>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
</gui-grid>
|
||||
</c-row>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-col>
|
||||
</c-row>
|
||||
<c-row *ngIf="raddata.value['strength-at-rates']">
|
||||
<c-col>
|
||||
<table style="word-break: break-word" small borderless cTable>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th style="text-wrap: nowrap;vertical-align: middle;padding: 5px;border-radius: 5px;background-color: #3399ff36;">Strength at rates</th>
|
||||
<td scope="row">
|
||||
<c-badge color="info" style="font-size: 0.85em;" class="mx-1" *ngFor="let st of strangth_at_rate_extract(raddata.value['strength-at-rates'])">{{st}}</c-badge>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-col>
|
||||
</c-tab-pane>
|
||||
<c-tab-pane *ngIf="!loading">
|
||||
<c-row *ngIf="is_radio && !radio_loading">
|
||||
<c-col xs md="12" *ngFor="let raddata of radio_devsensors | keyvalue">
|
||||
<c-card class="mb-1">
|
||||
<c-card-header>
|
||||
<h6>Radio data</h6>
|
||||
</c-card-header>
|
||||
<c-card-body>
|
||||
<h6>{{ raddata.key }}</h6>
|
||||
<app-widgets-dropdown [devicedata]="raddata.value"></app-widgets-dropdown>
|
||||
<c-row>
|
||||
<c-col md="3">
|
||||
<table style="word-break: break-word" small stripedColumns cTable>
|
||||
<tbody>
|
||||
<ng-container *ngFor="
|
||||
let d of raddata.value['data'] | keyvalue;
|
||||
let i = index
|
||||
">
|
||||
<tr *ngIf="i < objectlen(raddata.value['data']) / 4">
|
||||
<th style="width: 20%; text-wrap: nowrap">
|
||||
{{ d.key }}
|
||||
</th>
|
||||
<td scope="row">{{ d.value }}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-col>
|
||||
<c-col md="3">
|
||||
<table style="word-break: break-word" small stripedColumns cTable>
|
||||
<tbody>
|
||||
<ng-container *ngFor="
|
||||
let d of raddata.value['data'] | keyvalue;
|
||||
let i = index
|
||||
">
|
||||
<tr *ngIf="
|
||||
i >= objectlen(raddata.value['data']) / 4 &&
|
||||
i < (objectlen(raddata.value['data']) / 4) * 2
|
||||
">
|
||||
<th style="width: 20%; text-wrap: nowrap">
|
||||
{{ d.key }}
|
||||
</th>
|
||||
<td scope="row">{{ d.value }}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-col>
|
||||
<c-col md="3">
|
||||
<table style="word-break: break-word" small stripedColumns cTable>
|
||||
<tbody>
|
||||
<ng-container *ngFor="
|
||||
let d of raddata.value['data'] | keyvalue;
|
||||
let i = index
|
||||
">
|
||||
<tr *ngIf="
|
||||
i >= (objectlen(raddata.value['data']) / 4) * 2 &&
|
||||
i < (objectlen(raddata.value['data']) / 4) * 3
|
||||
">
|
||||
<th style="width: 20%; text-wrap: nowrap">
|
||||
{{ d.key }}
|
||||
</th>
|
||||
<td scope="row">{{ d.value }}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-col>
|
||||
<c-col md="3">
|
||||
<table small stripedColumns cTable>
|
||||
<tbody>
|
||||
<ng-container *ngFor="
|
||||
let d of raddata.value['data'] | keyvalue;
|
||||
let i = index
|
||||
">
|
||||
<tr *ngIf="
|
||||
i >= (objectlen(raddata.value['data']) / 4) * 3
|
||||
">
|
||||
<th>{{ d.key }}</th>
|
||||
<td scope="row">{{ d.value }}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-col>
|
||||
</c-row>
|
||||
<c-row *ngIf="raddata.value['strength-at-rates']">
|
||||
<c-col>
|
||||
<table style="word-break: break-word" small borderless cTable>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th style="
|
||||
text-wrap: nowrap;
|
||||
vertical-align: middle;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
background-color: #3399ff36;
|
||||
">
|
||||
Strength at rates
|
||||
</th>
|
||||
<td scope="row">
|
||||
<c-badge color="info" style="font-size: 0.85em" class="mx-1" *ngFor="
|
||||
let st of strangth_at_rate_extract(
|
||||
raddata.value['strength-at-rates']
|
||||
)
|
||||
">{{ st }}</c-badge>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-tab-pane>
|
||||
<c-tab-pane>
|
||||
<ng-container *ngIf="dhcp_server_available">
|
||||
<app-dhcp-info [tz]="tz" [dhcp_server_data]="dhcp_server_data" [small_screen]="small_screen" [columnMenu]="columnMenu"
|
||||
[sorting]="sorting" [searching]="searching" [infoPanel]="infoPanel" [paging]="paging"></app-dhcp-info>
|
||||
</ng-container>
|
||||
</c-tab-pane>
|
||||
<c-tab-pane style="width: 100%;padding-top: 10px;background-color: #fff;">
|
||||
<app-devlogs style="width: 100%;" [component_devid]="devid"></app-devlogs>
|
||||
</c-tab-pane>
|
||||
<c-tab-pane style="width: 100%;padding-top: 10px;background-color: #fff;">
|
||||
<app-authlogs style="width: 100%;" [component_devid]="devid"></app-authlogs>
|
||||
</c-tab-pane>
|
||||
<c-tab-pane style="width: 100%;padding-top: 10px;background-color: #fff;">
|
||||
<app-acclogs style="width: 100%" [component_devid]="devid"></app-acclogs>
|
||||
</c-tab-pane>
|
||||
</c-tab-content>
|
||||
</c-col>
|
||||
</c-row>
|
||||
|
||||
<c-modal #staticBackdropModal backdrop="static" size="xl" [visible]="InterfaceChartModalVisible"
|
||||
id="InterfaceChartModal">
|
||||
<c-modal-header>
|
||||
<h5 cModalTitle>{{interface_rate['name']}}</h5>
|
||||
<h5 cModalTitle>{{ interface_rate["name"] }}</h5>
|
||||
<button [cModalToggle]="staticBackdropModal.id" cButtonClose></button>
|
||||
</c-modal-header>
|
||||
<c-modal-body>
|
||||
<c-chart [data]="interface_rate" [options]="options" type="line">
|
||||
</c-chart>
|
||||
<c-chart [data]="interface_rate" [options]="options" type="line"> </c-chart>
|
||||
</c-modal-body>
|
||||
<c-modal-footer>
|
||||
<button [cModalToggle]="staticBackdropModal.id" cButton color="secondary">
|
||||
|
|
|
@ -5,3 +5,35 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nav-underline {
|
||||
border-bottom: 2px solid var(--cui-nav-underline-border-color, #c4c9d0)
|
||||
}
|
||||
|
||||
.nav-underline .nav-item {
|
||||
margin-bottom: -2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav-underline .nav-link {
|
||||
color: var(--cui-nav-underline-link-color, #768192);
|
||||
border-style: none none solid!important;
|
||||
border-width:2px;
|
||||
position:relative;
|
||||
bottom:-1px;
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
.interfaces gui-grid gui-structure{
|
||||
min-height: unset!important;
|
||||
}
|
||||
.nav-underline .nav-link:hover,.nav-underline .nav-link:focus {
|
||||
border-color: var(--cui-nav-underline-link-active-border-color, #321fdb)
|
||||
}
|
||||
|
||||
.nav-underline .nav-link.active,.nav-underline .show>.nav-link {
|
||||
color: var(--cui-nav-underline-link-active-color, #321fdb);
|
||||
background: transparent;
|
||||
border-color: var(--cui-nav-underline-link-active-border-color, #321fdb);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Component, OnDestroy, OnInit ,ViewEncapsulation} from "@angular/core";
|
||||
import { Router, ActivatedRoute } from "@angular/router";
|
||||
import { dataProvider } from "../../providers/mikrowizard/data";
|
||||
import { loginChecker } from "../../providers/login_checker";
|
||||
import {
|
||||
GuiSearching,
|
||||
GuiInfoPanel,
|
||||
GuiColumn,
|
||||
GuiColumnMenu,
|
||||
|
@ -34,13 +35,20 @@ type radiodata = {
|
|||
@Component({
|
||||
templateUrl: "device.component.html",
|
||||
styleUrls: ["device.component.scss"],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class DeviceComponent implements OnInit, OnDestroy {
|
||||
public uid: number;
|
||||
public sessionloaded: boolean = false;
|
||||
public uname: string;
|
||||
public tz: string;
|
||||
public ispro: boolean = false;
|
||||
|
||||
public small_screen=false;
|
||||
public show_dev_logs: boolean = false;
|
||||
public show_auth_logs: boolean = false;
|
||||
public show_acc_logs: boolean = false;
|
||||
public actice_tab_index: number = 0;
|
||||
public reloading: boolean = false;
|
||||
constructor(
|
||||
private data_provider: dataProvider,
|
||||
private route: ActivatedRoute,
|
||||
|
@ -58,8 +66,8 @@ export class DeviceComponent implements OnInit, OnDestroy {
|
|||
_self.uname = res.name;
|
||||
_self.tz = res.tz;
|
||||
_self.ispro = res.ISPRO;
|
||||
_self.sessionloaded = true;
|
||||
const userId = _self.uid;
|
||||
|
||||
if (res.role != "admin") {
|
||||
setTimeout(function () {
|
||||
_self.router.navigate(["/user/dashboard"]);
|
||||
|
@ -87,6 +95,9 @@ export class DeviceComponent implements OnInit, OnDestroy {
|
|||
public interface_rate: any = {};
|
||||
public options: any;
|
||||
public is_radio: boolean = false;
|
||||
public dhcp_server_available: boolean = false;
|
||||
public dhcp_server_data: any = {};
|
||||
|
||||
public sorting = {
|
||||
enabled: true,
|
||||
multiSorting: true,
|
||||
|
@ -95,8 +106,8 @@ export class DeviceComponent implements OnInit, OnDestroy {
|
|||
public paging: GuiPaging = {
|
||||
enabled: true,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
pageSizes: [5, 10, 25, 50],
|
||||
pageSize: 20,
|
||||
pageSizes: [20],
|
||||
display: GuiPagingDisplay.ADVANCED,
|
||||
};
|
||||
|
||||
|
@ -117,12 +128,18 @@ export class DeviceComponent implements OnInit, OnDestroy {
|
|||
columnsManager: true,
|
||||
schemaManager: true,
|
||||
};
|
||||
|
||||
searching: GuiSearching = {
|
||||
enabled: true,
|
||||
placeholder: "Search In table",
|
||||
};
|
||||
public rowSelection: boolean | GuiRowSelection = {
|
||||
enabled: true,
|
||||
type: GuiRowSelectionType.CHECKBOX,
|
||||
mode: GuiRowSelectionMode.MULTIPLE,
|
||||
};
|
||||
reload_dhcp_server(){
|
||||
this.get_DHCP_data();
|
||||
}
|
||||
Chartoptions = {
|
||||
responsive: true,
|
||||
_self :this,
|
||||
|
@ -250,9 +267,20 @@ export class DeviceComponent implements OnInit, OnDestroy {
|
|||
};
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
if (window.innerWidth <= 1200) {
|
||||
this.small_screen = true;
|
||||
}
|
||||
window.onresize = () => (this.small_screen = window.innerWidth <= 1200);
|
||||
this.devid = Number(this.route.snapshot.paramMap.get("id"));
|
||||
this.options = this.Chartoptions;
|
||||
this.initDeviceInfo();
|
||||
// wait untill sessionloaded is set
|
||||
let interval = setInterval(() => {
|
||||
if (this.sessionloaded) {
|
||||
clearInterval(interval);
|
||||
this.initDeviceInfo();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
optionsDefault = {
|
||||
|
@ -466,11 +494,43 @@ export class DeviceComponent implements OnInit, OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
get_DHCP_data() {
|
||||
if(!this.ispro)
|
||||
return;
|
||||
var _self = this;
|
||||
if(_self.reloading)
|
||||
return;
|
||||
_self.reloading = true;
|
||||
_self.data_provider
|
||||
.get_dev_dhcp_info(_self.devid)
|
||||
.then((res) => {
|
||||
_self.dhcp_server_available = Boolean(res.length);
|
||||
_self.dhcp_server_data = res;
|
||||
// loop in dhcp_server_data and create a new object with the data for chart for each dhcp server
|
||||
_self.reloading = false;
|
||||
_self.dhcp_server_data.forEach((element:any) => {
|
||||
var pooldata=element.pools[0];
|
||||
element.chartpools = {
|
||||
labels: ['Used', 'Free'],
|
||||
datasets: [{
|
||||
backgroundColor: [ '#E46651','#41B883'],
|
||||
data: [pooldata.used_ips, pooldata.available_ips]
|
||||
}]
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
reload_device(): void {
|
||||
this.initDeviceInfo();
|
||||
}
|
||||
initDeviceInfo(): void {
|
||||
var _self = this;
|
||||
if (this.reloading) return;
|
||||
clearInterval(this.data_interval);
|
||||
if(_self.ispro) _self.get_DHCP_data();
|
||||
this.updateData();
|
||||
this.data_interval = setInterval(() => {
|
||||
this.reloading = true;
|
||||
this.data_provider.get_dev_info(this.devid).then((res) => {
|
||||
_self.devdata = res;
|
||||
if ("is_radio" in res) _self.is_radio = res.is_radio;
|
||||
|
@ -480,12 +540,17 @@ export class DeviceComponent implements OnInit, OnDestroy {
|
|||
.then((res) => {
|
||||
_self.devsensors = res;
|
||||
_self.loading = false;
|
||||
_self.reloading = false;
|
||||
|
||||
if (_self.is_radio) _self.get_radio_data();
|
||||
|
||||
});
|
||||
});
|
||||
}, 60000);
|
||||
}, 30000);
|
||||
}
|
||||
show_history(itme:any) {
|
||||
return
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
clearInterval(this.data_interval);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,10 @@ import {
|
|||
ModalModule,
|
||||
TableModule,
|
||||
UtilitiesModule,
|
||||
BadgeModule
|
||||
BadgeModule,
|
||||
NavModule,
|
||||
TabsModule,
|
||||
SpinnerModule,
|
||||
} from "@coreui/angular";
|
||||
import { ChartjsModule } from "@coreui/angular-chartjs";
|
||||
|
||||
|
@ -22,7 +25,13 @@ import { DeviceComponent } from "./device.component";
|
|||
import { GuiGridModule } from "@generic-ui/ngx-grid";
|
||||
|
||||
import { WidgetsModule } from "../widgets/widgets.module";
|
||||
|
||||
import { DeviceInfoModule } from "./device-info/device-info.module";
|
||||
import { DhcpInfoModule } from "./dhcp-info/dhcp-info.module";
|
||||
import { DevLogsModule } from "../device_logs/devlogs.module";
|
||||
import { PingStatsModule } from "./ping-status/ping-status.module";
|
||||
import { ActiveUsersModule } from "./active-users/active-users.module";
|
||||
import { AuthModule } from "../auth_log/auth.module";
|
||||
import { AccModule } from "../acc_log/acc.module";
|
||||
@NgModule({
|
||||
imports: [
|
||||
DeviceRoutingModule,
|
||||
|
@ -36,12 +45,22 @@ import { WidgetsModule } from "../widgets/widgets.module";
|
|||
ButtonGroupModule,
|
||||
ChartjsModule,
|
||||
WidgetsModule,
|
||||
DeviceInfoModule,
|
||||
DhcpInfoModule,
|
||||
DevLogsModule,
|
||||
PingStatsModule,
|
||||
ActiveUsersModule,
|
||||
AuthModule,
|
||||
AccModule,
|
||||
SpinnerModule,
|
||||
GuiGridModule,
|
||||
NavbarModule,
|
||||
ModalModule,
|
||||
TableModule,
|
||||
UtilitiesModule,
|
||||
BadgeModule
|
||||
BadgeModule,
|
||||
NavModule,
|
||||
TabsModule,
|
||||
],
|
||||
declarations: [DeviceComponent],
|
||||
})
|
||||
|
|
168
src/app/views/device_detail/dhcp-info/dhcp-info.component.html
Normal file
168
src/app/views/device_detail/dhcp-info/dhcp-info.component.html
Normal file
|
@ -0,0 +1,168 @@
|
|||
<ng-container *ngFor="let item of dhcp_server_data ">
|
||||
<c-row style="padding: 10px 0;" >
|
||||
<c-col xl="3" sm="12">
|
||||
<c-row *ngIf="small_screen">
|
||||
<c-col sm="6" style="padding-right: 0;">
|
||||
<c-card class="h-100">
|
||||
<c-card-header>
|
||||
<h6>Statics</h6>
|
||||
</c-card-header>
|
||||
<c-card-body>
|
||||
<c-chart [data]="item['chartpools']" width="100%" type="doughnut"
|
||||
[options]="{aspectRatio:2,responsive:true,'plugins': { legend : {position: 'left',fullSize: true}}}">
|
||||
</c-chart>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-col>
|
||||
<c-col sm="6" style="padding-left: 0;">
|
||||
<c-card class="h-100">
|
||||
<c-card-header>
|
||||
<h6>DHCP server {{item['dhcp_server']}}</h6>
|
||||
</c-card-header>
|
||||
<c-card-body>
|
||||
Pool name: {{item['address_pool']}}<br />
|
||||
Static Ip: {{item['static_ips']}}<br />
|
||||
Dynamic Ip: {{item['dynamic_ips']}}<br />
|
||||
interface : {{item['interface']}}<br />
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-col>
|
||||
|
||||
</c-row>
|
||||
<c-card class="h-100" *ngIf="!small_screen">
|
||||
<c-card style="border: none;">
|
||||
<c-card-header>
|
||||
<h6>Statics</h6>
|
||||
</c-card-header>
|
||||
<c-card-body>
|
||||
<c-chart [data]="item['chartpools']" width="100%" type="doughnut">
|
||||
</c-chart>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
<c-card style="border: none;">
|
||||
<c-card-body>
|
||||
|
||||
<table cTable small>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">Server name</th>
|
||||
<td>{{item['dhcp_server']}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Pool name</th>
|
||||
<td>{{item['address_pool']}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Static Ip</th>
|
||||
<td>{{item['static_ips']}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Dynamic Ip</th>
|
||||
<td>{{item['dynamic_ips']}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Interface</th>
|
||||
<td>{{item['interface']}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Duplicate AC-ID</th>
|
||||
<td>{{item['duplicate_agent_circuit_ids'].length}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-card>
|
||||
</c-col>
|
||||
<c-col xl="9" sm="12">
|
||||
<c-card class="mb-1 h-100">
|
||||
<c-card-body>
|
||||
<gui-grid [source]="item['lease_table']" [columnMenu]="columnMenu" [sorting]="sorting"
|
||||
[searching]="searching" [infoPanel]="infoPanel" [rowHeight]="28" [autoResizeWidth]=true [paging]="paging">
|
||||
<gui-grid-column header="address" field="address">
|
||||
<ng-template let-value="item.address" let-item="item" let-index="index">
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Type" field="dynamic">
|
||||
<ng-template let-value="item" let-item="item" let-index="index">
|
||||
<span *ngIf="item['dynamic']">Dynamic</span>
|
||||
<span *ngIf="item['static']">Static</span>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="expire" field="expires-after">
|
||||
<ng-template let-value="item['expires-after']" let-item="item" let-index="index">
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="host-name" field="host-name">
|
||||
<ng-template let-value="item['host-name']" let-item="item" let-index="index">
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="last-seen" field="last-seen">
|
||||
<ng-template let-value="item['last-seen']" let-item="item" let-index="index">
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="status" field="status">
|
||||
<ng-template let-value="item['status']" let-item="item" let-index="index">
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="MAC" field="mac-address">
|
||||
<ng-template let-value="item['mac-address']" let-item="item" let-index="index">
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Actions" width="60" field="">
|
||||
<ng-template let-value="item" let-item="item" let-index="index">
|
||||
<button cButton color="info" size="sm" (click)="show_history(item)" class="mx-1"><i
|
||||
class="fa-solid fa-clock-rotate-left"></i></button>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
</gui-grid>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</ng-container>
|
||||
|
||||
|
||||
<c-modal #DhcpHistoryModal backdrop="static" size="xl" [visible]="dhcp_history_modal"
|
||||
id="DhcpHistoryModal" *ngIf="dhcp_history_modal">
|
||||
<c-modal-header>
|
||||
<h5 cModalTitle>History for {{current_dhcp['mac-address']}}</h5>
|
||||
<button [cModalToggle]="DhcpHistoryModal.id" cButtonClose></button>
|
||||
</c-modal-header>
|
||||
<c-modal-body style="overflow-x: auto;">
|
||||
<gui-grid style="min-width: 900px;" [source]="dhcp_history" [columnMenu]="columnMenu" [sorting]="sorting"
|
||||
[searching]="searching" [infoPanel]="infoPanel" [rowHeight]="35" [autoResizeWidth]=true [paging]="paging">
|
||||
<gui-grid-column header="comment" field="comment" >
|
||||
<ng-template let-value="item.comment" let-item="item" let-index="index">
|
||||
<div style="text-wrap: auto;font-weight: bold;line-height: normal;">{{value}}</div>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="type" field="detail" width="150">
|
||||
<ng-template let-value="item['detail']" let-item="item" let-index="index">
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="eventtime" field="eventtime" width="200">
|
||||
<ng-template let-value="item['eventtime']" let-item="item" let-index="index">
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Router" field="ip" width="250">
|
||||
<ng-template let-value="item['ip']" let-item="item" let-index="index">
|
||||
{{item.name}} ({{value}})
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
</gui-grid>
|
||||
</c-modal-body>
|
||||
<c-modal-footer>
|
||||
<button [cModalToggle]="DhcpHistoryModal.id" cButton color="secondary">
|
||||
Close
|
||||
</button>
|
||||
</c-modal-footer>
|
||||
</c-modal>
|
47
src/app/views/device_detail/dhcp-info/dhcp-info.component.ts
Normal file
47
src/app/views/device_detail/dhcp-info/dhcp-info.component.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
import { AfterContentInit, ChangeDetectionStrategy, ChangeDetectorRef, Component,Input } from '@angular/core';
|
||||
import { dataProvider } from "../../../providers/mikrowizard/data";
|
||||
import { formatInTimeZone } from "date-fns-tz";
|
||||
|
||||
@Component({
|
||||
selector: 'app-dhcp-info',
|
||||
templateUrl: './dhcp-info.component.html',
|
||||
styleUrls: ['./dhcp-info.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.Default
|
||||
})
|
||||
export class DhcpInfoComponent implements AfterContentInit {
|
||||
@Input() dhcp_server_data: any;
|
||||
@Input() small_screen: boolean=false;
|
||||
@Input() columnMenu: any;
|
||||
@Input() sorting: any;
|
||||
@Input() searching: any;
|
||||
@Input() infoPanel: any;
|
||||
@Input() paging: any;
|
||||
@Input() rowSelectionMode: any;
|
||||
@Input() tz: any;
|
||||
dhcp_history:any;
|
||||
dhcp_history_modal: boolean = false;
|
||||
current_dhcp:any;
|
||||
constructor(
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private data_provider: dataProvider,
|
||||
) {}
|
||||
|
||||
show_history(item:any) {
|
||||
var _self = this;
|
||||
this.data_provider.getDhcpHistory(item).then((res) => {
|
||||
_self.current_dhcp = item;
|
||||
this.dhcp_history = res.map((d: any) => {
|
||||
d.eventtime = formatInTimeZone(
|
||||
d.eventtime.split(".")[0] + ".000Z",
|
||||
_self.tz,
|
||||
"yyyy-MM-dd HH:mm:ss XXX"
|
||||
);
|
||||
return d;
|
||||
});
|
||||
this.dhcp_history_modal=!this.dhcp_history_modal;
|
||||
});
|
||||
}
|
||||
ngAfterContentInit(): void {
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
}
|
57
src/app/views/device_detail/dhcp-info/dhcp-info.module.ts
Normal file
57
src/app/views/device_detail/dhcp-info/dhcp-info.module.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import {
|
||||
ButtonGroupModule,
|
||||
ButtonModule,
|
||||
CardModule,
|
||||
FormModule,
|
||||
GridModule,
|
||||
ProgressModule,
|
||||
NavbarModule,
|
||||
AlertModule,
|
||||
ModalModule,
|
||||
TableModule,
|
||||
UtilitiesModule,
|
||||
BadgeModule,
|
||||
NavModule,
|
||||
TabsModule,
|
||||
} from '@coreui/angular';
|
||||
import { ChartjsModule } from '@coreui/angular-chartjs';
|
||||
|
||||
import { WidgetsModule } from "../../widgets/widgets.module";
|
||||
|
||||
// import { WidgetsRoutingModule } from './widgets-routing.module';
|
||||
import { DhcpInfoComponent } from './dhcp-info.component';
|
||||
import { GuiGridModule } from "@generic-ui/ngx-grid";
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
DhcpInfoComponent,
|
||||
],
|
||||
imports: [
|
||||
CardModule,
|
||||
AlertModule,
|
||||
CommonModule,
|
||||
GridModule,
|
||||
ProgressModule,
|
||||
FormModule,
|
||||
ButtonModule,
|
||||
ButtonGroupModule,
|
||||
ChartjsModule,
|
||||
WidgetsModule,
|
||||
NavbarModule,
|
||||
ModalModule,
|
||||
GuiGridModule,
|
||||
TableModule,
|
||||
UtilitiesModule,
|
||||
BadgeModule,
|
||||
NavModule,
|
||||
TabsModule,
|
||||
],
|
||||
exports: [
|
||||
DhcpInfoComponent,
|
||||
]
|
||||
})
|
||||
export class DhcpInfoModule {
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { DhcpInfoComponent } from './dhcp-info.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: DhcpInfoComponent,
|
||||
data: {
|
||||
title: 'Widgets'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class DhcpInfoRoutingModule {
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { PingStatsComponent } from './ping-status.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: PingStatsComponent,
|
||||
data: {
|
||||
title: 'Widgets'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class PingStatsRoutingModule {
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<c-row>
|
||||
<c-col xs>
|
||||
<c-card class="mb-1" style="border: none;">
|
||||
<c-card-header>
|
||||
<h6>Ping status</h6>
|
||||
</c-card-header>
|
||||
<c-card-body style="height: 275px;overflow: auto;">
|
||||
<h6 style="display: inline-block;">Successful pings : <c-badge color="success" style="font-size: 90%;">
|
||||
{{ping['successful_pings']}}</c-badge></h6>
|
||||
<h6 style="display: inline-block;margin-left: 0.5rem;"> | Failed pings : <c-badge color="danger"
|
||||
style="font-size: 90%;"> {{ping['failed_pings']}}</c-badge></h6>
|
||||
<h6 style="display: inline-block;margin-left: 0.5rem;">| Avrage : <c-badge color="dark" style="font-size: 90%;">
|
||||
{{ping['average_ping_time']}} ms</c-badge></h6>
|
||||
<table cTable small>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Host</th>
|
||||
<th scope="col">Time</th>
|
||||
<th scope="col">Status</th>
|
||||
<th scope="col">Quality</th>
|
||||
<th scope="col">details</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of ping['results']; index as i">
|
||||
<td>{{i+1}}</td>
|
||||
<td>{{item['host']}}</td>
|
||||
<td>{{item['time']}} ms</td>
|
||||
<td>{{item['status']}}</td>
|
||||
<td><i style="color: {{item['color']}};margin-right:2px;"
|
||||
class="{{item['icon']}}"></i>{{item['ping_quality']}}</td>
|
||||
<td><button cButton [cTooltip]="item['raw_response']" size="sm" style="padding: 0;" color="info"
|
||||
variant="ghost"><i class="fa-solid fa-info"></i>
|
||||
Details</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-card-body>
|
||||
</c-card>
|
||||
</c-col>
|
||||
</c-row>
|
|
@ -0,0 +1,19 @@
|
|||
import { AfterContentInit, ChangeDetectionStrategy, ChangeDetectorRef, Component,Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-ping-stats',
|
||||
templateUrl: './ping-status.component.html',
|
||||
styleUrls: ['./ping-status.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.Default
|
||||
})
|
||||
export class PingStatsComponent implements AfterContentInit {
|
||||
@Input() ping: any;
|
||||
constructor(
|
||||
private changeDetectorRef: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
ngAfterContentInit(): void {
|
||||
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import {
|
||||
ButtonGroupModule,
|
||||
ButtonModule,
|
||||
CardModule,
|
||||
FormModule,
|
||||
GridModule,
|
||||
ProgressModule,
|
||||
NavbarModule,
|
||||
ModalModule,
|
||||
TableModule,
|
||||
UtilitiesModule,
|
||||
BadgeModule,
|
||||
TooltipModule,
|
||||
} from '@coreui/angular';
|
||||
|
||||
// import { WidgetsRoutingModule } from './widgets-routing.module';
|
||||
import { PingStatsComponent } from './ping-status.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
PingStatsComponent,
|
||||
],
|
||||
imports: [
|
||||
CardModule,
|
||||
CommonModule,
|
||||
GridModule,
|
||||
ProgressModule,
|
||||
FormModule,
|
||||
ButtonModule,
|
||||
ButtonGroupModule,
|
||||
NavbarModule,
|
||||
ModalModule,
|
||||
TableModule,
|
||||
UtilitiesModule,
|
||||
BadgeModule,
|
||||
TooltipModule,
|
||||
],
|
||||
exports: [
|
||||
PingStatsComponent,
|
||||
]
|
||||
})
|
||||
export class PingStatsModule {
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
<c-card class="mb-1">
|
||||
<c-card-header>
|
||||
<h6>Radio data</h6>
|
||||
</c-card-header>
|
||||
<c-card-body>
|
||||
<h6>{{raddata.key}}</h6>
|
||||
<app-widgets-dropdown [devicedata]=raddata.value></app-widgets-dropdown>
|
||||
<c-row>
|
||||
<c-col md="3">
|
||||
<table style="word-break: break-word" small stripedColumns cTable>
|
||||
<tbody>
|
||||
<ng-container *ngFor="let d of raddata.value['data'] | keyvalue ; let i=index">
|
||||
<tr *ngIf="i<objectlen(raddata.value['data'])/4">
|
||||
<th style="width: 20%;text-wrap: nowrap;">{{d.key}}</th>
|
||||
<td scope="row">{{d.value}}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-col>
|
||||
<c-col md="3">
|
||||
|
||||
<table style="word-break: break-word" small stripedColumns cTable>
|
||||
<tbody>
|
||||
<ng-container *ngFor="let d of raddata.value['data'] | keyvalue ; let i=index">
|
||||
<tr *ngIf="i>=objectlen(raddata.value['data'])/4 && i<(objectlen(raddata.value['data'])/4)*2">
|
||||
<th style="width: 20%;text-wrap: nowrap;">{{d.key}}</th>
|
||||
<td scope="row">{{d.value}}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-col>
|
||||
<c-col md="3">
|
||||
<table style="word-break: break-word" small stripedColumns cTable>
|
||||
<tbody>
|
||||
<ng-container *ngFor="let d of raddata.value['data'] | keyvalue ; let i=index">
|
||||
<tr *ngIf="i>=(objectlen(raddata.value['data'])/4)*2 && i<(objectlen(raddata.value['data'])/4)*3">
|
||||
<th style="width: 20%;text-wrap: nowrap;">{{d.key}}</th>
|
||||
<td scope="row">{{d.value}}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-col>
|
||||
<c-col md="3">
|
||||
<table small stripedColumns cTable>
|
||||
<tbody>
|
||||
<ng-container *ngFor="let d of raddata.value['data'] | keyvalue ; let i=index">
|
||||
<tr *ngIf="i>=(objectlen(raddata.value['data'])/4)*3">
|
||||
<th>{{d.key}}</th>
|
||||
<td scope="row">{{d.value}}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-col>
|
||||
</c-row>
|
||||
<c-row *ngIf="raddata.value['strength-at-rates']">
|
||||
<c-col>
|
||||
<table style="word-break: break-word" small borderless cTable>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th
|
||||
style="text-wrap: nowrap;vertical-align: middle;padding: 5px;border-radius: 5px;background-color: #3399ff36;">
|
||||
Strength at rates</th>
|
||||
<td scope="row">
|
||||
<c-badge color="info" style="font-size: 0.85em;" class="mx-1"
|
||||
*ngFor="let st of strangth_at_rate_extract(raddata.value['strength-at-rates'])">{{st}}</c-badge>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</c-card-body>
|
||||
</c-card>
|
|
@ -0,0 +1,21 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { RadioDataComponent } from './radio-data.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: RadioDataComponent,
|
||||
data: {
|
||||
title: 'Widgets'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class RadioDataRoutingModule {
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import { AfterContentInit, ChangeDetectionStrategy, ChangeDetectorRef, Component,Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-iradio-data',
|
||||
templateUrl: './radio-data.component.html',
|
||||
styleUrls: ['./radio-data.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.Default
|
||||
})
|
||||
export class RadioDataComponent implements AfterContentInit {
|
||||
@Input() raddata: any;
|
||||
constructor(
|
||||
private changeDetectorRef: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
objectlen(object:any){
|
||||
return Object.keys(object).length;
|
||||
}
|
||||
strangth_at_rate_extract(data:string){
|
||||
return data.split(',');
|
||||
}
|
||||
ngAfterContentInit(): void {
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
}
|
56
src/app/views/device_detail/radio-data/radio-data.module.ts
Normal file
56
src/app/views/device_detail/radio-data/radio-data.module.ts
Normal file
|
@ -0,0 +1,56 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import {
|
||||
ButtonGroupModule,
|
||||
ButtonModule,
|
||||
CardModule,
|
||||
FormModule,
|
||||
GridModule,
|
||||
ProgressModule,
|
||||
NavbarModule,
|
||||
AlertModule,
|
||||
ModalModule,
|
||||
TableModule,
|
||||
UtilitiesModule,
|
||||
BadgeModule,
|
||||
NavModule,
|
||||
TabsModule,
|
||||
} from '@coreui/angular';
|
||||
import { IconModule } from '@coreui/icons-angular';
|
||||
import { ChartjsModule } from '@coreui/angular-chartjs';
|
||||
|
||||
import { WidgetsModule } from "../../widgets/widgets.module";
|
||||
|
||||
// import { WidgetsRoutingModule } from './widgets-routing.module';
|
||||
import { RadioDataComponent } from './radio-data.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
RadioDataComponent,
|
||||
],
|
||||
imports: [
|
||||
CardModule,
|
||||
AlertModule,
|
||||
CommonModule,
|
||||
GridModule,
|
||||
ProgressModule,
|
||||
FormModule,
|
||||
ButtonModule,
|
||||
ButtonGroupModule,
|
||||
ChartjsModule,
|
||||
WidgetsModule,
|
||||
NavbarModule,
|
||||
ModalModule,
|
||||
TableModule,
|
||||
UtilitiesModule,
|
||||
BadgeModule,
|
||||
NavModule,
|
||||
TabsModule,
|
||||
],
|
||||
exports: [
|
||||
RadioDataComponent,
|
||||
]
|
||||
})
|
||||
export class RadioDataModule {
|
||||
}
|
|
@ -1,10 +1,22 @@
|
|||
<c-row>
|
||||
<c-col xs>
|
||||
<c-card class="mb-4">
|
||||
<c-card class="mb-4" [ngStyle]="component_devid && {'border-top': 'none'}">
|
||||
<c-card-header>
|
||||
<c-row>
|
||||
<c-col xs [lg]="11">
|
||||
Device LOGS
|
||||
<c-col xs [lg]="11" style="display: flex;flex-direction: column;align-items: flex-start;">
|
||||
<h5>Device LOGS
|
||||
<a style="cursor: pointer;" (click)="reinitgrid('none','none')"><i
|
||||
*ngIf="devid!=0 && component_devid && !reloading" class="fa-solid fa-arrows-rotate"
|
||||
style="color: #74C0FC;"></i>
|
||||
<i *ngIf="devid!=0 && component_devid && reloading" class="fa-solid fa-arrows-rotate fa-spin"
|
||||
style="color: #74C0FC;"></i>
|
||||
</a>
|
||||
</h5>
|
||||
<c-badge color="warning" *ngIf="devid!=0 && !component_devid">Filtered Result For Device ID
|
||||
{{devid}}</c-badge>
|
||||
<c-alert color="warning" style="padding-top: 5px!important;font-size: 0.8rem;display: inline-block;" *ngIf="!filters['start_time'] && !filters['end_time']">
|
||||
<i class="fa-solid fa-triangle-exclamation mx-1"></i>Showing <strong>last 24 hours logs</strong> by default. Use filters to modify the date and time.
|
||||
</c-alert>
|
||||
</c-col>
|
||||
<c-col xs [lg]="1">
|
||||
<button (click)="toggleCollapse()" cButton class="me-1" color="primary"><i
|
||||
|
@ -121,12 +133,14 @@
|
|||
</gui-grid-column>
|
||||
<gui-grid-column header="eventtime" width='220' field="eventtime">
|
||||
<ng-template let-value="item.eventtime" let-item="item" let-index="index">
|
||||
<div *ngIf="item.fixtime" class="fixed_time"><span><c-badge *ngIf="item.status==true" color="danger" style="font-size: 0.65em!important;padding: 3px 5px;" size="sm"
|
||||
shape="rounded-pill">Event</c-badge> {{value}}</span><span>
|
||||
<c-badge *ngIf="item.status==true" color="success" style="font-size: 0.65em!important;padding: 3px 5px;" size="sm"
|
||||
shape="rounded-pill">Fixed</c-badge> {{item.fixtime}}</span>
|
||||
<div *ngIf="item.fixtime" class="fixed_time"><span><c-badge *ngIf="item.status==true" color="danger"
|
||||
style="font-size: 0.65em!important;padding: 3px 5px;" size="sm" shape="rounded-pill">Event</c-badge>
|
||||
{{value}}</span><span>
|
||||
<c-badge *ngIf="item.status==true" color="success"
|
||||
style="font-size: 0.65em!important;padding: 3px 5px;" size="sm" shape="rounded-pill">Fixed</c-badge>
|
||||
{{item.fixtime}}</span>
|
||||
</div>
|
||||
<div *ngIf="!item.fixtime"> {{ value }}</div>
|
||||
<div *ngIf="!item.fixtime"> {{ value }}</div>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Device" width='200' field="name">
|
||||
|
|
|
@ -128,4 +128,8 @@ padding: 0.5rem!important;
|
|||
.mat-mdc-form-field-infix{
|
||||
width:150px;
|
||||
}
|
||||
}
|
||||
.alert{
|
||||
padding: 5px!important;
|
||||
color:#644808!important;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit, ViewEncapsulation } from "@angular/core";
|
||||
import { Component, OnInit, ViewEncapsulation,Input } from "@angular/core";
|
||||
import { FormControl } from "@angular/forms";
|
||||
import { dataProvider } from "../../providers/mikrowizard/data";
|
||||
import { Router, ActivatedRoute } from "@angular/router";
|
||||
|
@ -20,11 +20,13 @@ import { Subject } from "rxjs";
|
|||
|
||||
|
||||
@Component({
|
||||
selector: 'app-devlogs',
|
||||
templateUrl: "devlogs.component.html",
|
||||
styleUrls: ["devlogs.component.scss"],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class DevLogsComponent implements OnInit {
|
||||
@Input() component_devid: any=false;
|
||||
public uid: number;
|
||||
public uname: string;
|
||||
public tz: string = "UTC"
|
||||
|
@ -40,6 +42,7 @@ export class DevLogsComponent implements OnInit {
|
|||
public event_types: any = [];
|
||||
public event_types_filtered: any = [];
|
||||
public filters_visible: boolean = false;
|
||||
public reloading: boolean = false;
|
||||
constructor(
|
||||
private data_provider: dataProvider,
|
||||
private router: Router,
|
||||
|
@ -174,7 +177,11 @@ export class DevLogsComponent implements OnInit {
|
|||
};
|
||||
ngOnInit(): void {
|
||||
var _self = this;
|
||||
this.devid = Number(this.route.snapshot.paramMap.get("devid"));
|
||||
if (this.component_devid) {
|
||||
this.devid = this.component_devid;
|
||||
} else{
|
||||
this.devid = Number(this.route.snapshot.paramMap.get("devid"));
|
||||
}
|
||||
if (this.devid > 0) {
|
||||
this.filters["devid"] = this.devid;
|
||||
}
|
||||
|
@ -210,6 +217,8 @@ export class DevLogsComponent implements OnInit {
|
|||
}
|
||||
initGridTable(): void {
|
||||
var _self = this;
|
||||
if(this.reloading) return;
|
||||
this.reloading = true;
|
||||
this.data_provider.get_dev_logs(this.filters).then((res) => {
|
||||
let index = 1;
|
||||
this.source = res.map((d: any) => {
|
||||
|
@ -233,8 +242,8 @@ export class DevLogsComponent implements OnInit {
|
|||
return d;
|
||||
});
|
||||
_self.event_types_filtered = _self.event_types;
|
||||
console.dir(this.source);
|
||||
this.loading = false;
|
||||
this.reloading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
GridModule,
|
||||
CollapseModule,
|
||||
BadgeModule,
|
||||
AlertModule,
|
||||
} from "@coreui/angular";
|
||||
import { NgxMatSelectSearchModule } from "ngx-mat-select-search";
|
||||
import { DevLogsRoutingModule } from "./devlogs-routing.module";
|
||||
|
@ -41,7 +42,9 @@ import { MatSelectModule } from "@angular/material/select";
|
|||
MatSelectModule,
|
||||
NgxMatSelectSearchModule,
|
||||
MatDatepickerModule,
|
||||
AlertModule,
|
||||
],
|
||||
declarations: [DevLogsComponent],
|
||||
exports: [DevLogsComponent],
|
||||
})
|
||||
export class DevLogsModule {}
|
||||
|
|
|
@ -118,6 +118,9 @@
|
|||
<!-- <button size="sm" (click)="single_device_action(item,'upgrade')" style="padding: 4px 7px;"
|
||||
cListGroupItem><i class="text-primary fa-solid fa-microchip"></i><small>
|
||||
Upgrade Firmware</small></button> -->
|
||||
<button size="sm" (click)="single_device_action(item,'devlogs')" style="padding: 4px 7px;"
|
||||
cListGroupItem><i class="fa-regular fa-rectangle-list"></i><small>
|
||||
Device Logs</small></button>
|
||||
<button size="sm" (click)="single_device_action(item,'logauth')" style="padding: 4px 7px;"
|
||||
cListGroupItem><i class="text-primary fa-regular fa-clock"></i><small>
|
||||
Show Auth Logs</small></button>
|
||||
|
@ -176,8 +179,8 @@
|
|||
<c-modal-body>
|
||||
<div *ngIf="scanwizard_step==1" class="mb-5" style="text-align: center;">
|
||||
<h5 class="mb-5">Please select searching method</h5>
|
||||
<button cButton color="info" (click)="scanwizard(2,'chip')" [disabled]="true" class="mx-1" size="lg"><img width="100px"
|
||||
src="assets/img/chip.png" /><br />Layer2 Scan</button>
|
||||
<button cButton color="info" (click)="scanwizard(2,'chip')" [disabled]="true" class="mx-1" size="lg"><img
|
||||
width="100px" src="assets/img/chip.png" /><br />Layer2 Scan</button>
|
||||
<button cButton color="info" (click)="scanwizard(2,'ip')" class="mx-1" size="lg"><img width="100px"
|
||||
src="assets/img/tcpip.png" /><br />TCP/IP Scan</button>
|
||||
</div>
|
||||
|
@ -256,8 +259,9 @@
|
|||
</div>
|
||||
</c-modal-body>
|
||||
<c-modal-footer>
|
||||
<h6 style="margin: 0 auto;" *ngIf="scanwizard_step==1" ><button cButton color="primary" (click)="show_exec()" style="margin: 0 auto;" variant="outline">Device scan logs</button></h6>
|
||||
<small *ngIf="scan_type=='ip'" >Empty username and password means system default
|
||||
<h6 style="margin: 0 auto;" *ngIf="scanwizard_step==1"><button cButton color="primary" (click)="show_exec()"
|
||||
style="margin: 0 auto;" variant="outline">Device scan logs</button></h6>
|
||||
<small *ngIf="scan_type=='ip'">Empty username and password means system default
|
||||
configuration</small>
|
||||
</c-modal-footer>
|
||||
</c-modal>
|
||||
|
@ -271,9 +275,7 @@
|
|||
<c-modal-body>
|
||||
<c-input-group class="mb-3">
|
||||
<gui-grid [autoResizeWidth]="true" *ngIf="ExecutedDataModalVisible" [searching]="searching"
|
||||
[source]="ExecutedData" [columnMenu]="columnMenu" [sorting]="sorting" [infoPanel]="infoPanel"
|
||||
[autoResizeWidth]=true
|
||||
[paging]="paging">
|
||||
[source]="ExecutedData" [columnMenu]="columnMenu" [sorting]="sorting" [infoPanel]="infoPanel" [paging]="paging">
|
||||
<gui-grid-column header="Start time" field="start">
|
||||
<ng-template let-value="item['started']" let-item="item" let-index="index">
|
||||
{{value}} </ng-template>
|
||||
|
@ -286,18 +288,18 @@
|
|||
<ng-template let-value="item['end_ip']" let-item="item" let-index="index">
|
||||
{{value}} </ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="End time" field="end">
|
||||
<gui-grid-column header="End time" field="end">
|
||||
<ng-template let-value="item['ended']" let-item="item" let-index="index">
|
||||
{{value}}
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
<gui-grid-column header="Logs" field="mac" align="center">
|
||||
<ng-template let-value="item['result']" let-item="item" let-index="index">
|
||||
<button (click)="exportToCsv(value)" color="primary" cButton>download</button>
|
||||
<button (click)="exportToCsv(value)" color="primary" cButton>download</button>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
</gui-grid>
|
||||
<br/>
|
||||
<br />
|
||||
</c-input-group>
|
||||
<hr />
|
||||
</c-modal-body>
|
||||
|
@ -377,7 +379,8 @@
|
|||
</c-input-group>
|
||||
<c-input-group class="mb-3">
|
||||
<span cInputGroupText>peer ip</span>
|
||||
<select aria-label="Default select example" cFormControl [(ngModel)]="selected_device['editform']['peer_ip']" cSelect>
|
||||
<select aria-label="Default select example" cFormControl [(ngModel)]="selected_device['editform']['peer_ip']"
|
||||
cSelect>
|
||||
<option *ngFor="let o of selected_device['editform']['ips']" [value]="o">{{o}}</option>
|
||||
</select>
|
||||
</c-input-group>
|
||||
|
|
|
@ -173,6 +173,9 @@ export class DevicesComponent implements OnInit, OnDestroy {
|
|||
case "logauth":
|
||||
this.router.navigate(["/authlog", { devid: dev.id }]);
|
||||
break;
|
||||
case "devlogs":
|
||||
this.router.navigate(["/devlogs", { devid: dev.id }]);
|
||||
break;
|
||||
case "logacc":
|
||||
this.router.navigate(["/accountlog", { devid: dev.id }]);
|
||||
break;
|
||||
|
@ -403,6 +406,9 @@ export class DevicesComponent implements OnInit, OnDestroy {
|
|||
"danger"
|
||||
);
|
||||
}
|
||||
else if ("error" in res) {
|
||||
_self.show_toast("Error", res.error, "danger");
|
||||
}
|
||||
else{
|
||||
_self.show_toast("info", "Updating Firmwares Sent", "light");
|
||||
_self.initGridTable();}
|
||||
|
|
|
@ -3,8 +3,11 @@
|
|||
<c-card class="mb-4">
|
||||
<c-card-header>
|
||||
<c-row>
|
||||
<c-col xs [lg]="11">
|
||||
Devices
|
||||
<c-col xs [lg]="11" style="display: flex;flex-direction: column;align-items: flex-start;">
|
||||
<h5>Devices</h5>
|
||||
<c-alert color="warning" style="padding-top: 5px!important;font-size: 0.8rem;display: inline-block;" *ngIf="!filters['start_time'] && !filters['end_time']">
|
||||
<i class="fa-solid fa-triangle-exclamation mx-1"></i>Showing <strong>last 24 hours logs</strong> by default. Use filters to modify the date and time.
|
||||
</c-alert>
|
||||
</c-col>
|
||||
<c-col xs [lg]="1">
|
||||
<button (click)="toggleCollapse()" cButton class="me-1" color="primary"><i
|
||||
|
|
|
@ -125,4 +125,8 @@ padding: 0.3rem!important;
|
|||
.mat-mdc-form-field-infix{
|
||||
width:150px;
|
||||
}
|
||||
}
|
||||
.alert{
|
||||
padding: 5px!important;
|
||||
color:#644808!important;
|
||||
}
|
|
@ -7,6 +7,7 @@ import {
|
|||
GridModule,
|
||||
CollapseModule,
|
||||
DropdownModule,
|
||||
AlertModule,
|
||||
} from "@coreui/angular";
|
||||
import { NgxMatSelectSearchModule } from "ngx-mat-select-search";
|
||||
import { SyslogRoutingModule } from "./syslog-routing.module";
|
||||
|
@ -35,6 +36,7 @@ import { MatSelectModule } from "@angular/material/select";
|
|||
MatSelectModule,
|
||||
NgxMatSelectSearchModule,
|
||||
MatDatepickerModule,
|
||||
AlertModule,
|
||||
],
|
||||
declarations: [SyslogComponent],
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue