Browse Source

Feature/schemas ui (#434)

* Better schema UI
pull/436/head
Sebastian Stehle 6 years ago
committed by GitHub
parent
commit
4dc5b137bb
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      frontend/app/features/schemas/declarations.ts
  2. 54
      frontend/app/features/schemas/module.ts
  3. 21
      frontend/app/features/schemas/pages/schema/schema-edit-form.component.html
  4. 5
      frontend/app/features/schemas/pages/schema/schema-edit-form.component.scss
  5. 15
      frontend/app/features/schemas/pages/schema/schema-edit-form.component.ts
  6. 25
      frontend/app/features/schemas/pages/schema/schema-export-form.component.html
  7. 23
      frontend/app/features/schemas/pages/schema/schema-export-form.component.scss
  8. 25
      frontend/app/features/schemas/pages/schema/schema-export-form.component.ts
  9. 36
      frontend/app/features/schemas/pages/schema/schema-fields.component.html
  10. 22
      frontend/app/features/schemas/pages/schema/schema-fields.component.scss
  11. 60
      frontend/app/features/schemas/pages/schema/schema-fields.component.ts
  12. 94
      frontend/app/features/schemas/pages/schema/schema-page.component.html
  13. 49
      frontend/app/features/schemas/pages/schema/schema-page.component.scss
  14. 42
      frontend/app/features/schemas/pages/schema/schema-page.component.ts
  15. 34
      frontend/app/features/schemas/pages/schema/schema-preview-urls-form.component.html
  16. 6
      frontend/app/features/schemas/pages/schema/schema-preview-urls-form.component.scss
  17. 19
      frontend/app/features/schemas/pages/schema/schema-preview-urls-form.component.ts
  18. 23
      frontend/app/features/schemas/pages/schema/schema-scripts-form.component.html
  19. 23
      frontend/app/features/schemas/pages/schema/schema-scripts-form.component.scss
  20. 19
      frontend/app/features/schemas/pages/schema/schema-scripts-form.component.ts
  21. 15
      frontend/app/features/settings/pages/more/more-page.component.html
  22. 5
      frontend/app/features/settings/pages/more/more-page.component.scss
  23. 2
      frontend/app/framework/angular/panel.component.html
  24. 4
      frontend/app/shared/services/help.service.spec.ts
  25. 2
      frontend/app/shared/services/help.service.ts

1
frontend/app/features/schemas/declarations.ts

@ -34,6 +34,7 @@ export * from './pages/schema/field-wizard.component';
export * from './pages/schema/field.component';
export * from './pages/schema/schema-edit-form.component';
export * from './pages/schema/schema-export-form.component';
export * from './pages/schema/schema-fields.component';
export * from './pages/schema/schema-page.component';
export * from './pages/schema/schema-preview-urls-form.component';
export * from './pages/schema/schema-scripts-form.component';

54
frontend/app/features/schemas/module.ts

@ -39,6 +39,7 @@ import {
ReferencesValidationComponent,
SchemaEditFormComponent,
SchemaExportFormComponent,
SchemaFieldsComponent,
SchemaFormComponent,
SchemaPageComponent,
SchemaPreviewUrlsFormComponent,
@ -57,8 +58,15 @@ const routes: Routes = [
children: [
{
path: ':schemaName',
component: SchemaPageComponent,
canActivate: [SchemaMustExistGuard],
component: SchemaPageComponent,
children: [
{
path: '',
redirectTo: 'fields'
},
{
path: 'fields',
children: [
{
path: 'help',
@ -68,7 +76,46 @@ const routes: Routes = [
}
}
]
}]
},
{
path: 'scripts',
children: [
{
path: 'help',
component: HelpComponent,
data: {
helpPage: '05-integrated/scripts'
}
}
]
},
{
path: 'json',
children: [
{
path: 'help',
component: HelpComponent,
data: {
helpPage: '05-integrated/schema-json'
}
}
]
},
{
path: 'more',
children: [
{
path: 'help',
component: HelpComponent,
data: {
helpPage: '05-integrated/preview'
}
}
]
}
]
}
]
}
];
@ -90,8 +137,8 @@ const routes: Routes = [
DateTimeUIComponent,
DateTimeValidationComponent,
FieldComponent,
FieldFormComponent,
FieldFormCommonComponent,
FieldFormComponent,
FieldFormUIComponent,
FieldFormValidationComponent,
FieldWizardComponent,
@ -105,6 +152,7 @@ const routes: Routes = [
ReferencesValidationComponent,
SchemaEditFormComponent,
SchemaExportFormComponent,
SchemaFieldsComponent,
SchemaFormComponent,
SchemaPageComponent,
SchemaPreviewUrlsFormComponent,

21
frontend/app/features/schemas/pages/schema/schema-edit-form.component.html

@ -1,10 +1,9 @@
<form [formGroup]="editForm.form" (ngSubmit)="saveSchema()">
<sqx-modal-dialog (close)="emitComplete()">
<ng-container title>
Edit Schema
</ng-container>
<div class="card">
<div class="card-header">Common</div>
<ng-container content>
<div class="card-body">
<div class="form-group">
<label for="schemaName">Name</label>
@ -17,6 +16,8 @@
<sqx-control-errors for="label" [submitted]="editForm.submitted | async"></sqx-control-errors>
<input type="text" class="form-control" id="schemaLabel" formControlName="label" />
<sqx-form-hint>If the label is defined it will be shown in the UI instead of the schema name.</sqx-form-hint>
</div>
<div class="form-group">
@ -36,11 +37,9 @@
<sqx-form-hint>Tags to annotate your schema for automation processes.</sqx-form-hint>
</div>
</ng-container>
<ng-container footer>
<button type="reset" class="float-left btn btn-secondary" (click)="emitComplete()">Cancel</button>
</div>
<div class="card-footer">
<button type="submit" class="float-right btn btn-primary" *ngIf="isEditable">Save</button>
</ng-container>
</sqx-modal-dialog>
</div>
</div>
</form>

5
frontend/app/features/schemas/pages/schema/schema-edit-form.component.scss

@ -1,6 +1,11 @@
@import '_vars';
@import '_mixins';
.card-header,
.card-footer {
padding: 1.25rem;
}
textarea {
resize: none;
}

15
frontend/app/features/schemas/pages/schema/schema-edit-form.component.ts

@ -5,10 +5,11 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Component, Input, OnInit } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import {
DialogService,
EditSchemaForm,
SchemaDetailsDto,
SchemasState
@ -22,9 +23,6 @@ import {
export class SchemaEditFormComponent implements OnInit {
public readonly standalone = { standalone: true };
@Output()
public complete = new EventEmitter();
@Input()
public schema: SchemaDetailsDto;
@ -33,6 +31,7 @@ export class SchemaEditFormComponent implements OnInit {
public isEditable = false;
constructor(
private readonly dialogs: DialogService,
private readonly formBuilder: FormBuilder,
private readonly schemasState: SchemasState
) {
@ -45,10 +44,6 @@ export class SchemaEditFormComponent implements OnInit {
this.editForm.setEnabled(this.isEditable);
}
public emitComplete() {
this.complete.emit();
}
public saveSchema() {
if (!this.isEditable) {
return;
@ -59,7 +54,9 @@ export class SchemaEditFormComponent implements OnInit {
if (value) {
this.schemasState.update(this.schema, value)
.subscribe(() => {
this.emitComplete();
this.dialogs.notifyInfo('Schema saved successfully.');
this.editForm.submitCompleted({ noReset: true });
}, error => {
this.editForm.submitFailed(error);
});

25
frontend/app/features/schemas/pages/schema/schema-export-form.component.html

@ -1,16 +1,8 @@
<form [formGroup]="synchronizeForm.form" (submit)="synchronizeSchema()">
<sqx-modal-dialog (close)="emitComplete()" large="true">
<ng-container title>
Export Schema
</ng-container>
<ng-container content>
<sqx-json-editor [noBorder]="true" formControlName="json"></sqx-json-editor>
</ng-container>
<ng-container footer *ngIf="schema.canSynchronize">
<form class="inner-form" [formGroup]="synchronizeForm.form" (submit)="synchronize()">
<div class="inner-header" *ngIf="isEditable">
<div class="row align-items-center">
<div class="col">
<div class="col"></div>
<div class="col-auto">
<div class="form-inline">
<div class="form-check pr-4">
<input class="form-check-input" type="checkbox" id="fieldsDelete" formControlName="fieldsDelete" />
@ -28,9 +20,12 @@
</div>
</div>
<div class="col-auto">
<button type="submit" class="btn btn-success" [disabled]="synchronizeForm.submitted | async">Synchronize</button>
<button type="submit" class="btn btn-primary" [disabled]="synchronizeForm.submitted | async">Synchronize</button>
</div>
</div>
</ng-container>
</sqx-modal-dialog>
</div>
<div class="inner-main">
<sqx-json-editor [noBorder]="true" formControlName="json"></sqx-json-editor>
</div>
</form>

23
frontend/app/features/schemas/pages/schema/schema-export-form.component.scss

@ -1,15 +1,24 @@
@import '_vars';
@import '_mixins';
:host ::ng-deep {
.modal-body {
padding: 0;
}
:host,
.inner-form,
.inner-main {
@include flex-box;
@include flex-grow(1);
@include flex-direction(column);
}
.modal-content {
height: 100%;
}
.inner-header,
.inner-main {
position: relative;
}
.inner-header {
padding: 1rem $panel-padding;
}
:host ::ng-deep {
.editor {
@include absolute(0, 0, 0, 0);
}

25
frontend/app/features/schemas/pages/schema/schema-export-form.component.ts

@ -5,10 +5,11 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
import { Component, Input, OnChanges } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import {
DialogService,
SchemaDetailsDto,
SchemasState,
SynchronizeSchemaForm
@ -20,25 +21,31 @@ import {
templateUrl: './schema-export-form.component.html'
})
export class SchemaExportFormComponent implements OnChanges {
@Output()
public complete = new EventEmitter();
@Input()
public schema: SchemaDetailsDto;
public synchronizeForm = new SynchronizeSchemaForm(this.formBuilder);
public isEditable = false;
constructor(
private readonly dialogs: DialogService,
private readonly formBuilder: FormBuilder,
private readonly schemasState: SchemasState
) {
}
public ngOnChanges() {
this.isEditable = this.schema.canUpdateScripts;
this.synchronizeForm.form.get('json')!.setValue(this.schema.export());
}
public synchronizeSchema() {
public synchronize() {
if (!this.isEditable) {
return;
}
const value = this.synchronizeForm.submit();
if (value) {
@ -50,14 +57,12 @@ export class SchemaExportFormComponent implements OnChanges {
this.schemasState.synchronize(this.schema, request)
.subscribe(() => {
this.synchronizeForm.submitCompleted();
this.dialogs.notifyInfo('Schema synchronized successfully.');
this.synchronizeForm.submitCompleted({ noReset: true });
}, error => {
this.synchronizeForm.submitFailed(error);
});
}
}
public emitComplete() {
this.complete.emit();
}
}

36
frontend/app/features/schemas/pages/schema/schema-fields.component.html

@ -0,0 +1,36 @@
<div class="fields">
<div class="table-items-row table-items-row-empty" *ngIf="schema && schema.fields.length === 0">
No field created yet.
<button type="button" class="btn btn-success btn-sm ml-2" (click)="addFieldDialog.show()" *ngIf="schema.canAddField">
<i class="icon icon-plus"></i> Add Field
</button>
</div>
<ng-container *ngIf="patternsState.patterns | async; let patterns">
<div
cdkDropList
[cdkDropListDisabled]="!schema.canOrderFields"
[cdkDropListData]="schema.fields"
(cdkDropListDropped)="sortFields($event)">
<div *ngFor="let field of schema.fields; trackBy: trackByFieldFn"
class="table-drag"
cdkDrag
cdkDragLockAxis="y">
<sqx-field [field]="field" [schema]="schema" [patterns]="patterns">
<i cdkDragHandle class="icon-drag2 drag-handle"></i>
</sqx-field>
</div>
</div>
<button type="button" class="btn btn-success field-button" (click)="addFieldDialog.show()" *ngIf="schema.canAddField">
<i class="icon icon-plus field-button-icon"></i> <div class="field-button-text">Add Field</div>
</button>
</ng-container>
</div>
<ng-container *sqxModal="addFieldDialog">
<sqx-field-wizard [schema]="schema"
(complete)="addFieldDialog.hide()">
</sqx-field-wizard>
</ng-container>

22
frontend/app/features/schemas/pages/schema/schema-fields.component.scss

@ -0,0 +1,22 @@
@import '_vars';
@import '_mixins';
.fields {
padding: $panel-padding;
}
.field-button {
& {
@include circle(5.25rem);
@include box-shadow(0, 8px, 16px, .3);
@include absolute(auto, 6rem, 1rem, auto);
}
&-icon {
font-weight: bold;
}
&-text {
font-size: .9rem;
}
}

60
frontend/app/features/schemas/pages/schema/schema-fields.component.ts

@ -0,0 +1,60 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
// tslint:disable:no-shadowed-variable
import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { Component, Input, OnInit } from '@angular/core';
import {
DialogModel,
fadeAnimation,
FieldDto,
fieldTypes,
PatternsState,
SchemaDetailsDto,
SchemasState,
sorted
} from '@app/shared';
@Component({
selector: 'sqx-schema-fields',
styleUrls: ['./schema-fields.component.scss'],
templateUrl: './schema-fields.component.html',
animations: [
fadeAnimation
]
})
export class SchemaFieldsComponent implements OnInit {
public fieldTypes = fieldTypes;
@Input()
public schema: SchemaDetailsDto;
public addFieldDialog = new DialogModel();
public trackByFieldFn: (index: number, field: FieldDto) => any;
constructor(
public readonly schemasState: SchemasState,
public readonly patternsState: PatternsState
) {
this.trackByFieldFn = this.trackByField.bind(this);
}
public ngOnInit() {
this.patternsState.load();
}
public sortFields(event: CdkDragDrop<ReadonlyArray<FieldDto>>) {
this.schemasState.orderFields(this.schema, sorted(event)).subscribe();
}
public trackByField(index: number, field: FieldDto) {
return field.fieldId + this.schema.id;
}
}

94
frontend/app/features/schemas/pages/schema/schema-page.component.html

@ -1,15 +1,15 @@
<sqx-title [message]="schemasState.schemaName"></sqx-title>
<sqx-panel desiredWidth="60rem" [showSidebar]="true">
<ng-container title>
<i class="schema-edit icon-pencil" (click)="editSchemaDialog.show()"></i> <span (dblclick)="editSchemaDialog.show()">{{schema.displayName}}</span>
<sqx-panel desiredWidth="50rem" contentClass="grid" [showSidebar]="true">
<ng-container header>
<ul class="nav nav-tabs2">
<li class="nav-item" *ngFor="let tab of selectableTabs">
<a class="nav-link" [routerLink]="['./', tab.toLowerCase()]" routerLinkActive="active">{{tab}}</a>
</li>
</ul>
</ng-container>
<ng-container menu>
<button type="button" class="btn btn-text mr-1" (click)="exportDialog.show()">
JSON View
</button>
<div class="btn-group mr-1" #buttonPublish>
<button type="button" class="btn btn-publishing btn-toggle" [class.btn-success]="schema.isPublished" [disabled]="!schema.canPublish" (click)="publish()">
Published
@ -26,13 +26,6 @@
<ng-container *sqxModal="editOptionsDropdown;closeAlways:true">
<div class="dropdown-menu" [sqxAnchoredTo]="buttonOptions" @fade>
<a class="dropdown-item" (click)="configureScriptsDialog.show()">
Scripts
</a>
<a class="dropdown-item" (click)="configurePreviewUrlsDialog.show()">
Preview Urls
</a>
<ng-container *ngIf="schemasState.canCreate">
<div class="dropdown-divider"></div>
@ -66,39 +59,28 @@
</ng-container>
<ng-container content>
<div class="table-items-row table-items-row-empty" *ngIf="schema && schema.fields.length === 0">
No field created yet.
<button type="button" class="btn btn-success btn-sm ml-2" (click)="addFieldDialog.show()" *ngIf="schema.canAddField">
<i class="icon icon-plus"></i> Add Field
</button>
</div>
<ng-container *ngIf="patternsState.patterns | async; let patterns">
<div class="schemas"
cdkDropList
[cdkDropListDisabled]="!schema.canOrderFields"
[cdkDropListData]="schema.fields"
(cdkDropListDropped)="sortFields($event)">
<div *ngFor="let field of schema.fields; trackBy: trackByFieldFn"
class="table-drag"
cdkDrag
cdkDragLockAxis="y">
<sqx-field [field]="field" [schema]="schema" [patterns]="patterns">
<i cdkDragHandle class="icon-drag2 drag-handle"></i>
</sqx-field>
</div>
<ng-container [ngSwitch]="selectedTab">
<ng-container *ngSwitchCase="'fields'">
<sqx-schema-fields [schema]="schema"></sqx-schema-fields>
</ng-container>
<ng-container *ngSwitchCase="'scripts'">
<sqx-schema-scripts-form [schema]="schema"></sqx-schema-scripts-form>
</ng-container>
<ng-container *ngSwitchCase="'json'">
<sqx-schema-export-form [schema]="schema"></sqx-schema-export-form>
</ng-container>
<ng-container *ngSwitchCase="'more'">
<div class="cards">
<sqx-schema-preview-urls-form [schema]="schema"></sqx-schema-preview-urls-form>
<sqx-schema-edit-form [schema]="schema"></sqx-schema-edit-form>
</div>
<button type="button" class="btn btn-success field-button" (click)="addFieldDialog.show()" *ngIf="schema.canAddField">
<i class="icon icon-plus field-button-icon"></i> <div class="field-button-text">Add Field</div>
</button>
</ng-container>
</ng-container>
</ng-container>
<ng-container sidebar>
<div class="panel-nav">
<a class="panel-link" routerLink="help" routerLinkActive="active" title="Help" titlePosition="left">
<a class="panel-link" [routerLink]="[selectedTab, 'help']" routerLinkActive="active" title="Help" titlePosition="left">
<i class="icon-help"></i>
</a>
</div>
@ -106,33 +88,3 @@
</sqx-panel>
<router-outlet></router-outlet>
<ng-container *sqxModal="editSchemaDialog">
<sqx-schema-edit-form [schema]="schema"
(complete)="editSchemaDialog.hide()">
</sqx-schema-edit-form>
</ng-container>
<ng-container *sqxModal="addFieldDialog">
<sqx-field-wizard [schema]="schema"
(complete)="addFieldDialog.hide()">
</sqx-field-wizard>
</ng-container>
<ng-container *sqxModal="configureScriptsDialog">
<sqx-schema-scripts-form [schema]="schema"
(complete)="configureScriptsDialog.hide()">
</sqx-schema-scripts-form>
</ng-container>
<ng-container *sqxModal="configurePreviewUrlsDialog">
<sqx-schema-preview-urls-form [schema]="schema"
(complete)="configurePreviewUrlsDialog.hide()">
</sqx-schema-preview-urls-form>
</ng-container>
<ng-container *sqxModal="exportDialog">
<sqx-schema-export-form [schema]="schema"
(complete)="exportDialog.hide()">
</sqx-schema-export-form>
</ng-container>

49
frontend/app/features/schemas/pages/schema/schema-page.component.scss

@ -8,50 +8,15 @@
}
}
.btn-group-sm {
margin-top: .25rem;
.cards {
padding: $panel-padding;
overflow-y: auto;
}
.btn-sm {
margin-top: .25rem;
}
.btn-success {
margin: 0;
}
.schemas {
padding-bottom: 7rem;
}
.schema {
&-edit {
color: $color-border-dark;
font-size: .9rem;
font-weight: normal;
padding: .6rem .25rem;
border: 0;
background: transparent;
vertical-align: baseline;
}
}
.field-icon {
color: $color-border-dark;
}
.field-button {
& {
@include circle(5.25rem);
@include box-shadow(0, 8px, 16px, .3);
@include absolute(auto, 6rem, 1rem, auto);
}
&-icon {
font-weight: bold;
}
.nav-tabs2 {
@include absolute(auto, auto, 0, auto);
&-text {
font-size: .9rem;
a {
padding-bottom: 1.5rem;
}
}

42
frontend/app/features/schemas/pages/schema/schema-page.component.ts

@ -7,22 +7,17 @@
// tslint:disable:no-shadowed-variable
import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import {
DialogModel,
fadeAnimation,
FieldDto,
fieldTypes,
MessageBus,
ModalModel,
PatternsState,
ResourceOwner,
SchemaDetailsDto,
SchemasState,
sorted
Types
} from '@app/shared';
import {
@ -38,33 +33,30 @@ import {
]
})
export class SchemaPageComponent extends ResourceOwner implements OnInit {
public fieldTypes = fieldTypes;
public schema: SchemaDetailsDto;
public addFieldDialog = new DialogModel();
public configurePreviewUrlsDialog = new DialogModel();
public configureScriptsDialog = new DialogModel();
public editOptionsDropdown = new ModalModel();
public editSchemaDialog = new DialogModel();
public exportDialog = new DialogModel();
public trackByFieldFn: (index: number, field: FieldDto) => any;
public selectedTab = 'fields';
public selectableTabs: ReadonlyArray<string> = ['Fields', 'Scripts', 'Json', 'More'];
constructor(
public readonly schemasState: SchemasState,
public readonly patternsState: PatternsState,
private readonly route: ActivatedRoute,
private readonly router: Router,
private readonly messageBus: MessageBus
) {
super();
this.trackByFieldFn = this.trackByField.bind(this);
}
public ngOnInit() {
this.patternsState.load();
this.own(
this.router.events
.subscribe(event => {
if (Types.is(event, NavigationEnd)) {
this.selectedTab = this.route.firstChild!.snapshot.routeConfig!.path!;
}
}));
this.own(
this.schemasState.selectedSchema
@ -81,14 +73,6 @@ export class SchemaPageComponent extends ResourceOwner implements OnInit {
this.schemasState.unpublish(this.schema).subscribe();
}
public sortFields(event: CdkDragDrop<ReadonlyArray<FieldDto>>) {
this.schemasState.orderFields(this.schema, sorted(event)).subscribe();
}
public trackByField(index: number, field: FieldDto) {
return field.fieldId + this.schema.id;
}
public deleteSchema() {
this.schemasState.delete(this.schema)
.subscribe(() => {
@ -100,6 +84,10 @@ export class SchemaPageComponent extends ResourceOwner implements OnInit {
this.messageBus.emit(new SchemaCloning(this.schema.export()));
}
public selectTab(tab: string) {
this.selectedTab = tab;
}
private back() {
this.router.navigate(['../'], { relativeTo: this.route });
}

34
frontend/app/features/schemas/pages/schema/schema-preview-urls-form.component.html

@ -1,17 +1,11 @@
<form [formGroup]="editForm.form" (ngSubmit)="saveSchema()">
<sqx-modal-dialog (close)="emitComplete()" large="true">
<ng-container title>
Preview Urls
</ng-container>
<div class="card mb-2">
<div class="card-header">Preview URLs</div>
<ng-container content>
<sqx-form-hint>
Adding a preview url generates a link in the content editor, referring to your custom preview or production environment where the content item is used.
</sqx-form-hint>
<sqx-form-hint>
Read the <a href="https://https://docs.squidex.io/guides/09-preview" sqxExternalLink>documentation</a> before setting up custom content preview urls.
</sqx-form-hint>
<div class="card-body">
<sqx-form-alert>
Checkout the integrated help page to learn more about preview URL's.
</sqx-form-alert>
<div class="content">
<div class="mt-4" *ngIf="!isEditable && editForm.form.controls.length === 0">
@ -28,7 +22,7 @@
<div class="col pr-1">
<sqx-control-errors [for]="form.get('url')" fieldName="Url" [submitted]="editForm.submitted | async"></sqx-control-errors>
<input type="text" class="form-control" maxlength="1000" [formControl]="form.get('url')" placeholder="Url with placeholders like ${id} or ${data.slug}" />
<input type="text" class="form-control" maxlength="1000" [formControl]="form.get('url')" placeholder="Url with variables" />
</div>
<div class="col-auto col-options">
@ -52,7 +46,7 @@
<div class="col pr-1">
<sqx-control-errors for="url" [submitted]="addForm.submitted | async"></sqx-control-errors>
<input type="text" class="form-control" maxlength="1000" formControlName="url" placeholder="Url with placeholders like ${id} or ${data.slug}" />
<input type="text" class="form-control" maxlength="1000" formControlName="url" placeholder="Url with variables" />
</div>
<div class="col-auto col-options">
@ -66,11 +60,9 @@
</div>
</div>
</div>
</ng-container>
<ng-container footer>
<button type="reset" class="float-left btn btn-secondary" (click)="emitComplete()" [disabled]="editForm.submitted | async">Cancel</button>
<button type="submit" class="float-right btn btn-primary" *ngIf="isEditable">Save</button>
</ng-container>
</sqx-modal-dialog>
</div>
<div class="card-footer" *ngIf="isEditable">
<button type="submit" class="float-right btn btn-primary" [class.invisible]="!isEditable">Save</button>
</div>
</div>
</form>

6
frontend/app/features/schemas/pages/schema/schema-preview-urls-form.component.scss

@ -2,12 +2,12 @@
@import '_mixins';
.content {
min-height: 200px;
margin-top: 1rem;
}
.btn-delete {
margin-left: 42px;
.card-header,
.card-footer {
padding: 1.25rem;
}
.col-options {

19
frontend/app/features/schemas/pages/schema/schema-preview-urls-form.component.ts

@ -5,12 +5,13 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Component, Input, OnChanges } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import {
AddPreviewUrlForm,
ConfigurePreviewUrlsForm,
DialogService,
SchemaDetailsDto,
SchemasState
} from '@app/shared';
@ -20,10 +21,7 @@ import {
styleUrls: ['./schema-preview-urls-form.component.scss'],
templateUrl: './schema-preview-urls-form.component.html'
})
export class SchemaPreviewUrlsFormComponent implements OnInit {
@Output()
public complete = new EventEmitter();
export class SchemaPreviewUrlsFormComponent implements OnChanges {
@Input()
public schema: SchemaDetailsDto;
@ -34,22 +32,19 @@ export class SchemaPreviewUrlsFormComponent implements OnInit {
public isEditable = false;
constructor(
private readonly dialogs: DialogService,
private readonly formBuilder: FormBuilder,
private readonly schemasState: SchemasState
) {
}
public ngOnInit() {
public ngOnChanges() {
this.isEditable = this.schema.canUpdateUrls;
this.editForm.load(this.schema.previewUrls);
this.editForm.setEnabled(this.isEditable);
}
public emitComplete() {
this.complete.emit();
}
public cancelAdd() {
this.addForm.submitCompleted();
}
@ -78,7 +73,9 @@ export class SchemaPreviewUrlsFormComponent implements OnInit {
if (value) {
this.schemasState.configurePreviewUrls(this.schema, value)
.subscribe(() => {
this.emitComplete();
this.dialogs.notifyInfo('Preview URLs successfully.');
this.editForm.submitCompleted({ noReset: true });
}, error => {
this.editForm.submitFailed(error);
});

23
frontend/app/features/schemas/pages/schema/schema-scripts-form.component.html

@ -1,30 +1,19 @@
<form [formGroup]="editForm.form" (ngSubmit)="saveSchema()">
<sqx-modal-dialog (close)="emitComplete()" large="true">
<ng-container title>
Scripts
</ng-container>
<ng-container tabs>
<form class="inner-form" [formGroup]="editForm.form" (ngSubmit)="saveSchema()">
<div class="inner-header">
<ul class="nav nav-tabs2">
<li class="nav-item" *ngFor="let script of editForm.form.controls | sqxKeys">
<a class="nav-link" [class.active]="selectedField === script" (click)="selectField(script)">{{script | titlecase}}</a>
</li>
</ul>
</ng-container>
<ng-container content>
<div class="form-group">
<button type="submit" class="float-right btn btn-primary" [class.invisible]="!isEditable">Save</button>
</div>
<div class="inner-main">
<ng-container *ngFor="let script of editForm.form.controls | sqxKeys">
<ng-container *ngIf="selectedField === script">
<sqx-code-editor [noBorder]="true" [formControlName]="script"></sqx-code-editor>
</ng-container>
</ng-container>
</div>
</ng-container>
<ng-container footer>
<button type="reset" class="float-left btn btn-secondary" (click)="emitComplete()" [disabled]="editForm.submitted | async">Cancel</button>
<button type="submit" class="float-right btn btn-primary" *ngIf="isEditable">Save</button>
</ng-container>
</sqx-modal-dialog>
</form>

23
frontend/app/features/schemas/pages/schema/schema-scripts-form.component.scss

@ -1,19 +1,24 @@
@import '_vars';
@import '_mixins';
.nav-link {
cursor: default;
:host,
.inner-form,
.inner-main {
@include flex-box;
@include flex-grow(1);
@include flex-direction(column);
}
:host ::ng-deep {
.modal-body {
padding: 0;
}
.inner-header,
.inner-main {
position: relative;
}
.modal-content {
height: 100%;
}
.inner-header {
padding: 1rem $panel-padding;
}
:host ::ng-deep {
.editor {
@include absolute(0, 0, 0, 0);
}

19
frontend/app/features/schemas/pages/schema/schema-scripts-form.component.ts

@ -5,10 +5,11 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Component, Input, OnChanges } from '@angular/core';
import { FormBuilder } from '@angular/forms';
import {
DialogService,
EditScriptsForm,
SchemaDetailsDto,
SchemasState
@ -19,10 +20,7 @@ import {
styleUrls: ['./schema-scripts-form.component.scss'],
templateUrl: './schema-scripts-form.component.html'
})
export class SchemaScriptsFormComponent implements OnInit {
@Output()
public complete = new EventEmitter();
export class SchemaScriptsFormComponent implements OnChanges {
@Input()
public schema: SchemaDetailsDto;
@ -33,22 +31,19 @@ export class SchemaScriptsFormComponent implements OnInit {
public isEditable = false;
constructor(
private readonly dialogs: DialogService,
private readonly formBuilder: FormBuilder,
private readonly schemasState: SchemasState
) {
}
public ngOnInit() {
public ngOnChanges() {
this.isEditable = this.schema.canUpdateScripts;
this.editForm.load(this.schema.scripts);
this.editForm.setEnabled(this.isEditable);
}
public emitComplete() {
this.complete.emit();
}
public selectField(field: string) {
this.selectedField = field;
}
@ -63,7 +58,9 @@ export class SchemaScriptsFormComponent implements OnInit {
if (value) {
this.schemasState.configureScripts(this.schema, value)
.subscribe(() => {
this.emitComplete();
this.dialogs.notifyInfo('Scripts saved successfully.');
this.editForm.submitCompleted({ noReset: true });
}, error => {
this.editForm.submitFailed(error);
});

15
frontend/app/features/settings/pages/more/more-page.component.html

@ -4,11 +4,11 @@
</ng-container>
<ng-container content>
<div class="card mb-4">
<form [formGroup]="updateForm.form" (ngSubmit)="save()">
<div class="card mb-2">
<h3 class="card-header">General</h3>
<div class="card-body">
<form [formGroup]="updateForm.form" (ngSubmit)="save()">
<sqx-form-error [error]="updateForm.error | async"></sqx-form-error>
<div class="form-group">
@ -32,14 +32,15 @@
<input type="text" class="form-control" id="description" maxlength="100" formControlName="description" />
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
<div class="card-footer">
<button type="submit" class="float-right btn btn-primary">Save</button>
</div>
</div>
<div class="card mb-4">
</form>
<div class="card mb-2">
<h3 class="card-header">Image</h3>
<div class="card-body">

5
frontend/app/features/settings/pages/more/more-page.component.scss

@ -17,6 +17,11 @@
}
}
.card-header,
.card-footer {
padding: 1.25rem;
}
@mixin overlay {
& {
@include transition(opacity .4s ease);

2
frontend/app/framework/angular/panel.component.html

@ -29,6 +29,8 @@
</ng-template>
</ng-container>
<ng-content select="[header]"></ng-content>
<div class="panel-header-row" *ngIf="showSecondHeader">
<ng-content select="[secondHeader]"></ng-content>
</div>

4
frontend/app/shared/services/help.service.spec.ts

@ -35,7 +35,7 @@ describe('ClientsService', () => {
helpSections = result;
});
const req = httpMock.expectOne('https://raw.githubusercontent.com/Squidex/squidex-docs/master/01-chapter/02-article.md');
const req = httpMock.expectOne('https://raw.githubusercontent.com/squidex/squidex-docs2/master/01-chapter/02-article.md');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();
@ -54,7 +54,7 @@ describe('ClientsService', () => {
helpSections = result;
});
const req = httpMock.expectOne('https://raw.githubusercontent.com/Squidex/squidex-docs/master/01-chapter/02-article.md');
const req = httpMock.expectOne('https://raw.githubusercontent.com/squidex/squidex-docs2/master/01-chapter/02-article.md');
expect(req.request.method).toEqual('GET');
expect(req.request.headers.get('If-Match')).toBeNull();

2
frontend/app/shared/services/help.service.ts

@ -18,7 +18,7 @@ export class HelpService {
}
public getHelp(helpPage: string): Observable<string> {
const url = `https://raw.githubusercontent.com/Squidex/squidex-docs/master/${helpPage}.md`;
const url = `https://raw.githubusercontent.com/squidex/squidex-docs2/master/${helpPage}.md`;
return this.http.get(url, { responseType: 'text' }).pipe(
catchError(() => of('')));

Loading…
Cancel
Save