mirror of https://github.com/Squidex/squidex.git
44 changed files with 1081 additions and 634 deletions
@ -0,0 +1,44 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Injectable } from '@angular/core'; |
|||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; |
|||
import { Observable } from 'rxjs'; |
|||
|
|||
import { allParams } from 'framework'; |
|||
|
|||
import { SchemasState } from './../state/schemas.state'; |
|||
|
|||
@Injectable() |
|||
export class SchemaMustExistGuard implements CanActivate { |
|||
constructor( |
|||
private readonly schemasState: SchemasState, |
|||
private readonly router: Router |
|||
) { |
|||
} |
|||
|
|||
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> { |
|||
const params = allParams(route); |
|||
|
|||
const schemaName = params['schemaName']; |
|||
|
|||
if (!schemaName) { |
|||
throw 'Route must contain schema name.'; |
|||
} |
|||
|
|||
const result = |
|||
this.schemasState.selectSchema(schemaName) |
|||
.do(dto => { |
|||
if (!dto) { |
|||
this.router.navigate(['/404']); |
|||
} |
|||
}) |
|||
.map(u => u !== null); |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
@ -0,0 +1,128 @@ |
|||
<div class="modal-dialog modal-lg"> |
|||
<div class="modal-content" *ngIf="!editForm"> |
|||
<form [formGroup]="addFieldForm" (ngSubmit)="addField(false, false)"> |
|||
<div class="modal-header"> |
|||
<h4 class="modal-title"> |
|||
Create Field |
|||
</h4> |
|||
|
|||
<button type="button" class="close" data-dismiss="modal" aria-label="Close" (click)="complete()"> |
|||
<span aria-hidden="true">×</span> |
|||
</button> |
|||
</div> |
|||
|
|||
<div class="modal-body"> |
|||
<h3 class="wizard-title">Create Field</h3> |
|||
|
|||
<div *ngIf="addFieldError"> |
|||
<div class="form-alert form-alert-error" [innerHTML]="addFieldError"></div> |
|||
</div> |
|||
|
|||
<div class="form-group"> |
|||
<div class="row"> |
|||
<div class="col-4 type" *ngFor="let fieldType of fieldTypes"> |
|||
<label> |
|||
<input type="radio" class="radio-input" formControlName="type" value="{{fieldType.type}}" /> |
|||
|
|||
<div class="row no-gutters"> |
|||
<div class="col col-auto"> |
|||
<div class="type-icon" [class.active]="addFieldForm.controls.type.value === fieldType.type"> |
|||
<i class="icon-type-{{fieldType.type}}"></i> |
|||
</div> |
|||
</div> |
|||
<div class="col"> |
|||
<div class="type-title">{{fieldType.type}}</div> |
|||
<div class="type-text text-muted">{{fieldType.description}}</div> |
|||
</div> |
|||
</div> |
|||
|
|||
</label> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="form-group"> |
|||
<sqx-control-errors for="name" [submitted]="addFieldFormSubmitted"></sqx-control-errors> |
|||
|
|||
<input type="text" class="form-control" formControlName="name" maxlength="40" placeholder="Enter field name" sqxFocusOnInit /> |
|||
</div> |
|||
|
|||
<div class="form-group"> |
|||
<div class="form-check"> |
|||
<input class="form-check-input" type="checkbox" id="isLocalizable" formControlName="isLocalizable" /> |
|||
<label class="form-check-label" for="isLocalizable"> |
|||
Localizable |
|||
</label> |
|||
</div> |
|||
|
|||
<small class="form-text text-muted"> |
|||
You can the field as localizable. It means that is dependent on the language, for example a city name. |
|||
</small> |
|||
</div> |
|||
</div> |
|||
<div class="modal-footer"> |
|||
<div class="clearfix"> |
|||
<button type="reset" class="float-left btn btn-secondary" (click)="complete()">Cancel</button> |
|||
|
|||
<div class="float-right"> |
|||
<button class="btn btn-success" (click)="addField(false, false)">Create and close</button> |
|||
<button class="btn btn-success" (click)="addField(true, false)">Create and new field</button> |
|||
<button class="btn btn-success" (click)="addField(false, true)">Create and configure</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
|
|||
<div class="modal-content" *ngIf="editForm"> |
|||
<form [formGroup]="editForm"> |
|||
<div class="modal-header"> |
|||
|
|||
<h4 class="modal-title" *ngIf="editStep === 1"> |
|||
Step 1 of 3: Common Properties |
|||
</h4> |
|||
<h4 class="modal-title" *ngIf="editStep === 2"> |
|||
Step 2 of 3: Validation |
|||
</h4> |
|||
<h4 class="modal-title" *ngIf="editStep === 3"> |
|||
Step 3 of 3: Editing Settings |
|||
</h4> |
|||
|
|||
<button type="button" class="close" data-dismiss="modal" aria-label="Close" (click)="complete()"> |
|||
<span aria-hidden="true">×</span> |
|||
</button> |
|||
</div> |
|||
|
|||
<div class="modal-body"> |
|||
<h3 class="wizard-title" *ngIf="editStep === 1">Common</h3> |
|||
<h3 class="wizard-title" *ngIf="editStep === 2">Validation</h3> |
|||
<h3 class="wizard-title" *ngIf="editStep === 3">Editing</h3> |
|||
|
|||
<div [class.hidden]="editStep !== 1"> |
|||
<sqx-field-form-common [editForm]="editForm" [editFormSubmitted]="editFormSubmitted" [showName]="false" [field]="editField"></sqx-field-form-common> |
|||
</div> |
|||
|
|||
<div [class.hidden]="editStep !== 2"> |
|||
<sqx-field-form-validation [editForm]="editForm" [field]="editField"></sqx-field-form-validation> |
|||
</div> |
|||
|
|||
<div [class.hidden]="editStep !== 3"> |
|||
<sqx-field-form-ui [editForm]="editForm" [field]="editField"></sqx-field-form-ui> |
|||
</div> |
|||
</div> |
|||
<div class="modal-footer"> |
|||
<div class="clearfix"> |
|||
<button type="reset" class="float-left btn btn-secondary" (click)="complete()">Cancel</button> |
|||
|
|||
<div class="float-right" *ngIf="editStep !== 2"> |
|||
<button class="btn btn-primary" (click)="next()">Next</button> |
|||
</div> |
|||
<div class="float-right" *ngIf="editStep === 2"> |
|||
<button class="btn btn-success" (click)="configureField(false)">Configure and close</button> |
|||
<button class="btn btn-success" (click)="configureField(true)">Configure and new field</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,65 @@ |
|||
@import '_vars'; |
|||
@import '_mixins'; |
|||
|
|||
$icon-size: 4.5rem; |
|||
|
|||
.form-check { |
|||
margin-top: .4rem; |
|||
margin-bottom: -.2rem; |
|||
} |
|||
|
|||
.type { |
|||
& { |
|||
margin-bottom: .5rem; |
|||
} |
|||
|
|||
&-title { |
|||
font-weight: bold; |
|||
} |
|||
|
|||
&-text { |
|||
font-size: .9rem; |
|||
} |
|||
|
|||
&-icon { |
|||
& { |
|||
@include border-radius; |
|||
height: $icon-size; |
|||
color: $color-theme-blue; |
|||
cursor: pointer; |
|||
border: 1px solid $color-border; |
|||
background: transparent; |
|||
margin-right: .5rem; |
|||
line-height: $icon-size; |
|||
font-size: 1.75rem; |
|||
font-weight: normal; |
|||
text-align: center; |
|||
width: $icon-size; |
|||
} |
|||
|
|||
.radio-input { |
|||
display: none; |
|||
} |
|||
|
|||
&.active { |
|||
& { |
|||
@include box-shadow(0, 0, 10px, .5); |
|||
background: $color-theme-blue; |
|||
border-color: $color-theme-blue; |
|||
color: $color-dark-foreground; |
|||
} |
|||
|
|||
&:hover { |
|||
color: $color-dark-foreground; |
|||
} |
|||
} |
|||
|
|||
&:hover { |
|||
border-color: $color-border-dark; |
|||
} |
|||
} |
|||
|
|||
.radio-input { |
|||
display: none; |
|||
} |
|||
} |
|||
@ -0,0 +1,149 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Component, EventEmitter, Input, Output } from '@angular/core'; |
|||
import { FormBuilder, Validators, FormGroup } from '@angular/forms'; |
|||
|
|||
import { |
|||
AddFieldDto, |
|||
AppContext, |
|||
createProperties, |
|||
fadeAnimation, |
|||
FieldDto, |
|||
fieldTypes, |
|||
SchemaDetailsDto, |
|||
UpdateFieldDto, |
|||
ValidatorsEx |
|||
} from 'shared'; |
|||
|
|||
import { SchemasState } from './../../state/schemas.state'; |
|||
|
|||
@Component({ |
|||
selector: 'sqx-field-wizard', |
|||
styleUrls: ['./field-wizard.component.scss'], |
|||
templateUrl: './field-wizard.component.html', |
|||
providers: [ |
|||
AppContext |
|||
], |
|||
animations: [ |
|||
fadeAnimation |
|||
] |
|||
}) |
|||
export class FieldWizardComponent { |
|||
public fieldTypes = fieldTypes; |
|||
|
|||
public editFormSubmitted = false; |
|||
public editStep = 0; |
|||
public editForm: FormGroup | null; |
|||
public editField: FieldDto | null; |
|||
|
|||
public addFieldError = ''; |
|||
public addFieldFormSubmitted = false; |
|||
public addFieldForm = |
|||
this.formBuilder.group({ |
|||
type: ['String', |
|||
[ |
|||
Validators.required |
|||
]], |
|||
name: ['', |
|||
[ |
|||
Validators.required, |
|||
Validators.maxLength(40), |
|||
ValidatorsEx.pattern('[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*', 'Name must be a valid javascript name in camel case.') |
|||
]], |
|||
isLocalizable: [false] |
|||
}); |
|||
|
|||
@Input() |
|||
public schema: SchemaDetailsDto; |
|||
|
|||
@Output() |
|||
public completed = new EventEmitter(); |
|||
|
|||
constructor( |
|||
private readonly formBuilder: FormBuilder, |
|||
private readonly schemasState: SchemasState |
|||
) { |
|||
} |
|||
|
|||
public complete() { |
|||
this.completed.emit(); |
|||
} |
|||
|
|||
public next() { |
|||
this.editStep++; |
|||
} |
|||
|
|||
public addField(next: boolean, configure: boolean) { |
|||
this.addFieldFormSubmitted = true; |
|||
|
|||
if (this.addFieldForm.valid) { |
|||
this.addFieldForm.disable(); |
|||
|
|||
const properties = createProperties(this.addFieldForm.controls['type'].value); |
|||
|
|||
const partitioning = |
|||
this.addFieldForm.controls['isLocalizable'].value ? |
|||
'language' : |
|||
'invariant'; |
|||
|
|||
const requestDto = new AddFieldDto(this.addFieldForm.controls['name'].value, partitioning, properties); |
|||
|
|||
this.schemasState.addField(this.schema, requestDto) |
|||
.subscribe(dto => { |
|||
this.resetFieldForm(); |
|||
|
|||
if (configure) { |
|||
this.editField = dto; |
|||
this.editStep = 1; |
|||
this.editForm = new FormGroup({}); |
|||
} else if (!next) { |
|||
this.complete(); |
|||
} |
|||
}, error => { |
|||
this.resetFieldForm(error.displayMessage); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
public configureField(next: boolean) { |
|||
this.editFormSubmitted = true; |
|||
|
|||
if (this.editForm!.valid) { |
|||
const properties = createProperties(this.editField!.properties['fieldType'], this.editForm!.value); |
|||
|
|||
this.schemasState.updateField(this.schema, this.editField!, new UpdateFieldDto(properties)) |
|||
.subscribe(() => { |
|||
this.resetEditForm(); |
|||
|
|||
if (this.next) { |
|||
this.editField = null; |
|||
this.editForm = null; |
|||
this.editStep = 1; |
|||
} else { |
|||
this.complete(); |
|||
} |
|||
}, error => { |
|||
this.resetEditForm(); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
private resetEditForm() { |
|||
this.editForm!.enable(); |
|||
this.editForm!.reset(this.editField!.properties); |
|||
this.editFormSubmitted = false; |
|||
} |
|||
|
|||
private resetFieldForm(error = '') { |
|||
this.addFieldError = error; |
|||
this.addFieldForm.enable(); |
|||
this.addFieldForm.reset({ type: 'String' }); |
|||
this.addFieldFormSubmitted = false; |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,56 @@ |
|||
<div [formGroup]="editForm"> |
|||
<div class="form-group row" *ngIf="showName"> |
|||
<label class="col col-3 col-form-label" for="fieldName">Name</label> |
|||
|
|||
<div class="col col-6"> |
|||
<input type="text" class="form-control" id="fieldName" readonly [ngModel]="field.name" [ngModelOptions]="{standalone: true}" /> |
|||
|
|||
<small class="form-text text-muted"> |
|||
The name of the field in the API response. |
|||
</small> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="form-group row"> |
|||
<label class="col col-3 col-form-label" for="fieldLabel">Label</label> |
|||
|
|||
<div class="col col-6"> |
|||
<sqx-control-errors for="label" [submitted]="editFormSubmitted"></sqx-control-errors> |
|||
|
|||
<input type="text" class="form-control" id="fieldLabel" maxlength="100" formControlName="label" /> |
|||
|
|||
<small class="form-text text-muted"> |
|||
Define the display name for the field for documentation and user interfaces. |
|||
</small> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="form-group row"> |
|||
<label class="col col-3 col-form-label" for="fieldHints">Hints</label> |
|||
|
|||
<div class="col col-6"> |
|||
<sqx-control-errors for="hints" [submitted]="editFormSubmitted"></sqx-control-errors> |
|||
|
|||
<input type="text" class="form-control" id="fieldHints" maxlength="100" formControlName="hints" /> |
|||
|
|||
<small class="form-text text-muted"> |
|||
Define some hints for the user and editor for the field for documentation and user interfaces. |
|||
</small> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="form-group row"> |
|||
<div class="col col-9 offset-3"> |
|||
<div class="form-check"> |
|||
<input class="form-check-input" type="checkbox" id="fieldListfield" formControlName="isListField" /> |
|||
<label class="form-check-label" for="fieldListfield"> |
|||
List Field |
|||
</label> |
|||
</div> |
|||
|
|||
<small class="form-text text-muted"> |
|||
List fields are shown as a column in the content list. If no list field is defined, the first field is shown by default. |
|||
</small> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,2 @@ |
|||
@import '_vars'; |
|||
@import '_mixins'; |
|||
@ -0,0 +1,47 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; |
|||
import { FormControl, FormGroup, Validators } from '@angular/forms'; |
|||
|
|||
import { FieldDto } from 'shared'; |
|||
|
|||
@Component({ |
|||
selector: 'sqx-field-form-common', |
|||
styleUrls: ['field-form-common.component.scss'], |
|||
templateUrl: 'field-form-common.component.html', |
|||
changeDetection: ChangeDetectionStrategy.OnPush |
|||
}) |
|||
export class FieldFormCommonComponent implements OnInit { |
|||
@Input() |
|||
public editForm: FormGroup; |
|||
|
|||
@Input() |
|||
public editFormSubmitted = false; |
|||
|
|||
@Input() |
|||
public showName = true; |
|||
|
|||
@Input() |
|||
public field: FieldDto; |
|||
|
|||
public ngOnInit() { |
|||
this.editForm.setControl('label', |
|||
new FormControl(this.field.properties.label, |
|||
Validators.maxLength(100))); |
|||
|
|||
this.editForm.setControl('hints', |
|||
new FormControl(this.field.properties.label, |
|||
Validators.maxLength(100))); |
|||
|
|||
this.editForm.setControl('isRequired', |
|||
new FormControl(this.field.properties.isRequired)); |
|||
|
|||
this.editForm.setControl('isListField', |
|||
new FormControl(this.field.properties.isListField)); |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
<div [ngSwitch]="field.properties.fieldType"> |
|||
<div *ngSwitchCase="'Number'"> |
|||
<sqx-number-ui [editForm]="editForm" [properties]="field.properties"></sqx-number-ui> |
|||
</div> |
|||
<div *ngSwitchCase="'String'"> |
|||
<sqx-string-ui [editForm]="editForm" [properties]="field.properties"></sqx-string-ui> |
|||
</div> |
|||
<div *ngSwitchCase="'Boolean'"> |
|||
<sqx-boolean-ui [editForm]="editForm" [properties]="field.properties"></sqx-boolean-ui> |
|||
</div> |
|||
<div *ngSwitchCase="'DateTime'"> |
|||
<sqx-date-time-ui [editForm]="editForm" [properties]="field.properties"></sqx-date-time-ui> |
|||
</div> |
|||
<div *ngSwitchCase="'Geolocation'"> |
|||
<sqx-geolocation-ui [editForm]="editForm" [properties]="field.properties"></sqx-geolocation-ui> |
|||
</div> |
|||
<div *ngSwitchCase="'Json'"> |
|||
<sqx-json-ui [editForm]="editForm" [properties]="field.properties"></sqx-json-ui> |
|||
</div> |
|||
<div *ngSwitchCase="'Assets'"> |
|||
<sqx-assets-ui [editForm]="editForm" [properties]="field.properties"></sqx-assets-ui> |
|||
</div> |
|||
<div *ngSwitchCase="'References'"> |
|||
<sqx-references-ui [editForm]="editForm" [properties]="field.properties"></sqx-references-ui> |
|||
</div> |
|||
<div *ngSwitchCase="'Tags'"> |
|||
<sqx-tags-ui [editForm]="editForm" [properties]="field.properties"></sqx-tags-ui> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,2 @@ |
|||
@import '_vars'; |
|||
@import '_mixins'; |
|||
@ -0,0 +1,25 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; |
|||
import { FormGroup } from '@angular/forms'; |
|||
|
|||
import { FieldDto } from 'shared'; |
|||
|
|||
@Component({ |
|||
selector: 'sqx-field-form-ui', |
|||
styleUrls: ['field-form-ui.component.scss'], |
|||
templateUrl: 'field-form-ui.component.html', |
|||
changeDetection: ChangeDetectionStrategy.OnPush |
|||
}) |
|||
export class FieldFormUIComponent { |
|||
@Input() |
|||
public editForm: FormGroup; |
|||
|
|||
@Input() |
|||
public field: FieldDto; |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
<div [ngSwitch]="field.properties.fieldType"> |
|||
<div *ngSwitchCase="'Number'"> |
|||
<sqx-number-validation [editForm]="editForm" [properties]="field.properties"></sqx-number-validation> |
|||
</div> |
|||
<div *ngSwitchCase="'String'"> |
|||
<sqx-string-validation [editForm]="editForm" [properties]="field.properties"></sqx-string-validation> |
|||
</div> |
|||
<div *ngSwitchCase="'Boolean'"> |
|||
<sqx-boolean-validation [editForm]="editForm" [properties]="field.properties"></sqx-boolean-validation> |
|||
</div> |
|||
<div *ngSwitchCase="'DateTime'"> |
|||
<sqx-date-time-validation [editForm]="editForm" [properties]="field.properties"></sqx-date-time-validation> |
|||
</div> |
|||
<div *ngSwitchCase="'Geolocation'"> |
|||
<sqx-geolocation-validation [editForm]="editForm" [properties]="field.properties"></sqx-geolocation-validation> |
|||
</div> |
|||
<div *ngSwitchCase="'Json'"> |
|||
<sqx-json-validation [editForm]="editForm" [properties]="field.properties"></sqx-json-validation> |
|||
</div> |
|||
<div *ngSwitchCase="'Assets'"> |
|||
<sqx-assets-validation [editForm]="editForm" [properties]="field.properties"></sqx-assets-validation> |
|||
</div> |
|||
<div *ngSwitchCase="'References'"> |
|||
<sqx-references-validation [editForm]="editForm" [properties]="field.properties"></sqx-references-validation> |
|||
</div> |
|||
<div *ngSwitchCase="'Tags'"> |
|||
<sqx-tags-validation [editForm]="editForm" [properties]="field.properties"></sqx-tags-validation> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,2 @@ |
|||
@import '_vars'; |
|||
@import '_mixins'; |
|||
@ -0,0 +1,25 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; |
|||
import { FormGroup } from '@angular/forms'; |
|||
|
|||
import { FieldDto } from 'shared'; |
|||
|
|||
@Component({ |
|||
selector: 'sqx-field-form-validation', |
|||
styleUrls: ['field-form-validation.component.scss'], |
|||
templateUrl: 'field-form-validation.component.html', |
|||
changeDetection: ChangeDetectionStrategy.OnPush |
|||
}) |
|||
export class FieldFormValidationComponent { |
|||
@Input() |
|||
public editForm: FormGroup; |
|||
|
|||
@Input() |
|||
public field: FieldDto; |
|||
} |
|||
@ -0,0 +1,201 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import { Injectable } from '@angular/core'; |
|||
import { BehaviorSubject, Observable } from 'rxjs'; |
|||
|
|||
import 'framework/utils/rxjs-extensions'; |
|||
|
|||
import { |
|||
AddFieldDto, |
|||
AppsStoreService, |
|||
AuthService, |
|||
CreateSchemaDto, |
|||
DateTime, |
|||
DialogService, |
|||
ErrorDto, |
|||
FieldDto, |
|||
ImmutableArray, |
|||
Notification, |
|||
SchemaDto, |
|||
SchemaDetailsDto, |
|||
SchemasService, |
|||
UpdateFieldDto |
|||
} from 'shared'; |
|||
|
|||
@Injectable() |
|||
export class SchemasState { |
|||
public schemasItems = new BehaviorSubject<ImmutableArray<SchemaDto>>(ImmutableArray.empty()); |
|||
|
|||
public selectedSchema = new BehaviorSubject<SchemaDetailsDto | null>(null); |
|||
|
|||
private get app() { |
|||
return this.appsState.app$.value!.name; |
|||
} |
|||
|
|||
private get user() { |
|||
return this.authState.user!.token; |
|||
} |
|||
|
|||
constructor( |
|||
private readonly schemasService: SchemasService, |
|||
private readonly dialogs: DialogService, |
|||
private readonly authState: AuthService, |
|||
private readonly appsState: AppsStoreService |
|||
) { |
|||
} |
|||
|
|||
public selectSchema(id: string | null): Observable<boolean> { |
|||
const observable = |
|||
!id ? |
|||
Observable.of(null) : |
|||
Observable.of(<SchemaDetailsDto>this.schemasItems.value.find(x => x.id === id && x instanceof SchemaDetailsDto)) |
|||
.switchMap(schema => { |
|||
if (!schema) { |
|||
return this.schemasService.getSchema(this.appsState.app$.value!.name, id).catch(() => Observable.of(null)); |
|||
} else { |
|||
return Observable.of(schema); |
|||
} |
|||
}); |
|||
|
|||
return observable |
|||
.do(schema => { |
|||
this.selectedSchema.next(schema); |
|||
}) |
|||
.map(s => s !== null); |
|||
} |
|||
|
|||
public load(): Observable<any> { |
|||
return this.schemasService.getSchemas(this.app) |
|||
.catch(error => this.notifyError(error)) |
|||
.do(dtos => { |
|||
this.schemasItems.nextBy(v => ImmutableArray.of(dtos)); |
|||
}); |
|||
} |
|||
|
|||
public create(request: CreateSchemaDto) { |
|||
return this.schemasService.postSchema(this.app, request, this.user, DateTime.now()) |
|||
.do(dto => { |
|||
this.schemasItems.nextBy(v => v.push(dto)); |
|||
}); |
|||
} |
|||
|
|||
public addField(schema: SchemaDetailsDto, request: AddFieldDto): Observable<FieldDto> { |
|||
return this.schemasService.postField(this.app, schema.name, request, schema.version) |
|||
.do(dto => { |
|||
this.replaceSchema(schema.addField(dto.payload, this.user, dto.version)); |
|||
}).map(d => d.payload); |
|||
} |
|||
|
|||
public publish(schema: SchemaDto): Observable<any> { |
|||
return this.schemasService.publishSchema(this.app, schema.name, schema.version) |
|||
.catch(error => this.notifyError(error)) |
|||
.do(dto => { |
|||
this.replaceSchema(schema.publish(this.user, dto.version)); |
|||
}); |
|||
} |
|||
|
|||
public unpublish(schema: SchemaDto): Observable<any> { |
|||
return this.schemasService.unpublishSchema(this.app, schema.name, schema.version) |
|||
.catch(error => this.notifyError(error)) |
|||
.do(dto => { |
|||
this.replaceSchema(schema.unpublish(this.user, dto.version)); |
|||
}); |
|||
} |
|||
|
|||
public enableField(schema: SchemaDetailsDto, field: FieldDto): Observable<any> { |
|||
return this.schemasService.enableField(this.app, schema.name, field.fieldId, schema.version) |
|||
.catch(error => this.notifyError(error)) |
|||
.do(dto => { |
|||
this.replaceSchema(schema.updateField(field.enable(), this.user, dto.version)); |
|||
}); |
|||
} |
|||
|
|||
public disableField(schema: SchemaDetailsDto, field: FieldDto): Observable<any> { |
|||
return this.schemasService.disableField(this.app, schema.name, field.fieldId, schema.version) |
|||
.catch(error => this.notifyError(error)) |
|||
.do(dto => { |
|||
this.replaceSchema(schema.updateField(field.disable(), this.user, dto.version)); |
|||
}); |
|||
} |
|||
|
|||
public lockField(schema: SchemaDetailsDto, field: FieldDto): Observable<any> { |
|||
return this.schemasService.lockField(this.app, schema.name, field.fieldId, schema.version) |
|||
.catch(error => this.notifyError(error)) |
|||
.do(dto => { |
|||
this.replaceSchema(schema.updateField(field.lock(), this.user, dto.version)); |
|||
}); |
|||
} |
|||
|
|||
public showField(schema: SchemaDetailsDto, field: FieldDto): Observable<any> { |
|||
return this.schemasService.showField(this.app, schema.name, field.fieldId, schema.version) |
|||
.catch(error => this.notifyError(error)) |
|||
.do(dto => { |
|||
this.replaceSchema(schema.updateField(field.show(), this.user, dto.version)); |
|||
}); |
|||
} |
|||
|
|||
public hideField(schema: SchemaDetailsDto, field: FieldDto): Observable<any> { |
|||
return this.schemasService.hideField(this.app, schema.name, field.fieldId, schema.version) |
|||
.catch(error => this.notifyError(error)) |
|||
.do(dto => { |
|||
this.replaceSchema(schema.updateField(field.hide(), this.user, dto.version)); |
|||
}); |
|||
} |
|||
|
|||
public deleteField(schema: SchemaDetailsDto, field: FieldDto): Observable<any> { |
|||
return this.schemasService.deleteField(this.app, schema.name, field.fieldId, schema.version) |
|||
.catch(error => this.notifyError(error)) |
|||
.do(dto => { |
|||
this.replaceSchema(schema.removeField(field, this.user, dto.version)); |
|||
}); |
|||
} |
|||
|
|||
public sortFields(schema: SchemaDetailsDto, fields: FieldDto[]): Observable<any> { |
|||
return this.schemasService.putFieldOrdering(this.app, schema.name, fields.map(t => t.fieldId), schema.version) |
|||
.catch(error => this.notifyError(error)) |
|||
.do(dto => { |
|||
this.replaceSchema(schema.replaceFields(fields, this.user, dto.version)); |
|||
}); |
|||
} |
|||
|
|||
public updateField(schema: SchemaDetailsDto, field: FieldDto, request: UpdateFieldDto): Observable<any> { |
|||
return this.schemasService.putField(this.app, schema.name, field.fieldId, request, schema.version) |
|||
.catch(error => this.notifyError(error)) |
|||
.do(dto => { |
|||
this.replaceSchema(schema.updateField(field.update(request.properties), this.user, dto.version)); |
|||
}); |
|||
} |
|||
|
|||
public delete(schema: SchemaDto): Observable<any> { |
|||
return this.schemasService.deleteSchema(this.app, schema.name, schema.version) |
|||
.catch(error => this.notifyError(error)) |
|||
.do(dto => { |
|||
this.schemasItems.nextBy(v => v.filter(s => s.id !== schema.id)); |
|||
}); |
|||
} |
|||
|
|||
private replaceSchema(schema: SchemaDto) { |
|||
this.schemasItems.nextBy(v => v.replaceBy('id', schema)); |
|||
|
|||
this.selectedSchema.nextBy(v => v !== null && v.id === schema.id ? <SchemaDetailsDto>schema : v); |
|||
} |
|||
|
|||
public trackBy(index: number, schema: SchemaDto): any { |
|||
return schema.id; |
|||
} |
|||
|
|||
private notifyError(error: string | ErrorDto) { |
|||
if (error instanceof ErrorDto) { |
|||
this.dialogs.notify(Notification.error(error.displayMessage)); |
|||
} else { |
|||
this.dialogs.notify(Notification.error(error)); |
|||
} |
|||
|
|||
return Observable.throw(error); |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
|
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. |
|||
*/ |
|||
|
|||
import 'framework/utils/rxjs-extensions'; |
|||
|
|||
import { Injectable } from '@angular/core'; |
|||
import { BehaviorSubject, Observable } from 'rxjs'; |
|||
|
|||
import { DateTime, ImmutableArray } from 'framework'; |
|||
|
|||
import { |
|||
AppDto, |
|||
AppsService, |
|||
CreateAppDto |
|||
} from './../services/apps.service'; |
|||
|
|||
@Injectable() |
|||
export class AppsState { |
|||
public apps = new BehaviorSubject<ImmutableArray<AppDto>>(ImmutableArray.empty()); |
|||
|
|||
public selectedApp = new BehaviorSubject<AppDto | null>(null); |
|||
|
|||
constructor( |
|||
private readonly appsService: AppsService |
|||
) { |
|||
} |
|||
|
|||
public loadApps(): Observable<any> { |
|||
return this.appsService.getApps() |
|||
.do(dtos => { |
|||
this.apps.nextBy(v => ImmutableArray.of(dtos)); |
|||
}); |
|||
} |
|||
|
|||
public selectApp(name: string | null): Observable<AppDto | null> { |
|||
const observable = |
|||
!name ? |
|||
Observable.of(null) : |
|||
Observable.of(this.apps.value.find(x => x.name === name) || null); |
|||
|
|||
return observable |
|||
.do(app => { |
|||
this.selectedApp.next(app); |
|||
}); |
|||
} |
|||
|
|||
public createApp(dto: CreateAppDto, now?: DateTime): Observable<AppDto> { |
|||
return this.appsService.postApp(dto) |
|||
.do(app => { |
|||
this.apps.nextBy(v => v.push(app)); |
|||
}); |
|||
} |
|||
|
|||
public deleteApp(name: string): Observable<any> { |
|||
return this.appsService.deleteApp(name) |
|||
.do(app => { |
|||
this.apps.nextBy(v => v.filter(a => a.name !== name)); |
|||
}); |
|||
} |
|||
} |
|||
|
After Width: | Height: | Size: 909 B |
Loading…
Reference in new issue