adding chat pot
This commit is contained in:
parent
290e13038f
commit
f5e53da1b1
BIN
ai/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
ai/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
ai/__pycache__/local_ai_api.cpython-311.pyc
Normal file
BIN
ai/__pycache__/local_ai_api.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -21,6 +21,9 @@
|
|||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||||
|
|
||||||
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={{ deployment_timestamp }}">
|
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={{ deployment_timestamp }}">
|
||||||
|
<!-- Chatbot Widget CSS -->
|
||||||
|
<link rel="stylesheet" href="{% static 'css/chatbot.css' %}?v={{ deployment_timestamp }}">
|
||||||
|
|
||||||
{% block head %}{% endblock %}
|
{% block head %}{% endblock %}
|
||||||
<style>
|
<style>
|
||||||
body { font-family: 'Outfit', sans-serif; background-color: #f8f9fa; }
|
body { font-family: 'Outfit', sans-serif; background-color: #f8f9fa; }
|
||||||
@ -185,8 +188,40 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<!-- Chatbot Widget HTML -->
|
||||||
|
<div id="chatbot-widget">
|
||||||
|
<button id="chatbot-button" title="{% trans 'Chat with MASAR AI' %}">
|
||||||
|
<i class="fa-solid fa-robot"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div id="chatbot-window">
|
||||||
|
<div id="chatbot-header">
|
||||||
|
<h5>MASAR AI</h5>
|
||||||
|
<button id="chatbot-close">×</button>
|
||||||
|
</div>
|
||||||
|
<div id="chatbot-messages">
|
||||||
|
<div class="chat-message bot">
|
||||||
|
{% trans "Hello! I am MASAR, your AI assistant. How can I help you today?" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="chatbot-input-container">
|
||||||
|
<input type="text" id="chatbot-input" placeholder="{% trans 'Type a message...' %}">
|
||||||
|
<button id="chatbot-send">
|
||||||
|
<i class="fa-solid fa-paper-plane"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="px-3 pb-2 text-center">
|
||||||
|
<a href="https://wa.me/{{ app_settings.contact_phone|default:'' }}" target="_blank" class="small text-muted text-decoration-none">
|
||||||
|
<i class="fa-brands fa-whatsapp text-success"></i> {% trans "Talk to a Human" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Bootstrap 5 JS Bundle -->
|
<!-- Bootstrap 5 JS Bundle -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<!-- Chatbot Widget JS -->
|
||||||
|
<script src="{% static 'js/chatbot.js' %}?v={{ deployment_timestamp }}"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@ -33,4 +33,5 @@ urlpatterns = [
|
|||||||
path("admin/financials/", views.admin_financials, name="admin_financials"),
|
path("admin/financials/", views.admin_financials, name="admin_financials"),
|
||||||
path("admin/refund/<str:receipt_number>/", views.issue_refund, name="issue_refund"),
|
path("admin/refund/<str:receipt_number>/", views.issue_refund, name="issue_refund"),
|
||||||
path("admin/settings/", views.admin_app_settings, name="admin_app_settings"),
|
path("admin/settings/", views.admin_app_settings, name="admin_app_settings"),
|
||||||
|
path("api/chat/", views.chat_api, name="chat_api"),
|
||||||
]
|
]
|
||||||
@ -7,7 +7,7 @@ from django.utils import timezone
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from .models import (
|
from .models import (
|
||||||
Profile, Truck, Shipment, Bid, Message, OTPCode, Country, City,
|
Profile, Truck, Shipment, Bid, Message, OTPCode, Country, City,
|
||||||
AppSetting, Banner, HomeSection, Transaction, ContactMessage, Testimonial
|
AppSetting, Banner, HomeSection, Transaction, ContactMessage, Testimonial, WhatsAppConfig
|
||||||
)
|
)
|
||||||
from .forms import (
|
from .forms import (
|
||||||
TruckForm, ShipmentForm, BidForm, UserRegistrationForm,
|
TruckForm, ShipmentForm, BidForm, UserRegistrationForm,
|
||||||
@ -23,8 +23,10 @@ from django.contrib.auth.forms import AuthenticationForm
|
|||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse, JsonResponse
|
||||||
from django.contrib.sites.shortcuts import get_current_site
|
from django.contrib.sites.shortcuts import get_current_site
|
||||||
|
from django.utils.translation import get_language
|
||||||
|
from ai.local_ai_api import LocalAIApi
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@ -778,4 +780,52 @@ def contact(request):
|
|||||||
return render(request, 'core/contact.html', {
|
return render(request, 'core/contact.html', {
|
||||||
'form': form,
|
'form': form,
|
||||||
'app_settings': app_settings
|
'app_settings': app_settings
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
def chat_api(request):
|
||||||
|
if request.method != 'POST':
|
||||||
|
return JsonResponse({'success': False, 'error': 'Method not allowed'}, status=405)
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.loads(request.body)
|
||||||
|
user_message = data.get('message', '')
|
||||||
|
if not user_message:
|
||||||
|
return JsonResponse({'success': False, 'error': 'Message is empty'})
|
||||||
|
|
||||||
|
# Maintain conversation history in session
|
||||||
|
history = request.session.get('chat_history', [])
|
||||||
|
|
||||||
|
# System prompt to define the bot's personality
|
||||||
|
system_prompt = {
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are MASAR, the AI assistant for MASAR CARGO, a logistics and trucking marketplace in Oman. "
|
||||||
|
"You help shippers find trucks and truck owners find shipments. "
|
||||||
|
"Answer questions politely and concisely. If the user asks for something you can't do, "
|
||||||
|
"suggest contacting support via WhatsApp. "
|
||||||
|
"Current language: " + (get_language() or 'en')
|
||||||
|
}
|
||||||
|
|
||||||
|
messages_input = [system_prompt] + history + [{"role": "user", "content": user_message}]
|
||||||
|
|
||||||
|
response = LocalAIApi.create_response({
|
||||||
|
"input": messages_input
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.get("success"):
|
||||||
|
ai_reply = LocalAIApi.extract_text(response)
|
||||||
|
|
||||||
|
# Update history
|
||||||
|
history.append({"role": "user", "content": user_message})
|
||||||
|
history.append({"role": "assistant", "content": ai_reply})
|
||||||
|
# Keep only last 10 messages for context efficiency
|
||||||
|
request.session['chat_history'] = history[-10:]
|
||||||
|
|
||||||
|
return JsonResponse({'success': True, 'reply': ai_reply})
|
||||||
|
else:
|
||||||
|
logger.error(f"AI Error: {response.get('error')}")
|
||||||
|
return JsonResponse({'success': False, 'error': 'AI failed to respond'})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Chat API Error: {str(e)}")
|
||||||
|
return JsonResponse({'success': False, 'error': str(e)})
|
||||||
|
|||||||
Binary file not shown.
@ -2093,3 +2093,15 @@ msgstr "حدث خطأ أثناء تحديث شاحنتك. يرجى التحقق
|
|||||||
|
|
||||||
#~ msgid "Contact for Renewal"
|
#~ msgid "Contact for Renewal"
|
||||||
#~ msgstr "الاتصال للتجديد"
|
#~ msgstr "الاتصال للتجديد"
|
||||||
|
|
||||||
|
msgid "Chat with MASAR AI"
|
||||||
|
msgstr "تحدث مع مسار الذكي"
|
||||||
|
|
||||||
|
msgid "Hello! I am MASAR, your AI assistant. How can I help you today?"
|
||||||
|
msgstr "أهلاً بك! أنا مسار، مساعدك الذكي. كيف يمكنني مساعدتك اليوم؟"
|
||||||
|
|
||||||
|
msgid "Type a message..."
|
||||||
|
msgstr "اكتب رسالة..."
|
||||||
|
|
||||||
|
msgid "Talk to a Human"
|
||||||
|
msgstr "تحدث مع شخص حقيقي"
|
||||||
|
|||||||
165
static/css/chatbot.css
Normal file
165
static/css/chatbot.css
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
/* Chatbot Floating Widget Styles */
|
||||||
|
#chatbot-widget {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 1000;
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-button {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, #007bff, #0056b3);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-button:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
box-shadow: 0 6px 20px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-button svg {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-window {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 80px;
|
||||||
|
right: 0;
|
||||||
|
width: 350px;
|
||||||
|
height: 500px;
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.15);
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-window.active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-header {
|
||||||
|
background: linear-gradient(135deg, #007bff, #0056b3);
|
||||||
|
color: white;
|
||||||
|
padding: 15px 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-header h5 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-close {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-messages {
|
||||||
|
flex: 1;
|
||||||
|
padding: 15px;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message {
|
||||||
|
max-width: 80%;
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-radius: 15px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message.bot {
|
||||||
|
align-self: flex-start;
|
||||||
|
background: #f0f2f5;
|
||||||
|
color: #333;
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message.user {
|
||||||
|
align-self: flex-end;
|
||||||
|
background: #007bff;
|
||||||
|
color: white;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-input-container {
|
||||||
|
padding: 15px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-input {
|
||||||
|
flex: 1;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 8px 15px;
|
||||||
|
outline: none;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-send {
|
||||||
|
background: #007bff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RTL Support */
|
||||||
|
[dir="rtl"] #chatbot-widget {
|
||||||
|
right: auto;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] #chatbot-window {
|
||||||
|
right: auto;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .chat-message.bot {
|
||||||
|
border-bottom-left-radius: 15px;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .chat-message.user {
|
||||||
|
border-bottom-right-radius: 15px;
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typing-indicator {
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #888;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
99
static/js/chatbot.js
Normal file
99
static/js/chatbot.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const chatbotButton = document.getElementById('chatbot-button');
|
||||||
|
const chatbotWindow = document.getElementById('chatbot-window');
|
||||||
|
const chatbotClose = document.getElementById('chatbot-close');
|
||||||
|
const chatbotInput = document.getElementById('chatbot-input');
|
||||||
|
const chatbotSend = document.getElementById('chatbot-send');
|
||||||
|
const chatbotMessages = document.getElementById('chatbot-messages');
|
||||||
|
|
||||||
|
if (!chatbotButton) return;
|
||||||
|
|
||||||
|
// Toggle window
|
||||||
|
chatbotButton.addEventListener('click', () => {
|
||||||
|
chatbotWindow.classList.toggle('active');
|
||||||
|
if (chatbotWindow.classList.contains('active')) {
|
||||||
|
chatbotInput.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
chatbotClose.addEventListener('click', () => {
|
||||||
|
chatbotWindow.classList.remove('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send message function
|
||||||
|
async function sendMessage() {
|
||||||
|
const message = chatbotInput.value.trim();
|
||||||
|
if (!message) return;
|
||||||
|
|
||||||
|
// Add user message to UI
|
||||||
|
appendMessage('user', message);
|
||||||
|
chatbotInput.value = '';
|
||||||
|
|
||||||
|
// Add typing indicator
|
||||||
|
const typingId = 'typing-' + Date.now();
|
||||||
|
const typingDiv = document.createElement('div');
|
||||||
|
typingDiv.id = typingId;
|
||||||
|
typingDiv.className = 'typing-indicator';
|
||||||
|
typingDiv.innerText = document.documentElement.lang === 'ar' ? 'جاري الكتابة...' : 'Typing...';
|
||||||
|
chatbotMessages.appendChild(typingDiv);
|
||||||
|
chatbotMessages.scrollTop = chatbotMessages.scrollHeight;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/chat/', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ message: message })
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
// Remove typing indicator
|
||||||
|
const typingElement = document.getElementById(typingId);
|
||||||
|
if (typingElement) typingElement.remove();
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
appendMessage('bot', data.reply);
|
||||||
|
} else {
|
||||||
|
appendMessage('bot', 'Sorry, I encountered an error. Please try again later.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
const typingElement = document.getElementById(typingId);
|
||||||
|
if (typingElement) typingElement.remove();
|
||||||
|
appendMessage('bot', 'Network error. Please check your connection.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendMessage(role, text) {
|
||||||
|
const msgDiv = document.createElement('div');
|
||||||
|
msgDiv.className = `chat-message ${role}`;
|
||||||
|
msgDiv.innerText = text;
|
||||||
|
chatbotMessages.appendChild(msgDiv);
|
||||||
|
chatbotMessages.scrollTop = chatbotMessages.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event listeners for sending
|
||||||
|
chatbotSend.addEventListener('click', sendMessage);
|
||||||
|
chatbotInput.addEventListener('keypress', (e) => {
|
||||||
|
if (e.key === 'Enter') sendMessage();
|
||||||
|
});
|
||||||
|
|
||||||
|
// CSRF helper
|
||||||
|
function getCookie(name) {
|
||||||
|
let cookieValue = null;
|
||||||
|
if (document.cookie && document.cookie !== '') {
|
||||||
|
const cookies = document.cookie.split(';');
|
||||||
|
for (let i = 0; i < cookies.length; i++) {
|
||||||
|
const cookie = cookies[i].trim();
|
||||||
|
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||||||
|
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cookieValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
165
staticfiles/css/chatbot.css
Normal file
165
staticfiles/css/chatbot.css
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
/* Chatbot Floating Widget Styles */
|
||||||
|
#chatbot-widget {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 1000;
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-button {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, #007bff, #0056b3);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-button:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
box-shadow: 0 6px 20px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-button svg {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-window {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 80px;
|
||||||
|
right: 0;
|
||||||
|
width: 350px;
|
||||||
|
height: 500px;
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.15);
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-window.active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-header {
|
||||||
|
background: linear-gradient(135deg, #007bff, #0056b3);
|
||||||
|
color: white;
|
||||||
|
padding: 15px 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-header h5 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-close {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-messages {
|
||||||
|
flex: 1;
|
||||||
|
padding: 15px;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message {
|
||||||
|
max-width: 80%;
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-radius: 15px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message.bot {
|
||||||
|
align-self: flex-start;
|
||||||
|
background: #f0f2f5;
|
||||||
|
color: #333;
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message.user {
|
||||||
|
align-self: flex-end;
|
||||||
|
background: #007bff;
|
||||||
|
color: white;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-input-container {
|
||||||
|
padding: 15px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-input {
|
||||||
|
flex: 1;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 8px 15px;
|
||||||
|
outline: none;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chatbot-send {
|
||||||
|
background: #007bff;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RTL Support */
|
||||||
|
[dir="rtl"] #chatbot-widget {
|
||||||
|
right: auto;
|
||||||
|
left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] #chatbot-window {
|
||||||
|
right: auto;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .chat-message.bot {
|
||||||
|
border-bottom-left-radius: 15px;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir="rtl"] .chat-message.user {
|
||||||
|
border-bottom-right-radius: 15px;
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typing-indicator {
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #888;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
99
staticfiles/js/chatbot.js
Normal file
99
staticfiles/js/chatbot.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const chatbotButton = document.getElementById('chatbot-button');
|
||||||
|
const chatbotWindow = document.getElementById('chatbot-window');
|
||||||
|
const chatbotClose = document.getElementById('chatbot-close');
|
||||||
|
const chatbotInput = document.getElementById('chatbot-input');
|
||||||
|
const chatbotSend = document.getElementById('chatbot-send');
|
||||||
|
const chatbotMessages = document.getElementById('chatbot-messages');
|
||||||
|
|
||||||
|
if (!chatbotButton) return;
|
||||||
|
|
||||||
|
// Toggle window
|
||||||
|
chatbotButton.addEventListener('click', () => {
|
||||||
|
chatbotWindow.classList.toggle('active');
|
||||||
|
if (chatbotWindow.classList.contains('active')) {
|
||||||
|
chatbotInput.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
chatbotClose.addEventListener('click', () => {
|
||||||
|
chatbotWindow.classList.remove('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send message function
|
||||||
|
async function sendMessage() {
|
||||||
|
const message = chatbotInput.value.trim();
|
||||||
|
if (!message) return;
|
||||||
|
|
||||||
|
// Add user message to UI
|
||||||
|
appendMessage('user', message);
|
||||||
|
chatbotInput.value = '';
|
||||||
|
|
||||||
|
// Add typing indicator
|
||||||
|
const typingId = 'typing-' + Date.now();
|
||||||
|
const typingDiv = document.createElement('div');
|
||||||
|
typingDiv.id = typingId;
|
||||||
|
typingDiv.className = 'typing-indicator';
|
||||||
|
typingDiv.innerText = document.documentElement.lang === 'ar' ? 'جاري الكتابة...' : 'Typing...';
|
||||||
|
chatbotMessages.appendChild(typingDiv);
|
||||||
|
chatbotMessages.scrollTop = chatbotMessages.scrollHeight;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/chat/', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ message: message })
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
// Remove typing indicator
|
||||||
|
const typingElement = document.getElementById(typingId);
|
||||||
|
if (typingElement) typingElement.remove();
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
appendMessage('bot', data.reply);
|
||||||
|
} else {
|
||||||
|
appendMessage('bot', 'Sorry, I encountered an error. Please try again later.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
const typingElement = document.getElementById(typingId);
|
||||||
|
if (typingElement) typingElement.remove();
|
||||||
|
appendMessage('bot', 'Network error. Please check your connection.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendMessage(role, text) {
|
||||||
|
const msgDiv = document.createElement('div');
|
||||||
|
msgDiv.className = `chat-message ${role}`;
|
||||||
|
msgDiv.innerText = text;
|
||||||
|
chatbotMessages.appendChild(msgDiv);
|
||||||
|
chatbotMessages.scrollTop = chatbotMessages.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event listeners for sending
|
||||||
|
chatbotSend.addEventListener('click', sendMessage);
|
||||||
|
chatbotInput.addEventListener('keypress', (e) => {
|
||||||
|
if (e.key === 'Enter') sendMessage();
|
||||||
|
});
|
||||||
|
|
||||||
|
// CSRF helper
|
||||||
|
function getCookie(name) {
|
||||||
|
let cookieValue = null;
|
||||||
|
if (document.cookie && document.cookie !== '') {
|
||||||
|
const cookies = document.cookie.split(';');
|
||||||
|
for (let i = 0; i < cookies.length; i++) {
|
||||||
|
const cookie = cookies[i].trim();
|
||||||
|
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||||||
|
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cookieValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user