Deploy: 2026-05-22 02:09:48

This commit is contained in:
Hamza-Ayed
2026-05-22 02:09:48 +03:00
parent e5c6c54ca0
commit f793092a17
7 changed files with 637 additions and 17 deletions

View File

@@ -4,6 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nabeh Hub — WhatsApp Gateway & CRM</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<meta name="description" content="Connect, manage, and scale your WhatsApp communication.">
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
@@ -636,7 +637,10 @@
<div class="auth-wrapper">
<div class="auth-card">
<div class="brand-header">
<div class="brand-logo" id="main-brand-logo">Nabeh</div>
<div class="brand-logo" id="main-brand-logo" style="display: flex; align-items: center; justify-content: center; gap: 12px; margin-bottom: 0.75rem;">
<img src="/logo.svg" alt="Nabeh Logo" style="width: 48px; height: 48px; border-radius: 12px; box-shadow: 0 0 15px rgba(6, 182, 212, 0.4);">
<span style="font-family: 'Outfit', sans-serif; font-size: 2.2rem; font-weight: 800; background: linear-gradient(135deg, #fff 30%, var(--primary-accent) 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; letter-spacing: -0.05em;">Nabeh</span>
</div>
<div class="brand-subtitle">WhatsApp Hub & Marketing Automation</div>
</div>
@@ -704,10 +708,13 @@
<!-- Main Dashboard View -->
<template x-if="isLoggedIn">
<div>
<div class="dashboard-header">
<div>
<h1 style="font-size: 1.8rem; margin-bottom: 0.25rem;">Nabeh Dashboard</h1>
<p class="text-muted" style="font-size: 0.9rem;">Connected to system: <span class="font-semibold" x-text="user.name"></span></p>
<div class="dashboard-header" style="display: flex; align-items: center; justify-content: space-between;">
<div style="display: flex; align-items: center; gap: 16px;">
<img src="/logo.svg" alt="Nabeh Logo" style="width: 52px; height: 52px; border-radius: 12px; box-shadow: 0 0 15px rgba(6, 182, 212, 0.35); border: 1px solid rgba(255, 255, 255, 0.08);">
<div>
<h1 style="font-size: 1.8rem; margin: 0 0 0.15rem 0;">Nabeh Dashboard</h1>
<p class="text-muted" style="font-size: 0.9rem; margin: 0;">Connected to system: <span class="font-semibold" x-text="user.name"></span></p>
</div>
</div>
<div class="user-info">
<div class="avatar" x-text="user.name.charAt(0).toUpperCase()"></div>
@@ -733,6 +740,9 @@
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'chatbot' }" @click="activeDashboardTab = 'chatbot'; fetchChatbotSettings()" id="nav-chatbot-btn">
<span>🤖</span> AI Chatbot Settings
</button>
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'integrations' }" @click="activeDashboardTab = 'integrations'; fetchEndpoints()" id="nav-integrations-btn">
<span>🔌</span> API Integrations
</button>
</div>
<!-- Right Dashboard Panels -->
@@ -1057,6 +1067,53 @@
</form>
</div>
<!-- Panel: API Integrations -->
<div class="panel" x-show="activeDashboardTab === 'integrations'" id="panel-integrations">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;">
<h2 style="font-size: 1.4rem; margin: 0;">API Endpoints Integration</h2>
<button class="btn btn-primary" style="width: auto;" @click="endpointForm = { id: null, name: '', endpoint_url: '', action_type: 'verify_payment', description: '', headers: '' }; showAddEndpointModal = true" id="add-endpoint-btn">
+ Add API Integration
</button>
</div>
<p class="text-muted" style="margin-bottom: 1.5rem; font-size: 0.9rem;">
Configure external web APIs for multi-tenant integrations. The chatbot can fetch user profiles or verify payment details dynamically.
</p>
<div class="data-table-container">
<table class="data-table">
<thead>
<tr>
<th>Integration Name</th>
<th>Action Type</th>
<th>Endpoint URL</th>
<th>Description</th>
<th style="width: 150px; text-align: center;">Actions</th>
</tr>
</thead>
<tbody>
<template x-for="endpoint in endpoints" :key="endpoint.id">
<tr>
<td class="font-semibold" x-text="endpoint.name"></td>
<td>
<span class="status-badge" :class="endpoint.action_type === 'verify_payment' ? 'badge-waiting_qr' : 'badge-connecting'" style="margin: 0; padding: 0.2rem 0.5rem; font-size: 0.75rem;" x-text="endpoint.action_type === 'verify_payment' ? 'Verify Payment' : 'Fetch User Info'"></span>
</td>
<td style="font-family: monospace; font-size: 0.85rem;" x-text="endpoint.endpoint_url"></td>
<td x-text="endpoint.description || 'No description provided.'"></td>
<td style="text-align: center; display: flex; gap: 0.5rem; justify-content: center; align-items: center;">
<button class="btn btn-secondary" style="width: auto; padding: 0.4rem 0.8rem; font-size: 0.8rem;" @click="editEndpoint(endpoint)" :id="'edit-endpoint-btn-' + endpoint.id">Edit</button>
<button class="btn btn-danger" style="width: auto; padding: 0.4rem 0.8rem; font-size: 0.8rem;" @click="deleteEndpoint(endpoint.id)" :id="'delete-endpoint-btn-' + endpoint.id">Delete</button>
</td>
</tr>
</template>
</tbody>
</table>
<template x-if="endpoints.length === 0">
<div class="empty-state" id="empty-endpoints-state">No API endpoints configured yet. Connect Intaleq or Salla integrations.</div>
</template>
</div>
</div>
</div>
</div>
@@ -1097,6 +1154,53 @@
</div>
</div>
<!-- Modal: Add/Edit Endpoint Integration -->
<div class="modal-overlay" x-show="showAddEndpointModal" id="modal-add-endpoint" style="display: none;">
<div class="modal-card">
<div class="modal-header">
<h3 class="modal-title" x-text="endpointForm.id ? 'Edit API Endpoint Integration' : 'Add API Endpoint Integration'"></h3>
<button class="modal-close" @click="showAddEndpointModal = false">&times;</button>
</div>
<form @submit.prevent="submitAddEndpoint()" id="form-add-endpoint">
<div class="modal-body">
<div class="form-group">
<label class="form-label">Integration Name</label>
<input type="text" class="form-input" x-model="endpointForm.name" required placeholder="e.g. Intaleq Driver Lookup" id="add-endpoint-name">
</div>
<div class="form-group">
<label class="form-label">Endpoint URL</label>
<input type="url" class="form-input" x-model="endpointForm.endpoint_url" required placeholder="https://yourdomain.com/api/nabeh/action" id="add-endpoint-url">
</div>
<div class="form-group">
<label class="form-label">Action Type</label>
<select class="form-input" x-model="endpointForm.action_type" required id="add-endpoint-action-type">
<option value="verify_payment">Verify Payment (تحليل وتأكيد الدفع)</option>
<option value="fetch_user_info">Fetch User Info (جلب معلومات السائق أو العميل)</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Description</label>
<textarea class="form-input" x-model="endpointForm.description" placeholder="A brief explanation of what this endpoint does..." id="add-endpoint-description"></textarea>
</div>
<div class="form-group">
<label class="form-label">Custom HTTP Headers (JSON Object)</label>
<textarea class="form-input" style="font-family: monospace; font-size: 0.85rem; height: 120px;" x-model="endpointForm.headers" placeholder='{&#10; "Authorization": "Bearer YOUR_SECRET_TOKEN",&#10; "X-Custom-Source": "Nabeh App"&#10;}' id="add-endpoint-headers"></textarea>
<span style="font-size: 0.75rem; color: var(--text-secondary); display: block; margin-top: 0.25rem;">
Optionally define any authorization tokens or custom headers in JSON format.
</span>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" style="width: auto;" @click="showAddEndpointModal = false">Cancel</button>
<button type="submit" class="btn btn-primary" style="width: auto;" :disabled="actionLoading">
<span x-show="!actionLoading" x-text="endpointForm.id ? 'Save Changes' : 'Create Integration'"></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">
@@ -1231,6 +1335,18 @@
showAddContactModal: false,
showNewTemplateModal: false,
showLaunchCampaignModal: false,
showAddEndpointModal: false,
// Endpoint Form
endpointForm: {
id: null,
name: '',
endpoint_url: '',
action_type: 'verify_payment',
description: '',
headers: ''
},
endpoints: [],
// Forms
contactName: '',
@@ -1358,6 +1474,7 @@
this.contacts = [];
this.templates = [];
this.campaigns = [];
this.endpoints = [];
},
initializeDashboard() {
@@ -1471,6 +1588,94 @@
}
},
// Custom API Integrations CRUD
async fetchEndpoints() {
try {
const response = await fetch('/api/endpoints', {
headers: { 'Authorization': `Bearer ${this.token}` }
});
const data = await response.json();
if (response.ok) {
this.endpoints = data.data || [];
}
} catch (err) {
console.error('Error fetching endpoints:', err);
}
},
async submitAddEndpoint() {
this.actionLoading = true;
try {
if (this.endpointForm.headers && this.endpointForm.headers.trim()) {
try {
JSON.parse(this.endpointForm.headers);
} catch (jsonErr) {
alert('Invalid JSON in Custom Headers. Please verify format.');
this.actionLoading = false;
return;
}
}
const response = await fetch('/api/endpoints', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`
},
body: JSON.stringify(this.endpointForm)
});
const data = await response.json();
if (response.ok) {
this.showAddEndpointModal = false;
this.endpointForm = { id: null, name: '', endpoint_url: '', action_type: 'verify_payment', description: '', headers: '' };
await this.fetchEndpoints();
} else {
alert(data.message || 'Failed to save integration endpoint.');
}
} catch (err) {
console.error('Error saving endpoint:', err);
} finally {
this.actionLoading = false;
}
},
editEndpoint(endpoint) {
this.endpointForm = {
id: endpoint.id,
name: endpoint.name,
endpoint_url: endpoint.endpoint_url,
action_type: endpoint.action_type,
description: endpoint.description || '',
headers: endpoint.headers || ''
};
this.showAddEndpointModal = true;
},
async deleteEndpoint(id) {
if (!confirm('Are you sure you want to delete this integration endpoint?')) return;
this.actionLoading = true;
try {
const response = await fetch('/api/endpoints', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`
},
body: JSON.stringify({ id: id })
});
const data = await response.json();
if (response.ok) {
await this.fetchEndpoints();
} else {
alert(data.message || 'Failed to delete endpoint.');
}
} catch (err) {
console.error('Error deleting endpoint:', err);
} finally {
this.actionLoading = false;
}
},
// CRM List Fetchers
async fetchContacts() {
this.selectedContactIds = [];