429 lines
25 KiB
HTML
429 lines
25 KiB
HTML
{% extends "layouts/base.html" %}
|
|
{% load i18n %}
|
|
|
|
{% block title %}{% trans "Suggestion Detail" %} - PX360{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.page-header-gradient {
|
|
background: linear-gradient(135deg, #005696 0%, #0069a8 50%, #007bbd 100%);
|
|
color: white;
|
|
padding: 1.5rem 2rem;
|
|
border-radius: 1rem;
|
|
margin-bottom: 1.5rem;
|
|
box-shadow: 0 10px 15px -3px rgba(0, 86, 150, 0.2);
|
|
}
|
|
.section-card {
|
|
background: white;
|
|
border-radius: 1rem;
|
|
border: 2px solid #e2e8f0;
|
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
overflow: hidden;
|
|
transition: all 0.3s ease;
|
|
}
|
|
.section-card:hover {
|
|
border-color: #005696;
|
|
box-shadow: 0 10px 25px -5px rgba(0, 86, 150, 0.15);
|
|
}
|
|
.section-header {
|
|
padding: 1rem 1.5rem;
|
|
border-bottom: 2px solid #e2e8f0;
|
|
background: linear-gradient(to right, #f8fafc, #f1f5f9);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
}
|
|
.section-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 0.75rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="page-header-gradient">
|
|
<div class="flex justify-between items-center">
|
|
<div>
|
|
<div class="flex items-center gap-2 text-blue-100 text-sm mb-2">
|
|
<a href="{% url 'feedback:feedback_list' %}" class="hover:text-white transition">{% trans "Suggestions" %}</a>
|
|
<i data-lucide="chevron-right" class="w-3 h-3"></i>
|
|
<span class="text-white">{% trans "Detail" %}</span>
|
|
</div>
|
|
<h1 class="text-2xl font-bold">{% trans "Suggestion Detail" %}</h1>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
{% if can_edit %}
|
|
<a href="{% url 'feedback:feedback_update' feedback.id %}" class="inline-flex items-center px-4 py-2.5 bg-white/10 border-2 border-white/30 text-white font-medium rounded-xl hover:bg-white hover:text-navy transition">
|
|
<i data-lucide="pencil" class="w-4 h-4 me-2"></i>{% trans "Edit" %}
|
|
</a>
|
|
{% endif %}
|
|
<a href="{% url 'feedback:feedback_list' %}" class="inline-flex items-center px-4 py-2.5 bg-white text-navy font-medium rounded-xl hover:bg-blue-50 transition">
|
|
<i data-lucide="arrow-left" class="w-4 h-4 me-2"></i>{% trans "Back" %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
<div class="lg:col-span-2 space-y-6">
|
|
|
|
<div class="section-card">
|
|
<div class="section-header flex items-center justify-between flex-wrap gap-2">
|
|
<div class="flex items-center gap-3">
|
|
<div class="section-icon bg-cyan-500/10">
|
|
<i data-lucide="message-square" class="w-5 h-5 text-cyan-600"></i>
|
|
</div>
|
|
<h5 class="text-lg font-semibold text-gray-800">{{ feedback.title }}</h5>
|
|
</div>
|
|
<div class="flex flex-wrap gap-1.5">
|
|
<span class="px-2.5 py-1 rounded-full text-[10px] font-bold uppercase
|
|
{% if feedback.feedback_type == 'compliment' %}bg-green-100 text-green-700
|
|
{% elif feedback.feedback_type == 'suggestion' %}bg-cyan-100 text-cyan-700
|
|
{% elif feedback.feedback_type == 'inquiry' %}bg-amber-100 text-amber-700
|
|
{% else %}bg-slate-100 text-slate-600{% endif %}">
|
|
{{ feedback.get_feedback_type_display }}
|
|
</span>
|
|
<span class="px-2.5 py-1 rounded-full text-[10px] font-bold uppercase
|
|
{% if feedback.status == 'submitted' %}bg-blue-100 text-blue-700
|
|
{% elif feedback.status == 'reviewed' %}bg-orange-100 text-orange-700
|
|
{% elif feedback.status == 'acknowledged' %}bg-green-100 text-green-700
|
|
{% elif feedback.status == 'closed' %}bg-slate-100 text-slate-600
|
|
{% else %}bg-slate-100 text-slate-600{% endif %}">
|
|
{{ feedback.get_status_display }}
|
|
</span>
|
|
<span class="px-2.5 py-1 rounded-full text-[10px] font-bold uppercase
|
|
{% if feedback.priority == 'critical' %}bg-red-500 text-white
|
|
{% elif feedback.priority == 'high' %}bg-orange-100 text-orange-700
|
|
{% elif feedback.priority == 'medium' %}bg-yellow-100 text-yellow-700
|
|
{% else %}bg-green-100 text-green-700{% endif %}">
|
|
{{ feedback.get_priority_display }}
|
|
</span>
|
|
{% if feedback.is_featured %}
|
|
<span class="bg-yellow-100 text-yellow-700 px-2 py-0.5 rounded text-[10px] font-bold">{% trans "FEATURED" %}</span>
|
|
{% endif %}
|
|
{% if feedback.is_public %}
|
|
<span class="bg-blue-100 text-blue-700 px-2 py-0.5 rounded text-[10px] font-bold">{% trans "PUBLIC" %}</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="p-6">
|
|
<div class="bg-slate-50 border border-slate-200 rounded-lg p-4 mb-6">
|
|
<p class="text-sm text-gray-800 whitespace-pre-wrap leading-relaxed">{{ feedback.message }}</p>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-2 gap-x-8 gap-y-0">
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-32">{% trans "Hospital" %}</span>
|
|
<span class="text-sm text-gray-800 flex-1">{{ feedback.hospital.name }}</span>
|
|
</div>
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-32">{% trans "Category" %}</span>
|
|
<span class="flex-1">
|
|
<span class="bg-slate-100 px-2 py-0.5 rounded text-[10px] font-bold text-slate-600">{{ feedback.get_category_display }}</span>
|
|
{% if feedback.subcategory %}
|
|
<span class="text-slate-400 mx-1">/</span>
|
|
<span class="text-sm text-slate-600">{{ feedback.subcategory }}</span>
|
|
{% endif %}
|
|
</span>
|
|
</div>
|
|
|
|
{% if feedback.location %}
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-32">{% trans "Location" %}</span>
|
|
<span class="text-sm text-gray-800 flex-1">
|
|
{{ feedback.location.name }}
|
|
{% if feedback.main_section %} <i data-lucide="chevron-right" class="w-3 h-3 inline-block text-slate-300"></i> {{ feedback.main_section.name }}{% endif %}
|
|
{% if feedback.subsection %} <i data-lucide="chevron-right" class="w-3 h-3 inline-block text-slate-300"></i> {{ feedback.subsection.name }}{% endif %}
|
|
</span>
|
|
</div>
|
|
{% else %}
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-32">{% trans "Location" %}</span>
|
|
<span class="text-sm text-slate-400 flex-1 italic">{% trans "Not specified" %}</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-32">{% trans "Sentiment" %}</span>
|
|
<span class="flex-1 flex items-center gap-2">
|
|
<span class="px-2.5 py-1 rounded-full text-[10px] font-bold uppercase
|
|
{% if feedback.sentiment == 'positive' %}bg-green-100 text-green-700
|
|
{% elif feedback.sentiment == 'negative' %}bg-red-100 text-red-700
|
|
{% else %}bg-slate-100 text-slate-600{% endif %}">
|
|
{{ feedback.get_sentiment_display }}
|
|
</span>
|
|
{% if feedback.sentiment_score %}
|
|
<span class="text-xs text-slate-400">({{ feedback.sentiment_score|floatformat:2 }})</span>
|
|
{% endif %}
|
|
</span>
|
|
</div>
|
|
|
|
{% if feedback.department %}
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-32">{% trans "Department" %}</span>
|
|
<span class="text-sm text-gray-800 flex-1">{{ feedback.department.name }}</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if feedback.rating %}
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-32">{% trans "Rating" %}</span>
|
|
<span class="flex-1 flex items-center gap-1">
|
|
<span class="text-yellow-500">
|
|
{% for i in "12345" %}
|
|
{% if forloop.counter <= feedback.rating %}<i data-lucide="star" class="w-4 h-4 inline-block" style="fill:currentColor"></i>
|
|
{% else %}<i data-lucide="star" class="w-4 h-4 inline-block"></i>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</span>
|
|
<span class="text-xs text-slate-400 ml-1">{{ feedback.rating }}/5</span>
|
|
</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if feedback.source %}
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-32">{% trans "Source" %}</span>
|
|
<span class="flex-1">
|
|
<span class="bg-slate-100 px-2 py-0.5 rounded text-[10px] font-bold text-slate-600">{{ feedback.source }}</span>
|
|
</span>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if feedback.encounter_id %}
|
|
<div class="flex py-3 border-b border-slate-100">
|
|
<span class="text-sm font-semibold text-slate-500 w-32">{% trans "Encounter ID" %}</span>
|
|
<span class="text-sm text-gray-800 flex-1"><code class="bg-slate-100 px-2 py-0.5 rounded text-xs">{{ feedback.encounter_id }}</code></span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="mt-4 pt-4 border-t border-slate-200">
|
|
<h6 class="text-xs font-bold text-slate uppercase mb-3">{% trans "Contact" %}</h6>
|
|
{% if feedback.patient %}
|
|
<div class="flex flex-wrap gap-x-6 gap-y-1 text-sm">
|
|
<span><span class="font-semibold text-gray-800">{{ feedback.patient.get_full_name }}</span></span>
|
|
<span class="text-slate-500">{% trans "MRN:" %} {{ feedback.patient.mrn }}</span>
|
|
{% if feedback.patient.phone %}
|
|
<span class="text-slate-500">{{ feedback.patient.phone }}</span>
|
|
{% endif %}
|
|
{% if feedback.patient.email %}
|
|
<span class="text-slate-500">{{ feedback.patient.email }}</span>
|
|
{% endif %}
|
|
</div>
|
|
{% else %}
|
|
{% if feedback.contact_name or feedback.contact_phone or feedback.contact_email %}
|
|
<div class="flex flex-wrap gap-x-6 gap-y-1 text-sm">
|
|
{% if feedback.contact_name %}
|
|
<span class="font-semibold text-gray-800">{{ feedback.contact_name }}</span>
|
|
{% endif %}
|
|
{% if feedback.contact_phone %}
|
|
<span class="text-slate-500">{{ feedback.contact_phone }}</span>
|
|
{% endif %}
|
|
{% if feedback.contact_email %}
|
|
<span class="text-slate-500">{{ feedback.contact_email }}</span>
|
|
{% endif %}
|
|
</div>
|
|
{% else %}
|
|
<div class="flex items-center gap-2 text-sm text-slate-400 italic">
|
|
<i data-lucide="user-x" class="w-4 h-4"></i>{% trans "Anonymous Suggestion" %}
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="mt-4 pt-4 border-t border-slate-200 flex items-center justify-between">
|
|
<span class="text-xs text-slate-400">
|
|
<i data-lucide="calendar" class="w-3 h-3 inline-block me-1"></i>{{ feedback.created_at|date:"F d, Y H:i" }}
|
|
</span>
|
|
<span class="text-xs text-slate-400">
|
|
ID: <code class="bg-slate-100 px-1.5 py-0.5 rounded">{{ feedback.id|slice:":8" }}</code>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if feedback.has_ai_analysis %}
|
|
<div class="section-card border-blue-200">
|
|
<div class="section-header bg-blue-50">
|
|
<div class="section-icon bg-blue-500/20">
|
|
<i data-lucide="sparkles" class="w-5 h-5 text-blue-600"></i>
|
|
</div>
|
|
<h5 class="text-lg font-semibold text-blue-800">{% trans "AI Analysis" %}</h5>
|
|
</div>
|
|
<div class="p-6">
|
|
<p class="text-sm text-gray-800 mb-4">{{ feedback.ai_short_description_en }}</p>
|
|
<div class="flex flex-wrap gap-4 mb-4">
|
|
<div>
|
|
<span class="text-[10px] font-bold text-slate uppercase">{% trans "Category" %}</span>
|
|
<div class="mt-1">
|
|
<span class="bg-blue-100 text-blue-700 px-2 py-0.5 rounded text-[10px] font-bold uppercase">{{ feedback.ai_category }}</span>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<span class="text-[10px] font-bold text-slate uppercase">{% trans "Priority" %}</span>
|
|
<div class="mt-1">
|
|
<span class="px-2.5 py-1 rounded-full text-[10px] font-bold uppercase
|
|
{% if feedback.priority == 'high' %}bg-orange-100 text-orange-700
|
|
{% elif feedback.priority == 'medium' %}bg-yellow-100 text-yellow-700
|
|
{% else %}bg-green-100 text-green-700{% endif %}">
|
|
{{ feedback.get_priority_display }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% if feedback.ai_reasoning_en %}
|
|
<p class="text-xs text-slate-500 bg-slate-50 border border-slate-200 rounded-lg p-3 mb-4">{{ feedback.ai_reasoning_en }}</p>
|
|
{% endif %}
|
|
|
|
{% if feedback.ai_suggested_actions %}
|
|
<div class="pt-4 border-t border-slate-200">
|
|
<h6 class="text-sm font-bold text-navy mb-3 flex items-center gap-2">
|
|
<i data-lucide="zap" class="w-4 h-4"></i>{% trans "Suggested Actions" %}
|
|
</h6>
|
|
<div class="space-y-2">
|
|
{% for action in feedback.ai_suggested_actions %}
|
|
<div class="flex items-center justify-between gap-3 p-2.5 bg-slate-50 rounded-lg border border-slate-200">
|
|
<div class="flex-1 min-w-0">
|
|
<p class="text-sm text-gray-800 font-medium truncate">{{ action.action_en }}</p>
|
|
</div>
|
|
<div class="flex items-center gap-2 flex-shrink-0">
|
|
<span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase
|
|
{% if action.priority == 'high' %}bg-orange-100 text-orange-700
|
|
{% elif action.priority == 'medium' %}bg-yellow-100 text-yellow-700
|
|
{% else %}bg-green-100 text-green-700{% endif %}">
|
|
{{ action.priority }}
|
|
</span>
|
|
{% if can_edit %}
|
|
<form method="post" action="{% url 'feedback:feedback_create_action' feedback.id %}">
|
|
{% csrf_token %}
|
|
<input type="hidden" name="action_title" value="{{ action.action_en }}">
|
|
<input type="hidden" name="action_description" value="{{ action.action_en }}">
|
|
<input type="hidden" name="action_category" value="{{ action.category }}">
|
|
<input type="hidden" name="action_priority" value="{{ action.priority }}">
|
|
<button type="submit" class="p-1.5 text-blue-600 hover:bg-blue-50 rounded border border-transparent hover:border-blue-200 transition" title="{% trans 'Create PX Action' %}">
|
|
<i data-lucide="plus-circle" class="w-4 h-4"></i>
|
|
</button>
|
|
</form>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
|
|
</div>
|
|
|
|
<div class="space-y-6">
|
|
{% if can_edit %}
|
|
<div class="section-card">
|
|
<div class="section-header">
|
|
<div class="section-icon bg-green-500/10">
|
|
<i data-lucide="zap" class="w-5 h-5 text-green-600"></i>
|
|
</div>
|
|
<h5 class="text-lg font-semibold text-gray-800">{% trans "Actions" %}</h5>
|
|
</div>
|
|
<div class="p-6 space-y-4">
|
|
<form method="post" action="{% url 'feedback:feedback_change_status' feedback.id %}">
|
|
{% csrf_token %}
|
|
<label class="text-xs font-bold text-slate uppercase">{% trans "Status" %}</label>
|
|
<select name="status" class="w-full mt-1 px-3 py-2 bg-white border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-navy">
|
|
{% for value, label in status_choices %}
|
|
<option value="{{ value }}" {% if feedback.status == value %}selected{% endif %}>{{ label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
<textarea name="note" rows="2" placeholder="{% trans 'Add a note...' %}" class="w-full mt-2 px-3 py-2 bg-white border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-navy"></textarea>
|
|
<button type="submit" class="w-full mt-2 px-4 py-2 bg-navy text-white text-sm font-semibold rounded-lg hover:bg-navy/90 transition">
|
|
<i data-lucide="refresh-cw" class="w-3 h-3 inline-block me-1"></i>{% trans "Update" %}
|
|
</button>
|
|
</form>
|
|
|
|
<hr class="border-slate-100">
|
|
|
|
<form method="post" action="{% url 'feedback:feedback_assign' feedback.id %}">
|
|
{% csrf_token %}
|
|
<label class="text-xs font-bold text-slate uppercase">{% trans "Assign To" %}</label>
|
|
<select name="user_id" class="w-full mt-1 px-3 py-2 bg-white border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-navy">
|
|
<option value="">{% trans "Select user..." %}</option>
|
|
{% for user in assignable_users %}
|
|
<option value="{{ user.id }}" {% if feedback.assigned_to.id == user.id %}selected{% endif %}>{{ user.get_full_name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
<button type="submit" class="w-full mt-2 px-4 py-2 bg-navy text-white text-sm font-semibold rounded-lg hover:bg-navy/90 transition">
|
|
<i data-lucide="user-check" class="w-3 h-3 inline-block me-1"></i>{% trans "Assign" %}
|
|
</button>
|
|
</form>
|
|
|
|
<hr class="border-slate-100">
|
|
|
|
<div class="space-y-2">
|
|
<label class="text-xs font-bold text-slate uppercase">{% trans "Linked Actions" %}</label>
|
|
<a href="{% url 'rca:rca_create' %}?related_model=feedback&related_id={{ feedback.pk }}"
|
|
class="flex items-center gap-2 p-3 bg-purple-50 rounded-lg hover:bg-purple-100 transition text-purple-700 font-medium text-sm">
|
|
<i data-lucide="search" class="w-4 h-4"></i>
|
|
{% trans "Initiate RCA" %}
|
|
</a>
|
|
<a href="{% url 'projects:project_create' %}?related_model=feedback&related_id={{ feedback.pk }}"
|
|
class="flex items-center gap-2 p-3 bg-teal-50 rounded-lg hover:bg-teal-100 transition text-teal-700 font-medium text-sm">
|
|
<i data-lucide="folder-plus" class="w-4 h-4"></i>
|
|
{% trans "Create QI Project" %}
|
|
</a>
|
|
</div>
|
|
|
|
<hr class="border-slate-100">
|
|
|
|
{% comment %} <form method="post" action="{% url 'feedback:feedback_add_response' feedback.id %}">
|
|
{% csrf_token %}
|
|
<label class="text-xs font-bold text-slate uppercase">{% trans "Add Response" %}</label>
|
|
<select name="response_type" class="w-full mt-1 px-3 py-2 bg-white border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-navy">
|
|
<option value="response">{% trans "Response to Patient" %}</option>
|
|
<option value="note">{% trans "Internal Note" %}</option>
|
|
<option value="acknowledgment">{% trans "Acknowledgment" %}</option>
|
|
</select>
|
|
<textarea name="message" rows="3" placeholder="{% trans 'Enter your response...' %}" required class="w-full mt-2 px-3 py-2 bg-white border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-navy"></textarea>
|
|
<label class="flex items-center gap-2 mt-2 text-sm text-slate-600">
|
|
<input type="checkbox" name="is_internal" class="rounded border-slate-300">
|
|
{% trans "Internal only" %}
|
|
</label>
|
|
<button type="submit" class="w-full mt-2 px-4 py-2 bg-green-600 text-white text-sm font-semibold rounded-lg hover:bg-green-700 transition">
|
|
<i data-lucide="message-circle" class="w-3 h-3 inline-block me-1"></i>{% trans "Add Response" %}
|
|
</button>
|
|
</form> {% endcomment %}
|
|
|
|
<hr class="border-slate-100">
|
|
|
|
{% comment %} <div class="flex gap-2">
|
|
<form method="post" action="{% url 'feedback:feedback_toggle_featured' feedback.id %}" class="flex-1">
|
|
{% csrf_token %}
|
|
<button type="submit" class="w-full px-3 py-2 border border-yellow-300 text-yellow-700 text-xs font-semibold rounded-lg hover:bg-yellow-50 transition">
|
|
<i data-lucide="star" class="w-3 h-3 inline-block me-1"></i>
|
|
{% if feedback.is_featured %}{% trans "Unfeature" %}{% else %}{% trans "Feature" %}{% endif %}
|
|
</button>
|
|
</form>
|
|
<form method="post" action="{% url 'feedback:feedback_toggle_follow_up' feedback.id %}" class="flex-1">
|
|
{% csrf_token %}
|
|
<button type="submit" class="w-full px-3 py-2 border border-blue-300 text-blue-700 text-xs font-semibold rounded-lg hover:bg-blue-50 transition">
|
|
<i data-lucide="flag" class="w-3 h-3 inline-block me-1"></i>
|
|
{% if feedback.requires_follow_up %}{% trans "Unflag" %}{% else %}{% trans "Follow-up" %}{% endif %}
|
|
</button>
|
|
</form>
|
|
</div> {% endcomment %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|