import { useState } from "react"; import { LuChevronDown, LuCopy, LuGripVertical, LuTrash2 } from "react-icons/lu"; import { useSortable } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import cronstrue from "cronstrue"; import { namespacedPath, HTTP_METHODS, triggerDestinations } from "../../lib/workflow-doc.js"; import { CRON_CUSTOM, CRON_PRESETS, matchCronPreset, scheduleForPreset } from "../../lib/cron-presets.js"; import { FormInput, FormSelect } from "../FormControls.jsx"; import { AuthPicker } from "./AuthPicker.jsx"; export function TriggerCard(props) { if (props.sortable === false) { return ; } return ; } function TriggerCardSortable(props) { const drag = useSortable({ id: props.trigger.uiId, disabled: props.disabled, }); return ; } function TriggerCardView({ trigger, index, owner, onChange, onRemove, disabled, auths = [], pages = [], workflows = [], excludeFile, sortable = true, defaultExpanded = false, drag, }) { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = drag ?? { attributes: {}, listeners: {}, setNodeRef: undefined, transform: null, transition: undefined, isDragging: false, }; const style = { transform: CSS.Transform.toString(transform), transition, opacity: isDragging ? 0.6 : 1, }; const type = trigger.type; const [expanded, setExpanded] = useState(defaultExpanded); const alertDestinations = triggerDestinations(workflows, { owner, excludeFile }); return (
{sortable !== false ? ( ) : null}
{expanded ? ( <> {type === "HTTP" ? ( ) : null} {type === "cron" ? ( ) : null} {type === "workflow" ? (

Other workflows can call this one with trigger-workflow.js. No extra fields.

) : null} {type !== "HTTP" && type !== "cron" && type !== "workflow" ? (

Unknown trigger type. Edit it in the YAML tab.

) : null} ) : null}
); } function triggerSummary(trigger, owner) { const type = trigger?.type; /** @type {string[]} */ const failureParts = []; if (trigger.onConsecutiveFailures && trigger.onFailureWorkflow) { failureParts.push(`onFailure@${trigger.onFailureWorkflow}`); } if (trigger.disableOnConsecutiveFailures) { failureParts.push("auto-disable"); } const failure = failureParts.length ? ` ยท ${failureParts.join(", ")}` : ""; if (type === "HTTP") { return `${trigger.method || "POST"} ${namespacedPath(owner || "owner", trigger.path || "/")}${failure}`; } if (type === "cron") { return `${trigger.schedule || ""}${failure}`; } if (type === "workflow") return "callable"; return ""; } function typeLabel(type) { if (type === "HTTP") return "HTTP"; if (type === "cron") return "Cron"; if (type === "workflow") return "Workflow"; return type || "Trigger"; } function Field({ label, children, hint }) { return (
{label ? {label} : null} {children} {hint ? {hint} : null}
); } function HttpFields({ trigger, owner, disabled, onChange, auths, pages, alertDestinations }) { const path = trigger.path || "/"; const url = namespacedPath(owner || "owner", path); const authEntries = Array.isArray(trigger.auth) ? trigger.auth : []; const selectedIds = authEntries.filter((e) => typeof e === "string" && e).map(String); const inlineEntries = authEntries.filter((e) => e && typeof e === "object"); function setAuthIds(nextIds) { const next = [...nextIds, ...inlineEntries]; onChange({ ...trigger, auth: next.length ? next : null }); } function copyUrl() { if (typeof navigator?.clipboard?.writeText === "function") { navigator.clipboard.writeText(url); } } return (
onChange({ ...trigger, method: e.target.value })} > {HTTP_METHODS.map((m) => ( ))} {HTTP_METHODS.includes(trigger.method || "POST") ? null : ( )} onChange({ ...trigger, path: e.target.value })} placeholder="/hook" />
onChange({ ...trigger, response: e.target.value })} > {pages.map((p) => ( ))} {trigger.response && !pages.some((p) => p.name === trigger.response) ? ( ) : null}
); } function CronFields({ trigger, disabled, onChange, alertDestinations }) { const preset = matchCronPreset(trigger.schedule); let human = ""; try { human = cronstrue.toString(trigger.schedule || "", { throwExceptionOnParseError: true }); } catch { human = "Invalid cron expression"; } return (

{human}

Cron runs use this workflow's top-level data as the payload. Set it in YAML or the Test panel prefill.

); } function FailureAlertFields({ trigger, disabled, onChange, alertDestinations }) { return (

Failure alert

onChange({ ...trigger, onConsecutiveFailures: e.target.value }) } placeholder="e.g. 3" /> After this many sequential failed runs for this trigger, JerapahFlow starts the selected workflow (it must declare a workflow{" "} trigger). } > onChange({ ...trigger, onFailureWorkflow: e.target.value })} > {alertDestinations.map((w) => ( ))} {trigger.onFailureWorkflow && !alertDestinations.some( (w) => (w.name ?? w.file) === trigger.onFailureWorkflow, ) ? ( ) : null}
); }