diff --git a/dev/Horizon issues and Next Steps.docx b/dev/Horizon issues and Next Steps.docx new file mode 100644 index 0000000..2afc0bf Binary files /dev/null and b/dev/Horizon issues and Next Steps.docx differ diff --git a/dev/Seubert-Claims-LukeBillman.md b/dev/Seubert-Claims-LukeBillman.md new file mode 100644 index 0000000..462ea24 --- /dev/null +++ b/dev/Seubert-Claims-LukeBillman.md @@ -0,0 +1,147 @@ +## Key Outcomes + +The meeting resolved critical issues with disappearing tasks and established a comprehensive solution for handling client renewal dates. All tasks prior to April 1, 2026 will be marked complete to clear historical backlog, and a new client-level renewal date field will be implemented to ensure consistent task generation across different renewal group configurations. +## Decisions Made + +### Task Visibility and Historical Data + +- **Mark all tasks before April 1, 2026 as complete** to eliminate the backlog of 599+ overdue tasks that were causing confusion across user dashboards +- **Standardize overdue task definition** across all system views (tasks page, manager page, and workload page) to mean: past the due date, not marked as N/A or completed, with no lower bound restriction + +### Client Renewal Date Logic + +- **Add a "Client Renewal" date field** at the client level that will be editable by managers and serve as the source of truth for client-level tasks +- **Implement automatic date suggestion logic** using the following priority: (1) default/starred group renewal date, (2) if only one group exists, use that group's date, (3) if only individual policies exist, use policy date +- **Allow manual override** of the suggested client renewal date during client setup to accommodate complex scenarios like transportation clients with multiple renewal groups + +### Meeting Cadence + +- **Establish weekly or bi-weekly recurring meetings** short-term to ensure system issues are addressed promptly +- **Explore adding in-app functionality** for submitting change requests or bug reports that can be quickly triaged as either bugs for immediate fixes or design issues requiring meetings + +## Issues Resolved + +### Disappearing Tasks Problem + +- **Root cause identified**: Original 7-day visibility window was removed to address initial backlog, but this caused legitimate overdue tasks to accumulate without being hidden +- **Dashboard inconsistencies**: Task counts differed between primary dashboard and task dashboard due to three different overdue definitions being applied across different system views +- **Example case**: Mimi showed 599 past due tasks in one view but only 2 in another view + +### JG Contracting Renewal Task Generation Failure + +- **Problem**: Client with January 1, 2027 renewal wasn't generating expected claim review tasks in April and August 2026 +- **Root cause**: System was following the June 30 renewal date of a single unassigned policy instead of the January 1 date of the main renewal group containing the bulk of policies +- **Why it happened**: System used "nearest to year start" logic (default date rule) which selected June 30 as closer to year start than January 1, applying this to client-level tasks +- **Evidence**: Client-level tasks appeared at 275 days before June 30 (September 28) and 90 days before June 30 (April 1), confirming they followed the wrong renewal date + +### Group Numbering Confusion + +- **Issue**: JG Contracting's single renewal group was labeled "Group 2" instead of "Group 1" +- **Explanation**: Group functionality and UI were built on different days, causing inconsistent labeling logic + +## Technical Implementation Details + +### Task Status Update Execution + +- **Bulk update completed**: 3,292 tasks with due date before April 1, 2026 and status "not started" were marked as completed +- **Logging implemented**: Bulk changes are now logged to track why large numbers of tasks changed status +- **Potential issue identified**: Some tasks that were previously marked complete after April 1 may have been reset to "not started" - requires investigation + +### Task Source Tracking + +- **New icon added**: Small settings icon appears next to policies on the right side of the screen to show where tasks originated from +- **Helps debugging**: Allows users to trace which renewal date or policy group generated specific tasks + +### Client Renewal Date Field Implementation + +- **Location**: Will appear in the client setup screen alongside existing renewal group configuration +- **Label**: "Client Renewal" to allow future reuse for other purposes beyond just tasking +- **Behavior**: Auto-populated by logic on client creation but editable by managers at any time +- **No automatic overwriting**: Once set, the date won't change automatically unless a specific triggering action occurs + +## Complex Scenarios Addressed + +### Transportation Clients with Multiple Renewal Groups + +- **Use case**: Clients with auto and motor truck cargo renewing 1/1, but AUCAC and truck broker liability renewing 3/1 +- **Challenge**: System must allow selection of which group's renewal date should drive client-level tasks +- **Solution**: Manager can manually select the appropriate renewal date during setup, typically following the "big line" (highest premium policy) + +### Single Policy vs. Multiple Groups + +- **Single policy scenario**: If client has only one policy with no groups, system will use that policy's renewal date +- **Multiple groups scenario**: System will follow the starred/default group's renewal date +- **Override capability**: Regardless of automatic selection, managers can change the client renewal date if business logic dictates otherwise + +## Pending Confirmation + +- **Task reset investigation needed**: Verify whether tasks completed after April 1, 2026 were incorrectly reset to "not started" during the bulk update +- **Sync date range behavior**: Confirm that policy syncs only capture policies within the specified date range (e.g., a policy starting 8/1/25 expiring 8/1/26 would be missed if sync runs for 1/1/26-12/31/26) + +## Technical Specifications + +### Overdue Task Definition (Standardized) + +- **Criteria**: Task is overdue if all of the following are true: + - Due date is before today + - Status is NOT "N/A" + - Status is NOT "Completed" + - No lower bound date restriction (perpetual visibility) + +### Task Generation Logic Hierarchy + +1. **Client-level tasks**: Follow the client renewal date field +2. **Group-level tasks**: Follow their specific group's renewal date +3. **Policy-level tasks**: Follow individual policy renewal dates + +### Default Date Rule Logic (Pre-Fix) + +- **Original behavior**: Selected renewal date nearest to start of year across all policies and groups +- **Problem**: Did not account for which policies/groups were primary or contained bulk of coverage +- **New behavior**: Will populate suggested client renewal date but allow override + +## Action Items + +- **Lawrence**: Implement client renewal date field in client setup screen with auto-population logic and manual override capability +- **Lawrence**: Investigate and resolve task reset issue where completed tasks after 4/1 may have been changed to "not started" +- **Lawrence**: Update sync date range to clearly indicate which policies will be captured based on effective/expiration dates +- **Luke**: Set up weekly or bi-weekly recurring meeting with Lawrence to review ongoing system issues +- **Luke**: Review task counts across all users to verify the April 1 bulk completion resolved dashboard inconsistencies + +## Open Questions + +### Task Visibility After Bulk Update + +- Why are some tasks showing different statuses than expected after the bulk completion of pre-April 1 tasks? +- Are there any completed tasks from April 2026 onwards that were incorrectly reset? + +### Sync Functionality + +- Does the sync date range need to be adjusted to capture policies with effective dates before the sync year but expiration dates within it? +- Should sync default dates be modified to be more inclusive of cross-year policies? + +## System Behavior Notes + +### Task Page vs. Dashboard Discrepancies (Pre-Fix) + +- **Tasks page**: Counted all overdue tasks with no lower bound +- **Manager page**: Used configurable overdue window (last 30 days only) +- **Workload KPI**: Counted all tasks including unassigned ones +- **Result**: Same user could see vastly different overdue counts depending on which view they accessed + +### Renewal Group Star Functionality + +- **Purpose**: Star indicates the default renewal group that should drive client-level task generation +- **Previous assumption**: System was following starred groups for client-level tasks +- **Actual behavior**: System was using "nearest to year start" logic instead, ignoring the star designation + +### Policy Sync Date Range Default + +- **Current behavior**: Sync defaults to current calendar year (1/1/26 - 12/31/26) +- **Limitation**: Policies with effective dates in previous year but expiration in current year will be missed unless date range is manually adjusted + +## Next Meeting + +- **Frequency**: Weekly or bi-weekly to be scheduled +- **Topics**: Continue working through system functionality issues and enhancements +- **Participants**: Luke, Lawrence, potentially Dawn and Christy as needed diff --git a/dev/audit-logging-fix-plan.md b/dev/audit-logging-fix-plan.md new file mode 100644 index 0000000..ea585cb --- /dev/null +++ b/dev/audit-logging-fix-plan.md @@ -0,0 +1,108 @@ +# Audit Logging Fix Plan +_Scheduled: after hours_ + +## Problem + +Two code paths can change a task's status without capturing the **previous** status in the audit log: + +1. **`POST /api/tasks/[id]/notes`** — When a user adds a note and simultaneously changes task status, the audit entry is `TASK_NOTE_ADDED` with only `statusChange: `. The old status is never recorded. +2. **`src/lib/shape-import/run-import.ts`** — SHAPE import directly calls `prisma.task.update()` and `prisma.task.create()` with `COMPLETED`/`NA` status. Zero per-task audit entries. + +The notes-route gap is the most operationally urgent because users are actively using it daily. The import gap is lower risk (import is a rare admin operation). + +--- + +## Fix 1 — Notes Route: Capture Old Status (Priority: High) + +**File:** `src/app/api/tasks/[id]/notes/route.ts` + +### What to change + +The task is already fetched before the note is written (to check assignment). Add the old status to the audit log, and emit a **separate** `TASK_STATUS_CHANGED` entry when a status change accompanies the note. + +```ts +// After fetching task (line ~42), old status is already available as task.status + +if (status && ['COMPLETED', 'NOT_STARTED', 'IN_PROGRESS'].includes(status)) { + await prisma.task.update({ ... }) // existing logic, unchanged + + // ADD: dedicated status-change audit entry + await prisma.auditLog.create({ + data: { + userId, + action: 'TASK_STATUS_CHANGED', + entityType: 'Task', + entityId: id, + oldValues: { status: task.status }, + newValues: { status, source: 'note' }, + }, + }) +} + +// Existing TASK_NOTE_ADDED entry — update to include oldStatus too +await prisma.auditLog.create({ + data: { + userId, + action: 'TASK_NOTE_ADDED', + entityType: 'Task', + entityId: id, + oldValues: { status: task.status }, // ADD this + newValues: { noteId: note.id, statusChange: status ?? null }, + }, +}) +``` + +### Verification after deploy +```sql +-- Confirm old status is now captured +SELECT old_values->>'status', new_values->>'status', new_values->>'source', created_at +FROM audit_logs +WHERE action = 'TASK_STATUS_CHANGED' +ORDER BY created_at DESC +LIMIT 10; +``` + +--- + +## Fix 2 — PATCH Route: Confirm old_values.status is always set (Priority: Medium) + +**File:** `src/app/api/tasks/[id]/route.ts` + +The `TASK_UPDATED` entries already capture `oldValues: { status: task.status }`, but only when a status field is present in the request body. Verify the task is always fetched before the update (it is — line ~30 fetches `task`). No code change needed; just confirm via spot-check. + +```sql +-- Spot-check: any TASK_UPDATED entries missing old_values.status +SELECT COUNT(*) FROM audit_logs +WHERE action = 'TASK_UPDATED' + AND old_values->>'status' IS NULL; +``` + +--- + +## Fix 3 — SHAPE Import: Per-task Audit Logging (Priority: Low) + +**File:** `src/lib/shape-import/run-import.ts` + +This is a bulk operation run rarely by admins. The `ShapeImportRun` table already records run-level stats. Acceptable to leave per-task audit logging out for now given the volume (hundreds of tasks per run would bloat the audit table). + +**Recommendation:** Add a summary `SHAPE_IMPORT_TASK_SUMMARY` audit entry per import run that records counts by status (created COMPLETED, created NOT_STARTED, updated, skipped). Already partially done via `ImportStats`. + +--- + +## Deployment Steps + +1. Apply Fix 1 to `notes/route.ts` +2. TypeScript check: `npx tsc --noEmit` +3. Commit: `git commit -m "Fix: capture old status in task notes audit log"` +4. Push + deploy: `docker compose -f /opt/stacks/horizon/docker-compose.yml up -d --build` +5. Run verification queries above to confirm +6. Optionally apply Fix 3 summary entry to shape import + +--- + +## Notes + +- No DB schema changes required — `old_values` and `new_values` are already `jsonb` columns. +- No migration needed. +- Zero downtime deploy (standard Next.js container rebuild). +- Fix 1 is safe to apply during off-hours with no user impact. diff --git a/ondeck/src/app/(dashboard)/clients/[id]/page.tsx b/ondeck/src/app/(dashboard)/clients/[id]/page.tsx index 05ff44c..d29b9d5 100644 --- a/ondeck/src/app/(dashboard)/clients/[id]/page.tsx +++ b/ondeck/src/app/(dashboard)/clients/[id]/page.tsx @@ -66,6 +66,7 @@ export default async function ClientDetailPage({ OR: [ { policyGroupId: null }, { policyGroup: { renewalDate: { gte: cutoff } } }, + { status: { notIn: ['COMPLETED', 'CANCELLED', 'NA'] } }, ], AND: [ { diff --git a/ondeck/src/app/(dashboard)/manager/setup/page.tsx b/ondeck/src/app/(dashboard)/manager/setup/page.tsx index 9030d9d..d18d618 100644 --- a/ondeck/src/app/(dashboard)/manager/setup/page.tsx +++ b/ondeck/src/app/(dashboard)/manager/setup/page.tsx @@ -30,28 +30,40 @@ export default async function SetupQueuePage() { redirect('/dashboard') } - const clients = await prisma.client.findMany({ - where: { - designation: { name: { in: ['Shape', 'Shape 2'] } }, - policies: { some: {} }, - OR: [ - { claimsAdvocateId: null }, - { setupCompletedAt: null }, - ], - }, - select: { - id: true, - name: true, - createdAt: true, - claimsAdvocateId: true, - setupCompletedAt: true, - policies: { - select: { expirationDate: true }, + const DEAD_STATUSES = ['Cancelled', 'Expired', 'Non-Renewed', 'Rewritten', 'Not taken'] + + const [clients, skippedCount] = await Promise.all([ + prisma.client.findMany({ + where: { + designation: { name: { in: ['Shape', 'Shape 2'] } }, + policies: { some: { status: { notIn: DEAD_STATUSES } } }, + OR: [ + { claimsAdvocateId: null }, + { setupCompletedAt: null }, + ], }, - _count: { select: { policies: true } }, - }, - orderBy: { createdAt: 'asc' }, - }) + select: { + id: true, + name: true, + createdAt: true, + claimsAdvocateId: true, + setupCompletedAt: true, + policies: { + where: { status: { notIn: DEAD_STATUSES } }, + select: { expirationDate: true }, + }, + _count: { select: { policies: { where: { status: { notIn: DEAD_STATUSES } } } } }, + }, + orderBy: { createdAt: 'asc' }, + }), + prisma.client.count({ + where: { + designation: { name: { in: ['Shape', 'Shape 2'] } }, + OR: [{ claimsAdvocateId: null }, { setupCompletedAt: null }], + NOT: { policies: { some: { status: { notIn: DEAD_STATUSES } } } }, + }, + }), + ]) const now = new Date() @@ -85,6 +97,11 @@ export default async function SetupQueuePage() {

