Phase 3 Multi-Project CRM Implementation:
- Add UnifiedContact model for cross-project contact linking
- Add Organization model with domain-based auto-detection
- Add ActivityTimeline model for comprehensive interaction tracking
- Add SLATracking model for 24-hour response commitment
- Add ResponseTemplate model with variable substitution
- Add CRM controller with 8 API endpoints
- Add Inbox controller for unified communications
- Add CRM dashboard frontend with tabs (Contacts, Orgs, SLA, Templates)
- Add Contact Management interface (Phase 1)
- Add Unified Inbox interface (Phase 2)
- Integrate CRM routes into main API
Critical Bug Fixes:
- Fix newsletter DELETE button (event handler context issue)
- Fix case submission invisible button (invalid CSS class)
- Fix Chart.js CSP violation (add cdn.jsdelivr.net to policy)
- Fix Chart.js SRI integrity hash mismatch
Technical Details:
- Email-based contact deduplication across projects
- Automatic organization linking via email domain
- Cross-project activity timeline aggregation
- SLA breach detection and alerting system
- Template rendering with {placeholder} substitution
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
117 lines
5 KiB
HTML
117 lines
5 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Contact Management | Tractatus Admin</title>
|
|
<link rel="stylesheet" href="/css/tailwind.css?v=0.1.0.1729786000000">
|
|
<link rel="stylesheet" href="/css/tractatus-theme.min.css?v=0.1.0.1729786000000">
|
|
<script defer src="/js/admin/auth-check.js?v=0.1.0.1729786000000"></script>
|
|
</head>
|
|
<body class="bg-gray-50">
|
|
<div id="admin-navbar" data-page-title="Contact Management" data-page-icon="default"></div>
|
|
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
|
|
<!-- Header -->
|
|
<div class="mb-8">
|
|
<h1 class="text-3xl font-bold text-gray-900">Contact Management</h1>
|
|
<p class="mt-2 text-gray-600">Manage contact form submissions and track response times</p>
|
|
</div>
|
|
|
|
<!-- Stats Overview -->
|
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
|
|
<div class="bg-white rounded-lg shadow p-6">
|
|
<div class="text-sm font-medium text-gray-500">Total Contacts</div>
|
|
<div class="mt-2 text-3xl font-semibold text-gray-900" id="stat-total">-</div>
|
|
</div>
|
|
<div class="bg-white rounded-lg shadow p-6">
|
|
<div class="text-sm font-medium text-gray-500">New</div>
|
|
<div class="mt-2 text-3xl font-semibold text-orange-600" id="stat-new">-</div>
|
|
</div>
|
|
<div class="bg-white rounded-lg shadow p-6">
|
|
<div class="text-sm font-medium text-gray-500">Assigned</div>
|
|
<div class="mt-2 text-3xl font-semibold text-blue-600" id="stat-assigned">-</div>
|
|
</div>
|
|
<div class="bg-white rounded-lg shadow p-6">
|
|
<div class="text-sm font-medium text-gray-500">Responded</div>
|
|
<div class="mt-2 text-3xl font-semibold text-green-600" id="stat-responded">-</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filters -->
|
|
<div class="bg-white rounded-lg shadow p-6 mb-8">
|
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Status</label>
|
|
<select id="filter-status" class="w-full border border-gray-300 rounded px-3 py-2">
|
|
<option value="">All Statuses</option>
|
|
<option value="new" selected>New</option>
|
|
<option value="assigned">Assigned</option>
|
|
<option value="responded">Responded</option>
|
|
<option value="closed">Closed</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Type</label>
|
|
<select id="filter-type" class="w-full border border-gray-300 rounded px-3 py-2">
|
|
<option value="">All Types</option>
|
|
<option value="general">General Inquiry</option>
|
|
<option value="partnership">Partnership</option>
|
|
<option value="technical">Technical</option>
|
|
<option value="feedback">Feedback</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Priority</label>
|
|
<select id="filter-priority" class="w-full border border-gray-300 rounded px-3 py-2">
|
|
<option value="">All Priorities</option>
|
|
<option value="low">Low</option>
|
|
<option value="normal">Normal</option>
|
|
<option value="high">High</option>
|
|
</select>
|
|
</div>
|
|
<div class="flex items-end">
|
|
<button id="refresh-btn" class="w-full px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
|
|
Refresh
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Contacts List -->
|
|
<div class="bg-white rounded-lg shadow">
|
|
<div class="px-6 py-4 border-b border-gray-200">
|
|
<h2 class="text-lg font-semibold text-gray-900">Contact Submissions</h2>
|
|
</div>
|
|
<div id="contacts-container" class="divide-y divide-gray-200">
|
|
<!-- Will be populated by JS -->
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- Contact Detail Modal -->
|
|
<div id="contact-detail-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
|
|
<div class="bg-white rounded-lg shadow-xl max-w-3xl w-full mx-4 max-h-[90vh] overflow-y-auto">
|
|
<div class="p-6">
|
|
<div class="flex items-center justify-between mb-6">
|
|
<h2 class="text-2xl font-bold text-gray-900">Contact Details</h2>
|
|
<button id="close-detail-modal" class="text-gray-400 hover:text-gray-600 text-2xl leading-none">×</button>
|
|
</div>
|
|
|
|
<div id="contact-detail-content">
|
|
<!-- Will be populated dynamically -->
|
|
</div>
|
|
|
|
<div class="flex items-center justify-end gap-3 mt-6 pt-6 border-t">
|
|
<button id="close-detail-btn" class="px-4 py-2 border border-gray-300 rounded text-gray-700 hover:bg-gray-50">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="/js/components/navbar-admin.js?v=0.1.0.1729786000000"></script>
|
|
<script src="/js/admin/contact-management.js?v=0.1.0.1729786000000"></script>
|
|
</body>
|
|
</html>
|