Deploy: 2026-05-22 00:54:36
This commit is contained in:
@@ -618,6 +618,14 @@
|
||||
}
|
||||
.font-semibold { font-weight: 600; }
|
||||
.text-muted { color: var(--text-secondary); }
|
||||
@keyframes pulse-red {
|
||||
0% { transform: scale(0.85); opacity: 0.5; }
|
||||
50% { transform: scale(1.15); opacity: 1; }
|
||||
100% { transform: scale(0.85); opacity: 0.5; }
|
||||
}
|
||||
.recording-pulse {
|
||||
animation: pulse-red 1.2s infinite;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body x-data="app()" x-init="checkAuth()">
|
||||
@@ -1005,8 +1013,41 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="chatbot-prompt-group">
|
||||
<label class="form-label" x-text="chatbotSettings.trigger_type === 'gemini_ai' ? 'System Instruction Prompt' : 'Predefined Auto-Reply Message'"></label>
|
||||
<textarea class="form-input" x-model="chatbotSettings.ai_prompt" rows="5" required :placeholder="chatbotSettings.trigger_type === 'gemini_ai' ? 'You are a helpful customer support assistant... Respond concisely and politely in Arabic.' : 'Thank you for reaching out!'" id="chatbot-prompt-input"></textarea>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; flex-wrap: wrap; gap: 0.5rem;">
|
||||
<label class="form-label" x-text="chatbotSettings.trigger_type === 'gemini_ai' ? 'System Instruction Prompt' : 'Predefined Auto-Reply Message'" style="margin-bottom: 0;"></label>
|
||||
<div x-show="chatbotSettings.trigger_type === 'gemini_ai'" style="display: flex; gap: 0.35rem; flex-wrap: wrap; align-items: center;">
|
||||
<button type="button" class="btn btn-secondary" style="font-size: 0.75rem; padding: 0.25rem 0.5rem; width: auto;" @click="loadPromptTemplate('nabeh')">قالب تطبيق نبيه (سوري)</button>
|
||||
<button type="button" class="btn btn-secondary" style="font-size: 0.75rem; padding: 0.25rem 0.5rem; width: auto;" @click="loadPromptTemplate('store')">قالب متجر إلكتروني</button>
|
||||
<button type="button" class="btn btn-secondary" style="font-size: 0.75rem; padding: 0.25rem 0.5rem; width: auto;" @click="loadPromptTemplate('general')">قالب عام (إنجليزي)</button>
|
||||
|
||||
<!-- Voice Recording Button -->
|
||||
<button type="button" class="btn" :class="isRecording ? 'btn-danger' : 'btn-secondary'" style="font-size: 0.75rem; padding: 0.25rem 0.5rem; width: auto; display: flex; align-items: center; gap: 0.25rem;" @click="toggleVoiceRecording()">
|
||||
<svg x-show="!isRecording" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" x2="12" y1="19" y2="22"/></svg>
|
||||
<span x-show="isRecording" class="recording-pulse" style="display: inline-block; width: 8px; height: 8px; background-color: white; border-radius: 50%;"></span>
|
||||
<span x-text="isRecording ? 'إيقاف التسجيل (' + formatRecordTime(recordingTime) + ')' : '🎤 تسجيل الإرشادات'"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<textarea class="form-input" x-model="chatbotSettings.ai_prompt" rows="7" required :placeholder="chatbotSettings.trigger_type === 'gemini_ai' ? 'أدخل تعليمات الذكاء الاصطناعي هنا...' : 'شكراً لتواصلك معنا!'" id="chatbot-prompt-input" :disabled="generatingFromVoice"></textarea>
|
||||
|
||||
<!-- Voice Processing Loading Status -->
|
||||
<div x-show="generatingFromVoice" style="margin-top: 0.5rem; display: flex; align-items: center; gap: 0.5rem; font-size: 0.8rem; color: var(--primary-accent);" dir="rtl">
|
||||
<span class="spinner" style="border-top-color: var(--primary-accent); display: inline-block;"></span>
|
||||
<span>جاري صياغة التوجيهات من صوتك باستخدام الذكاء الاصطناعي... يرجى الانتظار</span>
|
||||
</div>
|
||||
|
||||
<!-- Hints & Guidelines for Merchant -->
|
||||
<div x-show="chatbotSettings.trigger_type === 'gemini_ai'" style="margin-top: 0.75rem; padding: 0.75rem; background: rgba(59, 130, 246, 0.05); border: 1px dashed rgba(59, 130, 246, 0.3); border-radius: 8px; font-size: 0.8rem; color: var(--text-secondary);" dir="rtl">
|
||||
<strong style="color: var(--text-primary); display: block; margin-bottom: 0.35rem; display: flex; align-items: center; gap: 0.35rem;">
|
||||
💡 نصائح وتوجيهات لكتابة تعليمات ممتازة:
|
||||
</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>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary" :disabled="actionLoading" id="chatbot-save-btn">
|
||||
@@ -1209,6 +1250,14 @@
|
||||
|
||||
groups: [],
|
||||
|
||||
// Voice Recording States
|
||||
isRecording: false,
|
||||
recordingTime: 0,
|
||||
recordingIntervalId: null,
|
||||
mediaRecorder: null,
|
||||
audioChunks: [],
|
||||
generatingFromVoice: false,
|
||||
|
||||
chatbotSettings: {
|
||||
is_active: '0',
|
||||
trigger_type: 'keyword',
|
||||
@@ -1570,6 +1619,37 @@
|
||||
}
|
||||
},
|
||||
|
||||
loadPromptTemplate(type) {
|
||||
if (type === 'nabeh') {
|
||||
this.chatbotSettings.ai_prompt = `أنتِ "سارة"، موظفة خدمة العملاء الافتراضية الذكية والودودة لتطبيق "نبيه" (Nabeh).
|
||||
مهمتكِ هي مساعدة المستخدمين والإجابة على استفساراتهم بلطف وأدب بالمسائل التقنية والتجارية المتعلقة بالتطبيق.
|
||||
|
||||
اتبعي القواعد التالية بدقة عند الرد:
|
||||
1. في أول رسالة تواصل مع العميل، عرّفي عن نفسكِ دائماً بالقول: "معك سارة من فريق تطبيق نبيه، كيف بقدر أساعدك اليوم؟" (باللهجة السورية الودية).
|
||||
2. تحدثي دائماً باللهجة السورية اللطيفة والترحيبية والودية عند التحدث باللغة العربية.
|
||||
3. إذا سأل العميل أو تحدث باللغة الإنجليزية، فتحدثي معه باللغة الإنجليزية بشكل احترافي وودي وحافظي على نفس الهوية والمساعدة.
|
||||
4. كوني مختصرة ومباشرة في إجاباتكِ وتجنبي الإطالة غير الضرورية.
|
||||
5. ساعدي المستخدمين في فهم ميزات تطبيق "نبيه" لإدارة وتسهيل حملات واتساب والردود الذكية.`;
|
||||
} else if (type === 'store') {
|
||||
this.chatbotSettings.ai_prompt = `أنت موظف خدمة عملاء ذكي ومرحب لمتجرنا الإلكتروني.
|
||||
مهمتك هي مساعدة العملاء والإجابة على استفساراتهم حول المنتجات، الشحن، والطلبات بلطف وأدب.
|
||||
|
||||
اتبع القواعد التالية عند الرد:
|
||||
1. رحب بالعميل دائماً بشكل ودّي وسريع في بداية المحادثة.
|
||||
2. أجب عن الأسئلة بدقة واختصار.
|
||||
3. إذا سأل العميل عن حالة طلب، اطلب منه تزويدك برقم الطلب للتحقق منه.
|
||||
4. حافظ على نبرة إيجابية ومحترفة.`;
|
||||
} else if (type === 'general') {
|
||||
this.chatbotSettings.ai_prompt = `You are a professional and helpful customer support assistant.
|
||||
Your goal is to assist users with their general inquiries, troubleshoot issues, and provide helpful guidance.
|
||||
|
||||
Guidelines:
|
||||
1. Be polite, clear, and professional.
|
||||
2. Provide concise and accurate information.
|
||||
3. If you do not know the answer, politely ask the user to wait while you check with the team.`;
|
||||
}
|
||||
},
|
||||
|
||||
async saveChatbotSettings() {
|
||||
this.actionLoading = true;
|
||||
try {
|
||||
@@ -1603,6 +1683,98 @@
|
||||
}
|
||||
},
|
||||
|
||||
formatRecordTime(seconds) {
|
||||
const m = Math.floor(seconds / 60);
|
||||
const s = seconds % 60;
|
||||
return `${m}:${s < 10 ? '0' : ''}${s}`;
|
||||
},
|
||||
|
||||
async toggleVoiceRecording() {
|
||||
if (this.isRecording) {
|
||||
await this.stopVoiceRecording();
|
||||
} else {
|
||||
await this.startVoiceRecording();
|
||||
}
|
||||
},
|
||||
|
||||
async startVoiceRecording() {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
this.audioChunks = [];
|
||||
this.mediaRecorder = new MediaRecorder(stream);
|
||||
|
||||
this.mediaRecorder.ondataavailable = (event) => {
|
||||
if (event.data && event.data.size > 0) {
|
||||
this.audioChunks.push(event.data);
|
||||
}
|
||||
};
|
||||
|
||||
this.mediaRecorder.onstop = async () => {
|
||||
const mimeType = this.mediaRecorder.mimeType || 'audio/webm';
|
||||
const audioBlob = new Blob(this.audioChunks, { type: mimeType });
|
||||
// Stop all audio tracks to release microphone
|
||||
stream.getTracks().forEach(track => track.stop());
|
||||
await this.sendAudioToBackend(audioBlob);
|
||||
};
|
||||
|
||||
this.mediaRecorder.start();
|
||||
this.isRecording = true;
|
||||
this.recordingTime = 0;
|
||||
|
||||
this.recordingIntervalId = setInterval(() => {
|
||||
this.recordingTime++;
|
||||
if (this.recordingTime >= 180) { // 3 minutes max limit
|
||||
this.stopVoiceRecording();
|
||||
}
|
||||
}, 1000);
|
||||
} catch (err) {
|
||||
console.error('Error starting audio recording:', err);
|
||||
alert('تعذر الوصول إلى الميكروفون. يرجى التحقق من صلاحيات المتصفح.');
|
||||
}
|
||||
},
|
||||
|
||||
async stopVoiceRecording() {
|
||||
if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
|
||||
this.mediaRecorder.stop();
|
||||
}
|
||||
if (this.recordingIntervalId) {
|
||||
clearInterval(this.recordingIntervalId);
|
||||
this.recordingIntervalId = null;
|
||||
}
|
||||
this.isRecording = false;
|
||||
},
|
||||
|
||||
async sendAudioToBackend(audioBlob) {
|
||||
this.generatingFromVoice = true;
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('audio', audioBlob, 'voice_instruction.webm');
|
||||
|
||||
const response = await fetch('/api/chatbot/generate-prompt-from-audio', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.token}`
|
||||
},
|
||||
body: formData
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
this.chatbotSettings.ai_prompt = data.prompt;
|
||||
// Automatically save the generated prompt in database
|
||||
await this.saveChatbotSettings();
|
||||
alert('تم توليد التوجيهات وحفظها بنجاح!');
|
||||
} else {
|
||||
alert(data.message || 'فشلت عملية صياغة التوجيهات من الصوت.');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error sending audio to backend:', err);
|
||||
alert('حدث خطأ أثناء التواصل مع السيرفر لصياغة التوجيهات.');
|
||||
} finally {
|
||||
this.generatingFromVoice = false;
|
||||
}
|
||||
},
|
||||
|
||||
openAddContactModal() {
|
||||
this.contactName = '';
|
||||
this.contactPhone = '';
|
||||
|
||||
@@ -66,6 +66,7 @@ $router->post('/api/campaigns', [\App\Controllers\CampaignController::class,
|
||||
|
||||
$router->get('/api/chatbot/rules', [\App\Controllers\ChatbotController::class, 'index'], [\App\Middlewares\AuthMiddleware::class]);
|
||||
$router->post('/api/chatbot/rules',[\App\Controllers\ChatbotController::class, 'store'], [\App\Middlewares\AuthMiddleware::class]);
|
||||
$router->post('/api/chatbot/generate-prompt-from-audio', [\App\Controllers\ChatbotController::class, 'generatePromptFromAudio'], [\App\Middlewares\AuthMiddleware::class]);
|
||||
|
||||
// 4. Dispatch the request
|
||||
$router->dispatch($request, $response);
|
||||
|
||||
Reference in New Issue
Block a user