HH/templates/accounts/onboarding/checklist_list.html
2026-02-22 08:35:53 +03:00

447 lines
24 KiB
HTML

{% extends "layouts/base.html" %}
{% load i18n %}
{% block title %}{% trans "Acknowledgement Checklist Items" %}{% endblock %}
{% block content %}
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Page Header -->
<div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-8 gap-4">
<div>
<h2 class="text-3xl font-bold text-gray-800 mb-2 flex items-center gap-2">
<i data-lucide="list-checks" class="w-8 h-8 text-navy"></i>
{% trans "Checklist Items Management" %}
</h2>
<p class="text-gray-500">{% trans "Manage acknowledgement checklist items" %}</p>
</div>
<button type="button" class="bg-light0 text-white px-6 py-3 rounded-xl font-bold hover:bg-navy transition flex items-center gap-2 shadow-lg shadow-blue-200" onclick="document.getElementById('createChecklistItemModal').classList.remove('hidden')">
<i data-lucide="plus" class="w-5 h-5"></i>
{% trans "Add Checklist Item" %}
</button>
</div>
<!-- Checklist Items List -->
<div class="bg-white rounded-2xl shadow-sm border border-gray-50 overflow-hidden">
<div class="px-6 py-4 border-b border-gray-100 flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
<h3 class="font-bold text-gray-800 flex items-center gap-2">
<i data-lucide="list" class="w-5 h-5 text-navy"></i>
{% trans "Checklist Items" %}
</h3>
<div class="flex items-center gap-2 w-full md:w-auto">
<input type="text" id="searchInput" placeholder="{% trans 'Search items...' %}"
class="flex-1 md:w-64 px-4 py-2 border-2 border-gray-200 rounded-xl text-gray-800 focus:ring-2 focus:ring-navy focus:border-transparent transition">
<button class="px-4 py-2 text-gray-600 bg-gray-100 rounded-xl hover:bg-gray-200 transition">
<i data-lucide="search" class="w-4 h-4"></i>
</button>
</div>
</div>
<div class="overflow-x-auto">
<table class="w-full" id="itemsTable">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">{% trans "Item Text" %}</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">{% trans "Role" %}</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">{% trans "Linked Content" %}</th>
<th class="px-6 py-4 text-center text-xs font-bold text-gray-500 uppercase tracking-wider">{% trans "Required" %}</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">{% trans "Order" %}</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">{% trans "Status" %}</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">{% trans "Created" %}</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
{% for item in checklist_items %}
<tr class="hover:bg-gray-50 transition">
<td class="px-6 py-4">
<strong class="text-gray-800">{{ item.text_en }}</strong>
{% if item.code %}
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-bold bg-gray-100 text-gray-600 ml-2">{{ item.code }}</span>
{% endif %}
{% if item.description_en %}
<p class="text-sm text-gray-500 mt-1">{{ item.description_en }}</p>
{% endif %}
</td>
<td class="px-6 py-4">
{% if item.role %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-bold bg-blue-100 text-blue-700">
{{ item.get_role_display }}
</span>
{% else %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-bold bg-gray-100 text-gray-700">{% trans "All Roles" %}</span>
{% endif %}
</td>
<td class="px-6 py-4">
{% if item.content %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-bold bg-gray-100 text-gray-700">
{{ item.content.title_en }}
</span>
{% else %}
<span class="text-gray-400">-</span>
{% endif %}
</td>
<td class="px-6 py-4 text-center">
{% if item.is_required %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-bold bg-red-100 text-red-700">
<i data-lucide="alert-circle" class="w-3 h-3 mr-1"></i>
{% trans "Yes" %}
</span>
{% else %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-bold bg-gray-100 text-gray-700">{% trans "No" %}</span>
{% endif %}
</td>
<td class="px-6 py-4 text-gray-800">{{ item.order }}</td>
<td class="px-6 py-4">
{% if item.is_active %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-bold bg-emerald-100 text-emerald-700">
<i data-lucide="check" class="w-3 h-3 mr-1"></i>
{% trans "Active" %}
</span>
{% else %}
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-bold bg-gray-100 text-gray-700">
<i data-lucide="x" class="w-3 h-3 mr-1"></i>
{% trans "Inactive" %}
</span>
{% endif %}
</td>
<td class="px-6 py-4 text-gray-500 text-sm">
{{ item.created_at|date:"M d, Y" }}
</td>
<td class="px-6 py-4">
<div class="flex gap-2">
<button class="px-3 py-2 text-blue-600 bg-blue-50 rounded-lg hover:bg-blue-100 transition font-medium text-sm" title="{% trans 'Edit' %}">
<i data-lucide="pencil" class="w-4 h-4"></i>
</button>
<button class="px-3 py-2 text-red-600 bg-red-50 rounded-lg hover:bg-red-100 transition font-medium text-sm" title="{% trans 'Delete' %}">
<i data-lucide="trash-2" class="w-4 h-4"></i>
</button>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="8" class="text-center py-12">
<i data-lucide="clipboard-data" class="w-16 h-16 text-gray-300 mx-auto mb-4"></i>
<p class="text-gray-500">{% trans "No checklist items found" %}</p>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<!-- Create Checklist Item Modal -->
<div id="createChecklistItemModal" class="fixed inset-0 bg-black/50 hidden items-center justify-center z-50">
<div class="bg-white rounded-2xl shadow-xl w-full max-w-4xl max-h-[90vh] overflow-y-auto m-4">
<div class="px-6 py-4 border-b border-gray-100 flex justify-between items-center">
<h3 class="font-bold text-gray-800 flex items-center gap-2">
<i data-lucide="plus-circle" class="w-5 h-5 text-navy"></i>
{% trans "Add New Checklist Item" %}
</h3>
<button type="button" onclick="document.getElementById('createChecklistItemModal').classList.add('hidden')" class="text-gray-400 hover:text-gray-600 transition">
<i data-lucide="x" class="w-6 h-6"></i>
</button>
</div>
<div class="p-6">
<form id="createChecklistItemForm">
{% csrf_token %}
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Code -->
<div>
<label for="code" class="block text-sm font-bold text-gray-700 mb-2">
<i data-lucide="tag" class="w-4 h-4 inline mr-1"></i>
{% trans "Code" %} <span class="text-red-500">*</span>
</label>
<input type="text" id="code" name="code" required
class="w-full px-4 py-2.5 border-2 border-gray-200 rounded-xl text-gray-800 focus:ring-2 focus:ring-navy focus:border-transparent transition"
placeholder="{% trans 'Unique identifier for this item (e.g., CLINIC_P1)' %}">
</div>
<!-- Role -->
<div>
<label for="role" class="block text-sm font-bold text-gray-700 mb-2">
<i data-lucide="user-cog" class="w-4 h-4 inline mr-1"></i>
{% trans "Role" %}
</label>
<select id="role" name="role" class="w-full px-4 py-2.5 border-2 border-gray-200 rounded-xl text-gray-800 focus:ring-2 focus:ring-navy focus:border-transparent transition">
<option value="">{% trans "All Roles" %}</option>
<option value="px_admin">{% trans "PX Admin" %}</option>
<option value="hospital_admin">{% trans "Hospital Admin" %}</option>
<option value="department_manager">{% trans "Department Manager" %}</option>
<option value="px_coordinator">{% trans "PX Coordinator" %}</option>
<option value="physician">{% trans "Physician" %}</option>
<option value="nurse">{% trans "Nurse" %}</option>
<option value="staff">{% trans "Staff" %}</option>
<option value="viewer">{% trans "Viewer" %}</option>
</select>
<p class="text-sm text-gray-400 mt-1">{% trans "Leave empty to apply to all roles" %}</p>
</div>
<!-- Linked Content -->
<div>
<label for="content" class="block text-sm font-bold text-gray-700 mb-2">
<i data-lucide="file-text" class="w-4 h-4 inline mr-1"></i>
{% trans "Linked Content" %}
</label>
<select id="content" name="content" class="w-full px-4 py-2.5 border-2 border-gray-200 rounded-xl text-gray-800 focus:ring-2 focus:ring-navy focus:border-transparent transition">
<option value="">{% trans "No linked content" %}</option>
{% for content_item in content_list %}
<option value="{{ content_item.id }}">{{ content_item.title_en }}</option>
{% endfor %}
</select>
<p class="text-sm text-gray-400 mt-1">{% trans "Optional content section to link with this item" %}</p>
</div>
<!-- Order -->
<div>
<label for="order" class="block text-sm font-bold text-gray-700 mb-2">
<i data-lucide="arrow-down-1-0" class="w-4 h-4 inline mr-1"></i>
{% trans "Display Order" %}
</label>
<input type="number" id="order" name="order" value="0" min="0"
class="w-full px-4 py-2.5 border-2 border-gray-200 rounded-xl text-gray-800 focus:ring-2 focus:ring-navy focus:border-transparent transition">
<p class="text-sm text-gray-400 mt-1">{% trans "Order in which this item appears (lower = first)" %}</p>
</div>
<!-- Text (English) -->
<div class="md:col-span-2">
<label for="text_en" class="block text-sm font-bold text-gray-700 mb-2">
<i data-lucide="type" class="w-4 h-4 inline mr-1"></i>
{% trans "Text (English)" %} <span class="text-red-500">*</span>
</label>
<input type="text" id="text_en" name="text_en" required
class="w-full px-4 py-2.5 border-2 border-gray-200 rounded-xl text-gray-800 focus:ring-2 focus:ring-navy focus:border-transparent transition"
placeholder="{% trans 'Main text for the checklist item' %}">
</div>
<!-- Text (Arabic) -->
<div class="md:col-span-2">
<label for="text_ar" class="block text-sm font-bold text-gray-700 mb-2">
<i data-lucide="type" class="w-4 h-4 inline mr-1"></i>
{% trans "Text (Arabic)" %}
</label>
<input type="text" id="text_ar" name="text_ar" dir="rtl"
class="w-full px-4 py-2.5 border-2 border-gray-200 rounded-xl text-gray-800 focus:ring-2 focus:ring-navy focus:border-transparent transition"
placeholder="{% trans 'Arabic translation (optional)' %}">
</div>
<!-- Description (English) -->
<div class="md:col-span-2">
<label for="description_en" class="block text-sm font-bold text-gray-700 mb-2">
<i data-lucide="file-text" class="w-4 h-4 inline mr-1"></i>
{% trans "Description (English)" %}
</label>
<textarea id="description_en" name="description_en" rows="3"
class="w-full px-4 py-2.5 border-2 border-gray-200 rounded-xl text-gray-800 focus:ring-2 focus:ring-navy focus:border-transparent transition"
placeholder="{% trans 'Additional details (optional)' %}"></textarea>
</div>
<!-- Description (Arabic) -->
<div class="md:col-span-2">
<label for="description_ar" class="block text-sm font-bold text-gray-700 mb-2">
<i data-lucide="file-text" class="w-4 h-4 inline mr-1"></i>
{% trans "Description (Arabic)" %}
</label>
<textarea id="description_ar" name="description_ar" rows="3" dir="rtl"
class="w-full px-4 py-2.5 border-2 border-gray-200 rounded-xl text-gray-800 focus:ring-2 focus:ring-navy focus:border-transparent transition"
placeholder="{% trans 'Arabic translation (optional)' %}"></textarea>
</div>
<!-- Configuration -->
<div>
<label for="is_required" class="block text-sm font-bold text-gray-700 mb-2">
<i data-lucide="check-circle" class="w-4 h-4 inline mr-1"></i>
{% trans "Required" %}
</label>
<div class="flex items-center gap-3">
<input type="checkbox" id="is_required" name="is_required" checked
class="w-5 h-5 text-navy border-gray-300 rounded focus:ring-2 focus:ring-navy">
<span class="text-gray-700">{% trans "Item must be acknowledged" %}</span>
</div>
</div>
<div>
<label for="is_active" class="block text-sm font-bold text-gray-700 mb-2">
<i data-lucide="toggle-left" class="w-4 h-4 inline mr-1"></i>
{% trans "Active" %}
</label>
<div class="flex items-center gap-3">
<input type="checkbox" id="is_active" name="is_active" checked
class="w-5 h-5 text-navy border-gray-300 rounded focus:ring-2 focus:ring-navy">
<span class="text-gray-700">{% trans "Item is visible" %}</span>
</div>
</div>
</div>
<!-- Error Alert -->
<div class="bg-red-50 border border-red-200 rounded-xl p-4 mt-6 hidden" id="formError">
<div class="flex items-center gap-2 text-red-700">
<i data-lucide="alert-triangle" class="w-5 h-5"></i>
<span id="formErrorMessage"></span>
</div>
</div>
</form>
</div>
<div class="px-6 py-4 border-t border-gray-100 flex justify-end gap-3">
<button type="button" onclick="document.getElementById('createChecklistItemModal').classList.add('hidden')"
class="px-6 py-2.5 border-2 border-gray-300 text-gray-700 rounded-xl font-bold hover:bg-gray-50 transition flex items-center gap-2">
<i data-lucide="x-circle" class="w-4 h-4"></i>
{% trans "Cancel" %}
</button>
<button type="button" onclick="saveChecklistItem()" id="saveChecklistItemBtn"
class="bg-light0 text-white px-6 py-2.5 rounded-xl font-bold hover:bg-navy transition flex items-center gap-2">
<i data-lucide="save" class="w-4 h-4"></i>
<span id="saveBtnText">{% trans "Save Item" %}</span>
<span class="animate-spin hidden" id="saveBtnSpinner">
<i data-lucide="loader-2" class="w-4 h-4"></i>
</span>
</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
lucide.createIcons();
// Search functionality
document.getElementById('searchInput').addEventListener('keyup', function() {
const searchValue = this.value.toLowerCase();
const table = document.getElementById('itemsTable');
const rows = table.getElementsByTagName('tr');
for (let i = 1; i < rows.length; i++) {
const row = rows[i];
const cells = row.getElementsByTagName('td');
let found = false;
for (let j = 0; j < cells.length; j++) {
const cellText = cells[j].textContent.toLowerCase();
if (cellText.includes(searchValue)) {
found = true;
break;
}
}
row.style.display = found ? '' : 'none';
}
});
});
// Save checklist item
async function saveChecklistItem() {
const form = document.getElementById('createChecklistItemForm');
const saveBtn = document.getElementById('saveChecklistItemBtn');
const saveBtnText = document.getElementById('saveBtnText');
const saveBtnSpinner = document.getElementById('saveBtnSpinner');
const errorAlert = document.getElementById('formError');
const errorMessage = document.getElementById('formErrorMessage');
// Hide previous errors
errorAlert.classList.add('hidden');
// Validate form
if (!form.checkValidity()) {
form.reportValidity();
return;
}
// Prepare form data
const formData = new FormData(form);
const data = {
code: formData.get('code'),
role: formData.get('role') || null,
content: formData.get('content') || null,
text_en: formData.get('text_en'),
text_ar: formData.get('text_ar'),
description_en: formData.get('description_en'),
description_ar: formData.get('description_ar'),
is_required: formData.get('is_required') === 'on',
is_active: formData.get('is_active') === 'on',
order: parseInt(formData.get('order')) || 0
};
// Show loading state
saveBtn.disabled = true;
saveBtnText.textContent = '{% trans "Saving..." %}';
saveBtnSpinner.classList.remove('hidden');
try {
// Get CSRF token
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
// Send API request
const response = await fetch('/api/accounts/onboarding/checklist/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
},
body: JSON.stringify(data)
});
const responseData = await response.json();
if (response.ok) {
// Close modal
document.getElementById('createChecklistItemModal').classList.add('hidden');
// Show success message
showAlert('{% trans "Checklist item created successfully!" %}', 'success');
// Reload page to show new item
setTimeout(() => {
window.location.reload();
}, 1000);
} else {
// Show error
errorMessage.textContent = responseData.error || responseData.detail || '{% trans "Failed to create checklist item" %}';
errorAlert.classList.remove('hidden');
}
} catch (error) {
console.error('Error:', error);
errorMessage.textContent = '{% trans "An error occurred. Please try again." %}';
errorAlert.classList.remove('hidden');
} finally {
// Reset button state
saveBtn.disabled = false;
saveBtnText.textContent = '{% trans "Save Item" %}';
saveBtnSpinner.classList.add('hidden');
}
}
// Show alert message
function showAlert(message, type = 'info') {
// Create alert element
const alert = document.createElement('div');
const bgColor = type === 'success' ? 'bg-emerald-500' : 'bg-blue-500';
alert.className = `fixed top-4 right-4 ${bgColor} text-white px-6 py-4 rounded-xl shadow-lg z-50 flex items-center gap-3`;
alert.innerHTML = `
${message}
<button type="button" onclick="this.parentElement.remove()" class="text-white/80 hover:text-white">
<i data-lucide="x" class="w-4 h-4"></i>
</button>
`;
// Add to body
document.body.appendChild(alert);
lucide.createIcons();
// Auto dismiss after 3 seconds
setTimeout(() => {
alert.remove();
}, 3000);
}
// Reset form when modal is hidden
document.getElementById('createChecklistItemModal').addEventListener('click', function(e) {
if (e.target === this) {
this.classList.add('hidden');
document.getElementById('createChecklistItemForm').reset();
document.getElementById('formError').classList.add('hidden');
}
});
</script>
{% endblock %}