Skip to content

Commit bc57532

Browse files
feat: stacks fallbacks (#626)
1 parent a867a81 commit bc57532

File tree

23 files changed

+603
-12
lines changed

23 files changed

+603
-12
lines changed

src/app/stacks/ActionsDropdown.tsx

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import HorizontalDots from "@/assets/icons/dots-horizontal.svg?react";
2+
import Edit from "@/assets/icons/edit.svg?react";
3+
import Trash from "@/assets/icons/trash.svg?react";
4+
import { DialogItem } from "@/components/dialog/DialogItem";
5+
import {
6+
DropdownMenu,
7+
DropdownMenuContent,
8+
DropdownMenuTrigger
9+
} from "@zenml-io/react-component-library";
10+
import { useRef, useState } from "react";
11+
import { DeleteStackDialog, UpdateStackDialog } from "./DialogItems";
12+
13+
type Props = { name: string };
14+
export function StackActionsMenu({ name }: Props) {
15+
const [hasOpenDialog, setHasOpenDialog] = useState(false);
16+
const [dropdownOpen, setDropdownOpen] = useState(false);
17+
18+
const dropdownTriggerRef = useRef(null);
19+
const focusRef = useRef<HTMLElement | null>(null);
20+
21+
function handleDialogItemSelect() {
22+
focusRef.current = dropdownTriggerRef.current;
23+
}
24+
25+
function handleDialogItemOpenChange(open: boolean) {
26+
setHasOpenDialog(open);
27+
if (open === false) {
28+
setDropdownOpen(false);
29+
}
30+
}
31+
32+
return (
33+
<DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}>
34+
<DropdownMenu>
35+
<DropdownMenuTrigger className="z-10" ref={dropdownTriggerRef}>
36+
<HorizontalDots className="h-5 w-5 fill-theme-text-secondary" />
37+
</DropdownMenuTrigger>
38+
<DropdownMenuContent
39+
hidden={hasOpenDialog}
40+
onCloseAutoFocus={(event) => {
41+
if (focusRef.current) {
42+
focusRef.current.focus();
43+
focusRef.current = null;
44+
event.preventDefault();
45+
}
46+
}}
47+
className="z-10"
48+
align="end"
49+
sideOffset={1}
50+
>
51+
<DialogItem
52+
onSelect={handleDialogItemSelect}
53+
onOpenChange={handleDialogItemOpenChange}
54+
icon={<Edit className="h-3 w-3 !fill-neutral-400" />}
55+
triggerChildren="Update"
56+
>
57+
<UpdateStackDialog
58+
name={name}
59+
className="lg:min-w-[600px]"
60+
closeModal={() => handleDialogItemOpenChange(false)}
61+
/>
62+
</DialogItem>
63+
<DialogItem
64+
onSelect={handleDialogItemSelect}
65+
onOpenChange={handleDialogItemOpenChange}
66+
icon={<Trash className="h-3 w-3 !fill-neutral-400" />}
67+
triggerChildren="Delete"
68+
>
69+
<DeleteStackDialog
70+
name={name}
71+
className="lg:min-w-[600px]"
72+
closeModal={() => handleDialogItemOpenChange(false)}
73+
/>
74+
</DialogItem>
75+
</DropdownMenuContent>
76+
</DropdownMenu>
77+
</DropdownMenu>
78+
);
79+
}

src/app/stacks/DialogItems.tsx

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { DialogContent, DialogHeader, DialogTitle } from "@zenml-io/react-component-library";
2+
import { ComponentPropsWithoutRef, ElementRef, forwardRef } from "react";
3+
import { InfoBox as InfoboxPrimitive } from "@/components/Infobox";
4+
import { Codesnippet } from "@/components/CodeSnippet";
5+
6+
type Props = {
7+
closeModal?: () => void;
8+
name: string;
9+
};
10+
11+
export const UpdateStackDialog = forwardRef<
12+
ElementRef<typeof DialogContent>,
13+
ComponentPropsWithoutRef<typeof DialogContent> & Props
14+
>(({ closeModal, name, ...rest }, ref) => {
15+
return (
16+
<DialogContent {...rest} ref={ref}>
17+
<DialogHeader>
18+
<DialogTitle>Update Stack</DialogTitle>
19+
</DialogHeader>
20+
<div className="space-y-5 p-7">
21+
<Infobox action="update" />
22+
<div className="space-y-1">
23+
<p className="text-text-sm text-theme-text-secondary">Update a stack</p>
24+
<Codesnippet
25+
codeClasses="whitespace-pre-wrap"
26+
wrap
27+
code={`zenml stack update ${name} -o NEW_ORCHESTRATOR_NAME`}
28+
/>
29+
</div>
30+
</div>
31+
</DialogContent>
32+
);
33+
});
34+
35+
UpdateStackDialog.displayName = "UpdateStackDialog";
36+
37+
export const DeleteStackDialog = forwardRef<
38+
ElementRef<typeof DialogContent>,
39+
ComponentPropsWithoutRef<typeof DialogContent> & Props
40+
>(({ closeModal, name, ...rest }, ref) => {
41+
return (
42+
<DialogContent {...rest} ref={ref}>
43+
<DialogHeader>
44+
<DialogTitle>Delete Stack</DialogTitle>
45+
</DialogHeader>
46+
<div className="space-y-5 p-7">
47+
<Infobox action="delete" />
48+
<div className="space-y-1">
49+
<p className="text-text-sm text-theme-text-secondary">Delete a stack</p>
50+
<Codesnippet codeClasses="whitespace-pre-wrap" wrap code={`zenml stack delete ${name}`} />
51+
</div>
52+
</div>
53+
</DialogContent>
54+
);
55+
});
56+
57+
DeleteStackDialog.displayName = "DeleteStackDialog";
58+
59+
type InfoProps = {
60+
action: "delete" | "update" | "describe";
61+
};
62+
export function Infobox({ action }: InfoProps) {
63+
function getAction() {
64+
switch (action) {
65+
case "delete":
66+
return "delete";
67+
case "update":
68+
return "update";
69+
case "describe":
70+
return "get details of";
71+
}
72+
}
73+
74+
return (
75+
<InfoboxPrimitive>
76+
<div className="flex w-full flex-wrap justify-between gap-2">
77+
<div className="min-w-0">
78+
<p className="truncate text-text-sm font-semibold">
79+
We are working on the new Stacks experience.
80+
</p>
81+
<p className="truncate text-text-sm">
82+
Meanwhile you can use the CLI to {getAction()} your stack.
83+
</p>
84+
</div>
85+
</div>
86+
</InfoboxPrimitive>
87+
);
88+
}

src/app/stacks/columns.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { Stack } from "@/types/stack";
55
import { User } from "@/types/user";
66
import { ColumnDef } from "@tanstack/react-table";
77
import { Avatar, AvatarFallback } from "@zenml-io/react-component-library";
8+
import { StackActionsMenu } from "./ActionsDropdown";
9+
import { StackSheet } from "@/components/stacks/Sheet";
810

911
export function getStackColumns(): ColumnDef<Stack>[] {
1012
return [
@@ -21,7 +23,9 @@ export function getStackColumns(): ColumnDef<Stack>[] {
2123
</Avatar>
2224
<div>
2325
<div className="flex items-center gap-1">
24-
<h2 className="text-text-md font-semibold">{name}</h2>
26+
<StackSheet stackId={id}>
27+
<h2 className="text-text-md font-semibold">{name}</h2>
28+
</StackSheet>
2529
</div>
2630
<div className="flex items-center gap-1">
2731
<p className="text-text-xs text-theme-text-secondary">{id.split("-")[0]}</p>
@@ -51,6 +55,14 @@ export function getStackColumns(): ColumnDef<Stack>[] {
5155
if (!author) return null;
5256
return <InlineAvatar username={author.name} />;
5357
}
58+
},
59+
{
60+
id: "actions",
61+
header: "",
62+
accessorKey: "name",
63+
cell: ({ getValue }) => {
64+
return <StackActionsMenu name={getValue<string>()} />;
65+
}
5466
}
5567
];
5668
}

