# Lead Deletion Feature Design

**Issue:** #479 - Deleting Unwanted Leads
**Branch:** `feature/lead-deletion`
**Date:** 2026-01-26

## Problem

Dealers receive spam submissions through their lead forms and need a way to delete unwanted leads.

## Solution

Add individual and bulk lead deletion with a 10-second undo window. Deletions are optimistic (instant UI feedback) with the actual API call deferred until the undo window expires.

## User Experience

### Deleting Leads

1. **Individual deletion:** Trash icon on each lead row
   - Desktop: visible on hover
   - Mobile: always visible

2. **Bulk deletion:** Checkboxes + "Delete selected" button
   - Checkbox column as first column
   - Header checkbox selects all on current page
   - Bulk action bar appears when 1+ leads selected

### Undo Flow

1. Leads disappear from the list immediately
2. Banner appears at top of list: "X lead(s) deleted" with Undo button
3. User has 10 seconds to click Undo
4. If Undo clicked: leads restore to original positions
5. If timer expires: API call deletes leads permanently

### Multiple Deletions

If the user deletes more leads before the undo window expires, the deletions combine:

- Banner updates to show total count
- Single Undo restores all pending leads
- Timer resets to 10 seconds

## API Design

### DELETE /api/dealer/leads

**Request:**

```typescript
{
  leadIds: string[]  // max 100 IDs per request
}
```

**Response:**

```typescript
{
  deleted: number; // count of successfully deleted leads
}
```

**Security:**

- Requires authenticated session (NextAuth)
- Tier-gated: Growth+ only (`hasLeadFormAccess()`)
- Ownership verified: only deletes leads where `dealerId` matches session user
- Input validated: array of strings, max 100 items

## Frontend State

```typescript
// Selected leads for bulk operations
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());

// Leads pending deletion (removed from UI, waiting for timer)
const [pendingDeletion, setPendingDeletion] = useState<{
  leads: LeadData[]; // Actual lead objects for restore
  timeoutId: number; // To clear if undo clicked
  expiresAt: number; // Timestamp for countdown display
} | null>(null);
```

### Delete Flow

1. Remove leads from displayed list
2. Add leads to `pendingDeletion.leads` (merge if already pending)
3. Clear existing timeout, start new 10-second timeout
4. Clear selection state

### Undo Flow

1. Clear the timeout
2. Merge `pendingDeletion.leads` back into displayed list
3. Sort by timestamp to restore original positions
4. Clear `pendingDeletion` state

### Timeout Expiry Flow

1. Call `DELETE /api/dealer/leads` with lead IDs
2. Clear `pendingDeletion` state
3. Refetch leads to sync pagination counts

## Files to Change

**Create/Modify:**

- `app/api/dealer/leads/route.ts` - Add DELETE handler
- `app/dashboard/leads/page.tsx` - Selection state, delete UI, pending deletion banner
- `app/dashboard/leads/leads.module.css` - Checkbox, trash icon hover, banner styles

**No changes needed:**

- Prisma schema (hard delete at DB level)
- Toast component (undo UI is inline banner, not toast)
- Types (`LeadData` interface sufficient)

## Out of Scope

- Lead archival (move to archive instead of delete)
- Soft delete at database level
- Lead filtering/search
- Export functionality
