parent
e2dd56ab72
commit
e97c19f19d
@ -0,0 +1,46 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Manage Database Columns</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button (click)="dismissModal(false)">
|
||||
<ion-icon name="close"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col size="12">
|
||||
<ion-button (click)="onAddColumnClick()" fill="outline">Add Column</ion-button>
|
||||
<ion-button (click)="dismissModal(true)" color="success" fill="outline">Save</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
<ion-row
|
||||
*ngFor="let colSet of columnSets; let i = index"
|
||||
>
|
||||
<ion-col size="5">
|
||||
<ion-item>
|
||||
<ion-label position="floating">Field Label</ion-label>
|
||||
<ion-input type="text" required [(ngModel)]="columnSets[i].headerName"></ion-input>
|
||||
</ion-item>
|
||||
</ion-col>
|
||||
<ion-col size="5">
|
||||
<ion-item>
|
||||
<ion-label position="floating">Data Type</ion-label>
|
||||
<ion-select interface="popover" [(ngModel)]="columnSets[i].Type">
|
||||
<ion-select-option value="text">Text</ion-select-option>
|
||||
<ion-select-option value="number">Number</ion-select-option>
|
||||
<ion-select-option value="textarea">Text-Area</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
</ion-col>
|
||||
<ion-col size="2" align-items-center>
|
||||
<ion-button fill="outline" color="light" (click)="onDeleteClick(i)">
|
||||
<ion-icon color="danger" name="trash"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-content>
|
@ -0,0 +1,41 @@
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {ModalController} from '@ionic/angular';
|
||||
|
||||
@Component({
|
||||
selector: 'editor-database-columns',
|
||||
templateUrl: './columns.component.html',
|
||||
styleUrls: ['./columns.component.scss'],
|
||||
})
|
||||
export class ColumnsComponent implements OnInit {
|
||||
@Input() columnSets: Array<{headerName: string, field: string, Type: string}> = [];
|
||||
|
||||
constructor(
|
||||
protected modals: ModalController
|
||||
) { }
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
onAddColumnClick() {
|
||||
this.columnSets.push({headerName: '', field: '', Type: ''});
|
||||
}
|
||||
|
||||
dismissModal(doSave = true) {
|
||||
if ( doSave ) {
|
||||
this.columnSets = this.columnSets.map(x => {
|
||||
x.field = x.headerName;
|
||||
return x;
|
||||
})
|
||||
this.modals.dismiss(this.columnSets);
|
||||
} else {
|
||||
this.modals.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
onDeleteClick(i) {
|
||||
const newSets = this.columnSets.filter((x, index) => {
|
||||
return index !== i;
|
||||
});
|
||||
this.columnSets = newSets;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<div class="database-wrapper">
|
||||
<ion-toolbar>
|
||||
<ion-buttons>
|
||||
<ion-button (click)="onManageColumns()"><ion-icon name="build" color="primary"></ion-icon> Manage Columns</ion-button>
|
||||
<ion-button (click)="onInsertRow()"><ion-icon name="add-circle" color="success"></ion-icon> Insert Row</ion-button>
|
||||
<ion-button (click)="onRemoveRow()" [disabled]="lastClickRow < 0"><ion-icon name="remove-circle" color="danger"></ion-icon> Delete Row</ion-button>
|
||||
<ion-button (click)="onSyncRecords()"><ion-icon name="save" [color]="dirty ? 'warning' : 'success'"></ion-icon> Sync Records</ion-button>
|
||||
<ion-button (click)="onDropDatabase()"><ion-icon name="alert" color="danger"></ion-icon> Drop Database</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
<div class="grid-wrapper">
|
||||
<ag-grid-angular
|
||||
style="width: 100%; height: 500px;"
|
||||
class="ag-theme-balham"
|
||||
[rowData]="rowData"
|
||||
[columnDefs]="columnDefs"
|
||||
(rowClicked)="onRowClicked($event)"
|
||||
(cellValueChanged)="onCellValueChanged()"
|
||||
#agGridElement
|
||||
></ag-grid-angular>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,4 @@
|
||||
div.database-wrapper {
|
||||
border: 2px solid #8c8c8c;
|
||||
border-radius: 3px;
|
||||
}
|
@ -0,0 +1,219 @@
|
||||
import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
|
||||
import HostRecord from '../../../structures/HostRecord';
|
||||
import {ApiService} from '../../../service/api.service';
|
||||
import {Observable} from 'rxjs';
|
||||
import {AlertController, ModalController} from '@ionic/angular';
|
||||
import {ColumnsComponent} from './columns/columns.component';
|
||||
import {AgGridAngular} from 'ag-grid-angular';
|
||||
|
||||
@Component({
|
||||
selector: 'editor-database',
|
||||
templateUrl: './database.component.html',
|
||||
styleUrls: ['./database.component.scss'],
|
||||
})
|
||||
export class DatabaseComponent implements OnInit {
|
||||
@Input() hostRecord: HostRecord;
|
||||
@Output() hostRecordChange = new EventEmitter<HostRecord>();
|
||||
@Output() requestParentSave = new EventEmitter<DatabaseComponent>();
|
||||
@Output() requestParentDelete = new EventEmitter<DatabaseComponent>();
|
||||
@ViewChild('agGridElement', {static: false}) agGridElement: AgGridAngular;
|
||||
|
||||
public dbRecord: any;
|
||||
public pendingSetup = true;
|
||||
public dirty = false;
|
||||
protected lastClickRow = -1;
|
||||
|
||||
constructor(
|
||||
protected api: ApiService,
|
||||
protected modals: ModalController,
|
||||
protected alerts: AlertController,
|
||||
) { }
|
||||
|
||||
title = 'app';
|
||||
columnDefs = [];
|
||||
rowData = [];
|
||||
|
||||
ngOnInit() {
|
||||
this.getInitObservable().subscribe(() => {
|
||||
this.getColumnLoadObservable().subscribe(() => {
|
||||
this.getDataLoadObservable().subscribe(() => {
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onCellValueChanged() {
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
async onManageColumns() {
|
||||
const modal = await this.modals.create({
|
||||
component: ColumnsComponent,
|
||||
componentProps: {columnSets: this.columnDefs},
|
||||
});
|
||||
|
||||
modal.onDidDismiss().then(result => {
|
||||
if ( result.data ) {
|
||||
this.columnDefs = result.data.map(x => {
|
||||
x.editable = true;
|
||||
if ( x.Type === 'text' ) {
|
||||
x.editor = 'agTextCellEditor';
|
||||
} else if ( x.Type === 'number' ) {
|
||||
x.valueFormatter = (value) => {
|
||||
const num = parseFloat(value.value);
|
||||
if ( !isNaN(num) ) {
|
||||
return num;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
} else if ( x.Type === 'textarea' ) {
|
||||
x.editor = 'agPopupTextCellEditor';
|
||||
}
|
||||
return x;
|
||||
});
|
||||
const endpoint = `/db/${this.hostRecord.PageId}/${this.hostRecord.UUID}/set/${this.hostRecord.Value.Value}/columns`
|
||||
this.api.post(endpoint, {columns: this.columnDefs}).subscribe(res => {
|
||||
this.columnDefs = res.data;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await modal.present();
|
||||
}
|
||||
|
||||
onInsertRow() {
|
||||
this.rowData.push({});
|
||||
this.agGridElement.api.setRowData(this.rowData);
|
||||
}
|
||||
|
||||
async onRemoveRow() {
|
||||
const alert = await this.alerts.create({
|
||||
header: 'Are you sure?',
|
||||
message: `You are about to delete row ${this.lastClickRow + 1}. This cannot be undone.`,
|
||||
buttons: [
|
||||
{
|
||||
text: 'Keep It',
|
||||
role: 'cancel',
|
||||
},
|
||||
{
|
||||
text: 'Delete It',
|
||||
handler: () => {
|
||||
const newRows = this.rowData.filter((x, i) => {
|
||||
return i !== this.lastClickRow;
|
||||
});
|
||||
|
||||
this.rowData = newRows;
|
||||
this.agGridElement.api.setRowData(this.rowData);
|
||||
this.lastClickRow = -1;
|
||||
},
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
await alert.present();
|
||||
}
|
||||
|
||||
async onDropDatabase() {
|
||||
const alert = await this.alerts.create({
|
||||
header: 'Are you sure?',
|
||||
message: `You are about to delete this database and all its entries. This action cannot be undone.`,
|
||||
buttons: [
|
||||
{
|
||||
text: 'Keep It',
|
||||
role: 'cancel',
|
||||
},
|
||||
{
|
||||
text: 'Delete It',
|
||||
handler: async () => {
|
||||
this.api.post(`/db/${this.hostRecord.PageId}/${this.hostRecord.UUID}/drop/${this.hostRecord.Value.Value}`).subscribe();
|
||||
this.requestParentDelete.emit(this);
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await alert.present();
|
||||
}
|
||||
|
||||
onRowClicked($event) {
|
||||
this.lastClickRow = $event.rowIndex;
|
||||
}
|
||||
|
||||
getDataLoadObservable(): Observable<any> {
|
||||
return new Observable<any>(sub => {
|
||||
this.api.get(`/db/${this.hostRecord.PageId}/${this.hostRecord.UUID}/get/${this.hostRecord.Value.Value}/data`).subscribe(res => {
|
||||
this.rowData = res.data.map(x => x.RowData);
|
||||
this.agGridElement.api.setRowData(this.rowData);
|
||||
sub.next();
|
||||
sub.complete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onSyncRecords() {
|
||||
this.api.post(`/db/${this.hostRecord.PageId}/${this.hostRecord.UUID}/set/${this.hostRecord.Value.Value}/data`, this.rowData)
|
||||
.subscribe(res => {
|
||||
this.rowData = res.data.map(x => x.RowData);
|
||||
this.agGridElement.api.setRowData(this.rowData);
|
||||
this.dirty = false;
|
||||
});
|
||||
}
|
||||
|
||||
getColumnLoadObservable(): Observable<any> {
|
||||
return new Observable<any>(sub => {
|
||||
this.api.get(`/db/${this.hostRecord.PageId}/${this.hostRecord.UUID}/get/${this.hostRecord.Value.Value}/columns`).subscribe(res => {
|
||||
this.columnDefs = res.data.map(x => {
|
||||
x.editable = true;
|
||||
if ( x.Type === 'text' ) {
|
||||
x.editor = 'agTextCellEditor';
|
||||
} else if ( x.Type === 'number' ) {
|
||||
x.valueFormatter = (value) => {
|
||||
const num = parseFloat(value.value);
|
||||
if ( !isNaN(num) ) {
|
||||
return num;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
} else if ( x.Type === 'textarea' ) {
|
||||
x.editor = 'agPopupTextCellEditor';
|
||||
}
|
||||
return x;
|
||||
});
|
||||
sub.next();
|
||||
sub.complete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getInitObservable(): Observable<any> {
|
||||
return new Observable<any>(sub => {
|
||||
if ( this.hostRecord && this.pendingSetup ) {
|
||||
if ( !this.hostRecord.Value.Value ) {
|
||||
this.api.post(`/db/${this.hostRecord.PageId}/${this.hostRecord.UUID}/create`).subscribe(res => {
|
||||
this.dbRecord = res.data;
|
||||
this.hostRecord.Value.Mode = 'database';
|
||||
this.hostRecord.Value.Value = res.data.UUID;
|
||||
this.hostRecord.value = res.data.UUID;
|
||||
this.hostRecordChange.emit(this.hostRecord);
|
||||
this.pendingSetup = false;
|
||||
sub.next(true);
|
||||
sub.complete();
|
||||
});
|
||||
} else {
|
||||
this.api.get(`/db/${this.hostRecord.PageId}/${this.hostRecord.UUID}/get/${this.hostRecord.Value.Value}`).subscribe(res => {
|
||||
this.dbRecord = res.data;
|
||||
this.pendingSetup = false;
|
||||
sub.next(true);
|
||||
sub.complete();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.pendingSetup = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
|
||||
import { HostComponent } from './host.component';
|
||||
|
||||
describe('HostComponent', () => {
|
||||
let component: HostComponent;
|
||||
let fixture: ComponentFixture<HostComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ HostComponent ],
|
||||
imports: [IonicModule.forRoot()]
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(HostComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
Loading…
Reference in new issue