Skip to content

Commit

Permalink
Allow creating workflow steps in workflow editor modal
Browse files Browse the repository at this point in the history
  • Loading branch information
tkleinke committed Mar 3, 2025
1 parent 7459baa commit a5d2c3a
Show file tree
Hide file tree
Showing 12 changed files with 287 additions and 3 deletions.
4 changes: 2 additions & 2 deletions core/src/services/project-configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ export class ProjectConfiguration {
private filterRegularCategories(): Array<CategoryForm> {

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

return flow(this.categoryForms,
removeTrees('Image', 'Project', TYPE_CATALOG, TYPE, 'StoragePlace'),
removeTrees('Image', 'Project', TYPE_CATALOG, TYPE, 'StoragePlace', 'WorkflowStep'),
Tree.flatten
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { Component } from '@angular/core';
import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { CategoryForm, FieldDocument, Document, RelationsManager, Relation, Resource,
Datastore } from 'idai-field-core';
import { Menus } from '../../../../services/menus';
import { MenuContext } from '../../../../services/menu-context';
import { DoceditComponent } from '../../../docedit/docedit.component';
import { Messages } from '../../../messages/messages';
import { M } from '../../../messages/m';


@Component({
templateUrl: './workflow-editor-modal.html',
host: {
'(window:keydown)': 'onKeyDown($event)'
},
standalone: false
})
/**
* @author Thomas Kleinke
*/
export class WorkflowEditorModalComponent {

public document: FieldDocument;
public workflowSteps: Array<Document>;


constructor(private activeModal: NgbActiveModal,
private menus: Menus,
private modalService: NgbModal,
private relationsManager: RelationsManager,
private datastore: Datastore,
private messages: Messages) {}


public cancel = () => this.activeModal.close();


public async onKeyDown(event: KeyboardEvent) {

if (event.key === 'Escape' && this.menus.getContext() === MenuContext.WORKFLOW_EDITOR) {
this.cancel();
}
}


public async initialize() {

await this.updateWorkflowSteps();
}


public async createWorkflowStep(category: CategoryForm) {

const newWorkflowStep: Document = await this.openEditorModal(
WorkflowEditorModalComponent.buildWorkflowStepDocument(category)
);
if (!newWorkflowStep) return;

await this.addRelation(newWorkflowStep);
await this.updateWorkflowSteps();
}


/**
* @returns edited document if changes have been saved, undefined if the modal has been canceled
*/
private async openEditorModal(document: Document): Promise<Document|undefined> {

this.menus.setContext(MenuContext.DOCEDIT);

const doceditRef = this.modalService.open(DoceditComponent,
{ size: 'lg', backdrop: 'static', keyboard: false, animation: false });
doceditRef.componentInstance.setDocument(document);

try {
return (await doceditRef.result).document;
} catch(_) {
// Modal has been canceled
return undefined;
} finally {
this.menus.setContext(MenuContext.WORKFLOW_EDITOR);
}
}


private async addRelation(workflowStep: Document) {

const oldVersion: Document = Document.clone(workflowStep);

const resource: Resource = this.document.resource;
if (!resource.relations) resource.relations = {};
if (!resource.relations[Relation.HAS_WORKFLOW_STEP]) resource.relations[Relation.HAS_WORKFLOW_STEP] = [];
resource.relations[Relation.HAS_WORKFLOW_STEP].push(workflowStep.resource.id);

try {
await this.relationsManager.update(this.document, oldVersion);
} catch (err) {
console.error(err);
this.messages.add([M.DOCEDIT_ERROR_SAVE]);
}
}


private async updateWorkflowSteps() {

const targetIds: string[] = this.document.resource.relations?.[Relation.HAS_WORKFLOW_STEP] ?? [];
this.workflowSteps = await this.datastore.getMultiple(targetIds);
}


private static buildWorkflowStepDocument(category: CategoryForm): Document {

return <Document> {
resource: {
relations: {},
category: category.name
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<div id="view-modal-header" class="modal-header">
<div class="text-truncate heading"
i18n="@@resources.workflowEditorModal.header">
Arbeitssschritte für Ressource <b>{{document.resource.identifier}}</b> dokumentieren
</div>
<button id="workflow-editor-cancel-button" class="btn btn-primary btn-square" (click)="cancel()">
<span class="mdi mdi-close"></span>
</button>
</div>
<div id="view-modal-body" class="modal-body bg-light workflow-editor-modal-body">
<div id="workflow-steps-container">
<div *ngFor="let workflowStep of workflowSteps">
<document-teaser [document]="workflowStep"></document-teaser>
</div>
</div>
<div id="workflow-step-plus-button-container">
<workflow-step-plus-button [baseDocument]="document"
(onCategorySelected)="createWorkflowStep($event)"></workflow-step-plus-button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.workflow-editor-modal-body {
padding: 16px 16px 22px 16px !important;

#workflow-steps-container {
display: block;
width: 100%;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
background-color: white;
border: 1px solid #ccc;
border-radius: 10px;
user-select: none;
}

#workflow-step-plus-button-container {
position: absolute;
bottom: 4px;
width: 100%;
height: 39px;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
import { CategoryForm, FieldDocument, ProjectConfiguration, Relation } from 'idai-field-core';


@Component({
selector: 'workflow-step-plus-button',
templateUrl: './workflow-step-plus-button.html',
standalone: false
})
/**
* @author Thomas Kleinke
*/
export class WorkflowStepPlusButtonComponent implements OnChanges {

@Input() baseDocument: FieldDocument;

@Output() onCategorySelected: EventEmitter<CategoryForm> = new EventEmitter<CategoryForm>();

public topLevelCategoriesArray: Array<CategoryForm>;


constructor(private projectConfiguration: ProjectConfiguration) {}


public selectCategory = (category: CategoryForm) => this.onCategorySelected.emit(category);


ngOnChanges() {

this.topLevelCategoriesArray = this.initializeTopLevelCategoriesArray();
}


public hasMultipleCategoryOptions(): boolean {

return this.topLevelCategoriesArray.length > 1 || this.topLevelCategoriesArray[0].children?.length > 0;
}


private initializeTopLevelCategoriesArray(): Array<CategoryForm> {

return this.projectConfiguration.getTopLevelCategories().filter(category => {
return this.projectConfiguration.isAllowedRelationDomainCategory(
this.baseDocument.resource.category,
category.name,
Relation.HAS_WORKFLOW_STEP
);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<div *ngIf="topLevelCategoriesArray">
<div *ngIf="hasMultipleCategoryOptions()"
class="circular-button green-button"
[ngbPopover]="categoryPickerMenu"
#popover="ngbPopover"
triggers="manual"
autoClose="outside"
(click)="popover.toggle();"
placement="top">
<span class="mdi mdi-plus"></span>
</div>

<div *ngIf="!hasMultipleCategoryOptions()"
class="circular-button category-button"
(click)="selectCategory(topLevelCategoriesArray[0])">
<category-icon [category]="topLevelCategoriesArray[0].name" size="41"></category-icon>
<div class="plus-sign-circle">
<span class="mdi mdi-plus mdi-18px"></span>
</div>
</div>
</div>

<ng-template #categoryPickerMenu>
<div>
<div id="category-picker-menu">
<div class="popover-custom-title">
<span i18n="@@resources.plusButton.selectCategory">Bitte wählen Sie eine Kategorie aus.</span>
</div>
<category-picker [topLevelCategoriesArray]="topLevelCategoriesArray"
(onCategoryPicked)="selectCategory($event)"></category-picker>
</div>
</div>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
workflow-step-plus-button {
.circular-button {
left: calc(50% - 20px);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ export class SidebarListComponent extends BaseList implements AfterViewInit, OnC
case 'scan-storage-place':
await this.resourcesComponent.scanStoragePlace(this.contextMenu.documents as Array<FieldDocument>);
break;
case 'edit-workflow':
await this.resourcesComponent.editWorkflow(this.selectedDocument);
break;
case 'edit-geometry':
await this.viewFacade.setSelectedDocument(this.selectedDocument.resource.id);
this.menuService.setContext(MenuContext.GEOMETRY_EDIT);
Expand Down
22 changes: 22 additions & 0 deletions desktop/src/app/components/resources/resources.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { MsgWithParams } from '../messages/msg-with-params';
import { QrCodeEditorModalComponent } from './actions/edit-qr-code/qr-code-editor-modal.component';
import { StoragePlaceScanner } from './actions/scan-storage-place/storage-place-scanner';
import { WarningsService } from '../../services/warnings/warnings-service';
import { WorkflowEditorModalComponent } from './actions/edit-workflow/workflow-editor-modal.component';


@Component({
Expand Down Expand Up @@ -212,6 +213,27 @@ export class ResourcesComponent implements OnDestroy {
}


public async editWorkflow(document: Document) {

try {
this.menuService.setContext(MenuContext.WORKFLOW_EDITOR);

const modalRef: NgbModalRef = this.modalService.open(
WorkflowEditorModalComponent,
{ size: 'lg', animation: false, backdrop: 'static', keyboard: false }
);
modalRef.componentInstance.document = document;
await modalRef.componentInstance.initialize();
AngularUtility.blurActiveElement();
await modalRef.result;
} catch (err) {
console.error(err);
} finally {
this.menuService.setContext(MenuContext.DEFAULT);
}
}


public async moveDocuments(documents: Array<FieldDocument>) {

this.quitGeometryEditing();
Expand Down
4 changes: 4 additions & 0 deletions desktop/src/app/components/resources/resources.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ import { StoragePlaceScanner } from './actions/scan-storage-place/storage-place-
import { ScanStoragePlaceModalComponent } from './actions/scan-storage-place/scan-storage-place-modal.component';
import { PrintSettingsModalComponent } from './actions/edit-qr-code/print-settings/print-settings-modal.component';
import { CreatePrintSettingsProfileModalComponent } from './actions/edit-qr-code/print-settings/create-print-settings-profile-modal.component';
import { WorkflowEditorModalComponent } from './actions/edit-workflow/workflow-editor-modal.component';
import { WorkflowStepPlusButtonComponent } from './actions/edit-workflow/workflow-step-plus-button.component';

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

Expand Down Expand Up @@ -99,6 +101,8 @@ const remote = window.require('@electron/remote');
CreatePrintSettingsProfileModalComponent,
DeleteQrCodeModalComponent,
ScanStoragePlaceModalComponent,
WorkflowEditorModalComponent,
WorkflowStepPlusButtonComponent
],
providers: [
{ provide: StateSerializer, useClass: StandardStateSerializer },
Expand Down
2 changes: 2 additions & 0 deletions desktop/src/app/components/resources/resources.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
@import 'actions/edit-qr-code/delete-qr-code-modal';
@import 'actions/edit-qr-code/print-settings/print-settings-modal';
@import 'actions/scan-storage-place/scan-storage-place-modal';
@import 'actions/edit-workflow/workflow-editor-modal';
@import 'actions/edit-workflow/workflow-step-plus-button';
@import 'grid-list/grid-list';
@import 'grid-list/grid';
@import 'grid-list/grid-item';
Expand Down
4 changes: 3 additions & 1 deletion desktop/src/app/services/menu-context.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export type MenuContext = 'default'|'docedit'|'modal'|'projects'|'configuration'|'geometryEdit'|'mapLayersEdit'
|'georeferenceEdit'|'configurationEdit'|'configurationValuelistEdit'|'configurationSubfieldEdit'
|'configurationModal'|'configurationManagement'|'warnings'|'qrCodeEditor'|'qrCodeScanner'|'printSettingsModal';
|'configurationModal'|'configurationManagement'|'warnings'|'qrCodeEditor'|'qrCodeScanner'|'printSettingsModal'
|'workflowEditor'


/**
Expand All @@ -24,4 +25,5 @@ export module MenuContext {
export const QR_CODE_EDITOR = 'qrCodeEditor';
export const QR_CODE_SCANNER = 'qrCodeScanner';
export const PRINT_SETTINGS_MODAL = 'printSettingsModal';
export const WORKFLOW_EDITOR = 'workflowEditor';
}

0 comments on commit a5d2c3a

Please sign in to comment.