Browse Source

Test

pull/379/head
Sebastian Stehle 7 years ago
parent
commit
2b030de8e0
  1. 1
      src/Squidex/app/features/settings/declarations.ts
  2. 4
      src/Squidex/app/features/settings/module.ts
  3. 49
      src/Squidex/app/features/settings/pages/workflows/workflow-step.component.html
  4. 23
      src/Squidex/app/features/settings/pages/workflows/workflow-step.component.scss
  5. 20
      src/Squidex/app/features/settings/pages/workflows/workflow-step.component.ts
  6. 52
      src/Squidex/app/features/settings/pages/workflows/workflow-transition.component.html
  7. 6
      src/Squidex/app/features/settings/pages/workflows/workflow-transition.component.scss
  8. 84
      src/Squidex/app/features/settings/pages/workflows/workflow-transition.component.ts
  9. 4
      src/Squidex/app/features/settings/pages/workflows/workflows-page.component.html
  10. 26
      src/Squidex/app/features/settings/pages/workflows/workflows-page.component.ts
  11. 8
      src/Squidex/app/framework/angular/forms/focus-on-init.directive.ts
  12. 36
      src/Squidex/app/shared/services/workflows.service.ts

1
src/Squidex/app/features/settings/declarations.ts

@ -19,6 +19,7 @@ export * from './pages/plans/plans-page.component';
export * from './pages/roles/role.component'; export * from './pages/roles/role.component';
export * from './pages/roles/roles-page.component'; export * from './pages/roles/roles-page.component';
export * from './pages/workflows/workflow-step.component'; export * from './pages/workflows/workflow-step.component';
export * from './pages/workflows/workflow-transition.component';
export * from './pages/workflows/workflows-page.component'; export * from './pages/workflows/workflows-page.component';
export * from './settings-area.component'; export * from './settings-area.component';

4
src/Squidex/app/features/settings/module.ts

@ -32,7 +32,8 @@ import {
RolesPageComponent, RolesPageComponent,
SettingsAreaComponent, SettingsAreaComponent,
WorkflowsPageComponent, WorkflowsPageComponent,
WorkflowStepComponent WorkflowStepComponent,
WorkflowTransitionComponent
} from './declarations'; } from './declarations';
const routes: Routes = [ const routes: Routes = [
@ -204,6 +205,7 @@ const routes: Routes = [
RolesPageComponent, RolesPageComponent,
SettingsAreaComponent, SettingsAreaComponent,
WorkflowsPageComponent, WorkflowsPageComponent,
WorkflowTransitionComponent,
WorkflowStepComponent WorkflowStepComponent
] ]
}) })

49
src/Squidex/app/features/settings/pages/workflows/workflow-step.component.html

@ -25,37 +25,13 @@
</div> </div>
</div> </div>
<div class="row transition no-gutters" *ngFor="let transition of transitions"> <div class="step-inner">
<div class="col-auto"> <sqx-workflow-transition *ngFor="let transition of transitions; trackBy: trackByTransition"
<i class="icon-arrow-right text-decent"></i> [transition]="transition"
</div> [roles]="roles"
<div class="col col-step"> (remove)="transitionRemove.emit(transition)"
<div class="transition-to"> (update)="changeTransition(transition, $event)">
<div class="color-circle" [style.background]="transition.step.color"></div> {{transition.to}} </sqx-workflow-transition>
</div>
</div>
<div class="col-auto col-label">
<span class="text-decent">when</span>
</div>
<div class="col">
<button class="btn btn-block btn-outline-secondary dashed">
Add Expression
</button>
</div>
<div class="col-auto col-label">
<span class="text-decent">for</span>
</div>
<div class="col">
<button class="btn btn-block btn-outline-secondary dashed">
Add Role
</button>
</div>
<div class="col-auto pl-2">
<button type="button" class="btn btn-text-danger" (click)="transitionRemove.emit(transition)">
<i class="icon-bin2"></i>
</button>
</div>
</div>
<div class="row transition no-gutters" *ngIf="openSteps.length > 0"> <div class="row transition no-gutters" *ngIf="openSteps.length > 0">
<div class="col-auto"> <div class="col-auto">
@ -74,4 +50,15 @@
</button> </button>
</div> </div>
</div> </div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="preventUpdates_{{step.name}}"
[ngModel]="step.noUpdate"
(ngModelChange)="changeNoUpdate($event)" />
<label class="form-check-label" for="preventUpdates_{{step.name}}">
Prevent updates
</label>
</div>
</div>
</div> </div>

23
src/Squidex/app/features/settings/pages/workflows/workflow-step.component.scss

@ -1,6 +1,7 @@
@import '_vars'; @import '_vars';
@import '_mixins'; @import '_mixins';
:host ::ng-deep {
.color { .color {
line-height: 2.8rem; line-height: 2.8rem;
} }
@ -12,13 +13,18 @@
display: inline-block; display: inline-block;
} }
.dashed { .col-label {
border-style: dashed; padding: 0 .5rem;
}
.col-step {
min-width: 10rem;
max-width: 10rem;
padding-left: .5rem;
} }
.transition { .transition {
& { & {
padding-left: 1rem;
margin-top: .25rem; margin-top: .25rem;
margin-bottom: .5rem; margin-bottom: .5rem;
line-height: 2.2rem; line-height: 2.2rem;
@ -30,19 +36,14 @@
line-height: 1.2rem; line-height: 1.2rem;
} }
} }
}
.step { .step {
& { & {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
}
.col-label { &-inner {
padding: 0 .5rem; padding-left: 1rem;
} }
.col-step {
min-width: 10rem;
max-width: 10rem;
padding-left: .5rem;
} }

20
src/Squidex/app/features/settings/pages/workflows/workflow-step.component.ts

@ -8,10 +8,12 @@
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { import {
RoleDto,
WorkflowDto, WorkflowDto,
WorkflowStep, WorkflowStep,
WorkflowStepValues, WorkflowStepValues,
WorkflowTransition, WorkflowTransition,
WorkflowTransitionValues,
WorkflowTransitionView WorkflowTransitionView
} from '@app/shared'; } from '@app/shared';
@ -27,12 +29,18 @@ export class WorkflowStepComponent implements OnChanges {
@Input() @Input()
public step: WorkflowStep; public step: WorkflowStep;
@Input()
public roles: RoleDto[];
@Output() @Output()
public transitionAdd = new EventEmitter<WorkflowStep>(); public transitionAdd = new EventEmitter<WorkflowStep>();
@Output() @Output()
public transitionRemove = new EventEmitter<WorkflowTransition>(); public transitionRemove = new EventEmitter<WorkflowTransition>();
@Output()
public transitionUpdate = new EventEmitter<{ transition: WorkflowTransition, values: WorkflowTransitionValues }>();
@Output() @Output()
public update = new EventEmitter<WorkflowStepValues>(); public update = new EventEmitter<WorkflowStepValues>();
@ -58,6 +66,10 @@ export class WorkflowStepComponent implements OnChanges {
} }
} }
public changeTransition(transition: WorkflowTransition, values: WorkflowTransitionValues) {
this.transitionUpdate.emit({ transition, values });
}
public changeName(name: string) { public changeName(name: string) {
this.rename.emit(name); this.rename.emit(name);
} }
@ -65,5 +77,13 @@ export class WorkflowStepComponent implements OnChanges {
public changeColor(color: string) { public changeColor(color: string) {
this.update.emit({ color }); this.update.emit({ color });
} }
public changeNoUpdate(noUpdate: boolean) {
this.update.emit({ noUpdate });
}
public trackByTransition(index: number, transition: WorkflowTransition) {
return transition.to;
}
} }

52
src/Squidex/app/features/settings/pages/workflows/workflow-transition.component.html

@ -0,0 +1,52 @@
<div class="row transition no-gutters">
<div class="col-auto">
<i class="icon-arrow-right text-decent"></i>
</div>
<div class="col col-step">
<div class="transition-to">
<div class="color-circle" [style.background]="transition.step.color"></div> {{transition.to}}
</div>
</div>
<div class="col-auto col-label">
<span class="text-decent">when</span>
</div>
<div class="col">
<ng-container *ngIf="elementsActive['expression']; else noExpression">
<input class="form-control" sqxFocusOnInit (focus)="focusElement('expression')" (blur)="blurElement('expression')" spellcheck="false"
[ngModelOptions]="onBlur"
[ngModel]="transition.expression"
(ngModelChange)="changeExpression($event)" />
</ng-container>
<ng-template #noExpression>
<button class="btn btn-block btn-outline-secondary dashed" (click)="showElement('expression')">
Add Expression
</button>
</ng-template>
</div>
<div class="col-auto col-label">
<span class="text-decent">for</span>
</div>
<div class="col">
<ng-container *ngIf="elementsActive['role']; else noRole">
<select class="form-control" sqxFocusOnInit (focus)="focusElement('role')" (blur)="blurElement('role')"
[ngModelOptions]="onBlur"
[ngModel]="transition.role"
(ngModelChange)="changeRole($event)">
<option *ngFor="let role of roles; trackBy: trackByRole" [ngValue]="role.name">{{role.name}}</option>
<option [ngValue]="null">- All Roles -</option>
</select>
</ng-container>
<ng-template #noRole>
<button class="btn btn-block btn-outline-secondary dashed" (click)="showElement('role')">
Add Role
</button>
</ng-template>
</div>
<div class="col-auto pl-2">
<button type="button" class="btn btn-text-danger" (click)="remove.emit()">
<i class="icon-bin2"></i>
</button>
</div>
</div>

