117 lines
3.2 KiB
Markdown
117 lines
3.2 KiB
Markdown
# Task F4: Notification Framework Foundation
|
|
|
|
## Scope Delivered
|
|
|
|
- Added notification foundation schema for events, inbox items, templates, and deliveries.
|
|
- Added reusable notification services for event publishing, recipient resolution, template rendering, and inbox mutations.
|
|
- Integrated approval lifecycle notifications for submit, approve-complete, reject, return, and cancel flows.
|
|
- Replaced the old mock/Zustand notification UI with API-backed React Query notifications in the header bell and notifications page.
|
|
|
|
## Schema
|
|
|
|
New tables:
|
|
|
|
- `app_notification_events`
|
|
- `app_notifications`
|
|
- `app_notification_templates`
|
|
- `app_notification_deliveries`
|
|
|
|
Key behavior:
|
|
|
|
- organization-scoped rows throughout
|
|
- `dedupe_key` support on events
|
|
- inbox rows track `read_at`, `archived_at`, and `status`
|
|
- delivery rows prepare future multi-channel expansion
|
|
|
|
## Event Flow
|
|
|
|
Current flow:
|
|
|
|
1. Approval service completes its main action.
|
|
2. Approval service calls `publishNotificationEvent()` with a safe wrapper.
|
|
3. Notification foundation persists `app_notification_events`.
|
|
4. Notification foundation resolves recipients and renders the matching template.
|
|
5. In-app notifications are created in `app_notifications`.
|
|
6. Header bell and `/dashboard/notifications` read from the inbox APIs.
|
|
|
|
Notification failures are intentionally non-blocking for approval actions. Failures are stored on the notification event row and audited.
|
|
|
|
## Recipient Resolution
|
|
|
|
Implemented resolvers:
|
|
|
|
- `explicit_user`
|
|
- `approval_current_step_approvers`
|
|
- `approval_requester`
|
|
|
|
Current approval approver resolution uses:
|
|
|
|
- active CRM role assignments mapped to the current step role code
|
|
- membership `business_role` as a fallback path
|
|
|
|
Recipients are deduplicated and actor exclusion is supported per event rule.
|
|
|
|
## Template Syntax
|
|
|
|
Supported placeholders use simple token replacement:
|
|
|
|
- `{{quotationCode}}`
|
|
- `{{workflowName}}`
|
|
- `{{actorName}}`
|
|
- `{{currentStepRoleName}}`
|
|
- `{{entityLink}}`
|
|
- `{{remark}}`
|
|
|
|
Rules:
|
|
|
|
- missing values render as `-`
|
|
- plain text only
|
|
- no code execution
|
|
|
|
## Approval Integration
|
|
|
|
Integrated events:
|
|
|
|
- `approval.requested`
|
|
- `approval.step.approved` for step-to-step progression
|
|
- `approval.completed` for final approval completion
|
|
- `approval.step.rejected`
|
|
- `approval.returned`
|
|
- `approval.cancelled`
|
|
|
|
Notes:
|
|
|
|
- final approval emits `approval.completed` instead of duplicating both requester notifications
|
|
- notification delivery is intentionally best-effort and does not roll back approvals
|
|
|
|
## Inbox APIs
|
|
|
|
Added routes:
|
|
|
|
- `GET /api/notifications`
|
|
- `GET /api/notifications/unread-count`
|
|
- `POST /api/notifications/[id]/read`
|
|
- `POST /api/notifications/read-all`
|
|
- `POST /api/notifications/[id]/archive`
|
|
|
|
Access model:
|
|
|
|
- `notifications.read`
|
|
- `notifications.update`
|
|
- `notifications.admin`
|
|
|
|
Normal users can operate only on their own notifications because server-side filtering always scopes by `recipient_user_id`.
|
|
|
|
## Default Templates
|
|
|
|
Foundation seed now upserts default `in_app` templates for all approval events. Seed remains idempotent and organization-scoped.
|
|
|
|
## Current Limitations
|
|
|
|
- no SMTP sending yet
|
|
- no LINE delivery yet
|
|
- no webhook delivery yet
|
|
- no reminder/escalation scheduler yet
|
|
- no admin template editor UI yet
|
|
- no real-time push/SSE yet
|