orcheo-studio 0.21.3 → 0.23.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/.bumpversion.cfg +1 -1
- package/dist/assets/{arc-CI8iBP22.js → arc-4lqv6Kjm.js} +1 -1
- package/dist/assets/{architectureDiagram-VXUJARFQ-9lccnHEL.js → architectureDiagram-VXUJARFQ-DjhiFMDA.js} +1 -1
- package/dist/assets/{blockDiagram-VD42YOAC-CJPKWPSp.js → blockDiagram-VD42YOAC-AZ1Rspxy.js} +1 -1
- package/dist/assets/{c4Diagram-YG6GDRKO-CuW7nex5.js → c4Diagram-YG6GDRKO-eH7LJ-pB.js} +1 -1
- package/dist/assets/channel-BbjHmdR-.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB-BVQ3rR7o.js → chunk-4BX2VUAB-DJPBOwLj.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-BTpqJ_MA.js → chunk-55IACEB6-BtkroFP3.js} +1 -1
- package/dist/assets/{chunk-B4BG7PRW-Duatgdyr.js → chunk-B4BG7PRW-9UuoCVI1.js} +1 -1
- package/dist/assets/{chunk-DI55MBZ5-CxEvlbgl.js → chunk-DI55MBZ5-DFossSVg.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-C07lS0oH.js → chunk-FMBD7UC4-DZoCKsqW.js} +1 -1
- package/dist/assets/{chunk-QN33PNHL-C09PhK0w.js → chunk-QN33PNHL-CGFY-glY.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-Dy3GgrIN.js → chunk-QZHKN3VN-CBx9MjYx.js} +1 -1
- package/dist/assets/{chunk-TZMSLE5B-9HPWR_AG.js → chunk-TZMSLE5B-BQ1P9b2p.js} +1 -1
- package/dist/assets/classDiagram-2ON5EDUG-B3gA5-gj.js +1 -0
- package/dist/assets/classDiagram-v2-WZHVMYZB-B3gA5-gj.js +1 -0
- package/dist/assets/clone-Ctw1pu1z.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-B9TWyynz.js → cose-bilkent-S5V4N54A-Dy5ctOeT.js} +1 -1
- package/dist/assets/{dagre-6UL2VRFP-DFuJOuNQ.js → dagre-6UL2VRFP-CFPE5-yw.js} +1 -1
- package/dist/assets/{diagram-PSM6KHXK-3Jj-OXdd.js → diagram-PSM6KHXK-DmESDfR2.js} +1 -1
- package/dist/assets/{diagram-QEK2KX5R-DtAwZnbJ.js → diagram-QEK2KX5R-CumSxfGH.js} +1 -1
- package/dist/assets/{diagram-S2PKOQOG-atZg3Snz.js → diagram-S2PKOQOG-C1AaIowU.js} +1 -1
- package/dist/assets/{erDiagram-Q2GNP2WA-BoqqRkgy.js → erDiagram-Q2GNP2WA-BKk1ijaf.js} +1 -1
- package/dist/assets/{flowDiagram-NV44I4VS-Bmg68IjU.js → flowDiagram-NV44I4VS-BFMJTeTZ.js} +1 -1
- package/dist/assets/{ganttDiagram-JELNMOA3-53TZ4oeR.js → ganttDiagram-JELNMOA3-CYobSBkw.js} +1 -1
- package/dist/assets/{gitGraphDiagram-V2S2FVAM-D1DddraJ.js → gitGraphDiagram-V2S2FVAM-DYNLBSGu.js} +1 -1
- package/dist/assets/{graph-Cb10Gpkt.js → graph-CCH_5x2X.js} +1 -1
- package/dist/assets/index-BVY_PYJU.css +1 -0
- package/dist/assets/index-CD3NusgQ.js +790 -0
- package/dist/assets/{infoDiagram-HS3SLOUP-DxsHJs_0.js → infoDiagram-HS3SLOUP-C8K2nOgc.js} +1 -1
- package/dist/assets/{isUndefined-D1qU3_L5.js → isUndefined-Cg-i6rYz.js} +1 -1
- package/dist/assets/{journeyDiagram-XKPGCS4Q-CZDE4S5c.js → journeyDiagram-XKPGCS4Q-UjysRuQi.js} +1 -1
- package/dist/assets/{kanban-definition-3W4ZIXB7-pRhzMZ6H.js → kanban-definition-3W4ZIXB7-C0mLJbkK.js} +1 -1
- package/dist/assets/{layout-C0AW3yEL.js → layout-Ctq7ed-p.js} +1 -1
- package/dist/assets/{linear-DobjN4tM.js → linear-MdTfjVMd.js} +1 -1
- package/dist/assets/{min-B3Oh8jmi.js → min-CkeT6BIA.js} +1 -1
- package/dist/assets/{mindmap-definition-VGOIOE7T-g-SEeftZ.js → mindmap-definition-VGOIOE7T-B1KVkl4d.js} +1 -1
- package/dist/assets/{pieDiagram-ADFJNKIX-DiwhMRkx.js → pieDiagram-ADFJNKIX-Cw6TMSJj.js} +1 -1
- package/dist/assets/{quadrantDiagram-AYHSOK5B-BIZMdGTC.js → quadrantDiagram-AYHSOK5B-8Lq_anf6.js} +1 -1
- package/dist/assets/{requirementDiagram-UZGBJVZJ-D5nzjn-8.js → requirementDiagram-UZGBJVZJ-CXBuMo24.js} +1 -1
- package/dist/assets/{sankeyDiagram-TZEHDZUN-CnCESEvf.js → sankeyDiagram-TZEHDZUN-DggxeLrr.js} +1 -1
- package/dist/assets/{sequenceDiagram-WL72ISMW-DKBzac_O.js → sequenceDiagram-WL72ISMW-DZqrjzbw.js} +1 -1
- package/dist/assets/{stateDiagram-FKZM4ZOC-CcT1-5cM.js → stateDiagram-FKZM4ZOC-D4rhKl_S.js} +1 -1
- package/dist/assets/stateDiagram-v2-4FDKWEC3-C81OVjQ3.js +1 -0
- package/dist/assets/{timeline-definition-IT6M3QCI-DhFmWcr3.js → timeline-definition-IT6M3QCI-vp3knils.js} +1 -1
- package/dist/assets/{treemap-GDKQZRPO-CtMKB2mb.js → treemap-GDKQZRPO-RByzG6o8.js} +1 -1
- package/dist/assets/{xychartDiagram-PRI3JC2R-OhMt1B4k.js → xychartDiagram-PRI3JC2R-DOLBdPza.js} +1 -1
- package/dist/index.html +2 -7
- package/index.html +0 -5
- package/package.json +1 -1
- package/src/App.tsx +27 -2
- package/src/features/account/pages/invitation-accept.tsx +92 -0
- package/src/features/account/pages/workspace-members.tsx +153 -42
- package/src/features/auth/components/require-auth.tsx +13 -18
- package/src/features/auth/lib/auth-api.ts +196 -0
- package/src/features/auth/lib/auth-session.ts +0 -1
- package/src/features/auth/pages/login.tsx +169 -16
- package/src/features/auth/pages/verify.tsx +89 -0
- package/src/features/chatkit/components/public-chat-widget.tsx +89 -8
- package/src/features/chatkit/lib/chatkit-client.ts +66 -0
- package/src/features/chatkit/pages/public-chat.tsx +24 -33
- package/src/features/shared/components/top-navigation/account-menu.tsx +20 -7
- package/src/features/shared/components/top-navigation/studio-brand.tsx +14 -3
- package/src/features/workflow/components/dialogs/publish-workflow-dialog.tsx +121 -0
- package/src/features/workflow/data/templates/candidate-badges.ts +5 -0
- package/src/features/workflow/data/workflow-types.ts +1 -0
- package/src/features/workflow/lib/workflow-storage-api.ts +35 -4
- package/src/features/workflow/lib/workflow-storage-helpers.ts +15 -6
- package/src/features/workflow/lib/workflow-storage.ts +2 -1
- package/src/features/workflow/lib/workflow-storage.types.ts +10 -0
- package/src/features/workflow/pages/workflow/components/workflow-tab-content.tsx +122 -37
- package/src/features/workflow/pages/workflow/hooks/controller/build-layout-props.ts +2 -0
- package/src/features/workflow/pages/workflow/hooks/controller/use-workflow-lifecycle.ts +1 -0
- package/src/features/workflow/pages/workflow/hooks/use-workflow-loader.ts +5 -0
- package/src/features/workflow/pages/workflow/hooks/use-workflow-metadata-state.ts +3 -0
- package/src/features/workflow/pages/workflow-gallery/create-team-dialog.tsx +137 -0
- package/src/features/workflow/pages/workflow-gallery/onboard-team-dialog.tsx +57 -0
- package/src/features/workflow/pages/workflow-gallery/team-section.tsx +58 -0
- package/src/features/workflow/pages/workflow-gallery/use-workflow-gallery-actions.ts +124 -21
- package/src/features/workflow/pages/workflow-gallery/use-workflow-gallery-state.ts +22 -2
- package/src/features/workflow/pages/workflow-gallery/use-workflow-gallery.ts +2 -0
- package/src/features/workflow/pages/workflow-gallery/workflow-card.tsx +26 -5
- package/src/features/workflow/pages/workflow-gallery/workflow-gallery-tabs.tsx +92 -17
- package/src/features/workflow/pages/workflow-gallery.tsx +45 -1
- package/src/lib/api.ts +91 -2
- package/src/lib/workspace-routing.ts +15 -0
- package/src/main.tsx +7 -0
- package/src/vite-env.d.ts +1 -0
- package/dist/assets/channel-C-DaAO-p.js +0 -1
- package/dist/assets/classDiagram-2ON5EDUG-B2LNkTFb.js +0 -1
- package/dist/assets/classDiagram-v2-WZHVMYZB-B2LNkTFb.js +0 -1
- package/dist/assets/clone-DcGVOYZT.js +0 -1
- package/dist/assets/index-B0_h2wCA.css +0 -1
- package/dist/assets/index-DmIcay1T.js +0 -775
- package/dist/assets/stateDiagram-v2-4FDKWEC3-Cle1H2JX.js +0 -1
- package/src/features/MODULES.md +0 -199
- package/src/features/auth/components/auto-login.tsx +0 -51
- package/src/features/auth/lib/oidc-client.ts +0 -565
- package/src/features/auth/pages/oauth-callback.tsx +0 -63
|
@@ -48,9 +48,20 @@ export default function StudioBrand() {
|
|
|
48
48
|
</Badge>
|
|
49
49
|
</button>
|
|
50
50
|
</TooltipTrigger>
|
|
51
|
-
<TooltipContent side="bottom" className="max-w-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
<TooltipContent side="bottom" className="max-w-sm">
|
|
52
|
+
Beta Disclaimer & Terms: Orcheo Cloud is a free beta for evaluation
|
|
53
|
+
and testing, provided as-is. Data, workflows, and credentials may be
|
|
54
|
+
reset, deleted, or not migrated, so do not use it as your only
|
|
55
|
+
storage for critical production data; read the{" "}
|
|
56
|
+
<a
|
|
57
|
+
href="https://ai-colleagues.com/terms"
|
|
58
|
+
target="_blank"
|
|
59
|
+
rel="noreferrer"
|
|
60
|
+
className="font-medium underline underline-offset-2"
|
|
61
|
+
>
|
|
62
|
+
full terms
|
|
63
|
+
</a>
|
|
64
|
+
.
|
|
54
65
|
</TooltipContent>
|
|
55
66
|
</Tooltip>
|
|
56
67
|
</TooltipProvider>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Check, Globe, Lock } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
import { Button } from "@/design-system/ui/button";
|
|
5
|
+
import {
|
|
6
|
+
Dialog,
|
|
7
|
+
DialogContent,
|
|
8
|
+
DialogDescription,
|
|
9
|
+
DialogFooter,
|
|
10
|
+
DialogHeader,
|
|
11
|
+
DialogTitle,
|
|
12
|
+
} from "@/design-system/ui/dialog";
|
|
13
|
+
import { cn } from "@/lib/utils";
|
|
14
|
+
|
|
15
|
+
export type PublishVisibility = "public" | "workspace";
|
|
16
|
+
|
|
17
|
+
interface PublishOption {
|
|
18
|
+
value: PublishVisibility;
|
|
19
|
+
title: string;
|
|
20
|
+
description: string;
|
|
21
|
+
icon: typeof Globe;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const PUBLISH_OPTIONS: PublishOption[] = [
|
|
25
|
+
{
|
|
26
|
+
value: "public",
|
|
27
|
+
title: "Public",
|
|
28
|
+
description:
|
|
29
|
+
"Anyone with the link can use the workflow. No sign-in required.",
|
|
30
|
+
icon: Globe,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
value: "workspace",
|
|
34
|
+
title: "Workspace only",
|
|
35
|
+
description:
|
|
36
|
+
"Only signed-in members of this workspace can use the workflow.",
|
|
37
|
+
icon: Lock,
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
interface PublishWorkflowDialogProps {
|
|
42
|
+
open: boolean;
|
|
43
|
+
isPending?: boolean;
|
|
44
|
+
onOpenChange: (open: boolean) => void;
|
|
45
|
+
onConfirm: (requireLogin: boolean) => Promise<void> | void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function PublishWorkflowDialog({
|
|
49
|
+
open,
|
|
50
|
+
isPending = false,
|
|
51
|
+
onOpenChange,
|
|
52
|
+
onConfirm,
|
|
53
|
+
}: PublishWorkflowDialogProps) {
|
|
54
|
+
const [selected, setSelected] = useState<PublishVisibility>("public");
|
|
55
|
+
const handleOpenChange = (nextOpen: boolean) => {
|
|
56
|
+
if (!nextOpen) {
|
|
57
|
+
setSelected("public");
|
|
58
|
+
}
|
|
59
|
+
onOpenChange(nextOpen);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<Dialog open={open} onOpenChange={handleOpenChange}>
|
|
64
|
+
<DialogContent>
|
|
65
|
+
<DialogHeader>
|
|
66
|
+
<DialogTitle>Publish workflow</DialogTitle>
|
|
67
|
+
<DialogDescription>
|
|
68
|
+
Choose who can access the published chat.
|
|
69
|
+
</DialogDescription>
|
|
70
|
+
</DialogHeader>
|
|
71
|
+
<div className="grid gap-3 py-2">
|
|
72
|
+
{PUBLISH_OPTIONS.map((option) => {
|
|
73
|
+
const Icon = option.icon;
|
|
74
|
+
const isSelected = selected === option.value;
|
|
75
|
+
return (
|
|
76
|
+
<button
|
|
77
|
+
key={option.value}
|
|
78
|
+
type="button"
|
|
79
|
+
aria-pressed={isSelected}
|
|
80
|
+
onClick={() => setSelected(option.value)}
|
|
81
|
+
disabled={isPending}
|
|
82
|
+
className={cn(
|
|
83
|
+
"flex items-start gap-3 rounded-lg border p-3 text-left transition-colors",
|
|
84
|
+
isSelected
|
|
85
|
+
? "border-primary bg-primary/5"
|
|
86
|
+
: "border-border hover:bg-muted/40",
|
|
87
|
+
)}
|
|
88
|
+
>
|
|
89
|
+
<Icon className="mt-0.5 h-5 w-5 shrink-0 text-muted-foreground" />
|
|
90
|
+
<div className="min-w-0 flex-1">
|
|
91
|
+
<p className="text-sm font-medium">{option.title}</p>
|
|
92
|
+
<p className="text-xs text-muted-foreground">
|
|
93
|
+
{option.description}
|
|
94
|
+
</p>
|
|
95
|
+
</div>
|
|
96
|
+
{isSelected ? (
|
|
97
|
+
<Check className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
|
|
98
|
+
) : null}
|
|
99
|
+
</button>
|
|
100
|
+
);
|
|
101
|
+
})}
|
|
102
|
+
</div>
|
|
103
|
+
<DialogFooter>
|
|
104
|
+
<Button
|
|
105
|
+
variant="outline"
|
|
106
|
+
onClick={() => handleOpenChange(false)}
|
|
107
|
+
disabled={isPending}
|
|
108
|
+
>
|
|
109
|
+
Cancel
|
|
110
|
+
</Button>
|
|
111
|
+
<Button
|
|
112
|
+
onClick={() => void onConfirm(selected === "workspace")}
|
|
113
|
+
disabled={isPending}
|
|
114
|
+
>
|
|
115
|
+
{isPending ? "Publishing..." : "Publish"}
|
|
116
|
+
</Button>
|
|
117
|
+
</DialogFooter>
|
|
118
|
+
</DialogContent>
|
|
119
|
+
</Dialog>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
@@ -132,6 +132,11 @@ export const getCandidateBadgeDefinition = (
|
|
|
132
132
|
): CandidateBadgeDefinition | undefined =>
|
|
133
133
|
candidateBadges.find((badge) => badge.workflow.id === workflowId);
|
|
134
134
|
|
|
135
|
+
export const getCandidateBadgeByHandle = (
|
|
136
|
+
handle: string,
|
|
137
|
+
): CandidateBadgeDefinition | undefined =>
|
|
138
|
+
candidateBadges.find((badge) => badge.handle === handle);
|
|
139
|
+
|
|
135
140
|
export const getCandidateTemplateDefinition = (
|
|
136
141
|
templateId: string,
|
|
137
142
|
): WorkflowTemplateDefinition | undefined =>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { authFetch } from "@/lib/auth-fetch";
|
|
2
2
|
import { buildBackendHttpUrl, getBackendBaseUrl } from "@/lib/config";
|
|
3
3
|
import type {
|
|
4
|
+
ApiTeam,
|
|
4
5
|
ApiWorkflow,
|
|
5
6
|
ApiWorkflowPagePayload,
|
|
6
7
|
ApiWorkflowRun,
|
|
@@ -105,11 +106,19 @@ export const fetchWorkflowPageData = async (
|
|
|
105
106
|
|
|
106
107
|
export const fetchPublicWorkflow = async (
|
|
107
108
|
workflowId: string,
|
|
109
|
+
context?: { workspaceSlug?: string; teamSlug?: string },
|
|
108
110
|
): Promise<PublicWorkflowMetadata | undefined> => {
|
|
109
111
|
try {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
const params = new URLSearchParams();
|
|
113
|
+
if (context?.workspaceSlug) {
|
|
114
|
+
params.set("workspace_slug", context.workspaceSlug);
|
|
115
|
+
}
|
|
116
|
+
if (context?.teamSlug) {
|
|
117
|
+
params.set("team_slug", context.teamSlug);
|
|
118
|
+
}
|
|
119
|
+
const queryString = params.toString();
|
|
120
|
+
const url = `${PUBLIC_WORKFLOW_PATH}/${workflowId}/public${queryString ? `?${queryString}` : ""}`;
|
|
121
|
+
return await request<PublicWorkflowMetadata>(url);
|
|
113
122
|
} catch (error) {
|
|
114
123
|
if (
|
|
115
124
|
error instanceof ApiRequestError &&
|
|
@@ -175,12 +184,34 @@ export interface ApiCandidate {
|
|
|
175
184
|
export const fetchCandidates = async (): Promise<ApiCandidate[]> =>
|
|
176
185
|
request<ApiCandidate[]>("/api/candidates");
|
|
177
186
|
|
|
187
|
+
export const fetchTeams = async (): Promise<ApiTeam[]> =>
|
|
188
|
+
request<ApiTeam[]>("/api/teams");
|
|
189
|
+
|
|
190
|
+
export const createTeam = async (input: {
|
|
191
|
+
name: string;
|
|
192
|
+
slug: string;
|
|
193
|
+
}): Promise<ApiTeam> =>
|
|
194
|
+
request<ApiTeam>("/api/teams", {
|
|
195
|
+
method: "POST",
|
|
196
|
+
body: JSON.stringify(input),
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
export const deleteTeam = async (teamId: string): Promise<void> =>
|
|
200
|
+
request<void>(`/api/teams/${teamId}`, {
|
|
201
|
+
method: "DELETE",
|
|
202
|
+
expectJson: false,
|
|
203
|
+
});
|
|
204
|
+
|
|
178
205
|
export const onboardCandidate = async (
|
|
179
206
|
candidateId: string,
|
|
207
|
+
teamId?: string,
|
|
180
208
|
): Promise<ApiWorkflow> =>
|
|
181
209
|
request<ApiWorkflow>("/api/candidates/onboard", {
|
|
182
210
|
method: "POST",
|
|
183
|
-
body: JSON.stringify({
|
|
211
|
+
body: JSON.stringify({
|
|
212
|
+
id: candidateId,
|
|
213
|
+
...(teamId ? { team_id: teamId } : {}),
|
|
214
|
+
}),
|
|
184
215
|
});
|
|
185
216
|
|
|
186
217
|
export const fetchWorkflowListeners = async (
|
|
@@ -135,9 +135,15 @@ export const toFlowEdges = (edges: WorkflowEdge[]): FlowEdge[] =>
|
|
|
135
135
|
|
|
136
136
|
export const getWorkflowRouteRef = (
|
|
137
137
|
workflow:
|
|
138
|
-
| Pick<ApiWorkflow, "id" | "handle">
|
|
139
|
-
| Pick<Workflow, "id" | "handle">,
|
|
140
|
-
): string =>
|
|
138
|
+
| Pick<ApiWorkflow, "id" | "handle" | "team_id">
|
|
139
|
+
| Pick<Workflow, "id" | "handle" | "teamId">,
|
|
140
|
+
): string => {
|
|
141
|
+
const teamId = "team_id" in workflow ? workflow.team_id : workflow.teamId;
|
|
142
|
+
if (teamId) {
|
|
143
|
+
return workflow.id;
|
|
144
|
+
}
|
|
145
|
+
return workflow.handle ?? workflow.id;
|
|
146
|
+
};
|
|
141
147
|
|
|
142
148
|
export const resolveWorkflowVersionMermaidSource = (
|
|
143
149
|
version:
|
|
@@ -212,8 +218,7 @@ const parseWorkflowMetadata = (
|
|
|
212
218
|
}
|
|
213
219
|
|
|
214
220
|
const metadataRecord = metadata as Record<string, unknown>;
|
|
215
|
-
const workflowMetadata =
|
|
216
|
-
metadataRecord.workflow ?? metadataRecord.canvas;
|
|
221
|
+
const workflowMetadata = metadataRecord.workflow ?? metadataRecord.canvas;
|
|
217
222
|
if (!workflowMetadata || typeof workflowMetadata !== "object") {
|
|
218
223
|
return (
|
|
219
224
|
resolveTemplateFallback() ?? {
|
|
@@ -226,7 +231,9 @@ const parseWorkflowMetadata = (
|
|
|
226
231
|
}
|
|
227
232
|
|
|
228
233
|
const workflowRecord = workflowMetadata as Record<string, unknown>;
|
|
229
|
-
const snapshotPayload = workflowRecord.snapshot as
|
|
234
|
+
const snapshotPayload = workflowRecord.snapshot as
|
|
235
|
+
| WorkflowSnapshot
|
|
236
|
+
| undefined;
|
|
230
237
|
const summaryPayload = workflowRecord.summary as
|
|
231
238
|
| WorkflowDiffResult["summary"]
|
|
232
239
|
| undefined;
|
|
@@ -351,6 +358,7 @@ export const toStoredWorkflow = (
|
|
|
351
358
|
return {
|
|
352
359
|
id: workflow.id,
|
|
353
360
|
handle: workflow.handle ?? undefined,
|
|
361
|
+
teamId: workflow.team_id ?? undefined,
|
|
354
362
|
name: workflow.name,
|
|
355
363
|
description: workflow.description ?? undefined,
|
|
356
364
|
avatarEmoji,
|
|
@@ -366,6 +374,7 @@ export const toStoredWorkflow = (
|
|
|
366
374
|
lastRun: undefined,
|
|
367
375
|
isArchived: workflow.is_archived,
|
|
368
376
|
isPublic: workflow.is_public,
|
|
377
|
+
requireLogin: workflow.require_login,
|
|
369
378
|
shareUrl: workflow.share_url ?? null,
|
|
370
379
|
chatkitStartScreenPrompts: workflow.chatkit?.start_screen_prompts ?? null,
|
|
371
380
|
chatkitSupportedModels: workflow.chatkit?.supported_models ?? null,
|
|
@@ -458,8 +458,9 @@ export const deleteWorkflow = async (
|
|
|
458
458
|
|
|
459
459
|
export const onboardCandidateAsWorkflow = async (
|
|
460
460
|
candidateId: string,
|
|
461
|
+
teamId?: string,
|
|
461
462
|
): Promise<StoredWorkflow> => {
|
|
462
|
-
const apiWorkflow = await onboardCandidate(candidateId);
|
|
463
|
+
const apiWorkflow = await onboardCandidate(candidateId, teamId);
|
|
463
464
|
invalidateWorkflowCache(apiWorkflow.id);
|
|
464
465
|
const stored = await ensureWorkflow(apiWorkflow.id);
|
|
465
466
|
if (!stored) {
|
|
@@ -6,9 +6,17 @@ import type {
|
|
|
6
6
|
import type { WorkflowDiffResult, WorkflowSnapshot } from "./workflow-diff";
|
|
7
7
|
import type { RJSFSchema } from "@rjsf/utils";
|
|
8
8
|
|
|
9
|
+
export interface ApiTeam {
|
|
10
|
+
id: string;
|
|
11
|
+
slug: string;
|
|
12
|
+
name: string;
|
|
13
|
+
is_default: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
9
16
|
export interface ApiWorkflow {
|
|
10
17
|
id: string;
|
|
11
18
|
handle?: string | null;
|
|
19
|
+
team_id?: string | null;
|
|
12
20
|
name: string;
|
|
13
21
|
slug: string;
|
|
14
22
|
description: string | null;
|
|
@@ -237,9 +245,11 @@ export interface WorkflowVersionRecord {
|
|
|
237
245
|
|
|
238
246
|
export interface StoredWorkflow extends Workflow {
|
|
239
247
|
versions: WorkflowVersionRecord[];
|
|
248
|
+
teamId?: string | null;
|
|
240
249
|
draftAccess?: "personal" | "authenticated" | "workspace";
|
|
241
250
|
isArchived?: boolean;
|
|
242
251
|
isPublic?: boolean;
|
|
252
|
+
requireLogin?: boolean;
|
|
243
253
|
shareUrl?: string | null;
|
|
244
254
|
chatkitStartScreenPrompts?: ChatKitStartScreenPrompt[] | null;
|
|
245
255
|
chatkitSupportedModels?: ChatKitSupportedModel[] | null;
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
AlertTriangle,
|
|
5
5
|
Copy,
|
|
6
6
|
ExternalLink,
|
|
7
|
+
LoaderCircle,
|
|
7
8
|
Play,
|
|
8
9
|
RefreshCw,
|
|
9
10
|
UserMinus,
|
|
@@ -25,8 +26,15 @@ import {
|
|
|
25
26
|
} from "@/design-system/ui/alert-dialog";
|
|
26
27
|
import { Button } from "@/design-system/ui/button";
|
|
27
28
|
import { Switch } from "@/design-system/ui/switch";
|
|
29
|
+
import {
|
|
30
|
+
Tooltip,
|
|
31
|
+
TooltipContent,
|
|
32
|
+
TooltipProvider,
|
|
33
|
+
TooltipTrigger,
|
|
34
|
+
} from "@/design-system/ui/tooltip";
|
|
28
35
|
import { toast } from "@/hooks/use-toast";
|
|
29
36
|
import { ConfirmDeleteWorkflowDialog } from "@features/workflow/components/dialogs/confirm-delete-workflow-dialog";
|
|
37
|
+
import { PublishWorkflowDialog } from "@features/workflow/components/dialogs/publish-workflow-dialog";
|
|
30
38
|
import { UpdateWorkflowDialog } from "@features/workflow/components/dialogs/update-workflow-dialog";
|
|
31
39
|
import { deleteWorkflow } from "@features/workflow/lib/workflow-storage";
|
|
32
40
|
import {
|
|
@@ -57,10 +65,12 @@ export interface WorkflowTabContentProps {
|
|
|
57
65
|
isLoading: boolean;
|
|
58
66
|
loadError: string | null;
|
|
59
67
|
isRunPending: boolean;
|
|
68
|
+
isRunning: boolean;
|
|
60
69
|
onRunWorkflow: () => Promise<void>;
|
|
61
70
|
onSaveConfig: (nextConfig: WorkflowRunnableConfig | null) => Promise<void>;
|
|
62
71
|
hasCronTriggerNode: boolean;
|
|
63
72
|
initialIsPublished: boolean;
|
|
73
|
+
initialRequireLogin: boolean;
|
|
64
74
|
initialShareUrl: string | null;
|
|
65
75
|
missingCredentials?: string[];
|
|
66
76
|
}
|
|
@@ -186,10 +196,12 @@ export function WorkflowTabContent({
|
|
|
186
196
|
isLoading,
|
|
187
197
|
loadError,
|
|
188
198
|
isRunPending,
|
|
199
|
+
isRunning,
|
|
189
200
|
onRunWorkflow,
|
|
190
201
|
onSaveConfig,
|
|
191
202
|
hasCronTriggerNode,
|
|
192
203
|
initialIsPublished,
|
|
204
|
+
initialRequireLogin,
|
|
193
205
|
initialShareUrl,
|
|
194
206
|
missingCredentials = [],
|
|
195
207
|
}: WorkflowTabContentProps) {
|
|
@@ -198,9 +210,11 @@ export function WorkflowTabContent({
|
|
|
198
210
|
const latestVersion = versions.at(-1);
|
|
199
211
|
const [isConfigOpen, setIsConfigOpen] = useState(false);
|
|
200
212
|
const [isPublished, setIsPublished] = useState(initialIsPublished);
|
|
213
|
+
const [requireLogin, setRequireLogin] = useState(initialRequireLogin);
|
|
201
214
|
const [isScheduled, setIsScheduled] = useState(false);
|
|
202
215
|
const [shareUrl, setShareUrl] = useState<string | null>(initialShareUrl);
|
|
203
216
|
const [isPublishPending, setIsPublishPending] = useState(false);
|
|
217
|
+
const [isPublishDialogOpen, setIsPublishDialogOpen] = useState(false);
|
|
204
218
|
const [isSchedulePending, setIsSchedulePending] = useState(false);
|
|
205
219
|
const [diagramSvg, setDiagramSvg] = useState<string | null>(null);
|
|
206
220
|
const [diagramError, setDiagramError] = useState<string | null>(null);
|
|
@@ -216,12 +230,14 @@ export function WorkflowTabContent({
|
|
|
216
230
|
if (!workflowId) {
|
|
217
231
|
setIsPublished(false);
|
|
218
232
|
setIsScheduled(false);
|
|
233
|
+
setRequireLogin(false);
|
|
219
234
|
setShareUrl(null);
|
|
220
235
|
return;
|
|
221
236
|
}
|
|
222
237
|
setIsPublished(initialIsPublished);
|
|
238
|
+
setRequireLogin(initialRequireLogin);
|
|
223
239
|
setShareUrl(initialShareUrl);
|
|
224
|
-
}, [initialIsPublished, initialShareUrl, workflowId]);
|
|
240
|
+
}, [initialIsPublished, initialRequireLogin, initialShareUrl, workflowId]);
|
|
225
241
|
|
|
226
242
|
useEffect(() => {
|
|
227
243
|
if (!workflowId) {
|
|
@@ -375,6 +391,8 @@ export function WorkflowTabContent({
|
|
|
375
391
|
|
|
376
392
|
const canConfigure = Boolean(workflowId);
|
|
377
393
|
const canRun = Boolean(workflowId && latestVersion);
|
|
394
|
+
const runButtonDisabled = !canRun || isRunPending || isRunning;
|
|
395
|
+
const runButtonLabel = isRunPending || isRunning ? "Running..." : "Run";
|
|
378
396
|
const latestConfig = latestVersion?.runnableConfig ?? null;
|
|
379
397
|
const canToggleSchedule = hasCronTriggerNode || isScheduled;
|
|
380
398
|
|
|
@@ -409,33 +427,60 @@ export function WorkflowTabContent({
|
|
|
409
427
|
return;
|
|
410
428
|
}
|
|
411
429
|
|
|
430
|
+
if (nextValue) {
|
|
431
|
+
setIsPublishDialogOpen(true);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
412
435
|
setIsPublishPending(true);
|
|
413
436
|
try {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
"Workflow is now public and available via its chat URL.",
|
|
423
|
-
});
|
|
424
|
-
} else {
|
|
425
|
-
await unpublishWorkflow(workflowId, "studio");
|
|
426
|
-
setIsPublished(false);
|
|
427
|
-
setShareUrl(null);
|
|
428
|
-
toast({
|
|
429
|
-
title: "Workflow unpublished",
|
|
430
|
-
description: "Workflow is now private.",
|
|
431
|
-
});
|
|
432
|
-
}
|
|
437
|
+
await unpublishWorkflow(workflowId, "studio");
|
|
438
|
+
setIsPublished(false);
|
|
439
|
+
setRequireLogin(false);
|
|
440
|
+
setShareUrl(null);
|
|
441
|
+
toast({
|
|
442
|
+
title: "Workflow unpublished",
|
|
443
|
+
description: "Workflow is now private.",
|
|
444
|
+
});
|
|
433
445
|
} catch (error) {
|
|
434
|
-
setIsPublished(
|
|
446
|
+
setIsPublished(true);
|
|
435
447
|
toast({
|
|
436
|
-
title:
|
|
437
|
-
|
|
438
|
-
|
|
448
|
+
title: "Failed to unpublish workflow",
|
|
449
|
+
description: getErrorMessage(error, "Unable to update publish status."),
|
|
450
|
+
variant: "destructive",
|
|
451
|
+
});
|
|
452
|
+
} finally {
|
|
453
|
+
setIsPublishPending(false);
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
const handleConfirmPublish = async (requireLogin: boolean) => {
|
|
458
|
+
if (!workflowId) {
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
setIsPublishPending(true);
|
|
463
|
+
try {
|
|
464
|
+
const result = await publishWorkflow(workflowId, {
|
|
465
|
+
actor: "studio",
|
|
466
|
+
requireLogin,
|
|
467
|
+
});
|
|
468
|
+
setIsPublished(true);
|
|
469
|
+
setRequireLogin(result.workflow.require_login);
|
|
470
|
+
setShareUrl(result.shareUrl);
|
|
471
|
+
setIsPublishDialogOpen(false);
|
|
472
|
+
toast({
|
|
473
|
+
title: "Workflow published",
|
|
474
|
+
description:
|
|
475
|
+
result.message ??
|
|
476
|
+
(requireLogin
|
|
477
|
+
? "Workflow is now available to signed-in workspace members."
|
|
478
|
+
: "Workflow is now public and available via its chat URL."),
|
|
479
|
+
});
|
|
480
|
+
} catch (error) {
|
|
481
|
+
setIsPublished(false);
|
|
482
|
+
toast({
|
|
483
|
+
title: "Failed to publish workflow",
|
|
439
484
|
description: getErrorMessage(error, "Unable to update publish status."),
|
|
440
485
|
variant: "destructive",
|
|
441
486
|
});
|
|
@@ -537,15 +582,31 @@ export function WorkflowTabContent({
|
|
|
537
582
|
</p>
|
|
538
583
|
</div>
|
|
539
584
|
<div className="flex items-center gap-4">
|
|
540
|
-
<
|
|
541
|
-
<
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
585
|
+
<TooltipProvider>
|
|
586
|
+
<Tooltip>
|
|
587
|
+
<TooltipTrigger asChild>
|
|
588
|
+
<div className="flex items-center gap-2">
|
|
589
|
+
<span className="text-sm text-muted-foreground">
|
|
590
|
+
Publish
|
|
591
|
+
</span>
|
|
592
|
+
<Switch
|
|
593
|
+
aria-label="Publish workflow"
|
|
594
|
+
checked={isPublished}
|
|
595
|
+
onCheckedChange={(checked) =>
|
|
596
|
+
void handlePublishToggle(checked)
|
|
597
|
+
}
|
|
598
|
+
disabled={isPublishPending}
|
|
599
|
+
/>
|
|
600
|
+
</div>
|
|
601
|
+
</TooltipTrigger>
|
|
602
|
+
<TooltipContent className="max-w-xs">
|
|
603
|
+
Expose this workflow through a shareable chat URL. When
|
|
604
|
+
publishing, choose <strong>Public</strong> (anyone with the
|
|
605
|
+
link) or <strong>Workspace only</strong> (sign-in required for
|
|
606
|
+
members of this workspace).
|
|
607
|
+
</TooltipContent>
|
|
608
|
+
</Tooltip>
|
|
609
|
+
</TooltipProvider>
|
|
549
610
|
<div className="flex items-center gap-2">
|
|
550
611
|
<span className="text-sm text-muted-foreground">Schedule</span>
|
|
551
612
|
<Switch
|
|
@@ -578,16 +639,23 @@ export function WorkflowTabContent({
|
|
|
578
639
|
) : null}
|
|
579
640
|
<Button
|
|
580
641
|
onClick={() => {
|
|
642
|
+
if (isRunPending || isRunning) {
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
581
645
|
if (hasMissingCredentials) {
|
|
582
646
|
setIsMissingCredentialsDialogOpen(true);
|
|
583
647
|
return;
|
|
584
648
|
}
|
|
585
649
|
void onRunWorkflow();
|
|
586
650
|
}}
|
|
587
|
-
disabled={
|
|
651
|
+
disabled={runButtonDisabled}
|
|
588
652
|
>
|
|
589
|
-
|
|
590
|
-
|
|
653
|
+
{isRunPending || isRunning ? (
|
|
654
|
+
<LoaderCircle className="mr-1.5 h-4 w-4 animate-spin" />
|
|
655
|
+
) : (
|
|
656
|
+
<Play className="mr-1.5 h-4 w-4" />
|
|
657
|
+
)}
|
|
658
|
+
{runButtonLabel}
|
|
591
659
|
</Button>
|
|
592
660
|
<Button
|
|
593
661
|
variant="outline"
|
|
@@ -599,11 +667,21 @@ export function WorkflowTabContent({
|
|
|
599
667
|
</div>
|
|
600
668
|
</div>
|
|
601
669
|
|
|
670
|
+
{(isRunPending || isRunning) && (
|
|
671
|
+
<div
|
|
672
|
+
role="status"
|
|
673
|
+
className="rounded-md border border-primary/30 bg-primary/5 px-3 py-2 text-sm text-primary"
|
|
674
|
+
>
|
|
675
|
+
Workflow run in progress. Check the latest record on the Trace tab
|
|
676
|
+
for live status.
|
|
677
|
+
</div>
|
|
678
|
+
)}
|
|
679
|
+
|
|
602
680
|
{isPublished && shareUrl && (
|
|
603
681
|
<div className="flex items-center justify-between rounded-md border border-border/60 bg-muted/20 px-3 py-2">
|
|
604
682
|
<div className="min-w-0">
|
|
605
683
|
<p className="text-xs uppercase tracking-wide text-muted-foreground">
|
|
606
|
-
Public URL
|
|
684
|
+
{requireLogin ? "Members-only URL" : "Public URL"}
|
|
607
685
|
</p>
|
|
608
686
|
<a
|
|
609
687
|
href={shareUrl}
|
|
@@ -728,6 +806,13 @@ export function WorkflowTabContent({
|
|
|
728
806
|
/>
|
|
729
807
|
) : null}
|
|
730
808
|
|
|
809
|
+
<PublishWorkflowDialog
|
|
810
|
+
open={isPublishDialogOpen}
|
|
811
|
+
isPending={isPublishPending}
|
|
812
|
+
onOpenChange={setIsPublishDialogOpen}
|
|
813
|
+
onConfirm={handleConfirmPublish}
|
|
814
|
+
/>
|
|
815
|
+
|
|
731
816
|
<AlertDialog
|
|
732
817
|
open={isMissingCredentialsDialogOpen}
|
|
733
818
|
onOpenChange={setIsMissingCredentialsDialogOpen}
|
|
@@ -83,12 +83,14 @@ export function buildWorkflowLayoutProps(
|
|
|
83
83
|
isLoading: core.metadata.isWorkflowLoading,
|
|
84
84
|
loadError: core.metadata.workflowLoadError,
|
|
85
85
|
isRunPending: execution.isRunPending,
|
|
86
|
+
isRunning: core.execution.isRunning,
|
|
86
87
|
onRunWorkflow: execution.handleRunPersistedWorkflow,
|
|
87
88
|
onSaveConfig: resources.saver.handleSaveWorkflowConfig,
|
|
88
89
|
hasCronTriggerNode: hasSchedulableCronTrigger(
|
|
89
90
|
core.metadata.workflowVersions ?? [],
|
|
90
91
|
),
|
|
91
92
|
initialIsPublished: core.metadata.isWorkflowPublic,
|
|
93
|
+
initialRequireLogin: core.metadata.workflowRequireLogin,
|
|
92
94
|
initialShareUrl: core.metadata.workflowShareUrl,
|
|
93
95
|
missingCredentials: resources.credentialReadiness.missingCredentials,
|
|
94
96
|
};
|
|
@@ -18,6 +18,7 @@ export function useWorkflowLifecycle(
|
|
|
18
18
|
setChatkitStartScreenPrompts: core.metadata.setChatkitStartScreenPrompts,
|
|
19
19
|
setChatkitSupportedModels: core.metadata.setChatkitSupportedModels,
|
|
20
20
|
setIsWorkflowPublic: core.metadata.setIsWorkflowPublic,
|
|
21
|
+
setWorkflowRequireLogin: core.metadata.setWorkflowRequireLogin,
|
|
21
22
|
setWorkflowShareUrl: core.metadata.setWorkflowShareUrl,
|
|
22
23
|
setIsWorkflowLoading: core.metadata.setIsWorkflowLoading,
|
|
23
24
|
setWorkflowLoadError: core.metadata.setWorkflowLoadError,
|
|
@@ -28,6 +28,7 @@ interface UseWorkflowLoaderParams {
|
|
|
28
28
|
SetStateAction<ChatKitSupportedModel[] | null>
|
|
29
29
|
>;
|
|
30
30
|
setIsWorkflowPublic: Dispatch<SetStateAction<boolean>>;
|
|
31
|
+
setWorkflowRequireLogin: Dispatch<SetStateAction<boolean>>;
|
|
31
32
|
setWorkflowShareUrl: Dispatch<SetStateAction<string | null>>;
|
|
32
33
|
setIsWorkflowLoading: Dispatch<SetStateAction<boolean>>;
|
|
33
34
|
setWorkflowLoadError: Dispatch<SetStateAction<string | null>>;
|
|
@@ -46,6 +47,7 @@ export function useWorkflowLoader({
|
|
|
46
47
|
setChatkitStartScreenPrompts,
|
|
47
48
|
setChatkitSupportedModels,
|
|
48
49
|
setIsWorkflowPublic,
|
|
50
|
+
setWorkflowRequireLogin,
|
|
49
51
|
setWorkflowShareUrl,
|
|
50
52
|
setIsWorkflowLoading,
|
|
51
53
|
setWorkflowLoadError,
|
|
@@ -67,6 +69,7 @@ export function useWorkflowLoader({
|
|
|
67
69
|
setChatkitStartScreenPrompts(null);
|
|
68
70
|
setChatkitSupportedModels(null);
|
|
69
71
|
setIsWorkflowPublic(false);
|
|
72
|
+
setWorkflowRequireLogin(false);
|
|
70
73
|
setWorkflowShareUrl(null);
|
|
71
74
|
setExecutions([]);
|
|
72
75
|
setActiveExecutionId(null);
|
|
@@ -102,6 +105,7 @@ export function useWorkflowLoader({
|
|
|
102
105
|
);
|
|
103
106
|
setChatkitSupportedModels(persisted.chatkitSupportedModels ?? null);
|
|
104
107
|
setIsWorkflowPublic(persisted.isPublic ?? false);
|
|
108
|
+
setWorkflowRequireLogin(persisted.requireLogin ?? false);
|
|
105
109
|
setWorkflowShareUrl(persisted.shareUrl ?? null);
|
|
106
110
|
setExecutions([]);
|
|
107
111
|
setActiveExecutionId(null);
|
|
@@ -124,6 +128,7 @@ export function useWorkflowLoader({
|
|
|
124
128
|
setChatkitStartScreenPrompts(null);
|
|
125
129
|
setChatkitSupportedModels(null);
|
|
126
130
|
setIsWorkflowPublic(false);
|
|
131
|
+
setWorkflowRequireLogin(false);
|
|
127
132
|
setWorkflowShareUrl(null);
|
|
128
133
|
setExecutions([]);
|
|
129
134
|
setActiveExecutionId(null);
|