feat: add health data management workspace
This commit is contained in:
1
.trellis/tasks/07-02-care-service-workspace/check.jsonl
Normal file
1
.trellis/tasks/07-02-care-service-workspace/check.jsonl
Normal 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."}
|
||||
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`
|
||||
@@ -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."}
|
||||
74
.trellis/tasks/07-02-care-service-workspace/implement.md
Normal file
74
.trellis/tasks/07-02-care-service-workspace/implement.md
Normal 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.
|
||||
117
.trellis/tasks/07-02-care-service-workspace/prd.md
Normal file
117
.trellis/tasks/07-02-care-service-workspace/prd.md
Normal 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.
|
||||
26
.trellis/tasks/07-02-care-service-workspace/task.json
Normal file
26
.trellis/tasks/07-02-care-service-workspace/task.json
Normal 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": {}
|
||||
}
|
||||
Reference in New Issue
Block a user