import { Button } from "@agent-native/toolkit/ui/button"; import { IconBolt, IconClock, IconEye, IconLoader2, IconPlayerPause, IconPlayerPlay, IconPlus, IconTrash, } from "@tabler/icons-react"; import { useCallback, useState, type ReactNode } from "react"; import { sendToAgentChat } from "../agent-chat.js"; import { AgentEmptyState } from "../agent-page/AgentEmptyState.js"; import { useAutomations, useManageAutomation, type Automation, type JobsScope, } from "../agent-page/use-jobs.js"; import { AgentAskPopover } from "../AgentAskPopover.js"; import { agentNativePath } from "../api-path.js"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "../components/ui/dialog.js"; import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js"; import { PromptComposer } from "../composer/index.js"; import { useFormatters, useT } from "../i18n.js"; export interface AutomationsListProps { scope?: JobsScope; compact?: boolean; emptyMessage?: string; emptyState?: ReactNode; } export const AUTOMATION_CREATION_SCOPE = "personal" as const; export function automationCreationContext(): string { return `The user wants to create a new ${AUTOMATION_CREATION_SCOPE} automation. Use manage-automations with action=define to create it. Ask clarifying questions if needed about what event to trigger on, conditions, and what actions to take.`; } export function AutomationsList({ scope = "user", compact = false, emptyMessage, emptyState, }: AutomationsListProps) { const t = useT(); const { formatDate } = useFormatters(); const query = useAutomations(scope); const mutation = useManageAutomation(scope); const [deleteTarget, setDeleteTarget] = useState(null); const [detailsTarget, setDetailsTarget] = useState(null); const formatDateTime = (value: string | null) => { if (!value || Number.isNaN(new Date(value).getTime())) return null; return formatDate(value, { month: "short", day: "numeric", hour: "numeric", minute: "2-digit", }); }; if (query.isLoading) { return (
{t("jobs.loading", { defaultValue: "Loading…" })}
); } if (query.error) { return (

{t("jobs.automationsLoadError", { defaultValue: "Could not load automations.", })}

); } const automations = query.data ?? []; if (automations.length === 0) { if (emptyState) return emptyState; return ( } /> ); } return (
{automations.map((automation) => { const lastRun = formatDateTime(automation.lastRun); const nextRun = formatDateTime(automation.nextRun); const trigger = automation.triggerType === "event" ? t("jobs.automationEventTrigger", { defaultValue: "On {{event}}", event: automation.event ?? "event", }) : (automation.scheduleDescription ?? automation.schedule ?? t("jobs.automationScheduleTrigger", { defaultValue: "Scheduled task", })); return (
{automation.triggerType === "event" ? ( ) : ( )}

{automation.name.replace(/-/g, " ")}

{automation.enabled ? t("jobs.enabled", { defaultValue: "Enabled" }) : t("jobs.disabled", { defaultValue: "Disabled" })} {automation.lastStatus ? ( {automation.lastStatus} ) : null}

{trigger}

{automation.body}

{lastRun || nextRun ? (
{lastRun ? ( {t("jobs.lastRun", { defaultValue: "Last run" })}:{" "} {lastRun} ) : null} {nextRun ? ( {t("jobs.nextRun", { defaultValue: "Next run" })}:{" "} {nextRun} ) : null}
) : null}
{automation.canUpdate ? ( <> ) : null}
); })} {mutation.error ? (

{mutation.error.message || t("jobs.automationUpdateError", { defaultValue: "Could not update automation.", })}

) : null} { if (!open && !mutation.isPending) setDeleteTarget(null); }} > {t("jobs.deleteAutomationTitle", { defaultValue: "Delete automation?", })} {t("jobs.deleteAutomationDescription", { defaultValue: "This permanently removes the automation and cannot be undone.", })} { if (!open) setDetailsTarget(null); }} > {detailsTarget?.name.replace(/-/g, " ") ?? t("jobs.automationDetails", { defaultValue: "Automation details", })} {detailsTarget ? detailsTarget.triggerType === "event" ? t("jobs.automationEventDetails", { defaultValue: "Runs when {{event}}.", event: detailsTarget.event ?? "an event fires", }) : (detailsTarget.scheduleDescription ?? detailsTarget.schedule ?? t("jobs.automationScheduleTrigger", { defaultValue: "Scheduled task", })) : null} {detailsTarget ? (
{detailsTarget.condition ? (

{t("jobs.condition", { defaultValue: "Condition" })}

{detailsTarget.condition}

) : null}

{t("jobs.instructions", { defaultValue: "Instructions" })}

{detailsTarget.body}

) : null}
); } export function AutomationsSection() { const t = useT(); const { data: automations = [] } = useAutomations("user"); const [newOpen, setNewOpen] = useState(false); const [toast, setToast] = useState<{ kind: "ok" | "err"; text: string; } | null>(null); const showToast = useCallback( (kind: "ok" | "err", text: string, ms = 2500) => { setToast({ kind, text }); window.setTimeout(() => setToast(null), ms); }, [], ); const handleFireTestEvent = useCallback(async () => { showToast( "ok", t("jobs.firingTestEvent", { defaultValue: "Firing test event…" }), ); try { const res = await fetch( agentNativePath("/_agent-native/automations/fire-test"), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ data: {} }), }, ); if (!res.ok) { showToast( "err", t("jobs.fireEventError", { defaultValue: "Failed to fire event ({{status}})", status: res.status, }), ); return; } showToast("ok", t("jobs.eventFired", { defaultValue: "Event fired" })); } catch (err: unknown) { showToast( "err", err instanceof Error ? err.message : t("jobs.fireEventErrorGeneric", { defaultValue: "Failed to fire event.", }), ); } }, [showToast, t]); const handleNewSubmit = useCallback((text: string) => { const trimmed = text.trim(); if (!trimmed) return; window.dispatchEvent( new CustomEvent("agent-panel:set-mode", { detail: { mode: "chat" }, }), ); sendToAgentChat({ message: trimmed, context: automationCreationContext(), submit: true, newTab: true, }); setNewOpen(false); }, []); return (

{t("jobs.newAutomation", { defaultValue: "New automation" })}

{automations.length > 0 ? ( ) : null}
{toast ? (

{toast.text}

) : null}
); }