Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 41 additions & 118 deletions src/components/Editor/PipelineDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useEffect, useState } from "react";

import { PipelineValidationList } from "@/components/Editor/components/PipelineValidationList/PipelineValidationList";
import { useValidationIssueNavigation } from "@/components/Editor/hooks/useValidationIssueNavigation";
import { ArtifactsList } from "@/components/shared/ArtifactsList/ArtifactsList";
import { CopyText } from "@/components/shared/CopyText/CopyText";
import { Button } from "@/components/ui/button";
import { Icon } from "@/components/ui/icon";
Expand Down Expand Up @@ -98,18 +99,6 @@ const PipelineDetails = () => {
);
};

const handleInputCopy = (input: InputSpec) => {
const value = input.value ?? input.default;

if (!value) {
notify("Copy failed: Input has no value", "error");
return;
}

void navigator.clipboard.writeText(value);
notify("Input value copied to clipboard", "success");
};

const handleDigestCopy = () => {
navigator.clipboard.writeText(digest);
notify("Digest copied to clipboard", "success");
Expand Down Expand Up @@ -232,112 +221,46 @@ const PipelineDetails = () => {
</BlockStack>
)}

{/* Artifacts (Inputs & Outputs) */}
<BlockStack>
<Text as="h3" size="md" weight="semibold" className="mb-1">
Artifacts
</Text>
<BlockStack gap="4">
<BlockStack>
<Text as="h4" size="sm" weight="semibold" className="mb-1">
Inputs
</Text>
{componentSpec.inputs && componentSpec.inputs.length > 0 ? (
<div className="flex flex-col">
{componentSpec.inputs.map((input) => {
return (
<div
className="flex flex-row justify-between even:bg-white odd:bg-gray-100 gap-1 px-2 py-1 rounded-xs items-center"
key={input.name}
>
<div className="text-xs flex-1 truncate max-w-[200px]">
<span className="font-semibold">{input.name}:</span>{" "}
{input.value || input.default || "No value"}
</div>

<div className="text-xs flex-1 font-mono truncate max-w-[100px]">
<span className="font-semibold">Type:</span>{" "}
{typeSpecToString(input?.type)}
</div>
<InlineStack
className="min-w-24"
align="end"
blockAlign="center"
>
<Button
type="button"
variant="ghost"
size="min"
onClick={() => handleInputCopy(input)}
className="text-muted-foreground hover:text-foreground"
>
<Icon name="Copy" className="size-3" />
</Button>

<Button
type="button"
variant="ghost"
className="text-xs text-muted-foreground cursor-pointer hover:bg-transparent"
size="sm"
onClick={() => handleInputEdit(input)}
>
Edit
</Button>
</InlineStack>
</div>
);
})}
</div>
) : (
<div className="text-xs text-muted-foreground">No inputs</div>
)}
</BlockStack>
<BlockStack>
<Text as="h4" size="sm" weight="semibold" className="mb-1">
Outputs
</Text>
{componentSpec.outputs && componentSpec.outputs.length > 0 ? (
<div className="flex flex-col">
{componentSpec.outputs.map((output) => (
<div
className="flex flex-row justify-between even:bg-white odd:bg-gray-100 gap-1 px-2 py-1 rounded-xs items-center"
key={output.name}
>
<div className="text-xs flex-1 truncate max-w-[200px]">
<span className="font-semibold">{output.name}:</span>{" "}
{
getOutputConnectedDetails(graphSpec, output.name)
.outputName
}
</div>
<div className="text-xs">
<span className="font-semibold">Type:</span>{" "}
{typeSpecToString(
getOutputConnectedDetails(graphSpec, output.name)
.outputType,
)}
</div>

<InlineStack className="min-w-24" align={"end"}>
<Button
type="button"
variant="ghost"
className="text-xs text-muted-foreground cursor-pointer hover:bg-transparent"
size="sm"
onClick={() => handleOutputEdit(output)}
>
Edit
</Button>
</InlineStack>
</div>
))}
</div>
) : (
<div className="text-xs text-muted-foreground">No outputs</div>
)}
</BlockStack>
</BlockStack>
</BlockStack>
<ArtifactsList
inputs={(componentSpec.inputs ?? []).map((input) => ({
name: input.name,
type: typeSpecToString(input?.type),
value: input.value || input.default,
actions: (
<Button
type="button"
variant="ghost"
size="min"
onClick={() => handleInputEdit(input)}
className="text-muted-foreground hover:text-foreground h-4 w-4"
>
<Icon name="Pencil" className="size-2.5" />
</Button>
),
}))}
outputs={(componentSpec.outputs ?? []).map((output) => {
const connectedDetails = getOutputConnectedDetails(
graphSpec,
output.name,
);
return {
name: output.name,
type: typeSpecToString(connectedDetails.outputType),
value: connectedDetails.outputName,
actions: (
<Button
type="button"
variant="ghost"
size="min"
onClick={() => handleOutputEdit(output)}
className="text-muted-foreground hover:text-foreground h-4 w-4"
>
<Icon name="Pencil" className="size-2.5" />
</Button>
),
};
})}
/>

