Skip to content

Commit a5d2c3a

Browse files
committed
Allow creating workflow steps in workflow editor modal
1 parent 7459baa commit a5d2c3a

12 files changed

+287
-3
lines changed

core/src/services/project-configuration.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ export class ProjectConfiguration {
313313
private filterRegularCategories(): Array<CategoryForm> {
314314

315315
return flow(this.categoryForms,
316-
removeTrees('Place', 'Project', TYPE_CATALOG, TYPE, 'StoragePlace', 'Image', 'Operation'),
316+
removeTrees('Place', 'Project', TYPE_CATALOG, TYPE, 'StoragePlace', 'WorkflowStep', 'Image', 'Operation'),
317317
Tree.flatten
318318
);
319319
}
@@ -322,7 +322,7 @@ export class ProjectConfiguration {
322322
private filterFieldCategories(): Array<CategoryForm> {
323323

324324
return flow(this.categoryForms,
325-
removeTrees('Image', 'Project', TYPE_CATALOG, TYPE, 'StoragePlace'),
325+
removeTrees('Image', 'Project', TYPE_CATALOG, TYPE, 'StoragePlace', 'WorkflowStep'),
326326
Tree.flatten
327327
);
328328
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { Component } from '@angular/core';
2+
import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap';
3+
import { CategoryForm, FieldDocument, Document, RelationsManager, Relation, Resource,
4+
Datastore } from 'idai-field-core';
5+
import { Menus } from '../../../../services/menus';
6+
import { MenuContext } from '../../../../services/menu-context';
7+
import { DoceditComponent } from '../../../docedit/docedit.component';
8+
import { Messages } from '../../../messages/messages';
9+
import { M } from '../../../messages/m';
10+
11+
12+
@Component({
13+
templateUrl: './workflow-editor-modal.html',
14+
host: {
15+
'(window:keydown)': 'onKeyDown($event)'
16+
},
17+
standalone: false
18+
})
19+
/**
20+
* @author Thomas Kleinke
21+
*/
22+
export class WorkflowEditorModalComponent {
23+
24+
public document: FieldDocument;
25+
public workflowSteps: Array<Document>;
26+
27+
28+
constructor(private activeModal: NgbActiveModal,
29+
private menus: Menus,
30+
private modalService: NgbModal,
31+
private relationsManager: RelationsManager,
32+
private datastore: Datastore,
33+
private messages: Messages) {}
34+
35+
36+
public cancel = () => this.activeModal.close();
37+
38+
39+
public async onKeyDown(event: KeyboardEvent) {
40+
41+
if (event.key === 'Escape' && this.menus.getContext() === MenuContext.WORKFLOW_EDITOR) {
42+
this.cancel();
43+
}
44+
}
45+
46+
47+
public async initialize() {
48+
49+
await this.updateWorkflowSteps();
50+
}
51+
52+
53+
public async createWorkflowStep(category: CategoryForm) {
54+
55+
const newWorkflowStep: Document = await this.openEditorModal(
56+
WorkflowEditorModalComponent.buildWorkflowStepDocument(category)
57+
);
58+
if (!newWorkflowStep) return;
59+
60+
await this.addRelation(newWorkflowStep);
61+
await this.updateWorkflowSteps();
62+
}
63+
64+
65+
/**
66+
* @returns edited document if changes have been saved, undefined if the modal has been canceled
67+
*/
68+
private async openEditorModal(document: Document): Promise<Document|undefined> {
69+
70+
this.menus.setContext(MenuContext.DOCEDIT);
71+
72+
const doceditRef = this.modalService.open(DoceditComponent,
73+
{ size: 'lg', backdrop: 'static', keyboard: false, animation: false });
74+
doceditRef.componentInstance.setDocument(document);
75+
76+
try {
77+
return (await doceditRef.result).document;
78+
} catch(_) {
79+
// Modal has been canceled
80+
return undefined;
81+
} finally {
82+
this.menus.setContext(MenuContext.WORKFLOW_EDITOR);
83+
}
84+
}
85+
86+
87+
private async addRelation(workflowStep: Document) {
88+
89+
const oldVersion: Document = Document.clone(workflowStep);
90+
91+
const resource: Resource = this.document.resource;
92+
if (!resource.relations) resource.relations = {};
93+
if (!resource.relations[Relation.HAS_WORKFLOW_STEP]) resource.relations[Relation.HAS_WORKFLOW_STEP] = [];
94+
resource.relations[Relation.HAS_WORKFLOW_STEP].push(workflowStep.resource.id);
95+
96+
try {
97+
await this.relationsManager.update(this.document, oldVersion);
98+
} catch (err) {
99+
console.error(err);
100+
this.messages.add([M.DOCEDIT_ERROR_SAVE]);
101+
}
102+
}
103+
104+
105+
private async updateWorkflowSteps() {
106+
107+
const targetIds: string[] = this.document.resource.relations?.[Relation.HAS_WORKFLOW_STEP] ?? [];
108+
this.workflowSteps = await this.datastore.getMultiple(targetIds);
109+
}
110+
111+
112+
private static buildWorkflowStepDocument(category: CategoryForm): Document {
113+
114+
return <Document> {
115+
resource: {
116+
relations: {},
117+
category: category.name
118+
}
119+
};
120+
}
121+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<div id="view-modal-header" class="modal-header">
2+
<div class="text-truncate heading"
3+
i18n="@@resources.workflowEditorModal.header">
4+
Arbeitssschritte für Ressource <b>{{document.resource.identifier}}</b> dokumentieren
5+
</div>
6+
<button id="workflow-editor-cancel-button" class="btn btn-primary btn-square" (click)="cancel()">
7+
<span class="mdi mdi-close"></span>
8+
</button>
9+
</div>
10+
<div id="view-modal-body" class="modal-body bg-light workflow-editor-modal-body">
11+
<div id="workflow-steps-container">
12+
<div *ngFor="let workflowStep of workflowSteps">
13+
<document-teaser [document]="workflowStep"></document-teaser>
14+
</div>
15+
</div>
16+
<div id="workflow-step-plus-button-container">
17+
<workflow-step-plus-button [baseDocument]="document"
18+
(onCategorySelected)="createWorkflowStep($event)"></workflow-step-plus-button>
19+
</div>
20+
</div>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
.workflow-editor-modal-body {
2+
padding: 16px 16px 22px 16px !important;
3+
4+
#workflow-steps-container {
5+
display: block;
6+
width: 100%;
7+
height: 100%;
8+
overflow-x: hidden;
9+
overflow-y: auto;
10+
background-color: white;
11+
border: 1px solid #ccc;
12+
border-radius: 10px;
13+
user-select: none;
14+
}
15+
16+
#workflow-step-plus-button-container {
17+
position: absolute;
18+
bottom: 4px;
19+
width: 100%;
20+
height: 39px;
21+
}
22+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
2+
import { CategoryForm, FieldDocument, ProjectConfiguration, Relation } from 'idai-field-core';
3+
4+
5+
@Component({
6+
selector: 'workflow-step-plus-button',
7+
templateUrl: './workflow-step-plus-button.html',
8+
standalone: false
9+
})
10+
/**
11+
* @author Thomas Kleinke
12+
*/
13+
export class WorkflowStepPlusButtonComponent implements OnChanges {
14+
15+
@Input() baseDocument: FieldDocument;
16+
17+
@Output() onCategorySelected: EventEmitter<CategoryForm> = new EventEmitter<CategoryForm>();
18+
19+
public topLevelCategoriesArray: Array<CategoryForm>;
20+
21+
22+
constructor(private projectConfiguration: ProjectConfiguration) {}
23+
24+
25+
public selectCategory = (category: CategoryForm) => this.onCategorySelected.emit(category);
26+
27+
28+
ngOnChanges() {
29+
30+
this.topLevelCategoriesArray = this.initializeTopLevelCategoriesArray();
31+
}
32+
33+
34+
public hasMultipleCategoryOptions(): boolean {
35+
36+
return this.topLevelCategoriesArray.length > 1 || this.topLevelCategoriesArray[0].children?.length > 0;
37+
}
38+
39+
40+
private initializeTopLevelCategoriesArray(): Array<CategoryForm> {
41+
42+
return this.projectConfiguration.getTopLevelCategories().filter(category => {
43+
return this.projectConfiguration.isAllowedRelationDomainCategory(
44+
this.baseDocument.resource.category,
45+
category.name,
46+
Relation.HAS_WORKFLOW_STEP
47+
);
48+
});
49+
}
50+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<div *ngIf="topLevelCategoriesArray">
2+
<div *ngIf="hasMultipleCategoryOptions()"
3+
class="circular-button green-button"
4+
[ngbPopover]="categoryPickerMenu"
5+
#popover="ngbPopover"
6+
triggers="manual"
7+
autoClose="outside"
8+
(click)="popover.toggle();"
9+
placement="top">
10+
<span class="mdi mdi-plus"></span>
11+
</div>
12+
13+
<div *ngIf="!hasMultipleCategoryOptions()"
14+
class="circular-button category-button"
15+
(click)="selectCategory(topLevelCategoriesArray[0])">
16+
<category-icon [category]="topLevelCategoriesArray[0].name" size="41"></category-icon>
17+
<div class="plus-sign-circle">
18+
<span class="mdi mdi-plus mdi-18px"></span>
19+
</div>
20+
</div>
21+
</div>
22+
23+
<ng-template #categoryPickerMenu>
24+
<div>
25+
<div id="category-picker-menu">
26+
<div class="popover-custom-title">
27+
<span i18n="@@resources.plusButton.selectCategory">Bitte wählen Sie eine Kategorie aus.</span>
28+
</div>
29+
<category-picker [topLevelCategoriesArray]="topLevelCategoriesArray"
30+
(onCategoryPicked)="selectCategory($event)"></category-picker>
31+
</div>
32+
</div>
33+
</ng-template>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
workflow-step-plus-button {
2+
.circular-button {
3+
left: calc(50% - 20px);
4+
}
5+
}

desktop/src/app/components/resources/map/list/sidebar-list.component.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ export class SidebarListComponent extends BaseList implements AfterViewInit, OnC
163163
case 'scan-storage-place':
164164
await this.resourcesComponent.scanStoragePlace(this.contextMenu.documents as Array<FieldDocument>);
165165
break;
166+
case 'edit-workflow':
167+
await this.resourcesComponent.editWorkflow(this.selectedDocument);
168+
break;
166169
case 'edit-geometry':
167170
await this.viewFacade.setSelectedDocument(this.selectedDocument.resource.id);
168171
this.menuService.setContext(MenuContext.GEOMETRY_EDIT);

desktop/src/app/components/resources/resources.component.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { MsgWithParams } from '../messages/msg-with-params';
2121
import { QrCodeEditorModalComponent } from './actions/edit-qr-code/qr-code-editor-modal.component';
2222
import { StoragePlaceScanner } from './actions/scan-storage-place/storage-place-scanner';
2323
import { WarningsService } from '../../services/warnings/warnings-service';
24+
import { WorkflowEditorModalComponent } from './actions/edit-workflow/workflow-editor-modal.component';
2425

2526

2627
@Component({
@@ -212,6 +213,27 @@ export class ResourcesComponent implements OnDestroy {
212213
}
213214

214215

216+
public async editWorkflow(document: Document) {
217+
218+
try {
219+
this.menuService.setContext(MenuContext.WORKFLOW_EDITOR);
220+
221+
const modalRef: NgbModalRef = this.modalService.open(
222+
WorkflowEditorModalComponent,
223+
{ size: 'lg', animation: false, backdrop: 'static', keyboard: false }
224+
);
225+
modalRef.componentInstance.document = document;
226+
await modalRef.componentInstance.initialize();
227+
AngularUtility.blurActiveElement();
228+
await modalRef.result;
229+
} catch (err) {
230+
console.error(err);
231+
} finally {
232+
this.menuService.setContext(MenuContext.DEFAULT);
233+
}
234+
}
235+
236+
215237
public async moveDocuments(documents: Array<FieldDocument>) {
216238

217239
this.quitGeometryEditing();

desktop/src/app/components/resources/resources.module.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ import { StoragePlaceScanner } from './actions/scan-storage-place/storage-place-
5454
import { ScanStoragePlaceModalComponent } from './actions/scan-storage-place/scan-storage-place-modal.component';
5555
import { PrintSettingsModalComponent } from './actions/edit-qr-code/print-settings/print-settings-modal.component';
5656
import { CreatePrintSettingsProfileModalComponent } from './actions/edit-qr-code/print-settings/create-print-settings-profile-modal.component';
57+
import { WorkflowEditorModalComponent } from './actions/edit-workflow/workflow-editor-modal.component';
58+
import { WorkflowStepPlusButtonComponent } from './actions/edit-workflow/workflow-step-plus-button.component';
5759

5860
const remote = window.require('@electron/remote');
5961

@@ -99,6 +101,8 @@ const remote = window.require('@electron/remote');
99101
CreatePrintSettingsProfileModalComponent,
100102
DeleteQrCodeModalComponent,
101103
ScanStoragePlaceModalComponent,
104+
WorkflowEditorModalComponent,
105+
WorkflowStepPlusButtonComponent
102106
],
103107
providers: [
104108
{ provide: StateSerializer, useClass: StandardStateSerializer },

desktop/src/app/components/resources/resources.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
@import 'actions/edit-qr-code/delete-qr-code-modal';
1515
@import 'actions/edit-qr-code/print-settings/print-settings-modal';
1616
@import 'actions/scan-storage-place/scan-storage-place-modal';
17+
@import 'actions/edit-workflow/workflow-editor-modal';
18+
@import 'actions/edit-workflow/workflow-step-plus-button';
1719
@import 'grid-list/grid-list';
1820
@import 'grid-list/grid';
1921
@import 'grid-list/grid-item';

desktop/src/app/services/menu-context.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export type MenuContext = 'default'|'docedit'|'modal'|'projects'|'configuration'|'geometryEdit'|'mapLayersEdit'
22
|'georeferenceEdit'|'configurationEdit'|'configurationValuelistEdit'|'configurationSubfieldEdit'
3-
|'configurationModal'|'configurationManagement'|'warnings'|'qrCodeEditor'|'qrCodeScanner'|'printSettingsModal';
3+
|'configurationModal'|'configurationManagement'|'warnings'|'qrCodeEditor'|'qrCodeScanner'|'printSettingsModal'
4+
|'workflowEditor'
45

56

67
/**
@@ -24,4 +25,5 @@ export module MenuContext {
2425
export const QR_CODE_EDITOR = 'qrCodeEditor';
2526
export const QR_CODE_SCANNER = 'qrCodeScanner';
2627
export const PRINT_SETTINGS_MODAL = 'printSettingsModal';
28+
export const WORKFLOW_EDITOR = 'workflowEditor';
2729
}

0 commit comments

Comments
 (0)