import { useEffect, useState } from "react"; import { Clock3, RefreshCw, Save, Trash2, Webhook, Zap } from "lucide-react"; import type { RoutineTrigger } from "@paperclipai/shared"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Badge } from "@/components/ui/badge"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { ScheduleEditor } from "./ScheduleEditor"; import { buildRoutineTriggerPatch } from "../lib/routine-trigger-patch"; import { describeCron } from "../lib/cron-readable"; const signingModes = ["bearer", "hmac_sha256", "github_hmac", "none"]; const SIGNING_MODES_WITHOUT_REPLAY_WINDOW = new Set(["github_hmac", "none"]); function getLocalTimezone(): string { try { return Intl.DateTimeFormat().resolvedOptions().timeZone; } catch { return "UTC"; } } /** * Single trigger card with its own field-edit + save state (§3.2). Extracted * from the previous inline `TriggerEditor` in `RoutineDetail.tsx` — same logic. */ export function RoutineTriggerCard({ trigger, onSave, onRotate, onDelete, disabled, }: { trigger: RoutineTrigger; onSave: (id: string, patch: Record) => void; onRotate: (id: string) => void; onDelete: (id: string) => void; disabled?: boolean; }) { const [draft, setDraft] = useState({ label: trigger.label ?? "", cronExpression: trigger.cronExpression ?? "", signingMode: trigger.signingMode ?? "bearer", replayWindowSec: String(trigger.replayWindowSec ?? 300), }); useEffect(() => { setDraft({ label: trigger.label ?? "", cronExpression: trigger.cronExpression ?? "", signingMode: trigger.signingMode ?? "bearer", replayWindowSec: String(trigger.replayWindowSec ?? 300), }); }, [trigger]); const KindIcon = trigger.kind === "schedule" ? Clock3 : trigger.kind === "webhook" ? Webhook : Zap; const humanCron = trigger.kind === "schedule" ? describeCron(draft.cronExpression) : null; const lastResultOk = trigger.lastResult != null && /succeed|success|ok|200|delivered/i.test(String(trigger.lastResult)); return (
event.preventDefault()} >
{trigger.label ?? trigger.kind}
{humanCron ? (

{humanCron}

) : null}
{trigger.lastResult ? ( {String(trigger.lastResult)} ) : null} {trigger.kind === "schedule" && trigger.nextRunAt ? `Next: ${new Date(trigger.nextRunAt).toLocaleString()}` : trigger.kind === "webhook" ? "Webhook" : "API"}
setDraft((current) => ({ ...current, label: event.target.value }))} />
{trigger.kind === "schedule" && (
setDraft((current) => ({ ...current, cronExpression })) } />
)} {trigger.kind === "webhook" && ( <>
{!SIGNING_MODES_WITHOUT_REPLAY_WINDOW.has(draft.signingMode) && (
setDraft((current) => ({ ...current, replayWindowSec: event.target.value })) } />
)} )}
{!disabled && (
{trigger.kind === "webhook" && ( )}
)}
); }