HH/templates/organizations/staff_detail.html
2026-02-22 08:35:53 +03:00

558 lines
28 KiB
HTML

{% extends "layouts/base.html" %}
{% load i18n %}
{% block title %}{{ staff.get_full_name }} - {% trans "Staff Details" %}{% endblock %}
{% block content %}
<!-- Back Button -->
<div class="mb-6">
<a href="{% url 'organizations:staff_list' %}" class="inline-flex items-center gap-2 px-4 py-2 border border-slate-200 rounded-xl text-slate hover:text-navy hover:bg-light transition text-sm font-semibold">
<i data-lucide="arrow-left" class="w-4 h-4"></i> {% trans "Back to Staff List" %}
</a>
</div>
<!-- Breadcrumb & Header -->
<header class="mb-6">
<div class="flex items-center gap-2 text-sm text-slate mb-2">
<a href="{% url 'organizations:staff_list' %}">{% trans "Staff" %}</a>
<i data-lucide="chevron-right" class="w-4 h-4"></i>
<span class="font-bold text-navy">{{ staff.get_full_name }}</span>
{% if staff.status == 'active' %}
<span class="ml-2 px-2 py-0.5 rounded-full text-[10px] font-bold uppercase tracking-wider bg-green-100 text-green-700">{% trans "Active" %}</span>
{% else %}
<span class="ml-2 px-2 py-0.5 rounded-full text-[10px] font-bold uppercase tracking-wider bg-slate-100 text-slate-700">{% trans "Inactive" %}</span>
{% endif %}
</div>
<div class="flex items-center justify-between">
<div class="flex items-center gap-4">
<div class="w-14 h-14 bg-navy rounded-2xl flex items-center justify-center text-white font-bold text-xl">
{{ staff.first_name|first }}{{ staff.last_name|first }}
</div>
<div>
<h1 class="text-2xl font-bold text-navy">{{ staff.get_full_name }}</h1>
<p class="text-slate text-sm">{{ staff.job_title|default:"-" }} <span class="text-slate-300">|</span> {{ staff.get_staff_type_display }}</p>
</div>
{% if staff.is_head %}
<span class="bg-yellow-100 text-yellow-700 text-xs px-3 py-1.5 rounded-full font-bold flex items-center gap-1">
<i data-lucide="crown" class="w-3 h-3"></i> {% trans "Department Head" %}
</span>
{% endif %}
</div>
<div class="flex items-center gap-3">
{% if user.is_px_admin or user.is_hospital_admin %}
<a href="{% url 'organizations:staff_update' staff.id %}" class="bg-navy text-white px-5 py-2.5 rounded-xl text-sm font-bold hover:bg-blue flex items-center gap-2 transition shadow-lg shadow-navy/20">
<i data-lucide="edit" class="w-4 h-4"></i> {% trans "Edit" %}
</a>
{% endif %}
</div>
</div>
</header>
<!-- Main Content Grid -->
<div class="grid grid-cols-12 gap-8">
<!-- Left Column (Content) -->
<div class="col-span-8 space-y-6">
<!-- Personal Information -->
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
<h3 class="font-bold text-navy mb-6 text-lg flex items-center gap-2">
<i data-lucide="user" class="w-5 h-5 text-blue"></i>
{% trans "Personal Information" %}
</h3>
<div class="grid grid-cols-2 gap-6">
<div>
<label class="text-xs font-bold text-slate uppercase block mb-1">{% trans "First Name (English)" %}</label>
<div class="font-bold text-navy">{{ staff.first_name }}</div>
</div>
<div>
<label class="text-xs font-bold text-slate uppercase block mb-1">{% trans "Last Name (English)" %}</label>
<div class="font-bold text-navy">{{ staff.last_name }}</div>
</div>
<div>
<label class="text-xs font-bold text-slate uppercase block mb-1">{% trans "First Name (Arabic)" %}</label>
<div class="font-bold text-navy" dir="rtl">{{ staff.first_name_ar|default:"-" }}</div>
</div>
<div>
<label class="text-xs font-bold text-slate uppercase block mb-1">{% trans "Last Name (Arabic)" %}</label>
<div class="font-bold text-navy" dir="rtl">{{ staff.last_name_ar|default:"-" }}</div>
</div>
{% if staff.license_number %}
<div>
<label class="text-xs font-bold text-slate uppercase block mb-1">{% trans "License Number" %}</label>
<div class="font-bold text-slate-800">{{ staff.license_number }}</div>
</div>
{% endif %}
{% if staff.specialization %}
<div>
<label class="text-xs font-bold text-slate uppercase block mb-1">{% trans "Specialization" %}</label>
<div class="font-bold text-slate-800">{{ staff.specialization }}</div>
</div>
{% endif %}
</div>
</section>
<!-- Organization -->
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
<h3 class="font-bold text-navy mb-6 text-lg flex items-center gap-2">
<i data-lucide="building-2" class="w-5 h-5 text-blue"></i>
{% trans "Organization" %}
</h3>
<div class="grid grid-cols-2 gap-6">
<div>
<label class="text-xs font-bold text-slate uppercase block mb-1">{% trans "Hospital" %}</label>
<div class="font-bold text-navy">{{ staff.hospital.name }}</div>
</div>
<div>
<label class="text-xs font-bold text-slate uppercase block mb-1">{% trans "Department" %}</label>
<div class="font-bold text-slate-800">{{ staff.department.name|default:"-" }}</div>
</div>
<div>
<label class="text-xs font-bold text-slate uppercase block mb-1">{% trans "Employee ID" %}</label>
<div class="font-bold text-slate-800">{{ staff.employee_id|default:"-" }}</div>
</div>
<div>
<label class="text-xs font-bold text-slate uppercase block mb-1">{% trans "Section" %}</label>
<div class="font-bold text-slate-800">{{ staff.section|default:"-" }}</div>
</div>
<div>
<label class="text-xs font-bold text-slate uppercase block mb-1">{% trans "Subsection" %}</label>
<div class="font-bold text-slate-800">{{ staff.subsection|default:"-" }}</div>
</div>
</div>
</section>
<!-- Contact Information -->
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
<h3 class="font-bold text-navy mb-6 text-lg flex items-center gap-2">
<i data-lucide="mail" class="w-5 h-5 text-blue"></i>
{% trans "Contact Information" %}
</h3>
<div class="grid grid-cols-2 gap-6">
<div>
<label class="text-xs font-bold text-slate uppercase block mb-1">{% trans "Email" %}</label>
<div class="font-bold text-slate-800">
{% if staff.email %}
<a href="mailto:{{ staff.email }}" class="text-blue hover:underline">{{ staff.email }}</a>
{% else %}
<span class="text-slate">-</span>
{% endif %}
</div>
</div>
<div>
<label class="text-xs font-bold text-slate uppercase block mb-1">{% trans "Phone" %}</label>
<div class="font-bold text-slate-800">
{% if staff.phone %}
<a href="tel:{{ staff.phone }}" class="text-blue hover:underline">{{ staff.phone }}</a>
{% else %}
<span class="text-slate">-</span>
{% endif %}
</div>
</div>
</div>
</section>
<!-- Hierarchy -->
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
<h3 class="font-bold text-navy mb-6 text-lg flex items-center gap-2">
<i data-lucide="git-branch" class="w-5 h-5 text-blue"></i>
{% trans "Hierarchy" %}
</h3>
<div class="space-y-4">
<div class="flex justify-between items-center border-b border-slate-100 pb-3">
<span class="text-xs font-bold text-slate uppercase">{% trans "Reports To" %}</span>
<span class="font-bold text-slate-800">
{% if staff.report_to %}
<a href="{% url 'organizations:staff_detail' staff.report_to.id %}" class="text-blue hover:underline flex items-center gap-1">
{{ staff.report_to.get_full_name }}
</a>
{% else %}
<span class="text-slate">-</span>
{% endif %}
</span>
</div>
<div>
<span class="text-xs font-bold text-slate uppercase block mb-2">{% trans "Direct Reports" %}</span>
{% if staff.direct_reports.exists %}
<div class="flex flex-wrap gap-2">
{% for direct_report in staff.direct_reports.all %}
<a href="{% url 'organizations:staff_detail' direct_report.id %}" class="px-3 py-1.5 bg-light rounded-lg text-sm font-semibold text-navy hover:bg-blue-100 transition">
{{ direct_report.get_full_name }}
</a>
{% endfor %}
</div>
{% else %}
<span class="text-slate text-sm">-</span>
{% endif %}
</div>
</div>
</section>
</div>
<!-- Right Column (Sidebar) -->
<div class="col-span-4 space-y-6">
<!-- User Account -->
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
<h3 class="font-bold text-navy mb-4 text-sm flex items-center gap-2">
<i data-lucide="user-circle" class="w-4 h-4 text-blue"></i>
{% trans "User Account" %}
</h3>
{% if staff.user %}
<div class="bg-green-50 border border-green-200 rounded-xl p-4 mb-4 flex items-start gap-3">
<i data-lucide="check-circle" class="w-5 h-5 text-green-600 flex-shrink-0 mt-0.5"></i>
<div>
<strong class="text-green-800 block text-sm">{% trans "Account Linked" %}</strong>
<span class="text-green-700 text-xs">{% trans "Staff can log in to the system" %}</span>
</div>
</div>
<ul class="space-y-3 text-sm">
<li class="flex justify-between border-b border-slate-100 pb-2">
<span class="text-slate">{% trans "Username" %}</span>
<span class="font-bold text-navy">{{ staff.user.username }}</span>
</li>
<li class="flex justify-between border-b border-slate-100 pb-2">
<span class="text-slate">{% trans "Active" %}</span>
<span class="font-bold">
{% if staff.user.is_active %}
<span class="px-2 py-0.5 rounded-lg text-xs font-bold bg-green-100 text-green-600">{% trans "Yes" %}</span>
{% else %}
<span class="px-2 py-0.5 rounded-lg text-xs font-bold bg-red-100 text-red-600">{% trans "No" %}</span>
{% endif %}
</span>
</li>
<li class="flex justify-between">
<span class="text-slate">{% trans "Created" %}</span>
<span class="font-bold text-slate-800 text-xs">{{ staff.user.date_joined|date:"M d, Y" }}</span>
</li>
</ul>
{% if user.is_px_admin or user.is_hospital_admin %}
<div class="space-y-2 mt-4 pt-4 border-t border-slate-100">
<button type="button" onclick="sendInvitation()" class="w-full px-4 py-2.5 bg-white border border-slate-200 text-navy rounded-xl font-semibold hover:bg-light transition flex items-center justify-center gap-2 text-sm">
<i data-lucide="mail" class="w-4 h-4"></i> {% trans "Resend Invitation" %}
</button>
<button type="button" onclick="resetPasswordAndResend()" class="w-full px-4 py-2.5 bg-white border border-slate-200 text-blue rounded-xl font-semibold hover:bg-blue-50 transition flex items-center justify-center gap-2 text-sm">
<i data-lucide="key" class="w-4 h-4"></i> {% trans "Reset Password" %}
</button>
<button type="button" onclick="unlinkUserAccount()" class="w-full px-4 py-2.5 bg-white border border-red-200 text-red-600 rounded-xl font-semibold hover:bg-red-50 transition flex items-center justify-center gap-2 text-sm">
<i data-lucide="user-minus" class="w-4 h-4"></i> {% trans "Unlink Account" %}
</button>
</div>
{% endif %}
{% else %}
<div class="bg-amber-50 border border-amber-200 rounded-xl p-4 mb-4 flex items-start gap-3">
<i data-lucide="alert-circle" class="w-5 h-5 text-amber-500 flex-shrink-0 mt-0.5"></i>
<div>
<strong class="text-amber-800 block text-sm">{% trans "No User Account" %}</strong>
<span class="text-amber-700 text-xs">{% trans "Staff cannot log in yet" %}</span>
</div>
</div>
<p class="text-slate text-sm mb-4">
{% trans "Create a user account to allow this staff member to log in to the system." %}
</p>
{% if user.is_px_admin or user.is_hospital_admin %}
{% if staff.email %}
<button type="button" onclick="createUserAccount()" class="w-full px-4 py-3 bg-navy text-white rounded-xl font-semibold hover:bg-blue transition flex items-center justify-center gap-2 text-sm">
<i data-lucide="user-plus" class="w-4 h-4"></i> {% trans "Create User Account" %}
</button>
{% else %}
<div class="bg-slate-50 border border-slate-200 rounded-xl p-3 text-center">
<i data-lucide="info" class="w-4 h-4 text-slate-400 inline mb-1"></i>
<p class="text-slate text-xs">{% trans "Add email address first" %}</p>
</div>
{% endif %}
{% endif %}
{% endif %}
</section>
<!-- System Info -->
<section class="bg-white rounded-2xl p-6 shadow-sm border border-slate-100">
<h3 class="font-bold text-navy mb-4 text-sm flex items-center gap-2">
<i data-lucide="clock" class="w-4 h-4 text-blue"></i>
{% trans "System Info" %}
</h3>
<ul class="space-y-3 text-sm">
<li class="flex justify-between border-b border-slate-100 pb-2">
<span class="text-slate">{% trans "Status" %}</span>
<span class="font-bold">
{% if staff.status == 'active' %}
<span class="px-2 py-0.5 rounded-lg text-xs font-bold bg-green-100 text-green-600">{% trans "Active" %}</span>
{% else %}
<span class="px-2 py-0.5 rounded-lg text-xs font-bold bg-slate-100 text-slate-500">{% trans "Inactive" %}</span>
{% endif %}
</span>
</li>
<li class="flex justify-between border-b border-slate-100 pb-2">
<span class="text-slate">{% trans "Created" %}</span>
<span class="font-bold text-slate-800 text-xs">{{ staff.created_at|date:"M d, Y" }}</span>
</li>
<li class="flex justify-between">
<span class="text-slate">{% trans "Updated" %}</span>
<span class="font-bold text-slate-800 text-xs">{{ staff.updated_at|date:"M d, Y" }}</span>
</li>
</ul>
</section>
</div>
</div>
<!-- Create User Modal -->
<div class="hidden fixed inset-0 bg-black/50 flex items-center justify-center z-50" id="createUserModal">
<div class="bg-white rounded-2xl shadow-2xl max-w-md w-full mx-4 overflow-hidden">
<div class="bg-navy text-white px-6 py-4 flex justify-between items-center">
<h5 class="font-bold text-lg">{% trans "Create User Account" %}</h5>
<button type="button" class="text-white hover:bg-blue rounded-full p-1 transition" onclick="closeModal('createUserModal')">
<i data-lucide="x" class="w-5 h-5"></i>
</button>
</div>
<div class="p-6">
<p class="text-slate mb-2">{% trans "Create a user account for" %} <strong class="text-navy">{{ staff.get_full_name }}</strong>?</p>
<p class="text-slate text-sm">{% trans "Credentials will be emailed to" %} <strong>{{ staff.email }}</strong>.</p>
</div>
<div class="px-6 py-4 bg-slate-50 flex justify-end gap-3">
<button type="button" class="px-4 py-2 border border-slate-200 text-slate rounded-xl font-semibold hover:bg-white transition" onclick="closeModal('createUserModal')">{% trans "Cancel" %}</button>
<button type="button" class="px-4 py-2 bg-navy text-white rounded-xl font-semibold hover:bg-blue transition" onclick="confirmCreateUser()">{% trans "Create" %}</button>
</div>
</div>
</div>
<!-- Send Invitation Modal -->
<div class="hidden fixed inset-0 bg-black/50 flex items-center justify-center z-50" id="sendInvitationModal">
<div class="bg-white rounded-2xl shadow-2xl max-w-md w-full mx-4 overflow-hidden">
<div class="bg-navy text-white px-6 py-4 flex justify-between items-center">
<h5 class="font-bold text-lg">{% trans "Send Invitation" %}</h5>
<button type="button" class="text-white hover:bg-blue rounded-full p-1 transition" onclick="closeModal('sendInvitationModal')">
<i data-lucide="x" class="w-5 h-5"></i>
</button>
</div>
<div class="p-6">
<p class="text-slate mb-2">{% trans "Send invitation email to" %} <strong class="text-navy">{{ staff.get_full_name }}</strong>?</p>
<p class="text-slate text-sm">{% trans "A new password will be generated and emailed." %}</p>
</div>
<div class="px-6 py-4 bg-slate-50 flex justify-end gap-3">
<button type="button" class="px-4 py-2 border border-slate-200 text-slate rounded-xl font-semibold hover:bg-white transition" onclick="closeModal('sendInvitationModal')">{% trans "Cancel" %}</button>
<button type="button" class="px-4 py-2 bg-navy text-white rounded-xl font-semibold hover:bg-blue transition" onclick="confirmSendInvitation()">{% trans "Send" %}</button>
</div>
</div>
</div>
<!-- Unlink User Modal -->
<div class="hidden fixed inset-0 bg-black/50 flex items-center justify-center z-50" id="unlinkUserModal">
<div class="bg-white rounded-2xl shadow-2xl max-w-md w-full mx-4 overflow-hidden">
<div class="bg-red-500 text-white px-6 py-4 flex justify-between items-center">
<h5 class="font-bold text-lg">{% trans "Unlink User Account" %}</h5>
<button type="button" class="text-white hover:bg-red-600 rounded-full p-1 transition" onclick="closeModal('unlinkUserModal')">
<i data-lucide="x" class="w-5 h-5"></i>
</button>
</div>
<div class="p-6">
<p class="text-slate mb-2">{% trans "Unlink user account from" %} <strong class="text-red-500">{{ staff.get_full_name }}</strong>?</p>
<div class="bg-red-50 border border-red-200 rounded-xl p-3 mt-3">
<p class="text-red-700 text-sm flex items-start gap-2">
<i data-lucide="alert-triangle" class="w-4 h-4 flex-shrink-0 mt-0.5"></i>
{% trans "This will remove login access. The user account will still exist but will no longer be linked to this staff profile." %}
</p>
</div>
</div>
<div class="px-6 py-4 bg-slate-50 flex justify-end gap-3">
<button type="button" class="px-4 py-2 border border-slate-200 text-slate rounded-xl font-semibold hover:bg-white transition" onclick="closeModal('unlinkUserModal')">{% trans "Cancel" %}</button>
<button type="button" class="px-4 py-2 bg-red-500 text-white rounded-xl font-semibold hover:bg-red-600 transition" onclick="confirmUnlinkUser()">{% trans "Unlink" %}</button>
</div>
</div>
</div>
<!-- New Password Display Modal -->
<div class="hidden fixed inset-0 bg-black/50 flex items-center justify-center z-50" id="newPasswordModal">
<div class="bg-white rounded-2xl shadow-2xl max-w-md w-full mx-4 overflow-hidden">
<div class="bg-green-500 text-white px-6 py-4 flex justify-between items-center">
<h5 class="font-bold text-lg flex items-center gap-2">
<i data-lucide="check-circle" class="w-6 h-6"></i> {% trans "Password Reset Successful" %}
</h5>
<button type="button" class="text-white hover:bg-green-600 rounded-full p-1 transition" onclick="closeModal('newPasswordModal')">
<i data-lucide="x" class="w-5 h-5"></i>
</button>
</div>
<div class="p-6">
<div class="bg-green-50 border border-green-200 rounded-xl p-4 mb-4">
<p class="text-green-700 font-semibold flex items-center gap-2">
<i data-lucide="check" class="w-5 h-5"></i>
{% trans "Password reset and email sent successfully!" %}
</p>
</div>
<div class="border-2 border-yellow-300 rounded-xl overflow-hidden">
<div class="bg-yellow-400 text-yellow-900 px-4 py-3 font-bold flex items-center gap-2">
<i data-lucide="key" class="w-5 h-5"></i> {% trans "New Password" %}
</div>
<div class="p-4 bg-white">
<p class="text-slate text-sm mb-3">{% trans "Copy this password:" %}</p>
<div class="flex gap-2">
<input type="text" id="newPasswordDisplay" class="flex-1 px-4 py-3 border border-slate-200 rounded-xl font-bold text-lg text-navy" readonly>
<button type="button" class="px-4 py-3 bg-slate-100 text-slate rounded-xl hover:bg-slate-200 transition" onclick="copyNewPassword()">
<i data-lucide="copy" class="w-5 h-5"></i>
</button>
</div>
</div>
</div>
</div>
<div class="px-6 py-4 bg-slate-50 flex justify-end">
<button type="button" class="px-4 py-2 bg-navy text-white rounded-xl font-semibold hover:bg-blue transition" onclick="closeModal('newPasswordModal')">{% trans "Done" %}</button>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
function closeModal(modalId) {
document.getElementById(modalId).classList.add('hidden');
}
function showModal(modalId) {
document.getElementById(modalId).classList.remove('hidden');
}
function createUserAccount() {
showModal('createUserModal');
}
function confirmCreateUser() {
const btn = event.target;
btn.disabled = true;
btn.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin inline"></i> {% trans "Creating..." %}';
fetch(`/organizations/api/staff/{{ staff.id }}/create_user_account/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
},
body: JSON.stringify({})
})
.then(response => response.json())
.then(data => {
closeModal('createUserModal');
if (data.message) alert(data.message);
location.reload();
})
.catch(error => {
alert('Error: ' + error.message);
btn.disabled = false;
btn.innerHTML = '{% trans "Create" %}';
});
}
function sendInvitation() {
showModal('sendInvitationModal');
}
function confirmSendInvitation() {
const btn = event.target;
btn.disabled = true;
btn.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin inline"></i> {% trans "Sending..." %}';
fetch(`/organizations/api/staff/{{ staff.id }}/send_invitation/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
},
body: JSON.stringify({})
})
.then(response => response.json())
.then(data => {
closeModal('sendInvitationModal');
if (data.message) alert(data.message);
location.reload();
})
.catch(error => {
alert('Error: ' + error.message);
btn.disabled = false;
btn.innerHTML = '{% trans "Send" %}';
});
}
function unlinkUserAccount() {
showModal('unlinkUserModal');
}
function confirmUnlinkUser() {
const btn = event.target;
btn.disabled = true;
btn.innerHTML = '<i data-lucide="loader-2" class="w-4 h-4 animate-spin inline"></i> {% trans "Unlinking..." %}';
fetch(`/organizations/api/staff/{{ staff.id }}/unlink_user/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
},
body: JSON.stringify({})
})
.then(response => response.json())
.then(data => {
closeModal('unlinkUserModal');
if (data.message) alert(data.message);
location.reload();
})
.catch(error => {
alert('Error: ' + error.message);
btn.disabled = false;
btn.innerHTML = '{% trans "Unlink" %}';
});
}
function resetPasswordAndResend() {
if (!confirm('{% trans "Reset password and resend credentials?" %}')) {
return;
}
fetch(`/organizations/api/staff/{{ staff.id }}/reset_password_and_resend/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
},
body: JSON.stringify({})
})
.then(response => response.json())
.then(data => {
if (data.success && data.new_password) {
document.getElementById('newPasswordDisplay').value = data.new_password;
showModal('newPasswordModal');
} else if (data.error) {
alert('Error: ' + data.error);
}
})
.catch(error => {
alert('Error: ' + error.message);
});
}
function copyNewPassword() {
const passwordInput = document.getElementById('newPasswordDisplay');
passwordInput.select();
passwordInput.setSelectionRange(0, 99999);
try {
navigator.clipboard.writeText(passwordInput.value).then(function() {
const btn = event.target.closest('button');
const originalHTML = btn.innerHTML;
btn.innerHTML = '<i data-lucide="check" class="w-5 h-5"></i>';
setTimeout(() => {
btn.innerHTML = originalHTML;
lucide.createIcons();
}, 1500);
});
} catch (err) {
document.execCommand('copy');
alert('{% trans "Password copied!" %}');
}
}
// Initialize Lucide icons
document.addEventListener('DOMContentLoaded', function() {
lucide.createIcons();
});
</script>
{% endblock %}