Added domains page
This commit is contained in:
parent
d1a32ec860
commit
43b911eb60
16
frontend/src/app/apitypes/Domain.apitype.ts
Normal file
16
frontend/src/app/apitypes/Domain.apitype.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
export class DomainApitype {
|
||||
|
||||
public id = 0;
|
||||
|
||||
public name = '';
|
||||
|
||||
public type = '';
|
||||
|
||||
public master: string = null;
|
||||
|
||||
public records = 0;
|
||||
|
||||
constructor(init: Object) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
12
frontend/src/app/apitypes/List.apitype.ts
Normal file
12
frontend/src/app/apitypes/List.apitype.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { PagingApitype } from './Paging.apitype';
|
||||
|
||||
export class ListApitype<T> {
|
||||
|
||||
public paging: PagingApitype;
|
||||
|
||||
public results: T[] = [];
|
||||
|
||||
constructor(init: Object) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
12
frontend/src/app/apitypes/Paging.apitype.ts
Normal file
12
frontend/src/app/apitypes/Paging.apitype.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
export class PagingApitype {
|
||||
|
||||
public page = 1;
|
||||
|
||||
public total = 1;
|
||||
|
||||
public pagesize: number = null;
|
||||
|
||||
constructor(init: Object) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,6 @@
|
|||
import { PagesizeComponent } from './partials/pagesize/pagesize.component';
|
||||
import { PagingComponent } from './partials/paging/paging.component';
|
||||
import { DomainsOperation } from './operations/domains.operations';
|
||||
import { PasswordOperation } from './operations/password.operations';
|
||||
import { AuthGuard } from './services/auth-guard.service';
|
||||
import { FocusDirective } from './utils/Focus.directive';
|
||||
|
@ -21,6 +24,7 @@ import { HttpService } from './services/http.service';
|
|||
import { SessionOperation } from './operations/session.operation';
|
||||
import { DomainsComponent } from './pages/domains/domains.component';
|
||||
import { PasswordComponent } from './pages/password/password.component';
|
||||
import { EditSlaveComponent } from './pages/edit-slave/edit-slave.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -35,7 +39,10 @@ import { PasswordComponent } from './pages/password/password.component';
|
|||
LoginComponent,
|
||||
DomainsComponent,
|
||||
FocusDirective,
|
||||
PasswordComponent
|
||||
PasswordComponent,
|
||||
PagingComponent,
|
||||
PagesizeComponent,
|
||||
EditSlaveComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
@ -48,6 +55,7 @@ import { PasswordComponent } from './pages/password/password.component';
|
|||
HttpService,
|
||||
SessionOperation,
|
||||
PasswordOperation,
|
||||
DomainsOperation,
|
||||
AuthGuard
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
|
|
|
@ -11,7 +11,13 @@ export class ModalOptionsDatatype {
|
|||
|
||||
public acceptClass: 'primary';
|
||||
|
||||
constructor(init: Object) {
|
||||
constructor(init: {
|
||||
heading: string
|
||||
body: string
|
||||
acceptText: string
|
||||
dismisText: string,
|
||||
acceptClass?: string
|
||||
}) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
|
35
frontend/src/app/operations/domains.operations.ts
Normal file
35
frontend/src/app/operations/domains.operations.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { DomainApitype } from './../apitypes/Domain.apitype';
|
||||
import { ListApitype } from './../apitypes/List.apitype';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpService } from '../services/http.service';
|
||||
import { StateService } from '../services/state.service';
|
||||
import { SessionApitype } from '../apitypes/Session.apitype';
|
||||
|
||||
@Injectable()
|
||||
export class DomainsOperation {
|
||||
|
||||
constructor(private http: HttpService, private gs: StateService) { }
|
||||
|
||||
public async getList(page?: number, pageSize?: number, query?: string, sort?: Array<String>): Promise<ListApitype<DomainApitype>> {
|
||||
try {
|
||||
return new ListApitype<DomainApitype>(await this.http.get('/domains', {
|
||||
page: page,
|
||||
pagesize: pageSize,
|
||||
query: query
|
||||
}));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return new ListApitype<DomainApitype>({ paging: {}, results: [] });
|
||||
}
|
||||
}
|
||||
|
||||
public async delete(domainId: number): Promise<boolean> {
|
||||
try {
|
||||
await this.http.delete(['/domains', domainId.toString()]);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,30 @@
|
|||
<p>
|
||||
domains works!
|
||||
</p>
|
||||
<app-pagesize [pagesizes]="gs.pageSizes" [currentPagesize]="gs.pageSize" (pagesizeChange)="onPagesizeChange($event)"></app-pagesize>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="table-responsive-lg">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-5">ID</th>
|
||||
<th>Name</th>
|
||||
<th class="w-25">Type</th>
|
||||
<th class="w-15">Records</th>
|
||||
<th *ngIf="gs.isAdmin" class="w-5"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let domain of domainList" class="cursor-pointer" (click)="onDomainClick(domain)">
|
||||
<td>{{ domain.id }}</td>
|
||||
<td>{{ domain.name }}</td>
|
||||
<td>{{ domain.type }}</td>
|
||||
<td>{{ domain.records }}</td>
|
||||
<td *ngIf="gs.isAdmin">
|
||||
<app-fa-icon class="cursor-pointer" icon="trash" (click)="onDeleteDomain(domain)"></app-fa-icon>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<app-paging [pagingInfo]="pagingInfo" [pageWidth]="3" (pageChange)="onPageChange($event)"></app-paging>
|
|
@ -1,12 +1,65 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { ModalOptionsDatatype } from './../../datatypes/modal-options.datatype';
|
||||
import { ModalService } from './../../services/modal.service';
|
||||
import { StateService } from './../../services/state.service';
|
||||
import { DomainApitype } from './../../apitypes/Domain.apitype';
|
||||
import { PagingApitype } from './../../apitypes/Paging.apitype';
|
||||
import { DomainsOperation } from './../../operations/domains.operations';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-domains',
|
||||
templateUrl: './domains.component.html',
|
||||
styleUrls: ['./domains.component.scss']
|
||||
selector: 'app-domains',
|
||||
templateUrl: './domains.component.html',
|
||||
styleUrls: ['./domains.component.scss']
|
||||
})
|
||||
export class DomainsComponent {
|
||||
export class DomainsComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
public pagingInfo = new PagingApitype({});
|
||||
public pageRequested = 1;
|
||||
|
||||
public domainList: DomainApitype[] = [];
|
||||
|
||||
constructor(private domains: DomainsOperation, public gs: StateService, private modal: ModalService) { }
|
||||
|
||||
public ngOnInit() {
|
||||
this.loadData();
|
||||
}
|
||||
|
||||
public async loadData() {
|
||||
const res = await this.domains.getList(this.pageRequested, this.gs.pageSize);
|
||||
|
||||
this.pagingInfo = res.paging;
|
||||
this.domainList = res.results;
|
||||
}
|
||||
|
||||
public async onDeleteDomain(domain: DomainApitype) {
|
||||
try {
|
||||
await this.modal.showMessage(new ModalOptionsDatatype({
|
||||
heading: 'Confirm deletion',
|
||||
body: 'Are you shure you want to delete ' + domain.name + '?',
|
||||
acceptText: 'Delete',
|
||||
dismisText: 'Cancel',
|
||||
acceptClass: 'danger'
|
||||
}));
|
||||
|
||||
await this.domains.delete(domain.id);
|
||||
|
||||
await this.loadData();
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
public async onPageChange(newPage: number) {
|
||||
this.pageRequested = newPage;
|
||||
await this.loadData();
|
||||
}
|
||||
|
||||
public async onPagesizeChange(pagesize: number) {
|
||||
this.gs.pageSize = pagesize;
|
||||
this.pageRequested = 1;
|
||||
await this.loadData();
|
||||
}
|
||||
|
||||
public async onDomainClick(domain: DomainApitype) {
|
||||
alert(domain.id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<app-alert-message>Username or password is invalid!</app-alert-message>
|
||||
</app-alert>
|
||||
|
||||
<button type="submit" class="btn btn-success float-right" [disabled]="!loginForm.valid">Login</button>
|
||||
<button type="submit" class="btn btn-primary float-right" [disabled]="!loginForm.valid">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<app-alert-message>Password change sucessfull.</app-alert-message>
|
||||
</app-alert>
|
||||
|
||||
<button type="submit" class="btn btn-success float-right" [disabled]="!passwordForm.valid">Change</button>
|
||||
<button type="submit" class="btn btn-primary float-right" [disabled]="!passwordForm.valid">Change</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
12
frontend/src/app/partials/pagesize/pagesize.component.html
Normal file
12
frontend/src/app/partials/pagesize/pagesize.component.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<div class="row">
|
||||
<nav class="mr-3 ml-auto">
|
||||
<ul class="pagination pagination-sm">
|
||||
<li class="disabled page-item">
|
||||
<span class="page-link">Entries per page</span>
|
||||
</li>
|
||||
<li *ngFor="let i of pagesizes" class="page-item" [class.active]="i === currentPagesize" (click)="newPagesize(i)">
|
||||
<span class="page-link">{{ i }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
23
frontend/src/app/partials/pagesize/pagesize.component.ts
Normal file
23
frontend/src/app/partials/pagesize/pagesize.component.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { PagingApitype } from './../../apitypes/Paging.apitype';
|
||||
import { Component, Input, EventEmitter, Output } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pagesize',
|
||||
templateUrl: './pagesize.component.html',
|
||||
styleUrls: ['./pagesize.component.scss']
|
||||
})
|
||||
export class PagesizeComponent {
|
||||
|
||||
@Input() pagesizes: Array<number>;
|
||||
|
||||
@Input() currentPagesize: number;
|
||||
|
||||
@Output() pagesizeChange = new EventEmitter<number>();
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public newPagesize(pagesize: number) {
|
||||
this.pagesizeChange.emit(pagesize);
|
||||
}
|
||||
}
|
21
frontend/src/app/partials/paging/paging.component.html
Normal file
21
frontend/src/app/partials/paging/paging.component.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
<div *ngIf="pagingInfo.total > 1" class="row">
|
||||
<nav class="mx-auto">
|
||||
<ul class="pagination">
|
||||
<li *ngIf="pagingInfo.page > 1" class="page-item" (click)="previous()">
|
||||
<app-fa-icon class="page-link" icon="angle-left"></app-fa-icon>
|
||||
</li>
|
||||
<li *ngIf="truncatesLeft()" class="page-item disabled">
|
||||
<app-fa-icon class="page-link" icon="ellipsis-h"></app-fa-icon>
|
||||
</li>
|
||||
<li *ngFor="let i of createNumbers()" class="page-item" [class.active]="i === pagingInfo.page" (click)="newPage(i)">
|
||||
<span class="page-link">{{ i }}</span>
|
||||
</li>
|
||||
<li *ngIf="truncatesRight()" class="page-item disabled">
|
||||
<app-fa-icon class="page-link" icon="ellipsis-h"></app-fa-icon>
|
||||
</li>
|
||||
<li *ngIf="pagingInfo.page < pagingInfo.total" class="page-item " (click)="next()">
|
||||
<app-fa-icon class="page-link " icon="angle-right "></app-fa-icon>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
49
frontend/src/app/partials/paging/paging.component.ts
Normal file
49
frontend/src/app/partials/paging/paging.component.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
import { PagingApitype } from './../../apitypes/Paging.apitype';
|
||||
import { Component, Input, EventEmitter, Output } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-paging',
|
||||
templateUrl: './paging.component.html',
|
||||
styleUrls: ['./paging.component.scss']
|
||||
})
|
||||
export class PagingComponent {
|
||||
|
||||
@Input() pagingInfo: PagingApitype;
|
||||
|
||||
@Input() pageWidth = 2;
|
||||
|
||||
@Output() pageChange = new EventEmitter<number>();
|
||||
|
||||
constructor() { }
|
||||
|
||||
public createNumbers(): Array<number> {
|
||||
const min = Math.max(1, this.pagingInfo.page - this.pageWidth);
|
||||
const max = Math.min(this.pagingInfo.total, this.pagingInfo.page + this.pageWidth);
|
||||
|
||||
const pages = [];
|
||||
for (let i = min; i <= max; i++) {
|
||||
pages.push(i);
|
||||
}
|
||||
return pages;
|
||||
}
|
||||
|
||||
public truncatesLeft(): boolean {
|
||||
return this.pagingInfo.page - this.pageWidth > 1;
|
||||
}
|
||||
|
||||
public truncatesRight(): boolean {
|
||||
return this.pagingInfo.page + this.pageWidth < this.pagingInfo.total;
|
||||
}
|
||||
|
||||
public previous(): void {
|
||||
this.pageChange.emit(this.pagingInfo.page - 1);
|
||||
}
|
||||
|
||||
public next(): void {
|
||||
this.pageChange.emit(this.pagingInfo.page + 1);
|
||||
}
|
||||
|
||||
public newPage(page: number) {
|
||||
this.pageChange.emit(page);
|
||||
}
|
||||
}
|
|
@ -18,6 +18,10 @@ export class HttpService {
|
|||
public async get(url: string, params: Object = {}): Promise<any> {
|
||||
const parts = [];
|
||||
for (const [k, v] of Object.entries(params)) {
|
||||
if (v === undefined || v === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let value;
|
||||
if (v instanceof Array) {
|
||||
value = v.join(',');
|
||||
|
|
|
@ -1,53 +1,68 @@
|
|||
import { SessionApitype } from './../apitypes/Session.apitype';
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class StateService {
|
||||
|
||||
public _isLoggedIn = false;
|
||||
private _isLoggedIn = false;
|
||||
get isLoggedIn(): boolean {
|
||||
return this._isLoggedIn;
|
||||
}
|
||||
set isLoggedIn(_isLoggedIn: boolean) {
|
||||
this._isLoggedIn = _isLoggedIn;
|
||||
this.saveSessionStorage();
|
||||
this.saveLocalStorage();
|
||||
}
|
||||
|
||||
public _isAdmin = false;
|
||||
private _isAdmin = false;
|
||||
get isAdmin(): boolean {
|
||||
return this._isAdmin;
|
||||
}
|
||||
set isAdmin(_isAdmin: boolean) {
|
||||
this._isAdmin = _isAdmin;
|
||||
this.saveSessionStorage();
|
||||
this.saveLocalStorage();
|
||||
}
|
||||
|
||||
public _apiToken: string = null;
|
||||
private _apiToken: string = null;
|
||||
get apiToken(): string {
|
||||
return this._apiToken;
|
||||
}
|
||||
set apiToken(_apiToken: string) {
|
||||
this._apiToken = _apiToken;
|
||||
this.saveSessionStorage();
|
||||
this.saveLocalStorage();
|
||||
}
|
||||
|
||||
public _isNative = false;
|
||||
private _isNative = false;
|
||||
get isNative(): boolean {
|
||||
return this._isNative;
|
||||
}
|
||||
set isNative(_isNative: boolean) {
|
||||
this._isNative = _isNative;
|
||||
this.saveSessionStorage();
|
||||
this.saveLocalStorage();
|
||||
}
|
||||
|
||||
private _pageSize = 25;
|
||||
get pageSize(): number {
|
||||
return this._pageSize;
|
||||
}
|
||||
set pageSize(_pageSize: number) {
|
||||
this._pageSize = _pageSize;
|
||||
this.saveLocalStorage();
|
||||
}
|
||||
|
||||
private _pageSizes = [5, 10, 25, 50, 100];
|
||||
get pageSizes(): Array<number> {
|
||||
return this._pageSizes;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.loadSessiontorage();
|
||||
this.loadLocalStorage();
|
||||
}
|
||||
|
||||
private saveSessionStorage() {
|
||||
sessionStorage.setItem('pdnsmanagerstate', JSON.stringify(this));
|
||||
private saveLocalStorage() {
|
||||
localStorage.setItem('pdnsmanagerstate', JSON.stringify(this));
|
||||
}
|
||||
|
||||
private loadSessiontorage() {
|
||||
Object.assign(this, JSON.parse(sessionStorage.getItem('pdnsmanagerstate')));
|
||||
private loadLocalStorage() {
|
||||
Object.assign(this, JSON.parse(localStorage.getItem('pdnsmanagerstate')));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,30 @@
|
|||
/* Bootstrap and customizations */
|
||||
$table-cell-padding: .60rem !default;
|
||||
|
||||
$sizes: (
|
||||
0: 0%,
|
||||
5: 5%,
|
||||
10: 10%,
|
||||
15: 15%,
|
||||
20: 20%,
|
||||
25: 25%,
|
||||
30: 30%,
|
||||
35: 35%,
|
||||
40: 40%,
|
||||
45: 45%,
|
||||
50: 50%,
|
||||
55: 55%,
|
||||
60: 60%,
|
||||
65: 65%,
|
||||
70: 70%,
|
||||
75: 75%,
|
||||
80: 80%,
|
||||
85: 85%,
|
||||
90: 90%,
|
||||
95: 95%,
|
||||
100: 100%
|
||||
);
|
||||
|
||||
@import '~bootstrap/scss/bootstrap.scss';
|
||||
|
||||
/* Add font awesome */
|
||||
|
@ -26,4 +52,9 @@ $fa-font-path: "~font-awesome/fonts";
|
|||
|
||||
.ng-dirty.ng-valid.auto-validstate {
|
||||
@extend .is-valid;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cursor pointer class */
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue