Deploy: 2026-05-22 02:09:48
This commit is contained in:
@@ -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">×</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='{ "Authorization": "Bearer YOUR_SECRET_TOKEN", "X-Custom-Source": "Nabeh App" }' 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 = [];
|
||||
|
||||
Reference in New Issue
Block a user