diff --git a/frontend/app/features/dashboard/declarations.ts b/frontend/app/features/dashboard/declarations.ts index 5adab422b..74d81eefd 100644 --- a/frontend/app/features/dashboard/declarations.ts +++ b/frontend/app/features/dashboard/declarations.ts @@ -17,4 +17,5 @@ export * from './pages/cards/github-card.component'; export * from './pages/cards/history-card.component'; export * from './pages/cards/schema-card.component'; export * from './pages/cards/support-card.component'; +export * from './pages/dashboard-config.component'; export * from './pages/dashboard-page.component'; diff --git a/frontend/app/features/dashboard/module.ts b/frontend/app/features/dashboard/module.ts index 0de5d53bb..f1a3e785a 100644 --- a/frontend/app/features/dashboard/module.ts +++ b/frontend/app/features/dashboard/module.ts @@ -12,7 +12,7 @@ import { RouterModule, Routes } from '@angular/router'; import { SqxFrameworkModule, SqxSharedModule } from '@app/shared'; import { GridsterModule } from 'angular-gridster2'; import { ChartModule } from 'angular2-chartjs'; -import { ApiCallsCardComponent, ApiCallsSummaryCardComponent, ApiCardComponent, ApiPerformanceCardComponent, ApiTrafficCardComponent, AssetUploadsCountCardComponent, AssetUploadsSizeCardComponent, AssetUploadsSizeSummaryCardComponent, DashboardPageComponent, GithubCardComponent, HistoryCardComponent, SchemaCardComponent, SupportCardComponent } from './declarations'; +import { ApiCallsCardComponent, ApiCallsSummaryCardComponent, ApiCardComponent, ApiPerformanceCardComponent, ApiTrafficCardComponent, AssetUploadsCountCardComponent, AssetUploadsSizeCardComponent, AssetUploadsSizeSummaryCardComponent, DashboardConfigComponent, DashboardPageComponent, GithubCardComponent, HistoryCardComponent, SchemaCardComponent, SupportCardComponent } from './declarations'; const routes: Routes = [ { @@ -38,6 +38,7 @@ const routes: Routes = [ AssetUploadsCountCardComponent, AssetUploadsSizeCardComponent, AssetUploadsSizeSummaryCardComponent, + DashboardConfigComponent, DashboardPageComponent, GithubCardComponent, HistoryCardComponent, diff --git a/frontend/app/features/dashboard/pages/dashboard-config.component.html b/frontend/app/features/dashboard/pages/dashboard-config.component.html new file mode 100644 index 000000000..9389a575e --- /dev/null +++ b/frontend/app/features/dashboard/pages/dashboard-config.component.html @@ -0,0 +1,55 @@ + + + + + + + + + + + + Edit Config + + + +
+ +
+
+ + + + + +
+
+
\ No newline at end of file diff --git a/frontend/app/features/dashboard/pages/dashboard-config.component.scss b/frontend/app/features/dashboard/pages/dashboard-config.component.scss new file mode 100644 index 000000000..ed8c57ec1 --- /dev/null +++ b/frontend/app/features/dashboard/pages/dashboard-config.component.scss @@ -0,0 +1,5 @@ +.json-editor ::ng-deep { + .editor { + @include absolute(0, 0, 0, 0); + } +} \ No newline at end of file diff --git a/frontend/app/features/dashboard/pages/dashboard-config.component.ts b/frontend/app/features/dashboard/pages/dashboard-config.component.ts new file mode 100644 index 000000000..761495422 --- /dev/null +++ b/frontend/app/features/dashboard/pages/dashboard-config.component.ts @@ -0,0 +1,125 @@ +/* + * Squidex Headless CMS + * + * @license + * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. + */ + +// tslint:disable: readonly-array + +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; +import { AppDto, AppsState, AuthService, DialogModel, DialogService, fadeAnimation, ModalModel, Types, UIState } from '@app/shared'; +import { GridsterItem } from 'angular-gridster2'; +import { take } from 'rxjs/operators'; + +@Component({ + selector: 'sqx-dashboard-config', + styleUrls: ['./dashboard-config.component.scss'], + templateUrl: './dashboard-config.component.html', + animations: [ + fadeAnimation + ] +}) +export class DashboardConfigComponent implements OnChanges { + @Input() + public app: AppDto[]; + + @Input() + public config: GridsterItem[]; + + @Output() + public configChange = new EventEmitter(); + + public configDefaults = DEFAULT_CONFIG; + + public expertDialog = new DialogModel(); + public expertConfig: GridsterItem[]; + + public dropdownModal = new ModalModel(); + + constructor( + public readonly appsState: AppsState, + public readonly authState: AuthService, + private readonly dialogs: DialogService, + private readonly uiState: UIState + ) { + } + + public ngOnChanges(changes: SimpleChanges) { + if (changes['app']) { + this.uiState.getUser('dashboard.grid', DEFAULT_CONFIG).pipe(take(1)) + .subscribe(dto => { + this.setConfig(dto); + }); + } + } + + private setConfig(config: any) { + if (!Types.isArrayOfObject(config)) { + config = DEFAULT_CONFIG; + } + + this.configChange.emit(Types.clone(config)); + } + + public startExpertMode() { + this.dropdownModal.hide(); + + this.expertConfig = Types.clone(this.config); + this.expertDialog.show(); + } + + public completeExpertMode() { + this.setConfig(this.expertConfig); + + this.expertConfig = null!; + this.expertDialog.hide(); + + this.saveConfig(); + } + + public resetConfig() { + this.setConfig(Types.clone(DEFAULT_CONFIG)); + + this.saveConfig(); + } + + public saveConfig() { + this.uiState.set('dashboard.grid', this.config, true); + + this.dialogs.notifyInfo('Configuration saved.'); + } + + public addOrRemove(item: GridsterItem) { + const current = this.config.find(x => x.type === item.type); + + if (current) { + this.config.splice(this.config.indexOf(current), 1); + } else { + this.config.push(Types.clone(item)); + } + } + + public isSelected(item: GridsterItem) { + return this.config.find(x => x.type === item.type); + } +} + +const DEFAULT_CONFIG: GridsterItem[] = [ + { cols: 1, rows: 1, x: 0, y: 0, type: 'schemas', name: 'Schema' }, + { cols: 1, rows: 1, x: 1, y: 0, type: 'api', name: 'API Documentation' }, + { cols: 1, rows: 1, x: 2, y: 0, type: 'support', name: 'Support' }, + { cols: 1, rows: 1, x: 3, y: 0, type: 'github', name: 'Github' }, + + { cols: 2, rows: 1, x: 0, y: 1, type: 'api-calls', name: 'API Calls Chart' }, + { cols: 2, rows: 1, x: 2, y: 1, type: 'api-performance', name: 'API Performance Chart' }, + + { cols: 1, rows: 1, x: 0, y: 2, type: 'api-calls-summary', name: 'API Calls Summary' }, + { cols: 2, rows: 1, x: 1, y: 2, type: 'asset-uploads-count', name: 'Asset Uploads Count Chart' }, + { cols: 1, rows: 1, x: 2, y: 2, type: 'asset-uploads-size-summary', name: 'Asset Uploads Size Chart' }, + + { cols: 2, rows: 1, x: 0, y: 3, type: 'asset-uploads-size', name: 'Asset Total Storage Size' }, + { cols: 2, rows: 1, x: 2, y: 3, type: 'api-traffic', name: 'API Traffic Chart' }, + + { cols: 2, rows: 1, x: 0, y: 4, type: 'history', name: 'History' } +]; \ No newline at end of file diff --git a/frontend/app/features/dashboard/pages/dashboard-page.component.html b/frontend/app/features/dashboard/pages/dashboard-page.component.html index 919bbe460..b2d8a9333 100644 --- a/frontend/app/features/dashboard/pages/dashboard-page.component.html +++ b/frontend/app/features/dashboard/pages/dashboard-page.component.html @@ -11,7 +11,7 @@ - +
- - - - - +
\ No newline at end of file diff --git a/frontend/app/features/dashboard/pages/dashboard-page.component.ts b/frontend/app/features/dashboard/pages/dashboard-page.component.ts index e8008172d..730811626 100644 --- a/frontend/app/features/dashboard/pages/dashboard-page.component.ts +++ b/frontend/app/features/dashboard/pages/dashboard-page.component.ts @@ -8,47 +8,10 @@ // tslint:disable: readonly-array import { AfterViewInit, Component, OnInit, Renderer2, ViewChild } from '@angular/core'; -import { AppsState, AuthService, CallsUsageDto, CurrentStorageDto, DateTime, DialogService, fadeAnimation, LocalStoreService, ModalModel, ResourceOwner, StorageUsagePerDateDto, UIState, UsagesService } from '@app/shared'; +import { AppsState, AuthService, CallsUsageDto, CurrentStorageDto, DateTime, fadeAnimation, LocalStoreService, ResourceOwner, StorageUsagePerDateDto, UsagesService } from '@app/shared'; import { GridsterComponent, GridsterItem, GridType } from 'angular-gridster2'; import { switchMap } from 'rxjs/operators'; -const DEFAULT_CONFIG: ReadonlyArray = [ - { cols: 1, rows: 1, x: 0, y: 0, type: 'schemas', name: 'Schema' }, - { cols: 1, rows: 1, x: 1, y: 0, type: 'api', name: 'API Documentation' }, - { cols: 1, rows: 1, x: 2, y: 0, type: 'support', name: 'Support' }, - { cols: 1, rows: 1, x: 3, y: 0, type: 'github', name: 'Github' }, - - { cols: 2, rows: 1, x: 0, y: 1, type: 'api-calls', name: 'API Calls Chart' }, - { cols: 2, rows: 1, x: 2, y: 1, type: 'api-performance', name: 'API Performance Chart' }, - - { cols: 1, rows: 1, x: 0, y: 2, type: 'api-calls-summary', name: 'API Calls Summary' }, - { cols: 2, rows: 1, x: 1, y: 2, type: 'asset-uploads-count', name: 'Asset Uploads Count Chart' }, - { cols: 1, rows: 1, x: 2, y: 2, type: 'asset-uploads-size-summary', name: 'Asset Uploads Size Chart' }, - - { cols: 2, rows: 1, x: 0, y: 3, type: 'asset-uploads-size', name: 'Asset Total Storage Size' }, - { cols: 2, rows: 1, x: 2, y: 3, type: 'api-traffic', name: 'API Traffic Chart' }, - - { cols: 2, rows: 1, x: 0, y: 4, type: 'history', name: 'History' } -]; - -const DEFAULT_OPTIONS = { - displayGrid: 'onDrag&Resize', - fixedColWidth: 254, - fixedRowHeight: 254, - gridType: GridType.Fixed, - outerMargin: true, - outerMarginBottom: 16, - outerMarginLeft: 16, - outerMarginRight: 16, - outerMarginTop: 120, - draggable: { - enabled: true - }, - resizable: { - enabled: false - } -}; - @Component({ selector: 'sqx-dashboard-page', styleUrls: ['./dashboard-page.component.scss'], @@ -69,21 +32,16 @@ export class DashboardPageComponent extends ResourceOwner implements AfterViewIn public callsUsage: CallsUsageDto; public gridConfig: GridsterItem[]; - public gridModal = new ModalModel(); public gridOptions = DEFAULT_OPTIONS; - public allItems = DEFAULT_CONFIG; - public isScrolled = false; constructor( public readonly appsState: AppsState, public readonly authState: AuthService, private readonly renderer: Renderer2, - private readonly dialogs: DialogService, private readonly usagesService: UsagesService, - private readonly localStore: LocalStoreService, - private readonly uiState: UIState + private readonly localStore: LocalStoreService ) { super(); @@ -94,12 +52,6 @@ export class DashboardPageComponent extends ResourceOwner implements AfterViewIn const dateTo = DateTime.today().toStringFormat('yyyy-MM-dd'); const dateFrom = DateTime.today().addDays(-20).toStringFormat('yyyy-MM-dd'); - this.own( - this.uiState.getUser('dashboard.grid', DEFAULT_CONFIG) - .subscribe(dto => { - this.gridConfig = [...dto]; - })); - this.own( this.appsState.selectedApp.pipe( switchMap(app => this.usagesService.getTodayStorage(app.name))) @@ -134,33 +86,27 @@ export class DashboardPageComponent extends ResourceOwner implements AfterViewIn this.isStacked = value; } - public resetConfig() { - this.gridConfig = [...this.allItems]; - - this.saveConfig(); - } - - public saveConfig() { - this.uiState.set('dashboard.grid', this.gridConfig, true); - - this.dialogs.notifyInfo('Configuration saved.'); - } + public changeConfig(config: GridsterItem[]) { + this.gridConfig = config; - public isSelected(item: GridsterItem) { - return this.gridConfig && this.gridConfig.find(x => x.type === item.type); + this.grid.updateGrid(); } +} - public addOrRemove(item: GridsterItem) { - const found = this.gridConfig.find(x => x.type === item.type); - - if (found) { - this.gridConfig.splice(this.gridConfig.indexOf(found), 1); - } else { - this.gridConfig.push({ ...item }); - } - } - - public trackByItem(index: number, item: GridsterItem) { - return item.type; +const DEFAULT_OPTIONS = { + displayGrid: 'onDrag&Resize', + fixedColWidth: 254, + fixedRowHeight: 254, + gridType: GridType.Fixed, + outerMargin: true, + outerMarginBottom: 16, + outerMarginLeft: 16, + outerMarginRight: 16, + outerMarginTop: 120, + draggable: { + enabled: true + }, + resizable: { + enabled: false } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/frontend/app/framework/angular/modals/modal-dialog.component.html b/frontend/app/framework/angular/modals/modal-dialog.component.html index 1dc65839a..492ff363b 100644 --- a/frontend/app/framework/angular/modals/modal-dialog.component.html +++ b/frontend/app/framework/angular/modals/modal-dialog.component.html @@ -21,10 +21,8 @@ - diff --git a/frontend/app/framework/utils/types.ts b/frontend/app/framework/utils/types.ts index 2c030f00d..86dd9a830 100644 --- a/frontend/app/framework/utils/types.ts +++ b/frontend/app/framework/utils/types.ts @@ -56,6 +56,10 @@ export module Types { return isArrayOf(value, v => isNumber(v)); } + export function isArrayOfObject(value: any): value is Array { + return isArrayOf(value, v => isObject(v)); + } + export function isArrayOfString(value: any): value is Array { return isArrayOf(value, v => isString(v)); }