-
Enabled
+
+ Enabled
setDraft((current) => ({ ...current, enabled }))}>
@@ -166,29 +177,18 @@ function TriggerEditor({
{trigger.kind === "schedule" && (
- <>
-
- Cron
- setDraft((current) => ({ ...current, cronExpression: event.target.value }))}
- placeholder="0 10 * * *"
- />
-
-
- Timezone
- setDraft((current) => ({ ...current, timezone: event.target.value }))}
- placeholder="America/Chicago"
- />
-
- >
+
+ Schedule
+ setDraft((current) => ({ ...current, cronExpression }))}
+ />
+
)}
{trigger.kind === "webhook" && (
<>
-
-
Signing mode
+
+ Signing mode
setDraft((current) => ({ ...current, signingMode }))}
@@ -203,8 +203,8 @@ function TriggerEditor({
-
-
Replay window seconds
+
+ Replay window (seconds)
setDraft((current) => ({ ...current, replayWindowSec: event.target.value }))}
@@ -212,38 +212,40 @@ function TriggerEditor({
>
)}
-
-
- onSave(trigger.id, {
- label: draft.label.trim() || null,
- enabled: draft.enabled === "true",
- ...(trigger.kind === "schedule"
- ? { cronExpression: draft.cronExpression.trim(), timezone: draft.timezone.trim() }
- : {}),
- ...(trigger.kind === "webhook"
- ? {
- signingMode: draft.signingMode,
- replayWindowSec: Number(draft.replayWindowSec || "300"),
- }
- : {}),
- })
- }
- >
-
- Save trigger
+
+
+
+
+ onSave(trigger.id, {
+ label: draft.label.trim() || null,
+ enabled: draft.enabled === "true",
+ ...(trigger.kind === "schedule"
+ ? { cronExpression: draft.cronExpression.trim(), timezone: getLocalTimezone() }
+ : {}),
+ ...(trigger.kind === "webhook"
+ ? {
+ signingMode: draft.signingMode,
+ replayWindowSec: Number(draft.replayWindowSec || "300"),
+ }
+ : {}),
+ })
+ }
+ >
+
+ Save
+
+ {trigger.kind === "webhook" && (
+ onRotate(trigger.id)}>
+
+ Rotate secret
- {trigger.kind === "webhook" && (
- onRotate(trigger.id)}>
-
- Rotate secret
-
- )}
- {trigger.lastResult && Last result: {trigger.lastResult} }
-
-
-
+ )}
+ {trigger.lastResult &&
Last: {trigger.lastResult} }
+
+
);
}
@@ -266,7 +268,6 @@ export function RoutineDetail() {
kind: "schedule",
label: "",
cronExpression: "0 10 * * *",
- timezone: "UTC",
signingMode: "bearer",
replayWindowSec: "300",
});
@@ -447,13 +448,30 @@ export function RoutineDetail() {
},
});
+ const updateRoutineStatus = useMutation({
+ mutationFn: (status: string) => routinesApi.update(routineId!, { status }),
+ onSuccess: async () => {
+ await Promise.all([
+ queryClient.invalidateQueries({ queryKey: queryKeys.routines.detail(routineId!) }),
+ queryClient.invalidateQueries({ queryKey: queryKeys.routines.list(selectedCompanyId!) }),
+ ]);
+ },
+ onError: (error) => {
+ pushToast({
+ title: "Failed to update routine",
+ body: error instanceof Error ? error.message : "Paperclip could not update the routine.",
+ tone: "error",
+ });
+ },
+ });
+
const createTrigger = useMutation({
mutationFn: async (): Promise
=>
routinesApi.createTrigger(routineId!, {
kind: newTrigger.kind,
label: newTrigger.label.trim() || null,
...(newTrigger.kind === "schedule"
- ? { cronExpression: newTrigger.cronExpression.trim(), timezone: newTrigger.timezone.trim() }
+ ? { cronExpression: newTrigger.cronExpression.trim(), timezone: getLocalTimezone() }
: {}),
...(newTrigger.kind === "webhook"
? {
@@ -568,330 +586,318 @@ export function RoutineDetail() {
if (error || !routine) {
return (
-
-
- {error instanceof Error ? error.message : "Routine not found"}
-
-
+
+ {error instanceof Error ? error.message : "Routine not found"}
+
);
}
return (
-
+
+ {/* Header: status + actions */}
+
+
+ {routine.status.replaceAll("_", " ")}
+
+ {routine.activeIssue && (
+
+ {routine.activeIssue.identifier ?? routine.activeIssue.id.slice(0, 8)}
+
+ )}
+
+
runRoutine.mutate()} disabled={runRoutine.isPending} />
+ updateRoutineStatus.mutate("paused")}
+ onResume={() => updateRoutineStatus.mutate("active")}
+ disabled={updateRoutineStatus.isPending || routine.status === "archived"}
+ />
+
+
+
+ {/* Secret message banner */}
{secretMessage && (
-
-
- {secretMessage.title}
-
- Save this now. Paperclip will not show the secret value again.
-
-
-
-
-
Webhook URL
-
-
- copySecretValue("Webhook URL", secretMessage.webhookUrl)}>
-
- Copy URL
-
-
+
+
+
{secretMessage.title}
+
Save this now. Paperclip will not show the secret value again.
+
+
+
+
+ copySecretValue("Webhook URL", secretMessage.webhookUrl)}>
+
+ URL
+
-
-
Secret
-
-
- copySecretValue("Webhook secret", secretMessage.webhookSecret)}>
-
- Copy secret
-
-
+
+
+ copySecretValue("Webhook secret", secretMessage.webhookSecret)}>
+
+ Secret
+
-
-
+
+
)}
-
-
-
-
Routine definition
-
- Keep the work definition primary. Triggers, runs, and audit history branch off this source object.
-
-
-
-
- {routine.status.replaceAll("_", " ")}
-
-
runRoutine.mutate()} disabled={runRoutine.isPending}>
-
- Run now
-
-
-
-
-