Now add the `deleteFoundThreads` function. Let me find a good place — near the other delete functions:


Grep

^async function deleteThread\b|^function deleteThread\b


4026:async function deleteThread(threadId, subject) {


  4026→async function deleteThread(threadId, subject) {
  4027→    if (!confirm('Delete thread "' + subject + '" and all associated emails/analysis?\n\nThis cannot be undone.')) return;
  4028→
  4029→    try {
  4030→        const response = await fetch('api.php?action=delete_thread', {
  4031→            method: 'POST',
  4032→            headers: {'Content-Type': 'application/json'},
  4033→            body: JSON.stringify({thread_id: threadId})
  4034→        });
  4035→        const data = await response.json();
  4036→        if (data.success) {
  4037→            alert('Thread deleted. Removed: ' + (data.deleted.emails || 0) + ' emails.');
  4038→            loadEmails(state.emails.page);
  4039→        } else {
  4040→            alert('Error: ' + (data.error || 'Delete failed'));
  4041→        }
  4042→    } catch(e) {
  4043→        alert('Error deleting thread: ' + e.message);
  4044→    }
  4045→}
  4046→
  4047→async function deleteThreadsByEmail(email, domain) {
  4048→    var target = email || ('@' + domain);
  4049→    if (!confirm('Delete ALL threads involving ' + target + '?\n\nThis removes all emails, analysis, and AI responses for those threads. Cannot be undone.')) return;
  4050→
  4051→    try {
  4052→        var body = {};
  4053→        if (email) body.email = email;
  4054→        if (domain) body.domain = domain;
  4055→        const response = await fetch('api.php?action=delete_threads_by_email', {
  4056→            method: 'POST',
  4057→            headers: {'Content-Type': 'application/json'},
  4058→            body: JSON.stringify(body)
  4059→        });
  4060→        const data = await response.json();
  4061→        if (data.success) {
  4062→            alert('Deleted ' + data.deleted.thread_count + ' threads, ' + data.deleted.emails + ' emails.');
  4063→            loadContacts(state.contacts.page);
  4064→            loadEmails(state.emails.page);
  4065→        } else {

async function deleteThread(threadId, subject) {
    if (!confirm('Delete thread "' + subject + '" and all associated emails/analysis?\n\nThis cannot be undone.')) return;

    try {
        const response = await fetch('api.php?action=delete_thread', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({thread_id: threadId})
        });
        const data = await response.json();
        if (data.success) {
            alert('Thread deleted. Removed: ' + (data.deleted.emails || 0) + ' emails.');
            loadEmails(state.emails.page);
        } else {
            alert('Error: ' + (data.error || 'Delete failed'));
        }
    } catch(e) {
        alert('Error deleting thread: ' + e.message);
    }
}

async function deleteThreadsByEmail(email, domain) {
    var target = email || ('@' + domain);
    if (!confirm('Delete ALL threads involving ' + target + '?\n\nThis removes all emails, analysis, and AI responses for those threads. Cannot be undone.')) return;

    try {
        var body = {};
        if (email) body.email = email;
        if (domain) body.domain = domain;
        const response = await fetch('api.php?action=delete_threads_by_email', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify(body)
        });
        const data = await response.json();
        if (data.success) {
            alert('Deleted ' + data.deleted.thread_count + ' threads, ' + data.deleted.emails + ' emails.');
            loadContacts(state.contacts.page);
            loadEmails(state.emails.page);
        } else {


  4065→        } else {
  4066→            alert('Error: ' + (data.error || 'Delete failed'));
  4067→        }
  4068→    } catch(e) {
  4069→        alert('Error: ' + e.message);
  4070→    }
  4071→}
  4072→
  4073→function promptBulkDeleteByEmail() {
  4074→    var input = prompt('Enter email address (e.g. john@example.com) or domain (e.g. example.com) to delete all threads:');
  4075→    if (!input || !input.trim()) return;
  4076→    input = input.trim();
  4077→    if (input.indexOf('@') !== -1) {
  4078→        deleteThreadsByEmail(input, '');
  4079→    } else {

        } else {
            alert('Error: ' + (data.error || 'Delete failed'));
        }
    } catch(e) {
        alert('Error: ' + e.message);
    }
}

function promptBulkDeleteByEmail() {
    var input = prompt('Enter email address (e.g. john@example.com) or domain (e.g. example.com) to delete all threads:');
    if (!input || !input.trim()) return;
    input = input.trim();
    if (input.indexOf('@') !== -1) {
        deleteThreadsByEmail(input, '');
    } else {


Now add `deleteFoundThreads` after the existing delete functions:


  4073→function promptBulkDeleteByEmail() {
  4074→    var input = prompt('Enter email address (e.g. john@example.com) or domain (e.g. example.com) to delete all threads:');
  4075→    if (!input || !input.trim()) return;
  4076→    input = input.trim();
  4077→    if (input.indexOf('@') !== -1) {
  4078→        deleteThreadsByEmail(input, '');
  4079→    } else {
  4080→        deleteThreadsByEmail('', input);
  4081→    }
  4082→}
  4083→
  4084→// ============================================
  4085→// FILTER FUNCTIONS
  4086→// ============================================
  4087→function clearContactFilters() {
  4088→    document.getElementById('contact-search').value = '';
  4089→    document.getElementById('contact-type-filter').value = '';
  4090→    document.getElementById('contact-date-from').value = '';
  4091→    document.getElementById('contact-date-to').value = '';
  4092→    document.getElementById('contact-sort').value = 'last_contact:DESC';

function promptBulkDeleteByEmail() {
    var input = prompt('Enter email address (e.g. john@example.com) or domain (e.g. example.com) to delete all threads:');
    if (!input || !input.trim()) return;
    input = input.trim();
    if (input.indexOf('@') !== -1) {
        deleteThreadsByEmail(input, '');
    } else {
        deleteThreadsByEmail('', input);
    }
}

// ============================================
// FILTER FUNCTIONS
// ============================================
function clearContactFilters() {
    document.getElementById('contact-search').value = '';
    document.getElementById('contact-type-filter').value = '';
    document.getElementById('contact-date-from').value = '';
    document.getElementById('contact-date-to').value = '';
    document.getElementById('contact-sort').value = 'last_contact:DESC';


Edit

// ============================================
// BULK DELETE FOUND THREADS
// ============================================
async function deleteFoundThreads() {
    // Read current search/filter params (same as loadEmails)
    var tSearch = document.getElementById('email-search')?.value || '';
    var tContactFilter = document.getElementById('thread-contact-filter')?.value || '';
    var tDateFrom = document.getElementById('thread-date-from')?.value || '';
    var tDateTo = document.getElementById('thread-date-to')?.value || '';
    var tStatus = document.getElementById('thread-status-filter')?.value || '';

    // Build description of what we're deleting
    var filterDesc = [];
    if (tSearch) filterDesc.push('search: "' + tSearch + '"');
    if (tContactFilter) filterDesc.push('contact: "' + tContactFilter + '"');
    if (tDateFrom) filterDesc.push('from: ' + tDateFrom);
    if (tDateTo) filterDesc.push('to: ' + tDateTo);
    if (tStatus) filterDesc.push('status: ' + tStatus);

    // First, fetch total count to confirm
    var threadSortVal = (document.getElementById('thread-sort')?.value || 'last_date:DESC').split(':');
    var params = new URLSearchParams({ limit: 1, page: 1, sort: threadSortVal[0], order: threadSortVal[1] });
    if (tSearch) params.set('search', tSearch);
    if (tContactFilter) params.set('contact_name', tContactFilter);
    if (tDateFrom) params.set('date_from', tDateFrom);
    if (tDateTo) params.set('date_to', tDateTo);
    if (tStatus) params.set('status', tStatus);
    if (state.emails.contactId) params.set('contact', state.emails.contactId);

    try {
        var countResp = await fetch('api.php?action=threads&' + params.toString());
        var countData = await countResp.json();
        var total = countData.pagination?.total || 0;
        if (total === 0) { alert('No threads found to delete.'); return; }

        if (!confirm('DELETE ' + total + ' THREADS matching:\n' + filterDesc.join(', ') + '\n\nThis will remove all emails, analysis, and AI responses for these threads.\n\nThis CANNOT be undone. Continue?')) return;

        // Second confirm for large deletes
        if (total > 50) {
            if (!confirm('Are you SURE? You are about to permanently delete ' + total + ' threads. Type OK to confirm.')) return;
        }

        // Fetch all thread IDs page by page, then delete in batches
        var allThreadIds = [];
        var page = 1;
        var perPage = 100;
        var pages = Math.ceil(total / perPage);
        var deleteBtn = document.getElementById('delete-found-threads-btn');
        if (deleteBtn) deleteBtn.textContent = 'Collecting threads...';

        while (page <= pages) {
            if (deleteBtn) deleteBtn.textContent = 'Collecting ' + page + '/' + pages + '...';
            var fetchParams = new URLSearchParams({ limit: perPage, page: page, sort: threadSortVal[0], order: threadSortVal[1] });
            if (tSearch) fetchParams.set('search', tSearch);
            if (tContactFilter) fetchParams.set('contact_name', tContactFilter);
            if (tDateFrom) fetchParams.set('date_from', tDateFrom);
            if (tDateTo) fetchParams.set('date_to', tDateTo);
            if (tStatus) fetchParams.set('status', tStatus);
            if (state.emails.contactId) fetchParams.set('contact', state.emails.contactId);

            var resp = await fetch('api.php?action=threads&' + fetchParams.toString());
            var data = await resp.json();
            var threads = data.threads || [];
            if (threads.length === 0) break;
            threads.forEach(function(t) { allThreadIds.push(t.thread_id); });
            page++;
        }

        if (allThreadIds.length === 0) { alert('No thread IDs collected.'); return; }

        // Delete in batches of 100
        var totalEmails = 0;
        var batchSize = 100;
        var batches = Math.ceil(allThreadIds.length / batchSize);
        for (var b = 0; b < batches; b++) {
            if (deleteBtn) deleteBtn.textContent = 'Deleting batch ' + (b + 1) + '/' + batches + '...';
            var batch = allThreadIds.slice(b * batchSize, (b + 1) * batchSize);
            var delResp = await fetch('api.php?action=delete_threads_bulk', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({ thread_ids: batch })
            });
            var delData = await delResp.json();
            if (delData.success) {
                totalEmails += (delData.deleted.emails || 0);
            }
        }

        alert('Deleted ' + allThreadIds.length + ' threads (' + totalEmails + ' emails removed).');
        loadEmails(1);

    } catch(e) {
        alert('Error during bulk delete: ' + e.message);
        loadEmails(1);
    }
}

// ============================================
// FILTER FUNCTIONS
// ============================================