From fa0bbe64fb6a390d3a5a86389c635e5b37c49d72 Mon Sep 17 00:00:00 2001 From: Karli Date: Wed, 10 Sep 2025 06:34:05 +0300 Subject: [PATCH] update account form for email login --- .../_components/update-account-form.tsx | 63 +++++++++++++------ .../_lib/schemas/update-account.schema.ts | 26 +++++--- .../_lib/server/update-account.ts | 9 +-- .../_components/account-settings-form.tsx | 14 +++-- .../settings/_lib/account-settings.schema.ts | 4 +- .../server/schema/create-company.schema.ts | 2 +- packages/shared/src/utils.ts | 2 +- public/locales/en/account.json | 5 +- public/locales/en/common.json | 4 +- public/locales/et/account.json | 5 +- public/locales/et/common.json | 4 +- public/locales/ru/account.json | 5 +- public/locales/ru/common.json | 5 ++ 13 files changed, 102 insertions(+), 46 deletions(-) diff --git a/app/auth/update-account/_components/update-account-form.tsx b/app/auth/update-account/_components/update-account-form.tsx index 0741489..a657755 100644 --- a/app/auth/update-account/_components/update-account-form.tsx +++ b/app/auth/update-account/_components/update-account-form.tsx @@ -1,6 +1,9 @@ 'use client'; import Link from 'next/link'; +import { useTranslation } from 'react-i18next'; +import { useRouter } from 'next/navigation'; +import { z } from 'zod'; import { ExternalLink } from '@/public/assets/external-link'; import { zodResolver } from '@hookform/resolvers/zod'; @@ -21,9 +24,10 @@ import { Trans } from '@kit/ui/trans'; import { UpdateAccountSchemaClient } from '../_lib/schemas/update-account.schema'; import { onUpdateAccount } from '../_lib/server/update-account'; -import { z } from 'zod'; +import { toast } from '@kit/ui/sonner'; +import { pathsConfig } from '@/packages/shared/src/config'; -type UpdateAccountFormValues = z.infer; +type UpdateAccountFormValues = z.infer>; export function UpdateAccountForm({ defaultValues, @@ -32,33 +36,56 @@ export function UpdateAccountForm({ defaultValues: UpdateAccountFormValues, isEmailUser: boolean, }) { + const router = useRouter(); + const { t } = useTranslation('account'); + const form = useForm({ - resolver: zodResolver(UpdateAccountSchemaClient), + resolver: zodResolver(UpdateAccountSchemaClient({ isEmailUser })), mode: 'onChange', defaultValues, }); - const { firstName, lastName, personalCode, email, weight, height, userConsent } = defaultValues; + const { firstName, lastName, personalCode, email, userConsent } = defaultValues; + + const defaultValues_weight = "weight" in defaultValues ? defaultValues.weight : null; + const defaultValues_height = "height" in defaultValues ? defaultValues.height : null; const hasFirstName = !!firstName; const hasLastName = !!lastName; const hasPersonalCode = !!personalCode; const hasEmail = !!email; - const hasWeight = !!weight; - const hasHeight = !!height; - const onUpdateAccountOptions = async (values: UpdateAccountFormValues) => - onUpdateAccount({ - firstName: hasFirstName ? firstName : values.firstName, - lastName: hasLastName ? lastName : values.lastName, - personalCode: hasPersonalCode ? personalCode : values.personalCode, - email: hasEmail ? email : values.email, - phone: values.phone, - weight: (hasWeight ? weight : values.weight) as number, - height: (hasHeight ? height : values.height) as number, - userConsent: values.userConsent ?? userConsent, - city: values.city, - }); + const onUpdateAccountOptions = async (values: UpdateAccountFormValues) => { + const loading = toast.loading(t('updateAccount.updateAccountLoading')); + try { + const response = await onUpdateAccount({ + firstName: hasFirstName ? firstName : values.firstName, + lastName: hasLastName ? lastName : values.lastName, + personalCode: hasPersonalCode ? personalCode : values.personalCode, + email: hasEmail ? email : values.email, + phone: values.phone, + weight: ((("weight" in values && values.weight) ?? defaultValues_weight) || null) as number, + height: ((("height" in values && values.height) ?? defaultValues_height) || null) as number, + userConsent: values.userConsent ?? userConsent, + city: values.city, + }); + if (!response) { + throw new Error('Failed to update account'); + } + toast.dismiss(loading); + toast.success(t('updateAccount.updateAccountSuccess')); + + if (response.hasUnseenMembershipConfirmation) { + router.push(pathsConfig.auth.membershipConfirmation); + } else { + router.push(pathsConfig.app.selectPackage); + } + } catch (error) { + console.info("promiseresult error", error); + toast.error(t('updateAccount.updateAccountError')); + toast.dismiss(loading); + } + }; return (
diff --git a/app/auth/update-account/_lib/schemas/update-account.schema.ts b/app/auth/update-account/_lib/schemas/update-account.schema.ts index ad59e59..bba388c 100644 --- a/app/auth/update-account/_lib/schemas/update-account.schema.ts +++ b/app/auth/update-account/_lib/schemas/update-account.schema.ts @@ -12,7 +12,9 @@ const updateAccountSchema = { .string({ error: 'Last name is required', }) - .nonempty(), + .nonempty({ + error: 'common:formFieldError.stringNonEmpty', + }), personalCode: z.string().refine( (val) => { try { @@ -30,7 +32,7 @@ const updateAccountSchema = { }), phone: z .string({ - error: 'Phone number is required', + error: 'error:invalidPhone', }) .nonempty() .refine( @@ -75,18 +77,26 @@ export const UpdateAccountSchemaServer = z.object({ email: updateAccountSchema.email, phone: updateAccountSchema.phone, city: updateAccountSchema.city, - weight: updateAccountSchema.weight, - height: updateAccountSchema.height, + weight: updateAccountSchema.weight.nullable(), + height: updateAccountSchema.height.nullable(), userConsent: updateAccountSchema.userConsent, }); -export const UpdateAccountSchemaClient = z.object({ +export const UpdateAccountSchemaClient = ({ isEmailUser }: { isEmailUser: boolean }) => z.object({ firstName: updateAccountSchema.firstName, lastName: updateAccountSchema.lastName, personalCode: updateAccountSchema.personalCode, email: updateAccountSchema.email, phone: updateAccountSchema.phone, - city: updateAccountSchema.city, - weight: updateAccountSchema.weight.gt(-1).gte(0).nullable(), - height: updateAccountSchema.height.gt(-1).gte(0).nullable(), + ...(isEmailUser + ? { + city: z.string().optional(), + weight: z.number().optional(), + height: z.number().optional(), + } + : { + city: updateAccountSchema.city, + weight: updateAccountSchema.weight, + height: updateAccountSchema.height, + }), userConsent: updateAccountSchema.userConsent, }); diff --git a/app/auth/update-account/_lib/server/update-account.ts b/app/auth/update-account/_lib/server/update-account.ts index 7e70139..07e415b 100644 --- a/app/auth/update-account/_lib/server/update-account.ts +++ b/app/auth/update-account/_lib/server/update-account.ts @@ -1,7 +1,5 @@ 'use server'; -import { redirect } from 'next/navigation'; - import { updateCustomer } from '@lib/data/customer'; import { AccountSubmitData, createAuthApi } from '@kit/auth/api'; @@ -39,11 +37,8 @@ export const onUpdateAccount = enhanceAction( const hasUnseenMembershipConfirmation = await api.hasUnseenMembershipConfirmation(); - - if (hasUnseenMembershipConfirmation) { - redirect(pathsConfig.auth.membershipConfirmation); - } else { - redirect(pathsConfig.app.selectPackage); + return { + hasUnseenMembershipConfirmation, } }, { diff --git a/app/home/(user)/settings/_components/account-settings-form.tsx b/app/home/(user)/settings/_components/account-settings-form.tsx index 95db6cd..8513798 100644 --- a/app/home/(user)/settings/_components/account-settings-form.tsx +++ b/app/home/(user)/settings/_components/account-settings-form.tsx @@ -7,7 +7,6 @@ import { Trans } from 'react-i18next'; import { AccountWithParams } from '@kit/accounts/api'; import { useRevalidatePersonalAccountDataQuery } from '@kit/accounts/hooks/use-personal-account-data'; import { Button } from '@kit/ui/button'; -import { Card, CardTitle } from '@kit/ui/card'; import { Form, FormControl, @@ -25,7 +24,6 @@ import { SelectValue, } from '@kit/ui/select'; import { toast } from '@kit/ui/sonner'; -import { Switch } from '@kit/ui/switch'; import { AccountSettings, @@ -131,7 +129,11 @@ export default function AccountSettingsForm({ - + @@ -150,7 +152,11 @@ export default function AccountSettingsForm({ - + diff --git a/app/home/(user)/settings/_lib/account-settings.schema.ts b/app/home/(user)/settings/_lib/account-settings.schema.ts index a6944a4..8c3cece 100644 --- a/app/home/(user)/settings/_lib/account-settings.schema.ts +++ b/app/home/(user)/settings/_lib/account-settings.schema.ts @@ -12,8 +12,8 @@ export const accountSettingsSchema = z.object({ email: z.email({ error: 'error:invalidEmail' }).nullable(), phone: z.e164({ error: 'error:invalidPhone' }), accountParams: z.object({ - height: z.coerce.number({ error: 'error:invalidNumber' }), - weight: z.coerce.number({ error: 'error:invalidNumber' }), + height: z.coerce.number({ error: 'error:invalidNumber' }).gt(0), + weight: z.coerce.number({ error: 'error:invalidNumber' }).gt(0), isSmoker: z.boolean().optional().nullable(), }), }); diff --git a/packages/features/admin/src/lib/server/schema/create-company.schema.ts b/packages/features/admin/src/lib/server/schema/create-company.schema.ts index 42ef6cb..c2d33cf 100644 --- a/packages/features/admin/src/lib/server/schema/create-company.schema.ts +++ b/packages/features/admin/src/lib/server/schema/create-company.schema.ts @@ -10,7 +10,7 @@ const personalCodeSchema = z.string().refine( } }, { - message: 'Invalid personal code', + message: 'common:formFieldError.invalidPersonalCode', }, ); diff --git a/packages/shared/src/utils.ts b/packages/shared/src/utils.ts index 971a03e..877cbca 100644 --- a/packages/shared/src/utils.ts +++ b/packages/shared/src/utils.ts @@ -1,5 +1,5 @@ import { format } from 'date-fns'; -import Isikukood, { Gender } from 'isikukood'; +import Isikukood from 'isikukood'; /** * Check if the code is running in a browser environment. diff --git a/public/locales/en/account.json b/public/locales/en/account.json index 8e7020c..2872ede 100644 --- a/public/locales/en/account.json +++ b/public/locales/en/account.json @@ -130,7 +130,10 @@ "description": "Please enter your personal details to continue", "button": "Continue", "userConsentLabel": "I agree to the use of personal data on the platform", - "userConsentUrlTitle": "View privacy policy" + "userConsentUrlTitle": "View privacy policy", + "updateAccountLoading": "Updating account details...", + "updateAccountSuccess": "Account details updated", + "updateAccountError": "Updating account details error" }, "consentModal": { "title": "Before we start", diff --git a/public/locales/en/common.json b/public/locales/en/common.json index b26211f..cf41acd 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -129,7 +129,9 @@ "selectDate": "Select date" }, "formFieldError": { - "invalidPhoneNumber": "Please enter a valid Estonian phone number (must include country code +372)" + "invalidPhoneNumber": "Please enter a valid Estonian phone number (must include country code +372)", + "invalidPersonalCode": "Please enter a valid Estonian personal code", + "stringNonEmpty": "This field is required" }, "wallet": { "balance": "Your MedReport account balance", diff --git a/public/locales/et/account.json b/public/locales/et/account.json index e3e824c..86b36e3 100644 --- a/public/locales/et/account.json +++ b/public/locales/et/account.json @@ -130,7 +130,10 @@ "description": "Jätkamiseks palun sisestage enda isikuandmed", "button": "Jätka", "userConsentLabel": "Nõustun isikuandmete kasutamisega platvormil", - "userConsentUrlTitle": "Vaata isikuandmete töötlemise põhimõtteid" + "userConsentUrlTitle": "Vaata isikuandmete töötlemise põhimõtteid", + "updateAccountLoading": "Konto andmed uuendatakse...", + "updateAccountSuccess": "Konto andmed uuendatud", + "updateAccountError": "Konto andmete uuendamine ebaõnnestus" }, "consentModal": { "title": "Enne alustamist", diff --git a/public/locales/et/common.json b/public/locales/et/common.json index 792cc3a..485c009 100644 --- a/public/locales/et/common.json +++ b/public/locales/et/common.json @@ -129,7 +129,9 @@ "selectDate": "Vali kuupäev" }, "formFieldError": { - "invalidPhoneNumber": "Palun sisesta Eesti telefoninumber (peab sisaldama riigikoodi +372)" + "invalidPhoneNumber": "Palun sisesta Eesti telefoninumber (peab sisaldama riigikoodi +372)", + "invalidPersonalCode": "Palun sisesta Eesti isikukood", + "stringNonEmpty": "See väli on kohustuslik" }, "wallet": { "balance": "Sinu MedReporti konto saldo", diff --git a/public/locales/ru/account.json b/public/locales/ru/account.json index 9bfae35..bb3785b 100644 --- a/public/locales/ru/account.json +++ b/public/locales/ru/account.json @@ -130,7 +130,10 @@ "description": "Пожалуйста, введите личные данные для продолжения", "button": "Продолжить", "userConsentLabel": "Я согласен на использование персональных данных на платформе", - "userConsentUrlTitle": "Посмотреть политику конфиденциальности" + "userConsentUrlTitle": "Посмотреть политику конфиденциальности", + "updateAccountLoading": "Обновление данных аккаунта...", + "updateAccountSuccess": "Данные аккаунта обновлены", + "updateAccountError": "Не удалось обновить данные аккаунта" }, "consentModal": { "title": "Перед началом", diff --git a/public/locales/ru/common.json b/public/locales/ru/common.json index 545b9e6..28b5d6b 100644 --- a/public/locales/ru/common.json +++ b/public/locales/ru/common.json @@ -128,6 +128,11 @@ "amount": "Сумма", "selectDate": "Выберите дату" }, + "formFieldError": { + "invalidPhoneNumber": "Пожалуйста, введите действительный номер телефона (должен включать код страны +372)", + "invalidPersonalCode": "Пожалуйста, введите действительный персональный код", + "stringNonEmpty": "Это поле обязательно" + }, "wallet": { "balance": "Баланс вашего счета MedReport", "expiredAt": "Действительно до {{expiredAt}}"