Skip to content

Commit fd7d491

Browse files
committed
Abstracts artifacts list
1 parent e370733 commit fd7d491

File tree

3 files changed

+158
-173
lines changed

3 files changed

+158
-173
lines changed

src/components/Editor/PipelineDetails.tsx

Lines changed: 41 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");
@@ -232,112 +221,46 @@ const PipelineDetails = () => {
232221
</BlockStack>
233222
)}
234223

235-
{/* 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>
224+
<ArtifactsList
225+
inputs={(componentSpec.inputs ?? []).map((input) => ({
226+
name: input.name,
227+
type: typeSpecToString(input?.type),
228+
value: input.value || input.default,
229+
actions: (
230+
<Button
231+
type="button"
232+
variant="ghost"
233+
size="min"
234+
onClick={() => handleInputEdit(input)}
235+
className="text-muted-foreground hover:text-foreground h-4 w-4"
236+
>
237+
<Icon name="Pencil" className="size-2.5" />
238+
</Button>
239+
),
240+
}))}
241+
outputs={(componentSpec.outputs ?? []).map((output) => {
242+
const connectedDetails = getOutputConnectedDetails(
243+
graphSpec,
244+
output.name,
245+
);
246+
return {
247+
name: output.name,
248+
type: typeSpecToString(connectedDetails.outputType),
249+
value: connectedDetails.outputName,
250+
actions: (
251+
<Button
252+
type="button"
253+
variant="ghost"
254+
size="min"
255+
onClick={() => handleOutputEdit(output)}
256+
className="text-muted-foreground hover:text-foreground h-4 w-4"
257+
>
258+
<Icon name="Pencil" className="size-2.5" />
259+
</Button>
260+
),
261+
};
262+
})}
263+
/>
341264

342265
{/* Validations */}
343266
<BlockStack>

src/components/PipelineRun/RunDetails.tsx

Lines changed: 12 additions & 55 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";
@@ -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: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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+
disableCopy?: boolean;
19+
}
20+
21+
const ArtifactsSection = ({
22+
title,
23+
items,
24+
emptyMessage = "None",
25+
disableCopy = false,
26+
}: ArtifactsSectionProps) => (
27+
<BlockStack className="w-full">
28+
<Text as="h4" size="sm" weight="semibold" className="mb-1">
29+
{title}
30+
</Text>
31+
{items.length > 0 ? (
32+
<BlockStack className="w-full">
33+
{items.map((item) => (
34+
<InlineStack
35+
key={item.name}
36+
gap="2"
37+
align="space-between"
38+
blockAlign="center"
39+
className="even:bg-white odd:bg-gray-100 p-2 rounded-xs w-full"
40+
>
41+
<InlineStack
42+
gap="1"
43+
blockAlign="center"
44+
wrap="nowrap"
45+
className="flex-1 min-w-0 text-xs"
46+
>
47+
<Text as="span" weight="semibold" className="shrink-0">
48+
{item.name}:
49+
</Text>
50+
{item.value !== undefined ? (
51+
!disableCopy ? (
52+
<CopyText className="truncate">
53+
{item.value || "No value"}
54+
</CopyText>
55+
) : (
56+
<Text as="span" className="truncate">
57+
{item.value || "No value"}
58+
</Text>
59+
)
60+
) : (
61+
<Text as="span" tone="subdued">
62+
63+
</Text>
64+
)}
65+
</InlineStack>
66+
<Text size="xs" tone="subdued" className="shrink-0">
67+
{item.type}
68+
</Text>
69+
{item.actions}
70+
</InlineStack>
71+
))}
72+
</BlockStack>
73+
) : (
74+
<Text size="xs" tone="subdued">
75+
{emptyMessage}
76+
</Text>
77+
)}
78+
</BlockStack>
79+
);
80+
81+
interface ArtifactsListProps {
82+
inputs: ArtifactItem[];
83+
outputs: ArtifactItem[];
84+
}
85+
86+
export const ArtifactsList = ({ inputs, outputs }: ArtifactsListProps) => (
87+
<BlockStack className="w-full">
88+
<Text as="h3" size="md" weight="semibold" className="mb-1">
89+
Artifacts
90+
</Text>
91+
<BlockStack gap="4" className="w-full">
92+
<ArtifactsSection
93+
title="Inputs"
94+
items={inputs}
95+
emptyMessage="No inputs"
96+
/>
97+
<ArtifactsSection
98+
title="Outputs"
99+
items={outputs}
100+
emptyMessage="No outputs"
101+
disableCopy
102+
/>
103+
</BlockStack>
104+
</BlockStack>
105+
);

0 commit comments

Comments
 (0)