src/app/stacks/create/new-infrastructure/Providers/AWS.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Tick } from "@/components/Tick";
22
import { Avatar, AvatarFallback, Spinner } from "@zenml-io/react-component-library";
33
import { ComponentListItem, ProviderComponents } from ".";
4-
import { ComponentBadge } from "../ComponentBadge";
4+
import { ComponentBadge } from "../../../../../components/stack-components/ComponentBadge";
55
import { PermissionsCard } from "./PermissionsCard";
66

77
type Props = ProviderComponents;

src/app/stacks/create/new-infrastructure/Providers/GCP.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Tick } from "@/components/Tick";
22
import { Avatar, AvatarFallback, Spinner } from "@zenml-io/react-component-library";
33
import { ComponentListItem, ProviderComponents } from ".";
4-
import { ComponentBadge } from "../ComponentBadge";
4+
import { ComponentBadge } from "../../../../../components/stack-components/ComponentBadge";
55
import { PermissionsCard } from "./PermissionsCard";
66

77
type Props = ProviderComponents;
Lines changed: 2 additions & 2 deletions
Loading

src/assets/icons/edit.svg

Lines changed: 4 additions & 0 deletions
Loading

src/assets/icons/trash.svg

Lines changed: 4 additions & 0 deletions
Loading

src/components/breadcrumbs/Breadcrumbs.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export function Breadcrumbs() {
7171
<span
7272
className={`
7373
${isLastOne ? "pointer-events-none text-theme-text-primary" : "text-theme-text-secondary"}
74-
ml-1 flex items-center text-text-md font-semibold capitalize`}
74+
ml-1 flex items-center text-text-md font-semibold`}
7575
>
7676
{formatIdToTitleCase(value?.name as string)}
7777
</span>
@@ -80,7 +80,7 @@ export function Breadcrumbs() {
8080
<Link
8181
className={`${isLastOne || segment === "settings" ? "pointer-events-none" : ""}
8282
${isLastOne ? "font-semibold text-theme-text-primary" : "text-theme-text-secondary"}
83-
rounded-sm p-0.5 px-1 text-text-md capitalize hover:text-purple-900 hover:underline`}
83+
rounded-sm p-0.5 px-1 text-text-md hover:text-purple-900 hover:underline`}
8484
to={matchSegmentWithURL(segment, value?.id as string)}
8585
>
8686
{typeof value?.name === "string" ? (

src/components/breadcrumbs/SegmentsBreadcrumbs.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@ export const matchSegmentWithRequest = ({ segment, data }: { segment: string; da
66
const routeMap: { [key: string]: { [key: string]: { id?: string | null; name?: string } } } = {
77
// Pipelines
88
pipelines: {
9-
pipelines: { id: data?.body?.pipeline?.id, name: "pipelines" }
9+
pipelines: { id: data?.body?.pipeline?.id, name: "Pipelines" }
1010
},
1111
pipeline_detail: {
12-
pipelines: { id: data?.body?.pipeline?.id, name: "pipelines" },
12+
pipelines: { id: data?.body?.pipeline?.id, name: "Pipelines" },
1313
pipeline_detail: { id: data?.name, name: data?.name }
1414
},
1515
stacks: {
16-
stacks: { name: "stacks" }
16+
stacks: { name: "Stacks" }
1717
},
1818
create_stack: {
19-
stacks: { name: "stacks" },
19+
stacks: { name: "Stacks" },
2020
create: { name: "New Stack" }
2121
},
2222
runs: {
23-
pipelines: { id: data?.body?.pipeline?.id, name: "pipelines" },
23+
pipelines: { id: data?.body?.pipeline?.id, name: "Pipelines" },
2424
pipeline_detail: {
2525
id: data?.body?.pipeline?.name,
2626
name: data?.body?.pipeline?.name

0 commit comments

Comments
 (0)