/** * Unified Inbox Admin Page * View and manage all communications: contacts, media inquiries, case submissions */ let allItems = []; let currentItem = null; // Initialize page document.addEventListener('DOMContentLoaded', async () => { await loadStats(); await loadInbox(); setupEventListeners(); }); /** * Load statistics */ async function loadStats() { try { const token = localStorage.getItem('admin_token'); const response = await fetch('/api/inbox/stats', { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error('Failed to load stats'); const data = await response.json(); const stats = data.stats; document.getElementById('stat-total').textContent = stats.total.all; document.getElementById('stat-new').textContent = stats.total.new; document.getElementById('stat-assigned').textContent = stats.total.assigned; document.getElementById('stat-responded').textContent = stats.total.responded; } catch (error) { console.error('Error loading stats:', error); } } /** * Load inbox items */ async function loadInbox() { try { const token = localStorage.getItem('admin_token'); const type = document.getElementById('filter-type').value; const status = document.getElementById('filter-status').value; const priority = document.getElementById('filter-priority').value; let url = '/api/inbox?limit=50'; if (type && type !== 'all') url += `&type=${type}`; if (status) url += `&status=${status}`; const response = await fetch(url, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error('Failed to load inbox'); const data = await response.json(); allItems = data.items; // Apply client-side priority filter if needed if (priority) { allItems = allItems.filter(item => item._priority === priority); } renderInbox(); } catch (error) { console.error('Error loading inbox:', error); showError('Failed to load inbox'); } } /** * Render inbox items list */ function renderInbox() { const container = document.getElementById('inbox-container'); if (allItems.length === 0) { container.innerHTML = `

No items found matching the selected filters.

`; return; } container.innerHTML = allItems.map(item => renderInboxItem(item)).join(''); // Add click listeners container.querySelectorAll('[data-item-id]').forEach(el => { el.addEventListener('click', () => { const itemId = el.getAttribute('data-item-id'); const itemType = el.getAttribute('data-item-type'); showItemDetail(itemId, itemType); }); }); } /** * Render single inbox item */ function renderInboxItem(item) { const typeBadge = getTypeBadge(item._type); const statusBadge = getStatusBadge(item._displayStatus); const priorityBadge = getPriorityBadge(item._priority); // Extract display data based on type let title, subtitle, preview; if (item._type === 'contact') { title = item.contact.name; subtitle = item.contact.email; preview = item.inquiry.subject || item.inquiry.message; } else if (item._type === 'media') { title = item.contact.name; subtitle = `${item.contact.outlet} • ${item.contact.email}`; preview = item.inquiry.subject; } else if (item._type === 'case') { title = item.submitter.name; subtitle = item.submitter.email; preview = item.case_study.title; } return `

${escapeHtml(title)}

${typeBadge} ${priorityBadge} ${statusBadge}

${escapeHtml(subtitle)}

${escapeHtml(preview)}

${formatDate(item._created)}
`; } /** * Show item detail modal */ async function showItemDetail(itemId, itemType) { try { const token = localStorage.getItem('admin_token'); let endpoint; if (itemType === 'contact') { endpoint = `/api/contact/admin/${itemId}`; } else if (itemType === 'media') { endpoint = `/api/media/inquiries/${itemId}`; } else if (itemType === 'case') { endpoint = `/api/cases/submissions/${itemId}`; } const response = await fetch(endpoint, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error('Failed to load item'); const data = await response.json(); currentItem = { ...data.contact || data.inquiry || data.submission, _type: itemType }; renderItemDetail(currentItem); document.getElementById('item-detail-modal').classList.remove('hidden'); } catch (error) { console.error('Error loading item detail:', error); alert('Failed to load item details'); } } /** * Render item detail based on type */ function renderItemDetail(item) { const content = document.getElementById('item-detail-content'); if (item._type === 'contact') { content.innerHTML = renderContactDetail(item); } else if (item._type === 'media') { content.innerHTML = renderMediaDetail(item); } else if (item._type === 'case') { content.innerHTML = renderCaseDetail(item); } } /** * Render contact detail */ function renderContactDetail(contact) { return `

Contact Information

Name:
${escapeHtml(contact.contact.name)}
Email:
${contact.contact.organization ? `
Organization:
${escapeHtml(contact.contact.organization)}
` : ''}

Inquiry

${contact.inquiry.subject ? `
Subject:
${escapeHtml(contact.inquiry.subject)}
` : ''}
Message:
${escapeHtml(contact.inquiry.message)}
Manage in Contact Manager
`; } /** * Render media inquiry detail */ function renderMediaDetail(media) { return `

