578 lines
31 KiB
HTML
578 lines
31 KiB
HTML
{% extends "layouts/base.html" %}
|
|
{% load i18n %}
|
|
{% load static %}
|
|
|
|
{% block title %}{% trans "Action" %} #{{ action.id|slice:":8" }} - PX360{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.timeline {
|
|
position: relative;
|
|
padding-left: 30px;
|
|
}
|
|
.timeline::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: 8px;
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 2px;
|
|
background: #e2e8f0;
|
|
}
|
|
.timeline-item {
|
|
position: relative;
|
|
padding-bottom: 24px;
|
|
}
|
|
.timeline-item::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: -26px;
|
|
top: 5px;
|
|
width: 16px;
|
|
height: 16px;
|
|
border-radius: 50%;
|
|
background: #fff;
|
|
border: 3px solid #f57c00;
|
|
z-index: 1;
|
|
}
|
|
.timeline-item.status_change::before { border-color: #1e3a5f; }
|
|
.timeline-item.assignment::before { border-color: #388e3c; }
|
|
.timeline-item.escalation::before { border-color: #d32f2f; }
|
|
.timeline-item.approval::before { border-color: #4caf50; }
|
|
.timeline-item.evidence::before { border-color: #f57c00; }
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<!-- Back Button -->
|
|
<div class="mb-6">
|
|
<a href="{% url 'actions:action_list' %}" class="inline-flex items-center gap-2 text-slate hover:text-navy transition font-medium">
|
|
<i data-lucide="arrow-left" class="w-4 h-4"></i> {% trans "Back to Actions" %}
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Action Header -->
|
|
<div class="bg-white rounded-2xl shadow-sm border border-gray-50 p-6 mb-6">
|
|
<div class="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-6">
|
|
<div class="flex-1">
|
|
<div class="flex flex-wrap items-center gap-3 mb-3">
|
|
<h1 class="text-2xl font-bold text-navy">{{ action.title }}</h1>
|
|
{% if action.status == 'completed' %}
|
|
<span class="px-3 py-1 rounded-full text-sm font-semibold bg-green-100 text-green-600">{{ action.get_status_display }}</span>
|
|
{% elif action.status == 'in_progress' %}
|
|
<span class="px-3 py-1 rounded-full text-sm font-semibold bg-blue-100 text-blue-600">{{ action.get_status_display }}</span>
|
|
{% elif action.status == 'cancelled' %}
|
|
<span class="px-3 py-1 rounded-full text-sm font-semibold bg-gray-100 text-gray-600">{{ action.get_status_display }}</span>
|
|
{% else %}
|
|
<span class="px-3 py-1 rounded-full text-sm font-semibold bg-yellow-100 text-yellow-600">{{ action.get_status_display }}</span>
|
|
{% endif %}
|
|
|
|
{% if action.priority == 'high' %}
|
|
<span class="px-3 py-1 rounded-full text-sm font-semibold bg-red-100 text-red-600">{{ action.get_priority_display }}</span>
|
|
{% elif action.priority == 'medium' %}
|
|
<span class="px-3 py-1 rounded-full text-sm font-semibold bg-orange-100 text-orange-600">{{ action.get_priority_display }}</span>
|
|
{% else %}
|
|
<span class="px-3 py-1 rounded-full text-sm font-semibold bg-green-100 text-green-600">{{ action.get_priority_display }}</span>
|
|
{% endif %}
|
|
|
|
{% if action.escalation_level > 0 %}
|
|
<span class="px-3 py-1 rounded-full text-sm font-semibold bg-red-500 text-white">
|
|
<i data-lucide="arrow-up-circle" class="w-3 h-3 inline me-1"></i>Level {{ action.escalation_level }}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="flex flex-wrap items-center gap-4 text-sm text-slate">
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="hash" class="w-4 h-4"></i>
|
|
<strong>ID:</strong> {{ action.id|slice:":8" }}
|
|
</span>
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="building" class="w-4 h-4"></i>
|
|
{{ action.hospital.name_en }}
|
|
</span>
|
|
{% if action.department %}
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="door-open" class="w-4 h-4"></i>
|
|
{{ action.department.name_en }}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- SLA Progress -->
|
|
<div class="lg:w-72 p-4 rounded-xl {% if action.is_overdue %}bg-red-50 border border-red-200{% else %}bg-slate-50{% endif %}">
|
|
<div class="flex items-center gap-2 mb-3 text-sm font-semibold text-slate">
|
|
<i data-lucide="clock" class="w-4 h-4"></i> {% trans "SLA Progress" %}
|
|
</div>
|
|
<div class="w-full h-6 bg-gray-200 rounded-full overflow-hidden mb-2">
|
|
<div class="h-full rounded-full flex items-center justify-center text-xs font-bold text-white {% if action.is_overdue %}bg-red-500{% elif sla_progress > 80 %}bg-orange-500{% else %}bg-green-500{% endif %}" style="width: {{ sla_progress }}%">
|
|
{{ sla_progress }}%
|
|
</div>
|
|
</div>
|
|
<div class="flex justify-between text-xs text-slate">
|
|
<span>{% trans "Due:" %} {{ action.due_at|date:"M d, Y H:i" }}</span>
|
|
{% if action.is_overdue %}
|
|
<span class="text-red-600 font-bold">{% trans "OVERDUE" %}</span>
|
|
{% else %}
|
|
<span>{{ action.due_at|timeuntil }} {% trans "left" %}</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex flex-col lg:flex-row gap-6">
|
|
<!-- Main Content -->
|
|
<div class="flex-1">
|
|
<!-- Tabs -->
|
|
<div class="bg-white rounded-2xl shadow-sm border border-gray-50 overflow-hidden">
|
|
<div class="border-b border-gray-100">
|
|
<nav class="flex gap-1 p-2" role="tablist">
|
|
<button type="button" class="tab-btn active px-4 py-2 rounded-xl text-sm font-semibold transition" data-tab="details">
|
|
<i data-lucide="info" class="w-4 h-4 inline me-1"></i> {% trans "Details" %}
|
|
</button>
|
|
<button type="button" class="tab-btn px-4 py-2 rounded-xl text-sm font-semibold transition" data-tab="logs">
|
|
<i data-lucide="history" class="w-4 h-4 inline me-1"></i> {% trans "Activity" %} ({{ logs.count }})
|
|
</button>
|
|
<button type="button" class="tab-btn px-4 py-2 rounded-xl text-sm font-semibold transition" data-tab="evidence">
|
|
<i data-lucide="file-check" class="w-4 h-4 inline me-1"></i> {% trans "Evidence" %} ({{ evidence_attachments.count }})
|
|
</button>
|
|
<button type="button" class="tab-btn px-4 py-2 rounded-xl text-sm font-semibold transition" data-tab="attachments">
|
|
<i data-lucide="paperclip" class="w-4 h-4 inline me-1"></i> {% trans "Attachments" %} ({{ attachments.count }})
|
|
</button>
|
|
</nav>
|
|
</div>
|
|
|
|
<!-- Details Tab -->
|
|
<div id="tab-details" class="tab-content p-6">
|
|
<h3 class="text-lg font-bold text-navy mb-4">{% trans "Action Details" %}</h3>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
|
<div>
|
|
<div class="text-xs font-semibold text-slate uppercase tracking-wide mb-1">{% trans "Category" %}</div>
|
|
<span class="px-3 py-1 rounded-full text-sm font-medium bg-slate-100 text-slate-700">{{ action.get_category_display }}</span>
|
|
</div>
|
|
<div>
|
|
<div class="text-xs font-semibold text-slate uppercase tracking-wide mb-1">{% trans "Priority" %}</div>
|
|
<span class="px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-700">{{ action.get_priority_display }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="border-gray-100 mb-6">
|
|
|
|
<div class="mb-6">
|
|
<div class="text-xs font-semibold text-slate uppercase tracking-wide mb-2">{% trans "Description" %}</div>
|
|
<p class="text-gray-700 leading-relaxed">{{ action.description|linebreaks }}</p>
|
|
</div>
|
|
|
|
{% if action.action_plan %}
|
|
<hr class="border-gray-100 mb-6">
|
|
<div class="mb-6">
|
|
<div class="text-xs font-semibold text-slate uppercase tracking-wide mb-2">{% trans "Action Plan" %}</div>
|
|
<div class="bg-blue-50 border border-blue-100 rounded-xl p-4 text-gray-700">{{ action.action_plan|linebreaks }}</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if action.outcome %}
|
|
<hr class="border-gray-100 mb-6">
|
|
<div class="mb-6">
|
|
<div class="text-xs font-semibold text-slate uppercase tracking-wide mb-2">{% trans "Outcome" %}</div>
|
|
<div class="bg-green-50 border border-green-100 rounded-xl p-4 text-gray-700">{{ action.outcome|linebreaks }}</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<hr class="border-gray-100 mb-6">
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<div>
|
|
<div class="text-xs font-semibold text-slate uppercase tracking-wide mb-1">{% trans "Created" %}</div>
|
|
<div class="text-gray-700">{{ action.created_at|date:"M d, Y H:i" }}</div>
|
|
</div>
|
|
<div>
|
|
<div class="text-xs font-semibold text-slate uppercase tracking-wide mb-1">{% trans "Last Updated" %}</div>
|
|
<div class="text-gray-700">{{ action.updated_at|date:"M d, Y H:i" }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Activity Log Tab -->
|
|
<div id="tab-logs" class="tab-content p-6 hidden">
|
|
<h3 class="text-lg font-bold text-navy mb-4">{% trans "Activity Log" %}</h3>
|
|
|
|
{% if logs %}
|
|
<div class="timeline">
|
|
{% for log in logs %}
|
|
<div class="timeline-item {{ log.log_type }}">
|
|
<div class="bg-slate-50 rounded-xl p-4 border border-slate-100">
|
|
<div class="flex justify-between items-start mb-2">
|
|
<div class="flex items-center gap-2">
|
|
<span class="px-2.5 py-1 rounded-lg text-xs font-semibold bg-navy text-white">{{ log.get_log_type_display }}</span>
|
|
{% if log.created_by %}
|
|
<span class="text-xs text-slate">by {{ log.created_by.get_full_name }}</span>
|
|
{% endif %}
|
|
</div>
|
|
<span class="text-xs text-slate">{{ log.created_at|date:"M d, Y H:i" }}</span>
|
|
</div>
|
|
<p class="text-gray-700 text-sm">{{ log.message }}</p>
|
|
{% if log.old_status and log.new_status %}
|
|
<div class="mt-2 flex items-center gap-2">
|
|
<span class="px-2 py-0.5 rounded text-xs font-medium bg-slate-100 text-slate-600">{{ log.old_status }}</span>
|
|
<i data-lucide="arrow-right" class="w-3 h-3 text-slate"></i>
|
|
<span class="px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-600">{{ log.new_status }}</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-12">
|
|
<i data-lucide="history" class="w-16 h-16 mx-auto mb-4 text-gray-300"></i>
|
|
<p class="text-slate">{% trans "No activity log entries yet" %}</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Evidence Tab -->
|
|
<div id="tab-evidence" class="tab-content p-6 hidden">
|
|
<h3 class="text-lg font-bold text-navy mb-4">{% trans "Evidence Files" %}</h3>
|
|
|
|
{% if evidence_attachments %}
|
|
<div class="space-y-3">
|
|
{% for evidence in evidence_attachments %}
|
|
<div class="flex items-center justify-between p-4 bg-orange-50 border-l-4 border-orange-400 rounded-r-xl hover:translate-x-1 transition">
|
|
<div class="flex items-center gap-3">
|
|
<i data-lucide="file-check" class="w-5 h-5 text-orange-500"></i>
|
|
<div>
|
|
<div class="font-semibold text-gray-800">{{ evidence.filename }}</div>
|
|
<div class="text-xs text-slate">
|
|
{% trans "Uploaded by" %} {{ evidence.uploaded_by.get_full_name }}
|
|
{% trans "on" %} {{ evidence.created_at|date:"M d, Y H:i" }}
|
|
({{ evidence.file_size|filesizeformat }})
|
|
</div>
|
|
{% if evidence.description %}
|
|
<div class="text-sm text-gray-600 mt-1">{{ evidence.description }}</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<a href="{{ evidence.file.url }}" target="_blank" class="p-2 rounded-lg bg-navy text-white hover:bg-blue transition">
|
|
<i data-lucide="download" class="w-4 h-4"></i>
|
|
</a>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-12">
|
|
<i data-lucide="file-check" class="w-16 h-16 mx-auto mb-4 text-gray-300"></i>
|
|
<p class="text-slate mb-2">{% trans "No evidence files uploaded yet" %}</p>
|
|
{% if action.requires_approval and action.status != 'closed' %}
|
|
<p class="text-orange-500 text-sm">
|
|
<i data-lucide="alert-triangle" class="w-4 h-4 inline me-1"></i>
|
|
{% trans "Evidence is required before requesting approval" %}
|
|
</p>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Attachments Tab -->
|
|
<div id="tab-attachments" class="tab-content p-6 hidden">
|
|
<h3 class="text-lg font-bold text-navy mb-4">{% trans "All Attachments" %}</h3>
|
|
|
|
{% if attachments %}
|
|
<div class="space-y-3">
|
|
{% for attachment in attachments %}
|
|
<div class="flex items-center justify-between p-4 bg-slate-50 border border-slate-100 rounded-xl hover:bg-slate-100 transition">
|
|
<div class="flex items-center gap-3">
|
|
<i data-lucide="file" class="w-5 h-5 text-slate"></i>
|
|
<div>
|
|
<div class="font-semibold text-gray-800">
|
|
{{ attachment.filename }}
|
|
{% if attachment.is_evidence %}
|
|
<span class="ms-2 px-2 py-0.5 rounded text-xs font-medium bg-orange-100 text-orange-600">{% trans "Evidence" %}</span>
|
|
{% endif %}
|
|
</div>
|
|
<div class="text-xs text-slate">
|
|
{% trans "Uploaded by" %} {{ attachment.uploaded_by.get_full_name }}
|
|
{% trans "on" %} {{ attachment.created_at|date:"M d, Y H:i" }}
|
|
({{ attachment.file_size|filesizeformat }})
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<a href="{{ attachment.file.url }}" target="_blank" class="p-2 rounded-lg bg-navy text-white hover:bg-blue transition">
|
|
<i data-lucide="download" class="w-4 h-4"></i>
|
|
</a>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-12">
|
|
<i data-lucide="paperclip" class="w-16 h-16 mx-auto mb-4 text-gray-300"></i>
|
|
<p class="text-slate">{% trans "No attachments" %}</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="lg:w-80 space-y-4">
|
|
<!-- Approval Required -->
|
|
{% if action.status == 'pending_approval' and can_approve %}
|
|
<div class="bg-green-50 border border-green-200 rounded-2xl overflow-hidden">
|
|
<div class="px-4 py-3 bg-green-500 text-white">
|
|
<h3 class="font-bold flex items-center gap-2">
|
|
<i data-lucide="check-circle" class="w-4 h-4"></i> {% trans "Approval Required" %}
|
|
</h3>
|
|
</div>
|
|
<div class="p-4">
|
|
<p class="text-sm text-slate mb-3">{% trans "This action is awaiting your approval." %}</p>
|
|
<form method="post" action="{% url 'actions:action_approve' action.id %}">
|
|
{% csrf_token %}
|
|
<button type="submit" class="w-full bg-green-500 text-white px-4 py-2.5 rounded-xl font-semibold hover:bg-green-600 transition flex items-center justify-center gap-2">
|
|
<i data-lucide="check-circle" class="w-4 h-4"></i> {% trans "Approve Action" %}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Quick Actions -->
|
|
{% if can_edit %}
|
|
<div class="bg-white rounded-2xl shadow-sm border border-gray-50 overflow-hidden">
|
|
<div class="px-4 py-3 bg-navy text-white">
|
|
<h3 class="font-bold flex items-center gap-2">
|
|
<i data-lucide="zap" class="w-4 h-4"></i> {% trans "Quick Actions" %}
|
|
</h3>
|
|
</div>
|
|
<div class="p-4 space-y-4">
|
|
<!-- Assign -->
|
|
<form method="post" action="{% url 'actions:action_assign' action.id %}">
|
|
{% csrf_token %}
|
|
<label class="block text-xs font-semibold text-slate uppercase tracking-wide mb-2">{% trans "Assign To" %}</label>
|
|
<div class="flex gap-2">
|
|
<select name="user_id" class="flex-1 px-3 py-2 border border-gray-200 rounded-xl text-sm focus:ring-2 focus:ring-navy focus:border-transparent" required>
|
|
<option value="">{% trans "Select user..." %}</option>
|
|
{% for user_obj in assignable_users %}
|
|
<option value="{{ user_obj.id }}" {% if action.assigned_to and action.assigned_to.id == user_obj.id %}selected{% endif %}>
|
|
{{ user_obj.get_full_name }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
<button type="submit" class="px-3 py-2 bg-navy text-white rounded-xl hover:bg-blue transition">
|
|
<i data-lucide="user-check" class="w-4 h-4"></i>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
<!-- Change Status -->
|
|
<form method="post" action="{% url 'actions:action_change_status' action.id %}">
|
|
{% csrf_token %}
|
|
<label class="block text-xs font-semibold text-slate uppercase tracking-wide mb-2">{% trans "Change Status" %}</label>
|
|
<select name="status" class="w-full px-3 py-2 border border-gray-200 rounded-xl text-sm mb-2 focus:ring-2 focus:ring-navy focus:border-transparent" required>
|
|
{% for value, label in status_choices %}
|
|
<option value="{{ value }}" {% if action.status == value %}selected{% endif %}>{{ label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
<textarea name="note" class="w-full px-3 py-2 border border-gray-200 rounded-xl text-sm mb-2 focus:ring-2 focus:ring-navy focus:border-transparent" rows="2" placeholder="{% trans 'Optional note...' %}"></textarea>
|
|
<button type="submit" class="w-full bg-navy text-white px-4 py-2.5 rounded-xl font-semibold hover:bg-blue transition flex items-center justify-center gap-2">
|
|
<i data-lucide="refresh-cw" class="w-4 h-4"></i> {% trans "Update Status" %}
|
|
</button>
|
|
</form>
|
|
|
|
<!-- Escalate -->
|
|
<button type="button" onclick="document.getElementById('escalateModal').classList.remove('hidden')" class="w-full bg-red-500 text-white px-4 py-2.5 rounded-xl font-semibold hover:bg-red-600 transition flex items-center justify-center gap-2">
|
|
<i data-lucide="arrow-up-circle" class="w-4 h-4"></i> {% trans "Escalate" %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Convert to QI Project -->
|
|
{% if user.is_px_admin or user.is_hospital_admin or user.is_department_manager %}
|
|
<div class="bg-white rounded-2xl shadow-sm border border-gray-50 overflow-hidden">
|
|
<div class="px-4 py-3 bg-blue-500 text-white">
|
|
<h3 class="font-bold flex items-center gap-2">
|
|
<i data-lucide="kanban" class="w-4 h-4"></i> {% trans "QI Project" %}
|
|
</h3>
|
|
</div>
|
|
<div class="p-4">
|
|
{% if action.qi_projects.exists %}
|
|
<p class="text-sm text-slate mb-2">{% trans "Linked to:" %}</p>
|
|
<ul class="space-y-2 mb-3">
|
|
{% for project in action.qi_projects.all %}
|
|
<li>
|
|
<a href="{% url 'projects:project_detail' pk=project.pk %}" class="flex items-center gap-2 text-navy hover:underline">
|
|
<i data-lucide="link" class="w-4 h-4"></i>{{ project.name }}
|
|
</a>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% else %}
|
|
<p class="text-sm text-slate mb-3">
|
|
{% trans "Convert this action to a QI project for long-term tracking." %}
|
|
</p>
|
|
{% endif %}
|
|
<a href="{% url 'projects:convert_action' action_pk=action.pk %}"
|
|
class="w-full {% if action.qi_projects.exists %}bg-gray-300 text-gray-500 cursor-not-allowed{% else %}bg-blue-500 text-white hover:bg-blue-600{% endif %} px-4 py-2.5 rounded-xl font-semibold transition flex items-center justify-center gap-2">
|
|
<i data-lucide="arrow-right-circle" class="w-4 h-4"></i>
|
|
{% if action.qi_projects.exists %}{% trans "Already Converted" %}{% else %}{% trans "Convert to Project" %}{% endif %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Add Note -->
|
|
<div class="bg-white rounded-2xl shadow-sm border border-gray-50 overflow-hidden">
|
|
<div class="px-4 py-3 bg-green-500 text-white">
|
|
<h3 class="font-bold flex items-center gap-2">
|
|
<i data-lucide="message-square" class="w-4 h-4"></i> {% trans "Add Note" %}
|
|
</h3>
|
|
</div>
|
|
<div class="p-4">
|
|
<form method="post" action="{% url 'actions:action_add_note' action.id %}">
|
|
{% csrf_token %}
|
|
<textarea name="note" class="w-full px-3 py-2 border border-gray-200 rounded-xl text-sm mb-2 focus:ring-2 focus:ring-navy focus:border-transparent" rows="3" placeholder="{% trans 'Enter your note...' %}" required></textarea>
|
|
<button type="submit" class="w-full bg-green-500 text-white px-4 py-2.5 rounded-xl font-semibold hover:bg-green-600 transition flex items-center justify-center gap-2">
|
|
<i data-lucide="plus-circle" class="w-4 h-4"></i> {% trans "Add Note" %}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Assignment Info -->
|
|
<div class="bg-white rounded-2xl shadow-sm border border-gray-50 overflow-hidden">
|
|
<div class="px-4 py-3 border-b border-gray-100">
|
|
<h3 class="font-bold text-navy flex items-center gap-2">
|
|
<i data-lucide="info" class="w-4 h-4"></i> {% trans "Assignment Info" %}
|
|
</h3>
|
|
</div>
|
|
<div class="p-4 space-y-4">
|
|
<div>
|
|
<div class="text-xs font-semibold text-slate uppercase tracking-wide mb-1">{% trans "Assigned To" %}</div>
|
|
{% if action.assigned_to %}
|
|
<div class="font-medium text-gray-800">{{ action.assigned_to.get_full_name }}</div>
|
|
<div class="text-xs text-slate">{% trans "Assigned" %} {{ action.assigned_at|date:"M d, Y H:i" }}</div>
|
|
{% else %}
|
|
<div class="text-slate">{% trans "Unassigned" %}</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% if action.approved_by %}
|
|
<div>
|
|
<div class="text-xs font-semibold text-slate uppercase tracking-wide mb-1">{% trans "Approved By" %}</div>
|
|
<div class="font-medium text-gray-800">{{ action.approved_by.get_full_name }}</div>
|
|
<div class="text-xs text-slate">{{ action.approved_at|date:"M d, Y H:i" }}</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if action.closed_by %}
|
|
<div>
|
|
<div class="text-xs font-semibold text-slate uppercase tracking-wide mb-1">{% trans "Closed By" %}</div>
|
|
<div class="font-medium text-gray-800">{{ action.closed_by.get_full_name }}</div>
|
|
<div class="text-xs text-slate">{{ action.closed_at|date:"M d, Y H:i" }}</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- SLA Info -->
|
|
<div class="bg-white rounded-2xl shadow-sm border border-gray-50 overflow-hidden">
|
|
<div class="px-4 py-3 bg-orange-500 text-white">
|
|
<h3 class="font-bold flex items-center gap-2">
|
|
<i data-lucide="clock" class="w-4 h-4"></i> {% trans "SLA Information" %}
|
|
</h3>
|
|
</div>
|
|
<div class="p-4 space-y-3">
|
|
<div>
|
|
<div class="text-xs font-semibold text-slate uppercase tracking-wide mb-1">{% trans "Due Date" %}</div>
|
|
<div class="{% if action.is_overdue %}text-red-600 font-bold{% else %}text-gray-800{% endif %}">
|
|
{{ action.due_at|date:"M d, Y H:i" }}
|
|
</div>
|
|
</div>
|
|
{% if action.escalated_at %}
|
|
<div>
|
|
<div class="text-xs font-semibold text-slate uppercase tracking-wide mb-1">{% trans "Escalated" %}</div>
|
|
<div class="flex items-center gap-2">
|
|
{{ action.escalated_at|date:"M d, Y H:i" }}
|
|
<span class="px-2 py-0.5 rounded text-xs font-bold bg-red-100 text-red-600">Level {{ action.escalation_level }}</span>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% if action.reminder_sent_at %}
|
|
<div>
|
|
<div class="text-xs font-semibold text-slate uppercase tracking-wide mb-1">{% trans "Reminder Sent" %}</div>
|
|
<div class="text-gray-800">{{ action.reminder_sent_at|date:"M d, Y H:i" }}</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Escalate Modal -->
|
|
<div id="escalateModal" class="fixed inset-0 bg-black/50 z-50 hidden flex items-center justify-center p-4">
|
|
<div class="bg-white rounded-2xl shadow-xl max-w-md w-full">
|
|
<form method="post" action="{% url 'actions:action_escalate' action.id %}">
|
|
{% csrf_token %}
|
|
<div class="p-4 border-b border-gray-100">
|
|
<h3 class="text-lg font-bold text-navy">{% trans "Escalate Action" %}</h3>
|
|
</div>
|
|
<div class="p-4">
|
|
<div class="bg-orange-50 border border-orange-200 rounded-xl p-3 mb-4 text-sm text-orange-700">
|
|
<i data-lucide="alert-triangle" class="w-4 h-4 inline me-2"></i>
|
|
{% trans "This will escalate the action to the next level." %}
|
|
{% if action.escalation_level > 0 %}
|
|
{% trans "Current level:" %} {{ action.escalation_level }}
|
|
{% endif %}
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs font-semibold text-slate uppercase tracking-wide mb-2">{% trans "Reason for Escalation" %}</label>
|
|
<textarea name="reason" class="w-full px-3 py-2 border border-gray-200 rounded-xl focus:ring-2 focus:ring-navy focus:border-transparent" rows="3" placeholder="{% trans 'Explain why this action needs escalation...' %}" required></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="p-4 border-t border-gray-100 flex justify-end gap-3">
|
|
<button type="button" onclick="document.getElementById('escalateModal').classList.add('hidden')" class="px-4 py-2 bg-gray-100 text-gray-700 rounded-xl font-semibold hover:bg-gray-200 transition">
|
|
{% trans "Cancel" %}
|
|
</button>
|
|
<button type="submit" class="px-4 py-2 bg-red-500 text-white rounded-xl font-semibold hover:bg-red-600 transition flex items-center gap-2">
|
|
<i data-lucide="arrow-up-circle" class="w-4 h-4"></i> {% trans "Escalate" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Tab switching
|
|
const tabBtns = document.querySelectorAll('.tab-btn');
|
|
tabBtns.forEach(btn => {
|
|
btn.addEventListener('click', function() {
|
|
// Update button styles
|
|
tabBtns.forEach(b => {
|
|
b.classList.remove('bg-navy', 'text-white');
|
|
b.classList.add('text-slate', 'hover:bg-gray-100');
|
|
});
|
|
this.classList.add('bg-navy', 'text-white');
|
|
this.classList.remove('text-slate', 'hover:bg-gray-100');
|
|
|
|
// Show/hide content
|
|
const tabId = this.dataset.tab;
|
|
document.querySelectorAll('.tab-content').forEach(content => {
|
|
content.classList.add('hidden');
|
|
});
|
|
document.getElementById('tab-' + tabId).classList.remove('hidden');
|
|
});
|
|
});
|
|
|
|
// Initialize first tab as active
|
|
const firstTab = document.querySelector('.tab-btn');
|
|
if (firstTab) {
|
|
firstTab.classList.add('bg-navy', 'text-white');
|
|
firstTab.classList.remove('text-slate', 'hover:bg-gray-100');
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %} |