6
src/Squidex/app/features/settings/pages/workflows/workflow-transition.component.scss

@ -0,0 +1,6 @@
@import '_vars';
@import '_mixins';
.dashed {
border-style: dashed;
}

84
src/Squidex/app/features/settings/pages/workflows/workflow-transition.component.ts

@ -0,0 +1,84 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import {
RoleDto,
WorkflowTransitionValues,
WorkflowTransitionView
} from '@app/shared';
@Component({
selector: 'sqx-workflow-transition',
styleUrls: ['./workflow-transition.component.scss'],
templateUrl: './workflow-transition.component.html'
})
export class WorkflowTransitionComponent implements OnChanges {
@Input()
public transition: WorkflowTransitionView;
@Input()
public roles: RoleDto[];
@Output()
public update = new EventEmitter<WorkflowTransitionValues>();
@Output()
public remove = new EventEmitter();
public elementsActive: { [name: string]: boolean } = {};
public elementsFocused: { [name: string]: boolean } = {};
public elementsValid: { [name: string]: boolean } = {};
public onBlur = { updateOn: 'blur' };
public ngOnChanges(changes: SimpleChanges) {
if (changes['transition']) {
if (this.transition.expression) {
this.elementsValid['expression'] = true;
this.elementsActive['expression'] = true;
}
if (this.transition.role) {
this.elementsValid['role'] = true;
this.elementsActive['role'] = true;
}
}
}
public changeExpression(expression: string) {
this.update.emit({ expression });
}
public changeRole(role: string) {
this.update.emit({ role });
}
public showElement(name: string) {
this.elementsActive[name] = true;
}
public focusElement(name: string) {
this.elementsFocused[name] = true;
}
public blurElement(name: string) {
this.elementsFocused[name] = false;
setTimeout(() => {
if (!this.elementsFocused[name] && !this.elementsValid[name]) {
this.elementsActive[name] = false;
}
}, 2000);
}
public trackByRole(index: number, role: RoleDto) {
return role.name;
}
}

4
src/Squidex/app/features/settings/pages/workflows/workflows-page.component.html

@ -20,15 +20,19 @@
</ng-container> </ng-container>
<ng-container content> <ng-container content>
<ng-container *ngIf="rolesState.roles | async; let roles">
<sqx-workflow-step *ngFor="let step of workflow.steps; trackBy: trackByStep" <sqx-workflow-step *ngFor="let step of workflow.steps; trackBy: trackByStep"
[workflow]="workflow" [workflow]="workflow"
[step]="step" [step]="step"
[roles]="roles"
(rename)="renameStep(step, $event)" (rename)="renameStep(step, $event)"
(remove)="removeStep(step)" (remove)="removeStep(step)"
(transitionAdd)="addTransiton(step, $event)" (transitionAdd)="addTransiton(step, $event)"
(transitionRemove)="removeTransition(step, $event)" (transitionRemove)="removeTransition(step, $event)"
(transitionUpdate)="updateTransition($event)"
(update)="updateStep(step, $event)"> (update)="updateStep(step, $event)">
</sqx-workflow-step> </sqx-workflow-step>
</ng-container>
<button class="btn btn-success" (click)="addStep()"> <button class="btn btn-success" (click)="addStep()">
Add Step Add Step

26
src/Squidex/app/features/settings/pages/workflows/workflows-page.component.ts

@ -9,10 +9,12 @@ import { Component, OnInit } from '@angular/core';
import { import {
MathHelper, MathHelper,
RolesState,
WorkflowDto, WorkflowDto,
WorkflowStep, WorkflowStep,
WorkflowStepValues, WorkflowStepValues,
WorkflowTransition WorkflowTransition,
WorkflowTransitionValues
} from '@app/shared'; } from '@app/shared';
@Component({ @Component({
@ -25,8 +27,24 @@ export class WorkflowsPageComponent implements OnInit {
public workflow: WorkflowDto; public workflow: WorkflowDto;
constructor(
public readonly rolesState: RolesState
) {
}
public ngOnInit() { public ngOnInit() {
this.workflow = new WorkflowDto().setStep('Published', { color: 'green', isLocked: true }); this.rolesState.load();
this.workflow =
new WorkflowDto()
.setStep('Archived', { color: '#eb3142', noUpdate: true })
.setStep('Draft', { color: '#8091a5' })
.setStep('Published', { color: '#4bb958', isLocked: true })
.setTransition('Archived', 'Draft')
.setTransition('Draft', 'Archived')
.setTransition('Draft', 'Published')
.setTransition('Published', 'Draft')
.setTransition('Published', 'Archived');
} }
public reload() { public reload() {
@ -51,6 +69,10 @@ export class WorkflowsPageComponent implements OnInit {
this.workflow = this.workflow.removeTransition(from.name, transition.to); this.workflow = this.workflow.removeTransition(from.name, transition.to);
} }
public updateTransition(update: { transition: WorkflowTransition, values: WorkflowTransitionValues }) {
this.workflow = this.workflow.setTransition(update.transition.from, update.transition.to, update.values);
}
public updateStep(step: WorkflowStep, values: WorkflowStepValues) { public updateStep(step: WorkflowStep, values: WorkflowStepValues) {
this.workflow = this.workflow.setStep(step.name, values); this.workflow = this.workflow.setStep(step.name, values);
} }

8
src/Squidex/app/framework/angular/forms/focus-on-init.directive.ts

@ -17,7 +17,7 @@ export class FocusOnInitDirective implements AfterViewInit {
public select: boolean; public select: boolean;
constructor( constructor(
private readonly element: ElementRef private readonly element: ElementRef<HTMLElement>
) { ) {
} }
@ -28,8 +28,10 @@ export class FocusOnInitDirective implements AfterViewInit {
} }
if (this.select) { if (this.select) {
if (Types.isFunction(this.element.nativeElement.select)) { const input: HTMLInputElement = <any>this.element.nativeElement;
this.element.nativeElement.select();
if (Types.isFunction(input.select)) {
input.select();
} }
} }
}, 100); }, 100);

36
src/Squidex/app/shared/services/workflows.service.ts

@ -26,6 +26,8 @@ export class WorkflowDto extends Model<WorkflowDto> {
public onCloned() { public onCloned() {
this.steps.sort((a, b) => compareStringsAsc(a.name, b.name)); this.steps.sort((a, b) => compareStringsAsc(a.name, b.name));
this.transitions.sort((a, b) => compareStringsAsc(a.to, b.to));
} }
public getOpenSteps(step: WorkflowStep) { public getOpenSteps(step: WorkflowStep) {
@ -41,7 +43,19 @@ export class WorkflowDto extends Model<WorkflowDto> {
} }
public setStep(name: string, values: Partial<WorkflowStepValues>) { public setStep(name: string, values: Partial<WorkflowStepValues>) {
const steps = [...this.steps.filter(s => s.name !== name), { name, ...values }]; const found = this.getStep(name);
if (found) {
const { name: _, ...existing } = found;
if (found.isLocked) {
return this;
}
values = { ...values, ...existing };
}
const steps = [...this.steps.filter(s => s !== found), { name, ...values }];
return this.with({ steps }); return this.with({ steps });
} }
@ -93,29 +107,37 @@ export class WorkflowDto extends Model<WorkflowDto> {
return this.with({ transitions }); return this.with({ transitions });
} }
public setTransition(from: string, to: string, values: Partial<WorkflowTransitionValues>) { public setTransition(from: string, to: string, values?: Partial<WorkflowTransitionValues>) {
const stepFrom = this.steps.find(s => s.name === from); const stepFrom = this.getStep(from);
if (!stepFrom) { if (!stepFrom) {
return this; return this;
} }
const stepTo = this.steps.find(s => s.name === to); const stepTo = this.getStep(to);
if (!stepTo) { if (!stepTo) {
return this; return this;
} }
const transitions = [...this.transitions.filter(t => t.from !== from || t.to !== to), { from, to, ...values }]; const found = this.transitions.find(x => x.from === from && x.to === to);
if (found) {
const { from: _, to: __, ...existing } = found;
values = { ...values, ...existing };
}
const transitions = [...this.transitions.filter(t => t !== found), { from, to, ...values }];
return this.with({ transitions }); return this.with({ transitions });
} }
} }
export type WorkflowStepValues = { color?: string; isLocked?: boolean; }; export type WorkflowStepValues = { color?: string; isLocked?: boolean; noUpdate?: boolean; };
export type WorkflowStep = { name: string } & WorkflowStepValues; export type WorkflowStep = { name: string } & WorkflowStepValues;
export type WorkflowTransitionValues = { expression?: string }; export type WorkflowTransitionValues = { expression?: string; role?: string; };
export type WorkflowTransition = { from: string; to: string } & WorkflowTransitionValues; export type WorkflowTransition = { from: string; to: string } & WorkflowTransitionValues;
export type WorkflowTransitionView = { step: WorkflowStep } & WorkflowTransition; export type WorkflowTransitionView = { step: WorkflowStep } & WorkflowTransition;
Loading…
Cancel
Save