New onboarding choose plan page

This commit is contained in:
James Ritchie
2023-11-28 15:33:53 +00:00
parent f2bf69b6c6
commit 26445ca367
2 changed files with 117 additions and 1 deletions
@@ -41,7 +41,7 @@ export default function SegmentedControl({
value={option.value}
className={({ active, checked }) =>
cn(
"relative flex h-full grow cursor-pointer rounded-[2px] font-normal focus:outline-none",
"relative flex h-full grow cursor-pointer rounded-[2px] text-center font-normal focus:outline-none",
active
? "ring-offset-2 focus-visible:ring focus-visible:ring-indigo-500 focus-visible:ring-opacity-60"
: "",
@@ -0,0 +1,116 @@
import { useForm } from "@conform-to/react";
import { parse } from "@conform-to/zod";
import { ChartBarIcon } from "@heroicons/react/20/solid";
import type { ActionFunction, LoaderFunctionArgs } from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import { Form, useActionData } from "@remix-run/react";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { z } from "zod";
import { PricingCalculator } from "~/components/billing/PricingCalculator";
import { PricingTiers, TierEnterprise, TierFree, TierPro } from "~/components/billing/PricingTiers";
import { RunsVolumeDiscountTable } from "~/components/billing/RunsVolumeDiscountTable";
import { Button } from "~/components/primitives/Buttons";
import { Header1 } from "~/components/primitives/Headers";
import {
Sheet,
SheetBody,
SheetContent,
SheetHeader,
SheetTrigger,
} from "~/components/primitives/Sheet";
import { createOrganization } from "~/models/organization.server";
import { NewOrganizationPresenter } from "~/presenters/NewOrganizationPresenter.server";
import { commitCurrentProjectSession, setCurrentProjectId } from "~/services/currentProject.server";
import { requireUserId } from "~/services/session.server";
import { projectPath } from "~/utils/pathBuilder";
const schema = z.object({
orgName: z.string().min(3).max(50),
projectName: z.string().min(3).max(50),
});
export const loader = async ({ request }: LoaderFunctionArgs) => {
const userId = await requireUserId(request);
const presenter = new NewOrganizationPresenter();
const { hasOrganizations } = await presenter.call({ userId });
return typedjson({
hasOrganizations,
});
};
export const action: ActionFunction = async ({ request }) => {
const userId = await requireUserId(request);
const formData = await request.formData();
const submission = parse(formData, { schema });
if (!submission.value || submission.intent !== "submit") {
return json(submission);
}
try {
const organization = await createOrganization({
title: submission.value.orgName,
userId,
projectName: submission.value.projectName,
});
const project = organization.projects[0];
const session = await setCurrentProjectId(project.id, request);
return redirect(projectPath(organization, project), {
headers: {
"Set-Cookie": await commitCurrentProjectSession(session),
},
});
} catch (error: any) {
return json({ errors: { body: error.message } }, { status: 400 });
}
};
export default function ChoosePlanPage() {
const { hasOrganizations } = useTypedLoaderData<typeof loader>();
const lastSubmission = useActionData();
const [form, { orgName, projectName }] = useForm({
id: "create-organization",
// TODO: type this
lastSubmission: lastSubmission as any,
onValidate({ formData }) {
return parse(formData, { schema });
},
});
return (
<div className="mx-auto flex h-full w-full max-w-[80rem] flex-col items-center justify-center gap-12 overflow-y-auto px-12">
<Header1>Subscribe for full access</Header1>
<PricingTiers>
<TierFree />
<TierPro />
<TierEnterprise />
</PricingTiers>
<Sheet>
<SheetTrigger>
<Button variant="tertiary/small" LeadingIcon={ChartBarIcon} leadingIconClassName="px-0">
Estimate usage
</Button>
</SheetTrigger>
<SheetContent size="content">
<SheetHeader className="justify-between">
<div className="flex items-center gap-4">
<Header1>Estimate your usage</Header1>
</div>
</SheetHeader>
<SheetBody>
<PricingCalculator />
<div className="mt-8 rounded border border-border p-6">
<RunsVolumeDiscountTable />
</div>
</SheetBody>
</SheetContent>
</Sheet>
</div>
);
}