Deploy: 2026-05-25 00:33:34

This commit is contained in:
Hamza-Ayed
2026-05-25 00:33:34 +03:00
parent 7359206eb3
commit 88006e18d0

View File

@@ -765,8 +765,8 @@
<span class="nav-text" x-text="lang === 'ar' ? 'لوحة المشرف العام' : 'Super Admin'"></span> <span class="nav-text" x-text="lang === 'ar' ? 'لوحة المشرف العام' : 'Super Admin'"></span>
</button> </button>
<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'; fetchMetaSessions()" id="nav-whatsapp-btn">
<span>📱</span> <span x-text="lang === 'ar' ? 'اتصال الواتساب' : 'WhatsApp Connection'"></span> <span>📱</span> <span x-text="lang === 'ar' ? 'قنوات الاتصال' : 'Communication Channels'"></span>
</button> </button>
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'billing' }" @click="activeDashboardTab = 'billing'; fetchPlans()" id="nav-billing-btn"> <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> <span>💳</span> <span x-text="lang === 'ar' ? 'الباقات والاشتراكات' : 'Billing & Plans'"></span>
@@ -897,8 +897,16 @@
</div> </div>
</div> </div>
<div class="panel" x-show="activeDashboardTab === 'whatsapp'" id="panel-whatsapp"> <div class="panel" x-show="activeDashboardTab === 'whatsapp'" id="panel-whatsapp" x-data="{ channelTab: 'whatsapp' }">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''"> <!-- Sub-tabs for Channels -->
<div style="display: flex; gap: 1rem; border-bottom: 1px solid var(--card-border); margin-bottom: 1.5rem;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
<button class="tab-btn" :class="{ 'active': channelTab === 'whatsapp' }" @click="channelTab = 'whatsapp'" x-text="lang === 'ar' ? 'واتساب' : 'WhatsApp'"></button>
<button class="tab-btn" :class="{ 'active': channelTab === 'messenger' }" @click="channelTab = 'messenger'; fetchMetaSessions()" x-text="lang === 'ar' ? 'فيسبوك ماسنجر' : 'Facebook Messenger'"></button>
<button class="tab-btn" :class="{ 'active': channelTab === 'instagram' }" @click="channelTab = 'instagram'; fetchMetaSessions()" x-text="lang === 'ar' ? 'إنستغرام' : 'Instagram'"></button>
</div>
<div x-show="channelTab === '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> <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;"> <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-warning" x-show="whatsappSessions.length >= whatsappMaxSessions" x-text="lang === 'ar' ? 'تم الوصول للحد الأقصى (' + whatsappSessions.length + '/' + whatsappMaxSessions + ')' : 'Limit Reached (' + whatsappSessions.length + '/' + whatsappMaxSessions + ')'"></span>
@@ -1058,6 +1066,112 @@
</template> </template>
</div> </div>
</div> </div>
</div> <!-- End of WhatsApp Sub-tab -->
<!-- Facebook Messenger Sub-tab -->
<div x-show="channelTab === 'messenger'">
<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' ? 'ربط فيسبوك ماسنجر' : 'Facebook Messenger Integration'"></h2>
<button @click="openMetaConnectModal('messenger')" class="btn btn-primary" style="padding: 0.5rem 1rem; font-size: 0.85rem;">
<span x-text="lang === 'ar' ? '+ ربط صفحة جديدة' : '+ Connect New Page'"></span>
</button>
</div>
<p class="text-muted" style="margin-bottom: 1.5rem; font-size: 0.9rem;" x-text="lang === 'ar' ? 'قم بربط صفحات فيسبوك الخاصة بك لتفعيل الردود التلقائية وروبوت المحادثة بالذكاء الاصطناعي لرسائل العملاء.' : 'Connect your Facebook Pages to enable AI automated replies for customer messages.'"></p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 1.5rem; margin-bottom: 2rem;">
<template x-for="session in metaSessions.filter(s => s.channel_type === 'messenger')" :key="session.id">
<div class="status-box" style="margin: 0; padding: 1.5rem; display: flex; flex-direction: column; justify-content: space-between; height: 100%; border: 1px solid var(--card-border); background: var(--card-bg); border-radius: 16px;">
<div>
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 1rem;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
<div>
<h3 style="font-size: 1.1rem; font-weight: 700;" x-text="session.page_name"></h3>
<span style="font-family: monospace; font-size: 0.8rem; color: var(--text-muted);" x-text="'ID: ' + session.page_id"></span>
</div>
<div class="status-badge badge-connected" style="margin: 0; font-size: 0.75rem; padding: 0.2rem 0.5rem; background: rgba(16, 185, 129, 0.12); color: #34d399; border: 1px solid rgba(16, 185, 129, 0.25);">
<span x-text="session.status"></span>
</div>
</div>
</div>
<div style="display: flex; justify-content: flex-end;">
<button @click="deleteMetaSession(session.id)" class="btn btn-danger" style="padding: 0.4rem 0.8rem; font-size: 0.8rem;" :disabled="actionLoading">
<span x-text="lang === 'ar' ? 'حذف / قطع الاتصال' : 'Disconnect / Delete'"></span>
</button>
</div>
</div>
</template>
<template x-if="metaSessions.filter(s => s.channel_type === 'messenger').length === 0">
<div style="grid-column: 1 / -1; text-align: center; padding: 3rem; color: var(--text-muted);" x-text="lang === 'ar' ? 'لا توجد صفحات فيسبوك مرتبطة حالياً. اضغط على الزر أعلاه للربط.' : 'No Facebook pages connected. Click the button above to link one.'"></div>
</template>
</div>
</div>
<!-- Instagram Business Sub-tab -->
<div x-show="channelTab === 'instagram'">
<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' ? 'ربط حساب إنستغرام للأعمال' : 'Instagram Business Integration'"></h2>
<button @click="openMetaConnectModal('instagram')" class="btn btn-primary" style="padding: 0.5rem 1rem; font-size: 0.85rem;">
<span x-text="lang === 'ar' ? '+ ربط حساب جديد' : '+ Connect New Profile'"></span>
</button>
</div>
<p class="text-muted" style="margin-bottom: 1.5rem; font-size: 0.9rem;" x-text="lang === 'ar' ? 'قم بربط حسابات إنستغرام للأعمال الخاصة بك لتفعيل روبوت المحادثة المخصص لرسائل المتابعين والعملاء.' : 'Connect your Instagram Business accounts to enable automated chatbot replies for customer direct messages.'"></p>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 1.5rem; margin-bottom: 2rem;">
<template x-for="session in metaSessions.filter(s => s.channel_type === 'instagram')" :key="session.id">
<div class="status-box" style="margin: 0; padding: 1.5rem; display: flex; flex-direction: column; justify-content: space-between; height: 100%; border: 1px solid var(--card-border); background: var(--card-bg); border-radius: 16px;">
<div>
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 1rem;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
<div>
<h3 style="font-size: 1.1rem; font-weight: 700;" x-text="session.page_name"></h3>
<span style="font-family: monospace; font-size: 0.8rem; color: var(--text-muted);" x-text="'ID: ' + session.page_id"></span>
</div>
<div class="status-badge badge-connected" style="margin: 0; font-size: 0.75rem; padding: 0.2rem 0.5rem; background: rgba(16, 185, 129, 0.12); color: #34d399; border: 1px solid rgba(16, 185, 129, 0.25);">
<span x-text="session.status"></span>
</div>
</div>
</div>
<div style="display: flex; justify-content: flex-end;">
<button @click="deleteMetaSession(session.id)" class="btn btn-danger" style="padding: 0.4rem 0.8rem; font-size: 0.8rem;" :disabled="actionLoading">
<span x-text="lang === 'ar' ? 'حذف / قطع الاتصال' : 'Disconnect / Delete'"></span>
</button>
</div>
</div>
</template>
<template x-if="metaSessions.filter(s => s.channel_type === 'instagram').length === 0">
<div style="grid-column: 1 / -1; text-align: center; padding: 3rem; color: var(--text-muted);" x-text="lang === 'ar' ? 'لا توجد حسابات إنستغرام مرتبطة حالياً. اضغط على الزر أعلاه للربط.' : 'No Instagram profiles connected. Click the button above to link one.'"></div>
</template>
</div>
</div>
<!-- Meta Connection Modal -->
<div class="modal-overlay" x-show="showMetaConnectModal" style="display: none; z-index: 2000;">
<div class="modal-card">
<div class="modal-header">
<h3 class="modal-title" x-text="newMetaSession.channel_type === 'messenger' ? (lang === 'ar' ? 'ربط صفحة فيسبوك' : 'Link Facebook Page') : (lang === 'ar' ? 'ربط حساب إنستغرام' : 'Link Instagram Profile')"></h3>
<button class="modal-close" @click="showMetaConnectModal = false">&times;</button>
</div>
<div class="modal-body">
<div class="form-group">
<label class="form-label" x-text="lang === 'ar' ? 'الاسم' : 'Name'"></label>
<input type="text" x-model="newMetaSession.page_name" class="form-input" :placeholder="lang === 'ar' ? 'اسم الصفحة أو الحساب' : 'Page or Profile Name'">
</div>
<div class="form-group">
<label class="form-label" x-text="lang === 'ar' ? 'المعرّف (ID)' : 'ID'"></label>
<input type="text" x-model="newMetaSession.page_id" class="form-input" :placeholder="lang === 'ar' ? 'معرّف الصفحة أو الحساب' : 'Page or Account ID'">
</div>
<div class="form-group">
<label class="form-label" x-text="lang === 'ar' ? 'رمز الوصول (Access Token)' : 'Access Token'"></label>
<input type="text" x-model="newMetaSession.page_access_token" class="form-input" :placeholder="lang === 'ar' ? 'رمز الوصول للصفحة' : 'Page Access Token'">
</div>
</div>
<div class="modal-footer" style="display: flex; justify-content: flex-end; gap: 1rem;">
<button class="btn btn-secondary" style="width: auto;" @click="showMetaConnectModal = false" x-text="lang === 'ar' ? 'إلغاء' : 'Cancel'"></button>
<button class="btn btn-primary" style="width: auto;" @click="connectMetaSession()" :disabled="actionLoading">
<span x-show="!actionLoading" x-text="lang === 'ar' ? 'ربط وتفعيل' : 'Connect & Enable'"></span>
<span x-show="actionLoading" class="spinner"></span>
</button>
</div>
</div>
</div>
</div> </div>
<!-- Panel: Billing & Plans --> <!-- Panel: Billing & Plans -->
@@ -1821,6 +1935,14 @@
whatsappSessions: [], whatsappSessions: [],
whatsappMaxSessions: 1, whatsappMaxSessions: 1,
newSessionName: '', newSessionName: '',
metaSessions: [],
newMetaSession: {
channel_type: 'messenger',
page_id: '',
page_name: '',
page_access_token: ''
},
showMetaConnectModal: false,
staff: [], staff: [],
showAddStaffModal: false, showAddStaffModal: false,
staffForm: { staffForm: {
@@ -2032,6 +2154,7 @@
initializeDashboard() { initializeDashboard() {
this.fetchWhatsappSessions(); this.fetchWhatsappSessions();
this.fetchWhatsappStatus(); this.fetchWhatsappStatus();
this.fetchMetaSessions();
this.fetchSallaStatus(); this.fetchSallaStatus();
this.fetchWooCommerceStatus(); this.fetchWooCommerceStatus();
// Set up persistent background status check // Set up persistent background status check
@@ -2195,6 +2318,77 @@
this.actionLoading = false; this.actionLoading = false;
} }
}, },
async fetchMetaSessions() {
if (!this.token) return;
try {
const response = await fetch('/api/meta/sessions', {
headers: { 'Authorization': `Bearer ${this.token}` }
});
const data = await response.json();
if (response.ok && data.status === 'success') {
this.metaSessions = data.data || [];
}
} catch (err) {
console.error('Failed to fetch meta sessions', err);
}
},
openMetaConnectModal(type) {
this.newMetaSession = {
channel_type: type,
page_id: '',
page_name: '',
page_access_token: ''
};
this.showMetaConnectModal = true;
},
async connectMetaSession() {
this.actionLoading = true;
try {
const response = await fetch('/api/meta/sessions/connect', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`
},
body: JSON.stringify(this.newMetaSession)
});
const data = await response.json();
if (response.ok && data.status === 'success') {
this.showMetaConnectModal = false;
this.fetchMetaSessions();
} else {
alert(data.message || 'Connection failed');
}
} catch (err) {
alert('Network error connecting Meta session');
} finally {
this.actionLoading = false;
}
},
async deleteMetaSession(id) {
if (!confirm(this.lang === 'ar' ? 'هل أنت متأكد من حذف هذه القناة؟' : 'Are you sure you want to disconnect this channel?')) {
return;
}
this.actionLoading = true;
try {
const response = await fetch('/api/meta/sessions', {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`
},
body: JSON.stringify({ session_id: id })
});
const data = await response.json();
if (response.ok && data.status === 'success') {
this.fetchMetaSessions();
}
} catch (err) {
console.error('Failed to delete meta session', err);
} finally {
this.actionLoading = false;
}
},
async fetchWhatsappSessions() { async fetchWhatsappSessions() {
if (!this.token) return; if (!this.token) return;
try { try {