fix: responsiveness of sideNav component

This commit is contained in:
Koosha Lahouti
2025-08-05 17:15:35 -07:00
parent 512b1ad1cf
commit b09d7c96aa
9 changed files with 511 additions and 435 deletions

View File

@@ -0,0 +1,22 @@
import { Box } from '@mui/material';
import React from 'react';
interface PageWrapperProps {
children: React.ReactNode;
}
export function PageWrapper({ children }: PageWrapperProps) {
return (
<Box
sx={{
mx: 'auto',
width: { xs: '100%', sm: '754px' },
backgroundColor: 'background.paper',
display: 'flex',
flexDirection: 'column',
}}
>
{children}
</Box>
);
}

View File

@@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next';
import { DeviceMessage, Logout } from 'iconsax-react'; import { DeviceMessage, Logout } from 'iconsax-react';
import Logo from '@/components/Logo'; import Logo from '@/components/Logo';
import { CardContainer } from '@/components/CardContainer'; import { CardContainer } from '@/components/CardContainer';
import { PageWrapper } from '../PageWrapper';
export function ActiveDevices() { export function ActiveDevices() {
const { t } = useTranslation('activeDevices'); const { t } = useTranslation('activeDevices');
@@ -39,25 +40,13 @@ export function ActiveDevices() {
]; ];
return ( return (
<Box <PageWrapper>
sx={{ <Box sx={{ display: 'flex', alignItems: 'center', py: 2, height: 84 }}>
mx: 'auto',
backgroundColor: 'background.paper',
display: 'flex',
flexDirection: 'column',
}}
>
<Box
sx={{
display: 'flex',
alignItems: 'center',
py: 2,
height: 84,
}}
>
<Logo /> <Logo />
</Box> </Box>
<Box sx={{ width: '100%', height: 1, bgcolor: 'divider' }} /> <Box sx={{ width: '100%', height: 1, bgcolor: 'divider' }} />
<CardContainer <CardContainer
title={t('active.activeDevices')} title={t('active.activeDevices')}
subtitle={t('active.activeDevicesCaption')} subtitle={t('active.activeDevicesCaption')}
@@ -68,8 +57,8 @@ export function ActiveDevices() {
borderRadius: '10px', borderRadius: '10px',
borderColor: 'error.main', borderColor: 'error.main',
color: 'error.main', color: 'error.main',
border: '1px solid',
textTransform: 'none', textTransform: 'none',
width: { xs: '100%', sm: 'auto' },
}} }}
> >
{t('active.deletDevicesButton')} {t('active.deletDevicesButton')}
@@ -82,111 +71,125 @@ export function ActiveDevices() {
py: 2, py: 2,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
gap: 2, // gap: 2,
flex: 1,
}} }}
> >
<Box sx={{ width: '100%', maxWidth: '754px' }}> {devices.map((device) => (
{devices.map((device) => ( <Box
key={device.id}
sx={{
display: 'flex',
flexDirection: { xs: 'column', sm: 'row' },
alignItems: { xs: 'flex-start', sm: 'center' },
minHeight: 50,
}}
>
<Typography
variant="body2"
sx={{
flexBasis: { xs: '100%', sm: 'auto' },
mb: { xs: 1, sm: 0 },
minWidth: { sm: '138px' },
order: { xs: 1, sm: 1 },
}}
>
{device.timeAndDate}
</Typography>
<Box <Box
key={device.id}
sx={{ sx={{
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: 1, gap: 1,
height: 50, flexBasis: { xs: '100%', sm: 'auto' },
mb: { xs: 1, sm: 0 },
minWidth: { sm: '138px' },
order: { xs: 2, sm: 2 },
}} }}
> >
<Typography <DeviceMessage size={24} color="#82B1FF" />
variant="body2" <Typography variant="body2" noWrap>
sx={{ width: { xs: '100%', sm: '138px' } }} {device.deviceModel}
>
{device.timeAndDate}
</Typography> </Typography>
<Box </Box>
sx={{
display: 'flex', <Typography
alignItems: 'center', variant="body2"
width: { xs: '100%', sm: '138px' }, sx={{
gap: 1, flexBasis: { xs: '100%', sm: 'auto' },
}} mb: { xs: 1, sm: 0 },
> minWidth: { sm: '138px' },
<DeviceMessage size={24} color="#82B1FF" /> order: { xs: 3, sm: 3 },
<Typography }}
variant="body2" >
noWrap {device.ip}
sx={{ width: { xs: '100%', sm: '138px' } }} </Typography>
>
{device.deviceModel} <Box
</Typography> sx={{
</Box> flexBasis: { xs: '100%', sm: 'auto' },
<Typography mb: { xs: 1, sm: 0 },
variant="body2" textAlign: { xs: 'left', sm: 'center' },
sx={{ width: { xs: '100%', sm: '138px' } }} minWidth: { sm: '138px' },
> order: { xs: 4, sm: 4 },
{device.ip} }}
</Typography> >
<Box {device.current && (
sx={{
width: { xs: '100%', sm: '138px' },
textAlign: { xs: 'left', sm: 'center' },
}}
>
{device.current && (
<Button
variant="outlined"
sx={{
borderRadius: '15px',
border: '2px solid',
borderColor: 'success.main',
height: '30px',
whiteSpace: 'nowrap',
color: 'success.main',
// width: '93px',
textTransform: 'none',
}}
>
{t('active.currentDevice')}
</Button>
)}
</Box>
<Box
sx={{
width: { xs: '100%', sm: '138px' },
textAlign: { xs: 'left', sm: 'center' },
}}
>
<Button <Button
size="small"
variant="outlined" variant="outlined"
startIcon={<Logout size={18} color="#E53935" />}
disabled={device.current}
sx={{ sx={{
color: 'error.main',
// width: '80%',
minWidth: 0,
borderRadius: '15px', borderRadius: '15px',
borderColor: 'error.main', border: '2px solid',
p: '4px 8px', borderColor: 'success.main',
display: 'flex', height: '30px',
alignItems: 'center',
justifyContent: 'center',
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
color: 'success.main',
textTransform: 'none', textTransform: 'none',
'& .MuiButton-startIcon': {
marginRight: '4px',
marginLeft: 0,
},
}} }}
> >
{t('active.deleteDevice')} {t('active.currentDevice')}
</Button> </Button>
</Box> )}
</Box> </Box>
))}
</Box> <Box
sx={{
flexBasis: { xs: '100%', sm: 'auto' },
mb: { xs: 1, sm: 0 },
textAlign: { xs: 'left', sm: 'center' },
minWidth: { sm: '150px' },
order: { xs: 5, sm: 5 },
}}
>
<Button
size="small"
variant="outlined"
startIcon={<Logout size={18} color="#E53935" />}
disabled={device.current}
sx={{
color: 'error.main',
minWidth: 0,
borderRadius: '15px',
borderColor: 'error.main',
p: '4px 8px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
whiteSpace: 'nowrap',
textTransform: 'none',
'& .MuiButton-startIcon': {
marginRight: '4px',
marginLeft: 0,
},
}}
>
{t('active.deleteDevice')}
</Button>
</Box>
</Box>
))}
</Box> </Box>
</CardContainer> </CardContainer>
</Box> </PageWrapper>
); );
} }

