@stigmer/react 3.1.13 → 3.1.15
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/AgentDetailView.d.ts +9 -9
- package/agent/AgentDetailView.d.ts.map +1 -1
- package/agent/AgentDetailView.js +23 -31
- package/agent/AgentDetailView.js.map +1 -1
- package/agent-instance/AgentInstanceEmptyState.d.ts +4 -1
- package/agent-instance/AgentInstanceEmptyState.d.ts.map +1 -1
- package/agent-instance/AgentInstanceEmptyState.js +12 -5
- package/agent-instance/AgentInstanceEmptyState.js.map +1 -1
- package/agent-instance/AgentInstanceList.d.ts +13 -2
- package/agent-instance/AgentInstanceList.d.ts.map +1 -1
- package/agent-instance/AgentInstanceList.js +8 -4
- package/agent-instance/AgentInstanceList.js.map +1 -1
- package/agent-instance/useAgentInstances.d.ts +10 -3
- package/agent-instance/useAgentInstances.d.ts.map +1 -1
- package/agent-instance/useAgentInstances.js +12 -4
- package/agent-instance/useAgentInstances.js.map +1 -1
- package/button/Button.d.ts +54 -0
- package/button/Button.d.ts.map +1 -0
- package/button/Button.js +42 -0
- package/button/Button.js.map +1 -0
- package/button/index.d.ts +3 -0
- package/button/index.d.ts.map +1 -0
- package/button/index.js +2 -0
- package/button/index.js.map +1 -0
- package/empty-state/EmptyState.d.ts.map +1 -1
- package/empty-state/EmptyState.js +8 -3
- package/empty-state/EmptyState.js.map +1 -1
- package/empty-state/types.d.ts +2 -0
- package/empty-state/types.d.ts.map +1 -1
- package/execution/index.d.ts +1 -1
- package/execution/index.d.ts.map +1 -1
- package/execution/index.js.map +1 -1
- package/execution/useCreateAgentExecution.d.ts +81 -9
- package/execution/useCreateAgentExecution.d.ts.map +1 -1
- package/execution/useCreateAgentExecution.js +42 -4
- package/execution/useCreateAgentExecution.js.map +1 -1
- package/index.d.ts +5 -3
- package/index.d.ts.map +1 -1
- package/index.js +4 -2
- package/index.js.map +1 -1
- package/package.json +4 -4
- package/session/useNewSessionFlow.d.ts +6 -3
- package/session/useNewSessionFlow.d.ts.map +1 -1
- package/session/useNewSessionFlow.js +34 -35
- package/session/useNewSessionFlow.js.map +1 -1
- package/sharing/AgentShareList.d.ts +46 -0
- package/sharing/AgentShareList.d.ts.map +1 -0
- package/sharing/AgentShareList.js +159 -0
- package/sharing/AgentShareList.js.map +1 -0
- package/sharing/ShareAgentDialog.d.ts +38 -25
- package/sharing/ShareAgentDialog.d.ts.map +1 -1
- package/sharing/ShareAgentDialog.js +131 -62
- package/sharing/ShareAgentDialog.js.map +1 -1
- package/sharing/index.d.ts +10 -8
- package/sharing/index.d.ts.map +1 -1
- package/sharing/index.js +5 -4
- package/sharing/index.js.map +1 -1
- package/sharing/useAgentShares.d.ts +48 -0
- package/sharing/useAgentShares.d.ts.map +1 -0
- package/sharing/useAgentShares.js +44 -0
- package/sharing/useAgentShares.js.map +1 -0
- package/sharing/useCanCreateAgentShare.d.ts +43 -0
- package/sharing/useCanCreateAgentShare.d.ts.map +1 -0
- package/sharing/useCanCreateAgentShare.js +44 -0
- package/sharing/useCanCreateAgentShare.js.map +1 -0
- package/sharing/useDeleteAgentShare.d.ts +42 -0
- package/sharing/useDeleteAgentShare.d.ts.map +1 -0
- package/sharing/useDeleteAgentShare.js +45 -0
- package/sharing/useDeleteAgentShare.js.map +1 -0
- package/sharing/useSaveAgentShare.d.ts +43 -18
- package/sharing/useSaveAgentShare.d.ts.map +1 -1
- package/sharing/useSaveAgentShare.js +55 -22
- package/sharing/useSaveAgentShare.js.map +1 -1
- package/src/agent/AgentDetailView.tsx +37 -43
- package/src/agent-instance/AgentInstanceEmptyState.tsx +24 -43
- package/src/agent-instance/AgentInstanceList.tsx +29 -16
- package/src/agent-instance/__tests__/agent-instance-hooks.test.tsx +19 -0
- package/src/agent-instance/useAgentInstances.ts +12 -3
- package/src/button/Button.tsx +104 -0
- package/src/button/__tests__/Button.test.tsx +82 -0
- package/src/button/index.ts +2 -0
- package/src/empty-state/EmptyState.tsx +8 -10
- package/src/empty-state/types.ts +2 -0
- package/src/execution/__tests__/useCreateAgentExecution.test.tsx +92 -0
- package/src/execution/index.ts +2 -0
- package/src/execution/useCreateAgentExecution.ts +116 -10
- package/src/index.ts +17 -9
- package/src/session/__tests__/useNewSessionFlow.test.tsx +121 -52
- package/src/session/useNewSessionFlow.ts +38 -37
- package/src/sharing/AgentShareList.tsx +502 -0
- package/src/sharing/ShareAgentDialog.tsx +310 -112
- package/src/sharing/__tests__/AgentShareList.test.tsx +388 -0
- package/src/sharing/__tests__/ShareAgentDialog.test.tsx +311 -312
- package/src/sharing/__tests__/useAgentShares.test.tsx +186 -0
- package/src/sharing/__tests__/useCanCreateAgentShare.test.tsx +190 -0
- package/src/sharing/index.ts +10 -12
- package/src/sharing/useAgentShares.ts +74 -0
- package/src/sharing/useCanCreateAgentShare.ts +74 -0
- package/src/sharing/useDeleteAgentShare.ts +73 -0
- package/src/sharing/useSaveAgentShare.ts +73 -23
- package/src/workflow/WorkflowDetailView.tsx +9 -0
- package/src/workflow/instance/WorkflowInstanceEmptyState.tsx +24 -43
- package/src/workflow/instance/WorkflowInstanceList.tsx +29 -16
- package/src/workflow/useWorkflowInstances.ts +10 -3
- package/styles.css +1 -1
- package/workflow/WorkflowDetailView.d.ts +8 -1
- package/workflow/WorkflowDetailView.d.ts.map +1 -1
- package/workflow/WorkflowDetailView.js +2 -2
- package/workflow/WorkflowDetailView.js.map +1 -1
- package/workflow/instance/WorkflowInstanceEmptyState.d.ts +4 -1
- package/workflow/instance/WorkflowInstanceEmptyState.d.ts.map +1 -1
- package/workflow/instance/WorkflowInstanceEmptyState.js +12 -5
- package/workflow/instance/WorkflowInstanceEmptyState.js.map +1 -1
- package/workflow/instance/WorkflowInstanceList.d.ts +13 -2
- package/workflow/instance/WorkflowInstanceList.d.ts.map +1 -1
- package/workflow/instance/WorkflowInstanceList.js +8 -4
- package/workflow/instance/WorkflowInstanceList.js.map +1 -1
- package/workflow/useWorkflowInstances.d.ts +8 -3
- package/workflow/useWorkflowInstances.d.ts.map +1 -1
- package/workflow/useWorkflowInstances.js +10 -4
- package/workflow/useWorkflowInstances.js.map +1 -1
- package/sharing/useAgentShare.d.ts +0 -49
- package/sharing/useAgentShare.d.ts.map +0 -1
- package/sharing/useAgentShare.js +0 -64
- package/sharing/useAgentShare.js.map +0 -1
- package/sharing/useCreateExternalShareLink.d.ts +0 -90
- package/sharing/useCreateExternalShareLink.d.ts.map +0 -1
- package/sharing/useCreateExternalShareLink.js +0 -65
- package/sharing/useCreateExternalShareLink.js.map +0 -1
- package/sharing/useShareAgent.d.ts +0 -69
- package/sharing/useShareAgent.d.ts.map +0 -1
- package/sharing/useShareAgent.js +0 -42
- package/sharing/useShareAgent.js.map +0 -1
- package/src/sharing/__tests__/useAgentShare.test.tsx +0 -210
- package/src/sharing/__tests__/useCreateExternalShareLink.test.tsx +0 -194
- package/src/sharing/__tests__/useShareAgent.test.tsx +0 -131
- package/src/sharing/useAgentShare.ts +0 -105
- package/src/sharing/useCreateExternalShareLink.tsx +0 -141
- package/src/sharing/useShareAgent.tsx +0 -107
|
@@ -76,6 +76,48 @@ function sharingAudienceToProto(audience: SharingAudience): AgentShareAudience {
|
|
|
76
76
|
: AgentShareAudience.public;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Projects a share into the editable {@link AgentShareDraft} — the
|
|
81
|
+
* single constructor for the full-spec drafts every save requires.
|
|
82
|
+
* `null` (no share yet) seeds the same defaults the server would apply
|
|
83
|
+
* on first create.
|
|
84
|
+
*
|
|
85
|
+
* Shared by the Share dialog (draft seeding) and the share list's
|
|
86
|
+
* pause/resume toggle, so a toggle rebuilds the complete spec with only
|
|
87
|
+
* `enabled` flipped and can never wipe origins, messages, or credential
|
|
88
|
+
* bindings — the fails-closed posture {@link AgentShareDraft} exists to
|
|
89
|
+
* enforce.
|
|
90
|
+
*/
|
|
91
|
+
export function draftFromShare(share: AgentShare | null): AgentShareDraft {
|
|
92
|
+
const spec = share?.spec;
|
|
93
|
+
return {
|
|
94
|
+
enabled: spec?.enabled ?? false,
|
|
95
|
+
audience: sharingAudienceFromProto(spec?.audience),
|
|
96
|
+
allowedOrigins: spec?.allowedOrigins ?? [],
|
|
97
|
+
messages: {
|
|
98
|
+
rateLimited: spec?.messages?.rateLimited ?? "",
|
|
99
|
+
unavailable: spec?.messages?.unavailable ?? "",
|
|
100
|
+
conversationEnded: spec?.messages?.conversationEnded ?? "",
|
|
101
|
+
},
|
|
102
|
+
environmentRefs: (spec?.environmentRefs ?? []).map((ref) => ({
|
|
103
|
+
org: ref.org,
|
|
104
|
+
slug: ref.slug,
|
|
105
|
+
})),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Identity for a share being created: its display name and URL slug in
|
|
111
|
+
* the sharing org's namespace. Only consulted when `current` is `null`
|
|
112
|
+
* — an existing share's identity is immutable (decision 011 D2).
|
|
113
|
+
*/
|
|
114
|
+
export interface AgentShareCreateIdentity {
|
|
115
|
+
/** Display name for the new share. */
|
|
116
|
+
readonly name: string;
|
|
117
|
+
/** URL slug for the new share — appears in `/chat/<org>/<slug>`. */
|
|
118
|
+
readonly slug: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
79
121
|
/** Return value of {@link useSaveAgentShare}. */
|
|
80
122
|
export interface UseSaveAgentShareReturn {
|
|
81
123
|
/**
|
|
@@ -83,12 +125,14 @@ export interface UseSaveAgentShareReturn {
|
|
|
83
125
|
* persisted {@link AgentShare} — adopt it as the new baseline (its
|
|
84
126
|
* `status.shareLinkToken` carries the live link token).
|
|
85
127
|
*
|
|
86
|
-
* `current` is the share being edited, or `null`
|
|
87
|
-
*
|
|
128
|
+
* `current` is the share being edited, or `null` to create one —
|
|
129
|
+
* named by `createIdentity` when given, else the server's defaults
|
|
130
|
+
* (the agent's own name and slug).
|
|
88
131
|
*/
|
|
89
132
|
readonly save: (
|
|
90
133
|
draft: AgentShareDraft,
|
|
91
134
|
current: AgentShare | null,
|
|
135
|
+
createIdentity?: AgentShareCreateIdentity,
|
|
92
136
|
) => Promise<AgentShare | undefined>;
|
|
93
137
|
/** `true` while the RPC is in flight. */
|
|
94
138
|
readonly isPending: boolean;
|
|
@@ -97,36 +141,36 @@ export interface UseSaveAgentShareReturn {
|
|
|
97
141
|
}
|
|
98
142
|
|
|
99
143
|
/**
|
|
100
|
-
* Behavior hook that persists an agent's
|
|
144
|
+
* Behavior hook that persists an agent share's configuration.
|
|
101
145
|
*
|
|
102
146
|
* Commits via `stigmer.agentShare.apply()` — an idempotent upsert keyed
|
|
103
147
|
* on the share's `(org, slug)` identity — so one code path serves both
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
148
|
+
* creation (the server authorizes on the referenced agent's `can_edit`)
|
|
149
|
+
* and every later edit. There is deliberately no create/update
|
|
150
|
+
* branching: the share slug is unique per org, which makes
|
|
151
|
+
* apply-by-identity exact, and the generated `AgentShareInput` carries
|
|
152
|
+
* no `metadata.id` for an update to route by anyway. A create that
|
|
153
|
+
* collides on `(org, slug)` rejects with an already-exists error rather
|
|
154
|
+
* than touching the existing share.
|
|
110
155
|
*
|
|
111
156
|
* Disabling is a save with `enabled: false` — a config-preserving pause
|
|
112
157
|
* (decision 011 D1). Deleting the share is a separate, destructive
|
|
113
|
-
* operation
|
|
158
|
+
* operation ({@link useDeleteAgentShare}).
|
|
114
159
|
*
|
|
115
|
-
* `shareOrg` selects which org
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
160
|
+
* `shareOrg` selects which org owns a created share and defaults to the
|
|
161
|
+
* agent's own org. Pass the viewer's org to create a **cross-org
|
|
162
|
+
* share** (decision 013) — the server then authorizes on the public
|
|
163
|
+
* agent's `can_execute` plus `can_create_agent_share` in the sharing
|
|
164
|
+
* org, and bills that org.
|
|
120
165
|
*
|
|
121
166
|
* Pass `null` for `agent` to produce a stable no-op (useful while the
|
|
122
167
|
* agent is still loading).
|
|
123
168
|
*
|
|
124
169
|
* @example
|
|
125
170
|
* ```tsx
|
|
126
|
-
* const { share } = useAgentShare(agent);
|
|
127
171
|
* const { save, isPending } = useSaveAgentShare(agent);
|
|
128
172
|
*
|
|
129
|
-
* const persisted = await save({ ...
|
|
173
|
+
* const persisted = await save({ ...draftFromShare(share), enabled: true }, share);
|
|
130
174
|
* // persisted.status.shareLinkToken is the live link token
|
|
131
175
|
* ```
|
|
132
176
|
*/
|
|
@@ -147,6 +191,7 @@ export function useSaveAgentShare(
|
|
|
147
191
|
async (
|
|
148
192
|
draft: AgentShareDraft,
|
|
149
193
|
current: AgentShare | null,
|
|
194
|
+
createIdentity?: AgentShareCreateIdentity,
|
|
150
195
|
): Promise<AgentShare | undefined> => {
|
|
151
196
|
if (!agentOrg || !agentSlug) return undefined;
|
|
152
197
|
|
|
@@ -156,13 +201,18 @@ export function useSaveAgentShare(
|
|
|
156
201
|
try {
|
|
157
202
|
return await stigmer.agentShare.apply({
|
|
158
203
|
// Identity: the existing share's org/slug when editing (a
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
// sharing org + the
|
|
162
|
-
// own D2 default, made
|
|
204
|
+
// share may carry a non-default slug — apply with the agent's
|
|
205
|
+
// slug would create a SECOND share); when creating, the
|
|
206
|
+
// sharing org + the caller-chosen identity, falling back to
|
|
207
|
+
// the agent's own slug/name (the server's D2 default, made
|
|
208
|
+
// explicit).
|
|
163
209
|
org: current?.metadata?.org || resolvedShareOrg,
|
|
164
|
-
slug: current?.metadata?.slug || agentSlug,
|
|
165
|
-
name:
|
|
210
|
+
slug: current?.metadata?.slug || createIdentity?.slug || agentSlug,
|
|
211
|
+
name:
|
|
212
|
+
current?.metadata?.name ||
|
|
213
|
+
createIdentity?.name ||
|
|
214
|
+
agentName ||
|
|
215
|
+
agentSlug,
|
|
166
216
|
agentRef: { org: agentOrg, slug: agentSlug },
|
|
167
217
|
enabled: draft.enabled,
|
|
168
218
|
// Written as the explicit enum value, never left unspecified —
|
|
@@ -76,6 +76,13 @@ export interface WorkflowDetailViewProps {
|
|
|
76
76
|
* Receives the execution ID — use for navigation to the execution viewer.
|
|
77
77
|
*/
|
|
78
78
|
readonly onExecutionClick?: (executionId: string) => void;
|
|
79
|
+
/**
|
|
80
|
+
* The viewer's active organization slug, feeding the Instances tab:
|
|
81
|
+
* it scopes the list to this org's instances of the workflow, so a
|
|
82
|
+
* member of several orgs sees the current org context only. Omit to
|
|
83
|
+
* default to the workflow's own org.
|
|
84
|
+
*/
|
|
85
|
+
readonly viewerOrg?: string;
|
|
79
86
|
/**
|
|
80
87
|
* Called when the user clicks "Create Instance" in the Instances tab.
|
|
81
88
|
* Opens the create instance dialog.
|
|
@@ -157,6 +164,7 @@ export function WorkflowDetailView({
|
|
|
157
164
|
onTabChange,
|
|
158
165
|
defaultTab,
|
|
159
166
|
onExecutionClick,
|
|
167
|
+
viewerOrg,
|
|
160
168
|
onCreateInstanceClick,
|
|
161
169
|
onInstanceClick,
|
|
162
170
|
onInstanceRunClick,
|
|
@@ -300,6 +308,7 @@ export function WorkflowDetailView({
|
|
|
300
308
|
workflowId={meta?.id ?? ""}
|
|
301
309
|
defaultInstanceId={workflow?.status?.defaultInstanceId}
|
|
302
310
|
org={org}
|
|
311
|
+
viewerOrg={viewerOrg}
|
|
303
312
|
onCreateClick={onCreateInstanceClick}
|
|
304
313
|
onInstanceClick={onInstanceClick}
|
|
305
314
|
onRunClick={onInstanceRunClick}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { EmptyState } from "../../empty-state/EmptyState.js";
|
|
4
4
|
|
|
5
5
|
/** Props for {@link WorkflowInstanceEmptyState}. */
|
|
6
6
|
export interface WorkflowInstanceEmptyStateProps {
|
|
7
|
-
/** Called when the user clicks "Create
|
|
7
|
+
/** Called when the user clicks "Create instance". */
|
|
8
8
|
readonly onCreateClick?: () => void;
|
|
9
9
|
/** Additional CSS class names. */
|
|
10
10
|
readonly className?: string;
|
|
@@ -15,62 +15,43 @@ export interface WorkflowInstanceEmptyStateProps {
|
|
|
15
15
|
*
|
|
16
16
|
* Communicates the value proposition of workflow instances without
|
|
17
17
|
* mentioning the platform-managed default instance (implementation detail).
|
|
18
|
+
*
|
|
19
|
+
* A thin composition over the shared {@link EmptyState} primitive: only the
|
|
20
|
+
* icon and copy are specific to workflow instances.
|
|
18
21
|
*/
|
|
19
22
|
export function WorkflowInstanceEmptyState({
|
|
20
23
|
onCreateClick,
|
|
21
24
|
className,
|
|
22
25
|
}: WorkflowInstanceEmptyStateProps) {
|
|
23
26
|
return (
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
and access controls. Each instance binds environments (credentials,
|
|
41
|
-
secrets, settings) and can be shared independently.
|
|
42
|
-
</p>
|
|
43
|
-
|
|
44
|
-
{onCreateClick && (
|
|
45
|
-
<button
|
|
46
|
-
type="button"
|
|
47
|
-
onClick={onCreateClick}
|
|
48
|
-
className={cn(
|
|
49
|
-
"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5",
|
|
50
|
-
"text-sm font-medium",
|
|
51
|
-
"bg-primary text-primary-foreground",
|
|
52
|
-
"hover:bg-primary/90",
|
|
53
|
-
"focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
54
|
-
)}
|
|
55
|
-
>
|
|
56
|
-
<PlusIcon />
|
|
57
|
-
Create Instance
|
|
58
|
-
</button>
|
|
59
|
-
)}
|
|
60
|
-
</div>
|
|
27
|
+
<EmptyState
|
|
28
|
+
variant="first-use"
|
|
29
|
+
icon={<LayersIcon />}
|
|
30
|
+
title="No instances yet"
|
|
31
|
+
description={
|
|
32
|
+
"Create instances to run this workflow with different configurations " +
|
|
33
|
+
"and access controls. Each instance binds environments (credentials, " +
|
|
34
|
+
"secrets, settings) and can be shared independently."
|
|
35
|
+
}
|
|
36
|
+
action={
|
|
37
|
+
onCreateClick
|
|
38
|
+
? { label: "Create instance", onClick: onCreateClick, icon: <PlusIcon /> }
|
|
39
|
+
: undefined
|
|
40
|
+
}
|
|
41
|
+
className={className}
|
|
42
|
+
/>
|
|
61
43
|
);
|
|
62
44
|
}
|
|
63
45
|
|
|
64
46
|
function LayersIcon() {
|
|
65
47
|
return (
|
|
66
|
-
<svg width="
|
|
48
|
+
<svg width="24" height="24" viewBox="0 0 20 20" fill="none" aria-hidden="true">
|
|
67
49
|
<path
|
|
68
50
|
d="M10 2L2 6l8 4 8-4-8-4zM2 10l8 4 8-4M2 14l8 4 8-4"
|
|
69
51
|
stroke="currentColor"
|
|
70
52
|
strokeWidth="1.5"
|
|
71
53
|
strokeLinecap="round"
|
|
72
54
|
strokeLinejoin="round"
|
|
73
|
-
className="text-muted-foreground"
|
|
74
55
|
/>
|
|
75
56
|
</svg>
|
|
76
57
|
);
|
|
@@ -78,8 +59,8 @@ function LayersIcon() {
|
|
|
78
59
|
|
|
79
60
|
function PlusIcon() {
|
|
80
61
|
return (
|
|
81
|
-
<svg width="
|
|
82
|
-
<path d="
|
|
62
|
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
|
63
|
+
<path d="M6 2v8M2 6h8" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
|
|
83
64
|
</svg>
|
|
84
65
|
);
|
|
85
66
|
}
|
|
@@ -5,6 +5,7 @@ import { cn } from "@stigmer/theme";
|
|
|
5
5
|
import type { WorkflowInstance } from "@stigmer/protos/ai/stigmer/agentic/workflowinstance/v1/api_pb";
|
|
6
6
|
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
7
7
|
import { useWorkflowInstances } from "../useWorkflowInstances.js";
|
|
8
|
+
import { Button } from "../../button/Button.js";
|
|
8
9
|
import { useEnvironmentList } from "../../environment/useEnvironmentList.js";
|
|
9
10
|
import { ResourceVisibilityControl } from "../../library/ResourceVisibilityControl.js";
|
|
10
11
|
import { PermissionGate } from "../../iam-policy/PermissionGate.js";
|
|
@@ -16,8 +17,19 @@ export interface WorkflowInstanceListProps {
|
|
|
16
17
|
readonly workflowId: string;
|
|
17
18
|
/** The default instance ID (from workflow.status.defaultInstanceId) — filtered out of the list. */
|
|
18
19
|
readonly defaultInstanceId?: string;
|
|
19
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* The WORKFLOW's organization slug (needed for environment resolution
|
|
22
|
+
* and the create flow). Not the list scope — that is `viewerOrg`,
|
|
23
|
+
* which differs from `org` when viewing another org's workflow.
|
|
24
|
+
*/
|
|
20
25
|
readonly org: string;
|
|
26
|
+
/**
|
|
27
|
+
* The viewer's active organization slug. Scopes the list to this
|
|
28
|
+
* org's instances of the workflow, so a member of several orgs sees
|
|
29
|
+
* exactly the current org context's instances. Omit to default to
|
|
30
|
+
* the workflow's own org.
|
|
31
|
+
*/
|
|
32
|
+
readonly viewerOrg?: string;
|
|
21
33
|
/** Called when the user wants to create a new instance. */
|
|
22
34
|
readonly onCreateClick?: () => void;
|
|
23
35
|
/** Called when the user clicks an instance row. */
|
|
@@ -45,6 +57,7 @@ export function WorkflowInstanceList({
|
|
|
45
57
|
workflowId,
|
|
46
58
|
defaultInstanceId,
|
|
47
59
|
org,
|
|
60
|
+
viewerOrg,
|
|
48
61
|
onCreateClick,
|
|
49
62
|
onInstanceClick,
|
|
50
63
|
onRunClick,
|
|
@@ -52,7 +65,13 @@ export function WorkflowInstanceList({
|
|
|
52
65
|
refreshKey,
|
|
53
66
|
className,
|
|
54
67
|
}: WorkflowInstanceListProps) {
|
|
55
|
-
|
|
68
|
+
// Scope the list to the org whose context the viewer is in; when the
|
|
69
|
+
// host passes no viewerOrg the scope falls back to the workflow's own
|
|
70
|
+
// org (the same-org owner flow).
|
|
71
|
+
const { instances, isLoading, error, refetch } = useWorkflowInstances(
|
|
72
|
+
workflowId,
|
|
73
|
+
viewerOrg || org,
|
|
74
|
+
);
|
|
56
75
|
const { environments } = useEnvironmentList(org);
|
|
57
76
|
|
|
58
77
|
// Refetch when refreshKey changes (signals external mutation like create/delete)
|
|
@@ -100,27 +119,21 @@ export function WorkflowInstanceList({
|
|
|
100
119
|
{userInstances.length} {userInstances.length === 1 ? "instance" : "instances"}
|
|
101
120
|
</h3>
|
|
102
121
|
{onCreateClick && (
|
|
103
|
-
<
|
|
104
|
-
|
|
122
|
+
<Button
|
|
123
|
+
variant="outline"
|
|
124
|
+
size="xs"
|
|
125
|
+
icon={<PlusIcon />}
|
|
105
126
|
onClick={onCreateClick}
|
|
106
|
-
className={cn(
|
|
107
|
-
"inline-flex items-center gap-1 rounded-md px-2.5 py-1",
|
|
108
|
-
"text-xs font-medium",
|
|
109
|
-
"border border-border text-foreground",
|
|
110
|
-
"hover:bg-accent-hover",
|
|
111
|
-
"focus:outline-none focus:ring-2 focus:ring-ring",
|
|
112
|
-
)}
|
|
113
127
|
>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
</button>
|
|
128
|
+
Create instance
|
|
129
|
+
</Button>
|
|
117
130
|
)}
|
|
118
131
|
</div>
|
|
119
132
|
|
|
120
133
|
<div className="overflow-hidden rounded-lg border border-border">
|
|
121
134
|
<table className="w-full text-sm">
|
|
122
135
|
<thead>
|
|
123
|
-
<tr className="border-b border-border bg-muted
|
|
136
|
+
<tr className="border-b border-border bg-muted-subtle">
|
|
124
137
|
<th className="px-4 py-2 text-left font-medium text-muted-foreground">Name</th>
|
|
125
138
|
<th className="px-4 py-2 text-left font-medium text-muted-foreground">Environments</th>
|
|
126
139
|
<th className="px-4 py-2 text-left font-medium text-muted-foreground">Visibility</th>
|
|
@@ -267,7 +280,7 @@ function LoadingSkeleton() {
|
|
|
267
280
|
return (
|
|
268
281
|
<div className="space-y-2 py-4">
|
|
269
282
|
{[1, 2, 3].map((i) => (
|
|
270
|
-
<div key={i} className="h-12 rounded-md bg-muted
|
|
283
|
+
<div key={i} className="h-12 animate-pulse rounded-md bg-muted-faint" />
|
|
271
284
|
))}
|
|
272
285
|
</div>
|
|
273
286
|
);
|
|
@@ -21,21 +21,27 @@ export interface UseWorkflowInstancesReturn {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* Data hook that fetches
|
|
24
|
+
* Data hook that fetches instances for a specific workflow.
|
|
25
25
|
*
|
|
26
26
|
* Wraps `stigmer.workflowInstance.getByWorkflow()` with loading
|
|
27
27
|
* and error state management. Used on the Workflow detail page
|
|
28
28
|
* "Instances" tab to show environment-bound deployments.
|
|
29
29
|
*
|
|
30
|
+
* Pass `org` to scope the list to one organization's instances — the
|
|
31
|
+
* org-context view a console tab needs. The server applies the scope
|
|
32
|
+
* (it only ever narrows the permission-bounded view); omit `org` for
|
|
33
|
+
* the full permission-bounded list.
|
|
34
|
+
*
|
|
30
35
|
* Pass `null` as `workflowId` to skip fetching (stable no-op).
|
|
31
36
|
*
|
|
32
37
|
* @example
|
|
33
38
|
* ```tsx
|
|
34
|
-
* const { instances, isLoading } = useWorkflowInstances(workflow.metadata?.id);
|
|
39
|
+
* const { instances, isLoading } = useWorkflowInstances(workflow.metadata?.id, viewerOrg);
|
|
35
40
|
* ```
|
|
36
41
|
*/
|
|
37
42
|
export function useWorkflowInstances(
|
|
38
43
|
workflowId: string | null | undefined,
|
|
44
|
+
org?: string,
|
|
39
45
|
): UseWorkflowInstancesReturn {
|
|
40
46
|
const stigmer = useStigmer();
|
|
41
47
|
|
|
@@ -44,6 +50,7 @@ export function useWorkflowInstances(
|
|
|
44
50
|
const resp = await stigmer.workflowInstance.getByWorkflow(
|
|
45
51
|
create(GetWorkflowInstancesByWorkflowRequestSchema, {
|
|
46
52
|
workflowId,
|
|
53
|
+
org: org ?? "",
|
|
47
54
|
}),
|
|
48
55
|
);
|
|
49
56
|
return resp.entries ? [...resp.entries] : [];
|
|
@@ -56,7 +63,7 @@ export function useWorkflowInstances(
|
|
|
56
63
|
isRefetching,
|
|
57
64
|
error,
|
|
58
65
|
refetch,
|
|
59
|
-
} = useFetch<readonly WorkflowInstance[]>(fetchFn, [workflowId, stigmer], []);
|
|
66
|
+
} = useFetch<readonly WorkflowInstance[]>(fetchFn, [workflowId, org, stigmer], []);
|
|
60
67
|
|
|
61
68
|
return { instances, isLoading, isRefetching, error, refetch };
|
|
62
69
|
}
|