diff --git a/src/components/form/text-field.tsx b/src/components/form/text-field.tsx index 24d8b06..9fc6297 100644 --- a/src/components/form/text-field.tsx +++ b/src/components/form/text-field.tsx @@ -23,7 +23,19 @@ export default function TextField({ icon?: React.ReactNode; }) { const field = useFieldContext(); - const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid; + + // Collect errors from both the standard errors array and errorMap + const standardErrors = field.state.meta.errors; + const errorMapErrors = Object.values(field.state.meta.errorMap).filter( + (error): error is string => typeof error === "string" + ); + const allErrors = [ + ...standardErrors, + ...errorMapErrors.map((msg) => ({ message: msg })), + ]; + + const isInvalid = allErrors.length > 0; + return ( {label} @@ -39,7 +51,7 @@ export default function TextField({ value={field.state.value as string} /> - {isInvalid && } + {isInvalid && } {description && {description}} ); diff --git a/src/features/auth/api/auth-actions.ts b/src/features/auth/api/auth-actions.ts index be1869d..4295b63 100644 --- a/src/features/auth/api/auth-actions.ts +++ b/src/features/auth/api/auth-actions.ts @@ -1,12 +1,25 @@ import { createServerFn } from "@tanstack/react-start"; import { getRequestHeaders } from "@tanstack/react-start/server"; +import { eq } from "drizzle-orm"; +import { z } from "zod"; +import { user } from "@/db/schema/auth"; import { auth } from "@/lib/server/auth"; +import { db } from "@/lib/server/db"; import { authOrRedirectMiddleware } from "@/lib/server/middleware"; export const getAuthSessionOrRedirect = createServerFn({ method: "GET" }) .middleware([authOrRedirectMiddleware]) .handler(async ({ context }) => ({ user: context.user })); +export const checkEmailExists = createServerFn({ method: "POST" }) + .inputValidator(z.object({ email: z.email() })) + .handler(async ({ data }) => { + const existingUser = await db.query.user.findFirst({ + where: eq(user.email, data.email), + }); + return { exists: !!existingUser }; + }); + export const getSession = createServerFn({ method: "GET" }).handler( async () => { const session = await auth.api.getSession({ diff --git a/src/features/auth/components/sign-up-form.tsx b/src/features/auth/components/sign-up-form.tsx index e3b914d..84212ff 100644 --- a/src/features/auth/components/sign-up-form.tsx +++ b/src/features/auth/components/sign-up-form.tsx @@ -5,6 +5,7 @@ import { Lock, Mail, User } from "lucide-react"; import React, { useState } from "react"; import { toast } from "sonner"; import { FieldGroup } from "@/components/ui/field"; +import { checkEmailExists } from "@/features/auth/api/auth-actions"; import { authKeys } from "@/features/auth/api/auth-queries"; import { type SignUpSchema, @@ -64,11 +65,28 @@ export default function SignUpForm() { validators: { onSubmit: signUpSchema, }, - onSubmit: ({ value }) => { + onSubmit: async ({ value }) => { if (isPending) { return; } + const email = (value as SignUpSchema).email; + + if (email) { + const { exists } = await checkEmailExists({ data: { email } }); + + if (exists) { + form.setFieldMeta("email", (meta) => ({ + ...meta, + errorMap: { + ...meta.errorMap, + onSubmit: "This email is already registered", + }, + })); + return; + } + } + signUpMutate(value as SignUpSchema); }, });