407 lines
20 KiB
HTML
407 lines
20 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static i18n %}
|
|
{% load widget_tweaks %}
|
|
|
|
{% block title %}{% trans "Meeting Details" %} - {{ block.super }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
|
|
|
|
<!-- Top Bar: Back & Actions -->
|
|
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-6">
|
|
<a href="{% url 'list_meetings' %}"
|
|
class="inline-flex items-center gap-2 px-4 py-2.5 rounded-lg text-sm font-medium text-gray-600 hover:bg-gray-50 transition-all duration-200">
|
|
<i data-lucide="arrow-left" class="w-4 h-4"></i>
|
|
{% trans "Back to Meetings" %}
|
|
</a>
|
|
|
|
<div class="flex flex-wrap gap-2">
|
|
<a href="{% url 'update_meeting' meeting.slug %}"
|
|
class="inline-flex items-center gap-2 px-4 py-2.5 rounded-lg text-sm font-medium text-white transition-all duration-200"
|
|
style="background-color: #9d2235;"
|
|
onmouseover="this.style.backgroundColor='#7a1a29'"
|
|
onmouseout="this.style.backgroundColor='#9d2235'">
|
|
<i data-lucide="edit-2" class="w-4 h-4"></i>
|
|
{% trans "Edit Meeting" %}
|
|
</a>
|
|
|
|
<form method="post" action="{% url 'send_application_invitation' meeting.slug %}" style="display: inline;">
|
|
{% csrf_token %}
|
|
<button type="submit"
|
|
onclick="return confirm('{% trans "Send invitation email to candidate?" %}')"
|
|
class="inline-flex items-center gap-2 px-4 py-2.5 rounded-lg text-sm font-medium border-2 border-blue-500 text-blue-600 hover:bg-blue-50 transition-all duration-200">
|
|
<i data-lucide="mail" class="w-4 h-4"></i>
|
|
{% trans "Send Invitation" %}
|
|
</button>
|
|
</form>
|
|
|
|
<form method="post" action="{% url 'delete_meeting' meeting.slug %}" style="display: inline;">
|
|
{% csrf_token %}
|
|
<button type="submit"
|
|
onclick="return confirm('{% trans "Are you sure you want to delete this meeting? This action is permanent." %}')"
|
|
class="inline-flex items-center gap-2 px-4 py-2.5 rounded-lg text-sm font-medium text-white bg-red-600 hover:bg-red-700 transition-all duration-200">
|
|
<i data-lucide="trash-2" class="w-4 h-4"></i>
|
|
{% trans "Delete" %}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Title -->
|
|
<div class="mb-6">
|
|
<h1 class="text-2xl sm:text-3xl font-bold text-gray-900 flex flex-wrap items-center gap-3">
|
|
<span class="flex items-center gap-3">
|
|
<i data-lucide="video" class="w-8 h-8" style="color: #9d2235;"></i>
|
|
{{ meeting.topic|default:"[Meeting Topic N/A]" }}
|
|
</span>
|
|
<span class="inline-block px-4 py-1.5 rounded-full text-xs font-semibold uppercase tracking-wide bg-green-100 text-green-800">
|
|
{{ meeting.status|title|default:'N/A' }}
|
|
</span>
|
|
</h1>
|
|
</div>
|
|
|
|
<!-- Main Cards Grid -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
|
|
|
|
<!-- Interview Detail Card -->
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
|
<h2 class="text-lg font-bold mb-6 pb-3 border-b border-gray-200 flex items-center gap-2" style="color: #9d2235;">
|
|
<i data-lucide="briefcase" class="w-5 h-5"></i>
|
|
{% trans "Interview Detail" %}
|
|
</h2>
|
|
|
|
<div class="space-y-4">
|
|
<div class="flex py-3 border-b border-dashed border-gray-200">
|
|
<span class="w-2/5 text-sm font-semibold text-gray-700">{% trans "Job Title" %}:</span>
|
|
<span class="w-3/5 text-sm text-gray-900 font-medium">{{ meeting.get_job.title|default:"N/A" }}</span>
|
|
</div>
|
|
<div class="flex py-3 border-b border-dashed border-gray-200">
|
|
<span class="w-2/5 text-sm font-semibold text-gray-700">{% trans "Candidate Name" %}:</span>
|
|
<span class="w-3/5 text-sm text-gray-900 font-medium">
|
|
<a href="#" class="hover:underline" style="color: #9d2235;">{{ meeting.candidate_full_name|default:"N/A" }}</a>
|
|
</span>
|
|
</div>
|
|
<div class="flex py-3 border-b border-dashed border-gray-200">
|
|
<span class="w-2/5 text-sm font-semibold text-gray-700">{% trans "Candidate Email" %}:</span>
|
|
<span class="w-3/5 text-sm text-gray-900 font-medium">
|
|
<a href="#" class="hover:underline" style="color: #9d2235;">{{ meeting.get_candidate.email|default:"N/A" }}</a>
|
|
</span>
|
|
</div>
|
|
<div class="flex py-3 border-b border-dashed border-gray-200">
|
|
<span class="w-2/5 text-sm font-semibold text-gray-700">{% trans "Job Type" %}:</span>
|
|
<span class="w-3/5 text-sm text-gray-900 font-medium">{{ meeting.get_job.job_type|default:"N/A" }}</span>
|
|
</div>
|
|
{% if meeting.get_candidate.belong_to_agency %}
|
|
<div class="flex py-3 border-b border-dashed border-gray-200">
|
|
<span class="w-2/5 text-sm font-semibold text-gray-700">{% trans "Agency" %}:</span>
|
|
<span class="w-3/5 text-sm text-gray-900 font-medium">
|
|
<a href="#" class="hover:underline" style="color: #9d2235;">{{ meeting.get_candidate.hiring_agency.name|default:"N/A" }}</a>
|
|
</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Connection Details Card -->
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
|
<h2 class="text-lg font-bold mb-6 pb-3 border-b border-gray-200 flex items-center gap-2" style="color: #9d2235;">
|
|
<i data-lucide="info" class="w-5 h-5"></i>
|
|
{% trans "Connection Details" %}
|
|
</h2>
|
|
|
|
<div class="space-y-4">
|
|
<div class="flex py-3 border-b border-dashed border-gray-200">
|
|
<span class="w-2/5 text-sm font-semibold text-gray-700">{% trans "Date & Time" %}:</span>
|
|
<span class="w-3/5 text-sm text-gray-900 font-medium">{{ meeting.start_time|date:"M d, Y H:i"|default:"N/A" }}</span>
|
|
</div>
|
|
<div class="flex py-3 border-b border-dashed border-gray-200">
|
|
<span class="w-2/5 text-sm font-semibold text-gray-700">{% trans "Duration" %}:</span>
|
|
<span class="w-3/5 text-sm text-gray-900 font-medium">{{ meeting.duration|default:"N/A" }} {% trans "minutes" %}</span>
|
|
</div>
|
|
<div class="flex py-3 border-b border-dashed border-gray-200">
|
|
<span class="w-2/5 text-sm font-semibold text-gray-700">{% trans "Meeting ID" %}:</span>
|
|
<span class="w-3/5 text-sm text-gray-900 font-medium">{{ meeting.meeting_id|default:"N/A" }}</span>
|
|
</div>
|
|
<div class="flex py-3 border-b border-dashed border-gray-200">
|
|
<span class="w-2/5 text-sm font-semibold text-gray-700">{% trans "Host Email" %}:</span>
|
|
<span class="w-3/5 text-sm text-gray-900 font-medium">{{ meeting.host_email|default:"N/A" }}</span>
|
|
</div>
|
|
|
|
{% if meeting.join_url %}
|
|
<div class="pt-4">
|
|
<div class="relative">
|
|
<div id="copy-message" class="absolute -top-8 left-1/2 transform -translate-x-1/2 px-3 py-1.5 text-white text-sm font-semibold rounded-lg opacity-0 transition-opacity duration-300" style="background-color: #198754;">
|
|
{% trans "Copied!" %}
|
|
</div>
|
|
<div class="bg-gray-50 border border-gray-200 rounded-lg p-4">
|
|
<div class="flex items-center justify-between gap-3">
|
|
<div class="flex-1 min-w-0">
|
|
<span class="text-sm font-semibold text-gray-700 block mb-1">{% trans "Join URL" %}:</span>
|
|
<span id="meeting-join-url" class="text-sm text-gray-900 break-all">{{ meeting.join_url }}</span>
|
|
</div>
|
|
<button onclick="copyLink()"
|
|
class="flex-shrink-0 px-4 py-2 rounded-lg text-sm font-medium text-white transition-all duration-200 hover:opacity-90"
|
|
style="background-color: #9d2235;"
|
|
title="{% trans 'Copy URL' %}">
|
|
<i data-lucide="copy" class="w-4 h-4"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Manage Participants Button -->
|
|
<div class="mb-6">
|
|
<button type="button"
|
|
class="inline-flex items-center gap-2 px-6 py-3 rounded-lg text-sm font-medium text-white transition-all duration-200"
|
|
style="background-color: #9d2235;"
|
|
onmouseover="this.style.backgroundColor='#7a1a29'"
|
|
onmouseout="this.style.backgroundColor='#9d2235'"
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#assignParticipants">
|
|
<i data-lucide="users" class="w-4 h-4"></i>
|
|
{% trans "Manage Participants" %} {% if total_participants %}({{ total_participants }}){% endif %}
|
|
</button>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- Assign Participants Modal -->
|
|
<div class="modal fade" id="assignParticipants" tabindex="-1" aria-labelledby="assignParticipantsLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg" role="document">
|
|
<div class="modal-content rounded-xl">
|
|
<div class="modal-header px-6 py-4 border-b border-gray-200" style="background-color: #f8f9fa;">
|
|
<h5 class="modal-title text-lg font-bold flex items-center gap-2" style="color: #9d2235;">
|
|
<i data-lucide="users" class="w-5 h-5"></i>
|
|
{% trans "Manage all participants" %}
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
|
|
<i data-lucide="x" class="w-5 h-5"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<form method="post" action="{% url 'create_interview_participants' meeting.interview.slug %}">
|
|
{% csrf_token %}
|
|
<div class="modal-body p-6">
|
|
<h4 class="text-xl font-bold text-gray-900 mb-4">{{ meeting.name }}</h4>
|
|
<hr class="border-gray-200 mb-4">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div>
|
|
<h5 class="text-lg font-bold mb-3 flex items-center gap-2" style="color: #9d2235;">
|
|
<span>👥</span> {% trans "Participants" %}
|
|
</h5>
|
|
<div>
|
|
{% if form.participants.errors %}
|
|
<div class="mb-2 text-sm text-red-600">{{ form.participants.errors.0 }}</div>
|
|
{% endif %}
|
|
{{ form.participants|add_class:"w-full px-4 py-2.5 border border-gray-200 rounded-lg text-sm focus:ring-2 focus:ring-temple-red/20 focus:border-temple-red outline-none transition" }}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h5 class="text-lg font-bold mb-3 flex items-center gap-2" style="color: #9d2235;">
|
|
<span>👨💼</span> {% trans "Users" %}
|
|
</h5>
|
|
<div>
|
|
{% if form.system_users.errors %}
|
|
<div class="mb-2 text-sm text-red-600">{{ form.system_users.errors.0 }}</div>
|
|
{% endif %}
|
|
{{ form.system_users|add_class:"w-full px-4 py-2.5 border border-gray-200 rounded-lg text-sm focus:ring-2 focus:ring-temple-red/20 focus:border-temple-red outline-none transition" }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal-footer px-6 py-4 border-t border-gray-200 flex justify-end gap-3">
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal">
|
|
{% trans "Close" %}
|
|
</button>
|
|
<button type="submit"
|
|
class="inline-flex items-center gap-2 px-6 py-2.5 rounded-lg text-sm font-medium text-white transition-all duration-200"
|
|
style="background-color: #9d2235;"
|
|
onmouseover="this.style.backgroundColor='#7a1a29'"
|
|
onmouseout="this.style.backgroundColor='#9d2235'">
|
|
<i data-lucide="save" class="w-4 h-4"></i>
|
|
{% trans "Save" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Email Modal -->
|
|
<div class="modal fade" id="emailModal" tabindex="-1" aria-labelledby="emailModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-xl modal-dialog-centered">
|
|
<div class="modal-content rounded-xl">
|
|
<div class="modal-header px-6 py-4 border-b border-gray-200" style="background-color: #f8f9fa;">
|
|
<h5 class="modal-title text-lg font-bold flex items-center gap-2" style="color: #9d2235;">
|
|
<span>📧</span> {% trans "Compose Interview Invitation" %}
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
|
|
<i data-lucide="x" class="w-5 h-5"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<form method="post" action="{% url 'send_interview_email' meeting.interview.slug %}">
|
|
{% csrf_token %}
|
|
<div class="modal-body p-6">
|
|
|
|
<div class="mb-6">
|
|
<label for="{{ email_form.subject.id_for_label }}" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{% trans "Subject" %}
|
|
</label>
|
|
{{ email_form.subject|add_class:"w-full px-4 py-2.5 border border-gray-200 rounded-lg text-sm focus:ring-2 focus:ring-temple-red/20 focus:border-temple-red outline-none transition" }}
|
|
</div>
|
|
|
|
<!-- Tabs -->
|
|
<ul class="flex border-b border-gray-200 mb-6" id="messageTabs" role="tablist">
|
|
<li class="role-presentation">
|
|
<button class="px-6 py-3 text-sm font-semibold border-b-2 transition-colors"
|
|
id="candidate-tab"
|
|
data-bs-toggle="tab"
|
|
data-bs-target="#candidate-pane"
|
|
type="button"
|
|
role="tab"
|
|
aria-controls="candidate-pane"
|
|
aria-selected="true"
|
|
style="border-color: #9d2235; color: #9d2235;">
|
|
{% if candidate.belong_to_an_agency %}
|
|
{% trans "Agency Message" %}
|
|
{% else %}
|
|
{% trans "Candidate Message" %}
|
|
{% endif %}
|
|
</button>
|
|
</li>
|
|
<li class="role-presentation">
|
|
<button class="px-6 py-3 text-sm font-semibold border-b-2 border-transparent text-gray-600 hover:text-gray-900 transition-colors"
|
|
id="participants-tab"
|
|
data-bs-toggle="tab"
|
|
data-bs-target="#participants-pane"
|
|
type="button"
|
|
role="tab"
|
|
aria-controls="participants-pane"
|
|
aria-selected="false">
|
|
{% trans "Panel Message (Interviewers)" %}
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="tab-content">
|
|
<!-- Candidate/Agency Pane -->
|
|
<div class="tab-pane fade show active" id="candidate-pane" role="tabpanel" aria-labelledby="candidate-tab">
|
|
<p class="text-sm text-gray-600 mb-4">
|
|
{% if candidate.belong_to_an_agency %}
|
|
{% trans "This email will be sent to candidate's hiring agency." %}
|
|
{% else %}
|
|
{% trans "This email will be sent to the candidate." %}
|
|
{% endif %}
|
|
</p>
|
|
|
|
{% if not candidate.belong_to_an_agency %}
|
|
<div class="mb-4">
|
|
{{ email_form.message_for_candidate|add_class:"w-full px-4 py-3 border border-gray-200 rounded-lg text-sm focus:ring-2 focus:ring-temple-red/20 focus:border-temple-red outline-none transition resize-vertical" }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if candidate.belong_to_an_agency %}
|
|
<div class="mb-4">
|
|
{{ email_form.message_for_agency|add_class:"w-full px-4 py-3 border border-gray-200 rounded-lg text-sm focus:ring-2 focus:ring-temple-red/20 focus:border-temple-red outline-none transition resize-vertical" }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Participants Pane -->
|
|
<div class="tab-pane fade" id="participants-pane" role="tabpanel" aria-labelledby="participants-tab">
|
|
<p class="text-sm text-gray-600 mb-4">
|
|
{% trans "This email will be sent to internal and external interview participants." %}
|
|
</p>
|
|
<div class="mb-4">
|
|
{{ email_form.message_for_participants|add_class:"w-full px-4 py-3 border border-gray-200 rounded-lg text-sm focus:ring-2 focus:ring-temple-red/20 focus:border-temple-red outline-none transition resize-vertical" }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal-footer px-6 py-4 border-t border-gray-200 flex justify-end gap-3">
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal">
|
|
{% trans "Close" %}
|
|
</button>
|
|
<button type="submit"
|
|
class="inline-flex items-center gap-2 px-6 py-2.5 rounded-lg text-sm font-medium text-white transition-all duration-200"
|
|
style="background-color: #9d2235;"
|
|
onmouseover="this.style.backgroundColor='#7a1a29'"
|
|
onmouseout="this.style.backgroundColor='#9d2235'">
|
|
<i data-lucide="send" class="w-4 h-4"></i>
|
|
{% trans "Send Invitation" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
if (typeof lucide !== 'undefined') {
|
|
lucide.createIcons();
|
|
}
|
|
});
|
|
|
|
// Copy Link Function
|
|
function copyLink() {
|
|
const urlElement = document.getElementById('meeting-join-url');
|
|
const messageElement = document.getElementById('copy-message');
|
|
const textToCopy = urlElement.textContent || urlElement.innerText;
|
|
|
|
clearTimeout(window.copyMessageTimeout);
|
|
|
|
function showMessage(success) {
|
|
messageElement.textContent = success ? '{% trans "Copied!" %}' : '{% trans "Copy Failed." %}';
|
|
messageElement.style.backgroundColor = success ? '#198754' : '#dc3545';
|
|
messageElement.style.opacity = '1';
|
|
|
|
window.copyMessageTimeout = setTimeout(() => {
|
|
messageElement.style.opacity = '0';
|
|
}, 2000);
|
|
}
|
|
|
|
if (navigator.clipboard && window.isSecureContext) {
|
|
navigator.clipboard.writeText(textToCopy).then(() => {
|
|
showMessage(true);
|
|
}).catch(err => {
|
|
console.error('Could not copy text: ', err);
|
|
fallbackCopyTextToClipboard(textToCopy, showMessage);
|
|
});
|
|
} else {
|
|
fallbackCopyTextToClipboard(textToCopy, showMessage);
|
|
}
|
|
}
|
|
|
|
function fallbackCopyTextToClipboard(text, callback) {
|
|
const textArea = document.createElement("textarea");
|
|
textArea.value = text;
|
|
|
|
textArea.style.top = "0";
|
|
textArea.style.left = "0";
|
|
textArea.style.position = "fixed";
|
|
|
|
document.body.appendChild(textArea);
|
|
textArea.focus();
|
|
textArea.select();
|
|
|
|
let success = false;
|
|
try {
|
|
success = document.execCommand('copy');
|
|
} catch (err) {
|
|
console.error('Fallback: Oops, unable to copy', err);
|
|
}
|
|
|
|
document.body.removeChild(textArea);
|
|
callback(success);
|
|
}
|
|
</script>
|
|
{% endblock %} |