Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions app/guid-node/workflow/-components/cancel-run-dialog/component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { action } from '@ember/object';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

interface WorkflowCancelRunDialogArgs {
open: boolean;
run: {
id: string;
status?: string;
statusRaw?: string;
started_at?: string; // eslint-disable-line camelcase
created?: string;
} | null;
isSubmitting: boolean;
error: string | null;
onClose: () => void;
onConfirm: (reason: string) => void;
}

export default class WorkflowCancelRunDialog extends Component<WorkflowCancelRunDialogArgs> {
@tracked reason = '';

@action
updateReason(event: Event): void {
const target = event.target as HTMLTextAreaElement;
this.reason = target.value;
}

@action
handleConfirm(): void {
this.args.onConfirm(this.reason);
}
}

declare module '@glimmer/component' {
export default interface Registry {
'Workflow::CancelRunDialog': typeof WorkflowCancelRunDialog;
} // eslint-disable-line semi
}
74 changes: 74 additions & 0 deletions app/guid-node/workflow/-components/cancel-run-dialog/template.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{{#if @open}}
<div class="modal fade in workflow-cancel-dialog" tabindex="-1" role="dialog" aria-modal="true" style="display: block;">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" aria-label={{t 'workflow.console.close'}} {{on "click" @onClose}}>
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">{{t 'workflow.console.runs.cancelDialogTitle'}}</h4>
</div>
<div class="modal-body">
<p>{{t 'workflow.console.runs.confirmCancel' processId=@run.id}}</p>

<dl class="dl-horizontal">
<dt>{{t 'workflow.console.runs.columns.processId'}}</dt>
<dd>{{@run.id}}</dd>
<dt>{{t 'workflow.console.runs.columns.status'}}</dt>
<dd>
<span class="label label-default {{if @run.status (concat 'status-' @run.status)}}">
{{or @run.statusRaw @run.status}}
</span>
</dd>
<dt>{{t 'workflow.console.runs.columns.started'}}</dt>
<dd>{{or @run.started_at @run.created}}</dd>
</dl>

<div class="form-group">
<label for="cancel-reason">{{t 'workflow.console.runs.cancelReasonLabel'}}</label>
<Textarea
id="cancel-reason"
class="form-control"
@value={{this.reason}}
{{on "input" this.updateReason}}
rows="3"
placeholder={{t 'workflow.console.runs.cancelReasonPlaceholder'}}
/>
<p class="help-block">{{t 'workflow.console.runs.cancelReasonHint'}}</p>
</div>

{{#if @error}}
<div class="alert alert-danger" role="alert">
{{@error}}
</div>
{{/if}}
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-default"
{{on "click" @onClose}}
disabled={{@isSubmitting}}
>
{{t 'general.cancel'}}
</button>
<button
type="button"
class="btn btn-danger"
{{on "click" this.handleConfirm}}
disabled={{@isSubmitting}}
>
{{#if @isSubmitting}}
<i class="fa fa-spinner fa-spin"></i>
{{t 'workflow.console.runs.actions.cancelling'}}
{{else}}
<i class="fa fa-times"></i>
{{t 'workflow.console.runs.cancelConfirmButton'}}
{{/if}}
</button>
</div>
</div>
</div>
</div>
<div class="modal-backdrop fade in"></div>
{{/if}}
144 changes: 144 additions & 0 deletions app/guid-node/workflow/-components/cedar-form/component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { action } from '@ember/object';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

import {
WorkflowTaskForm,
WorkflowVariable,
} from '../../types';

interface CedarFormArgs {
form: WorkflowTaskForm;
onChange: (variables: WorkflowVariable[], isValid: boolean) => void;
}

type CedarEditorElement = HTMLElement & {
templateObject?: unknown;
config?: unknown;
currentMetadata?: Record<string, unknown>;
};

const CEDAR_SCRIPT_URL = '/static/cedar-embeddable-editor.js';
let cedarLoader: Promise<void> | null = null;

function ensureCedarEditorScript(): Promise<void> {
if (typeof window === 'undefined') {
return Promise.resolve();
}
if (window.customElements && window.customElements.get('cedar-embeddable-editor')) {
return Promise.resolve();
}
if (cedarLoader) {
return cedarLoader;
}
cedarLoader = new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = CEDAR_SCRIPT_URL;
script.async = true;
script.onload = () => resolve();
script.onerror = () => reject(new Error('Failed to load CEDAR editor script.'));
document.head.appendChild(script);
});
return cedarLoader;
}

export default class CedarForm extends Component<CedarFormArgs> {
@tracked cedarError: string | null = null;

private cedarEditor: CedarEditorElement | null = null;

get cedarTemplate(): unknown {
const { data } = this.args.form;
return data !== undefined ? data : null;
}

get cedarTemplateObject(): Record<string, unknown> | null {
const template = this.cedarTemplate;
if (!template) {
return null;
}
if (typeof template === 'string') {
try {
return JSON.parse(template) as Record<string, unknown>;
} catch (error) {
const err = error as Error;
this.cedarError = (err && err.message) || String(error);
return null;
}
}
if (typeof template === 'object') {
return template as Record<string, unknown>;
}
return null;
}

@action
async setupCedarHost(element: HTMLElement): Promise<void> {
if (!element) {
return;
}
element.innerHTML = ''; // eslint-disable-line no-param-reassign
this.cedarEditor = null;
this.cedarError = null;
if (!this.cedarTemplateObject) {
this.notifyChange();
return;
}
try {
await ensureCedarEditorScript();
const editor = document.createElement('cedar-embeddable-editor') as CedarEditorElement;
editor.templateObject = this.cedarTemplateObject;
editor.config = JSON.stringify({
showSampleTemplateLinks: false,
terminologyIntegratedSearchUrl: 'https://terminology.metadatacenter.org/bioportal/integrated-search',
showTemplateSourceData: false,
showInstanceDataCore: false,
});
element.appendChild(editor);
this.cedarEditor = editor;
this.notifyChange();
} catch (error) {
const err = error as Error;
this.cedarError = (err && err.message) || String(error);
}
}

@action
refreshCedarTemplate(element: HTMLElement, template: Record<string, unknown> | null): void {
if (template && this.cedarEditor) {
this.cedarEditor.templateObject = template;
this.notifyChange();
} else if (template && !this.cedarEditor) {
this.setupCedarHost(element);
}
}

getCurrentVariables(): WorkflowVariable[] {
if (!this.cedarEditor || !this.cedarEditor.currentMetadata) {
return [];
}
const metadata = this.cedarEditor.currentMetadata;
const variables: WorkflowVariable[] = [];
for (const key of Object.keys(metadata)) {
if (key === '@context') {
continue;
}
const entry = metadata[key];
if (entry && typeof entry === 'object' && Object.prototype.hasOwnProperty.call(entry as object, '@value')) {
variables.push({
name: key,
value: (entry as Record<string, unknown>)['@value'],
type: 'string',
});
continue;
}
variables.push({ name: key, value: entry, type: 'json' });
}
return variables;
}

private notifyChange(): void {
const variables = this.getCurrentVariables();
this.args.onChange(variables, true);
}
}
10 changes: 10 additions & 0 deletions app/guid-node/workflow/-components/cedar-form/template.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{{#if this.cedarError}}
<div class="alert alert-warning" role="alert">
{{this.cedarError}}
</div>
{{/if}}

<div class="form-group">
<label class="control-label">{{t 'workflow.console.tasks.dialog.formSection'}}</label>
<div class="workflow-cedar-editor" {{did-insert this.setupCedarHost}} {{did-update this.refreshCedarTemplate this.cedarTemplateObject}}></div>
</div>
Loading