diff --git a/resources/js/components/automations/TestRunPanel.vue b/resources/js/components/automations/TestRunPanel.vue index 0f797768..5dbaca7b 100644 --- a/resources/js/components/automations/TestRunPanel.vue +++ b/resources/js/components/automations/TestRunPanel.vue @@ -12,12 +12,15 @@ import { Checkbox } from '@/components/ui/checkbox'; import { Label } from '@/components/ui/label'; import { useAutomationEcho } from '@/composables/echo/useAutomationEcho'; import { test as testAutomation } from '@/routes/app/automations'; +import { NodeRunStatus, type NodeRunStatusValue } from '@/types/automation/node-run-status'; +import { NodeType, type NodeTypeValue } from '@/types/automation/node-type'; +import { RunStatus, type RunStatusValue } from '@/types/automation/run-status'; interface NodeRun { id: string; node_id: string; - node_type: string; - status: string; + node_type: NodeTypeValue; + status: NodeRunStatusValue; input: Record | null; output: Record | null; error: { message?: string } | null; @@ -27,7 +30,7 @@ interface NodeRun { interface Run { id: string; - status: string; + status: RunStatusValue; context: Record | null; error: { message?: string } | null; started_at: string | null; @@ -98,29 +101,35 @@ const runTest = async () => { await start(); }; -const statusLabel = (status: string): string => { - const map: Record = { - running: trans('automations.test.in_progress'), - completed: trans('automations.test.completed'), - failed: trans('automations.test.failed'), - waiting: trans('automations.test.waiting'), +const statusLabel = (status: NodeRunStatusValue): string => { + const map: Partial> = { + [NodeRunStatus.Running]: trans('automations.test.in_progress'), + [NodeRunStatus.Completed]: trans('automations.test.completed'), + [NodeRunStatus.Failed]: trans('automations.test.failed'), }; return map[status] ?? status; }; -const nodeStatusIcon = (status: string) => { - if (status === 'completed') return IconCircleCheck; - if (status === 'failed') return IconAlertCircle; - if (status === 'running') return IconLoader2; +const nodeStatusIcon = (status: NodeRunStatusValue) => { + if (status === NodeRunStatus.Completed) return IconCircleCheck; + if (status === NodeRunStatus.Failed) return IconAlertCircle; + if (status === NodeRunStatus.Running) return IconLoader2; return IconCircleDot; }; +const nodeStatusTile = (status: NodeRunStatusValue): string => { + if (status === NodeRunStatus.Completed) return 'bg-emerald-200 text-emerald-900'; + if (status === NodeRunStatus.Failed) return 'bg-rose-200 text-rose-900'; + if (status === NodeRunStatus.Running) return 'bg-amber-200 text-amber-900'; + return 'bg-zinc-200 text-zinc-900'; +}; + // Fetch nodes (RSS / HTTP) short-circuit via the `no_items` handle with an // output of `{ fetch: { count: 0 } }` when nothing new arrived. Surface that // as an explicit note instead of an uninformative empty JSON blob. const isZeroFetchResult = (nodeRun: NodeRun): boolean => { - if (nodeRun.status !== 'completed') return false; - if (nodeRun.node_type !== 'fetch_rss' && nodeRun.node_type !== 'http_request') return false; + if (nodeRun.status !== NodeRunStatus.Completed) return false; + if (nodeRun.node_type !== NodeType.FetchRss && nodeRun.node_type !== NodeType.HttpRequest) return false; const fetch = nodeRun.output?.fetch as { count?: number } | undefined; return fetch?.count === 0; }; @@ -170,7 +179,7 @@ const isZeroFetchResult = (nodeRun: NodeRun): boolean => {
@@ -190,18 +199,12 @@ const isZeroFetchResult = (nodeRun: NodeRun): boolean => { >
- +
-

{{ nodeRun.node_type.replace('_', ' ') }}

+

{{ $t(`automations.node_type.${nodeRun.node_type}`) }}

{{ statusLabel(nodeRun.status) }}

diff --git a/resources/js/pages/automations/Invocations.vue b/resources/js/pages/automations/Invocations.vue index 6d490373..151fd6b8 100644 --- a/resources/js/pages/automations/Invocations.vue +++ b/resources/js/pages/automations/Invocations.vue @@ -35,10 +35,13 @@ import { } from '@/components/ui/tooltip'; import date from '@/date'; import type { Automation } from '@/types/automation/automation'; +import type { NodeRunStatusValue } from '@/types/automation/node-run-status'; +import type { NodeTypeValue } from '@/types/automation/node-type'; +import { RunStatus, type RunStatusValue } from '@/types/automation/run-status'; type Invocation = { id: string; - status: string; + status: RunStatusValue; is_manual: boolean; node_run_count: number; duration_ms: number | null; @@ -51,8 +54,8 @@ type Invocation = { type NodeRun = { id: string; node_id: string; - node_type: string; - status: string; + node_type: NodeTypeValue; + status: NodeRunStatusValue; error: { message?: string } | null; started_at: string | null; finished_at: string | null; @@ -74,25 +77,25 @@ const statusLabel = computed(() => ); const statusVariant = ( - status: string, + status: RunStatusValue | NodeRunStatusValue, ): 'default' | 'secondary' | 'destructive' | 'outline' => { - if (status === 'completed') return 'default'; - if (status === 'failed' || status === 'cancelled') return 'destructive'; - if (status === 'running' || status === 'waiting') return 'secondary'; + if (status === RunStatus.Completed) return 'default'; + if (status === RunStatus.Failed || status === RunStatus.Cancelled) return 'destructive'; + if (status === RunStatus.Running || status === RunStatus.Waiting) return 'secondary'; return 'outline'; }; const summary = (invocation: Invocation): string => { - if (invocation.status === 'failed') + if (invocation.status === RunStatus.Failed) return ( invocation.error_message ?? trans('automations.invocations.summary.failed') ); - if (invocation.status === 'completed') + if (invocation.status === RunStatus.Completed) return trans('automations.invocations.summary.completed'); - if (invocation.status === 'running' || invocation.status === 'waiting') + if (invocation.status === RunStatus.Running || invocation.status === RunStatus.Waiting) return trans('automations.invocations.summary.running'); - if (invocation.status === 'cancelled') + if (invocation.status === RunStatus.Cancelled) return trans('automations.invocations.summary.cancelled'); return trans('automations.invocations.summary.pending'); }; diff --git a/resources/js/types/automation/node-run-status.ts b/resources/js/types/automation/node-run-status.ts new file mode 100644 index 00000000..7384c716 --- /dev/null +++ b/resources/js/types/automation/node-run-status.ts @@ -0,0 +1,8 @@ +export const NodeRunStatus = { + Running: 'running', + Completed: 'completed', + Failed: 'failed', + Skipped: 'skipped', +} as const; + +export type NodeRunStatusValue = (typeof NodeRunStatus)[keyof typeof NodeRunStatus]; diff --git a/resources/js/types/automation/run-status.ts b/resources/js/types/automation/run-status.ts new file mode 100644 index 00000000..3099577e --- /dev/null +++ b/resources/js/types/automation/run-status.ts @@ -0,0 +1,10 @@ +export const RunStatus = { + Pending: 'pending', + Running: 'running', + Waiting: 'waiting', + Completed: 'completed', + Failed: 'failed', + Cancelled: 'cancelled', +} as const; + +export type RunStatusValue = (typeof RunStatus)[keyof typeof RunStatus];