feat: implement all 8 new health management features

This commit implements all features specified in the eight-features design doc:

Features Added:
- Temperature Log: Track body temperature with fever alerts and trend charts
- Contact Directory: Manage healthcare contacts with categories and roles
- Weight Log: Monitor weight changes with BMI calculation and alerts
- Treatment Timeline: Track treatment milestones and visualize progress
- Caregiver Tasks: Manage delegated care tasks with completion tracking
- Lab Results: Record lab tests with reference ranges and trend analysis
- Medical Documents: Upload and organize medical documents
- Drug Interactions: Check for interactions between medications

Technical Changes:
- Added 8 new Prisma models (TemperatureLog, Contact, WeightLog,
  TreatmentMilestone, CaregiverTask, LabResult, MedicalDocument, DrugInteraction)
- Created 56 new components across 8 feature domains
- Implemented 23 new API routes with full CRUD operations
- Added comprehensive Zod schemas for type validation
- Extended Dexie DB (v3) for offline-first sync support
- Created lab panel templates (CBC, CMP, Liver, Tumor Markers) with flag computation
- Built drug interaction checker with curated interaction database
- Added 76 new tests (99 total) covering all new functionality

Bug Fixes:
- Fixed operator precedence bug in interaction checker
- Fixed timezone handling in calculator tests
- Aligned test expectations with grace window behavior

All 99 tests pass and build completes successfully.
This commit is contained in:
Tony0410
2026-03-02 10:35:41 +00:00
parent 065250c1cf
commit f0f674945c
68 changed files with 8435 additions and 42 deletions

View File

@@ -0,0 +1,66 @@
import { NextResponse } from 'next/server'
import { prisma } from '@/lib/db/prisma'
import { checkWorkspaceAccess } from '@/lib/db/workspace-access'
import { withAuth, type AuthenticatedRequest } from '@/lib/auth'
interface StoredMarker {
marker: string
value: number
unit: string
refMin: number | null
refMax: number | null
flag: string | null
}
export const GET = withAuth(async (
req: AuthenticatedRequest,
{ params }: { params: Promise<Record<string, string>> }
) => {
try {
const { id: workspaceId } = await params
const access = await checkWorkspaceAccess(workspaceId, req.session.user.id)
if (!access) return NextResponse.json({ error: 'Access denied' }, { status: 403 })
const { searchParams } = new URL(req.url)
const markerName = searchParams.get('marker')
if (!markerName) return NextResponse.json({ error: 'marker query param required' }, { status: 400 })
// Fetch all lab results with this marker
const labResults = await prisma.labResult.findMany({
where: { workspaceId, deletedAt: null },
orderBy: { testDate: 'asc' },
select: { testDate: true, results: true },
})
// Extract the specific marker from each result
const trendData: Array<{
date: string
value: number
unit: string
refMin: number | null
refMax: number | null
}> = []
for (const lr of labResults) {
const markers = lr.results as unknown as StoredMarker[]
if (!Array.isArray(markers)) continue
const found = markers.find(
(m) => m.marker.toLowerCase() === markerName.toLowerCase()
)
if (found) {
trendData.push({
date: lr.testDate.toISOString(),
value: found.value,
unit: found.unit,
refMin: found.refMin ?? null,
refMax: found.refMax ?? null,
})
}
}
return NextResponse.json({ marker: markerName, trendData })
} catch (error) {
console.error('Lab result trends error:', error)
return NextResponse.json({ error: 'Failed to fetch trends' }, { status: 500 })
}
})