Media Contact

Name:
${escapeHtml(media.contact.name)}
Email:
Outlet:
${escapeHtml(media.contact.outlet)}
${media.contact.phone ? `
Phone:
${escapeHtml(media.contact.phone)}
` : ''}

Inquiry

Subject:
${escapeHtml(media.inquiry.subject)}
Message:
${escapeHtml(media.inquiry.message)}
${media.inquiry.deadline ? `
Deadline:
${formatDate(media.inquiry.deadline)}
` : ''}
${media.ai_triage ? `

AI Triage

Urgency: ${getUrgencyBadge(media.ai_triage.urgency)}
${media.ai_triage.claude_summary ? `
Summary:
${escapeHtml(media.ai_triage.claude_summary)}
` : ''}
` : ''}
Manage in Media Triage
`; } /** * Render case submission detail */ function renderCaseDetail(caseItem) { return `

Submitter

Name:
${escapeHtml(caseItem.submitter.name)}
Email:
${caseItem.submitter.organization ? `
Organization:
${escapeHtml(caseItem.submitter.organization)}
` : ''}

Case Study

Title:
${escapeHtml(caseItem.case_study.title)}
Description:
${escapeHtml(caseItem.case_study.description)}
Failure Mode:
${escapeHtml(caseItem.case_study.failure_mode)}
${caseItem.ai_review && caseItem.ai_review.relevance_score ? `

AI Review

Relevance Score: ${Math.round(caseItem.ai_review.relevance_score * 100)}%
${caseItem.ai_review.recommended_category ? `
Recommended Category: ${escapeHtml(caseItem.ai_review.recommended_category)}
` : ''}
` : ''}
Manage in Case Moderation
`; } /** * Setup event listeners */ function setupEventListeners() { document.getElementById('filter-type').addEventListener('change', loadInbox); document.getElementById('filter-status').addEventListener('change', loadInbox); document.getElementById('filter-priority').addEventListener('change', loadInbox); document.getElementById('refresh-btn').addEventListener('click', () => { loadStats(); loadInbox(); }); document.getElementById('close-detail-modal').addEventListener('click', closeDetailModal); document.getElementById('close-detail-btn').addEventListener('click', closeDetailModal); document.getElementById('item-detail-modal').addEventListener('click', (e) => { if (e.target.id === 'item-detail-modal') { closeDetailModal(); } }); } /** * Close detail modal */ function closeDetailModal() { document.getElementById('item-detail-modal').classList.add('hidden'); currentItem = null; } /** * Utility: Get type badge */ function getTypeBadge(type) { const colors = { contact: 'bg-blue-100 text-blue-800', media: 'bg-purple-100 text-purple-800', case: 'bg-green-100 text-green-800' }; const labels = { contact: '📬 Contact', media: '📰 Media', case: '📋 Case' }; return `${labels[type] || type}`; } /** * Utility: Get priority badge */ function getPriorityBadge(priority) { const colors = { low: 'bg-gray-100 text-gray-600', normal: 'bg-blue-100 text-blue-800', medium: 'bg-blue-100 text-blue-800', high: 'bg-red-100 text-red-800' }; return `${priority}`; } /** * Utility: Get status badge */ function getStatusBadge(status) { const colors = { new: 'bg-orange-100 text-orange-800', pending: 'bg-orange-100 text-orange-800', assigned: 'bg-blue-100 text-blue-800', triaged: 'bg-blue-100 text-blue-800', responded: 'bg-green-100 text-green-800', closed: 'bg-gray-100 text-gray-600', approved: 'bg-green-100 text-green-800', rejected: 'bg-red-100 text-red-800', needs_info: 'bg-yellow-100 text-yellow-800' }; return `${status}`; } /** * Utility: Get urgency badge */ function getUrgencyBadge(urgency) { const colors = { low: 'bg-gray-100 text-gray-800', medium: 'bg-blue-100 text-blue-800', high: 'bg-red-100 text-red-800' }; return `${urgency}`; } /** * Utility: Format date */ function formatDate(dateString) { if (!dateString) return 'N/A'; const date = new Date(dateString); return date.toLocaleString('en-NZ', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } /** * Utility: Escape HTML */ function escapeHtml(text) { if (!text) return ''; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } /** * Utility: Show error */ function showError(message) { const container = document.getElementById('inbox-container'); container.innerHTML = `

${escapeHtml(message)}

`; }