HH/templates/core/public_track.html
ismail c5f76b3855
Some checks are pending
Build and Push Docker Image / build (push) Waiting to run
updates
2026-05-11 14:45:30 +03:00

438 lines
20 KiB
HTML

{% extends "layouts/public_base.html" %}
{% load i18n static %}
{% block title %}{% trans "Track Your Submission" %} - PX360{% endblock %}
{% block extra_css %}
<style>
header.glass-card {
display: none !important;
}
.lang-switcher {
position: fixed;
top: 1rem;
right: 1rem;
z-index: 100;
display: flex;
gap: 0.5rem;
}
.lang-switcher a {
padding: 0.5rem 1rem;
border-radius: 0.75rem;
border: 2px solid rgba(255,255,255,0.3);
background: rgba(255,255,255,0.1);
backdrop-filter: blur(10px);
color: white;
font-weight: 600;
font-size: 0.875rem;
transition: all 0.2s ease;
text-decoration: none;
display: flex;
align-items: center;
gap: 0.5rem;
}
.lang-switcher a:hover {
background: rgba(255,255,255,0.2);
border-color: rgba(255,255,255,0.5);
}
.lang-switcher a.active {
background: white;
color: #005696;
border-color: white;
}
[dir="rtl"] .lang-switcher {
right: auto;
left: 1rem;
}
.selection-card {
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
cursor: pointer;
}
.selection-card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px -6px rgba(0, 86, 150, 0.2);
}
.selection-card.active {
transform: translateY(-4px);
box-shadow: 0 12px 24px -6px rgba(0, 86, 150, 0.3);
}
.timeline-dot::after {
content: '';
position: absolute;
width: 2px;
height: 100%;
background: #e2e8f0;
left: 50%;
transform: translateX(-50%);
top: 24px;
z-index: 0;
}
.timeline-item:last-child .timeline-dot::after {
display: none;
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-slide-up {
animation: slideUp 0.5s ease-out forwards;
}
@keyframes subtle-float {
0% { transform: translateY(0px); }
50% { transform: translateY(-5px); }
100% { transform: translateY(0px); }
}
.float-icon { animation: subtle-float 3s ease-in-out infinite; }
.spinner {
display: inline-block;
width: 1.5rem;
height: 1.5rem;
border: 3px solid rgba(255,255,255,0.3);
border-radius: 50%;
border-top-color: white;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
{% endblock %}
{% block content %}
<!-- Language Switcher -->
<div class="lang-switcher">
{% get_available_languages as LANGUAGES %}
{% get_current_language as LANGUAGE_CODE %}
{% for lang_code, lang_name in LANGUAGES %}
<a href="{% url 'core:set_language' %}?language={{ lang_code }}"
class="{% if LANGUAGE_CODE == lang_code %}active{% endif %}">
<span>{% if lang_code == 'en' %}🇬🇧{% elif lang_code == 'ar' %}🇸🇦{% endif %}</span>
<span>{{ lang_name }}</span>
</a>
{% endfor %}
</div>
<div class="max-w-4xl mx-auto px-4 py-8 md:py-12">
<!-- Logo Banner -->
<div class="rounded-3xl shadow-2xl overflow-hidden mb-8 text-center animate-fade-in">
<div class="bg-white w-full py-8 px-6 flex items-center justify-center">
<img src="{% static 'img/hh-logo.png' %}" alt="Al Hammadi Hospital" class="max-h-16 w-auto object-contain">
</div>
<div class="bg-white p-8">
<h1 class="text-2xl font-bold text-navy mb-3">{% trans "Track Your Submission" %}</h1>
<p class="text-slate text-base max-w-xl mx-auto">
{% trans "Select a category below and enter your reference number to check the status." %}
</p>
</div>
</div>
<!-- Selection Cards -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8" id="selectionCards">
<!-- Complaint Card -->
<div class="selection-card bg-white rounded-2xl shadow-xl border-2 border-transparent p-6 text-center relative overflow-hidden group"
data-type="complaint" onclick="selectType('complaint')">
<div class="absolute top-0 left-0 right-0 h-2 bg-gradient-to-r from-red-400 to-red-600"></div>
<div class="w-14 h-14 rounded-2xl bg-gradient-to-br from-red-50 to-red-100 mx-auto mb-3 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<i data-lucide="alert-circle" class="w-7 h-7 text-red-600"></i>
</div>
<h3 class="text-lg font-bold text-navy mb-1">{% trans "Complaint" %}</h3>
<p class="text-slate text-xs">{% trans "e.g., CMP-20240101-123456" %}</p>
</div>
<!-- Inquiry Card -->
<div class="selection-card bg-white rounded-2xl shadow-xl border-2 border-transparent p-6 text-center relative overflow-hidden group"
data-type="inquiry" onclick="selectType('inquiry')">
<div class="absolute top-0 left-0 right-0 h-2 bg-gradient-to-r from-amber-400 to-amber-600"></div>
<div class="w-14 h-14 rounded-2xl bg-gradient-to-br from-amber-50 to-amber-100 mx-auto mb-3 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<i data-lucide="help-circle" class="w-7 h-7 text-amber-600"></i>
</div>
<h3 class="text-lg font-bold text-navy mb-1">{% trans "Inquiry" %}</h3>
<p class="text-slate text-xs">{% trans "e.g., INQ-20260428-123456" %}</p>
</div>
<!-- Observation Card -->
<div class="selection-card bg-white rounded-2xl shadow-xl border-2 border-transparent p-6 text-center relative overflow-hidden group"
data-type="observation" onclick="selectType('observation')">
<div class="absolute top-0 left-0 right-0 h-2 bg-gradient-to-r from-purple-400 to-purple-600"></div>
<div class="w-14 h-14 rounded-2xl bg-gradient-to-br from-purple-50 to-purple-100 mx-auto mb-3 flex items-center justify-center group-hover:scale-110 transition-transform duration-300">
<i data-lucide="eye" class="w-7 h-7 text-purple-600"></i>
</div>
<h3 class="text-lg font-bold text-navy mb-1">{% trans "Observation" %}</h3>
<p class="text-slate text-xs">{% trans "e.g., OBS-ABC123" %}</p>
</div>
</div>
<!-- Search Form (hidden until type selected) -->
<div id="searchForm" class="glass-card rounded-3xl shadow-2xl p-8 mb-8 hidden">
<form id="trackForm" class="max-w-lg mx-auto">
{% csrf_token %}
<input type="hidden" id="trackType" name="type" value="">
<div class="relative group">
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
<i data-lucide="hash" class="w-5 h-5 text-slate/40 group-focus-within:text-blue transition-colors"></i>
</div>
<input type="text" id="referenceInput" name="reference"
class="w-full pl-12 pr-6 py-5 border-2 border-slate-100 rounded-2xl text-navy text-lg focus:ring-4 focus:ring-blue/10 focus:border-blue transition-all duration-300 bg-white/80 placeholder:text-slate/30"
placeholder="{% trans 'Enter your reference number' %}"
required>
</div>
<button type="submit" id="trackBtn" class="w-full mt-4 bg-navy hover:bg-navy/90 text-white px-8 py-5 rounded-2xl font-bold text-lg transition-all duration-300 shadow-lg shadow-navy/20 hover:shadow-xl hover:-translate-y-1 flex items-center justify-center gap-3">
<i data-lucide="crosshair" class="w-5 h-5"></i>
{% trans "Track Status" %}
</button>
</form>
<p class="text-center text-slate/50 text-xs mt-6 uppercase tracking-widest font-semibold">
<i data-lucide="info" class="w-3 h-3 inline mr-1"></i>
{% trans "Found in your confirmation email or SMS" %}
</p>
</div>
<!-- Error Message -->
<div id="errorBox" class="bg-white rounded-3xl shadow-lg p-6 mb-8 hidden flex items-center gap-4">
<div class="w-12 h-12 bg-rose-100 rounded-xl flex items-center justify-center text-rose-600 shrink-0">
<i data-lucide="alert-circle" class="w-6 h-6"></i>
</div>
<div>
<h3 class="font-bold text-rose-900">{% trans "Not Found" %}</h3>
<p id="errorText" class="text-rose-700/80 text-sm"></p>
</div>
</div>
<!-- Loading -->
<div id="loadingBox" class="text-center py-16 hidden">
<div class="inline-block w-14 h-14 border-4 border-slate-200 border-t-blue rounded-full animate-spin mb-4"></div>
<p class="text-slate text-sm font-medium">{% trans "Looking up your submission..." %}</p>
</div>
<!-- Results -->
<div id="resultsBox" class="hidden animate-slide-up">
<!-- Status Header -->
<div class="bg-white rounded-3xl shadow-2xl p-6 md:p-8 mb-6">
<div class="flex flex-col md:flex-row md:items-center justify-between gap-6">
<div>
<span class="text-xs font-bold text-slate/40 uppercase tracking-widest block mb-1" id="resultRefLabel"></span>
<h2 class="text-3xl font-black text-navy" id="resultReference"></h2>
</div>
<div class="flex items-center gap-3">
<div class="text-right hidden md:block">
<span class="text-xs font-bold text-slate/40 uppercase tracking-widest block mb-1">{% trans "Current Status" %}</span>
<p class="font-bold text-navy" id="resultStatusText"></p>
</div>
<div id="resultStatusBadge" class="px-6 py-3 rounded-2xl text-sm font-black uppercase tracking-wider shadow-sm border-b-4"></div>
<div id="resultEscalated" class="px-4 py-2 rounded-2xl text-xs font-bold uppercase tracking-wider bg-red-50 text-red-700 border border-red-200 flex items-center gap-2 hidden">
<i data-lucide="alert-triangle" class="w-4 h-4"></i>
{% trans "Escalated" %}
</div>
</div>
</div>
<div class="mt-8 h-2 w-full bg-slate-100 rounded-full overflow-hidden">
<div id="resultProgressBar" class="h-full bg-navy transition-all duration-1000" style="width: 0%"></div>
</div>
</div>
<!-- Info Cards -->
<div id="resultInfoCards" class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-10"></div>
<!-- Timeline -->
<div class="bg-white rounded-3xl shadow-2xl p-8 md:p-10">
<h3 class="text-2xl font-bold text-navy mb-10 flex items-center gap-3">
<div class="p-2 bg-navy text-white rounded-lg">
<i data-lucide="list-checks" class="w-5 h-5"></i>
</div>
{% trans "Resolution Journey" %}
</h3>
<div id="resultTimeline" class="space-y-1"></div>
<div id="resultNoTimeline" class="text-center py-12 hidden">
<div class="w-20 h-20 bg-slate-50 rounded-full flex items-center justify-center mx-auto mb-4">
<i data-lucide="loader" class="w-8 h-8 text-slate/30 animate-spin"></i>
</div>
<p class="text-slate/60 font-medium">{% trans "Your submission is being reviewed. Updates will appear here." %}</p>
</div>
</div>
<!-- Privacy Note -->
<div class="bg-white/80 backdrop-blur rounded-2xl p-5 mt-6 flex items-start gap-3 border border-white/50">
<i data-lucide="shield" class="w-5 h-5 text-navy mt-0.5 shrink-0"></i>
<p class="text-sm text-slate/60">{% trans "For privacy reasons, detailed notes and internal communications are not shown here." %}</p>
</div>
</div>
<!-- Back Link -->
<div class="text-center mt-8">
<a href="{% url 'core:public_submit_landing' %}" class="text-sm text-blue hover:text-blue-700 transition font-medium">
<i data-lucide="arrow-left" class="w-4 h-4 inline mr-1"></i>
{% trans "Back to Submit Feedback" %}
</a>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof lucide !== 'undefined') lucide.createIcons();
var selectedType = null;
window.selectType = function(type) {
selectedType = type;
document.getElementById('trackType').value = type;
document.getElementById('searchForm').classList.remove('hidden');
document.getElementById('errorBox').classList.add('hidden');
document.getElementById('resultsBox').classList.add('hidden');
document.getElementById('loadingBox').classList.add('hidden');
var placeholders = {
complaint: "{% trans 'e.g., CMP-20240101-123456' %}",
inquiry: "{% trans 'e.g., INQ-20260428-123456' %}",
observation: "{% trans 'e.g., OBS-ABC123' %}"
};
document.getElementById('referenceInput').placeholder = placeholders[type] || '';
document.getElementById('referenceInput').focus();
document.querySelectorAll('.selection-card').forEach(function(card) {
var isActive = card.getAttribute('data-type') === type;
card.classList.toggle('active', isActive);
card.style.borderColor = isActive ? (type === 'complaint' ? '#ef4444' : type === 'inquiry' ? '#f59e0b' : '#a855f7') : 'transparent';
});
};
document.getElementById('trackForm').addEventListener('submit', function(e) {
e.preventDefault();
var reference = document.getElementById('referenceInput').value.trim();
if (!reference || !selectedType) return;
document.getElementById('errorBox').classList.add('hidden');
document.getElementById('resultsBox').classList.add('hidden');
document.getElementById('loadingBox').classList.remove('hidden');
var btn = document.getElementById('trackBtn');
var originalBtn = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = '<span class="spinner"></span> {% trans "Searching..." %}';
var url = '{% url "core:public_track_api" %}?type=' + encodeURIComponent(selectedType) + '&reference=' + encodeURIComponent(reference);
fetch(url)
.then(function(r) { return r.json(); })
.then(function(data) {
btn.disabled = false;
btn.innerHTML = originalBtn;
document.getElementById('loadingBox').classList.add('hidden');
if (data.found) {
renderResults(data);
} else {
document.getElementById('errorText').textContent = data.error || "{% trans 'No submission found with this reference number.' %}";
document.getElementById('errorBox').classList.remove('hidden');
document.getElementById('errorBox').style.display = 'flex';
lucide.createIcons();
}
})
.catch(function() {
btn.disabled = false;
btn.innerHTML = originalBtn;
document.getElementById('loadingBox').classList.add('hidden');
document.getElementById('errorText').textContent = "{% trans 'An error occurred. Please try again.' %}";
document.getElementById('errorBox').classList.remove('hidden');
document.getElementById('errorBox').style.display = 'flex';
lucide.createIcons();
});
});
function renderResults(data) {
var refLabels = {
complaint: "{% trans 'Case Reference' %}",
inquiry: "{% trans 'Inquiry Reference' %}",
observation: "{% trans 'Tracking Code' %}"
};
document.getElementById('resultRefLabel').textContent = refLabels[data.type] || "{% trans 'Reference' %}";
document.getElementById('resultReference').textContent = data.reference;
document.getElementById('resultStatusText').textContent = data.status_display;
var colorMap = {
amber: 'bg-amber-50 text-amber-700 border-amber-200',
blue: 'bg-blue-50 text-blue-700 border-blue-200',
emerald: 'bg-emerald-50 text-emerald-700 border-emerald-200',
rose: 'bg-rose-50 text-rose-700 border-rose-200',
slate: 'bg-slate-50 text-slate-700 border-slate-200',
sky: 'bg-sky-50 text-sky-700 border-sky-200',
teal: 'bg-teal-50 text-teal-700 border-teal-200'
};
var badge = document.getElementById('resultStatusBadge');
badge.className = 'px-6 py-3 rounded-2xl text-sm font-black uppercase tracking-wider shadow-sm border-b-4 ' + (colorMap[data.status_color] || colorMap.slate);
badge.textContent = data.status_display;
var esc = document.getElementById('resultEscalated');
if (data.escalated) { esc.classList.remove('hidden'); } else { esc.classList.add('hidden'); }
document.getElementById('resultProgressBar').style.width = data.progress + '%';
var cardsHtml = '';
(data.info_cards || []).forEach(function(card) {
var iconColor = card.alert ? 'text-rose-500' : (card.severity === 'critical' || card.severity === 'high' ? 'text-rose-500' : card.severity === 'medium' ? 'text-amber-500' : 'text-blue');
var alertDot = card.alert ? '<span class="absolute top-2 right-2 flex h-2 w-2"><span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-rose-400 opacity-75"></span><span class="relative inline-flex rounded-full h-2 w-2 bg-rose-500"></span></span>' : '';
cardsHtml += '<div class="bg-white p-6 rounded-2xl border border-slate-100 shadow-sm hover:shadow-md transition relative overflow-hidden">' +
'<i data-lucide="' + card.icon + '" class="w-5 h-5 ' + iconColor + ' mb-3"></i>' +
'<span class="block text-xs font-bold text-slate/50 uppercase">' + card.label + '</span>' +
'<p class="font-bold text-navy truncate">' + card.value + '</p>' +
alertDot +
'</div>';
});
document.getElementById('resultInfoCards').innerHTML = cardsHtml;
var timeline = data.timeline || [];
var timelineEl = document.getElementById('resultTimeline');
var noTimelineEl = document.getElementById('resultNoTimeline');
if (timeline.length > 0) {
noTimelineEl.classList.add('hidden');
var tlHtml = '';
timeline.forEach(function(item) {
var iconBg = item.type === 'status_change' ? 'bg-amber-100 text-amber-600' : (item.type === 'resolution' || item.type === 'response' ? 'bg-emerald-100 text-emerald-600' : 'bg-blue-50 text-blue-600');
tlHtml += '<div class="timeline-item flex gap-6 pb-10 relative">' +
'<div class="timeline-dot shrink-0 relative z-10">' +
'<div class="w-12 h-12 rounded-2xl flex items-center justify-center shadow-sm border-2 border-white ' + iconBg + '">' +
'<i data-lucide="' + item.icon + '" class="w-6 h-6"></i>' +
'</div>' +
'</div>' +
'<div class="flex-1 pt-1">' +
'<div class="flex flex-col md:flex-row md:items-center justify-between mb-2">' +
'<h4 class="font-black text-navy text-lg">' + item.title + '</h4>' +
'<time class="text-sm font-medium text-slate/40">' + item.created_at + '</time>' +
'</div>';
if (item.comment) {
tlHtml += '<div class="bg-slate-50/50 rounded-2xl p-5 border border-slate-100 text-slate-700 leading-relaxed shadow-inner">' + item.comment.replace(/\n/g, '<br>') + '</div>';
}
tlHtml += '</div></div>';
});
timelineEl.innerHTML = tlHtml;
timelineEl.classList.remove('hidden');
} else {
timelineEl.innerHTML = '';
timelineEl.classList.add('hidden');
noTimelineEl.classList.remove('hidden');
}
document.getElementById('resultsBox').classList.remove('hidden');
lucide.createIcons();
document.getElementById('resultsBox').scrollIntoView({ behavior: 'smooth', block: 'start' });
}
});
</script>
{% endblock %}