New Client Setup

Clients that need a claims advocate or renewal group configuration. + {skippedCount > 0 && ( + + ({skippedCount} skipped — no active policies) + + )}

diff --git a/ondeck/src/app/(dashboard)/tasks/page.tsx b/ondeck/src/app/(dashboard)/tasks/page.tsx index b5f80b4..6176d1d 100644 --- a/ondeck/src/app/(dashboard)/tasks/page.tsx +++ b/ondeck/src/app/(dashboard)/tasks/page.tsx @@ -29,11 +29,12 @@ export default async function TasksPage() { policy: { status: { in: ['Cancelled', 'Expired', 'Non-Renewed', 'Rewritten', 'Not taken'] } }, }, }, - // Exclude tasks on expired renewal groups + // Exclude tasks on expired renewal groups — but NEVER hide open tasks { OR: [ { policyGroupId: null }, { policyGroup: { renewalDate: { gte: cutoff } } }, + { status: { notIn: TERMINAL_STATUSES } }, ], }, // Keep ALL open tasks regardless of age; only show terminal tasks completed/closed recently diff --git a/ondeck/src/app/api/clients/[id]/setup/route.ts b/ondeck/src/app/api/clients/[id]/setup/route.ts index 5435229..f395b2d 100644 --- a/ondeck/src/app/api/clients/[id]/setup/route.ts +++ b/ondeck/src/app/api/clients/[id]/setup/route.ts @@ -129,10 +129,16 @@ async function handleSave( }) }) - const updated = await prisma.client.findUnique({ - where: { id: clientId }, - select: { id: true, name: true, renewalDate: true, setupCompletedAt: true, claimsAdvocateId: true }, - }) + const [updated, savedGroups] = await Promise.all([ + prisma.client.findUnique({ + where: { id: clientId }, + select: { id: true, name: true, renewalDate: true, setupCompletedAt: true, claimsAdvocateId: true }, + }), + prisma.policyGroup.findMany({ + where: { clientId }, + select: { id: true, name: true, renewalDate: true }, + }), + ]) // If starred group changed, recalculate due dates on open client-level template tasks const renewalDateChanged = @@ -208,7 +214,7 @@ async function handleSave( await Promise.all(auditEntries) - return NextResponse.json({ client: updated }) + return NextResponse.json({ client: updated, groups: savedGroups }) } catch (error) { console.error('Setup save error:', error) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) diff --git a/ondeck/src/app/api/clients/[id]/tasks/route.ts b/ondeck/src/app/api/clients/[id]/tasks/route.ts index b2c4cab..5d8460a 100644 --- a/ondeck/src/app/api/clients/[id]/tasks/route.ts +++ b/ondeck/src/app/api/clients/[id]/tasks/route.ts @@ -30,10 +30,11 @@ export async function GET( where.NOT = { policy: { status: { in: ['Cancelled', 'Expired', 'Non-Renewed', 'Rewritten', 'Not taken'] } }, } - // Exclude tasks on expired renewal groups + // Exclude tasks on expired renewal groups — but NEVER hide open tasks where.OR = [ { policyGroupId: null }, { policyGroup: { renewalDate: { gte: cutoff } } }, + { status: { notIn: ['COMPLETED', 'CANCELLED', 'NA'] } }, ] // Keep ALL open tasks regardless of age; only hide ancient terminal tasks where.AND = [ diff --git a/ondeck/src/app/api/policy-groups/[id]/route.ts b/ondeck/src/app/api/policy-groups/[id]/route.ts index b8d1ecd..f5eec68 100644 --- a/ondeck/src/app/api/policy-groups/[id]/route.ts +++ b/ondeck/src/app/api/policy-groups/[id]/route.ts @@ -153,6 +153,26 @@ export async function PATCH( }, }) + // Recalculate due dates on open template tasks when renewal date changes + let tasksRescheduled = 0 + if (renewalDate !== undefined) { + const newRenewalDate = new Date(renewalDate) + const openTasks = await prisma.task.findMany({ + where: { + policyGroupId: id, + templateId: { not: null }, + status: { notIn: ['COMPLETED', 'CANCELLED', 'NA'] }, + }, + select: { id: true, daysOffset: true }, + }) + for (const t of openTasks) { + const newDue = new Date(newRenewalDate) + newDue.setDate(newDue.getDate() + (t.daysOffset ?? 0)) + await prisma.task.update({ where: { id: t.id }, data: { dueDate: newDue } }) + } + tasksRescheduled = openTasks.length + } + await prisma.auditLog.create({ data: { userId: (session.user as any).id, @@ -160,11 +180,11 @@ export async function PATCH( entityType: 'PolicyGroup', entityId: group.id, oldValues: { name: existing.name, renewalDate: existing.renewalDate }, - newValues: { name, renewalDate, notes, policyIds }, + newValues: { name, renewalDate, notes, policyIds, tasksRescheduled }, }, }) - return NextResponse.json(group) + return NextResponse.json({ ...group, tasksRescheduled }) } catch (error) { console.error('Policy group PATCH error:', error) return NextResponse.json({ error: 'Internal server error' }, { status: 500 }) diff --git a/ondeck/src/app/api/tasks/[id]/notes/route.ts b/ondeck/src/app/api/tasks/[id]/notes/route.ts index 94c0e87..1fbc067 100644 --- a/ondeck/src/app/api/tasks/[id]/notes/route.ts +++ b/ondeck/src/app/api/tasks/[id]/notes/route.ts @@ -72,6 +72,16 @@ export async function POST( : { completedAt: null, completedBy: null }), }, }) + await prisma.auditLog.create({ + data: { + userId, + action: 'TASK_STATUS_CHANGED', + entityType: 'Task', + entityId: id, + oldValues: { status: task.status }, + newValues: { status, source: 'note', noteId: note.id }, + }, + }) } await prisma.auditLog.create({ @@ -80,6 +90,7 @@ export async function POST( action: 'TASK_NOTE_ADDED', entityType: 'Task', entityId: id, + oldValues: { status: task.status }, newValues: { noteId: note.id, statusChange: status ?? null }, }, }) diff --git a/ondeck/src/components/admin/sync-trigger.tsx b/ondeck/src/components/admin/sync-trigger.tsx index 435ec5f..70f15c2 100644 --- a/ondeck/src/components/admin/sync-trigger.tsx +++ b/ondeck/src/components/admin/sync-trigger.tsx @@ -13,8 +13,9 @@ export function SyncTrigger() { const [progress, setProgress] = useState(0) const [status, setStatus] = useState<'idle' | 'syncing' | 'success' | 'error'>('idle') const [result, setResult] = useState(null) - const [startDate, setStartDate] = useState('2026-01-01') - const [endDate, setEndDate] = useState('2026-12-31') + const currentYear = new Date().getFullYear() + const [startDate, setStartDate] = useState(`${currentYear - 1}-01-01`) + const [endDate, setEndDate] = useState(`${currentYear + 2}-12-31`) const handleSync = async () => { setSyncing(true) diff --git a/ondeck/src/components/clients/client-detail.tsx b/ondeck/src/components/clients/client-detail.tsx index 052ca99..eb1d2f9 100644 --- a/ondeck/src/components/clients/client-detail.tsx +++ b/ondeck/src/components/clients/client-detail.tsx @@ -32,7 +32,7 @@ import { import { Textarea } from '@/components/ui/textarea' import { Building2, MapPin, Phone, Mail, FileText, CheckSquare, CalendarRange, Users, X, Plus, UserCheck, StickyNote, Pencil, Trash2, ChevronDown, ChevronUp, ArrowUp, ArrowDown, ArrowUpDown, GitBranch, ChevronRight, Settings2, MessageSquare } from 'lucide-react' import { Combobox, type ComboboxGroup } from '@/components/ui/combobox' -import { formatDate, formatRenewalDate } from '@/lib/utils' +import { formatDate, formatRenewalDate, daysUntil } from '@/lib/utils' import { PolicyGroupManager } from '@/components/clients/policy-group-manager' import { AdditionalServiceModal } from '@/components/tasks/additional-service-modal' import { TaskCard } from '@/components/tasks/task-card' @@ -61,6 +61,7 @@ export function ClientDetail({ client, designations, policyGroups = [], allPolic const [saving, setSaving] = useState(false) const [notes, setNotes] = useState(client.notes || '') const [notesSaving, setNotesSaving] = useState(false) + const [notesSavedAt, setNotesSavedAt] = useState(null) const [tasks, setTasks] = useState(client.tasks || []) const [contacts, setContacts] = useState([]) const [contactsLoading, setContactsLoading] = useState(false) @@ -296,6 +297,7 @@ export function ClientDetail({ client, designations, policyGroups = [], allPolic body: JSON.stringify({ notes: notes || null }), }) if (!res.ok) throw new Error() + setNotesSavedAt(new Date().toLocaleString()) toast.success('Notes saved') } catch { toast.error('Failed to save notes') @@ -461,9 +463,16 @@ export function ClientDetail({ client, designations, policyGroups = [], allPolic onChange={(e) => setNotes(e.target.value)} rows={4} /> - +
+ + {notesSavedAt && ( + + Saved {notesSavedAt} + + )} +
)} @@ -514,11 +523,18 @@ export function ClientDetail({ client, designations, policyGroups = [], allPolic Policy #: {policy.policyNumber || 'N/A'}

- - +
+ {(() => { + const d = daysUntil(policy.expirationDate) + if (d <= 0) return null + if (d <= 30) return {d}d + if (d <= 90) return {d}d + return null + })()} + Renewal {formatRenewalDate(policy.expirationDate)} - - + +
{/* Policy Details Grid */} diff --git a/ondeck/src/components/renewal-groups/setup-wizard.tsx b/ondeck/src/components/renewal-groups/setup-wizard.tsx index bcb06be..996b94d 100644 --- a/ondeck/src/components/renewal-groups/setup-wizard.tsx +++ b/ondeck/src/components/renewal-groups/setup-wizard.tsx @@ -399,7 +399,17 @@ export function SetupWizard({ toast.success('Draft saved') setWizardDirty(false) } else { - toast.success('Setup complete!') + const groups: { id: string }[] = data.groups ?? [] + if (groups.length > 0) { + toast.loading('Generating tasks…', { id: 'gen-tasks' }) + await Promise.allSettled( + groups.map((g) => + fetch(`/api/policy-groups/${g.id}/generate-tasks`, { method: 'POST' }) + ) + ) + toast.dismiss('gen-tasks') + } + toast.success('Setup complete! Tasks generated.') router.push(`/clients/${clientId}`) } } catch (err: any) { diff --git a/ondeck/src/lib/auth.ts b/ondeck/src/lib/auth.ts index 5400727..99937cd 100644 --- a/ondeck/src/lib/auth.ts +++ b/ondeck/src/lib/auth.ts @@ -36,7 +36,7 @@ export const authOptions: NextAuthOptions = { if (account) { // Create or update user in database const user = await prisma.user.upsert({ - where: { email: account.email }, + where: { entraOid: `local-${account.username}` }, update: { displayName: account.name, lastLoginAt: new Date(),