198 lines
9.0 KiB
HTML
198 lines
9.0 KiB
HTML
{% load i18n %}
|
|
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
|
|
<h3 class="text-xl font-bold text-navy mb-6">{% trans "Resolution" %}</h3>
|
|
|
|
{% if complaint.status == 'resolved' or complaint.status == 'closed' %}
|
|
<div class="bg-green-50 border border-green-200 rounded-2xl p-6 mb-6">
|
|
<div class="flex items-center gap-2 mb-4">
|
|
<i data-lucide="check-circle" class="w-6 h-6 text-green-500"></i>
|
|
<h4 class="font-bold text-green-800">{% trans "Complaint Resolved" %}</h4>
|
|
</div>
|
|
{% if complaint.resolution %}
|
|
<p class="text-slate-700 mb-4">{{ complaint.resolution|linebreaks }}</p>
|
|
{% endif %}
|
|
<div class="text-sm text-slate">
|
|
<p>{% trans "Resolved by:" %} {{ complaint.resolved_by.get_full_name|default:"-" }}</p>
|
|
<p>{% trans "Resolved at:" %} {{ complaint.resolved_at|date:"M d, Y H:i"|default:"-" }}</p>
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="bg-yellow-50 border border-yellow-200 rounded-2xl p-6 mb-6">
|
|
<div class="flex items-center gap-2 mb-4">
|
|
<i data-lucide="clock" class="w-6 h-6 text-yellow-500"></i>
|
|
<h4 class="font-bold text-yellow-800">{% trans "Pending Resolution" %}</h4>
|
|
</div>
|
|
<p class="text-slate mb-4">{% trans "This complaint has not been resolved yet." %}</p>
|
|
</div>
|
|
|
|
{% if can_edit and complaint.is_active_status %}
|
|
{% if complaint.assigned_to == current_user %}
|
|
<form method="post" action="{% url 'complaints:complaint_change_status' pk=complaint.pk %}" id="resolutionForm">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="status" value="resolved">
|
|
|
|
<!-- AI Generate Resolution Button -->
|
|
{% if explanations %}
|
|
<div class="mb-4">
|
|
<button type="button" onclick="generateAIResolution()" id="aiGenerateBtn" class="w-full px-4 py-3 bg-gradient-to-r from-navy to-blue text-white rounded-xl font-semibold hover:opacity-90 transition flex items-center justify-center gap-2">
|
|
<i data-lucide="sparkles" class="w-5 h-5"></i>
|
|
<span>{% trans "Analyze Complaint & Generate Resolution Note" %}</span>
|
|
</button>
|
|
<div id="aiLoading" class="hidden mt-3 text-center">
|
|
<div class="inline-flex items-center gap-2 text-slate">
|
|
<i data-lucide="loader-2" class="w-5 h-5 animate-spin"></i>
|
|
<span>{% trans "AI is analyzing complaint and explanations..." %}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- AI Generated Resolutions Selection -->
|
|
<div id="aiResolutionSelection" class="hidden mb-4">
|
|
<label class="block text-sm font-semibold text-slate mb-2">{% trans "Select AI Generated Resolution" %}</label>
|
|
<div class="space-y-3">
|
|
<!-- English Option -->
|
|
<div class="border border-slate-200 rounded-xl p-4 cursor-pointer hover:border-navy transition" onclick="selectResolution('en')">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<input type="radio" name="resolution_language" value="en" id="resEn" class="w-4 h-4 text-navy">
|
|
<label for="resEn" class="font-semibold text-navy cursor-pointer">{% trans "English" %}</label>
|
|
</div>
|
|
<div id="resolutionEnText" class="text-sm text-slate bg-slate-50 rounded-lg p-3 max-h-32 overflow-y-auto"></div>
|
|
</div>
|
|
|
|
<!-- Arabic Option -->
|
|
<div class="border border-slate-200 rounded-xl p-4 cursor-pointer hover:border-navy transition" onclick="selectResolution('ar')">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<input type="radio" name="resolution_language" value="ar" id="resAr" class="w-4 h-4 text-navy">
|
|
<label for="resAr" class="font-semibold text-navy cursor-pointer">{% trans "Arabic" %}</label>
|
|
</div>
|
|
<div id="resolutionArText" class="text-sm text-slate bg-slate-50 rounded-lg p-3 max-h-32 overflow-y-auto text-right" dir="rtl"></div>
|
|
</div>
|
|
</div>
|
|
<p class="text-xs text-slate mt-2">{% trans "Click on a card to select. You can edit the selected resolution in the text area below before submitting." %}</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-semibold text-slate mb-2">{% trans "Resolution Notes" %}</label>
|
|
<textarea name="resolution" id="resolutionTextarea" rows="6" class="w-full border border-slate-200 rounded-xl p-4 text-sm focus:ring-2 focus:ring-navy/20 outline-none" placeholder="{% trans 'Enter resolution details...' %}"></textarea>
|
|
</div>
|
|
<button type="submit" class="w-full px-6 py-3 bg-green-500 text-white rounded-xl font-bold hover:bg-green-600 transition flex items-center justify-center gap-2">
|
|
<i data-lucide="check-circle" class="w-5 h-5"></i> {% trans "Mark as Resolved" %}
|
|
</button>
|
|
</form>
|
|
{% else %}
|
|
<!-- Show message that activation is required to resolve -->
|
|
<div class="bg-yellow-50 border border-yellow-200 rounded-xl p-4 text-center">
|
|
<i data-lucide="lock" class="w-5 h-5 text-yellow-600 mx-auto mb-2"></i>
|
|
<p class="text-sm text-yellow-700 font-medium">{% trans "Activate this complaint to resolve it" %}</p>
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
{% endif %}
|
|
</section>
|
|
|
|
<script>
|
|
// Store generated resolutions
|
|
let generatedResolutions = {
|
|
en: '',
|
|
ar: ''
|
|
};
|
|
|
|
function generateAIResolution() {
|
|
const btn = document.getElementById('aiGenerateBtn');
|
|
const loading = document.getElementById('aiLoading');
|
|
const selectionDiv = document.getElementById('aiResolutionSelection');
|
|
|
|
// Get CSRF token from the form's hidden input
|
|
const csrfInput = document.querySelector('#resolutionForm input[name="csrfmiddlewaretoken"]');
|
|
const csrfToken = csrfInput ? csrfInput.value : null;
|
|
|
|
if (!csrfToken) {
|
|
alert('{% trans "CSRF token not found. Please refresh the page and try again." %}');
|
|
return;
|
|
}
|
|
|
|
// Show loading, hide button
|
|
btn.disabled = true;
|
|
btn.classList.add('opacity-50', 'cursor-not-allowed');
|
|
loading.classList.remove('hidden');
|
|
selectionDiv.classList.add('hidden');
|
|
|
|
fetch(`/complaints/api/complaints/{{ complaint.id }}/generate_ai_resolution/`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRFToken': csrfToken,
|
|
'Content-Type': 'application/json'
|
|
},
|
|
credentials: 'same-origin'
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
return response.json().then(data => {
|
|
throw new Error(data.error || 'Request failed');
|
|
});
|
|
}
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
if (data.success && data.resolution_en && data.resolution_ar) {
|
|
// Store generated resolutions
|
|
generatedResolutions.en = data.resolution_en;
|
|
generatedResolutions.ar = data.resolution_ar;
|
|
|
|
// Display in selection cards
|
|
document.getElementById('resolutionEnText').textContent = data.resolution_en;
|
|
document.getElementById('resolutionArText').textContent = data.resolution_ar;
|
|
|
|
// Show selection div
|
|
selectionDiv.classList.remove('hidden');
|
|
|
|
// Auto-select English by default
|
|
selectResolution('en');
|
|
|
|
// Scroll to selection
|
|
selectionDiv.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
} else {
|
|
alert(data.error || '{% trans "Failed to generate resolution. Please try again." %}');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
alert(error.message || '{% trans "An error occurred. Please try again." %}');
|
|
})
|
|
.finally(() => {
|
|
btn.disabled = false;
|
|
btn.classList.remove('opacity-50', 'cursor-not-allowed');
|
|
loading.classList.add('hidden');
|
|
});
|
|
}
|
|
|
|
function selectResolution(lang) {
|
|
const textarea = document.getElementById('resolutionTextarea');
|
|
const radioEn = document.getElementById('resEn');
|
|
const radioAr = document.getElementById('resAr');
|
|
|
|
if (lang === 'en') {
|
|
radioEn.checked = true;
|
|
radioAr.checked = false;
|
|
textarea.value = generatedResolutions.en;
|
|
textarea.dir = 'ltr';
|
|
} else {
|
|
radioEn.checked = false;
|
|
radioAr.checked = true;
|
|
textarea.value = generatedResolutions.ar;
|
|
textarea.dir = 'rtl';
|
|
}
|
|
|
|
// Highlight selected card
|
|
const cards = document.querySelectorAll('#aiResolutionSelection > div');
|
|
cards.forEach((card, index) => {
|
|
if ((lang === 'en' && index === 0) || (lang === 'ar' && index === 1)) {
|
|
card.classList.add('border-navy', 'bg-light');
|
|
} else {
|
|
card.classList.remove('border-navy', 'bg-light');
|
|
}
|
|
});
|
|
}
|
|
</script>
|