Files
Dashboard/ui/components/NodeWorkflowEngine/NodeWorkflowEngine.tsx
T
dejanandClaude Opus 5 b8bf987d0e feat(dashboard): Analytics-Dashboard als Gestaltungsvorlage zur Übergabe
UI/UX-Prototyp der Analytics-Dashboards in HiveGrid, fachlich auf einen
SHK-/Handwerksbetrieb zugeschnitten. Ziel ist die Übergabe an die Entwicklung,
nicht der Produktivbetrieb.

Inhalt: sechs Seiten (Übersicht, Finanzen, Aufträge, Kunden, Team, Komponenten),
26 KPIs, neun Chart-Bausteine, Filtersystem mit seitenabhängiger Relevanz und ein
deterministischer Demo-Datensatz (420 Kunden, 1.900 Aufträge, Stichtag 30.07.2026).

Drei Quellen sind zusammengeführt: die fachliche Planung mit dem Branchenpartner
(Januar 2026), die Subframe-Komponentenbibliothek aus dem HiveGrid-Pitch und
IBM Carbon Data Visualization für die Diagrammregeln. Herkunft und bewusste
Abweichungen stehen in docs/HERKUNFT.md.

Dokumentation: README, HANDOVER (was echte Logik braucht), docs/DESIGN-SYSTEM,
KPI-KATALOG, DATENMODELL, NAVIGATION, HERKUNFT, ZIELSYSTEM (Einbauort in
HiveGrid Cloud) und LEITPLANKEN (welche Cloud-Screens als Vorbild taugen).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPeZNwSeU7qG3RRCwaxEZX
2026-08-29 17:25:20 +02:00

902 lines
32 KiB
TypeScript