{/* Validations */}
<BlockStack>
Expand Down
67 changes: 12 additions & 55 deletions src/components/PipelineRun/RunDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Frown } from "lucide-react";

import { ArtifactsList } from "@/components/shared/ArtifactsList/ArtifactsList";
import { CopyText } from "@/components/shared/CopyText/CopyText";
import { BlockStack, InlineStack } from "@/components/ui/layout";
import { Spinner } from "@/components/ui/spinner";
Expand Down Expand Up @@ -201,61 +202,17 @@ export const RunDetails = () => {
</BlockStack>
)}

<BlockStack className="w-full">
<Text as="h3" size="md" weight="semibold" className="mb-1">
Artifacts
</Text>
<BlockStack gap="4" className="w-full">
<BlockStack className="w-full">
<Text as="h4" size="sm" weight="semibold" className="mb-1">
Inputs
</Text>
{componentSpec.inputs && componentSpec.inputs.length > 0 ? (
<div className="flex flex-col w-full">
{componentSpec.inputs.map((input) => (
<div
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"
key={input.name}
>
<div className="text-xs flex-1 truncate">
<span className="font-semibold">{input.name}</span>
</div>
<div className="text-xs text-muted-foreground">
{typeof input.type === "string" ? input.type : "object"}
</div>
</div>
))}
</div>
) : (
<div className="text-xs text-muted-foreground">No inputs</div>
)}
</BlockStack>
<BlockStack className="w-full">
<Text as="h4" size="sm" weight="semibold" className="mb-1">
Outputs
</Text>
{componentSpec.outputs && componentSpec.outputs.length > 0 ? (
<div className="flex flex-col w-full">
{componentSpec.outputs.map((output) => (
<div
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"
key={output.name}
>
<div className="text-xs flex-1 truncate">
<span className="font-semibold">{output.name}</span>
</div>
<div className="text-xs text-muted-foreground">
{typeof output.type === "string" ? output.type : "object"}
</div>
</div>
))}
</div>
) : (
<div className="text-xs text-muted-foreground">No outputs</div>
)}
</BlockStack>
</BlockStack>
</BlockStack>
<ArtifactsList
inputs={(componentSpec.inputs ?? []).map((input) => ({
name: input.name,
type: typeof input.type === "string" ? input.type : "object",
value: input.value ?? input.default,
}))}
outputs={(componentSpec.outputs ?? []).map((output) => ({
name: output.name,
type: typeof output.type === "string" ? output.type : "object",
}))}
/>
</BlockStack>
);
};
105 changes: 105 additions & 0 deletions src/components/shared/ArtifactsList/ArtifactsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { type ReactNode } from "react";

import { CopyText } from "@/components/shared/CopyText/CopyText";
import { BlockStack, InlineStack } from "@/components/ui/layout";
import { Text } from "@/components/ui/typography";

interface ArtifactItem {
name: string;
type: string;
value?: string;
actions?: ReactNode;
}

interface ArtifactsSectionProps {
title: string;
items: ArtifactItem[];
emptyMessage?: string;
disableCopy?: boolean;
}

const ArtifactsSection = ({
title,
items,
emptyMessage = "None",
disableCopy = false,
}: ArtifactsSectionProps) => (
<BlockStack className="w-full">
<Text as="h4" size="sm" weight="semibold" className="mb-1">
{title}
</Text>
{items.length > 0 ? (
<BlockStack className="w-full">
{items.map((item) => (
<InlineStack
key={item.name}
gap="2"
align="space-between"
blockAlign="center"
className="even:bg-white odd:bg-gray-100 p-2 rounded-xs w-full"
>
<InlineStack
gap="1"
blockAlign="center"
wrap="nowrap"
className="flex-1 min-w-0 text-xs"
>
<Text as="span" weight="semibold" className="shrink-0">
{item.name}:
</Text>
{item.value !== undefined ? (
!disableCopy ? (
<CopyText className="truncate">
{item.value || "No value"}
</CopyText>
) : (
<Text as="span" className="truncate">
{item.value || "No value"}
</Text>
)
) : (
<Text as="span" tone="subdued">
</Text>
)}
</InlineStack>
<Text size="xs" tone="subdued" className="shrink-0">
{item.type}
</Text>
{item.actions}
</InlineStack>
))}
</BlockStack>
) : (
<Text size="xs" tone="subdued">
{emptyMessage}
</Text>
)}
</BlockStack>
);

interface ArtifactsListProps {
inputs: ArtifactItem[];
outputs: ArtifactItem[];
}

export const ArtifactsList = ({ inputs, outputs }: ArtifactsListProps) => (
<BlockStack className="w-full">
<Text as="h3" size="md" weight="semibold" className="mb-1">
Artifacts
</Text>
<BlockStack gap="4" className="w-full">
<ArtifactsSection
title="Inputs"
items={inputs}
emptyMessage="No inputs"
/>
<ArtifactsSection
title="Outputs"
items={outputs}
emptyMessage="No outputs"
disableCopy
/>
</BlockStack>
</BlockStack>
);