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
28 changes: 25 additions & 3 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Expenses from './pages/Expenses';
import Users from './pages/Users';
import Dashboard from './pages/Dashboard';
import ProtectedRoute from './components/ProtectedRoute';
import GuestRoute from './components/GuestRoute';
import ForgotPassword from './pages/ForgotPassword';
import NotFound from './pages/NotFound';
import { ThemeProvider, CssBaseline, Box } from '@mui/material';
Expand Down Expand Up @@ -43,8 +44,22 @@ function App() {
<Box component="main" sx={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
<Route
path="/login"
element={
<GuestRoute>
<Login />
</GuestRoute>
}
/>
Comment on lines +47 to +54
<Route
path="/register"
element={
<GuestRoute>
<Register />
</GuestRoute>
}
/>
<Route
path="/profile"
element={
Expand Down Expand Up @@ -85,7 +100,14 @@ function App() {
</ProtectedRoute>
}
/>
<Route path="/forgot-password" element={<ForgotPassword />} />
<Route
path="/forgot-password"
element={
<GuestRoute>
<ForgotPassword />
</GuestRoute>
}
/>
<Route path="*" element={<NotFound />} />
</Routes>
</Box>
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/components/GuestRoute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import { Navigate } from 'react-router-dom';
import { isLoggedIn } from '../services/auth';

function GuestRoute({ children, redirectTo = '/' }) {
if (isLoggedIn()) {
return <Navigate to={redirectTo} replace />;
}
return children;
}

export default GuestRoute;
Comment on lines +1 to +12
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 current implementation of GuestRoute is static and only checks the authentication status during the initial render. This means the component won't automatically react if the authentication state changes (for example, if a user logs out in another tab or if the session expires) unless a manual refresh or navigation occurs. Since auth.js provides an onAuthChange utility, it is recommended to use it within a useEffect hook to make the component reactive to authentication state transitions.

import React, { useState, useEffect } from 'react';
import { Navigate } from 'react-router-dom';
import { isLoggedIn, onAuthChange } from '../services/auth';

function GuestRoute({ children, redirectTo = '/' }) {
  const [authenticated, setAuthenticated] = useState(isLoggedIn());

  useEffect(() => {
    // Subscribe to auth changes to ensure the component reacts to login/logout events
    return onAuthChange(() => {
      setAuthenticated(isLoggedIn());
    });
  }, []);

  if (authenticated) {
    return <Navigate to={redirectTo} replace />;
  }
  return children;
}

export default GuestRoute;

20 changes: 5 additions & 15 deletions frontend/src/components/LoadingOverlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
// Render free-tier specs — surface a friendly note when a request hangs long
// enough that the user might think the app is broken. Numbers come from
// Render's published free-instance limits (see README "Live API" section).
const FREE_TIER_INFO = "We're on Render's free tier (0.1 CPU / 512 MB RAM). When the service has been idle, the first request triggers a cold start that usually takes 30–60 seconds. Subsequent requests are fast.";
const FREE_TIER_INFO =
"We're on Render's free tier (0.1 CPU / 512 MB RAM). When the service has been idle, the first request triggers a cold start that usually takes 30–60 seconds. Subsequent requests are fast.";

const LONG_LOAD_MS = 4000;
const COLD_START_MS = 10000;
Expand Down Expand Up @@ -35,12 +36,7 @@ function LoadingOverlay({ loading, longLoadMs = LONG_LOAD_MS, coldStartMs = COLD

if (!loading) return null;

const message =
stage === 'cold'
? 'Still waking up the server…'
: stage === 'slow'
? 'Render is taking a while to load up…'
: 'Loading data…';
const message = stage === 'cold' ? 'Still waking up the server…' : stage === 'slow' ? 'Render is taking a while to load up…' : 'Loading data…';

return (
<Box
Expand Down Expand Up @@ -86,9 +82,7 @@ function LoadingOverlay({ loading, longLoadMs = LONG_LOAD_MS, coldStartMs = COLD
}}
>
<Typography variant="caption" sx={{ color: '#f7f6f2', lineHeight: 1.4 }}>
{stage === 'cold'
? `Hang tight — the backend may be cold-starting (${elapsed}s elapsed).`
: 'First request after idle can be slow.'}
{stage === 'cold' ? `Hang tight — the backend may be cold-starting (${elapsed}s elapsed).` : 'First request after idle can be slow.'}
</Typography>
<Tooltip
arrow
Expand All @@ -104,11 +98,7 @@ function LoadingOverlay({ loading, longLoadMs = LONG_LOAD_MS, coldStartMs = COLD
},
}}
>
<InfoOutlinedIcon
fontSize="small"
aria-label="Why is this slow?"
sx={{ color: '#f7f6f2', cursor: 'help', flexShrink: 0 }}
/>
<InfoOutlinedIcon fontSize="small" aria-label="Why is this slow?" sx={{ color: '#f7f6f2', cursor: 'help', flexShrink: 0 }} />
</Tooltip>
</Stack>
</Fade>
Expand Down
Loading