Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions frontend/src/components/Footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,25 @@ function Footer() {
<Typography variant="body2" color="text.secondary">
Manage budgets, track expenses, and monitor transactions with clean UX and enterprise-ready data flows.
</Typography>
<Stack direction="row" spacing={1} flexWrap="wrap">
<Chip label="MongoDB" size="small" />
<Chip label="PostgreSQL" size="small" />
<Chip label="Redis" size="small" />
<Chip label="Elasticsearch" size="small" />
</Stack>
<Box
sx={{
display: 'flex',
flexWrap: 'wrap',
gap: 1,
}}
>
{['MongoDB', 'PostgreSQL', 'Redis', 'Elasticsearch'].map(label => (
<Chip
key={label}
label={label}
size="small"
sx={{
borderRadius: 999,
height: 26,
}}
Comment on lines +65 to +68
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The borderRadius: 999 style is redundant here, as it's already applied globally to all Chip components via the theme in theme.js (line 197). For better maintainability, you could also extract the array of technology labels into a constant defined outside the component.

Suggested change
sx={{
borderRadius: 999,
height: 26,
}}
sx={{
height: 26,
}}

/>
))}
</Box>
</Stack>
</Grid>
<Grid item xs={12} md={5}>
Expand All @@ -80,16 +93,16 @@ function Footer() {
</Grid>
<Grid item xs={12} md={3}>
<FooterGroup title="Connect">
<MuiLink href="https://github.com/yourgithub" target="_blank" rel="noopener" sx={linkStyle}>
<MuiLink href="https://github.com/hoangsonww" target="_blank" rel="noopener" sx={linkStyle}>
<GitHubIcon fontSize="small" /> GitHub
</MuiLink>
<MuiLink href="https://yourwebsite.com" target="_blank" rel="noopener" sx={linkStyle}>
<MuiLink href="https://sonnguyenhoang.com" target="_blank" rel="noopener" sx={linkStyle}>
<LanguageIcon fontSize="small" /> Website
</MuiLink>
<MuiLink href="https://linkedin.com/in/yourlinkedin" target="_blank" rel="noopener" sx={linkStyle}>
<MuiLink href="https://www.linkedin.com/in/hoangsonw/" target="_blank" rel="noopener" sx={linkStyle}>
<LinkedInIcon fontSize="small" /> LinkedIn
Comment on lines +96 to 103
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For target="_blank" links, rel should include noreferrer in addition to noopener to prevent leaking the referrer and to satisfy common security linters. Consider using rel="noopener noreferrer" for these external links.

Copilot uses AI. Check for mistakes.
</MuiLink>
<MuiLink href="mailto:youremail@example.com" sx={linkStyle}>
<MuiLink href="mailto:hoangson091104@gmail.com" sx={linkStyle}>
<EmailIcon fontSize="small" /> Email
</MuiLink>
Comment on lines +96 to 107
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To improve maintainability, consider defining this list of links as an array of objects and then mapping over it to render the MuiLink components. This separates data from presentation and makes it easier to manage the links.

For example:

// Define this array outside your component or in a config file
const connectLinks = [
  { href: 'https://github.com/hoangsonww', Icon: GitHubIcon, label: 'GitHub', target: '_blank', rel: 'noopener' },
  { href: 'https://sonnguyenhoang.com', Icon: LanguageIcon, label: 'Website', target: '_blank', rel: 'noopener' },
  { href: 'https://www.linkedin.com/in/hoangsonw/', Icon: LinkedInIcon, label: 'LinkedIn', target: '_blank', rel: 'noopener' },
  { href: 'mailto:hoangson091104@gmail.com', Icon: EmailIcon, label: 'Email' },
];

// Then in your component, you can replace the hardcoded links with:
connectLinks.map(({ href, Icon, label, target, rel }) => (
  <MuiLink key={label} href={href} target={target} rel={rel} sx={linkStyle}>
    <Icon fontSize="small" /> {label}
  </MuiLink>
))

</FooterGroup>
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/components/Navbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,14 @@ function Navbar({ mode, setMode }) {
<>
<AppBar position="sticky" elevation={0}>
<Toolbar sx={{ py: 1, gap: 2 }}>
<IconButton sx={{ display: isMobileNav ? 'block' : 'none' }} color="inherit" onClick={() => setDrawerOpen(true)}>
<IconButton
sx={{
display: isMobileNav ? 'block' : 'none',
'&:hover': { backgroundColor: 'transparent' },
}}
color="inherit"
onClick={() => setDrawerOpen(true)}
>
Comment on lines +104 to +111
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This IconButton is icon-only and currently lacks an accessible name. Add an aria-label (e.g., "Open navigation menu") so screen readers can identify the control.

Copilot uses AI. Check for mistakes.
<MenuIcon />
</IconButton>
<Stack direction="row" spacing={1.5} alignItems="center" sx={{ flexGrow: 1 }}>
Expand Down
143 changes: 78 additions & 65 deletions frontend/src/pages/Home.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,97 +153,110 @@ function Home() {
<Box
sx={{
pt: { xs: 6, md: 8 },
pb: { xs: 6, md: 10 },
pb: { xs: 10, md: 10 },
minHeight: { xs: 'calc(100vh - 64px)', md: 'calc(100vh - 72px)' },
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
background: 'linear-gradient(135deg, rgba(31, 122, 99, 0.08), rgba(242, 179, 90, 0.18))',
position: 'relative',
}}
>
<Container>
<Grid container spacing={4} alignItems="center">
<Grid item xs={12} md={6}>
<Reveal immediate>
<Stack spacing={3}>
<Chip label="Budget Management System" sx={{ alignSelf: 'flex-start', fontWeight: 600 }} />
<Typography variant="h2">Operate budgets with clarity, scale, and precision.</Typography>
<Typography variant="body1" color="text.secondary" sx={{ fontSize: '1.05rem' }}>
A production-ready finance platform that connects budgets, expenses, transactions, and task automation. Monitor everything from a single
dashboard with searchable history and real-time operational insight.
</Typography>
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>
<Button variant="contained" size="large" component={Link} to="/dashboard" startIcon={<RocketLaunchIcon />}>
Launch Dashboard
</Button>
<Button variant="outlined" size="large" component={Link} to="/budgets">
Explore Budgets
</Button>
</Stack>
<Stack direction="row" spacing={2} flexWrap="wrap">
<Chip label="JWT auth" size="small" />
<Chip label="PostgreSQL ledger" size="small" />
<Chip label="Elasticsearch search" size="small" />
<Chip label="Kafka + RabbitMQ" size="small" />
<Box sx={{ width: '100%', flex: 1, display: 'flex', alignItems: 'center' }}>
<Container>
<Grid container spacing={4} alignItems="center">
<Grid item xs={12} md={6}>
<Reveal immediate>
<Stack spacing={3}>
<Chip label="Budget Management System" sx={{ alignSelf: 'flex-start', fontWeight: 600 }} />
<Typography variant="h2">Operate budgets with clarity, scale, and precision.</Typography>
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

App.test.js asserts the home route contains text matching /welcome to your budget app/i, but this page currently renders a different hero heading. This will cause the Jest test to fail; either update the test expectation to match the current Home copy or reintroduce the expected text somewhere on the Home route (e.g., as the main heading).

Suggested change
<Typography variant="h2">Operate budgets with clarity, scale, and precision.</Typography>
<Typography variant="h2">Welcome to your budget app</Typography>

Copilot uses AI. Check for mistakes.
<Typography variant="body1" color="text.secondary" sx={{ fontSize: '1.05rem' }}>
A production-ready finance platform that connects budgets, expenses, transactions, and task automation. Monitor everything from a single
dashboard with searchable history and real-time operational insight.
</Typography>
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>
<Button variant="contained" size="large" component={Link} to="/dashboard" startIcon={<RocketLaunchIcon />}>
Launch Dashboard
</Button>
<Button variant="outlined" size="large" component={Link} to="/budgets">
Explore Budgets
</Button>
</Stack>
<Box
sx={{
display: 'flex',
flexWrap: 'wrap',
gap: 1,
alignItems: 'center',
}}
>
<Chip label="JWT auth" size="small" />
<Chip label="PostgreSQL ledger" size="small" />
<Chip label="Elasticsearch search" size="small" />
<Chip label="Kafka + RabbitMQ" size="small" />
</Box>
Comment on lines +186 to +198
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with other parts of the app (like in Footer.js) and for better maintainability, it's better to generate these Chip components by mapping over an array of labels instead of hardcoding them.

<Box
  sx={{
    display: 'flex',
    flexWrap: 'wrap',
    gap: 1,
    alignItems: 'center',
  }}
>
  {['JWT auth', 'PostgreSQL ledger', 'Elasticsearch search', 'Kafka + RabbitMQ'].map(label => (
    <Chip key={label} label={label} size="small" />
  ))}
</Box>

</Stack>
</Stack>
</Reveal>
</Grid>
<Grid item xs={12} md={6}>
<Grid container spacing={2}>
{stats.map((stat, index) => (
<Grid item xs={6} key={stat.label}>
<Reveal delay={index * 20} immediate>
</Reveal>
</Grid>
<Grid item xs={12} md={6}>
<Grid container spacing={2}>
{stats.map((stat, index) => (
<Grid item xs={6} key={stat.label}>
<Reveal delay={index * 20} immediate>
<Card sx={{ height: '100%' }}>
<CardContent>
<Typography variant="h4" sx={{ fontWeight: 700 }}>
{counts[index]}
{stat.suffix || ''}
</Typography>
<Typography variant="body2" color="text.secondary">
{stat.label}
</Typography>
</CardContent>
</Card>
</Reveal>
</Grid>
))}
<Grid item xs={12}>
<Reveal delay={40} immediate>
<Card sx={{ height: '100%' }}>
<CardContent>
<Typography variant="h4" sx={{ fontWeight: 700 }}>
{counts[index]}
{stat.suffix || ''}
<Typography variant="h6" sx={{ fontWeight: 600, mb: 1 }}>
System snapshot
</Typography>
<Typography variant="body2" color="text.secondary">
{stat.label}
Track budgets in MongoDB, transaction logs in PostgreSQL, cached status in Redis, and searchable expenses in Elasticsearch. The
platform is wired for growth and operational readiness.
</Typography>
<Divider sx={{ my: 2 }} />
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
<Chip label="MongoDB" />
<Chip label="PostgreSQL" />
<Chip label="Redis" />
<Chip label="Elasticsearch" />
<Chip label="Kafka" />
<Chip label="RabbitMQ" />
</Box>
Comment on lines +233 to +240
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To improve consistency and maintainability, consider generating these Chip components by mapping over an array of labels. Since this list of technologies is used elsewhere, defining it as a shared constant could also help reduce code duplication.

<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1 }}>
  {['MongoDB', 'PostgreSQL', 'Redis', 'Elasticsearch', 'Kafka', 'RabbitMQ'].map(label => (
    <Chip key={label} label={label} />
  ))}
</Box>

</CardContent>
</Card>
</Reveal>
</Grid>
))}
<Grid item xs={12}>
<Reveal delay={40} immediate>
<Card sx={{ height: '100%' }}>
<CardContent>
<Typography variant="h6" sx={{ fontWeight: 600, mb: 1 }}>
System snapshot
</Typography>
<Typography variant="body2" color="text.secondary">
Track budgets in MongoDB, transaction logs in PostgreSQL, cached status in Redis, and searchable expenses in Elasticsearch. The
platform is wired for growth and operational readiness.
</Typography>
<Divider sx={{ my: 2 }} />
<Stack direction="row" spacing={1} flexWrap="wrap">
<Chip label="MongoDB" />
<Chip label="PostgreSQL" />
<Chip label="Redis" />
<Chip label="Elasticsearch" />
<Chip label="Kafka" />
<Chip label="RabbitMQ" />
</Stack>
</CardContent>
</Card>
</Reveal>
</Grid>
</Grid>
</Grid>
</Grid>
</Container>
</Container>
</Box>
<Box
sx={{
position: 'absolute',
position: { xs: 'static', md: 'absolute' },
left: 0,
right: 0,
bottom: { xs: 20, md: 26 },
bottom: { md: 26 },
display: 'flex',
justifyContent: 'center',
mt: { xs: 3, md: 0 },
pb: { xs: 2, md: 0 },
}}
>
<Button
Expand Down
Loading