src/App.tsx
import { useState, type ChangeEvent } from 'react'
import {
Avatar,
Box,
Button,
Chip,
Container,
FormControlLabel,
LinearProgress,
Paper,
Stack,
Switch,
Typography,
} from '@mui/material'
import { MetricCard } from './components/MetricCard'
export default function App() {
const [focusMode, setFocusMode] = useState(true)
const [progress, setProgress] = useState(68)
const completeSprint = () => {
setProgress((value) => Math.min(value + 8, 100))
}
return (
<Box
component="main"
sx={{
minHeight: '100vh',
py: { xs: 4, md: 7 },
background:
'radial-gradient(circle at 10% 10%, rgba(37, 99, 235, 0.14), transparent 32rem), #f7f9fc',
}}
>
<Container maxWidth="md">
<Stack spacing={3}>
<Stack
direction={{ xs: 'column', sm: 'row' }}
spacing={2}
sx={{
alignItems: { xs: 'flex-start', sm: 'center' },
justifyContent: 'space-between',
}}
>
<Stack direction="row" spacing={1.5} sx={{ alignItems: 'center' }}>
<Avatar sx={{ bgcolor: 'primary.main', fontWeight: 800 }}>M</Avatar>
<Box>
<Typography variant="overline" color="primary.main">
Material UI 9 · React 19
</Typography>
<Typography variant="h4" component="h1">
Product pulse
</Typography>
</Box>
</Stack>
<Chip label="Sprint 14" color="primary" variant="outlined" />
</Stack>
<Paper
elevation={0}
sx={{
overflow: 'hidden',
border: '1px solid',
borderColor: 'divider',
borderRadius: 4,
}}
>
<Box sx={{ p: { xs: 3, md: 4 } }}>
<Stack
direction={{ xs: 'column', md: 'row' }}
spacing={3}
sx={{
alignItems: { xs: 'stretch', md: 'center' },
justifyContent: 'space-between',
}}
>
<Box>
<Typography variant="h5">Launch readiness</Typography>
<Typography color="text.secondary" sx={{ mt: 0.75, maxWidth: 520 }}>
A responsive dashboard composed with real Material UI
components, the sx prop, and a custom theme.
</Typography>
</Box>
<Button variant="contained" size="large" onClick={completeSprint}>
Complete task
</Button>
</Stack>
<Box sx={{ mt: 4 }}>
<Stack
direction="row"
sx={{ mb: 1, justifyContent: 'space-between' }}
>
<Typography variant="body2" fontWeight={700}>
Sprint progress
</Typography>
<Typography variant="body2" color="text.secondary">
{progress}%
</Typography>
</Stack>
<LinearProgress
variant="determinate"
value={progress}
sx={{ height: 9, borderRadius: 999 }}
/>
</Box>
</Box>
<Box
sx={{
display: 'grid',
gridTemplateColumns: { xs: '1fr', sm: 'repeat(3, 1fr)' },
gap: 1,
p: 1,
bgcolor: 'grey.100',
}}
>
<MetricCard label="Tasks closed" value="24" detail="+6 this week" />
<MetricCard label="Review time" value="1.8h" detail="12% faster" />
<MetricCard label="Team health" value="92%" detail="On track" />
</Box>
</Paper>
<Paper
variant="outlined"
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: 2,
p: 2.5,
borderRadius: 3,
}}
>
<Box>
<Typography fontWeight={700}>Focus mode</Typography>
<Typography variant="body2" color="text.secondary">
Reduce notifications while the sprint is active.
</Typography>
</Box>
<FormControlLabel
control={
<Switch
checked={focusMode}
onChange={(event: ChangeEvent<HTMLInputElement>) =>
setFocusMode(event.target.checked)
}
/>
}
label={focusMode ? 'On' : 'Off'}
labelPlacement="start"
sx={{ m: 0 }}
/>
</Paper>
</Stack>
</Container>
</Box>
)
}