Skip to main content

Funding Queue Remove From Queue Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.
Goal: Change the funding queue table’s single-item delete action so it closes the linked processing submission and removes the contact from the queue without deleting any fundingPlans rows. Architecture: Keep the existing queue data model and reuse the existing processing-submission status workflow. The page and hook will expose a queue-specific remove action keyed by processingSubmissionId, and FundingQueueTable will update its action menu, confirmation dialog, and toasts to describe removing from queue rather than deleting plans. Tech Stack: Next.js App Router, React, TypeScript, Convex mutations/queries, Bun tests, ESLint

File Map

Modify
  • app/admin/internal/funding-queue/page.tsx
    • Pass the renamed queue action prop into FundingQueueTable.
  • lib/hooks/useAdminFundingQueue.ts
    • Replace the single-item plan deletion helper with a submission-closing helper that calls the existing setSubmissionStatusMutation.
  • components/funding-queue/FundingQueueTable.tsx
    • Show the single-item action only when a queue item has processingSubmission, store the submission id in dialog state, and update the copy/toasts from delete wording to remove-from-queue wording.
  • tests/fundingQueueBulkActions.test.ts
    • Update source assertions so the queue table checks for the new remove-from-queue action instead of plan deletion copy.
Create
  • tests/fundingQueueRemoveFromQueue.test.ts
    • Add source assertions for the hook/page wiring that prove the queue now uses setSubmissionStatus and passes the renamed prop.

Task 1: Lock in the behavior with focused source tests

Files:
  • Modify: tests/fundingQueueBulkActions.test.ts
  • Create: tests/fundingQueueRemoveFromQueue.test.ts
  • Step 1: Update the existing table-copy test to fail on the old delete wording
test('renders bulk action controls and a remove-from-queue action in the funding queue table', () => {
  const source = readSourceFile('components/funding-queue/FundingQueueTable.tsx');

  expect(source).toContain('selectedSubmissionIds');
  expect(source).toContain('handleBulkClaimSubmissions');
  expect(source).toContain('handleBulkReleaseSubmissions');
  expect(source).toContain('Claim Selected');
  expect(source).toContain('Release Selected');
  expect(source).toContain('Delete All Plans');
  expect(source).toContain('Remove from Queue');
});
  • Step 2: Add a new wiring test for the page and hook
import { describe, expect, test } from 'bun:test';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';

function readSourceFile(path: string) {
  return readFileSync(join(process.cwd(), path), 'utf8');
}

describe('funding queue remove-from-queue wiring', () => {
  test('passes the queue removal action from the page into the table', () => {
    const source = readSourceFile('app/admin/internal/funding-queue/page.tsx');

    expect(source).toContain('removeFromQueue');
    expect(source).toContain('onRemoveFromQueue={removeFromQueue}');
  });

  test('closes the processing submission instead of deleting a plan', () => {
    const source = readSourceFile('lib/hooks/useAdminFundingQueue.ts');

    expect(source).toContain("const removeFromQueue = useCallback");
    expect(source).toContain("status: 'closed_no_longer_processing'");
    expect(source).toContain('setSubmissionStatusMutation');
  });
});
  • Step 3: Run the targeted tests and confirm they fail before implementation
Run: bun test tests/fundingQueueBulkActions.test.ts tests/fundingQueueRemoveFromQueue.test.ts Expected: FAIL because FundingQueueTable.tsx, page.tsx, and useAdminFundingQueue.ts still use onDeletePlan/deletePlan and old delete wording.

Task 2: Rewire the funding queue single-item action to close submissions

Files:
  • Modify: lib/hooks/useAdminFundingQueue.ts
  • Modify: app/admin/internal/funding-queue/page.tsx
  • Step 1: Replace the queue helper in useAdminFundingQueue
const removeFromQueue = useCallback(async (
  submissionId: Id<'processingSubmissions'>
): Promise<{ success: boolean; error?: string }> => {
  try {
    await setSubmissionStatusMutation({
      submissionId,
      status: 'closed_no_longer_processing',
    });

    return { success: true };
  } catch (error) {
    console.error('[useAdminFundingQueue] Remove from queue error:', error);
    return { success: false, error: 'Failed to remove contact from queue' };
  }
}, [setSubmissionStatusMutation]);
  • Step 2: Export the renamed helper from the hook return object
return {
  queueItems: queueItemsWithResolvedAssignees,
  stats: mergedStats,
  queueStats: queueStatsData,
  totalContacts,
  hasMore,
  removeFromQueue,
  deleteAllPlansForContact,
  claimSubmission,
  unassignSubmission,
  setSubmissionStatus,
  bulkClaimSubmissions,
  bulkReleaseSubmissions,
};
  • Step 3: Pass the renamed prop through the funding queue page
const {
  queueItems,
  stats,
  totalContacts,
  hasMore,
  isLoading,
  removeFromQueue,
  deleteAllPlansForContact,
  claimSubmission,
  unassignSubmission,
  bulkClaimSubmissions,
  bulkReleaseSubmissions,
  setSubmissionStatus,
  setCurrentUserId,
  fetchContactNotes,
  addContactNote,
  sendInternalComment,
} = useAdminFundingQueue({ locationId: null, callerEmail: user?.email || undefined, callerLocationId: user?.locationId || undefined });

<FundingQueueTable
  items={queueItems}
  loading={isLoading}
  locationId={undefined}
  authLocationId={authLocationId || undefined}
  onRemoveFromQueue={removeFromQueue}
  onDeleteAllPlans={deleteAllPlansForContact}
  onClaimSubmission={claimSubmission}
  onUnassignSubmission={unassignSubmission}
  onBulkClaimSubmissions={bulkClaimSubmissions}
  onBulkReleaseSubmissions={bulkReleaseSubmissions}
  onSetSubmissionStatus={setSubmissionStatus}
  fetchContactNotes={fetchContactNotes}
  addContactNote={addContactNote}
  sendInternalComment={sendInternalComment}
/>
  • Step 4: Run the new wiring test and confirm the hook/page changes pass
Run: bun test tests/fundingQueueRemoveFromQueue.test.ts Expected: PASS for the page/hook assertions, while the table-copy assertion can still fail until Task 3 is complete.

Task 3: Update the funding queue table UI and copy

Files:
  • Modify: components/funding-queue/FundingQueueTable.tsx
  • Test: tests/fundingQueueBulkActions.test.ts
  • Step 1: Rename the prop and dialog state to track a submission instead of a plan
onRemoveFromQueue?: (
  submissionId: Id<'processingSubmissions'>
) => Promise<{ success: boolean; error?: string }>;

const [queueItemToRemove, setQueueItemToRemove] = useState<{
  submissionId: Id<'processingSubmissions'>;
  name: string;
} | null>(null);
  • Step 2: Update the handler to call the queue-removal action
const handleRemoveFromQueue = useCallback(async () => {
  if (!queueItemToRemove || !onRemoveFromQueue) return;

  setIsDeleting(true);
  try {
    const result = await onRemoveFromQueue(queueItemToRemove.submissionId);
    if (result.success) {
      toast.success('Removed from queue');
      setDeleteDialogOpen(false);
      setQueueItemToRemove(null);
    } else {
      toast.error(result.error || 'Failed to remove from queue');
    }
  } catch (error) {
    console.error('Error removing from queue:', error);
    toast.error(error instanceof Error ? error.message : 'Failed to remove from queue');
  } finally {
    setIsDeleting(false);
  }
}, [queueItemToRemove, onRemoveFromQueue]);
  • Step 3: Show the action only when the queue item has a submission and update the menu copy
{item.processingSubmission && onRemoveFromQueue && (
  <DropdownMenuItem
    onClick={() => {
      setQueueItemToRemove({
        submissionId: item.processingSubmission!._id,
        name: singlePlan?.lenderName || singlePlan?.cardIssuer || contactName,
      });
      setDeleteDialogOpen(true);
    }}
    className="text-red-600 dark:text-red-400"
  >
    <Trash2 className="mr-2 h-4 w-4" />
    Remove from Queue
  </DropdownMenuItem>
)}
  • Step 4: Update the confirmation dialog wording
<DialogTitle className="flex items-center gap-2 text-destructive">
  <Trash2 className="h-5 w-5" />
  Remove from Queue
</DialogTitle>
<DialogDescription>
  Are you sure you want to remove <strong>{queueItemToRemove?.name}</strong> from the funding queue? Funding plans will be preserved.
</DialogDescription>
<Button
  variant="destructive"
  onClick={handleRemoveFromQueue}
  disabled={isDeleting}
>
  {isDeleting ? 'Removing...' : 'Remove from Queue'}
</Button>
  • Step 5: Run the table-focused test and make sure the updated copy is covered
Run: bun test tests/fundingQueueBulkActions.test.ts Expected: PASS with Remove from Queue and Delete All Plans both present in the source.

Task 4: Full verification

Files:
  • Modify: components/funding-queue/FundingQueueTable.tsx
  • Modify: lib/hooks/useAdminFundingQueue.ts
  • Modify: app/admin/internal/funding-queue/page.tsx
  • Modify: tests/fundingQueueBulkActions.test.ts
  • Create: tests/fundingQueueRemoveFromQueue.test.ts
  • Step 1: Run the focused queue tests
Run: bun test tests/fundingQueueBulkActions.test.ts tests/fundingQueueRemoveFromQueue.test.ts Expected: PASS
  • Step 2: Run the required type check
Run: bunx tsc --noEmit Expected: PASS with no TypeScript errors
  • Step 3: Run the required lint check
Run: bun run lint Expected: PASS with no lint errors