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