Skip to content

Commit f57e566

Browse files
committed
Abstracts artifacts list
1 parent 921cf8a commit f57e566

File tree

4 files changed

+167
-194
lines changed

4 files changed

+167
-194
lines changed

src/components/Editor/PipelineDetails.tsx

Lines changed: 42 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useEffect, useState } from "react";
33

44
import { PipelineValidationList } from "@/components/Editor/components/PipelineValidationList/PipelineValidationList";
55
import { useValidationIssueNavigation } from "@/components/Editor/hooks/useValidationIssueNavigation";
6+
import { ArtifactsList } from "@/components/shared/ArtifactsList/ArtifactsList";
67
import { CopyText } from "@/components/shared/CopyText/CopyText";
78
import { Button } from "@/components/ui/button";
89
import { Icon } from "@/components/ui/icon";
@@ -98,18 +99,6 @@ const PipelineDetails = () => {
9899
);
99100
};
100101

101-
const handleInputCopy = (input: InputSpec) => {
102-
const value = input.value ?? input.default;
103-
104-
if (!value) {
105-
notify("Copy failed: Input has no value", "error");
106-
return;
107-
}
108-
109-
void navigator.clipboard.writeText(value);
110-
notify("Input value copied to clipboard", "success");
111-
};
112-
113102
const handleDigestCopy = () => {
114103
navigator.clipboard.writeText(digest);
115104
notify("Digest copied to clipboard", "success");
@@ -134,7 +123,7 @@ const PipelineDetails = () => {
134123
className="p-2 h-full"
135124
data-context-panel="pipeline-details"
136125
>
137-
<CopyText className="text-lg font-semibold" showButton={false}>
126+
<CopyText className="text-lg font-semibold">
138127
{componentSpec.name ?? "Unnamed Pipeline"}
139128
</CopyText>
140129
<InlineStack gap="2">
@@ -233,111 +222,46 @@ const PipelineDetails = () => {
233222
)}
234223

235224
{/* Artifacts (Inputs & Outputs) */}
236-
<BlockStack>
237-
<Text as="h3" size="md" weight="semibold" className="mb-1">
238-
Artifacts
239-
</Text>
240-
<BlockStack gap="4">
241-
<BlockStack>
242-
<Text as="h4" size="sm" weight="semibold" className="mb-1">
243-
Inputs
244-
</Text>
245-
{componentSpec.inputs && componentSpec.inputs.length > 0 ? (
246-
<div className="flex flex-col">
247-
{componentSpec.inputs.map((input) => {
248-
return (
249-
<div
250-
className="flex flex-row justify-between even:bg-white odd:bg-gray-100 gap-1 px-2 py-1 rounded-xs items-center"
251-
key={input.name}
252-
>
253-
<div className="text-xs flex-1 truncate max-w-[200px]">
254-
<span className="font-semibold">{input.name}:</span>{" "}
255-
{input.value || input.default || "No value"}
256-
</div>
257-
258-
<div className="text-xs flex-1 font-mono truncate max-w-[100px]">
259-
<span className="font-semibold">Type:</span>{" "}
260-
{typeSpecToString(input?.type)}
261-
</div>
262-
<InlineStack
263-
className="min-w-24"
264-
align="end"
265-
blockAlign="center"
266-
>
267-
<Button
268-
type="button"
269-
variant="ghost"
270-
size="min"
271-
onClick={() => handleInputCopy(input)}
272-
className="text-muted-foreground hover:text-foreground"
273-
>
274-
<Icon name="Copy" className="size-3" />
275-
</Button>
276-
277-
<Button
278-
type="button"
279-
variant="ghost"
280-
className="text-xs text-muted-foreground cursor-pointer hover:bg-transparent"
281-
size="sm"
282-
onClick={() => handleInputEdit(input)}
283-
>
284-
Edit
285-
</Button>
286-
</InlineStack>
287-
</div>
288-
);
289-
})}
290-
</div>
291-
) : (
292-
<div className="text-xs text-muted-foreground">No inputs</div>
293-
)}
294-
</BlockStack>
295-
<BlockStack>
296-
<Text as="h4" size="sm" weight="semibold" className="mb-1">
297-
Outputs
298-
</Text>
299-
{componentSpec.outputs && componentSpec.outputs.length > 0 ? (
300-
<div className="flex flex-col">
301-
{componentSpec.outputs.map((output) => (
302-
<div
303-
className="flex flex-row justify-between even:bg-white odd:bg-gray-100 gap-1 px-2 py-1 rounded-xs items-center"
304-
key={output.name}
305-
>
306-
<div className="text-xs flex-1 truncate max-w-[200px]">
307-
<span className="font-semibold">{output.name}:</span>{" "}
308-
{
309-
getOutputConnectedDetails(graphSpec, output.name)
310-
.outputName
311-
}
312-
</div>
313-
<div className="text-xs">
314-
<span className="font-semibold">Type:</span>{" "}
315-
{typeSpecToString(
316-
getOutputConnectedDetails(graphSpec, output.name)
317-
.outputType,
318-
)}
319-
</div>
320-
321-
<InlineStack className="min-w-24" align={"end"}>
322-
<Button
323-
type="button"
324-
variant="ghost"
325-
className="text-xs text-muted-foreground cursor-pointer hover:bg-transparent"
326-
size="sm"
327-
onClick={() => handleOutputEdit(output)}
328-
>
329-
Edit
330-
</Button>
331-
</InlineStack>
332-
</div>
333-
))}
334-
</div>
335-
) : (
336-
<div className="text-xs text-muted-foreground">No outputs</div>
337-
)}
338-
</BlockStack>
339-
</BlockStack>
340-
</BlockStack>
225+
<ArtifactsList
226+
inputs={(componentSpec.inputs ?? []).map((input) => ({
227+
name: input.name,
228+
type: typeSpecToString(input?.type),
229+
value: input.value || input.default,
230+
actions: (
231+
<Button
232+
type="button"
233+
variant="ghost"
234+
size="min"
235+
onClick={() => handleInputEdit(input)}
236+
className="text-muted-foreground hover:text-foreground h-4 w-4"
237+
>
238+
<Icon name="Pencil" className="size-2.5" />
239+
</Button>
240+
),
241+
}))}
242+
outputs={(componentSpec.outputs ?? []).map((output) => {
243+
const connectedDetails = getOutputConnectedDetails(
244+
graphSpec,
245+
output.name,
246+
);
247+
return {
248+
name: output.name,
249+
type: typeSpecToString(connectedDetails.outputType),
250+
value: connectedDetails.outputName,
251+
actions: (
252+
<Button
253+
type="button"
254+
variant="ghost"
255+
size="min"
256+
onClick={() => handleOutputEdit(output)}
257+
className="text-muted-foreground hover:text-foreground h-4 w-4"
258+
>
259+
<Icon name="Pencil" className="size-2.5" />
260+
</Button>
261+
),
262+
};
263+
})}
264+
/>
341265

342266
{/* Validations */}
343267
<BlockStack>

src/components/PipelineRun/RunDetails.tsx

Lines changed: 13 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Frown } from "lucide-react";
22

3+
import { ArtifactsList } from "@/components/shared/ArtifactsList/ArtifactsList";
34
import { CopyText } from "@/components/shared/CopyText/CopyText";
45
import { BlockStack, InlineStack } from "@/components/ui/layout";
56
import { Spinner } from "@/components/ui/spinner";
@@ -87,7 +88,7 @@ export const RunDetails = () => {
8788

8889
return (
8990
<BlockStack gap="6" className="p-2 h-full">
90-
<CopyText className="text-lg font-semibold" showButton={false}>
91+
<CopyText className="text-lg font-semibold">
9192
{componentSpec.name ?? "Unnamed Pipeline"}
9293
</CopyText>
9394

@@ -201,61 +202,17 @@ export const RunDetails = () => {
201202
</BlockStack>
202203
)}
203204

204-
<BlockStack className="w-full">
205-
<Text as="h3" size="md" weight="semibold" className="mb-1">
206-
Artifacts
207-
</Text>
208-
<BlockStack gap="4" className="w-full">
209-
<BlockStack className="w-full">
210-
<Text as="h4" size="sm" weight="semibold" className="mb-1">
211-
Inputs
212-
</Text>
213-
{componentSpec.inputs && componentSpec.inputs.length > 0 ? (
214-
<div className="flex flex-col w-full">
215-
{componentSpec.inputs.map((input) => (
216-
<div
217-
className="flex flex-row justify-between even:bg-white odd:bg-gray-100 gap-1 px-2 py-1 rounded-xs items-center w-full"
218-
key={input.name}
219-
>
220-
<div className="text-xs flex-1 truncate">
221-
<span className="font-semibold">{input.name}</span>
222-
</div>
223-
<div className="text-xs text-muted-foreground">
224-
{typeof input.type === "string" ? input.type : "object"}
225-
</div>
226-
</div>
227-
))}
228-
</div>
229-
) : (
230-
<div className="text-xs text-muted-foreground">No inputs</div>
231-
)}
232-
</BlockStack>
233-
<BlockStack className="w-full">
234-
<Text as="h4" size="sm" weight="semibold" className="mb-1">
235-
Outputs
236-
</Text>
237-
{componentSpec.outputs && componentSpec.outputs.length > 0 ? (
238-
<div className="flex flex-col w-full">
239-
{componentSpec.outputs.map((output) => (
240-
<div
241-
className="flex flex-row justify-between even:bg-white odd:bg-gray-100 gap-1 px-2 py-1 rounded-xs items-center w-full"
242-
key={output.name}
243-
>
244-
<div className="text-xs flex-1 truncate">
245-
<span className="font-semibold">{output.name}</span>
246-
</div>
247-
<div className="text-xs text-muted-foreground">
248-
{typeof output.type === "string" ? output.type : "object"}
249-
</div>
250-
</div>
251-
))}
252-
</div>
253-
) : (
254-
<div className="text-xs text-muted-foreground">No outputs</div>
255-
)}
256-
</BlockStack>
257-
</BlockStack>
258-
</BlockStack>
205+
<ArtifactsList
206+
inputs={(componentSpec.inputs ?? []).map((input) => ({
207+
name: input.name,
208+
type: typeof input.type === "string" ? input.type : "object",
209+
value: input.value ?? input.default,
210+
}))}
211+
outputs={(componentSpec.outputs ?? []).map((output) => ({
212+
name: output.name,
213+
type: typeof output.type === "string" ? output.type : "object",
214+
}))}
215+
/>
259216
</BlockStack>
260217
);
261218
};
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { type ReactNode } from "react";
2+
3+
import { CopyText } from "@/components/shared/CopyText/CopyText";
4+
import { BlockStack, InlineStack } from "@/components/ui/layout";
5+
import { Text } from "@/components/ui/typography";
6+
7+
interface ArtifactItem {
8+
name: string;
9+
type: string;
10+
value?: string;
11+
actions?: ReactNode;
12+
}
13+
14+
interface ArtifactsSectionProps {
15+
title: string;
16+
items: ArtifactItem[];
17+
emptyMessage?: string;
18+
}
19+
20+
const ArtifactsSection = ({
21+
title,
22+
items,
23+
emptyMessage = "None",
24+
}: ArtifactsSectionProps) => (
25+
<BlockStack className="w-full">
26+
<Text as="h4" size="sm" weight="semibold" className="mb-1">
27+
{title}
28+
</Text>
29+
{items.length > 0 ? (
30+
<BlockStack className="w-full">
31+
{items.map((item) => (
32+
<InlineStack
33+
key={item.name}
34+
gap="2"
35+
align="space-between"
36+
blockAlign="center"
37+
className="even:bg-white odd:bg-gray-100 p-2 rounded-xs w-full"
38+
>
39+
<InlineStack
40+
gap="1"
41+
blockAlign="center"
42+
wrap="nowrap"
43+
className="flex-1 min-w-0 text-xs"
44+
>
45+
<Text as="span" weight="semibold" className="shrink-0">
46+
{item.name}:
47+
</Text>
48+
{item.value !== undefined ? (
49+
<CopyText className="truncate">
50+
{item.value || "No value"}
51+
</CopyText>
52+
) : (
53+
<Text as="span" tone="subdued">
54+
55+
</Text>
56+
)}
57+
</InlineStack>
58+
<Text size="xs" tone="subdued" className="shrink-0">
59+
{item.type}
60+
</Text>
61+
{item.actions}
62+
</InlineStack>
63+
))}
64+
</BlockStack>
65+
) : (
66+
<Text size="xs" tone="subdued">
67+
{emptyMessage}
68+
</Text>
69+
)}
70+
</BlockStack>
71+
);
72+
73+
interface ArtifactsListProps {
74+
inputs: ArtifactItem[];
75+
outputs: ArtifactItem[];
76+
}
77+
78+
export const ArtifactsList = ({ inputs, outputs }: ArtifactsListProps) => (
79+
<BlockStack className="w-full">
80+
<Text as="h3" size="md" weight="semibold" className="mb-1">
81+
Artifacts
82+
</Text>
83+
<BlockStack gap="4" className="w-full">
84+
<ArtifactsSection
85+
title="Inputs"
86+
items={inputs}
87+
emptyMessage="No inputs"
88+
/>
89+
<ArtifactsSection
90+
title="Outputs"
91+
items={outputs}
92+
emptyMessage="No outputs"
93+
/>
94+
</BlockStack>
95+
</BlockStack>
96+
);

0 commit comments

Comments
 (0)