Phase 4: Support LID identity scheme and fix incoming message parsing

This commit is contained in:
Hamza-Ayed
2026-05-21 23:44:25 +03:00
parent 0a1752ce44
commit ec7c9ffc29
9 changed files with 790 additions and 20 deletions

View File

@@ -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">&times;</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">&times;</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">&times;</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>