333 lines
22 KiB
HTML
333 lines
22 KiB
HTML
{% extends "base.html" %}
|
|
{% load static i18n %}
|
|
|
|
{% block title %}{% trans "Interviews & Meetings" %} - {{ block.super }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
|
|
|
|
<!-- Header -->
|
|
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-6">
|
|
<h1 class="text-2xl sm:text-3xl font-bold flex items-center gap-3 text-gray-900">
|
|
<div class="w-12 h-12 rounded-xl flex items-center justify-center" style="background-color: rgba(157, 34, 53, 0.1);">
|
|
<i data-lucide="calendar" class="w-6 h-6" style="color: #9d2235;"></i>
|
|
</div>
|
|
{% trans "Interviews & Meetings" %}
|
|
</h1>
|
|
</div>
|
|
|
|
<!-- Filter Card -->
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-200 mb-6">
|
|
<div class="px-6 py-4 border-b border-gray-100" style="background-color: linear-gradient(to right, #9d2235, #7a1a29);">
|
|
<h2 class="text-lg font-bold flex items-center gap-2 text-white">
|
|
<i data-lucide="filter" class="w-5 h-5"></i>
|
|
{% trans "Filter Meetings" %}
|
|
</h2>
|
|
</div>
|
|
<div class="p-6">
|
|
<form method="get" action="" class="space-y-4">
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
<!-- Search -->
|
|
<div>
|
|
<label for="search" class="block text-sm font-semibold text-gray-700 mb-2">{% trans "Search by Topic" %}</label>
|
|
<div class="relative">
|
|
<input type="text" name="q" id="search" value="{{ search_query }}"
|
|
class="w-full px-4 py-2.5 pl-10 border border-gray-200 rounded-lg text-sm focus:ring-2 focus:ring-temple-red/20 focus:border-temple-red outline-none transition"
|
|
placeholder="{% trans 'Search topic...' %}">
|
|
<i data-lucide="search" class="w-4 h-4 text-gray-400 absolute left-3 top-1/2 transform -translate-y-1/2"></i>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Type Filter -->
|
|
<div>
|
|
<label for="type_filter" class="block text-sm font-semibold text-gray-700 mb-2">{% trans "Interview Type" %}</label>
|
|
<select name="type" id="type_filter"
|
|
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 bg-white">
|
|
<option value="">{% trans "All Types" %}</option>
|
|
<option value="Remote" {% if type_filter == 'Remote' %}selected{% endif %}>{% trans "Remote" %}</option>
|
|
<option value="Onsite" {% if type_filter == 'Onsite' %}selected{% endif %}>{% trans "Onsite" %}</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Status Filter -->
|
|
<div>
|
|
<label for="status" class="block text-sm font-semibold text-gray-700 mb-2">{% trans "Filter by Status" %}</label>
|
|
<select name="status" id="status"
|
|
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 bg-white">
|
|
<option value="">{% trans "All Statuses" %}</option>
|
|
{% for choice, display in status_choices %}
|
|
<option value="{{ choice }}" {% if status_filter == choice %}selected{% endif %}>{{ display }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Candidate Name Filter -->
|
|
<div>
|
|
<label for="candidate_name" class="block text-sm font-semibold text-gray-700 mb-2">{% trans "Candidate Name" %}</label>
|
|
<input type="text" name="candidate_name" id="candidate_name" value="{{ candidate_name_filter }}"
|
|
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"
|
|
placeholder="{% trans 'Search by candidate...' %}">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filter Actions -->
|
|
<div class="flex flex-wrap gap-2">
|
|
<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="filter" class="w-4 h-4"></i>
|
|
{% trans "Apply Filters" %}
|
|
</button>
|
|
{% if status_filter or search_query or candidate_name_filter or type_filter %}
|
|
<a href="{% url 'list_meetings' %}"
|
|
class="inline-flex items-center gap-2 px-6 py-2.5 rounded-lg text-sm font-medium border-2 border-gray-300 text-gray-700 hover:bg-gray-50 transition-all duration-200">
|
|
<i data-lucide="x" class="w-4 h-4"></i>
|
|
{% trans "Clear All" %}
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{% if meetings_data %}
|
|
<div id="meetings-list">
|
|
<!-- View Switcher -->
|
|
{% include "includes/_list_view_switcher.html" with list_id="meetings-list" %}
|
|
|
|
<!-- Card View -->
|
|
<div class="card-view active grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{% for meeting in meetings_data %}
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-200 hover:shadow-md transition-shadow duration-200 overflow-hidden">
|
|
<div class="p-6 flex flex-col h-full">
|
|
<!-- Header -->
|
|
<div class="flex justify-between items-start mb-4">
|
|
<div class="flex-1">
|
|
<h5 class="text-lg font-bold mb-2" style="color: #9d2235;">
|
|
{{ meeting.topic }}
|
|
</h5>
|
|
<span class="inline-block px-3 py-1 rounded-full text-xs font-semibold {% if meeting.type == 'Remote' %}bg-green-100 text-green-800{% else %}bg-blue-100 text-blue-800{% endif %}">
|
|
{{ meeting.type|title }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Details -->
|
|
<div class="flex-1 space-y-3 mb-4">
|
|
<div class="flex items-start gap-3 text-sm">
|
|
<i data-lucide="user" class="w-4 h-4 text-gray-500 flex-shrink-0 mt-0.5"></i>
|
|
<div>
|
|
<span class="text-gray-600">{% trans "Candidate" %}:</span>
|
|
<span class="text-gray-900 ml-1">{{ meeting.interview.application.person.full_name|default:"N/A" }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-start gap-3 text-sm">
|
|
<i data-lucide="briefcase" class="w-4 h-4 text-gray-500 flex-shrink-0 mt-0.5"></i>
|
|
<div>
|
|
<span class="text-gray-600">{% trans "Job" %}:</span>
|
|
<span class="text-gray-900 ml-1">{{ meeting.interview.job.title|default:"N/A" }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
{% if meeting.type == 'Remote' %}
|
|
<div class="flex items-start gap-3 text-sm">
|
|
<i data-lucide="link" class="w-4 h-4 text-gray-500 flex-shrink-0 mt-0.5"></i>
|
|
<div>
|
|
<span class="text-gray-600">{% trans "Remote ID" %}:</span>
|
|
<span class="text-gray-900 ml-1">{{ meeting.meeting_id|default:meeting.location.id }}</span>
|
|
</div>
|
|
</div>
|
|
{% elif meeting.type == 'Onsite' %}
|
|
<div class="flex items-start gap-3 text-sm">
|
|
<i data-lucide="map-pin" class="w-4 h-4 text-gray-500 flex-shrink-0 mt-0.5"></i>
|
|
<div>
|
|
<span class="text-gray-600">{% trans "Location" %}:</span>
|
|
<span class="text-gray-900 ml-1">{{ meeting.details.room_number|default:meeting.details.physical_address|truncatechars:30 }}</span>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="flex items-start gap-3 text-sm">
|
|
<i data-lucide="clock" class="w-4 h-4 text-gray-500 flex-shrink-0 mt-0.5"></i>
|
|
<div>
|
|
<span class="text-gray-600">{% trans "Start" %}:</span>
|
|
<span class="text-gray-900 ml-1">{{ meeting.start_time|date:"M d, Y H:i" }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-start gap-3 text-sm">
|
|
<i data-lucide="timer" class="w-4 h-4 text-gray-500 flex-shrink-0 mt-0.5"></i>
|
|
<div>
|
|
<span class="text-gray-600">{% trans "Duration" %}:</span>
|
|
<span class="text-gray-900 ml-1">{{ meeting.duration }} min</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Status Badge -->
|
|
<div class="mb-4">
|
|
<span class="inline-block px-3 py-1 rounded-full text-xs font-semibold bg-green-100 text-green-800">
|
|
{{ meeting.interview.get_status_display }}
|
|
</span>
|
|
</div>
|
|
|
|
<!-- Actions -->
|
|
<div class="pt-4 border-t border-gray-200 flex flex-wrap gap-2">
|
|
<a href="{% url 'meeting_details' meeting.slug %}"
|
|
class="inline-flex items-center gap-1.5 px-4 py-2 rounded-lg text-xs font-medium border-2 border-gray-300 text-gray-700 hover:bg-gray-50 transition-all duration-200">
|
|
<i data-lucide="eye" class="w-3.5 h-3.5"></i>
|
|
{% trans "View" %}
|
|
</a>
|
|
|
|
{% if meeting.type == 'Remote' and meeting.join_url %}
|
|
<a href="{{ meeting.join_url }}" target="_blank"
|
|
class="inline-flex items-center gap-1.5 px-4 py-2 rounded-lg text-xs 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="sign-in" class="w-3.5 h-3.5"></i>
|
|
{% trans "Join" %}
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<!-- Table View -->
|
|
<div class="table-view hidden">
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full">
|
|
<thead class="text-xs font-semibold text-left uppercase" style="background-color: #9d2235; color: white;">
|
|
<tr>
|
|
<th class="px-6 py-4">{% trans "Topic" %}</th>
|
|
<th class="px-6 py-4">{% trans "Type" %}</th>
|
|
<th class="px-6 py-4">{% trans "Candidate" %}</th>
|
|
<th class="px-6 py-4">{% trans "Job" %}</th>
|
|
<th class="px-6 py-4">{% trans "Start Time" %}</th>
|
|
<th class="px-6 py-4">{% trans "Duration" %}</th>
|
|
<th class="px-6 py-4">{% trans "Status" %}</th>
|
|
<th class="px-6 py-4 text-right">{% trans "Actions" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="divide-y divide-gray-200">
|
|
{% for meeting in meetings_data %}
|
|
<tr class="hover:bg-gray-50 transition-colors">
|
|
<td class="px-6 py-4">
|
|
<span class="font-semibold" style="color: #9d2235;">{{ meeting.topic }}</span>
|
|
</td>
|
|
<td class="px-6 py-4">
|
|
<span class="inline-block px-3 py-1 rounded-full text-xs font-semibold {% if meeting.type == 'Remote' %}bg-green-100 text-green-800{% else %}bg-blue-100 text-blue-800{% endif %}">
|
|
{{ meeting.type|title }}
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4">
|
|
<a href="{% url 'application_detail' meeting.interview.application.person.slug %}"
|
|
class="hover:underline" style="color: #9d2235;">
|
|
{{ meeting.interview.application.person.full_name }}
|
|
</a>
|
|
</td>
|
|
<td class="px-6 py-4">
|
|
<a href="{% url 'job_detail' meeting.interview.job.slug %}"
|
|
class="hover:underline" style="color: #9d2235;">
|
|
{{ meeting.interview.job.title }}
|
|
</a>
|
|
</td>
|
|
<td class="px-6 py-4 text-gray-700">{{ meeting.start_time|date:"M d, Y H:i" }}</td>
|
|
<td class="px-6 py-4 text-gray-700">{{ meeting.duration }} min</td>
|
|
<td class="px-6 py-4">
|
|
<span class="inline-block px-3 py-1 rounded-full text-xs font-semibold bg-green-100 text-green-800">
|
|
{{ meeting.interview.get_status_display }}
|
|
</span>
|
|
</td>
|
|
<td class="px-6 py-4 text-right">
|
|
<div class="flex justify-end gap-2">
|
|
{% if meeting.type == 'Remote' and meeting.join_url %}
|
|
<a href="{{ meeting.join_url }}" target="_blank"
|
|
class="inline-flex items-center justify-center w-8 h-8 rounded text-white transition-all duration-200"
|
|
style="background-color: #9d2235;"
|
|
onmouseover="this.style.backgroundColor='#7a1a29'"
|
|
onmouseout="this.style.backgroundColor='#9d2235'">
|
|
<i data-lucide="sign-in" class="w-4 h-4"></i>
|
|
</a>
|
|
{% endif %}
|
|
<a href="{% url 'meeting_details' meeting.slug %}"
|
|
class="inline-flex items-center justify-center w-8 h-8 rounded border-2 border-gray-300 text-gray-700 hover:bg-gray-50 transition-all duration-200">
|
|
<i data-lucide="eye" class="w-4 h-4"></i>
|
|
</a>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
{% if is_paginated %}
|
|
<nav aria-label="Page navigation" class="mt-6">
|
|
<ul class="flex justify-center items-center gap-2">
|
|
{% if page_obj.has_previous %}
|
|
<li>
|
|
<a href="?page=1{% if status_filter %}&status={{ status_filter }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}{% if type_filter %}&type={{ type_filter }}{% endif %}{% if candidate_name_filter %}&candidate_name={{ candidate_name_filter }}{% endif %}"
|
|
class="inline-flex items-center justify-center w-10 h-10 rounded-lg border-2 border-gray-300 text-gray-700 hover:bg-gray-50 transition-all duration-200">
|
|
<i data-lucide="chevrons-left" class="w-4 h-4"></i>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="?page={{ page_obj.previous_page_number }}{% if status_filter %}&status={{ status_filter }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}{% if type_filter %}&type={{ type_filter }}{% endif %}{% if candidate_name_filter %}&candidate_name={{ candidate_name_filter }}{% endif %}"
|
|
class="inline-flex items-center justify-center w-10 h-10 rounded-lg border-2 border-gray-300 text-gray-700 hover:bg-gray-50 transition-all duration-200">
|
|
<i data-lucide="chevron-left" class="w-4 h-4"></i>
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
|
|
<li class="px-4 py-2">
|
|
<span class="text-gray-700 font-medium">{{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
|
|
</li>
|
|
|
|
{% if page_obj.has_next %}
|
|
<li>
|
|
<a href="?page={{ page_obj.next_page_number }}{% if status_filter %}&status={{ status_filter }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}{% if type_filter %}&type={{ type_filter }}{% endif %}{% if candidate_name_filter %}&candidate_name={{ candidate_name_filter }}{% endif %}"
|
|
class="inline-flex items-center justify-center w-10 h-10 rounded-lg border-2 border-gray-300 text-gray-700 hover:bg-gray-50 transition-all duration-200">
|
|
<i data-lucide="chevron-right" class="w-4 h-4"></i>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="?page={{ page_obj.paginator.num_pages }}{% if status_filter %}&status={{ status_filter }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}{% if type_filter %}&type={{ type_filter }}{% endif %}{% if candidate_name_filter %}&candidate_name={{ candidate_name_filter }}{% endif %}"
|
|
class="inline-flex items-center justify-center w-10 h-10 rounded-lg border-2 border-gray-300 text-gray-700 hover:bg-gray-50 transition-all duration-200">
|
|
<i data-lucide="chevrons-right" class="w-4 h-4"></i>
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
</nav>
|
|
{% endif %}
|
|
{% else %}
|
|
<!-- Empty State -->
|
|
<div class="bg-white rounded-xl shadow-sm border border-gray-200 py-16 text-center">
|
|
<div class="w-20 h-20 mx-auto mb-6 rounded-full flex items-center justify-center" style="background-color: rgba(157, 34, 53, 0.1);">
|
|
<i data-lucide="calendar" class="w-10 h-10" style="color: #9d2235;"></i>
|
|
</div>
|
|
<h3 class="text-xl font-bold text-gray-900 mb-2">{% trans "No interviews or meetings found" %}</h3>
|
|
<p class="text-gray-600">{% trans "Create your first interview or adjust your filters." %}</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
if (typeof lucide !== 'undefined') {
|
|
lucide.createIcons();
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %} |