diff --git a/package-lock.json b/package-lock.json
index fcab9b1..d0ad8a6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3150,6 +3150,7 @@
"react": "*"
}
},
+
"node_modules/iconsax-reactjs": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/iconsax-reactjs/-/iconsax-reactjs-0.0.8.tgz",
@@ -3160,6 +3161,7 @@
"react": "*"
}
},
+
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
diff --git a/public/locales/en/profileSetting.json b/public/locales/en/profileSetting.json
new file mode 100644
index 0000000..cbc6846
--- /dev/null
+++ b/public/locales/en/profileSetting.json
@@ -0,0 +1,42 @@
+{
+ "settingForm": {
+ "titlePersonalInfo": "My Personal Information",
+ "descriptionPersonalInfo": "This information is only for your identification and remains with Harmony.",
+ "titlePhoneNumber": "My phone number",
+ "descriptionPhoneNumber": "This information is only for your identification and remains with Harmony.",
+ "titleSocial": "My email and social medias",
+ "descriptionSocial": "This information is only for your identification and remains with Harmony.",
+ "rejectButton": "Cancel",
+ "saveButton": "Save",
+ "editButton": "Edit",
+ "editPhoneNumber": "Change phone number",
+ "addEmailOrSocialButton": "Add email / social",
+ "addEmailButton": "Add email",
+ "name": "Name",
+ "familyName": "Family Name",
+ "country": "Country",
+ "gender": "Gender",
+ "nationalCode": "National code",
+ "man": "Male",
+ "woman": "Female",
+ "genderPlaceholder": "Male",
+ "newPhoneNumber": "New phone number",
+ "verificationCodeButton": "Send verification code",
+ "verificationCode": "Verification code",
+ "checkCode": "Check code",
+ "successButton": "Confirmed",
+ "email": "Email",
+ "apple": "Apple",
+ "google": "Google",
+ "newEmail": "New email",
+ "dialogHeader": "By activating your email, you can use this email to log in the next time you log in.",
+ "or": "Or",
+ "emailError": "Please enter a valid email.",
+ "profilePicture": "User account image",
+ "allowedFormat": "Allowed formats: PNG, JPEG, GIF (maximum 10 MB)",
+ "uploadPicture": "Upload image",
+ "phoneNumberText": "Your new contact number will replace your previous contact number.",
+ "verb": ".",
+ "notDetermined": "Not determined"
+ }
+}
diff --git a/public/locales/fa/profileSetting.json b/public/locales/fa/profileSetting.json
new file mode 100644
index 0000000..d1942d0
--- /dev/null
+++ b/public/locales/fa/profileSetting.json
@@ -0,0 +1,43 @@
+{
+ "settingForm": {
+ "titlePersonalInfo": "اطلاعات شخصی من",
+ "descriptionPersonalInfo": "این اطلاعات شما صرفا برای احراز هویت شما است و نزد هارمونی باقی میماند",
+ "titlePhoneNumber": "شماره تماس من",
+ "descriptionPhoneNumber": "این اطلاعات شما صرفا برای احراز هویت شما است و نزد هارمونی باقی میماند",
+ "titleSocial": "ایمیل و شبکه های اجتماعی من",
+ "descriptionSocial": "این اطلاعات شما صرفاً برای احراز هویت شما است و نزد هارمونی باقی میماند",
+ "rejectButton": "لغو",
+ "saveButton": "ذخیره",
+ "editButton": "ویرایش",
+ "editPhoneNumber": "تغییر شماره تماس",
+ "addEmailOrSocialButton": "افزودن ایمیل / سوشال",
+ "addEmailButton": "افزودن ایمیل",
+ "name": "نام",
+ "familyName": "نام خانوادگی",
+ "country": "کشور",
+ "gender": "جنسیت",
+ "nationalCode": "کد ملی",
+ "man": "مرد",
+ "woman": "زن",
+ "genderPlaceholder": "مرد",
+ "newPhoneNumber": "شماره تماس جدید",
+ "verificationCodeButton": "ارسال کد تایید",
+ "verificationCode": "کد تایید",
+ "checkCode": "بررسی کد",
+ "successButton": "تایید شد",
+ "email": "ایمیل",
+ "apple": "اپل",
+ "google": "گوگل",
+ "newEmail": "ایمیل جدید",
+ "dialogHeader": "با فعالسازی ایمیل میتوانید در دفعات بعدی ورود برای ورود از این ایمیل استفاده کنید",
+ "or": "یا",
+ "emailError": "لطفا یک ایمیل معتبر وارد کنید",
+ "profilePicture": "تصویر حساب کاربری",
+ "allowedFormat": "فرمتهای مجاز: PNG، JPEG، GIF (حداکثر ۱۰ مگابایت)",
+ "uploadPicture": "بارگذاری تصویر",
+ "phoneNumberText": "شماره تماس جدید شما جایگزین شماره تماس قبلی",
+ "verb": "خواهد شد",
+ "notDetermined": "تعیین نشده",
+ "successfulChangePhone": "شماره تماس با موفقیت تغییر کرد"
+ }
+}
diff --git a/src/App.tsx b/src/App.tsx
index 50ac647..b1c1097 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,25 +1,19 @@
import { CssBaseline, useColorScheme } from '@mui/material';
import './App.css';
import { LanguageManager } from './components/LanguageManager';
-import { Settings } from './features/profile/Settings';
+
function App() {
return (
<>
- {/* */}
- {/* */}
-
- {/*
-
- */}
+
{/*
{t('helloWorld')}
-
diff --git a/src/components/CountDownTimer.tsx b/src/components/CountDownTimer.tsx
new file mode 100644
index 0000000..655fa9b
--- /dev/null
+++ b/src/components/CountDownTimer.tsx
@@ -0,0 +1,39 @@
+import { useState, useEffect } from 'react';
+
+interface CountdownTimerProps {
+ initialSeconds: number;
+ onComplete?: () => void;
+}
+
+export function CountDownTimer({
+ initialSeconds,
+ onComplete,
+}: CountdownTimerProps) {
+ const [secondsLeft, setSecondsLeft] = useState(initialSeconds);
+
+ useEffect(() => {
+ setSecondsLeft(initialSeconds);
+ }, [initialSeconds]);
+
+ useEffect(() => {
+ if (secondsLeft <= 0) {
+ onComplete?.();
+ return;
+ }
+ const timer = setInterval(() => {
+ setSecondsLeft((prev) => prev - 1);
+ }, 1000);
+ return () => clearInterval(timer);
+ }, [secondsLeft, onComplete]);
+
+ const toPersianDigits = (str: string) =>
+ str.replace(/\d/g, (d: string) => '۰۱۲۳۴۵۶۷۸۹'[parseInt(d)]);
+
+ const formatTime = (totalSeconds: number) => {
+ const minutes = String(Math.floor(totalSeconds / 60)).padStart(2, '0');
+ const seconds = String(totalSeconds % 60).padStart(2, '0');
+ return toPersianDigits(`${minutes}:${seconds}`);
+ };
+
+ return {formatTime(secondsLeft)};
+}
diff --git a/src/components/Countries.tsx b/src/components/Countries.tsx
new file mode 100644
index 0000000..ee4f74a
--- /dev/null
+++ b/src/components/Countries.tsx
@@ -0,0 +1,40 @@
+// components/Countries.ts
+
+export interface Country {
+ name: string;
+ fa: string;
+ flag: string;
+}
+
+export const countries: Country[] = [
+ { name: 'Afghanistan', fa: 'افغانستان', flag: 'af' },
+ { name: 'Albania', fa: 'آلبانی', flag: 'al' },
+ { name: 'Algeria', fa: 'الجزایر', flag: 'dz' },
+ { name: 'Argentina', fa: 'آرژانتین', flag: 'ar' },
+ { name: 'Armenia', fa: 'ارمنستان', flag: 'am' },
+ { name: 'Australia', fa: 'استرالیا', flag: 'au' },
+ { name: 'Austria', fa: 'اتریش', flag: 'at' },
+ { name: 'Bahrain', fa: 'بحرین', flag: 'bh' },
+ { name: 'Canada', fa: 'کانادا', flag: 'ca' },
+ { name: 'China', fa: 'چین', flag: 'cn' },
+ { name: 'France', fa: 'فرانسه', flag: 'fr' },
+ { name: 'Germany', fa: 'آلمان', flag: 'de' },
+ { name: 'India', fa: 'هند', flag: 'in' },
+ { name: 'Iran', fa: 'ایران', flag: 'ir' },
+ { name: 'Iraq', fa: 'عراق', flag: 'iq' },
+ { name: 'Italy', fa: 'ایتالیا', flag: 'it' },
+ { name: 'Japan', fa: 'ژاپن', flag: 'jp' },
+ { name: 'Netherlands', fa: 'هلند', flag: 'nl' },
+ { name: 'Pakistan', fa: 'پاکستان', flag: 'pk' },
+ { name: 'Qatar', fa: 'قطر', flag: 'qa' },
+ { name: 'Russia', fa: 'روسیه', flag: 'ru' },
+ { name: 'Saudi Arabia', fa: 'عربستان سعودی', flag: 'sa' },
+ { name: 'Spain', fa: 'اسپانیا', flag: 'es' },
+ { name: 'Sweden', fa: 'سوئد', flag: 'se' },
+ { name: 'Switzerland', fa: 'سوئیس', flag: 'ch' },
+ { name: 'Turkey', fa: 'ترکیه', flag: 'tr' },
+ { name: 'United Arab Emirates', fa: 'امارات متحده عربی', flag: 'ae' },
+ { name: 'United Kingdom', fa: 'بریتانیا', flag: 'gb' },
+ { name: 'United States', fa: 'ایالات متحده آمریکا', flag: 'us' },
+ { name: 'Yemen', fa: 'یمن', flag: 'ye' },
+];
diff --git a/src/components/CountryFlag.tsx b/src/components/CountryFlag.tsx
new file mode 100644
index 0000000..b7be516
--- /dev/null
+++ b/src/components/CountryFlag.tsx
@@ -0,0 +1,46 @@
+import { Box, Typography } from '@mui/material';
+
+export const countryCodeMap: { [key: string]: string } = {
+ ایران: 'ir',
+ قطر: 'qa',
+ آلمان: 'de',
+ آمریکا: 'us',
+ فرانسه: 'fr',
+ ایتالیا: 'it',
+ اسپانیا: 'es',
+ انگلیس: 'gb',
+ کانادا: 'ca',
+ استرالیا: 'au',
+ چین: 'cn',
+ ژاپن: 'jp',
+ هند: 'in',
+ روسیه: 'ru',
+ برزیل: 'br',
+ آرژانتین: 'ar',
+ ترکیه: 'tr',
+ سوئیس: 'ch',
+ سوئد: 'se',
+ نروژ: 'no',
+ عربستان: 'sa',
+ امارات: 'ae',
+ عراق: 'iq',
+ پاکستان: 'pk',
+};
+
+export function CountryFlag({ country }: { country: string }) {
+ const countryCode = countryCodeMap[country] || 'un';
+ const flagUrl = `https://flagcdn.com/w40/${countryCode}.png`;
+
+ return (
+
+
+ {country}
+
+ );
+}
diff --git a/src/components/Toast.tsx b/src/components/Toast.tsx
index 713c36f..0f0927e 100644
--- a/src/components/Toast.tsx
+++ b/src/components/Toast.tsx
@@ -1,6 +1,8 @@
import { Alert, Snackbar, type AlertColor } from '@mui/material';
+
import { type PropsWithChildren } from 'react';
+
export interface ToastProps extends PropsWithChildren {
color: AlertColor | undefined;
open: boolean;
diff --git a/src/features/authentication/components/UserCompletionForm.tsx b/src/features/authentication/components/UserCompletionForm.tsx
new file mode 100644
index 0000000..7f71c92
--- /dev/null
+++ b/src/features/authentication/components/UserCompletionForm.tsx
@@ -0,0 +1,353 @@
+import {
+ TextField,
+ FormControl,
+ InputLabel,
+ MenuItem,
+ Select,
+ Box,
+ type SelectChangeEvent,
+ Switch,
+ FormGroup,
+ Button,
+ Typography,
+ Link,
+} from '@mui/material';
+import React, { useEffect, useState } from 'react';
+
+export function UserCompletionForm() {
+ const [sex, setSex] = useState('');
+ const [showPassword, setShowPassword] = useState(false);
+ const [showEmail, setShowEmail] = useState(false);
+ const [password, setPassword] = useState('');
+ const [email, setEmail] = useState('');
+ const [confirmPassword, setConfirmPassword] = useState('');
+ const [codeSent, setCodeSent] = useState(false);
+ const [verificationCode, setVerificationCode] = useState('');
+ const [buttonState, setButtonState] = useState('default'); // default | counting | sent
+ const [countdown, setCountdown] = useState(60);
+ const matchPassword = password === confirmPassword;
+ const hasNumber = /\d/.test(password);
+ const hasMinLength = password.length >= 8;
+ const hasUpperAndLower = /[A-Z]/.test(password) && /[a-z]/.test(password);
+ const hasSpecialChar = /[!@#$%^&*]/.test(password);
+ const correctEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
+
+ const handleTogglePassword = (e: React.ChangeEvent) => {
+ setShowPassword(e.target.checked);
+ };
+ const handleToggleEmail = (e: React.ChangeEvent) => {
+ setShowEmail(e.target.checked);
+ };
+
+ const handleChange = (e: SelectChangeEvent) => {
+ setSex(e.target.value);
+ };
+
+ const onClickCodeSent = () => {
+ setCodeSent(true);
+ setButtonState('sent');
+ setTimeout(() => {
+ setButtonState('counting');
+ setCountdown(60);
+ }, 1000);
+ };
+
+ useEffect(() => {
+ let timer: ReturnType;
+ if (buttonState === 'counting' && countdown > 0) {
+ timer = setInterval(() => {
+ setCountdown((prev) => prev - 1);
+ }, 1000);
+ }
+ if (countdown === 0) {
+ setButtonState('default');
+ }
+ return () => clearInterval(timer);
+ }, [buttonState, countdown]);
+
+ const toPersianDigits = (str: string) =>
+ str.replace(/\d/g, (d: string) => '۰۱۲۳۴۵۶۷۸۹'[parseInt(d)]);
+
+ const getButtonLabel = () => {
+ if (buttonState === 'sent') return 'ارسال شد!';
+ if (buttonState === 'counting') {
+ const minutes = String(Math.floor(countdown / 60)).padStart(2, '0');
+ const seconds = String(countdown % 60).padStart(2, '0');
+ const time = `${minutes}:${seconds}`;
+ return toPersianDigits(time);
+ }
+ return 'ارسال کد تایید';
+ };
+
+ return (
+
+
+
+
+ تکمیل اطلاعات حساب کاربری
+
+
+ اطلاعات کسب و کار خود را وارد کنید
+
+
+
+
+
+
+
+
+ جنسیت
+
+
+
+
+
+
+
+
+ تعیین رمز عبور
+
+
+ {showPassword && (
+
+
+ setPassword(e.target.value)}
+ variant="outlined"
+ sx={{
+ '& .MuiOutlinedInput-root': {
+ height: 45,
+ },
+ }}
+ />
+ {password && (
+
+
+ شامل عدد
+
+
+
+ حداقل 8 کاراکتر
+
+
+
+ شامل یک حرف بزرگ و کوچک
+
+
+
+ شامل علامت(!@#$%^&*)
+
+
+ )}
+
+ {showPassword && (
+ setConfirmPassword(e.target.value)}
+ error={confirmPassword.length > 0 && !matchPassword}
+ helperText={
+ confirmPassword.length > 0 && !matchPassword
+ ? 'مطابقت ندارد'
+ : ' '
+ }
+ sx={{
+ width: '330px',
+ '& .MuiOutlinedInput-root': {
+ height: 45,
+ },
+ }}
+ />
+ )}
+
+ )}
+
+
+
+
+ اتصال ایمیل خود
+
+
+ {showEmail && (
+
+
+
+ setEmail(e.target.value)}
+ sx={{
+ width: '330px',
+ '& .MuiOutlinedInput-root': {
+ height: 45,
+ },
+ }}
+ />
+ {email && (
+
+ فرم درست ایمیل وارد کنید
+
+ )}
+
+
+
+ {codeSent && (
+
+ setVerificationCode(e.target.value)}
+ sx={{
+ width: '330px',
+ '& .MuiOutlinedInput-root': {
+ height: 45,
+ },
+ }}
+ />
+
+
+ )}
+
+ )}
+
+
+ ادامه فرایند ثبت نام به منزله تایید و قبول{' '}
+
+ قوانین و مقررات هارمونی
+ {' '}
+ می باشد.
+
+
+
+
+
+ );
+}
diff --git a/src/features/profile/components/PersonalInformation.tsx b/src/features/profile/components/PersonalInformation.tsx
new file mode 100644
index 0000000..d06b551
--- /dev/null
+++ b/src/features/profile/components/PersonalInformation.tsx
@@ -0,0 +1,129 @@
+import React, { useState } from 'react';
+import { Box, Button } from '@mui/material';
+import { useTranslation } from 'react-i18next';
+import { CardContainer } from '@/components/CardContainer';
+import { ProfileImage } from './personlInformation/ProfileImage';
+import { InfoRowDisplay } from './personlInformation/InfoRowDisplay';
+import { InfoRowEdit } from './personlInformation/InfoRowEdit';
+
+export function PersonalInformation() {
+ const { t } = useTranslation('profileSetting');
+ const [isEditing, setIsEditing] = useState(false);
+ const [gender, setGender] = useState('');
+ const [uploadedImageUrl, setUploadedImageUrl] = useState(null);
+
+ const initialData = {
+ firstName: 'محمد حسین',
+ lastName: 'برزهگر',
+ country: 'قطر',
+ gender: 'مرد',
+ nationalCode: '',
+ };
+ const [data, setData] = useState(initialData);
+
+ const initials = `${data.firstName?.trim()[0] || ''}${data.lastName?.trim()[0] || ''}`;
+
+ const toggleEdit = () => {
+ setIsEditing((prev) => !prev);
+ if (isEditing) {
+ setData((prev) => ({
+ ...prev,
+ gender: gender === 'male' ? 'مرد' : gender === 'female' ? 'زن' : '',
+ }));
+ } else {
+ setGender(
+ data.gender === 'مرد' ? 'male' : data.gender === 'زن' ? 'female' : '',
+ );
+ }
+ };
+
+ return (
+
+ {isEditing && (
+
+ )}
+
+
+ }
+ >
+
+ {isEditing && (
+ {
+ const file = e.target.files?.[0];
+ if (file) {
+ const reader = new FileReader();
+ reader.onload = () =>
+ setUploadedImageUrl(reader.result as string);
+ reader.readAsDataURL(file);
+ }
+ }}
+ />
+ )}
+
+ {isEditing ? (
+
+ ) : (
+
+ )}
+
+
+ );
+}
diff --git a/src/features/profile/components/PhoneNumber.tsx b/src/features/profile/components/PhoneNumber.tsx
new file mode 100644
index 0000000..7cf2cff
--- /dev/null
+++ b/src/features/profile/components/PhoneNumber.tsx
@@ -0,0 +1,286 @@
+import React, { useState, type ChangeEvent } from 'react';
+import { Box, Typography, Button, TextField, IconButton } from '@mui/material';
+import { Edit, Refresh, TickCircle } from 'iconsax-react';
+import { useTranslation } from 'react-i18next';
+import { CardContainer } from '@/components/CardContainer';
+import { CountDownTimer } from '@/components/CountDownTimer';
+import { Toast } from '@/components/Toast';
+
+export function PhoneNumber() {
+ const { t } = useTranslation('profileSetting');
+ const [isEditing, setIsEditing] = useState(false);
+ const [phoneNumber, setPhoneNumber] = useState('');
+ const [verificationCode, setVerificationCode] = useState('');
+ const [showToast, setShowToast] = useState(false);
+ const [buttonState, setButtonState] = useState<'default' | 'counting'>(
+ 'default',
+ );
+ const [isVerifying, setIsVerifying] = useState(false);
+ const [isVerified, setIsVerified] = useState(false);
+
+ const [phones, setPhone] = useState([
+ { phone: '09123456789', time: '۱ ماه پیش', withCode: '+989123456789' },
+ ]);
+
+ const toggleEdit = () => {
+ setIsEditing((prev) => {
+ const enteringEditMode = !prev;
+ if (enteringEditMode) {
+ setPhoneNumber('');
+ setVerificationCode('');
+ setIsVerified(false);
+ setButtonState('default');
+ setShowToast(false);
+ }
+ return enteringEditMode;
+ });
+ };
+
+ const handlePhoneNumberChange = (e: ChangeEvent
) => {
+ const value = e.target.value.replace(/\D/g, '');
+ setPhoneNumber(value);
+ };
+
+ const handleVerificationCodeChange = (e: ChangeEvent) => {
+ const value = e.target.value.replace(/\D/g, '');
+ setVerificationCode(value);
+ };
+
+ const handleSendCode = () => {
+ if (!phoneNumber) return;
+ setButtonState('counting');
+ setIsVerified(false);
+ };
+
+ const handleVerifyCode = () => {
+ setIsVerifying(true);
+ setTimeout(() => {
+ setIsVerifying(false);
+ setIsVerified(true);
+ setShowToast(true);
+ const newPhone = '+98' + phoneNumber.slice(1);
+ setPhone([
+ { phone: phoneNumber, time: 'چند ثانیه پیش', withCode: newPhone },
+ ]);
+ }, 1500);
+ };
+
+ const handleVerifyClick = () => {
+ if (!verificationCode || isVerifying) return;
+ handleVerifyCode();
+ setTimeout(() => setShowToast(true), 1600);
+ };
+
+ return (
+
+ {isEditing && (
+
+ )}
+
+
+ }
+ >
+ {isEditing ? (
+
+
+
+ {t('settingForm.editPhoneNumber')}
+
+
+ {t('settingForm.phoneNumberText')}({phones.map((p) => p.withCode)}
+ ){t('settingForm.verb')}
+
+
+
+
+ {
+ setButtonState('default');
+ setPhoneNumber('');
+ }}
+ sx={{ mr: 1 }}
+ >
+
+
+ ) : null,
+ }}
+ />
+
+ {isVerified ? (
+
+
+
+ {t('settingForm.successButton')}
+
+
+ ) : (
+
+ )}
+
+
+ {buttonState === 'counting' && !isVerified && (
+
+
+
+
+
+ )}
+
+ setShowToast(false)}
+ >
+ {t('settingForm.successfulChangePhone')}
+
+
+ ) : (
+
+ {phones.map((item, index) => (
+
+
+ {item.phone}
+
+
+ {item.time}
+
+
+ ))}
+
+ )}
+
+ );
+}
diff --git a/src/features/profile/components/SocialMedia.tsx b/src/features/profile/components/SocialMedia.tsx
new file mode 100644
index 0000000..1d8b3ab
--- /dev/null
+++ b/src/features/profile/components/SocialMedia.tsx
@@ -0,0 +1,331 @@
+import React, { useState } from 'react';
+import {
+ Box,
+ Button,
+ Typography,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ IconButton,
+ TextField,
+ Menu,
+ MenuItem,
+ ListItemIcon,
+ ListItemText,
+ useMediaQuery,
+} from '@mui/material';
+import type { Theme } from '@mui/material/styles';
+import { useTranslation } from 'react-i18next';
+import { CardContainer } from '@/components/CardContainer';
+
+import {
+ Google,
+ Apple,
+ Sms,
+ Trash,
+ CloseSquare,
+ Message,
+ ArrowDown3,
+} from 'iconsax-react';
+
+export function SocialMedia() {
+ const { t } = useTranslation('profileSetting');
+ const [openDialog, setOpenDialog] = useState(false);
+ const [emailInput, setEmailInput] = useState('');
+ const [emailError, setEmailError] = useState(false);
+ const [anchor, setAnchor] = useState(null);
+ const openMenu = Boolean(anchor);
+ const fullScreen = useMediaQuery((theme: Theme) =>
+ theme.breakpoints.down('sm'),
+ );
+
+ const handleOpenDialog = () => setOpenDialog(true);
+ const handleCloseDialog = () => setOpenDialog(false);
+
+ const handleClickMenu = (e: React.MouseEvent) =>
+ setAnchor(e.currentTarget);
+ const handleCloseMenu = () => setAnchor(null);
+
+ const handleEmailChange = (e: React.ChangeEvent) => {
+ const value = e.target.value;
+ setEmailInput(value);
+ setEmailError(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value));
+ };
+
+ const emailList = [
+ { email: 'emailtemp@email.com', provider: 'email', time: '1 ماه پیش' },
+ { email: 'emailtemp@gmail.com', provider: 'google', time: '1 ماه پیش' },
+ { email: 'emailtemp@icloud.com', provider: 'apple', time: '1 ماه پیش' },
+ ] as const;
+
+ return (
+
+
+
+
+
+
+
+ }
+ >
+
+ {emailList.map((item, index) => (
+
+
+ {item.provider === 'google' && (
+
+ )}
+ {item.provider === 'apple' && (
+
+ )}
+ {item.provider === 'email' && (
+
+ )}
+
+
+
+ {item.email}
+
+
+ {item.time}
+
+
+
+
+
+
+
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/features/profile/components/UserForm.tsx b/src/features/profile/components/UserForm.tsx
new file mode 100644
index 0000000..bcfd593
--- /dev/null
+++ b/src/features/profile/components/UserForm.tsx
@@ -0,0 +1,14 @@
+import { Box } from '@mui/material';
+import { PersonalInformation } from './PersonalInformation';
+import { PhoneNumber } from './PhoneNumber';
+import { SocialMedia } from './SocialMedia';
+
+export function UserForm() {
+ return (
+
+
+
+
+
+ );
+}
diff --git a/src/features/profile/components/personlInformation/DisplayField.tsx b/src/features/profile/components/personlInformation/DisplayField.tsx
new file mode 100644
index 0000000..f6690fd
--- /dev/null
+++ b/src/features/profile/components/personlInformation/DisplayField.tsx
@@ -0,0 +1,24 @@
+import { Box, Typography } from '@mui/material';
+import { useTranslation } from 'react-i18next';
+
+export function DisplayField({
+ label,
+ value,
+}: {
+ label: string;
+ value: string;
+}) {
+ const { t } = useTranslation('profileSetting');
+ const displayValue = value?.trim() || t('settingForm.notDetermined');
+
+ return (
+
+
+ {label}
+
+
+ {displayValue}
+
+
+ );
+}
diff --git a/src/features/profile/components/personlInformation/InfoRowDisplay.tsx b/src/features/profile/components/personlInformation/InfoRowDisplay.tsx
new file mode 100644
index 0000000..d581865
--- /dev/null
+++ b/src/features/profile/components/personlInformation/InfoRowDisplay.tsx
@@ -0,0 +1,85 @@
+import { Box, Typography, Avatar } from '@mui/material';
+import { useTranslation } from 'react-i18next';
+import { CountryFlag } from '@/components/CountryFlag';
+import { DisplayField } from './DisplayField';
+
+export function InfoRowDisplay({
+ data,
+ uploadedImageUrl,
+ initials,
+}: {
+ data: any;
+ uploadedImageUrl: string | null;
+ initials: string;
+}) {
+ const { t } = useTranslation('profileSetting');
+ const displayValue = (value: string) =>
+ value?.trim() || t('settingForm.notDetermined');
+
+ return (
+
+
+
+
+ {initials}
+
+
+
+ {t('settingForm.name')} و {t('settingForm.familyName')}
+
+
+ {`${displayValue(data.firstName)} ${displayValue(data.lastName)}`}
+
+
+
+
+
+
+ {t('settingForm.country')}
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/features/profile/components/personlInformation/InfoRowEdit.tsx b/src/features/profile/components/personlInformation/InfoRowEdit.tsx
new file mode 100644
index 0000000..5969583
--- /dev/null
+++ b/src/features/profile/components/personlInformation/InfoRowEdit.tsx
@@ -0,0 +1,97 @@
+import {
+ Box,
+ TextField,
+ FormControl,
+ MenuItem,
+ Select,
+ Autocomplete,
+} from '@mui/material';
+import { useTranslation } from 'react-i18next';
+import { CountryFlag, countryCodeMap } from '@/components/CountryFlag';
+
+export function InfoRowEdit({ data, setData, gender, setGender }: any) {
+ const { t } = useTranslation('profileSetting');
+
+ return (
+
+ {[
+ {
+ name: 'firstName',
+ label: t('settingForm.name'),
+ value: data.firstName,
+ },
+ {
+ name: 'lastName',
+ label: t('settingForm.familyName'),
+ value: data.lastName,
+ },
+ {
+ name: 'nationalCode',
+ label: t('settingForm.nationalCode'),
+ value: data.nationalCode,
+ },
+ ].map((field, idx) => (
+
+
+ setData((prev: any) => ({
+ ...prev,
+ [field.name]: e.target.value,
+ }))
+ }
+ label={field.label}
+ />
+
+ ))}
+
+
+
+
+
+
+
+
+ setData((prev: any) => ({ ...prev, country: newValue || '' }))
+ }
+ renderOption={(props, option) => (
+
+
+
+ )}
+ renderInput={(params) => (
+
+ )}
+ />
+
+ );
+}
diff --git a/src/features/profile/components/personlInformation/ProfileImage.tsx b/src/features/profile/components/personlInformation/ProfileImage.tsx
new file mode 100644
index 0000000..3d848c8
--- /dev/null
+++ b/src/features/profile/components/personlInformation/ProfileImage.tsx
@@ -0,0 +1,62 @@
+import { Box, Avatar, Typography, Button } from '@mui/material';
+import { useTranslation } from 'react-i18next';
+import { Camera } from 'iconsax-react';
+
+export function ProfileImage({
+ initials,
+ uploadedImageUrl,
+ onImageChange,
+}: {
+ initials: string;
+ uploadedImageUrl: string | null;
+ onImageChange: (e: React.ChangeEvent) => void;
+}) {
+ const { t } = useTranslation('profileSetting');
+
+ return (
+
+
+ {initials}
+
+
+
+ {t('settingForm.profilePicture')}
+
+
+ {t('settingForm.allowedFormat')}
+
+
+ }
+ >
+ {t('settingForm.uploadPicture')}
+
+
+
+
+
+ );
+}