diff --git a/src/app/onboarding/StarterSetup/index.tsx b/src/app/onboarding/StarterSetup/index.tsx
index c71c97b79..e2373b4fd 100644
--- a/src/app/onboarding/StarterSetup/index.tsx
+++ b/src/app/onboarding/StarterSetup/index.tsx
@@ -2,7 +2,7 @@ import ChevronDown from "@/assets/icons/chevron-down.svg?react";
import { Tick } from "@/components/Tick";
import { useServerInfo } from "@/data/server/info-query";
import { useOnboarding } from "@/data/server/onboarding-state";
-import { getProgress, getStarterSetupItems } from "@/lib/onboarding";
+import { getStarterSetupItems, getOnboardingProgress } from "@/lib/onboarding";
import { checkIsLocalServer } from "@/lib/server";
import { OnboardingChecklistItemName } from "@/types/onboarding";
import {
@@ -25,12 +25,14 @@ export function StarterSetupList() {
if (onboarding.isError || serverInfo.isError) return null;
const isLocalServer = checkIsLocalServer(serverInfo.data.deployment_type || "other");
- const { items: STARTER_SETUP_ITEMS } = getStarterSetupItems(isLocalServer);
- const doneItems = getProgress(
- onboarding.data,
- STARTER_SETUP_ITEMS as OnboardingChecklistItemName[]
- );
- const progress = (doneItems / STARTER_SETUP_ITEMS.length) * 100;
+ const STARTER_SETUP_ITEMS = getStarterSetupItems(isLocalServer);
+
+ const { doneItems, progress } = getOnboardingProgress({
+ itemList: STARTER_SETUP_ITEMS,
+ onboardingState: onboarding.data,
+ finalStep: "starter_setup_completed"
+ });
+
const order: OnboardingChecklistItemName[] = [
...(STARTER_SETUP_ITEMS as OnboardingChecklistItemName[]),
"starter_setup_completed"
diff --git a/src/components/onboarding/ChecklistItem.tsx b/src/components/onboarding/ChecklistItem.tsx
index e7178c235..873c94fd2 100644
--- a/src/components/onboarding/ChecklistItem.tsx
+++ b/src/components/onboarding/ChecklistItem.tsx
@@ -8,21 +8,34 @@ import {
} from "@zenml-io/react-component-library";
import { PropsWithChildren, ReactNode, useState } from "react";
import { Tick } from "../Tick";
+import { SkippedStep } from "./SkippedStep";
type Props = {
completed: boolean;
active?: boolean;
title: ReactNode;
itemName: OnboardingChecklistItemName;
+ hasDownstream?: boolean;
};
-export function ChecklistItem({ completed, title, children }: PropsWithChildren) {
+export function ChecklistItem({
+ completed,
+ title,
+ children,
+ hasDownstream
+}: PropsWithChildren) {
const [open, setOpen] = useState(false);
return (
- {completed ?
:
}
+ {completed ? (
+
+ ) : hasDownstream ? (
+
+ ) : (
+
+ )}
diff --git a/src/components/onboarding/SkippedStep.tsx b/src/components/onboarding/SkippedStep.tsx
new file mode 100644
index 000000000..a60a5c0d5
--- /dev/null
+++ b/src/components/onboarding/SkippedStep.tsx
@@ -0,0 +1,17 @@
+import { cn } from "@zenml-io/react-component-library";
+import { HTMLAttributes } from "react";
+import Forward from "@/assets/icons/chevron-right-double.svg?react";
+
+export function SkippedStep({ className, ...rest }: HTMLAttributes
) {
+ return (
+
+
+
+ );
+}
diff --git a/src/lib/onboarding.spec.ts b/src/lib/onboarding.spec.ts
index 1376cce20..d5d035454 100644
--- a/src/lib/onboarding.spec.ts
+++ b/src/lib/onboarding.spec.ts
@@ -1,16 +1,85 @@
-import { describe, test, expect } from "vitest";
-import { getStarterSetupItems } from "./onboarding";
+import { OnboardingChecklistItemName } from "@/types/onboarding";
+import { describe, expect, test } from "vitest";
+import { checkDownstreamStep, getProgress, getStarterSetupItems } from "./onboarding";
describe("returns the correct items for the starter setup based on the deployment type", () => {
test("doesnt return connect step for local deployment", () => {
const isLocal = true;
const items = getStarterSetupItems(isLocal);
- expect(items).toEqual(["run_first_pipeline"]);
+ expect(items).toEqual(["pipeline_run"]);
});
test("includes the connect step for non-local deployments", () => {
const isLocal = false;
const items = getStarterSetupItems(isLocal);
- expect(items).toEqual(["connect_zenml", "run_first_pipeline"]);
+ expect(items).toEqual(["device_verified", "pipeline_run"]);
+ });
+});
+
+describe("returns the correct progress, depending on the current state", () => {
+ test("returns 2 if the first two steps are done", () => {
+ const onboardingState: OnboardingChecklistItemName[] = ["device_verified", "pipeline_run"];
+ const finalStep = "starter_setup_completed";
+ const items: OnboardingChecklistItemName[] = [
+ "device_verified",
+ "pipeline_run",
+ "remote_artifact_store_created"
+ ];
+ const progress = getProgress(onboardingState, items, finalStep);
+ expect(progress).toBe(2);
+ });
+
+ test("returns 0 if no steps are done", () => {
+ const onboardingState: OnboardingChecklistItemName[] = [];
+ const finalStep = "starter_setup_completed";
+ const items: OnboardingChecklistItemName[] = ["device_verified", "pipeline_run"];
+ const progress = getProgress(onboardingState, items, finalStep);
+ expect(progress).toBe(0);
+ });
+
+ test("returns 2 if only the second step is run", () => {
+ const onboardingState: OnboardingChecklistItemName[] = ["pipeline_run"];
+ const finalStep = "starter_setup_completed";
+ const items: OnboardingChecklistItemName[] = [
+ "device_verified",
+ "pipeline_run",
+ "pipeline_run_with_remote_orchestrator"
+ ];
+ const progress = getProgress(onboardingState, items, finalStep);
+ expect(progress).toBe(2);
+ });
+
+ test("returns 2 if only the finalStep is there", () => {
+ const onboardingState: OnboardingChecklistItemName[] = ["starter_setup_completed"];
+ const finalStep = "starter_setup_completed";
+ const items: OnboardingChecklistItemName[] = ["device_verified", "pipeline_run"];
+ const progress = getProgress(onboardingState, items, finalStep);
+ expect(progress).toBe(2);
+ });
+});
+
+describe("checks if the item has downstream items", () => {
+ test("returns true if a downstream step is there", () => {
+ const itemName = "device_verified";
+ const onboardingState: OnboardingChecklistItemName[] = [
+ "device_verified",
+ "pipeline_run",
+ "starter_setup_completed"
+ ];
+ const order: OnboardingChecklistItemName[] = [
+ "device_verified",
+ "pipeline_run",
+ "starter_setup_completed"
+ ];
+ const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState, order);
+ expect(hasDownStreamFinishded).toBe(true);
+ });
+
+ test("returns false if there is no downstream step", () => {
+ const itemName = "pipeline_run";
+ const onboardingState: OnboardingChecklistItemName[] = ["pipeline_run"];
+ const order: OnboardingChecklistItemName[] = ["pipeline_run", "starter_setup_completed"];
+ const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState, order);
+ expect(hasDownStreamFinishded).toBe(false);
});
});
diff --git a/src/lib/onboarding.ts b/src/lib/onboarding.ts
index a94ca4ea4..9de45ccf3 100644
--- a/src/lib/onboarding.ts
+++ b/src/lib/onboarding.ts
@@ -2,9 +2,27 @@ import { OnboardingChecklistItemName, OnboardingResponse } from "@/types/onboard
export function getProgress(
onboardingState: OnboardingResponse,
- checklistItems: OnboardingChecklistItemName[]
+ checklistItems: OnboardingChecklistItemName[],
+ finalStep: OnboardingChecklistItemName
) {
- return checklistItems.filter((item) => onboardingState.includes(item)).length;
+ // Filter out the finalStep from the checklist items
+ const filteredItems = checklistItems.filter((item) => item !== finalStep);
+
+ // If the final step is present in the onboarding state, return the length of the filtered items
+ if (onboardingState.includes(finalStep)) {
+ return filteredItems.length;
+ }
+
+ // Find the highest index of any present item in the onboarding state
+ let highestIndex = -1;
+ filteredItems.forEach((item, index) => {
+ if (onboardingState.includes(item)) {
+ highestIndex = index;
+ }
+ });
+
+ // If any item is found, return the highest index + 1 as the completed count
+ return highestIndex + 1;
}
export function hasOnboardingItem(
@@ -14,23 +32,20 @@ export function hasOnboardingItem(
return state.includes(item);
}
-export function getStarterSetupItems(isLocal: boolean) {
- return {
- items: [...(isLocal ? [] : ["device_verified" as OnboardingChecklistItemName]), "pipeline_run"],
- finishedItem: "starter_setup_completed" as OnboardingChecklistItemName
- };
+export function getStarterSetupItems(isLocal: boolean): OnboardingChecklistItemName[] {
+ return [...(isLocal ? [] : ["device_verified" as OnboardingChecklistItemName]), "pipeline_run"];
}
-export function getProductionSetupItems() {
- return {
- items: [
- "service_connector_created",
- "remote_artifact_store_created",
- "pipeline_run_with_remote_artifact_store",
- "stack_with_remote_artifact_store_created"
- ] as OnboardingChecklistItemName[],
- finishedItem: "production_setup_completed" as OnboardingChecklistItemName
- };
+/**
+ * This fun
+ */
+export function getProductionSetupItems(): OnboardingChecklistItemName[] {
+ return [
+ "service_connector_created",
+ "remote_artifact_store_created",
+ "pipeline_run_with_remote_artifact_store",
+ "stack_with_remote_artifact_store_created"
+ ];
}
/**
@@ -42,6 +57,24 @@ export function checkDownstreamStep(
order: OnboardingChecklistItemName[]
) {
const currentIndex = order.indexOf(item);
+ if (currentIndex === -1) {
+ return false; // If the item is not found in the order array, return false
+ }
const downstreamSteps = order.slice(currentIndex + 1);
return downstreamSteps.some((step) => state.includes(step));
}
+
+type Args = {
+ onboardingState: OnboardingResponse;
+ itemList: OnboardingChecklistItemName[];
+ finalStep: OnboardingChecklistItemName;
+};
+export function getOnboardingProgress({ onboardingState, itemList, finalStep }: Args) {
+ const doneItems = getProgress(onboardingState, itemList, finalStep);
+ const progress = (doneItems / itemList.length) * 100;
+
+ return {
+ doneItems,
+ progress
+ };
+}
From 1a4992a11f08683a50e2e51bd2e9449a92fff3c0 Mon Sep 17 00:00:00 2001
From: Cahllagerfeld <43843195+Cahllagerfeld@users.noreply.github.com>
Date: Fri, 21 Jun 2024 08:49:29 +0000
Subject: [PATCH 04/15] feat: bring in sidebar item again
---
.../AuthenticatedLayout/OnboardingItem.tsx | 129 +++++++++---------
src/layouts/AuthenticatedLayout/Sidebar.tsx | 2 +
2 files changed, 68 insertions(+), 63 deletions(-)
diff --git a/src/layouts/AuthenticatedLayout/OnboardingItem.tsx b/src/layouts/AuthenticatedLayout/OnboardingItem.tsx
index 46ce0b9bc..6d2a5792d 100644
--- a/src/layouts/AuthenticatedLayout/OnboardingItem.tsx
+++ b/src/layouts/AuthenticatedLayout/OnboardingItem.tsx
@@ -1,71 +1,74 @@
-// import ChevronRight from "@/assets/icons/chevron-right.svg?react";
-// import { useServerSettings } from "@/data/server/get-server-settings";
-// import { useServerInfo } from "@/data/server/info-query";
-// import { PRODUCTION_SETUP_ITEMS } from "@/lib/constants";
-// import { getOnboardingState, getProgress, getStarterSetupItems } from "@/lib/onboarding";
-// import { checkIsLocalServer } from "@/lib/server";
-// import { routes } from "@/router/routes";
-// import { Box, ProgressBar, Skeleton, useSidebarContext } from "@zenml-io/react-component-library";
-// import { Link } from "react-router-dom";
+import ChevronRight from "@/assets/icons/chevron-right.svg?react";
+import { useServerInfo } from "@/data/server/info-query";
+import { useOnboarding } from "@/data/server/onboarding-state";
+import { getProductionSetupItems, getProgress, getStarterSetupItems } from "@/lib/onboarding";
+import { checkIsLocalServer } from "@/lib/server";
+import { routes } from "@/router/routes";
+import { Box, ProgressBar, Skeleton, useSidebarContext } from "@zenml-io/react-component-library";
+import { Link } from "react-router-dom";
-// export function OnboardingItem() {
-// const { isPending, isError, data } = useServerSettings({ throwOnError: true });
-// const serverInfo = useServerInfo();
-// const { isOpen } = useSidebarContext();
-// if (isError || serverInfo.isError) return null;
-// if (isPending || serverInfo.isPending) {
-// return (
-//
-//
-//
-// );
-// }
+export function OnboardingItem() {
+ const onboarding = useOnboarding({ refetchInterval: 3600 * 1000 });
+ const serverInfo = useServerInfo();
+ const { isOpen } = useSidebarContext();
+ if (onboarding.isError || serverInfo.isError) return null;
+ if (onboarding.isPending || serverInfo.isPending) {
+ return (
+
+
+
+ );
+ }
-// const STARTER_SETUP_ITEMS = getStarterSetupItems(
-// checkIsLocalServer(serverInfo.data.deployment_type || "other")
-// );
+ const STARTER_SETUP_ITEMS = getStarterSetupItems(
+ checkIsLocalServer(serverInfo.data.deployment_type || "other")
+ );
-// const onboardingState = getOnboardingState(data || {});
-// const isStarterSetupFinished =
-// getProgress(onboardingState, STARTER_SETUP_ITEMS) === STARTER_SETUP_ITEMS.length;
+ const PRODUCTION_SETUP_ITEMS = getProductionSetupItems();
-// const isProductionSetupFinished =
-// getProgress(onboardingState, PRODUCTION_SETUP_ITEMS) === PRODUCTION_SETUP_ITEMS.length;
+ const isStarterSetupFinished =
+ getProgress(onboarding.data, STARTER_SETUP_ITEMS, "starter_setup_completed") ===
+ STARTER_SETUP_ITEMS.length;
-// const doneItems = getProgress(
-// onboardingState,
-// isStarterSetupFinished ? PRODUCTION_SETUP_ITEMS : STARTER_SETUP_ITEMS
-// );
+ const isProductionSetupFinished =
+ getProgress(onboarding.data, PRODUCTION_SETUP_ITEMS, "production_setup_completed") ===
+ PRODUCTION_SETUP_ITEMS.length;
-// const activeFlow = {
-// title: isStarterSetupFinished ? "Production Setup" : "Starter Setup",
-// doneItems,
-// total: isStarterSetupFinished ? PRODUCTION_SETUP_ITEMS.length : STARTER_SETUP_ITEMS.length
-// };
+ const doneItems = getProgress(
+ onboarding.data,
+ isStarterSetupFinished ? PRODUCTION_SETUP_ITEMS : STARTER_SETUP_ITEMS,
+ isStarterSetupFinished ? "production_setup_completed" : "starter_setup_completed"
+ );
-// if (isProductionSetupFinished && isStarterSetupFinished) {
-// return null;
-// }
-// const progress = (activeFlow.doneItems / activeFlow.total) * 100;
+ const activeFlow = {
+ title: isStarterSetupFinished ? "Production Setup" : "Starter Setup",
+ doneItems,
+ total: isStarterSetupFinished ? PRODUCTION_SETUP_ITEMS.length : STARTER_SETUP_ITEMS.length
+ };
-// return (
-//
-//
-//
-//
-// {activeFlow.title}
-//
-//
-//
-//
-// {activeFlow.doneItems}/{activeFlow.total}
-//
-//
-//
-//
-//
-//
-// );
-// }
+ if (isProductionSetupFinished && isStarterSetupFinished) {
+ return null;
+ }
+ const progress = (activeFlow.doneItems / activeFlow.total) * 100;
+
+ return (
+
+
+
+
+ {activeFlow.title}
+
+
+
+
+ {activeFlow.doneItems}/{activeFlow.total}
+
+
+
+
+
+
+ );
+}
diff --git a/src/layouts/AuthenticatedLayout/Sidebar.tsx b/src/layouts/AuthenticatedLayout/Sidebar.tsx
index 17c6dae18..11510d669 100644
--- a/src/layouts/AuthenticatedLayout/Sidebar.tsx
+++ b/src/layouts/AuthenticatedLayout/Sidebar.tsx
@@ -22,6 +22,7 @@ import { ReactNode } from "react";
import { Link, LinkProps, matchPath, useLocation } from "react-router-dom";
import { SidebarImage, SidebarTitle } from "./SidebarFragments";
import { WhatsNewButton } from "./WhatsNewButton";
+import { OnboardingItem } from "./OnboardingItem";
export function Sidebar() {
const { setIsOpen, isOpen } = useSidebarContext();
@@ -47,6 +48,7 @@ export function Sidebar() {
+
Date: Fri, 21 Jun 2024 09:03:12 +0000
Subject: [PATCH 05/15] style: small adjustment
---
src/components/onboarding/ChecklistItem.tsx | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/src/components/onboarding/ChecklistItem.tsx b/src/components/onboarding/ChecklistItem.tsx
index 873c94fd2..5a724b27d 100644
--- a/src/components/onboarding/ChecklistItem.tsx
+++ b/src/components/onboarding/ChecklistItem.tsx
@@ -4,7 +4,8 @@ import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
- ProgressOutstanding
+ ProgressOutstanding,
+ cn
} from "@zenml-io/react-component-library";
import { PropsWithChildren, ReactNode, useState } from "react";
import { Tick } from "../Tick";
@@ -15,7 +16,7 @@ type Props = {
active?: boolean;
title: ReactNode;
itemName: OnboardingChecklistItemName;
- hasDownstream?: boolean;
+ hasDownstream: boolean;
};
export function ChecklistItem({
completed,
@@ -37,7 +38,7 @@ export function ChecklistItem({
)}
-
+
@@ -65,15 +66,17 @@ export function ChecklistItem({
type HeaderProps = {
completed: boolean;
title: ReactNode;
+ skipped: boolean;
};
-export function ChecklistHeader({ completed, title }: HeaderProps) {
+export function ChecklistHeader({ completed, title, skipped }: HeaderProps) {
return (
{title}
From a5e236856b743c307b353682199d1b977b0396cd Mon Sep 17 00:00:00 2001
From: Cahllagerfeld <43843195+Cahllagerfeld@users.noreply.github.com>
Date: Fri, 21 Jun 2024 09:43:50 +0000
Subject: [PATCH 06/15] chore: coderabbit suggestions
---
src/app/onboarding/StarterSetup/Items.tsx | 2 +-
src/data/server/activate-server-mutation.ts | 2 +-
src/data/server/get-server-settings.ts | 2 +-
src/data/server/onboarding-state.ts | 2 +-
src/data/server/update-server-settings-mutation.ts | 2 +-
src/data/users/activate-user-mutation.ts | 2 +-
src/data/users/update-current-user-mutation.ts | 2 +-
7 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/app/onboarding/StarterSetup/Items.tsx b/src/app/onboarding/StarterSetup/Items.tsx
index f11d84374..a433cadd1 100644
--- a/src/app/onboarding/StarterSetup/Items.tsx
+++ b/src/app/onboarding/StarterSetup/Items.tsx
@@ -8,7 +8,7 @@ import { OnboardingChecklistItemName, OnboardingResponse } from "@/types/onboard
import { Box, Skeleton, buttonVariants } from "@zenml-io/react-component-library";
type Props = {
- onboardingState?: OnboardingResponse;
+ onboardingState: OnboardingResponse;
order: OnboardingChecklistItemName[];
};
export function ConnectZenMLStep({ onboardingState, order }: Props) {
diff --git a/src/data/server/activate-server-mutation.ts b/src/data/server/activate-server-mutation.ts
index dd08ff008..02959d13d 100644
--- a/src/data/server/activate-server-mutation.ts
+++ b/src/data/server/activate-server-mutation.ts
@@ -20,7 +20,7 @@ export async function updateServerSettings(body: ServerActivationPayload) {
const errorData: string = await res
.json()
.then((data) => {
- if (data.detail instanceof Array) {
+ if (Array.isArray(data.detail)) {
return data.detail[1];
}
return data.detail;
diff --git a/src/data/server/get-server-settings.ts b/src/data/server/get-server-settings.ts
index ce41bbe99..8693543cc 100644
--- a/src/data/server/get-server-settings.ts
+++ b/src/data/server/get-server-settings.ts
@@ -21,7 +21,7 @@ export async function fetchServerSettings(): Promise
{
const errorData: string = await res
.json()
.then((data) => {
- if (data.detail instanceof Array) {
+ if (Array.isArray(data.detail)) {
return data.detail[1];
}
return data.detail;
diff --git a/src/data/server/onboarding-state.ts b/src/data/server/onboarding-state.ts
index 5eb70783f..353bdcf0d 100644
--- a/src/data/server/onboarding-state.ts
+++ b/src/data/server/onboarding-state.ts
@@ -21,7 +21,7 @@ export async function fetchOnboarding() {
const errorData: string = await res
.json()
.then((data) => {
- if (data.detail instanceof Array) {
+ if (Array.isArray(data.detail)) {
return data.detail[1];
}
return data.detail;
diff --git a/src/data/server/update-server-settings-mutation.ts b/src/data/server/update-server-settings-mutation.ts
index dab0e2849..bc03d9677 100644
--- a/src/data/server/update-server-settings-mutation.ts
+++ b/src/data/server/update-server-settings-mutation.ts
@@ -19,7 +19,7 @@ export async function updateServerSettings(body: ServerSettigsUpdate) {
const errorData: string = await res
.json()
.then((data) => {
- if (data.detail instanceof Array) {
+ if (Array.isArray(data.detail)) {
return data.detail[1];
}
return data.detail;
diff --git a/src/data/users/activate-user-mutation.ts b/src/data/users/activate-user-mutation.ts
index 8a8661cd1..d69dac83f 100644
--- a/src/data/users/activate-user-mutation.ts
+++ b/src/data/users/activate-user-mutation.ts
@@ -24,7 +24,7 @@ export async function activateUser({ userId, body }: ActivateUserArgs) {
const errorData: string = await res
.json()
.then((data) => {
- if (data.detail instanceof Array) {
+ if (Array.isArray(data.detail)) {
return data.detail[1];
}
return data.detail;
diff --git a/src/data/users/update-current-user-mutation.ts b/src/data/users/update-current-user-mutation.ts
index 49162ae0d..fb910a64d 100644
--- a/src/data/users/update-current-user-mutation.ts
+++ b/src/data/users/update-current-user-mutation.ts
@@ -19,7 +19,7 @@ export async function updateUser(body: UpdateUser) {
const errorData: string = await res
.json()
.then((data) => {
- if (data.detail instanceof Array) {
+ if (Array.isArray(data.detail)) {
return data.detail[1];
}
return data.detail;
From 1ddb15b9c3d8e77fbb0bb9a1bad078644da85176 Mon Sep 17 00:00:00 2001
From: Cahllagerfeld <43843195+Cahllagerfeld@users.noreply.github.com>
Date: Wed, 26 Jun 2024 12:44:39 +0000
Subject: [PATCH 07/15] refactor: helper functions
---
src/app/onboarding/ProductionSetup/Items.tsx | 55 ++----
src/app/onboarding/ProductionSetup/index.tsx | 62 +++---
src/app/onboarding/StarterSetup/Items.tsx | 33 +---
src/app/onboarding/StarterSetup/index.tsx | 35 ++--
src/components/onboarding/ChecklistItem.tsx | 2 -
.../AuthenticatedLayout/OnboardingItem.tsx | 42 ++--
src/lib/onboarding.spec.ts | 71 ++++---
src/lib/onboarding.ts | 179 ++++++++++++------
src/types/onboarding.ts | 6 +
9 files changed, 249 insertions(+), 236 deletions(-)
diff --git a/src/app/onboarding/ProductionSetup/Items.tsx b/src/app/onboarding/ProductionSetup/Items.tsx
index adefad4c1..3d640b4a6 100644
--- a/src/app/onboarding/ProductionSetup/Items.tsx
+++ b/src/app/onboarding/ProductionSetup/Items.tsx
@@ -1,31 +1,22 @@
import { Codesnippet } from "@/components/CodeSnippet";
import { HelpBox } from "@/components/fallback-pages/Helpbox";
import { ChecklistItem } from "@/components/onboarding/ChecklistItem";
-import { checkDownstreamStep, hasOnboardingItem } from "@/lib/onboarding";
-import { OnboardingChecklistItemName, OnboardingResponse } from "@/types/onboarding";
+import {} from "@/lib/onboarding";
+import { OnboardingStep } from "@/types/onboarding";
import { useState } from "react";
import { getArtifactStoreStep } from "./ArtifactStore";
import { getServiceConnectorStep } from "./ConnectorContent";
import { CloudProvider, ProviderSelect } from "./ProviderSelect";
-type Props = {
- onboardingState?: OnboardingResponse;
- order: OnboardingChecklistItemName[];
- active?: boolean;
-};
-
-export function CreateServiceConnector({ onboardingState, active, order }: Props) {
+export function CreateServiceConnector({ hasDownstreamStep, active, completed }: OnboardingStep) {
const [selectedProvider, setSelectedProvider] = useState("aws");
- const itemName: OnboardingChecklistItemName = "service_connector_created";
- const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState || [], order);
- const item = hasOnboardingItem(itemName, onboardingState || []);
+
return (
A service connector grants users of your ZenML tenant the ability to access components like
@@ -55,18 +46,12 @@ export function CreateServiceConnector({ onboardingState, active, order }: Props
);
}
-export function CreateArtifactStore({ onboardingState, active, order }: Props) {
+export function CreateArtifactStore({ completed, active, hasDownstreamStep }: OnboardingStep) {
const [selectedProvider, setSelectedProvider] = useState("aws");
- const itemName: OnboardingChecklistItemName = "remote_artifact_store_created";
- const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState || [], order);
-
- const item = hasOnboardingItem(itemName, onboardingState || []);
-
return (
@@ -99,16 +84,11 @@ export function CreateArtifactStore({ onboardingState, active, order }: Props) {
);
}
-export function CreateNewStack({ onboardingState, active, order }: Props) {
- const itemName: OnboardingChecklistItemName = "stack_with_remote_orchestrator_created";
- const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState || [], order);
- const item = hasOnboardingItem(itemName, onboardingState || []);
-
+export function CreateNewStack({ completed, active, hasDownstreamStep }: OnboardingStep) {
return (
@@ -132,16 +112,11 @@ export function CreateNewStack({ onboardingState, active, order }: Props) {
);
}
-export function RunNewPipeline({ active, onboardingState, order }: Props) {
- const itemName: OnboardingChecklistItemName = "pipeline_run_with_remote_artifact_store";
- const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState || [], order);
- const item = hasOnboardingItem(itemName, onboardingState || []);
-
+export function RunNewPipeline({ active, completed, hasDownstreamStep }: OnboardingStep) {
return (
diff --git a/src/app/onboarding/ProductionSetup/index.tsx b/src/app/onboarding/ProductionSetup/index.tsx
index bded9e2c4..83ad99290 100644
--- a/src/app/onboarding/ProductionSetup/index.tsx
+++ b/src/app/onboarding/ProductionSetup/index.tsx
@@ -2,14 +2,8 @@ import ChevronDown from "@/assets/icons/chevron-down.svg?react";
import { Tick } from "@/components/Tick";
import { useServerInfo } from "@/data/server/info-query";
import { useOnboarding } from "@/data/server/onboarding-state";
-import {
- getProductionSetupItems,
- getProgress,
- getStarterSetupItems,
- getOnboardingProgress
-} from "@/lib/onboarding";
+import { getProductionSetup, getStarterSetup } from "@/lib/onboarding";
import { checkIsLocalServer } from "@/lib/server";
-import { OnboardingChecklistItemName } from "@/types/onboarding";
import {
Collapsible,
CollapsibleContent,
@@ -35,29 +29,17 @@ export function ProductionSetupChecklist() {
return ;
if (onboarding.isError || serverInfo.isError) return null;
- const STARTER_SETUP_ITEMS = getStarterSetupItems(
+ const starterSetup = getStarterSetup(
+ onboarding.data,
checkIsLocalServer(serverInfo.data.deployment_type || "other")
);
- const PRODUCTION_SETUP_ITEMS = getProductionSetupItems();
-
- const isStarterSetupFinished =
- getProgress(
- onboarding.data,
- STARTER_SETUP_ITEMS as OnboardingChecklistItemName[],
- "starter_setup_completed"
- ) === STARTER_SETUP_ITEMS.length;
-
- const { progress, doneItems } = getOnboardingProgress({
- itemList: PRODUCTION_SETUP_ITEMS,
- onboardingState: onboarding.data,
- finalStep: "production_setup_completed"
- });
+ const { progress, totalItems, itemsDone, getItem } = getProductionSetup(onboarding.data);
- const order: OnboardingChecklistItemName[] = [
- ...(PRODUCTION_SETUP_ITEMS as OnboardingChecklistItemName[]),
- "production_setup_completed"
- ];
+ const connectorStep = getItem("service_connector_created");
+ const storeStep = getItem("remote_artifact_store_created");
+ const stackStep = getItem("stack_with_remote_artifact_store_created");
+ const pipelineStep = getItem("pipeline_run_with_remote_artifact_store");
return (
<>
@@ -73,7 +55,7 @@ export function ProductionSetupChecklist() {
) : (
- {doneItems}/{PRODUCTION_SETUP_ITEMS.length}
+ {itemsDone}/{totalItems}
)}
@@ -84,7 +66,7 @@ export function ProductionSetupChecklist() {
(10 min)
- {isStarterSetupFinished ? (
+ {starterSetup.isFinished ? (
"Level up your skills in a production setting."
) : (
@@ -104,30 +86,30 @@ export function ProductionSetupChecklist() {
diff --git a/src/app/onboarding/StarterSetup/Items.tsx b/src/app/onboarding/StarterSetup/Items.tsx
index a433cadd1..4066b3b38 100644
--- a/src/app/onboarding/StarterSetup/Items.tsx
+++ b/src/app/onboarding/StarterSetup/Items.tsx
@@ -3,25 +3,17 @@ import { Codesnippet } from "@/components/CodeSnippet";
import { HelpBox } from "@/components/fallback-pages/Helpbox";
import { ChecklistItem } from "@/components/onboarding/ChecklistItem";
import { useServerInfo } from "@/data/server/info-query";
-import { checkDownstreamStep, hasOnboardingItem } from "@/lib/onboarding";
-import { OnboardingChecklistItemName, OnboardingResponse } from "@/types/onboarding";
+import { OnboardingStep } from "@/types/onboarding";
import { Box, Skeleton, buttonVariants } from "@zenml-io/react-component-library";
-type Props = {
- onboardingState: OnboardingResponse;
- order: OnboardingChecklistItemName[];
-};
-export function ConnectZenMLStep({ onboardingState, order }: Props) {
+export function ConnectZenMLStep({ completed, hasDownstreamStep, active }: OnboardingStep) {
const { data } = useServerInfo({ throwOnError: true });
- const itemName: OnboardingChecklistItemName = "device_verified";
- const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState || [], order);
- const item = hasOnboardingItem(itemName, onboardingState || []);
return (
@@ -32,9 +24,7 @@ export function ConnectZenMLStep({ onboardingState, order }: Props) {
/>
-
- Login to your ZenML Cloud tenant
-
+
Login to your ZenML Server
@@ -43,15 +33,12 @@ export function ConnectZenMLStep({ onboardingState, order }: Props) {
);
}
-export function RunFirstPipeline({ onboardingState, order }: Props) {
- const itemName: OnboardingChecklistItemName = "pipeline_run";
- const item = hasOnboardingItem(itemName, onboardingState || []);
- const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState || [], order);
+export function RunFirstPipeline({ active, completed, hasDownstreamStep }: OnboardingStep) {
return (
diff --git a/src/app/onboarding/StarterSetup/index.tsx b/src/app/onboarding/StarterSetup/index.tsx
index e2373b4fd..94a143a4d 100644
--- a/src/app/onboarding/StarterSetup/index.tsx
+++ b/src/app/onboarding/StarterSetup/index.tsx
@@ -2,9 +2,8 @@ import ChevronDown from "@/assets/icons/chevron-down.svg?react";
import { Tick } from "@/components/Tick";
import { useServerInfo } from "@/data/server/info-query";
import { useOnboarding } from "@/data/server/onboarding-state";
-import { getStarterSetupItems, getOnboardingProgress } from "@/lib/onboarding";
+import { getStarterSetup } from "@/lib/onboarding";
import { checkIsLocalServer } from "@/lib/server";
-import { OnboardingChecklistItemName } from "@/types/onboarding";
import {
Collapsible,
CollapsibleContent,
@@ -25,18 +24,12 @@ export function StarterSetupList() {
if (onboarding.isError || serverInfo.isError) return null;
const isLocalServer = checkIsLocalServer(serverInfo.data.deployment_type || "other");
- const STARTER_SETUP_ITEMS = getStarterSetupItems(isLocalServer);
-
- const { doneItems, progress } = getOnboardingProgress({
- itemList: STARTER_SETUP_ITEMS,
- onboardingState: onboarding.data,
- finalStep: "starter_setup_completed"
- });
-
- const order: OnboardingChecklistItemName[] = [
- ...(STARTER_SETUP_ITEMS as OnboardingChecklistItemName[]),
- "starter_setup_completed"
- ];
+ const { progress, itemsDone, totalItems, getItem } = getStarterSetup(
+ onboarding.data,
+ isLocalServer
+ );
+ const connectStep = getItem("device_verified");
+ const pipelineStep = getItem("pipeline_run");
return (
- {doneItems}/{STARTER_SETUP_ITEMS.length}
+ {itemsDone}/{totalItems}
)}
@@ -77,11 +70,19 @@ export function StarterSetupList() {
{!isLocalServer && (
-
+
)}
-
+
diff --git a/src/components/onboarding/ChecklistItem.tsx b/src/components/onboarding/ChecklistItem.tsx
index 5a724b27d..dbd98aaa8 100644
--- a/src/components/onboarding/ChecklistItem.tsx
+++ b/src/components/onboarding/ChecklistItem.tsx
@@ -1,5 +1,4 @@
import ChevronDown from "@/assets/icons/chevron-down.svg?react";
-import { OnboardingChecklistItemName } from "@/types/onboarding";
import {
Collapsible,
CollapsibleContent,
@@ -15,7 +14,6 @@ type Props = {
completed: boolean;
active?: boolean;
title: ReactNode;
- itemName: OnboardingChecklistItemName;
hasDownstream: boolean;
};
export function ChecklistItem({
diff --git a/src/layouts/AuthenticatedLayout/OnboardingItem.tsx b/src/layouts/AuthenticatedLayout/OnboardingItem.tsx
index 6d2a5792d..d1f4872e6 100644
--- a/src/layouts/AuthenticatedLayout/OnboardingItem.tsx
+++ b/src/layouts/AuthenticatedLayout/OnboardingItem.tsx
@@ -1,7 +1,7 @@
import ChevronRight from "@/assets/icons/chevron-right.svg?react";
import { useServerInfo } from "@/data/server/info-query";
import { useOnboarding } from "@/data/server/onboarding-state";
-import { getProductionSetupItems, getProgress, getStarterSetupItems } from "@/lib/onboarding";
+import { getProductionSetup, getStarterSetup } from "@/lib/onboarding";
import { checkIsLocalServer } from "@/lib/server";
import { routes } from "@/router/routes";
import { Box, ProgressBar, Skeleton, useSidebarContext } from "@zenml-io/react-component-library";
@@ -20,36 +20,18 @@ export function OnboardingItem() {
);
}
- const STARTER_SETUP_ITEMS = getStarterSetupItems(
- checkIsLocalServer(serverInfo.data.deployment_type || "other")
- );
-
- const PRODUCTION_SETUP_ITEMS = getProductionSetupItems();
-
- const isStarterSetupFinished =
- getProgress(onboarding.data, STARTER_SETUP_ITEMS, "starter_setup_completed") ===
- STARTER_SETUP_ITEMS.length;
-
- const isProductionSetupFinished =
- getProgress(onboarding.data, PRODUCTION_SETUP_ITEMS, "production_setup_completed") ===
- PRODUCTION_SETUP_ITEMS.length;
-
- const doneItems = getProgress(
+ const starterSetup = getStarterSetup(
onboarding.data,
- isStarterSetupFinished ? PRODUCTION_SETUP_ITEMS : STARTER_SETUP_ITEMS,
- isStarterSetupFinished ? "production_setup_completed" : "starter_setup_completed"
+ checkIsLocalServer(serverInfo.data.deployment_type || "other")
);
+ const productionSetup = getProductionSetup(onboarding.data);
- const activeFlow = {
- title: isStarterSetupFinished ? "Production Setup" : "Starter Setup",
- doneItems,
- total: isStarterSetupFinished ? PRODUCTION_SETUP_ITEMS.length : STARTER_SETUP_ITEMS.length
- };
-
- if (isProductionSetupFinished && isStarterSetupFinished) {
- return null;
- }
- const progress = (activeFlow.doneItems / activeFlow.total) * 100;
+ const title = starterSetup.isFinished ? "Production Setup" : "Starter Setup";
+ const completedItems = starterSetup.isFinished
+ ? productionSetup.itemsDone
+ : starterSetup.itemsDone;
+ const totalItems = starterSetup.isFinished ? productionSetup.totalItems : starterSetup.totalItems;
+ const progress = starterSetup.isFinished ? productionSetup.progress : starterSetup.progress;
return (
@@ -58,12 +40,12 @@ export function OnboardingItem() {
- {activeFlow.title}
+ {title}
- {activeFlow.doneItems}/{activeFlow.total}
+ {completedItems}/{totalItems}
diff --git a/src/lib/onboarding.spec.ts b/src/lib/onboarding.spec.ts
index d5d035454..055205edf 100644
--- a/src/lib/onboarding.spec.ts
+++ b/src/lib/onboarding.spec.ts
@@ -1,6 +1,11 @@
-import { OnboardingChecklistItemName } from "@/types/onboarding";
+import { OnboardingChecklistItemName, OnboardingResponse } from "@/types/onboarding";
import { describe, expect, test } from "vitest";
-import { checkDownstreamStep, getProgress, getStarterSetupItems } from "./onboarding";
+import {
+ checkDownstreamStep,
+ getProgress,
+ getStarterSetupItems,
+ getOnboardingLength
+} from "./onboarding";
describe("returns the correct items for the starter setup based on the deployment type", () => {
test("doesnt return connect step for local deployment", () => {
@@ -18,44 +23,35 @@ describe("returns the correct items for the starter setup based on the deploymen
describe("returns the correct progress, depending on the current state", () => {
test("returns 2 if the first two steps are done", () => {
- const onboardingState: OnboardingChecklistItemName[] = ["device_verified", "pipeline_run"];
- const finalStep = "starter_setup_completed";
- const items: OnboardingChecklistItemName[] = [
- "device_verified",
- "pipeline_run",
- "remote_artifact_store_created"
- ];
- const progress = getProgress(onboardingState, items, finalStep);
+ const state: OnboardingResponse = ["device_verified", "pipeline_run"];
+ const flow = "starter";
+ const progress = getProgress(state, flow, false);
expect(progress).toBe(2);
});
test("returns 0 if no steps are done", () => {
const onboardingState: OnboardingChecklistItemName[] = [];
- const finalStep = "starter_setup_completed";
- const items: OnboardingChecklistItemName[] = ["device_verified", "pipeline_run"];
- const progress = getProgress(onboardingState, items, finalStep);
+ const progress = getProgress(onboardingState, "starter", false);
expect(progress).toBe(0);
});
test("returns 2 if only the second step is run", () => {
const onboardingState: OnboardingChecklistItemName[] = ["pipeline_run"];
- const finalStep = "starter_setup_completed";
- const items: OnboardingChecklistItemName[] = [
- "device_verified",
- "pipeline_run",
- "pipeline_run_with_remote_orchestrator"
- ];
- const progress = getProgress(onboardingState, items, finalStep);
+ const progress = getProgress(onboardingState, "starter", false);
expect(progress).toBe(2);
});
test("returns 2 if only the finalStep is there", () => {
const onboardingState: OnboardingChecklistItemName[] = ["starter_setup_completed"];
- const finalStep = "starter_setup_completed";
- const items: OnboardingChecklistItemName[] = ["device_verified", "pipeline_run"];
- const progress = getProgress(onboardingState, items, finalStep);
+ const progress = getProgress(onboardingState, "starter", false);
expect(progress).toBe(2);
});
+
+ test("returns correct value if flow is local", () => {
+ const onboardingState: OnboardingChecklistItemName[] = ["device_verified"];
+ const progress = getProgress(onboardingState, "starter", true);
+ expect(progress).toBe(0);
+ });
});
describe("checks if the item has downstream items", () => {
@@ -66,20 +62,33 @@ describe("checks if the item has downstream items", () => {
"pipeline_run",
"starter_setup_completed"
];
- const order: OnboardingChecklistItemName[] = [
- "device_verified",
- "pipeline_run",
- "starter_setup_completed"
- ];
- const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState, order);
+ const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState, "starter", false);
expect(hasDownStreamFinishded).toBe(true);
});
test("returns false if there is no downstream step", () => {
const itemName = "pipeline_run";
const onboardingState: OnboardingChecklistItemName[] = ["pipeline_run"];
- const order: OnboardingChecklistItemName[] = ["pipeline_run", "starter_setup_completed"];
- const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState, order);
+ const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState, "starter", false);
+ expect(hasDownStreamFinishded).toBe(false);
+ });
+ test("returns correct value if starter setup is local", () => {
+ const itemName = "device_verified";
+ const onboardingState: OnboardingChecklistItemName[] = ["device_verified", "pipeline_run"];
+ const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState, "starter", true);
expect(hasDownStreamFinishded).toBe(false);
});
});
+
+describe("checks if the correct length is returned", () => {
+ test("returns correct value for local starter setup", () => {
+ const isLocal = true;
+ const items = getOnboardingLength("starter", isLocal);
+ expect(items).toBe(1);
+ });
+ test("returns correct value for non-local starter setup", () => {
+ const isLocal = false;
+ const items = getOnboardingLength("starter", isLocal);
+ expect(items).toBe(2);
+ });
+});
diff --git a/src/lib/onboarding.ts b/src/lib/onboarding.ts
index 9de45ccf3..9c848f7dd 100644
--- a/src/lib/onboarding.ts
+++ b/src/lib/onboarding.ts
@@ -1,45 +1,12 @@
import { OnboardingChecklistItemName, OnboardingResponse } from "@/types/onboarding";
-export function getProgress(
- onboardingState: OnboardingResponse,
- checklistItems: OnboardingChecklistItemName[],
- finalStep: OnboardingChecklistItemName
-) {
- // Filter out the finalStep from the checklist items
- const filteredItems = checklistItems.filter((item) => item !== finalStep);
-
- // If the final step is present in the onboarding state, return the length of the filtered items
- if (onboardingState.includes(finalStep)) {
- return filteredItems.length;
- }
-
- // Find the highest index of any present item in the onboarding state
- let highestIndex = -1;
- filteredItems.forEach((item, index) => {
- if (onboardingState.includes(item)) {
- highestIndex = index;
- }
- });
-
- // If any item is found, return the highest index + 1 as the completed count
- return highestIndex + 1;
-}
-
-export function hasOnboardingItem(
- item: OnboardingChecklistItemName,
- state: OnboardingResponse
-): boolean {
- return state.includes(item);
-}
+export type Flow = "starter" | "production";
export function getStarterSetupItems(isLocal: boolean): OnboardingChecklistItemName[] {
return [...(isLocal ? [] : ["device_verified" as OnboardingChecklistItemName]), "pipeline_run"];
}
-/**
- * This fun
- */
-export function getProductionSetupItems(): OnboardingChecklistItemName[] {
+function getProductionSetupItems(): OnboardingChecklistItemName[] {
return [
"service_connector_created",
"remote_artifact_store_created",
@@ -48,33 +15,139 @@ export function getProductionSetupItems(): OnboardingChecklistItemName[] {
];
}
-/**
- * This function checks if there is a step with a higher index than the current one that is finished
- */
+const finalSteps: {
+ starter: OnboardingChecklistItemName;
+ production: OnboardingChecklistItemName;
+} = {
+ starter: "starter_setup_completed",
+ production: "production_setup_completed"
+};
+
+export function getStarterSetup(data: OnboardingResponse, isLocal: boolean) {
+ const flowType: Flow = "starter";
+ const itemsDone = getProgress(data, flowType, isLocal);
+ const totalItems = getOnboardingLength(flowType, isLocal);
+ return {
+ itemsDone,
+ totalItems,
+ items: getStarterSetupItems(isLocal),
+ isFinished: itemsDone === totalItems,
+ progress: (itemsDone / totalItems) * 100,
+ finalStep: finalSteps.starter,
+ hasItem: (item: OnboardingChecklistItemName) => hasOnboardingItem(item, data),
+ getItem: (item: OnboardingChecklistItemName) => getItem(item, data, flowType)
+ };
+}
+
+export function getProductionSetup(data: OnboardingResponse) {
+ const flowType: Flow = "production";
+ const itemsDone = getProgress(data, flowType);
+ const totalItems = getOnboardingLength(flowType);
+ return {
+ itemsDone,
+ totalItems,
+ items: getProductionSetupItems(),
+ isFinished: itemsDone === totalItems,
+ progress: (itemsDone / totalItems) * 100,
+ finalStep: finalSteps.starter,
+ hasItem: (item: OnboardingChecklistItemName) => hasOnboardingItem(item, data),
+ getItem: (item: OnboardingChecklistItemName) => getItem(item, data, flowType)
+ };
+}
+
+function getItem(
+ item: OnboardingChecklistItemName,
+ state: OnboardingResponse,
+ flow: Flow,
+ isLocal?: boolean
+) {
+ return {
+ isCompleted: state.includes(item),
+ hasDownStreamStep: checkDownstreamStep(item, state, "starter", isLocal || false),
+ isActive: isStepActive(item, state, flow, isLocal || false)
+ };
+}
+
+function hasOnboardingItem(item: OnboardingChecklistItemName, state: OnboardingResponse): boolean {
+ return state.includes(item);
+}
+
export function checkDownstreamStep(
item: OnboardingChecklistItemName,
state: OnboardingResponse,
- order: OnboardingChecklistItemName[]
+ flow: Flow,
+ isLocal?: boolean
) {
- const currentIndex = order.indexOf(item);
+ const order =
+ flow === "starter" ? getStarterSetupItems(isLocal || false) : getProductionSetupItems();
+ const withFinalStep = [...order, finalSteps[flow]];
+ const currentIndex = withFinalStep.indexOf(item);
if (currentIndex === -1) {
return false; // If the item is not found in the order array, return false
}
- const downstreamSteps = order.slice(currentIndex + 1);
+ const downstreamSteps = withFinalStep.slice(currentIndex + 1);
return downstreamSteps.some((step) => state.includes(step));
}
-type Args = {
- onboardingState: OnboardingResponse;
- itemList: OnboardingChecklistItemName[];
- finalStep: OnboardingChecklistItemName;
-};
-export function getOnboardingProgress({ onboardingState, itemList, finalStep }: Args) {
- const doneItems = getProgress(onboardingState, itemList, finalStep);
- const progress = (doneItems / itemList.length) * 100;
+function isStepActive(
+ step: OnboardingChecklistItemName,
+ state: OnboardingResponse,
+ flowType: Flow,
+ isLocal: boolean
+): boolean {
+ const flow = flowType === "starter" ? getStarterSetupItems(isLocal) : getProductionSetupItems();
+ if (flow.length === 0) {
+ return false;
+ }
- return {
- doneItems,
- progress
- };
+ const stepIndex = flow.indexOf(step);
+
+ if (stepIndex === -1) {
+ // Step is not in the setup list
+ return false;
+ }
+
+ if (state.includes(step)) {
+ // Step is already done
+ return false;
+ }
+
+ if (stepIndex === 0) {
+ // First step is active if not done
+ return true;
+ }
+
+ // Check if the previous step is done
+ const previousStep = flow[stepIndex - 1];
+ return state.includes(previousStep);
+}
+
+export function getProgress(onboardingState: OnboardingResponse, flow: Flow, isLocal?: boolean) {
+ const items =
+ flow === "starter" ? getStarterSetupItems(isLocal || false) : getProductionSetupItems();
+ const finalStep = finalSteps[flow];
+ // Filter out the finalStep from the checklist items
+ const filteredItems = items.filter((item) => item !== finalStep);
+
+ // If the final step is present in the onboarding state, return the length of the filtered items
+ if (onboardingState.includes(finalStep)) {
+ return filteredItems.length;
+ }
+
+ // Find the highest index of any present item in the onboarding state
+ let highestIndex = -1;
+ filteredItems.forEach((item, index) => {
+ if (onboardingState.includes(item)) {
+ highestIndex = index;
+ }
+ });
+
+ // If any item is found, return the highest index + 1 as the completed count
+ return highestIndex + 1;
+}
+
+export function getOnboardingLength(flow: Flow, isLocal?: boolean) {
+ const items =
+ flow === "starter" ? getStarterSetupItems(isLocal || false) : getProductionSetupItems();
+ return items.length;
}
diff --git a/src/types/onboarding.ts b/src/types/onboarding.ts
index 8f53be39b..d833c4817 100644
--- a/src/types/onboarding.ts
+++ b/src/types/onboarding.ts
@@ -12,3 +12,9 @@ export type OnboardingChecklistItemName =
| "production_setup_completed";
export type OnboardingResponse = OnboardingChecklistItemName[];
+
+export type OnboardingStep = {
+ completed: boolean;
+ active: boolean;
+ hasDownstreamStep: boolean;
+};
From b56272aeb31c3a483856d020a6fe149c740a12c3 Mon Sep 17 00:00:00 2001
From: Cahllagerfeld <43843195+Cahllagerfeld@users.noreply.github.com>
Date: Wed, 10 Jul 2024 12:22:55 +0000
Subject: [PATCH 08/15] chore: update types
---
src/types/core.ts | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/src/types/core.ts b/src/types/core.ts
index 73cef0a2f..e9bb6dedf 100644
--- a/src/types/core.ts
+++ b/src/types/core.ts
@@ -4430,6 +4430,10 @@ export type components = {
* @default
*/
description?: string | null;
+ /** The stack labels. */
+ labels?: {
+ [key: string]: unknown;
+ } | null;
/**
* The service connectors dictionary for the full stack registration.
* @description The UUID of an already existing service connector or request information to create a service connector from scratch.
@@ -4443,10 +4447,6 @@ export type components = {
components: {
[key: string]: unknown;
};
- /** Labels to be set. */
- labels?: {
- [key: string]: unknown;
- };
};
/** HTTPValidationError */
HTTPValidationError: {
@@ -7931,6 +7931,10 @@ export type components = {
components?: {
[key: string]: unknown;
} | null;
+ /** The stack labels. */
+ labels?: {
+ [key: string]: unknown;
+ } | null;
};
/**
* StackResponse
@@ -7992,6 +7996,10 @@ export type components = {
description?: string | null;
/** The path to the stack spec used for mlstacks deployments. */
stack_spec_path?: string | null;
+ /** The stack labels. */
+ labels?: {
+ [key: string]: unknown;
+ } | null;
};
/**
* StackResponseResources
@@ -8015,6 +8023,10 @@ export type components = {
components?: {
[key: string]: unknown;
} | null;
+ /** The stack labels. */
+ labels?: {
+ [key: string]: unknown;
+ } | null;
};
/**
* Step
From b21d695edd9d003c592700d2c8fce32cb25098dd Mon Sep 17 00:00:00 2001
From: Cahllagerfeld <43843195+Cahllagerfeld@users.noreply.github.com>
Date: Wed, 10 Jul 2024 12:34:59 +0000
Subject: [PATCH 09/15] refactor: remove unnecessary steps
---
src/app/onboarding/ProductionSetup/Items.tsx | 100 +++----------------
src/app/onboarding/ProductionSetup/index.tsx | 27 +----
src/components/onboarding/ChecklistItem.tsx | 5 +-
src/lib/onboarding.ts | 7 +-
src/types/onboarding.ts | 5 -
5 files changed, 19 insertions(+), 125 deletions(-)
diff --git a/src/app/onboarding/ProductionSetup/Items.tsx b/src/app/onboarding/ProductionSetup/Items.tsx
index 3d640b4a6..e64db0b00 100644
--- a/src/app/onboarding/ProductionSetup/Items.tsx
+++ b/src/app/onboarding/ProductionSetup/Items.tsx
@@ -1,88 +1,12 @@
+import Plus from "@/assets/icons/plus.svg?react";
import { Codesnippet } from "@/components/CodeSnippet";
import { HelpBox } from "@/components/fallback-pages/Helpbox";
import { ChecklistItem } from "@/components/onboarding/ChecklistItem";
import {} from "@/lib/onboarding";
+import { routes } from "@/router/routes";
import { OnboardingStep } from "@/types/onboarding";
-import { useState } from "react";
-import { getArtifactStoreStep } from "./ArtifactStore";
-import { getServiceConnectorStep } from "./ConnectorContent";
-import { CloudProvider, ProviderSelect } from "./ProviderSelect";
-
-export function CreateServiceConnector({ hasDownstreamStep, active, completed }: OnboardingStep) {
- const [selectedProvider, setSelectedProvider] = useState("aws");
-
- return (
-
-
- A service connector grants users of your ZenML tenant the ability to access components like
- your artifact store{" "}
-
-
-
-
-
- Select your cloud provider
-
-
-
- {getServiceConnectorStep(selectedProvider)}
-
-
-
-
-
- );
-}
-
-export function CreateArtifactStore({ completed, active, hasDownstreamStep }: OnboardingStep) {
- const [selectedProvider, setSelectedProvider] = useState("aws");
- return (
-
-
- Configuring a remote artifact store will version your pipeline's data directly in your cloud
- provider{" "}
-
-
-
-
-
- Select your cloud provider
-
-
-
- {getArtifactStoreStep(selectedProvider)}
-
-
-
-
-
- );
-}
+import { Button } from "@zenml-io/react-component-library";
+import { Link } from "react-router-dom";
export function CreateNewStack({ completed, active, hasDownstreamStep }: OnboardingStep) {
return (
@@ -97,14 +21,14 @@ export function CreateNewStack({ completed, active, hasDownstreamStep }: Onboard
-
-
- Download the quickstart example to your local machine
-
-
+
+
Connect your Cloud to deploy your ZenML pipelines in a remote stack.
+
+
+
+ Register a stack
+
+
diff --git a/src/app/onboarding/ProductionSetup/index.tsx b/src/app/onboarding/ProductionSetup/index.tsx
index 83ad99290..3d8c60deb 100644
--- a/src/app/onboarding/ProductionSetup/index.tsx
+++ b/src/app/onboarding/ProductionSetup/index.tsx
@@ -13,12 +13,7 @@ import {
cn
} from "@zenml-io/react-component-library";
import { useState } from "react";
-import {
- CreateArtifactStore,
- CreateNewStack,
- CreateServiceConnector,
- RunNewPipeline
-} from "./Items";
+import { CreateNewStack, RunNewPipeline } from "./Items";
export function ProductionSetupChecklist() {
const onboarding = useOnboarding({ refetchInterval: 5000 });
@@ -36,10 +31,8 @@ export function ProductionSetupChecklist() {
const { progress, totalItems, itemsDone, getItem } = getProductionSetup(onboarding.data);
- const connectorStep = getItem("service_connector_created");
- const storeStep = getItem("remote_artifact_store_created");
- const stackStep = getItem("stack_with_remote_artifact_store_created");
- const pipelineStep = getItem("pipeline_run_with_remote_artifact_store");
+ const stackStep = getItem("stack_with_remote_orchestrator_created");
+ const pipelineStep = getItem("pipeline_run_with_remote_orchestrator");
return (
<>
@@ -84,20 +77,6 @@ export function ProductionSetupChecklist() {
-
-
-
-
-
-
) {
- const [open, setOpen] = useState(false);
+ const [open, setOpen] = useState(active);
return (
diff --git a/src/lib/onboarding.ts b/src/lib/onboarding.ts
index 9c848f7dd..8adcd3278 100644
--- a/src/lib/onboarding.ts
+++ b/src/lib/onboarding.ts
@@ -7,12 +7,7 @@ export function getStarterSetupItems(isLocal: boolean): OnboardingChecklistItemN
}
function getProductionSetupItems(): OnboardingChecklistItemName[] {
- return [
- "service_connector_created",
- "remote_artifact_store_created",
- "pipeline_run_with_remote_artifact_store",
- "stack_with_remote_artifact_store_created"
- ];
+ return ["stack_with_remote_orchestrator_created", "pipeline_run_with_remote_orchestrator"];
}
const finalSteps: {
diff --git a/src/types/onboarding.ts b/src/types/onboarding.ts
index d833c4817..e90d1c9c7 100644
--- a/src/types/onboarding.ts
+++ b/src/types/onboarding.ts
@@ -2,12 +2,7 @@ export type OnboardingChecklistItemName =
| "device_verified"
| "pipeline_run"
| "starter_setup_completed"
- | "service_connector_created"
- | "remote_artifact_store_created"
- | "remote_orchestrator_created"
- | "stack_with_remote_artifact_store_created"
| "stack_with_remote_orchestrator_created"
- | "pipeline_run_with_remote_artifact_store"
| "pipeline_run_with_remote_orchestrator"
| "production_setup_completed";
From fb97e8f97b4035fce489f8680bc36c9eabc3c1e9 Mon Sep 17 00:00:00 2001
From: Cahllagerfeld <43843195+Cahllagerfeld@users.noreply.github.com>
Date: Wed, 10 Jul 2024 13:28:47 +0000
Subject: [PATCH 10/15] chore: update wording
---
src/app/onboarding/ProductionSetup/Items.tsx | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/src/app/onboarding/ProductionSetup/Items.tsx b/src/app/onboarding/ProductionSetup/Items.tsx
index e64db0b00..6945e7350 100644
--- a/src/app/onboarding/ProductionSetup/Items.tsx
+++ b/src/app/onboarding/ProductionSetup/Items.tsx
@@ -47,11 +47,7 @@ export function RunNewPipeline({ active, completed, hasDownstreamStep }: Onboard
Run the pipeline
From fbae3a017989034635d2a30666c3ec97d9fba2e5 Mon Sep 17 00:00:00 2001
From: Cahllagerfeld <43843195+Cahllagerfeld@users.noreply.github.com>
Date: Wed, 10 Jul 2024 14:10:47 +0000
Subject: [PATCH 11/15] refactor: change check for completion
---
src/lib/onboarding.ts | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/lib/onboarding.ts b/src/lib/onboarding.ts
index 8adcd3278..3e3d7bb6d 100644
--- a/src/lib/onboarding.ts
+++ b/src/lib/onboarding.ts
@@ -20,13 +20,14 @@ const finalSteps: {
export function getStarterSetup(data: OnboardingResponse, isLocal: boolean) {
const flowType: Flow = "starter";
+ const finalStep = finalSteps[flowType];
const itemsDone = getProgress(data, flowType, isLocal);
const totalItems = getOnboardingLength(flowType, isLocal);
return {
itemsDone,
totalItems,
items: getStarterSetupItems(isLocal),
- isFinished: itemsDone === totalItems,
+ isFinished: data.includes(finalStep),
progress: (itemsDone / totalItems) * 100,
finalStep: finalSteps.starter,
hasItem: (item: OnboardingChecklistItemName) => hasOnboardingItem(item, data),
@@ -36,13 +37,14 @@ export function getStarterSetup(data: OnboardingResponse, isLocal: boolean) {
export function getProductionSetup(data: OnboardingResponse) {
const flowType: Flow = "production";
+ const finalStep = finalSteps[flowType];
const itemsDone = getProgress(data, flowType);
const totalItems = getOnboardingLength(flowType);
return {
itemsDone,
totalItems,
items: getProductionSetupItems(),
- isFinished: itemsDone === totalItems,
+ isFinished: data.includes(finalStep),
progress: (itemsDone / totalItems) * 100,
finalStep: finalSteps.starter,
hasItem: (item: OnboardingChecklistItemName) => hasOnboardingItem(item, data),
From f530f37a16475306bd1fd962ed57c6cee953e0cc Mon Sep 17 00:00:00 2001
From: Cahllagerfeld <43843195+Cahllagerfeld@users.noreply.github.com>
Date: Wed, 10 Jul 2024 14:19:06 +0000
Subject: [PATCH 12/15] chore: adjust test
---
src/app/onboarding/StarterSetup/Items.tsx | 1 -
src/lib/onboarding.spec.ts | 12 ++++++++++++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/src/app/onboarding/StarterSetup/Items.tsx b/src/app/onboarding/StarterSetup/Items.tsx
index 4066b3b38..76b692898 100644
--- a/src/app/onboarding/StarterSetup/Items.tsx
+++ b/src/app/onboarding/StarterSetup/Items.tsx
@@ -8,7 +8,6 @@ import { Box, Skeleton, buttonVariants } from "@zenml-io/react-component-library
export function ConnectZenMLStep({ completed, hasDownstreamStep, active }: OnboardingStep) {
const { data } = useServerInfo({ throwOnError: true });
-
return (
{
const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState, "starter", true);
expect(hasDownStreamFinishded).toBe(false);
});
+
+ test("only final step is there", () => {
+ const itemName: OnboardingChecklistItemName = "stack_with_remote_orchestrator_created";
+ const onboarding_state: OnboardingChecklistItemName[] = ["production_setup_completed"];
+ const hasDownStreamFinished = checkDownstreamStep(
+ itemName,
+ onboarding_state,
+ "production",
+ false
+ );
+ expect(hasDownStreamFinished).toBe(true);
+ });
});
describe("checks if the correct length is returned", () => {
From 0c8bfdc1a3b17c852188a698c6ee52d9066a4ec4 Mon Sep 17 00:00:00 2001
From: Cahllagerfeld <43843195+Cahllagerfeld@users.noreply.github.com>
Date: Wed, 10 Jul 2024 14:26:40 +0000
Subject: [PATCH 13/15] feat: redirect back to onboarding
---
src/app/onboarding/ProductionSetup/Items.tsx | 4 +++-
src/app/stacks/create/SmartSetup.tsx | 7 +++++--
src/app/stacks/create/new-infrastructure/Wizard.tsx | 6 ++++--
3 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/src/app/onboarding/ProductionSetup/Items.tsx b/src/app/onboarding/ProductionSetup/Items.tsx
index 6945e7350..652424742 100644
--- a/src/app/onboarding/ProductionSetup/Items.tsx
+++ b/src/app/onboarding/ProductionSetup/Items.tsx
@@ -9,6 +9,8 @@ import { Button } from "@zenml-io/react-component-library";
import { Link } from "react-router-dom";
export function CreateNewStack({ completed, active, hasDownstreamStep }: OnboardingStep) {
+ const link =
+ routes.stacks.create.index + "?" + new URLSearchParams({ origin: "onboarding" }).toString();
return (
Connect your Cloud to deploy your ZenML pipelines in a remote stack.
-
+
Register a stack
diff --git a/src/app/stacks/create/SmartSetup.tsx b/src/app/stacks/create/SmartSetup.tsx
index 744151cd8..37c5de9d2 100644
--- a/src/app/stacks/create/SmartSetup.tsx
+++ b/src/app/stacks/create/SmartSetup.tsx
@@ -4,7 +4,7 @@ import { useServerInfo } from "@/data/server/info-query";
import { checkIsLocalServer } from "@/lib/server";
import { routes } from "@/router/routes";
import { Skeleton } from "@zenml-io/react-component-library";
-import { Link } from "react-router-dom";
+import { Link, useSearchParams } from "react-router-dom";
import { CreateStackOptionCard } from "./OptionCard";
export function SmartSetup() {
@@ -34,10 +34,13 @@ type Props = {
isLocalDeployment: boolean;
};
function NewInfrastructure({ isLocalDeployment }: Props) {
+ const [searchParams] = useSearchParams();
+ const link =
+ routes.stacks.create.newInfra + (searchParams.size >= 1 ? `?${searchParams.toString()}` : "");
return (
{isLocalDeployment && }
-
+
Date: Wed, 10 Jul 2024 14:41:54 +0000
Subject: [PATCH 14/15] fix: downstream step
---
src/lib/onboarding.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/lib/onboarding.ts b/src/lib/onboarding.ts
index 3e3d7bb6d..778f479fe 100644
--- a/src/lib/onboarding.ts
+++ b/src/lib/onboarding.ts
@@ -60,7 +60,7 @@ function getItem(
) {
return {
isCompleted: state.includes(item),
- hasDownStreamStep: checkDownstreamStep(item, state, "starter", isLocal || false),
+ hasDownStreamStep: checkDownstreamStep(item, state, flow, isLocal || false),
isActive: isStepActive(item, state, flow, isLocal || false)
};
}
From 517c610b31bb96c54271263e402728c436ed9b9e Mon Sep 17 00:00:00 2001
From: Cahllagerfeld <43843195+Cahllagerfeld@users.noreply.github.com>
Date: Wed, 10 Jul 2024 14:57:28 +0000
Subject: [PATCH 15/15] chore: hide onboarding item
---
src/layouts/AuthenticatedLayout/OnboardingItem.tsx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/layouts/AuthenticatedLayout/OnboardingItem.tsx b/src/layouts/AuthenticatedLayout/OnboardingItem.tsx
index d1f4872e6..325d125e3 100644
--- a/src/layouts/AuthenticatedLayout/OnboardingItem.tsx
+++ b/src/layouts/AuthenticatedLayout/OnboardingItem.tsx
@@ -33,6 +33,8 @@ export function OnboardingItem() {
const totalItems = starterSetup.isFinished ? productionSetup.totalItems : starterSetup.totalItems;
const progress = starterSetup.isFinished ? productionSetup.progress : starterSetup.progress;
+ if (starterSetup.isFinished && productionSetup.isFinished) return null;
+
return (