Track HiveGrid Cloud planning reference

This commit is contained in:
david committed 2026-06-26 20:48:01 +02:00
1 parent b3d5ea6764
commit e341c5938a
5784 files changed
+1104929 -3

No files matched your search

@@ -0,0 +1,99 @@
import { Position, Handle, type NodeProps } from "@xyflow/react";
import { t as i18nT } from "i18next";
import { WorkflowBlockDto } from "../../dtos/WorkflowBlockDto";
import { getWorkflowBlockCategoryLabel, getWorkflowBlockName, WorkflowBlockTypes } from "../../dtos/WorkflowBlockTypes";
import WorkflowContext from "../../context/WorkflowContext";
import { useContext, useMemo } from "react";
import WorkflowBlockUtils from "../../helpers/WorkflowBlockUtils";
import { WorkflowDto } from "../../dtos/WorkflowDto";
import clsx from "clsx";
import { useParams } from "react-router";
export default function BlockEditorNode({ id, data }: NodeProps) {
const params = useParams();
const { onNodeDelete, isNodeSelected, incomingBlockIds } = useContext(WorkflowContext);
const block = data.block as WorkflowBlockDto;
const workflow = data.workflow as WorkflowDto;
const workflowBlock = WorkflowBlockTypes.find((x) => x.value === block.type);
const errors = useMemo(
() => (workflow && block ? WorkflowBlockUtils.getBlockErrors({ workflow, block, validationContext: { incomingBlockIds } }) : []),
[block, incomingBlockIds, workflow]
);
if (!workflow) {
return <div>{i18nT("workflows.blocks.node.unknownWorkflow")}</div>;
}
if (!workflowBlock) {
return <div>{i18nT("workflows.blocks.node.unknownBlockType", { value: block.type })}</div>;
}
const workflowBlockName = getWorkflowBlockName(workflowBlock, i18nT);
const workflowBlockCategory = getWorkflowBlockCategoryLabel(workflowBlock.category, i18nT);
return (
<button
type="button"
data-ui-id="ui.workflows.builder.node"
data-workflow-node-id={block.id}
data-workflow-node-kind="block"
className={clsx(
"relative flex h-[80px] w-[250px] flex-col justify-center rounded-md border p-2 text-left focus:ring-1 focus:ring-blue-600 focus:ring-offset-2",
isNodeSelected(id) ? "bg-blue-50 ring-1 ring-blue-600 ring-offset-2" : "bg-background",
errors.length ? "border-red-300" : "border-border"
)}
onKeyDown={(e) => {
if (e.key === "Delete" || e.key === "Backspace") {
e.preventDefault();
e.stopPropagation();
onNodeDelete(block.id);
}
}}
>
<div className="w-full space-y-2">
<div className="flex justify-between space-x-2">
<div className="flex items-center space-x-2 truncate">
<workflowBlock.icon className="text-muted-foreground h-4 w-4 shrink-0" />
<div className="text-foreground/80 truncate text-sm font-medium">{workflowBlockName}</div>
</div>
<div>
<div
className={clsx(
"rounded-md border px-2 py-1 text-xs",
workflowBlock.type === "trigger" ? "border-yellow-200 bg-yellow-50 text-yellow-800" : "border-border text-muted-foreground bg-secondary"
)}
>
{workflowBlock.type === "trigger" ? <span>{i18nT("workflows.blocks.types.trigger")}</span> : <span>{workflowBlockCategory}</span>}
</div>
</div>
</div>
<div className="border-border border-t" />
{errors.length > 0 ? (
<div>
<div className="line-clamp-1 text-xs text-red-500">{errors.join(", ")}</div>
</div>
) : (
<div className=" text-muted-foreground line-clamp-1 text-xs">
{workflowBlock.appliesToAllTenants && !params.tenant && <span className="text-xs text-blue-500">({i18nT("workflows.blocks.node.allAccounts")}) </span>}
{block.description || block.input.event || i18nT("workflows.blocks.node.noDescription")}
</div>
)}
{workflowBlock.type === "action" && (
<Handle type="target" position={Position.Top} id="in" style={{ opacity: 0, width: "100%", height: "100%", top: 0 }} />
)}
<Handle
type="source"
position={Position.Bottom}
id="out"
style={{
bottom: "-7px",
left: "calc(50%)",
width: "15px",
height: "15px",
background: "#fff",
border: "1px solid #2563eb",
}}
/>
</div>
</button>
);
}
@@ -0,0 +1,92 @@
import { Position, Handle, type NodeProps } from "@xyflow/react";
import { t as i18nT } from "i18next";
import { WorkflowBlockDto } from "../../dtos/WorkflowBlockDto";
import { getWorkflowBlockCategoryLabel, getWorkflowBlockName, WorkflowBlockTypes } from "../../dtos/WorkflowBlockTypes";
import WorkflowContext from "../../context/WorkflowContext";
import { useContext, useMemo } from "react";
import WorkflowBlockUtils from "../../helpers/WorkflowBlockUtils";
import { WorkflowDto } from "../../dtos/WorkflowDto";
import clsx from "clsx";
import { WorkflowBlockExecutionDto } from "../../dtos/WorkflowBlockExecutionDto";
export default function BlockExecutionNode({ id, data }: NodeProps) {
const { isNodeSelected, incomingBlockIds } = useContext(WorkflowContext);
const block = data.block as WorkflowBlockDto;
const workflow = data.workflow as WorkflowDto;
const workflowBlockExecution = data.workflowBlockExecution as WorkflowBlockExecutionDto | null;
const workflowBlock = WorkflowBlockTypes.find((x) => x.value === block.type);
const errors = useMemo(
() => (workflow && block ? WorkflowBlockUtils.getBlockErrors({ workflow, block, validationContext: { incomingBlockIds } }) : []),
[block, incomingBlockIds, workflow]
);
if (!workflow) {
return <div>{i18nT("workflows.blocks.node.unknownWorkflow")}</div>;
}
if (!workflowBlock) {
return <div>{i18nT("workflows.blocks.node.unknownBlockType", { value: block.type })}</div>;
}
const workflowBlockName = getWorkflowBlockName(workflowBlock, i18nT);
const workflowBlockCategory = getWorkflowBlockCategoryLabel(workflowBlock.category, i18nT);
return (
<button
type="button"
data-ui-id="ui.workflows.builder.node"
data-workflow-node-id={block.id}
className={clsx(
"relative flex h-[80px] w-[250px] flex-col justify-center rounded-md border p-2 text-left focus:ring-1 focus:ring-offset-2",
isNodeSelected(id) ? "ring-1 ring-offset-2" : "",
!workflowBlockExecution && "bg-background border-border focus:ring-blue-600",
workflowBlockExecution?.status === "success" && "border-teal-300 bg-teal-50 ring-teal-600 focus:ring-teal-600",
workflowBlockExecution?.status === "error" && "border-red-300 bg-red-50 ring-red-600 focus:ring-red-600"
)}
>
<div className="w-full space-y-2">
<div className="flex justify-between space-x-2">
<div className="flex items-center space-x-2 truncate">
<workflowBlock.icon className="text-muted-foreground h-4 w-4 shrink-0" />
<div className="text-foreground/80 truncate text-sm font-medium">{workflowBlockName}</div>
</div>
<div>
<div
className={clsx(
"rounded-md border px-2 py-1 text-xs",
workflowBlock.type === "trigger" ? "border-yellow-200 bg-yellow-50 text-yellow-800" : "border-border text-muted-foreground bg-secondary"
)}
>
{workflowBlock.type === "trigger" ? <span>{i18nT("workflows.blocks.types.trigger")}</span> : <span>{workflowBlockCategory}</span>}
</div>
</div>
</div>
<div className="border-border border-t" />
{errors.length > 0 ? (
<div>
<div className="line-clamp-1 text-xs text-red-500">{errors.join(", ")}</div>
</div>
) : (
<div className=" text-muted-foreground line-clamp-1 text-xs">{block.description || block.input.event || i18nT("workflows.blocks.node.noDescription")}</div>
)}
{workflowBlock.type === "action" && (
<Handle type="target" position={Position.Top} id="in" style={{ opacity: 0, width: "100%", height: "100%", top: 0 }} isConnectable={false} />
)}
<Handle
type="source"
position={Position.Bottom}
isConnectable={false}
id="out"
style={{
bottom: "-7px",
left: "calc(50%)",
width: "15px",
height: "15px",
background: "#fff",
border: "1px solid #2563eb",
}}
/>
</div>
</button>
);
}
@@ -0,0 +1,173 @@
import { useEffect, useState } from "react";
import { t as i18nT } from "i18next";
import ButtonPrimary from "~/components/ui/buttons/ButtonPrimary";
import ButtonSecondary from "~/components/ui/buttons/ButtonSecondary";
import { WorkflowConditionOperators, WorkflowConditionsDto, WorkflowConditionsGroupDto } from "~/modules/workflowEngine/dtos/WorkflowConditionDtos";
import WorkflowConditionUtils from "~/modules/workflowEngine/helpers/WorkflowConditionUtils";
import WorkflowVariableTextInput from "../../variables/WorkflowVariableTextInput";
import { WorkflowDto } from "~/modules/workflowEngine/dtos/WorkflowDto";
import { WorkflowBlockDto } from "~/modules/workflowEngine/dtos/WorkflowBlockDto";
import { TrashIcon } from "~/components/ui/icons/vendor";
function ConditionsGroupInput({
workflow,
block,
type,
conditionsGroup,
onSave,
onCancel,
}: {
workflow: WorkflowDto;
block: WorkflowBlockDto;
type: "if" | "switch";
conditionsGroup: WorkflowConditionsGroupDto;
onSave: (conditionsGroup: WorkflowConditionsGroupDto) => void;
onCancel: () => void;
}) {
const [conditions, setConditions] = useState<WorkflowConditionsDto[]>(
conditionsGroup.conditions.length > 0
? conditionsGroup.conditions
: [
{
index: 0,
variable: "",
operator: "isNotEmpty",
value: "",
},
]
);
const [errors, setErrors] = useState<{ condition: WorkflowConditionsDto; errors: string[] }[]>([]);
const [conditionGroupType, setConditionGroupType] = useState<"AND" | "OR">(conditionsGroup.type);
useEffect(() => {
setErrors(WorkflowConditionUtils.getConditionsErrors(conditions));
}, [conditions]);
function updateConditionInGroup(idx: number, updatedCondition: WorkflowConditionsDto) {
const operator = WorkflowConditionUtils.getOperator(updatedCondition.operator);
if (!operator?.requiresValue) {
updatedCondition.value = "";
}
setConditions(conditions.map((condition, i) => (i === idx ? updatedCondition : condition)));
}
function removeConditionFromGroup(idx: number) {
setConditions(conditions.filter((_condition, i) => i !== idx));
}
function addConditionToGroup() {
const newCondition: WorkflowConditionsDto = {
index: conditions.length,
variable: "",
operator: "isNotEmpty",
value: "",
};
setConditions([...conditions, newCondition]);
}
return (
<div>
{conditions.length === 0 ? (
<div className=" border-border bg-secondary/90 flex flex-col items-center justify-center rounded-md border p-2 text-center">
<div className="text-muted-foreground text-sm font-medium">{i18nT("workflows.conditions.empty")}</div>
</div>
) : (
<div className="space-y-1">
<div className="flex h-10 items-center justify-between space-x-2">
{/* <div className="font-medium">{conditionsGroup.name}</div> */}
<div className="font-medium">{i18nT("workflows.conditions.count", { count: conditions.length })}</div>
{conditions.length > 1 && (
<select
aria-label={i18nT("workflows.conditions.logic.label")}
value={conditionGroupType}
onChange={(e) => setConditionGroupType(e.target.value as "AND" | "OR")}
className="border-border bg-secondary hover:bg-secondary/90 col-span-2 rounded-lg border px-2 py-2 pr-10 text-sm focus:outline-hidden"
>
<option value="AND">{i18nT("workflows.conditions.logic.and")}</option>
<option value="OR">{i18nT("workflows.conditions.logic.or")}</option>
</select>
)}
</div>
<div className="space-y-1 pb-2">
{conditions.map((condition, idx) => (
<div key={idx} className="group relative">
<button
type="button"
aria-label={i18nT("workflows.conditions.deleteCondition")}
onClick={() => removeConditionFromGroup(idx)}
className="text-muted-foreground absolute right-2 top-2.5 hidden rounded-md hover:text-red-500 focus:outline-hidden focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 group-focus-within:block group-hover:block"
>
<TrashIcon className="h-4 w-4" />
</button>
<div className="grid grid-cols-1 gap-1 sm:grid-cols-10">
<WorkflowVariableTextInput
workflow={workflow}
block={block}
className="sm:col-span-4"
name="variable"
placeholder={i18nT("workflows.conditions.variable")}
value={condition.variable}
onChange={(e) => updateConditionInGroup(idx, { ...condition, variable: e })}
/>
<select
aria-label={i18nT("workflows.conditions.operator")}
className="border-border bg-secondary hover:bg-secondary/90 w-full rounded-lg border px-2 py-2 text-sm focus:outline-hidden sm:col-span-2"
value={condition.operator}
onChange={(e) => updateConditionInGroup(idx, { ...condition, operator: e.target.value as any })}
>
{WorkflowConditionOperators.map((operator) => {
return (
<option key={operator.value} value={operator.value}>
{i18nT(operator.translationKey)}
</option>
);
})}
</select>
{/* <input
className="col-span-4 bg-secondary border border-border hover:bg-secondary/90 rounded-lg w-full px-2 py-2 text-sm focus:outline-hidden"
type="text"
placeholder={i18nT("workflows.conditions.value")}
value={condition.value}
onChange={(e) => updateConditionInGroup(idx, { ...condition, value: e.target.value })}
/> */}
{WorkflowConditionUtils.getOperator(condition.operator)?.requiresValue && (
<WorkflowVariableTextInput
workflow={workflow}
block={block}
className="sm:col-span-4"
name="value"
placeholder={i18nT("workflows.conditions.value")}
value={condition.value}
onChange={(e) => updateConditionInGroup(idx, { ...condition, value: e })}
/>
)}
</div>
</div>
))}
</div>
</div>
)}
<div className="border-border border-t">
<div className="flex justify-between space-x-2 pt-2">
<div>
{/* <ButtonSecondary onClick={onCancel} className="mr-2">
Cancel
</ButtonSecondary> */}
<ButtonSecondary
onClick={() => addConditionToGroup()}
// className="bg-secondary hover:bg-secondary/90 border border-border px-2 py-1 rounded-md text-muted-foreground text-xs"
>
{i18nT("workflows.conditions.add")}
</ButtonSecondary>
</div>
<div className="flex items-center space-x-1">
<ButtonPrimary disabled={errors.length > 0} onClick={() => onSave({ ...conditionsGroup, conditions, type: conditionGroupType })}>
{i18nT("shared.save")}
</ButtonPrimary>
</div>
</div>
</div>
</div>
);
}
export default ConditionsGroupInput;
@@ -0,0 +1,150 @@
import { useState } from "react";
import { t as i18nT } from "i18next";
import SlideOverWideEmpty from "~/components/ui/slideOvers/SlideOverWideEmpty";
import { WorkflowConditionsGroupDto } from "~/modules/workflowEngine/dtos/WorkflowConditionDtos";
import ConditionsGroupInput from "./ConditionsGroupInput";
import { WorkflowBlockDto } from "~/modules/workflowEngine/dtos/WorkflowBlockDto";
import { WorkflowDto } from "~/modules/workflowEngine/dtos/WorkflowDto";
import WorkflowConditionUtils from "~/modules/workflowEngine/helpers/WorkflowConditionUtils";
import clsx from "clsx";
import { TrashIcon } from "~/components/ui/icons/vendor";
export default function ConditionsGroupsInfo({
workflow,
block,
type,
onUpdateConditionsGroups,
}: {
workflow: WorkflowDto;
block: WorkflowBlockDto;
type: "if" | "switch";
onUpdateConditionsGroups: (conditionsGroups: WorkflowConditionsGroupDto[]) => void;
}) {
const [selectedConditionsGroup, setSelectedConditionsGroup] = useState<WorkflowConditionsGroupDto | null>(null);
const [addingNewGroup, setAddingNewGroup] = useState(false);
function onSaveGroup(conditionsGroup: WorkflowConditionsGroupDto) {
if (conditionsGroup.index === block.conditionGroups.length) {
onUpdateConditionsGroups([...block.conditionGroups, conditionsGroup]);
} else {
onUpdateConditionsGroups(block.conditionGroups.map((group) => (group.index === conditionsGroup.index ? conditionsGroup : group)));
}
setSelectedConditionsGroup(null);
setAddingNewGroup(false);
}
function onDeleteGroup(idx: number) {
const newConditionsGroups = block.conditionGroups.filter((_group, i) => i !== idx);
onUpdateConditionsGroups(newConditionsGroups);
}
return (
<div className="space-y-1">
{block.conditionGroups.map((group) => {
return (
<div key={group.index} className="space-y-0.5">
{type === "switch" && (
<div className="text-muted-foreground text-xs font-medium uppercase">{i18nT("workflows.conditions.caseNumber", { index: group.index + 1 })}</div>
)}
{/* role=button instead of <button>: this row contains a nested delete <button>, so it cannot be a button element. */}
<div
role="button"
tabIndex={0}
onClick={() => setSelectedConditionsGroup(group)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
setSelectedConditionsGroup(group);
}
}}
className={clsx(
"border-border group relative cursor-pointer rounded-lg border border-dashed px-2 py-2 text-sm hover:border-dotted focus:outline-hidden",
group.conditions.length === 0
? "border-red-300 bg-red-50 text-red-800 hover:border-red-800 hover:bg-red-100"
: "border-border bg-background hover:bg-secondary/90 hover:border-border"
)}
>
{type === "switch" && (
<button
type="button"
aria-label={i18nT("workflows.conditions.deleteCase", { index: group.index + 1 })}
onClick={(e) => {
e.stopPropagation();
onDeleteGroup(group.index);
}}
className="text-muted-foreground absolute right-2 top-2.5 hidden rounded-md hover:text-red-500 focus:outline-hidden focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 group-focus-within:block group-hover:block"
>
<TrashIcon className="h-4 w-4" />
</button>
)}
<div className="space-y-0.5">
{group.conditions.length === 0 ? (
<div>
<div className="font-medium">{i18nT("workflows.conditions.empty")}</div>
</div>
) : group.conditions.length === 1 ? (
<div className=" border-border bg-secondary/90 flex flex-col items-center justify-center rounded-md border p-2 text-center">
<div key={group.index} className="flex items-center space-x-2">
<div className="font-medium">{WorkflowConditionUtils.getConditionString(group.conditions[0], i18nT)}</div>
</div>
</div>
) : (
<div>
<div className="font-medium">{i18nT("workflows.conditions.count", { count: group.conditions.length })}</div>
</div>
)}
</div>
</div>
</div>
);
})}
{type === "switch" && (
<button
type="button"
onClick={() => setAddingNewGroup(true)}
className="border-border text-muted-foreground bg-secondary hover:bg-secondary/90 rounded-md border px-2 py-1 text-xs"
>
{i18nT("workflows.conditions.addCase")}
</button>
)}
<SlideOverWideEmpty
className="sm:max-w-2xl"
title={!selectedConditionsGroup ? "" : type === "if" ? i18nT("workflows.conditions.ifTitle") : i18nT("workflows.conditions.caseTitle", { index: selectedConditionsGroup.index + 1 })}
open={!!selectedConditionsGroup}
onClose={() => setSelectedConditionsGroup(null)}
>
{selectedConditionsGroup && (
<ConditionsGroupInput
workflow={workflow}
block={block}
type={type}
conditionsGroup={selectedConditionsGroup}
onCancel={() => setSelectedConditionsGroup(null)}
onSave={onSaveGroup}
/>
)}
</SlideOverWideEmpty>
<SlideOverWideEmpty
className="sm:max-w-2xl"
title={i18nT("workflows.conditions.addCaseTitle", { index: block.conditionGroups.length + 1 })}
open={addingNewGroup}
onClose={() => setAddingNewGroup(false)}
>
{addingNewGroup && (
<ConditionsGroupInput
workflow={workflow}
block={block}
type={type}
conditionsGroup={{
index: block.conditionGroups.length,
type: "AND",
conditions: [],
}}
onCancel={() => setAddingNewGroup(false)}
onSave={onSaveGroup}
/>
)}
</SlideOverWideEmpty>
</div>
);
}
@@ -0,0 +1,5 @@
import { ArrowLeftRightIcon } from "~/components/ui/icons/vendor";
export default function ConditionalIcon({ className }: { className?: string }) {
return <ArrowLeftRightIcon className={className} />;
}
@@ -0,0 +1,7 @@
export default function DoNothingIcon({ className }: { className?: string }) {
return (
<svg className={className} fill="currentColor" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="50" height="50" viewBox="0 0 50 50">
<path d="M 32 9 L 32 21.851563 L 11 8.152344 L 11 41.847656 L 32 28.148438 L 32 41 L 40 41 L 40 9 Z"></path>
</svg>
);
}
@@ -0,0 +1,7 @@
export default function HttpRequestIcon({ className }: { className?: string }) {
return (
<svg className={className} fill="currentColor" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="50" height="50" viewBox="0 0 50 50">
<path d="M 3 6 C 2.449219 6 2 6.449219 2 7 L 2 43 C 2 43.554688 2.449219 44 3 44 L 47 44 C 47.554688 44 48 43.554688 48 43 L 48 7 C 48 6.449219 47.554688 6 47 6 Z M 14.5 16 C 14.9375 16 15.339844 16.300781 15.46875 16.71875 L 19.96875 31.34375 C 19.996094 31.441406 20 31.523438 20 31.625 L 20 32 C 20 32.554688 19.550781 33 19 33 C 18.449219 33 18 32.554688 18 32 L 18 31.78125 L 17.15625 29 L 11.84375 29 L 11 31.78125 L 11 32 C 11 32.554688 10.550781 33 10 33 C 9.449219 33 9 32.554688 9 32 L 9 31.625 C 9 31.523438 9.003906 31.441406 9.03125 31.34375 L 13.53125 16.71875 C 13.660156 16.300781 14.0625 16 14.5 16 Z M 24 16 L 28 16 C 30.207031 16 32 17.792969 32 20 L 32 23 C 32 25.207031 30.207031 27 28 27 L 25 27 L 25 32 C 25 32.554688 24.550781 33 24 33 C 23.449219 33 23 32.554688 23 32 L 23 17 C 23 16.449219 23.449219 16 24 16 Z M 35.5 16 L 38.5 16 C 39.054688 16 39.5 16.449219 39.5 17 C 39.5 17.550781 39.054688 18 38.5 18 L 38 18 L 38 31 L 38.5 31 C 39.054688 31 39.5 31.445313 39.5 32 C 39.5 32.554688 39.054688 33 38.5 33 L 35.5 33 C 34.945313 33 34.5 32.554688 34.5 32 C 34.5 31.445313 34.945313 31 35.5 31 L 36 31 L 36 18 L 35.5 18 C 34.945313 18 34.5 17.550781 34.5 17 C 34.5 16.449219 34.945313 16 35.5 16 Z M 25 18 L 25 25 L 28 25 C 29.101563 25 30 24.101563 30 23 L 30 20 C 30 18.898438 29.101563 18 28 18 Z M 14.5 20.40625 L 12.46875 27 L 16.53125 27 Z"></path>
</svg>
);
}
@@ -0,0 +1,7 @@
export default function LogIcon({ className }: { className?: string }) {
return (
<svg className={className} fill="currentColor" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="64" height="64" viewBox="0 0 64 64">
<path d="M 11 6 L 11 58 L 53 58 L 53 24.154297 C 53 22.117297 52.280703 20.144938 50.970703 18.585938 L 42.109375 8.0429688 C 41.021375 6.7479688 39.416609 6 37.724609 6 L 11 6 z M 15 10 L 36.695312 10 C 39.985312 10 41 11.365078 41 12.580078 C 41 14.365078 39 17 39 17 C 47.724993 21.129645 48.835785 21.437688 48.974609 24 L 15 24 L 15 10 z M 30.927734 27.791016 C 33.318734 27.791016 35.044922 29.770094 35.044922 32.996094 C 35.044922 36.222094 33.360672 38.228516 31.013672 38.228516 C 28.622672 38.228516 26.8125 36.291094 26.8125 32.996094 C 26.8125 29.770094 28.565734 27.791016 30.927734 27.791016 z M 41.285156 27.791016 C 42.855156 27.791016 43.959609 28.342172 44.849609 29.826172 L 43.109375 30.986328 C 42.727375 30.236328 41.922156 29.714844 41.285156 29.714844 C 39.800156 29.714844 39.050781 30.818094 39.050781 32.996094 C 39.050781 34.948094 39.928156 36.361328 41.285156 36.361328 C 42.290156 36.361328 42.981672 35.740047 43.138672 34.623047 L 41.257812 34.623047 L 41.257812 32.8125 L 45.259766 32.8125 L 45.259766 33.632812 C 45.259766 35.981812 43.563156 38.228516 41.285156 38.228516 C 38.824156 38.228516 37.169922 35.966094 37.169922 32.996094 C 37.169922 29.800094 38.626156 27.791016 41.285156 27.791016 z M 19.527344 27.931641 L 21.351562 27.931641 L 21.351562 36.291016 L 25.425781 36.291016 L 25.425781 38.087891 L 19.527344 38.087891 L 19.527344 27.931641 z M 30.927734 29.585938 C 29.570734 29.585938 28.609375 30.717141 28.609375 32.994141 C 28.609375 35.271141 29.640734 36.404297 30.927734 36.404297 C 32.242734 36.404297 33.248047 35.271141 33.248047 32.994141 C 33.248047 30.717141 32.412734 29.585937 30.927734 29.585938 z M 15 42 L 49 42 L 49 54 L 15 54 L 15 42 z"></path>
</svg>
);
}
@@ -0,0 +1,17 @@
import { type NodeProps } from "@xyflow/react";
import { t as i18nT } from "i18next";
export default function SetTriggerNode({ id }: NodeProps) {
return (
<div
data-ui-id="ui.workflows.builder.node"
data-workflow-node-id={id}
data-workflow-node-kind="set-trigger"
className="border-border h-20 w-64 rounded-md border border-dashed bg-slate-100 ring-1 ring-blue-600 ring-offset-2"
>
<div className="flex h-full flex-col justify-center space-y-1">
<div className="text-muted-foreground text-center text-xs">{i18nT("workflows.builder.setTriggerPrompt")}</div>
</div>
</div>
);
}
@@ -0,0 +1,7 @@
export default function TriggerManualIcon({ className }: { className?: string }) {
return (
<svg className={className} fill="currentColor" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="50" height="50" viewBox="0 0 50 50">
<path d="M 19.5 2 C 15.363281 2 12 5.363281 12 9.5 C 12 11.945313 13.183594 14.128906 15 15.5 L 15 12.65625 C 14.371094 11.761719 14 10.671875 14 9.5 C 14 6.464844 16.46875 4 19.5 4 C 22.53125 4 25 6.46875 25 9.5 C 25 10.671875 24.628906 11.761719 24 12.65625 L 24 15.5 C 25.816406 14.128906 27 11.945313 27 9.5 C 27 5.363281 23.636719 2 19.5 2 Z M 19.5 7.03125 C 18.535156 7.03125 17 7.695313 17 9.4375 L 17 34.0625 L 10.96875 28.03125 C 9.601563 26.664063 7.398438 26.664063 6.03125 28.03125 C 4.664063 29.398438 4.664063 31.601563 6.03125 32.96875 L 17 44 C 17.683594 44.683594 21.113281 48 25.78125 48 C 25.816406 48.003906 34 48 34 48 C 38.425781 48 43 44.632813 43 39 L 43 24.59375 C 43 23.21875 42.253906 22 40.53125 22 C 38.808594 22 38.03125 23.1875 38.03125 24.53125 L 38.03125 27 C 38.03125 27.550781 37.585938 28 37.03125 28 C 36.476563 28 36 27.550781 36 27 L 36 22.46875 C 36.019531 21.261719 35.28125 20 33.53125 20 C 31.78125 20 31 21.355469 31 22.5 L 31 25 C 31 25.550781 30.554688 26 30 26 C 29.449219 26 28.96875 25.550781 28.96875 25 L 29 21.5 C 29 20.488281 28.210938 19 26.5 19 C 24.789063 19 24 20.488281 24 21.5 L 24 25 C 24 25.550781 23.550781 26 23 26 C 22.449219 26 22 25.550781 22 25 L 22 9.5625 C 22 7.722656 20.507813 7.03125 19.5 7.03125 Z"></path>
</svg>
);
}