update account form for email login

This commit is contained in:
2025-09-10 06:34:05 +03:00
parent e3cdba6a7c
commit fa0bbe64fb
13 changed files with 102 additions and 46 deletions

View File

@@ -1,6 +1,9 @@
'use client'; 'use client';
import Link from 'next/link'; 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 { ExternalLink } from '@/public/assets/external-link';
import { zodResolver } from '@hookform/resolvers/zod'; 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 { UpdateAccountSchemaClient } from '../_lib/schemas/update-account.schema';
import { onUpdateAccount } from '../_lib/server/update-account'; 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<typeof UpdateAccountSchemaClient>; type UpdateAccountFormValues = z.infer<ReturnType<typeof UpdateAccountSchemaClient>>;
export function UpdateAccountForm({ export function UpdateAccountForm({
defaultValues, defaultValues,
@@ -32,33 +36,56 @@ export function UpdateAccountForm({
defaultValues: UpdateAccountFormValues, defaultValues: UpdateAccountFormValues,
isEmailUser: boolean, isEmailUser: boolean,
}) { }) {
const router = useRouter();
const { t } = useTranslation('account');
const form = useForm({ const form = useForm({
resolver: zodResolver(UpdateAccountSchemaClient), resolver: zodResolver(UpdateAccountSchemaClient({ isEmailUser })),
mode: 'onChange', mode: 'onChange',
defaultValues, 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 hasFirstName = !!firstName;
const hasLastName = !!lastName; const hasLastName = !!lastName;
const hasPersonalCode = !!personalCode; const hasPersonalCode = !!personalCode;
const hasEmail = !!email; const hasEmail = !!email;
const hasWeight = !!weight;
const hasHeight = !!height;
const onUpdateAccountOptions = async (values: UpdateAccountFormValues) => const onUpdateAccountOptions = async (values: UpdateAccountFormValues) => {
onUpdateAccount({ const loading = toast.loading(t('updateAccount.updateAccountLoading'));
firstName: hasFirstName ? firstName : values.firstName, try {
lastName: hasLastName ? lastName : values.lastName, const response = await onUpdateAccount({
personalCode: hasPersonalCode ? personalCode : values.personalCode, firstName: hasFirstName ? firstName : values.firstName,
email: hasEmail ? email : values.email, lastName: hasLastName ? lastName : values.lastName,
phone: values.phone, personalCode: hasPersonalCode ? personalCode : values.personalCode,
weight: (hasWeight ? weight : values.weight) as number, email: hasEmail ? email : values.email,
height: (hasHeight ? height : values.height) as number, phone: values.phone,
userConsent: values.userConsent ?? userConsent, weight: ((("weight" in values && values.weight) ?? defaultValues_weight) || null) as number,
city: values.city, 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 ( return (
<Form {...form}> <Form {...form}>

View File

@@ -12,7 +12,9 @@ const updateAccountSchema = {
.string({ .string({
error: 'Last name is required', error: 'Last name is required',
}) })
.nonempty(), .nonempty({
error: 'common:formFieldError.stringNonEmpty',
}),
personalCode: z.string().refine( personalCode: z.string().refine(
(val) => { (val) => {
try { try {
@@ -30,7 +32,7 @@ const updateAccountSchema = {
}), }),
phone: z phone: z
.string({ .string({
error: 'Phone number is required', error: 'error:invalidPhone',
}) })
.nonempty() .nonempty()
.refine( .refine(
@@ -75,18 +77,26 @@ export const UpdateAccountSchemaServer = z.object({
email: updateAccountSchema.email, email: updateAccountSchema.email,
phone: updateAccountSchema.phone, phone: updateAccountSchema.phone,
city: updateAccountSchema.city, city: updateAccountSchema.city,
weight: updateAccountSchema.weight, weight: updateAccountSchema.weight.nullable(),
height: updateAccountSchema.height, height: updateAccountSchema.height.nullable(),
userConsent: updateAccountSchema.userConsent, userConsent: updateAccountSchema.userConsent,
}); });
export const UpdateAccountSchemaClient = z.object({ export const UpdateAccountSchemaClient = ({ isEmailUser }: { isEmailUser: boolean }) => z.object({
firstName: updateAccountSchema.firstName, firstName: updateAccountSchema.firstName,
lastName: updateAccountSchema.lastName, lastName: updateAccountSchema.lastName,
personalCode: updateAccountSchema.personalCode, personalCode: updateAccountSchema.personalCode,
email: updateAccountSchema.email, email: updateAccountSchema.email,
phone: updateAccountSchema.phone, phone: updateAccountSchema.phone,
city: updateAccountSchema.city, ...(isEmailUser
weight: updateAccountSchema.weight.gt(-1).gte(0).nullable(), ? {
height: updateAccountSchema.height.gt(-1).gte(0).nullable(), city: z.string().optional(),
weight: z.number().optional(),
height: z.number().optional(),
}
: {
city: updateAccountSchema.city,
weight: updateAccountSchema.weight,
height: updateAccountSchema.height,
}),
userConsent: updateAccountSchema.userConsent, userConsent: updateAccountSchema.userConsent,
}); });

View File

@@ -1,7 +1,5 @@
'use server'; 'use server';
import { redirect } from 'next/navigation';
import { updateCustomer } from '@lib/data/customer'; import { updateCustomer } from '@lib/data/customer';
import { AccountSubmitData, createAuthApi } from '@kit/auth/api'; import { AccountSubmitData, createAuthApi } from '@kit/auth/api';
@@ -39,11 +37,8 @@ export const onUpdateAccount = enhanceAction(
const hasUnseenMembershipConfirmation = const hasUnseenMembershipConfirmation =
await api.hasUnseenMembershipConfirmation(); await api.hasUnseenMembershipConfirmation();
return {
if (hasUnseenMembershipConfirmation) { hasUnseenMembershipConfirmation,
redirect(pathsConfig.auth.membershipConfirmation);
} else {
redirect(pathsConfig.app.selectPackage);
} }
}, },
{ {

View File

@@ -7,7 +7,6 @@ import { Trans } from 'react-i18next';
import { AccountWithParams } from '@kit/accounts/api'; import { AccountWithParams } from '@kit/accounts/api';
import { useRevalidatePersonalAccountDataQuery } from '@kit/accounts/hooks/use-personal-account-data'; import { useRevalidatePersonalAccountDataQuery } from '@kit/accounts/hooks/use-personal-account-data';
import { Button } from '@kit/ui/button'; import { Button } from '@kit/ui/button';
import { Card, CardTitle } from '@kit/ui/card';
import { import {
Form, Form,
FormControl, FormControl,
@@ -25,7 +24,6 @@ import {
SelectValue, SelectValue,
} from '@kit/ui/select'; } from '@kit/ui/select';
import { toast } from '@kit/ui/sonner'; import { toast } from '@kit/ui/sonner';
import { Switch } from '@kit/ui/switch';
import { import {
AccountSettings, AccountSettings,
@@ -131,7 +129,11 @@ export default function AccountSettingsForm({
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input {...field} /> <Input
placeholder="cm"
type="number"
{...field}
/>
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -150,7 +152,11 @@ export default function AccountSettingsForm({
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input {...field} /> <Input
placeholder="kg"
type="number"
{...field}
/>
</FormControl> </FormControl>
<FormMessage /> <FormMessage />

View File

@@ -12,8 +12,8 @@ export const accountSettingsSchema = z.object({
email: z.email({ error: 'error:invalidEmail' }).nullable(), email: z.email({ error: 'error:invalidEmail' }).nullable(),
phone: z.e164({ error: 'error:invalidPhone' }), phone: z.e164({ error: 'error:invalidPhone' }),
accountParams: z.object({ accountParams: z.object({
height: z.coerce.number({ error: 'error:invalidNumber' }), height: z.coerce.number({ error: 'error:invalidNumber' }).gt(0),
weight: z.coerce.number({ error: 'error:invalidNumber' }), weight: z.coerce.number({ error: 'error:invalidNumber' }).gt(0),
isSmoker: z.boolean().optional().nullable(), isSmoker: z.boolean().optional().nullable(),
}), }),
}); });

View File

@@ -10,7 +10,7 @@ const personalCodeSchema = z.string().refine(
} }
}, },
{ {
message: 'Invalid personal code', message: 'common:formFieldError.invalidPersonalCode',
}, },
); );

View File

@@ -1,5 +1,5 @@
import { format } from 'date-fns'; import { format } from 'date-fns';
import Isikukood, { Gender } from 'isikukood'; import Isikukood from 'isikukood';
/** /**
* Check if the code is running in a browser environment. * Check if the code is running in a browser environment.

View File

@@ -130,7 +130,10 @@
"description": "Please enter your personal details to continue", "description": "Please enter your personal details to continue",
"button": "Continue", "button": "Continue",
"userConsentLabel": "I agree to the use of personal data on the platform", "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": { "consentModal": {
"title": "Before we start", "title": "Before we start",

View File

@@ -129,7 +129,9 @@
"selectDate": "Select date" "selectDate": "Select date"
}, },
"formFieldError": { "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": { "wallet": {
"balance": "Your MedReport account balance", "balance": "Your MedReport account balance",

View File

@@ -130,7 +130,10 @@
"description": "Jätkamiseks palun sisestage enda isikuandmed", "description": "Jätkamiseks palun sisestage enda isikuandmed",
"button": "Jätka", "button": "Jätka",
"userConsentLabel": "Nõustun isikuandmete kasutamisega platvormil", "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": { "consentModal": {
"title": "Enne alustamist", "title": "Enne alustamist",

View File

@@ -129,7 +129,9 @@
"selectDate": "Vali kuupäev" "selectDate": "Vali kuupäev"
}, },
"formFieldError": { "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": { "wallet": {
"balance": "Sinu MedReporti konto saldo", "balance": "Sinu MedReporti konto saldo",

View File

@@ -130,7 +130,10 @@
"description": "Пожалуйста, введите личные данные для продолжения", "description": "Пожалуйста, введите личные данные для продолжения",
"button": "Продолжить", "button": "Продолжить",
"userConsentLabel": "Я согласен на использование персональных данных на платформе", "userConsentLabel": "Я согласен на использование персональных данных на платформе",
"userConsentUrlTitle": "Посмотреть политику конфиденциальности" "userConsentUrlTitle": "Посмотреть политику конфиденциальности",
"updateAccountLoading": "Обновление данных аккаунта...",
"updateAccountSuccess": "Данные аккаунта обновлены",
"updateAccountError": "Не удалось обновить данные аккаунта"
}, },
"consentModal": { "consentModal": {
"title": "Перед началом", "title": "Перед началом",

View File

@@ -128,6 +128,11 @@
"amount": "Сумма", "amount": "Сумма",
"selectDate": "Выберите дату" "selectDate": "Выберите дату"
}, },
"formFieldError": {
"invalidPhoneNumber": "Пожалуйста, введите действительный номер телефона (должен включать код страны +372)",
"invalidPersonalCode": "Пожалуйста, введите действительный персональный код",
"stringNonEmpty": "Это поле обязательно"
},
"wallet": { "wallet": {
"balance": "Баланс вашего счета MedReport", "balance": "Баланс вашего счета MedReport",
"expiredAt": "Действительно до {{expiredAt}}" "expiredAt": "Действительно до {{expiredAt}}"