// @subframe/sync-disable
// @ts-nocheck
"use client";
/*
* Documentation:
* Node Workflow Engine — https://app.subframe.com/f023064a5ca0/library?component=Node+Workflow+Engine_e75d1b96-38eb-4380-aef0-a320a2d99e5c
*/
import React from "react";
import { FeatherAlertCircle } from "@subframe/core";
import { FeatherArrowRightLeft } from "@subframe/core";
import { FeatherBellRing } from "@subframe/core";
import { FeatherCalendar } from "@subframe/core";
import { FeatherCalendarClock } from "@subframe/core";
import { FeatherCalendarPlus } from "@subframe/core";
import { FeatherCalendarX } from "@subframe/core";
import { FeatherCheck } from "@subframe/core";
import { FeatherClipboardCheck } from "@subframe/core";
import { FeatherClipboardList } from "@subframe/core";
import { FeatherClock } from "@subframe/core";
import { FeatherFilter } from "@subframe/core";
import { FeatherFolderPlus } from "@subframe/core";
import { FeatherGitBranch } from "@subframe/core";
import { FeatherGitMerge } from "@subframe/core";
import { FeatherGitPullRequest } from "@subframe/core";
import { FeatherGlobe } from "@subframe/core";
import { FeatherInbox } from "@subframe/core";
import { FeatherMail } from "@subframe/core";
import { FeatherMailX } from "@subframe/core";
import { FeatherPlus } from "@subframe/core";
import { FeatherRepeat } from "@subframe/core";
import { FeatherSignpost } from "@subframe/core";
import { FeatherSquareCheck } from "@subframe/core";
import { FeatherStickyNote } from "@subframe/core";
import { FeatherTimer } from "@subframe/core";
import { FeatherTrophy } from "@subframe/core";
import { FeatherUserCheck } from "@subframe/core";
import { FeatherUserCog } from "@subframe/core";
import { FeatherUserPen } from "@subframe/core";
import { FeatherUserPlus } from "@subframe/core";
import { FeatherVariable } from "@subframe/core";
import { FeatherWebhook } from "@subframe/core";
import { FeatherZap } from "@subframe/core";
import * as SubframeCore from "@subframe/core";
import * as SubframeUtils from "../../utils";
export interface KatalogProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
}
const Katalog = React.forwardRef<HTMLDivElement, KatalogProps>(function Katalog(
{ className, ...otherProps }: KatalogProps,
ref
) {
return (
<div
className={SubframeUtils.twClassNames(
"flex w-full flex-col items-start gap-12 px-8 py-8",
className
)}
ref={ref}
{...otherProps}
>
<div className="flex w-full flex-col items-start gap-6">
<span className="text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-tertiary">
Auslöser
</span>
<div className="w-full items-start grid grid-cols-6 gap-x-8 gap-y-10 mobile:grid mobile:gap-x-8 mobile:gap-y-10 mobile:grid-cols-3">
<NodeWorkflowEngine art="neuer-kontakt" />
<NodeWorkflowEngine art="kontakt-geaendert" />
<NodeWorkflowEngine art="neue-anfrage" />
<NodeWorkflowEngine art="lead-qualifiziert" />
<NodeWorkflowEngine art="projekt-gewonnen" />
<NodeWorkflowEngine art="status-gewechselt" />
<NodeWorkflowEngine art="projekt-angelegt" />
<NodeWorkflowEngine art="deadline-naht" />
<NodeWorkflowEngine art="email-eingegangen" />
<NodeWorkflowEngine art="email-unzustellbar" />
<NodeWorkflowEngine art="formular-abgeschickt" />
<NodeWorkflowEngine art="termin-gebucht" />
<NodeWorkflowEngine art="termin-storniert" />
<NodeWorkflowEngine art="ereignis-erstellt" />
<NodeWorkflowEngine art="zeitplan" />
<NodeWorkflowEngine art="webhook-empfangen" />
</div>
</div>
<div className="flex h-px w-full flex-none flex-col items-center gap-2 bg-border-default" />
<div className="flex w-full flex-col items-start gap-6">
<span className="text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-tertiary">
Aktionen: Daten &amp; CRM
</span>
<div className="w-full items-start grid grid-cols-6 gap-x-8 gap-y-10 mobile:grid mobile:gap-x-8 mobile:gap-y-10 mobile:grid-cols-3">
<NodeWorkflowEngine art="kontakt-aendern" />
<NodeWorkflowEngine art="notiz-anlegen" />
</div>
</div>
<div className="flex h-px w-full flex-none flex-col items-center gap-2 bg-border-default" />
<div className="flex w-full flex-col items-start gap-6">
<span className="text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-tertiary">
Aktionen: Kommunikation
</span>
<div className="w-full items-start grid grid-cols-6 gap-x-8 gap-y-10 mobile:grid mobile:gap-x-8 mobile:gap-y-10 mobile:grid-cols-3">
<NodeWorkflowEngine art="benachrichtigung-senden" />
</div>
</div>
<div className="flex h-px w-full flex-none flex-col items-center gap-2 bg-border-default" />
<div className="flex w-full flex-col items-start gap-6">
<span className="text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-tertiary">
Aktionen: Aufgaben
</span>
<div className="w-full items-start grid grid-cols-6 gap-x-8 gap-y-10 mobile:grid mobile:gap-x-8 mobile:gap-y-10 mobile:grid-cols-3">
<NodeWorkflowEngine art="aufgabe-anlegen" />
<NodeWorkflowEngine art="aufgabe-zuweisen" />
</div>
</div>
<div className="flex h-px w-full flex-none flex-col items-center gap-2 bg-border-default" />
<div className="flex w-full flex-col items-start gap-6">
<span className="text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-tertiary">
Logik
</span>
<div className="w-full items-start grid grid-cols-6 gap-x-8 gap-y-10 mobile:grid mobile:gap-x-8 mobile:gap-y-10 mobile:grid-cols-3">
<NodeWorkflowEngine art="wenn-dann" />
<NodeWorkflowEngine art="warten" />
<NodeWorkflowEngine art="schleife" />
<NodeWorkflowEngine art="oder" />
<NodeWorkflowEngine art="und" />
<NodeWorkflowEngine art="switch" />
<NodeWorkflowEngine art="wert-setzen" />
<NodeWorkflowEngine art="filter" />
</div>
</div>
<div className="flex h-px w-full flex-none flex-col items-center gap-2 bg-border-default" />
<div className="flex w-full flex-col items-start gap-6">
<span className="text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-tertiary">
Integration
</span>
<div className="w-full items-start grid grid-cols-6 gap-x-8 gap-y-10 mobile:grid mobile:gap-x-8 mobile:gap-y-10 mobile:grid-cols-3">
<NodeWorkflowEngine art="http-anfrage" />
<div className="flex flex-col items-center gap-1 self-stretch">
<NodeWorkflowEngine art="pds-synchronisieren" />
</div>
</div>
</div>
</div>
);
});
export interface NodeItemProps
extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
art?:
| "ausloeser"
| "aktion"
| "logik"
| "multi-logik-3"
| "multi-logik-4"
| "ereignis";
farbe?: "gruen" | "violett" | "blau" | "rot" | "amber" | "grau";
status?: "standard" | "laeuft" | "fehler" | "erfolgreich";
icon?: React.ReactNode;
title?: React.ReactNode;
beschreibung?: React.ReactNode;
ausgewaehlt?: boolean;
inaktiv?: boolean;
portLabel1?: React.ReactNode;
portLabel2?: React.ReactNode;
connectedOutputPorts?: number[];
onInputPortClick?: () => void;
onOutputPortClick?: (port: number) => void;
onAddAfter?: (port: number) => void;
className?: string;
}
const NodeItem = React.forwardRef<HTMLDivElement, NodeItemProps>(
function NodeItem(
{
art = "aktion",
farbe = "blau",
status = "standard",
icon = <FeatherZap />,
title,
beschreibung,
ausgewaehlt = false,
inaktiv = false,
portLabel1,
portLabel2,
connectedOutputPorts = [],
onInputPortClick,
onOutputPortClick,
onAddAfter,
className,
...otherProps
}: NodeItemProps,
ref
) {
return (
<div
className={SubframeUtils.twClassNames(
"group/3535de66 flex cursor-pointer flex-col items-center gap-1.5 group/node-root",
{ "opacity-50 pointer-events-none": inaktiv },
className
)}
ref={ref}
{...otherProps}
>
<div
className={SubframeUtils.twClassNames(
"hidden items-center justify-center -mb-4 z-10",
{ flex: art === "ereignis" }
)}
>
<NodeWorkflowEngine.Port
label="3"
richtung={art === "ereignis" ? "oben" : "unten"}
portType="input"
portIndex={1}
onPortClick={() => onInputPortClick?.()}
/>
</div>
<div className="flex items-center">
<div
className={SubframeUtils.twClassNames(
"flex items-start -mr-[11px] z-10",
{ hidden: art === "ereignis" || art === "ausloeser" }
)}
>
<NodeWorkflowEngine.Port
className="h-auto w-auto flex-none self-stretch"
label="3"
portType="input"
portIndex={1}
onPortClick={() => onInputPortClick?.()}
/>
</div>
<div
className={SubframeUtils.twClassNames(
"flex h-32 w-32 flex-none items-center justify-center rounded-lg border-[6px] border-solid border-border-default bg-surface-ui-subtle-blue-default relative group-hover/3535de66:shadow-lg",
{
"border-[6px] border-solid border-[#0791f066] group-hover/3535de66:shadow-none":
ausgewaehlt,
"border-2 border-solid border-border-negative-solid":
status === "fehler",
"bg-surface-neutral-secondary": farbe === "grau",
"bg-surface-ui-subtle-yellow-default": farbe === "amber",
"bg-surface-ui-subtle-red-default": farbe === "rot",
"bg-surface-ui-subtle-purple-default": farbe === "violett",
"bg-surface-ui-subtle-green-default": farbe === "gruen",
"rounded-round": art === "ereignis",
"rounded-s-round": art === "ausloeser",
}
)}
>
{icon ? (
<SubframeCore.IconWrapper
className={SubframeUtils.twClassNames(
"text-title-page font-title-page text-icon-ui-blue",
{
"text-icon-neutral-tertiary": farbe === "grau",
"text-icon-ui-yellow": farbe === "amber",
"text-icon-ui-red": farbe === "rot",
"text-icon-ui-purple": farbe === "violett",
"text-icon-feedback-positive": farbe === "gruen",
}
)}
>
{icon}
</SubframeCore.IconWrapper>
) : null}
<div
className={SubframeUtils.twClassNames(
"hidden items-start absolute -top-2 -right-2",
{
flex:
status === "erfolgreich" ||
status === "fehler" ||
status === "laeuft",
}
)}
>
<NodeWorkflowEngine.StatusBadge
status={
status === "fehler"
? "fehler"
: status === "laeuft"
? "laeuft"
: "erfolgreich"
}
/>
</div>
</div>
<div
className={SubframeUtils.twClassNames(
"flex flex-col items-start gap-5 -ml-[11px] z-10",
{
hidden: art === "ereignis",
"gap-1": art === "multi-logik-4",
"gap-2.5": art === "multi-logik-3",
}
)}
>
<NodeWorkflowEngine.Port
label={portLabel1}
richtung="rechts"
portType="output"
portIndex={1}
connected={connectedOutputPorts.includes(1)}
onPortClick={() => onOutputPortClick?.(1)}
onAddClick={() => onAddAfter?.(1)}
/>
<div
className={SubframeUtils.twClassNames("hidden items-start", {
flex:
art === "multi-logik-4" ||
art === "multi-logik-3" ||
art === "logik",
})}
>
<NodeWorkflowEngine.Port
className="h-auto w-auto flex-none self-stretch"
label={portLabel2}
richtung="rechts"
portType="output"
portIndex={2}
connected={connectedOutputPorts.includes(2)}
onPortClick={() => onOutputPortClick?.(2)}
onAddClick={() => onAddAfter?.(2)}
/>
</div>
<div
className={SubframeUtils.twClassNames("hidden items-start", {
flex: art === "multi-logik-4" || art === "multi-logik-3",
})}
>
<NodeWorkflowEngine.Port
className="h-auto w-auto flex-none self-stretch"
label="3"
richtung="rechts"
portType="output"
portIndex={3}
connected={connectedOutputPorts.includes(3)}
onPortClick={() => onOutputPortClick?.(3)}
onAddClick={() => onAddAfter?.(3)}
/>
</div>
<div
className={SubframeUtils.twClassNames("hidden items-start", {
flex: art === "multi-logik-4",
})}
>
<NodeWorkflowEngine.Port
className="h-auto w-auto flex-none self-stretch"
label="4"
richtung="rechts"
portType="output"
portIndex={4}
connected={connectedOutputPorts.includes(4)}
onPortClick={() => onOutputPortClick?.(4)}
onAddClick={() => onAddAfter?.(4)}
/>
</div>
</div>
</div>
<div className="flex flex-col items-center gap-0.5">
<span className="text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-primary text-center">
{title}
</span>
<span className="text-body-extra-small-regular-150 font-body-extra-small-regular-150 text-text-neutral-tertiary text-center">
{beschreibung}
</span>
</div>
</div>
);
}
);
export interface PortProps extends React.HTMLAttributes<HTMLDivElement> {
label?: React.ReactNode;
richtung?: "keine" | "rechts" | "oben" | "unten";
portType?: "input" | "output";
portIndex?: number;
connected?: boolean;
onPortClick?: React.MouseEventHandler<HTMLDivElement>;
onAddClick?: React.MouseEventHandler<HTMLDivElement>;
className?: string;
}
const Port = React.forwardRef<HTMLDivElement, PortProps>(function Port(
{
label,
richtung = "keine",
portType,
portIndex,
connected = false,
onPortClick,
onAddClick,
className,
...otherProps
}: PortProps,
ref
) {
const handlePortClick: React.MouseEventHandler<HTMLDivElement> = (event) => {
event.stopPropagation();
onPortClick?.(event);
};
const handleAddClick: React.MouseEventHandler<HTMLDivElement> = (event) => {
event.stopPropagation();
onAddClick?.(event);
};
return (
<div
className={SubframeUtils.twClassNames(
"group/21cef75b flex cursor-pointer items-center",
{
"flex-col": richtung === "unten" || richtung === "oben",
"h-8": richtung === "rechts",
},
className
)}
ref={ref}
data-workflow-port
{...otherProps}
>
<div
className={SubframeUtils.twClassNames("hidden flex-col items-center", {
flex: richtung === "oben" && !connected,
})}
>
<div
className="flex h-8 w-8 flex-none items-center justify-center rounded-sm border border-solid border-border-strong bg-surface-neutral-secondary cursor-pointer hover:bg-surface-neutral-tertiary"
data-workflow-port-add
onClick={handleAddClick}
>
<FeatherPlus className="text-body-extra-small-semibold-0 font-body-extra-small-semibold-0 text-icon-neutral-secondary" />
</div>
<div className="flex h-12 w-6 flex-none items-start justify-center">
<div className="flex w-px flex-none items-start self-stretch bg-border-strong" />
</div>
</div>
<div
className="flex h-4 w-4 flex-none items-start rounded-round border-2 border-solid border-border-strong bg-surface-neutral-secondary z-10 group-hover/21cef75b:border-2 group-hover/21cef75b:border-solid group-hover/21cef75b:border-border-information-solid group-hover/21cef75b:shadow-focus"
data-workflow-port-dot
data-workflow-port-type={portType}
data-workflow-port-index={portIndex}
onClick={handlePortClick}
/>
<div
className={SubframeUtils.twClassNames("hidden items-center", {
flex: richtung === "rechts" && !connected,
})}
>
<div className="flex h-px w-16 flex-none items-start bg-border-strong -ml-2 -mt-0.5" />
<div
className="flex h-8 w-8 flex-none items-center justify-center rounded-sm border border-solid border-border-strong bg-surface-neutral-secondary cursor-pointer hover:bg-surface-neutral-tertiary"
data-workflow-port-add
onClick={handleAddClick}
>
<FeatherPlus className="text-body-extra-small-semibold-0 font-body-extra-small-semibold-0 text-icon-neutral-secondary" />
</div>
</div>
<div
className={SubframeUtils.twClassNames("hidden flex-col items-center", {
flex: richtung === "unten" && !connected,
})}
>
<div className="flex h-12 w-4 flex-none items-start justify-center -mt-2">
<div className="flex w-px flex-none items-start self-stretch bg-border-strong" />
</div>
<div
className="flex h-8 w-8 flex-none items-center justify-center rounded-sm border border-solid border-border-strong bg-surface-neutral-secondary cursor-pointer hover:bg-surface-neutral-tertiary"
data-workflow-port-add
onClick={handleAddClick}
>
<FeatherPlus className="text-body-extra-small-semibold-0 font-body-extra-small-semibold-0 text-icon-neutral-secondary" />
</div>
</div>
</div>
);
});
export interface StatusBadgeProps extends React.HTMLAttributes<HTMLDivElement> {
status?: "fehler" | "laeuft" | "erfolgreich";
className?: string;
}
const StatusBadge = React.forwardRef<HTMLDivElement, StatusBadgeProps>(
function StatusBadge(
{ status = "erfolgreich", className, ...otherProps }: StatusBadgeProps,
ref
) {
return (
<div
className={SubframeUtils.twClassNames(
"group/e60ff70b flex h-5 w-5 items-center justify-center rounded-sm bg-surface-feedback-solid-positive-default",
{
"border border-solid border-border-default bg-surface-neutral-secondary":
status === "laeuft",
"bg-surface-feedback-solid-negative-default": status === "fehler",
},
className
)}
ref={ref}
{...otherProps}
>
{status === "fehler" ? (
<FeatherAlertCircle
className={SubframeUtils.twClassNames(
"text-body-extra-small-semibold-0 font-body-extra-small-semibold-0 text-icon-neutral-on-solid",
{ "hidden text-icon-neutral-primary": status === "laeuft" }
)}
/>
) : (
<FeatherCheck
className={SubframeUtils.twClassNames(
"text-body-extra-small-semibold-0 font-body-extra-small-semibold-0 text-icon-neutral-on-solid",
{ "hidden text-icon-neutral-primary": status === "laeuft" }
)}
/>
)}
<SubframeCore.Loader
className={SubframeUtils.twClassNames(
"hidden text-body-extra-small-regular-0 font-body-extra-small-regular-0 text-icon-neutral-tertiary",
{ "inline-flex": status === "laeuft" }
)}
/>
</div>
);
}
);
export interface NodeWorkflowEngineRootProps
extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
art?:
| "neuer-kontakt"
| "kontakt-geaendert"
| "neue-anfrage"
| "lead-qualifiziert"
| "projekt-gewonnen"
| "status-gewechselt"
| "projekt-angelegt"
| "deadline-naht"
| "email-eingegangen"
| "email-unzustellbar"
| "formular-abgeschickt"
| "termin-gebucht"
| "termin-storniert"
| "ereignis-erstellt"
| "zeitplan"
| "webhook-empfangen"
| "kontakt-aendern"
| "notiz-anlegen"
| "benachrichtigung-senden"
| "aufgabe-anlegen"
| "aufgabe-zuweisen"
| "wenn-dann"
| "warten"
| "schleife"
| "oder"
| "und"
| "switch"
| "wert-setzen"
| "filter"
| "http-anfrage"
| "pds-synchronisieren";
onInputPortClick?: () => void;
onOutputPortClick?: (port: number) => void;
onAddAfter?: (port: number) => void;
connectedOutputPorts?: number[];
title?: React.ReactNode;
beschreibung?: React.ReactNode;
className?: string;
}
const NodeWorkflowEngineRoot = React.forwardRef<
HTMLDivElement,
NodeWorkflowEngineRootProps
>(function NodeWorkflowEngineRoot(
{
art = "neuer-kontakt",
onInputPortClick,
onOutputPortClick,
onAddAfter,
connectedOutputPorts = [],
title,
beschreibung,
className,
...otherProps
}: NodeWorkflowEngineRootProps,
ref
) {
return (
<div
className={SubframeUtils.twClassNames(
"group/e75d1b96 flex flex-col items-center",
className
)}
ref={ref}
{...otherProps}
>
<NodeItem
art={
art === "pds-synchronisieren"
? "aktion"
: art === "http-anfrage"
? "aktion"
: art === "filter"
? "aktion"
: art === "wert-setzen"
? "aktion"
: art === "switch"
? "multi-logik-3"
: art === "und"
? "logik"
: art === "oder"
? "logik"
: art === "schleife"
? "logik"
: art === "warten"
? "aktion"
: art === "wenn-dann"
? "logik"
: art === "aufgabe-zuweisen"
? "aktion"
: art === "aufgabe-anlegen"
? "aktion"
: art === "benachrichtigung-senden"
? "aktion"
: art === "notiz-anlegen"
? "aktion"
: art === "kontakt-aendern"
? "aktion"
: "ausloeser"
}
farbe={
art === "pds-synchronisieren"
? "rot"
: art === "http-anfrage"
? "grau"
: art === "filter"
? "violett"
: art === "wert-setzen"
? "violett"
: art === "switch"
? "violett"
: art === "und"
? "violett"
: art === "oder"
? "violett"
: art === "schleife"
? "violett"
: art === "warten"
? "violett"
: art === "wenn-dann"
? "violett"
: art === "aufgabe-zuweisen"
? "amber"
: art === "aufgabe-anlegen"
? "amber"
: art === "benachrichtigung-senden"
? "blau"
: art === "notiz-anlegen"
? "rot"
: art === "kontakt-aendern"
? "rot"
: "gruen"
}
icon={
art === "pds-synchronisieren" ? (
<img
src="/logos/pds.svg"
alt="PDS"
className="h-9 w-[74px] object-contain"
/>
) : art === "http-anfrage" ? (
<FeatherGlobe />
) : art === "filter" ? (
<FeatherFilter />
) : art === "wert-setzen" ? (
<FeatherVariable />
) : art === "switch" ? (
<FeatherSignpost />
) : art === "und" ? (
<FeatherGitPullRequest />
) : art === "oder" ? (
<FeatherGitMerge />
) : art === "schleife" ? (
<FeatherRepeat />
) : art === "warten" ? (
<FeatherTimer />
) : art === "wenn-dann" ? (
<FeatherGitBranch />
) : art === "aufgabe-zuweisen" ? (
<FeatherClipboardList />
) : art === "aufgabe-anlegen" ? (
<FeatherSquareCheck />
) : art === "benachrichtigung-senden" ? (
<FeatherBellRing />
) : art === "notiz-anlegen" ? (
<FeatherStickyNote />
) : art === "kontakt-aendern" ? (
<FeatherUserPen />
) : art === "webhook-empfangen" ? (
<FeatherWebhook />
) : art === "zeitplan" ? (
<FeatherClock />
) : art === "ereignis-erstellt" ? (
<FeatherCalendar />
) : art === "termin-storniert" ? (
<FeatherCalendarX />
) : art === "termin-gebucht" ? (
<FeatherCalendarPlus />
) : art === "formular-abgeschickt" ? (
<FeatherClipboardCheck />
) : art === "email-unzustellbar" ? (
<FeatherMailX />
) : art === "email-eingegangen" ? (
<FeatherMail />
) : art === "deadline-naht" ? (
<FeatherCalendarClock />
) : art === "projekt-angelegt" ? (
<FeatherFolderPlus />
) : art === "status-gewechselt" ? (
<FeatherArrowRightLeft />
) : art === "projekt-gewonnen" ? (
<FeatherTrophy />
) : art === "lead-qualifiziert" ? (
<FeatherUserCheck />
) : art === "neue-anfrage" ? (
<FeatherInbox />
) : art === "kontakt-geaendert" ? (
<FeatherUserCog />
) : (
<FeatherUserPlus />
)
}
title={
title ??
(art === "pds-synchronisieren"
? "PDS synchronisieren"
: art === "http-anfrage"
? "HTTP-Anfrage"
: art === "filter"
? "Filter"
: art === "wert-setzen"
? "Wert setzen"
: art === "switch"
? "Switch"
: art === "und"
? "Und"
: art === "oder"
? "Oder"
: art === "schleife"
? "Schleife"
: art === "warten"
? "Warten"
: art === "wenn-dann"
? "Wenn-Dann"
: art === "aufgabe-zuweisen"
? "Aufgabe zuweisen"
: art === "aufgabe-anlegen"
? "Aufgabe anlegen"
: art === "benachrichtigung-senden"
? "Benachrichtigung senden"
: art === "notiz-anlegen"
? "Notiz anlegen"
: art === "kontakt-aendern"
? "Kontakt ändern"
: art === "webhook-empfangen"
? "Webhook empfangen"
: art === "zeitplan"
? "Zeitplan"
: art === "ereignis-erstellt"
? "Ereignis erstellt"
: art === "termin-storniert"
? "Termin storniert"
: art === "termin-gebucht"
? "Termin gebucht"
: art === "formular-abgeschickt"
? "Formular abgeschickt"
: art === "email-unzustellbar"
? "E-Mail unzustellbar"
: art === "email-eingegangen"
? "E-Mail eingegangen"
: art === "deadline-naht"
? "Deadline naht"
: art === "projekt-angelegt"
? "Projekt angelegt"
: art === "status-gewechselt"
? "Status gewechselt"
: art === "projekt-gewonnen"
? "Projekt gewonnen"
: art === "lead-qualifiziert"
? "Lead qualifiziert"
: art === "neue-anfrage"
? "Neue Anfrage"
: art === "kontakt-geaendert"
? "Kontakt geändert"
: "Neuer Kontakt")
}
beschreibung={
beschreibung ??
(art === "pds-synchronisieren"
? "Daten mit PDS austauschen"
: art === "http-anfrage"
? "Externen Dienst aufrufen"
: art === "filter"
? "Datensätze filtern"
: art === "wert-setzen"
? "Variable setzen oder lesen"
: art === "switch"
? "Mehrere Pfade verzweigen"
: art === "und"
? "Wenn alle Bedingungen zutreffen"
: art === "oder"
? "Wenn mind. eine Bedingung zutrifft"
: art === "schleife"
? "Für jedes Element wiederholen"
: art === "warten"
? "Pausiert, z. B. 3 Tage"
: art === "wenn-dann"
? "Verzweigt nach Bedingung"
: art === "aufgabe-zuweisen"
? "Person oder Team zuordnen"
: art === "aufgabe-anlegen"
? "Neue Aufgabe erstellen"
: art === "benachrichtigung-senden"
? "E-Mail, Push oder intern"
: art === "notiz-anlegen"
? "Notiz am Datensatz speichern"
: art === "kontakt-aendern"
? "Datensatz aktualisieren"
: art === "webhook-empfangen"
? "Wenn ein externes System ruft"
: art === "zeitplan"
? "Zu festen Zeiten, z. B. montags 8 Uhr"
: art === "ereignis-erstellt"
? "Wenn ein Kalender-Eintrag entsteht"
: art === "termin-storniert"
? "Wenn ein Termin abgesagt wird"
: art === "termin-gebucht"
? "Wenn ein Termin gebucht wird"
: art === "formular-abgeschickt"
? "Wenn ein Formular eingeht"
: art === "email-unzustellbar"
? "Wenn eine Kampagnen-Mail bounct"
: art === "email-eingegangen"
? "Wenn eine E-Mail ankommt"
: art === "deadline-naht"
? "Wenn eine Frist bald abläuft"
: art === "projekt-angelegt"
? "Wenn ein Projekt erstellt wird"
: art === "status-gewechselt"
? "Wenn eine Karte die Spalte wechselt"
: art === "projekt-gewonnen"
? "Wenn ein Projekt gewonnen wird"
: art === "lead-qualifiziert"
? "Wenn ein Lead qualifiziert wird"
: art === "neue-anfrage"
? "Wenn eine Anfrage eingeht"
: art === "kontakt-geaendert"
? "Wenn sich Kontaktdaten ändern"
: "Wenn ein Kontakt angelegt wird")
}
inaktiv={false}
portLabel1={
art === "und"
? "Ja"
: art === "oder"
? "Ja"
: art === "schleife"
? "Weiter"
: art === "wenn-dann"
? "Ja"
: "1"
}
portLabel2={
art === "und"
? "Nein"
: art === "oder"
? "Nein"
: art === "schleife"
? "Fertig"
: art === "wenn-dann"
? "Nein"
: "2"
}
onInputPortClick={onInputPortClick}
onOutputPortClick={onOutputPortClick}
onAddAfter={onAddAfter}
connectedOutputPorts={connectedOutputPorts}
/>
</div>
);
});
export const NodeWorkflowEngine = Object.assign(NodeWorkflowEngineRoot, {
Katalog,
NodeItem,
Port,
StatusBadge,
});