@stigmer/react 3.5.3 → 3.7.0
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/agent/AgentPicker.d.ts +10 -1
- package/agent/AgentPicker.d.ts.map +1 -1
- package/agent/AgentPicker.js +2 -2
- package/agent/AgentPicker.js.map +1 -1
- package/billing/CreditLedgerTable.d.ts.map +1 -1
- package/billing/CreditLedgerTable.js +2 -7
- package/billing/CreditLedgerTable.js.map +1 -1
- package/channel/AgentChannelsPanel.d.ts.map +1 -1
- package/channel/AgentChannelsPanel.js +22 -5
- package/channel/AgentChannelsPanel.js.map +1 -1
- package/channel/ChannelTemplatesDialog.d.ts +53 -0
- package/channel/ChannelTemplatesDialog.d.ts.map +1 -0
- package/channel/ChannelTemplatesDialog.js +150 -0
- package/channel/ChannelTemplatesDialog.js.map +1 -0
- package/channel/index.d.ts +8 -0
- package/channel/index.d.ts.map +1 -1
- package/channel/index.js +4 -0
- package/channel/index.js.map +1 -1
- package/channel/providers.d.ts +12 -0
- package/channel/providers.d.ts.map +1 -1
- package/channel/providers.js +14 -2
- package/channel/providers.js.map +1 -1
- package/channel/templatePresentation.d.ts +37 -0
- package/channel/templatePresentation.d.ts.map +1 -0
- package/channel/templatePresentation.js +69 -0
- package/channel/templatePresentation.js.map +1 -0
- package/channel/useChannelTemplateList.d.ts +64 -0
- package/channel/useChannelTemplateList.d.ts.map +1 -0
- package/channel/useChannelTemplateList.js +69 -0
- package/channel/useChannelTemplateList.js.map +1 -0
- package/channel/useChannelTemplateReadiness.d.ts +53 -0
- package/channel/useChannelTemplateReadiness.d.ts.map +1 -0
- package/channel/useChannelTemplateReadiness.js +42 -0
- package/channel/useChannelTemplateReadiness.js.map +1 -0
- package/composer/ComposerToolbar.d.ts +6 -1
- package/composer/ComposerToolbar.d.ts.map +1 -1
- package/composer/ComposerToolbar.js +2 -2
- package/composer/ComposerToolbar.js.map +1 -1
- package/composer/SessionComposer.d.ts +9 -0
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +13 -2
- package/composer/SessionComposer.js.map +1 -1
- package/dashboard/useDashboardFailedRuns.d.ts.map +1 -1
- package/dashboard/useDashboardFailedRuns.js +2 -0
- package/dashboard/useDashboardFailedRuns.js.map +1 -1
- package/execution/useCreateAgentExecution.d.ts +14 -0
- package/execution/useCreateAgentExecution.d.ts.map +1 -1
- package/execution/useCreateAgentExecution.js +5 -0
- package/execution/useCreateAgentExecution.js.map +1 -1
- package/index.d.ts +4 -2
- package/index.d.ts.map +1 -1
- package/index.js +10 -1
- package/index.js.map +1 -1
- package/internal/Pagination.d.ts +15 -0
- package/internal/Pagination.d.ts.map +1 -0
- package/internal/Pagination.js +20 -0
- package/internal/Pagination.js.map +1 -0
- package/library/useExportResource.d.ts +4 -3
- package/library/useExportResource.d.ts.map +1 -1
- package/library/useExportResource.js +1 -1
- package/library/useExportResource.js.map +1 -1
- package/models/ModelSelector.d.ts +26 -1
- package/models/ModelSelector.d.ts.map +1 -1
- package/models/ModelSelector.js +27 -6
- package/models/ModelSelector.js.map +1 -1
- package/models/index.d.ts +2 -0
- package/models/index.d.ts.map +1 -1
- package/models/index.js +1 -0
- package/models/index.js.map +1 -1
- package/models/registry.d.ts +10 -0
- package/models/registry.d.ts.map +1 -1
- package/models/registry.js +3 -0
- package/models/registry.js.map +1 -1
- package/models/service-tier.d.ts +27 -0
- package/models/service-tier.d.ts.map +1 -0
- package/models/service-tier.js +34 -0
- package/models/service-tier.js.map +1 -0
- package/organization/OrgProvider.d.ts +11 -4
- package/organization/OrgProvider.d.ts.map +1 -1
- package/organization/OrgProvider.js +28 -4
- package/organization/OrgProvider.js.map +1 -1
- package/package.json +4 -4
- package/resource-detail/useDeleteResource.d.ts +1 -1
- package/resource-detail/useDeleteResource.d.ts.map +1 -1
- package/resource-detail/useDeleteResource.js +5 -0
- package/resource-detail/useDeleteResource.js.map +1 -1
- package/resource-workbench/components/ResourceWorkbench.d.ts +10 -1
- package/resource-workbench/components/ResourceWorkbench.d.ts.map +1 -1
- package/resource-workbench/components/ResourceWorkbench.js +6 -2
- package/resource-workbench/components/ResourceWorkbench.js.map +1 -1
- package/resource-workbench/hooks/useResourceCollection.d.ts +8 -0
- package/resource-workbench/hooks/useResourceCollection.d.ts.map +1 -1
- package/resource-workbench/hooks/useResourceCollection.js +2 -2
- package/resource-workbench/hooks/useResourceCollection.js.map +1 -1
- package/schedule/CadenceField.d.ts +31 -0
- package/schedule/CadenceField.d.ts.map +1 -0
- package/schedule/CadenceField.js +116 -0
- package/schedule/CadenceField.js.map +1 -0
- package/schedule/ScheduleDetailView.d.ts +81 -0
- package/schedule/ScheduleDetailView.d.ts.map +1 -0
- package/schedule/ScheduleDetailView.js +648 -0
- package/schedule/ScheduleDetailView.js.map +1 -0
- package/schedule/ScheduleForm.d.ts +46 -0
- package/schedule/ScheduleForm.d.ts.map +1 -0
- package/schedule/ScheduleForm.js +214 -0
- package/schedule/ScheduleForm.js.map +1 -0
- package/schedule/ScheduleRowActions.d.ts +20 -0
- package/schedule/ScheduleRowActions.d.ts.map +1 -0
- package/schedule/ScheduleRowActions.js +54 -0
- package/schedule/ScheduleRowActions.js.map +1 -0
- package/schedule/ScheduleRunsTable.d.ts +72 -0
- package/schedule/ScheduleRunsTable.d.ts.map +1 -0
- package/schedule/ScheduleRunsTable.js +161 -0
- package/schedule/ScheduleRunsTable.js.map +1 -0
- package/schedule/TimeZoneField.d.ts +32 -0
- package/schedule/TimeZoneField.d.ts.map +1 -0
- package/schedule/TimeZoneField.js +104 -0
- package/schedule/TimeZoneField.js.map +1 -0
- package/schedule/cadence.d.ts +95 -0
- package/schedule/cadence.d.ts.map +1 -0
- package/schedule/cadence.js +242 -0
- package/schedule/cadence.js.map +1 -0
- package/schedule/index.d.ts +37 -0
- package/schedule/index.d.ts.map +1 -0
- package/schedule/index.js +19 -0
- package/schedule/index.js.map +1 -0
- package/schedule/scheduleColumns.d.ts +26 -0
- package/schedule/scheduleColumns.d.ts.map +1 -0
- package/schedule/scheduleColumns.js +77 -0
- package/schedule/scheduleColumns.js.map +1 -0
- package/schedule/scheduleListFn.d.ts +50 -0
- package/schedule/scheduleListFn.d.ts.map +1 -0
- package/schedule/scheduleListFn.js +65 -0
- package/schedule/scheduleListFn.js.map +1 -0
- package/schedule/scheduleState.d.ts +43 -0
- package/schedule/scheduleState.d.ts.map +1 -0
- package/schedule/scheduleState.js +67 -0
- package/schedule/scheduleState.js.map +1 -0
- package/schedule/useCreateSchedule.d.ts +57 -0
- package/schedule/useCreateSchedule.d.ts.map +1 -0
- package/schedule/useCreateSchedule.js +61 -0
- package/schedule/useCreateSchedule.js.map +1 -0
- package/schedule/useResumeSchedule.d.ts +30 -0
- package/schedule/useResumeSchedule.d.ts.map +1 -0
- package/schedule/useResumeSchedule.js +45 -0
- package/schedule/useResumeSchedule.js.map +1 -0
- package/schedule/useSchedule.d.ts +39 -0
- package/schedule/useSchedule.d.ts.map +1 -0
- package/schedule/useSchedule.js +44 -0
- package/schedule/useSchedule.js.map +1 -0
- package/schedule/useScheduleCount.d.ts +38 -0
- package/schedule/useScheduleCount.d.ts.map +1 -0
- package/schedule/useScheduleCount.js +30 -0
- package/schedule/useScheduleCount.js.map +1 -0
- package/schedule/useScheduleList.d.ts +48 -0
- package/schedule/useScheduleList.d.ts.map +1 -0
- package/schedule/useScheduleList.js +43 -0
- package/schedule/useScheduleList.js.map +1 -0
- package/schedule/useScheduleRuns.d.ts +44 -0
- package/schedule/useScheduleRuns.d.ts.map +1 -0
- package/schedule/useScheduleRuns.js +48 -0
- package/schedule/useScheduleRuns.js.map +1 -0
- package/schedule/useSetScheduleEnabled.d.ts +31 -0
- package/schedule/useSetScheduleEnabled.d.ts.map +1 -0
- package/schedule/useSetScheduleEnabled.js +53 -0
- package/schedule/useSetScheduleEnabled.js.map +1 -0
- package/schedule/useTriggerSchedule.d.ts +41 -0
- package/schedule/useTriggerSchedule.d.ts.map +1 -0
- package/schedule/useTriggerSchedule.js +62 -0
- package/schedule/useTriggerSchedule.js.map +1 -0
- package/schedule/useUpdateScheduleSpec.d.ts +49 -0
- package/schedule/useUpdateScheduleSpec.d.ts.map +1 -0
- package/schedule/useUpdateScheduleSpec.js +70 -0
- package/schedule/useUpdateScheduleSpec.js.map +1 -0
- package/search/useResourceCount.d.ts +2 -1
- package/search/useResourceCount.d.ts.map +1 -1
- package/search/useResourceCount.js +6 -1
- package/search/useResourceCount.js.map +1 -1
- package/session/useNewSessionFlow.d.ts.map +1 -1
- package/session/useNewSessionFlow.js +1 -0
- package/session/useNewSessionFlow.js.map +1 -1
- package/session/useSessionConversation.d.ts +11 -0
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js +1 -0
- package/session/useSessionConversation.js.map +1 -1
- package/session/useSessionPageFlow.d.ts.map +1 -1
- package/session/useSessionPageFlow.js +1 -0
- package/session/useSessionPageFlow.js.map +1 -1
- package/src/agent/AgentPicker.tsx +13 -1
- package/src/billing/CreditLedgerTable.tsx +3 -42
- package/src/channel/AgentChannelsPanel.tsx +43 -1
- package/src/channel/ChannelTemplatesDialog.tsx +433 -0
- package/src/channel/__tests__/AgentChannelsPanel.test.tsx +73 -0
- package/src/channel/__tests__/ChannelTemplatesDialog.test.tsx +292 -0
- package/src/channel/__tests__/providers.test.ts +9 -0
- package/src/channel/__tests__/templatePresentation.test.ts +96 -0
- package/src/channel/__tests__/useChannelTemplateList.test.tsx +216 -0
- package/src/channel/__tests__/useChannelTemplateReadiness.test.tsx +124 -0
- package/src/channel/index.ts +15 -0
- package/src/channel/providers.ts +26 -2
- package/src/channel/templatePresentation.ts +82 -0
- package/src/channel/useChannelTemplateList.ts +123 -0
- package/src/channel/useChannelTemplateReadiness.ts +69 -0
- package/src/composer/ComposerToolbar.tsx +9 -0
- package/src/composer/SessionComposer.tsx +24 -1
- package/src/composer/__tests__/SessionComposer-serviceTier.test.tsx +156 -0
- package/src/dashboard/__tests__/useDashboardFailedRuns.test.tsx +106 -0
- package/src/dashboard/useDashboardFailedRuns.ts +2 -0
- package/src/execution/useCreateAgentExecution.ts +18 -0
- package/src/index.ts +70 -0
- package/src/internal/Pagination.tsx +72 -0
- package/src/library/useExportResource.ts +4 -3
- package/src/models/ModelSelector.tsx +100 -3
- package/src/models/__tests__/ModelSelector-serviceTier.test.tsx +172 -0
- package/src/models/__tests__/useModelRegistry.test.tsx +15 -0
- package/src/models/index.ts +2 -0
- package/src/models/registry.ts +16 -0
- package/src/models/service-tier.ts +49 -0
- package/src/organization/OrgProvider.tsx +29 -4
- package/src/organization/__tests__/OrgProvider.orgSwitch.test.tsx +89 -0
- package/src/resource-detail/useDeleteResource.ts +11 -1
- package/src/resource-workbench/__tests__/ResourceWorkbench.test.tsx +112 -0
- package/src/resource-workbench/components/ResourceWorkbench.tsx +32 -15
- package/src/resource-workbench/hooks/useResourceCollection.ts +11 -1
- package/src/schedule/CadenceField.tsx +365 -0
- package/src/schedule/ScheduleDetailView.tsx +1651 -0
- package/src/schedule/ScheduleForm.tsx +615 -0
- package/src/schedule/ScheduleRowActions.tsx +124 -0
- package/src/schedule/ScheduleRunsTable.tsx +424 -0
- package/src/schedule/TimeZoneField.tsx +199 -0
- package/src/schedule/__tests__/ScheduleDetailView.test.tsx +683 -0
- package/src/schedule/__tests__/cadence.test.ts +278 -0
- package/src/schedule/__tests__/scheduleColumnsRowActions.test.tsx +176 -0
- package/src/schedule/__tests__/scheduleCreation.test.tsx +537 -0
- package/src/schedule/__tests__/scheduleHooks.test.tsx +399 -0
- package/src/schedule/__tests__/scheduleListFn.test.ts +128 -0
- package/src/schedule/__tests__/scheduleState.test.ts +133 -0
- package/src/schedule/cadence.ts +298 -0
- package/src/schedule/index.ts +69 -0
- package/src/schedule/scheduleColumns.tsx +118 -0
- package/src/schedule/scheduleListFn.ts +108 -0
- package/src/schedule/scheduleState.ts +96 -0
- package/src/schedule/useCreateSchedule.ts +88 -0
- package/src/schedule/useResumeSchedule.ts +67 -0
- package/src/schedule/useSchedule.ts +67 -0
- package/src/schedule/useScheduleCount.ts +57 -0
- package/src/schedule/useScheduleList.ts +83 -0
- package/src/schedule/useScheduleRuns.ts +93 -0
- package/src/schedule/useSetScheduleEnabled.ts +84 -0
- package/src/schedule/useTriggerSchedule.ts +90 -0
- package/src/schedule/useUpdateScheduleSpec.ts +108 -0
- package/src/search/__tests__/useResourceCount.test.tsx +89 -0
- package/src/search/useResourceCount.ts +8 -2
- package/src/session/useNewSessionFlow.ts +1 -0
- package/src/session/useSessionConversation.ts +12 -0
- package/src/session/useSessionPageFlow.ts +1 -0
- package/src/workflow/WorkflowDashboard.tsx +1 -0
- package/src/workflow/__tests__/inspector-forms.test.tsx +119 -2
- package/src/workflow/inspector/forms/AgentCallForm.tsx +260 -37
- package/src/workflow/inspector/tabs/RuntimeTab.tsx +72 -33
- package/src/workflow/starter-workflow-yaml.ts +2 -1
- package/src/workflow/useWorkflowExecutionList.ts +13 -2
- package/styles.css +1 -1
- package/workflow/CanvasTransitionEdge.d.ts +1 -1
- package/workflow/WorkflowDashboard.d.ts.map +1 -1
- package/workflow/WorkflowDashboard.js +1 -0
- package/workflow/WorkflowDashboard.js.map +1 -1
- package/workflow/inspector/forms/AgentCallForm.d.ts +18 -2
- package/workflow/inspector/forms/AgentCallForm.d.ts.map +1 -1
- package/workflow/inspector/forms/AgentCallForm.js +87 -9
- package/workflow/inspector/forms/AgentCallForm.js.map +1 -1
- package/workflow/inspector/tabs/RuntimeTab.d.ts +2 -1
- package/workflow/inspector/tabs/RuntimeTab.d.ts.map +1 -1
- package/workflow/inspector/tabs/RuntimeTab.js +31 -5
- package/workflow/inspector/tabs/RuntimeTab.js.map +1 -1
- package/workflow/starter-workflow-yaml.d.ts +1 -1
- package/workflow/starter-workflow-yaml.d.ts.map +1 -1
- package/workflow/starter-workflow-yaml.js +2 -1
- package/workflow/starter-workflow-yaml.js.map +1 -1
- package/workflow/useWorkflowExecutionList.d.ts +10 -0
- package/workflow/useWorkflowExecutionList.d.ts.map +1 -1
- package/workflow/useWorkflowExecutionList.js +3 -2
- package/workflow/useWorkflowExecutionList.js.map +1 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@stigmer/theme";
|
|
4
|
+
import type { Schedule } from "@stigmer/protos/ai/stigmer/agentic/schedule/v1/api_pb";
|
|
5
|
+
import { ConfirmDialog } from "../resource-detail/ConfirmDialog.js";
|
|
6
|
+
import { useConfirmAction } from "../resource-detail/useConfirmAction.js";
|
|
7
|
+
import { deriveScheduleState } from "./scheduleState.js";
|
|
8
|
+
import { useResumeSchedule } from "./useResumeSchedule.js";
|
|
9
|
+
import { useTriggerSchedule } from "./useTriggerSchedule.js";
|
|
10
|
+
|
|
11
|
+
/** Props for {@link ScheduleRowActions}. */
|
|
12
|
+
export interface ScheduleRowActionsProps {
|
|
13
|
+
/** The schedule this row represents. */
|
|
14
|
+
readonly schedule: Schedule;
|
|
15
|
+
/** Called after a successful action so the list can refetch. */
|
|
16
|
+
readonly onChanged?: () => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Compact per-row actions for a schedule list: **Run now** on an active
|
|
21
|
+
* schedule (confirmation-gated — it starts a real, billable execution)
|
|
22
|
+
* and **Resume** on a platform-paused one. Exactly one action renders
|
|
23
|
+
* at a time — the one that is currently meaningful; an owner-disabled
|
|
24
|
+
* schedule renders neither (its remedy lives on the detail page).
|
|
25
|
+
*
|
|
26
|
+
* Shared by every console's schedule workbench via `renderItemAction`
|
|
27
|
+
* so row behavior never diverges between apps (DD-016).
|
|
28
|
+
*/
|
|
29
|
+
export function ScheduleRowActions({
|
|
30
|
+
schedule,
|
|
31
|
+
onChanged,
|
|
32
|
+
}: ScheduleRowActionsProps) {
|
|
33
|
+
const { resumeSchedule, isResuming } = useResumeSchedule();
|
|
34
|
+
const { triggerSchedule, isTriggering } = useTriggerSchedule();
|
|
35
|
+
const { confirmState, confirm, handleConfirm, handleCancel } =
|
|
36
|
+
useConfirmAction();
|
|
37
|
+
|
|
38
|
+
const info = deriveScheduleState(schedule.spec, schedule.status);
|
|
39
|
+
const scheduleId = schedule.metadata?.id ?? "";
|
|
40
|
+
const name = schedule.metadata?.name || schedule.metadata?.slug || "schedule";
|
|
41
|
+
|
|
42
|
+
const handleTrigger = async () => {
|
|
43
|
+
const confirmed = await confirm({
|
|
44
|
+
title: "Run this schedule now?",
|
|
45
|
+
description:
|
|
46
|
+
`"${name}" starts a real agent execution immediately, outside ` +
|
|
47
|
+
"the cron cadence.",
|
|
48
|
+
confirmLabel: "Start run",
|
|
49
|
+
variant: "default",
|
|
50
|
+
});
|
|
51
|
+
if (!confirmed) return;
|
|
52
|
+
await triggerSchedule(scheduleId);
|
|
53
|
+
onChanged?.();
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const handleResume = async () => {
|
|
57
|
+
await resumeSchedule(scheduleId);
|
|
58
|
+
onChanged?.();
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
if (info.state === "disabled") return null;
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
// Row actions must never bubble into the row's navigate-to-detail
|
|
65
|
+
// click handler. The span is a non-interactive event fence — the
|
|
66
|
+
// real interactive element (with keyboard support) is the button.
|
|
67
|
+
<span onClick={(e) => e.stopPropagation()}>
|
|
68
|
+
{info.state === "active" ? (
|
|
69
|
+
<RowActionButton
|
|
70
|
+
label={`Run ${name} now`}
|
|
71
|
+
busy={isTriggering}
|
|
72
|
+
onClick={() => void handleTrigger()}
|
|
73
|
+
>
|
|
74
|
+
Run now
|
|
75
|
+
</RowActionButton>
|
|
76
|
+
) : (
|
|
77
|
+
<RowActionButton
|
|
78
|
+
label={`Resume ${name}`}
|
|
79
|
+
busy={isResuming}
|
|
80
|
+
onClick={() => void handleResume()}
|
|
81
|
+
>
|
|
82
|
+
Resume
|
|
83
|
+
</RowActionButton>
|
|
84
|
+
)}
|
|
85
|
+
|
|
86
|
+
{confirmState && (
|
|
87
|
+
<ConfirmDialog
|
|
88
|
+
state={confirmState}
|
|
89
|
+
onConfirm={handleConfirm}
|
|
90
|
+
onCancel={handleCancel}
|
|
91
|
+
/>
|
|
92
|
+
)}
|
|
93
|
+
</span>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function RowActionButton({
|
|
98
|
+
label,
|
|
99
|
+
busy,
|
|
100
|
+
onClick,
|
|
101
|
+
children,
|
|
102
|
+
}: {
|
|
103
|
+
readonly label: string;
|
|
104
|
+
readonly busy: boolean;
|
|
105
|
+
readonly onClick: () => void;
|
|
106
|
+
readonly children: React.ReactNode;
|
|
107
|
+
}) {
|
|
108
|
+
return (
|
|
109
|
+
<button
|
|
110
|
+
type="button"
|
|
111
|
+
aria-label={label}
|
|
112
|
+
onClick={onClick}
|
|
113
|
+
disabled={busy}
|
|
114
|
+
className={cn(
|
|
115
|
+
"rounded-md border border-input bg-background px-2 py-1 text-xs font-medium text-foreground",
|
|
116
|
+
"hover:bg-accent hover:text-accent-foreground",
|
|
117
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
118
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
119
|
+
)}
|
|
120
|
+
>
|
|
121
|
+
{children}
|
|
122
|
+
</button>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { cn } from "@stigmer/theme";
|
|
5
|
+
import { timestampDate } from "@bufbuild/protobuf/wkt";
|
|
6
|
+
import {
|
|
7
|
+
ScheduleRunOrigin,
|
|
8
|
+
ScheduleRunOutcome,
|
|
9
|
+
type ScheduleRun,
|
|
10
|
+
} from "@stigmer/protos/ai/stigmer/agentic/schedule/v1/io_pb";
|
|
11
|
+
import { formatRelativeTime } from "../activity/format-relative-time.js";
|
|
12
|
+
import { ErrorMessage } from "../error/ErrorMessage.js";
|
|
13
|
+
import { Pagination } from "../internal/Pagination.js";
|
|
14
|
+
import { useScheduleRuns } from "./useScheduleRuns.js";
|
|
15
|
+
|
|
16
|
+
/** Props for {@link ScheduleRunsTable}. */
|
|
17
|
+
export interface ScheduleRunsTableProps {
|
|
18
|
+
/** ID of the schedule whose run history to show. */
|
|
19
|
+
readonly scheduleId: string;
|
|
20
|
+
/**
|
|
21
|
+
* Called when the user activates a run's execution reference
|
|
22
|
+
* (`aex_…`). When omitted, the id renders as plain text.
|
|
23
|
+
*/
|
|
24
|
+
readonly onNavigateToExecution?: (executionId: string) => void;
|
|
25
|
+
/**
|
|
26
|
+
* The instant relative timestamps are computed against.
|
|
27
|
+
* Injectable for deterministic tests and Scenar fixtures.
|
|
28
|
+
* @default new Date() at render
|
|
29
|
+
*/
|
|
30
|
+
readonly now?: Date;
|
|
31
|
+
/** Runs per page. @default 25 */
|
|
32
|
+
readonly pageSize?: number;
|
|
33
|
+
/** Additional CSS classes for the root container. */
|
|
34
|
+
readonly className?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Paginated run-history table for a schedule — the fire ledger,
|
|
39
|
+
* rendered in full (project DD-017 D-7).
|
|
40
|
+
*
|
|
41
|
+
* Every fire leaves a row, INCLUDING fires that created no execution (a
|
|
42
|
+
* refused launch gate, a missing target agent), carrying the refusing
|
|
43
|
+
* gate's copy verbatim. Columns: outcome, origin, fired time (relative,
|
|
44
|
+
* with the absolute instant on hover), duration (recorded → completed;
|
|
45
|
+
* "—" while a run is in flight or when no run was created), and the
|
|
46
|
+
* execution reference.
|
|
47
|
+
*
|
|
48
|
+
* Owns its data: give it a `scheduleId` and it fetches via
|
|
49
|
+
* {@link useScheduleRuns} with internal page state. To force a refresh
|
|
50
|
+
* from outside (e.g. after triggering a manual run), remount with a
|
|
51
|
+
* React `key` — the standard reset idiom (DD-014); the remount also
|
|
52
|
+
* returns to page 1, where the new run appears.
|
|
53
|
+
*
|
|
54
|
+
* Handles loading, error, and empty states automatically.
|
|
55
|
+
* Zero Console dependencies — safe for platform builder embedding.
|
|
56
|
+
* All visual properties flow through `--stgm-*` design tokens.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```tsx
|
|
60
|
+
* <ScheduleRunsTable
|
|
61
|
+
* scheduleId={schedule.metadata.id}
|
|
62
|
+
* onNavigateToExecution={(id) => router.push(`/executions/${id}`)}
|
|
63
|
+
* />
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export function ScheduleRunsTable({
|
|
67
|
+
scheduleId,
|
|
68
|
+
onNavigateToExecution,
|
|
69
|
+
now,
|
|
70
|
+
pageSize = 25,
|
|
71
|
+
className,
|
|
72
|
+
}: ScheduleRunsTableProps) {
|
|
73
|
+
const [pageNum, setPageNum] = useState(1);
|
|
74
|
+
const { runs, totalCount, isLoading, error, refetch } = useScheduleRuns(
|
|
75
|
+
scheduleId,
|
|
76
|
+
{ page: pageNum, pageSize },
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
if (error) {
|
|
80
|
+
return <ErrorMessage error={error} retry={refetch} className={className} />;
|
|
81
|
+
}
|
|
82
|
+
if (isLoading && runs.length === 0) {
|
|
83
|
+
return (
|
|
84
|
+
<div className={cn("space-y-2", className)} aria-busy="true">
|
|
85
|
+
{Array.from({ length: 5 }, (_, i) => (
|
|
86
|
+
<div key={i} className="h-9 animate-pulse rounded-lg bg-muted-subtle" />
|
|
87
|
+
))}
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
if (runs.length === 0) {
|
|
92
|
+
return (
|
|
93
|
+
<div className={className}>
|
|
94
|
+
<EmptyRuns />
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const renderNow = now ?? new Date();
|
|
100
|
+
const totalPages = Math.ceil(totalCount / pageSize);
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<div className={className}>
|
|
104
|
+
<div
|
|
105
|
+
role="table"
|
|
106
|
+
aria-label="Run history"
|
|
107
|
+
className="overflow-hidden rounded-lg border border-border"
|
|
108
|
+
>
|
|
109
|
+
<div
|
|
110
|
+
role="row"
|
|
111
|
+
className={cn(
|
|
112
|
+
rowGridClasses,
|
|
113
|
+
"border-b border-border px-3.5 py-2 text-[0.65rem] font-medium uppercase tracking-wider text-muted-foreground",
|
|
114
|
+
)}
|
|
115
|
+
>
|
|
116
|
+
<span role="columnheader">Outcome</span>
|
|
117
|
+
<span role="columnheader" className="hidden sm:block">
|
|
118
|
+
Origin
|
|
119
|
+
</span>
|
|
120
|
+
<span role="columnheader">Fired</span>
|
|
121
|
+
<span role="columnheader" className="hidden sm:block">
|
|
122
|
+
Duration
|
|
123
|
+
</span>
|
|
124
|
+
<span role="columnheader" className="text-right">
|
|
125
|
+
Execution
|
|
126
|
+
</span>
|
|
127
|
+
</div>
|
|
128
|
+
{runs.map((run, i) => (
|
|
129
|
+
<RunTableRow
|
|
130
|
+
key={runKey(run, i)}
|
|
131
|
+
run={run}
|
|
132
|
+
now={renderNow}
|
|
133
|
+
onNavigateToExecution={onNavigateToExecution}
|
|
134
|
+
/>
|
|
135
|
+
))}
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
{totalPages > 1 && (
|
|
139
|
+
<Pagination
|
|
140
|
+
pageNum={pageNum}
|
|
141
|
+
totalPages={totalPages}
|
|
142
|
+
onPageChange={setPageNum}
|
|
143
|
+
ariaLabel="Run history pagination"
|
|
144
|
+
className="mt-3"
|
|
145
|
+
/>
|
|
146
|
+
)}
|
|
147
|
+
</div>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Mobile keeps the three load-bearing columns (outcome, fired,
|
|
152
|
+
// execution); origin and duration join at the sm breakpoint.
|
|
153
|
+
const rowGridClasses =
|
|
154
|
+
"grid grid-cols-[7rem_1fr_auto] items-center gap-x-4 sm:grid-cols-[7rem_5.5rem_1fr_5rem_minmax(0,12rem)]";
|
|
155
|
+
|
|
156
|
+
function RunTableRow({
|
|
157
|
+
run,
|
|
158
|
+
now,
|
|
159
|
+
onNavigateToExecution,
|
|
160
|
+
}: {
|
|
161
|
+
readonly run: ScheduleRun;
|
|
162
|
+
readonly now: Date;
|
|
163
|
+
readonly onNavigateToExecution?: (executionId: string) => void;
|
|
164
|
+
}) {
|
|
165
|
+
const fireDate = run.nominalFireTime
|
|
166
|
+
? timestampDate(run.nominalFireTime)
|
|
167
|
+
: null;
|
|
168
|
+
const badge = runOutcomeBadge(run.outcome);
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<div
|
|
172
|
+
role="row"
|
|
173
|
+
className={cn(
|
|
174
|
+
rowGridClasses,
|
|
175
|
+
"border-b border-border-muted px-3.5 py-2.5 last:border-b-0",
|
|
176
|
+
)}
|
|
177
|
+
>
|
|
178
|
+
<span role="cell">
|
|
179
|
+
<span
|
|
180
|
+
className={cn(
|
|
181
|
+
"inline-flex items-center rounded-full px-2 py-0.5 text-[0.65rem] font-medium",
|
|
182
|
+
badge.className,
|
|
183
|
+
)}
|
|
184
|
+
>
|
|
185
|
+
{badge.label}
|
|
186
|
+
</span>
|
|
187
|
+
</span>
|
|
188
|
+
<span role="cell" className="hidden text-xs text-muted-foreground sm:block">
|
|
189
|
+
{runOriginLabel(run.origin)}
|
|
190
|
+
</span>
|
|
191
|
+
<span
|
|
192
|
+
role="cell"
|
|
193
|
+
className="text-xs text-muted-foreground"
|
|
194
|
+
title={fireDate ? fireDate.toLocaleString() : undefined}
|
|
195
|
+
>
|
|
196
|
+
{fireDate ? formatRelativeTime(fireDate, now) : "—"}
|
|
197
|
+
</span>
|
|
198
|
+
<span
|
|
199
|
+
role="cell"
|
|
200
|
+
className="hidden text-xs tabular-nums text-muted-foreground sm:block"
|
|
201
|
+
>
|
|
202
|
+
{formatRunDuration(run) ?? "—"}
|
|
203
|
+
</span>
|
|
204
|
+
<span role="cell" className="min-w-0 text-right">
|
|
205
|
+
{run.executionId ? (
|
|
206
|
+
onNavigateToExecution ? (
|
|
207
|
+
<button
|
|
208
|
+
type="button"
|
|
209
|
+
onClick={() => onNavigateToExecution(run.executionId)}
|
|
210
|
+
className={cn(
|
|
211
|
+
"max-w-full truncate font-mono text-[0.65rem] text-primary underline-offset-2 hover:underline",
|
|
212
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm",
|
|
213
|
+
)}
|
|
214
|
+
>
|
|
215
|
+
{run.executionId}
|
|
216
|
+
</button>
|
|
217
|
+
) : (
|
|
218
|
+
<span className="font-mono text-[0.65rem] text-muted-foreground">
|
|
219
|
+
{run.executionId}
|
|
220
|
+
</span>
|
|
221
|
+
)
|
|
222
|
+
) : (
|
|
223
|
+
<span className="text-xs text-muted-foreground">—</span>
|
|
224
|
+
)}
|
|
225
|
+
</span>
|
|
226
|
+
{run.reason && (
|
|
227
|
+
<p
|
|
228
|
+
role="cell"
|
|
229
|
+
className="col-span-full mt-1 whitespace-pre-wrap break-words text-xs text-muted-foreground"
|
|
230
|
+
>
|
|
231
|
+
{run.reason}
|
|
232
|
+
</p>
|
|
233
|
+
)}
|
|
234
|
+
</div>
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
// Compact list — the Overview tab's "recent runs" strip
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Compact run list: badge, origin, relative time, refusal reason, and
|
|
244
|
+
* execution link per row — the at-a-glance form used by the detail
|
|
245
|
+
* view's Overview tab. Presentational: runs come in as props (the
|
|
246
|
+
* caller owns the fetch, so one hook can also feed the Runs tab badge).
|
|
247
|
+
*
|
|
248
|
+
* Not barrel-exported: the public run-history surface is
|
|
249
|
+
* {@link ScheduleRunsTable}; this exists for the detail view's strip.
|
|
250
|
+
*/
|
|
251
|
+
export function ScheduleRunsCompactList({
|
|
252
|
+
runs,
|
|
253
|
+
isLoading,
|
|
254
|
+
now,
|
|
255
|
+
onNavigateToExecution,
|
|
256
|
+
}: {
|
|
257
|
+
readonly runs: readonly ScheduleRun[];
|
|
258
|
+
readonly isLoading: boolean;
|
|
259
|
+
readonly now: Date;
|
|
260
|
+
readonly onNavigateToExecution?: (executionId: string) => void;
|
|
261
|
+
}) {
|
|
262
|
+
if (isLoading && runs.length === 0) {
|
|
263
|
+
return (
|
|
264
|
+
<div className="space-y-2 px-4 py-3" aria-busy="true">
|
|
265
|
+
<div className="h-4 w-full animate-pulse rounded bg-muted" />
|
|
266
|
+
<div className="h-4 w-3/4 animate-pulse rounded bg-muted" />
|
|
267
|
+
</div>
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
if (runs.length === 0) {
|
|
271
|
+
return <EmptyRuns />;
|
|
272
|
+
}
|
|
273
|
+
return (
|
|
274
|
+
<ul className="divide-y divide-border">
|
|
275
|
+
{runs.map((run, i) => (
|
|
276
|
+
<CompactRunRow
|
|
277
|
+
key={runKey(run, i)}
|
|
278
|
+
run={run}
|
|
279
|
+
now={now}
|
|
280
|
+
onNavigateToExecution={onNavigateToExecution}
|
|
281
|
+
/>
|
|
282
|
+
))}
|
|
283
|
+
</ul>
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function CompactRunRow({
|
|
288
|
+
run,
|
|
289
|
+
now,
|
|
290
|
+
onNavigateToExecution,
|
|
291
|
+
}: {
|
|
292
|
+
readonly run: ScheduleRun;
|
|
293
|
+
readonly now: Date;
|
|
294
|
+
readonly onNavigateToExecution?: (executionId: string) => void;
|
|
295
|
+
}) {
|
|
296
|
+
const fireDate = run.nominalFireTime
|
|
297
|
+
? timestampDate(run.nominalFireTime)
|
|
298
|
+
: null;
|
|
299
|
+
const badge = runOutcomeBadge(run.outcome);
|
|
300
|
+
|
|
301
|
+
return (
|
|
302
|
+
<li className="flex flex-col gap-1 px-4 py-2.5">
|
|
303
|
+
<div className="flex items-center gap-2">
|
|
304
|
+
<span
|
|
305
|
+
className={cn(
|
|
306
|
+
"inline-flex items-center rounded-full px-2 py-0.5 text-[0.65rem] font-medium",
|
|
307
|
+
badge.className,
|
|
308
|
+
)}
|
|
309
|
+
>
|
|
310
|
+
{badge.label}
|
|
311
|
+
</span>
|
|
312
|
+
<span className="text-xs text-muted-foreground">
|
|
313
|
+
{runOriginLabel(run.origin)}
|
|
314
|
+
</span>
|
|
315
|
+
<span className="text-xs text-muted-foreground-subtle">·</span>
|
|
316
|
+
<span
|
|
317
|
+
className="text-xs text-muted-foreground"
|
|
318
|
+
title={fireDate ? fireDate.toLocaleString() : undefined}
|
|
319
|
+
>
|
|
320
|
+
{fireDate ? formatRelativeTime(fireDate, now) : "—"}
|
|
321
|
+
</span>
|
|
322
|
+
</div>
|
|
323
|
+
{run.reason && (
|
|
324
|
+
<p className="whitespace-pre-wrap break-words text-xs text-muted-foreground">
|
|
325
|
+
{run.reason}
|
|
326
|
+
</p>
|
|
327
|
+
)}
|
|
328
|
+
{run.executionId &&
|
|
329
|
+
(onNavigateToExecution ? (
|
|
330
|
+
<button
|
|
331
|
+
type="button"
|
|
332
|
+
onClick={() => onNavigateToExecution(run.executionId)}
|
|
333
|
+
className={cn(
|
|
334
|
+
"self-start font-mono text-[0.65rem] text-primary underline-offset-2 hover:underline",
|
|
335
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm",
|
|
336
|
+
)}
|
|
337
|
+
>
|
|
338
|
+
{run.executionId}
|
|
339
|
+
</button>
|
|
340
|
+
) : (
|
|
341
|
+
<span className="self-start font-mono text-[0.65rem] text-muted-foreground">
|
|
342
|
+
{run.executionId}
|
|
343
|
+
</span>
|
|
344
|
+
))}
|
|
345
|
+
</li>
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// ---------------------------------------------------------------------------
|
|
350
|
+
// Shared pieces
|
|
351
|
+
// ---------------------------------------------------------------------------
|
|
352
|
+
|
|
353
|
+
function EmptyRuns() {
|
|
354
|
+
return (
|
|
355
|
+
<p className="px-4 py-6 text-center text-xs text-muted-foreground">
|
|
356
|
+
No runs yet. Use “Run now” to fire a test run — every fire,
|
|
357
|
+
including a refused one, is recorded here.
|
|
358
|
+
</p>
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Stable row key from the fire ledger's natural identity
|
|
364
|
+
* (nominal fire time + origin), with the index as a collision tail.
|
|
365
|
+
*/
|
|
366
|
+
function runKey(run: ScheduleRun, index: number): string {
|
|
367
|
+
const fireMs = run.nominalFireTime
|
|
368
|
+
? timestampDate(run.nominalFireTime).getTime()
|
|
369
|
+
: "no-time";
|
|
370
|
+
return `${fireMs}-${run.origin}-${index}`;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function runOriginLabel(origin: ScheduleRunOrigin): string {
|
|
374
|
+
return origin === ScheduleRunOrigin.MANUAL ? "Manual" : "Scheduled";
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/** Verbatim-label badges for a run outcome, colored by health. */
|
|
378
|
+
export function runOutcomeBadge(outcome: ScheduleRunOutcome): {
|
|
379
|
+
label: string;
|
|
380
|
+
className: string;
|
|
381
|
+
} {
|
|
382
|
+
switch (outcome) {
|
|
383
|
+
case ScheduleRunOutcome.STARTED:
|
|
384
|
+
return { label: "Started", className: "bg-info/10 text-info" };
|
|
385
|
+
case ScheduleRunOutcome.COMPLETED:
|
|
386
|
+
return { label: "Completed", className: "bg-success/10 text-success" };
|
|
387
|
+
case ScheduleRunOutcome.REFUSED:
|
|
388
|
+
return { label: "Refused", className: "bg-warning/10 text-warning" };
|
|
389
|
+
case ScheduleRunOutcome.TARGET_MISSING:
|
|
390
|
+
return { label: "Target missing", className: "bg-warning/10 text-warning" };
|
|
391
|
+
case ScheduleRunOutcome.SKIPPED:
|
|
392
|
+
return { label: "Skipped", className: "bg-muted text-muted-foreground" };
|
|
393
|
+
case ScheduleRunOutcome.FAILED:
|
|
394
|
+
return { label: "Failed", className: "bg-destructive/10 text-destructive" };
|
|
395
|
+
case ScheduleRunOutcome.TIMED_OUT:
|
|
396
|
+
return { label: "Timed out", className: "bg-destructive/10 text-destructive" };
|
|
397
|
+
default:
|
|
398
|
+
return { label: "Unknown", className: "bg-muted text-muted-foreground" };
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Wall-clock duration of a run, `recorded_at` → `completed_at`, in the
|
|
404
|
+
* compact unit style the schedule views use elsewhere (`42s`, `5m 12s`,
|
|
405
|
+
* `2h 3m`). `null` while the run is in flight or when the fire created
|
|
406
|
+
* no run at all — the caller renders the em-dash.
|
|
407
|
+
*/
|
|
408
|
+
function formatRunDuration(run: ScheduleRun): string | null {
|
|
409
|
+
if (!run.recordedAt || !run.completedAt) return null;
|
|
410
|
+
const ms =
|
|
411
|
+
timestampDate(run.completedAt).getTime() -
|
|
412
|
+
timestampDate(run.recordedAt).getTime();
|
|
413
|
+
if (ms < 0) return null;
|
|
414
|
+
|
|
415
|
+
const totalSeconds = Math.round(ms / 1000);
|
|
416
|
+
if (totalSeconds < 1) return "<1s";
|
|
417
|
+
if (totalSeconds < 60) return `${totalSeconds}s`;
|
|
418
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
419
|
+
const seconds = totalSeconds % 60;
|
|
420
|
+
if (minutes < 60) return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
|
421
|
+
const hours = Math.floor(minutes / 60);
|
|
422
|
+
const remMinutes = minutes % 60;
|
|
423
|
+
return remMinutes > 0 ? `${hours}h ${remMinutes}m` : `${hours}h`;
|
|
424
|
+
}
|