HH/templates/accounts/onboarding/step_checklist.html
2026-04-08 17:13:35 +03:00

240 lines
12 KiB
HTML

{% extends "layouts/base.html" %}
{% load i18n %}
{% block sidebar %}{% endblock %}
{% block title %}{% trans "Acknowledgement Checklist" %}{% endblock %}
{% block content %}
<div class="min-h-screen bg-gradient-to-br from-light to-blue-50 flex items-center justify-center py-12 px-4">
<div class="max-w-3xl w-full">
<div class="bg-white rounded-2xl shadow-xl p-8 md:p-12">
<!-- Header -->
<div class="text-center mb-8">
<div class="inline-flex items-center justify-center w-20 h-20 bg-navy rounded-full mb-6">
<i data-lucide="clipboard-check" class="w-10 h-10 text-white"></i>
</div>
<h1 class="text-xl md:text-2xl font-bold text-navy mb-1">
{% trans "Acknowledgement Checklist" %}
</h1>
<p class="text-sm text-slate">
{% trans "Please review and acknowledge the following items" %}
</p>
</div>
<!-- Progress -->
<div class="mb-8">
<div class="flex items-center justify-between mb-2">
<span class="text-sm font-medium text-gray-500">
{{ acknowledged_count }} {% trans "of" %} {{ total_count }} {% trans "completed" %}
</span>
<span id="progressText" class="text-sm font-medium text-navy">{{ progress_percentage }}%</span>
</div>
<div class="w-full bg-gray-200 h-2.5 rounded-full overflow-hidden">
<div id="progressBar" class="bg-navy h-full rounded-full transition-all duration-500" style="width: {{ progress_percentage }}%"></div>
</div>
</div>
<!-- Checklist Items grouped by category -->
{% regroup checklist_items by category as category_groups %}
<div class="space-y-6 mb-8" id="checklistContainer">
{% for category, items in category_groups %}
<div>
<h3 class="text-lg font-bold text-gray-800 mb-3 flex items-center gap-2">
<span class="inline-flex items-center justify-center w-8 h-8 rounded-lg text-white text-sm" style="background-color: {{ category.color }}">
<i data-lucide="{{ category.icon|default:'folder' }}" class="w-4 h-4"></i>
</span>
{{ category.get_localized_name }}
<span class="text-xs font-normal text-gray-400">({{ items|length }})</span>
</h3>
<div class="space-y-3">
{% for item in items %}
<div id="item-{{ item.id }}" data-acknowledged="{% if item.is_acknowledged %}true{% else %}false{% endif %}" class="group flex items-start gap-4 p-4 border-2 rounded-2xl transition cursor-pointer
{% if item.is_acknowledged %}bg-green-50 border-green-200{% else %}bg-white border-gray-100 hover:border-blue-200 hover:shadow-sm{% endif %}">
<!-- Status Icon -->
<div class="flex-shrink-0 mt-0.5">
{% if item.is_acknowledged %}
<div class="w-7 h-7 rounded-full bg-green-500 flex items-center justify-center">
<i data-lucide="check" class="w-4 h-4 text-white"></i>
</div>
{% else %}
<div class="w-7 h-7 rounded-full border-2 border-gray-300 group-hover:border-navy transition"></div>
{% endif %}
</div>
<!-- Content -->
<div class="flex-1 min-w-0">
<div class="flex items-start justify-between gap-2">
<div class="font-semibold text-gray-800">
{{ item.get_localized_text }}
</div>
<div class="flex items-center gap-2 flex-shrink-0">
{% if item.is_required %}
<span class="text-xs bg-red-50 text-red-600 font-medium px-2 py-0.5 rounded-full">
{% trans "Required" %}
</span>
{% endif %}
<span class="text-xs bg-gray-100 text-gray-500 px-2 py-0.5 rounded-full font-mono">
{{ item.code }}
</span>
</div>
</div>
{% if item.get_localized_description %}
<p class="text-sm text-gray-500 mt-1">{{ item.get_localized_description }}</p>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
{% empty %}
<div class="text-center py-12">
<i data-lucide="inbox" class="w-12 h-12 text-gray-300 mx-auto mb-3"></i>
<p class="text-gray-500">{% trans "No checklist items found." %}</p>
</div>
{% endfor %}
</div>
<!-- Signature Modal -->
<div id="signatureModal" class="hidden fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4">
<div class="bg-white rounded-2xl shadow-2xl max-w-md w-full p-8">
<div class="text-center mb-6">
<div class="inline-flex items-center justify-center w-16 h-16 bg-blue-50 rounded-2xl mb-4">
<i data-lucide="pen-tool" class="w-8 h-8 text-navy"></i>
</div>
<h3 class="text-xl font-bold text-gray-800">{% trans "Sign to Acknowledge" %}</h3>
<p id="signatureItemText" class="text-sm text-gray-500 mt-1"></p>
</div>
<div class="mb-6">
<label class="block text-gray-700 font-medium mb-2">{% trans "Type your full name as signature" %}</label>
<input type="text" id="signatureInput" required autocomplete="name"
class="w-full px-4 py-3 border-2 border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-navy focus:border-transparent text-lg"
placeholder="{% trans 'Your full name' %}">
</div>
<div class="flex gap-3">
<button onclick="closeSignatureModal()" class="flex-1 px-6 py-3 border-2 border-gray-200 rounded-xl font-medium text-gray-600 hover:bg-gray-50 transition">
{% trans "Cancel" %}
</button>
<button onclick="submitAcknowledgement()" id="submitSignatureBtn" class="flex-1 px-6 py-3 bg-navy text-white rounded-xl font-bold hover:bg-blue transition shadow-lg">
{% trans "Acknowledge" %}
</button>
</div>
</div>
</div>
<!-- Continue Button -->
<a href="/accounts/onboarding/wizard/activation/" class="block w-full bg-navy text-white px-6 py-4 rounded-xl font-bold text-center hover:bg-blue transition shadow-lg">
{% trans "Continue to Account Setup" %}
<i data-lucide="arrow-right" class="w-5 h-5 inline ml-2"></i>
</a>
<!-- Help Tip -->
<div class="bg-blue-50 border border-blue-200 rounded-2xl p-5 mt-6">
<div class="flex items-start gap-3">
<i data-lucide="info" class="w-6 h-6 text-blue-600 flex-shrink-0 mt-0.5"></i>
<div>
<p class="text-blue-800 font-medium mb-1">{% trans "Important" %}</p>
<p class="text-blue-700 text-sm">
{% trans "Click on each item to acknowledge it with your signature. Required items must be acknowledged before you can activate your account." %}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let pendingItemId = null;
document.addEventListener('DOMContentLoaded', function() {
lucide.createIcons();
document.querySelectorAll('#checklistContainer [id^="item-"]').forEach(el => {
el.addEventListener('click', function() {
const itemId = this.id.replace('item-', '');
if (this.dataset.acknowledged === 'true') return;
pendingItemId = itemId;
openSignatureModal(this.querySelector('.font-semibold').textContent.trim());
});
});
});
function openSignatureModal(itemText) {
document.getElementById('signatureItemText').textContent = itemText;
document.getElementById('signatureInput').value = '';
document.getElementById('signatureModal').classList.remove('hidden');
document.getElementById('signatureInput').focus();
}
function closeSignatureModal() {
document.getElementById('signatureModal').classList.add('hidden');
pendingItemId = null;
}
function submitAcknowledgement() {
const signature = document.getElementById('signatureInput').value.trim();
if (!signature) {
document.getElementById('signatureInput').classList.add('border-red-400', 'ring-2', 'ring-red-200');
document.getElementById('signatureInput').focus();
return;
}
const btn = document.getElementById('submitSignatureBtn');
btn.disabled = true;
btn.innerHTML = '<i data-lucide="loader-2" class="w-5 h-5 inline animate-spin"></i> {% trans "Processing..." %}';
lucide.createIcons();
fetch('/accounts/users/onboarding/acknowledge/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': document.querySelector('input[name="csrfmiddlewaretoken"]')?.value,
},
body: JSON.stringify({
checklist_item_id: pendingItemId,
signature: signature,
}),
})
.then(response => {
if (!response.ok) throw new Error('Failed');
return response.json();
})
.then(data => {
const itemEl = document.getElementById('item-' + pendingItemId);
if (itemEl) {
itemEl.dataset.acknowledged = 'true';
itemEl.classList.remove('bg-white', 'border-gray-100', 'hover:border-blue-200', 'hover:shadow-sm');
itemEl.classList.add('bg-green-50', 'border-green-200');
itemEl.querySelector('.flex-shrink-0.mt-0\\.5').innerHTML =
'<div class="w-7 h-7 rounded-full bg-green-500 flex items-center justify-center">' +
'<i data-lucide="check" class="w-4 h-4 text-white"></i></div>';
}
updateProgress();
closeSignatureModal();
})
.catch(err => {
alert('{% trans "Failed to acknowledge item. Please try again." %}');
})
.finally(() => {
btn.disabled = false;
btn.innerHTML = '{% trans "Acknowledge" %}';
lucide.createIcons();
});
}
function updateProgress() {
const total = {{ total_count }};
const acknowledged = document.querySelectorAll('#checklistContainer [data-acknowledged="true"]').length;
const percentage = total > 0 ? Math.round((acknowledged / total) * 100) : 0;
document.getElementById('progressBar').style.width = percentage + '%';
document.getElementById('progressText').textContent = percentage + '%';
document.querySelector('#progressText').previousElementSibling.innerHTML =
acknowledged + ' {% trans "of" %} ' + total + ' {% trans "completed" %}';
}
document.getElementById('signatureInput')?.addEventListener('keydown', function(e) {
if (e.key === 'Enter') submitAcknowledgement();
});
</script>
{% endblock %}