diff --git a/src/app/deployments/[deploymentId]/_components/code-collapsible.tsx b/src/app/deployments/[deploymentId]/_components/code-collapsible.tsx
index e1915d365..82e98251f 100644
--- a/src/app/deployments/[deploymentId]/_components/code-collapsible.tsx
+++ b/src/app/deployments/[deploymentId]/_components/code-collapsible.tsx
@@ -60,7 +60,7 @@ function DeploymentCodeContent({ deployment }: Props) {
return (
-
+
Invocation
diff --git a/src/app/deployments/[deploymentId]/_components/deployment-detail.tsx b/src/app/deployments/[deploymentId]/_components/deployment-detail.tsx
index 9d80a2162..da186fb57 100644
--- a/src/app/deployments/[deploymentId]/_components/deployment-detail.tsx
+++ b/src/app/deployments/[deploymentId]/_components/deployment-detail.tsx
@@ -35,7 +35,7 @@ function DeploymentDetailContent({ deployment }: Props) {
return (
-
+
Details
diff --git a/src/app/deployments/[deploymentId]/_components/endpoint.tsx b/src/app/deployments/[deploymentId]/_components/endpoint.tsx
index af6a990e0..104519e78 100644
--- a/src/app/deployments/[deploymentId]/_components/endpoint.tsx
+++ b/src/app/deployments/[deploymentId]/_components/endpoint.tsx
@@ -27,7 +27,7 @@ function EndpointCollapsibleContent({ deployment }: Props) {
return (
-
+
Endpoint
diff --git a/src/app/deployments/[deploymentId]/_components/stack-collapsible.tsx b/src/app/deployments/[deploymentId]/_components/stack-collapsible.tsx
index d2d49e775..b5c3e3cc7 100644
--- a/src/app/deployments/[deploymentId]/_components/stack-collapsible.tsx
+++ b/src/app/deployments/[deploymentId]/_components/stack-collapsible.tsx
@@ -1,12 +1,13 @@
+import AlertCircle from "@/assets/icons/alert-circle.svg?react";
import { CollapsibleCard } from "@/components/CollapsibleCard";
-import { StackInfo } from "@/components/stacks/info";
+import { EmptyState } from "@/components/EmptyState";
+import { StackInfoFull } from "@/components/stacks/info/stack-info-full";
import { pipelineSnapshotQueries } from "@/data/pipeline-snapshots";
import { useStack } from "@/data/stacks/stack-detail-query";
import { Deployment } from "@/types/deployments";
import { PipelineSnapshot } from "@/types/pipeline-snapshots";
import { useQuery } from "@tanstack/react-query";
import { Skeleton } from "@zenml-io/react-component-library";
-import { AlertEmptyState } from "./common";
import { DeploymentDetailWrapper } from "./fetch-wrapper";
type DeploymentStackCollapsibleContentProps = {
@@ -14,15 +15,7 @@ type DeploymentStackCollapsibleContentProps = {
};
export function DeploymentStackCollapsible() {
- return ;
-}
-
-function DeploymentStackCollapsibleWrapper({ deployment }: DeploymentStackCollapsibleContentProps) {
- return (
-
-
-
- );
+ return ;
}
function DeploymentStackCollapsibleContent({ deployment }: DeploymentStackCollapsibleContentProps) {
@@ -46,7 +39,7 @@ function DeploymentStackCollapsibleWithSnapshot({ snapshotId }: { snapshotId: st
if (snapshotQuery.isError) {
return (
-
@@ -77,8 +70,8 @@ function DeploymentStackCollapsibleStackSection({
if (stackQuery.isError) {
return (
-
);
@@ -88,12 +81,28 @@ function DeploymentStackCollapsibleStackSection({
const snapshotConfig = snapshot.metadata?.pipeline_configuration.settings || {};
- return ;
+ return ;
+}
+
+function StackCollapsibleEmptyState({ title, subtitle }: { title: string; subtitle?: string }) {
+ return (
+
+ }
+ >
+
+
{title}
+ {subtitle &&
{subtitle}
}
+
+
+
+ );
}
function NoStackEmptyState() {
return (
-
diff --git a/src/app/snapshots/[snapshotId]/_components/snapshot-detail.tsx b/src/app/snapshots/[snapshotId]/_components/snapshot-detail.tsx
index 2f7c50e53..0e9ff37fa 100644
--- a/src/app/snapshots/[snapshotId]/_components/snapshot-detail.tsx
+++ b/src/app/snapshots/[snapshotId]/_components/snapshot-detail.tsx
@@ -41,7 +41,7 @@ function DetailsContent({ snapshot }: Props) {
return (
-
+
Details
diff --git a/src/app/snapshots/[snapshotId]/_components/snapshot-stack-content.tsx b/src/app/snapshots/[snapshotId]/_components/snapshot-stack-content.tsx
deleted file mode 100644
index fb84119da..000000000
--- a/src/app/snapshots/[snapshotId]/_components/snapshot-stack-content.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import StackIcon from "@/assets/icons/stack.svg?react";
-import { CollapsibleChevron } from "@/components/collapsible-chevron";
-import { ComponentCollapsible } from "@/components/stacks/info/ComponentCollapsible";
-import { Stack, StackComponentsList } from "@/types/stack";
-import {
- CollapsibleContent,
- CollapsibleHeader,
- CollapsiblePanel,
- CollapsibleTrigger,
- Tag
-} from "@zenml-io/react-component-library";
-import { useState } from "react";
-
-type Props = {
- stack: Stack;
-};
-
-export function StackContent({ stack }: Props) {
- const [open, setOpen] = useState(true);
-
- const stackName = stack.name;
-
- const components = Object.values(
- (stack.metadata?.components as StackComponentsList) ?? {}
- ).flat();
-
- return (
-
-
-
-
- Stack
-
-
-
-
- {stackName}
-
-
-
-
-
- {components.map((c) => (
- -
-
-
- ))}
-
-
-
- );
-}
diff --git a/src/app/snapshots/[snapshotId]/_components/snapshot-stack.tsx b/src/app/snapshots/[snapshotId]/_components/snapshot-stack.tsx
index a47636546..135afb788 100644
--- a/src/app/snapshots/[snapshotId]/_components/snapshot-stack.tsx
+++ b/src/app/snapshots/[snapshotId]/_components/snapshot-stack.tsx
@@ -1,9 +1,9 @@
+import { StackInfoFull } from "@/components/stacks/info/stack-info-full";
import { pipelineSnapshotQueries } from "@/data/pipeline-snapshots";
import { stackQueries } from "@/data/stacks";
import { useQuery } from "@tanstack/react-query";
import { Skeleton } from "@zenml-io/react-component-library/components/server";
import { useParams } from "react-router-dom";
-import { StackContent } from "./snapshot-stack-content";
export function SnapshotStack() {
const { snapshotId } = useParams() as {
@@ -39,5 +39,5 @@ export function SnapshotStack() {
return Failed to load stack.
;
}
- return ;
+ return ;
}
diff --git a/src/components/CollapsibleCard.tsx b/src/components/CollapsibleCard.tsx
index f57eeca64..374f4ee01 100644
--- a/src/components/CollapsibleCard.tsx
+++ b/src/components/CollapsibleCard.tsx
@@ -1,4 +1,3 @@
-import ChevronDown from "@/assets/icons/chevron-down.svg?react";
import {
CollapsibleContent,
CollapsibleHeader,
@@ -8,6 +7,7 @@ import {
} from "@zenml-io/react-component-library";
import { cn } from "@zenml-io/react-component-library/utilities";
import { useState } from "react";
+import { CollapsibleChevron } from "./collapsible-chevron";
import { CopyMetadataButton } from "./copy-metadata-button";
type CollapsibleCardProps = {
@@ -35,12 +35,8 @@ export function CollapsibleCard({
return (
-
-
+
+
{title}
@@ -59,7 +55,7 @@ export function CollapsibleCard({
);
}
-type Props = Omit & {
+type Props = CollapsibleCardProps & {
copyText: string;
displayCopyButton?: boolean;
};
@@ -69,6 +65,7 @@ export function CollapsibleCardWithCopy({
displayCopyButton = true,
headerClassName,
className,
+ headerChildren,
...props
}: Props) {
return (
@@ -80,7 +77,9 @@ export function CollapsibleCardWithCopy({
headerClassName
)}
headerChildren={
- displayCopyButton ? (
+ headerChildren ? (
+ headerChildren
+ ) : displayCopyButton ? (
diff --git a/src/components/NestedCollapsible.tsx b/src/components/NestedCollapsible.tsx
index a9767db64..03d4e031c 100644
--- a/src/components/NestedCollapsible.tsx
+++ b/src/components/NestedCollapsible.tsx
@@ -23,6 +23,7 @@ type Props = {
sortKeysAlphabetically?: boolean;
className?: string;
schema?: JSONSchemaDefinition;
+ headerChildren?: ReactNode;
};
const regex = /^$/;
@@ -36,7 +37,8 @@ export function NestedCollapsible({
className,
children,
isInitialOpen = false,
- sortKeysAlphabetically = true
+ sortKeysAlphabetically = true,
+ headerChildren
}: PropsWithChildren) {
const objects: { [key: string]: Record } = {};
const nonObjects: { [key: string]: unknown } = {};
@@ -62,6 +64,7 @@ export function NestedCollapsible({
return (
-
+
Failed to fetch Stack
;
}
- const config = (run.metadata?.config.settings as { [key: string]: any } | undefined) || {};
+ const config = run.metadata?.config.settings || {};
- return ;
+ return ;
}
diff --git a/src/components/stacks/info/ComponentCollapsible.tsx b/src/components/stacks/info/ComponentCollapsible.tsx
deleted file mode 100644
index 4f88b7e24..000000000
--- a/src/components/stacks/info/ComponentCollapsible.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import { snakeCaseToTitleCase } from "@/lib/strings";
-import { sanitizeUrl } from "@/lib/url";
-import { routes } from "@/router/routes";
-import { StackComponent } from "@/types/components";
-import { Box, Button } from "@zenml-io/react-component-library/components/server";
-import { Link } from "react-router-dom";
-import { NestedCollapsible } from "../../NestedCollapsible";
-import { ComponentBadge } from "../../stack-components/ComponentBadge";
-
-type Props = {
- component: StackComponent;
- objectConfig: Record;
-};
-export function ComponentCollapsible({ component, objectConfig }: Props) {
- const keyName = `${component.body?.type}.${component.body?.flavor_name}`;
- const settings = objectConfig?.[keyName] ?? undefined;
-
- if (!settings || Object.keys(settings).length === 0) {
- return (
-
-
-
-
-
- );
- }
-
- return (
-
-
-
- }
- data={settings}
- >
-
-
- );
-}
-
-export function ComponentCollapsibleItem({ component }: { component: StackComponent }) {
- return (
- <>
-
-

-
-
{component.name}
-
{component.id.split("-")[0]}
-
-
-
- {snakeCaseToTitleCase(component.body?.type || "")}
-
- >
- );
-}
diff --git a/src/components/stacks/info/StackHeader.tsx b/src/components/stacks/info/StackHeader.tsx
deleted file mode 100644
index b9157162d..000000000
--- a/src/components/stacks/info/StackHeader.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Stack } from "@/types/stack";
-import StackIcon from "@/assets/icons/stack.svg?react";
-import { Avatar, AvatarFallback } from "@zenml-io/react-component-library/components/client";
-import { Box, Tag } from "@zenml-io/react-component-library/components/server";
-
-type Props = {
- stack: Stack;
-};
-
-export function StackHeader({ stack }: Props) {
- return (
-
-
-
- {stack.name[0]}
-
-
-
{stack.name}
-
{stack.id.split("-")[0]}
-
-
-
-
- Stack
-
-
- );
-}
diff --git a/src/components/stacks/info/index.tsx b/src/components/stacks/info/index.tsx
deleted file mode 100644
index ac272147b..000000000
--- a/src/components/stacks/info/index.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { Stack, StackComponentsList } from "@/types/stack";
-import { InfoBox } from "../../Infobox";
-import { ComponentCollapsible } from "./ComponentCollapsible";
-import { StackHeader } from "./StackHeader";
-
-type Props = {
- stack: Stack;
- displayInfoBox?: boolean;
- objectConfig: Record;
-};
-export function StackInfo({ stack, objectConfig, displayInfoBox = true }: Props) {
- const allComponents = Object.values(
- (stack.metadata?.components as StackComponentsList) || {}
- ).flat();
- return (
-
- {displayInfoBox &&
}
-
-
- {allComponents.map((component) => (
- -
-
-
- ))}
-
-
- );
-}
-
-function StackInfobox() {
- return (
-
-
- Current run-specific stack settings. Click on a component for full default settings.
-
-
- );
-}
diff --git a/src/components/stacks/info/sort-component.ts b/src/components/stacks/info/sort-component.ts
new file mode 100644
index 000000000..60acd0240
--- /dev/null
+++ b/src/components/stacks/info/sort-component.ts
@@ -0,0 +1,16 @@
+import { StackComponent } from "@/types/components";
+import { StackComponentType } from "@/types/components";
+import { stackComponentTypes } from "@/lib/constants";
+
+export function sortComponents(components: StackComponent[]) {
+ return components.sort((a, b) => {
+ const typeA = a.body?.type ?? "";
+ const typeB = b.body?.type ?? "";
+ const indexA = stackComponentTypes.indexOf(typeA as StackComponentType);
+ const indexB = stackComponentTypes.indexOf(typeB as StackComponentType);
+ // If type not found in array, put it at the end
+ const orderA = indexA === -1 ? stackComponentTypes.length : indexA;
+ const orderB = indexB === -1 ? stackComponentTypes.length : indexB;
+ return orderA - orderB;
+ });
+}
diff --git a/src/components/stacks/info/stack-info-collapsible.tsx b/src/components/stacks/info/stack-info-collapsible.tsx
new file mode 100644
index 000000000..b22b34386
--- /dev/null
+++ b/src/components/stacks/info/stack-info-collapsible.tsx
@@ -0,0 +1,45 @@
+import {
+ Avatar,
+ AvatarFallback,
+ CollapsibleContent,
+ CollapsibleHeader,
+ CollapsiblePanel,
+ CollapsibleTrigger
+} from "@zenml-io/react-component-library";
+import { cn } from "@zenml-io/react-component-library/utilities";
+import { PropsWithChildren, useState } from "react";
+import { CollapsibleChevron } from "../../collapsible-chevron";
+
+type Props = {
+ stackName: string;
+};
+
+export function StackInfoCollapsible({ children, stackName }: PropsWithChildren) {
+ const [open, setOpen] = useState(true);
+ return (
+
+
+
+
+
+
+ {stackName[0] || "S"}
+
+
+
+
+
+
+
+ {children}
+
+
+ );
+}
diff --git a/src/components/stacks/info/stack-info-component-list-item.tsx b/src/components/stacks/info/stack-info-component-list-item.tsx
new file mode 100644
index 000000000..d3d5f5477
--- /dev/null
+++ b/src/components/stacks/info/stack-info-component-list-item.tsx
@@ -0,0 +1,100 @@
+import { ComponentIcon } from "@/components/ComponentIcon";
+import { snakeCaseToTitleCase } from "@/lib/strings";
+import { isObject } from "@/lib/type-guards";
+import { sanitizeUrl } from "@/lib/url";
+import { routes } from "@/router/routes";
+import { StackComponent, StackComponentType } from "@/types/components";
+import { Box } from "@zenml-io/react-component-library/components/server";
+import { Link } from "react-router-dom";
+import { NestedCollapsible } from "../../NestedCollapsible";
+import { CopyMetadataButton } from "@/components/copy-metadata-button";
+import { PropsWithChildren } from "react";
+
+type Props = {
+ component: StackComponent;
+ objectConfig: Record;
+};
+export function StackInfoComponentListItem({ component, objectConfig }: Props) {
+ const keyName = `${component.body?.type}.${component.body?.flavor_name}`;
+ const settings = objectConfig?.[keyName] ?? undefined;
+
+ const isValidObject = isObject(settings);
+
+ if (!isValidObject || Object.keys(settings).length === 0) {
+ return (
+
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+ }
+ data={settings as Record}
+ >
+ );
+}
+
+type ComponentCollapsibleHeaderProps = {
+ componentName: string;
+ componentLogoUrl: string | undefined;
+ componentType: StackComponentType;
+};
+
+function ComponentCollapsibleHeader({
+ componentLogoUrl,
+ componentName,
+ componentType,
+ children
+}: PropsWithChildren) {
+ return (
+
+
+ {snakeCaseToTitleCase(componentType)}
+
+
+
+ {componentLogoUrl ? (
+
})
+ ) : (
+
+ )}
+
{componentName}
+
+ {children}
+
+
+ );
+}
diff --git a/src/components/stacks/info/stack-info-component-list.tsx b/src/components/stacks/info/stack-info-component-list.tsx
new file mode 100644
index 000000000..6ee0ff4b8
--- /dev/null
+++ b/src/components/stacks/info/stack-info-component-list.tsx
@@ -0,0 +1,23 @@
+import { Stack, StackComponentsList } from "@/types/stack";
+import { StackInfoComponentListItem } from "./stack-info-component-list-item";
+import { sortComponents } from "./sort-component";
+
+type Props = {
+ stack: Stack;
+ objectConfig: Record;
+};
+export function StackInfoComponentList({ stack, objectConfig }: Props) {
+ const allComponents = sortComponents(
+ Object.values((stack.metadata?.components as StackComponentsList) || {}).flat()
+ );
+
+ return (
+
+ {allComponents.map((component) => (
+ -
+
+
+ ))}
+
+ );
+}
diff --git a/src/components/stacks/info/stack-info-full.tsx b/src/components/stacks/info/stack-info-full.tsx
new file mode 100644
index 000000000..e9465157a
--- /dev/null
+++ b/src/components/stacks/info/stack-info-full.tsx
@@ -0,0 +1,16 @@
+import { Stack } from "@/types/stack";
+import { StackInfoCollapsible } from "./stack-info-collapsible";
+import { StackInfoComponentList } from "./stack-info-component-list";
+
+type Props = {
+ stack: Stack;
+ objectConfig: Record;
+};
+
+export function StackInfoFull({ stack, objectConfig }: Props) {
+ return (
+
+
+
+ );
+}
diff --git a/src/components/steps/step-sheet/StacksTab.tsx b/src/components/steps/step-sheet/StacksTab.tsx
index 2d9fb73b7..3bb5122b2 100644
--- a/src/components/steps/step-sheet/StacksTab.tsx
+++ b/src/components/steps/step-sheet/StacksTab.tsx
@@ -1,5 +1,5 @@
import AlertCircle from "@/assets/icons/alert-circle.svg?react";
-import { StackInfo } from "@/components/stacks/info";
+import { StackInfoFull } from "@/components/stacks/info/stack-info-full";
import { usePipelineRun } from "@/data/pipeline-runs/pipeline-run-detail-query";
import { useStack } from "@/data/stacks/stack-detail-query";
import { useStepDetail } from "@/data/steps/step-detail-query";
@@ -20,7 +20,7 @@ export function StackTab({ stepId }: Props) {
if (run.isError || step.isError) return Something went wrong fetching the run
;
const stackId = run.data?.resources?.stack?.id;
- const config = (step.data.metadata?.config.settings as { [key: string]: any } | undefined) || {};
+ const config = step.data.metadata?.config.settings || {};
if (!stackId)
return (
@@ -39,7 +39,7 @@ export function StackTab({ stepId }: Props) {
type StackTabContentProps = {
stackId: string;
- objectConfig: Record;
+ objectConfig: Record;
};
function StackTabContent({ stackId, objectConfig }: StackTabContentProps) {
const { data, isError, isPending } = useStack({ stackId: stackId });
@@ -50,5 +50,5 @@ function StackTabContent({ stackId, objectConfig }: StackTabContentProps) {
return Failed to fetch Stack
;
}
- return ;
+ return ;
}