Skip to content

Commit

Permalink
Merge branch 'future' into refactor/prd-390--onboarding
Browse files Browse the repository at this point in the history
  • Loading branch information
Cahllagerfeld committed Jul 12, 2024
2 parents 517c610 + f9e9e3d commit 2de2aa3
Show file tree
Hide file tree
Showing 42 changed files with 833 additions and 144 deletions.
2 changes: 1 addition & 1 deletion src/app/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function OverviewHeader() {
{serverSettings ? (
<Avatar size="xxl" type="square">
<AvatarImage
alt="Logo displayed for the tenant"
alt="Logo displayed for the server"
src={getGradientImage(serverSettings.body?.server_name || "default", 64)}
/>
<AvatarFallback size="xxl">
Expand Down
6 changes: 3 additions & 3 deletions src/app/artifacts/Fragments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ export function InfoBox() {
return (
<InfoBoxPrimitive>
<div className="flex w-full flex-wrap items-center gap-x-2 gap-y-0.5 text-text-md">
<p className="font-semibold">This is a ZenML Cloud feature. </p>
<p className="font-semibold">This is a ZenML Pro feature. </p>
<p>
Upgrade to ZenML Cloud to access the Artifact Control Plane and interact with your
artifacts in the Dashboard.
Upgrade to ZenML Pro to access the Artifact Control Plane and interact with your artifacts
in the Dashboard.
</p>
</div>
</InfoBoxPrimitive>
Expand Down
4 changes: 2 additions & 2 deletions src/app/artifacts/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ export default function ModelsPage() {
<PageHeader className="flex items-center gap-1">
<h1 className="text-display-xs font-semibold">Artifacts</h1>
<Badge color="purple" rounded size="sm">
<span className="font-semibold text-primary-500">Cloud</span>
<span className="font-semibold text-primary-500">Pro</span>
</Badge>
</PageHeader>
<div className="layout-container space-y-5 py-5">
<InfoBox />
<CTASection
image={{ src: ACP, alt: "Screenshot of the ZenML Cloud Model Control plane" }}
image={{ src: ACP, alt: "Screenshot of the ZenML Pro Model Control plane" }}
features={artifactFeatures}
/>
<CommandSection />
Expand Down
4 changes: 2 additions & 2 deletions src/app/models/Fragments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ export function InfoBox() {
return (
<InfoBoxPrimitive>
<div className="flex w-full flex-wrap items-center gap-x-2 gap-y-0.5 text-text-md">
<p className="font-semibold">This is a ZenML Cloud feature. </p>
<p className="font-semibold">This is a ZenML Pro feature. </p>
<p>
Upgrade to ZenML Cloud to access the Model Control Plane and interact with your models in
Upgrade to ZenML Pro to access the Model Control Plane and interact with your models in
the Dashboard.
</p>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/app/models/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ export default function ModelsPage() {
<PageHeader className="flex items-center gap-1">
<h1 className="text-display-xs font-semibold">Models</h1>
<Badge color="purple" rounded size="sm">
<span className="font-semibold text-primary-500">Cloud</span>
<span className="font-semibold text-primary-500">Pro</span>
</Badge>
</PageHeader>
<div className="layout-container space-y-5 py-5">
<InfoBox />
<CTASection
image={{ src: MCP, alt: "Screenshot of the ZenML Cloud Model Control plane" }}
image={{ src: MCP, alt: "Screenshot of the ZenML Pro Model Control plane" }}
features={modelFeatures}
/>
<CommandSection />
Expand Down
79 changes: 79 additions & 0 deletions src/app/stacks/ActionsDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import HorizontalDots from "@/assets/icons/dots-horizontal.svg?react";
import Edit from "@/assets/icons/edit.svg?react";
import Trash from "@/assets/icons/trash.svg?react";
import { DialogItem } from "@/components/dialog/DialogItem";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger
} from "@zenml-io/react-component-library";
import { useRef, useState } from "react";
import { DeleteStackDialog, UpdateStackDialog } from "./DialogItems";

type Props = { name: string };
export function StackActionsMenu({ name }: Props) {
const [hasOpenDialog, setHasOpenDialog] = useState(false);
const [dropdownOpen, setDropdownOpen] = useState(false);

const dropdownTriggerRef = useRef(null);
const focusRef = useRef<HTMLElement | null>(null);

function handleDialogItemSelect() {
focusRef.current = dropdownTriggerRef.current;
}

function handleDialogItemOpenChange(open: boolean) {
setHasOpenDialog(open);
if (open === false) {
setDropdownOpen(false);
}
}

return (
<DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}>
<DropdownMenu>
<DropdownMenuTrigger className="z-10" ref={dropdownTriggerRef}>
<HorizontalDots className="h-5 w-5 fill-theme-text-secondary" />
</DropdownMenuTrigger>
<DropdownMenuContent
hidden={hasOpenDialog}
onCloseAutoFocus={(event) => {
if (focusRef.current) {
focusRef.current.focus();
focusRef.current = null;
event.preventDefault();
}
}}
className="z-10"
align="end"
sideOffset={1}
>
<DialogItem
onSelect={handleDialogItemSelect}
onOpenChange={handleDialogItemOpenChange}
icon={<Edit className="h-3 w-3 !fill-neutral-400" />}
triggerChildren="Update"
>
<UpdateStackDialog
name={name}
className="lg:min-w-[600px]"
closeModal={() => handleDialogItemOpenChange(false)}
/>
</DialogItem>
<DialogItem
onSelect={handleDialogItemSelect}
onOpenChange={handleDialogItemOpenChange}
icon={<Trash className="h-3 w-3 !fill-neutral-400" />}
triggerChildren="Delete"
>
<DeleteStackDialog
name={name}
className="lg:min-w-[600px]"
closeModal={() => handleDialogItemOpenChange(false)}
/>
</DialogItem>
</DropdownMenuContent>
</DropdownMenu>
</DropdownMenu>
);
}
88 changes: 88 additions & 0 deletions src/app/stacks/DialogItems.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { DialogContent, DialogHeader, DialogTitle } from "@zenml-io/react-component-library";
import { ComponentPropsWithoutRef, ElementRef, forwardRef } from "react";
import { InfoBox as InfoboxPrimitive } from "@/components/Infobox";
import { Codesnippet } from "@/components/CodeSnippet";

type Props = {
closeModal?: () => void;
name: string;
};

export const UpdateStackDialog = forwardRef<
ElementRef<typeof DialogContent>,
ComponentPropsWithoutRef<typeof DialogContent> & Props
>(({ closeModal, name, ...rest }, ref) => {
return (
<DialogContent {...rest} ref={ref}>
<DialogHeader>
<DialogTitle>Update Stack</DialogTitle>
</DialogHeader>
<div className="space-y-5 p-7">
<Infobox action="update" />
<div className="space-y-1">
<p className="text-text-sm text-theme-text-secondary">Update a stack</p>
<Codesnippet
codeClasses="whitespace-pre-wrap"
wrap
code={`zenml stack update ${name} -o NEW_ORCHESTRATOR_NAME`}
/>
</div>
</div>
</DialogContent>
);
});

UpdateStackDialog.displayName = "UpdateStackDialog";

export const DeleteStackDialog = forwardRef<
ElementRef<typeof DialogContent>,
ComponentPropsWithoutRef<typeof DialogContent> & Props
>(({ closeModal, name, ...rest }, ref) => {
return (
<DialogContent {...rest} ref={ref}>
<DialogHeader>
<DialogTitle>Delete Stack</DialogTitle>
</DialogHeader>
<div className="space-y-5 p-7">
<Infobox action="delete" />
<div className="space-y-1">
<p className="text-text-sm text-theme-text-secondary">Delete a stack</p>
<Codesnippet codeClasses="whitespace-pre-wrap" wrap code={`zenml stack delete ${name}`} />
</div>
</div>
</DialogContent>
);
});

DeleteStackDialog.displayName = "DeleteStackDialog";

type InfoProps = {
action: "delete" | "update" | "describe";
};
export function Infobox({ action }: InfoProps) {
function getAction() {
switch (action) {
case "delete":
return "delete";
case "update":
return "update";
case "describe":
return "get details of";
}
}

return (
<InfoboxPrimitive>
<div className="flex w-full flex-wrap justify-between gap-2">
<div className="min-w-0">
<p className="truncate text-text-sm font-semibold">
We are working on the new Stacks experience.
</p>
<p className="truncate text-text-sm">
Meanwhile you can use the CLI to {getAction()} your stack.
</p>
</div>
</div>
</InfoboxPrimitive>
);
}
14 changes: 13 additions & 1 deletion src/app/stacks/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { Stack } from "@/types/stack";
import { User } from "@/types/user";
import { ColumnDef } from "@tanstack/react-table";
import { Avatar, AvatarFallback } from "@zenml-io/react-component-library";
import { StackActionsMenu } from "./ActionsDropdown";
import { StackSheet } from "@/components/stacks/Sheet";

export function getStackColumns(): ColumnDef<Stack>[] {
return [
Expand All @@ -21,7 +23,9 @@ export function getStackColumns(): ColumnDef<Stack>[] {
</Avatar>
<div>
<div className="flex items-center gap-1">
<h2 className="text-text-md font-semibold">{name}</h2>
<StackSheet stackId={id}>
<h2 className="text-text-md font-semibold">{name}</h2>
</StackSheet>
</div>
<div className="flex items-center gap-1">
<p className="text-text-xs text-theme-text-secondary">{id.split("-")[0]}</p>
Expand Down Expand Up @@ -51,6 +55,14 @@ export function getStackColumns(): ColumnDef<Stack>[] {
if (!author) return null;
return <InlineAvatar username={author.name} />;
}
},
{
id: "actions",
header: "",
accessorKey: "name",
cell: ({ getValue }) => {
return <StackActionsMenu name={getValue<string>()} />;
}
}
];
}
7 changes: 1 addition & 6 deletions src/app/stacks/create/new-infrastructure/Providers/AWS.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import { Tick } from "@/components/Tick";
import { Avatar, AvatarFallback, Spinner } from "@zenml-io/react-component-library";
import { ComponentListItem, ProviderComponents } from ".";
import { ComponentBadge } from "../ComponentBadge";
import { ComponentBadge } from "../../../../../components/stack-components/ComponentBadge";
import { PermissionsCard } from "./PermissionsCard";

type Props = ProviderComponents;

export const awsPrizes = {
orchestratorCosts: "$0.45",
storageCosts: "$4.90"
};

export function AWSComponents({
stackName,
isLoading,
Expand Down
78 changes: 69 additions & 9 deletions src/app/stacks/create/new-infrastructure/Providers/GCP.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import ConfigIcon from "@/assets/icons/logs.svg?react";
import { Codesnippet } from "@/components/CodeSnippet";
import { InfoBox } from "@/components/Infobox";
import { Tick } from "@/components/Tick";
import { Avatar, AvatarFallback, Spinner } from "@zenml-io/react-component-library";
import { stackQueries } from "@/data/stacks";
import { useQuery } from "@tanstack/react-query";
import { Avatar, AvatarFallback, Skeleton, Spinner } from "@zenml-io/react-component-library";
import { ComponentListItem, ProviderComponents } from ".";
import { ComponentBadge } from "../ComponentBadge";
import { ComponentBadge } from "../../../../../components/stack-components/ComponentBadge";
import { useNewInfraFormContext } from "../NewInfraFormContext";
import { PermissionsCard } from "./PermissionsCard";

type Props = ProviderComponents;

export const gcpPrizes = {
orchestratorCosts: "$0.27",
storageCosts: "$4.60"
};

export function GcpComponents({
stackName,
isLoading,
Expand All @@ -36,8 +37,8 @@ export function GcpComponents({
subtitle={components?.connector?.id || "Manage access to GCP resources"}
badge={<ComponentBadge type="annotator">Service Connector</ComponentBadge>}
img={{
src: "https://public-flavor-logos.s3.eu-central-1.amazonaws.com/service_connector/iam.webp",
alt: "Service connector logo"
src: "https://public-flavor-logos.s3.eu-central-1.amazonaws.com/service_connector/gcp-iam.webp",
alt: "Service Account logo"
}}
/>
{displayPermissions && <PermissionsCard />}
Expand Down Expand Up @@ -81,6 +82,65 @@ export function GcpComponents({
}}
/>
</div>
<div className="py-3 pl-9 pr-5">
<ComponentListItem
title={components?.imageBuilder?.name || "Cloud Build"}
isLoading={isLoading}
isSuccess={isSuccess}
subtitle={components?.imageBuilder?.id || "Build, test, and deploy images"}
badge={<ComponentBadge type="image_builder">Image Builder</ComponentBadge>}
img={{
src: "https://public-flavor-logos.s3.eu-central-1.amazonaws.com/image_builder/gcp.png",
alt: "Cloud Build logo"
}}
/>
</div>
</div>
);
}

export function GCPWarning() {
return (
<InfoBox className="border-warning-300 bg-warning-50" intent="warning">
The Cloud Shell session will warn you that the ZenML GitHub repository is untrusted. We
recommend that you review the contents of the repository and then check the Trust repo
checkbox to proceed with the deployment, otherwise the Cloud Shell session will not be
authenticated to access your GCP projects.
</InfoBox>
);
}

export function GCPCodesnippet() {
const { data } = useNewInfraFormContext();
const deploymentConfig = useQuery({
...stackQueries.stackDeploymentConfig({
provider: "gcp",
stack_name: data.stackName!,
location: data.location
})
});
if (deploymentConfig.isError) return null;
if (deploymentConfig.isPending) return <Skeleton className="h-[200px] w-full" />;

return (
<section className="space-y-5 border-t border-theme-border-moderate pt-5">
<div className="space-y-1">
<p className="flex items-center gap-1 text-text-lg font-semibold">
<ConfigIcon className="h-5 w-5 fill-primary-400" />
Configuration
</p>
<p className="text-theme-text-secondary">
You will be asked to provide the following configuration values during the deployment
process.
</p>
</div>
<Codesnippet
fullWidth
highlightCode
codeClasses="whitespace-pre-wrap word-break-all"
wrap
code={deploymentConfig.data.configuration || ""}
/>
</section>
);
}
Loading

0 comments on commit 2de2aa3

Please sign in to comment.