feat: add homepage & auth

This commit is contained in:
2024-02-23 19:36:10 +07:00
parent d579c1b7c5
commit 735fa9354b
42 changed files with 981 additions and 70 deletions
+3
View File
@@ -0,0 +1,3 @@
import MainLayout from "~/components/layout/main-layout";
export { MainLayout as Layout };
+49 -8
View File
@@ -1,20 +1,61 @@
import { Button } from "~/components/ui/button";
import type { Data } from "./+data";
import { useData } from "~/renderer/hooks";
import Link from "~/renderer/link";
import Footer from "~/components/containers/footer";
const HomePage = () => {
const { projects } = useData<Data>();
return (
<div>
<h1>Posts</h1>
<>
<main>
<section className="container py-8 md:py-16 min-h-dvh md:min-h-[80vh] flex items-center justify-center flex-col text-center">
<h1 className="text-4xl md:text-5xl lg:text-6xl font-medium text-transparent bg-gradient-to-b from-gray-200 to-gray-100 bg-clip-text">
Create and Share Web Snippets
</h1>
<p className="text-lg mt-8 md:mt-12 text-gray-300">
Collaborate with Ease, Share with the World
</p>
{projects.map((project: any) => (
<Link key={project.id} href={`/${project.slug}`}>
{project.title}
</Link>
))}
</div>
<div className="flex flex-col sm:flex-row gap-3 items-center mt-8 md:mt-12">
<Button href="/get-started" size="lg">
Getting Started
</Button>
<Button href="#browse" size="lg" variant="outline">
Browse Projects
</Button>
</div>
</section>
<section id="browse" className="container max-w-5xl py-8 md:py-16">
<h2 className="text-4xl font-medium border-b pb-3 mb-8 border-white/20">
Community Projects
</h2>
<div className="grid sm:grid-cols-2 md:grid-cols-3 gap-3 md:gap-4">
{projects.map((project) => (
<Link
key={project.id}
href={`/${project.slug}`}
className="border border-white/20 hover:border-white/40 rounded-lg transition-colors overflow-hidden"
>
<div className="w-full aspect-[3/2] bg-gray-900"></div>
<div className="py-2 px-3 flex items-center gap-3">
<div className="size-8 rounded-full bg-white/80"></div>
<div>
<p className="text-md truncate">{project.title}</p>
<p className="text-xs truncate">{project.user.name}</p>
</div>
</div>
</Link>
))}
</div>
</section>
</main>
<Footer />
</>
);
};
+143
View File
@@ -0,0 +1,143 @@
import { Button } from "~/components/ui/button";
import Card, { CardTitle } from "~/components/ui/card";
import { FormLabel } from "~/components/ui/form-field";
import Input from "~/components/ui/input";
import { useData } from "~/renderer/hooks";
import { Data } from "./+data";
import { useForm } from "~/hooks/useForm";
import { z } from "zod";
import { Controller } from "react-hook-form";
import { navigate } from "vike/client/router";
import Divider from "~/components/ui/divider";
import trpc from "~/lib/trpc";
import { useAuth } from "~/hooks/useAuth";
import { usePageContext } from "~/renderer/context";
const schema = z.object({
forkFromId: z.number(),
title: z.string(),
user: z
.object({
name: z.string().min(3),
email: z.string().email(),
password: z.string().min(6),
})
.optional(),
});
const initialValue: z.infer<typeof schema> = {
forkFromId: 0,
title: "",
};
const GetStartedPage = () => {
const { presets } = useData<Data>();
const { isLoggedIn } = useAuth();
const ctx = usePageContext();
const form = useForm(schema, initialValue);
const create = trpc.project.create.useMutation({
onSuccess(data) {
navigate(`/${data.slug}`);
},
});
const onSubmit = form.handleSubmit((values) => {
create.mutate(values);
});
return (
<div className="container max-w-3xl min-h-[80dvh] flex flex-col items-center justify-center py-8 md:py-16">
<Card className="w-full md:p-8">
<CardTitle>Create New Project</CardTitle>
<form onSubmit={onSubmit}>
<FormLabel>Select Preset</FormLabel>
<Controller
control={form.control}
name="forkFromId"
render={({ field }) => (
<div className="flex md:grid md:grid-cols-3 gap-4 overflow-x-auto md:overflow-x-hidden">
{presets.map((preset) => (
<Button
key={preset.projectId}
variant={
field.value === preset.projectId ? "default" : "outline"
}
className="flex py-16 border border-white/40 shrink-0 w-[160px] md:w-auto"
onClick={() => field.onChange(preset.projectId)}
>
<p className="text-wrap">{preset.title}</p>
</Button>
))}
</div>
)}
/>
<Input
form={form}
name="title"
label="Title"
placeholder="Optional"
className="mt-4"
/>
{!isLoggedIn ? (
<div className="mt-8">
<p className="text-lg">Account detail</p>
<Divider className="mt-2 mb-4" />
<div className="grid md:grid-cols-2 gap-x-2 gap-y-4">
<div className="order-1 md:order-2 flex flex-col items-center justify-center gap-3 py-4">
<p className="text-center">Already have an account?</p>
<Button
href={`/auth/login?return=${encodeURI(ctx.urlPathname)}`}
variant="outline"
>
Log in now
</Button>
</div>
<div className="order-2 md:order-1 space-y-3">
<Input
form={form}
name="user.name"
label="Full Name"
placeholder="John Doe"
/>
<Input
form={form}
name="user.email"
label="Email Address"
placeholder="[email protected]"
/>
<Input
form={form}
type="password"
name="user.password"
label="Password"
placeholder="P@ssw0rd123!"
/>
</div>
</div>
</div>
) : null}
<Divider className="my-6" />
<Button
type="submit"
size="lg"
isLoading={create.isPending}
className="w-full md:w-[160px]"
>
Create
</Button>
</form>
</Card>
</div>
);
};
export default GetStartedPage;
+11
View File
@@ -0,0 +1,11 @@
import { PageContext } from "vike/types";
import trpcServer from "~/server/api/trpc/trpc";
export const data = async (ctx: PageContext) => {
const trpc = await trpcServer(ctx);
const presets = await trpc.project.getTemplates();
return { presets };
};
export type Data = Awaited<ReturnType<typeof data>>;