feat: add care execution workspace

This commit is contained in:
2026-07-03 00:35:04 -07:00
parent df7c2efcc5
commit b6b15acc69
20 changed files with 4270 additions and 18 deletions

View File

@@ -270,3 +270,70 @@ await database
.set({ status, updatedAt: new Date() })
.where(and(eq(healthAnomalyReviews.id, id), eq(healthAnomalyReviews.organizationId, organizationId)));
```
## Scenario: Care Execution Workspace
### 1. Scope / Trigger
- Trigger: adding or changing persisted daily care task execution behavior.
- Applies when replacing reserved operational module placeholders with real care task data. Care records must be Drizzle-backed and organization-scoped.
### 2. Signatures
- `GET /api/care/tasks`
- `PATCH /api/care/tasks/[id]`
- `listCareExecutionData(organizationId: string): Promise<CareExecutionData>`
- `updateCareTaskStatus(input): Promise<CareTask | { success: false; reason: string; status: number }>`
- Drizzle table: `care_tasks`
### 3. Contracts
- `/app/care` and `/app/{organizationSlug}/care` render the real care workspace once `care_tasks` exists.
- Reads require `care:read`; mutations require `care:manage`.
- `GET /api/care/tasks` returns `{ success: true; reason: string; data: CareExecutionData }`; client refresh code must read `result.data`.
- `PATCH /api/care/tasks/[id]` request: `{ status: "pending" | "in_progress" | "completed" | "cancelled"; executionNotes?: string }`.
- Completing a task sets `completedAt` and `completedByAccountId`; moving a task away from `completed` clears completion metadata.
- Mutations update by both `id` and active `organizationId`, then write an audit log after success.
### 4. Validation & Error Matrix
- Missing active organization on list -> `400` / `请选择机构后查看护理任务`.
- Missing active organization on mutation -> `400` / `请选择机构后处理护理任务`.
- Invalid status -> `400` / `护理任务状态无效`.
- Missing or cross-organization task ID -> `404` / `护理任务不存在`.
- Missing permission -> `403` from `requirePermission`; route must not call domain helpers.
- Update returning no row -> structured mutation failure.
### 5. Good/Base/Bad Cases
- Good: keep care task examples in default workspace seed tied to real seeded elders.
- Good: use one additive `care_tasks` table for MVP execution records instead of building a full recurring care-plan engine.
- Good: preserve `/app/health` separation; care execution is operational and not the health admin settings page.
- Base: `elderId` can be nullable for future public-area checks, but seeded MVP examples should use real elders.
- Bad: render fake care metrics in `ModulePage` after the module becomes real.
- Bad: update task status by ID alone without `organizationId`.
- Bad: only disable buttons in the UI while leaving Route Handlers on broad elder permissions.
### 6. Tests Required
- `pnpm test` with API assertions for list and status update routes.
- Route tests must cover happy path, permission denial, missing organization, invalid status, and missing/cross-organization task IDs.
- `pnpm db:generate` after schema changes and review generated SQL for only care enum/table/index/FK changes.
- `pnpm lint`, `pnpm type-check`, and `pnpm build`.
### 7. Wrong vs Correct
#### Wrong
```ts
await database.update(careTasks).set({ status }).where(eq(careTasks.id, id));
```
#### Correct
```ts
await database
.update(careTasks)
.set({ status, updatedAt: new Date() })
.where(and(eq(careTasks.id, id), eq(careTasks.organizationId, organizationId)));
```

View File

@@ -0,0 +1,104 @@
# Care Execution Workspace Design
## Summary
Replace the reserved `/care` module page with a real operational workspace backed by persisted care execution records. This task owns daily care execution only: task list, status movement, completion notes, and seeded examples.
## Route Boundary
- Unscoped route: `app/(app)/app/care/page.tsx`
- Scoped route: `app/(app)/app/[organizationSlug]/care/page.tsx`
- Navigation item remains in the `运营` group.
- Permission should move from `elder:update` to explicit care permissions:
- `care:read`
- `care:manage`
The unscoped page should:
1. Load `getCurrentAuthContext()`.
2. Redirect unauthenticated users to `/login`.
3. Redirect users without `care:read` to the workspace dashboard.
4. Require an active organization.
5. Load care data through a focused server helper.
6. Render a client workspace with `canManage` based on `care:manage`.
## Data Model
Add one MVP table: `care_tasks`.
- `id`
- `organizationId`
- `elderId` nullable, because some tasks can be room/public-area checks later
- `title`
- `careType`: `daily_care | meal | medication | rehab | inspection | cleaning | other`
- `priority`: `low | normal | high | urgent`
- `status`: `pending | in_progress | completed | cancelled`
- `scheduledAt`
- `assigneeLabel`
- `executionNotes`
- `completedAt`
- `createdByAccountId`
- `completedByAccountId`
- `createdAt`
- `updatedAt`
Indexes:
- `(organizationId, status, priority)` for queues.
- `(organizationId, scheduledAt)` for daily schedule ordering.
## Server Helpers
Create `modules/care/`:
- `modules/care/types.ts`
- enum values, labels, DTOs, validators.
- `modules/care/server/operations.ts`
- `listCareExecutionData(organizationId: string)`
- `updateCareTaskStatus(input)`
- `modules/care/components/CareWorkspaceClient.tsx`
- dense tabs/filters/table and status action dialogs.
Reads should batch the care tasks and elder names in one query shape. Mutations must filter by `organizationId` and update by task ID plus organization ID.
## API Design
- `GET /api/care/tasks`
- permission: `care:read`
- response: `{ success: true; reason: string; data: CareExecutionData }`
- `PATCH /api/care/tasks/[id]`
- permission: `care:manage`
- request: `{ status: "pending" | "in_progress" | "completed" | "cancelled"; executionNotes?: string }`
- completion sets `completedAt` and `completedByAccountId`
- moving out of completed clears completion metadata
- records audit log
All failures use `{ success: false; reason: string }`.
## UI Design
The care workspace should be operational and compact:
- Metrics: pending, in-progress, completed today, high/urgent.
- Filters: status, priority, care type, search by elder/title/assignee.
- Table columns: task, elder, type, priority, status, scheduled time, assignee, completed time, actions.
- Actions:
- pending -> start
- in-progress/pending -> complete with notes
- completed/cancelled are visible but not primary work queue items
- Users without `care:manage` can view but action buttons are disabled or hidden.
## Default Workspace Data
Extend `seedDefaultWorkspaceData()` with care tasks after seeded elders exist. Examples should cover:
- pending morning care
- in-progress rehabilitation or inspection
- completed meal/medication task
- urgent/high overdue-style task scheduled in the past
## Compatibility And Rollback
- Existing `/app/care` placeholder is replaced only for this module.
- New tables are additive; rollback is dropping generated care migration and removing `modules/care`, care API routes, and route/page changes.
- Dashboard integration remains out of scope until the dedicated dashboard task.

View File

@@ -0,0 +1,61 @@
# Implementation Plan
## 1. Schema And Migration
- Add care enums and `care_tasks` table to `modules/core/server/schema.ts`.
- Add indexes for queue and schedule queries.
- Run `pnpm db:generate`.
- Review generated SQL.
## 2. Types And Permissions
- Add `care:read` and `care:manage` to `modules/core/types.ts`.
- Add permission definitions and default role grants in `modules/core/server/permissions.ts`.
- Move navigation `/care` permission to `care:read`.
- Add care DTOs, labels, and validators in `modules/care/types.ts`.
## 3. TDD API Tests
- Add `app/api/care/care-routes.test.ts` before implementation.
- Cover:
- list care tasks
- permission denial
- missing active organization
- start task
- complete task with notes
- invalid status
- missing/cross-organization task
## 4. Server Operations And API Routes
- Add `modules/care/server/operations.ts`.
- Add `GET /api/care/tasks`.
- Add `PATCH /api/care/tasks/[id]`.
- Use `requirePermission`, structured failures, organization scoping, and audit logs.
## 5. Workspace UI
- Replace `app/(app)/app/care/page.tsx` placeholder with real Server Component.
- Keep scoped route delegating to the unscoped route.
- Add `modules/care/components/CareWorkspaceClient.tsx`.
- Implement metrics, filters, table, and start/complete actions.
## 6. Default Workspace Data
- Extend `modules/core/server/default-workspace-data.ts` with care task examples tied to seeded elders.
- Keep seed insertion inside the first-run default workspace transaction.
## 7. Verification
- `pnpm test`
- `pnpm lint`
- `pnpm type-check`
- `pnpm db:generate`
- Review generated SQL.
- `pnpm build`
## Rollback Points
- Before migration generation: remove schema and care module files.
- After migration generation: remove generated care migration plus schema changes.
- After UI/API: remove care API routes, `modules/care`, page changes, permissions, nav permission change, and seed rows.

View File

@@ -3,7 +3,7 @@
"name": "care-execution-workspace",
"title": "护理服务执行工作台",
"description": "",
"status": "planning",
"status": "in_progress",
"dev_type": null,
"scope": null,
"package": null,