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:
parent
8bc0391100
commit
cd5a62c983
28 changed files with 2104 additions and 15710 deletions
70
web/normogen-web/src/pages/Dashboard.tsx
Normal file
70
web/normogen-web/src/pages/Dashboard.tsx
Normal 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'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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue