HH/templates/complaints/partials/resolution_panel.html
2026-02-22 08:35:53 +03:00

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>