feat: add health data management workspace
This commit is contained in:
191
.trellis/tasks/07-02-care-service-workspace/design.md
Normal file
191
.trellis/tasks/07-02-care-service-workspace/design.md
Normal file
@@ -0,0 +1,191 @@
|
||||
# Health Care Admin Page Design
|
||||
|
||||
## Summary
|
||||
|
||||
Add a dedicated management route under `/settings/health` for persisted elder health data. This keeps operational navigation (`/health`) separate from backend data administration, matching the user's direction to split the management surface into its own page.
|
||||
|
||||
## Route Boundary
|
||||
|
||||
- Unscoped route: `app/(app)/app/settings/health/page.tsx`
|
||||
- Scoped route: `app/(app)/app/[organizationSlug]/settings/health/page.tsx`
|
||||
- Optional redirect source: none in MVP. `/app/health` stays independent.
|
||||
- Navigation: add a management item under `管理系统`.
|
||||
- Breadcrumbs: add `"/settings/health": "健康数据管理"`.
|
||||
|
||||
The page should follow existing settings pages:
|
||||
|
||||
1. Server Component loads `getCurrentAuthContext()`.
|
||||
2. Redirect unauthenticated users to `/login`.
|
||||
3. Redirect users without `health:read` back to a safe workspace route.
|
||||
4. Load persisted initial data through server helpers.
|
||||
5. Render a focused Client Component for filtering and mutation UI.
|
||||
|
||||
## Data Model
|
||||
|
||||
All tables use `organizationId` for tenant scope and `createdAt` / `updatedAt` timestamps.
|
||||
|
||||
### `health_profiles`
|
||||
|
||||
- `id`
|
||||
- `organizationId`
|
||||
- `elderId`
|
||||
- `allergyNotes`
|
||||
- `medicalHistory`
|
||||
- `medicationNotes`
|
||||
- `careRestrictions`
|
||||
- `emergencyNotes`
|
||||
- `createdAt`
|
||||
- `updatedAt`
|
||||
|
||||
Constraint: unique `(organizationId, elderId)` so each elder has one admin health profile.
|
||||
|
||||
### `vital_records`
|
||||
|
||||
- `id`
|
||||
- `organizationId`
|
||||
- `elderId`
|
||||
- `recordedAt`
|
||||
- `source`: `manual | device | import`
|
||||
- `systolicBp`
|
||||
- `diastolicBp`
|
||||
- `heartRate`
|
||||
- `temperatureTenths`
|
||||
- `spo2`
|
||||
- `bloodGlucoseTenths`
|
||||
- `weightTenths`
|
||||
- `notes`
|
||||
- `createdByAccountId`
|
||||
- `createdAt`
|
||||
- `updatedAt`
|
||||
|
||||
Integer tenths avoid PostgreSQL numeric string handling for MVP decimal values.
|
||||
|
||||
### `chronic_conditions`
|
||||
|
||||
- `id`
|
||||
- `organizationId`
|
||||
- `elderId`
|
||||
- `name`
|
||||
- `status`: `active | controlled | resolved`
|
||||
- `diagnosedAt`
|
||||
- `treatmentNotes`
|
||||
- `followUpNotes`
|
||||
- `createdAt`
|
||||
- `updatedAt`
|
||||
|
||||
### `health_anomaly_reviews`
|
||||
|
||||
- `id`
|
||||
- `organizationId`
|
||||
- `elderId`
|
||||
- `vitalRecordId`
|
||||
- `severity`: `info | warning | critical`
|
||||
- `status`: `pending | reviewed | resolved`
|
||||
- `title`
|
||||
- `description`
|
||||
- `reviewedByAccountId`
|
||||
- `reviewedAt`
|
||||
- `resolutionNotes`
|
||||
- `createdAt`
|
||||
- `updatedAt`
|
||||
|
||||
`vitalRecordId` is nullable so reviews can also be created directly against an elder health concern.
|
||||
|
||||
## Server Helpers
|
||||
|
||||
Create `modules/health/`:
|
||||
|
||||
- `modules/health/types.ts`
|
||||
- shared enum values, labels, API DTO types, and validators.
|
||||
- `modules/health/server/operations.ts`
|
||||
- `listHealthAdminData(organizationId: string)`
|
||||
- `upsertHealthProfile(...)`
|
||||
- `createVitalRecord(...)`
|
||||
- `createChronicCondition(...)`
|
||||
- `updateHealthReview(...)`
|
||||
- `modules/health/components/HealthAdminClient.tsx`
|
||||
- client-side tabs, filters, dialogs/forms, optimistic-free refresh.
|
||||
|
||||
Server reads should batch queries by organization and join elders once. Avoid per-elder database calls.
|
||||
|
||||
## API Design
|
||||
|
||||
MVP route handlers:
|
||||
|
||||
- `GET /api/health/admin`
|
||||
- permission: `health:read`
|
||||
- returns metrics, elders, profiles, recent vitals, chronic conditions, reviews.
|
||||
- `PUT /api/health/profiles/[elderId]`
|
||||
- permission: `health:manage`
|
||||
- validates elder belongs to active organization.
|
||||
- upserts one profile.
|
||||
- `POST /api/health/vitals`
|
||||
- permission: `health:manage`
|
||||
- validates elder belongs to active organization.
|
||||
- creates vital record.
|
||||
- may create a pending review if values exceed MVP thresholds.
|
||||
- `POST /api/health/chronic-conditions`
|
||||
- permission: `health:manage`
|
||||
- validates elder belongs to active organization.
|
||||
- creates chronic condition.
|
||||
- `PATCH /api/health/reviews/[id]`
|
||||
- permission: `health:manage`
|
||||
- validates review belongs to active organization.
|
||||
- updates status and review metadata.
|
||||
|
||||
All responses use `ApiResult`.
|
||||
|
||||
## Permission Design
|
||||
|
||||
Update `modules/core/types.ts` and `modules/core/server/permissions.ts`:
|
||||
|
||||
- Add `health:read`
|
||||
- Add `health:manage`
|
||||
- Add permission definitions in category `健康`
|
||||
- Grant defaults:
|
||||
- `org_admin`: read/manage
|
||||
- `manager`: read/manage
|
||||
- `caregiver`: read/manage unless user chooses stricter admin-only behavior
|
||||
- `viewer`: read
|
||||
|
||||
This avoids overloading `elder:update` for a separate health management page.
|
||||
|
||||
## UI Design
|
||||
|
||||
The page should look like an internal management console:
|
||||
|
||||
- Header: `健康数据管理`, short description, manage badge if allowed.
|
||||
- Metrics: elders with profiles, vitals today, active chronic conditions, pending reviews.
|
||||
- Tabs:
|
||||
- `健康档案`: elder list with profile completeness and edit dialog.
|
||||
- `生命体征`: recent vitals table and add-vital dialog.
|
||||
- `慢病记录`: condition table and add-condition dialog.
|
||||
- `异常复核`: pending/reviewed/resolved queue with review action.
|
||||
- Filters: elder search, status/severity select, source select where useful.
|
||||
|
||||
Keep tables and cards dense; no landing-page composition.
|
||||
|
||||
## Compatibility And Rollback
|
||||
|
||||
- Existing `/app/health` remains unchanged in MVP, limiting frontend blast radius.
|
||||
- New tables are additive; rollback is dropping the generated health tables/enums and removing nav/API/page files.
|
||||
- Existing seed file has uncommitted work. Implementation must read and preserve user changes in `modules/core/server/default-workspace-data.ts` before adding health seed rows.
|
||||
|
||||
## Validation
|
||||
|
||||
Required:
|
||||
|
||||
- `pnpm db:generate`
|
||||
- Review generated SQL in `drizzle/`
|
||||
- `pnpm lint`
|
||||
- `pnpm type-check`
|
||||
- `pnpm build`
|
||||
|
||||
Manual checks:
|
||||
|
||||
- login as authorized user and open `/app/{slug}/settings/health`
|
||||
- create/update a health profile
|
||||
- create a vital record
|
||||
- create or review an abnormal item
|
||||
- verify reload persists records
|
||||
- verify mutation with insufficient permission returns `403`
|
||||
Reference in New Issue
Block a user