From 18634cbf49e01cc57e521134d80a06066c9ef167 Mon Sep 17 00:00:00 2001 From: Koosha Lahouti Date: Mon, 6 Oct 2025 12:52:45 +0330 Subject: [PATCH 1/3] feat: add the language menu in login or regsiter form --- public/locales/en/authentication.json | 5 +- public/locales/fa/authentication.json | 5 +- .../AuthenticationSteps/LanguageSwitcher.tsx | 46 ++++++ .../AuthenticationSteps/LoginRegiserForm.tsx | 132 +++++++++--------- 4 files changed, 122 insertions(+), 66 deletions(-) create mode 100644 src/features/authentication/components/AuthenticationSteps/LanguageSwitcher.tsx diff --git a/public/locales/en/authentication.json b/public/locales/en/authentication.json index 9ce99c6..9265dc5 100644 --- a/public/locales/en/authentication.json +++ b/public/locales/en/authentication.json @@ -8,7 +8,10 @@ "emailIsInvalid": "Email is invalid", "phoneNumberIsInvalid": "Phone number is invalid", "thisFieldIsRequired": "This field is required", - "googleAuthenticationFailed": "Login with google failed" + "googleAuthenticationFailed": "Login with google failed", + "persian": "Persian(Fa)", + "english": "English(En)", + "accountInfo": "Harmony Account - 2025" }, "verify": { "verify": "Verify", diff --git a/public/locales/fa/authentication.json b/public/locales/fa/authentication.json index 01591a6..5c09ec3 100644 --- a/public/locales/fa/authentication.json +++ b/public/locales/fa/authentication.json @@ -8,7 +8,10 @@ "emailIsInvalid": "ایمیل وارد شده نامعتبر میباشد", "phoneNumberIsInvalid": "شماره وارد شده نامعتبر میباشد", "thisFieldIsRequired": "این فیلد الزامی است", - "googleAuthenticationFailed": "ورود با گوگل با خطا مواجه شد" + "googleAuthenticationFailed": "ورود با گوگل با خطا مواجه شد", + "persian": "فارسی(Fa)", + "english": "انگلیسی(En)", + "accountInfo": "۱۴۰۴-هارمونی اکانت" }, "verify": { "verify": "اعتبارسنجی", diff --git a/src/features/authentication/components/AuthenticationSteps/LanguageSwitcher.tsx b/src/features/authentication/components/AuthenticationSteps/LanguageSwitcher.tsx new file mode 100644 index 0000000..b751c09 --- /dev/null +++ b/src/features/authentication/components/AuthenticationSteps/LanguageSwitcher.tsx @@ -0,0 +1,46 @@ +import { Box, Typography, MenuItem, Select, Stack } from '@mui/material'; +import { Global } from 'iconsax-react'; +import { useTranslation } from 'react-i18next'; + +export default function LanguageAccountBar() { + const { t, i18n } = useTranslation('authentication'); + + const handleChange = (event: any) => { + const lang = event.target.value; + i18n.changeLanguage(lang); + document.body.dir = lang === 'fa' ? 'rtl' : 'ltr'; + }; + + return ( + + + + + + + + {t('loginForm.accountInfo')} + + + ); +} diff --git a/src/features/authentication/components/AuthenticationSteps/LoginRegiserForm.tsx b/src/features/authentication/components/AuthenticationSteps/LoginRegiserForm.tsx index 5a7e42b..0d9a792 100644 --- a/src/features/authentication/components/AuthenticationSteps/LoginRegiserForm.tsx +++ b/src/features/authentication/components/AuthenticationSteps/LoginRegiserForm.tsx @@ -15,6 +15,7 @@ import { useApi } from '@/hooks/useApi'; import type { GenerateTokenResponse } from '../../api/identityAPI'; import { GoogleAuthenticationV2 } from './GoogleAuthenticationV2'; import { replacePersianWithRealNumbers } from '@/utils/replacePersianWithRealNumbers'; +import LanguageAccountBar from './LanguageSwitcher'; export interface LoginRegisterFormProps { loginRegisterValue: string; @@ -143,72 +144,75 @@ export function LoginRegisterForm({ const showAdornment = authType === 'phone' && loginRegisterValue.length > 0; return ( - - { - e.preventDefault(); - e.stopPropagation(); - if (!userStatusLoading) { - void handleSubmit(); - } - }} - > - - {t('loginForm.title')} - - {t('loginForm.description')} - - - - - ), - startAdornment: i18n.dir() === 'ltr' && ( - - ), - }, + <> + + { + e.preventDefault(); + e.stopPropagation(); + if (!userStatusLoading) { + void handleSubmit(); + } }} - sx={{ my: 4 }} - /> + > + + {t('loginForm.title')} + + {t('loginForm.description')} + + - - - - + ), + startAdornment: i18n.dir() === 'ltr' && ( + + ), + }, + }} + sx={{ my: 4 }} /> - - - + + + + + + + + + + ); } From 9e42f036e494a9b615f56e555439a24e04472f05 Mon Sep 17 00:00:00 2001 From: Koosha Lahouti Date: Mon, 6 Oct 2025 15:46:31 +0330 Subject: [PATCH 2/3] fix: show number keyboard in mobile in otp forms --- src/components/DigitsInput.tsx | 39 ++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/components/DigitsInput.tsx b/src/components/DigitsInput.tsx index f009b82..d38f67f 100644 --- a/src/components/DigitsInput.tsx +++ b/src/components/DigitsInput.tsx @@ -25,6 +25,10 @@ const DigitInput: React.FC = ({ const { i18n } = useTranslation(); const inputRefs = useRef>([]); + const isMobile = + typeof navigator !== 'undefined' && + /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent); + useEffect(() => { inputRefs.current[0]?.focus(); }, []); @@ -44,7 +48,7 @@ const DigitInput: React.FC = ({ setCode(newCode); handleDigitInputValueChange(newCode); - if (value && index < 4 - 1) { + if (value && index < newCode.length - 1) { inputRefs.current[index + 1]?.focus(); } }; @@ -53,37 +57,33 @@ const DigitInput: React.FC = ({ event: KeyboardEvent, index: number, ) => { - event.preventDefault(); - if (index >= 0) { + if (event.key === 'Backspace' && code[index]) { + event.preventDefault(); handleChange('', index); - inputRefs.current[index - 1]?.focus(); + if (index > 0) inputRefs.current[index - 1]?.focus(); } }; const handlePaste = (event: React.ClipboardEvent) => { event.preventDefault(); - const pastedData = event.clipboardData.getData('text').replace(/\D/g, ''); // Remove non-digit characters + const pastedData = event.clipboardData.getData('text').replace(/\D/g, ''); const newCode = [...code]; pastedData.split('').forEach((digit, i) => { - if (i < code.length) { - newCode[i] = digit; - } + if (i < newCode.length) newCode[i] = digit; }); setCode(newCode); handleDigitInputValueChange(newCode); - // Focus the next empty input after the last pasted character - const lastIndex = Math.min(pastedData.length, code.length) - 1; - if (lastIndex >= 0 && inputRefs.current[lastIndex]) { - inputRefs.current[lastIndex]?.focus(); - } + const nextEmpty = newCode.findIndex((d) => d === ''); + const focusIndex = nextEmpty === -1 ? newCode.length - 1 : nextEmpty; + inputRefs.current[focusIndex]?.focus(); }; return ( = ({ autoFocus={index === 0} value={digit} onChange={(e) => handleChange(e.target.value, index)} - onKeyDown={(e) => e.key === 'Backspace' && handleBackspace(e, index)} - onPaste={(e) => handlePaste(e)} + onKeyDown={(e) => handleBackspace(e, index)} + onPaste={handlePaste} + type={isMobile ? 'tel' : 'text'} slotProps={{ htmlInput: { maxLength: 1, + inputMode: isMobile ? 'numeric' : undefined, + pattern: isMobile ? '[0-9]*' : undefined, + autoComplete: 'one-time-code', + name: 'one-time-code', + enterKeyHint: 'done', + dir: 'ltr', sx: { height: '72px', color: error From 38b3b470d3e8f98c5c15737069ff67333e6aa2aa Mon Sep 17 00:00:00 2001 From: Koosha Lahouti Date: Tue, 7 Oct 2025 08:29:31 +0330 Subject: [PATCH 3/3] fix: numeric keyboard in mobile for otp forms --- .../components/UserCompletionForm/EmailSection.tsx | 13 ++++++++----- src/features/profile/api/settingsApi.ts | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/features/authentication/components/UserCompletionForm/EmailSection.tsx b/src/features/authentication/components/UserCompletionForm/EmailSection.tsx index 9eed37b..702901e 100644 --- a/src/features/authentication/components/UserCompletionForm/EmailSection.tsx +++ b/src/features/authentication/components/UserCompletionForm/EmailSection.tsx @@ -214,13 +214,9 @@ export function EmailSection(props: EmailSectionProps) { handleBlur('verificationCode')} error={touched.verificationCode && !!errors.verificationCode} @@ -229,6 +225,13 @@ export function EmailSection(props: EmailSectionProps) { inputMode: 'numeric', pattern: '[0-9]*', maxLength: 4, + autoComplete: 'one-time-code', + name: 'one-time-code', + dir: 'ltr', + }} + sx={{ + flex: '1 1 260px', + '& .MuiOutlinedInput-root': { height: 56 }, }} /> diff --git a/src/features/profile/api/settingsApi.ts b/src/features/profile/api/settingsApi.ts index f840d7f..7213cd9 100644 --- a/src/features/profile/api/settingsApi.ts +++ b/src/features/profile/api/settingsApi.ts @@ -48,7 +48,7 @@ export async function saveProfile(payload: { export async function sendVerificationCode(payload: { phoneNumber: string }) { return apiClient.post( - '/Profile/SendVerfiyPhoneNumberCode', + '/Profile/SendChangePhoneNumberCode', payload, ); }