Files

12 KiB

Next.js Drizzle Full-Stack CRUD, RBAC, Audit Logs, and Admission Ops

Goal

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

  • The repository is already a Next.js 15 App Router project with routes under app/.
  • Drizzle and PostgreSQL dependencies are installed: drizzle-orm, drizzle-kit, and postgres.
  • Drizzle schema exists at modules/core/server/schema.ts, with migrations under drizzle/.
  • The schema already includes accounts, sessions, organizations, memberships, roles, permissions, elders, rooms, beds, admissions, audit logs, system incidents, invitations, and system settings.
  • modules/core/server/db.ts owns the Drizzle/PostgreSQL connection and requires DATABASE_URL.
  • 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.
  • 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

  • Use the existing Drizzle/PostgreSQL layer for all new and changed persistent business data.
  • 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.
  • Preserve server-side account/session APIs, HTTP-only cookie sessions, built-in platform and organization roles, membership permissions, and audit logging.
  • Complete full CRUD for elder profiles as the first business entity, backed by Drizzle.
  • 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 an operator-friendly bed/admission UI under the app workspace instead of exposing "create through API" placeholders.
  • 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

Authentication

  • 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.
  • Logout must clear the session cookie and record an audit event when possible.
  • App routes must not depend on localStorage for authentication.

Roles and Permissions

  • The system must include built-in roles:
    • platform_admin: full platform access.
    • platform_operator: platform organization/account operations without full security ownership.
    • platform_auditor: platform read/audit 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.
  • UI navigation or controls may hide unavailable actions, but hidden UI must not be the only enforcement.

Elder CRUD

  • Users with permission can list, create, update, and delete elder profiles.
  • 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.
  • 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 must be persisted server-side and visible in the settings page.
  • Each audit record must include timestamp, actor account ID/email when available, action, target type, target ID when available, result, and human-readable reason.
  • Failed permission checks must be logged without exposing sensitive internals to the client.

Data and API

  • API responses must use a consistent { success, reason, ... } shape.
  • Server-side data helpers must avoid browser APIs.
  • New persistent mutations must use Drizzle queries or transactions through the server database boundary, not JSON file writes.
  • 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

  • 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.
  • pnpm lint, pnpm type-check, pnpm build, and relevant Drizzle migration validation should pass before marking implementation complete.

Acceptance Criteria

  • 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.
  • 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.
  • Unauthorized role attempts against protected elder/account/audit APIs return a forbidden response and create audit log entries.
  • Settings pages display built-in roles, account list, organizations, system status, and recent audit log entries from server data.
  • /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 Drizzle migrations remain coherent with modules/core/server/schema.ts.
  • pnpm lint passes.
  • pnpm type-check passes.
  • pnpm build passes.

Out of Scope

  • Reverting to JSON-file persistence.
  • Full historical data migration from any old .data/teatea.json files unless a separate migration task is created.
  • Replacing the current Route Handlers with oRPC.
  • Replacing the current custom session implementation with better-auth.
  • 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.
  • 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.

Open Questions

  • 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

  • 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.