View File

@@ -19,6 +19,7 @@ import { PasswordValidationItem } from './PasswordValidation';
import { Toast } from '@/components/Toast'; import { Toast } from '@/components/Toast';
import Logo from '@/components/Logo'; import Logo from '@/components/Logo';
import { CardContainer } from '@/components/CardContainer'; import { CardContainer } from '@/components/CardContainer';
import { PageWrapper } from '../PageWrapper';
export function PasswordSecurity() { export function PasswordSecurity() {
const theme = useTheme(); const theme = useTheme();
@@ -70,14 +71,7 @@ export function PasswordSecurity() {
}, [password, validPassword]); }, [password, validPassword]);
return ( return (
<Box <PageWrapper>
sx={{
mx: 'auto',
backgroundColor: 'background.paper',
display: 'flex',
flexDirection: 'column',
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', py: 2, height: 84 }}> <Box sx={{ display: 'flex', alignItems: 'center', py: 2, height: 84 }}>
<Logo /> <Logo />
</Box> </Box>
@@ -148,7 +142,6 @@ export function PasswordSecurity() {
</Typography> </Typography>
)} )}
</Box> </Box>
<Dialog <Dialog
open={open} open={open}
onClose={handleClose} onClose={handleClose}
@@ -198,7 +191,6 @@ export function PasswordSecurity() {
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
sx={{ '& .MuiOutlinedInput-root': { borderRadius: 2 } }} sx={{ '& .MuiOutlinedInput-root': { borderRadius: 2 } }}
/> />
{password && showValidation && ( {password && showValidation && (
<Box sx={{ display: 'flex', justifyContent: 'center' }}> <Box sx={{ display: 'flex', justifyContent: 'center' }}>
<Box sx={{ maxWidth: '364px', width: '100%' }}> <Box sx={{ maxWidth: '364px', width: '100%' }}>
@@ -221,7 +213,6 @@ export function PasswordSecurity() {
</Box> </Box>
</Box> </Box>
)} )}
<TextField <TextField
label={t('securityForm.confirmPassword')} label={t('securityForm.confirmPassword')}
type="password" type="password"
@@ -257,7 +248,6 @@ export function PasswordSecurity() {
mt: 2, mt: 2,
}} }}
/> />
{password && showValidation && ( {password && showValidation && (
<Box sx={{ display: 'flex', justifyContent: 'center' }}> <Box sx={{ display: 'flex', justifyContent: 'center' }}>
<Box sx={{ maxWidth: '364px', width: '100%' }}> <Box sx={{ maxWidth: '364px', width: '100%' }}>
@@ -280,7 +270,6 @@ export function PasswordSecurity() {
</Box> </Box>
</Box> </Box>
)} )}
<TextField <TextField
label={t('securityForm.confirmPassword')} label={t('securityForm.confirmPassword')}
type="password" type="password"
@@ -325,7 +314,6 @@ export function PasswordSecurity() {
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
<Toast <Toast
color="success" color="success"
open={showPasswordAlert} open={showPasswordAlert}
@@ -335,6 +323,6 @@ export function PasswordSecurity() {
</Toast> </Toast>
</Box> </Box>
</CardContainer> </CardContainer>
</Box> </PageWrapper>
); );
} }

