@agent-native/dispatch 0.32.0 → 0.33.0-nightly-20260831223557
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/ask_app.js +0 -1
- package/dist/actions/ask_app.js.map +1 -1
- package/dist/actions/provider-api-register.d.ts +8 -8
- package/dist/components/automation-create-dialog.d.ts +16 -0
- package/dist/components/automation-create-dialog.d.ts.map +1 -0
- package/dist/components/automation-create-dialog.js +213 -0
- package/dist/components/automation-create-dialog.js.map +1 -0
- package/dist/components/automation-details-panel.d.ts.map +1 -1
- package/dist/components/automation-details-panel.js +35 -4
- package/dist/components/automation-details-panel.js.map +1 -1
- package/dist/hooks/use-navigation-state.d.ts +1 -0
- package/dist/hooks/use-navigation-state.d.ts.map +1 -1
- package/dist/hooks/use-navigation-state.js +5 -0
- package/dist/hooks/use-navigation-state.js.map +1 -1
- package/dist/lib/automation-display.d.ts.map +1 -1
- package/dist/lib/automation-display.js +4 -0
- package/dist/lib/automation-display.js.map +1 -1
- package/dist/lib/automations.d.ts +2 -1
- package/dist/lib/automations.d.ts.map +1 -1
- package/dist/lib/automations.js.map +1 -1
- package/dist/routes/pages/automations.d.ts.map +1 -1
- package/dist/routes/pages/automations.js +16 -24
- package/dist/routes/pages/automations.js.map +1 -1
- package/dist/server/lib/mcp-gateway.d.ts.map +1 -1
- package/dist/server/lib/mcp-gateway.js +27 -3
- package/dist/server/lib/mcp-gateway.js.map +1 -1
- package/package.json +3 -3
- package/src/actions/ask_app.ts +0 -1
- package/src/components/automation-create-dialog.tsx +759 -0
- package/src/components/automation-details-panel.tsx +80 -12
- package/src/hooks/use-navigation-state.spec.ts +13 -0
- package/src/hooks/use-navigation-state.ts +6 -0
- package/src/lib/automation-display.ts +2 -0
- package/src/lib/automations.ts +2 -1
- package/src/routes/pages/automations.spec.tsx +11 -0
- package/src/routes/pages/automations.tsx +27 -57
- package/src/server/lib/mcp-gateway.spec.ts +37 -2
- package/src/server/lib/mcp-gateway.ts +40 -3
package/dist/actions/ask_app.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ask_app.js","sourceRoot":"","sources":["../../src/actions/ask_app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAExE,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,8HAA8H;IAChI,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;QAClE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0CAA0C,CAAC;QACxE,KAAK,EAAE,CAAC;aACL,OAAO,EAAE;aACT,QAAQ,EAAE;aACV,QAAQ,CAAC,4DAA4D,CAAC;QACzE,SAAS,EAAE,CAAC;aACT,MAAM,EAAE;aACR,GAAG,EAAE;aACL,GAAG,CAAC,CAAC,CAAC;aACN,
|
|
1
|
+
{"version":3,"file":"ask_app.js","sourceRoot":"","sources":["../../src/actions/ask_app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAExE,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,8HAA8H;IAChI,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;QAClE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0CAA0C,CAAC;QACxE,KAAK,EAAE,CAAC;aACL,OAAO,EAAE;aACT,QAAQ,EAAE;aACV,QAAQ,CAAC,4DAA4D,CAAC;QACzE,SAAS,EAAE,CAAC;aACT,MAAM,EAAE;aACR,GAAG,EAAE;aACL,GAAG,CAAC,CAAC,CAAC;aACN,QAAQ,EAAE;aACV,QAAQ,CAAC,sCAAsC,CAAC;KACpD,CAAC;IACF,GAAG,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,CAChD,wBAAwB,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;CAC/D,CAAC,CAAC","sourcesContent":["import { defineAction } from \"@agent-native/core/action\";\nimport { z } from \"zod\";\n\nimport { askGrantedDispatchMcpApp } from \"../server/lib/mcp-gateway.js\";\n\nexport default defineAction({\n description:\n \"Send a natural-language request to an app available through Dispatch MCP. Use list_apps first to see which apps are granted.\",\n schema: z.object({\n app: z.string().describe(\"Granted app id, e.g. mail or calendar.\"),\n message: z.string().describe(\"The request to send to that app's agent.\"),\n async: z\n .boolean()\n .optional()\n .describe(\"Start a durable task and return immediately with a taskId.\"),\n maxWaitMs: z\n .number()\n .int()\n .min(0)\n .optional()\n .describe(\"Maximum inline wait in milliseconds.\"),\n }),\n run: async ({ app, message, async, maxWaitMs }) =>\n askGrantedDispatchMcpApp(app, message, { async, maxWaitMs }),\n});\n"]}
|
|
@@ -47,9 +47,9 @@ declare const _default: import("node_modules/@agent-native/core/dist").ActionDef
|
|
|
47
47
|
notes?: string | undefined;
|
|
48
48
|
scope?: "org" | "user" | undefined;
|
|
49
49
|
}, {
|
|
50
|
+
deleted?: undefined;
|
|
50
51
|
id?: undefined;
|
|
51
52
|
provider?: undefined;
|
|
52
|
-
deleted?: undefined;
|
|
53
53
|
providers: {
|
|
54
54
|
id: string;
|
|
55
55
|
label: string;
|
|
@@ -65,29 +65,29 @@ declare const _default: import("node_modules/@agent-native/core/dist").ActionDef
|
|
|
65
65
|
label?: undefined;
|
|
66
66
|
message?: undefined;
|
|
67
67
|
} | {
|
|
68
|
-
count?: undefined;
|
|
69
|
-
id?: undefined;
|
|
70
68
|
deleted?: undefined;
|
|
69
|
+
id?: undefined;
|
|
71
70
|
providers?: undefined;
|
|
71
|
+
count?: undefined;
|
|
72
72
|
found: boolean;
|
|
73
73
|
provider: import("node_modules/@agent-native/core/dist/provider-api").CustomProviderConfig;
|
|
74
74
|
registered?: undefined;
|
|
75
75
|
label?: undefined;
|
|
76
76
|
message?: undefined;
|
|
77
77
|
} | {
|
|
78
|
-
count?: undefined;
|
|
79
|
-
provider?: undefined;
|
|
80
78
|
deleted?: undefined;
|
|
79
|
+
provider?: undefined;
|
|
81
80
|
providers?: undefined;
|
|
81
|
+
count?: undefined;
|
|
82
82
|
found: boolean;
|
|
83
83
|
id: string;
|
|
84
84
|
registered?: undefined;
|
|
85
85
|
label?: undefined;
|
|
86
86
|
message?: undefined;
|
|
87
87
|
} | {
|
|
88
|
-
count?: undefined;
|
|
89
88
|
provider?: undefined;
|
|
90
89
|
providers?: undefined;
|
|
90
|
+
count?: undefined;
|
|
91
91
|
found?: undefined;
|
|
92
92
|
deleted: boolean;
|
|
93
93
|
id: string;
|
|
@@ -95,10 +95,10 @@ declare const _default: import("node_modules/@agent-native/core/dist").ActionDef
|
|
|
95
95
|
label?: undefined;
|
|
96
96
|
message?: undefined;
|
|
97
97
|
} | {
|
|
98
|
-
count?: undefined;
|
|
99
|
-
provider?: undefined;
|
|
100
98
|
deleted?: undefined;
|
|
99
|
+
provider?: undefined;
|
|
101
100
|
providers?: undefined;
|
|
101
|
+
count?: undefined;
|
|
102
102
|
found?: undefined;
|
|
103
103
|
registered: boolean;
|
|
104
104
|
id: string;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type TriggerType = "schedule" | "event" | "webhook";
|
|
2
|
+
interface CreateAutomationResult {
|
|
3
|
+
created: true;
|
|
4
|
+
name: string;
|
|
5
|
+
triggerType: TriggerType;
|
|
6
|
+
webhookPath: string | null;
|
|
7
|
+
}
|
|
8
|
+
export interface AutomationCreateDialogProps {
|
|
9
|
+
open: boolean;
|
|
10
|
+
initialScope?: "personal" | "organization";
|
|
11
|
+
onOpenChange: (open: boolean) => void;
|
|
12
|
+
onCreated: (result: CreateAutomationResult) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare function AutomationCreateDialog({ open, initialScope, onOpenChange, onCreated, }: AutomationCreateDialogProps): import("node_modules/@types/react").JSX.Element;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=automation-create-dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"automation-create-dialog.d.ts","sourceRoot":"","sources":["../../src/components/automation-create-dialog.tsx"],"names":[],"mappings":"AA+CA,KAAK,WAAW,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;AAkBpD,UAAU,sBAAsB;IAC9B,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AA4dD,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC;IAC3C,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,SAAS,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,IAAI,CAAC;CACrD;AAED,wBAAgB,sBAAsB,CAAC,EACrC,IAAI,EACJ,YAAyB,EACzB,YAAY,EACZ,SAAS,GACV,EAAE,2BAA2B,mDAwM7B"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { DEFAULT_AUTOMATION_SCHEDULE_DRAFT, AUTOMATION_WEEKDAYS, automationScheduleToCron, formatAutomationTime, } from "@agent-native/core/client/agent-page/automation-schedule";
|
|
3
|
+
import { TimezoneSelect, browserTimezone, } from "@agent-native/core/client/agent-page/timezone-select";
|
|
4
|
+
import { useActionMutation, useActionQuery, } from "@agent-native/core/client/hooks";
|
|
5
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
6
|
+
import { IconBolt, IconCalendarEvent, IconClock, IconCode, IconLoader2, } from "@tabler/icons-react";
|
|
7
|
+
import { useEffect, useState } from "react";
|
|
8
|
+
import { Button } from "./ui/button.js";
|
|
9
|
+
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "./ui/dialog.js";
|
|
10
|
+
import { Input } from "./ui/input.js";
|
|
11
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "./ui/select.js";
|
|
12
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs.js";
|
|
13
|
+
import { Textarea } from "./ui/textarea.js";
|
|
14
|
+
const PRESETS = [
|
|
15
|
+
{
|
|
16
|
+
value: "hourly",
|
|
17
|
+
labelKey: "jobs.schedulePreset.hourly",
|
|
18
|
+
detailKey: "jobs.schedulePreset.hourlyDetail",
|
|
19
|
+
label: "Every hour",
|
|
20
|
+
detail: "At the top of the hour",
|
|
21
|
+
draft: { preset: "hourly" },
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
value: "daily-midnight",
|
|
25
|
+
labelKey: "jobs.schedulePreset.dailyMidnight",
|
|
26
|
+
detailKey: "jobs.schedulePreset.dailyMidnightDetail",
|
|
27
|
+
label: "Every day at midnight",
|
|
28
|
+
detail: "12:00 AM",
|
|
29
|
+
draft: { preset: "daily-midnight" },
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
value: "daily-noon",
|
|
33
|
+
labelKey: "jobs.schedulePreset.dailyNoon",
|
|
34
|
+
detailKey: "jobs.schedulePreset.dailyNoonDetail",
|
|
35
|
+
label: "Every day at noon",
|
|
36
|
+
detail: "12:00 PM",
|
|
37
|
+
draft: { preset: "daily-noon" },
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
value: "weekdays",
|
|
41
|
+
labelKey: "jobs.schedulePreset.weekdays",
|
|
42
|
+
detailKey: "jobs.schedulePreset.weekdaysDetail",
|
|
43
|
+
label: "Every weekday",
|
|
44
|
+
detail: "Monday to Friday at 9:00 AM",
|
|
45
|
+
draft: { preset: "weekdays", time: "09:00" },
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
value: "weekly",
|
|
49
|
+
labelKey: "jobs.schedulePreset.weekly",
|
|
50
|
+
detailKey: "jobs.schedulePreset.weeklyDetail",
|
|
51
|
+
label: "Every week",
|
|
52
|
+
detail: "Sunday at 9:00 AM",
|
|
53
|
+
draft: { preset: "weekly", weekday: 0, time: "09:00" },
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
function presetClass(selected) {
|
|
57
|
+
return ("rounded-lg border px-3 py-2 text-left transition-colors " +
|
|
58
|
+
(selected
|
|
59
|
+
? "border-primary bg-primary/5 text-foreground"
|
|
60
|
+
: "border-border/70 bg-background hover:bg-muted/50"));
|
|
61
|
+
}
|
|
62
|
+
function scheduleIsValid(value) {
|
|
63
|
+
return value.trim().split(/\s+/).length === 5;
|
|
64
|
+
}
|
|
65
|
+
function ScheduleFields({ draft, saving, onChange, }) {
|
|
66
|
+
const t = useT();
|
|
67
|
+
const customMinute = Number(draft.time.split(":")[1] ?? 0);
|
|
68
|
+
return (_jsxs("div", { className: "space-y-3 rounded-lg border border-border/70 bg-muted/20 p-3", children: [_jsxs("div", { className: "grid gap-3 sm:grid-cols-[1fr_1fr]", children: [_jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: t("jobs.repeatEvery", { defaultValue: "Repeat every" }) }), _jsx(Input, { type: "number", min: 1, max: 31, value: draft.interval, disabled: saving, onChange: (event) => onChange({
|
|
69
|
+
interval: Math.min(draft.unit === "day" ? 1 : 31, Math.max(1, Number(event.target.value) || 1)),
|
|
70
|
+
}), className: "h-9 bg-background text-sm" })] }), _jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: t("jobs.scheduleUnit", { defaultValue: "Unit" }) }), _jsxs(Select, { value: draft.unit, disabled: saving, onValueChange: (value) => onChange({
|
|
71
|
+
unit: value,
|
|
72
|
+
...(value === "day" ? { interval: 1 } : {}),
|
|
73
|
+
}), children: [_jsx(SelectTrigger, { className: "h-9 bg-background text-sm", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "hour", children: t("jobs.hours", { defaultValue: "hour(s)" }) }), _jsx(SelectItem, { value: "day", children: t("jobs.day", { defaultValue: "day" }) }), _jsx(SelectItem, { value: "week", children: t("jobs.weeks", { defaultValue: "week(s)" }) }), _jsx(SelectItem, { value: "month", children: t("jobs.months", { defaultValue: "month(s)" }) })] })] })] })] }), draft.unit === "hour" ? (_jsxs("label", { className: "block space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: t("jobs.atMinute", { defaultValue: "At minute" }) }), _jsx(Input, { type: "number", min: 0, max: 59, value: customMinute, disabled: saving, onChange: (event) => onChange({
|
|
74
|
+
time: "00:" +
|
|
75
|
+
String(Math.min(59, Math.max(0, Number(event.target.value) || 0))).padStart(2, "0"),
|
|
76
|
+
}), className: "h-9 bg-background text-sm" })] })) : null, draft.unit === "week" ? (_jsxs("label", { className: "block space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: t("jobs.onDay", { defaultValue: "On" }) }), _jsxs(Select, { value: String(draft.weekday), disabled: saving, onValueChange: (value) => onChange({ weekday: Number(value) }), children: [_jsx(SelectTrigger, { className: "h-9 bg-background text-sm", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: AUTOMATION_WEEKDAYS.map((day) => (_jsx(SelectItem, { value: String(day.value), children: day.short }, day.value))) })] })] })) : null, draft.unit === "month" ? (_jsxs("label", { className: "block space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: t("jobs.dayOfMonth", { defaultValue: "Day of month" }) }), _jsx(Input, { type: "number", min: 1, max: 31, value: draft.monthDay, disabled: saving, onChange: (event) => onChange({
|
|
77
|
+
monthDay: Math.min(31, Math.max(1, Number(event.target.value) || 1)),
|
|
78
|
+
}), className: "h-9 bg-background text-sm" })] })) : null, draft.unit !== "hour" ? (_jsxs("label", { className: "block space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: t("jobs.atTime", { defaultValue: "At" }) }), _jsx(Input, { type: "time", value: draft.time, disabled: saving, onChange: (event) => onChange({ time: event.target.value }), className: "h-9 bg-background text-sm" })] })) : null] }));
|
|
79
|
+
}
|
|
80
|
+
function ScheduleBuilder({ saving, timezone, onTimezoneChange, onChange, }) {
|
|
81
|
+
const t = useT();
|
|
82
|
+
const [draft, setDraft] = useState(DEFAULT_AUTOMATION_SCHEDULE_DRAFT);
|
|
83
|
+
const [advanced, setAdvanced] = useState(false);
|
|
84
|
+
const [advancedValue, setAdvancedValue] = useState("0 * * * *");
|
|
85
|
+
const friendly = automationScheduleToCron(draft);
|
|
86
|
+
const activeSchedule = advanced
|
|
87
|
+
? advancedValue.trim()
|
|
88
|
+
: (friendly.schedule ?? "");
|
|
89
|
+
const valid = scheduleIsValid(activeSchedule) && (!friendly.error || advanced);
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
onChange(valid ? activeSchedule : "");
|
|
92
|
+
}, [activeSchedule, onChange, valid]);
|
|
93
|
+
function choosePreset(value, presetDraft) {
|
|
94
|
+
setDraft((current) => ({ ...current, ...presetDraft, preset: value }));
|
|
95
|
+
setAdvanced(false);
|
|
96
|
+
}
|
|
97
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "text-xs font-medium text-foreground", children: t("jobs.repeat", { defaultValue: "Repeat" }) }), _jsxs("div", { className: "grid gap-2 sm:grid-cols-2", children: [PRESETS.map((preset) => (_jsxs("button", { type: "button", "aria-pressed": !advanced && draft.preset === preset.value, disabled: saving, className: presetClass(!advanced && draft.preset === preset.value), onClick: () => choosePreset(preset.value, preset.draft), children: [_jsx("span", { className: "block text-sm font-medium", children: t(preset.labelKey, { defaultValue: preset.label }) }), _jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: t(preset.detailKey, { defaultValue: preset.detail }) })] }, preset.value))), _jsxs("button", { type: "button", "aria-pressed": !advanced && draft.preset === "custom", disabled: saving, className: presetClass(!advanced && draft.preset === "custom"), onClick: () => choosePreset("custom", {
|
|
98
|
+
preset: "custom",
|
|
99
|
+
unit: draft.unit || "day",
|
|
100
|
+
}), children: [_jsx("span", { className: "block text-sm font-medium", children: t("jobs.schedulePreset.custom", { defaultValue: "Custom" }) }), _jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: t("jobs.schedulePreset.customDetail", {
|
|
101
|
+
defaultValue: "Set your own repeat pattern",
|
|
102
|
+
}) })] })] })] }), !advanced && draft.preset === "custom" ? (_jsx(ScheduleFields, { draft: draft, saving: saving, onChange: (patch) => setDraft((current) => ({ ...current, ...patch })) })) : null, !advanced && friendly.schedule ? (_jsx("p", { className: "text-xs text-muted-foreground", children: t("jobs.schedulePreview", {
|
|
103
|
+
defaultValue: "Runs {{time}}.",
|
|
104
|
+
time: draft.preset === "hourly"
|
|
105
|
+
? t("jobs.schedulePreview.hourly", {
|
|
106
|
+
defaultValue: "at the start of every hour",
|
|
107
|
+
})
|
|
108
|
+
: draft.preset === "daily-midnight"
|
|
109
|
+
? t("jobs.schedulePreview.dailyMidnight", {
|
|
110
|
+
defaultValue: "every day at midnight",
|
|
111
|
+
})
|
|
112
|
+
: draft.preset === "daily-noon"
|
|
113
|
+
? t("jobs.schedulePreview.dailyNoon", {
|
|
114
|
+
defaultValue: "every day at noon",
|
|
115
|
+
})
|
|
116
|
+
: t("jobs.schedulePreview.atTime", {
|
|
117
|
+
defaultValue: "at {{time}}",
|
|
118
|
+
time: formatAutomationTime(draft.time),
|
|
119
|
+
}),
|
|
120
|
+
}) })) : null, !advanced && friendly.error ? (_jsx("p", { className: "text-xs text-destructive", children: friendly.error === "daily-interval"
|
|
121
|
+
? t("jobs.dailyIntervalAdvanced", {
|
|
122
|
+
defaultValue: "Every few days needs the Advanced cron editor below.",
|
|
123
|
+
})
|
|
124
|
+
: t("jobs.weeklyIntervalAdvanced", {
|
|
125
|
+
defaultValue: "Custom weekly intervals beyond one week are available under Advanced.",
|
|
126
|
+
}) })) : null, _jsxs("div", { children: [_jsx("label", { className: "text-xs font-medium text-foreground", children: t("jobs.timezone", { defaultValue: "Timezone" }) }), _jsx("div", { className: "mt-1", children: _jsx(TimezoneSelect, { value: timezone, disabled: saving, onChange: onTimezoneChange, suggested: [browserTimezone()] }) })] }), _jsxs("details", { className: "group rounded-lg border border-border/70 bg-muted/20", open: advanced, onToggle: (event) => {
|
|
127
|
+
const next = event.currentTarget.open;
|
|
128
|
+
if (next && !advanced && friendly.schedule) {
|
|
129
|
+
setAdvancedValue(friendly.schedule);
|
|
130
|
+
}
|
|
131
|
+
setAdvanced(next);
|
|
132
|
+
}, children: [_jsxs("summary", { className: "flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 text-xs font-medium text-foreground [&::-webkit-details-marker]:hidden", children: [_jsx(IconCode, { className: "size-4 text-muted-foreground" }), t("jobs.advancedSchedule", {
|
|
133
|
+
defaultValue: "Advanced - cron expression",
|
|
134
|
+
}), _jsx("span", { className: "ms-auto text-muted-foreground group-open:hidden", children: t("jobs.show", { defaultValue: "Show" }) }), _jsx("span", { className: "ms-auto hidden text-muted-foreground group-open:inline", children: t("jobs.hide", { defaultValue: "Hide" }) })] }), _jsxs("div", { className: "border-t border-border/60 px-3 pb-3 pt-2.5", children: [_jsx(Input, { className: "font-mono text-sm", value: advancedValue, spellCheck: false, autoComplete: "off", disabled: saving, onFocus: () => setAdvanced(true), onChange: (event) => {
|
|
135
|
+
setAdvanced(true);
|
|
136
|
+
setAdvancedValue(event.target.value);
|
|
137
|
+
} }), _jsx("p", { className: "mt-1 text-[11px] text-muted-foreground", children: t("jobs.cronFormatHint", {
|
|
138
|
+
defaultValue: "minute hour day-of-month month day-of-week",
|
|
139
|
+
}) })] })] }), activeSchedule && !valid ? (_jsx("p", { className: "text-xs text-destructive", children: t("jobs.cronFieldCount", {
|
|
140
|
+
defaultValue: "A cron expression needs exactly 5 fields.",
|
|
141
|
+
}) })) : null] }));
|
|
142
|
+
}
|
|
143
|
+
function EventFields({ open, event, onChange, }) {
|
|
144
|
+
const t = useT();
|
|
145
|
+
const eventsQuery = useActionQuery("list-automation-events", {}, { enabled: open, staleTime: 60_000 });
|
|
146
|
+
return (_jsxs(_Fragment, { children: [_jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: t("jobs.startWhen", { defaultValue: "Start when" }) }), _jsxs(Select, { value: event, onValueChange: onChange, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: eventsQuery.isLoading
|
|
147
|
+
? t("jobs.loadingEventTypes", {
|
|
148
|
+
defaultValue: "Loading event types...",
|
|
149
|
+
})
|
|
150
|
+
: t("jobs.chooseAppEvent", {
|
|
151
|
+
defaultValue: "Choose an app event",
|
|
152
|
+
}) }) }), _jsx(SelectContent, { children: (eventsQuery.data ?? []).map((item) => (_jsx(SelectItem, { value: item.name, children: item.name }, item.name))) })] })] }), eventsQuery.data?.find((item) => item.name === event)?.description ? (_jsx("p", { className: "mt-2 text-xs text-muted-foreground", children: eventsQuery.data.find((item) => item.name === event)?.description })) : null, eventsQuery.error ? (_jsx("p", { className: "mt-2 text-xs text-destructive", children: t("jobs.loadEventTypesError", {
|
|
153
|
+
defaultValue: "Could not load event types.",
|
|
154
|
+
}) })) : null] }));
|
|
155
|
+
}
|
|
156
|
+
export function AutomationCreateDialog({ open, initialScope = "personal", onOpenChange, onCreated, }) {
|
|
157
|
+
const t = useT();
|
|
158
|
+
const [name, setName] = useState("");
|
|
159
|
+
const [scope, setScope] = useState(initialScope);
|
|
160
|
+
const [triggerType, setTriggerType] = useState("schedule");
|
|
161
|
+
const [schedule, setSchedule] = useState("0 * * * *");
|
|
162
|
+
const [timezone, setTimezone] = useState(browserTimezone());
|
|
163
|
+
const [event, setEvent] = useState("");
|
|
164
|
+
const [body, setBody] = useState("");
|
|
165
|
+
const create = useActionMutation("manage-automation", {
|
|
166
|
+
onSuccess: onCreated,
|
|
167
|
+
});
|
|
168
|
+
const resetCreate = create.reset;
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
if (!open)
|
|
171
|
+
return;
|
|
172
|
+
setName("");
|
|
173
|
+
setScope(initialScope);
|
|
174
|
+
setTriggerType("schedule");
|
|
175
|
+
setSchedule("0 * * * *");
|
|
176
|
+
setTimezone(browserTimezone());
|
|
177
|
+
setEvent("");
|
|
178
|
+
setBody("");
|
|
179
|
+
resetCreate();
|
|
180
|
+
}, [initialScope, open, resetCreate]);
|
|
181
|
+
const canSubmit = name.trim().length > 0 &&
|
|
182
|
+
body.trim().length > 0 &&
|
|
183
|
+
(triggerType !== "event" || event.length > 0) &&
|
|
184
|
+
(triggerType !== "schedule" || scheduleIsValid(schedule));
|
|
185
|
+
function submit() {
|
|
186
|
+
if (!canSubmit)
|
|
187
|
+
return;
|
|
188
|
+
create.mutate({
|
|
189
|
+
operation: "create",
|
|
190
|
+
name: name.trim(),
|
|
191
|
+
scope,
|
|
192
|
+
triggerType,
|
|
193
|
+
body: body.trim(),
|
|
194
|
+
...(triggerType === "schedule"
|
|
195
|
+
? { schedule, timezone }
|
|
196
|
+
: triggerType === "event"
|
|
197
|
+
? { event }
|
|
198
|
+
: {}),
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "max-h-[90vh] overflow-y-auto sm:max-w-2xl", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: t("jobs.newAutomation", { defaultValue: "New automation" }) }), _jsx(DialogDescription, { children: t("jobs.createAutomationDescription", {
|
|
202
|
+
defaultValue: "Choose what starts it, then describe the work in plain language.",
|
|
203
|
+
}) })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid gap-3 sm:grid-cols-[1fr_12rem]", children: [_jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: t("jobs.name", { defaultValue: "Name" }) }), _jsx(Input, { value: name, disabled: create.isPending, placeholder: t("jobs.automationNamePlaceholder", {
|
|
204
|
+
defaultValue: "morning-digest",
|
|
205
|
+
}), onChange: (event) => setName(event.target.value) })] }), _jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: t("jobs.scope", { defaultValue: "Scope" }) }), _jsxs(Select, { value: scope, disabled: create.isPending, onValueChange: (value) => setScope(value), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "personal", children: t("jobs.personal", { defaultValue: "Personal" }) }), _jsx(SelectItem, { value: "organization", children: t("jobs.organization", { defaultValue: "Organization" }) })] })] })] })] }), _jsxs(Tabs, { value: triggerType, onValueChange: (value) => setTriggerType(value), children: [_jsxs(TabsList, { className: "grid h-auto w-full grid-cols-3", children: [_jsxs(TabsTrigger, { value: "schedule", className: "gap-2 py-2 text-xs", children: [_jsx(IconClock, { className: "size-3.5" }), t("jobs.schedule", { defaultValue: "Schedule" })] }), _jsxs(TabsTrigger, { value: "webhook", className: "gap-2 py-2 text-xs", children: [_jsx(IconBolt, { className: "size-3.5" }), t("jobs.webhook", { defaultValue: "Webhook" })] }), _jsxs(TabsTrigger, { value: "event", className: "gap-2 py-2 text-xs", children: [_jsx(IconCalendarEvent, { className: "size-3.5" }), t("jobs.appEvent", { defaultValue: "App event" })] })] }), _jsx(TabsContent, { value: "schedule", className: "mt-3", children: _jsx(ScheduleBuilder, { saving: create.isPending, timezone: timezone, onTimezoneChange: setTimezone, onChange: setSchedule }, open ? "schedule-open" : "schedule-closed") }), _jsx(TabsContent, { value: "webhook", className: "mt-3", children: _jsx("div", { className: "rounded-lg border border-primary/20 bg-primary/5 p-3", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(IconBolt, { className: "mt-0.5 size-4 shrink-0 text-primary" }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-foreground", children: t("jobs.webhookTriggerDescription", {
|
|
206
|
+
defaultValue: "Run when a service sends an HTTP POST",
|
|
207
|
+
}) }), _jsx("p", { className: "mt-1 text-xs leading-5 text-muted-foreground", children: t("jobs.webhookSetupDescription", {
|
|
208
|
+
defaultValue: "After you save, Dispatch gives you a private URL to paste into GitHub, Stripe, or another webhook provider.",
|
|
209
|
+
}) })] })] }) }) }), _jsx(TabsContent, { value: "event", className: "mt-3", children: _jsx(EventFields, { open: open, event: event, onChange: setEvent }) })] }), _jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: t("jobs.whatShouldItDo", { defaultValue: "What should it do?" }) }), _jsx(Textarea, { value: body, disabled: create.isPending, placeholder: t("jobs.automationBodyPlaceholder", {
|
|
210
|
+
defaultValue: "Review new support requests and summarize anything urgent in a shared note.",
|
|
211
|
+
}), rows: 5, onChange: (event) => setBody(event.target.value) })] }), create.error ? (_jsx("p", { className: "text-xs text-destructive", children: create.error.message })) : null] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "outline", disabled: create.isPending, onClick: () => onOpenChange(false), children: t("jobs.cancel", { defaultValue: "Cancel" }) }), _jsxs(Button, { type: "button", disabled: !canSubmit || create.isPending, onClick: submit, children: [create.isPending ? (_jsx(IconLoader2, { className: "size-4 animate-spin" })) : null, t("jobs.createAutomation", { defaultValue: "Create automation" })] })] })] }) }));
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=automation-create-dialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"automation-create-dialog.js","sourceRoot":"","sources":["../../src/components/automation-create-dialog.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n DEFAULT_AUTOMATION_SCHEDULE_DRAFT,\n AUTOMATION_WEEKDAYS,\n automationScheduleToCron,\n formatAutomationTime,\n type AutomationScheduleDraft,\n type AutomationSchedulePreset,\n type AutomationScheduleUnit,\n} from \"@agent-native/core/client/agent-page/automation-schedule\";\nimport {\n TimezoneSelect,\n browserTimezone,\n} from \"@agent-native/core/client/agent-page/timezone-select\";\nimport {\n useActionMutation,\n useActionQuery,\n} from \"@agent-native/core/client/hooks\";\nimport { useT } from \"@agent-native/core/client/i18n\";\nimport {\n IconBolt,\n IconCalendarEvent,\n IconClock,\n IconCode,\n IconLoader2,\n} from \"@tabler/icons-react\";\nimport { useEffect, useState } from \"react\";\n\nimport { Button } from \"./ui/button\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"./ui/dialog\";\nimport { Input } from \"./ui/input\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"./ui/select\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"./ui/tabs\";\nimport { Textarea } from \"./ui/textarea\";\n\ntype TriggerType = \"schedule\" | \"event\" | \"webhook\";\n\ninterface AutomationEvent {\n name: string;\n description?: string;\n}\n\ninterface CreateAutomationInput {\n operation: \"create\";\n name: string;\n scope: \"personal\" | \"organization\";\n triggerType: TriggerType;\n body: string;\n schedule?: string;\n timezone?: string;\n event?: string;\n}\n\ninterface CreateAutomationResult {\n created: true;\n name: string;\n triggerType: TriggerType;\n webhookPath: string | null;\n}\n\nconst PRESETS: {\n value: AutomationSchedulePreset;\n labelKey: string;\n detailKey: string;\n label: string;\n detail: string;\n draft: Partial<AutomationScheduleDraft>;\n}[] = [\n {\n value: \"hourly\",\n labelKey: \"jobs.schedulePreset.hourly\",\n detailKey: \"jobs.schedulePreset.hourlyDetail\",\n label: \"Every hour\",\n detail: \"At the top of the hour\",\n draft: { preset: \"hourly\" },\n },\n {\n value: \"daily-midnight\",\n labelKey: \"jobs.schedulePreset.dailyMidnight\",\n detailKey: \"jobs.schedulePreset.dailyMidnightDetail\",\n label: \"Every day at midnight\",\n detail: \"12:00 AM\",\n draft: { preset: \"daily-midnight\" },\n },\n {\n value: \"daily-noon\",\n labelKey: \"jobs.schedulePreset.dailyNoon\",\n detailKey: \"jobs.schedulePreset.dailyNoonDetail\",\n label: \"Every day at noon\",\n detail: \"12:00 PM\",\n draft: { preset: \"daily-noon\" },\n },\n {\n value: \"weekdays\",\n labelKey: \"jobs.schedulePreset.weekdays\",\n detailKey: \"jobs.schedulePreset.weekdaysDetail\",\n label: \"Every weekday\",\n detail: \"Monday to Friday at 9:00 AM\",\n draft: { preset: \"weekdays\", time: \"09:00\" },\n },\n {\n value: \"weekly\",\n labelKey: \"jobs.schedulePreset.weekly\",\n detailKey: \"jobs.schedulePreset.weeklyDetail\",\n label: \"Every week\",\n detail: \"Sunday at 9:00 AM\",\n draft: { preset: \"weekly\", weekday: 0, time: \"09:00\" },\n },\n];\n\nfunction presetClass(selected: boolean): string {\n return (\n \"rounded-lg border px-3 py-2 text-left transition-colors \" +\n (selected\n ? \"border-primary bg-primary/5 text-foreground\"\n : \"border-border/70 bg-background hover:bg-muted/50\")\n );\n}\n\nfunction scheduleIsValid(value: string): boolean {\n return value.trim().split(/\\s+/).length === 5;\n}\n\nfunction ScheduleFields({\n draft,\n saving,\n onChange,\n}: {\n draft: AutomationScheduleDraft;\n saving: boolean;\n onChange: (patch: Partial<AutomationScheduleDraft>) => void;\n}) {\n const t = useT();\n const customMinute = Number(draft.time.split(\":\")[1] ?? 0);\n return (\n <div className=\"space-y-3 rounded-lg border border-border/70 bg-muted/20 p-3\">\n <div className=\"grid gap-3 sm:grid-cols-[1fr_1fr]\">\n <label className=\"space-y-1.5 text-xs text-muted-foreground\">\n <span>{t(\"jobs.repeatEvery\", { defaultValue: \"Repeat every\" })}</span>\n <Input\n type=\"number\"\n min={1}\n max={31}\n value={draft.interval}\n disabled={saving}\n onChange={(event) =>\n onChange({\n interval: Math.min(\n draft.unit === \"day\" ? 1 : 31,\n Math.max(1, Number(event.target.value) || 1),\n ),\n })\n }\n className=\"h-9 bg-background text-sm\"\n />\n </label>\n <label className=\"space-y-1.5 text-xs text-muted-foreground\">\n <span>{t(\"jobs.scheduleUnit\", { defaultValue: \"Unit\" })}</span>\n <Select\n value={draft.unit}\n disabled={saving}\n onValueChange={(value) =>\n onChange({\n unit: value as AutomationScheduleUnit,\n ...(value === \"day\" ? { interval: 1 } : {}),\n })\n }\n >\n <SelectTrigger className=\"h-9 bg-background text-sm\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"hour\">\n {t(\"jobs.hours\", { defaultValue: \"hour(s)\" })}\n </SelectItem>\n <SelectItem value=\"day\">\n {t(\"jobs.day\", { defaultValue: \"day\" })}\n </SelectItem>\n <SelectItem value=\"week\">\n {t(\"jobs.weeks\", { defaultValue: \"week(s)\" })}\n </SelectItem>\n <SelectItem value=\"month\">\n {t(\"jobs.months\", { defaultValue: \"month(s)\" })}\n </SelectItem>\n </SelectContent>\n </Select>\n </label>\n </div>\n\n {draft.unit === \"hour\" ? (\n <label className=\"block space-y-1.5 text-xs text-muted-foreground\">\n <span>{t(\"jobs.atMinute\", { defaultValue: \"At minute\" })}</span>\n <Input\n type=\"number\"\n min={0}\n max={59}\n value={customMinute}\n disabled={saving}\n onChange={(event) =>\n onChange({\n time:\n \"00:\" +\n String(\n Math.min(59, Math.max(0, Number(event.target.value) || 0)),\n ).padStart(2, \"0\"),\n })\n }\n className=\"h-9 bg-background text-sm\"\n />\n </label>\n ) : null}\n\n {draft.unit === \"week\" ? (\n <label className=\"block space-y-1.5 text-xs text-muted-foreground\">\n <span>{t(\"jobs.onDay\", { defaultValue: \"On\" })}</span>\n <Select\n value={String(draft.weekday)}\n disabled={saving}\n onValueChange={(value) => onChange({ weekday: Number(value) })}\n >\n <SelectTrigger className=\"h-9 bg-background text-sm\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {AUTOMATION_WEEKDAYS.map((day) => (\n <SelectItem key={day.value} value={String(day.value)}>\n {day.short}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n ) : null}\n\n {draft.unit === \"month\" ? (\n <label className=\"block space-y-1.5 text-xs text-muted-foreground\">\n <span>{t(\"jobs.dayOfMonth\", { defaultValue: \"Day of month\" })}</span>\n <Input\n type=\"number\"\n min={1}\n max={31}\n value={draft.monthDay}\n disabled={saving}\n onChange={(event) =>\n onChange({\n monthDay: Math.min(\n 31,\n Math.max(1, Number(event.target.value) || 1),\n ),\n })\n }\n className=\"h-9 bg-background text-sm\"\n />\n </label>\n ) : null}\n\n {draft.unit !== \"hour\" ? (\n <label className=\"block space-y-1.5 text-xs text-muted-foreground\">\n <span>{t(\"jobs.atTime\", { defaultValue: \"At\" })}</span>\n <Input\n type=\"time\"\n value={draft.time}\n disabled={saving}\n onChange={(event) => onChange({ time: event.target.value })}\n className=\"h-9 bg-background text-sm\"\n />\n </label>\n ) : null}\n </div>\n );\n}\n\nfunction ScheduleBuilder({\n saving,\n timezone,\n onTimezoneChange,\n onChange,\n}: {\n saving: boolean;\n timezone: string;\n onTimezoneChange: (value: string) => void;\n onChange: (value: string) => void;\n}) {\n const t = useT();\n const [draft, setDraft] = useState<AutomationScheduleDraft>(\n DEFAULT_AUTOMATION_SCHEDULE_DRAFT,\n );\n const [advanced, setAdvanced] = useState(false);\n const [advancedValue, setAdvancedValue] = useState(\"0 * * * *\");\n const friendly = automationScheduleToCron(draft);\n const activeSchedule = advanced\n ? advancedValue.trim()\n : (friendly.schedule ?? \"\");\n const valid =\n scheduleIsValid(activeSchedule) && (!friendly.error || advanced);\n\n useEffect(() => {\n onChange(valid ? activeSchedule : \"\");\n }, [activeSchedule, onChange, valid]);\n\n function choosePreset(\n value: AutomationSchedulePreset,\n presetDraft: Partial<AutomationScheduleDraft>,\n ) {\n setDraft((current) => ({ ...current, ...presetDraft, preset: value }));\n setAdvanced(false);\n }\n\n return (\n <div className=\"space-y-3\">\n <div className=\"space-y-2\">\n <div className=\"text-xs font-medium text-foreground\">\n {t(\"jobs.repeat\", { defaultValue: \"Repeat\" })}\n </div>\n <div className=\"grid gap-2 sm:grid-cols-2\">\n {PRESETS.map((preset) => (\n <button\n key={preset.value}\n type=\"button\"\n aria-pressed={!advanced && draft.preset === preset.value}\n disabled={saving}\n className={presetClass(\n !advanced && draft.preset === preset.value,\n )}\n onClick={() => choosePreset(preset.value, preset.draft)}\n >\n <span className=\"block text-sm font-medium\">\n {t(preset.labelKey, { defaultValue: preset.label })}\n </span>\n <span className=\"mt-0.5 block text-xs text-muted-foreground\">\n {t(preset.detailKey, { defaultValue: preset.detail })}\n </span>\n </button>\n ))}\n <button\n type=\"button\"\n aria-pressed={!advanced && draft.preset === \"custom\"}\n disabled={saving}\n className={presetClass(!advanced && draft.preset === \"custom\")}\n onClick={() =>\n choosePreset(\"custom\", {\n preset: \"custom\",\n unit: draft.unit || \"day\",\n })\n }\n >\n <span className=\"block text-sm font-medium\">\n {t(\"jobs.schedulePreset.custom\", { defaultValue: \"Custom\" })}\n </span>\n <span className=\"mt-0.5 block text-xs text-muted-foreground\">\n {t(\"jobs.schedulePreset.customDetail\", {\n defaultValue: \"Set your own repeat pattern\",\n })}\n </span>\n </button>\n </div>\n </div>\n\n {!advanced && draft.preset === \"custom\" ? (\n <ScheduleFields\n draft={draft}\n saving={saving}\n onChange={(patch) =>\n setDraft((current) => ({ ...current, ...patch }))\n }\n />\n ) : null}\n\n {!advanced && friendly.schedule ? (\n <p className=\"text-xs text-muted-foreground\">\n {t(\"jobs.schedulePreview\", {\n defaultValue: \"Runs {{time}}.\",\n time:\n draft.preset === \"hourly\"\n ? t(\"jobs.schedulePreview.hourly\", {\n defaultValue: \"at the start of every hour\",\n })\n : draft.preset === \"daily-midnight\"\n ? t(\"jobs.schedulePreview.dailyMidnight\", {\n defaultValue: \"every day at midnight\",\n })\n : draft.preset === \"daily-noon\"\n ? t(\"jobs.schedulePreview.dailyNoon\", {\n defaultValue: \"every day at noon\",\n })\n : t(\"jobs.schedulePreview.atTime\", {\n defaultValue: \"at {{time}}\",\n time: formatAutomationTime(draft.time),\n }),\n })}\n </p>\n ) : null}\n {!advanced && friendly.error ? (\n <p className=\"text-xs text-destructive\">\n {friendly.error === \"daily-interval\"\n ? t(\"jobs.dailyIntervalAdvanced\", {\n defaultValue:\n \"Every few days needs the Advanced cron editor below.\",\n })\n : t(\"jobs.weeklyIntervalAdvanced\", {\n defaultValue:\n \"Custom weekly intervals beyond one week are available under Advanced.\",\n })}\n </p>\n ) : null}\n\n <div>\n <label className=\"text-xs font-medium text-foreground\">\n {t(\"jobs.timezone\", { defaultValue: \"Timezone\" })}\n </label>\n <div className=\"mt-1\">\n <TimezoneSelect\n value={timezone}\n disabled={saving}\n onChange={onTimezoneChange}\n suggested={[browserTimezone()]}\n />\n </div>\n </div>\n\n <details\n className=\"group rounded-lg border border-border/70 bg-muted/20\"\n open={advanced}\n onToggle={(event) => {\n const next = event.currentTarget.open;\n if (next && !advanced && friendly.schedule) {\n setAdvancedValue(friendly.schedule);\n }\n setAdvanced(next);\n }}\n >\n <summary className=\"flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 text-xs font-medium text-foreground [&::-webkit-details-marker]:hidden\">\n <IconCode className=\"size-4 text-muted-foreground\" />\n {t(\"jobs.advancedSchedule\", {\n defaultValue: \"Advanced - cron expression\",\n })}\n <span className=\"ms-auto text-muted-foreground group-open:hidden\">\n {t(\"jobs.show\", { defaultValue: \"Show\" })}\n </span>\n <span className=\"ms-auto hidden text-muted-foreground group-open:inline\">\n {t(\"jobs.hide\", { defaultValue: \"Hide\" })}\n </span>\n </summary>\n <div className=\"border-t border-border/60 px-3 pb-3 pt-2.5\">\n <Input\n className=\"font-mono text-sm\"\n value={advancedValue}\n spellCheck={false}\n autoComplete=\"off\"\n disabled={saving}\n onFocus={() => setAdvanced(true)}\n onChange={(event) => {\n setAdvanced(true);\n setAdvancedValue(event.target.value);\n }}\n />\n <p className=\"mt-1 text-[11px] text-muted-foreground\">\n {t(\"jobs.cronFormatHint\", {\n defaultValue: \"minute hour day-of-month month day-of-week\",\n })}\n </p>\n </div>\n </details>\n\n {activeSchedule && !valid ? (\n <p className=\"text-xs text-destructive\">\n {t(\"jobs.cronFieldCount\", {\n defaultValue: \"A cron expression needs exactly 5 fields.\",\n })}\n </p>\n ) : null}\n </div>\n );\n}\n\nfunction EventFields({\n open,\n event,\n onChange,\n}: {\n open: boolean;\n event: string;\n onChange: (value: string) => void;\n}) {\n const t = useT();\n const eventsQuery = useActionQuery<AutomationEvent[]>(\n \"list-automation-events\",\n {},\n { enabled: open, staleTime: 60_000 },\n );\n\n return (\n <>\n <label className=\"space-y-1.5 text-xs text-muted-foreground\">\n <span>{t(\"jobs.startWhen\", { defaultValue: \"Start when\" })}</span>\n <Select value={event} onValueChange={onChange}>\n <SelectTrigger>\n <SelectValue\n placeholder={\n eventsQuery.isLoading\n ? t(\"jobs.loadingEventTypes\", {\n defaultValue: \"Loading event types...\",\n })\n : t(\"jobs.chooseAppEvent\", {\n defaultValue: \"Choose an app event\",\n })\n }\n />\n </SelectTrigger>\n <SelectContent>\n {(eventsQuery.data ?? []).map((item) => (\n <SelectItem key={item.name} value={item.name}>\n {item.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n {eventsQuery.data?.find((item) => item.name === event)?.description ? (\n <p className=\"mt-2 text-xs text-muted-foreground\">\n {eventsQuery.data.find((item) => item.name === event)?.description}\n </p>\n ) : null}\n {eventsQuery.error ? (\n <p className=\"mt-2 text-xs text-destructive\">\n {t(\"jobs.loadEventTypesError\", {\n defaultValue: \"Could not load event types.\",\n })}\n </p>\n ) : null}\n </>\n );\n}\n\nexport interface AutomationCreateDialogProps {\n open: boolean;\n initialScope?: \"personal\" | \"organization\";\n onOpenChange: (open: boolean) => void;\n onCreated: (result: CreateAutomationResult) => void;\n}\n\nexport function AutomationCreateDialog({\n open,\n initialScope = \"personal\",\n onOpenChange,\n onCreated,\n}: AutomationCreateDialogProps) {\n const t = useT();\n const [name, setName] = useState(\"\");\n const [scope, setScope] = useState<\"personal\" | \"organization\">(initialScope);\n const [triggerType, setTriggerType] = useState<TriggerType>(\"schedule\");\n const [schedule, setSchedule] = useState(\"0 * * * *\");\n const [timezone, setTimezone] = useState(browserTimezone());\n const [event, setEvent] = useState(\"\");\n const [body, setBody] = useState(\"\");\n const create = useActionMutation<\n CreateAutomationResult,\n CreateAutomationInput\n >(\"manage-automation\", {\n onSuccess: onCreated,\n });\n\n const resetCreate = create.reset;\n\n useEffect(() => {\n if (!open) return;\n setName(\"\");\n setScope(initialScope);\n setTriggerType(\"schedule\");\n setSchedule(\"0 * * * *\");\n setTimezone(browserTimezone());\n setEvent(\"\");\n setBody(\"\");\n resetCreate();\n }, [initialScope, open, resetCreate]);\n\n const canSubmit =\n name.trim().length > 0 &&\n body.trim().length > 0 &&\n (triggerType !== \"event\" || event.length > 0) &&\n (triggerType !== \"schedule\" || scheduleIsValid(schedule));\n\n function submit() {\n if (!canSubmit) return;\n create.mutate({\n operation: \"create\",\n name: name.trim(),\n scope,\n triggerType,\n body: body.trim(),\n ...(triggerType === \"schedule\"\n ? { schedule, timezone }\n : triggerType === \"event\"\n ? { event }\n : {}),\n });\n }\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-h-[90vh] overflow-y-auto sm:max-w-2xl\">\n <DialogHeader>\n <DialogTitle>\n {t(\"jobs.newAutomation\", { defaultValue: \"New automation\" })}\n </DialogTitle>\n <DialogDescription>\n {t(\"jobs.createAutomationDescription\", {\n defaultValue:\n \"Choose what starts it, then describe the work in plain language.\",\n })}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4\">\n <div className=\"grid gap-3 sm:grid-cols-[1fr_12rem]\">\n <label className=\"space-y-1.5 text-xs text-muted-foreground\">\n <span>{t(\"jobs.name\", { defaultValue: \"Name\" })}</span>\n <Input\n value={name}\n disabled={create.isPending}\n placeholder={t(\"jobs.automationNamePlaceholder\", {\n defaultValue: \"morning-digest\",\n })}\n onChange={(event) => setName(event.target.value)}\n />\n </label>\n <label className=\"space-y-1.5 text-xs text-muted-foreground\">\n <span>{t(\"jobs.scope\", { defaultValue: \"Scope\" })}</span>\n <Select\n value={scope}\n disabled={create.isPending}\n onValueChange={(value) =>\n setScope(value as \"personal\" | \"organization\")\n }\n >\n <SelectTrigger>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"personal\">\n {t(\"jobs.personal\", { defaultValue: \"Personal\" })}\n </SelectItem>\n <SelectItem value=\"organization\">\n {t(\"jobs.organization\", { defaultValue: \"Organization\" })}\n </SelectItem>\n </SelectContent>\n </Select>\n </label>\n </div>\n\n <Tabs\n value={triggerType}\n onValueChange={(value) => setTriggerType(value as TriggerType)}\n >\n <TabsList className=\"grid h-auto w-full grid-cols-3\">\n <TabsTrigger value=\"schedule\" className=\"gap-2 py-2 text-xs\">\n <IconClock className=\"size-3.5\" />\n {t(\"jobs.schedule\", { defaultValue: \"Schedule\" })}\n </TabsTrigger>\n <TabsTrigger value=\"webhook\" className=\"gap-2 py-2 text-xs\">\n <IconBolt className=\"size-3.5\" />\n {t(\"jobs.webhook\", { defaultValue: \"Webhook\" })}\n </TabsTrigger>\n <TabsTrigger value=\"event\" className=\"gap-2 py-2 text-xs\">\n <IconCalendarEvent className=\"size-3.5\" />\n {t(\"jobs.appEvent\", { defaultValue: \"App event\" })}\n </TabsTrigger>\n </TabsList>\n <TabsContent value=\"schedule\" className=\"mt-3\">\n <ScheduleBuilder\n key={open ? \"schedule-open\" : \"schedule-closed\"}\n saving={create.isPending}\n timezone={timezone}\n onTimezoneChange={setTimezone}\n onChange={setSchedule}\n />\n </TabsContent>\n <TabsContent value=\"webhook\" className=\"mt-3\">\n <div className=\"rounded-lg border border-primary/20 bg-primary/5 p-3\">\n <div className=\"flex items-start gap-2\">\n <IconBolt className=\"mt-0.5 size-4 shrink-0 text-primary\" />\n <div>\n <p className=\"text-sm font-medium text-foreground\">\n {t(\"jobs.webhookTriggerDescription\", {\n defaultValue: \"Run when a service sends an HTTP POST\",\n })}\n </p>\n <p className=\"mt-1 text-xs leading-5 text-muted-foreground\">\n {t(\"jobs.webhookSetupDescription\", {\n defaultValue:\n \"After you save, Dispatch gives you a private URL to paste into GitHub, Stripe, or another webhook provider.\",\n })}\n </p>\n </div>\n </div>\n </div>\n </TabsContent>\n <TabsContent value=\"event\" className=\"mt-3\">\n <EventFields open={open} event={event} onChange={setEvent} />\n </TabsContent>\n </Tabs>\n\n <label className=\"space-y-1.5 text-xs text-muted-foreground\">\n <span>\n {t(\"jobs.whatShouldItDo\", { defaultValue: \"What should it do?\" })}\n </span>\n <Textarea\n value={body}\n disabled={create.isPending}\n placeholder={t(\"jobs.automationBodyPlaceholder\", {\n defaultValue:\n \"Review new support requests and summarize anything urgent in a shared note.\",\n })}\n rows={5}\n onChange={(event) => setBody(event.target.value)}\n />\n </label>\n\n {create.error ? (\n <p className=\"text-xs text-destructive\">{create.error.message}</p>\n ) : null}\n </div>\n\n <DialogFooter>\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled={create.isPending}\n onClick={() => onOpenChange(false)}\n >\n {t(\"jobs.cancel\", { defaultValue: \"Cancel\" })}\n </Button>\n <Button\n type=\"button\"\n disabled={!canSubmit || create.isPending}\n onClick={submit}\n >\n {create.isPending ? (\n <IconLoader2 className=\"size-4 animate-spin\" />\n ) : null}\n {t(\"jobs.createAutomation\", { defaultValue: \"Create automation\" })}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"automation-details-panel.d.ts","sourceRoot":"","sources":["../../src/components/automation-details-panel.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"automation-details-panel.d.ts","sourceRoot":"","sources":["../../src/components/automation-details-panel.tsx"],"names":[],"mappings":"AAqBA,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,oBAAoB,CAAC;AAgB5B,MAAM,WAAW,2BAA2B;IAC1C,UAAU,EAAE,sBAAsB,CAAC;IACnC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAkED,wBAAgB,sBAAsB,CAAC,EACrC,UAAU,EACV,UAAkB,EAClB,QAAQ,GACT,EAAE,2BAA2B,mDAqX7B"}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { appPath } from "@agent-native/core/client/api-path";
|
|
3
|
+
import { writeClipboardText } from "@agent-native/core/client/clipboard";
|
|
2
4
|
import { useActionQuery } from "@agent-native/core/client/hooks";
|
|
3
|
-
import {
|
|
5
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
6
|
+
import { IconAlertTriangle, IconCheck, IconCopy, IconClock, IconExternalLink, IconPlayerPause, IconPlayerPlay, } from "@tabler/icons-react";
|
|
7
|
+
import { useState } from "react";
|
|
4
8
|
import { Link } from "react-router";
|
|
5
9
|
import { automationScopeLabel, automationStatus, automationTarget, } from "../lib/automation-display.js";
|
|
6
10
|
import { automationRunScope, } from "../lib/automations.js";
|
|
@@ -50,6 +54,7 @@ function runAsLabel(value) {
|
|
|
50
54
|
return "Default identity";
|
|
51
55
|
}
|
|
52
56
|
export function AutomationDetailsPanel({ automation, isToggling = false, onToggle, }) {
|
|
57
|
+
const t = useT();
|
|
53
58
|
const runsQuery = useActionQuery("list-automation-runs", {
|
|
54
59
|
name: automation.name,
|
|
55
60
|
scope: automationRunScope(automation),
|
|
@@ -57,7 +62,16 @@ export function AutomationDetailsPanel({ automation, isToggling = false, onToggl
|
|
|
57
62
|
}, { staleTime: 5_000 });
|
|
58
63
|
const runs = runsQuery.data ?? [];
|
|
59
64
|
const status = automationStatus(automation);
|
|
60
|
-
const isScheduled = automation.triggerType
|
|
65
|
+
const isScheduled = automation.triggerType === "schedule";
|
|
66
|
+
const [copied, setCopied] = useState(false);
|
|
67
|
+
const webhookUrl = automation.webhookPath && typeof window !== "undefined"
|
|
68
|
+
? window.location.origin + appPath(automation.webhookPath)
|
|
69
|
+
: null;
|
|
70
|
+
const triggerLabel = automation.triggerType === "webhook"
|
|
71
|
+
? t("jobs.webhook", { defaultValue: "Webhook" })
|
|
72
|
+
: isScheduled
|
|
73
|
+
? "Schedule"
|
|
74
|
+
: "Event";
|
|
61
75
|
const prompt = automation.body?.trim();
|
|
62
76
|
const hasExecutionTarget = Boolean(automation.executionHostId ||
|
|
63
77
|
automation.executionEngine ||
|
|
@@ -69,9 +83,26 @@ export function AutomationDetailsPanel({ automation, isToggling = false, onToggl
|
|
|
69
83
|
? "size-2 shrink-0 rounded-full bg-destructive"
|
|
70
84
|
: status.tone === "default"
|
|
71
85
|
? "size-2 shrink-0 rounded-full bg-primary"
|
|
72
|
-
: "size-2 shrink-0 rounded-full bg-muted-foreground/40" }), _jsx(Badge, { variant: status.tone === "danger" ? "destructive" : "outline", className: "h-5", children: status.label })] }), _jsx("h2", { className: "mt-2 break-words text-base font-semibold text-foreground", children: automation.name }), _jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: [
|
|
86
|
+
: "size-2 shrink-0 rounded-full bg-muted-foreground/40" }), _jsx(Badge, { variant: status.tone === "danger" ? "destructive" : "outline", className: "h-5", children: status.label })] }), _jsx("h2", { className: "mt-2 break-words text-base font-semibold text-foreground", children: automation.name }), _jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: [triggerLabel, " \u00B7 ", automationScopeLabel(automation)] })] }) }), _jsxs("div", { className: "min-h-0 flex-1 space-y-5 overflow-y-auto p-5", children: [_jsxs("section", { children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Prompt" }), prompt ? (_jsx("pre", { className: "mt-2 max-h-64 overflow-auto whitespace-pre-wrap break-words rounded-md bg-muted/40 p-3 font-sans text-sm leading-6 text-foreground", children: prompt })) : (_jsx("p", { className: "mt-2 rounded-md border border-dashed p-3 text-sm text-muted-foreground", children: "No prompt recorded." }))] }), _jsxs("section", { className: "border-t pt-5", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Configuration" }), _jsxs("dl", { className: "mt-3 grid grid-cols-2 gap-x-4 gap-y-4", children: [_jsx(DetailField, { label: "Trigger", value: automation.triggerType === "webhook"
|
|
87
|
+
? t("jobs.webhookTrigger", {
|
|
88
|
+
defaultValue: "Webhook-triggered",
|
|
89
|
+
})
|
|
90
|
+
: isScheduled
|
|
91
|
+
? "Scheduled"
|
|
92
|
+
: "Event-triggered" }), _jsx(DetailField, { label: isScheduled ? "Schedule" : "Event", value: automationTarget(automation) }), isScheduled ? (_jsxs(_Fragment, { children: [automation.schedule ? (_jsx(DetailField, { label: "Cron expression", value: automation.schedule, mono: true })) : null, _jsx(DetailField, { label: "Timezone", value: automation.timezone || "Account default" })] })) : null, webhookUrl ? (_jsxs("div", { className: "col-span-2 rounded-md border border-primary/20 bg-primary/5 p-3", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("span", { className: "text-xs font-medium text-foreground", children: t("jobs.webhookUrl", { defaultValue: "Webhook URL" }) }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "h-7 shrink-0 gap-1.5 text-xs", onClick: () => {
|
|
93
|
+
void writeClipboardText(webhookUrl).then((didCopy) => {
|
|
94
|
+
setCopied(didCopy);
|
|
95
|
+
if (didCopy) {
|
|
96
|
+
window.setTimeout(() => setCopied(false), 1800);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}, children: [copied ? (_jsx(IconCheck, { className: "size-3.5" })) : (_jsx(IconCopy, { className: "size-3.5" })), copied
|
|
100
|
+
? t("common.copied", { defaultValue: "Copied" })
|
|
101
|
+
: t("common.copy", { defaultValue: "Copy URL" })] })] }), _jsx("code", { className: "mt-2 block break-all text-[11px] leading-5 text-muted-foreground", children: webhookUrl }), _jsx("p", { className: "mt-2 text-xs text-muted-foreground", children: t("jobs.webhookUrlHint", {
|
|
102
|
+
defaultValue: "Paste this URL into a service that sends HTTP POST webhooks.",
|
|
103
|
+
}) })] })) : null, _jsx(DetailField, { label: "Condition", value: automation.condition || "Always" }), _jsx(DetailField, { label: "Domain", value: automation.domain || "Default" }), _jsx(DetailField, { label: "Model", value: automation.model || "Default model" }), _jsx(DetailField, { label: "Mode", value: automation.mode || "Agentic" }), _jsx(DetailField, { label: "Runs as", value: runAsLabel(automation.runAs) }), isScheduled ? (_jsx(DetailField, { label: "Next run", value: automation.enabled
|
|
73
104
|
? formatTimestamp(automation.nextRun)
|
|
74
|
-
: "Paused" }), _jsx(DetailField, { label: "Last run", value: formatTimestamp(automation.lastRun) }), _jsx(DetailField, { label: "Last checked", value: formatTimestamp(automation.lastCheck) }), _jsx(DetailField, { label: "Created by", value: automation.createdBy || "Unknown" })] })] }), _jsxs("section", { className: "border-t pt-5", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Capabilities" }), automation.mcpTools && automation.mcpTools.length > 0 ? (_jsx("div", { className: "mt-2 flex flex-wrap gap-1.5", children: automation.mcpTools.map((toolName) => (_jsx("code", { className: "max-w-full break-all rounded bg-muted px-1.5 py-1 text-[11px] text-muted-foreground", children: toolName }, toolName))) })) : (_jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "No explicit tool allowlist." }))] }), hasExecutionTarget ? (_jsxs("section", { className: "border-t pt-5", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Execution target" }), _jsxs("dl", { className: "mt-3 grid grid-cols-2 gap-x-4 gap-y-4", children: [_jsx(DetailField, { label: "Host", value: automation.executionHostId || "Server default", mono: Boolean(automation.executionHostId) }), _jsx(DetailField, { label: "Engine", value: automation.executionEngine || "Default" }), automation.executionCwd ? (_jsx(DetailField, { label: "Working directory", value: automation.executionCwd, mono: true })) : null] })] })) : null, hasDelivery ? (_jsxs("section", { className: "border-t pt-5", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Delivery" }), _jsxs("dl", { className: "mt-3 grid grid-cols-2 gap-x-4 gap-y-4", children: [automation.deliveryPlatform ? (_jsx(DetailField, { label: "Platform", value: automation.deliveryPlatform })) : null, automation.deliveryDestination ? (_jsx(DetailField, { label: "Destination", value: automation.deliveryDestination })) : null, automation.deliveryThreadRef ? (_jsx(DetailField, { label: "Thread reference", value: automation.deliveryThreadRef, mono: true })) : null] })] })) : null, automation.lastError ? (_jsxs("section", { className: "flex items-start gap-2 border-t border-destructive/30 pt-5", children: [_jsx(IconAlertTriangle, { className: "mt-0.5 size-4 shrink-0 text-destructive" }), _jsxs("div", { className: "min-w-0", children: [_jsx("h3", { className: "text-sm font-semibold text-destructive", children: "Latest scheduler result" }), _jsx("p", { className: "mt-1 break-words text-sm text-muted-foreground", children: automation.lastError })] })] })) : null, _jsxs("section", { className: "border-t pt-5", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Past runs" }), runs.length > 0 ? (_jsxs(Badge, { variant: "outline", children: [runs.length, " shown"] })) : null] }), _jsx("div", { className: "mt-3", children: runsQuery.isLoading ? (_jsx("div", { className: "space-y-3 rounded-md border p-3", children: Array.from({ length: 3 }).map((_, index) => (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-4 w-2/3" }), _jsx(Skeleton, { className: "h-3 w-1/2" })] }, index))) })) : runsQuery.isError ? (_jsx(ActionQueryError, { error: runsQuery.error, onRetry: () => void runsQuery.refetch() })) : runs.length === 0 ? (_jsx("p", { className: "rounded-md border border-dashed p-3 text-sm text-muted-foreground", children: "No execution recorded yet." })) : (_jsx("ul", { className: "divide-y rounded-md border", children: runs.map((run) => {
|
|
105
|
+
: "Paused" })) : null, _jsx(DetailField, { label: "Last run", value: formatTimestamp(automation.lastRun) }), _jsx(DetailField, { label: "Last checked", value: formatTimestamp(automation.lastCheck) }), _jsx(DetailField, { label: "Created by", value: automation.createdBy || "Unknown" })] })] }), _jsxs("section", { className: "border-t pt-5", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Capabilities" }), automation.mcpTools && automation.mcpTools.length > 0 ? (_jsx("div", { className: "mt-2 flex flex-wrap gap-1.5", children: automation.mcpTools.map((toolName) => (_jsx("code", { className: "max-w-full break-all rounded bg-muted px-1.5 py-1 text-[11px] text-muted-foreground", children: toolName }, toolName))) })) : (_jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: "No explicit tool allowlist." }))] }), hasExecutionTarget ? (_jsxs("section", { className: "border-t pt-5", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Execution target" }), _jsxs("dl", { className: "mt-3 grid grid-cols-2 gap-x-4 gap-y-4", children: [_jsx(DetailField, { label: "Host", value: automation.executionHostId || "Server default", mono: Boolean(automation.executionHostId) }), _jsx(DetailField, { label: "Engine", value: automation.executionEngine || "Default" }), automation.executionCwd ? (_jsx(DetailField, { label: "Working directory", value: automation.executionCwd, mono: true })) : null] })] })) : null, hasDelivery ? (_jsxs("section", { className: "border-t pt-5", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Delivery" }), _jsxs("dl", { className: "mt-3 grid grid-cols-2 gap-x-4 gap-y-4", children: [automation.deliveryPlatform ? (_jsx(DetailField, { label: "Platform", value: automation.deliveryPlatform })) : null, automation.deliveryDestination ? (_jsx(DetailField, { label: "Destination", value: automation.deliveryDestination })) : null, automation.deliveryThreadRef ? (_jsx(DetailField, { label: "Thread reference", value: automation.deliveryThreadRef, mono: true })) : null] })] })) : null, automation.lastError ? (_jsxs("section", { className: "flex items-start gap-2 border-t border-destructive/30 pt-5", children: [_jsx(IconAlertTriangle, { className: "mt-0.5 size-4 shrink-0 text-destructive" }), _jsxs("div", { className: "min-w-0", children: [_jsx("h3", { className: "text-sm font-semibold text-destructive", children: "Latest scheduler result" }), _jsx("p", { className: "mt-1 break-words text-sm text-muted-foreground", children: automation.lastError })] })] })) : null, _jsxs("section", { className: "border-t pt-5", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsx("h3", { className: "text-sm font-semibold text-foreground", children: "Past runs" }), runs.length > 0 ? (_jsxs(Badge, { variant: "outline", children: [runs.length, " shown"] })) : null] }), _jsx("div", { className: "mt-3", children: runsQuery.isLoading ? (_jsx("div", { className: "space-y-3 rounded-md border p-3", children: Array.from({ length: 3 }).map((_, index) => (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-4 w-2/3" }), _jsx(Skeleton, { className: "h-3 w-1/2" })] }, index))) })) : runsQuery.isError ? (_jsx(ActionQueryError, { error: runsQuery.error, onRetry: () => void runsQuery.refetch() })) : runs.length === 0 ? (_jsx("p", { className: "rounded-md border border-dashed p-3 text-sm text-muted-foreground", children: "No execution recorded yet." })) : (_jsx("ul", { className: "divide-y rounded-md border", children: runs.map((run) => {
|
|
75
106
|
const debugPath = runDebugPath(run);
|
|
76
107
|
const duration = formatDuration(run);
|
|
77
108
|
return (_jsxs("li", { className: "space-y-2 p-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2 text-sm", children: [_jsx(Badge, { variant: runStatusVariant(run.status), children: run.status }), _jsx("span", { className: "text-muted-foreground", children: formatTimestamp(run.startedAt) }), duration ? (_jsxs("span", { className: "inline-flex items-center gap-1 text-xs text-muted-foreground", children: [_jsx(IconClock, { className: "size-3.5" }), duration] })) : null, debugPath ? (_jsxs(Link, { to: debugPath, className: "ml-auto inline-flex items-center gap-1 text-xs font-medium text-foreground underline-offset-4 hover:underline", children: [_jsx(IconExternalLink, { className: "size-3.5" }), "Open debug"] })) : null] }), run.error ? (_jsx("p", { className: "break-words text-sm text-destructive", children: run.error })) : null] }, run.id));
|