Deploy: 2026-05-23 01:13:51
This commit is contained in:
@@ -774,9 +774,12 @@
|
||||
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'chatbot' }" @click="activeDashboardTab = 'chatbot'; fetchChatbotSettings()" id="nav-chatbot-btn">
|
||||
<span>🤖</span> <span x-text="lang === 'ar' ? 'روبوت الذكاء الاصطناعي' : 'AI Chatbot Settings'"></span>
|
||||
</button>
|
||||
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'integrations' }" @click="activeDashboardTab = 'integrations'; fetchEndpoints(); fetchSallaStatus()" id="nav-integrations-btn">
|
||||
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'integrations' }" @click="activeDashboardTab = 'integrations'; fetchEndpoints(); fetchSallaStatus(); fetchWooCommerceStatus()" id="nav-integrations-btn">
|
||||
<span>🔌</span> <span x-text="lang === 'ar' ? 'الربط البرمجي والمنصات (Integrations)' : 'API & Platform Integrations'"></span>
|
||||
</button>
|
||||
<button class="nav-item" :class="{ 'active': activeDashboardTab === 'staff' }" @click="activeDashboardTab = 'staff'; fetchStaff(); fetchWhatsappSessions()" id="nav-staff-btn">
|
||||
<span>👥</span> <span x-text="lang === 'ar' ? 'الموظفين والوكلاء' : 'CS Agents & Team'"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Right Dashboard Panels -->
|
||||
@@ -797,107 +800,159 @@
|
||||
|
||||
<!-- Panel: WhatsApp Connection -->
|
||||
<div class="panel" x-show="activeDashboardTab === 'whatsapp'" id="panel-whatsapp">
|
||||
<h2 style="font-size: 1.4rem; margin-bottom: 1.5rem;">WhatsApp Integration</h2>
|
||||
|
||||
<div class="grid-two">
|
||||
<!-- Connection Control Card -->
|
||||
<div class="status-box">
|
||||
<div class="status-badge" :class="{
|
||||
'badge-disconnected': !whatsappSession || whatsappSession.status === 'disconnected',
|
||||
'badge-connecting': whatsappSession && whatsappSession.status === 'connecting',
|
||||
'badge-waiting_qr': whatsappSession && whatsappSession.status === 'waiting_qr',
|
||||
'badge-connected': whatsappSession && whatsappSession.status === 'connected'
|
||||
}">
|
||||
<span x-text="whatsappSession ? whatsappSession.status : 'disconnected'"></span>
|
||||
</div>
|
||||
|
||||
<h3 style="font-size: 1.25rem; margin-bottom: 0.5rem;">
|
||||
<template x-if="whatsappSession && whatsappSession.status === 'connected'">
|
||||
<span>WhatsApp Connected</span>
|
||||
</template>
|
||||
<template x-if="!whatsappSession || whatsappSession.status !== 'connected'">
|
||||
<span>Session Inactive</span>
|
||||
</template>
|
||||
</h3>
|
||||
|
||||
<p class="text-muted" style="font-size: 0.9rem; margin-bottom: 1.5rem; max-width: 250px;">
|
||||
<template x-if="whatsappSession && whatsappSession.phone">
|
||||
<span>Active number: <strong x-text="whatsappSession.phone" style="color: var(--text-primary);"></strong></span>
|
||||
</template>
|
||||
<template x-if="!whatsappSession || !whatsappSession.phone">
|
||||
<span>Start a connection session to link your phone.</span>
|
||||
</template>
|
||||
</p>
|
||||
|
||||
<!-- Actions -->
|
||||
<template x-if="!whatsappSession || whatsappSession.status === 'disconnected'">
|
||||
<button @click="connectWhatsapp()" class="btn btn-primary" :disabled="actionLoading" id="btn-request-qr">
|
||||
<span x-show="!actionLoading">Generate QR Code</span>
|
||||
<span x-show="actionLoading" class="spinner"></span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<template x-if="whatsappSession && whatsappSession.status !== 'disconnected'">
|
||||
<button @click="disconnectWhatsapp()" class="btn btn-danger" :disabled="actionLoading" id="btn-disconnect-session">
|
||||
<span x-show="!actionLoading">Disconnect Session</span>
|
||||
<span x-show="actionLoading" class="spinner"></span>
|
||||
</button>
|
||||
</template>
|
||||
<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">
|
||||
<span x-text="lang === 'ar' ? '+ إضافة خط جديد' : '+ Add New Line'"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- QR Display Card -->
|
||||
<div class="flex-center flex-column panel" style="background: rgba(10, 11, 20, 0.2); border-color: rgba(255, 255, 255, 0.03);" id="qr-display-container">
|
||||
<template x-if="whatsappSession && whatsappSession.status === 'connecting'">
|
||||
<div class="text-center">
|
||||
<div class="spinner spinner-large" style="margin-bottom: 1rem;"></div>
|
||||
<p class="font-semibold">Establishing Connection...</p>
|
||||
<p class="text-muted" style="font-size: 0.85rem; margin-top: 0.25rem;">Checking gateway processes and requesting channel</p>
|
||||
</div>
|
||||
</template>
|
||||
<p class="text-muted" style="margin-bottom: 1.5rem; font-size: 0.9rem;" x-text="lang === 'ar' ? 'قم بإضافة وإدارة أرقام واتساب متعددة لشركتك طبقاً لباقة اشتراكك. يمكنك ربط كل موظف بخط محدد لمتابعة المحادثات.' : 'Add and manage multiple WhatsApp connections. You can assign customer service agents to specific phone lines based on your plan features.'"></p>
|
||||
|
||||
<template x-if="whatsappSession && whatsappSession.status === 'waiting_qr'">
|
||||
<div class="text-center">
|
||||
<p class="font-semibold">Scan QR Code</p>
|
||||
<p class="text-muted" style="font-size: 0.85rem; margin-top: 0.25rem;">Scan using Link Devices inside WhatsApp</p>
|
||||
|
||||
<div class="qr-wrapper">
|
||||
<div id="qrcode-canvas" x-init="$nextTick(() => renderQr())"></div>
|
||||
<!-- Sessions Grid Layout -->
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 1.5rem; margin-bottom: 2rem;">
|
||||
<template x-for="session in whatsappSessions" :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.name"></h3>
|
||||
<span style="font-family: monospace; font-size: 0.8rem; color: var(--text-muted);" x-text="session.session_key"></span>
|
||||
</div>
|
||||
<div class="status-badge" :class="{
|
||||
'badge-disconnected': session.status === 'disconnected',
|
||||
'badge-connecting': session.status === 'connecting',
|
||||
'badge-waiting_qr': session.status === 'waiting_qr',
|
||||
'badge-connected': session.status === 'connected'
|
||||
}" style="margin: 0; font-size: 0.75rem; padding: 0.2rem 0.5rem;">
|
||||
<span x-text="session.status"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Diagnostics -->
|
||||
<div style="font-size: 0.75rem; margin: 0.5rem 0; display: flex; flex-direction: column; gap: 0.25rem;">
|
||||
<template x-if="!whatsappSession.qr_code">
|
||||
<span style="color: var(--danger-accent);">⚠️ Decryption issue: QR code string is empty.</span>
|
||||
<p style="font-size: 0.9rem; margin-bottom: 1.5rem; color: var(--text-muted);" :style="lang === 'ar' ? 'text-align: right;' : 'text-align: left;'">
|
||||
<template x-if="session.phone">
|
||||
<span>📞 <strong x-text="session.phone" style="color: var(--text-main);"></strong></span>
|
||||
</template>
|
||||
<template x-if="whatsappSession.qr_code">
|
||||
<span style="color: var(--success-accent);">✓ Encrypted QR data retrieved successfully.</span>
|
||||
<template x-if="!session.phone">
|
||||
<span x-text="lang === 'ar' ? 'الخط غير مرتبط برقم هاتف بعد.' : 'No phone linked yet. Scan QR code.'"></span>
|
||||
</template>
|
||||
<template x-if="typeof window.QRCode === 'undefined'">
|
||||
<span style="color: var(--danger-accent);">⚠️ QRCode library failed to load (Integrity/CSP issue).</span>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div style="font-size: 0.8rem; display: flex; align-items: center; justify-content: center; gap: 0.5rem;" class="text-muted">
|
||||
<div class="spinner" style="width: 14px; height: 14px; border-width: 2px;"></div>
|
||||
<span>Waiting for connection handshake...</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template x-if="whatsappSession && whatsappSession.status === 'connected'">
|
||||
<div class="text-center" style="padding: 2rem 0;">
|
||||
<div style="font-size: 3rem; color: var(--success-accent); margin-bottom: 0.5rem; text-shadow: 0 0 20px var(--success-glow);">✓</div>
|
||||
<p class="font-semibold" style="font-size: 1.15rem; color: var(--success-accent);">Gateway fully connected</p>
|
||||
<p class="text-muted" style="font-size: 0.85rem; margin-top: 0.25rem; max-width: 250px; margin-left: auto; margin-right: auto;">
|
||||
You can now create templates and launch marketing broadcast campaigns.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template x-if="!whatsappSession || whatsappSession.status === 'disconnected'">
|
||||
<div class="text-center text-muted" style="padding: 3rem 0;">
|
||||
<span style="font-size: 2.5rem; display: block; margin-bottom: 0.5rem;">🔌</span>
|
||||
<p>No active WhatsApp link</p>
|
||||
<div style="display: flex; gap: 0.5rem; justify-content: flex-end; flex-wrap: wrap;">
|
||||
<!-- Connect/Scan QR Action -->
|
||||
<template x-if="session.status === 'disconnected' || session.status === 'waiting_qr' || session.status === 'connecting'">
|
||||
<button @click="connectWhatsapp(session.id)" class="btn btn-primary" style="padding: 0.4rem 0.8rem; font-size: 0.8rem;" :disabled="actionLoading">
|
||||
<span x-text="lang === 'ar' ? 'ربط / مسح رمز QR' : 'Link / Scan QR'"></span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!-- Disconnect Action -->
|
||||
<template x-if="session.status === 'connected'">
|
||||
<button @click="disconnectWhatsapp(session.id)" class="btn btn-glass" style="padding: 0.4rem 0.8rem; font-size: 0.8rem; color: var(--warning);" :disabled="actionLoading">
|
||||
<span x-text="lang === 'ar' ? 'قطع الاتصال' : 'Disconnect'"></span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!-- Delete Action -->
|
||||
<button @click="deleteWhatsappSession(session.id)" class="btn btn-danger" style="padding: 0.4rem 0.8rem; font-size: 0.8rem;" :disabled="actionLoading">
|
||||
<span x-text="lang === 'ar' ? 'حذف' : 'Delete'"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template x-if="whatsappSessions.length === 0">
|
||||
<div style="grid-column: 1 / -1; text-align: center; padding: 3rem; color: var(--text-muted);" x-text="lang === 'ar' ? 'لا توجد قنوات واتساب نشطة. أضف خطاً جديداً أعلاه للبدء.' : 'No active WhatsApp channels configured. Create a session above to get started.'"></div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="grid-two" style="align-items: start;">
|
||||
<!-- QR Display Card (Active selected session) -->
|
||||
<div class="card" style="margin: 0;" x-show="whatsappSession && (whatsappSession.status === 'connecting' || whatsappSession.status === 'waiting_qr' || whatsappSession.status === 'connected')">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
|
||||
<h3 style="font-size: 1.1rem; font-weight: 700;" x-text="(lang === 'ar' ? 'ربط الخط: ' : 'Linking Line: ') + (whatsappSession?.name || '')"></h3>
|
||||
<button class="modal-close" style="font-size: 1.25rem;" @click="whatsappSession = null">×</button>
|
||||
</div>
|
||||
|
||||
<div class="flex-center flex-column" style="background: rgba(10, 11, 20, 0.2); border: 1px solid var(--card-border); border-radius: 12px; padding: 2rem;">
|
||||
<template x-if="whatsappSession?.status === 'connecting'">
|
||||
<div class="text-center">
|
||||
<div class="spinner spinner-large" style="margin-bottom: 1rem;"></div>
|
||||
<p class="font-semibold" x-text="lang === 'ar' ? 'جاري الاتصال بالبوابة...' : 'Connecting to Gateway...'"></p>
|
||||
<p class="text-muted" style="font-size: 0.8rem; margin-top: 0.25rem;" x-text="lang === 'ar' ? 'يرجى الانتظار لحين جلب حالة الخط وطلب الرمز من البوابة.' : 'Preparing session and requesting QR code stream.'"></p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template x-if="whatsappSession?.status === 'waiting_qr'">
|
||||
<div class="text-center">
|
||||
<p class="font-semibold" x-text="lang === 'ar' ? 'امسح رمز الاستجابة السريعة (QR Code)' : 'Scan QR Code'"></p>
|
||||
<p class="text-muted" style="font-size: 0.8rem; margin-top: 0.25rem; margin-bottom: 1rem;" x-text="lang === 'ar' ? 'افتح واتساب > الأجهزة المرتبطة > ربط جهاز' : 'Open WhatsApp > Linked Devices > Link a Device'"></p>
|
||||
|
||||
<div class="qr-wrapper" style="background: #ffffff; padding: 1rem; border-radius: 12px; display: inline-block;">
|
||||
<div id="qrcode-canvas" x-init="$nextTick(() => renderQr())"></div>
|
||||
</div>
|
||||
|
||||
<div style="font-size: 0.8rem; display: flex; align-items: center; justify-content: center; gap: 0.5rem; margin-top: 1rem;" class="text-muted">
|
||||
<div class="spinner" style="width: 14px; height: 14px; border-width: 2px;"></div>
|
||||
<span x-text="lang === 'ar' ? 'بانتظار إتمام المصادقة من الهاتف...' : 'Waiting for connection handshake...'"></span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template x-if="whatsappSession?.status === 'connected'">
|
||||
<div class="text-center" style="padding: 1.5rem 0;">
|
||||
<div style="font-size: 3rem; color: var(--success); margin-bottom: 0.5rem; text-shadow: 0 0 20px rgba(16,185,129,0.3);">✓</div>
|
||||
<p class="font-semibold" style="font-size: 1.1rem; color: var(--success);" x-text="lang === 'ar' ? 'الخط متصل بالكامل برقم الهاتف!' : 'Line fully linked and connected!'"></p>
|
||||
<p class="text-muted" style="font-size: 0.85rem; margin-top: 0.25rem;" x-text="lang === 'ar' ? 'الرقم متصل ويمكنه الآن إرسال الحملات ورموز التحقق واستقبال الطلبات.' : 'This line is active and ready to deliver campaign broadcasts and verification OTPs.'"></p>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OTP Test Widget Card -->
|
||||
<div class="card" style="margin: 0;">
|
||||
<h3 style="font-size: 1.1rem; font-weight: 700; margin-bottom: 1.25rem; display: flex; align-items: center; gap: 0.5rem;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
|
||||
<span>🔑</span>
|
||||
<span x-text="lang === 'ar' ? 'أداة اختبار إرسال رمز التحقق (OTP)' : 'OTP Deliverability Test Tool'"></span>
|
||||
</h3>
|
||||
|
||||
<form @submit.prevent="sendOtpTest()">
|
||||
<div class="form-group">
|
||||
<label class="form-label" x-text="lang === 'ar' ? 'خط الإرسال (WhatsApp Line)' : 'Sender Line (WhatsApp)'"></label>
|
||||
<select x-model="otpSessionId" required>
|
||||
<option value="" x-text="lang === 'ar' ? '-- اختر الخط --' : '-- Choose Line --'"></option>
|
||||
<template x-for="session in whatsappSessions" :key="session.id">
|
||||
<option :value="session.id" x-text="session.name + (session.phone ? ' (' + session.phone + ')' : ' - ' + session.status)"></option>
|
||||
</template>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" x-text="lang === 'ar' ? 'رقم الهاتف المستلم (مع رمز الدولة)' : 'Recipient Phone (with country code)'"></label>
|
||||
<input type="text" x-model="otpPhone" class="form-input" required placeholder="966500000000" id="otp-test-recipient">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" x-text="lang === 'ar' ? 'نوع الرسالة' : 'OTP Type'"></label>
|
||||
<select x-model="otpType">
|
||||
<option value="text" x-text="lang === 'ar' ? 'رسالة نصية على الواتساب' : 'Text Message'"></option>
|
||||
<option value="voice" x-text="lang === 'ar' ? 'رسالة صوتية (Voice Note OTP)' : 'Voice Note (Google TTS)'"></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary" style="width: 100%; margin-top: 1rem;" :disabled="actionLoading" id="btn-send-otp-test">
|
||||
<span x-show="!actionLoading" x-text="lang === 'ar' ? 'إرسال رمز التحقق الآن' : 'Deliver OTP Code'"></span>
|
||||
<span x-show="actionLoading" class="spinner"></span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<template x-if="otpStatusMsg">
|
||||
<div class="banner" :class="otpErrorCode ? 'banner-danger' : 'banner-success'" style="margin-top: 1rem; font-size: 0.85rem;" id="otp-test-banner">
|
||||
<span x-text="otpStatusMsg"></span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@@ -1171,6 +1226,84 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- WooCommerce Integration Section -->
|
||||
<div style="background: rgba(99, 102, 241, 0.05); border: 1px solid rgba(99, 102, 241, 0.15); border-radius: 12px; padding: 1.5rem; margin-bottom: 2rem; box-shadow: 0 4px 20px rgba(99, 102, 241, 0.05);">
|
||||
<div style="display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: 1.5rem;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
|
||||
<div style="display: flex; align-items: center; gap: 1rem; flex: 1;" :style="lang === 'ar' ? 'flex-direction: row-reverse; text-align: right;' : ''">
|
||||
<div style="background: var(--primary); width: 48px; height: 48px; border-radius: 10px; display: flex; align-items: center; justify-content: center; box-shadow: 0 0 15px rgba(99, 102, 241, 0.4);">
|
||||
<span style="font-size: 1.5rem;">⚙️</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 style="font-size: 1.2rem; margin: 0; display: flex; align-items: center; gap: 0.5rem;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
|
||||
<span x-text="lang === 'ar' ? 'ربط متجر ووكومرس (WooCommerce)' : 'WooCommerce Store Integration'"></span>
|
||||
<template x-if="woocommerceStatus && woocommerceStatus.connected">
|
||||
<span class="status-badge badge-connected" style="margin: 0; padding: 0.15rem 0.5rem; font-size: 0.7rem;" x-text="lang === 'ar' ? 'متصل' : 'Connected'"></span>
|
||||
</template>
|
||||
<template x-if="!woocommerceStatus || !woocommerceStatus.connected">
|
||||
<span class="status-badge badge-disconnected" style="margin: 0; padding: 0.15rem 0.5rem; font-size: 0.7rem;" x-text="lang === 'ar' ? 'غير متصل' : 'Disconnected'"></span>
|
||||
</template>
|
||||
</h3>
|
||||
<p class="text-muted" style="margin: 0.25rem 0 0 0; font-size: 0.85rem;" x-text="lang === 'ar' ? 'قم بربط متجر WooCommerce لإرسال إشعارات تغيير حالة الطلبات للعملاء تلقائيًا عبر الواتساب.' : 'Link your WooCommerce store to trigger automated customer notifications via WhatsApp on order events.'"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 0.75rem; align-items: center;">
|
||||
<template x-if="woocommerceLoading">
|
||||
<span class="spinner"></span>
|
||||
</template>
|
||||
<template x-if="!woocommerceLoading && woocommerceStatus && woocommerceStatus.connected">
|
||||
<div style="display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
|
||||
<div style="text-align: right;" :style="lang === 'ar' ? 'text-align: right;' : 'text-align: left;'">
|
||||
<span style="font-size: 0.8rem; color: var(--text-secondary);" x-text="lang === 'ar' ? 'المتجر المرتبط:' : 'Connected URL:'"></span>
|
||||
<strong style="display: block; font-size: 0.95rem; color: #fff;" x-text="woocommerceStatus.store_url"></strong>
|
||||
</div>
|
||||
<button @click="disconnectWooCommerce()" class="btn btn-danger" style="width: auto; font-size: 0.9rem; padding: 0.6rem 1.2rem;" id="disconnect-woo-btn">
|
||||
<span x-text="lang === 'ar' ? 'إلغاء الربط' : 'Disconnect'"></span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- WooCommerce Connect Form -->
|
||||
<template x-if="!woocommerceStatus || !woocommerceStatus.connected">
|
||||
<form @submit.prevent="connectWooCommerce()" style="margin-top: 1.5rem; border-top: 1px solid rgba(255,255,255,0.05); padding-top: 1.5rem;">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 1.25rem;">
|
||||
<div class="form-group" style="margin: 0;">
|
||||
<label class="form-label" x-text="lang === 'ar' ? 'رابط المتجر (Store URL)' : 'Store URL'"></label>
|
||||
<input type="url" class="form-input" x-model="wooForm.store_url" required placeholder="https://my-store.com">
|
||||
</div>
|
||||
<div class="form-group" style="margin: 0;">
|
||||
<label class="form-label" x-text="lang === 'ar' ? 'Consumer Key (ck_...)' : 'Consumer Key (ck_...)'"></label>
|
||||
<input type="text" class="form-input" x-model="wooForm.consumer_key" required placeholder="ck_...">
|
||||
</div>
|
||||
<div class="form-group" style="margin: 0;">
|
||||
<label class="form-label" x-text="lang === 'ar' ? 'Consumer Secret (cs_...)' : 'Consumer Secret (cs_...)'"></label>
|
||||
<input type="password" class="form-input" x-model="wooForm.consumer_secret" required placeholder="cs_...">
|
||||
</div>
|
||||
<div class="form-group" style="margin: 0;">
|
||||
<label class="form-label" x-text="lang === 'ar' ? 'Webhook Secret (اختياري)' : 'Webhook Secret (Optional)'"></label>
|
||||
<input type="text" class="form-input" x-model="wooForm.webhook_secret" placeholder="Secret code to secure webhook signature">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" style="width: auto;" :disabled="woocommerceLoading" id="connect-woo-btn">
|
||||
<span x-text="lang === 'ar' ? 'ربط ووكومرس وتفعيل الاشعارات' : 'Link WooCommerce Store'"></span>
|
||||
</button>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<!-- WooCommerce Connected Details -->
|
||||
<template x-if="woocommerceStatus && woocommerceStatus.connected">
|
||||
<div style="margin-top: 1rem; border-top: 1px solid rgba(255,255,255,0.05); padding-top: 1rem; font-size: 0.85rem;">
|
||||
<div style="background: rgba(255,255,255,0.02); border: 1px dashed var(--card-border-hover); border-radius: 8px; padding: 1rem; font-family: monospace;">
|
||||
<p style="font-weight: 600; margin-bottom: 0.25rem; color: var(--text-main);" x-text="lang === 'ar' ? 'رابط الـ Webhook الخاص بمتجرك:' : 'Delivery URL for WooCommerce Webhook:'"></p>
|
||||
<p style="word-break: break-all; color: var(--secondary);" x-text="woocommerceStatus.webhook_url"></p>
|
||||
<p style="margin-top: 0.5rem; color: var(--text-muted); font-size: 0.8rem;" x-text="lang === 'ar' ? 'قم بإنشاء Webhooks في ووكومرس للأحداث (Order Created & Order Updated) والصق هذا الرابط هناك.' : 'Create Order Created and Order Updated webhooks in WooCommerce settings using this delivery URL.'"></p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="data-table-container">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
@@ -1205,6 +1338,59 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Panel: Customer Service Team & Staff -->
|
||||
<div class="panel" x-show="activeDashboardTab === 'staff'" id="panel-staff">
|
||||
<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' ? 'فريق خدمة العملاء والوكلاء (Staff)' : 'Customer Service Agents & Staff'"></h2>
|
||||
<button class="btn btn-primary" style="width: auto;" @click="staffForm = { name: '', email: '', password: '', whatsapp_session_id: '' }; showAddStaffModal = true" id="add-agent-btn">
|
||||
<span x-text="lang === 'ar' ? '+ إضافة موظف جديد' : '+ Add CS Agent'"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="text-muted" style="margin-bottom: 1.5rem; font-size: 0.9rem;" x-text="lang === 'ar' ? 'قم بإضافة موظفي خدمة العملاء وتعيين كل منهم لرقم واتساب محدد. يستطيع كل موظف قراءة والرد على رسائل الرقم المخصص له فقط.' : 'Add agents to your team and bind them to specific WhatsApp lines. Each staff member can only view and manage chats for their assigned numbers.'"></p>
|
||||
|
||||
<div class="data-table-container">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th x-text="lang === 'ar' ? 'الاسم' : 'Agent Name'"></th>
|
||||
<th x-text="lang === 'ar' ? 'البريد الإلكتروني' : 'Email Address'"></th>
|
||||
<th x-text="lang === 'ar' ? 'رقم الواتساب المعين' : 'Assigned WhatsApp Line'"></th>
|
||||
<th style="width: 250px; text-align: center;" x-text="lang === 'ar' ? 'خيارات وتغيير التعيين' : 'Management & Assign'"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template x-for="agent in staff" :key="agent.id">
|
||||
<tr>
|
||||
<td class="font-semibold" x-text="agent.name"></td>
|
||||
<td style="font-family: monospace; font-size: 0.85rem;" x-text="agent.email"></td>
|
||||
<td>
|
||||
<span class="badge badge-primary" x-show="agent.whatsapp_session_id" x-text="agent.session_name + (agent.session_phone ? ' (' + agent.session_phone + ')' : '')"></span>
|
||||
<span class="badge badge-danger" x-show="!agent.whatsapp_session_id" x-text="lang === 'ar' ? 'غير معين' : 'Unassigned'"></span>
|
||||
</td>
|
||||
<td style="text-align: center; display: flex; gap: 0.5rem; justify-content: center; align-items: center;" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
|
||||
<!-- Assign Session Select -->
|
||||
<select style="font-size: 0.8rem; padding: 0.3rem 0.5rem; width: auto;" :value="agent.whatsapp_session_id || ''" @change="assignSessionToStaff(agent.id, $event.target.value)">
|
||||
<option value="" x-text="lang === 'ar' ? '-- بدون تعيين --' : '-- Unassigned --'"></option>
|
||||
<template x-for="session in whatsappSessions" :key="session.id">
|
||||
<option :value="session.id" x-text="session.name + (session.phone ? ' (' + session.phone + ')' : '')"></option>
|
||||
</template>
|
||||
</select>
|
||||
<button class="btn btn-danger" style="width: auto; padding: 0.3rem 0.6rem; font-size: 0.8rem; margin: 0;" @click="deleteStaff(agent.id)">
|
||||
<span x-text="lang === 'ar' ? 'حذف' : 'Delete'"></span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
<template x-if="staff.length === 0">
|
||||
<div class="empty-state" id="empty-staff-state" x-text="lang === 'ar' ? 'لم تقم بإضافة موظفي خدمة عملاء بعد.' : 'No customer service agents added yet.'"></div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1245,6 +1431,48 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Add Staff / Agent -->
|
||||
<div class="modal-overlay" x-show="showAddStaffModal" id="modal-add-staff" style="display: none;">
|
||||
<div class="modal-card">
|
||||
<div class="modal-header" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
|
||||
<h3 class="modal-title" x-text="lang === 'ar' ? 'إضافة موظف خدمة عملاء جديد' : 'Add Customer Service Agent'"></h3>
|
||||
<button class="modal-close" @click="showAddStaffModal = false">×</button>
|
||||
</div>
|
||||
<form @submit.prevent="submitAddStaff()" id="form-add-staff">
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label class="form-label" x-text="lang === 'ar' ? 'اسم الموظف' : 'Agent Name'"></label>
|
||||
<input type="text" class="form-input" x-model="staffForm.name" required placeholder="Ali Ahmed">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" x-text="lang === 'ar' ? 'البريد الإلكتروني' : 'Email Address'"></label>
|
||||
<input type="email" class="form-input" x-model="staffForm.email" required placeholder="ali@example.com">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" x-text="lang === 'ar' ? 'كلمة المرور' : 'Password'"></label>
|
||||
<input type="password" class="form-input" x-model="staffForm.password" required placeholder="******">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" x-text="lang === 'ar' ? 'تعيين خط واتساب' : 'Assign WhatsApp Line'"></label>
|
||||
<select x-model="staffForm.whatsapp_session_id">
|
||||
<option value="" x-text="lang === 'ar' ? '-- بدون تعيين --' : '-- Leave Unassigned --'"></option>
|
||||
<template x-for="session in whatsappSessions" :key="session.id">
|
||||
<option :value="session.id" x-text="session.name + (session.phone ? ' (' + session.phone + ')' : '')"></option>
|
||||
</template>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer" :style="lang === 'ar' ? 'flex-direction: row-reverse' : ''">
|
||||
<button type="button" class="btn btn-secondary" style="width: auto;" @click="showAddStaffModal = false" x-text="lang === 'ar' ? 'إلغاء' : 'Cancel'"></button>
|
||||
<button type="submit" class="btn btn-primary" style="width: auto;" :disabled="actionLoading">
|
||||
<span x-show="!actionLoading" x-text="lang === 'ar' ? 'إضافة الموظف' : 'Add Agent'"></span>
|
||||
<span x-show="actionLoading" class="spinner"></span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</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">
|
||||
@@ -1422,6 +1650,29 @@
|
||||
// Dashboard States
|
||||
activeDashboardTab: 'whatsapp',
|
||||
whatsappSession: null,
|
||||
whatsappSessions: [],
|
||||
newSessionName: '',
|
||||
staff: [],
|
||||
showAddStaffModal: false,
|
||||
staffForm: {
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
whatsapp_session_id: ''
|
||||
},
|
||||
woocommerceStatus: null,
|
||||
woocommerceLoading: false,
|
||||
wooForm: {
|
||||
store_url: '',
|
||||
consumer_key: '',
|
||||
consumer_secret: '',
|
||||
webhook_secret: ''
|
||||
},
|
||||
otpPhone: '',
|
||||
otpType: 'text',
|
||||
otpSessionId: '',
|
||||
otpStatusMsg: '',
|
||||
otpErrorCode: '',
|
||||
contacts: [],
|
||||
selectedContactIds: [],
|
||||
bulkGroupId: '',
|
||||
@@ -1582,6 +1833,9 @@
|
||||
this.user = null;
|
||||
this.isLoggedIn = false;
|
||||
this.whatsappSession = null;
|
||||
this.whatsappSessions = [];
|
||||
this.staff = [];
|
||||
this.woocommerceStatus = null;
|
||||
this.contacts = [];
|
||||
this.templates = [];
|
||||
this.campaigns = [];
|
||||
@@ -1592,8 +1846,10 @@
|
||||
},
|
||||
|
||||
initializeDashboard() {
|
||||
this.fetchWhatsappSessions();
|
||||
this.fetchWhatsappStatus();
|
||||
this.fetchSallaStatus();
|
||||
this.fetchWooCommerceStatus();
|
||||
// Set up persistent background status check
|
||||
this.startPolling();
|
||||
|
||||
@@ -1615,8 +1871,9 @@
|
||||
|
||||
async fetchWhatsappStatus() {
|
||||
if (!this.token) return;
|
||||
const queryParam = this.whatsappSession ? `?session_id=${this.whatsappSession.id}` : '';
|
||||
try {
|
||||
const response = await fetch('/api/whatsapp/status', {
|
||||
const response = await fetch(`/api/whatsapp/status${queryParam}`, {
|
||||
headers: { 'Authorization': `Bearer ${this.token}` }
|
||||
});
|
||||
const data = await response.json();
|
||||
@@ -1625,13 +1882,88 @@
|
||||
if (this.whatsappSession && this.whatsappSession.status === 'waiting_qr') {
|
||||
this.$nextTick(() => this.renderQr());
|
||||
}
|
||||
|
||||
if (this.whatsappSessions && this.whatsappSessions.length > 0 && this.whatsappSession) {
|
||||
const idx = this.whatsappSessions.findIndex(s => s.id === this.whatsappSession.id);
|
||||
if (idx !== -1) {
|
||||
this.whatsappSessions[idx] = this.whatsappSession;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to retrieve session status:', err);
|
||||
}
|
||||
},
|
||||
|
||||
async connectWhatsapp() {
|
||||
async fetchWhatsappSessions() {
|
||||
if (!this.token) return;
|
||||
try {
|
||||
const response = await fetch('/api/whatsapp/sessions', {
|
||||
headers: { 'Authorization': `Bearer ${this.token}` }
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
this.whatsappSessions = data.data || [];
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to retrieve sessions list:', err);
|
||||
}
|
||||
},
|
||||
|
||||
async createWhatsappSession() {
|
||||
this.actionLoading = true;
|
||||
try {
|
||||
const response = await fetch('/api/whatsapp/sessions', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.token}`
|
||||
},
|
||||
body: JSON.stringify({ name: this.newSessionName })
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
this.newSessionName = '';
|
||||
await this.fetchWhatsappSessions();
|
||||
} else {
|
||||
alert(data.message || 'Failed to create WhatsApp session');
|
||||
}
|
||||
} catch (err) {
|
||||
alert('Error communicating with backend Gateway API.');
|
||||
} finally {
|
||||
this.actionLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async deleteWhatsappSession(sessionId) {
|
||||
if (!confirm('Are you sure you want to delete this WhatsApp session? This will remove all associated connection settings.')) return;
|
||||
this.actionLoading = true;
|
||||
try {
|
||||
const response = await fetch('/api/whatsapp/sessions', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.token}`
|
||||
},
|
||||
body: JSON.stringify({ session_id: sessionId })
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
if (this.whatsappSession && this.whatsappSession.id === sessionId) {
|
||||
this.whatsappSession = null;
|
||||
}
|
||||
await this.fetchWhatsappSessions();
|
||||
} else {
|
||||
alert(data.message || 'Failed to delete session');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error deleting session:', err);
|
||||
} finally {
|
||||
this.actionLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async connectWhatsapp(sessionId) {
|
||||
this.actionLoading = true;
|
||||
try {
|
||||
const response = await fetch('/api/whatsapp/qr', {
|
||||
@@ -1639,10 +1971,16 @@
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.token}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
},
|
||||
body: JSON.stringify({ session_id: sessionId })
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
const found = this.whatsappSessions.find(s => s.id === sessionId);
|
||||
if (found) {
|
||||
this.whatsappSession = found;
|
||||
this.whatsappSession.status = 'connecting';
|
||||
}
|
||||
await this.fetchWhatsappStatus();
|
||||
} else {
|
||||
alert(data.message || 'Failed to initialize session');
|
||||
@@ -1654,8 +1992,8 @@
|
||||
}
|
||||
},
|
||||
|
||||
async disconnectWhatsapp() {
|
||||
if (!confirm('Are you sure you want to disconnect your WhatsApp link?')) return;
|
||||
async disconnectWhatsapp(sessionId) {
|
||||
if (!confirm('Are you sure you want to disconnect this WhatsApp link?')) return;
|
||||
this.actionLoading = true;
|
||||
try {
|
||||
const response = await fetch('/api/whatsapp/disconnect', {
|
||||
@@ -1663,14 +2001,214 @@
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.token}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ session_id: sessionId })
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
await this.fetchWhatsappSessions();
|
||||
if (this.whatsappSession && this.whatsappSession.id === sessionId) {
|
||||
await this.fetchWhatsappStatus();
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
this.actionLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// Customer Service Staff methods
|
||||
async fetchStaff() {
|
||||
this.staffLoading = true;
|
||||
try {
|
||||
const response = await fetch('/api/staff', {
|
||||
headers: { 'Authorization': `Bearer ${this.token}` }
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
this.staff = data.data || [];
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error fetching staff list:', err);
|
||||
} finally {
|
||||
this.staffLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async submitAddStaff() {
|
||||
this.actionLoading = true;
|
||||
try {
|
||||
const response = await fetch('/api/staff', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.token}`
|
||||
},
|
||||
body: JSON.stringify(this.staffForm)
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
this.showAddStaffModal = false;
|
||||
this.staffForm = { name: '', email: '', password: '', whatsapp_session_id: '' };
|
||||
await this.fetchStaff();
|
||||
} else {
|
||||
const errs = data.errors || {};
|
||||
const firstErr = Object.values(errs)[0]?.[0] || data.error || 'Failed to create agent';
|
||||
alert(firstErr);
|
||||
}
|
||||
} catch (err) {
|
||||
alert('Network error while adding agent');
|
||||
} finally {
|
||||
this.actionLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async deleteStaff(agentId) {
|
||||
if (!confirm('Are you sure you want to remove this customer service agent?')) return;
|
||||
this.actionLoading = true;
|
||||
try {
|
||||
const response = await fetch('/api/staff', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.token}`
|
||||
},
|
||||
body: JSON.stringify({ agent_id: agentId })
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
await this.fetchStaff();
|
||||
} else {
|
||||
alert(data.error || 'Failed to delete agent');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
this.actionLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async assignSessionToStaff(agentId, sessionId) {
|
||||
try {
|
||||
const response = await fetch('/api/staff/assign', {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
agent_id: agentId,
|
||||
whatsapp_session_id: sessionId ? parseInt(sessionId) : null
|
||||
})
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
await this.fetchStaff();
|
||||
} else {
|
||||
alert(data.error || 'Failed to assign session');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error assigning session:', err);
|
||||
}
|
||||
},
|
||||
|
||||
// WooCommerce Integration methods
|
||||
async fetchWooCommerceStatus() {
|
||||
try {
|
||||
const response = await fetch('/api/integrations/woocommerce/status', {
|
||||
headers: { 'Authorization': `Bearer ${this.token}` }
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
this.woocommerceStatus = data;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error fetching WooCommerce status:', err);
|
||||
}
|
||||
},
|
||||
|
||||
async connectWooCommerce() {
|
||||
this.woocommerceLoading = true;
|
||||
try {
|
||||
const response = await fetch('/api/integrations/woocommerce/connect', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.token}`
|
||||
},
|
||||
body: JSON.stringify(this.wooForm)
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
await this.fetchWooCommerceStatus();
|
||||
} else {
|
||||
alert(data.message || 'Failed to connect WooCommerce store');
|
||||
}
|
||||
} catch (err) {
|
||||
alert('Network error while connecting WooCommerce');
|
||||
} finally {
|
||||
this.woocommerceLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
async disconnectWooCommerce() {
|
||||
if (!confirm('Are you sure you want to disconnect WooCommerce integration? Webhooks will no longer notify customers.')) return;
|
||||
this.woocommerceLoading = true;
|
||||
try {
|
||||
const response = await fetch('/api/integrations/woocommerce/disconnect', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.token}`
|
||||
}
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
await this.fetchWhatsappStatus();
|
||||
this.wooForm = { store_url: '', consumer_key: '', consumer_secret: '', webhook_secret: '' };
|
||||
await this.fetchWooCommerceStatus();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
this.woocommerceLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// OTP Testing Tool methods
|
||||
async sendOtpTest() {
|
||||
if (!this.otpSessionId) {
|
||||
alert('Please select a WhatsApp line to send from.');
|
||||
return;
|
||||
}
|
||||
this.actionLoading = true;
|
||||
this.otpStatusMsg = '';
|
||||
this.otpErrorCode = '';
|
||||
try {
|
||||
const response = await fetch('/api/otp/send', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${this.token}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
phone: this.otpPhone,
|
||||
type: this.otpType,
|
||||
session_id: parseInt(this.otpSessionId)
|
||||
})
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
this.otpStatusMsg = this.lang === 'ar'
|
||||
? `✓ تم إرسال رمز التحقق بنجاح! الرمز المرسل هو: ${data.code}`
|
||||
: `✓ OTP delivered successfully! Code sent: ${data.code}`;
|
||||
} else {
|
||||
this.otpErrorCode = 'error';
|
||||
this.otpStatusMsg = data.error || 'Failed to deliver OTP';
|
||||
}
|
||||
} catch (err) {
|
||||
this.otpErrorCode = 'error';
|
||||
this.otpStatusMsg = 'Network error while delivering OTP';
|
||||
} finally {
|
||||
this.actionLoading = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user