From 45110d05b5287600a88ef27df0f8ff3d23c2540f Mon Sep 17 00:00:00 2001 From: Kenneth Date: Tue, 16 Dec 2025 00:48:06 +0000 Subject: [PATCH] feat: impl sign up page --- apps/drive-web/src/auth/api.ts | 35 ++++- apps/drive-web/src/routes/login.tsx | 3 +- apps/drive-web/src/routes/sign-up.tsx | 190 ++++++++++++-------------- 3 files changed, 119 insertions(+), 109 deletions(-) diff --git a/apps/drive-web/src/auth/api.ts b/apps/drive-web/src/auth/api.ts index 1b4bb74..bfa8d0c 100644 --- a/apps/drive-web/src/auth/api.ts +++ b/apps/drive-web/src/auth/api.ts @@ -1,14 +1,20 @@ import { mutationOptions } from "@tanstack/react-query" import { type } from "arktype" -import { accountsQuery } from "../account/api" -import { fetchApi } from "../lib/api" -import { currentUserQuery } from "../user/api" -import { User } from "../user/user" +import { Account } from "@/account/account" +import { accountsQuery } from "@/account/api" +import { fetchApi } from "@/lib/api" +import { currentUserQuery } from "@/user/api" +import { User } from "@/user/user" const LoginResponseSchema = type({ user: User, }) +const SignUpResponse = type({ + account: Account, + user: User, +}) + export const loginMutation = mutationOptions({ mutationFn: async (data: { email: string; password: string }) => { const [_, result] = await fetchApi("POST", "/auth/login", { @@ -25,3 +31,24 @@ export const loginMutation = mutationOptions({ context.client.invalidateQueries(accountsQuery) }, }) + +export const signUpMutation = mutationOptions({ + mutationFn: async (data: { + displayName: string + email: string + password: string + }) => { + const [_, result] = await fetchApi("POST", "/accounts", { + body: JSON.stringify({ + ...data, + tokenDelivery: "cookie", + }), + returns: SignUpResponse, + }) + return result + }, + onSuccess: (data, _, __, context) => { + context.client.setQueryData(currentUserQuery.queryKey, data.user) + context.client.setQueryData(accountsQuery.queryKey, [data.account]) + }, +}) diff --git a/apps/drive-web/src/routes/login.tsx b/apps/drive-web/src/routes/login.tsx index f8a0cc9..6ef8475 100644 --- a/apps/drive-web/src/routes/login.tsx +++ b/apps/drive-web/src/routes/login.tsx @@ -165,7 +165,8 @@ function LoginForm() { {isPending ? "Logging in…" : "Login"} - Don't have an account? Sign up + Don't have an account?{" "} + Sign up diff --git a/apps/drive-web/src/routes/sign-up.tsx b/apps/drive-web/src/routes/sign-up.tsx index 5f50fde..e31a786 100644 --- a/apps/drive-web/src/routes/sign-up.tsx +++ b/apps/drive-web/src/routes/sign-up.tsx @@ -3,6 +3,7 @@ import { createFileRoute, useNavigate } from "@tanstack/react-router" import { GalleryVerticalEnd } from "lucide-react" import type React from "react" import { toast } from "sonner" +import { signUpMutation } from "@/auth/api" import { Button } from "@/components/ui/button" import { Card, @@ -14,34 +15,21 @@ import { import { Field, FieldDescription, - FieldError, FieldGroup, FieldLabel, + FieldSeparator, } from "@/components/ui/field" import { Input } from "@/components/ui/input" -import { type AuthErrorCode, authClient, BetterAuthError } from "../auth" +import { cn } from "@/lib/utils" export const Route = createFileRoute("/sign-up")({ - component: SignupPage, + component: RouteComponent, }) -type SignUpParams = { - displayName: string - email: string - password: string - confirmPassword: string -} - -class PasswordMismatchError extends Error { - constructor() { - super("Passwords do not match") - } -} - -function SignupPage() { +function RouteComponent() { return (
-
+
Drexa - - - +
) } -function SignUpFormContainer({ children }: React.PropsWithChildren) { +function SignupFormCard({ className, ...props }: React.ComponentProps<"div">) { return ( -
+
- - Create your account - + Create an account - Enter your email below to create your account + Enter your information below to create your account - {children} + + + + + By clicking continue, you agree to our{" "} + Terms of Service and{" "} + Privacy Policy. +
) } @@ -84,20 +75,7 @@ function SignupForm() { isPending, error: signUpError, } = useMutation({ - mutationFn: async (data: SignUpParams) => { - if (data.password !== data.confirmPassword) { - throw new PasswordMismatchError() - } - const { data: signUpData, error } = await authClient.signUp.email({ - name: data.displayName, - email: data.email, - password: data.password, - }) - if (error) { - throw new BetterAuthError(error.code as AuthErrorCode) - } - return signUpData - }, + ...signUpMutation, onSuccess: () => { navigate({ to: "/", @@ -117,43 +95,62 @@ function SignupForm() { displayName: formData.get("displayName") as string, email: formData.get("email") as string, password: formData.get("password") as string, - confirmPassword: formData.get("confirmPassword") as string, }) } - let passwordFieldError = null - let emailFieldError = null - if (signUpError instanceof BetterAuthError) { - switch (signUpError.errorCode) { - case "PASSWORD_TOO_SHORT": - passwordFieldError = - "Password must be at least 8 characters long" - break - case "INVALID_EMAIL": - emailFieldError = "Invalid email address" - break - default: - passwordFieldError = null - } - } else if (signUpError instanceof PasswordMismatchError) { - passwordFieldError = "Passwords do not match" - } - return (
- Your Name + + + + + Or continue with + + + Display Name - This is how you will be referred to on Drexa. You can - change this later. + This will be displayed in the app and to other users. + Can be any name of your choice. @@ -165,51 +162,36 @@ function SignupForm() { placeholder="m@example.com" required /> - {emailFieldError ? ( - {emailFieldError} - ) : null} - - - Password - - - - - Confirm Password - - - - - {passwordFieldError ? ( - {passwordFieldError} - ) : ( - - Must be at least 8 characters long. - - )} - - - - Already have an account? Sign in + Already have an account? Sign in