Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic Onboarding #615

Merged
merged 18 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/activate-server/ServerNameStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function SetServerNameStep({ setUsername }: Props) {
const { mutate } = useActivateServer({
onSuccess: async (data) => {
setUsername(data.name);
loginMutate({ username: data.name, password: serverSettings.admin_password });
loginMutate({ username: data.name, password: serverSettings.admin_password ?? undefined });
},
onError: (error) => {
if (error instanceof Error) {
Expand Down
1 change: 0 additions & 1 deletion src/app/activate-user/AccountDetailsStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export function AccountDetailsStep() {
const { setNewUser, newUser } = useActivationContext();

function handleDetailsSubmit({ fullName, getUpdates, email }: AccountDetailForm) {
//@ts-expect-error null is the initial value it needs to be set to
setNewUser((prev) => ({
...prev,
...(email ? { email } : { email: null }),
Expand Down
2 changes: 1 addition & 1 deletion src/app/activate-user/AwarenessStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function AwarenessStep({ userId, setUsername }: Props) {
const { mutate } = useActivateUser({
onSuccess: async (data) => {
setUsername(data.name);
loginMutate({ username: data.name, password: newUser.password });
loginMutate({ username: data.name, password: newUser.password ?? undefined });
},
onError: (error) => {
if (error instanceof Error) {
Expand Down
48 changes: 29 additions & 19 deletions src/app/onboarding/ProductionSetup/Items.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import { ChecklistItem } from "@/components/onboarding/ChecklistItem";
import { CloudProvider, ProviderSelect } from "./ProviderSelect";
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 { useState } from "react";
import { getOnboardingItem } from "@/lib/onboarding";
import { OnboardingChecklistItemName, OnboardingState } from "@/types/onboarding";
import { getServiceConnectorStep } from "./ConnectorContent";
import { getArtifactStoreStep } from "./ArtifactStore";
import { Codesnippet } from "@/components/CodeSnippet";
import { getServiceConnectorStep } from "./ConnectorContent";
import { CloudProvider, ProviderSelect } from "./ProviderSelect";

type Props = {
onboardingState?: OnboardingState;
onboardingState?: OnboardingResponse;
order: OnboardingChecklistItemName[];
active?: boolean;
};

export function CreateServiceConnector({ onboardingState, active }: Props) {
export function CreateServiceConnector({ onboardingState, active, order }: Props) {
const [selectedProvider, setSelectedProvider] = useState<CloudProvider>("aws");
const itemName: OnboardingChecklistItemName = "create_service_connector";
const item = getOnboardingItem(onboardingState || {}, itemName);
const itemName: OnboardingChecklistItemName = "service_connector_created";
const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState || [], order);
const item = hasOnboardingItem(itemName, onboardingState || []);
return (
<ChecklistItem
hasDownstream={hasDownStreamFinishded}
active={active}
Cahllagerfeld marked this conversation as resolved.
Show resolved Hide resolved
completed={!!item}
title="Create a service connector"
Expand Down Expand Up @@ -52,13 +55,16 @@ export function CreateServiceConnector({ onboardingState, active }: Props) {
);
}

export function CreateArtifactStore({ onboardingState, active }: Props) {
export function CreateArtifactStore({ onboardingState, active, order }: Props) {
const [selectedProvider, setSelectedProvider] = useState<CloudProvider>("aws");
const itemName: OnboardingChecklistItemName = "create_remote_artifact_store";
const item = getOnboardingItem(onboardingState || {}, itemName);
const itemName: OnboardingChecklistItemName = "remote_artifact_store_created";
const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState || [], order);

const item = hasOnboardingItem(itemName, onboardingState || []);

return (
<ChecklistItem
hasDownstream={hasDownStreamFinishded}
itemName={itemName}
completed={!!item}
title="Create an artifact store"
Expand Down Expand Up @@ -93,12 +99,14 @@ export function CreateArtifactStore({ onboardingState, active }: Props) {
);
}

export function CreateNewStack({ onboardingState, active }: Props) {
const itemName: OnboardingChecklistItemName = "create_remote_stack";
const item = getOnboardingItem(onboardingState || {}, itemName);
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 || []);

return (
<ChecklistItem
hasDownstream={hasDownStreamFinishded}
itemName={itemName}
completed={!!item}
title="Create a new stack"
Expand All @@ -124,12 +132,14 @@ export function CreateNewStack({ onboardingState, active }: Props) {
);
}

export function RunNewPipeline({ active, onboardingState }: Props) {
const itemName: OnboardingChecklistItemName = "run_remote_pipeline";
const item = getOnboardingItem(onboardingState || {}, itemName);
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 || []);

return (
<ChecklistItem
hasDownstream={hasDownStreamFinishded}
itemName={itemName}
completed={!!item}
title="Run the pipeline in the new stack"
Expand Down
58 changes: 44 additions & 14 deletions src/app/onboarding/ProductionSetup/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import ChevronDown from "@/assets/icons/chevron-down.svg?react";
import { Tick } from "@/components/Tick";
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 { useOnboarding } from "@/data/server/onboarding-state";
import {
getProductionSetupItems,
getProgress,
getStarterSetupItems,
getOnboardingProgress
} from "@/lib/onboarding";
import { checkIsLocalServer } from "@/lib/server";
import { OnboardingChecklistItemName } from "@/types/onboarding";
import {
Collapsible,
CollapsibleContent,
Expand All @@ -22,22 +27,37 @@ import {
} from "./Items";

export function ProductionSetupChecklist() {
const { isError, isPending, data } = useServerSettings({ throwOnError: true });
const onboarding = useOnboarding({ refetchInterval: 5000 });
const serverInfo = useServerInfo();
const [open, setOpen] = useState(true);

if (isPending || serverInfo.isPending) return <Skeleton className="h-[200px] w-full" />;
if (isError || serverInfo.isError) return null;
if (onboarding.isPending || serverInfo.isPending)
return <Skeleton className="h-[200px] w-full" />;
if (onboarding.isError || serverInfo.isError) return null;
Cahllagerfeld marked this conversation as resolved.
Show resolved Hide resolved

const STARTER_SETUP_ITEMS = getStarterSetupItems(
checkIsLocalServer(serverInfo.data.deployment_type || "other")
);

const onboardingState = getOnboardingState(data);
const PRODUCTION_SETUP_ITEMS = getProductionSetupItems();

const isStarterSetupFinished =
getProgress(onboardingState, STARTER_SETUP_ITEMS) === STARTER_SETUP_ITEMS.length;
const doneItems = getProgress(onboardingState, PRODUCTION_SETUP_ITEMS);
const progress = (doneItems / PRODUCTION_SETUP_ITEMS.length) * 100;
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 order: OnboardingChecklistItemName[] = [
...(PRODUCTION_SETUP_ITEMS as OnboardingChecklistItemName[]),
"production_setup_completed"
];

return (
<>
Expand Down Expand Up @@ -84,21 +104,31 @@ export function ProductionSetupChecklist() {
<ul className="divide-y divide-theme-border-moderate">
<li className="py-5 first:pt-0 last:pb-0">
<CreateServiceConnector
onboardingState={onboardingState}
order={order}
onboardingState={onboarding.data}
active={isStarterSetupFinished}
/>
</li>
<li className="py-5 first:pt-0 last:pb-0">
<CreateArtifactStore
onboardingState={onboardingState}
order={order}
onboardingState={onboarding.data}
active={isStarterSetupFinished}
/>
</li>
<li className="py-5 first:pt-0 last:pb-0">
<CreateNewStack onboardingState={onboardingState} active={isStarterSetupFinished} />
<CreateNewStack
order={order}
onboardingState={onboarding.data}
active={isStarterSetupFinished}
/>
</li>
<li className="py-5 first:pt-0 last:pb-0">
<RunNewPipeline onboardingState={onboardingState} active={isStarterSetupFinished} />
<RunNewPipeline
order={order}
onboardingState={onboarding.data}
active={isStarterSetupFinished}
/>
</li>
</ul>
</CollapsibleContent>
Expand Down
41 changes: 27 additions & 14 deletions src/app/onboarding/StarterSetup/Items.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import { ChecklistItem } from "@/components/onboarding/ChecklistItem";
import Help from "@/assets/icons/help.svg?react";
import { Codesnippet } from "@/components/CodeSnippet";
import { HelpBox } from "@/components/fallback-pages/Helpbox";
import { Box, Skeleton, buttonVariants } from "@zenml-io/react-component-library";
import Help from "@/assets/icons/help.svg?react";
import { OnboardingChecklistItemName, OnboardingState } from "@/types/onboarding";
import { getOnboardingItem } from "@/lib/onboarding";
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 { Box, Skeleton, buttonVariants } from "@zenml-io/react-component-library";

type Props = {
onboardingState?: OnboardingState;
onboardingState: OnboardingResponse;
order: OnboardingChecklistItemName[];
};
export function ConnectZenMLStep({ onboardingState }: Props) {
export function ConnectZenMLStep({ onboardingState, order }: Props) {
const { data } = useServerInfo({ throwOnError: true });

const itemName = "connect_zenml";
const item = getOnboardingItem(onboardingState || {}, itemName);
const itemName: OnboardingChecklistItemName = "device_verified";
const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState || [], order);
const item = hasOnboardingItem(itemName, onboardingState || []);
return (
<ChecklistItem itemName={itemName} completed={!!item} title="Connect to ZenML">
<ChecklistItem
hasDownstream={hasDownStreamFinishded}
itemName={itemName}
completed={!!item}
title="Connect to ZenML"
>
<div className="flex flex-col gap-5">
<div>
<p className="mb-1 text-text-sm text-theme-text-secondary">Install ZenML</p>
Expand All @@ -36,11 +43,17 @@ export function ConnectZenMLStep({ onboardingState }: Props) {
);
}

export function RunFirstPipeline({ onboardingState }: Props) {
const itemName: OnboardingChecklistItemName = "run_first_pipeline";
const item = getOnboardingItem(onboardingState || {}, itemName);
export function RunFirstPipeline({ onboardingState, order }: Props) {
const itemName: OnboardingChecklistItemName = "pipeline_run";
const item = hasOnboardingItem(itemName, onboardingState || []);
const hasDownStreamFinishded = checkDownstreamStep(itemName, onboardingState || [], order);
return (
<ChecklistItem itemName={itemName} completed={!!item} title="Run your first pipeline">
<ChecklistItem
hasDownstream={hasDownStreamFinishded}
itemName={itemName}
completed={!!item}
title="Run your first pipeline"
>
Cahllagerfeld marked this conversation as resolved.
Show resolved Hide resolved
<div className="flex flex-col gap-5">
<div>
<p className="mb-1 text-text-sm text-theme-text-secondary">
Expand Down
28 changes: 19 additions & 9 deletions src/app/onboarding/StarterSetup/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import ChevronDown from "@/assets/icons/chevron-down.svg?react";
import { Tick } from "@/components/Tick";
import { useServerSettings } from "@/data/server/get-server-settings";
import { useServerInfo } from "@/data/server/info-query";
import { getOnboardingState, getProgress, getStarterSetupItems } from "@/lib/onboarding";
import { useOnboarding } from "@/data/server/onboarding-state";
import { getStarterSetupItems, getOnboardingProgress } from "@/lib/onboarding";
import { checkIsLocalServer } from "@/lib/server";
import { OnboardingChecklistItemName } from "@/types/onboarding";
import {
Collapsible,
CollapsibleContent,
Expand All @@ -15,18 +16,27 @@ import { useState } from "react";
import { ConnectZenMLStep, RunFirstPipeline } from "./Items";

export function StarterSetupList() {
const { isError, isPending, data } = useServerSettings({ throwOnError: true });
const onboarding = useOnboarding({ refetchInterval: 5000 });
const serverInfo = useServerInfo();
const [open, setOpen] = useState(true);

if (isPending || serverInfo.isPending) return <Skeleton className="h-[200px] w-full" />;
if (isError || serverInfo.isError) return null;
if (onboarding.isPending || serverInfo.isPending)
return <Skeleton className="h-[200px] w-full" />;
if (onboarding.isError || serverInfo.isError) return null;

const isLocalServer = checkIsLocalServer(serverInfo.data.deployment_type || "other");
const STARTER_SETUP_ITEMS = getStarterSetupItems(isLocalServer);

const doneItems = getProgress(getOnboardingState(data), STARTER_SETUP_ITEMS);
const progress = (doneItems / STARTER_SETUP_ITEMS.length) * 100;
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"
];

return (
<Collapsible
Expand Down Expand Up @@ -67,11 +77,11 @@ export function StarterSetupList() {
<ul className="divide-y divide-theme-border-moderate">
{!isLocalServer && (
<li className="py-5 first:pt-0 last:pb-0">
<ConnectZenMLStep onboardingState={getOnboardingState(data)} />
<ConnectZenMLStep order={order} onboardingState={onboarding.data} />
</li>
)}
<li className="py-5 first:pt-0 last:pb-0">
<RunFirstPipeline onboardingState={getOnboardingState(data)} />
<RunFirstPipeline order={order} onboardingState={onboarding.data} />
</li>
</ul>
</CollapsibleContent>
Expand Down
2 changes: 1 addition & 1 deletion src/app/runs/[id]/_Tabs/Configuration/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function ConfigurationTab() {

return (
<div className="grid grid-cols-1 gap-5">
<NestedCollapsible title="Parameters" data={data.metadata?.config.parameters} />
<NestedCollapsible title="Parameters" data={data.metadata?.config.parameters ?? undefined} />
{(buildData?.metadata?.images as BuildItemMap)?.orchestrator && (
<DockerImageCollapsible data={buildData?.metadata?.images?.orchestrator as BuildItem} />
)}
Expand Down
4 changes: 2 additions & 2 deletions src/app/settings/notifications/NotificationsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ export function NotificationsForm({ settings }: Props) {
const { control, handleSubmit, watch } = useForm<NotificationFormType>({
resolver: zodResolver(NotificationFormSchema),
defaultValues: {
announcements: settings.body?.display_announcements,
updates: settings.body?.display_updates
announcements: settings.body?.display_announcements ?? undefined,
updates: settings.body?.display_updates ?? undefined
}
});

Expand Down
2 changes: 1 addition & 1 deletion src/app/settings/profile/UpdateProfileForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export function UpdateProfileForm({ user }: Props) {
Email
</label>
<Input
placeholder={user.metadata?.email}
placeholder={user.metadata?.email ?? undefined}
{...register("email")}
id={emailId}
className="w-full"
Expand Down
1 change: 0 additions & 1 deletion src/app/survey/AccountDetailsStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export function AccountDetailsStep({ user }: Props) {
const { setSurveyStep } = useSurveyContext();
const { setUser } = useSurveyUserContext();
function handleDetailsSubmit({ fullName, getUpdates, email }: AccountDetailForm) {
//@ts-expect-error null is the initial value it needs to be set to
setUser((prev) => ({
...prev,
...(email ? { email } : { email: null }),
Expand Down
4 changes: 2 additions & 2 deletions src/components/ExecutionStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Initializing from "@/assets/icons/initializing.svg?react";
import Cached from "@/assets/icons/cached.svg?react";
import Running from "@/assets/icons/dots-circle.svg?react";

export function getExecutionStatusColor(status?: ExecutionStatus) {
export function getExecutionStatusColor(status?: ExecutionStatus | null) {
if (!status) return null;
switch (status) {
case "completed":
Expand Down Expand Up @@ -58,7 +58,7 @@ export function ExecutionStatusIcon({
status,
className
}: {
status?: ExecutionStatus;
status?: ExecutionStatus | null;
className?: string;
}) {
if (!status) return null;
Expand Down
Loading