docs: sync drizzle task contracts
This commit is contained in:
112
.trellis/spec/backend/drizzle-postgres-current.md
Normal file
112
.trellis/spec/backend/drizzle-postgres-current.md
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
# Drizzle PostgreSQL Current Persistence Contract
|
||||||
|
|
||||||
|
## Scenario: Current Project After PostgreSQL Migration
|
||||||
|
|
||||||
|
### 1. Scope / Trigger
|
||||||
|
|
||||||
|
- Trigger: implementing or modifying auth, RBAC, audit, elder, facility, admission, organization, settings, or dashboard data flows in this repository.
|
||||||
|
- Applies because this project already has `drizzle-orm`, `postgres`, Drizzle migrations under `drizzle/`, and schema definitions under `modules/core/server/schema.ts`.
|
||||||
|
- This supersedes local JSON persistence for current feature work. Local JSON guidance is only historical or for projects that have not adopted Drizzle yet.
|
||||||
|
|
||||||
|
### 2. Signatures
|
||||||
|
|
||||||
|
- `getDatabase(): AppDatabase`
|
||||||
|
- `checkDatabaseConnection(): Promise<{ ok: boolean; reason: string }>`
|
||||||
|
- `readData(): Promise<AppData>`
|
||||||
|
- `recordAuditLog(input: AuditInput): Promise<AuditLog>`
|
||||||
|
- `requirePermission(permission: Permission, auditContext: DeniedAuditContext): Promise<PermissionCheckSuccess | PermissionCheckFailure>`
|
||||||
|
- Route Handlers use standard `GET`, `POST`, `PATCH`, and `DELETE` exports and return `Response`.
|
||||||
|
|
||||||
|
### 3. Contracts
|
||||||
|
|
||||||
|
- Environment key: `DATABASE_URL` is required for database-backed runtime behavior.
|
||||||
|
- Drizzle source of truth: `modules/core/server/schema.ts`.
|
||||||
|
- Migration output: `drizzle/`.
|
||||||
|
- Database config: `drizzle.config.ts`.
|
||||||
|
- Session cookie:
|
||||||
|
- Name: `teatea_session`
|
||||||
|
- Flags: `httpOnly`, `sameSite: "lax"`, `path: "/"`
|
||||||
|
- Lifetime: 7 days
|
||||||
|
- API response shape:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type ApiResult<T extends Record<string, unknown>> =
|
||||||
|
| ({ success: true; reason: string } & T)
|
||||||
|
| { success: false; reason: string };
|
||||||
|
```
|
||||||
|
|
||||||
|
- `modules/core/server/store.ts` is a compatibility read model:
|
||||||
|
- `readData()` may aggregate Drizzle rows for existing pages.
|
||||||
|
- `writeData()` and `updateData()` must remain unavailable after migration.
|
||||||
|
- New mutation code must use Drizzle queries or transactions.
|
||||||
|
|
||||||
|
### 4. Validation & Error Matrix
|
||||||
|
|
||||||
|
- Missing `DATABASE_URL` -> throw during `getDatabase()` and surface a clear database configuration failure.
|
||||||
|
- Missing/expired session -> `401` with `{ success: false, reason: "未登录或会话已过期" }`.
|
||||||
|
- Missing permission -> `403` with `{ success: false, reason: "权限不足" }` and a denied audit log.
|
||||||
|
- Invalid JSON body -> `400` with a Chinese user-facing `reason`.
|
||||||
|
- Missing record by ID -> `404` with `{ success: false, reason: "<entity>不存在" }`.
|
||||||
|
- Duplicate account email -> `409` with `{ success: false, reason: "账号已存在" }`.
|
||||||
|
- Bed/admission conflict -> structured failure response and no partial mutation.
|
||||||
|
|
||||||
|
### 5. Good/Base/Bad Cases
|
||||||
|
|
||||||
|
- Good: UI submits to a Route Handler, the handler validates input, calls `requirePermission`, mutates Drizzle tables in a transaction when multiple tables are involved, records audit, and returns `ApiResult`.
|
||||||
|
- Base: Server Component reads data directly through focused server helpers or the temporary `readData()` compatibility model.
|
||||||
|
- Bad: New mutation code calls `writeData()` or `updateData()`.
|
||||||
|
- Bad: New code treats `.data/teatea.json` as the active persistence layer.
|
||||||
|
- Bad: API auth is enforced only by hidden UI controls.
|
||||||
|
|
||||||
|
### 6. Tests Required
|
||||||
|
|
||||||
|
- `pnpm lint`
|
||||||
|
- `pnpm type-check`
|
||||||
|
- `pnpm build`
|
||||||
|
- When schema changes are made: `pnpm db:generate`, then review generated SQL and run `pnpm db:migrate` against the configured development database.
|
||||||
|
- Manual or automated integration assertions:
|
||||||
|
- first setup creates platform admin, organization, organization roles, membership, and cookie session
|
||||||
|
- protected app route redirects without cookie
|
||||||
|
- protected app route renders with a valid cookie
|
||||||
|
- CRUD mutation persists across reload/API list
|
||||||
|
- denied permission returns `403` and writes an audit log
|
||||||
|
- admission mutation updates `admissions`, `beds`, and `elders` consistently
|
||||||
|
|
||||||
|
### 7. Wrong vs Correct
|
||||||
|
|
||||||
|
#### Wrong
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await updateData((data) => {
|
||||||
|
data.beds.push(newBed);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Correct
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const database = getDatabase();
|
||||||
|
await database.insert(beds).values({
|
||||||
|
organizationId,
|
||||||
|
roomId,
|
||||||
|
code,
|
||||||
|
status: "available",
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Wrong
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await database.insert(admissions).values({ organizationId, elderId, bedId });
|
||||||
|
await database.update(beds).set({ status: "occupied" }).where(eq(beds.id, bedId));
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Correct
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await database.transaction(async (transaction) => {
|
||||||
|
await transaction.insert(admissions).values({ organizationId, elderId, bedId });
|
||||||
|
await transaction.update(beds).set({ status: "occupied" }).where(eq(beds.id, bedId));
|
||||||
|
await transaction.update(elders).set({ status: "active" }).where(eq(elders.id, elderId));
|
||||||
|
});
|
||||||
|
```
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
| [database.md](./database.md) | Drizzle ORM, queries, transactions, SQL patterns | Database operations |
|
| [database.md](./database.md) | Drizzle ORM, queries, transactions, SQL patterns | Database operations |
|
||||||
| [authentication.md](./authentication.md) | better-auth, sessions, OAuth, protected procedures | Auth-related features |
|
| [authentication.md](./authentication.md) | better-auth, sessions, OAuth, protected procedures | Auth-related features |
|
||||||
| [logging.md](./logging.md) | Structured logging, Sentry tracing, telemetry | Debugging, observability |
|
| [logging.md](./logging.md) | Structured logging, Sentry tracing, telemetry | Debugging, observability |
|
||||||
|
| [drizzle-postgres-current.md](./drizzle-postgres-current.md) | Current project Drizzle/PostgreSQL persistence boundary | Any persisted feature in this repository |
|
||||||
| [local-json-mvp.md](./local-json-mvp.md) | Local JSON persistence/session/API contracts before database adoption | Single-repo MVP features without DB/oRPC deps |
|
| [local-json-mvp.md](./local-json-mvp.md) | Local JSON persistence/session/API contracts before database adoption | Single-repo MVP features without DB/oRPC deps |
|
||||||
| [deployment.md](./deployment.md) | Wulanchabu deployment target and validation contract | Deploying this project |
|
| [deployment.md](./deployment.md) | Wulanchabu deployment target and validation contract | Deploying this project |
|
||||||
| [performance.md](./performance.md) | Concurrency, caching, batch processing, streaming | Performance optimization |
|
| [performance.md](./performance.md) | Concurrency, caching, batch processing, streaming | Performance optimization |
|
||||||
|
|||||||
@@ -2,112 +2,156 @@
|
|||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
This task keeps the app in a single Next.js project and adds a small server-side domain layer inside `modules/`:
|
This task keeps the app in a single Next.js project and continues the Drizzle/PostgreSQL-backed server domain layer inside `modules/`:
|
||||||
|
|
||||||
- `modules/core/server/`: server-only persistence, session, permissions, and audit helpers.
|
- `modules/core/server/`: Drizzle connection, schema, session, permissions, audit helpers, and compatibility read models.
|
||||||
- `modules/auth/`: UI and client interactions for setup, login, logout, and session state.
|
- `modules/auth/`: UI and client interactions for setup, register, login, logout, and session state.
|
||||||
- `modules/elders/`: elder schemas/types, server actions or API client helpers, and UI components.
|
- `modules/elders/`: elder schemas/types, validation, and CRUD UI components.
|
||||||
- `modules/settings/`: role/account/audit display components.
|
- `modules/settings/`: role/account/organization/audit/status display and management components.
|
||||||
- `app/api/.../route.ts`: Route Handlers for auth, session, elders, accounts, and audit APIs.
|
- `modules/facilities` or existing page-local modules: room, bed, occupancy, admission, transfer, and discharge UI as the feature grows.
|
||||||
|
- `app/api/.../route.ts`: Route Handlers for auth, session, elders, facilities, admissions, accounts, roles, organizations, status, and audit APIs.
|
||||||
|
|
||||||
The persistence boundary should be centralized behind helper functions that read/write JSON files. UI and route handlers must not know file paths directly.
|
The persistence boundary is now Drizzle/PostgreSQL, not JSON files. UI and route handlers should use domain helpers or direct Drizzle queries through `getDatabase()` in server-only modules. New mutation paths must not use `writeData()` or `updateData()`.
|
||||||
|
|
||||||
## Data Storage
|
## Data Storage
|
||||||
|
|
||||||
Use a JSON file store under `.data/teatea.json` by default. The file should contain:
|
The current persistent model is PostgreSQL with Drizzle schema in `modules/core/server/schema.ts` and generated migrations under `drizzle/`.
|
||||||
|
|
||||||
```ts
|
Important existing tables:
|
||||||
type AppData = {
|
|
||||||
accounts: Account[];
|
|
||||||
sessions: Session[];
|
|
||||||
elders: Elder[];
|
|
||||||
auditLogs: AuditLog[];
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
The store module owns initialization, read, write, and update operations. Updates should read the current snapshot, apply a synchronous mutation callback, and write the full file back. This is enough for the MVP and gives a clear future replacement boundary for Drizzle/Postgres.
|
- Identity and tenancy: `accounts`, `sessions`, `organizations`, `memberships`, `join_requests`, `organization_invitations`.
|
||||||
|
- Authorization: `roles`, `permissions`, `role_permissions`.
|
||||||
|
- Operations: `elders`, `rooms`, `beds`, `admissions`.
|
||||||
|
- Governance and status: `audit_logs`, `system_incidents`, `system_settings`.
|
||||||
|
|
||||||
|
`modules/core/server/store.ts` should be treated as a compatibility read model only:
|
||||||
|
|
||||||
|
- `readData()` may aggregate Drizzle rows into existing UI-friendly types while legacy pages are migrated.
|
||||||
|
- `writeData()` and `updateData()` intentionally throw after the PostgreSQL migration.
|
||||||
|
- New feature work should prefer focused query helpers so the compatibility model does not become the long-term domain layer.
|
||||||
|
|
||||||
## API Contracts
|
## API Contracts
|
||||||
|
|
||||||
Route Handlers return a consistent response shape:
|
Route Handlers return a consistent response shape:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
type ApiResult<T> =
|
type ApiResult<T extends Record<string, unknown>> =
|
||||||
| ({ success: true; reason: string } & T)
|
| ({ success: true; reason: string } & T)
|
||||||
| { success: false; reason: string };
|
| { success: false; reason: string };
|
||||||
```
|
```
|
||||||
|
|
||||||
Planned endpoints:
|
Existing or expected endpoints:
|
||||||
|
|
||||||
- `GET /api/auth/bootstrap`: returns whether setup is required.
|
- `GET /api/auth/bootstrap`: returns whether setup is required.
|
||||||
- `POST /api/auth/setup`: creates the first admin account, creates a session, logs setup.
|
- `POST /api/auth/setup`: creates the first platform admin, first organization, session, roles/membership, and audit log.
|
||||||
|
- `POST /api/auth/register`: creates a user registration or invitation-based account flow where enabled.
|
||||||
- `POST /api/auth/login`: validates credentials, creates a session, logs login.
|
- `POST /api/auth/login`: validates credentials, creates a session, logs login.
|
||||||
- `POST /api/auth/logout`: deletes current session cookie/session, logs logout.
|
- `POST /api/auth/logout`: deletes current session cookie/session, logs logout.
|
||||||
- `GET /api/auth/session`: returns current account and permissions.
|
- `GET /api/auth/session`: returns current account, organization, membership, and permissions.
|
||||||
- `GET /api/elders`: lists elder profiles.
|
- `GET /api/elders`: lists elder profiles for the active organization.
|
||||||
- `POST /api/elders`: creates an elder profile.
|
- `POST /api/elders`: creates an elder profile and optional active admission.
|
||||||
- `PATCH /api/elders/[id]`: updates an elder profile.
|
- `PATCH /api/elders/[id]`: updates an elder profile.
|
||||||
- `DELETE /api/elders/[id]`: deletes an elder profile.
|
- `DELETE /api/elders/[id]`: deletes an elder profile.
|
||||||
|
- `GET /api/facilities/rooms`: lists rooms for the active organization.
|
||||||
|
- `POST /api/facilities/rooms`: creates room records when facility management UI exposes this.
|
||||||
|
- `GET /api/facilities/beds`: lists beds and current occupancy.
|
||||||
|
- `POST /api/facilities/beds`: creates bed records when facility management UI exposes this.
|
||||||
|
- `GET /api/admissions`: lists admission and transfer history.
|
||||||
|
- `POST /api/admissions`: admits or transfers an elder into an available bed.
|
||||||
|
- Planned: `PATCH /api/admissions/[id]` or equivalent mutation endpoint for transfer/discharge if POST cannot express the workflow cleanly.
|
||||||
- `GET /api/settings/accounts`: lists accounts for authorized roles.
|
- `GET /api/settings/accounts`: lists accounts for authorized roles.
|
||||||
- `GET /api/settings/roles`: lists built-in role definitions.
|
- `GET /api/settings/roles`: lists built-in and organization roles.
|
||||||
|
- `GET /api/settings/permissions`: lists permission coverage.
|
||||||
- `GET /api/audit-logs`: lists recent audit events.
|
- `GET /api/audit-logs`: lists recent audit events.
|
||||||
|
|
||||||
## Authentication
|
## Authentication
|
||||||
|
|
||||||
Sessions use an HTTP-only cookie. Route Handlers read and write cookies with `await cookies()` from `next/headers`, matching current Next.js behavior.
|
Sessions use an HTTP-only cookie named `teatea_session`. Route Handlers read and write cookies with `await cookies()` from `next/headers`, matching current Next.js behavior.
|
||||||
|
|
||||||
Passwords are never stored in plain text. For the MVP, use Node built-in crypto with a per-account salt and `scryptSync` or `scrypt` for password hashing. This avoids adding a dependency while keeping the current local milestone meaningfully better than browser-only auth.
|
Passwords are never stored in plain text. The current implementation uses Node built-in crypto with per-account salt and `scryptSync`, which is acceptable for this local MVP until a dedicated auth library is introduced in a separate task.
|
||||||
|
|
||||||
|
Protected app routes should use server session state where practical instead of client-only localStorage guards. Server-rendered pages that depend on session cookies must opt out of static prerendering with `export const dynamic = "force-dynamic"` where needed.
|
||||||
|
|
||||||
## Permissions
|
## Permissions
|
||||||
|
|
||||||
Define built-in role permissions in one server/shared constants module:
|
Permissions and role definitions live in the core server/shared type boundary:
|
||||||
|
|
||||||
- `account:read`
|
- Platform: `platform:manage`, `organization:read`, `organization:manage`.
|
||||||
- `account:manage`
|
- Account/role/security: `account:read`, `account:manage`, `role:read`, `role:manage`, `permission:read`, `audit:read`.
|
||||||
- `audit:read`
|
- Operations: `facility:read`, `facility:manage`, `admission:read`, `admission:manage`, `elder:read`, `elder:create`, `elder:update`, `elder:delete`.
|
||||||
- `elder:read`
|
- Status: `incident:read`, `incident:manage`.
|
||||||
- `elder:create`
|
|
||||||
- `elder:update`
|
|
||||||
- `elder:delete`
|
|
||||||
|
|
||||||
Route Handlers call a shared `requirePermission(permission)` helper. This helper returns an authenticated context or a structured forbidden/unauthorized result and writes denied audit entries.
|
Route Handlers call a shared `requirePermission(permission)` helper. This helper returns an authenticated context or a structured forbidden/unauthorized response and writes denied audit entries.
|
||||||
|
|
||||||
## Audit Logging
|
## Admission Transactions
|
||||||
|
|
||||||
Audit logging is implemented as a server helper that appends immutable records to the store:
|
Bed/admission mutations must run inside a Drizzle transaction.
|
||||||
|
|
||||||
- timestamp
|
Admit flow:
|
||||||
- actor account ID/email if known
|
|
||||||
- action
|
|
||||||
- target type
|
|
||||||
- target ID
|
|
||||||
- result: `success` or `denied` or `failure`
|
|
||||||
- reason
|
|
||||||
|
|
||||||
Audit writes are part of the route handler flow. The MVP accepts best-effort logging for logout when a session has already expired.
|
1. Validate active organization and `admission:manage`.
|
||||||
|
2. Validate elder belongs to the active organization.
|
||||||
|
3. Validate target bed belongs to the active organization and has status `available`.
|
||||||
|
4. Close or transfer any active admission for that elder if the operation is a transfer.
|
||||||
|
5. Insert a new `admissions` row with status `active`.
|
||||||
|
6. Set target bed status to `occupied`.
|
||||||
|
7. Set elder status to `active`.
|
||||||
|
8. Record an audit log.
|
||||||
|
|
||||||
|
Transfer flow:
|
||||||
|
|
||||||
|
1. Find active admission for the elder.
|
||||||
|
2. Set previous admission status to `transferred` and `dischargedAt` to now.
|
||||||
|
3. Set previous bed status to `available`.
|
||||||
|
4. Insert the new active admission and occupy the target bed.
|
||||||
|
5. Record an audit log.
|
||||||
|
|
||||||
|
Discharge flow:
|
||||||
|
|
||||||
|
1. Find active admission.
|
||||||
|
2. Set admission status to `discharged` and `dischargedAt` to now.
|
||||||
|
3. Set bed status to `available`.
|
||||||
|
4. Set elder status to `discharged` or another explicitly selected status.
|
||||||
|
5. Record an audit log.
|
||||||
|
|
||||||
|
All conflict checks must return structured API failures rather than partially mutating state.
|
||||||
|
|
||||||
## UI Flow
|
## UI Flow
|
||||||
|
|
||||||
Protected app pages should use server session state where practical instead of a client-only `AuthGate`. The app layout can fetch session data and redirect unauthenticated users before rendering protected content.
|
The app should remain an operational workspace: dense, restrained, and action-oriented.
|
||||||
|
|
||||||
The elder page becomes a real data view:
|
Elder page:
|
||||||
|
|
||||||
- Server Component loads initial elders.
|
- Server Component loads initial elders and available beds.
|
||||||
- Client component handles create/edit/delete forms and refreshes after mutations.
|
- Client component handles create/edit/delete forms and refreshes after mutations.
|
||||||
- Controls are disabled or hidden based on current permissions.
|
- Controls are disabled or hidden based on current permissions, while APIs still enforce permission checks.
|
||||||
|
|
||||||
The settings page becomes a server-loaded administrative view:
|
Bed/admission page:
|
||||||
|
|
||||||
- Role definitions table.
|
- Replace raw API placeholders with page-local controls.
|
||||||
- Accounts table.
|
- Use tabs or segmented navigation for overview, bed status, admissions/history, and management actions.
|
||||||
- Recent audit log table.
|
- Place admit/transfer/discharge actions inside the bed/admission workspace, not as a dead global top-bar button.
|
||||||
|
- Show occupancy metrics, active admissions, room/bed tables, and history from Drizzle-backed data.
|
||||||
|
|
||||||
|
Dashboard:
|
||||||
|
|
||||||
|
- Replace hard-coded counters for implemented domains with Drizzle-backed data.
|
||||||
|
- Use a standard chart library for selected charts such as occupancy distribution or admission activity.
|
||||||
|
- Keep chart usage modest; tables and status lists remain the primary record surfaces.
|
||||||
|
|
||||||
|
Screenshot feedback:
|
||||||
|
|
||||||
|
- Move page-level secondary navigation into tabs where appropriate.
|
||||||
|
- Remove oversized intro/hero blocks from routine operational pages.
|
||||||
|
- Remove redundant header action controls that are not wired to the current page workflow.
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
Static module pages not in scope remain untouched except for auth/layout integration. Existing visual design should be preserved: dense operational screens, restrained cards/tables, and Tailwind/Radix-compatible UI components.
|
Static module pages not in scope remain untouched except for auth/layout integration. Existing visual design should be preserved: dense operational screens, restrained cards/tables, project UI adapters under `components/ui/*`, and Tailwind-compatible layout.
|
||||||
|
|
||||||
## Risks and Rollback
|
## Risks and Rollback
|
||||||
|
|
||||||
- File persistence is not safe for high-concurrency production writes. This is acceptable for MVP and documented as a migration boundary.
|
- Drizzle schema and migrations are now the persistence source of truth; mismatches between schema and migrations can block deployment. Rollback point: keep migration changes separate from UI-only work.
|
||||||
- Replacing `AuthGate` with server-side auth can affect all `/app` routes. Rollback point: keep the old component until server session redirect is working.
|
- Admission mutations touch multiple tables. Rollback point: keep transaction helpers isolated and temporarily render read-only admission data if mutation UI is unstable.
|
||||||
- Password hashing must use Node runtime APIs, so affected Route Handlers should run in the Node runtime if needed.
|
- Replacing global header actions can affect user navigation habits. Rollback point: remove only the dead top-bar "入住" button while keeping the sidebar and pages stable.
|
||||||
|
- Adding a chart library increases client bundle size. Rollback point: limit chart usage to one focused client component and keep tables as the fallback data surface.
|
||||||
|
|||||||
@@ -2,76 +2,91 @@
|
|||||||
|
|
||||||
## Checklist
|
## Checklist
|
||||||
|
|
||||||
1. Add core server modules:
|
1. Reconcile existing Drizzle migration state:
|
||||||
- JSON store read/write helpers.
|
- Confirm `modules/core/server/schema.ts` matches `drizzle/` migrations.
|
||||||
- Account/session/password helpers.
|
- Confirm `modules/core/server/db.ts` is the only PostgreSQL connection boundary.
|
||||||
- Role and permission definitions.
|
- Confirm `modules/core/server/store.ts` is treated as a compatibility read model only.
|
||||||
- Audit logging helper.
|
- Remove task assumptions that mention JSON file writes as the target persistence layer.
|
||||||
- Shared API response helpers.
|
|
||||||
|
|
||||||
2. Add auth APIs:
|
2. Verify existing auth/RBAC/audit slice:
|
||||||
- `GET /api/auth/bootstrap`
|
- Confirm setup creates platform admin, organization, session, organization roles, membership, and audit log.
|
||||||
- `POST /api/auth/setup`
|
- Confirm login/logout uses server APIs and the `teatea_session` HTTP-only cookie.
|
||||||
- `POST /api/auth/login`
|
- Confirm `requirePermission` logs denied permission checks.
|
||||||
- `POST /api/auth/logout`
|
- Confirm roles and permissions cover facility and admission operations.
|
||||||
- `GET /api/auth/session`
|
|
||||||
|
|
||||||
3. Migrate auth UI:
|
3. Finish elder CRUD on Drizzle:
|
||||||
- Update `AuthPanel` to call server APIs.
|
- Keep validation in `modules/elders/types.ts`.
|
||||||
- Update `SignOutButton` to call logout API.
|
- Ensure list/create/update/delete APIs query and mutate Drizzle only.
|
||||||
- Replace or bypass localStorage-only `AuthGate` with server session protection in the app layout.
|
- Ensure elder create with `bedId` performs admission and bed status updates inside a transaction.
|
||||||
- Keep unauthenticated redirects and setup redirects working.
|
- Ensure UI refresh and error handling remain usable.
|
||||||
|
|
||||||
4. Add elder APIs and UI:
|
4. Add or complete bed/admission APIs:
|
||||||
- Create elder types and input validators.
|
- Keep `GET /api/facilities/rooms` and `GET /api/facilities/beds` Drizzle-backed.
|
||||||
- Implement list/create/update/delete Route Handlers.
|
- Keep `POST /api/admissions` transactional for admit/transfer.
|
||||||
- Replace `app/(app)/app/elders/page.tsx` static content with server-loaded CRUD UI.
|
- Add discharge and explicit transfer mutation support if the existing POST shape is not enough.
|
||||||
|
- Return structured `{ success, reason, ... }` responses for conflicts and validation failures.
|
||||||
|
- Write audit events for admission create, transfer, discharge, and facility mutations implemented in this task.
|
||||||
|
|
||||||
5. Add settings/audit UI:
|
5. Build bed/admission UI:
|
||||||
- Implement settings/account, role, and audit APIs.
|
- Replace "please create through API" empty states with usable controls where permission allows.
|
||||||
- Replace `app/(app)/app/settings/page.tsx` static content with server-loaded tables.
|
- Add tabs or segmented navigation for overview, bed status, admission actions/history, and facility records.
|
||||||
|
- Move admission actions into the bed/admission workspace.
|
||||||
|
- Remove or replace the dead global top-bar "入住" button in `AppShell`.
|
||||||
|
- Ensure page layout matches screenshot feedback: compact workspace header, no oversized intro block, local page actions.
|
||||||
|
|
||||||
6. Wire audit events:
|
6. Introduce selected Phase 2 UI affordances:
|
||||||
- Account setup/create.
|
- Add tabs to pages that combine overview and management modes.
|
||||||
- Login/logout.
|
- Keep implementation incremental; do not attempt every sidebar module.
|
||||||
- Elder create/update/delete.
|
- Keep user-facing copy operational and concise.
|
||||||
- Denied permission checks.
|
|
||||||
|
|
||||||
7. Verification:
|
7. Add standard chart usage:
|
||||||
|
- Decide the chart library before adding dependency. Recharts is the likely default unless a better project fit is chosen.
|
||||||
|
- Install the library only after checking existing dependencies.
|
||||||
|
- Replace at least one meaningful hand-built chart with a data-backed chart component.
|
||||||
|
- Keep chart component client-only and pass serializable server data into it.
|
||||||
|
|
||||||
|
8. Replace hard-coded operational counters where data exists:
|
||||||
|
- Dashboard bed/elder/admission counters should come from Drizzle-backed data.
|
||||||
|
- Bed/admission workspace metrics should compute from server-loaded rooms, beds, and admissions.
|
||||||
|
- Do not fabricate counters for modules that remain out of scope.
|
||||||
|
|
||||||
|
9. Verification:
|
||||||
- Run `pnpm lint`.
|
- Run `pnpm lint`.
|
||||||
- Run `pnpm type-check`.
|
- Run `pnpm type-check`.
|
||||||
- Run `pnpm build`.
|
- Run `pnpm build`.
|
||||||
- Start dev server and manually exercise setup/login/CRUD/settings if build passes.
|
- Run Drizzle generation/check commands when schema changes are made.
|
||||||
|
- Start dev server and manually exercise setup/login/elder CRUD/bed admission/transfer/discharge/settings if build passes.
|
||||||
|
|
||||||
## Files Expected to Change
|
## Files Expected to Change
|
||||||
|
|
||||||
- `modules/auth/components/AuthPanel.tsx`
|
- `.trellis/tasks/07-01-nextjs-fullstack-crud-rbac-audit/prd.md`
|
||||||
- `modules/auth/components/AuthGate.tsx`
|
- `.trellis/tasks/07-01-nextjs-fullstack-crud-rbac-audit/design.md`
|
||||||
- `modules/auth/components/SignOutButton.tsx`
|
- `.trellis/tasks/07-01-nextjs-fullstack-crud-rbac-audit/implement.md`
|
||||||
- `app/(app)/app/layout.tsx`
|
- `modules/shared/components/AppShell.tsx`
|
||||||
- `app/(app)/app/elders/page.tsx`
|
- `app/(app)/app/beds/page.tsx`
|
||||||
- `app/(app)/app/settings/page.tsx`
|
- `app/(app)/app/dashboard/page.tsx`
|
||||||
|
- `modules/dashboard/components/DashboardHome.tsx`
|
||||||
|
- `app/api/admissions/route.ts`
|
||||||
|
- Potentially `app/api/admissions/[id]/route.ts`
|
||||||
|
- Potentially facility UI components under `modules/` if the bed page is split into smaller client/server components.
|
||||||
|
|
||||||
## Files Expected to Be Created
|
## Files Expected to Stay As Existing Foundations
|
||||||
|
|
||||||
- `modules/core/server/store.ts`
|
- `modules/core/server/db.ts`
|
||||||
|
- `modules/core/server/schema.ts`
|
||||||
- `modules/core/server/auth.ts`
|
- `modules/core/server/auth.ts`
|
||||||
- `modules/core/server/permissions.ts`
|
- `modules/core/server/permissions.ts`
|
||||||
- `modules/core/server/audit.ts`
|
- `modules/core/server/audit.ts`
|
||||||
- `modules/core/server/api.ts`
|
- `modules/core/server/api.ts`
|
||||||
- `modules/elders/types.ts`
|
- `modules/core/server/store.ts` as a temporary Drizzle-backed read model.
|
||||||
- `modules/elders/components/EldersClient.tsx`
|
- Existing auth route handlers unless verification finds a concrete bug.
|
||||||
- `modules/settings/components/SettingsOverview.tsx`
|
- Existing elder route handlers unless admission or bed assignment behavior requires a narrow fix.
|
||||||
- `app/api/auth/bootstrap/route.ts`
|
|
||||||
- `app/api/auth/setup/route.ts`
|
## Dependency Notes
|
||||||
- `app/api/auth/login/route.ts`
|
|
||||||
- `app/api/auth/logout/route.ts`
|
- Do not add oRPC or better-auth in this task.
|
||||||
- `app/api/auth/session/route.ts`
|
- Do not reintroduce JSON-file persistence.
|
||||||
- `app/api/elders/route.ts`
|
- A standard chart library may be added after dependency review. Prefer a focused dashboard dependency over a broad visualization stack.
|
||||||
- `app/api/elders/[id]/route.ts`
|
|
||||||
- `app/api/settings/accounts/route.ts`
|
|
||||||
- `app/api/settings/roles/route.ts`
|
|
||||||
- `app/api/audit-logs/route.ts`
|
|
||||||
|
|
||||||
## Validation Commands
|
## Validation Commands
|
||||||
|
|
||||||
@@ -79,11 +94,18 @@
|
|||||||
pnpm lint
|
pnpm lint
|
||||||
pnpm type-check
|
pnpm type-check
|
||||||
pnpm build
|
pnpm build
|
||||||
pnpm dev
|
pnpm db:generate
|
||||||
|
```
|
||||||
|
|
||||||
|
If schema files are changed, also validate migrations and run the migration flow against the configured development database:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm db:migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
## Rollback Points
|
## Rollback Points
|
||||||
|
|
||||||
- If server auth blocks all app routes, revert only layout/AuthGate changes while keeping APIs.
|
- If admission mutation UI is unstable, keep the Drizzle APIs and temporarily render a read-only admissions table.
|
||||||
- If elder CRUD UI is unstable, keep APIs and temporarily render a read-only server table.
|
- If discharge/transfer API design becomes too broad, implement only admission create/transfer in this slice and keep discharge as a clearly scoped follow-up.
|
||||||
- If file store causes build/runtime issues, move the data directory to `/tmp` behind the same store API for verification, then restore `.data` once filesystem assumptions are fixed.
|
- If chart dependency causes build or bundle issues, remove the chart component and keep the server data preparation in place.
|
||||||
|
- If tabs cause layout regressions, keep the page-local action placement and defer only the tab styling.
|
||||||
|
|||||||
@@ -1,57 +1,94 @@
|
|||||||
# Next.js Full-Stack CRUD, RBAC, and Audit Logs
|
# Next.js Drizzle Full-Stack CRUD, RBAC, Audit Logs, and Admission Ops
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
|
|
||||||
Turn the current static/local-storage Next.js app into a minimal real full-stack application for the first operational slice: account setup/login, built-in RBAC, elder profile CRUD, and auditable administrative actions.
|
Stabilize the current Next.js 15 application around the Drizzle/PostgreSQL persistence layer that has already been introduced, then finish the first operational slice and prepare a controlled Phase 2 path for bed/admission operations, tabs-based workspace navigation, and real chart components.
|
||||||
|
|
||||||
The first release should remove mock/local-only behavior from the core flow and persist data through server-side APIs so the app can be exercised end-to-end without browser localStorage state.
|
The near-term release should keep moving away from static/mock-only surfaces, but it must treat the existing Drizzle schema and migrations as the source of truth. JSON-file persistence is no longer the target architecture for this task.
|
||||||
|
|
||||||
## Confirmed Facts
|
## Confirmed Facts
|
||||||
|
|
||||||
- The repository is already a Next.js 15 App Router project with routes under `app/`.
|
- The repository is already a Next.js 15 App Router project with routes under `app/`.
|
||||||
- Protected app screens are currently guarded by `modules/auth/components/AuthGate.tsx`, which reads localStorage through `modules/auth/lib/local-auth.ts`.
|
- Drizzle and PostgreSQL dependencies are installed: `drizzle-orm`, `drizzle-kit`, and `postgres`.
|
||||||
- Login/register/setup UI exists in `modules/auth/components/AuthPanel.tsx`, but password input is not validated server-side and accounts are browser-local.
|
- Drizzle schema exists at `modules/core/server/schema.ts`, with migrations under `drizzle/`.
|
||||||
- The "老人档案" route at `app/(app)/app/elders/page.tsx` is currently a static `ModulePage`.
|
- The schema already includes accounts, sessions, organizations, memberships, roles, permissions, elders, rooms, beds, admissions, audit logs, system incidents, invitations, and system settings.
|
||||||
- The "权限设置" route at `app/(app)/app/settings/page.tsx` is currently a static `ModulePage`.
|
- `modules/core/server/db.ts` owns the Drizzle/PostgreSQL connection and requires `DATABASE_URL`.
|
||||||
- The project has no installed database/auth dependencies such as Drizzle, PostgreSQL client, oRPC, Zod, or better-auth.
|
- `modules/core/server/store.ts` is now a Drizzle-backed compatibility read model. `readData()` aggregates data from PostgreSQL, while `writeData()` and `updateData()` intentionally throw after the PostgreSQL migration.
|
||||||
- Current `package.json` already supports `pnpm lint`, `pnpm type-check`, and `pnpm build`.
|
- Server-side authentication, password hashing, HTTP-only session cookies, role seeding, permission checks, and audit logging are implemented around Drizzle.
|
||||||
|
- Elder CRUD APIs at `app/api/elders/*` write through Drizzle and can create an active admission when a bed is selected.
|
||||||
|
- Facility and admission routes exist for rooms, beds, and admissions, but the bed/admission UI is still mostly read-only and does not yet provide a complete operator workflow.
|
||||||
|
- The dashboard still contains hard-coded operational counters and hand-built chart-like progress bars.
|
||||||
|
- The app shell has a top-bar "入住" button, but it is not wired to a usable admission workflow.
|
||||||
|
- The screenshot feedback requires moving workspace-level navigation into tabs, removing oversized module intro blocks, and avoiding redundant header actions such as the extra "入住" affordance in the top bar.
|
||||||
|
- Current `package.json` already supports `pnpm lint`, `pnpm type-check`, `pnpm build`, `pnpm db:generate`, `pnpm db:migrate`, and `pnpm db:studio`.
|
||||||
|
|
||||||
## MVP Scope
|
## MVP Scope
|
||||||
|
|
||||||
- Implement a real server-side persistence layer using JSON files under a server-owned data directory for this milestone. This is not mock data: API mutations must write durable data on disk during local/runtime execution.
|
- Use the existing Drizzle/PostgreSQL layer for all new and changed persistent business data.
|
||||||
- Use Next.js Route Handlers for the first API surface instead of adding oRPC/Drizzle/better-auth in this milestone.
|
- Keep Next.js Route Handlers as the current API surface for this milestone. Do not introduce oRPC or better-auth in this task unless a separate migration task is created.
|
||||||
- Replace localStorage authentication with server-side account/session APIs and HTTP-only cookie sessions.
|
- Preserve server-side account/session APIs, HTTP-only cookie sessions, built-in platform and organization roles, membership permissions, and audit logging.
|
||||||
- Provide built-in roles and permission groups without user-defined custom role creation in this milestone.
|
- Complete full CRUD for elder profiles as the first business entity, backed by Drizzle.
|
||||||
- Implement full CRUD for elder profiles as the first business entity.
|
- Complete basic bed and admission operations: list rooms/beds, show current occupancy, create occupancy/admission, support bed transfer, and keep bed/admission/elder status consistent in a transaction.
|
||||||
- Implement a permissions/settings page that shows accounts, roles, permission coverage, and audit logs from server data.
|
- Implement an operator-friendly bed/admission UI under the app workspace instead of exposing "create through API" placeholders.
|
||||||
- Record audit log entries for login, logout, account creation, elder create/update/delete, and permission-sensitive denied actions.
|
- Add workspace tabs where the current UI needs secondary navigation, especially for operational pages that combine overview, records, and management views.
|
||||||
|
- Replace hard-coded counters on implemented areas with Drizzle-backed data.
|
||||||
|
- Use a standard React chart library for selected operational charts instead of hand-rolled visual bars where the visualization is meaningful and data-backed.
|
||||||
|
- Keep settings pages showing accounts, roles, permission coverage, organizations, status, and audit logs from server data.
|
||||||
|
- Record audit log entries for login, logout, account creation, elder create/update/delete, admission create/transfer/discharge, facility mutations, and permission-sensitive denied actions.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
### Authentication
|
### Authentication
|
||||||
|
|
||||||
- Setup must create the first administrator account on the server.
|
- Setup must create the first platform administrator account, initial organization, initial organization membership, and cookie session on the server.
|
||||||
- Login must validate account credentials on the server and set an HTTP-only session cookie.
|
- Login must validate account credentials on the server and set an HTTP-only session cookie.
|
||||||
- Logout must clear the session cookie and record an audit event when possible.
|
- Logout must clear the session cookie and record an audit event when possible.
|
||||||
- App routes must no longer depend on localStorage for authentication.
|
- App routes must not depend on localStorage for authentication.
|
||||||
|
|
||||||
### Roles and Permissions
|
### Roles and Permissions
|
||||||
|
|
||||||
- The system must include built-in roles:
|
- The system must include built-in roles:
|
||||||
- `admin`: full access to accounts, permissions, audit logs, and elder CRUD.
|
- `platform_admin`: full platform access.
|
||||||
- `manager`: elder CRUD and audit log viewing, but no account/role administration.
|
- `platform_operator`: platform organization/account operations without full security ownership.
|
||||||
- `caregiver`: elder read/update access for care-facing fields, no delete or account administration.
|
- `platform_auditor`: platform read/audit access.
|
||||||
- `viewer`: read-only elder access.
|
- `platform_ops`: platform operations and incident management.
|
||||||
|
- `org_admin`: full organization-level access.
|
||||||
|
- `manager`: elder, facility, admission, and audit operations inside an organization.
|
||||||
|
- `caregiver`: elder read/update plus read-only facility/admission access.
|
||||||
|
- `viewer`: read-only elder, facility, and admission access.
|
||||||
- Permission checks must run on the server for all protected APIs.
|
- Permission checks must run on the server for all protected APIs.
|
||||||
- UI navigation or controls may hide unavailable actions, but hidden UI must not be the only enforcement.
|
- UI navigation or controls may hide unavailable actions, but hidden UI must not be the only enforcement.
|
||||||
|
|
||||||
### Elder CRUD
|
### Elder CRUD
|
||||||
|
|
||||||
- Users with permission can list, create, update, and delete elder profiles.
|
- Users with permission can list, create, update, and delete elder profiles.
|
||||||
- Elder records must include at minimum: name, gender, birth date or age, care level, room/bed, status, primary contact, phone, medical notes, created/updated timestamps.
|
- Elder records must include at minimum: name, gender, age, care level, current room/bed when admitted, status, primary contact, phone, medical notes, created/updated timestamps.
|
||||||
- The elder page must show real server data and support create/edit/delete interactions.
|
- The elder page must show real server data and support create/edit/delete interactions.
|
||||||
- Invalid input must return structured API errors and show usable feedback in the UI.
|
- Invalid input must return structured API errors and show usable feedback in the UI.
|
||||||
|
|
||||||
|
### Bed and Admission Operations
|
||||||
|
|
||||||
|
- Operators with `facility:read` can view rooms, beds, occupancy status, current elder assignment, and admission history.
|
||||||
|
- Operators with `facility:manage` can create or update room and bed metadata where the UI exposes those controls.
|
||||||
|
- Operators with `admission:manage` can admit an elder to an available bed, transfer an active elder to another available bed, and discharge an elder from a bed.
|
||||||
|
- Admission mutations must be transactional: active admission records, bed statuses, and elder status must stay consistent.
|
||||||
|
- Bed assignment conflicts must be rejected with structured API errors.
|
||||||
|
- The UI must make common workflows discoverable without relying on raw API calls.
|
||||||
|
- The top app header should not contain redundant or dead "入住" controls. Admission actions should live inside the relevant bed/admission workspace.
|
||||||
|
|
||||||
|
### Workspace UI and Tabs
|
||||||
|
|
||||||
|
- The app shell should keep dense operational navigation and avoid large hero-style module introductions.
|
||||||
|
- Pages that combine multiple operator modes should use tabs or equivalent segmented navigation, not stacked explanatory sections.
|
||||||
|
- The screenshot feedback for notices/general workspace applies broadly: remove oversized first-screen intro blocks when they do not help an operator act, place secondary navigation tabs near the workspace header, and keep action controls local to the page.
|
||||||
|
- UI text should remain operational and data-oriented, not marketing copy.
|
||||||
|
|
||||||
|
### Charts
|
||||||
|
|
||||||
|
- Use a standard chart library for selected data-backed charts such as bed occupancy composition, admission activity, or status distribution.
|
||||||
|
- Avoid hand-coded fake chart bars for business metrics that should reflect persisted data.
|
||||||
|
- Keep charts lightweight, readable, and useful for operations; tables remain the primary surface for detailed records.
|
||||||
|
|
||||||
### Audit Logs
|
### Audit Logs
|
||||||
|
|
||||||
- Audit logs must be persisted server-side and visible in the settings page.
|
- Audit logs must be persisted server-side and visible in the settings page.
|
||||||
@@ -62,43 +99,57 @@ The first release should remove mock/local-only behavior from the core flow and
|
|||||||
|
|
||||||
- API responses must use a consistent `{ success, reason, ... }` shape.
|
- API responses must use a consistent `{ success, reason, ... }` shape.
|
||||||
- Server-side data helpers must avoid browser APIs.
|
- Server-side data helpers must avoid browser APIs.
|
||||||
- All file persistence operations must be centralized so future Drizzle/Postgres migration has a single boundary to replace.
|
- New persistent mutations must use Drizzle queries or transactions through the server database boundary, not JSON file writes.
|
||||||
- Do not use hard-coded UI counters for the implemented CRUD/settings/audit areas once server data exists.
|
- `readData()` may remain temporarily as a compatibility read model, but new mutation code must not depend on `writeData()` or `updateData()`.
|
||||||
|
- Prefer domain-specific Drizzle query helpers over growing the compatibility read model for every new use case.
|
||||||
|
- Do not use hard-coded UI counters for implemented CRUD, settings, audit, bed, admission, or dashboard areas once server data exists.
|
||||||
|
- Routes or server components that depend on cookies/session or live database state must opt out of static caching where required.
|
||||||
|
|
||||||
### Quality
|
### Quality
|
||||||
|
|
||||||
- Keep TypeScript strict without `any`, non-null assertions, or `@ts-ignore`/`@ts-expect-error`.
|
- Keep TypeScript strict without `any`, non-null assertions, or `@ts-ignore`/`@ts-expect-error`.
|
||||||
- Prefer Server Components for initial data loading and small Client Components only for forms/mutations.
|
- Prefer Server Components for initial data loading and small Client Components only for forms/mutations.
|
||||||
- `pnpm lint`, `pnpm type-check`, and `pnpm build` should pass before marking implementation complete.
|
- `pnpm lint`, `pnpm type-check`, `pnpm build`, and relevant Drizzle migration validation should pass before marking implementation complete.
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
- [ ] First-run setup creates a server-persisted admin account and redirects into the app.
|
- [ ] First-run setup creates a Drizzle-persisted platform admin account, initial organization, organization roles, membership, and cookie session, then redirects into the app.
|
||||||
- [ ] Login/logout uses server APIs and an HTTP-only cookie session, not localStorage.
|
- [ ] Login/logout uses server APIs and an HTTP-only cookie session, not localStorage.
|
||||||
- [ ] Visiting `/app/elders` while authenticated loads elder records from the server persistence layer.
|
- [ ] Visiting `/app/elders` while authenticated loads elder records from PostgreSQL through the server layer.
|
||||||
- [ ] An authorized user can create, edit, and delete elder records from the UI, and changes survive page reloads.
|
- [ ] An authorized user can create, edit, and delete elder records from the UI, and changes survive page reloads.
|
||||||
- [ ] Unauthorized role attempts against protected elder/account/audit APIs return a forbidden response and create audit log entries.
|
- [ ] Unauthorized role attempts against protected elder/account/audit APIs return a forbidden response and create audit log entries.
|
||||||
- [ ] `/app/settings` displays built-in roles, account list, and recent audit log entries from server data.
|
- [ ] Settings pages display built-in roles, account list, organizations, system status, and recent audit log entries from server data.
|
||||||
- [ ] Audit logs are written for account creation, login, logout, elder create/update/delete, and denied permission checks.
|
- [ ] `/app/beds` or the equivalent admission workspace shows Drizzle-backed room, bed, occupancy, and admission data without raw API placeholders.
|
||||||
|
- [ ] An authorized user can admit an elder to an available bed from the UI.
|
||||||
|
- [ ] An authorized user can transfer or discharge an active admission from the UI, with bed status and elder status updated transactionally.
|
||||||
|
- [ ] Admission conflict attempts return structured errors and do not corrupt bed occupancy state.
|
||||||
|
- [ ] The screenshot feedback is reflected in the workspace layout: tabs are introduced where needed, oversized intro blocks are removed, and redundant top-bar admission action is removed or replaced with a useful page-local action.
|
||||||
|
- [ ] At least one implemented operational chart uses a standard chart library and real server data.
|
||||||
|
- [ ] Audit logs are written for account creation, login, logout, elder create/update/delete, admission create/transfer/discharge, facility mutations implemented in this task, and denied permission checks.
|
||||||
- [ ] Existing static module pages outside the MVP continue to render.
|
- [ ] Existing static module pages outside the MVP continue to render.
|
||||||
|
- [ ] Existing Drizzle migrations remain coherent with `modules/core/server/schema.ts`.
|
||||||
- [ ] `pnpm lint` passes.
|
- [ ] `pnpm lint` passes.
|
||||||
- [ ] `pnpm type-check` passes.
|
- [ ] `pnpm type-check` passes.
|
||||||
- [ ] `pnpm build` passes.
|
- [ ] `pnpm build` passes.
|
||||||
|
|
||||||
## Out of Scope
|
## Out of Scope
|
||||||
|
|
||||||
- PostgreSQL/Drizzle migration.
|
- Reverting to JSON-file persistence.
|
||||||
- oRPC adoption.
|
- Full historical data migration from any old `.data/teatea.json` files unless a separate migration task is created.
|
||||||
- better-auth adoption.
|
- Replacing the current Route Handlers with oRPC.
|
||||||
- Password reset, email verification, OAuth, multi-factor authentication, or account invitations.
|
- Replacing the current custom session implementation with better-auth.
|
||||||
- Custom role builder UI.
|
- Password reset, email verification, OAuth, multi-factor authentication, or new invitation flows beyond what already exists.
|
||||||
|
- Full custom role builder beyond the existing role/permission management surface.
|
||||||
- CRUD implementation for every module in the sidebar.
|
- CRUD implementation for every module in the sidebar.
|
||||||
|
- Advanced care plan, billing, family app, device telemetry, and emergency workflow automation.
|
||||||
- Production-grade password hashing beyond Node built-in cryptographic hashing suitable for this local MVP.
|
- Production-grade password hashing beyond Node built-in cryptographic hashing suitable for this local MVP.
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
- None blocking. The MVP assumes "basic CRUD" means the first core business entity, elder profiles, plus real account/session/audit support.
|
- Which chart library should be standardized for the project? Recharts is a practical default for React dashboards, but the decision should be captured before adding the dependency.
|
||||||
|
- Should room/bed creation be part of this immediate slice, or should the UI focus first on admission, transfer, and discharge against existing room/bed records?
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- Next.js documentation confirms App Router Route Handlers support `GET`, `POST`, `PATCH`, and `DELETE`, `Response.json`, `request.json()`, and async `cookies()` from `next/headers` for cookie reads/writes in current versions.
|
- Next.js documentation confirms App Router Route Handlers support `GET`, `POST`, `PATCH`, and `DELETE`, `Response.json`, `request.json()`, and async `cookies()` from `next/headers` for cookie reads/writes in current versions.
|
||||||
|
- The current codebase already demonstrates the intended Drizzle direction. Future task text should not describe JSON files as the target persistence layer unless explicitly working on a compatibility migration.
|
||||||
|
|||||||
Reference in New Issue
Block a user