Renewal settings, client setup queue, manager setup page, combobox grouping, nav updates
This commit is contained in:
parent
d109a5f73a
commit
4315f704c7
29 changed files with 2588 additions and 65 deletions
168
tasks/prd-renewal-groups.md
Normal file
168
tasks/prd-renewal-groups.md
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
# PRD: Renewal Groups — Client/Policy Onboarding Workflow
|
||||
|
||||
**Status:** Ready for Development
|
||||
**Author:** Cascade (from user requirements)
|
||||
**Date:** 2026-04-09
|
||||
|
||||
---
|
||||
|
||||
## 1. Introduction / Overview
|
||||
|
||||
Renewal Groups logically cluster insurance policies that share a common renewal window. They drive the scheduling of SHAPE tasks and determine the `renewalDate` displayed on client cards throughout the app.
|
||||
|
||||
Today, Renewal Groups can be created and managed manually. What is **missing** is a structured onboarding flow that activates when a new client or policy enters the system (via AMS360 sync). This feature adds:
|
||||
|
||||
- A **New Client / Policy Setup page** where a Manager or Admin reviews incoming clients/policies that have not yet been configured.
|
||||
- A **system recommendation engine** that proposes Renewal Group groupings and a default `renewalDate` based on configurable rules.
|
||||
- A **UI** allowing the Manager/Admin to accept, modify, or override those recommendations before finalising the client's configuration.
|
||||
|
||||
---
|
||||
|
||||
## 2. Goals
|
||||
|
||||
1. Ensure every new client that enters via AMS360 sync gets a `renewalDate` and a `claimsAdvocate` before tasks are generated.
|
||||
2. Reduce manual configuration effort by surfacing system-generated grouping recommendations.
|
||||
3. Give Managers/Admins full control to customise the grouping window, default renewal date logic, and per-client overrides.
|
||||
4. Keep existing Renewal Groups untouched — this flow only applies to unconfigured clients/policies.
|
||||
|
||||
---
|
||||
|
||||
## 3. User Stories
|
||||
|
||||
- **As a Manager**, I want to see a list of clients that have synced from AMS360 but have not been fully configured, so I can quickly identify what needs attention.
|
||||
- **As a Manager**, I want the system to recommend how to group a new client's policies into Renewal Groups based on their expiration dates, so I don't have to figure it out manually.
|
||||
- **As a Manager**, I want to customise the grouping window (default 90 days) and the renewal date selection rule (default: earliest date closest to the start of the year within the group), so the defaults match our business practice but can be adjusted.
|
||||
- **As a Manager**, I want to assign a `claimsAdvocate` and optionally add notes for any new client during setup, so the client is fully configured in one step.
|
||||
- **As a Manager**, I want to accept, modify, or reject system recommendations for each group before saving, so I remain in control of the final configuration.
|
||||
- **As an Admin**, I want to be able to reopen the setup wizard for any client at any time (e.g., after a new policy is added mid-year), so I can re-group without losing existing data.
|
||||
|
||||
---
|
||||
|
||||
## 4. Functional Requirements
|
||||
|
||||
### 4.1 New Client Setup Queue
|
||||
|
||||
1. The system **must** display a dedicated "New Client Setup" page (or prominent section in the Manager view) listing all clients that:
|
||||
- Were added via AMS360 sync, **and**
|
||||
- Are missing either a `renewalDate` **or** a `claimsAdvocate`.
|
||||
2. Each entry in the queue **must** show: client name, number of policies, earliest and latest policy expiration dates, and how long they have been in the queue (days since sync).
|
||||
3. The queue **must** be accessible only to users with the **Manager** or **Admin** role.
|
||||
4. Clicking a client in the queue **must** open the Setup Wizard for that client.
|
||||
|
||||
### 4.2 Setup Wizard — Policy Overview
|
||||
|
||||
5. The wizard **must** display all policies belonging to the new client with: policy number, type, carrier, and expiration date.
|
||||
6. Policies with no expiration date **must** be flagged with a visual warning and excluded from auto-grouping recommendations (but still assignable manually).
|
||||
|
||||
### 4.3 Recommendation Engine
|
||||
|
||||
7. The system **must** group policies into recommended Renewal Groups using the following default logic:
|
||||
- Policies whose expiration dates fall within a **90-day window** of each other are placed in the same group.
|
||||
- The window size (90 days) **must** be configurable per-run by the Manager in the wizard UI.
|
||||
8. Within each recommended group, the system **must** calculate a default `renewalDate` as follows:
|
||||
- Select the expiration date that is **nearest to the start of the calendar year** (i.e., earliest in January–March if available, otherwise the earliest overall) and add **1 day** (`expirationDate + 1`).
|
||||
- This rule **must** be configurable: the Manager can switch to "earliest in group" or "latest in group" or manually enter a date.
|
||||
9. The recommendation engine **must** also handle **standalone policies** (policies not grouped with any other policy):
|
||||
- Their `renewalDate` is `expirationDate + 1`.
|
||||
- They are displayed as a single-policy "group" in the wizard.
|
||||
|
||||
### 4.4 Setup Wizard — Review & Customise
|
||||
|
||||
10. The wizard **must** display system-recommended groups as drag-and-drop cards (using `@dnd-kit`), each showing:
|
||||
- Proposed group name (editable inline).
|
||||
- Member policies with their expiration dates.
|
||||
- Proposed `renewalDate` (editable via date picker).
|
||||
- Renewal date derivation rule selector (nearest-to-year-start / earliest / latest / manual).
|
||||
11. The Manager **must** be able to:
|
||||
- Move a policy from one recommended group to another via drag-and-drop or a move selector.
|
||||
- Split a group by removing a policy and creating a new group from it.
|
||||
- Merge two groups by dragging one group's policies into another.
|
||||
- Delete a group (policies revert to ungrouped / standalone).
|
||||
- Create a blank group manually and add policies to it.
|
||||
12. The wizard **must** show a live preview of the resulting `renewalDate` for each group as policies are moved.
|
||||
13. The **90-day grouping window** input **must** be visible at the top of the wizard and re-running it **must** re-compute recommendations (with a confirmation if the Manager has already made manual changes).
|
||||
|
||||
### 4.5 Claims Advocate Assignment
|
||||
|
||||
14. The wizard **must** include a **Claims Advocate** selector (Claims-department users only) that applies to the whole client.
|
||||
15. The advocate selector **must** be required before the wizard can be finalised.
|
||||
|
||||
### 4.6 Notes
|
||||
|
||||
16. The wizard **must** include a free-text notes field for client-level notes and an optional per-group notes field.
|
||||
|
||||
### 4.7 Finalisation
|
||||
|
||||
17. On "Save & Complete", the system **must**:
|
||||
- Persist all Renewal Groups (new or modified) to the database.
|
||||
- Set `client.renewalDate` to the `renewalDate` of the **default renewal group** — the Manager must designate exactly one group as default (radio/star selector on each group card); there is no auto-selection.
|
||||
- If there is only one group, it is automatically treated as the default without requiring an explicit selection.
|
||||
- Set `client.claimsAdvocate`.
|
||||
- Remove the client from the setup queue.
|
||||
18. The system **must** allow a Manager to **Save as Draft** — keeping the client in the queue but preserving partial work.
|
||||
19. After finalisation, the wizard **must** be re-openable from the client detail page (e.g., an "Edit Setup" action) for re-grouping when new policies are added.
|
||||
|
||||
### 4.8 Renewal Date Display Rules (existing behaviour, confirmed)
|
||||
|
||||
20. A policy that belongs to a Renewal Group **must** display `renewalDate` from its group.
|
||||
21. A standalone policy (no group) **must** display `expirationDate + 1` as its renewal date throughout the app.
|
||||
22. A client with no policies and no groups **must** show no renewal date until one is manually assigned.
|
||||
|
||||
### 4.9 Global Settings (Admin Panel)
|
||||
|
||||
23. Admins **must** be able to set **global defaults** in the Admin Panel for:
|
||||
- Grouping window (default: 90 days).
|
||||
- Renewal date rule (default: nearest-to-year-start).
|
||||
These defaults pre-populate the wizard but can be overridden per client per run.
|
||||
24. Global settings **must not** retroactively change already-finalised Renewal Groups.
|
||||
|
||||
---
|
||||
|
||||
## 5. Non-Goals (Out of Scope)
|
||||
|
||||
- Automated assignment of a `claimsAdvocate` by the system — this always requires a human decision.
|
||||
- Email or external notifications when new clients enter the queue (in-app only for now).
|
||||
- Bulk "apply to all" finalisation without per-client review.
|
||||
- Any changes to how existing, already-configured Renewal Groups behave in the rest of the app.
|
||||
- Exposing this workflow to Claims Advocates or non-privileged users.
|
||||
|
||||
---
|
||||
|
||||
## 6. Design Considerations
|
||||
|
||||
- The Setup Queue should appear as a **badge/alert on the Manager nav item** showing count of unconfigured clients.
|
||||
- The wizard should be a **full-page or large modal** flow — not a drawer — given the amount of information.
|
||||
- Recommended groups should use a **card-based kanban-style layout** so policies can be visually moved between groups.
|
||||
- Use existing UI conventions: shadcn/ui cards, Tailwind, lucide-react icons, existing Badge/Select/DatePicker components.
|
||||
- The grouping-window control should be a **number input with a "Re-run Recommendations" button** adjacent to it.
|
||||
- Drag-and-drop: use `@dnd-kit/core` (already a common pattern in the stack) or a lightweight alternative.
|
||||
|
||||
---
|
||||
|
||||
## 7. Technical Considerations
|
||||
|
||||
- **Database:** `RenewalGroup` model already exists with `renewalDate`, `clientId`, `policyGroupId` relations. A `setupCompletedAt` timestamp on `Client` (nullable) is the simplest way to track queue membership — `NULL` means unconfigured.
|
||||
- **Queue query:** Clients where `claimsAdvocateId IS NULL OR renewalDate IS NULL` and `createdAt` is post-sync.
|
||||
- **Recommendation engine:** Pure TypeScript function — accepts a list of `{ policyId, expirationDate }` and config `{ windowDays, rule }`, returns `{ groups: { policies[], proposedRenewalDate }[] }`. Can live in `src/lib/renewal-group-recommendations.ts`.
|
||||
- **Global settings:** Store in the existing `sync_config` table or a new `app_settings` key-value table. Surfaced in the Admin Panel under a new "Renewal Groups" settings section.
|
||||
- **Re-run safety:** Track `wizardDirty` client-side; prompt confirmation before overwriting manual changes with a fresh recommendation run.
|
||||
- **Drag-and-drop:** `@dnd-kit/core` + `@dnd-kit/sortable` for policy cards within and between groups.
|
||||
|
||||
---
|
||||
|
||||
## 8. Success Metrics
|
||||
|
||||
- Zero new clients remain in the setup queue for more than 5 business days after sync.
|
||||
- Manager setup time per client < 5 minutes for a typical 3–5 policy client.
|
||||
- Recommendation acceptance rate (no manual changes) tracked in `shape_import_logs` or a new event log — target ≥ 60%.
|
||||
|
||||
---
|
||||
|
||||
## 9. Open Questions
|
||||
|
||||
~~All questions resolved.~~
|
||||
|
||||
1. **Default group**: The Manager explicitly designates exactly one group as the default. No auto-selection.
|
||||
2. **New policy on existing client**: The recommendation engine runs again and suggests a group or standalone; the Manager reviews and confirms via the wizard re-open flow.
|
||||
3. **Global settings**: Managed in the Admin Panel.
|
||||
4. **Drag-and-drop**: `@dnd-kit/core` + `@dnd-kit/sortable` — installed as a project dependency.
|
||||
92
tasks/tasks-prd-renewal-groups.md
Normal file
92
tasks/tasks-prd-renewal-groups.md
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
## Relevant Files
|
||||
|
||||
### New Files
|
||||
- `ondeck/src/lib/renewal-group-recommendations.ts` - Pure TS recommendation engine: groups policies by expiration window, calculates renewal dates per rule.
|
||||
- `ondeck/src/lib/renewal-group-recommendations.test.ts` - Unit tests for the recommendation engine.
|
||||
- `ondeck/src/app/(dashboard)/manager/setup/page.tsx` - New Client Setup Queue page (server component).
|
||||
- `ondeck/src/app/(dashboard)/manager/setup/[clientId]/page.tsx` - Setup Wizard page for a specific client (server component, fetches client + policies).
|
||||
- `ondeck/src/components/renewal-groups/setup-wizard.tsx` - Main wizard client component with state, drag-and-drop orchestration, advocate selector, and save logic.
|
||||
- `ondeck/src/components/renewal-groups/group-card.tsx` - Droppable group card showing policies, inline name editor, renewal date picker, rule selector, and default-group radio.
|
||||
- `ondeck/src/components/renewal-groups/policy-chip.tsx` - Draggable policy pill used inside group cards and the unassigned policies tray.
|
||||
- `ondeck/src/app/api/clients/setup-queue/route.ts` - GET: returns unconfigured clients (missing `renewalDate` or `claimsAdvocate`).
|
||||
- `ondeck/src/app/api/clients/[id]/setup/route.ts` - POST: persists wizard output (groups, renewalDate, claimsAdvocate, notes, setupCompletedAt). PUT: saves draft.
|
||||
- `ondeck/src/app/api/admin/renewal-settings/route.ts` - GET/PUT: reads and writes global renewal group defaults from `app_settings`.
|
||||
- `ondeck/src/app/(dashboard)/admin/renewal-settings/page.tsx` - Admin Panel page for global defaults (grouping window, renewal date rule).
|
||||
|
||||
### Modified Files
|
||||
- `ondeck/prisma/schema.prisma` - Add `setupCompletedAt DateTime?` to `Client`; add new `AppSetting` model for key-value settings store.
|
||||
- `ondeck/src/app/(dashboard)/layout.tsx` - Add badge on Manager nav item showing count of unconfigured clients.
|
||||
- `ondeck/src/app/(dashboard)/manager/page.tsx` - Add "New Client Setup" summary card linking to the queue page.
|
||||
- `ondeck/src/app/(dashboard)/clients/[id]/page.tsx` - Pass `setupCompletedAt` and policies to `ClientDetail`; add "Edit Setup" button for Manager/Admin.
|
||||
- `ondeck/src/components/clients/client-detail.tsx` - Render "Edit Setup" button that navigates to the wizard for the current client.
|
||||
- `ondeck/src/app/(dashboard)/admin/page.tsx` - Add "Renewal Groups" section linking to the new settings page.
|
||||
|
||||
### Notes
|
||||
|
||||
- Unit tests should be placed alongside source files (e.g., `renewal-group-recommendations.ts` and `renewal-group-recommendations.test.ts` in the same directory).
|
||||
- Run tests with `npx jest src/lib/renewal-group-recommendations`.
|
||||
- After any `schema.prisma` change, run `npx prisma migrate dev --name <migration-name>` and `npx prisma generate`.
|
||||
- `@dnd-kit/core`, `@dnd-kit/sortable`, and `@dnd-kit/utilities` are already installed.
|
||||
|
||||
---
|
||||
|
||||
## Tasks
|
||||
|
||||
- [x] 1.0 Database & Schema
|
||||
- [x] 1.1 Add `setupCompletedAt DateTime? @map("setup_completed_at")` field to the `Client` model in `prisma/schema.prisma`.
|
||||
- [x] 1.2 Add a new `AppSetting` model to `prisma/schema.prisma` with fields `key String @id`, `value String @db.Text`, `updatedAt DateTime @updatedAt`. Map to table `app_settings`.
|
||||
- [x] 1.3 Run `npx prisma migrate dev --name add_setup_completed_at_and_app_settings` to create and apply the migration. (Used `prisma db push` due to pre-existing schema drift.)
|
||||
- [x] 1.4 Run `npx prisma generate` to update the Prisma client.
|
||||
- [x] 1.5 Seed two default rows into `app_settings`: `renewal_group_window_days = "90"` and `renewal_group_date_rule = "nearest-to-year-start"`.
|
||||
|
||||
- [x] 2.0 Recommendation Engine
|
||||
- [x] 2.1 Create `src/lib/renewal-group-recommendations.ts`. Define and export the input types: `PolicyInput { policyId: string; expirationDate: Date }` and `RecommendationConfig { windowDays: number; rule: 'nearest-to-year-start' | 'earliest' | 'latest' }`.
|
||||
- [x] 2.2 Implement the grouping algorithm: sort policies by `expirationDate`, then use a sliding-window approach to cluster policies whose dates fall within `windowDays` of each other into the same group.
|
||||
- [x] 2.3 Implement the `renewalDate` calculation for each group based on `config.rule`: `nearest-to-year-start` picks the expiration date with the lowest day-of-year value; `earliest` picks the minimum; `latest` picks the maximum. All results are `expirationDate + 1 day`.
|
||||
- [x] 2.4 Handle standalone policies (groups of size 1) — they are valid output and their `renewalDate` is simply `expirationDate + 1`.
|
||||
- [x] 2.5 Policies with a `null` expiration date must be excluded from grouping and returned separately as `ungroupable: PolicyInput[]`.
|
||||
- [x] 2.6 Write unit tests in `renewal-group-recommendations.test.ts` covering: basic 90-day grouping, cross-year-boundary grouping, all three date rules, standalone policy, null-date exclusion, and empty input. (12/12 pass)
|
||||
|
||||
- [x] 3.0 New Client Setup Queue
|
||||
- [x] 3.1 Create `src/app/api/clients/setup-queue/route.ts` — GET handler that queries clients where `claimsAdvocateId IS NULL OR setupCompletedAt IS NULL`, including policy count, earliest/latest expiration dates, and `createdAt`. Returns sorted by `createdAt ASC`.
|
||||
- [x] 3.2 Create `src/app/(dashboard)/manager/setup/page.tsx` — server component that fetches the queue via Prisma and renders a table/list of unconfigured clients. Restrict to Manager/Admin roles; redirect others.
|
||||
- [x] 3.3 Each queue row must show: client name (linked to wizard), policy count, earliest expiry, latest expiry, days in queue (today − `createdAt`).
|
||||
- [x] 3.4 Add a "Days in queue" warning: highlight rows where `daysInQueue > 5` in amber, `> 10` in red.
|
||||
- [x] 3.5 Update `src/components/layout/nav-bar.tsx` to fetch the unconfigured client count client-side and show a numeric badge on the Manager nav link when count > 0.
|
||||
- [x] 3.6 Update `src/app/(dashboard)/manager/page.tsx` and `manager-page-client.tsx` to add a "New Client Setup" summary card showing the queue count and a link to `/manager/setup`.
|
||||
|
||||
- [x] 4.0 Setup Wizard UI
|
||||
- [x] 4.1 Create `src/app/(dashboard)/manager/setup/[clientId]/page.tsx` — server component that fetches the client, all its policies, existing policy groups, Claims-dept users, and global settings. Passes all data to `<SetupWizard />`.
|
||||
- [x] 4.2 Create `src/components/renewal-groups/setup-wizard.tsx` — client component with full state management.
|
||||
- [x] 4.3 On mount, if no existing groups, auto-run the recommendation engine. If existing groups already exist, load them directly.
|
||||
- [x] 4.4 Render a top toolbar in the wizard: `windowDays` number input, `rule` select dropdown, and a "Re-run Recommendations" button with dirty-state confirmation dialog.
|
||||
- [x] 4.5 Create `src/components/renewal-groups/policy-chip.tsx` — `useDraggable` component with no-expiry warning.
|
||||
- [x] 4.6 Create `src/components/renewal-groups/group-card.tsx` — `useDroppable` card with inline name editor, rule selector, live renewal date, manual override, notes, and default-group star.
|
||||
- [x] 4.7 Render an "Unassigned Policies" tray at the bottom of the wizard.
|
||||
- [x] 4.8 Implement policy drag-and-drop via `@dnd-kit/core`.
|
||||
- [x] 4.9 Add group management: "Add Group" button, trash icon to delete, "Split" context action on each chip.
|
||||
- [x] 4.10 Add Claims Advocate selector (filtered to Claims dept). "Save & Complete" disabled if no advocate.
|
||||
- [x] 4.11 Single-default-group rule with star radio buttons; hidden when only one group.
|
||||
- [x] 4.12 "Save & Complete" — validates, calls POST, navigates to client detail on success.
|
||||
- [x] 4.13 "Save as Draft" — calls PUT, shows toast, keeps `setupCompletedAt` null.
|
||||
- [x] 4.14 Breadcrumb/back link to the queue page at the top of the wizard.
|
||||
|
||||
- [x] 5.0 API Routes
|
||||
- [x] 5.1 Create `src/app/api/clients/[id]/setup/route.ts` — POST handler with Prisma transaction: upsert groups, update policy assignments, set client fields including `setupCompletedAt`.
|
||||
- [x] 5.2 PUT handler on same route for draft saves (omits `setupCompletedAt`).
|
||||
- [x] 5.3 Both handlers protected: return `403` if not Manager or Admin.
|
||||
- [x] 5.4 Create `src/app/api/admin/renewal-settings/route.ts` — GET/PUT with Admin-only write guard.
|
||||
- [x] 5.5 `setup-queue/route.ts` accepts `?count=true` query param returning `{ count: number }`.
|
||||
|
||||
- [x] 6.0 Admin Panel — Global Settings
|
||||
- [x] 6.1 Create `src/app/(dashboard)/admin/renewal-settings/page.tsx` — server component reading settings and rendering `<RenewalSettingsForm />`.
|
||||
- [x] 6.2 Settings form: number input for window days (1–365) and select for date rule.
|
||||
- [x] 6.3 On save, calls `PUT /api/admin/renewal-settings`. Shows success/error toast.
|
||||
- [x] 6.4 Updated `src/app/(dashboard)/admin/page.tsx` with a "Renewal Groups" card linking to `/admin/renewal-settings`.
|
||||
|
||||
- [x] 7.0 Re-open Wizard from Client Detail
|
||||
- [x] 7.1 Updated `src/app/(dashboard)/clients/[id]/page.tsx` to pass `setupCompletedAt` and `canManageSetup` props.
|
||||
- [x] 7.2 Updated `src/components/clients/client-detail.tsx` to render "Edit Setup" / "Complete Setup" button for Manager/Admin.
|
||||
- [x] 7.3 Wizard loads existing `PolicyGroup` data when re-opened and shows a re-configure banner.
|
||||
- [x] 7.4 API POST handler deletes removed groups (DB groups not in payload) inside the same transaction.
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue