Add Medicine and Activity trackers with voice command support
Some checks failed
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled

Added new tracking pages:
- Medicine tracker: track medication name, dosage, unit, route, and reason
- Activity tracker: track play, exercise, walks, music, reading, tummy time, etc.
- Both pages follow existing tracker patterns with recent activities list

Voice command improvements:
- Updated voice classification to support medicine and activity types
- Added detailed extraction fields for medicine (medicineName, dosage, unit, route, reason)
- Added detailed extraction fields for activity (activityType, duration, description)
- Enhanced unknown intent dialog with manual tracker selection
- Updated tracker options to match implemented pages (removed milestone)

Backend changes:
- Added MEDICINE and ACTIVITY to ActivityType enum
- Created migration V013 to add medicine/activity to database CHECK constraint
- Updated voice service prompts to include medicine and activity extraction

Frontend changes:
- Created /track/medicine page with full CRUD operations
- Created /track/activity page with full CRUD operations
- Added Medicine card to /track page with MedicalServices icon
- Updated VoiceFloatingButton unknown dialog with 4 tracker options

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-02 11:46:10 +00:00
parent a813a36cea
commit 26306d7ed8
7 changed files with 1174 additions and 11 deletions

View File

@@ -16,9 +16,14 @@ import {
CircularProgress,
Chip,
IconButton,
Select,
MenuItem,
FormControl,
InputLabel,
} from '@mui/material';
import MicIcon from '@mui/icons-material/Mic';
import MicOffIcon from '@mui/icons-material/MicOff';
import AddIcon from '@mui/icons-material/Add';
import { useRouter } from 'next/navigation';
import { useVoiceInput } from '@/hooks/useVoiceInput';
import { useAuth } from '@/lib/auth/AuthContext';
@@ -43,6 +48,8 @@ export function VoiceFloatingButton() {
const [classificationResult, setClassificationResult] = useState<any>(null);
const [processedClassificationId, setProcessedClassificationId] = useState<string | null>(null);
const [showReview, setShowReview] = useState(false);
const [showUnknownDialog, setShowUnknownDialog] = useState(false);
const [manualTrackingType, setManualTrackingType] = useState<string>('feeding');
const [snackbar, setSnackbar] = useState<{
open: boolean;
message: string;
@@ -91,14 +98,10 @@ export function VoiceFloatingButton() {
setProcessingStatus(null);
setShowReview(true);
} else {
// For unknown or low confidence, show error and close dialog
// For unknown or low confidence, show unknown dialog
setProcessingStatus(null);
setOpen(false);
setSnackbar({
open: true,
message: 'Could not understand the command. Please try again or use manual entry.',
severity: 'warning',
});
setShowUnknownDialog(true);
}
}
}, [classification, isListening, isProcessing, open, transcript, processedClassificationId]);
@@ -295,6 +298,24 @@ export function VoiceFloatingButton() {
setSnackbar(prev => ({ ...prev, open: false }));
};
const handleRetry = () => {
setShowUnknownDialog(false);
setOpen(true);
reset();
setClassificationResult(null);
setProcessingStatus(null);
setProcessedClassificationId(null);
// Auto-start listening
setTimeout(() => {
startListening();
}, 300);
};
const handleManualTracking = () => {
setShowUnknownDialog(false);
router.push(`/track/${manualTrackingType}`);
};
return (
<>
{/* Floating button positioned in bottom-right */}
@@ -439,6 +460,44 @@ export function VoiceFloatingButton() {
/>
)}
{/* Unknown Intent Dialog */}
<Dialog open={showUnknownDialog} onClose={() => setShowUnknownDialog(false)} maxWidth="sm" fullWidth>
<DialogTitle>Could Not Understand Command</DialogTitle>
<DialogContent>
<Box sx={{ mb: 3 }}>
<Typography variant="body2" color="text.secondary" gutterBottom>
You said: "{transcript}"
</Typography>
<Typography variant="body2" sx={{ mt: 2 }}>
I couldn't identify a specific activity from your command. You can either try again or manually add an activity.
</Typography>
</Box>
<FormControl fullWidth sx={{ mt: 2 }}>
<InputLabel>Activity Type</InputLabel>
<Select
value={manualTrackingType}
onChange={(e) => setManualTrackingType(e.target.value)}
label="Activity Type"
>
<MenuItem value="feeding">Feeding</MenuItem>
<MenuItem value="sleep">Sleep</MenuItem>
<MenuItem value="diaper">Diaper Change</MenuItem>
<MenuItem value="medicine">Medicine</MenuItem>
</Select>
</FormControl>
</DialogContent>
<DialogActions>
<Button onClick={handleRetry} startIcon={<MicIcon />} color="primary">
Retry Voice Command
</Button>
<Button onClick={handleManualTracking} startIcon={<AddIcon />} variant="contained">
Add Manual Tracking
</Button>
</DialogActions>
</Dialog>
{/* Snackbar for feedback */}
<Snackbar
open={snackbar.open}