Phase 4: Support LID identity scheme and fix incoming message parsing
This commit is contained in:
@@ -540,13 +540,84 @@
|
||||
}
|
||||
|
||||
.gap-1 { gap: 0.25rem; }
|
||||
.gap-2 { gap: 0.5rem; }
|
||||
.gap-3 { gap: 0.75rem; }
|
||||
.gap-4 { gap: 1rem; }
|
||||
|
||||
.text-center { text-align: center; }
|
||||
.font-semibold { font-weight: 600; }
|
||||
.text-muted { color: var(--text-secondary); }
|
||||
|
||||
/* Modal Styles */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(10, 11, 20, 0.85);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1.5rem;
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
.modal-card {
|
||||
background: var(--card-bg);
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
border: 1px solid var(--card-border);
|
||||
border-radius: 16px;
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
|
||||
animation: slideDown 0.3s ease-out;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 1.25rem 1.5rem;
|
||||
border-bottom: 1px solid var(--card-border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-secondary);
|
||||
font-size: 1.5rem;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.modal-close:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 1.5rem;
|
||||
overflow-y: auto;
|
||||
max-height: 70vh;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 1rem 1.5rem;
|
||||
border-top: 1px solid var(--card-border);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 1rem;
|
||||
}
|
||||
.font-semibold { font-weight: 600; }
|
||||
.text-muted { color: var(--text-secondary); }
|
||||
</style>
|
||||
</head>
|
||||
<body x-data="app()" x-init="checkAuth()">
|
||||
@@ -651,6 +722,9 @@
|
||||
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'campaigns' }" @click="activeDashboardTab = 'campaigns'; fetchCampaigns()" id="nav-campaigns-btn">
|
||||
<span>📣</span> Marketing Campaigns
|
||||
</button>
|
||||
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'chatbot' }" @click="activeDashboardTab = 'chatbot'; fetchChatbotSettings()" id="nav-chatbot-btn">
|
||||
<span>🤖</span> AI Chatbot Settings
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Right Dashboard Panels -->
|
||||
@@ -806,7 +880,7 @@
|
||||
<div class="panel" x-show="activeDashboardTab === 'templates'" id="panel-templates">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.5rem;">
|
||||
<h2 style="font-size: 1.4rem;">Templates</h2>
|
||||
<button class="btn btn-primary" style="width: auto; padding: 0.5rem 1rem; font-size: 0.85rem;" id="btn-add-template">+ New Template</button>
|
||||
<button class="btn btn-primary" style="width: auto; padding: 0.5rem 1rem; font-size: 0.85rem;" @click="openNewTemplateModal()" id="btn-add-template">+ New Template</button>
|
||||
</div>
|
||||
|
||||
<div class="data-table-container">
|
||||
@@ -840,7 +914,7 @@
|
||||
<div class="panel" x-show="activeDashboardTab === 'campaigns'" id="panel-campaigns">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.5rem;">
|
||||
<h2 style="font-size: 1.4rem;">Campaigns</h2>
|
||||
<button class="btn btn-primary" style="width: auto; padding: 0.5rem 1rem; font-size: 0.85rem;" id="btn-add-campaign">+ Launch Campaign</button>
|
||||
<button class="btn btn-primary" style="width: auto; padding: 0.5rem 1rem; font-size: 0.85rem;" @click="openLaunchCampaignModal()" id="btn-add-campaign">+ Launch Campaign</button>
|
||||
</div>
|
||||
|
||||
<div class="data-table-container">
|
||||
@@ -872,8 +946,189 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Panel: AI Chatbot Settings -->
|
||||
<div class="panel" x-show="activeDashboardTab === 'chatbot'" id="panel-chatbot">
|
||||
<h2 style="font-size: 1.4rem; margin-bottom: 1.5rem;">AI Chatbot & Auto-Reply Settings</h2>
|
||||
|
||||
<form @submit.prevent="saveChatbotSettings()" id="chatbot-form">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Enable Chatbot</label>
|
||||
<select class="form-input" x-model="chatbotSettings.is_active" id="chatbot-active-select">
|
||||
<option value="1">Active</option>
|
||||
<option value="0">Disabled</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Auto-Reply Type</label>
|
||||
<select class="form-input" x-model="chatbotSettings.trigger_type" id="chatbot-trigger-type-select">
|
||||
<option value="keyword">Keyword-Match (Static reply)</option>
|
||||
<option value="gemini_ai">Gemini AI (Dynamic conversational responder)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group" x-show="chatbotSettings.trigger_type === 'keyword'" id="chatbot-keyword-group">
|
||||
<label class="form-label">Trigger Keywords (Comma separated)</label>
|
||||
<input type="text" class="form-input" x-model="chatbotSettings.keyword" placeholder="hello, price, discount, support" id="chatbot-keyword-input">
|
||||
</div>
|
||||
|
||||
<div class="form-group" x-show="chatbotSettings.trigger_type === 'gemini_ai'" id="chatbot-api-key-group">
|
||||
<label class="form-label">Google Gemini API Key</label>
|
||||
<input type="password" class="form-input" x-model="chatbotSettings.gemini_api_key" placeholder="••••••••" id="chatbot-api-key-input">
|
||||
<span style="font-size: 0.75rem; color: var(--text-secondary); display: block; margin-top: 0.25rem;">
|
||||
Leave empty to use the system default API key configured in .env.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="chatbot-prompt-group">
|
||||
<label class="form-label" x-text="chatbotSettings.trigger_type === 'gemini_ai' ? 'System Instruction Prompt' : 'Predefined Auto-Reply Message'"></label>
|
||||
<textarea class="form-input" x-model="chatbotSettings.ai_prompt" rows="5" required :placeholder="chatbotSettings.trigger_type === 'gemini_ai' ? 'You are a helpful customer support assistant... Respond concisely and politely in Arabic.' : 'Thank you for reaching out!'" id="chatbot-prompt-input"></textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary" :disabled="actionLoading" id="chatbot-save-btn">
|
||||
<span x-show="!actionLoading">Save Settings</span>
|
||||
<span x-show="actionLoading" class="spinner"></span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Add Contact -->
|
||||
<div class="modal-overlay" x-show="showAddContactModal" id="modal-add-contact" style="display: none;">
|
||||
<div class="modal-card">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">Add New Contact</h3>
|
||||
<button class="modal-close" @click="showAddContactModal = false">×</button>
|
||||
</div>
|
||||
<form @submit.prevent="submitAddContact()" id="form-add-contact">
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Name</label>
|
||||
<input type="text" class="form-input" x-model="contactName" required placeholder="John Doe" id="add-contact-name">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Phone Number (with Country Code)</label>
|
||||
<input type="text" class="form-input" x-model="contactPhone" required placeholder="966500000000" id="add-contact-phone">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Email</label>
|
||||
<input type="email" class="form-input" x-model="contactEmail" placeholder="john@example.com" id="add-contact-email">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Notes</label>
|
||||
<textarea class="form-input" x-model="contactNotes" placeholder="Additional details..." id="add-contact-notes"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" style="width: auto;" @click="showAddContactModal = false">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary" style="width: auto;" :disabled="actionLoading">
|
||||
<span x-show="!actionLoading">Create Contact</span>
|
||||
<span x-show="actionLoading" class="spinner"></span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal: New Template -->
|
||||
<div class="modal-overlay" x-show="showNewTemplateModal" id="modal-new-template" style="display: none;">
|
||||
<div class="modal-card">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">Create Message Template</h3>
|
||||
<button class="modal-close" @click="showNewTemplateModal = false">×</button>
|
||||
</div>
|
||||
<form @submit.prevent="submitNewTemplate()" id="form-new-template">
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Template Name</label>
|
||||
<input type="text" class="form-input" x-model="templateName" required placeholder="welcome_message" id="new-template-name">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Message Body</label>
|
||||
<textarea class="form-input" x-model="templateBody" rows="4" required placeholder="Hello {{name}}, welcome to Nabeh!" id="new-template-body"></textarea>
|
||||
<span style="font-size: 0.75rem; color: var(--text-secondary); display: block; margin-top: 0.25rem;">
|
||||
Use {{name}} to personalize with the contact's name.
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Template Type</label>
|
||||
<select class="form-input" x-model="templateType" id="new-template-type">
|
||||
<option value="text">Text Only</option>
|
||||
<option value="image">Image Attachment</option>
|
||||
<option value="video">Video Attachment</option>
|
||||
<option value="document">Document Attachment</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" x-show="templateType !== 'text'" id="new-template-media-group">
|
||||
<label class="form-label">Media URL</label>
|
||||
<input type="url" class="form-input" x-model="templateMediaUrl" placeholder="https://example.com/image.jpg" id="new-template-media-url">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" style="width: auto;" @click="showNewTemplateModal = false">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary" style="width: auto;" :disabled="actionLoading">
|
||||
<span x-show="!actionLoading">Create Template</span>
|
||||
<span x-show="actionLoading" class="spinner"></span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Launch Campaign -->
|
||||
<div class="modal-overlay" x-show="showLaunchCampaignModal" id="modal-launch-campaign" style="display: none;">
|
||||
<div class="modal-card">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">Launch Marketing Campaign</h3>
|
||||
<button class="modal-close" @click="showLaunchCampaignModal = false">×</button>
|
||||
</div>
|
||||
<form @submit.prevent="submitLaunchCampaign()" id="form-launch-campaign">
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label class="form-label">Campaign Name</label>
|
||||
<input type="text" class="form-input" x-model="campaignName" required placeholder="Ramadan Campaign 2026" id="launch-campaign-name">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Target Group</label>
|
||||
<select class="form-input" x-model="campaignGroupId" required id="launch-campaign-group">
|
||||
<option value="">-- Select Contact Group --</option>
|
||||
<template x-for="grp in groups" :key="grp.id">
|
||||
<option :value="grp.id" x-text="grp.name"></option>
|
||||
</template>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">WhatsApp Session Sender</label>
|
||||
<select class="form-input" x-model="campaignSessionId" required id="launch-campaign-session">
|
||||
<option value="">-- Select Sender Number --</option>
|
||||
<template x-if="whatsappSession">
|
||||
<option :value="whatsappSession.id" x-text="whatsappSession.name + ' (' + (whatsappSession.phone || 'Connected') + ')'"></option>
|
||||
</template>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Message Template</label>
|
||||
<select class="form-input" x-model="campaignTemplateId" required id="launch-campaign-template">
|
||||
<option value="">-- Select Template --</option>
|
||||
<template x-for="tpl in templates" :key="tpl.id">
|
||||
<option :value="tpl.id" x-text="tpl.name"></option>
|
||||
</template>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" style="width: auto;" @click="showLaunchCampaignModal = false">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary" style="width: auto;" :disabled="actionLoading">
|
||||
<span x-show="!actionLoading">Launch Broadcast</span>
|
||||
<span x-show="actionLoading" class="spinner"></span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user