fix: responsiveness

This commit is contained in:
2025-07-27 15:14:15 +03:30
parent 594de53486
commit bc4c105d70
7 changed files with 581 additions and 707 deletions

View File

@@ -1,3 +1,4 @@
import React from 'react';
import { Box, Typography } from '@mui/material'; import { Box, Typography } from '@mui/material';
export function CardContainer({ export function CardContainer({
@@ -14,27 +15,16 @@ export function CardContainer({
highlighted?: boolean; highlighted?: boolean;
}) { }) {
return ( return (
<Box <Box sx={{ width: '100%', bgcolor: 'background.paper' }}>
sx={{
width: '100%',
display: 'flex',
justifyContent: 'center',
px: { xs: 2, sm: 3, md: 4 },
}}
>
<Box <Box
sx={{ sx={{
marginInline: 'auto',
width: '100%', width: '100%',
maxWidth: { maxWidth: 'min(100%, 818px)',
xs: '100%', paddingInline: { xs: 2, sm: 3, md: 4 },
sm: '500px',
md: '818px',
},
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
gap: 2, gap: 2,
mx: 'auto',
px: { xs: 2, sm: 3, md: 4 },
}} }}
> >
<Box <Box
@@ -43,9 +33,7 @@ export function CardContainer({
justifyContent: 'space-between', justifyContent: 'space-between',
alignItems: { xs: 'flex-start', sm: 'center' }, alignItems: { xs: 'flex-start', sm: 'center' },
flexDirection: { xs: 'column', sm: 'row' }, flexDirection: { xs: 'column', sm: 'row' },
backgroundColor: highlighted bgcolor: highlighted ? 'primary.light' : 'background.default',
? 'primary.light'
: 'background.default',
p: 2, p: 2,
borderRadius: 1, borderRadius: 1,
gap: { xs: 1, sm: 0 }, gap: { xs: 1, sm: 0 },

View File

@@ -1,97 +0,0 @@
import React, { useState, useEffect } from 'react';
import { Box, Alert, IconButton, type AlertColor } from '@mui/material';
import {
TickCircle,
CloseSquare,
Warning2,
InfoCircle,
CloseCircle,
} from 'iconsax-react';
type AlertType = AlertColor;
interface CustomAlertProps {
message: string;
onClose: () => void;
severity?: AlertType;
open: boolean;
duration?: number;
delayOnClose?: number;
rtl?: boolean;
icon?: React.ReactNode;
}
const defaultIcons: Record<AlertType, React.ReactNode> = {
success: <TickCircle size="20" color="#fff" />,
error: <CloseSquare size="20" color="#fff" />,
warning: <Warning2 size="20" color="#fff" />,
info: <InfoCircle size="20" color="#fff" />,
};
export const CustomAlert: React.FC<CustomAlertProps> = ({
message,
severity,
open,
onClose,
duration = 4000,
delayOnClose = 2000,
rtl = false,
icon,
}) => {
const [visible, setVisible] = useState(open);
useEffect(() => {
setVisible(open);
}, [open]);
useEffect(() => {
if (visible && duration > 0) {
const timer = setTimeout(() => {
setVisible(false);
onClose();
}, duration);
return () => clearTimeout(timer);
}
}, [visible, duration, onClose]);
const handleClose = () => {
setTimeout(() => {
setVisible(false);
onClose();
}, delayOnClose);
};
if (!visible) return null;
return (
<Box
sx={{
position: 'fixed',
bottom: 20,
left: 20,
zIndex: 9999,
}}
>
<Alert
severity={severity}
variant="filled"
icon={icon ?? defaultIcons[severity ?? 'success']}
action={
<IconButton onClick={handleClose} sx={{ color: 'black', p: 0.5 }}>
<CloseCircle size="20" />
</IconButton>
}
sx={{
width: '396px',
flexDirection: 'row-reverse',
justifyContent: 'space-between',
alignItems: 'center',
textAlign: rtl ? 'right' : 'left',
direction: rtl ? 'rtl' : 'ltr',
}}
>
{message}
</Alert>
</Box>
);
};

23
src/components/Toast.tsx Normal file
View File

@@ -0,0 +1,23 @@
import { Alert, Snackbar, type AlertColor } from '@mui/material';
import React, { type PropsWithChildren } from 'react';
export interface ToastProps extends PropsWithChildren {
color: AlertColor | undefined;
open: boolean;
onClose: () => void;
}
export const Toast = ({ color, open, onClose, children }: ToastProps) => {
return (
<Snackbar sx={{ minWidth: '396px' }} open={open} onClose={onClose}>
<Alert
onClose={onClose}
severity={color}
variant="filled"
sx={{ width: '100%' }}
>
{children}
</Alert>
</Snackbar>
);
};

View File

@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { Box, Button } from '@mui/material'; import { Box, Button } from '@mui/material';
import { useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { CardContainer } from '@/components/CardContainer'; import { CardContainer } from '@/components/CardContainer';
import { ProfileImage } from './personlInformation/ProfileImage'; import { ProfileImage } from './personlInformation/ProfileImage';
@@ -38,110 +38,92 @@ export function PersonalInformation() {
}; };
return ( return (
<Box <CardContainer
sx={{ title={t('settingForm.titlePersonalInfo')}
display: 'flex', subtitle={t('settingForm.descriptionPersonalInfo')}
justifyContent: 'center', highlighted={isEditing}
width: '100%', action={
px: { xs: 1, sm: 2 }, <Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
backgroundColor: 'background.paper', {isEditing && (
}}
>
<CardContainer
title={t('settingForm.titlePersonalInfo')}
subtitle={t('settingForm.descriptionPersonalInfo')}
highlighted={isEditing}
action={
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
{isEditing && (
<Button
variant="text"
onClick={() => setIsEditing(false)}
size="large"
sx={{
color: 'primary.main',
textTransform: 'none',
width: {
xs: '100%',
sm: 'auto',
},
fontSize: { xs: '0.85rem', sm: '1rem' },
}}
>
{t('settingForm.rejectButton')}
</Button>
)}
<Button <Button
onClick={toggleEdit} variant="text"
onClick={() => setIsEditing(false)}
size="large" size="large"
variant="outlined"
sx={{ sx={{
color: 'primary.main',
textTransform: 'none', textTransform: 'none',
border: '1px solid', width: { xs: '100%', sm: 'auto' },
borderColor: 'primary.main', fontSize: { xs: '0.85rem', sm: '1rem' },
borderRadius: '4px',
backgroundColor: isEditing
? 'primary.main'
: 'background.paper',
color: isEditing ? 'primary.contrastText' : 'primary.main',
px: { xs: 2, sm: '22px' },
py: { xs: '6px', sm: '8px' },
width: {
xs: '100%',
sm: isEditing ? '85px' : '93px',
},
fontSize: { xs: '0.9rem', sm: '1rem' },
}} }}
> >
{isEditing {t('settingForm.rejectButton')}
? t('settingForm.saveButton')
: t('settingForm.editButton')}
</Button> </Button>
</Box>
}
>
<Box
sx={{
px: { xs: 2, sm: 3, md: 4 },
py: 2,
display: 'flex',
flexDirection: 'column',
gap: 2,
backgroundColor: 'background.paper',
// width: '754px',
}}
>
{isEditing && (
<ProfileImage
initials={initials}
uploadedImageUrl={uploadedImageUrl}
onImageChange={(e) => {
const file = e.target.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = () =>
setUploadedImageUrl(reader.result as string);
reader.readAsDataURL(file);
}
}}
/>
)}
{isEditing ? (
<InfoRowEdit
data={data}
setData={setData}
gender={gender}
setGender={setGender}
/>
) : (
<InfoRowDisplay
data={data}
uploadedImageUrl={uploadedImageUrl}
initials={initials}
/>
)} )}
<Button
onClick={toggleEdit}
size="large"
variant="outlined"
sx={{
textTransform: 'none',
border: '1px solid',
borderColor: 'primary.main',
borderRadius: '4px',
bgcolor: isEditing ? 'primary.main' : 'background.paper',
color: isEditing ? 'primary.contrastText' : 'primary.main',
px: { xs: 2, sm: '22px' },
py: { xs: '6px', sm: '8px' },
width: { xs: '100%', sm: isEditing ? '85px' : '93px' },
fontSize: { xs: '0.9rem', sm: '1rem' },
}}
>
{isEditing
? t('settingForm.saveButton')
: t('settingForm.editButton')}
</Button>
</Box> </Box>
</CardContainer> }
</Box> >
<Box
sx={{
px: { xs: 2, sm: 3, md: 4 },
py: 2,
display: 'flex',
flexDirection: 'column',
gap: 2,
bgcolor: 'background.paper',
}}
>
{isEditing && (
<ProfileImage
initials={initials}
uploadedImageUrl={uploadedImageUrl}
onImageChange={(e) => {
const file = e.target.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = () =>
setUploadedImageUrl(reader.result as string);
reader.readAsDataURL(file);
}
}}
/>
)}
{isEditing ? (
<InfoRowEdit
data={data}
setData={setData}
gender={gender}
setGender={setGender}
/>
) : (
<InfoRowDisplay
data={data}
uploadedImageUrl={uploadedImageUrl}
initials={initials}
/>
)}
</Box>
</CardContainer>
); );
} }

View File

@@ -1,58 +1,27 @@
import React, { useState, type ChangeEvent } from 'react';
import { Box, Typography, Button, TextField, IconButton } from '@mui/material'; import { Box, Typography, Button, TextField, IconButton } from '@mui/material';
import { Edit, Refresh, TickCircle } from 'iconsax-react'; import { Edit, Refresh, TickCircle } from 'iconsax-react';
import { useState, type ChangeEvent } from 'react'; import { useTranslation } from 'react-i18next';
import { CardContainer } from '@/components/CardContainer'; import { CardContainer } from '@/components/CardContainer';
import { CountDownTimer } from '@/components/CountDownTimer'; import { CountDownTimer } from '@/components/CountDownTimer';
import { useTranslation } from 'react-i18next'; import { Toast } from '@/components/Toast';
import { CustomAlert } from '@/components/CustomAlert';
export function PhoneNumber() { export function PhoneNumber() {
const { t } = useTranslation('profileSetting'); const { t } = useTranslation('profileSetting');
const [isEditing, setIsEditing] = useState(false); const [isEditing, setIsEditing] = useState(false);
const [phoneNumber, setPhoneNumber] = useState(''); const [phoneNumber, setPhoneNumber] = useState('');
const [verificationCode, setVerificationCode] = useState(''); const [verificationCode, setVerificationCode] = useState('');
const [showEmailAlert, setShowEmailAlert] = useState(false); const [showToast, setShowToast] = useState(false);
const [buttonState, setButtonState] = useState<'default' | 'counting'>( const [buttonState, setButtonState] = useState<'default' | 'counting'>(
'default', 'default',
); );
const [isVerifying, setIsVerifying] = useState(false); const [isVerifying, setIsVerifying] = useState(false);
const [isVerified, setIsVerified] = useState(false); const [isVerified, setIsVerified] = useState(false);
const handleVerifyClick = () => {
if (!verificationCode || isVerifying) return;
handleVerifyCode();
setTimeout(() => {
setShowEmailAlert(true);
}, 1600);
};
const [phones, setPhone] = useState([ const [phones, setPhone] = useState([
{ { phone: '09123456789', time: '۱ ماه پیش', withCode: '+989123456789' },
phone: '09123456789',
time: '۱ ماه پیش',
withCode: '+989123456789',
},
]); ]);
const handleSendCode = () => {
setButtonState('counting');
setIsVerified(false);
};
const handleVerifyCode = () => {
setIsVerifying(true);
setTimeout(() => {
setIsVerifying(false);
setIsVerified(true);
setShowEmailAlert(true);
const newPhone = '+98' + phoneNumber.slice(1);
setPhone([
{ phone: phoneNumber, time: 'چند ثانیه پیش', withCode: newPhone },
]);
}, 1500);
};
const toggleEdit = () => { const toggleEdit = () => {
setIsEditing((prev) => { setIsEditing((prev) => {
const enteringEditMode = !prev; const enteringEditMode = !prev;
@@ -61,7 +30,7 @@ export function PhoneNumber() {
setVerificationCode(''); setVerificationCode('');
setIsVerified(false); setIsVerified(false);
setButtonState('default'); setButtonState('default');
setShowEmailAlert(false); setShowToast(false);
} }
return enteringEditMode; return enteringEditMode;
}); });
@@ -77,245 +46,241 @@ export function PhoneNumber() {
setVerificationCode(value); 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 ( return (
<Box <CardContainer
sx={{ title={t('settingForm.titlePhoneNumber')}
display: 'flex', subtitle={t('settingForm.descriptionPhoneNumber')}
justifyContent: 'center', highlighted={isEditing}
width: '100%', action={
px: 2, <Box sx={{ display: 'flex', gap: 1 }}>
backgroundColor: 'background.paper', {isEditing && (
}}
>
<CardContainer
title={t('settingForm.titlePhoneNumber')}
subtitle={t('settingForm.descriptionPhoneNumber')}
highlighted={isEditing}
action={
<Box sx={{ display: 'flex', gap: 1 }}>
{isEditing && (
<Button
variant="text"
onClick={toggleEdit}
size="large"
sx={{
color: 'primary.main',
textTransform: 'none',
width: '43px',
}}
>
{t('settingForm.rejectButton')}
</Button>
)}
<Button <Button
variant="text"
onClick={toggleEdit} onClick={toggleEdit}
size="large" size="large"
variant="outlined"
sx={{ sx={{
color: 'primary.main',
textTransform: 'none', textTransform: 'none',
border: '1px solid', width: '43px',
borderColor: 'primary.main',
borderRadius: '4px',
backgroundColor: isEditing
? 'primary.main'
: 'background.paper',
color: isEditing ? 'primary.contrastText' : 'primary.main',
width: isEditing ? '85px' : '161px',
whiteSpace: 'nowrap',
}} }}
> >
{isEditing {t('settingForm.rejectButton')}
? t('settingForm.saveButton')
: t('settingForm.editPhoneNumber')}
</Button> </Button>
</Box> )}
} <Button
> onClick={toggleEdit}
{isEditing ? ( size="large"
<Box variant="outlined"
sx={{ px: { xs: 2, sm: 4 }, backgroundColor: 'background.paper' }} sx={{
textTransform: 'none',
border: '1px solid',
borderColor: 'primary.main',
borderRadius: '4px',
bgcolor: isEditing ? 'primary.main' : 'background.paper',
color: isEditing ? 'primary.contrastText' : 'primary.main',
width: { xs: '100%', sm: isEditing ? '85px' : '161px' },
whiteSpace: 'nowrap',
}}
> >
<Box sx={{ mb: 2 }}> {isEditing
<Typography variant="h6"> ? t('settingForm.saveButton')
{t('settingForm.editPhoneNumber')} : t('settingForm.editPhoneNumber')}
</Typography> </Button>
<Typography variant="body2" color="text.secondary"> </Box>
{t('settingForm.phoneNumberText')}( }
{phones.map((p) => p.withCode)}){t('settingForm.verb')} >
</Typography> {isEditing ? (
</Box> <Box sx={{ px: { xs: 2, sm: 4 }, bgcolor: 'background.paper' }}>
<Box sx={{ mb: 2 }}>
<Typography variant="h6">
{t('settingForm.editPhoneNumber')}
</Typography>
<Typography variant="body2" color="text.secondary">
{t('settingForm.phoneNumberText')}({phones.map((p) => p.withCode)}
){t('settingForm.verb')}
</Typography>
</Box>
<Box
sx={{
display: 'flex',
flexWrap: 'wrap',
gap: 2,
alignItems: 'center',
}}
>
<TextField
name="phoneNumber"
label={t('settingForm.newPhoneNumber')}
type="tel"
value={phoneNumber}
onChange={handlePhoneNumberChange}
sx={{ flex: '1 1 240px', minWidth: 0 }}
placeholder="09123456789"
inputProps={{ dir: 'rtl', style: { textAlign: 'right' } }}
InputProps={{
endAdornment:
buttonState === 'counting' ? (
<IconButton
size="small"
onClick={() => {
setButtonState('default');
setPhoneNumber('');
}}
sx={{ mr: 1 }}
>
<Edit size="24" color="#64B5F6" />
</IconButton>
) : null,
}}
/>
{isVerified ? (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<TickCircle
size="24"
style={{ color: '#43A047' }}
variant="Bold"
/>
<Typography sx={{ color: 'success.main' }}>
{t('settingForm.successButton')}
</Typography>
</Box>
) : (
<Button
variant="text"
onClick={handleSendCode}
disabled={
buttonState === 'counting' || phoneNumber.length === 0
}
sx={{
textTransform: 'none',
minWidth: { xs: '100%', sm: 170 },
color: 'primary.main',
height: 56,
}}
>
{buttonState === 'counting' ? (
<CountDownTimer
initialSeconds={60}
onComplete={() => setButtonState('default')}
/>
) : (
t('settingForm.verificationCodeButton')
)}
</Button>
)}
</Box>
{buttonState === 'counting' && !isVerified && (
<Box <Box
sx={{ sx={{
display: 'flex', display: 'flex',
flexWrap: 'wrap', flexWrap: 'wrap',
gap: 2, gap: 2,
height: '88px', mt: 2,
alignItems: 'center',
}} }}
> >
<TextField <TextField
name="phoneNumber" name="verificationCode"
label={t('settingForm.newPhoneNumber')} label={t('settingForm.verificationCode')}
type="tel" type="tel"
value={phoneNumber} value={verificationCode}
onChange={handlePhoneNumberChange} onChange={handleVerificationCodeChange}
sx={{ flex: 1, minWidth: '250px', mb: 2 }} sx={{ flex: '1 1 240px', minWidth: 0 }}
placeholder="09123456789" placeholder={t('settingForm.verificationCode')}
inputProps={{ inputProps={{ dir: 'rtl', style: { textAlign: 'right' } }}
dir: 'rtl',
style: { textAlign: 'right' },
}}
InputProps={{
endAdornment:
buttonState === 'counting' ? (
<IconButton
size="small"
onClick={() => {
setButtonState('default');
setPhoneNumber('');
}}
sx={{ mr: 1 }}
>
<Edit size="24" color="#64B5F6" />
</IconButton>
) : null,
}}
/> />
{isVerified ? ( <Button
<Box variant="contained"
sx={{ onClick={handleVerifyClick}
display: 'flex', disabled={isVerifying || verificationCode.length === 0}
alignItems: 'center', sx={{
gap: 1, textTransform: 'none',
}} minWidth: { xs: '100%', sm: 170 },
> bgcolor: 'primary.main',
<TickCircle height: 56,
size="24" }}
style={{ color: '#43A047' }} >
variant="Bold" {isVerifying ? (
/> <Box
<Typography sx={{ color: 'success.main' }}> component="span"
{t('settingForm.successButton')}
</Typography>
</Box>
) : (
<Box sx={{}}>
<Button
variant="text"
onClick={handleSendCode}
disabled={
buttonState === 'counting' || phoneNumber.length === 0
}
sx={{ sx={{
textTransform: 'none', display: 'flex',
minWidth: '170px', animation: 'spin 1s linear infinite',
color: 'primary.main', '@keyframes spin': {
height: '56px', '0%': { transform: 'rotate(0deg)' },
'100%': { transform: 'rotate(360deg)' },
},
}} }}
> >
{buttonState === 'counting' ? ( <Refresh size="20" color="white" />
<CountDownTimer </Box>
initialSeconds={60} ) : (
onComplete={() => setButtonState('default')} t('settingForm.checkCode')
/> )}
) : ( </Button>
t('settingForm.verificationCodeButton')
)}
</Button>
</Box>
)}
</Box> </Box>
)}
{buttonState === 'counting' && !isVerified && ( <Toast
<Box color="success"
sx={{ open={showToast}
display: 'flex', onClose={() => setShowToast(false)}
flexWrap: 'wrap', >
gap: 2, {t('settingForm.successfulChangePhone')}
mt: 2, </Toast>
height: '88px', </Box>
}} ) : (
> <Box sx={{ px: { xs: 2, sm: 4 } }}>
<TextField {phones.map((item, index) => (
name="verificationCode" <Box
label={t('settingForm.verificationCode')} key={index}
type="tel" sx={{
value={verificationCode} display: 'flex',
onChange={handleVerificationCodeChange} flexDirection: 'column',
sx={{ flex: 1, minWidth: '250px' }} justifyContent: 'center',
placeholder={t('settingForm.verificationCode')} py: 2,
inputProps={{ width: '100%',
dir: 'rtl', }}
style: { textAlign: 'right' }, >
}} <Typography variant="h6" color="text.primary">
/> {item.phone}
</Typography>
<Button <Typography variant="caption" color="text.secondary">
variant="contained" {item.time}
onClick={handleVerifyClick} </Typography>
disabled={isVerifying || verificationCode.length === 0} </Box>
sx={{ ))}
textTransform: 'none', </Box>
minWidth: '170px', )}
backgroundColor: 'primary.main', </CardContainer>
height: '56px',
}}
>
{isVerifying ? (
<Box
component="span"
sx={{
display: 'flex',
animation: 'spin 1s linear infinite',
'@keyframes spin': {
'0%': { transform: 'rotate(0deg)' },
'100%': { transform: 'rotate(360deg)' },
},
}}
>
<Refresh size="20" color="white" />
</Box>
) : (
t('settingForm.checkCode')
)}
</Button>
</Box>
)}
<CustomAlert
message={t('settingForm.successfulChangePhone')}
rtl
open={showEmailAlert}
onClose={() => setShowEmailAlert(false)}
severity="success"
duration={4000}
delayOnClose={2000}
/>
</Box>
) : (
<Box sx={{ px: { xs: 2, sm: 4 } }}>
{phones.map((item, index) => (
<Box
key={index}
sx={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
height: '148px',
width: '100%',
}}
>
<Typography variant="h6" color="text.primary">
{item.phone}
</Typography>
<Typography variant="caption" color="text.secondary">
{item.time}
</Typography>
</Box>
))}
</Box>
)}
</CardContainer>
</Box>
); );
} }

View File

@@ -1,12 +1,4 @@
import { import React, { useState } from 'react';
Google,
Apple,
Sms,
Trash,
CloseSquare,
Message,
ArrowDown3,
} from 'iconsax-react';
import { import {
Box, Box,
Button, Button,
@@ -20,10 +12,21 @@ import {
MenuItem, MenuItem,
ListItemIcon, ListItemIcon,
ListItemText, ListItemText,
useMediaQuery,
} from '@mui/material'; } from '@mui/material';
import React, { useState } from 'react'; import type { Theme } from '@mui/material/styles';
import { CardContainer } from '@/components/CardContainer';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { CardContainer } from '@/components/CardContainer';
import {
Google,
Apple,
Sms,
Trash,
CloseSquare,
Message,
ArrowDown3,
} from 'iconsax-react';
export function SocialMedia() { export function SocialMedia() {
const { t } = useTranslation('profileSetting'); const { t } = useTranslation('profileSetting');
@@ -32,14 +35,17 @@ export function SocialMedia() {
const [emailError, setEmailError] = useState(false); const [emailError, setEmailError] = useState(false);
const [anchor, setAnchor] = useState<null | HTMLElement>(null); const [anchor, setAnchor] = useState<null | HTMLElement>(null);
const openMenu = Boolean(anchor); const openMenu = Boolean(anchor);
const fullScreen = useMediaQuery((theme: Theme) =>
theme.breakpoints.down('sm'),
);
const handleOpenDialog = () => setOpenDialog(true); const handleOpenDialog = () => setOpenDialog(true);
const handleCloseDialog = () => setOpenDialog(false); const handleCloseDialog = () => setOpenDialog(false);
const handleClickMenu = (e: React.MouseEvent<HTMLButtonElement>) => { const handleClickMenu = (e: React.MouseEvent<HTMLButtonElement>) =>
setAnchor(e.currentTarget); setAnchor(e.currentTarget);
};
const handleCloseMenu = () => setAnchor(null); const handleCloseMenu = () => setAnchor(null);
const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value; const value = e.target.value;
setEmailInput(value); setEmailInput(value);
@@ -49,271 +55,277 @@ export function SocialMedia() {
const emailList = [ const emailList = [
{ email: 'emailtemp@email.com', provider: 'email', time: '1 ماه پیش' }, { email: 'emailtemp@email.com', provider: 'email', time: '1 ماه پیش' },
{ email: 'emailtemp@gmail.com', provider: 'google', time: '1 ماه پیش' }, { email: 'emailtemp@gmail.com', provider: 'google', time: '1 ماه پیش' },
]; { email: 'emailtemp@icloud.com', provider: 'apple', time: '1 ماه پیش' },
] as const;
return ( return (
<Box <CardContainer
sx={{ title={t('settingForm.titleSocial')}
display: 'flex', subtitle={t('settingForm.descriptionSocial')}
justifyContent: 'center', action={
width: '100%', <Box
px: 2, sx={{
backgroundColor: 'background.paper', display: 'flex',
}} justifyContent: 'flex-start',
> flexWrap: 'wrap',
<CardContainer gap: 1,
title={t('settingForm.titleSocial')} }}
subtitle={t('settingForm.descriptionSocial')} >
action={
<Box <Box
sx={{ sx={{
display: 'flex', display: 'flex',
justifyContent: 'flex-start', flexDirection: { xs: 'column', sm: 'row' },
alignItems: { xs: 'stretch', sm: 'flex-start' },
gap: 1,
width: '100%',
}}
>
<Button
onClick={handleClickMenu}
variant="outlined"
sx={{
width: '100%',
maxWidth: { sm: 187 },
textTransform: 'none',
border: '1px solid',
borderColor: 'primary.main',
borderRadius: '8px',
color: 'primary.main',
fontSize: '14px',
px: 2,
py: 1,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
whiteSpace: 'nowrap',
}}
>
<Box
component="span"
sx={{
overflow: 'hidden',
textOverflow: 'ellipsis',
direction: 'rtl',
}}
>
{t('settingForm.addEmailOrSocialButton')}
</Box>
<ArrowDown3 size="20" color="#2979FF" />
</Button>
</Box>
<Menu
anchorEl={anchor}
open={openMenu}
onClose={handleCloseMenu}
PaperProps={{
sx: {
minWidth: 240,
maxWidth: '90vw',
},
}}
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
>
<MenuItem
onClick={() => {
handleCloseMenu();
handleOpenDialog();
}}
>
<ListItemIcon>
<Message size={20} color="black" />
</ListItemIcon>
<ListItemText>{t('settingForm.email')}</ListItemText>
</MenuItem>
<MenuItem>
<ListItemIcon>
<Google size={20} color="#4285F4" />
</ListItemIcon>
<ListItemText>{t('settingForm.google')}</ListItemText>
</MenuItem>
<MenuItem>
<ListItemIcon>
<Apple size={20} color="black" />
</ListItemIcon>
<ListItemText>{t('settingForm.apple')}</ListItemText>
</MenuItem>
</Menu>
</Box>
}
>
<Box sx={{ width: '100%', borderRadius: '8px', p: { xs: 1, sm: 2 } }}>
{emailList.map((item, index) => (
<Box
key={index}
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
flexWrap: 'wrap', flexWrap: 'wrap',
py: 1,
gap: 1, gap: 1,
}} }}
> >
<Box <Box
sx={{
display: 'flex',
flexDirection: { xs: 'column', sm: 'row' },
alignItems: { xs: 'stretch', sm: 'flex-start' },
gap: 1,
width: '100%',
}}
>
<Button
onClick={handleClickMenu}
variant="outlined"
sx={{
width: '100%',
maxWidth: { sm: '187px' },
textTransform: 'none',
border: '1px solid',
borderColor: 'primary.main',
borderRadius: '8px',
color: 'primary.main',
fontSize: '14px',
px: 2,
py: 1,
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
whiteSpace: 'nowrap',
}}
>
<Box
component="span"
sx={{
overflow: 'hidden',
textOverflow: 'ellipsis',
direction: 'rtl',
}}
>
{t('settingForm.addEmailOrSocialButton')}
</Box>
<ArrowDown3 size="20" color="#2979FF" />
</Button>
</Box>
<Menu
anchorEl={anchor}
open={openMenu}
onClose={handleCloseMenu}
PaperProps={{
sx: {
width: anchor ? `${anchor.offsetWidth}px` : undefined,
},
}}
>
<MenuItem
onClick={() => {
handleCloseMenu();
handleOpenDialog();
}}
>
<ListItemIcon>
<Message size={20} color="black" />
</ListItemIcon>
<ListItemText>{t('settingForm.email')}</ListItemText>
</MenuItem>
<MenuItem>
<ListItemIcon>
<Google size={20} color="#4285F4" />
</ListItemIcon>
<ListItemText>{t('settingForm.google')}</ListItemText>
</MenuItem>
<MenuItem>
<ListItemIcon>
<Apple size={20} color="black" />
</ListItemIcon>
<ListItemText>{t('settingForm.apple')}</ListItemText>
</MenuItem>
</Menu>
</Box>
}
>
<Box
sx={{
width: '100%',
borderRadius: '8px',
p: { xs: 1, sm: 2 },
}}
>
{emailList.map((item, index) => (
<Box
key={index}
sx={{ sx={{
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between',
flexWrap: 'wrap',
py: 1,
gap: 1, gap: 1,
minWidth: 0,
}} }}
> >
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}> {item.provider === 'google' && (
{item.provider === 'google' && ( <Google size="20" variant="Bold" color="#4285F4" />
<Google size="20" variant="Bold" color="#4285F4" /> )}
)} {item.provider === 'apple' && (
{item.provider === 'apple' && ( <Apple size="20" variant="Bold" color="black" />
<Apple size="20" variant="Bold" color="black" /> )}
)} {item.provider === 'email' && (
{item.provider === 'email' && ( <Sms size="20" variant="Bold" color="#1976d2" />
<Sms size="20" variant="Bold" color="#1976d2" /> )}
)}
<Box>
<Typography variant="h6">{item.email}</Typography>
<Typography variant="caption" color="text.secondary">
{item.time}
</Typography>
</Box>
</Box>
<IconButton size="small">
<Trash size="20" color="gray" variant="Outline" />
</IconButton>
</Box>
))}
</Box>
<Dialog <Box sx={{ minWidth: 0 }}>
open={openDialog} <Typography variant="h6" noWrap>
onClose={handleCloseDialog} {item.email}
fullWidth </Typography>
maxWidth="xs" <Typography variant="caption" color="text.secondary">
{item.time}
</Typography>
</Box>
</Box>
<IconButton size="small">
<Trash size="20" color="gray" variant="Outline" />
</IconButton>
</Box>
))}
</Box>
<Dialog
open={openDialog}
onClose={handleCloseDialog}
fullWidth
maxWidth="xs"
fullScreen={fullScreen}
sx={{
'& .MuiDialog-paper': { m: { xs: 1, sm: 'auto' }, borderRadius: 2 },
}}
>
<DialogTitle
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
fontWeight: 'bold',
fontSize: '16px',
gap: 1,
}}
> >
<DialogTitle <IconButton onClick={handleCloseDialog} size="small">
<CloseSquare size="24" color="#F5F5F5" />
</IconButton>
{t('settingForm.addEmailButton')}
</DialogTitle>
<DialogContent>
<Box
sx={{ sx={{
width: '100%',
display: 'flex', display: 'flex',
flexDirection: 'row', flexDirection: 'column',
alignItems: 'center', gap: 2,
fontWeight: 'bold',
fontSize: '16px',
gap: 1,
}} }}
> >
<IconButton onClick={handleCloseDialog} size="small"> <Box>
<CloseSquare size="24" color="#F5F5F5" /> <Typography fontWeight="bold">
</IconButton> {t('settingForm.newEmail')}
{t('settingForm.addEmailButton')} </Typography>
</DialogTitle> <Typography variant="body2" color="text.secondary">
<DialogContent> {t('settingForm.dialogHeader')}
</Typography>
</Box>
<TextField
fullWidth
type="email"
value={emailInput}
onChange={handleEmailChange}
error={emailError}
helperText={emailError ? t('settingForm.emailError') : ''}
label={t('settingForm.email')}
placeholder="abc@email.com"
sx={{ '& .MuiOutlinedInput-root': { borderRadius: '8px' } }}
/>
<Button
variant="contained"
fullWidth
sx={{ textTransform: 'none', borderRadius: '8px' }}
disabled={emailError || emailInput === ''}
>
{t('settingForm.verificationCodeButton')}
</Button>
<Box sx={{ display: 'flex', alignItems: 'center', my: 2 }}>
<Box sx={{ flex: 1, height: 1, bgcolor: '#ccc' }} />
<Typography sx={{ mx: 1, fontSize: '12px', color: 'gray' }}>
{t('settingForm.or')}
</Typography>
<Box sx={{ flex: 1, height: 1, bgcolor: '#ccc' }} />
</Box>
<Box <Box
sx={{ sx={{
width: '100%',
display: 'flex', display: 'flex',
flexDirection: 'column', gap: 1,
gap: 2, flexDirection: { xs: 'column', sm: 'row' },
}} }}
> >
<Box>
<Typography fontWeight="bold">
{t('settingForm.newEmail')}
</Typography>
<Typography variant="body2" color="text.secondary">
{t('settingForm.dialogHeader')}
</Typography>
</Box>
<TextField
fullWidth
type="email"
value={emailInput}
onChange={handleEmailChange}
error={emailError}
helperText={emailError ? t('settingForm.emailError') : ''}
label={t('settingForm.email')}
placeholder="abc@email.com"
sx={{
'& .MuiOutlinedInput-root': {
borderRadius: '8px',
},
}}
/>
<Button <Button
variant="contained"
fullWidth fullWidth
sx={{ sx={{
textTransform: 'none', textTransform: 'none',
border: 2,
borderColor: '#1976d2',
color: '#1976d2',
borderRadius: '8px', borderRadius: '8px',
// backgroundColor: '#1976d2',
}}
disabled={emailError || emailInput === ''}
>
{t('settingForm.verificationCodeButton')}
</Button>
<Box sx={{ display: 'flex', alignItems: 'center', my: 2 }}>
<Box sx={{ flex: 1, height: '1px', backgroundColor: '#ccc' }} />
<Typography sx={{ mx: 1, fontSize: '12px', color: 'gray' }}>
{t('settingForm.or')}
</Typography>
<Box sx={{ flex: 1, height: '1px', backgroundColor: '#ccc' }} />
</Box>
<Box
sx={{
display: 'flex', display: 'flex',
gap: 1, alignItems: 'center',
flexDirection: { xs: 'column', sm: 'row' }, justifyContent: 'center',
}} }}
> >
<Button <Google
fullWidth size="20"
sx={{ color="#4285F4"
textTransform: 'none', style={{ marginInlineStart: 8 }}
border: 2, />
borderColor: '#1976d2', {t('settingForm.google')}
color: '#1976d2', </Button>
borderRadius: '8px', <Button
display: 'flex', fullWidth
alignItems: 'center', sx={{
justifyContent: 'center', textTransform: 'none',
}} border: 2,
> borderColor: '#1976d2',
<Google size="20" color="#4285F4" style={{ marginLeft: 8 }} /> color: '#1976d2',
{t('settingForm.google')} borderRadius: '8px',
</Button> display: 'flex',
<Button alignItems: 'center',
fullWidth justifyContent: 'center',
sx={{ }}
textTransform: 'none', >
border: 2, <Apple
borderColor: '#1976d2', size="20"
color: '#1976d2', color="black"
borderRadius: '8px', style={{ marginInlineStart: 8 }}
display: 'flex', />
alignItems: 'center', {t('settingForm.apple')}
justifyContent: 'center', </Button>
}}
>
<Apple size="20" color="black" style={{ marginLeft: 8 }} />
{t('settingForm.apple')}
</Button>
</Box>
</Box> </Box>
</DialogContent> </Box>
</Dialog> </DialogContent>
</CardContainer> </Dialog>
</Box> </CardContainer>
); );
} }

View File

@@ -1,13 +1,14 @@
import { Box } from '@mui/material';
import { PersonalInformation } from './PersonalInformation'; import { PersonalInformation } from './PersonalInformation';
import { PhoneNumber } from './PhoneNumber'; import { PhoneNumber } from './PhoneNumber';
import { SocialMedia } from './SocialMedia'; import { SocialMedia } from './SocialMedia';
export function UserForm() { export function UserForm() {
return ( return (
<> <Box sx={{ width: '100%', overflowX: 'clip' }}>
<PersonalInformation /> <PersonalInformation />
<PhoneNumber /> <PhoneNumber />
<SocialMedia /> <SocialMedia />
</> </Box>
); );
} }