236 lines
7.9 KiB
JavaScript
236 lines
7.9 KiB
JavaScript
import { Avatar, Box, Chip, Grid, Paper, Stack, Typography, useMediaQuery } from '@mui/material';
|
||
import { useTheme } from '@mui/material/styles';
|
||
import { useQuery } from '@tanstack/react-query';
|
||
import {
|
||
MdBadge,
|
||
MdPerson,
|
||
MdLocationOn,
|
||
MdMail,
|
||
MdPhone,
|
||
MdPlace,
|
||
MdMyLocation,
|
||
MdLocationCity,
|
||
MdMap,
|
||
MdMarkunreadMailbox,
|
||
MdVerifiedUser
|
||
} from 'react-icons/md';
|
||
import CircularLoader from 'components/CircularLoader';
|
||
import Loader from 'components/Loader';
|
||
import { getusers } from 'pages/api/api';
|
||
|
||
// ---- shared design tokens (mirrors the DT block used across the console) ----
|
||
const DT = {
|
||
radiusCard: 16,
|
||
radiusInner: 12,
|
||
shadowSoft: '0 14px 40px rgba(15, 23, 42, 0.10)',
|
||
textPrimary: '#0f172a',
|
||
textSecondary: '#64748b',
|
||
textMuted: '#94a3b8',
|
||
borderSubtle: '#e2e8f0',
|
||
divider: '#f1f5f9',
|
||
surface: '#ffffff',
|
||
surfaceAlt: '#f8fafc'
|
||
};
|
||
const BRAND = '#662582';
|
||
const BRAND_LIGHT = '#9255AB';
|
||
const tint = (c) => `${c}08`;
|
||
const soft = (c) => `${c}18`;
|
||
|
||
const initialsOf = (name) =>
|
||
(name || '')
|
||
.trim()
|
||
.split(/\s+/)
|
||
.slice(0, 2)
|
||
.map((w) => w[0])
|
||
.join('')
|
||
.toUpperCase() || '–';
|
||
|
||
// A single read-only field rendered as an icon + uppercase label + value.
|
||
const InfoField = ({ icon: Icon, label, value, accent = BRAND }) => (
|
||
<Stack direction="row" spacing={1.5} alignItems="flex-start" sx={{ minWidth: 0 }}>
|
||
<Avatar sx={{ width: 36, height: 36, bgcolor: soft(accent), color: accent, flexShrink: 0 }}>
|
||
<Icon size={18} />
|
||
</Avatar>
|
||
<Box sx={{ minWidth: 0 }}>
|
||
<Typography
|
||
variant="overline"
|
||
sx={{ display: 'block', color: DT.textMuted, lineHeight: 1.6, letterSpacing: '0.06em' }}
|
||
>
|
||
{label}
|
||
</Typography>
|
||
<Typography
|
||
variant="body1"
|
||
sx={{ fontWeight: 600, color: DT.textPrimary, wordBreak: 'break-word' }}
|
||
title={value || ''}
|
||
>
|
||
{value || '—'}
|
||
</Typography>
|
||
</Box>
|
||
</Stack>
|
||
);
|
||
|
||
// A titled grouping card.
|
||
const SectionCard = ({ title, icon: Icon, children }) => (
|
||
<Paper
|
||
elevation={0}
|
||
sx={{
|
||
borderRadius: DT.radiusCard + 'px',
|
||
border: `1px solid ${DT.borderSubtle}`,
|
||
boxShadow: DT.shadowSoft,
|
||
overflow: 'hidden',
|
||
height: '100%'
|
||
}}
|
||
>
|
||
<Stack
|
||
direction="row"
|
||
alignItems="center"
|
||
spacing={1.25}
|
||
sx={{ px: 2.5, py: 1.75, borderBottom: `1px solid ${DT.divider}`, bgcolor: DT.surfaceAlt }}
|
||
>
|
||
<Avatar sx={{ width: 28, height: 28, bgcolor: soft(BRAND), color: BRAND }}>
|
||
<Icon size={16} />
|
||
</Avatar>
|
||
<Typography variant="subtitle1" sx={{ fontWeight: 700, color: DT.textPrimary }}>
|
||
{title}
|
||
</Typography>
|
||
</Stack>
|
||
<Box sx={{ p: 2.5 }}>{children}</Box>
|
||
</Paper>
|
||
);
|
||
|
||
const ViewProfile = () => {
|
||
const theme = useTheme();
|
||
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
||
const { data: userData, isLoading } = useQuery({
|
||
queryKey: ['getuser'],
|
||
queryFn: getusers
|
||
});
|
||
|
||
const fullname = userData?.fullname || userData?.firstname || 'User Profile';
|
||
|
||
return (
|
||
<>
|
||
{isLoading && (
|
||
<>
|
||
<Loader />
|
||
<CircularLoader />
|
||
</>
|
||
)}
|
||
|
||
<Box sx={{ p: { xs: 1.5, md: 0 } }}>
|
||
{/* ---- Gradient brand header ---- */}
|
||
<Paper
|
||
elevation={0}
|
||
sx={{
|
||
borderRadius: DT.radiusCard + 'px',
|
||
border: `1px solid ${DT.borderSubtle}`,
|
||
background: `linear-gradient(135deg, ${tint(BRAND)} 0%, ${tint(BRAND_LIGHT)} 100%)`,
|
||
boxShadow: DT.shadowSoft,
|
||
p: { xs: 2.5, md: 3.5 },
|
||
mb: { xs: 2.5, md: 3 }
|
||
}}
|
||
>
|
||
<Stack
|
||
direction={{ xs: 'column', sm: 'row' }}
|
||
spacing={{ xs: 2, sm: 3 }}
|
||
alignItems={{ xs: 'flex-start', sm: 'center' }}
|
||
>
|
||
<Avatar
|
||
sx={{
|
||
width: { xs: 64, md: 76 },
|
||
height: { xs: 64, md: 76 },
|
||
bgcolor: BRAND,
|
||
color: '#fff',
|
||
fontSize: { xs: 24, md: 28 },
|
||
fontWeight: 700,
|
||
boxShadow: '0 10px 24px rgba(102, 37, 130, 0.35)'
|
||
}}
|
||
>
|
||
{initialsOf(fullname)}
|
||
</Avatar>
|
||
<Box sx={{ minWidth: 0 }}>
|
||
<Typography variant="h3" sx={{ color: DT.textPrimary, fontWeight: 700, mb: 0.5 }}>
|
||
{fullname}
|
||
</Typography>
|
||
<Stack direction="row" spacing={1} flexWrap="wrap" useFlexGap alignItems="center">
|
||
{userData?.authname && (
|
||
<Chip
|
||
size="small"
|
||
icon={<MdVerifiedUser size={14} style={{ color: BRAND }} />}
|
||
label={userData.authname}
|
||
sx={{
|
||
bgcolor: soft(BRAND),
|
||
color: BRAND,
|
||
fontWeight: 700,
|
||
border: `1px solid ${BRAND}40`,
|
||
'& .MuiChip-icon': { ml: '6px' }
|
||
}}
|
||
/>
|
||
)}
|
||
{userData?.userid != null && userData?.userid !== '' && (
|
||
<Typography variant="body2" sx={{ color: DT.textSecondary, fontWeight: 600 }}>
|
||
ID #{userData.userid}
|
||
</Typography>
|
||
)}
|
||
{userData?.applocation && (
|
||
<Stack direction="row" spacing={0.5} alignItems="center" sx={{ color: DT.textSecondary }}>
|
||
<MdLocationOn size={15} />
|
||
<Typography variant="body2" sx={{ fontWeight: 600 }}>
|
||
{userData.applocation}
|
||
</Typography>
|
||
</Stack>
|
||
)}
|
||
</Stack>
|
||
</Box>
|
||
</Stack>
|
||
</Paper>
|
||
|
||
{/* ---- Grouped detail cards ---- */}
|
||
<Grid container spacing={{ xs: 2.5, md: 3 }}>
|
||
<Grid item xs={12} md={6}>
|
||
<SectionCard title="Account" icon={MdBadge}>
|
||
<Stack spacing={2.5}>
|
||
<InfoField icon={MdPerson} label="User Name" value={userData?.fullname} />
|
||
<InfoField icon={MdBadge} label="User ID" value={userData?.userid} />
|
||
<InfoField icon={MdVerifiedUser} label="Auth Name" value={userData?.authname} />
|
||
<InfoField icon={MdLocationOn} label="App Location" value={userData?.applocation} />
|
||
</Stack>
|
||
</SectionCard>
|
||
</Grid>
|
||
|
||
<Grid item xs={12} md={6}>
|
||
<SectionCard title="Contact" icon={MdMail}>
|
||
<Stack spacing={2.5}>
|
||
<InfoField icon={MdPhone} label="Contact No" value={userData?.contactno} accent="#0ea5e9" />
|
||
<InfoField icon={MdMail} label="E-Mail" value={userData?.email} accent="#0ea5e9" />
|
||
<InfoField icon={MdPlace} label="Address" value={userData?.address} accent="#0ea5e9" />
|
||
</Stack>
|
||
</SectionCard>
|
||
</Grid>
|
||
|
||
<Grid item xs={12}>
|
||
<SectionCard title="Location" icon={MdMap}>
|
||
<Grid container spacing={isMobile ? 2.5 : 3}>
|
||
<Grid item xs={12} sm={6} md={3}>
|
||
<InfoField icon={MdMyLocation} label="Suburb" value={userData?.suburb} accent="#14b8a6" />
|
||
</Grid>
|
||
<Grid item xs={12} sm={6} md={3}>
|
||
<InfoField icon={MdLocationCity} label="City" value={userData?.city} accent="#14b8a6" />
|
||
</Grid>
|
||
<Grid item xs={12} sm={6} md={3}>
|
||
<InfoField icon={MdMap} label="State" value={userData?.state} accent="#14b8a6" />
|
||
</Grid>
|
||
<Grid item xs={12} sm={6} md={3}>
|
||
<InfoField icon={MdMarkunreadMailbox} label="Postcode" value={userData?.postcode} accent="#14b8a6" />
|
||
</Grid>
|
||
</Grid>
|
||
</SectionCard>
|
||
</Grid>
|
||
</Grid>
|
||
</Box>
|
||
</>
|
||
);
|
||
};
|
||
|
||
export default ViewProfile;
|