feat: add health data management workspace

This commit is contained in:
2026-07-03 00:25:11 -07:00
parent dc034b6d85
commit 41807ff557
53 changed files with 7367 additions and 3 deletions

View File

@@ -0,0 +1 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}

View 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`

View File

@@ -0,0 +1 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}

View File

@@ -0,0 +1,74 @@
# Implementation Plan
## 1. Align Task Metadata
- Update task title/description if needed so the Trellis task reflects `健康数据管理` instead of care-service workspace wording.
- Keep the existing task directory unless a rename is explicitly requested.
## 2. Schema And Migration
- Add health enums and tables in `modules/core/server/schema.ts`.
- Add indexes/unique constraints:
- profile unique `(organizationId, elderId)`
- recent vital lookups by `(organizationId, elderId, recordedAt)`
- review queues by `(organizationId, status, severity)`
- Run `pnpm db:generate`.
- Review generated migration SQL.
## 3. Types And Permissions
- Add `health:read` and `health:manage` to `modules/core/types.ts`.
- Add permission definitions and default role grants in `modules/core/server/permissions.ts`.
- Add health DTOs, labels, validators, and formatting helpers in `modules/health/types.ts`.
## 4. Server Operations
- Create `modules/health/server/operations.ts`.
- Implement batched list helper for the admin page.
- Implement mutation helpers for:
- profile upsert
- vital create
- chronic condition create
- review status update
- Ensure all helpers require an organization ID and validate elder/review ownership.
## 5. API Routes
- Add `/api/health/admin`.
- Add `/api/health/profiles/[elderId]`.
- Add `/api/health/vitals`.
- Add `/api/health/chronic-conditions`.
- Add `/api/health/reviews/[id]`.
- Use `requirePermission`, structured `jsonSuccess` / `jsonFailure`, and audit logs.
## 6. Admin Page UI
- Add `app/(app)/app/settings/health/page.tsx`.
- Add `app/(app)/app/[organizationSlug]/settings/health/page.tsx` delegating to the unscoped route pattern.
- Add `modules/health/components/HealthAdminClient.tsx`.
- Use existing UI adapters for buttons, cards, dialogs, inputs, selects, tables, and badges.
- Add management nav and breadcrumbs.
## 7. Default Workspace Data
- Carefully inspect current uncommitted `modules/core/server/default-workspace-data.ts`.
- Add health seed records only if compatible with existing default seed flow.
- Tie seed records to seeded elders and organization.
## 8. Verification
- Run `pnpm lint`.
- Run `pnpm type-check`.
- Run `pnpm build`.
- If a database is available, run `pnpm db:migrate` and manually test:
- scoped management page loads.
- health profile upsert persists.
- vital record create persists.
- abnormal review update persists.
- insufficient permission returns `403`.
## Rollback Points
- Before migration generation: schema changes can be removed directly.
- After migration generation: remove generated migration files and schema changes together.
- After UI/API addition: remove `modules/health`, health API routes, settings health pages, nav/breadcrumb additions, and health permissions.

View File

@@ -0,0 +1,117 @@
# Health Care Admin Page
## Goal
Build a separate backend management page for health care data instead of turning the existing operational `健康照护` module into a management surface. The first usable slice should let authorized staff manage real persisted health profiles, vital signs, chronic conditions, and abnormal-review records for elders in the active organization.
## Background And Confirmed Facts
- The current `健康照护` operational route renders the generic placeholder only: `app/(app)/app/health/page.tsx`.
- The scoped `健康照护` route delegates to the unscoped placeholder: `app/(app)/app/[organizationSlug]/health/page.tsx`.
- The placeholder contract explicitly forbids fabricated operational records until real Drizzle-backed data exists: `.trellis/spec/frontend/components.md`.
- The sidebar already has a separate `管理系统` group for backend management pages: `modules/shared/lib/navigation.ts`.
- Existing management routes live under `/settings/...` and have scoped mirrors under `/app/[organizationSlug]/settings/...`.
- Breadcrumbs already treat `/settings/...` as the management area: `modules/shared/components/AppBreadcrumbs.tsx`.
- The current Drizzle schema includes organizations, elders, rooms, beds, admissions, audit logs, incidents, permissions, roles, and sessions, but no health profile, vital sign, chronic condition, or health review tables: `modules/core/server/schema.ts`.
- Existing persisted feature pages load data in Server Components and pass initial records into Client Components, for example `app/(app)/app/elders/page.tsx` and `app/(app)/app/beds/page.tsx`.
- The user explicitly clarified that this should be a separate backend management page, split from the current operational health page.
## Requirements
### Route And Navigation
- Add a new backend management route under the management area.
- Recommended route: `/app/settings/health` with scoped mirror `/app/{organizationSlug}/settings/health`.
- Add a `管理系统` navigation item labeled `健康数据管理`.
- Keep the existing `/app/health` operational page separate from this management page for this task.
- Breadcrumbs must show the new route under `工作台 > 管理系统 > 健康数据管理`.
### Data Model And Persistence
- Add Drizzle-backed tables for the health-care admin MVP.
- Health data must be scoped by `organizationId`.
- Health data that belongs to an elder must reference a real elder in the same organization.
- Minimum persisted domains:
- health profile: allergy notes, medical history, medication notes, restrictions, emergency health note.
- vital sign record: recorded time, source, blood pressure, heart rate, temperature, SpO2, blood glucose, weight, notes.
- chronic condition: condition name, status, diagnosed date or note, treatment/follow-up notes.
- abnormal review: severity, status, source vital record or elder, reviewer, reviewed time, handling notes.
- Generated migrations must stay coherent with `modules/core/server/schema.ts`.
- New mutations must use Drizzle queries or transactions through server boundaries, not legacy write helpers.
### Permissions
- Add explicit health permissions unless implementation review finds an existing permission is a better fit:
- `health:read`
- `health:manage`
- The new management navigation item should require `health:read`.
- Mutations should require `health:manage`.
- Default role seeding should grant sensible permissions:
- platform admin: all permissions through the existing all-permissions pattern.
- organization admin and manager: read/manage.
- caregiver: read/manage for health records if the product treats caregivers as health-data operators.
- viewer: read only.
### API And Server Boundaries
- Add health-specific Route Handlers under `/api/health/...`.
- All health APIs must call `requirePermission`.
- All APIs must validate active organization presence before reading or mutating organization-scoped data.
- Invalid or cross-organization IDs must return structured failures.
- Mutations must record audit logs for create/update/review actions.
- API responses must follow the existing `{ success, reason, ... }` shape.
### Admin UI
- The new page must be dense and operational, not a hero or marketing page.
- The first screen should show summary metrics and data management controls.
- Expected tabs or sections:
- 健康档案
- 生命体征
- 慢病记录
- 异常复核
- The page should support filtering by elder, status/severity, and search terms where useful.
- Authorized users should be able to create or update health records from the UI.
- Users without manage permission may view records but must not see working mutation paths.
- Use project UI adapters from `components/ui/*`.
- Do not fabricate UI-only health records.
### Default Workspace Data
- If seeded workspace data is used for first-run development/demo, it must create real persisted health records tied to real seeded elders.
- Seed records should cover normal vitals, abnormal vitals, chronic conditions, and at least one pending abnormal review.
## Acceptance Criteria
- [ ] `/app/settings/health` renders a real backend health management page.
- [ ] `/app/{organizationSlug}/settings/health` renders the same management page in the active workspace.
- [ ] The new management page is reachable from the `管理系统` navigation group.
- [ ] Existing `/app/health` remains separate from the backend management page.
- [ ] Health admin data is loaded from Drizzle-backed tables and scoped to the active organization.
- [ ] Health profiles, vital signs, chronic conditions, and abnormal reviews have persisted list views.
- [ ] Authorized users can create or update at least the MVP health records from the UI.
- [ ] Users without `health:manage` cannot mutate health records through the API.
- [ ] Health mutations write audit log entries.
- [ ] Invalid input and cross-organization references return structured failure responses.
- [ ] Seeded examples, if added, are real database rows tied to seeded elders.
- [ ] `pnpm db:generate` is run after schema changes and generated SQL is reviewed.
- [ ] `pnpm lint` passes.
- [ ] `pnpm type-check` passes.
- [ ] `pnpm build` passes.
## Out Of Scope
- Replacing the existing operational `/app/health` placeholder with a full clinical dashboard.
- Device telemetry integration.
- External medical system integration.
- AI-generated health recommendations.
- Complex clinical decision support.
- Full recurring measurement schedule engine.
- Family app notifications.
- Billing, pricing, or insurance flows.
- Replacing Route Handlers with oRPC.
- Replacing the current authentication/session implementation.
## Open Questions
- None currently blocking planning.

View File

@@ -0,0 +1,26 @@
{
"id": "care-service-workspace",
"name": "care-service-workspace",
"title": "新增健康照护后台管理页",
"description": "新增独立的健康数据管理后台页面,与现有运营健康照护入口分开。",
"status": "in_progress",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P2",
"creator": "TalexDreamSoul",
"assignee": "TalexDreamSoul",
"createdAt": "2026-07-02",
"completedAt": null,
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": "07-02-operations-module-roadmap",
"relatedFiles": [],
"notes": "",
"meta": {}
}