diff --git a/apps/web/package.json b/apps/web/package.json index 7c1a7a42b..7708635a8 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -81,6 +81,7 @@ "nanoid": "^5.0.9", "next": "^15.3.1", "next-auth": "^5.0.0-beta.25", + "next-safe-action": "^8.0.7", "php-serialize": "^4.1.1", "postcss": "^8.4.31", "react": "^19.1.0", diff --git a/apps/web/src/features/setup/actions.ts b/apps/web/src/features/setup/actions.ts index f1c17858e..387d424a3 100644 --- a/apps/web/src/features/setup/actions.ts +++ b/apps/web/src/features/setup/actions.ts @@ -5,95 +5,38 @@ import { posthog } from "@rallly/posthog/server"; import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; -import { getTranslation } from "@/i18n/server"; -import { auth } from "@/next-auth"; - +import { authActionClient } from "@/safe-action"; import { setupSchema } from "./schema"; -export type SetupFormState = { - message?: string | null; - errors?: { - name?: string[]; - timeZone?: string[]; - locale?: string[]; - }; -}; +export const updateUserAction = authActionClient + .inputSchema(setupSchema) + .action(async ({ parsedInput, ctx }) => { + const { name, timeZone, locale } = parsedInput; -export async function updateUserSetup( - formData: FormData, -): Promise { - const { t } = await getTranslation(); - const session = await auth(); - - if (!session?.user?.id) { - return { - message: t("errorNotAuthenticated", { - defaultValue: "Not authenticated", - }), - }; - } - - const validatedFields = setupSchema.safeParse({ - name: formData.get("name"), - timeZone: formData.get("timeZone"), - locale: formData.get("locale"), - }); - - if (!validatedFields.success) { - const errors = validatedFields.error.flatten().fieldErrors; - const translatedErrors = Object.entries(errors).reduce( - (acc, [key, value]) => { - acc[key as keyof typeof errors] = value?.map((errKey) => - t(errKey, { defaultValue: `Invalid ${key}` }), - ); - return acc; - }, - {} as Required["errors"], - ); - - return { - errors: translatedErrors, - message: t("errorInvalidFields", { - defaultValue: "Invalid fields. Please check your input.", - }), - }; - } - - const { name, timeZone, locale } = validatedFields.data; - - try { await prisma.user.update({ - where: { id: session.user.id }, + where: { id: ctx.user.id }, data: { name, timeZone, locale, }, }); - } catch (error) { - console.error("Failed to update user setup:", error); - return { - message: t("errorDatabaseUpdateFailed", { - defaultValue: "Database error: Failed to update settings.", - }), - }; - } - posthog?.capture({ - event: "user_setup_completed", - distinctId: session.user.id, - properties: { - $set: { - name, - timeZone, - locale, + posthog?.capture({ + event: "user_setup_completed", + distinctId: ctx.user.id, + properties: { + $set: { + name, + timeZone, + locale, + }, }, - }, + }); + + await posthog?.shutdown(); + + revalidatePath("/", "layout"); + + redirect("/"); }); - - await posthog?.shutdown(); - - revalidatePath("/", "layout"); - - redirect("/"); -} diff --git a/apps/web/src/features/setup/components/setup-form.tsx b/apps/web/src/features/setup/components/setup-form.tsx index d4553b9d8..383a41a98 100644 --- a/apps/web/src/features/setup/components/setup-form.tsx +++ b/apps/web/src/features/setup/components/setup-form.tsx @@ -11,7 +11,6 @@ import { FormMessage, } from "@rallly/ui/form"; import { Input } from "@rallly/ui/input"; -import * as React from "react"; import { useForm } from "react-hook-form"; import { LanguageSelect } from "@/components/poll/language-selector"; @@ -19,8 +18,9 @@ import { TimeZoneSelect } from "@/components/time-zone-picker/time-zone-select"; import { Trans } from "@/components/trans"; import { useTimezone } from "@/features/timezone"; import { useTranslation } from "@/i18n/client"; +import { useAction } from "next-safe-action/hooks"; -import { updateUserSetup } from "../actions"; +import { updateUserAction } from "../actions"; import { type SetupFormValues, setupSchema } from "../schema"; interface SetupFormProps { @@ -30,8 +30,8 @@ interface SetupFormProps { export function SetupForm({ defaultValues }: SetupFormProps) { const { timezone } = useTimezone(); const { i18n } = useTranslation(); - const [isSubmitting, setIsSubmitting] = React.useState(false); - const [serverError, setServerError] = React.useState(null); + const userSetupAction = useAction(updateUserAction); + const form = useForm({ resolver: zodResolver(setupSchema), defaultValues: { @@ -41,33 +41,13 @@ export function SetupForm({ defaultValues }: SetupFormProps) { }, }); - async function onSubmit(data: SetupFormValues) { - setIsSubmitting(true); - setServerError(null); - - // Construct FormData for the server action - const formData = new FormData(); - formData.append("name", data.name); - formData.append("timeZone", data.timeZone); - formData.append("locale", data.locale); - - const result = await updateUserSetup(formData); - - setIsSubmitting(false); - - if (result?.message) { - setServerError(result.message); - } - } - return (
- - {serverError && ( -

- {serverError} -

- )} + { + await userSetupAction.executeAsync(data); + })} + >
)} /> + {userSetupAction.result.serverError && ( + {userSetupAction.result.serverError} + )}