feat(web): Phase 3a — migrate CRA to Vite + make the app runnable

The frontend was a Create React App scaffold (deprecated) where App.tsx was
still the stock 'Learn React' boilerplate — the implemented login/register pages
and stores were never mounted (no router). This swaps to Vite/TS5, fixes the
broken API client, wires a real router + dashboard, and lands on a runnable app.

Tooling swap (CRA -> Vite):
* Dropped react-scripts, web-vitals, @types/jest; added vite 6 + @vitejs/plugin-react
  + vitest 3 + jsdom; bumped typescript 4.9 -> 5.6, @types/node 16 -> 22.
* New vite.config.ts (with dev-server /api proxy -> backend, no CORS in dev),
  tsconfig.json (target es2022, moduleResolution bundler, vite/client types),
  tsconfig.node.json, vite-env.d.ts, root index.html.
* .env / .env.example (VITE_API_URL + VITE_API_TARGET). Renamed index.tsx ->
  main.tsx (theme provider + CssBaseline). Deleted all CRA boilerplate
  (App.css, logo.svg, App.test.tsx, reportWebVitals.ts, react-app-env.d.ts,
  CRA index.html/logos/readme) + the orphaned empty web/src/ and mobile/ trees.
* node_modules 479MB -> 265MB; lockfile 695KB -> 198KB.

API client correctness (services/api.ts):
* Env var process.env.REACT_APP_* -> import.meta.env.VITE_*; dropped the
  hardcoded http://solaria:8001/api (now /api via the dev proxy).
* Fixed contract mismatches: getCurrentUser /auth/me -> /users/me;
  updateMedication PUT -> POST /:id; deleteMedication DELETE -> POST /:id/delete.
* Added refreshToken() (/auth/refresh) and server-side logout() (/auth/logout).
* Removed the /lab-results block (no backend route).
* 401 interceptor now attempts ONE silent refresh (deduped) before bouncing to
  /login, instead of hard-redirecting on every 401.
* CreateMedicationRequest type now matches the backend (required route +
  profile_id fields).

Router + dashboard:
* Real App.tsx with BrowserRouter: /login, /register (public), / (protected
  Dashboard), catch-all -> /.
* New Dashboard.tsx: MUI AppBar + welcome card, loads the current user on mount,
  logout button. The shell feature UIs get built into next.
* LoginPage/RegisterPage navigate to / (was /dashboard); store logout is now
  async; AuthTokens type includes refresh_token.
* Minimal MUI theme.ts.

Verified: npm run build clean (TS5 strict + Vite), dev server serves the app +
proxies /api to the backend, vitest runs. Solaria round-trip confirmed all
corrected endpoints (/users/me 200, /auth/refresh 200, /auth/logout 204,
medication create 200).
This commit is contained in:
goose 2026-06-28 00:47:06 -03:00
parent 8bc0391100
commit cd5a62c983
28 changed files with 2104 additions and 15710 deletions

View file

@ -0,0 +1,70 @@
import { useEffect, type FC } from 'react';
import { useNavigate } from 'react-router-dom';
import {
AppBar,
Toolbar,
Typography,
Button,
Container,
Card,
CardContent,
Box,
CircularProgress,
} from '@mui/material';
import { useAuthStore } from '../store/useStore';
export const Dashboard: FC = () => {
const navigate = useNavigate();
const { user, isAuthenticated, isLoading, loadUser, logout } = useAuthStore();
// On mount, refresh the user record from the backend (confirms the token is
// still valid and pulls the latest profile).
useEffect(() => {
if (isAuthenticated && !user) {
loadUser();
}
}, [isAuthenticated, user, loadUser]);
const handleLogout = async () => {
await logout();
navigate('/login', { replace: true });
};
return (
<>
<AppBar position="static">
<Toolbar>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Normogen
</Typography>
<Button color="inherit" onClick={handleLogout}>
Logout
</Button>
</Toolbar>
</AppBar>
<Container maxWidth="md" sx={{ mt: 4 }}>
{isLoading && (
<Box display="flex" justifyContent="center" mt={4}>
<CircularProgress />
</Box>
)}
<Card>
<CardContent>
<Typography variant="h5" gutterBottom>
Welcome{user ? `, ${user.username}` : ''}
</Typography>
<Typography variant="body1" color="text.secondary">
You&apos;re signed in to Normogen.
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ mt: 2 }}>
This is the dashboard shell. Medication management, health statistics,
and drug-interaction checking UIs will be built here.
</Typography>
</CardContent>
</Card>
</Container>
</>
);
};

View file

@ -24,7 +24,7 @@ export const LoginPage: React.FC = () => {
try {
await login(email, password);
navigate('/dashboard');
navigate('/');
} catch (err) {
// Error is handled by the store
}

View file

@ -38,7 +38,7 @@ export const RegisterPage: React.FC = () => {
try {
await register(username, email, password);
navigate('/dashboard');
navigate('/');
} catch (err) {
// Error is handled by the store
}