Deploy: 2026-05-23 02:42:32
This commit is contained in:
@@ -114,7 +114,8 @@ class AuthController extends BaseController
|
||||
'id' => $user['id'],
|
||||
'company_id' => $user['company_id'],
|
||||
'name' => $user['name'],
|
||||
'role' => $user['role']
|
||||
'role' => $user['role'],
|
||||
'is_super_admin' => (int)$user['company_id'] === 1
|
||||
]
|
||||
], 200);
|
||||
}
|
||||
|
||||
@@ -60,6 +60,24 @@ class StaffController extends BaseController
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch subscription limits for agents
|
||||
$activeSub = \App\Models\CompanySubscription::findActiveByCompany($companyId);
|
||||
$maxAgents = 1;
|
||||
if (isset($request->is_super_admin) && $request->is_super_admin) {
|
||||
$maxAgents = 999;
|
||||
} elseif ($activeSub) {
|
||||
$maxAgents = (int)($activeSub['max_agents'] ?? 1);
|
||||
}
|
||||
|
||||
$currentStaffCount = Database::selectOne("SELECT COUNT(*) as count FROM users WHERE company_id = ? AND role = 'staff'", [$companyId])['count'] ?? 0;
|
||||
if ($currentStaffCount >= $maxAgents) {
|
||||
$response->status(400)->json([
|
||||
'status' => 'error',
|
||||
'error' => "You have reached the maximum number of staff agents allowed by your plan ({$maxAgents})."
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$body = $request->getBody();
|
||||
$email = strtolower(trim($body['email']));
|
||||
|
||||
|
||||
@@ -102,9 +102,18 @@ class WhatsAppController extends BaseController
|
||||
$companyId = $request->company_id;
|
||||
$sessions = WhatsAppSession::findAllByCompany($companyId);
|
||||
|
||||
$activeSub = \App\Models\CompanySubscription::findActiveByCompany($companyId);
|
||||
$maxSessions = 1;
|
||||
if (isset($request->is_super_admin) && $request->is_super_admin) {
|
||||
$maxSessions = 999;
|
||||
} elseif ($activeSub) {
|
||||
$maxSessions = (int)$activeSub['max_sessions'];
|
||||
}
|
||||
|
||||
$response->json([
|
||||
'status' => 'success',
|
||||
'data' => $sessions
|
||||
'data' => $sessions,
|
||||
'max_sessions' => $maxSessions
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -120,8 +129,8 @@ class WhatsAppController extends BaseController
|
||||
// Fetch subscription limits
|
||||
$activeSub = \App\Models\CompanySubscription::findActiveByCompany($companyId);
|
||||
$maxSessions = 1;
|
||||
if ($companyId === 1) {
|
||||
$maxSessions = 10;
|
||||
if (isset($request->is_super_admin) && $request->is_super_admin) {
|
||||
$maxSessions = 999; // Unlimited for Super Admin
|
||||
} elseif ($activeSub) {
|
||||
$maxSessions = (int)$activeSub['max_sessions'];
|
||||
}
|
||||
|
||||
@@ -38,5 +38,6 @@ class AuthMiddleware
|
||||
$request->user_id = $payload['user_id'];
|
||||
$request->company_id = $payload['company_id'];
|
||||
$request->role = $payload['role'];
|
||||
$request->is_super_admin = (int)$payload['company_id'] === 1;
|
||||
}
|
||||
}
|
||||
|
||||
36
backend/migrate_super_admin_features.php
Normal file
36
backend/migrate_super_admin_features.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
|
||||
$dotenv->safeLoad();
|
||||
|
||||
try {
|
||||
$pdo = new PDO(
|
||||
"mysql:host=" . $_ENV['DB_HOST'] . ";dbname=" . $_ENV['DB_NAME'],
|
||||
$_ENV['DB_USER'],
|
||||
$_ENV['DB_PASS']
|
||||
);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
echo "=== Running Database Migrations: Super Admin Features ===\n";
|
||||
|
||||
// 1. Add max_agents to subscription_plans if not exists
|
||||
$result = $pdo->query("SHOW COLUMNS FROM `subscription_plans` LIKE 'max_agents'");
|
||||
if ($result->rowCount() === 0) {
|
||||
$pdo->exec("ALTER TABLE `subscription_plans` ADD COLUMN `max_agents` INT DEFAULT 1 AFTER `max_sessions`");
|
||||
echo "✅ Added 'max_agents' column to 'subscription_plans' table.\n";
|
||||
} else {
|
||||
echo "ℹ️ Column 'max_agents' already exists in 'subscription_plans' table. Skipping.\n";
|
||||
}
|
||||
|
||||
// 2. Update default plans limits (Starter: 1, Growth: 3, Pro: 10)
|
||||
$pdo->exec("UPDATE `subscription_plans` SET `max_agents` = 1 WHERE `id` = 1");
|
||||
$pdo->exec("UPDATE `subscription_plans` SET `max_agents` = 3 WHERE `id` = 2");
|
||||
$pdo->exec("UPDATE `subscription_plans` SET `max_agents` = 10 WHERE `id` = 3");
|
||||
|
||||
echo "✅ Updated default subscription plans limits.\n";
|
||||
|
||||
echo "Migration completed successfully!\n";
|
||||
} catch (PDOException $e) {
|
||||
echo "❌ Database error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
@@ -759,6 +759,12 @@
|
||||
<div class="dashboard-layout">
|
||||
<!-- 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">
|
||||
<span class="nav-icon">👑</span>
|
||||
<span class="nav-text" x-text="lang === 'ar' ? 'لوحة المشرف العام' : 'Super Admin'"></span>
|
||||
</button>
|
||||
|
||||
<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>
|
||||
@@ -799,12 +805,66 @@
|
||||
</template>
|
||||
|
||||
<!-- Panel: WhatsApp Connection -->
|
||||
<!-- Super Admin Panel -->
|
||||
<div class="panel" x-show="activeDashboardTab === 'super_admin'" id="panel-superadmin">
|
||||
<h2 x-text="lang === 'ar' ? 'لوحة المشرف العام' : 'Super Admin Dashboard'"></h2>
|
||||
<p class="text-muted" x-text="lang === 'ar' ? 'إدارة الشركات والباقات.' : 'Manage tenants and subscriptions.'"></p>
|
||||
|
||||
<div class="dashboard-stats" style="margin-top: 1rem;" x-show="superAdminStats">
|
||||
<div class="stat-card">
|
||||
<div class="stat-title" x-text="lang === 'ar' ? 'إجمالي الشركات' : 'Total Companies'"></div>
|
||||
<div class="stat-value" x-text="superAdminStats?.total_companies"></div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-title" x-text="lang === 'ar' ? 'أرقام الواتساب المربوطة' : 'Connected WhatsApps'"></div>
|
||||
<div class="stat-value" x-text="superAdminStats?.connected_sessions + ' / ' + superAdminStats?.total_sessions"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 style="margin-top:2rem;" x-text="lang === 'ar' ? 'قائمة الشركات' : 'Companies List'"></h3>
|
||||
<div class="data-table" style="overflow-x: auto;">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th x-text="lang === 'ar' ? 'اسم الشركة' : 'Company Name'"></th>
|
||||
<th x-text="lang === 'ar' ? 'الباقة الحالية' : 'Current Plan'"></th>
|
||||
<th x-text="lang === 'ar' ? 'استهلاك الرسائل' : 'Request Usage'"></th>
|
||||
<th x-text="lang === 'ar' ? 'حالة الواتساب' : 'WhatsApp Status'"></th>
|
||||
<th x-text="lang === 'ar' ? 'الإجراءات' : 'Actions'"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template x-for="company in superAdminCompanies" :key="company.id">
|
||||
<tr>
|
||||
<td x-text="company.id"></td>
|
||||
<td x-text="company.name"></td>
|
||||
<td x-text="company.plan_name ? company.plan_name : 'No Plan'"></td>
|
||||
<td x-text="company.request_usage"></td>
|
||||
<td x-text="company.active_sessions + ' / ' + company.sessions_count"></td>
|
||||
<td>
|
||||
<select class="form-input" style="padding: 0.2rem; font-size: 0.85rem;" @change="changeCompanyPlan(company.id, $event.target.value)">
|
||||
<option value="" disabled selected x-text="lang === 'ar' ? 'تغيير الباقة...' : 'Change Plan...'"></option>
|
||||
<template x-for="plan in superAdminPlans" :key="plan.id">
|
||||
<option :value="plan.id" x-text="plan.name"></option>
|
||||
</template>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel" x-show="activeDashboardTab === 'whatsapp'" id="panel-whatsapp">
|
||||
<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' ? 'إدارة قنوات اتصال واتساب' : 'WhatsApp Session Management'"></h2>
|
||||
<div style="display: flex; gap: 0.5rem; align-items: center;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
|
||||
<input type="text" x-model="newSessionName" :placeholder="lang === 'ar' ? 'اسم الرقم (مثال: الدعم)' : 'Session Name (e.g. Sales)'" class="form-input" style="max-width: 200px; padding: 0.5rem 0.8rem; font-size: 0.85rem;" id="new-session-name-input">
|
||||
<button @click="createWhatsappSession()" class="btn btn-primary" style="padding: 0.5rem 1rem; font-size: 0.85rem;" id="btn-create-session" :disabled="actionLoading">
|
||||
<div style="display: flex; gap: 0.5rem; align-items: center;">
|
||||
<span class="badge badge-warning" x-show="whatsappSessions.length >= whatsappMaxSessions" x-text="lang === 'ar' ? 'تم الوصول للحد الأقصى (' + whatsappSessions.length + '/' + whatsappMaxSessions + ')' : 'Limit Reached (' + whatsappSessions.length + '/' + whatsappMaxSessions + ')'"></span>
|
||||
<span class="badge badge-success" x-show="whatsappSessions.length < whatsappMaxSessions" x-text="whatsappSessions.length + ' / ' + whatsappMaxSessions + (lang === 'ar' ? ' أرقام' : ' Numbers')"></span>
|
||||
<input type="text" x-model="newSessionName" class="form-input" :placeholder="lang === 'ar' ? 'اسم الرقم (مثال: الدعم)' : 'Session Name (e.g. Sales)'" style="width: 250px;" id="new-session-name-input">
|
||||
<button @click="createWhatsappSession()" class="btn btn-primary" style="padding: 0.5rem 1rem; font-size: 0.85rem;" id="btn-create-session" :disabled="actionLoading || whatsappSessions.length >= whatsappMaxSessions">
|
||||
<span x-text="lang === 'ar' ? '+ إضافة خط جديد' : '+ Add New Line'"></span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -1649,9 +1709,13 @@
|
||||
user: null,
|
||||
|
||||
// Dashboard States
|
||||
superAdminStats: null,
|
||||
superAdminCompanies: [],
|
||||
superAdminPlans: [],
|
||||
activeDashboardTab: 'whatsapp',
|
||||
whatsappSession: null,
|
||||
whatsappSessions: [],
|
||||
whatsappMaxSessions: 1,
|
||||
newSessionName: '',
|
||||
staff: [],
|
||||
showAddStaffModal: false,
|
||||
@@ -1895,7 +1959,57 @@
|
||||
console.error('Failed to retrieve session status:', err);
|
||||
}
|
||||
},
|
||||
|
||||
// Super Admin methods
|
||||
async fetchSuperAdminStats() {
|
||||
if (!this.user?.is_super_admin) return;
|
||||
this.actionLoading = true;
|
||||
try {
|
||||
const response = await fetch('/api/admin/stats', {
|
||||
headers: { 'Authorization': `Bearer ${this.token}` }
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
this.superAdminStats = data.data.stats;
|
||||
this.superAdminCompanies = data.data.companies;
|
||||
this.superAdminPlans = data.data.plans;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
this.actionLoading = false;
|
||||
}
|
||||
},
|
||||
async changeCompanyPlan(companyId, planId) {
|
||||
if (!planId) return;
|
||||
if (!confirm(this.lang === 'ar' ? 'هل أنت متأكد من تغيير الباقة لهذه الشركة؟' : 'Are you sure you want to change the subscription plan for this company?')) return;
|
||||
|
||||
this.actionLoading = true;
|
||||
try {
|
||||
const response = await fetch('/api/admin/companies/subscribe', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
company_id: companyId,
|
||||
plan_id: planId,
|
||||
duration_days: 30
|
||||
})
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
alert(this.lang === 'ar' ? 'تم تحديث الباقة بنجاح' : 'Plan updated successfully');
|
||||
await this.fetchSuperAdminStats();
|
||||
} else {
|
||||
alert(data.error || 'Failed to update plan');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
this.actionLoading = false;
|
||||
}
|
||||
},
|
||||
async fetchWhatsappSessions() {
|
||||
if (!this.token) return;
|
||||
try {
|
||||
@@ -1905,6 +2019,15 @@
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
this.whatsappSessions = data.data || [];
|
||||
this.whatsappMaxSessions = data.max_sessions || 1;
|
||||
|
||||
if (this.whatsappSessions.length > 0 && (!this.whatsappSession || !this.whatsappSessions.find(s => s.id === this.whatsappSession.id))) {
|
||||
this.whatsappSession = this.whatsappSessions[0];
|
||||
this.fetchChatbotSettings();
|
||||
}
|
||||
if (this.whatsappSessions.length === 0) {
|
||||
this.whatsappSession = null;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to retrieve sessions list:', err);
|
||||
|
||||
Reference in New Issue
Block a user