orcheo-studio 0.23.1 → 0.24.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-CqnxfCHp.js → arc-DAv23sxp.js} +1 -1
- package/dist/assets/{architectureDiagram-VXUJARFQ-UGPIhA1j.js → architectureDiagram-VXUJARFQ-D_gUzQPQ.js} +1 -1
- package/dist/assets/{blockDiagram-VD42YOAC-Cn6wka40.js → blockDiagram-VD42YOAC-2_hx47qT.js} +1 -1
- package/dist/assets/{c4Diagram-YG6GDRKO-BvPRNdur.js → c4Diagram-YG6GDRKO-dehdDKxT.js} +1 -1
- package/dist/assets/channel-C3cH7p5A.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB-F_o1AmTw.js → chunk-4BX2VUAB-CtLOuP9F.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-Cl075es2.js → chunk-55IACEB6-CwinXV4-.js} +1 -1
- package/dist/assets/{chunk-B4BG7PRW-DcErQhy9.js → chunk-B4BG7PRW-BI7iPFRv.js} +1 -1
- package/dist/assets/{chunk-DI55MBZ5-CFxCndOp.js → chunk-DI55MBZ5-CNYNrYjW.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-XOcF0fgy.js → chunk-FMBD7UC4-BFVTHaU-.js} +1 -1
- package/dist/assets/{chunk-QN33PNHL-CjJH_Bvj.js → chunk-QN33PNHL-xWy7sSv1.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-nLb-Go8u.js → chunk-QZHKN3VN-Ds_R1QaP.js} +1 -1
- package/dist/assets/{chunk-TZMSLE5B-L07ovmT0.js → chunk-TZMSLE5B-D8g2OOzb.js} +1 -1
- package/dist/assets/classDiagram-2ON5EDUG-o6x5AEsV.js +1 -0
- package/dist/assets/classDiagram-v2-WZHVMYZB-o6x5AEsV.js +1 -0
- package/dist/assets/clone-CnJR7M_G.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-B6IcYcMk.js → cose-bilkent-S5V4N54A-DnkMvL-f.js} +1 -1
- package/dist/assets/{dagre-6UL2VRFP-CN7ADhef.js → dagre-6UL2VRFP-dpKYJ1gN.js} +1 -1
- package/dist/assets/{diagram-PSM6KHXK-3chqLyzk.js → diagram-PSM6KHXK-BnFoR8YQ.js} +1 -1
- package/dist/assets/{diagram-QEK2KX5R-MrbKZWaG.js → diagram-QEK2KX5R-Brg1AGVu.js} +1 -1
- package/dist/assets/{diagram-S2PKOQOG-Dr7BijIx.js → diagram-S2PKOQOG-Dhmom7Q4.js} +1 -1
- package/dist/assets/{erDiagram-Q2GNP2WA-Ck-v9a6N.js → erDiagram-Q2GNP2WA-BTez8fDL.js} +1 -1
- package/dist/assets/{flowDiagram-NV44I4VS-D662eMD1.js → flowDiagram-NV44I4VS-CVXmtc0D.js} +1 -1
- package/dist/assets/{ganttDiagram-JELNMOA3-c2mXK47M.js → ganttDiagram-JELNMOA3-CHbw3AtF.js} +1 -1
- package/dist/assets/{gitGraphDiagram-V2S2FVAM-DUMhfhsS.js → gitGraphDiagram-V2S2FVAM-CaPZ1SUE.js} +1 -1
- package/dist/assets/{graph-DkWlmVun.js → graph-DjLsaDIL.js} +1 -1
- package/dist/assets/index-DSthAh3a.css +1 -0
- package/dist/assets/{index-kH6EkBsZ.js → index-DlptXU2u.js} +192 -187
- package/dist/assets/{infoDiagram-HS3SLOUP-OTL7qvkP.js → infoDiagram-HS3SLOUP-D_9DW9VU.js} +1 -1
- package/dist/assets/{isUndefined-BmZvH1PX.js → isUndefined-BkijP54r.js} +1 -1
- package/dist/assets/{journeyDiagram-XKPGCS4Q-BYzqE5D8.js → journeyDiagram-XKPGCS4Q-xxRWc5wn.js} +1 -1
- package/dist/assets/{kanban-definition-3W4ZIXB7-NtzQ4y9p.js → kanban-definition-3W4ZIXB7-BCI2NbpB.js} +1 -1
- package/dist/assets/{layout-CwhyLMkP.js → layout-M840MAqW.js} +1 -1
- package/dist/assets/{linear-Cirf2V1x.js → linear--_gMRK53.js} +1 -1
- package/dist/assets/{min-C81vLJYh.js → min-D06s3WKk.js} +1 -1
- package/dist/assets/{mindmap-definition-VGOIOE7T-TDnLQIKZ.js → mindmap-definition-VGOIOE7T-B1-ALKFj.js} +1 -1
- package/dist/assets/{pieDiagram-ADFJNKIX-d0-JhjNZ.js → pieDiagram-ADFJNKIX-76kjoNcf.js} +1 -1
- package/dist/assets/{quadrantDiagram-AYHSOK5B-wCoihp8a.js → quadrantDiagram-AYHSOK5B-O_d3GTQl.js} +1 -1
- package/dist/assets/{requirementDiagram-UZGBJVZJ-BqiCIjcI.js → requirementDiagram-UZGBJVZJ-CasSIIPr.js} +1 -1
- package/dist/assets/{sankeyDiagram-TZEHDZUN-BPH0Pd1D.js → sankeyDiagram-TZEHDZUN-CL_kG7jD.js} +1 -1
- package/dist/assets/{sequenceDiagram-WL72ISMW-B8FfJraH.js → sequenceDiagram-WL72ISMW-RYnztN0k.js} +1 -1
- package/dist/assets/{stateDiagram-FKZM4ZOC-ZQR3Q9_8.js → stateDiagram-FKZM4ZOC-NHFLaLiz.js} +1 -1
- package/dist/assets/stateDiagram-v2-4FDKWEC3-3y5AUrwb.js +1 -0
- package/dist/assets/{timeline-definition-IT6M3QCI-D-arYxX5.js → timeline-definition-IT6M3QCI-BH1cZoua.js} +1 -1
- package/dist/assets/{treemap-GDKQZRPO-Dw5BHp_B.js → treemap-GDKQZRPO-QAQDPzuO.js} +1 -1
- package/dist/assets/{xychartDiagram-PRI3JC2R-Dt1zH6V2.js → xychartDiagram-PRI3JC2R-C3M4ZzCP.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +2 -74
- package/src/features/auth/lib/auth-api.ts +21 -3
- package/src/features/workflow/data/templates/candidate-badges.ts +11 -1
- package/src/features/workflow/data/workflow-types.ts +3 -0
- package/src/features/workflow/lib/workflow-storage-api.ts +15 -0
- package/src/features/workflow/lib/workflow-storage-helpers.ts +46 -0
- package/src/features/workflow/lib/workflow-storage.ts +17 -0
- package/src/features/workflow/lib/workflow-storage.types.ts +16 -0
- package/src/features/workflow/pages/workflow/hooks/use-workflow-saver.ts +3 -2
- package/src/features/workflow/pages/workflow-gallery/candidate-updates.ts +107 -0
- package/src/features/workflow/pages/workflow-gallery/use-workflow-gallery-actions.ts +29 -4
- package/src/features/workflow/pages/workflow-gallery/use-workflow-gallery-state.ts +29 -25
- package/src/features/workflow/pages/workflow-gallery/workflow-card.tsx +187 -6
- package/src/features/workflow/pages/workflow-gallery/workflow-gallery-tabs.tsx +17 -9
- package/src/features/workflow/pages/workflow-gallery.tsx +2 -0
- package/src/lib/auth-fetch.ts +81 -15
- package/vite.config.ts +0 -7
- package/dist/assets/channel-FjxqoZWk.js +0 -1
- package/dist/assets/classDiagram-2ON5EDUG-8JYNY9Ic.js +0 -1
- package/dist/assets/classDiagram-v2-WZHVMYZB-8JYNY9Ic.js +0 -1
- package/dist/assets/clone-NPVQC0Uc.js +0 -1
- package/dist/assets/index-BVY_PYJU.css +0 -1
- package/dist/assets/stateDiagram-v2-4FDKWEC3-uDYn1wPF.js +0 -1
- package/src/features/workflow/components/layouts/workflow-page-layout.tsx +0 -69
- package/src/features/workflow/data/templates/template-owner.ts +0 -7
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { useRef, useState } from "react";
|
|
2
2
|
import type { KeyboardEvent, MouseEvent, SyntheticEvent } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
Loader2,
|
|
5
|
+
MoreHorizontal,
|
|
6
|
+
RefreshCcw,
|
|
7
|
+
Send,
|
|
8
|
+
Star,
|
|
9
|
+
UserMinus,
|
|
10
|
+
UserPlus,
|
|
11
|
+
} from "lucide-react";
|
|
4
12
|
import { toast } from "@/hooks/use-toast";
|
|
5
13
|
import { Button } from "@/design-system/ui/button";
|
|
6
14
|
import {
|
|
@@ -9,6 +17,14 @@ import {
|
|
|
9
17
|
CardFooter,
|
|
10
18
|
CardHeader,
|
|
11
19
|
} from "@/design-system/ui/card";
|
|
20
|
+
import {
|
|
21
|
+
Dialog,
|
|
22
|
+
DialogContent,
|
|
23
|
+
DialogDescription,
|
|
24
|
+
DialogFooter,
|
|
25
|
+
DialogHeader,
|
|
26
|
+
DialogTitle,
|
|
27
|
+
} from "@/design-system/ui/dialog";
|
|
12
28
|
import {
|
|
13
29
|
DropdownMenu,
|
|
14
30
|
DropdownMenuContent,
|
|
@@ -16,15 +32,23 @@ import {
|
|
|
16
32
|
DropdownMenuSeparator,
|
|
17
33
|
DropdownMenuTrigger,
|
|
18
34
|
} from "@/design-system/ui/dropdown-menu";
|
|
35
|
+
import {
|
|
36
|
+
Tooltip,
|
|
37
|
+
TooltipContent,
|
|
38
|
+
TooltipProvider,
|
|
39
|
+
TooltipTrigger,
|
|
40
|
+
} from "@/design-system/ui/tooltip";
|
|
19
41
|
import { cn } from "@/lib/utils";
|
|
20
42
|
import { resolveAvatarUrl } from "@/assets/avatars";
|
|
21
43
|
import { ConfirmDeleteWorkflowDialog } from "@features/workflow/components/dialogs/confirm-delete-workflow-dialog";
|
|
22
44
|
import {
|
|
45
|
+
getCandidateBadgeByCandidateId,
|
|
23
46
|
getCandidateBadgeByHandle,
|
|
24
47
|
getCandidateBadgeDefinition,
|
|
25
48
|
} from "@features/workflow/data/templates/candidate-badges";
|
|
26
49
|
import { type Workflow } from "@features/workflow/data/workflow-data";
|
|
27
50
|
import { getWorkflowRouteRef } from "@features/workflow/lib/workflow-storage-helpers";
|
|
51
|
+
import { getCandidateUpdateAvailability } from "./candidate-updates";
|
|
28
52
|
import { WORKFLOW_GALLERY_CARD_ASPECT_CLASSNAME } from "./workflow-card-size";
|
|
29
53
|
|
|
30
54
|
const getWorkflowTemplateAvatar = (workflow: Workflow): string | undefined => {
|
|
@@ -49,6 +73,10 @@ interface WorkflowCardProps {
|
|
|
49
73
|
workflowId: string,
|
|
50
74
|
workflowName: string,
|
|
51
75
|
) => Promise<void> | void;
|
|
76
|
+
onUpdateCandidateWorkflow: (
|
|
77
|
+
workflowId: string,
|
|
78
|
+
candidateId: string,
|
|
79
|
+
) => Promise<void> | void;
|
|
52
80
|
}
|
|
53
81
|
|
|
54
82
|
export const WorkflowCard = ({
|
|
@@ -60,6 +88,7 @@ export const WorkflowCard = ({
|
|
|
60
88
|
onUseTemplate,
|
|
61
89
|
onExportWorkflow,
|
|
62
90
|
onDeleteWorkflow,
|
|
91
|
+
onUpdateCandidateWorkflow,
|
|
63
92
|
}: WorkflowCardProps) => {
|
|
64
93
|
const workflowRouteRef = getWorkflowRouteRef(workflow);
|
|
65
94
|
const isClickable = !isTemplate;
|
|
@@ -70,14 +99,25 @@ export const WorkflowCard = ({
|
|
|
70
99
|
const templateId = !isTemplate
|
|
71
100
|
? workflow.versions?.at(-1)?.templateId
|
|
72
101
|
: undefined;
|
|
102
|
+
const candidateSource = !isTemplate
|
|
103
|
+
? workflow.versions?.at(-1)?.candidateSource
|
|
104
|
+
: undefined;
|
|
73
105
|
const templateBadge = !isTemplate
|
|
74
|
-
? (
|
|
75
|
-
?
|
|
76
|
-
:
|
|
77
|
-
|
|
78
|
-
|
|
106
|
+
? ((candidateSource?.candidateId
|
|
107
|
+
? getCandidateBadgeByCandidateId(candidateSource.candidateId)
|
|
108
|
+
: undefined) ??
|
|
109
|
+
(candidateSource?.candidateHandle
|
|
110
|
+
? getCandidateBadgeByHandle(candidateSource.candidateHandle)
|
|
111
|
+
: undefined) ??
|
|
112
|
+
(templateId ? getCandidateBadgeDefinition(templateId) : undefined) ??
|
|
113
|
+
(workflow.handle
|
|
114
|
+
? getCandidateBadgeByHandle(workflow.handle)
|
|
115
|
+
: undefined))
|
|
79
116
|
: undefined;
|
|
80
117
|
const displayBadge = candidateBadge ?? templateBadge;
|
|
118
|
+
const candidateUpdate = !isTemplate
|
|
119
|
+
? getCandidateUpdateAvailability(workflow, templateBadge)
|
|
120
|
+
: undefined;
|
|
81
121
|
const headerLabel = isTemplate ? "Candidate" : workspaceLabel;
|
|
82
122
|
const rawHandle = workflow.handle ?? workflow.id;
|
|
83
123
|
const workflowSlug = isTemplate
|
|
@@ -85,6 +125,9 @@ export const WorkflowCard = ({
|
|
|
85
125
|
: rawHandle;
|
|
86
126
|
const displayHandle =
|
|
87
127
|
!isTemplate && teamSlug ? `${teamSlug}/${workflowSlug}` : workflowSlug;
|
|
128
|
+
const versionToDisplay = isTemplate
|
|
129
|
+
? (candidateBadge?.version ?? null)
|
|
130
|
+
: (workflow.versions?.at(-1)?.candidateSource?.candidateVersion ?? null);
|
|
88
131
|
const avatarUrl = resolveAvatarUrl(
|
|
89
132
|
workflow.avatarEmoji ?? getWorkflowTemplateAvatar(workflow),
|
|
90
133
|
workflow.id,
|
|
@@ -94,6 +137,8 @@ export const WorkflowCard = ({
|
|
|
94
137
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
95
138
|
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
|
96
139
|
const [isDeletePending, setIsDeletePending] = useState(false);
|
|
140
|
+
const [isUpdateDialogOpen, setIsUpdateDialogOpen] = useState(false);
|
|
141
|
+
const [isUpdatePending, setIsUpdatePending] = useState(false);
|
|
97
142
|
|
|
98
143
|
const suppressCardOpen = () => {
|
|
99
144
|
suppressCardOpenRef.current = true;
|
|
@@ -149,6 +194,20 @@ export const WorkflowCard = ({
|
|
|
149
194
|
}
|
|
150
195
|
};
|
|
151
196
|
|
|
197
|
+
const handleConfirmUpdate = async () => {
|
|
198
|
+
if (!candidateUpdate) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
setIsUpdatePending(true);
|
|
203
|
+
try {
|
|
204
|
+
await onUpdateCandidateWorkflow(workflow.id, candidateUpdate.candidateId);
|
|
205
|
+
setIsUpdateDialogOpen(false);
|
|
206
|
+
} finally {
|
|
207
|
+
setIsUpdatePending(false);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
152
211
|
return (
|
|
153
212
|
<>
|
|
154
213
|
<Card
|
|
@@ -266,6 +325,45 @@ export const WorkflowCard = ({
|
|
|
266
325
|
/>
|
|
267
326
|
</div>
|
|
268
327
|
|
|
328
|
+
{candidateUpdate ? (
|
|
329
|
+
<TooltipProvider delayDuration={200}>
|
|
330
|
+
<Tooltip>
|
|
331
|
+
<TooltipTrigger asChild>
|
|
332
|
+
<Button
|
|
333
|
+
variant="outline"
|
|
334
|
+
size="icon"
|
|
335
|
+
className="absolute right-10 top-0 h-8 w-8 border-emerald-500/40 bg-emerald-50 text-emerald-700 hover:bg-emerald-100 dark:bg-emerald-950/40 dark:text-emerald-300 dark:hover:bg-emerald-900/60"
|
|
336
|
+
aria-label={`Update ${workflow.name} to candidate version ${candidateUpdate.latestVersion}`}
|
|
337
|
+
data-card-action="true"
|
|
338
|
+
onClick={(event) => {
|
|
339
|
+
stopPropagation(event);
|
|
340
|
+
setIsUpdateDialogOpen(true);
|
|
341
|
+
}}
|
|
342
|
+
onPointerDown={stopPropagation}
|
|
343
|
+
>
|
|
344
|
+
<RefreshCcw className="h-3.5 w-3.5" />
|
|
345
|
+
</Button>
|
|
346
|
+
</TooltipTrigger>
|
|
347
|
+
<TooltipContent side="top" className="max-w-[280px]">
|
|
348
|
+
<div className="space-y-2 text-xs">
|
|
349
|
+
<div className="font-semibold text-foreground">
|
|
350
|
+
Candidate version {candidateUpdate.currentVersion} to{" "}
|
|
351
|
+
{candidateUpdate.latestVersion}
|
|
352
|
+
</div>
|
|
353
|
+
{candidateUpdate.latestSummary ? (
|
|
354
|
+
<p>{candidateUpdate.latestSummary}</p>
|
|
355
|
+
) : null}
|
|
356
|
+
{candidateUpdate.firstMigration ? (
|
|
357
|
+
<p className="text-amber-600 dark:text-amber-300">
|
|
358
|
+
{candidateUpdate.firstMigration}
|
|
359
|
+
</p>
|
|
360
|
+
) : null}
|
|
361
|
+
</div>
|
|
362
|
+
</TooltipContent>
|
|
363
|
+
</Tooltip>
|
|
364
|
+
</TooltipProvider>
|
|
365
|
+
) : null}
|
|
366
|
+
|
|
269
367
|
{!isTemplate ? (
|
|
270
368
|
<Button
|
|
271
369
|
variant="ghost"
|
|
@@ -295,6 +393,12 @@ export const WorkflowCard = ({
|
|
|
295
393
|
@{displayHandle}
|
|
296
394
|
</div>
|
|
297
395
|
|
|
396
|
+
{versionToDisplay ? (
|
|
397
|
+
<div className="mt-0.5 shrink-0 font-mono text-[9px] tracking-[0.04em] text-muted-foreground/60">
|
|
398
|
+
v{versionToDisplay}
|
|
399
|
+
</div>
|
|
400
|
+
) : null}
|
|
401
|
+
|
|
298
402
|
<div className="mt-3 flex min-h-0 w-full flex-1 flex-col items-center justify-start">
|
|
299
403
|
<div className="h-px w-8 rounded-full bg-border/80" />
|
|
300
404
|
|
|
@@ -343,6 +447,83 @@ export const WorkflowCard = ({
|
|
|
343
447
|
onConfirm={handleConfirmDelete}
|
|
344
448
|
/>
|
|
345
449
|
) : null}
|
|
450
|
+
|
|
451
|
+
{candidateUpdate ? (
|
|
452
|
+
<Dialog
|
|
453
|
+
open={isUpdateDialogOpen}
|
|
454
|
+
onOpenChange={(open) => {
|
|
455
|
+
if (!isUpdatePending) {
|
|
456
|
+
setIsUpdateDialogOpen(open);
|
|
457
|
+
}
|
|
458
|
+
}}
|
|
459
|
+
>
|
|
460
|
+
<DialogContent className="max-w-xl">
|
|
461
|
+
<DialogHeader>
|
|
462
|
+
<DialogTitle>Update candidate version</DialogTitle>
|
|
463
|
+
<DialogDescription>
|
|
464
|
+
Review release notes before updating {workflow.name} from{" "}
|
|
465
|
+
{candidateUpdate.currentVersion} to{" "}
|
|
466
|
+
{candidateUpdate.latestVersion}.
|
|
467
|
+
</DialogDescription>
|
|
468
|
+
</DialogHeader>
|
|
469
|
+
|
|
470
|
+
{candidateUpdate.isMajor ? (
|
|
471
|
+
<div className="rounded-md border border-amber-500/50 bg-amber-50 px-3 py-2 text-sm text-amber-900 dark:bg-amber-950/30 dark:text-amber-200">
|
|
472
|
+
This is a major candidate update. Review migration notes before
|
|
473
|
+
applying it to this colleague.
|
|
474
|
+
</div>
|
|
475
|
+
) : null}
|
|
476
|
+
|
|
477
|
+
<div className="max-h-[320px] space-y-3 overflow-y-auto pr-1">
|
|
478
|
+
{candidateUpdate.crossedUpdates.length > 0 ? (
|
|
479
|
+
candidateUpdate.crossedUpdates.map((note) => (
|
|
480
|
+
<div
|
|
481
|
+
key={note.version}
|
|
482
|
+
className="rounded-md border border-border/70 p-3 text-sm"
|
|
483
|
+
>
|
|
484
|
+
<div className="font-semibold">
|
|
485
|
+
Candidate version {note.version}
|
|
486
|
+
</div>
|
|
487
|
+
<p className="mt-1 text-muted-foreground">{note.summary}</p>
|
|
488
|
+
{note.migration ? (
|
|
489
|
+
<p className="mt-2 text-amber-700 dark:text-amber-300">
|
|
490
|
+
{note.migration}
|
|
491
|
+
</p>
|
|
492
|
+
) : null}
|
|
493
|
+
</div>
|
|
494
|
+
))
|
|
495
|
+
) : (
|
|
496
|
+
<p className="text-sm text-muted-foreground">
|
|
497
|
+
No release notes were provided for the versions being crossed.
|
|
498
|
+
</p>
|
|
499
|
+
)}
|
|
500
|
+
</div>
|
|
501
|
+
|
|
502
|
+
<DialogFooter>
|
|
503
|
+
<Button
|
|
504
|
+
type="button"
|
|
505
|
+
variant="outline"
|
|
506
|
+
onClick={() => setIsUpdateDialogOpen(false)}
|
|
507
|
+
disabled={isUpdatePending}
|
|
508
|
+
>
|
|
509
|
+
Cancel
|
|
510
|
+
</Button>
|
|
511
|
+
<Button
|
|
512
|
+
type="button"
|
|
513
|
+
onClick={handleConfirmUpdate}
|
|
514
|
+
disabled={isUpdatePending}
|
|
515
|
+
>
|
|
516
|
+
{isUpdatePending ? (
|
|
517
|
+
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
518
|
+
) : (
|
|
519
|
+
<RefreshCcw className="mr-2 h-4 w-4" />
|
|
520
|
+
)}
|
|
521
|
+
Update
|
|
522
|
+
</Button>
|
|
523
|
+
</DialogFooter>
|
|
524
|
+
</DialogContent>
|
|
525
|
+
</Dialog>
|
|
526
|
+
) : null}
|
|
346
527
|
</>
|
|
347
528
|
);
|
|
348
529
|
};
|
|
@@ -39,6 +39,10 @@ interface WorkflowGalleryTabsProps {
|
|
|
39
39
|
workflowId: string,
|
|
40
40
|
workflowName: string,
|
|
41
41
|
) => Promise<void> | void;
|
|
42
|
+
onUpdateCandidateWorkflow: (
|
|
43
|
+
workflowId: string,
|
|
44
|
+
candidateId: string,
|
|
45
|
+
) => Promise<void> | void;
|
|
42
46
|
onDeleteTeam?: (teamId: string) => void;
|
|
43
47
|
}
|
|
44
48
|
|
|
@@ -58,6 +62,7 @@ export const WorkflowGalleryTabs = ({
|
|
|
58
62
|
onUseTemplate,
|
|
59
63
|
onExportWorkflow,
|
|
60
64
|
onDeleteWorkflow,
|
|
65
|
+
onUpdateCandidateWorkflow,
|
|
61
66
|
onDeleteTeam,
|
|
62
67
|
}: WorkflowGalleryTabsProps) => {
|
|
63
68
|
const [isUploadOpen, setIsUploadOpen] = useState(false);
|
|
@@ -80,6 +85,7 @@ export const WorkflowGalleryTabs = ({
|
|
|
80
85
|
onUseTemplate={onUseTemplate}
|
|
81
86
|
onExportWorkflow={onExportWorkflow}
|
|
82
87
|
onDeleteWorkflow={onDeleteWorkflow}
|
|
88
|
+
onUpdateCandidateWorkflow={onUpdateCandidateWorkflow}
|
|
83
89
|
/>
|
|
84
90
|
))}
|
|
85
91
|
</div>
|
|
@@ -114,15 +120,17 @@ export const WorkflowGalleryTabs = ({
|
|
|
114
120
|
return (
|
|
115
121
|
<div className="flex flex-col gap-1 pb-6">
|
|
116
122
|
{independents.length > 0 ? renderGrid(independents) : null}
|
|
117
|
-
{[...byGroup.entries()]
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
{[...byGroup.entries()]
|
|
124
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
125
|
+
.map(([slug, items]) => (
|
|
126
|
+
<TeamSection
|
|
127
|
+
key={slug}
|
|
128
|
+
name={formatCandidateGroupName(slug)}
|
|
129
|
+
count={items.length}
|
|
130
|
+
>
|
|
131
|
+
{renderGrid(items)}
|
|
132
|
+
</TeamSection>
|
|
133
|
+
))}
|
|
126
134
|
</div>
|
|
127
135
|
);
|
|
128
136
|
};
|
|
@@ -62,6 +62,7 @@ export default function WorkflowGallery() {
|
|
|
62
62
|
handleImportStarterPack,
|
|
63
63
|
handleExportWorkflow,
|
|
64
64
|
handleDeleteWorkflow,
|
|
65
|
+
handleUpdateCandidateWorkflow,
|
|
65
66
|
handleOpenWorkflow,
|
|
66
67
|
onboardTarget,
|
|
67
68
|
confirmOnboardTeam,
|
|
@@ -102,6 +103,7 @@ export default function WorkflowGallery() {
|
|
|
102
103
|
onUseTemplate={handleUseTemplate}
|
|
103
104
|
onExportWorkflow={handleExportWorkflow}
|
|
104
105
|
onDeleteWorkflow={handleDeleteWorkflow}
|
|
106
|
+
onUpdateCandidateWorkflow={handleUpdateCandidateWorkflow}
|
|
105
107
|
onDeleteTeam={handleDeleteTeam}
|
|
106
108
|
/>
|
|
107
109
|
</div>
|
package/src/lib/auth-fetch.ts
CHANGED
|
@@ -1,35 +1,101 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getAccessToken,
|
|
3
|
+
getAuthTokens,
|
|
3
4
|
getDevAuthSessionHeaderValue,
|
|
4
5
|
} from "@features/auth/lib/auth-session";
|
|
6
|
+
import { refreshSession } from "@features/auth/lib/auth-api";
|
|
5
7
|
import { getWorkspaceSelectionHeaders } from "./workspace-session";
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
): Promise<Response> => {
|
|
12
|
-
const headers = new Headers(init.headers ?? {});
|
|
9
|
+
const buildAuthHeaders = (
|
|
10
|
+
headers: Headers,
|
|
11
|
+
shouldAttachAuth: boolean,
|
|
12
|
+
): string | null => {
|
|
13
13
|
const token = getAccessToken();
|
|
14
|
-
if (token &&
|
|
14
|
+
if (token && shouldAttachAuth) {
|
|
15
15
|
headers.set("Authorization", `Bearer ${token}`);
|
|
16
16
|
}
|
|
17
17
|
const devSession = getDevAuthSessionHeaderValue();
|
|
18
18
|
if (devSession && !headers.has("X-Orcheo-Dev-Session")) {
|
|
19
19
|
headers.set("X-Orcheo-Dev-Session", devSession);
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
return shouldAttachAuth ? token : null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const attachWorkspaceHeaders = (headers: Headers): void => {
|
|
25
|
+
for (const [name, value] of Object.entries(getWorkspaceSelectionHeaders())) {
|
|
26
|
+
if (!headers.has(name)) {
|
|
27
|
+
headers.set(name, value);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const fetchWithHeaders = (
|
|
33
|
+
input: RequestInfo | URL,
|
|
34
|
+
init: RequestInit,
|
|
35
|
+
headers: Headers,
|
|
36
|
+
): Promise<Response> =>
|
|
37
|
+
globalThis.fetch(input, {
|
|
31
38
|
...init,
|
|
32
39
|
credentials: init.credentials ?? "include",
|
|
33
40
|
headers,
|
|
34
41
|
});
|
|
42
|
+
|
|
43
|
+
const buildRequestHeaders = (
|
|
44
|
+
init: RequestInit,
|
|
45
|
+
shouldAttachAuth: boolean,
|
|
46
|
+
includeWorkspaceHeaders: boolean,
|
|
47
|
+
headers = new Headers(init.headers ?? {}),
|
|
48
|
+
): { headers: Headers; accessToken: string | null } => {
|
|
49
|
+
const accessToken = buildAuthHeaders(headers, shouldAttachAuth);
|
|
50
|
+
if (includeWorkspaceHeaders) {
|
|
51
|
+
attachWorkspaceHeaders(headers);
|
|
52
|
+
}
|
|
53
|
+
return { headers, accessToken };
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const authFetch = async (
|
|
57
|
+
input: RequestInfo | URL,
|
|
58
|
+
init: RequestInit = {},
|
|
59
|
+
options: { includeWorkspaceHeaders?: boolean } = {},
|
|
60
|
+
): Promise<Response> => {
|
|
61
|
+
const originalHeaders = new Headers(init.headers ?? {});
|
|
62
|
+
const shouldAttachAuth = !originalHeaders.has("Authorization");
|
|
63
|
+
// getAccessToken includes the shared 60-second expiry skew, so nearly expired
|
|
64
|
+
// tokens are treated as missing and refreshed before protected requests.
|
|
65
|
+
if (shouldAttachAuth && !getAccessToken() && getAuthTokens()?.refreshToken) {
|
|
66
|
+
await refreshSession();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const includeWorkspaceHeaders = options.includeWorkspaceHeaders ?? true;
|
|
70
|
+
const { headers, accessToken: requestAccessToken } = buildRequestHeaders(
|
|
71
|
+
init,
|
|
72
|
+
shouldAttachAuth,
|
|
73
|
+
includeWorkspaceHeaders,
|
|
74
|
+
originalHeaders,
|
|
75
|
+
);
|
|
76
|
+
const response = await fetchWithHeaders(input, init, headers);
|
|
77
|
+
if (response.status !== 401 || !shouldAttachAuth) {
|
|
78
|
+
return response;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const currentAccessToken = getAccessToken();
|
|
82
|
+
if (currentAccessToken && currentAccessToken !== requestAccessToken) {
|
|
83
|
+
const { headers: retryHeaders } = buildRequestHeaders(
|
|
84
|
+
init,
|
|
85
|
+
shouldAttachAuth,
|
|
86
|
+
includeWorkspaceHeaders,
|
|
87
|
+
);
|
|
88
|
+
return fetchWithHeaders(input, init, retryHeaders);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!getAuthTokens()?.refreshToken || !(await refreshSession())) {
|
|
92
|
+
return response;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const { headers: retryHeaders } = buildRequestHeaders(
|
|
96
|
+
init,
|
|
97
|
+
shouldAttachAuth,
|
|
98
|
+
includeWorkspaceHeaders,
|
|
99
|
+
);
|
|
100
|
+
return fetchWithHeaders(input, init, retryHeaders);
|
|
35
101
|
};
|
package/vite.config.ts
CHANGED
|
@@ -86,14 +86,7 @@ export default defineConfig({
|
|
|
86
86
|
'lodash/defaultsDeep',
|
|
87
87
|
'lodash/flatten',
|
|
88
88
|
// Other CJS packages
|
|
89
|
-
'react-split',
|
|
90
|
-
'dom-helpers',
|
|
91
89
|
'dayjs',
|
|
92
|
-
'invariant',
|
|
93
|
-
'copy-to-clipboard',
|
|
94
|
-
'moment',
|
|
95
|
-
'moment-timezone',
|
|
96
|
-
'papaparse',
|
|
97
90
|
// zustand and related
|
|
98
91
|
'zustand',
|
|
99
92
|
'zustand/traditional',
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{ap as o,aq as n}from"./index-kH6EkBsZ.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-B4BG7PRW-DcErQhy9.js";import{_ as i}from"./index-kH6EkBsZ.js";import"./chunk-FMBD7UC4-XOcF0fgy.js";import"./chunk-55IACEB6-Cl075es2.js";import"./chunk-QN33PNHL-CjJH_Bvj.js";var p={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{p as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-B4BG7PRW-DcErQhy9.js";import{_ as i}from"./index-kH6EkBsZ.js";import"./chunk-FMBD7UC4-XOcF0fgy.js";import"./chunk-55IACEB6-Cl075es2.js";import"./chunk-QN33PNHL-CjJH_Bvj.js";var p={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{p as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{ay as r}from"./index-kH6EkBsZ.js";var a=4;function n(o){return r(o,a)}export{n as c};
|