feat(web): Phase 3b — medication/health/interactions feature UIs
Build the three core feature UIs on top of the now-wired stores + corrected API client from Phase 3a, turning the runnable shell into a usable app. Dashboard (pages/Dashboard.tsx): AppBar (Normogen + username + logout) over an MUI Tabs container (Medications | Health | Interactions), widened to lg. User loads on mount (needed for profile_id in medication create). MedicationManager (components/medication/): full CRUD. * List of medication cards (name, dosage·frequency, active chip, instructions) with edit/delete actions and an empty state. * Create dialog: name/dosage/frequency/route(select)/instructions. profile_id sourced from user.profile_id with a 'default' fallback (TODO: real profiles). Payload typed to CreateMedicationRequest. * Edit dialog (UpdateMedicationRequest shape) + delete confirm. Guards the optional medication_id field. HealthStats (components/health/): trend cards + chart + add + table. * Trend summary cards from /health-stats/trends (avg/min/max + trend arrow). * recharts LineChart of a selected stat type over measured_at, with a type selector. Timestamp field is measured_at (not timestamp). * Record dialog (stat_type/value/unit/measured_at/notes) + recent-readings table (date-fns formatted). InteractionsChecker (components/interactions/): multi-select medication names (chips) -> POST /interactions/check. Results rendered as cards with a SeverityChip, description, and disclaimer. Success/empty states. Shared SeverityChip (components/common/): maps InteractionSeverity -> MUI Chip color (severe=error, moderate=warning, mild=success, unknown=default). Added @mui/icons-material@7 (pinned to match @mui/material 7; npm tried to grab v9 which needs material ^9). Verified: npm run build clean (TS5 strict), dev server serves the app + all component modules transform. Solaria round-trip confirmed every contract the UIs call: medication create/list (200), interactions warfarin+aspirin -> severe, health-stat create (201).
This commit is contained in:
parent
ef1bb9f4be
commit
1515923996
7 changed files with 886 additions and 60 deletions
|
|
@ -1,24 +1,20 @@
|
|||
import { useEffect, type FC } from 'react';
|
||||
import { useEffect, useState, type FC } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
AppBar,
|
||||
Toolbar,
|
||||
Typography,
|
||||
Button,
|
||||
Container,
|
||||
Card,
|
||||
CardContent,
|
||||
Box,
|
||||
CircularProgress,
|
||||
} from '@mui/material';
|
||||
import { AppBar, Toolbar, Typography, Button, Container, Box, Tabs, Tab } from '@mui/material';
|
||||
import { useAuthStore } from '../store/useStore';
|
||||
import { MedicationManager } from '../components/medication/MedicationManager';
|
||||
import { HealthStats } from '../components/health/HealthStats';
|
||||
import { InteractionsChecker } from '../components/interactions/InteractionsChecker';
|
||||
|
||||
type TabIndex = 0 | 1 | 2;
|
||||
|
||||
export const Dashboard: FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const { user, isAuthenticated, isLoading, loadUser, logout } = useAuthStore();
|
||||
const { user, isAuthenticated, loadUser, logout } = useAuthStore();
|
||||
const [tab, setTab] = useState<TabIndex>(0);
|
||||
|
||||
// On mount, refresh the user record from the backend (confirms the token is
|
||||
// still valid and pulls the latest profile).
|
||||
// still valid, and medication-create needs user.profile_id).
|
||||
useEffect(() => {
|
||||
if (isAuthenticated && !user) {
|
||||
loadUser();
|
||||
|
|
@ -37,34 +33,32 @@ export const Dashboard: FC = () => {
|
|||
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
|
||||
Normogen
|
||||
</Typography>
|
||||
{user && (
|
||||
<Typography variant="body2" sx={{ mr: 2, opacity: 0.85 }}>
|
||||
{user.username}
|
||||
</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>
|
||||
)}
|
||||
<Container maxWidth="lg" sx={{ mt: 3 }}>
|
||||
<Tabs value={tab} onChange={(_, v) => setTab(v as TabIndex)} sx={{ mb: 3 }}>
|
||||
<Tab label="Medications" />
|
||||
<Tab label="Health" />
|
||||
<Tab label="Interactions" />
|
||||
</Tabs>
|
||||
|
||||
<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>
|
||||
<Box>
|
||||
{tab === 0 && <MedicationManager />}
|
||||
{tab === 1 && <HealthStats />}
|
||||
{tab === 2 && <InteractionsChecker />}
|
||||
</Box>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue