Durchsuchbare Dokumentation aufrufen | Zurück zur Dokumentationsübersicht
Navigation: Dokumentationen agorum core > agorum core für Entwickler > agorum core cards
Diese Dokumentation beschreibt, wie Workflow-basierte Cards (Cardlets) für die Verwendung auf Mobilgeräten optimiert werden können. Am Beispiel des Freigabeworkflows agorum.workflow.releaseXEyes wird gezeigt, wie Benutzer direkt über die Objektkarte (z. B. im Eingang (aktuell)) interagieren können, ohne in die komplexe Workflow-Oberfläche wechseln zu müssen.
Ein typischer Anwendungsfall für das Bearbeiten eines Workflow-Schritts sind Freigabeworkflows, etwa für:
Problem: Das verantwortliche Management ist oft unterwegs und muss Dokumente freigeben, hat aber nur ein Smartphone zur Verfügung. Die Standard-Workflow-Oberfläche beinhaltet Bearbeitungsaufgaben, die auf Mobilgeräten nur schwer oder gar nicht durchgeführt werden können.
Lösung: Durch Workflow-Cards können Freigaben direkt über die Objektkarte durchgeführt werden:
Die Verwendung von Cards für Workflow-Schritte ist geeignet für:
Die Verwendung von Cards für Workflow-Schritte ist nicht geeignet für:
Hinweis: In Cards werden keine Eingabefelder direkt eingebettet. Benutzereingaben erfolgen stattdessen über Message-Popups (Dialoge).
Die folgenden Schritte erläutern die Integration eines Workflows in eine Card anhand des Freigabeworkflows agorum.workflow.releaseXEyes.
Der Decorator entscheidet, wann die Workflow-Card angezeigt wird. Im Beispiel wird geprüft, ob das Objekt zu einem Token des Workflows agorum.workflow.releaseXEyes gehört und der aktuelle Schritt uiReleaseStep ist.
let decorators = require('/agorum/roi/customers/agorum.cards/js/decorators');
let metadata = require('common/metadata');
/**
* @type {decorators.Decorator}
*/
module.exports = (cardlet, object) =>
decorators.with(cardlet, 'content', content => {
let data = metadata().load(object, 'sys_acw_processName', 'sys_acw_stepName').data();
if (data.sys_acw_processName !== 'agorum.workflow.releaseXEyes') return;
if (data.sys_acw_stepName !== 'uiReleaseStep') return;
// Add the Release X-Eyes Cardlet to content
content.items = (content.items || []).concat([
{
type: 'agorum.workflow.releasexeyes.releaseXEyes',
id: object.UUID,
},
]);
});
Wichtige Metadaten:
Das Cardlet definiert die Darstellung und Interaktion. Es lädt die Token-Variablen per workflow.get(tokenId) und rendert diese als Display-Gruppe. Zusätzlich werden je nach Zustand Buttons angezeigt.
/* global sc */
let _ = require('common/i18n').translate;
let aguila = require('common/aguila');
let cards = require('/agorum/roi/customers/agorum.cards/js/cards');
let workflow = require('/agorum/roi/customers/agorum.dev/js/lib/workflow');
let message = require('/agorum/roi/customers/agorum.composite/js/lib/message');
// TODO: remove after updating to 11.13.0+
let ui;
try {
ui = require('/agorum/roi/customers/acworkflow/js/common/ui');
} catch (ignored) {
(() => {})(ignored);
}
if (!ui || !ui.hasAcquired) {
ui = {
hasAcquired: variables => variables.sys_acw_assignee === sc.loginUserUuid,
mayAcquire: () => false,
};
}
/**
* Helper function: Removes HTML tags from a string
*
* @param {string} html
*/
let stripHtml = html => (html ? html.replace(/<[^>]*>/g, '').trim() : '');
/**
* Helper function: Shows an input popup
*
* @param {string} title
* @param {(comment: string) => void} fn
*/
let showCommentPopup = (title, fn) =>
aguila.enter(() =>
message
.popup(title)
.text(_('agorum.workflow.releasexeyes.popup.comment.text'))
.element({
type: 'agorum.composite.form.element.text',
name: 'comment',
label: _('agorum.workflow.releasexeyes.popup.comment.label'),
textArea: true,
validation: [
{
required: true,
},
],
})
.focus('comment')
.ok()
.cancel()
.show((buttonName, value) => {
if (buttonName === 'ok') {
fn(value.comment);
}
})
);
/**
* Build function: Creates the cardlet structure
*
* @type {cards.Builder}
*/
exports.build = (cx, def) => {
cx.meta = {
id: def.id,
};
let variables = workflow.get(def.id);
// Create display items
let displayItems = [
{ label: _('agorum.workflow.releasexeyes.label.step'), value: variables.step || '' },
{ label: _('agorum.workflow.releasexeyes.label.stepText'), value: variables.stepText || '' },
{ label: _('agorum.workflow.releasexeyes.label.releaseText'), html: stripHtml(variables.releaseText || '') },
];
// Add escalation date only if available
if (variables.doneBy) {
displayItems.push({
label: _('agorum.workflow.releasexeyes.label.escalationDate'),
value: new Date(variables.doneBy),
precision: 'minute',
});
}
let buttons;
let acquired = ui.hasAcquired(variables);
if (acquired) {
// show actions if acquired by this user
buttons = [
{
type: 'agorum.button',
name: 'approve',
text: _('agorum.workflow.releasexeyes.button.approve'),
color: 'success',
},
{
type: 'agorum.button',
name: 'reject',
text: _('agorum.workflow.releasexeyes.button.reject'),
color: 'warning',
},
{
type: 'agorum.vertical',
flex: 1,
},
{
type: 'agorum.button',
name: 'backToCreator',
text: _('agorum.workflow.releasexeyes.button.backToCreator'),
color: 'medium',
},
];
} else if (ui.mayAcquire(variables)) {
// show acquisition buttons, if allowed
let force = acquired === false;
buttons = [
{
type: 'agorum.button',
name: force ? 'acquireForce' : 'acquire',
text: _('agorum.workflow.releasexeyes.button.acquire' + (force ? '.force' : '')),
},
];
} else {
buttons = [];
}
return {
type: 'agorum.vertical',
items: [
{
type: 'agorum.display.group',
labelWidth: 150,
items: displayItems,
},
{
type: 'agorum.horizontal',
name: 'actions',
items: buttons,
},
],
};
};
let event = (exports.event = cards.eventHandler());
// Event handlers for "Acquire" buttons
event.path('actions', 'acquire').on('elementClicked', cx => workflow.acquire(cx.meta.id));
event.path('actions', 'acquireForce').on('elementClicked', cx => workflow.acquire(cx.meta.id, true));
// Event handler for "Back to Creator" button
event.path('actions', 'backToCreator').on('elementClicked', cx =>
showCommentPopup(_('agorum.workflow.releasexeyes.popup.backToCreator.title'), comment =>
aguila
.fork(
workflow.leave(cx.meta.id, 'backToCreator', {
editorComment: comment,
})
)
.then(
message.alert(
_('agorum.workflow.releasexeyes.message.success.title'),
_('agorum.workflow.releasexeyes.message.backToCreator.success')
)
)
)
);
// Event handler for "Approve" button
event.path('actions', 'approve').on('elementClicked', cx =>
showCommentPopup(_('agorum.workflow.releasexeyes.popup.approve.title'), comment =>
aguila
.fork(() =>
workflow.leave(cx.meta.id, 'released', {
editorComment: comment,
})
)
.then(() =>
message.alert(
_('agorum.workflow.releasexeyes.message.success.title'),
_('agorum.workflow.releasexeyes.message.approve.success')
)
)
)
);
// Event handler for "Reject" button
event.path('actions', 'reject').on('elementClicked', cx =>
showCommentPopup(_('agorum.workflow.releasexeyes.popup.reject.title'), comment =>
aguila
.fork(() =>
workflow.leave(cx.meta.id, 'notReleased', {
editorComment: comment,
})
)
.then(() =>
message.alert(
_('agorum.workflow.releasexeyes.message.success.title'),
_('agorum.workflow.releasexeyes.message.reject.success')
)
)
)
);
Wichtige Konzepte:
Wie im Beispiel zu Schritt 2 gezeigt, wird ein Message-Popup für Benutzereingaben verwendet. Beim Erstellen empfiehlt es sich, die mögliche Verwendung auf mobilen Geräten zu berücksichtigen:
Event-Handler verarbeiten Button-Klicks und steuern den Workflow. Relevante Aufrufe:
workflow.leave(tokenId, 'released', { editorComment: comment }) → Freigebenworkflow.leave(tokenId, 'notReleased', { editorComment: comment }) → Ablehnenworkflow.leave(tokenId, 'backToCreator', { editorComment: comment }) → Zurück zum Erstellerworkflow.acquire(tokenId) / workflow.acquire(tokenId, true) → Zuweisung übernehmen (optional)Zusätzlich wird in UI-Kontexten häufig aguila.enter() verwendet, um UI-Operationen thread-sicher auszuführen. In der Referenzimplementierung ist aguila.enter() bereits im Popup-Helper gekapselt.