Deploy: 2026-05-23 03:23:22

This commit is contained in:
Hamza-Ayed
2026-05-23 03:23:22 +03:00
parent 30301151c3
commit d686f8928b
10 changed files with 463 additions and 24 deletions

View File

@@ -760,7 +760,7 @@
<!-- Left Sidebar Nav -->
<div class="nav-menu">
<!-- Super Admin Dashboard -->
<button class="nav-item" x-show="user?.is_super_admin" :class="{ 'active': activeDashboardTab === 'super_admin' }" @click="activeDashboardTab = 'super_admin'; fetchSuperAdminStats()" id="nav-superadmin-btn">
<button class="nav-item" x-show="user?.is_super_admin" :class="{ 'active': activeDashboardTab === 'super_admin' }" @click="activeDashboardTab = 'super_admin'; fetchSuperAdminStats()" id="nav-superadmin-btn">
<span class="nav-icon">👑</span>
<span class="nav-text" x-text="lang === 'ar' ? 'لوحة المشرف العام' : 'Super Admin'"></span>
</button>
@@ -768,6 +768,9 @@
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'whatsapp' }" @click="activeDashboardTab = 'whatsapp'" id="nav-whatsapp-btn">
<span>📱</span> <span x-text="lang === 'ar' ? 'اتصال الواتساب' : 'WhatsApp Connection'"></span>
</button>
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'billing' }" @click="activeDashboardTab = 'billing'; fetchPlans()" id="nav-billing-btn">
<span>💳</span> <span x-text="lang === 'ar' ? 'الباقات والاشتراكات' : 'Billing & Plans'"></span>
</button>
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'contacts' }" @click="activeDashboardTab = 'contacts'; fetchContacts(); fetchGroups()" id="nav-contacts-btn">
<span>👥</span> <span x-text="lang === 'ar' ? 'دليل جهات الاتصال' : 'Contacts Directory'"></span>
</button>
@@ -790,6 +793,11 @@
<!-- Right Dashboard Panels -->
<div style="flex: 1;">
<template x-if="user?.subscription_status === 'trialing' || (user?.subscription_status === 'active' && user?.trial_days_left < 14)">
<div class="banner banner-warning" style="margin-bottom: 1.5rem;">
<span x-text="lang === 'ar' ? 'متبقي لك ' + user.trial_days_left + ' أيام من الفترة التجريبية. اشترك الآن لضمان استمرار الخدمة.' : 'You have ' + user.trial_days_left + ' days left in your trial. Subscribe now to ensure service continuity.'"></span>
</div>
</template>
<!-- Global Dashboard Banner -->
<template x-if="dashboardSuccess">
<div class="banner banner-success" style="margin-bottom: 1.5rem;" id="dashboard-success-banner">
@@ -821,6 +829,38 @@
</div>
</div>
<div style="margin-top: 2rem; margin-bottom: 2rem;" x-show="superAdminPending?.length > 0">
<h3 style="margin-bottom: 1rem;"><span style="color: var(--warning-color);"></span> <span x-text="lang === 'ar' ? 'طلبات الدفع بانتظار الموافقة' : 'Pending Approvals'"></span></h3>
<div class="table-container">
<table class="table">
<thead>
<tr>
<th x-text="lang === 'ar' ? 'معرف الشركة' : 'Company ID'"></th>
<th x-text="lang === 'ar' ? 'اسم الشركة' : 'Company Name'"></th>
<th x-text="lang === 'ar' ? 'الباقة المطلوبة' : 'Requested Plan'"></th>
<th x-text="lang === 'ar' ? 'طريقة الدفع' : 'Payment Method'"></th>
<th x-text="lang === 'ar' ? 'رقم الحوالة' : 'Receipt Reference'"></th>
<th x-text="lang === 'ar' ? 'الإجراء' : 'Action'"></th>
</tr>
</thead>
<tbody>
<template x-for="req in superAdminPending" :key="req.company_id">
<tr>
<td x-text="'#' + req.id"></td>
<td x-text="req.name"></td>
<td><span class="badge badge-info" x-text="req.plan_name"></span></td>
<td x-text="req.payment_method?.toUpperCase()"></td>
<td x-text="req.receipt_reference"></td>
<td>
<button class="btn btn-primary btn-sm" @click="approveBilling(req.id)" x-text="lang === 'ar' ? 'موافقة وتفعيل' : 'Approve & Activate'"></button>
</td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
<h3 style="margin-top:2rem;" x-text="lang === 'ar' ? 'قائمة الشركات' : 'Companies List'"></h3>
<div class="data-table" style="overflow-x: auto;">
<table>
@@ -1020,6 +1060,30 @@
</div>
</div>
<!-- Panel: Billing & Plans -->
<div class="panel" x-show="activeDashboardTab === 'billing'" id="panel-billing">
<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;" x-text="lang === 'ar' ? 'الباقات والاشتراكات' : 'Billing & Plans'"></h2>
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem;" :style="lang === 'ar' ? 'direction: rtl;' : ''">
<template x-for="plan in availablePlans" :key="plan.id">
<div class="card" style="border: 2px solid transparent; transition: 0.3s; padding: 2rem; text-align: center;" :style="userPlanId == plan.id ? 'border-color: var(--primary-accent);' : ''">
<h3 style="font-size: 1.5rem; margin-bottom: 0.5rem;" x-text="plan.name"></h3>
<div style="font-size: 2rem; font-weight: bold; color: var(--primary-accent); margin-bottom: 1rem;">
$<span x-text="plan.price"></span> <span style="font-size: 1rem; color: var(--text-muted);">/ <span x-text="lang === 'ar' ? 'شهر' : 'mo'"></span></span>
</div>
<ul style="list-style: none; padding: 0; margin-bottom: 1.5rem; text-align: start; line-height: 1.8;">
<li><span style="margin-right: 0.5rem;"></span> <span x-text="plan.max_sessions"></span> <span x-text="lang === 'ar' ? 'أرقام واتساب' : 'WhatsApp Numbers'"></span></li>
<li><span style="margin-right: 0.5rem;"></span> <span x-text="plan.max_requests"></span> <span x-text="lang === 'ar' ? 'رسالة نصية' : 'Text Messages'"></span></li>
<li><span style="margin-right: 0.5rem;"></span> <span x-text="plan.max_voice_requests"></span> <span x-text="lang === 'ar' ? 'رسالة صوتية' : 'Voice Notes'"></span></li>
</ul>
<button class="btn btn-primary" style="width: 100%;" @click="openCheckoutModal(plan)" :disabled="userPlanId == plan.id" x-text="userPlanId == plan.id ? (lang === 'ar' ? 'باقتك الحالية' : 'Current Plan') : (lang === 'ar' ? 'ترقية الآن' : 'Upgrade Now')"></button>
</div>
</template>
</div>
</div>
<!-- Panel: Contacts Directory -->
<div class="panel" x-show="activeDashboardTab === 'contacts'" id="panel-contacts">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.5rem;">
@@ -1216,7 +1280,7 @@
💡 نصائح وتوجيهات لكتابة تعليمات ممتازة:
</strong>
<ul style="list-style-type: disc; margin-right: 1.25rem; padding-left: 0; line-height: 1.5; margin-bottom: 0;">
<li><strong>الهوية والاسم:</strong> حدد اسم الروبوت بوضوح (مثال: "أنا سارة من فريق تطبيق نبيه").</li>
<li><strong>الهوية والاسم:</strong> حدد اسم الروبوت بوحضوح (مثال: "أنا سارة من فريق تطبيق نبيه").</li>
<li><strong>اللهجة والأسلوب:</strong> اطلب من الذكاء الاصطناعي الرد بلهجة معينة (مثال: اللهجة السورية أو الفصحى المبسطة).</li>
<li><strong>البيانات الأساسية:</strong> اكتب ساعات عمل المتجر، طرق الدفع والتوصيل، وسياسة الاستبدال لكي يجيب الروبوت بدقة.</li>
<li><strong>التعليمات اللغوية:</strong> قمنا بتضمين ميزة مطابقة اللغة تلقائياً (الرد بالإنجليزية على الرسائل الإنجليزية، وبالعربية على العربية).</li>
@@ -1455,6 +1519,45 @@
</div>
</div>
<!-- Modal: Checkout -->
<div class="modal-overlay" x-show="showCheckoutModal" id="modal-checkout" style="display: none;">
<div class="modal-card">
<div class="modal-header">
<h3 class="modal-title" x-text="lang === 'ar' ? 'إتمام عملية الدفع' : 'Complete Checkout'"></h3>
<button class="modal-close" @click="showCheckoutModal = false">&times;</button>
</div>
<div class="modal-body">
<p x-text="lang === 'ar' ? 'أنت تقوم بترقية حسابك إلى باقة: ' + selectedCheckoutPlan?.name : 'You are upgrading to: ' + selectedCheckoutPlan?.name"></p>
<p style="font-weight: bold; font-size: 1.2rem; margin-bottom: 1rem;">
<span x-text="lang === 'ar' ? 'المبلغ المطلوب: ' : 'Total Amount: '"></span> $<span x-text="selectedCheckoutPlan?.price"></span>
</p>
<div class="form-group">
<label class="form-label" x-text="lang === 'ar' ? 'طريقة الدفع' : 'Payment Method'"></label>
<select x-model="checkoutPaymentMethod" class="form-input">
<option value="paymob" x-text="lang === 'ar' ? 'البطاقة الائتمانية (Paymob)' : 'Credit Card (Paymob)'"></option>
<option value="cliq" x-text="lang === 'ar' ? 'كليك (CliQ) - الأردن' : 'CliQ Transfer (Jordan)'"></option>
<option value="binance" x-text="lang === 'ar' ? 'بينانس (Binance Pay)' : 'Crypto (Binance)'"></option>
</select>
</div>
<template x-if="checkoutPaymentMethod !== 'paymob'">
<div class="form-group">
<label class="form-label" x-text="lang === 'ar' ? 'رقم الحوالة / Transaction ID' : 'Receipt Reference / TXID'"></label>
<input type="text" x-model="checkoutReceipt" class="form-input" :placeholder="lang === 'ar' ? 'أدخل رقم الحوالة هنا' : 'Enter reference here'">
</div>
</template>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" @click="showCheckoutModal = false" x-text="lang === 'ar' ? 'إلغاء' : 'Cancel'"></button>
<button class="btn btn-primary" @click="submitCheckout()" :disabled="actionLoading">
<span x-show="!actionLoading" x-text="lang === 'ar' ? 'تأكيد الدفع' : 'Confirm Payment'"></span>
<span x-show="actionLoading" class="spinner"></span>
</button>
</div>
</div>
</div>
<!-- Modal: Add Contact -->
<div class="modal-overlay" x-show="showAddContactModal" id="modal-add-contact" style="display: none;">
<div class="modal-card">
@@ -1711,6 +1814,7 @@
// Dashboard States
superAdminStats: null,
superAdminCompanies: [],
superAdminPending: [],
superAdminPlans: [],
activeDashboardTab: 'whatsapp',
whatsappSession: null,
@@ -1725,6 +1829,16 @@
password: '',
whatsapp_session_id: ''
},
// Billing State
availablePlans: [],
userPlanId: null,
showCheckoutModal: false,
selectedCheckoutPlan: null,
checkoutPaymentMethod: 'paymob',
checkoutReceipt: '',
checkoutError: '',
checkoutSuccess: '',
woocommerceStatus: null,
woocommerceLoading: false,
wooForm: {
@@ -1934,6 +2048,71 @@
}
},
// Billing & Checkout Methods
async fetchPlans() {
try {
const response = await fetch('/api/plans', {
headers: { 'Authorization': `Bearer ${this.token}` }
});
const data = await response.json();
if (response.ok && data.status === 'success') {
this.availablePlans = data.data;
}
} catch (err) {
console.error('Failed to fetch plans', err);
}
},
openCheckoutModal(plan) {
this.selectedCheckoutPlan = plan;
this.checkoutPaymentMethod = 'paymob';
this.checkoutReceipt = '';
this.checkoutError = '';
this.checkoutSuccess = '';
this.showCheckoutModal = true;
},
async submitCheckout() {
if (this.checkoutPaymentMethod !== 'paymob' && !this.checkoutReceipt) {
this.checkoutError = this.lang === 'ar' ? 'الرجاء إدخال رقم الحوالة' : 'Please enter receipt reference';
return;
}
this.actionLoading = true;
this.checkoutError = '';
try {
const response = await fetch('/api/billing/upgrade', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`
},
body: JSON.stringify({
plan_id: this.selectedCheckoutPlan.id,
payment_method: this.checkoutPaymentMethod,
receipt_reference: this.checkoutReceipt
})
});
const data = await response.json();
if (response.ok && data.status === 'success') {
if (this.checkoutPaymentMethod === 'paymob' && data.checkout_url) {
window.location.href = data.checkout_url;
} else {
this.checkoutSuccess = data.message;
setTimeout(() => {
this.showCheckoutModal = false;
}, 3000);
}
} else {
this.checkoutError = data.error || 'Failed to submit payment request';
}
} catch (err) {
this.checkoutError = 'Network error while submitting payment';
} finally {
this.actionLoading = false;
}
},
async fetchWhatsappStatus() {
if (!this.token) return;
const queryParam = this.whatsappSession ? `?session_id=${this.whatsappSession.id}` : '';
@@ -1971,6 +2150,7 @@
if (response.ok && data.status === 'success') {
this.superAdminStats = data.data.stats;
this.superAdminCompanies = data.data.companies;
this.superAdminPending = data.data.pending_approvals;
this.superAdminPlans = data.data.plans;
}
} catch (err) {

View File

@@ -80,6 +80,11 @@ $router->post('/api/otp/send', [\App\Controllers\OTPController::class
// Super Admin Routes
$router->get('/api/admin/stats', [\App\Controllers\SuperAdminController::class, 'getStats'], [\App\Middlewares\AuthMiddleware::class]);
$router->post('/api/admin/companies/subscribe', [\App\Controllers\SuperAdminController::class, 'subscribeCompany'], [\App\Middlewares\AuthMiddleware::class]);
$router->post('/api/admin/companies/approve-billing', [\App\Controllers\SuperAdminController::class, 'approveBilling'], [\App\Middlewares\AuthMiddleware::class]);
// Billing & Subscription Routes
$router->get('/api/plans', [\App\Controllers\BillingController::class, 'getPlans'], [\App\Middlewares\AuthMiddleware::class]);
$router->post('/api/billing/upgrade', [\App\Controllers\BillingController::class, 'upgrade'], [\App\Middlewares\AuthMiddleware::class]);
// Phase 4 & 5: CRM, Templates & Campaigns Routes
$router->get('/api/contacts', [\App\Controllers\ContactController::class, 'index'], [\App\Middlewares\AuthMiddleware::class]);