2025-08-25 12:27:33 -06:00

618 lines
40 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SMS Campaign Manager</title>
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
<link rel="stylesheet" href="/static/css/dashboard.css">
</head>
<body class="bg-gray-50">
<div x-data="campaignApp" x-init="init()" x-cloak class="container mx-auto px-4 py-8 max-w-7xl">
<!-- Header with Connection Status -->
<div class="bg-white rounded-lg shadow-sm p-6 mb-6">
<div class="flex justify-between items-center">
<div>
<h1 class="text-3xl font-bold text-gray-800">📱 SMS Campaign Manager</h1>
<p class="text-gray-600 mt-1">Homelab Campaign Management Interface</p>
</div>
<div class="flex flex-col space-y-2">
<!-- Termux API Status -->
<div class="flex items-center">
<span class="text-sm font-medium text-gray-600 mr-2 w-20">Termux:</span>
<span x-show="phoneStatus.termux_connected" class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
🟢 Online
</span>
<span x-show="!phoneStatus.termux_connected" class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
🔴 Offline
</span>
</div>
<!-- ADB Status -->
<div class="flex items-center">
<span class="text-sm font-medium text-gray-600 mr-2 w-20">ADB:</span>
<span x-show="phoneStatus.adb_connected" class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
🟢 Online
</span>
<span x-show="!phoneStatus.adb_connected" class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
🔴 Offline
</span>
</div>
</div>
</div>
</div>
<!-- Tab Navigation -->
<div class="bg-white rounded-t-lg shadow-sm border-b">
<nav class="flex space-x-1 p-1">
<button @click="activeTab = 'campaigns'"
:class="activeTab === 'campaigns' ? 'bg-blue-500 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'"
class="px-4 py-2 rounded-lg font-medium transition-colors">
📋 Campaigns
</button>
<button @click="activeTab = 'conversations'; loadConversations()"
:class="activeTab === 'conversations' ? 'bg-blue-500 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'"
class="px-4 py-2 rounded-lg font-medium transition-colors">
💬 Conversations
</button>
<button @click="activeTab = 'templates'; loadSavedTemplates()"
:class="activeTab === 'templates' ? 'bg-blue-500 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'"
class="px-4 py-2 rounded-lg font-medium transition-colors">
📝 Templates
</button>
<button @click="activeTab = 'lists'; loadSavedLists()"
:class="activeTab === 'lists' ? 'bg-blue-500 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'"
class="px-4 py-2 rounded-lg font-medium transition-colors">
📋 Contact Lists
</button>
<button @click="activeTab = 'testing'"
:class="activeTab === 'testing' ? 'bg-blue-500 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'"
class="px-4 py-2 rounded-lg font-medium transition-colors">
🧪 System Testing
</button>
</nav>
</div>
<!-- Tab Content -->
<div class="bg-white rounded-b-lg shadow-sm min-h-[600px]">
<!-- Campaigns Tab (Preserving existing functionality) -->
<div x-show="activeTab === 'campaigns'" class="p-6">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Create Campaign Section -->
<div class="border rounded-lg p-4">
<h2 class="text-xl font-semibold mb-4">Create Campaign</h2>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Campaign Name</label>
<input type="text" x-model="campaignName"
placeholder="e.g., Weekend Volunteer Outreach"
class="w-full px-3 py-2 border rounded-lg">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1 flex items-center gap-2">
Use Saved Template
<button @click="console.log('Available templates:', savedTemplates); console.log('Selected template ID:', selectedTemplate); console.log('Current message:', messageTemplate)"
class="px-2 py-1 text-xs bg-gray-200 text-gray-700 rounded hover:bg-gray-300">Debug</button>
</label>
<select x-model="selectedTemplate" @change="loadTemplate($event.target.value)" class="w-full px-3 py-2 border rounded-lg mb-2">
<option value="">-- Select a saved template --</option>
<template x-for="template in savedTemplates" :key="template.id">
<option :value="template.id" x-text="`${template.name} (${template.category})`"></option>
</template>
</select>
<div x-show="selectedTemplate && messageTemplate" class="text-sm text-green-600 mb-2 flex justify-between items-center">
<span>✓ Template loaded successfully</span>
<button @click="clearTemplate()" class="text-xs text-gray-500 hover:text-gray-700 underline">
Clear Template
</button>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">
Message Template <span class="text-gray-500">(Use {name}, {phone}, {date}, {time} for variables)</span>
</label>
<textarea x-model="messageTemplate"
@input="onMessageTemplateChange()"
placeholder="Hi {name}! Hope all is well. I am wondering if you got my last email..."
class="w-full px-3 py-2 border rounded-lg h-24"></textarea>
<div x-show="messageTemplate && messageTemplate.includes('{name}')" class="mt-2 p-2 bg-gray-100 rounded text-sm">
<span class="font-medium text-gray-600">Preview:</span>
<span x-text="messageTemplate.replace('{name}', 'John')"></span>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Recipients CSV</label>
<input type="file" @change="handleFileUpload($event)"
accept=".csv"
class="w-full px-3 py-2 border rounded-lg">
<div x-show="uploadedFile" class="mt-2 text-sm text-green-600">
<span x-text="uploadedFile"></span>
</div>
</div>
<!-- Contact Preview Section -->
<div x-show="contactsPreview.length > 0" class="border rounded-lg p-4 bg-blue-50">
<h3 class="font-semibold text-blue-800 mb-2">📋 Contacts Preview</h3>
<div class="text-sm text-blue-600 mb-2">
Total: <span x-text="totalContacts"></span> contacts loaded
</div>
<div class="max-h-40 overflow-y-auto space-y-1">
<template x-for="(contact, index) in contactsPreview" :key="`preview-${index}`">
<div class="text-sm bg-white p-2 rounded border">
<span class="font-medium" x-text="contact.name || 'No Name'"></span> -
<span class="text-gray-600" x-text="contact.phone"></span>
<div x-show="contact.preview_message" class="text-xs text-gray-500 mt-1">
Preview: <span x-text="(contact.preview_message || '').substring(0, 50) + '...'"></span>
</div>
</div>
</template>
</div>
<div x-show="totalContacts > contactsPreview.length" class="text-xs text-blue-500 mt-2">
... and <span x-text="totalContacts - contactsPreview.length"></span> more contacts
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Use Saved List</label>
<select x-model="selectedList" @change="loadSavedList($event.target.value)" class="w-full px-3 py-2 border rounded-lg">
<option value="">-- Select a saved list --</option>
<template x-for="(list, index) in savedLists" :key="`list-${index}-${list.id || ''}`">
<option :value="list.id" x-text="`${list.name} (${list.total_contacts || 0} contacts)`"></option>
</template>
</select>
</div>
<div class="flex gap-2">
<button @click="saveTemplate()"
class="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-600">
Save Template
</button>
<button @click="testSMS()"
class="bg-yellow-500 text-white px-4 py-2 rounded hover:bg-yellow-600">
Test SMS
</button>
<button @click="startCampaign()"
:disabled="!campaignReady"
class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600 disabled:opacity-50">
Start Campaign
</button>
</div>
</div>
</div>
<!-- Campaign Status Section -->
<div class="space-y-4">
<!-- Analytics -->
<div class="border rounded-lg p-4">
<h3 class="font-semibold mb-3">Campaign Analytics</h3>
<div class="grid grid-cols-2 gap-4">
<div class="text-center">
<div class="text-2xl font-bold text-blue-600" x-text="analytics.total_sent || 0"></div>
<div class="text-sm text-gray-600">Total Sent</div>
</div>
<div class="text-center">
<div class="text-2xl font-bold text-green-600" x-text="analytics.responses || 0"></div>
<div class="text-sm text-gray-600">Responses</div>
</div>
<div class="text-center">
<div class="text-2xl font-bold text-yellow-600" x-text="analytics.follow_ups || 0"></div>
<div class="text-sm text-gray-600">Follow-ups Needed</div>
</div>
<div class="text-center">
<div class="text-2xl font-bold text-purple-600" x-text="analytics.opt_outs || 0"></div>
<div class="text-sm text-gray-600">Opt-outs</div>
</div>
</div>
</div>
<!-- Response Types -->
<div class="border rounded-lg p-4">
<h3 class="font-semibold mb-3">Response Types</h3>
<div x-show="!responseTypes.length" class="text-gray-500 text-sm">
No responses yet
</div>
<div class="space-y-2">
<template x-for="type in responseTypes" :key="type.type">
<div class="flex justify-between items-center">
<span x-text="type.type" class="text-sm"></span>
<span x-text="type.count" class="font-medium"></span>
</div>
</template>
</div>
</div>
<!-- Recent Campaigns -->
<div class="border rounded-lg p-4">
<h3 class="font-semibold mb-3">Recent Campaigns</h3>
<div x-show="!recentCampaigns.length" class="text-gray-500 text-sm">
No recent campaigns
</div>
<div class="space-y-2">
<template x-for="(campaign, index) in recentCampaigns" :key="`campaign-${index}-${campaign.id || ''}`">
<div class="border-b pb-2">
<div class="font-medium" x-text="campaign.name"></div>
<div class="text-sm text-gray-600">
<span x-text="campaign.sent_count || 0"></span> sent •
<span x-text="formatDate(campaign.created_at)"></span>
</div>
</div>
</template>
</div>
</div>
</div>
</div>
</div>
<!-- Conversations Tab -->
<div x-show="activeTab === 'conversations'" class="p-6">
<div id="conversations-container">
<!-- Enhanced conversations component will be loaded here -->
<include src="conversations_enhanced_component.html"></include>
</div>
</div>
<!-- System Testing Tab -->
<div x-show="activeTab === 'testing'" class="p-6">
<h2 class="text-xl font-semibold mb-4">🧪 System Testing & Diagnostics</h2>
<!-- Connection Tests -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<!-- Termux API Test -->
<div class="border rounded-lg p-4">
<h3 class="font-medium mb-3">📡 Termux API Test</h3>
<div class="text-sm text-gray-600 mb-3">
Endpoint: <code class="bg-gray-100 px-1">http://{{ phone_ip }}:5001</code>
</div>
<button @click="testTermuxConnection()"
:disabled="testingTermux"
class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 disabled:opacity-50 transition-colors">
<span x-show="!testingTermux">Test Termux API</span>
<span x-show="testingTermux">Testing...</span>
</button>
<div x-show="termuxTestResult" class="mt-3 p-3 rounded text-sm"
:class="termuxTestResult?.success ? 'bg-green-50 text-green-700' : 'bg-red-50 text-red-700'">
<pre x-text="JSON.stringify(termuxTestResult, null, 2)"></pre>
</div>
</div>
<!-- ADB Connection Test -->
<div class="border rounded-lg p-4">
<h3 class="font-medium mb-3">🔌 ADB Connection Test</h3>
<div class="text-sm text-gray-600 mb-3">
Device: <code class="bg-gray-100 px-1">{{ phone_ip }}:5555</code>
</div>
<button @click="testAdbConnection()"
:disabled="testingAdb"
class="bg-purple-500 text-white px-4 py-2 rounded hover:bg-purple-600 disabled:opacity-50 transition-colors">
<span x-show="!testingAdb">Test ADB</span>
<span x-show="testingAdb">Testing...</span>
</button>
<div x-show="adbTestResult" class="mt-3 p-3 rounded text-sm"
:class="adbTestResult?.success ? 'bg-green-50 text-green-700' : 'bg-red-50 text-red-700'">
<pre x-text="JSON.stringify(adbTestResult, null, 2)"></pre>
</div>
</div>
</div>
<!-- Test SMS Send -->
<div class="border rounded-lg p-4 mb-6">
<h3 class="font-medium mb-3">📨 Test SMS Send</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<input type="tel" x-model="testPhone"
placeholder="Phone number (e.g., 7801234567)"
class="border rounded px-3 py-2">
<input type="text" x-model="testMessage"
placeholder="Test message"
class="border rounded px-3 py-2">
</div>
<div class="mt-4 flex gap-2">
<button @click="sendTestSms('termux')"
:disabled="!testPhone || sendingTest"
class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600 disabled:opacity-50 transition-colors">
Send via Termux
</button>
<button @click="sendTestSms('adb')"
:disabled="!testPhone || sendingTest"
class="bg-purple-500 text-white px-4 py-2 rounded hover:bg-purple-600 disabled:opacity-50 transition-colors">
Send via ADB
</button>
<button @click="sendTestSms('auto')"
:disabled="!testPhone || sendingTest"
class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 disabled:opacity-50 transition-colors">
Auto (Best Available)
</button>
</div>
<div x-show="testSmsResult" class="mt-3 p-3 rounded text-sm"
:class="testSmsResult?.success ? 'bg-green-50 text-green-700' : 'bg-red-50 text-red-700'">
<pre x-text="JSON.stringify(testSmsResult, null, 2)"></pre>
</div>
</div>
<!-- System Information -->
<div class="border rounded-lg p-4">
<h3 class="font-medium mb-3"> System Information</h3>
<div class="grid grid-cols-2 gap-4 text-sm">
<div>
<span class="font-medium">Phone IP:</span>
<span x-text="phoneIP"></span>
</div>
<div>
<span class="font-medium">Preferred Method:</span>
<span x-text="phoneStatus.prefer_termux ? 'Termux API' : 'ADB'"></span>
</div>
<div>
<span class="font-medium">Last Check:</span>
<span x-text="formatTime(phoneStatus.last_check)"></span>
</div>
<div>
<span class="font-medium">Active Connection:</span>
<span x-text="phoneStatus.termux_connected ? 'Termux' : (phoneStatus.adb_connected ? 'ADB' : 'None')"></span>
</div>
</div>
</div>
</div>
<!-- Templates Tab -->
<div x-show="activeTab === 'templates'" class="p-6">
<div class="max-w-6xl mx-auto">
<h2 class="text-2xl font-bold text-gray-800 mb-6">📝 Message Templates</h2>
<!-- Create/Edit Template Form -->
<div class="mb-8 p-6 border rounded-lg bg-blue-50">
<h3 class="font-semibold text-gray-700 mb-4">
<span x-show="!editingTemplate">Create New Template</span>
<span x-show="editingTemplate">Edit Template</span>
</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Template Name</label>
<input type="text" x-model="templateForm.name"
placeholder="e.g., Volunteer Check-In"
class="w-full px-3 py-2 border rounded-lg">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Category</label>
<select x-model="templateForm.category" class="w-full px-3 py-2 border rounded-lg">
<option value="general">General</option>
<option value="volunteer">Volunteer</option>
<option value="reminder">Reminder</option>
<option value="gratitude">Gratitude</option>
<option value="followup">Follow-up</option>
</select>
</div>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Description</label>
<input type="text" x-model="templateForm.description"
placeholder="Brief description of when to use this template"
class="w-full px-3 py-2 border rounded-lg">
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">
Message Template <span class="text-gray-500">(Use {name} for personalization)</span>
</label>
<textarea x-model="templateForm.content"
placeholder="Hi {name}! Your message here..."
class="w-full px-3 py-2 border rounded-lg h-24"></textarea>
</div>
<div class="flex gap-2">
<button @click="saveNewTemplate()"
:disabled="!templateForm.name || !templateForm.content"
class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600 disabled:opacity-50">
<span x-show="!editingTemplate">Save Template</span>
<span x-show="editingTemplate">Update Template</span>
</button>
<button x-show="editingTemplate" @click="cancelEditTemplate()"
class="bg-gray-500 text-white px-4 py-2 rounded hover:bg-gray-600">
Cancel
</button>
</div>
</div>
<!-- Templates List -->
<div>
<h3 class="font-semibold text-gray-700 mb-4">Saved Templates</h3>
<div x-show="savedTemplates.length === 0" class="text-gray-500 text-center py-8 border rounded-lg">
No templates saved yet. Create one above to get started!
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<template x-for="template in savedTemplates" :key="template.id">
<div class="border rounded-lg p-4 hover:bg-gray-50 transition-colors">
<div class="flex justify-between items-start mb-3">
<div class="flex-1">
<div class="flex items-center gap-2 mb-2">
<h4 class="font-medium text-gray-800" x-text="template.name"></h4>
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-gray-100 text-gray-800"
x-text="template.category"></span>
<span x-show="template.is_favorite" class="text-yellow-500"></span>
</div>
<p x-show="template.description" class="text-sm text-gray-600 mb-2" x-text="template.description"></p>
</div>
</div>
<div class="text-sm text-gray-700 bg-white p-3 rounded border mb-3 italic">
"<span x-text="template.template || template.content"></span>"
</div>
<div class="flex justify-between items-center text-xs text-gray-500 mb-3">
<span>Used <span x-text="template.usage_count || 0"></span> times</span>
<span>Created <span x-text="formatDate(template.created_at)"></span></span>
</div>
<div class="flex gap-2">
<button @click="loadTemplateForEditing(template)"
class="text-blue-600 hover:text-blue-800 text-sm px-2 py-1 border border-blue-200 rounded hover:bg-blue-50">
Edit
</button>
<button @click="toggleTemplateFavorite(template)"
class="text-yellow-600 hover:text-yellow-800 text-sm px-2 py-1 border border-yellow-200 rounded hover:bg-yellow-50">
<span x-show="template.is_favorite">Unfav</span>
<span x-show="!template.is_favorite">Fav</span>
</button>
<button @click="deleteTemplate(template.id, template.name)"
class="text-red-600 hover:text-red-800 text-sm px-2 py-1 border border-red-200 rounded hover:bg-red-50">
Delete
</button>
</div>
</div>
</template>
</div>
</div>
</div>
</div>
<!-- Contact Lists Tab -->
<div x-show="activeTab === 'lists'" class="p-6">
<div class="max-w-6xl mx-auto">
<h2 class="text-2xl font-bold text-gray-800 mb-6">📋 Contact Lists</h2>
<!-- Upload CSV Section -->
<div class="mb-8 p-6 border rounded-lg bg-green-50">
<h3 class="font-semibold text-gray-700 mb-4">Upload New Contact List</h3>
<div class="flex flex-col md:flex-row gap-4">
<div class="flex-1">
<label class="block text-sm font-medium text-gray-700 mb-1">CSV File</label>
<input type="file" @change="handleListUpload($event)"
accept=".csv"
class="w-full px-3 py-2 border rounded-lg">
</div>
<div class="flex-1">
<label class="block text-sm font-medium text-gray-700 mb-1">List Name (optional)</label>
<input type="text" x-model="listUploadName"
placeholder="Leave blank for auto-generated name"
class="w-full px-3 py-2 border rounded-lg">
</div>
</div>
<div x-show="listUploadPreview.length > 0" class="mt-4 p-4 bg-white rounded border">
<h4 class="font-medium mb-2">Preview (<span x-text="listUploadPreview.length"></span> contacts)</h4>
<div class="max-h-40 overflow-y-auto space-y-2">
<template x-for="(contact, index) in listUploadPreview" :key="`preview-${index}`">
<div class="text-sm bg-gray-50 p-2 rounded border flex justify-between">
<div>
<span class="font-medium" x-text="contact.name || 'No Name'"></span>
<span class="text-gray-600 ml-2" x-text="contact.phone"></span>
</div>
</div>
</template>
</div>
<button @click="saveListFromPreview()"
class="mt-3 bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600">
Save List
</button>
</div>
</div>
<!-- Lists Display -->
<div>
<div class="flex justify-between items-center mb-4">
<h3 class="font-semibold text-gray-700">Saved Lists</h3>
<button @click="loadSavedLists()" class="text-blue-600 hover:text-blue-800 text-sm">
🔄 Refresh
</button>
</div>
<div x-show="!savedLists || savedLists.length === 0" class="text-gray-500 text-center py-8 border rounded-lg">
No contact lists saved yet. Upload a CSV file above to get started!
</div>
<div x-show="savedLists && savedLists.length > 0" class="grid grid-cols-1 gap-4">
<template x-for="list in savedLists" :key="list.id">
<div class="border rounded-lg p-4 hover:bg-gray-50 transition-colors">
<div class="flex justify-between items-start mb-3">
<div class="flex-1">
<div class="flex items-center gap-2 mb-2">
<h4 class="font-medium text-gray-800" x-text="list.name"></h4>
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-800"
x-text="`${list.total_contacts} contacts`"></span>
</div>
<p class="text-sm text-gray-600" x-text="`From: ${list.original_filename}`"></p>
</div>
</div>
<div class="flex justify-between items-center text-xs text-gray-500 mb-3">
<span>Used <span x-text="list.usage_count || 0"></span> times</span>
<span>Created <span x-text="formatDate(list.created_at)"></span></span>
<span x-show="list.last_used_at">Last used: <span x-text="formatDate(list.last_used_at)"></span></span>
</div>
<div class="flex gap-2">
<button @click="viewListContacts(list)"
class="text-blue-600 hover:text-blue-800 text-sm px-3 py-1 border border-blue-200 rounded hover:bg-blue-50">
👁 View
</button>
<button @click="useListForCampaign(list)"
class="text-green-600 hover:text-green-800 text-sm px-3 py-1 border border-green-200 rounded hover:bg-green-50">
📤 Use for Campaign
</button>
<button @click="downloadList(list)"
class="text-purple-600 hover:text-purple-800 text-sm px-3 py-1 border border-purple-200 rounded hover:bg-purple-50">
💾 Download
</button>
<button @click="deleteContactList(list.id, list.name)"
class="text-red-600 hover:text-red-800 text-sm px-3 py-1 border border-red-200 rounded hover:bg-red-50">
🗑 Delete
</button>
</div>
</div>
</template>
</div>
<!-- List Detail Modal -->
<div x-show="viewingList" x-cloak class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div class="bg-white rounded-lg p-6 max-w-4xl max-h-[80vh] w-full mx-4 overflow-hidden flex flex-col">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold">
<span x-text="viewingList ? viewingList.name : ''"></span>
<span class="text-sm font-normal text-gray-600">
(<span x-text="viewingList ? viewingList.total_contacts : 0"></span> contacts)
</span>
</h3>
<button @click="viewingList = null" class="text-gray-500 hover:text-gray-700"></button>
</div>
<div class="flex-1 overflow-y-auto">
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
<template x-for="(contact, index) in viewingListContacts" :key="`contact-${index}`">
<div class="border rounded p-3 bg-gray-50">
<div class="font-medium" x-text="contact.name || 'No Name'"></div>
<div class="text-gray-600" x-text="contact.phone"></div>
<div x-show="contact.email" class="text-gray-500 text-sm" x-text="contact.email"></div>
</div>
</template>
</div>
</div>
<div class="mt-4 pt-4 border-t flex justify-end gap-2">
<button @click="viewingList = null"
class="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600">
Close
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Load JavaScript files -->
<script src="/static/js/dashboard.js?v=2025082505"></script>
<script src="/static/js/lists.js?v=2025082505"></script>
<script src="/static/js/conversations_enhanced.js?v=2025082505"></script>
<script>
// Initialize phone IP from template
document.addEventListener('alpine:init', () => {
Alpine.data('campaignApp', () => {
const app = campaignApp();
app.phoneIP = '{{ phone_ip }}';
return app;
});
});
</script>
</body>
</html>