docs: design for per-client task generation + preview-before-generate
Adds a clientId targeting mode and a dryRun flag to the admin generate-and-assign tool, plus a confirmation dialog showing summary counts before any tasks are actually created. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
64ea7800ed
commit
270c38ff19
1 changed files with 121 additions and 0 deletions
|
|
@ -0,0 +1,121 @@
|
|||
# Task Generation: Per-Client Mode & Preview-Before-Generate — Design
|
||||
|
||||
**Status:** Approved. Ready for implementation planning.
|
||||
|
||||
## Problem
|
||||
|
||||
The admin "Generate & Assign Tasks" tool (`/api/tasks/generate-and-assign`,
|
||||
surfaced on the Tasks → Generate & Assign page) only targets clients by
|
||||
advocate + designation, which can silently sweep in every client that
|
||||
advocate/designation pair matches — as discovered this session, that's up to
|
||||
23 clients for a single pair. There's no way to generate tasks for just one
|
||||
specific client. There's also no confirmation step anywhere in this flow:
|
||||
clicking the button immediately writes tasks to the database with no
|
||||
preview of what will be created or a chance to back out.
|
||||
|
||||
Goal: add a per-client targeting mode, and require a preview-and-confirm
|
||||
step before any tasks are actually created, for this admin tool.
|
||||
|
||||
## Scope
|
||||
|
||||
**In scope:** the admin "Generate & Assign Tasks" page/endpoint only — both
|
||||
its existing advocate+designation mode and the new per-client mode.
|
||||
|
||||
**Explicitly out of scope** (decided during brainstorming): the Policy
|
||||
Group Manager's per-group "Generate Tasks" button, the Setup Wizard's
|
||||
silent auto-generation on client-setup completion, and the silent
|
||||
auto-regeneration that fires when a policy is removed from a group. None of
|
||||
these get a preview step in this phase.
|
||||
|
||||
## Decisions
|
||||
|
||||
| Question | Decision |
|
||||
|---|---|
|
||||
| Preview scope | Admin Generate & Assign page only (existing + new per-client mode) |
|
||||
| Per-client assignee | Always that client's own `claimsAdvocateId` — not an arbitrary admin pick |
|
||||
| Selection mode | Separate mode from advocate+designation (toggle), not a combined filter |
|
||||
| Preview detail level | Summary counts (total + per-client breakdown), not a full task-by-task list, for both modes |
|
||||
| Preview mechanism | Extend the existing endpoint with a `dryRun` flag rather than a separate preview endpoint or client-side estimate |
|
||||
|
||||
**Why `dryRun` on the existing endpoint, not a separate preview endpoint:**
|
||||
a separate endpoint would duplicate the client/group/policy/template-matching
|
||||
logic (including the past-due-date filter added earlier this session) —
|
||||
real risk of the preview drifting out of sync with what actually gets
|
||||
created, which is exactly the class of bug this session spent most of its
|
||||
time fixing. A `dryRun` flag on the same code path guarantees the preview
|
||||
can never lie about what will happen.
|
||||
|
||||
## Design
|
||||
|
||||
### API — `POST /api/tasks/generate-and-assign`
|
||||
|
||||
Body accepts **either** targeting mode, plus an optional flag:
|
||||
```
|
||||
{ advocateId, designationId, dryRun? } // existing mode
|
||||
{ clientId, dryRun? } // new mode
|
||||
```
|
||||
Server validates exactly one mode is provided (400 if both or neither).
|
||||
|
||||
**Client mode specifics:**
|
||||
- Assignee is always the target client's own `claimsAdvocateId`.
|
||||
- If that client has no claims advocate set, return an error
|
||||
(`"Client has no claims advocate assigned"`) rather than creating
|
||||
unassigned tasks.
|
||||
|
||||
**Refactor:** the route's three sections (renewal groups / ungrouped
|
||||
policies / client-level templates) already build a `tasksToCreate` array
|
||||
before writing (including the `isRelevantDueDate` past-due filter). Split
|
||||
each section into "compute what would be created" and "write it," so
|
||||
`dryRun: true` runs the exact same computation but skips the
|
||||
`createMany`/`create` calls and the audit log entry (nothing happened, so
|
||||
nothing to log). A real run (`dryRun` false/absent) behaves as today, plus
|
||||
records which mode triggered it (`advocate-designation` vs `client`) in the
|
||||
audit log's `newValues`.
|
||||
|
||||
**Dry-run response shape:**
|
||||
```json
|
||||
{
|
||||
"dryRun": true,
|
||||
"clientsFound": 3,
|
||||
"totalEstimatedTasks": 42,
|
||||
"advocateName": "Jane Smith",
|
||||
"clients": [
|
||||
{ "id": "cl_...", "name": "Acme Corp", "estimatedTasks": 14 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### UI — "Generate & Assign Tasks" card (`src/app/(dashboard)/tasks/assign/page-client.tsx`)
|
||||
|
||||
- Add a mode toggle: **By Advocate + Designation** (today's UI, unchanged)
|
||||
vs **By Client** (new — a searchable picker using the `clients` prop
|
||||
already passed into this component). In client mode, if the selected
|
||||
client has no `claimsAdvocateId`, disable the action and show a helper
|
||||
message explaining why.
|
||||
- The generate button no longer calls the endpoint for real directly. It
|
||||
first calls with `dryRun: true`:
|
||||
- `totalEstimatedTasks === 0` → just toast the existing "no new tasks"
|
||||
message, no dialog (nothing meaningful to confirm).
|
||||
- Otherwise → open an `AlertDialog` (this codebase's existing pattern for
|
||||
confirm-before-consequential-action) showing total counts + the
|
||||
per-client breakdown table, with **Confirm** (re-calls the endpoint
|
||||
without `dryRun`) and **Cancel** (closes, no side effects — nothing was
|
||||
written during the preview call).
|
||||
|
||||
### Testing plan
|
||||
Route-level tests for `generate-and-assign`:
|
||||
- `dryRun: true` and a real run produce identical counts for the same
|
||||
input (no drift between preview and reality).
|
||||
- `clientId` mode only ever touches that one client.
|
||||
- Missing-advocate error case for client mode.
|
||||
- Mutual-exclusivity validation (400 when both or neither targeting mode
|
||||
is provided).
|
||||
|
||||
## Out of scope / explicitly deferred
|
||||
- Preview/confirm for the per-group manual button, the Setup Wizard's
|
||||
auto-generation, and the remove-from-group auto-regeneration — all still
|
||||
fire immediately with no confirmation, unchanged by this phase.
|
||||
- The separately-discovered gap where policies that stay inside a group
|
||||
never get `POLICY`-level tasks generated by any automatic path, and the
|
||||
`generate-policy-tasks` route's missing idempotency check — both noted
|
||||
during this session's review but not addressed by this design.
|
||||
Loading…
Add table
Add a link
Reference in a new issue