View File

@@ -1,6 +1,7 @@
import { Box, Typography, Button } from '@mui/material'; import { Box, Typography, Button } from '@mui/material';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { CardContainer } from '@/components/CardContainer'; import { CardContainer } from '@/components/CardContainer';
import { PageWrapper } from '../PageWrapper';
export function RecentLogins() { export function RecentLogins() {
const { t } = useTranslation('security'); const { t } = useTranslation('security');
@@ -22,27 +23,25 @@ export function RecentLogins() {
]; ];
return ( return (
<Box <PageWrapper>
sx={{
mx: 'auto',
backgroundColor: 'background.paper',
display: 'flex',
flexDirection: 'column',
}}
>
<CardContainer <CardContainer
title={t('securityForm.recentLogins')} title={t('securityForm.recentLogins')}
subtitle={t('securityForm.description')} subtitle={t('securityForm.description')}
> >
<Box <Box
sx={{ sx={{
px: { xs: 2, sm: 3, md: 4 }, // px: { xs: 2, sm: 3, md: 4 },
py: 2, py: 2,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
gap: 2, gap: 2,
bgcolor: 'background.paper', bgcolor: 'background.paper',
flex: 1, flex: 1,
borderBottom: '1px solid',
borderColor: 'divider',
'&:last-child': {
borderBottom: 'none',
},
}} }}
> >
<Box sx={{ width: '100%', maxWidth: '754px', px: 4 }}> <Box sx={{ width: '100%', maxWidth: '754px', px: 4 }}>
@@ -50,31 +49,54 @@ export function RecentLogins() {
<Box <Box
sx={{ sx={{
display: 'flex', display: 'flex',
alignItems: 'center', flexDirection: { xs: 'column', sm: 'row' },
gap: 1, alignItems: { xs: 'flex-start', sm: 'center' },
height: '50px', minHeight: 50,
}} }}
key={d.id} key={d.id}
> >
<Typography <Typography
variant="body2" variant="body2"
sx={{ width: { xs: '100%', sm: '172.5px' } }} sx={{
flexBasis: { xs: '100%', sm: 'auto' },
mb: { xs: 1, sm: 0 },
minWidth: { sm: '172.5px' },
order: { xs: 1, sm: 1 },
}}
> >
{d.time} {d.time}
</Typography> </Typography>
<Typography <Typography
variant="body2" variant="body2"
sx={{ width: { xs: '100%', sm: '172.5px' } }} sx={{
flexBasis: { xs: '100%', sm: 'auto' },
mb: { xs: 1, sm: 0 },
minWidth: { sm: '172.5px' },
order: { xs: 1, sm: 1 },
}}
> >
{d.device} {d.device}
</Typography> </Typography>
<Typography <Typography
variant="body2" variant="body2"
sx={{ width: { xs: '100%', sm: '172.5px' } }} sx={{
flexBasis: { xs: '100%', sm: 'auto' },
mb: { xs: 1, sm: 0 },
minWidth: { sm: '172.5px' },
order: { xs: 1, sm: 1 },
}}
> >
{d.ip} {d.ip}
</Typography> </Typography>
<Box sx={{ width: { xs: '100%', sm: '172.5px' } }}> <Box
sx={{
flexBasis: { xs: '100%', sm: 'auto' },
mb: { xs: 1, sm: 0 },
textAlign: { xs: 'left', sm: 'center' },
minWidth: { sm: '172.5px' },
order: { xs: 4, sm: 4 },
}}
>
{d.current && ( {d.current && (
<Button <Button
variant="outlined" variant="outlined"
@@ -85,9 +107,7 @@ export function RecentLogins() {
height: '30px', height: '30px',
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
color: 'success.main', color: 'success.main',
width: '93px',
textTransform: 'none', textTransform: 'none',
fontSize: '0.75rem',
}} }}
> >
{t('securityForm.currentDevice')} {t('securityForm.currentDevice')}
@@ -99,6 +119,6 @@ export function RecentLogins() {
</Box> </Box>
</Box> </Box>
</CardContainer> </CardContainer>
</Box> </PageWrapper>
); );
} }

View File

@@ -11,6 +11,7 @@ import { CardContainer } from '@/components/CardContainer';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ThemeToggleButton } from '@/components/ThemToggle'; import { ThemeToggleButton } from '@/components/ThemToggle';
import Logo from '@/components/Logo'; import Logo from '@/components/Logo';
import { PageWrapper } from '../PageWrapper';
export function Setting() { export function Setting() {
const { t, i18n } = useTranslation(['setting']); const { t, i18n } = useTranslation(['setting']);
@@ -57,113 +58,143 @@ export function Setting() {
isEditing ? handleSave() : setIsEditing(true); isEditing ? handleSave() : setIsEditing(true);
return ( return (
<Box sx={{ backgroundColor: 'background.paper', minHeight: '100vh' }}> <PageWrapper>
<Box <Box sx={{ display: 'flex', alignItems: 'center', py: 2, height: 84 }}>
sx={{
display: 'flex',
alignItems: 'center',
py: 2,
height: 84,
}}
>
<Logo /> <Logo />
</Box> </Box>
<Box sx={{ width: '100%', height: 1, bgcolor: 'divider' }} /> <Box sx={{ width: '100%', height: 1, bgcolor: 'divider' }} />
<CardContainer <Box
title={t('settings.title')} sx={{
subtitle={t('settings.description')} width: '100%', // always span full width
highlighted={isEditing} px: { xs: 0, sm: 3 }, // no side padding on xs, keep padding on sm+
action={ mx: 0, // zero out any horizontal margin
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}> }}
{isEditing && ( >
<CardContainer
title={t('settings.title')}
subtitle={t('settings.description')}
highlighted={isEditing}
action={
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
{isEditing && (
<Button
variant="text"
onClick={handleCancel}
size="large"
sx={{
color: 'primary.main',
textTransform: 'none',
width: { xs: '100%', sm: 'auto' },
fontSize: { xs: '0.85rem', sm: '1rem' },
}}
>
{t('settings.rejectButton')}
</Button>
)}
<Button <Button
variant="text" onClick={handleEditToggle}
onClick={handleCancel}
size="large" size="large"
variant="outlined"
sx={{ sx={{
color: 'primary.main',
textTransform: 'none', textTransform: 'none',
width: { xs: '100%', sm: 'auto' }, border: '1px solid',
fontSize: { xs: '0.85rem', sm: '1rem' }, 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' },
}} }}
> >
{t('settings.rejectButton')} {isEditing
? t('settings.saveButton')
: t('settings.editButton')}
</Button> </Button>
)} </Box>
<Button }
onClick={handleEditToggle}
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('settings.saveButton') : t('settings.editButton')}
</Button>
</Box>
}
>
<Box
sx={{
px: { xs: 2, sm: 3, md: 4 },
py: 2,
display: 'flex',
flexDirection: 'column',
gap: 2,
bgcolor: 'background.paper',
}}
> >
<Box <Box
sx={{ sx={{
px: { xs: 2, sm: 3, md: 4 },
py: 2,
display: 'flex', display: 'flex',
flexDirection: { xs: 'column', sm: 'row' }, flexDirection: 'column',
gap: 2, gap: 2,
mt: 2, bgcolor: 'background.paper',
mx: 'auto', width: '100%',
width: { xs: '100%', md: 690 },
// px: 4,
}} }}
> >
<Box sx={{ flex: 1, maxWidth: { sm: 310 }, width: '100%' }}> <Box
{isEditing ? ( sx={{
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}> display: 'flex',
<Typography variant="body1">{t('settings.theme')}</Typography> flexDirection: { xs: 'column', sm: 'row' },
<ThemeToggleButton /> gap: 2,
</Box> mt: 2,
) : ( width: '100%',
<Box> }}
<Typography variant="caption" color="text.secondary"> >
{t('settings.theme')} <Box sx={{ flex: 1 }}>
</Typography> {isEditing ? (
<Typography variant="body1"> <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{mode === 'light' <Typography variant="body1">
? t('settings.light') {t('settings.theme')}
: t('settings.dark')} </Typography>
</Typography> <ThemeToggleButton />
</Box> </Box>
)} ) : (
<Box>
<Typography variant="caption" color="text.secondary">
{t('settings.theme')}
</Typography>
<Typography variant="body1">
{mode === 'light'
? t('settings.light')
: t('settings.dark')}
</Typography>
</Box>
)}
</Box>
<Box sx={{ flex: 1 }}>
{isEditing ? (
<Autocomplete
options={languageOptions}
getOptionLabel={(o) => o.label}
value={
languageOptions.find((o) => o.code === draftLanguage) ||
null
}
onChange={handleDraftLanguageChange}
renderInput={(p) => (
<TextField {...p} label={t('settings.language')} />
)}
size="medium"
fullWidth
/>
) : (
<Box>
<Typography variant="caption" color="text.secondary">
{t('settings.language')}
</Typography>
<Typography variant="body1">
{
languageOptions.find((o) => o.code === savedLanguage)
?.label
}
</Typography>
</Box>
)}
</Box>
</Box> </Box>
<Box sx={{ flex: 1, maxWidth: { sm: 310 }, width: '100%' }}> <Box sx={{ mt: 2, width: '100%' }}>
{isEditing ? ( {isEditing ? (
<Autocomplete <Autocomplete
options={languageOptions} options={calendarOptions}
getOptionLabel={(o) => o.label} value={selectedCalendar}
value={ onChange={(_, v) => v && setSelectedCalendar(v)}
languageOptions.find((o) => o.code === draftLanguage) ||
null
}
onChange={handleDraftLanguageChange}
renderInput={(p) => ( renderInput={(p) => (
<TextField {...p} label={t('settings.language')} /> <TextField {...p} label={t('settings.calendar')} />
)} )}
size="medium" size="medium"
fullWidth fullWidth
@@ -171,41 +202,15 @@ export function Setting() {
) : ( ) : (
<Box> <Box>
<Typography variant="caption" color="text.secondary"> <Typography variant="caption" color="text.secondary">
{t('settings.language')} {t('settings.calendar')}
</Typography>
<Typography variant="body1">
{
languageOptions.find((o) => o.code === savedLanguage)
?.label
}
</Typography> </Typography>
<Typography variant="body1">{selectedCalendar}</Typography>
</Box> </Box>
)} )}
</Box> </Box>
</Box> </Box>
<Box sx={{ mt: 2, maxWidth: { sm: 310 }, width: '100%' }}> </CardContainer>
{isEditing ? ( </Box>
<Autocomplete </PageWrapper>
options={calendarOptions}
value={selectedCalendar}
onChange={(_, v) => v && setSelectedCalendar(v)}
renderInput={(p) => (
<TextField {...p} label={t('settings.calendar')} />
)}
size="medium"
fullWidth
/>
) : (
<Box>
<Typography variant="caption" color="text.secondary">
{t('settings.calendar')}
</Typography>
<Typography variant="body1">{selectedCalendar}</Typography>
</Box>
)}
</Box>
</Box>
</CardContainer>
</Box>
); );
} }

View File

@@ -6,6 +6,7 @@ import { ProfileImage } from './personlInformation/ProfileImage';
import { InfoRowDisplay } from './personlInformation/InfoRowDisplay'; import { InfoRowDisplay } from './personlInformation/InfoRowDisplay';
import { InfoRowEdit } from './personlInformation/InfoRowEdit'; import { InfoRowEdit } from './personlInformation/InfoRowEdit';
import Logo from '@/components/Logo'; import Logo from '@/components/Logo';
import { PageWrapper } from '../PageWrapper';
export function PersonalInformation() { export function PersonalInformation() {
const { t } = useTranslation('profileSetting'); const { t } = useTranslation('profileSetting');
@@ -39,7 +40,7 @@ export function PersonalInformation() {
}; };
return ( return (
<Box sx={{ backgroundColor: 'background.paper' }}> <PageWrapper>
<Box <Box
sx={{ sx={{
display: 'flex', display: 'flex',
@@ -138,6 +139,6 @@ export function PersonalInformation() {
)} )}
</Box> </Box>
</CardContainer> </CardContainer>
</Box> </PageWrapper>
); );
} }

View File

@@ -1,10 +1,11 @@
import React, { useState, type ChangeEvent } from 'react'; import { 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 { useTranslation } from 'react-i18next'; 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 { Toast } from '@/components/Toast'; import { Toast } from '@/components/Toast';
import { PageWrapper } from '../PageWrapper';
export function PhoneNumber() { export function PhoneNumber() {
const { t } = useTranslation('profileSetting'); const { t } = useTranslation('profileSetting');
@@ -72,7 +73,7 @@ export function PhoneNumber() {
}; };
return ( return (
<Box sx={{ backgroundColor: 'background.paper' }}> <PageWrapper>
<CardContainer <CardContainer
title={t('settingForm.titlePhoneNumber')} title={t('settingForm.titlePhoneNumber')}
subtitle={t('settingForm.descriptionPhoneNumber')} subtitle={t('settingForm.descriptionPhoneNumber')}
@@ -283,6 +284,6 @@ export function PhoneNumber() {
</Box> </Box>
)} )}
</CardContainer> </CardContainer>
</Box> </PageWrapper>
); );
} }

View File

@@ -14,6 +14,8 @@ import {
ListItemText, ListItemText,
useMediaQuery, useMediaQuery,
} from '@mui/material'; } from '@mui/material';
import Slide from '@mui/material/Slide';
import type { TransitionProps } from '@mui/material/transitions';
import type { Theme } from '@mui/material/styles'; import type { Theme } from '@mui/material/styles';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { CardContainer } from '@/components/CardContainer'; import { CardContainer } from '@/components/CardContainer';
@@ -27,17 +29,34 @@ import {
Message, Message,
ArrowDown3, ArrowDown3,
} from 'iconsax-react'; } from 'iconsax-react';
import { PageWrapper } from '../PageWrapper';
const MobileSlide = React.forwardRef(function MobileSlide(
props: TransitionProps & { children: React.ReactElement<any, any> },
ref: React.Ref<unknown>,
) {
return <Slide direction="up" ref={ref} {...props} />;
});
export function SocialMedia() { export function SocialMedia() {
const { t } = useTranslation('profileSetting'); const { t } = useTranslation('profileSetting');
const [openDialog, setOpenDialog] = useState(false); const [openDialog, setOpenDialog] = useState(false);
const [emailInput, setEmailInput] = useState(''); const [emailInput, setEmailInput] = useState('');
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) => const fullScreen = useMediaQuery((theme: Theme) =>
theme.breakpoints.down('sm'), theme.breakpoints.down('sm'),
); );
const downMd = useMediaQuery((theme: Theme) => theme.breakpoints.down('md'));
const computedMaxWidth = (fullScreen ? 'xs' : downMd ? 'sm' : 'md') as
| 'xs'
| 'sm'
| 'md'
| 'lg'
| 'xl';
const handleOpenDialog = () => setOpenDialog(true); const handleOpenDialog = () => setOpenDialog(true);
const handleCloseDialog = () => setOpenDialog(false); const handleCloseDialog = () => setOpenDialog(false);
@@ -59,7 +78,7 @@ export function SocialMedia() {
] as const; ] as const;
return ( return (
<Box sx={{ backgroundColor: 'background.paper' }}> <PageWrapper>
<CardContainer <CardContainer
title={t('settingForm.titleSocial')} title={t('settingForm.titleSocial')}
subtitle={t('settingForm.descriptionSocial')} subtitle={t('settingForm.descriptionSocial')}
@@ -196,7 +215,7 @@ export function SocialMedia() {
</Box> </Box>
</Box> </Box>
<IconButton size="small"> <IconButton size="small" aria-label="Delete">
<Trash size="20" color="gray" variant="Outline" /> <Trash size="20" color="gray" variant="Outline" />
</IconButton> </IconButton>
</Box> </Box>
@@ -207,10 +226,18 @@ export function SocialMedia() {
open={openDialog} open={openDialog}
onClose={handleCloseDialog} onClose={handleCloseDialog}
fullWidth fullWidth
maxWidth="xs"
fullScreen={fullScreen} fullScreen={fullScreen}
sx={{ maxWidth={computedMaxWidth}
'& .MuiDialog-paper': { m: { xs: 1, sm: 'auto' }, borderRadius: 2 }, scroll="paper"
keepMounted
TransitionComponent={fullScreen ? MobileSlide : undefined}
PaperProps={{
sx: {
m: { xs: 0, sm: 2 },
borderRadius: { xs: 0, sm: 2 },
width: { xs: '100%', sm: 'auto' },
height: { xs: '100%', sm: 'auto' },
},
}} }}
> >
<DialogTitle <DialogTitle
@@ -221,113 +248,122 @@ export function SocialMedia() {
fontWeight: 'bold', fontWeight: 'bold',
fontSize: '16px', fontSize: '16px',
gap: 1, gap: 1,
p: { xs: 1.5, sm: 2 },
position: { xs: 'sticky', sm: 'static' },
top: 0,
bgcolor: 'background.paper',
zIndex: 1,
}} }}
> >
<IconButton onClick={handleCloseDialog}> <IconButton onClick={handleCloseDialog} aria-label="Close">
<CloseSquare size={24} color="#82B1FF" /> <CloseSquare size={24} color="#82B1FF" />
</IconButton> </IconButton>
{t('settingForm.addEmailButton')} {t('settingForm.addEmailButton')}
</DialogTitle> </DialogTitle>
<DialogContent> <DialogContent
dividers
sx={{
width: '100%',
display: 'flex',
flexDirection: 'column',
gap: 2,
px: { xs: 2, sm: 3 },
py: { xs: 1.5, sm: 2 },
}}
>
<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"
autoComplete="email"
inputMode="email"
sx={{ '& .MuiOutlinedInput-root': { borderRadius: 2 } }}
autoFocus
/>
<Button
variant="contained"
fullWidth
sx={{ textTransform: 'none', borderRadius: 2 }}
disabled={emailError || emailInput === ''}
>
{t('settingForm.verificationCodeButton')}
</Button>
<Box sx={{ display: 'flex', alignItems: 'center', my: 2 }}>
<Box sx={{ flex: 1, height: 1, bgcolor: 'divider' }} />
<Typography sx={{ mx: 1, fontSize: 12, color: 'text.secondary' }}>
{t('settingForm.or')}
</Typography>
<Box sx={{ flex: 1, height: 1, bgcolor: 'divider' }} />
</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={{ 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
sx={{ sx={{
textTransform: 'none',
border: 2,
borderColor: 'primary.main',
color: 'primary.main',
borderRadius: 2,
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: 'primary.main',
<Google color: 'primary.main',
size="20" borderRadius: 2,
color="#4285F4" display: 'flex',
style={{ marginInlineStart: 8 }} alignItems: 'center',
/> justifyContent: 'center',
{t('settingForm.google')} }}
</Button> >
<Button <Apple
fullWidth size="20"
sx={{ color="black"
textTransform: 'none', style={{ marginInlineStart: 8 }}
border: 2, />
borderColor: '#1976d2', {t('settingForm.apple')}
color: '#1976d2', </Button>
borderRadius: '8px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Apple
size="20"
color="black"
style={{ marginInlineStart: 8 }}
/>
{t('settingForm.apple')}
</Button>
</Box>
</Box> </Box>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
</CardContainer> </CardContainer>
</Box> </PageWrapper>
); );
} }

View File

@@ -1,3 +1,4 @@
import React, { useState } from 'react';
import { import {
createBrowserRouter, createBrowserRouter,
RouterProvider, RouterProvider,
@@ -18,7 +19,16 @@ import {
Sms, Sms,
Menu, Menu,
} from 'iconsax-react'; } from 'iconsax-react';
import { Box, Typography, useTheme, useMediaQuery } from '@mui/material'; import {
Box,
Typography,
useTheme,
useMediaQuery,
Drawer,
AppBar,
Toolbar,
IconButton,
} from '@mui/material';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ActiveDevices } from '../components/activeDevices/ActiveDevices'; import { ActiveDevices } from '../components/activeDevices/ActiveDevices';
@@ -58,19 +68,17 @@ export function Layout() {
const isMdUp = useMediaQuery(theme.breakpoints.up('md')); const isMdUp = useMediaQuery(theme.breakpoints.up('md'));
const location = useLocation(); const location = useLocation();
const { t } = useTranslation('sideMap'); const { t } = useTranslation('sideMap');
const [drawerOpen, setDrawerOpen] = useState(false);
const drawerWidth = 274; const contentMaxWidth = '100%';
const minimizedWidth = 64; const contentWidthMd = '810px';
const navWidth = isMdUp ? drawerWidth : minimizedWidth; const navWidthMd = '274px';
const contentWidth = 810;
const contentHeight = '78vh';
const totalWidth = navWidth + contentWidth;
const navConfig: NavItemConfig[] = [ const navConfig: NavItemConfig[] = [
{ {
text: t('side.account'), text: t('side.account'),
getIcon: (isSelected) => getIcon: (sel) =>
isSelected ? ( sel ? (
<ProfileCircle size={24} color="#1976d2" variant="Bold" /> <ProfileCircle size={24} color="#1976d2" variant="Bold" />
) : ( ) : (
<ProfileCircle size={24} color="#82B1FF" /> <ProfileCircle size={24} color="#82B1FF" />
@@ -79,8 +87,8 @@ export function Layout() {
children: [ children: [
{ {
text: t('side.personalInfo'), text: t('side.personalInfo'),
getIcon: (isSelected) => getIcon: (sel) =>
isSelected ? ( sel ? (
<Personalcard size={24} color="#1976d2" variant="Bold" /> <Personalcard size={24} color="#1976d2" variant="Bold" />
) : ( ) : (
<Personalcard size={24} color="#82B1FF" /> <Personalcard size={24} color="#82B1FF" />
@@ -89,8 +97,8 @@ export function Layout() {
}, },
{ {
text: t('side.phoneNumber'), text: t('side.phoneNumber'),
getIcon: (isSelected) => getIcon: (sel) =>
isSelected ? ( sel ? (
<Mobile size={24} color="#1976d2" variant="Bold" /> <Mobile size={24} color="#1976d2" variant="Bold" />
) : ( ) : (
<Mobile size={24} color="#82B1FF" /> <Mobile size={24} color="#82B1FF" />
@@ -99,8 +107,8 @@ export function Layout() {
}, },
{ {
text: t('side.email'), text: t('side.email'),
getIcon: (isSelected) => getIcon: (sel) =>
isSelected ? ( sel ? (
<Sms size={24} color="#1976d2" variant="Bold" /> <Sms size={24} color="#1976d2" variant="Bold" />
) : ( ) : (
<Sms size={24} color="#82B1FF" /> <Sms size={24} color="#82B1FF" />
@@ -111,8 +119,8 @@ export function Layout() {
}, },
{ {
text: t('side.security'), text: t('side.security'),
getIcon: (isSelected) => getIcon: (sel) =>
isSelected ? ( sel ? (
<Shield size={24} color="#1976d2" variant="Bold" /> <Shield size={24} color="#1976d2" variant="Bold" />
) : ( ) : (
<Shield size={24} color="#82B1FF" /> <Shield size={24} color="#82B1FF" />
@@ -121,8 +129,8 @@ export function Layout() {
children: [ children: [
{ {
text: t('side.password'), text: t('side.password'),
getIcon: (isSelected) => getIcon: (sel) =>
isSelected ? ( sel ? (
<PasswordCheck size={24} color="#1976d2" variant="Bold" /> <PasswordCheck size={24} color="#1976d2" variant="Bold" />
) : ( ) : (
<PasswordCheck size={24} color="#82B1FF" /> <PasswordCheck size={24} color="#82B1FF" />
@@ -131,8 +139,8 @@ export function Layout() {
}, },
{ {
text: t('side.verifiedAddress'), text: t('side.verifiedAddress'),
getIcon: (isSelected) => getIcon: (sel) =>
isSelected ? ( sel ? (
<LocationTick size={24} color="#1976d2" variant="Bold" /> <LocationTick size={24} color="#1976d2" variant="Bold" />
) : ( ) : (
<LocationTick size={24} color="#82B1FF" /> <LocationTick size={24} color="#82B1FF" />
@@ -141,8 +149,8 @@ export function Layout() {
}, },
{ {
text: t('side.recentLogins'), text: t('side.recentLogins'),
getIcon: (isSelected) => getIcon: (sel) =>
isSelected ? ( sel ? (
<Devices size={24} color="#1976d2" variant="Bold" /> <Devices size={24} color="#1976d2" variant="Bold" />
) : ( ) : (
<Devices size={24} color="#82B1FF" /> <Devices size={24} color="#82B1FF" />
@@ -153,8 +161,8 @@ export function Layout() {
}, },
{ {
text: t('side.activeDevices'), text: t('side.activeDevices'),
getIcon: (isSelected) => getIcon: (sel) =>
isSelected ? ( sel ? (
<Devices size={24} color="#1976d2" variant="Bold" /> <Devices size={24} color="#1976d2" variant="Bold" />
) : ( ) : (
<Devices size={24} color="#82B1FF" /> <Devices size={24} color="#82B1FF" />
@@ -163,8 +171,8 @@ export function Layout() {
}, },
{ {
text: t('side.settings'), text: t('side.settings'),
getIcon: (isSelected) => getIcon: (sel) =>
isSelected ? ( sel ? (
<SettingIcon size={24} color="#1976d2" variant="Bold" /> <SettingIcon size={24} color="#1976d2" variant="Bold" />
) : ( ) : (
<SettingIcon size={24} color="#82B1FF" /> <SettingIcon size={24} color="#82B1FF" />
@@ -177,43 +185,66 @@ export function Layout() {
<Box <Box
display="flex" display="flex"
flexDirection="column" flexDirection="column"
minHeight="100vh"
justifyContent="center" justifyContent="center"
alignItems="center" alignItems="center"
minHeight="100vh"
px={{ xs: 2, sm: 3 }} px={{ xs: 2, sm: 3 }}
> >
<Box> {!isMdUp && (
<Box <AppBar position="static">
width={totalWidth} <Toolbar sx={{ backgroundColor: 'background.paper' }}>
maxWidth="100%" <IconButton
mx="auto" edge="start"
display="flex" color="inherit"
flexDirection="row" onClick={() => setDrawerOpen(true)}
> >
<Box position="relative" width={navWidth}> <Menu size={24} color="#1976d2" variant="Bold" />
</IconButton>
</Toolbar>
</AppBar>
)}
<Box display="flex" width="100%" maxWidth="100vw">
{isMdUp && (
<Box flex="0 0 auto" width={navWidthMd} position="relative">
<SideNav <SideNav
navConfig={navConfig} navConfig={navConfig}
header={isMdUp ? <Header /> : <Menu size={24} color="#1976d2" />} header={<Header />}
activePath={location.pathname + location.hash} activePath={location.pathname + location.hash}
sideNavVariant={isMdUp ? 'full' : 'minimized'} sideNavVariant="full"
positioning="absolute" positioning="absolute"
// drawerWidth={drawerWidth}
// minimizedWidth={minimizedWidth}
/> />
</Box> </Box>
<Box )}
sx={{ <Box
width: contentWidth, flexGrow={1}
height: contentHeight, width="auto"
bgcolor: 'background.paper', maxWidth={contentMaxWidth}
px: { xs: 2, sm: 3 }, height={{ xs: 'auto', md: '78vh' }}
overflowY: 'auto', sx={{
}} bgcolor: 'background.paper',
> px: { xs: 2, sm: 3 },
<Outlet /> overflowY: 'auto',
</Box> }}
>
<Outlet />
</Box> </Box>
</Box> </Box>
{!isMdUp && (
<Drawer
anchor="left"
open={drawerOpen}
onClose={() => setDrawerOpen(false)}
>
<Box width={navWidthMd}>
<SideNav
navConfig={navConfig}
header={<Header />}
activePath={location.pathname + location.hash}
sideNavVariant="full"
/>
</Box>
</Drawer>
)}
</Box> </Box>
); );
} }
@@ -227,16 +258,7 @@ const router = createBrowserRouter([
{ {
path: '/profile', path: '/profile',
element: ( element: (
<Box <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
sx={{
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
gap: 2,
px: { xs: 2, sm: 0 },
}}
>
<div id="info"> <div id="info">
<PersonalInformation /> <PersonalInformation />
</div> </div>
@@ -252,15 +274,7 @@ const router = createBrowserRouter([
{ {
path: '/security', path: '/security',
element: ( element: (
<Box <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
sx={{
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
gap: 2,
}}
>
<div id="password"> <div id="password">
<PasswordSecurity /> <PasswordSecurity />
</div> </div>
@@ -271,22 +285,8 @@ const router = createBrowserRouter([
</Box> </Box>
), ),
}, },
{ { path: '/devices', element: <ActiveDevices /> },
path: '/devices', { path: '/setting', element: <Setting /> },
element: (
<Box sx={{ width: '100%', height: '100%' }}>
<ActiveDevices />
</Box>
),
},
{
path: '/setting',
element: (
<Box sx={{ width: '100%', height: '100%' }}>
<Setting />
</Box>
),
},
], ],
}, },
]); ]);