Skip to content

Commit 096babf

Browse files
feat: add form for upgrade (#694)
1 parent 52e50de commit 096babf

File tree

23 files changed

+592
-127
lines changed

23 files changed

+592
-127
lines changed

src/app/artifacts/Fragments.tsx

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,7 @@
11
import { Codesnippet } from "@/components/CodeSnippet";
2-
import { InfoBox as InfoBoxPrimitive } from "@/components/Infobox";
32
import { Box } from "@zenml-io/react-component-library";
43
import { useSearchParams } from "react-router-dom";
54

6-
export function InfoBox() {
7-
return (
8-
<InfoBoxPrimitive>
9-
<div className="flex w-full flex-wrap items-center gap-x-2 gap-y-0.5 text-text-md">
10-
<p className="font-semibold">This is a ZenML Pro feature. </p>
11-
<p>
12-
Upgrade to ZenML Pro to access the Artifact Control Plane and interact with your artifacts
13-
in the Dashboard.
14-
</p>
15-
</div>
16-
</InfoBoxPrimitive>
17-
);
18-
}
19-
205
export function CommandSection() {
216
const [searchParams] = useSearchParams();
227
const artifactName = searchParams.get("artifact");

src/app/artifacts/page.tsx

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,56 @@
1-
import { PageHeader } from "@/components/PageHeader";
2-
import { Badge } from "@zenml-io/react-component-library";
3-
import { CommandSection, InfoBox } from "./Fragments";
4-
import { CTASection, artifactFeatures } from "@/contents/cloud-only";
51
import ACP from "@/assets/images/acp.webp";
2+
import { PageHeader } from "@/components/PageHeader";
3+
import { ProBadge } from "@/components/pro/ProBadge";
4+
import {
5+
ProButtons,
6+
ProFeatureList,
7+
ProHeadline,
8+
ProImage,
9+
ProInfoBadge,
10+
ProWrapper
11+
} from "@/components/pro/ProCta";
12+
import { CommandSection } from "./Fragments";
613

714
export default function ModelsPage() {
815
return (
916
<div>
1017
<PageHeader className="flex items-center gap-1">
1118
<h1 className="text-display-xs font-semibold">Artifacts</h1>
12-
<Badge color="purple" rounded size="sm">
13-
<span className="font-semibold text-primary-500">Pro</span>
14-
</Badge>
19+
<ProBadge />
1520
</PageHeader>
1621
<div className="layout-container space-y-5 py-5">
17-
<InfoBox />
18-
<CTASection
19-
feature="artifact"
20-
image={{ src: ACP, alt: "Screenshot of the ZenML Pro Artifact Control plane" }}
21-
features={artifactFeatures}
22-
/>
22+
<ProWrapper className="relative overflow-y-hidden">
23+
<div className="w-full max-w-none space-y-5 lg:max-w-[900px]">
24+
<ProHeadline>Advanced Artifact Management Features with ZenML Pro</ProHeadline>
25+
<ProInfoBadge />
26+
<ProFeatureList
27+
features={[
28+
{
29+
title: "Artifact Control Plane Dashboard",
30+
subtitle: "Artifact management and monitoring"
31+
},
32+
{
33+
title: "Enterprise Security",
34+
subtitle: "Social SSO, RBAC, and User Management"
35+
},
36+
{
37+
title: "Managed ZenML Server",
38+
subtitle: "On your VPC or hosted on our infrastructure"
39+
},
40+
{
41+
title: "Advanced MLOps",
42+
subtitle: "CI/CD/CT, Model Control Plane and more"
43+
}
44+
]}
45+
/>
46+
<ProButtons />
47+
</div>
48+
<ProImage
49+
className="flex-1 translate-x-[10%] translate-y-[20%] scale-110"
50+
src={ACP}
51+
alt="Screenshot of the ZenML Pro Artifact Control Plane"
52+
/>
53+
</ProWrapper>
2354
<CommandSection />
2455
</div>
2556
</div>

src/app/models/Fragments.tsx

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,7 @@
11
import { Codesnippet } from "@/components/CodeSnippet";
2-
import { InfoBox as InfoBoxPrimitive } from "@/components/Infobox";
32
import { Box } from "@zenml-io/react-component-library";
43
import { useSearchParams } from "react-router-dom";
54

6-
export function InfoBox() {
7-
return (
8-
<InfoBoxPrimitive>
9-
<div className="flex w-full flex-wrap items-center gap-x-2 gap-y-0.5 text-text-md">
10-
<p className="font-semibold">This is a ZenML Pro feature. </p>
11-
<p>
12-
Upgrade to ZenML Pro to access the Model Control Plane and interact with your models in
13-
the Dashboard.
14-
</p>
15-
</div>
16-
</InfoBoxPrimitive>
17-
);
18-
}
19-
205
export function CommandSection() {
216
const [searchParams] = useSearchParams();
227
const modelName = searchParams.get("model");

src/app/models/page.tsx

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1-
import { PageHeader } from "@/components/PageHeader";
2-
import { Badge } from "@zenml-io/react-component-library";
3-
import { CommandSection, InfoBox } from "./Fragments";
4-
import { CTASection, modelFeatures } from "@/contents/cloud-only";
51
import MCP from "@/assets/images/mcp.webp";
2+
import { PageHeader } from "@/components/PageHeader";
3+
import { ProBadge } from "@/components/pro/ProBadge";
4+
import {
5+
ProButtons,
6+
ProFeatureList,
7+
ProHeadline,
8+
ProImage,
9+
ProInfoBadge,
10+
ProWrapper
11+
} from "@/components/pro/ProCta";
612
import { useTourContext } from "@/components/tour/TourContext";
713
import { useEffect } from "react";
14+
import { CommandSection } from "./Fragments";
815

916
export default function ModelsPage() {
1017
const {
@@ -22,17 +29,41 @@ export default function ModelsPage() {
2229
<div>
2330
<PageHeader className="flex items-center gap-1">
2431
<h1 className="text-display-xs font-semibold">Models</h1>
25-
<Badge color="purple" rounded size="sm">
26-
<span className="font-semibold text-primary-500">Pro</span>
27-
</Badge>
32+
<ProBadge />
2833
</PageHeader>
2934
<div className="layout-container space-y-5 py-5">
30-
<InfoBox />
31-
<CTASection
32-
feature="model"
33-
image={{ src: MCP, alt: "Screenshot of the ZenML Pro Model Control plane" }}
34-
features={modelFeatures}
35-
/>
35+
<ProWrapper className="relative overflow-y-hidden">
36+
<div className="w-full max-w-none space-y-5 lg:max-w-[900px]">
37+
<ProHeadline>Access Advanced Model Management Features with ZenML Pro</ProHeadline>
38+
<ProInfoBadge />
39+
<ProFeatureList
40+
features={[
41+
{
42+
title: "Model Control Plane Dashboard",
43+
subtitle: "Centralized model management and monitoring"
44+
},
45+
{
46+
title: "Enterprise Security",
47+
subtitle: "Social SSO, RBAC, and User Management"
48+
},
49+
{
50+
title: "Managed ZenML Server",
51+
subtitle: "On your VPC or hosted on our infrastructure"
52+
},
53+
{
54+
title: "Advanced MLOps",
55+
subtitle: "CI/CD/CT, Artifact Control Plane and more"
56+
}
57+
]}
58+
/>
59+
<ProButtons />
60+
</div>
61+
<ProImage
62+
className="flex-1 translate-x-[10%] translate-y-[20%] scale-110"
63+
src={MCP}
64+
alt="Screenshot of the ZenML Pro Artifact Control Plane"
65+
/>
66+
</ProWrapper>
3667
<CommandSection />
3768
</div>
3869
</div>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { createContext, Dispatch, SetStateAction, useContext, useState } from "react";
2+
3+
type UpgradeContextType = {
4+
submitSuccess: boolean;
5+
setSubmitSuccess: Dispatch<SetStateAction<boolean>>;
6+
};
7+
8+
export const UpgradeContext = createContext<UpgradeContextType | null>(null);
9+
10+
export function UpgradeProvider({ children }: { children: React.ReactNode }) {
11+
const [success, setSuccess] = useState(false);
12+
return (
13+
<UpgradeContext.Provider
14+
value={{
15+
setSubmitSuccess: setSuccess,
16+
submitSuccess: success
17+
}}
18+
>
19+
{children}
20+
</UpgradeContext.Provider>
21+
);
22+
}
23+
24+
export function useUpgradeContext() {
25+
const context = useContext(UpgradeContext);
26+
if (context === null) {
27+
throw new Error("useUpgradeContext must be used within an UpgradeProvider");
28+
}
29+
return context;
30+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import CheckCircle from "@/assets/icons/check-circle.svg?react";
2+
import { Tick } from "@/components/pro/ProCta";
3+
import { routes } from "@/router/routes";
4+
import { Button } from "@zenml-io/react-component-library/components/server";
5+
import { Link } from "react-router-dom";
6+
7+
const contents = [
8+
"Comprehensive documentation to get started",
9+
"Your 14-day trial license key",
10+
"Step-by-step deployment instructions"
11+
];
12+
13+
export function SubmitSuccess() {
14+
return (
15+
<section className="flex h-full w-full flex-col items-center justify-center space-y-5">
16+
<CheckCircle className="h-[120px] w-[120px] fill-theme-text-success" />
17+
<div className="space-y-7">
18+
<div className="max-w-[500px] space-y-2 text-center">
19+
<h1 className="text-display-xs font-semibold">You're on your way to ZenML Pro!</h1>
20+
<p className="text-theme-text-secondary">
21+
Thank you for choosing to upgrade to ZenML Pro. We've received your request and you'll
22+
receive an email with:
23+
</p>
24+
<ul className="mx-auto w-fit space-y-3">
25+
{contents.map((val, idx) => (
26+
<li className="flex items-center gap-1" key={idx}>
27+
<Tick />
28+
<span>{val}</span>
29+
</li>
30+
))}
31+
</ul>
32+
<p className="text-theme-text-secondary">
33+
Meanwhile, you can ask your questions in our{" "}
34+
<a
35+
className="link"
36+
href="https://zenml.io/slack"
37+
target="_blank"
38+
rel="noopener noreferrer"
39+
>
40+
Slack channel
41+
</a>{" "}
42+
or{" "}
43+
<a
44+
className="link"
45+
href="https://docs.zenml.io"
46+
target="_blank"
47+
rel="noopener noreferrer"
48+
>
49+
check our documentation.
50+
</a>
51+
</p>
52+
</div>
53+
<Button size="md" className="mx-auto">
54+
<Link to={routes.home}>Go to Dashboard</Link>
55+
</Button>
56+
</div>
57+
</section>
58+
);
59+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { useServerInfo } from "@/data/server/info-query";
2+
import { useCurrentUser } from "@/data/users/current-user-query";
3+
import { Button, Input, Skeleton } from "@zenml-io/react-component-library/components/server";
4+
import { useUpgradeForm } from "./useUpgradeForm";
5+
6+
export function UpgradeFormSection() {
7+
return (
8+
<div className="space-y-5">
9+
<HeadingSection />
10+
<Form />
11+
</div>
12+
);
13+
}
14+
15+
function HeadingSection() {
16+
return (
17+
<div className="flex flex-col items-center space-y-0.5">
18+
<h1 className="text-center text-display-xs font-semibold">
19+
Upgrade to ZenML Pro on your own VPC
20+
</h1>
21+
<p className="text-theme-text-secondary">Direct self-deployment, no sales calls</p>
22+
</div>
23+
);
24+
}
25+
26+
function Form() {
27+
const serverInfo = useServerInfo();
28+
const user = useCurrentUser();
29+
30+
const { form, handleSubmitForm, submitFormMutation } = useUpgradeForm();
31+
32+
const {
33+
register,
34+
handleSubmit,
35+
formState: { isValid, isSubmitting }
36+
} = form;
37+
38+
const { isPending } = submitFormMutation;
39+
40+
if (serverInfo.isPending || user.isPending) return <Skeleton className="h-[250px] w-full" />;
41+
if (serverInfo.isError || user.isError) return <p>Something went wrong....</p>;
42+
43+
return (
44+
<div className="space-y-5">
45+
<form
46+
onSubmit={handleSubmit((data) =>
47+
handleSubmitForm(data, user.data.id, !!serverInfo.data.debug)
48+
)}
49+
className="space-y-5"
50+
>
51+
<div className="space-y-0.5">
52+
<label htmlFor="name" className="text-text-sm">
53+
Your Name
54+
</label>
55+
<Input {...register("name")} id="name" className="w-full" />
56+
</div>
57+
<div className="space-y-0.5">
58+
<label htmlFor="company" className="text-text-sm">
59+
Company
60+
</label>
61+
<Input {...register("company")} id="company" className="w-full" />
62+
</div>
63+
<div className="space-y-0.5">
64+
<label htmlFor="email" className="text-text-sm">
65+
Email address
66+
</label>
67+
<Input {...register("email")} id="email" className="w-full" />
68+
</div>
69+
<Button
70+
size="md"
71+
className="w-full justify-center"
72+
disabled={isSubmitting || isPending || !isValid}
73+
type="submit"
74+
>
75+
{(isPending || isSubmitting) && (
76+
<div
77+
role="alert"
78+
aria-busy="true"
79+
className="full h-[20px] w-[20px] animate-spin rounded-rounded border-2 border-theme-text-negative border-b-theme-text-brand"
80+
></div>
81+
)}
82+
Continue
83+
</Button>
84+
</form>
85+
<p className="text-center text-text-xs text-theme-text-secondary">
86+
By submitting the form you accept our{" "}
87+
<a
88+
className="link"
89+
href="https://www.zenml.io/cloud-terms-and-privacy"
90+
target="_blank"
91+
rel="noopener noreferrer"
92+
>
93+
terms of use
94+
</a>{" "}
95+
and{" "}
96+
<a
97+
href="https://www.zenml.io/privacy-policy"
98+
target="_blank"
99+
rel="noopener noreferrer"
100+
className="link"
101+
>
102+
privacy policy
103+
</a>
104+
.
105+
</p>
106+
</div>
107+
);
108+
}

0 commit comments

Comments
 (0)