Deploy: 2026-05-22 02:16:46

This commit is contained in:
Hamza-Ayed
2026-05-22 02:16:46 +03:00
parent f793092a17
commit 479aedcbcf
4 changed files with 128 additions and 68 deletions

View File

@@ -43,6 +43,7 @@ class EndpointController extends BaseController
'endpoint_url' => $body['endpoint_url'], 'endpoint_url' => $body['endpoint_url'],
'action_type' => $body['action_type'], 'action_type' => $body['action_type'],
'description' => $body['description'] ?? null, 'description' => $body['description'] ?? null,
'api_key' => $body['api_key'] ?? null,
'headers' => $body['headers'] ?? null 'headers' => $body['headers'] ?? null
]; ];

View File

@@ -488,13 +488,19 @@ class WhatsAppController extends BaseController
]); ]);
$headers = ['Content-Type: application/json']; $headers = ['Content-Type: application/json'];
if ($endpoint && !empty($endpoint['headers'])) { if ($endpoint) {
if (!empty($endpoint['api_key'])) {
$headers[] = 'X-API-Key: ' . $endpoint['api_key'];
$headers[] = 'Authorization: Bearer ' . $endpoint['api_key'];
}
if (!empty($endpoint['headers'])) {
$customHeaders = json_decode($endpoint['headers'], true); $customHeaders = json_decode($endpoint['headers'], true);
if (is_array($customHeaders)) { if (is_array($customHeaders)) {
foreach ($customHeaders as $key => $value) { foreach ($customHeaders as $key => $value) {
$headers[] = "$key: $value"; $headers[] = "$key: $value";
} }
} }
}
} else { } else {
$headers[] = 'X-API-Key: ' . (getenv('ENTALEQ_API_KEY') ?: 'mock-key'); $headers[] = 'X-API-Key: ' . (getenv('ENTALEQ_API_KEY') ?: 'mock-key');
} }
@@ -540,6 +546,10 @@ class WhatsAppController extends BaseController
]); ]);
$headers = ['Content-Type: application/json']; $headers = ['Content-Type: application/json'];
if (!empty($endpoint['api_key'])) {
$headers[] = 'X-API-Key: ' . $endpoint['api_key'];
$headers[] = 'Authorization: Bearer ' . $endpoint['api_key'];
}
if (!empty($endpoint['headers'])) { if (!empty($endpoint['headers'])) {
$customHeaders = json_decode($endpoint['headers'], true); $customHeaders = json_decode($endpoint['headers'], true);
if (is_array($customHeaders)) { if (is_array($customHeaders)) {

View File

@@ -69,6 +69,7 @@ class CompanyEndpoint extends BaseModel
`endpoint_url` VARCHAR(512) NOT NULL, `endpoint_url` VARCHAR(512) NOT NULL,
`action_type` VARCHAR(100) NOT NULL, `action_type` VARCHAR(100) NOT NULL,
`description` TEXT NULL, `description` TEXT NULL,
`api_key` VARCHAR(255) NULL,
`headers` TEXT NULL, `headers` TEXT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
@@ -76,6 +77,14 @@ class CompanyEndpoint extends BaseModel
INDEX `idx_endpoint_action` (`action_type`) INDEX `idx_endpoint_action` (`action_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
"); ");
// Auto-migration: check if api_key column exists, if not, add it
try {
Database::execute("SELECT `api_key` FROM `company_endpoints` LIMIT 1");
} catch (\Exception $colException) {
Database::execute("ALTER TABLE `company_endpoints` ADD COLUMN `api_key` VARCHAR(255) NULL AFTER `description`");
}
$checked = true; $checked = true;
} catch (\Exception $e) { } catch (\Exception $e) {
error_log("Failed to ensure company_endpoints table: " . $e->getMessage()); error_log("Failed to ensure company_endpoints table: " . $e->getMessage());

View File

@@ -627,9 +627,37 @@
.recording-pulse { .recording-pulse {
animation: pulse-red 1.2s infinite; animation: pulse-red 1.2s infinite;
} }
/* RTL Overrides */
body[dir="rtl"] .nav-item {
text-align: right;
flex-direction: row;
}
body[dir="rtl"] .data-table {
text-align: right;
}
body[dir="rtl"] .modal-header {
flex-direction: row-reverse;
}
body[dir="rtl"] .form-group {
text-align: right;
}
body[dir="rtl"] .form-label {
text-align: right;
display: block;
}
body[dir="rtl"] .dashboard-header {
flex-direction: row-reverse;
}
body[dir="rtl"] .user-info {
flex-direction: row-reverse;
}
body[dir="rtl"] .modal-footer {
flex-direction: row-reverse;
}
</style> </style>
</head> </head>
<body x-data="app()" x-init="checkAuth()"> <body x-data="app()" x-init="checkAuth()" :dir="lang === 'ar' ? 'rtl' : 'ltr'">
<div class="app-container"> <div class="app-container">
<!-- Auth View --> <!-- Auth View -->
@@ -708,17 +736,23 @@
<!-- Main Dashboard View --> <!-- Main Dashboard View -->
<template x-if="isLoggedIn"> <template x-if="isLoggedIn">
<div> <div>
<div class="dashboard-header" style="display: flex; align-items: center; justify-content: space-between;"> <div class="dashboard-header" style="display: flex; align-items: center; justify-content: space-between;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
<div style="display: flex; align-items: center; gap: 16px;"> <div style="display: flex; align-items: center; gap: 16px;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
<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);"> <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> <div :style="lang === 'ar' ? 'text-align: right;' : ''">
<h1 style="font-size: 1.8rem; margin: 0 0 0.15rem 0;">Nabeh Dashboard</h1> <h1 style="font-size: 1.8rem; margin: 0 0 0.15rem 0;" x-text="lang === 'ar' ? 'لوحة تحكم نبيه' : '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> <p class="text-muted" style="font-size: 0.9rem; margin: 0;">
<span x-text="lang === 'ar' ? 'متصل بالنظام باسم:' : 'Connected to system:'"></span>
<span class="font-semibold" x-text="user.name"></span>
</p>
</div> </div>
</div> </div>
<div class="user-info"> <div class="user-info" style="display: flex; align-items: center; gap: 12px;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
<button @click="toggleLang()" class="btn btn-secondary" style="width: auto; padding: 0.5rem 1rem; font-size: 0.85rem;" id="lang-toggle-btn">
<span x-text="lang === 'ar' ? 'English 🇬🇧' : 'العربية 🇸🇦'"></span>
</button>
<div class="avatar" x-text="user.name.charAt(0).toUpperCase()"></div> <div class="avatar" x-text="user.name.charAt(0).toUpperCase()"></div>
<button @click="logout()" class="btn btn-secondary" style="width: auto; padding: 0.5rem 1rem; font-size: 0.85rem;" id="logout-btn">Log Out</button> <button @click="logout()" class="btn btn-secondary" style="width: auto; padding: 0.5rem 1rem; font-size: 0.85rem;" id="logout-btn" x-text="lang === 'ar' ? 'تسجيل الخروج' : 'Log Out'"></button>
</div> </div>
</div> </div>
@@ -726,22 +760,22 @@
<!-- Left Sidebar Nav --> <!-- Left Sidebar Nav -->
<div class="nav-menu"> <div class="nav-menu">
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'whatsapp' }" @click="activeDashboardTab = 'whatsapp'" id="nav-whatsapp-btn"> <button class="nav-item" :class="{ 'active': activeDashboardTab === 'whatsapp' }" @click="activeDashboardTab = 'whatsapp'" id="nav-whatsapp-btn">
<span>📱</span> WhatsApp Connection <span>📱</span> <span x-text="lang === 'ar' ? 'اتصال الواتساب' : 'WhatsApp Connection'"></span>
</button> </button>
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'contacts' }" @click="activeDashboardTab = 'contacts'; fetchContacts(); fetchGroups()" id="nav-contacts-btn"> <button class="nav-item" :class="{ 'active': activeDashboardTab === 'contacts' }" @click="activeDashboardTab = 'contacts'; fetchContacts(); fetchGroups()" id="nav-contacts-btn">
<span>👥</span> Contacts Directory <span>👥</span> <span x-text="lang === 'ar' ? 'دليل جهات الاتصال' : 'Contacts Directory'"></span>
</button> </button>
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'templates' }" @click="activeDashboardTab = 'templates'; fetchTemplates()" id="nav-templates-btn"> <button class="nav-item" :class="{ 'active': activeDashboardTab === 'templates' }" @click="activeDashboardTab = 'templates'; fetchTemplates()" id="nav-templates-btn">
<span>📝</span> Message Templates <span>📝</span> <span x-text="lang === 'ar' ? 'قوالب الرسائل' : 'Message Templates'"></span>
</button> </button>
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'campaigns' }" @click="activeDashboardTab = 'campaigns'; fetchCampaigns()" id="nav-campaigns-btn"> <button class="nav-item" :class="{ 'active': activeDashboardTab === 'campaigns' }" @click="activeDashboardTab = 'campaigns'; fetchCampaigns()" id="nav-campaigns-btn">
<span>📣</span> Marketing Campaigns <span>📣</span> <span x-text="lang === 'ar' ? 'الحملات التسويقية' : 'Marketing Campaigns'"></span>
</button> </button>
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'chatbot' }" @click="activeDashboardTab = 'chatbot'; fetchChatbotSettings()" id="nav-chatbot-btn"> <button class="nav-item" :class="{ 'active': activeDashboardTab === 'chatbot' }" @click="activeDashboardTab = 'chatbot'; fetchChatbotSettings()" id="nav-chatbot-btn">
<span>🤖</span> AI Chatbot Settings <span>🤖</span> <span x-text="lang === 'ar' ? 'روبوت الذكاء الاصطناعي' : 'AI Chatbot Settings'"></span>
</button> </button>
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'integrations' }" @click="activeDashboardTab = 'integrations'; fetchEndpoints()" id="nav-integrations-btn"> <button class="nav-item" :class="{ 'active': activeDashboardTab === 'integrations' }" @click="activeDashboardTab = 'integrations'; fetchEndpoints()" id="nav-integrations-btn">
<span>🔌</span> API Integrations <span>🔌</span> <span>ربط تطبيق نبيه بمشروعك (API Integrations)</span>
</button> </button>
</div> </div>
@@ -1069,26 +1103,25 @@
<!-- Panel: API Integrations --> <!-- Panel: API Integrations -->
<div class="panel" x-show="activeDashboardTab === 'integrations'" id="panel-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;"> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
<h2 style="font-size: 1.4rem; margin: 0;">API Endpoints Integration</h2> <h2 style="font-size: 1.4rem; margin: 0;" x-text="lang === 'ar' ? 'ربط تطبيق نبيه بمشروعك (API Integrations)' : '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"> <button class="btn btn-primary" style="width: auto;" @click="endpointForm = { id: null, name: '', endpoint_url: '', action_type: 'verify_payment', description: '', api_key: '' }; showAddEndpointModal = true" id="add-endpoint-btn">
+ Add API Integration <span x-text="lang === 'ar' ? '+ إضافة ربط برمجي' : '+ Add API Integration'"></span>
</button> </button>
</div> </div>
<p class="text-muted" style="margin-bottom: 1.5rem; font-size: 0.9rem;"> <p class="text-muted" style="margin-bottom: 1.5rem; font-size: 0.9rem;" x-text="lang === 'ar' ? 'قم بتهيئة واجهات برمجة التطبيقات الخارجية (Web APIs) للربط البرمجي بمشروعك. يمكن للروبوت جلب الملفات التعريفية للمستخدمين أو التحقق من تفاصيل الدفع ديناميكيًا.' : 'Configure external web APIs for multi-tenant integrations. The chatbot can fetch user profiles or verify payment details dynamically.'">
Configure external web APIs for multi-tenant integrations. The chatbot can fetch user profiles or verify payment details dynamically.
</p> </p>
<div class="data-table-container"> <div class="data-table-container">
<table class="data-table"> <table class="data-table">
<thead> <thead>
<tr> <tr>
<th>Integration Name</th> <th x-text="lang === 'ar' ? 'اسم الربط' : 'Integration Name'"></th>
<th>Action Type</th> <th x-text="lang === 'ar' ? 'نوع الإجراء' : 'Action Type'"></th>
<th>Endpoint URL</th> <th x-text="lang === 'ar' ? 'رابط نقطة النهاية (Endpoint URL)' : 'Endpoint URL'"></th>
<th>Description</th> <th x-text="lang === 'ar' ? 'الوصف' : 'Description'"></th>
<th style="width: 150px; text-align: center;">Actions</th> <th style="width: 150px; text-align: center;" x-text="lang === 'ar' ? 'الخيارات' : 'Actions'"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -1096,20 +1129,20 @@
<tr> <tr>
<td class="font-semibold" x-text="endpoint.name"></td> <td class="font-semibold" x-text="endpoint.name"></td>
<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> <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' ? (lang === 'ar' ? 'تأكيد الدفع' : 'Verify Payment') : (endpoint.action_type === 'fetch_user_info' ? (lang === 'ar' ? 'جلب بيانات العميل' : 'Fetch User Info') : endpoint.action_type)"></span>
</td> </td>
<td style="font-family: monospace; font-size: 0.85rem;" x-text="endpoint.endpoint_url"></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 x-text="endpoint.description || (lang === 'ar' ? 'لا يوجد وصف.' : 'No description provided.')"></td>
<td style="text-align: center; display: flex; gap: 0.5rem; justify-content: center; align-items: center;"> <td style="text-align: center; display: flex; gap: 0.5rem; justify-content: center; align-items: center;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
<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-secondary" style="width: auto; padding: 0.4rem 0.8rem; font-size: 0.8rem;" @click="editEndpoint(endpoint)" :id="'edit-endpoint-btn-' + endpoint.id" x-text="lang === 'ar' ? 'تعديل' : '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> <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" x-text="lang === 'ar' ? 'حذف' : 'Delete'"></button>
</td> </td>
</tr> </tr>
</template> </template>
</tbody> </tbody>
</table> </table>
<template x-if="endpoints.length === 0"> <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> <div class="empty-state" id="empty-endpoints-state" x-text="lang === 'ar' ? 'لم يتم تكوين نقاط نهاية واجهة برمجة تطبيقات (API Endpoints) بعد.' : 'No API endpoints configured yet. Connect Intaleq or Salla integrations.'"></div>
</template> </template>
</div> </div>
</div> </div>
@@ -1157,43 +1190,49 @@
<!-- Modal: Add/Edit Endpoint Integration --> <!-- Modal: Add/Edit Endpoint Integration -->
<div class="modal-overlay" x-show="showAddEndpointModal" id="modal-add-endpoint" style="display: none;"> <div class="modal-overlay" x-show="showAddEndpointModal" id="modal-add-endpoint" style="display: none;">
<div class="modal-card"> <div class="modal-card">
<div class="modal-header"> <div class="modal-header" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
<h3 class="modal-title" x-text="endpointForm.id ? 'Edit API Endpoint Integration' : 'Add API Endpoint Integration'"></h3> <h3 class="modal-title" x-text="endpointForm.id ? (lang === 'ar' ? 'تعديل ربط تطبيق نبيه بمشروعك' : 'Edit API Endpoint Integration') : (lang === 'ar' ? 'إضافة ربط تطبيق نبيه بمشروعك' : 'Add API Endpoint Integration')"></h3>
<button class="modal-close" @click="showAddEndpointModal = false">&times;</button> <button class="modal-close" @click="showAddEndpointModal = false">&times;</button>
</div> </div>
<form @submit.prevent="submitAddEndpoint()" id="form-add-endpoint"> <form @submit.prevent="submitAddEndpoint()" id="form-add-endpoint">
<div class="modal-body"> <div class="modal-body">
<div class="form-group"> <div class="form-group">
<label class="form-label">Integration Name</label> <label class="form-label" x-text="lang === 'ar' ? 'اسم الربط' : 'Integration Name'"></label>
<input type="text" class="form-input" x-model="endpointForm.name" required placeholder="e.g. Intaleq Driver Lookup" id="add-endpoint-name"> <input type="text" class="form-input" x-model="endpointForm.name" required placeholder="e.g. Intaleq Driver Lookup" id="add-endpoint-name">
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">Endpoint URL</label> <label class="form-label" x-text="lang === 'ar' ? 'رابط نقطة النهاية (Endpoint URL)' : '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"> <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>
<div class="form-group"> <div class="form-group">
<label class="form-label">Action Type</label> <label class="form-label" x-text="lang === 'ar' ? 'نوع الإجراء (Action Type)' : 'Action Type'"></label>
<select class="form-input" x-model="endpointForm.action_type" required id="add-endpoint-action-type"> <input type="text" class="form-input" x-model="endpointForm.action_type" required placeholder="e.g. verify_payment" id="add-endpoint-action-type">
<option value="verify_payment">Verify Payment (تحليل وتأكيد الدفع)</option>
<option value="fetch_user_info">Fetch User Info (جلب معلومات السائق أو العميل)</option> <!-- Shortcuts/Hint Tags -->
</select> <div style="display: flex; gap: 0.5rem; margin-top: 0.5rem; flex-wrap: wrap;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
<button type="button" class="btn btn-secondary" style="width: auto; padding: 0.25rem 0.5rem; font-size: 0.75rem; border-radius: 4px;" @click="endpointForm.action_type = 'verify_payment'">
verify_payment (تحليل وتأكيد الدفع)
</button>
<button type="button" class="btn btn-secondary" style="width: auto; padding: 0.25rem 0.5rem; font-size: 0.75rem; border-radius: 4px;" @click="endpointForm.action_type = 'fetch_user_info'">
fetch_user_info (جلب معلومات العميل)
</button>
</div>
<span style="font-size: 0.75rem; color: var(--text-secondary); display: block; margin-top: 0.25rem;" x-text="lang === 'ar' ? 'يمكنك كتابة نوع إجراء مخصص هنا لتسهيل ربط النظام أو الضغط على الاختصارات بالأعلى.' : 'You can type a custom action type here or click the shortcuts above.'"></span>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">Description</label> <label class="form-label" x-text="lang === 'ar' ? 'الوصف' : 'Description'"></label>
<textarea class="form-input" x-model="endpointForm.description" placeholder="A brief explanation of what this endpoint does..." id="add-endpoint-description"></textarea> <textarea class="form-input" x-model="endpointForm.description" placeholder="A brief explanation of what this endpoint does..." id="add-endpoint-description"></textarea>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">Custom HTTP Headers (JSON Object)</label> <label class="form-label" x-text="lang === 'ar' ? 'مفتاح واجهة برمجة التطبيقات (API Key)' : 'API Key'"></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> <input type="password" class="form-input" x-model="endpointForm.api_key" placeholder="e.g. key_xxxxxxxxxxxxxxxxx" id="add-endpoint-api-key">
<span style="font-size: 0.75rem; color: var(--text-secondary); display: block; margin-top: 0.25rem;"> <span style="font-size: 0.75rem; color: var(--text-secondary); display: block; margin-top: 0.25rem;" x-text="lang === 'ar' ? 'سيتم إرسال هذا المفتاح في ترويسات X-API-Key و Authorization: Bearer عند استدعاء الخادم.' : 'This key will be automatically sent in X-API-Key and Authorization: Bearer headers for outbound server-to-server calls.'"></span>
Optionally define any authorization tokens or custom headers in JSON format.
</span>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
<button type="button" class="btn btn-secondary" style="width: auto;" @click="showAddEndpointModal = false">Cancel</button> <button type="button" class="btn btn-secondary" style="width: auto;" @click="showAddEndpointModal = false" x-text="lang === 'ar' ? 'إلغاء' : 'Cancel'"></button>
<button type="submit" class="btn btn-primary" style="width: auto;" :disabled="actionLoading"> <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" x-text="endpointForm.id ? (lang === 'ar' ? 'حفظ التعديلات' : 'Save Changes') : (lang === 'ar' ? 'إنشاء الربط' : 'Create Integration')"></span>
<span x-show="actionLoading" class="spinner"></span> <span x-show="actionLoading" class="spinner"></span>
</button> </button>
</div> </div>
@@ -1305,6 +1344,9 @@
<script> <script>
function app() { function app() {
return { return {
// Multi-Language State
lang: localStorage.getItem('nabeh_lang') || 'ar',
// Auth States // Auth States
isLoggedIn: false, isLoggedIn: false,
authTab: 'login', authTab: 'login',
@@ -1344,7 +1386,7 @@
endpoint_url: '', endpoint_url: '',
action_type: 'verify_payment', action_type: 'verify_payment',
description: '', description: '',
headers: '' api_key: ''
}, },
endpoints: [], endpoints: [],
@@ -1383,6 +1425,11 @@
}, },
// Methods // Methods
toggleLang() {
this.lang = this.lang === 'ar' ? 'en' : 'ar';
localStorage.setItem('nabeh_lang', this.lang);
},
checkAuth() { checkAuth() {
this.token = localStorage.getItem('nabeh_token'); this.token = localStorage.getItem('nabeh_token');
const storedUser = localStorage.getItem('nabeh_user'); const storedUser = localStorage.getItem('nabeh_user');
@@ -1606,16 +1653,6 @@
async submitAddEndpoint() { async submitAddEndpoint() {
this.actionLoading = true; this.actionLoading = true;
try { 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', { const response = await fetch('/api/endpoints', {
method: 'POST', method: 'POST',
headers: { headers: {
@@ -1627,7 +1664,7 @@
const data = await response.json(); const data = await response.json();
if (response.ok) { if (response.ok) {
this.showAddEndpointModal = false; this.showAddEndpointModal = false;
this.endpointForm = { id: null, name: '', endpoint_url: '', action_type: 'verify_payment', description: '', headers: '' }; this.endpointForm = { id: null, name: '', endpoint_url: '', action_type: 'verify_payment', description: '', api_key: '' };
await this.fetchEndpoints(); await this.fetchEndpoints();
} else { } else {
alert(data.message || 'Failed to save integration endpoint.'); alert(data.message || 'Failed to save integration endpoint.');
@@ -1646,13 +1683,16 @@
endpoint_url: endpoint.endpoint_url, endpoint_url: endpoint.endpoint_url,
action_type: endpoint.action_type, action_type: endpoint.action_type,
description: endpoint.description || '', description: endpoint.description || '',
headers: endpoint.headers || '' api_key: endpoint.api_key || ''
}; };
this.showAddEndpointModal = true; this.showAddEndpointModal = true;
}, },
async deleteEndpoint(id) { async deleteEndpoint(id) {
if (!confirm('Are you sure you want to delete this integration endpoint?')) return; const confirmMsg = this.lang === 'ar'
? 'هل أنت متأكد من رغبتك في حذف ربط نقطة النهاية هذا؟'
: 'Are you sure you want to delete this integration endpoint?';
if (!confirm(confirmMsg)) return;
this.actionLoading = true; this.actionLoading = true;
try { try {
const response = await fetch('/api/endpoints', { const response = await fetch('/api/endpoints', {