258 lines
12 KiB
HTML
258 lines
12 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 %}
|
|
|
|
<!-- Resolution Outcome Display -->
|
|
{% if complaint.resolution_outcome %}
|
|
<div class="mb-4 pt-4 border-t border-green-200">
|
|
<p class="text-sm font-semibold text-green-800 mb-2">{% trans "Resolution Outcome" %}</p>
|
|
<div class="flex items-center gap-2">
|
|
<span class="inline-flex items-center px-3 py-1.5 rounded-lg text-sm font-bold
|
|
{% if complaint.resolution_outcome == 'patient' %}bg-blue-100 text-blue-800
|
|
{% elif complaint.resolution_outcome == 'hospital' %}bg-green-100 text-green-800
|
|
{% else %}bg-orange-100 text-orange-800{% endif %}">
|
|
{% if complaint.resolution_outcome == 'patient' %}
|
|
<i data-lucide="user" class="w-4 h-4 mr-1.5"></i>{% trans "Patient" %}
|
|
{% elif complaint.resolution_outcome == 'hospital' %}
|
|
<i data-lucide="building-2" class="w-4 h-4 mr-1.5"></i>{% trans "Hospital" %}
|
|
{% else %}
|
|
<i data-lucide="circle" class="w-4 h-4 mr-1.5"></i>{% trans "Other" %}
|
|
{% endif %}
|
|
</span>
|
|
{% if complaint.resolution_outcome == 'other' and complaint.resolution_outcome_other %}
|
|
<span class="text-sm text-slate">({{ complaint.resolution_outcome_other }})</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% 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>
|
|
|
|
<!-- Resolution Outcome - Who was in wrong/right -->
|
|
<div class="mb-4">
|
|
<label class="block text-sm font-semibold text-slate mb-2">{% trans "Resolution Outcome" %}</label>
|
|
<p class="text-xs text-slate mb-2">{% trans "Who was in wrong / who was in right?" %}</p>
|
|
<select name="resolution_outcome" id="resolutionOutcome" class="w-full border border-slate-200 rounded-xl p-3 text-sm focus:ring-2 focus:ring-navy/20 outline-none bg-white">
|
|
<option value="">-- {% trans "Select Outcome" %} --</option>
|
|
<option value="patient">{% trans "Patient" %}</option>
|
|
<option value="hospital">{% trans "Hospital" %}</option>
|
|
<option value="other">{% trans "Other — please specify" %}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Other specification field (shown when Other is selected) -->
|
|
<div class="mb-4 hidden" id="otherSpecificationDiv">
|
|
<label class="block text-sm font-semibold text-slate mb-2">{% trans "Please Specify" %}</label>
|
|
<textarea name="resolution_outcome_other" id="resolutionOutcomeOther" rows="3" class="w-full border border-slate-200 rounded-xl p-3 text-sm focus:ring-2 focus:ring-navy/20 outline-none" placeholder="{% trans 'Specify who was in wrong/right...' %}"></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');
|
|
}
|
|
});
|
|
}
|
|
|
|
// Show/hide "Other" specification field
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const outcomeSelect = document.getElementById('resolutionOutcome');
|
|
const otherDiv = document.getElementById('otherSpecificationDiv');
|
|
|
|
if (outcomeSelect) {
|
|
outcomeSelect.addEventListener('change', function() {
|
|
if (this.value === 'other') {
|
|
otherDiv.classList.remove('hidden');
|
|
} else {
|
|
otherDiv.classList.add('hidden');
|
|
}
|
|
});
|
|
}
|
|
});
|
|
</script>
|