Durchsuchbare Dokumentation aufrufen | Zurück zur Dokumentationsübersicht

Navigation: Dokumentationen agorum core > agorum core für Entwickler > agorum core cards


Cards für Workflows (Beispiel Freigabeworkflows)

Diese Dokumentation beschreibt, wie Workflow-basierte Cards für die Verwendung auf Mobilgeräten optimiert werden können. Workflows, beispielsweise Freigabeworkflows, können durch Cards so aufbereitet werden, dass Benutzer direkt über die Objektkarte auf dem Smartphone interagieren können, ohne in die komplexe Workflow-Oberfläche wechseln zu müssen.

Mobile Workflows bzw. Workflow-Schritte

Typisches Szenario

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:

  1. Eingang (aktuell) öffnen
  2. Workflow-Aufgabe sehen
  3. Mit einem Klick freigeben, ablehnen oder zurücksenden
  4. Kommentar optional hinzufügen

Wann macht die Optimierung für die mobile Verwendung Sinn?

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: Cards haben keine Input-Elemente! Für Eingaben werden Message-Popups verwendet.

Workflow-Integration

Die folgenden Schritte erläutern die Integration eines Workflows in eine Card anhand des Freigabeworkflows.

  1. Decorator prüft Workflow und Schritt → entscheidet über Anzeige
  2. Build-Funktion lädt Workflow-Variablen → zeigt Informationen an
  3. Button-Klick → Kommentar-Popup öffnen
  4. Kommentar eingeben → Workflow fortsetzen (workflow.leave())

Schritt 1: Decorator erstellen

Der Decorator entscheidet, wann die Workflow-Card angezeigt wird.

/**
 * Release X-Eyes Decorator
 * Integrates the Release X-Eyes Cardlet in the content area
 */

let decorators = require('/agorum/roi/customers/agorum.cards/js/decorators');
let metadata = require('common/metadata');

/**
 * Decorator function
 * @param {Object} cardlet - The cardlet created by previous decorators
 * @param {Object} object - The object to be displayed
 * @param {Object} def - The cardlet definition with optional parameters
 */
module.exports = (cardlet, object, def) => {
  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;

  decorators.with(cardlet, 'content', content => {
    // Add the Release X-Eyes Cardlet to content
    content.items = (content.items || []).concat([
      {
        type: 'agorum.workflow.releasexeyes.releaseXEyes',
        id: object.UUID,
      },
    ]);
  });
};

 

Wichtige Metadaten:

Schritt 2: Cardlet erstellen

Das Cardlet definiert die Darstellung und Interaktion.

/**
 * Release X-Eyes Cardlet
 * Displays text fields with labels and three action buttons
 *
 * name: agorum.workflow.releasexeyes.releaseXEyes
 */

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');
let aguila = require('common/aguila');
let i18n = require('common/i18n');

/**
 * Helper function: Removes HTML tags from a string
 */
let stripHtml = html => {
  if (!html) {
    return '';
  }
  return html.replace(/<[^>]*>/g, '').trim();
};

/**
 * Build function: Creates the cardlet structure
 */
let build = (cx, def) => {
  // Store state information in meta object
  cx.meta = {
    tokenId: def.id,
    step: '',
    stepText: '',
    releaseText: '',
    doneBy: '',
  };

  // Load token variables if token ID is available
  if (def.id) {
    try {
      let tokenVariables = workflow.get(def.id);

      // Read and assign workflow variables
      cx.meta.step = tokenVariables.step || '';
      cx.meta.stepText = tokenVariables.stepText || '';
      cx.meta.releaseText = stripHtml(tokenVariables.releaseText || '');
      cx.meta.doneBy = tokenVariables.doneBy; // ? formatDate(tokenVariables.doneBy) : '';

      // Store variables in meta object for later use
      cx.meta.tokenVariables = tokenVariables;
    } catch (error) {
      // Error handling without console.log
    }
  }

  // Create display items
  let displayItems = [
    { label: i18n.translate('agorum.workflow.releasexeyes.label.step'), value: cx.meta.step },
    { label: i18n.translate('agorum.workflow.releasexeyes.label.stepText'), value: cx.meta.stepText },
    { label: i18n.translate('agorum.workflow.releasexeyes.label.releaseText'), html: cx.meta.releaseText },
  ];

  // Add escalation date only if available
  if (cx.meta.doneBy) {
    displayItems.push({
      label: i18n.translate('agorum.workflow.releasexeyes.label.escalationDate'),
      value: cx.meta.doneBy && new Date(cx.meta.doneBy),
      precision: 'minute',
    });
  }

  return {
    type: 'agorum.vertical',
    items: [
      {
        type: 'agorum.display.group',
        labelWidth: 150,
        items: displayItems,
      },
      {
        type: 'agorum.horizontal',
        name: 'actions',
        items: [
          {
            type: 'agorum.button',
            name: 'approve',
            text: i18n.translate('agorum.workflow.releasexeyes.button.approve'),
            color: 'success',
          },
          {
            type: 'agorum.button',
            name: 'reject',
            text: i18n.translate('agorum.workflow.releasexeyes.button.reject'),
            color: 'warning',
          },
          {
            type: 'agorum.vertical',
            flex: 1,
          },
          {
            type: 'agorum.button',
            name: 'backToCreator',
            text: i18n.translate('agorum.workflow.releasexeyes.button.backToCreator'),
            color: 'medium',
          },
        ],
      },
    ],
  };
};

/**
 * Helper function: Shows an input popup
 */
let showCommentPopup = title => {
  return new Promise((resolve, reject) => {
    message
      .popup(title)
      .text(i18n.translate('agorum.workflow.releasexeyes.popup.comment.text'))
      .element({
        type: 'agorum.composite.form.element.text',
        name: 'comment',
        label: i18n.translate('agorum.workflow.releasexeyes.popup.comment.label'),
        textArea: true,
        validation: [
          {
            required: true,
          },
        ],
      })
      .value({ comment: '' })
      .focus('comment')
      .ok()
      .cancel()
      .show((buttonName, value) => {
        if (buttonName === 'ok') {
          resolve(value.comment);
        } else {
          reject('Abgebrochen');
        }
      });
  });
};

/**
 * Event handler: Processes events from the cardlet, only one event show belown
 */
let event = cards.eventHandler();

// Event handler for "Approve" button
event.path('actions', 'approve').on('elementClicked', (cx, param) => {
  // Show input popup
  aguila.enter(() => {
    showCommentPopup(i18n.translate('agorum.workflow.releasexeyes.popup.approve.title'))
      .then(comment => {
        try {
          // Workflow leave with outlet "released" and editorComment variable
          workflow.leave(cx.meta.tokenId, 'released', {
            editorComment: comment,
          });

          // Feedback to user
          message.alert(
            i18n.translate('agorum.workflow.releasexeyes.message.success.title'),
            i18n.translate('agorum.workflow.releasexeyes.message.approve.success')
          );
        } catch (error) {
          message.alert(
            i18n.translate('agorum.workflow.releasexeyes.message.error.title'),
            i18n.translate('agorum.workflow.releasexeyes.message.error.text') + ' ' + error.message
          );
        }
      })
      .catch(error => {
        // Cancelled or error
      });
  });
});

module.exports = { build: build, event: event };

 

Wichtige Konzepte:

Schritt 3: Kommentar-Popup erstellen

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: 

Schritt 4: Event-Handler implementieren

Event-Handler verarbeiten Button-Klicks und steuern den Workflow, wie im Beispiel zu Schritt 2 gezeigt. 

 

Wichtige Konzepte: