mirror of
https://github.com/MikroWizard/mikrofront.git
synced 2025-07-10 19:14:20 +02:00
Better backup list view and filtering
This commit is contained in:
parent
7e977823c5
commit
6cc1060e36
8 changed files with 297 additions and 49 deletions
|
@ -396,23 +396,22 @@ export class dataProvider {
|
|||
return this.MikroWizardRPC.sendJsonRequest("/api/firmware/get_firms", data);
|
||||
}
|
||||
|
||||
get_backups(devid:Number=0,page:Number,size:Number,search:any) {
|
||||
var data = {
|
||||
'devid':devid,
|
||||
'page':page,
|
||||
'size':size,
|
||||
'search':search
|
||||
}
|
||||
get_backups(data:any) {
|
||||
return this.MikroWizardRPC.sendJsonRequest("/api/backup/list", data);
|
||||
}
|
||||
|
||||
|
||||
get_backup(id:number){
|
||||
var data = {
|
||||
'id':id
|
||||
}
|
||||
return this.MikroWizardRPC.sendJsonRequest("/api/backup/get", data);
|
||||
}
|
||||
restore_backup(id:number){
|
||||
var data = {
|
||||
'backupid':id
|
||||
}
|
||||
return this.MikroWizardRPC.sendJsonRequest("/api/backup/restore", data);
|
||||
}
|
||||
|
||||
get_downloadable_firms() {
|
||||
|
||||
|
|
|
@ -189,8 +189,6 @@ export class MikroWizardProvider {
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public getUserContext(context: any) {
|
||||
localStorage.setItem("user_context", JSON.stringify(context));
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import { AuthRoutingModule } from './auth-routing.module';
|
|||
import { AuthComponent } from './auth.component';
|
||||
import { GuiGridModule } from '@generic-ui/ngx-grid';
|
||||
|
||||
import { faCoffee } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
import {MatDatepickerModule} from '@angular/material/datepicker';
|
||||
|
|
|
@ -1,13 +1,62 @@
|
|||
<c-row>
|
||||
<c-col xs>
|
||||
<c-card class="mb-4">
|
||||
<c-card-header>Backups</c-card-header>
|
||||
<c-card-header>
|
||||
<c-row>
|
||||
<c-col xs [lg]="8">
|
||||
Backups <c-badge color="warning" *ngIf="devid!=0">Filtered Result For Device ID {{devid}}</c-badge>
|
||||
</c-col>
|
||||
<c-col xs [lg]="3">
|
||||
<c-row>
|
||||
<c-col>
|
||||
<ng-container *ngIf="compareitems.length>0">
|
||||
<div>
|
||||
<c-badge color="dark" style="font-size: 0.7rem;"
|
||||
*ngFor="let item of compareitems;index as i">{{item.id}}:{{item.devname}} {{item.createdC}} <span
|
||||
style="cursor: pointer;" (click)="delete_compare(i)">X</span></c-badge>
|
||||
</div>
|
||||
</ng-container>
|
||||
</c-col>
|
||||
<c-col style="padding: 0;">
|
||||
<button *ngIf="compareitems.length>1" (click)="start_compare()" cButton class="me-1"
|
||||
color="primary">Compare</button>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</c-col>
|
||||
<c-col styyle="border-left: 1px solid #ccc;" xs [lg]="1">
|
||||
<button (click)="toggleCollapse()" cButton class="me-1" color="primary"><i
|
||||
class="fa-solid fa-filter mr-1"></i>Filter</button>
|
||||
</c-col>
|
||||
</c-row>
|
||||
</c-card-header>
|
||||
<c-card-body>
|
||||
<c-badge color="warning" *ngIf="devid!=0">Filtered Result For Device ID {{devid}}</c-badge>
|
||||
|
||||
<gui-grid [source]="source" [searching]="searching" [paging]="paging" [columnMenu]="columnMenu"
|
||||
[sorting]="sorting" [infoPanel]="infoPanel" [columnMenu]="columnMenu" [sorting]="sorting"
|
||||
[infoPanel]="infoPanel" [autoResizeWidth]=true>
|
||||
<c-row>
|
||||
<div [visible]="filters_visible" cCollapse>
|
||||
<c-col xs [lg]="12" class="example-form">
|
||||
<mat-form-field>
|
||||
<mat-label>Start date</mat-label>
|
||||
<input matInput [matDatepicker]="picker1" (dateChange)="reinitgrid('start',$event)"
|
||||
[(ngModel)]="filters['start_time']" />
|
||||
<mat-datepicker-toggle matIconSuffix [for]="picker1"></mat-datepicker-toggle>
|
||||
<mat-datepicker #picker1></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>End date</mat-label>
|
||||
<input matInput [matDatepicker]="picker2" (dateChange)="reinitgrid('end',$event)"
|
||||
[(ngModel)]="filters['end_time']" />
|
||||
<mat-datepicker-toggle matIconSuffix [for]="picker2"></mat-datepicker-toggle>
|
||||
<mat-datepicker #picker2></mat-datepicker>
|
||||
</mat-form-field>
|
||||
<mat-form-field *ngIf="ispro">
|
||||
<mat-label>Config search</mat-label>
|
||||
<input (ngModelChange)="reinitgrid('search',$event)" [(ngModel)]="filters['search']" matInput>
|
||||
</mat-form-field>
|
||||
</c-col>
|
||||
</div>
|
||||
</c-row>
|
||||
<gui-grid [source]="source" [paging]="paging" [columnMenu]="columnMenu" [sorting]="sorting"
|
||||
[infoPanel]="infoPanel" [columnMenu]="columnMenu" [sorting]="sorting" [infoPanel]="infoPanel"
|
||||
[autoResizeWidth]=true>
|
||||
<gui-grid-column header="#No" type="NUMBER" field="index" width=25 align="CENTER">
|
||||
<ng-template let-value="item.index" let-item="item" let-index="index">
|
||||
{{value}}
|
||||
|
@ -40,8 +89,10 @@
|
|||
</gui-grid-column>
|
||||
<gui-grid-column header="Action" field="id">
|
||||
<ng-template let-value="item.id" let-item="item" let-index="index">
|
||||
<button cButton color="info" size="sm" (click)="ShowBackup(item.id)" class="mx-1"><i
|
||||
<button cButton color="info" size="sm" (click)="ShowBackup(item)" class="mx-1"><i
|
||||
style="margin: 1px 5px;color:#ffffff;" class="fa-solid fa-eye"></i>Show backup</button>
|
||||
<button *ngIf="ispro" color="info" size="sm" (click)="add_for_compare(item)" class="mx-1"><i
|
||||
style="margin: 1px 5px;color:#ffffff;" class="fa-solid fa-eye"></i>Compare</button>
|
||||
</ng-template>
|
||||
</gui-grid-column>
|
||||
</gui-grid>
|
||||
|
@ -50,19 +101,94 @@
|
|||
</c-col>
|
||||
</c-row>
|
||||
|
||||
<c-modal #BakcupModal backdrop="static" [(visible)]="BakcupModalVisible" id="BakcupModal" size="xl">
|
||||
<c-modal #BakcupModal backdrop="static" [(visible)]="BakcupModalVisible" id="BakcupModal" [fullscreen]="true">
|
||||
<c-modal-header>
|
||||
<h6 cModalTitle>Please Confirm Action </h6>
|
||||
<button [cModalToggle]="BakcupModal.id" cButtonClose></button>
|
||||
</c-modal-header>
|
||||
<c-modal-body>
|
||||
<pre>
|
||||
<pre style="height: 100%;">
|
||||
<code *ngIf="!loading" language="properties" style="height:70vh" [highlight]="codeForHighlightAuto"
|
||||
lineNumbers></code></pre>
|
||||
</c-modal-body>
|
||||
<c-modal-footer>
|
||||
<c-modal-footer style="justify-content: space-between;">
|
||||
|
||||
<button [cdkCopyToClipboard]="codeForHighlightAuto" [style.background-color]="copy_msg ? 'green' : null" (click)="copy_this()" cButton color="secondary">
|
||||
<i class="fa-regular fa-copy"></i> To clipboard
|
||||
</button>
|
||||
<div>
|
||||
<button (click)="restore_backup(false)" class=" mx-3" cButton color="danger">
|
||||
Restore this
|
||||
</button>
|
||||
<button [cModalToggle]="BakcupModal.id" cButton color="info">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</c-modal-footer>
|
||||
</c-modal>
|
||||
|
||||
<c-modal #CompareModal backdrop="static" [(visible)]="CompareModalVisible" [fullscreen]="true" id="CompareModal">
|
||||
<c-modal-header>
|
||||
<h6 cModalTitle>Comparing Configs </h6>
|
||||
<c-form-check (click)="switch_compare_type()" sizing="xl" class="mx-5" switch>
|
||||
<h6>
|
||||
<input cFormCheckInput [checked]="compare_type=='unified'"
|
||||
style="width: 2.5rem;margin-left: -2.8em;cursor: pointer;" type="checkbox" />
|
||||
|
||||
<label cFormCheckLabel style="padding-top: calc((1.8em - 1rem) / 2);">
|
||||
<span *ngIf="compare_type=='sided'">Sided compare</span>
|
||||
<span *ngIf="compare_type=='unified'">Unified compare</span>
|
||||
</label>
|
||||
</h6>
|
||||
</c-form-check>
|
||||
<button [cModalToggle]="CompareModal.id" cButtonClose></button>
|
||||
</c-modal-header>
|
||||
<c-modal-body *ngIf="comparecontents.length>1">
|
||||
<h5>
|
||||
Comparing <c-badge color="dark" style="font-size: 0.8rem;">{{compareitems[0].id}}:{{compareitems[0].devname}}
|
||||
{{compareitems[0].createdC}} </c-badge> With
|
||||
<c-badge color="dark" style="font-size: 0.8rem;">{{compareitems[1].id}}:{{compareitems[1].devname}}
|
||||
{{compareitems[1].createdC}} </c-badge>
|
||||
</h5>
|
||||
<ngx-unified-diff *ngIf="compare_type=='unified'" class="ngx-diff-light-theme" [before]="comparecontents[0]"
|
||||
[after]="comparecontents[1]" />
|
||||
<ngx-side-by-side-diff *ngIf="compare_type=='sided'" class="ngx-diff-light-theme" [before]="comparecontents[0]"
|
||||
[after]="comparecontents[1]" />
|
||||
</c-modal-body>
|
||||
<c-modal-footer>
|
||||
<button [cModalToggle]="CompareModal.id" cButton color="info">
|
||||
Close
|
||||
</button>
|
||||
</c-modal-footer>
|
||||
</c-modal>
|
||||
|
||||
<c-modal #ConfirmModal backdrop="static" [(visible)]="ConfirmModalVisible" id="runConfirmModal">
|
||||
<c-modal-header>
|
||||
<h6 cModalTitle>Please Confirm Action </h6>
|
||||
<button [cModalToggle]="ConfirmModal.id" cButtonClose></button>
|
||||
</c-modal-header>
|
||||
<c-modal-body>
|
||||
<span>restore backup ?</span>
|
||||
<ng-container>
|
||||
Are you sure that You want to <code style="padding: 0!important;">Restore this configuration</code> on
|
||||
device?<br />
|
||||
<hr>
|
||||
<p class="text-danger">
|
||||
All Current device configuration will be reset:<br /><br />
|
||||
* All state data/history on router will be reset<br />
|
||||
* All other local users on router will be deleted<br />
|
||||
* After restore the password of the local user will be same as configured in MikroWizard<br />
|
||||
</p>
|
||||
</ng-container>
|
||||
</c-modal-body>
|
||||
<c-modal-footer>
|
||||
<button *ngIf="ispro" (click)="restore_backup(true)" cButton color="info">
|
||||
Restore this
|
||||
</button>
|
||||
<button cButton [cModalToggle]="ConfirmModal.id" color="info">
|
||||
Cancel
|
||||
</button>
|
||||
</c-modal-footer>
|
||||
</c-modal>
|
||||
|
||||
<c-toaster position="fixed" placement="top-end"></c-toaster>
|
|
@ -0,0 +1,4 @@
|
|||
@import 'ngx-diff/styles/default-theme';
|
||||
::ng-deep .modal-xl {
|
||||
--cui-modal-width: 90vw!important;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit } from "@angular/core";
|
||||
import { Component, OnInit, QueryList, ViewChildren } from "@angular/core";
|
||||
import { dataProvider } from "../../providers/mikrowizard/data";
|
||||
import { Router, ActivatedRoute } from "@angular/router";
|
||||
import { loginChecker } from "../../providers/login_checker";
|
||||
|
@ -14,23 +14,12 @@ import {
|
|||
GuiRowSelectionType,
|
||||
} from "@generic-ui/ngx-grid";
|
||||
import { formatInTimeZone } from "date-fns-tz";
|
||||
|
||||
interface IUser {
|
||||
name: string;
|
||||
state: string;
|
||||
registered: string;
|
||||
country: string;
|
||||
usage: number;
|
||||
period: string;
|
||||
payment: string;
|
||||
activity: string;
|
||||
avatar: string;
|
||||
status: string;
|
||||
color: string;
|
||||
}
|
||||
import { ToasterComponent } from "@coreui/angular";
|
||||
import { AppToastComponent } from "../toast-simple/toast.component";
|
||||
|
||||
@Component({
|
||||
templateUrl: "backups.component.html",
|
||||
styleUrls: ["backups.component.scss"],
|
||||
})
|
||||
export class BackupsComponent implements OnInit {
|
||||
public uid: number;
|
||||
|
@ -39,7 +28,13 @@ export class BackupsComponent implements OnInit {
|
|||
public filterText: string;
|
||||
public filters: any = {};
|
||||
public codeForHighlightAuto: string = "";
|
||||
|
||||
public ispro: boolean = false;
|
||||
public ConfirmModalVisible: boolean = false;
|
||||
public CompareModalVisible: boolean = false;
|
||||
public compareitems:any=[];
|
||||
public comparecontents:any=[];
|
||||
public compare_type="unified";
|
||||
public copy_msg:boolean=false;
|
||||
constructor(
|
||||
private data_provider: dataProvider,
|
||||
private router: Router,
|
||||
|
@ -56,6 +51,8 @@ export class BackupsComponent implements OnInit {
|
|||
_self.uid = res.uid;
|
||||
_self.uname = res.name;
|
||||
_self.tz = res.tz;
|
||||
_self.ispro = res['ISPRO']
|
||||
|
||||
const userId = _self.uid;
|
||||
|
||||
if (res.role != "admin") {
|
||||
|
@ -69,6 +66,7 @@ export class BackupsComponent implements OnInit {
|
|||
return value !== undefined && value !== null && value !== "";
|
||||
}
|
||||
}
|
||||
@ViewChildren(ToasterComponent) viewChildren!: QueryList<ToasterComponent>;
|
||||
|
||||
public source: Array<any> = [];
|
||||
public columns: Array<GuiColumn> = [];
|
||||
|
@ -77,7 +75,8 @@ export class BackupsComponent implements OnInit {
|
|||
public Selectedrows: any;
|
||||
public BakcupModalVisible: boolean = false;
|
||||
public devid: number = 0;
|
||||
|
||||
public filters_visible: boolean = false;
|
||||
public currentBackup:any=false;
|
||||
public sorting = {
|
||||
enabled: true,
|
||||
multiSorting: true,
|
||||
|
@ -94,6 +93,14 @@ export class BackupsComponent implements OnInit {
|
|||
display: GuiPagingDisplay.ADVANCED,
|
||||
};
|
||||
|
||||
toasterForm = {
|
||||
autohide: true,
|
||||
delay: 3000,
|
||||
position: "fixed",
|
||||
fade: true,
|
||||
closeButton: true,
|
||||
};
|
||||
|
||||
public columnMenu: GuiColumnMenu = {
|
||||
enabled: true,
|
||||
sort: true,
|
||||
|
@ -115,25 +122,124 @@ export class BackupsComponent implements OnInit {
|
|||
|
||||
ngOnInit(): void {
|
||||
this.devid = Number(this.route.snapshot.paramMap.get("devid"));
|
||||
if (this.devid > 0) {
|
||||
this.filters["devid"] = this.devid;
|
||||
}
|
||||
this.initGridTable();
|
||||
}
|
||||
|
||||
logger(item: any) {
|
||||
console.dir(item);
|
||||
}
|
||||
switch_compare_type(){
|
||||
if(this.compare_type=='unified')
|
||||
this.compare_type='sided'
|
||||
else
|
||||
this.compare_type='unified'
|
||||
}
|
||||
copy_this() {
|
||||
//show text copy to clipboard for 3 seconds
|
||||
this.copy_msg = true;
|
||||
setTimeout(() => {
|
||||
this.copy_msg = false;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
ShowBackup(id: number) {
|
||||
this.BakcupModalVisible = true;
|
||||
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;
|
||||
}
|
||||
|
||||
ShowBackup(backup: any) {
|
||||
var _self=this;
|
||||
this.loading = true;
|
||||
this.data_provider.get_backup(id).then((res) => {
|
||||
this.codeForHighlightAuto = res.content;
|
||||
this.loading = false;
|
||||
this.currentBackup = backup;
|
||||
this.data_provider.get_backup(backup.id).then((res) => {
|
||||
if('content' in res){
|
||||
_self.codeForHighlightAuto = res.content;
|
||||
_self.loading = false;
|
||||
_self.BakcupModalVisible = true;
|
||||
}
|
||||
else{
|
||||
this.show_toast('Error', 'Error loading backup file', 'danger')
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
toggleCollapse(): void {
|
||||
this.filters_visible = !this.filters_visible;
|
||||
}
|
||||
restore_backup(apply:boolean=false){
|
||||
var _slef=this;
|
||||
if (!apply){
|
||||
this.ConfirmModalVisible = true;
|
||||
return;
|
||||
}
|
||||
if (!this.currentBackup)
|
||||
return;
|
||||
if(apply){
|
||||
_slef.ConfirmModalVisible = false;
|
||||
_slef.BakcupModalVisible = true;
|
||||
this.show_toast('Success', 'Backup restored successfully', 'success')
|
||||
this.show_toast('Info', 'Wait for the router to reboot and apply config', 'info')
|
||||
this.data_provider.restore_backup(this.currentBackup.id).then((res) => {
|
||||
if ('status' in res){
|
||||
if(res['status']=='success'){
|
||||
this.show_toast('Success', 'Backup restored successfully', 'success')
|
||||
this.show_toast('Info', 'Wait for the router to reboot and apply config', 'info')
|
||||
}
|
||||
else
|
||||
this.show_toast('Error', 'Error restoring backup', 'danger')
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
start_compare(){
|
||||
var _self=this;
|
||||
this.comparecontents=[]
|
||||
this.compareitems.forEach((element:any) => {
|
||||
_self.data_provider.get_backup(element.id).then((res) => {
|
||||
if('content' in res){
|
||||
_self.comparecontents.push(res.content);
|
||||
}
|
||||
if(_self.comparecontents.length==_self.compareitems.length)
|
||||
_self.CompareModalVisible=true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
add_for_compare(item:any){
|
||||
//Only two items for compare
|
||||
if(this.compareitems.length<2)
|
||||
this.compareitems.filter((i:any)=>{
|
||||
return i.id!=item.id;
|
||||
}).length==this.compareitems.length && this.compareitems.push(item);
|
||||
else{
|
||||
//remove first element and add new item
|
||||
this.compareitems.shift();
|
||||
this.compareitems.push(item);
|
||||
}
|
||||
}
|
||||
delete_compare(i:number){
|
||||
//delete item index i from compareitems
|
||||
this.compareitems.splice(i,1);
|
||||
|
||||
}
|
||||
reinitgrid(field: string, $event: any) {
|
||||
if (field == "start") this.filters["start_time"] = $event.target.value;
|
||||
else if (field == "end") this.filters["end_time"] = $event.target.value;
|
||||
else if (field == "search") this.filters["search"] = $event;
|
||||
this.initGridTable();
|
||||
}
|
||||
|
||||
initGridTable(): void {
|
||||
var _self=this;
|
||||
this.data_provider.get_backups(this.devid, 0, 0, false).then((res) => {
|
||||
this.data_provider.get_backups(this.filters).then((res) => {
|
||||
let index = 1;
|
||||
this.source = res.map((d: any) => {
|
||||
d.index = index;
|
||||
|
@ -142,11 +248,9 @@ export class BackupsComponent implements OnInit {
|
|||
_self.tz,
|
||||
"yyyy-MM-dd HH:mm:ss XXX"
|
||||
);
|
||||
// d.created = [d.created.split("T")[0],d.created.split("T")[1].split(".")[0]].join(" ")
|
||||
index += 1;
|
||||
return d;
|
||||
});
|
||||
console.dir(this.source);
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,10 +10,19 @@ import {
|
|||
CollapseModule,
|
||||
BadgeModule,
|
||||
ModalModule,
|
||||
FormModule,
|
||||
ToastModule,
|
||||
} from "@coreui/angular";
|
||||
import { BackupsRoutingModule } from "./backups-routing.module";
|
||||
import { BackupsComponent } from "./backups.component";
|
||||
import { GuiGridModule } from "@generic-ui/ngx-grid";
|
||||
import { MatDatepickerModule } from "@angular/material/datepicker";
|
||||
import { MatInputModule } from "@angular/material/input";
|
||||
import { MatFormFieldModule } from "@angular/material/form-field";
|
||||
import { MatSelectModule } from "@angular/material/select";
|
||||
import { FormsModule } from "@angular/forms";
|
||||
import { UnifiedDiffComponent,SideBySideDiffComponent } from 'ngx-diff';
|
||||
import { ClipboardModule } from "@angular/cdk/clipboard";
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -21,6 +30,8 @@ import { GuiGridModule } from "@generic-ui/ngx-grid";
|
|||
CardModule,
|
||||
CommonModule,
|
||||
GridModule,
|
||||
FormModule,
|
||||
FormsModule,
|
||||
ButtonModule,
|
||||
ButtonModule,
|
||||
GuiGridModule,
|
||||
|
@ -30,6 +41,14 @@ import { GuiGridModule } from "@generic-ui/ngx-grid";
|
|||
HighlightAuto,
|
||||
HighlightLineNumbers,
|
||||
ModalModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatDatepickerModule,
|
||||
MatSelectModule,
|
||||
UnifiedDiffComponent,
|
||||
SideBySideDiffComponent,
|
||||
ToastModule,
|
||||
ClipboardModule
|
||||
],
|
||||
declarations: [BackupsComponent],
|
||||
})
|
||||
|
|
|
@ -57,7 +57,6 @@
|
|||
</c-row>
|
||||
<c-modal-header>
|
||||
|
||||
|
||||
<c-modal #EditTaskModal backdrop="static" size="xl" [(visible)]="EditTaskModalVisible" id="EditTaskModal">
|
||||
<c-modal-header>
|
||||
<h5 *ngIf="SelectedTask['action']=='edit'" cModalTitle>Editing device {{SelectedTask['name']}}</h5>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue