new list page addded"

This commit is contained in:
Faheed 2025-10-09 19:51:53 +03:00
parent d8a7442b9d
commit ff42e35855
16 changed files with 839 additions and 891 deletions

Binary file not shown.

View File

@ -242,6 +242,14 @@ class JobPosting(Base):
self.save() self.save()
class JobPostingImage(models.Model):
job=models.ForeignKey('JobPosting',on_delete=models.CASCADE,related_name='post_images')
post_image = models.ImageField(upload_to='post/',
height_field='photo_height',
width_field='photo_width')
post_image_height = models.PositiveIntegerField(null=True, blank=True)
post_image_width = models.PositiveIntegerField(null=True, blank=True)
class Candidate(Base): class Candidate(Base):
class Stage(models.TextChoices): class Stage(models.TextChoices):
APPLIED = "Applied", _("Applied") APPLIED = "Applied", _("Applied")

View File

@ -2,12 +2,12 @@
{% load static i18n crispy_forms_tags %} {% load static i18n crispy_forms_tags %}
{% load partials %} {% load partials %}
{% block title %}Form Templates - ATS{% endblock %} {% block title %}Form Templates - {{ block.super }}{% endblock %}
{% block customCSS %} {% block customCSS %}
<style> <style>
/* ================================================= */ /* ================================================= */
/* UI Variables (Matching Job List) */ /* UI Variables (Matching Standard Theme) */
/* ================================================= */ /* ================================================= */
:root { :root {
--kaauh-teal: #00636e; --kaauh-teal: #00636e;
@ -20,23 +20,22 @@
/* --- Typography and Color Overrides --- */ /* --- Typography and Color Overrides --- */
.text-primary { color: var(--kaauh-teal) !important; } .text-primary { color: var(--kaauh-teal) !important; }
/* --- Button Base Styles (Matching Job List) --- */ /* --- Button Base Styles (Consistent) --- */
.btn-main-action { .btn-main-action {
background-color: var(--kaauh-teal); background-color: var(--kaauh-teal);
border-color: var(--kaauh-teal); border-color: var(--kaauh-teal);
color: white; color: white;
font-weight: 600; font-weight: 600;
padding: 0.375rem 0.75rem;
border-radius: 0.5rem;
transition: all 0.2s ease; transition: all 0.2s ease;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: 0.4rem;
padding: 0.5rem 1rem;
} }
.btn-main-action:hover { .btn-main-action:hover {
background-color: var(--kaauh-teal-dark); background-color: var(--kaauh-teal-dark);
border-color: var(--kaauh-teal-dark); border-color: var(--kaauh-teal-dark);
transform: translateY(-1px); transform: none; /* Removed translate to match other lists */
box-shadow: 0 4px 8px rgba(0,0,0,0.15); box-shadow: 0 4px 8px rgba(0,0,0,0.15);
color: white; color: white;
} }
@ -51,18 +50,17 @@
color: white; color: white;
border-color: var(--kaauh-teal-dark); border-color: var(--kaauh-teal-dark);
} }
/* Primary Outline for View/Preview */
/* Size Utilities (matching Bootstrap convention) */ .btn-outline-primary {
.btn-lg { color: var(--kaauh-teal);
padding: 0.75rem 1.5rem; border-color: var(--kaauh-teal);
font-size: 1.1rem;
} }
.btn-sm { .btn-outline-primary:hover {
font-size: 0.8rem; background-color: var(--kaauh-teal);
padding: 0.3rem 0.6rem; color: white;
} }
/* --- Card and Layout Styles (Matching Job List) --- */ /* --- Card and Layout Styles (Consistent) --- */
.card { .card {
border: 1px solid var(--kaauh-border); border: 1px solid var(--kaauh-border);
border-radius: 0.75rem; border-radius: 0.75rem;
@ -70,42 +68,24 @@
background-color: white; background-color: white;
transition: transform 0.2s, box-shadow 0.2s; transition: transform 0.2s, box-shadow 0.2s;
} }
.card:not(.no-hover):hover {
/* Template Card Hover Effect (Consistent with job list card hover) */
.template-card {
height: 100%;
}
.template-card:hover {
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0,0,0,0.1) !important; box-shadow: 0 6px 16px rgba(0,0,0,0.1) !important;
} }
.card.no-hover:hover {
/* Card Header Theming */ transform: none;
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
}
/* Card Header (For Search/Filter Card) */
.card-header { .card-header {
/* FIX: Use !important to override default white/light backgrounds from Bootstrap */
background-color: var(--kaauh-teal-dark) !important;
border-bottom: 1px solid var(--kaauh-border);
color: white !important; /* Base color for header text */
font-weight: 600; font-weight: 600;
padding: 1rem 1.25rem; padding: 1.25rem;
border-radius: 0.75rem 0.75rem 0 0; border-bottom: 1px solid var(--kaauh-border);
} background-color: var(--kaauh-gray-light);
/* Ensure all elements within the header are visible */
.card-header h3 {
color: white !important;
font-weight: 700;
}
.card-header .fas {
color: white !important;
}
.card-header .small {
color: rgba(255, 255, 255, 0.7) !important;
} }
/* Stats Theming */ /* Stats Theming */
/* --- Content Styles (Stats, Description) --- */
.stat-value { .stat-value {
font-size: 1.5rem; font-size: 1.5rem;
font-weight: 800; font-weight: 800;
@ -116,58 +96,28 @@
color: var(--kaauh-primary-text); color: var(--kaauh-primary-text);
font-weight: 500; font-weight: 500;
} }
.card-description {
min-height: 60px;
color: var(--kaauh-primary-text);
}
/* Search Input Theming */ /* Table Styling (Consistent) */
.form-control { .table-view .table thead th {
border-radius: 0.5rem 0 0 0.5rem; background-color: var(--kaauh-teal-dark);
color: white;
font-weight: 600;
border-color: var(--kaauh-border); border-color: var(--kaauh-border);
border-radius: 0 0.5rem 0.5rem 0; text-transform: uppercase;
font-size: 0.8rem;
letter-spacing: 0.5px;
padding: 1rem;
} }
.form-control-search:focus { .table-view .table tbody td {
border-color: var(--kaauh-teal); vertical-align: middle;
box-shadow: 0 0 0 0.1rem rgba(0, 99, 110, 0.25); padding: 1rem;
}
.input-group-search .input-group-text {
background-color: white;
border-right: none;
border-color: var(--kaauh-border); border-color: var(--kaauh-border);
border-radius: 0.5rem 0 0 0.5rem;
} }
.input-group-search .form-control { .table-view .table tbody tr:hover {
border-left: none;
}
/* --- Danger Outline (Delete) --- */
.btn-outline-danger {
--bs-btn-color: #dc3545;
--bs-btn-border-color: #dc3545;
--bs-btn-hover-bg: #dc3545;
--bs-btn-hover-color: white;
}
/* Empty State Theming */
.empty-state {
text-align: center;
padding: 3rem 1rem;
color: var(--kaauh-primary-text);
border: 2px dashed var(--kaauh-border);
border-radius: 0.75rem;
background-color: var(--kaauh-gray-light); background-color: var(--kaauh-gray-light);
} }
.empty-state i {
font-size: 3.5rem;
margin-bottom: 1rem;
color: var(--kaauh-teal-dark);
}
.empty-state .btn-main-action .fas {
color: white !important;
}
/* --- Pagination Styling (Matching Job List) --- */ /* Pagination Styling (Consistent) */
.pagination .page-item .page-link { .pagination .page-item .page-link {
color: var(--kaauh-teal-dark); color: var(--kaauh-teal-dark);
border-color: var(--kaauh-border); border-color: var(--kaauh-border);
@ -180,30 +130,40 @@
.pagination .page-item:hover .page-link:not(.active) { .pagination .page-item:hover .page-link:not(.active) {
background-color: #e9ecef; background-color: #e9ecef;
} }
</style>
/* Empty State Icon Color */
.empty-state i, .text-center i.fa-3x {
color: var(--kaauh-teal-dark) !important;
}
/* Filter Buttons Container */
.filter-buttons {
display: flex;
gap: 0.5rem;
}
</style>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="container py-4"> <div class="container-fluid py-4">
<div class="d-flex justify-content-between align-items-center mb-4 pb-2 border-bottom border-primary"> <div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0 fw-bold" style="color: var(--kaauh-teal-dark); font-weight: 700;"> <h1 class="h3 mb-0" style="color: var(--kaauh-teal-dark); font-weight: 700;">
<i class="fas fa-file-alt me-2"></i>{% trans "Form Templates" %} <i class="fas fa-file-alt me-2"></i>{% trans "Form Templates" %}
</h1> </h1>
<button type="button" class="btn btn-main-action" data-bs-toggle="modal" data-bs-target="#createTemplateModal"> <button type="button" class="btn btn-main-action" data-bs-toggle="modal" data-bs-target="#createTemplateModal">
<i class="fas fa-plus me-1"></i> Create New Template <i class="fas fa-plus me-1"></i> {% trans "Create New Template" %}
</button> </button>
</div> </div>
{# Search/Filter Area - Matching Job List Structure #} {# Search/Filter Area - Matching Standard Structure #}
<div class="card mb-4 shadow-sm no-hover"> <div class="card mb-4 shadow-sm no-hover">
<div class="card-body"> <div class="card-body">
<h5 class="card-title text-muted mb-3" style="font-weight: 500;">Search Templates</h5>
<form method="get" class="row g-3 align-items-end"> <form method="get" class="row g-3 align-items-end">
<div class="col-md-6"> <div class="col-md-6">
<label for="search" class="form-label small text-muted">Search by Template Name</label> <label for="searchInput" class="form-label small text-muted">{% trans "Search by Template Name" %}</label>
<div class="input-group input-group-lg input-group-search"> <div class="input-group input-group-lg">
<span class="input-group-text"><i class="fas fa-search text-muted"></i></span> <span class="input-group-text"><i class="fas fa-search text-muted"></i></span>
<input type="text" name="q" id="searchInput" class="form-control form-control-search" <input type="text" name="q" id="searchInput" class="form-control form-control-search"
placeholder="{% trans 'Search templates by name...' %}" placeholder="{% trans 'Search templates by name...' %}"
@ -214,13 +174,13 @@
<div class="col-md-6"> <div class="col-md-6">
<div class="filter-buttons"> <div class="filter-buttons">
<button type="submit" class="btn btn-main-action btn-lg"> <button type="submit" class="btn btn-main-action btn-lg">
<i class="fas fa-filter me-1"></i> Search <i class="fas fa-filter me-1"></i> {% trans "Search" %}
</button> </button>
{# Show Clear button if search is active #} {# Show Clear button if search is active #}
{% if query %} {% if query %}
<a href="{% url 'form_templates_list' %}" class="btn btn-outline-danger btn-sm"> <a href="{% url 'form_templates_list' %}" class="btn btn-outline-secondary btn-lg">
<i class="fas fa-times me-1"></i> Clear Search <i class="fas fa-times me-1"></i> {% trans "Clear Search" %}
</a> </a>
{% endif %} {% endif %}
</div> </div>
@ -239,59 +199,62 @@
<div class="card-view active row g-4"> <div class="card-view active row g-4">
{% for template in templates %} {% for template in templates %}
<div class="col-lg-4 col-md-6"> <div class="col-lg-4 col-md-6">
<div class="card template-card h-100"> <div class="card template-card h-100 shadow-sm">
<div class="card-header ">
<h3 class="h5 mb-2">{{ template.name }}</h3>
<span><i class="fas fa-sync-alt me-1"></i> {{ template.job }}</span>
<div class="d-flex justify-content-between text-muted small">
<span><i class="fas fa-calendar me-1"></i> {{ template.created_at|date:"M d, Y" }}</span>
<span><i class="fas fa-sync-alt me-1"></i> {{ template.updated_at|timesince }} {% trans "ago" %}</span>
</div>
</div>
<div class="card-body d-flex flex-column"> <div class="card-body d-flex flex-column">
<h5 class="card-title fw-bold" style="color: var(--kaauh-teal-dark);">{{ template.name }}</h5>
{# Content area - includes stats and description #} <span class="text-muted small mb-3">
<div class="flex-grow-1"> <i class="fas fa-briefcase me-1"></i> {{ template.job|default:"N/A" }}
<div class="row text-center mb-3"> </span>
<div class="col-6">
<div class="stat-value">{{ template.get_stage_count }}</div> {# Stats #}
<div class="stat-label">{% trans "Stages" %}</div> <div class="row text-center mb-3">
</div> <div class="col-6 border-end">
<div class="col-6"> <div class="stat-value">{{ template.get_stage_count }}</div>
<div class="stat-value">{{ template.get_field_count }}</div> <div class="stat-label">{% trans "Stages" %}</div>
<div class="stat-label">{% trans "Fields" %}</div> </div>
</div> <div class="col-6">
<div class="stat-value">{{ template.get_field_count }}</div>
<div class="stat-label">{% trans "Fields" %}</div>
</div> </div>
<p class="card-text card-description small">
{% if template.description %}
{{ template.description|truncatewords:20 }}
{% else %}
<em class="text-muted">{% trans "No description provided" %}</em>
{% endif %}
</p>
</div> </div>
{# Description #}
<p class="card-text small text-muted flex-grow-1">
{% if template.description %}
{{ template.description|truncatewords:20 }}
{% else %}
<em class="text-muted">{% trans "No description provided" %}</em>
{% endif %}
</p>
{# Action area - visually separated with pt-2 border-top #} {# Action area #}
<div class="mt-auto pt-2 border-top"> <div class="mt-auto pt-2 border-top">
<div class="d-grid gap-2 d-md-flex justify-content-md-end"> <div class="d-flex gap-2 justify-content-end">
<a href="{% url 'form_wizard' template.id %}" class="btn btn-outline-secondary btn-sm action-btn"> <a href="{% url 'form_wizard' template.id %}" class="btn btn-outline-primary btn-sm" title="{% trans 'Preview' %}">
<i class="fas fa-eye me-1"></i> {% trans "Preview" %} <i class="fas fa-eye"></i>
</a> </a>
<a href="{% url 'form_builder' template.id %}" class="btn btn-outline-secondary btn-sm action-btn"> <a href="{% url 'form_builder' template.id %}" class="btn btn-outline-secondary btn-sm" title="{% trans 'Edit' %}">
<i class="fas fa-edit me-1"></i> {% trans "Edit" %} <i class="fas fa-edit"></i>
</a> </a>
<a href="{% url 'form_template_submissions_list' template.slug %}" class="btn btn-outline-secondary btn-sm action-btn"> <a href="{% url 'form_template_submissions_list' template.slug %}" class="btn btn-outline-secondary btn-sm" title="{% trans 'Submissions' %}">
<i class="fas fa-file-alt me-1"></i> {% trans "Submissions" %} <i class="fas fa-file-alt"></i>
</a> </a>
<button class="btn btn-outline-danger btn-sm action-btn delete" <button type="button" class="btn btn-outline-danger btn-sm" title="{% trans 'Delete' %}"
data-template-id="{{ template.id }}" data-bs-toggle="modal" data-bs-target="#deleteModal"
data-template-name="{{ template.name }}"> data-delete-url="#"
<i class="fas fa-trash me-1"></i> {% trans "Delete" %} data-item-name="{{ template.name }}">
<i class="fas fa-trash-alt"></i>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
<div class="card-footer bg-light text-muted small">
<div class="d-flex justify-content-between">
<span><i class="fas fa-calendar-alt me-1"></i> {% trans "Created:" %} {{ template.created_at|date:"M d, Y" }}</span>
<span><i class="fas fa-sync-alt me-1"></i> {{ template.updated_at|timesince }} {% trans "ago" %}</span>
</div>
</div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
@ -300,23 +263,23 @@
{# Table View #} {# Table View #}
<div class="table-view"> <div class="table-view">
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-hover"> <table class="table table-hover align-middle mb-0">
<thead> <thead>
<tr> <tr>
<th scope="col">{% trans "Template Name" %}</th> <th scope="col" style="width: 30%;">{% trans "Template Name" %}</th>
<th scope="col">{% trans "Job" %}</th> <th scope="col" style="width: 15%;">{% trans "Job" %}</th>
<th scope="col">{% trans "Stages" %}</th> <th scope="col" style="width: 8%;">{% trans "Stages" %}</th>
<th scope="col">{% trans "Fields" %}</th> <th scope="col" style="width: 8%;">{% trans "Fields" %}</th>
<th scope="col">{% trans "Created" %}</th> <th scope="col" style="width: 15%;">{% trans "Created" %}</th>
<th scope="col">{% trans "Last Updated" %}</th> <th scope="col" style="width: 15%;">{% trans "Last Updated" %}</th>
<th scope="col" class="text-end">{% trans "Actions" %}</th> <th scope="col" style="width: 9%;" class="text-end">{% trans "Actions" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for template in templates %} {% for template in templates %}
<tr> <tr>
<td class="fw-medium">{{ template.name }}</td> <td class="fw-medium text-primary">{{ template.name }}</td>
<td>{{ template.job }}</td> <td>{{ template.job|default:"N/A" }}</td>
<td>{{ template.get_stage_count }}</td> <td>{{ template.get_stage_count }}</td>
<td>{{ template.get_field_count }}</td> <td>{{ template.get_field_count }}</td>
<td>{{ template.created_at|date:"M d, Y" }}</td> <td>{{ template.created_at|date:"M d, Y" }}</td>
@ -329,11 +292,14 @@
<a href="{% url 'form_builder' template.id %}" class="btn btn-outline-secondary" title="{% trans 'Edit' %}"> <a href="{% url 'form_builder' template.id %}" class="btn btn-outline-secondary" title="{% trans 'Edit' %}">
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</a> </a>
<a href="{% url 'form_template_submissions_list' template.slug %}" class="btn btn-outline-info" title="{% trans 'Submissions' %}"> <a href="{% url 'form_template_submissions_list' template.slug %}" class="btn btn-outline-secondary" title="{% trans 'Submissions' %}">
<i class="fas fa-file-alt"></i> <i class="fas fa-file-alt"></i>
</a> </a>
<button class="btn btn-outline-danger delete-btn" data-bs-toggle="modal" data-bs-target="#deleteModal" data-template-id="{{ template.id }}" data-template-name="{{ template.name }}" title="{% trans 'Delete' %}"> <button type="button" class="btn btn-outline-danger" title="{% trans 'Delete' %}"
<i class="fas fa-trash"></i> data-bs-toggle="modal" data-bs-target="#deleteModal"
data-delete-url="#"
data-item-name="{{ template.name }}">
<i class="fas fa-trash-alt"></i>
</button> </button>
</div> </div>
</td> </td>
@ -345,10 +311,10 @@
</div> </div>
</div> </div>
{# Pagination (Standardized) #}
{% if templates.has_other_pages %} {% if templates.has_other_pages %}
<nav aria-label="Page navigation" class="mt-4"> <nav aria-label="Page navigation" class="mt-4">
<ul class="pagination justify-content-center"> <ul class="pagination justify-content-center">
{# Previous page logic #}
{% if templates.has_previous %} {% if templates.has_previous %}
<li class="page-item"> <li class="page-item">
<a class="page-link" href="?page=1{% if query %}&q={{ query }}{% endif %}">First</a> <a class="page-link" href="?page=1{% if query %}&q={{ query }}{% endif %}">First</a>
@ -358,12 +324,10 @@
</li> </li>
{% endif %} {% endif %}
{# Current Page #}
<li class="page-item active"> <li class="page-item active">
<span class="page-link">{{ templates.number }} of {{ templates.paginator.num_pages }}</span> <span class="page-link">{{ templates.number }} of {{ templates.paginator.num_pages }}</span>
</li> </li>
{# Next page logic #}
{% if templates.has_next %} {% if templates.has_next %}
<li class="page-item"> <li class="page-item">
<a class="page-link" href="?page={{ templates.next_page_number }}{% if query %}&q={{ query }}{% endif %}">Next</a> <a class="page-link" href="?page={{ templates.next_page_number }}{% if query %}&q={{ query }}{% endif %}">Next</a>
@ -387,9 +351,9 @@
{% trans "You haven't created any form templates yet." %} {% trans "You haven't created any form templates yet." %}
{% endif %} {% endif %}
</p> </p>
<a href="{% url 'form_builder' %}" class="btn btn-main-action mt-3"> <button type="button" class="btn btn-main-action mt-3" data-bs-toggle="modal" data-bs-target="#createTemplateModal">
<i class="fas fa-plus me-1 text-white"></i> {% trans "Create Your First Template" %} <i class="fas fa-plus me-1"></i> {% trans "Create Your First Template" %}
</a> </button>
</div> </div>
</div> </div>
{% endif %} {% endif %}
@ -398,13 +362,12 @@
{% include 'includes/delete_modal.html' %} {% include 'includes/delete_modal.html' %}
<!-- Create Template Modal -->
<div class="modal fade" id="createTemplateModal" tabindex="-1" aria-labelledby="createTemplateModalLabel" aria-hidden="true"> <div class="modal fade" id="createTemplateModal" tabindex="-1" aria-labelledby="createTemplateModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg"> <div class="modal-dialog modal-lg">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header bg-light">
<h5 class="modal-title" id="createTemplateModalLabel"> <h5 class="modal-title" id="createTemplateModalLabel">
<i class="fas fa-file-alt me-2"></i>Create New Form Template <i class="fas fa-file-alt me-2"></i>{% trans "Create New Form Template" %}
</h5> </h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
@ -416,12 +379,12 @@
</form> </form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
<button type="submit" form="createTemplateForm" class="btn btn-primary"> <button type="submit" form="createTemplateForm" class="btn btn-main-action">
<i class="fas fa-save me-1"></i>Create Template <i class="fas fa-save me-1"></i>{% trans "Create Template" %}
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -10,6 +10,8 @@
</div> </div>
<style> <style>
/* --- View Switcher Styles (Consolidated & Corrected) --- */
/* View Toggle Styles */ /* View Toggle Styles */
.view-toggle { .view-toggle {
border-radius: 0.25rem; border-radius: 0.25rem;
@ -28,13 +30,17 @@
/* Hide elements by default */ /* Hide elements by default */
.table-view, .table-view,
.card-view { .card-view {
display: none; display: none !important; /* Use !important to ensure hiding works */
} }
/* Show active view */ /* Show active view */
.table-view.active, .table-view.active {
display: block !important;
}
.card-view.active { .card-view.active {
display: block; /* Rely on the 'row' class which uses display: flex for proper column alignment. */
display: flex !important; /* rows often use display: flex in Bootstrap */
flex-wrap: wrap !important;
} }
/* Card View Styles */ /* Card View Styles */
@ -105,16 +111,19 @@
.table-view .table tbody tr { .table-view .table tbody tr {
transition: background-color 0.2s; transition: background-color 0.2s;
} }
/* NOTE: We need to assume var(--kaauh-gray-light) is defined in base.html or job_list.html */
.table-view .table tbody tr:hover { .table-view .table tbody tr:hover {
background-color: var(--kaauh-gray-light); background-color: #f0f0f0; /* Fallback color for hover */
} }
</style> </style>
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// Get the list ID from the data attribute const listContainer = document.getElementById('{{ list_id }}');
const listId = document.querySelector('.view-toggle').getAttribute('data-list-id'); if (!listContainer) return; // Exit if container isn't found
const listContainer = document.getElementById(listId);
// Get list ID from the data attribute (or use the variable from Django context if the button is loaded)
const listId = listContainer.id;
// Get saved view preference from localStorage // Get saved view preference from localStorage
const savedView = localStorage.getItem(`list_view_${listId}`) || 'table'; const savedView = localStorage.getItem(`list_view_${listId}`) || 'table';
@ -124,15 +133,18 @@ document.addEventListener('DOMContentLoaded', function() {
// Add click event listeners to view toggle buttons // Add click event listeners to view toggle buttons
document.querySelectorAll('.view-toggle').forEach(button => { document.querySelectorAll('.view-toggle').forEach(button => {
button.addEventListener('click', function() { // Ensure the button belongs to this list (optional check)
const view = this.getAttribute('data-view'); if (button.getAttribute('data-list-id') === listId) {
setView(view); button.addEventListener('click', function() {
}); const view = this.getAttribute('data-view');
setView(view);
});
}
}); });
function setView(view) { function setView(view) {
// Update button states // Update button states
document.querySelectorAll('.view-toggle').forEach(button => { document.querySelectorAll('.view-toggle[data-list-id="{{ list_id }}"]').forEach(button => {
if (button.getAttribute('data-view') === view) { if (button.getAttribute('data-view') === view) {
button.classList.add('active'); button.classList.add('active');
} else { } else {
@ -144,16 +156,17 @@ document.addEventListener('DOMContentLoaded', function() {
const tableView = listContainer.querySelector('.table-view'); const tableView = listContainer.querySelector('.table-view');
const cardView = listContainer.querySelector('.card-view'); const cardView = listContainer.querySelector('.card-view');
// Apply active class to the correct container
if (view === 'table') { if (view === 'table') {
tableView.classList.add('active'); if (tableView) tableView.classList.add('active');
cardView.classList.remove('active'); if (cardView) cardView.classList.remove('active');
} else { } else {
tableView.classList.remove('active'); if (tableView) tableView.classList.remove('active');
cardView.classList.add('active'); if (cardView) cardView.classList.add('active');
} }
// Save preference to localStorage // Save preference to localStorage
localStorage.setItem(`list_view_${listId}`, view); localStorage.setItem(`list_view_${listId}`, view);
} }
}); });
</script> </script>

View File

@ -261,7 +261,20 @@
</div> </div>
<div class="d-flex align-items-center gap-2"> <div class="d-flex align-items-center gap-2">
<span class="badge bg-success status-badge"> <span class="badge status-badge">
{% if job.status == "ACTIVE" %}
<span class="badge bg-success status-badge">
{% elif job.status == "DRAFT" %}
<span class="badge bg-secondary status-badge">
{% elif job.status == "CLOSED" %}
<span class="badge bg-warning status-badge">
{% elif job.status == "CANCELLED" %}
<span class="badge bg-danger status-badge">
{% elif job.status == "ARCHIVED" %}
<span class="badge bg-secondary status-badge">
{% else %}
<span class="badge bg-secondary status-badge">
{% endif %}
{{ job.get_status_display }} {{ job.get_status_display }}
<button type="button" class="btn btn-outline-light btn-sm ms-2" data-bs-toggle="modal" data-bs-target="#editStatusModal"> <button type="button" class="btn btn-outline-light btn-sm ms-2" data-bs-toggle="modal" data-bs-target="#editStatusModal">
<i class="fas fa-edit text-primary"></i> <i class="fas fa-edit text-primary"></i>
@ -550,7 +563,7 @@
</div> </div>
</div> </div>
</div> </div>
<!--image modal class-->
{% include "jobs/partials/image_upload.html" %} {% include "jobs/partials/image_upload.html" %}
<!-- JOB STATUS MODAL--> <!-- JOB STATUS MODAL-->

View File

@ -171,7 +171,7 @@
{# Card View (Default) #} {# Card View (Default) #}
<div class="card-view active row"> <div class="card-view active row">
{% for job in page_obj %} {% for job in page_obj %}
<div class="col-md-6 col-lg-4 mb-4"> <div class="col-md-6 col-lg-4 mb-4 ">
<div class="card job-card h-100"> <div class="card job-card h-100">
<div class="card-body d-flex flex-column"> <div class="card-body d-flex flex-column">
<div class="d-flex justify-content-between align-items-start mb-2"> <div class="d-flex justify-content-between align-items-start mb-2">

View File

@ -2,11 +2,11 @@
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="myModalLabel">Add New Comment</h5> <h5 class="modal-title" id="myModalLabel">Add New Image</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form method="post" action="{% url 'job_create' %}"> <form method="post" action="{% url 'job_detail' job.slug %}">
{% csrf_token %} {% csrf_token %}
{{ image_form }} {{ image_form }}
<div class="modal-footer"> <div class="modal-footer">

View File

@ -2,199 +2,120 @@
{% load static i18n %} {% load static i18n %}
{% block title %}{% trans "Zoom Meetings" %} - {{ block.super }}{% endblock %} {% block title %}{% trans "Zoom Meetings" %} - {{ block.super }}{% endblock %}
{% block customCSS %} {% block customCSS %}
<style> <style>
/* UI Variables for the KAAT-S Theme */ /* UI Variables for the KAAT-S Theme (Consistent with Reference) */
:root { :root {
--kaauh-teal: #00636e; --kaauh-teal: #00636e;
--kaauh-teal-dark: #004a53; --kaauh-teal-dark: #004a53;
--kaauh-border: #eaeff3; --kaauh-border: #eaeff3;
--kaauh-primary-text: #343a40; --kaauh-primary-text: #343a40;
--kaauh-gray: #6c757d; --kaauh-gray-light: #f8f9fa;
/* Status Colors based on KAAT-S example */
--kaauh-success: var(--kaauh-teal);
--kaauh-warning: #ffc107;
--kaauh-danger: #dc3545;
--kaauh-secondary: #6c757d;
} }
/* Base style for all buttons to inherit Bootstrap's basic structure/reset */ /* Enhanced Card Styling (Consistent) */
.btn-base { .card {
display: inline-block; border: 1px solid var(--kaauh-border);
text-align: center; border-radius: 0.75rem;
vertical-align: middle; box-shadow: 0 4px 12px rgba(0,0,0,0.06);
cursor: pointer; transition: transform 0.2s, box-shadow 0.2s;
user-select: none; background-color: white;
padding: 0.375rem 0.75rem; /* Standard default padding */ }
font-size: 1rem; .card:not(.no-hover):hover {
line-height: 1.5; transform: translateY(-2px);
border-radius: 0.25rem; box-shadow: 0 6px 16px rgba(0,0,0,0.1);
border: 1px solid transparent; }
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; .card.no-hover:hover {
/* FIX: Remove link underline for anchor tags used as buttons */ transform: none;
text-decoration: none; box-shadow: 0 4px 12px rgba(0,0,0,0.06);
} }
/* Small Button Size */ /* Main Action Button Style (Teal Theme) */
.btn-sm {
padding: 0.3rem 0.6rem;
font-size: 0.8rem;
font-weight: 600;
line-height: 1.5;
}
/* Main Action Button (Create Meeting) */
.btn-main-action { .btn-main-action {
background-color: var(--kaauh-teal); background-color: var(--kaauh-teal);
border-color: var(--kaauh-teal); border-color: var(--kaauh-teal);
color: white; color: white;
font-weight: 600; font-weight: 600;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
gap: 0.4rem;
padding: 0.5rem 1rem;
} }
.btn-main-action:hover { .btn-main-action:hover {
background-color: var(--kaauh-teal-dark); background-color: var(--kaauh-teal-dark);
border-color: var(--kaauh-teal-dark); border-color: var(--kaauh-teal-dark);
box-shadow: 0 4px 8px rgba(0,0,0,0.15); box-shadow: 0 4px 8px rgba(0,0,0,0.15);
text-decoration: none; /* Ensure no hover underline if base fails */
} }
/* Outline Primary (View/Join buttons) */ /* Secondary Button Style (For Edit/Outline - Consistent) */
.btn-kaats-outline-primary { .btn-outline-secondary {
color: var(--kaauh-teal-dark);
border-color: var(--kaauh-teal);
}
.btn-outline-secondary:hover {
background-color: var(--kaauh-teal-dark);
color: white;
border-color: var(--kaauh-teal-dark);
}
/* Primary Outline for View/Join */
.btn-outline-primary {
color: var(--kaauh-teal); color: var(--kaauh-teal);
border-color: var(--kaauh-teal); border-color: var(--kaauh-teal);
background-color: transparent;
} }
.btn-kaats-outline-primary:hover { .btn-outline-primary:hover {
background-color: var(--kaauh-teal); background-color: var(--kaauh-teal);
color: white; color: white;
text-decoration: none;
} }
/* Outline Secondary (Update button) */ /* Meeting Card Specifics (Adapted to Standard Card View) */
.btn-kaats-outline-secondary { .meeting-card .card-title {
color: var(--kaauh-secondary);
border-color: var(--kaauh-secondary);
background-color: transparent;
}
.btn-kaats-outline-secondary:hover {
background-color: var(--kaauh-secondary);
color: white;
border-color: var(--kaauh-secondary);
text-decoration: none;
}
/* Outline Danger (Delete button) */
.btn-kaats-outline-danger {
color: var(--kaauh-danger);
border-color: var(--kaauh-danger);
background-color: transparent;
}
.btn-kaats-outline-danger:hover {
background-color: var(--kaauh-danger);
color: white;
border-color: var(--kaauh-danger);
text-decoration: none;
}
/* --- General UI Styles (Unchanged) --- */
/* CARD GRID STYLES */
.meetings-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.5rem;
padding: 1rem;
}
.meeting-card {
background: white;
border-radius: 0.75rem;
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
padding: 1.5rem;
border: 1px solid var(--kaauh-border);
transition: transform 0.2s ease, box-shadow 0.2s ease;
height: 100%;
}
.meeting-card:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0,0,0,0.1);
}
/* TOPIC AND DETAILS STYLES */
.meeting-topic {
font-size: 1.15rem;
font-weight: 600;
color: var(--kaauh-teal-dark); color: var(--kaauh-teal-dark);
margin-bottom: 1rem;
border-bottom: 1px solid var(--kaauh-border);
padding-bottom: 0.5rem;
}
.meeting-detail {
display: flex;
margin-bottom: 0.75rem;
align-items: flex-start;
}
.detail-label {
font-weight: 600; font-weight: 600;
min-width: 80px; font-size: 1.15rem;
color: var(--kaauh-gray);
font-size: 0.9rem;
flex-shrink: 0;
} }
.detail-value { .meeting-card .card-text i {
flex: 1; color: var(--kaauh-teal);
font-size: 0.9rem; width: 1.25rem;
word-break: break-word;
color: var(--kaauh-primary-text);
} }
/* STATUS BADGE STYLES */ /* Status Badges (Standardized) */
.status-badge { .status-badge {
display: inline-block; font-size: 0.8rem;
font-size: 0.75rem;
font-weight: 700;
padding: 0.4em 0.8em; padding: 0.4em 0.8em;
border-radius: 0.4rem; border-radius: 0.4rem;
text-align: center;
color: white;
}
.bg-warning {
background: var(--kaauh-warning) !important;
color: var(--kaauh-primary-text) !important;
}
.bg-success {
background: var(--kaauh-success) !important;
color: white !important;
}
.bg-danger {
background: var(--kaauh-danger) !important;
color: white !important;
}
/* ACTION AREA STYLES */
.actions {
margin-top: 1rem;
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
/* Header Styling */
.card-header h1 {
color: var(--kaauh-teal-dark);
font-weight: 700; font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.7px;
} }
.card { /* Status Badge Mapping */
border: 1px solid var(--kaauh-border); .bg-waiting { background-color: #ffc107 !important; color: var(--kaauh-primary-text) !important;}
border-radius: 0.75rem; .bg-started { background-color: var(--kaauh-teal) !important; color: white !important;}
box-shadow: 0 4px 12px rgba(0,0,0,0.06); .bg-ended { background-color: #dc3545 !important; color: white !important;}
}
/* Empty State Icon Color */ /* Table Styling (Consistent with Reference) */
.text-muted.mb-3 { .table-view .table thead th {
color: var(--kaauh-teal-dark) !important; background-color: var(--kaauh-teal-dark);
color: white;
font-weight: 600;
border-color: var(--kaauh-border);
text-transform: uppercase;
font-size: 0.8rem;
letter-spacing: 0.5px;
padding: 1rem;
} }
.table-view .table tbody td {
/* Pagination Link Styling */ vertical-align: middle;
padding: 1rem;
border-color: var(--kaauh-border);
}
.table-view .table tbody tr:hover {
background-color: var(--kaauh-gray-light);
}
/* Pagination Link Styling (Consistent) */
.pagination .page-item .page-link { .pagination .page-item .page-link {
color: var(--kaauh-teal-dark); color: var(--kaauh-teal-dark);
border-color: var(--kaauh-border); border-color: var(--kaauh-border);
@ -208,120 +129,123 @@
background-color: #e9ecef; background-color: #e9ecef;
} }
/* RESPONSIVE STYLES */ /* Filter & Search Layout Adjustments */
@media (max-width: 768px) { .filter-buttons {
.meetings-grid { display: flex;
grid-template-columns: 1fr; gap: 0.5rem;
} }
.meeting-detail {
flex-direction: column; /* Icon color for empty state */
gap: 0.25rem; .text-muted.fa-3x {
} color: var(--kaauh-teal-dark) !important;
.detail-label {
min-width: auto;
}
} }
</style> </style>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="card"> <div class="container-fluid py-4">
<div class="card-header"> <div class="d-flex justify-content-between align-items-center mb-4">
<div class="d-flex justify-content-between align-items-center"> <h1 style="color: var(--kaauh-teal-dark); font-weight: 700;">
<h1 class="h3 mb-0"> <i class="fas fa-video me-2"></i> {% trans "Zoom Meetings" %}
{% include "icons/meeting.html" %} </h1>
{% trans "Zoom Meetings" %} <a href="{% url 'create_meeting' %}" class="btn btn-main-action">
</h1> <i class="fas fa-plus me-1"></i> {% trans "Create Meeting" %}
<div class="d-flex gap-2 align-items-center"> </a>
{% include "includes/search_form.html" with search_query=search_query %} </div>
<a href="{% url 'create_meeting' %}" class="btn-base btn-main-action"> <div class="card mb-4 shadow-sm no-hover">
<svg class="heroicon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <div class="card-body">
<path d="M12 4v16m8-8H4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path> <div class="row">
</svg> <div class="col-md-4">
{% trans "Create Meeting" %} <label for="search" class="form-label small text-muted">{% trans "Search by Topic" %}</label>
</a> <div class="input-group input-group-lg mb-3">
<form method="get" action="" class="w-100">
{% include "includes/search_form.html" with search_query=search_query %}
</form>
</div>
</div>
<div class="col-md-8">
<form method="GET" class="row g-3 align-items-end" >
{% if search_query %}<input type="hidden" name="q" value="{{ search_query }}">{% endif %}
<div class="col-md-3">
<label for="status" class="form-label small text-muted">{% trans "Filter by Status" %}</label>
<select name="status" id="status" class="form-select form-select-sm">
<option value="">{% trans "All Statuses" %}</option>
<option value="waiting" {% if status_filter == 'waiting' %}selected{% endif %}>{% trans "Waiting" %}</option>
<option value="started" {% if status_filter == 'started' %}selected{% endif %}>{% trans "Started" %}</option>
<option value="ended" {% if status_filter == 'ended' %}selected{% endif %}>{% trans "Ended" %}</option>
</select>
</div>
<div class="col-md-5">
<div class="filter-buttons">
<button type="submit" class="btn btn-main-action btn-lg">
<i class="fas fa-filter me-1"></i> {% trans "Apply Filters" %}
</button>
{% if status_filter or search_query %}
<a href="{% url 'meeting_list' %}" class="btn btn-outline-secondary btn-lg">
<i class="fas fa-times me-1"></i> {% trans "Clear" %}
</a>
{% endif %}
</div>
</div>
</form>
</div>
</div> </div>
</div> </div>
</div> </div>
{% if meetings %} {% if meetings %}
<div id="meetings-list"> <div id="meetings-list">
{# View Switcher #} {# View Switcher #}
{% include "includes/_list_view_switcher.html" with list_id="meetings-list" %} {% include "includes/_list_view_switcher.html" with list_id="meetings-list" %}
{# Card View (Default) #} {# Card View #}
<div class="card-view active"> <div class="card-view active row">
<div class="meetings-grid"> {% for meeting in meetings %}
{% for meeting in meetings %} <div class="col-md-6 col-lg-4 mb-4">
<div class="meeting-card"> <div class="card meeting-card h-100 shadow-sm">
<div class="meeting-topic">{{ meeting.topic }}</div> <div class="card-body d-flex flex-column">
<div class="d-flex justify-content-between align-items-start mb-2">
<div class="meeting-detail"> <h5 class="card-title flex-grow-1 me-3">{{ meeting.topic }}</h5>
<div class="detail-label">{% trans "ID" %}:</div> <span class="status-badge bg-{{ meeting.status }}">
<div class="detail-value">{{ meeting.meeting_id|default:meeting.id }}</div> {{ meeting.status|title }}
</div>
<div class="meeting-detail">
<div class="detail-label">{% trans "Start Time" %}:</div>
<div class="detail-value">{{ meeting.start_time|date:"M d, Y H:i" }}</div>
</div>
<div class="meeting-detail">
<div class="detail-label">{% trans "Duration" %}:</div>
<div class="detail-value">{{ meeting.duration }} minutes</div>
</div>
<div class="meeting-detail">
<div class="detail-label">{% trans "Status" %}:</div>
<div class="detail-value">
<span class="status-badge {% if meeting.status == 'waiting' %}bg-warning{% elif meeting.status == 'started' %}bg-success{% elif meeting.status == 'ended' %}bg-danger{% endif %}">
{% if meeting.status == 'waiting' %}
{% trans "Waiting" %}
{% elif meeting.status == 'started' %}
{% trans "Started" %}
{% elif meeting.status == 'ended' %}
{% trans "Ended" %}
{% endif %}
</span> </span>
</div> </div>
</div>
{% if meeting.join_url %} <p class="card-text text-muted small mb-3">
<div class="meeting-detail"> <i class="fas fa-hashtag"></i> {% trans "ID" %}: {{ meeting.meeting_id|default:meeting.id }}<br>
<div class="detail-label">{% trans "Join URL" %}:</div> <i class="fas fa-clock"></i> {% trans "Start" %}: {{ meeting.start_time|date:"M d, Y H:i" }}<br>
<div class="detail-value"> <i class="fas fa-stopwatch"></i> {% trans "Duration" %}: {{ meeting.duration }} minutes
<a href="{{ meeting.join_url }}" target="_blank" class="btn-base btn-kaats-outline-primary btn-sm"> </p>
{% trans "Join Meeting" %}
<div class="mt-auto pt-2 border-top">
<div class="d-flex gap-2">
<a href="{% url 'meeting_details' meeting.pk %}" class="btn btn-sm btn-outline-primary">
<i class="fas fa-eye"></i> {% trans "View" %}
</a> </a>
{% if meeting.join_url %}
<a href="{{ meeting.join_url }}" target="_blank" class="btn btn-sm btn-main-action">
<i class="fas fa-link"></i> {% trans "Join" %}
</a>
{% endif %}
<a href="{% url 'update_meeting' meeting.pk %}" class="btn btn-sm btn-outline-secondary">
<i class="fas fa-edit"></i>
</a>
<button type="button" class="btn btn-outline-danger btn-sm" title="{% trans 'Delete' %}"
data-bs-toggle="modal" data-bs-target="#deleteModal"
data-delete-url="{% url 'delete_meeting' meeting.pk %}"
data-item-name="{{ meeting.topic }}">
<i class="fas fa-trash-alt"></i>
</button>
</div> </div>
</div> </div>
{% endif %}
<div class="actions">
<a href="{% url 'meeting_details' meeting.pk %}" class="btn-base btn-kaats-outline-primary btn-sm" title="{% trans 'View' %}">
<svg class="heroicon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0 8.268-2.943-9.542-7z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</a>
<a href="{% url 'update_meeting' meeting.pk %}" class="btn-base btn-kaats-outline-secondary btn-sm" title="{% trans 'Update' %}">
<svg class="heroicon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</a>
<button type="button" class="btn-base btn-kaats-outline-danger btn-sm" title="{% trans 'Delete' %}"
data-bs-toggle="deleteModal"
data-delete-url="{% url 'delete_meeting' meeting.pk %}"
data-item-name="{{ meeting.topic }}">
<svg class="heroicon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</button>
</div> </div>
</div> </div>
{% endfor %} </div>
</div> {% endfor %}
</div> </div>
{# Table View #} {# Table View #}
@ -330,34 +254,33 @@
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th scope="col">{% trans "Topic" %}</th> <th scope="col" style="width: 30%;">{% trans "Topic" %}</th>
<th scope="col">{% trans "ID" %}</th> <th scope="col" style="width: 15%;">{% trans "ID" %}</th>
<th scope="col">{% trans "Start Time" %}</th> <th scope="col" style="width: 20%;">{% trans "Start Time" %}</th>
<th scope="col">{% trans "Duration" %}</th> <th scope="col" style="width: 10%;">{% trans "Duration" %}</th>
<th scope="col">{% trans "Status" %}</th> <th scope="col" style="width: 15%;">{% trans "Status" %}</th>
<th scope="col" class="text-end">{% trans "Actions" %}</th> <th scope="col" style="width: 10%;" class="text-end">{% trans "Actions" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for meeting in meetings %} {% for meeting in meetings %}
<tr> <tr>
<td><strong>{{ meeting.topic }}</strong></td> <td><strong class="text-primary">{{ meeting.topic }}</strong></td>
<td>{{ meeting.meeting_id|default:meeting.id }}</td> <td>{{ meeting.meeting_id|default:meeting.id }}</td>
<td>{{ meeting.start_time|date:"M d, Y H:i" }}</td> <td>{{ meeting.start_time|date:"M d, Y H:i" }}</td>
<td>{{ meeting.duration }} minutes</td> <td>{{ meeting.duration }} min</td>
<td> <td>
<span class="status-badge {% if meeting.status == 'waiting' %}bg-warning{% elif meeting.status == 'started' %}bg-success{% elif meeting.status == 'ended' %}bg-danger{% endif %}"> <span class="status-badge bg-{{ meeting.status }}">
{% if meeting.status == 'waiting' %} {{ meeting.status|title }}
{% trans "Waiting" %}
{% elif meeting.status == 'started' %}
{% trans "Started" %}
{% elif meeting.status == 'ended' %}
{% trans "Ended" %}
{% endif %}
</span> </span>
</td> </td>
<td class="text-end"> <td class="text-end">
<div class="btn-group btn-group-sm" role="group"> <div class="btn-group btn-group-sm" role="group">
{% if meeting.join_url %}
<a href="{{ meeting.join_url }}" target="_blank" class="btn btn-main-action" title="{% trans 'Join' %}">
<i class="fas fa-sign-in-alt"></i>
</a>
{% endif %}
<a href="{% url 'meeting_details' meeting.pk %}" class="btn btn-outline-primary" title="{% trans 'View' %}"> <a href="{% url 'meeting_details' meeting.pk %}" class="btn btn-outline-primary" title="{% trans 'View' %}">
<i class="fas fa-eye"></i> <i class="fas fa-eye"></i>
</a> </a>
@ -365,10 +288,10 @@
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</a> </a>
<button type="button" class="btn btn-outline-danger" title="{% trans 'Delete' %}" <button type="button" class="btn btn-outline-danger" title="{% trans 'Delete' %}"
data-bs-toggle="deleteModal" data-bs-toggle="modal" data-bs-target="#deleteModal"
data-delete-url="{% url 'delete_meeting' meeting.pk %}" data-delete-url="{% url 'delete_meeting' meeting.pk %}"
data-item-name="{{ meeting.topic }}"> data-item-name="{{ meeting.topic }}">
<i class="fas fa-trash"></i> <i class="fas fa-trash-alt"></i>
</button> </button>
</div> </div>
</td> </td>
@ -380,52 +303,45 @@
</div> </div>
</div> </div>
{# Pagination (Standardized) #}
{% if is_paginated %} {% if is_paginated %}
<nav aria-label="Page navigation" class="mt-4"> <nav aria-label="Page navigation" class="mt-4">
<ul class="pagination justify-content-center"> <ul class="pagination justify-content-center">
{% if page_obj.has_previous %} {% if page_obj.has_previous %}
<li class="page-item"> <li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}&search={{ search_query }}" aria-label="Previous"> <a class="page-link" href="?page=1{% if status_filter %}&status={{ status_filter }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}">First</a>
<span aria-hidden="true">&laquo;</span> </li>
</a> <li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if status_filter %}&status={{ status_filter }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}">Previous</a>
</li> </li>
{% endif %} {% endif %}
{% for num in page_obj.paginator.page_range %} <li class="page-item active">
{% if page_obj.number == num %} <span class="page-link">{{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
<li class="page-item active"><span class="page-link">{{ num }}</span></li> </li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}&search={{ search_query }}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %} {% if page_obj.has_next %}
<li class="page-item"> <li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}&search={{ search_query }}" aria-label="Next"> <a class="page-link" href="?page={{ page_obj.next_page_number }}{% if status_filter %}&status={{ status_filter }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}">Next</a>
<span aria-hidden="true">&raquo;</span> </li>
</a> <li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}{% if status_filter %}&status={{ status_filter }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}">Last</a>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
</nav> </nav>
{% endif %} {% endif %}
{% else %} {% else %}
<div class="text-center py-5"> <div class="text-center py-5 card shadow-sm">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-muted mb-3" style="width: 80px;"> <div class="card-body">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z" /> <i class="fas fa-video fa-3x mb-3" style="color: var(--kaauh-teal-dark);"></i>
</svg> <h3>{% trans "No Zoom meetings found" %}</h3>
<p class="text-muted">{% trans "No meetings found." %}</p> <p class="text-muted">{% trans "Create your first meeting or adjust your filters." %}</p>
{% if user.is_staff %} <a href="{% url 'create_meeting' %}" class="btn btn-main-action mt-3">
<a href="{% url 'create_meeting' %}" class="btn-base btn-main-action mt-3"> <i class="fas fa-plus me-1"></i> {% trans "Create Your First Meeting" %}
<svg class="heroicon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 4v16m8-8H4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
{% trans "Create Your First Meeting" %}
</a> </a>
{% endif %} </div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -115,8 +115,8 @@
color: var(--kaauh-teal); color: var(--kaauh-teal);
} }
.main-tabs .nav-link.active { .main-tabs .nav-link.active {
color: var(--kaauh-teal-dark); color: var(--kaauh-teal-dark) !important;
background-color: white; background-color: white !important;
border-bottom: 3px solid var(--kaauh-teal); border-bottom: 3px solid var(--kaauh-teal);
font-weight: 600; font-weight: 600;
} }
@ -141,8 +141,8 @@
border-right: none; border-right: none;
} }
.right-column-card .nav-link.active { .right-column-card .nav-link.active {
background-color: white; background-color: white !important;
color: var(--kaauh-teal-dark); color: var(--kaauh-teal-dark) !important;
border-bottom: 3px solid var(--kaauh-teal); border-bottom: 3px solid var(--kaauh-teal);
border-right-color: transparent; border-right-color: transparent;
margin-bottom: -1px; margin-bottom: -1px;

View File

@ -5,15 +5,16 @@
{% block customCSS %} {% block customCSS %}
<style> <style>
/* UI Variables for the KAAT-S Theme */ /* UI Variables for the KAAT-S Theme (Consistent with Reference) */
:root { :root {
--kaauh-teal: #00636e; --kaauh-teal: #00636e;
--kaauh-teal-dark: #004a53; --kaauh-teal-dark: #004a53;
--kaauh-border: #eaeff3; --kaauh-border: #eaeff3;
--kaauh-primary-text: #343a40; --kaauh-primary-text: #343a40;
--kaauh-gray-light: #f8f9fa; /* Added for hover/background consistency */
} }
/* Enhanced Card Styling */ /* Enhanced Card Styling (Consistent) */
.card { .card {
border: 1px solid var(--kaauh-border); border: 1px solid var(--kaauh-border);
border-radius: 0.75rem; border-radius: 0.75rem;
@ -21,19 +22,15 @@
transition: transform 0.2s, box-shadow 0.2s; transition: transform 0.2s, box-shadow 0.2s;
background-color: white; background-color: white;
} }
.card:hover { .card:not(.no-hover):hover { /* Use no-hover class for main structure cards */
/* Remove hover effect from the main list card to avoid visual noise */ transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0,0,0,0.1);
}
.card.no-hover:hover {
transform: none; transform: none;
box-shadow: 0 4px 12px rgba(0,0,0,0.06); box-shadow: 0 4px 12px rgba(0,0,0,0.06);
} }
.card-header {
font-weight: 600;
padding: 1.25rem;
border-bottom: 1px solid var(--kaauh-border);
background-color: #f8f9fa; /* Light header background */
}
/* Main Action Button Style (Teal Theme) */ /* Main Action Button Style (Teal Theme) */
.btn-main-action { .btn-main-action {
background-color: var(--kaauh-teal); background-color: var(--kaauh-teal);
@ -53,50 +50,63 @@
box-shadow: 0 4px 8px rgba(0,0,0,0.15); box-shadow: 0 4px 8px rgba(0,0,0,0.15);
} }
/* Secondary Action Buttons (used in table) */ /* Secondary Button Style (For Edit/Outline - Consistent) */
.btn-group .btn-outline-primary { .btn-outline-secondary {
color: var(--kaauh-teal); color: var(--kaauh-teal-dark);
border-color: var(--kaauh-border);
background-color: white;
}
.btn-group .btn-outline-primary:hover {
background-color: var(--kaauh-teal);
color: white;
border-color: var(--kaauh-teal); border-color: var(--kaauh-teal);
} }
.btn-outline-secondary:hover {
/* Table Styling */ background-color: var(--kaauh-teal-dark);
.table thead th { color: white;
border-color: var(--kaauh-teal-dark);
}
/* Card Specifics (Adapted from Job Card to Candidate Card) */
.candidate-card .card-title {
color: var(--kaauh-teal-dark); color: var(--kaauh-teal-dark);
font-weight: 600; font-weight: 600;
border-bottom: 2px solid var(--kaauh-teal); /* Highlight bottom border */ font-size: 1.15rem;
vertical-align: middle; }
padding: 0.75rem 1rem; .candidate-card .card-text i {
font-size: 0.95rem; color: var(--kaauh-teal);
width: 1.25rem;
} }
.table tbody tr:hover { /* Table & Card Badge Styling (Unified) */
background-color: #f3f9f9; /* Light teal hover for rows */
}
.table tbody td {
vertical-align: middle;
padding: 0.75rem 1rem;
}
/* Badge Styling */
.badge { .badge {
font-weight: 600; font-weight: 600;
padding: 0.4em 0.7em; padding: 0.4em 0.7em;
border-radius: 0.3rem; border-radius: 0.3rem;
text-transform: uppercase;
letter-spacing: 0.5px;
} }
/* Status Badge Mapping */ /* Status Badge Mapping (Using standard Bootstrap names where possible) */
.table .bg-primary { background-color: var(--kaauh-teal) !important; color: white !important;} /* Job Title Badge */ .bg-primary { background-color: var(--kaauh-teal) !important; color: white !important;} /* Main job/stage badge */
.table .bg-success { background-color: #28a745 !important; } /* Applied: Yes */ .bg-success { background-color: #28a745 !important; color: white !important;}
.table .bg-warning { background-color: #ffc107 !important; } /* Applied: No */ .bg-warning { background-color: #ffc107 !important; color: #343a40 !important;}
/* Pagination Link Styling */ /* Table Styling (Consistent with Reference) */
.table-view .table thead th {
background-color: var(--kaauh-teal-dark); /* Dark header background */
color: white;
font-weight: 600;
border-color: var(--kaauh-border);
text-transform: uppercase;
font-size: 0.8rem;
letter-spacing: 0.5px;
padding: 1rem;
}
.table-view .table tbody td {
vertical-align: middle;
padding: 1rem;
border-color: var(--kaauh-border);
}
.table-view .table tbody tr:hover {
background-color: var(--kaauh-gray-light);
}
/* Pagination Link Styling (Consistent) */
.pagination .page-item .page-link { .pagination .page-item .page-link {
color: var(--kaauh-teal-dark); color: var(--kaauh-teal-dark);
border-color: var(--kaauh-border); border-color: var(--kaauh-border);
@ -109,189 +119,213 @@
.pagination .page-item:hover .page-link:not(.active) { .pagination .page-item:hover .page-link:not(.active) {
background-color: #e9ecef; background-color: #e9ecef;
} }
/* Icon Sizing in Buttons */ /* Filter & Search Layout Adjustments */
.btn-group .btn-sm svg { .filter-buttons {
width: 1rem; display: flex;
height: 1rem; gap: 0.5rem;
} }
</style> </style>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="container-fluid py-4"> <div class="container-fluid py-4">
<div class="card shadow-sm"> <div class="d-flex justify-content-between align-items-center mb-4">
<div class="card-header"> <h1 style="color: var(--kaauh-teal-dark); font-weight: 700;">
<div class="d-flex justify-content-between align-items-center"> <i class="fas fa-users me-2"></i> {% trans "Candidate Profiles" %}
<h1 class="h3 mb-0" style="color: var(--kaauh-teal-dark); font-weight: 700;"> </h1>
<i class="fas fa-users me-2"></i> {% if user.is_staff %}
{% trans "Candidate Profiles" %} <a href="{% url 'candidate_create' %}" class="btn btn-main-action">
</h1> <i class="fas fa-plus me-1"></i> {% trans "Add New Candidate" %}
<div class="d-flex gap-3 align-items-center"> </a>
{# The search form structure should be updated to align with the job_list style if possible #} {% endif %}
{% include "includes/search_form.html" with search_query=search_query %} </div>
{% if user.is_staff %} <div class="card mb-4 shadow-sm no-hover">
<a href="{% url 'candidate_create' %}" class="btn btn-main-action"> <div class="card-body">
<i class="fas fa-plus"></i> <div class="row">
{% trans "Add New Candidate" %} <div class="col-md-4">
</a> <label for="search" class="form-label small text-muted">{% trans "Search by Name or Email" %}</label>
{% endif %} <div class="input-group input-group-lg mb-3">
<form method="get" action="" class="w-100">
{% include 'includes/search_form.html' %}
</form>
</div>
</div>
<div class="col-md-8">
{% url 'candidate_list' as candidate_list_url %}
<form method="GET" class="row g-3 align-items-end" >
{% if search_query %}<input type="hidden" name="q" value="{{ search_query }}">{% endif %}
<div class="col-md-3">
<label for="job_filter" class="form-label small text-muted">{% trans "Filter by Job" %}</label>
<select name="job" id="job_filter" class="form-select form-select-sm">
<option value="">{% trans "All Jobs" %}</option>
{% for job in available_jobs %} {# Assuming you pass a context variable 'available_jobs' #}
<option value="{{ job.slug }}" {% if job_filter == job.slug %}selected{% endif %}>{{ job.title }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-5">
<div class="filter-buttons">
<button type="submit" class="btn btn-main-action btn-lg">
<i class="fas fa-filter me-1"></i> {% trans "Apply Filters" %}
</button>
{% if job_filter or search_query %}
<a href="{% url 'candidate_list' %}" class="btn btn-outline-secondary btn-lg">
<i class="fas fa-times me-1"></i> {% trans "Clear" %}
</a>
{% endif %}
</div>
</div>
</form>
</div> </div>
</div> </div>
</div> </div>
</div>
{% if candidates %}
<div id="candidate-list">
{# View Switcher - list_id must match the container ID #}
{% include "includes/_list_view_switcher.html" with list_id="candidate-list" %}
{% if candidates %} {# Table View (Default) #}
<div id="candidate-list"> <div class="table-view active">
{# View Switcher #} <div class="table-responsive">
{% include "includes/_list_view_switcher.html" with list_id="candidate-list" %} <table class="table table-hover mb-0">
<thead>
{# Table View (Default) #} <tr>
<div class="table-view active"> <th scope="col" style="width: 20%;">{% trans "Name" %}</th>
<div class="table-responsive"> <th scope="col" style="width: 20%;">{% trans "Email" %}</th>
<table class="table table-hover mb-0"> <th scope="col" style="width: 15%;">{% trans "Phone" %}</th>
<thead> <th scope="col" style="width: 15%;">{% trans "Job" %}</th>
<th scope="col" style="width: 10%;">{% trans "Stage" %}</th>
<th scope="col" style="width: 10%;">{% trans "Created" %}</th>
<th scope="col" style="width: 10%;" class="text-end">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for candidate in candidates %}
<tr> <tr>
<th scope="col" style="width: 20%;">{% trans "Name" %}</th> <td class="fw-medium">{{ candidate.name }}</td>
<th scope="col" style="width: 20%;">{% trans "Email" %}</th> <td>{{ candidate.email }}</td>
<th scope="col" style="width: 15%;">{% trans "Phone" %}</th> <td>{{ candidate.phone }}</td>
<th scope="col" style="width: 15%;">{% trans "Job" %}</th> <td> <span class="badge bg-primary">{{ candidate.job.title }}</span></td>
<th scope="col" style="width: 10%;">{% trans "Stage" %}</th> <td>
<th scope="col" style="width: 10%;">{% trans "Created" %}</th> <span class="badge bg-primary">
<th scope="col" style="width: 10%;" class="text-center">{% trans "Actions" %}</th> {{ candidate.stage }}
</tr> </span>
</thead> </td>
<tbody> <td>{{ candidate.created_at|date:"M d, Y" }}</td>
{% for candidate in candidates %} <td class="text-end">
<tr> <div class="btn-group btn-group-sm" role="group">
<td><strong>{{ candidate.name }}</strong></td> <a href="{% url 'candidate_detail' candidate.slug %}" class="btn btn-outline-primary" title="{% trans 'View' %}">
<td>{{ candidate.email }}</td> <i class="fas fa-eye"></i>
<td>{{ candidate.phone }}</td>
<td> <span class="badge bg-primary">{{ candidate.job.title }}</span></td>
<td>
<span class="badge bg-primary">
{{ candidate.stage }}
</span>
</td>
<td>{{ candidate.created_at|date:"M d, Y" }}</td>
<td class="text-center">
<div class="btn-group btn-group-sm" role="group">
<a href="{% url 'candidate_detail' candidate.slug %}" class="btn btn-outline-primary" title="{% trans 'View' %}">
<i class="fas fa-eye"></i>
</a>
{% if user.is_staff %}
<a href="{% url 'candidate_update' candidate.slug %}" class="btn btn-outline-primary" title="{% trans 'Edit' %}">
<i class="fas fa-edit"></i>
</a>
<button type="button" class="btn btn-outline-danger" title="{% trans 'Delete' %}"
data-bs-toggle="deleteModal"
data-delete-url="{% url 'candidate_delete' candidate.slug %}"
data-item-name="{{ candidate.name }}">
<i class="fas fa-trash-alt"></i>
</button>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{# Card View #}
<div class="card-view">
<div class="row g-4">
{% for candidate in candidates %}
<div class="col-md-6 col-lg-4">
<div class="card h-100">
<div class="card-header">
<h5 class="h5 mb-1">{{ candidate.name }}</h5>
<small class="text-white-50">{{ candidate.email }}</small>
</div>
<div class="card-body">
<p class="card-text">
<strong>{% trans "Phone" %}:</strong> {{ candidate.phone|default:"N/A" }}<br>
<strong>{% trans "Job" %}:</strong> <span class="badge bg-primary">{{ candidate.job.title }}</span><br>
<strong>{% trans "Stage" %}:</strong> <span class="badge bg-primary">{{ candidate.stage }}</span><br>
<strong>{% trans "Created" %}:</strong> {{ candidate.created_at|date:"M d, Y" }}
</p>
</div>
<div class="card-footer">
<div class="d-flex gap-2">
<a href="{% url 'candidate_detail' candidate.slug %}" class="btn btn-sm btn-outline-primary w-100">
<i class="fas fa-eye"></i> {% trans "View" %}
</a> </a>
{% if user.is_staff %} {% if user.is_staff %}
<div class="btn-group w-100" role="group"> <a href="{% url 'candidate_update' candidate.slug %}" class="btn btn-outline-secondary" title="{% trans 'Edit' %}">
<a href="{% url 'candidate_update' candidate.slug %}" class="btn btn-sm btn-outline-secondary" title="{% trans 'Edit' %}"> <i class="fas fa-edit"></i>
<i class="fas fa-edit"></i> </a>
</a> <button type="button" class="btn btn-outline-danger" title="{% trans 'Delete' %}"
<button type="button" class="btn btn-outline-danger btn-sm" title="{% trans 'Delete' %}" data-bs-toggle="modal" data-bs-target="#deleteModal" {# Updated to standard Bootstrap usage #}
data-bs-toggle="deleteModal" data-delete-url="{% url 'candidate_delete' candidate.slug %}"
data-delete-url="{% url 'candidate_delete' candidate.slug %}" data-item-name="{{ candidate.name }}">
data-item-name="{{ candidate.name }}"> <i class="fas fa-trash-alt"></i>
<i class="fas fa-trash-alt"></i> </button>
</button>
</div>
{% endif %} {% endif %}
</div> </div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{# Card View #}
<div class="card-view row"> {# Added 'row' class for grid structure #}
{% for candidate in candidates %}
<div class="col-md-6 col-lg-4 mb-4"> {# Column wrapper for grid #}
<div class="card candidate-card h-100 shadow-sm">
<div class="card-body d-flex flex-column">
<div class="d-flex justify-content-between align-items-start mb-2">
<h5 class="card-title flex-grow-1 me-3">{{ candidate.name }}</h5>
<span class="badge bg-primary">{{ candidate.stage }}</span>
</div>
<p class="card-text text-muted small">
<i class="fas fa-envelope"></i> {{ candidate.email }}<br>
<i class="fas fa-phone-alt"></i> {{ candidate.phone|default:"N/A" }}<br>
<i class="fas fa-briefcase"></i> <span class="badge bg-primary">{{ candidate.job.title }}</span>
</p>
<div class="mt-auto pt-2 border-top">
<div class="d-flex gap-2">
<a href="{% url 'candidate_detail' candidate.slug %}" class="btn btn-sm btn-main-action">
<i class="fas fa-eye"></i> {% trans "View" %}
</a>
{% if user.is_staff %}
<a href="{% url 'candidate_update' candidate.slug %}" class="btn btn-sm btn-outline-secondary">
<i class="fas fa-edit"></i> {% trans "Edit" %}
</a>
<button type="button" class="btn btn-outline-danger btn-sm" title="{% trans 'Delete' %}"
data-bs-toggle="modal" data-bs-target="#deleteModal"
data-delete-url="{% url 'candidate_delete' candidate.slug %}"
data-item-name="{{ candidate.name }}">
<i class="fas fa-trash-alt"></i>
</button>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
{% endfor %} </div>
</div> </div>
</div> {% endfor %}
</div> </div>
</div>
{% if is_paginated %} {# Pagination (Standardized to Reference) #}
<div class="card-footer bg-white border-top"> {% if is_paginated %}
<nav aria-label="Page navigation"> <nav aria-label="Page navigation" class="mt-4">
<ul class="pagination justify-content-center mb-0"> <ul class="pagination justify-content-center">
{% if page_obj.has_previous %} {% if page_obj.has_previous %}
<li class="page-item"> <li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if search_query %}&q={{ search_query }}{% endif %}" aria-label="Previous"> <a class="page-link" href="?page=1{% if search_query %}&q={{ search_query }}{% endif %}{% if job_filter %}&job={{ job_filter }}{% endif %}">First</a>
<span aria-hidden="true">&laquo;</span> </li>
</a> <li class="page-item">
</li> <a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if search_query %}&q={{ search_query }}{% endif %}{% if job_filter %}&job={{ job_filter }}{% endif %}">Previous</a>
{% endif %} </li>
{% endif %}
{# Simplified pagination logic for theme consistency #} <li class="page-item active">
{% for num in page_obj.paginator.page_range %} <span class="page-link">{{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
{% if page_obj.number == num %} </li>
<li class="page-item active"><span class="page-link">{{ num }}</span></li>
{% elif num > page_obj.number|add:'-2' and num < page_obj.number|add:'2' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}{% if search_query %}&q={{ search_query }}{% endif %}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %} {% if page_obj.has_next %}
<li class="page-item"> <li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}{% if search_query %}&q={{ search_query }}{% endif %}" aria-label="Next"> <a class="page-link" href="?page={{ page_obj.next_page_number }}{% if search_query %}&q={{ search_query }}{% endif %}{% if job_filter %}&job={{ job_filter }}{% endif %}">Next</a>
<span aria-hidden="true">&raquo;</span> </li>
</a> <li class="page-item">
</li> <a class="page-link" href="?page={{ page_obj.paginator.num_pages }}{% if search_query %}&q={{ search_query }}{% endif %}{% if job_filter %}&job={{ job_filter }}{% endif %}">Last</a>
{% endif %} </li>
</ul> {% endif %}
</nav> </ul>
</div> </nav>
{% endif %} {% endif %}
{% else %} {% else %}
<div class="text-center py-5"> <div class="text-center py-5 card shadow-sm">
<div class="card-body">
<i class="fas fa-users fa-3x mb-3" style="color: var(--kaauh-teal-dark);"></i> <i class="fas fa-users fa-3x mb-3" style="color: var(--kaauh-teal-dark);"></i>
<h4 class="text-muted">{% trans "No candidates found." %}</h4> <h3>{% trans "No candidate profiles found" %}</h3>
<p class="text-muted">{% trans "Start by adding a new profile or adjusting your search filters." %}</p> <p class="text-muted">{% trans "Create your first candidate profile or adjust your filters." %}</p>
{% if user.is_staff %} {% if user.is_staff %}
<a href="{% url 'candidate_create' %}" class="btn btn-main-action mt-3"> <a href="{% url 'candidate_create' %}" class="btn btn-main-action mt-3">
<i class="fas fa-plus me-1"></i> <i class="fas fa-plus me-1"></i> {% trans "Add Candidate" %}
{% trans "Add Your First Candidate" %}
</a> </a>
{% endif %} {% endif %}
</div> </div>
{% endif %} </div>
</div> {% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -5,48 +5,53 @@
{% block customCSS %} {% block customCSS %}
<style> <style>
/* ================================================= */ /* UI Variables for the KAAT-S Theme (Consistent with Reference) */
/* THEME VARIABLES AND GLOBAL STYLES (FROM JOB DETAIL) */
/* ================================================= */
:root { :root {
--kaauh-teal: #00636e; --kaauh-teal: #00636e;
--kaauh-teal-dark: #004a53; --kaauh-teal-dark: #004a53;
--kaauh-border: #eaeff3; --kaauh-border: #eaeff3;
--kaauh-primary-text: #343a40; --kaauh-primary-text: #343a40;
--kaauh-gray-light: #f8f9fa; /* Added for hover/background consistency */
} }
/* Primary Color Overrides */ /* Enhanced Card Styling (Consistent) */
.text-primary { color: var(--kaauh-teal) !important; } .card {
border: 1px solid var(--kaauh-border);
border-radius: 0.75rem;
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
transition: transform 0.2s, box-shadow 0.2s;
background-color: white;
}
/* Standard card hover effect for list items */
.card:not(.no-hover):hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0,0,0,0.1);
}
.card.no-hover:hover {
transform: none;
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
}
/* Main Action Button Style (Applied to .btn-primary) */ /* Main Action Button Style (Teal Theme) */
.btn-main-action, .btn-primary { .btn-main-action {
background-color: var(--kaauh-teal); background-color: var(--kaauh-teal);
border-color: var(--kaauh-teal); border-color: var(--kaauh-teal);
color: white; color: white;
font-weight: 600; font-weight: 600;
padding: 0.6rem 1.2rem;
transition: all 0.2s ease; transition: all 0.2s ease;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 0.5rem; gap: 0.4rem;
text-decoration: none; /* Ensure links look like buttons */ padding: 0.5rem 1rem;
} }
.btn-main-action:hover, .btn-primary:hover {
.btn-main-action:hover {
background-color: var(--kaauh-teal-dark); background-color: var(--kaauh-teal-dark);
border-color: var(--kaauh-teal-dark); border-color: var(--kaauh-teal-dark);
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0,0,0,0.15); box-shadow: 0 4px 8px rgba(0,0,0,0.15);
} }
/* Outlined Button Styles for Table Actions */ /* Secondary Button Style (For Edit/Outline - Consistent) */
.btn-outline-primary {
color: var(--kaauh-teal);
border-color: var(--kaauh-teal);
}
.btn-outline-primary:hover {
background-color: var(--kaauh-teal);
color: white;
}
.btn-outline-secondary { .btn-outline-secondary {
color: var(--kaauh-teal-dark); color: var(--kaauh-teal-dark);
border-color: var(--kaauh-teal); border-color: var(--kaauh-teal);
@ -56,54 +61,44 @@
color: white; color: white;
border-color: var(--kaauh-teal-dark); border-color: var(--kaauh-teal-dark);
} }
/* Danger button remains standard for deletion */ .btn-outline-primary {
color: var(--kaauh-teal);
/* Card enhancements */ border-color: var(--kaauh-teal);
.card {
border: 1px solid var(--kaauh-border);
border-radius: 0.75rem;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
background-color: white;
} }
.btn-outline-primary:hover {
/* Colored Header Card */ background-color: var(--kaauh-teal);
.list-header-card {
background: linear-gradient(135deg, var(--kaauh-teal), #004d57);
color: white; color: white;
border-radius: 0.75rem 0.75rem 0 0;
padding: 1.5rem;
box-shadow: 0 4px 10px rgba(0,0,0,0.15);
}
.list-header-card h1 {
font-weight: 700;
margin: 0;
font-size: 1.8rem;
}
.heroicon {
width: 1.25rem;
height: 1.25rem;
vertical-align: text-bottom;
stroke: currentColor;
margin-right: 0.5rem;
} }
/* Table Styling */ /* Training Card Specifics */
.table thead th { .training-card .card-title {
background-color: #f8f9fa; color: var(--kaauh-teal-dark);
color: var(--kaauh-primary-text);
font-weight: 600; font-weight: 600;
border-bottom: 2px solid var(--kaauh-border); font-size: 1.15rem;
}
.table tbody tr:hover {
background-color: #f0f8ff; /* Light hover color */
}
.btn-group .btn-sm {
padding: 0.35rem 0.6rem;
} }
/* Pagination Styling */ /* Table Styling (Consistent with Reference) */
.pagination .page-link { .table-view .table thead th {
background-color: var(--kaauh-teal-dark); /* Dark header background */
color: white;
font-weight: 600;
border-color: var(--kaauh-border);
text-transform: uppercase;
font-size: 0.8rem;
letter-spacing: 0.5px;
padding: 1rem;
}
.table-view .table tbody td {
vertical-align: middle;
padding: 1rem;
border-color: var(--kaauh-border);
}
.table-view .table tbody tr:hover {
background-color: var(--kaauh-gray-light);
}
/* Pagination Link Styling (Consistent) */
.pagination .page-item .page-link {
color: var(--kaauh-teal-dark); color: var(--kaauh-teal-dark);
border-color: var(--kaauh-border); border-color: var(--kaauh-border);
} }
@ -112,173 +107,179 @@
border-color: var(--kaauh-teal); border-color: var(--kaauh-teal);
color: white; color: white;
} }
.pagination .page-item:hover .page-link:not(.active) {
background-color: #e9ecef;
}
/* Filter & Search Layout Adjustments */
.filter-buttons {
display: flex;
gap: 0.5rem;
}
/* Override for the primary text color being used for card-titles in table */
.text-primary { color: var(--kaauh-teal) !important; }
</style> </style>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="container-fluid py-4"> <div class="container-fluid py-4">
<div class="card shadow-sm"> <div class="d-flex justify-content-between align-items-center mb-4">
<h1 style="color: var(--kaauh-teal-dark); font-weight: 700;">
<i class="fas fa-graduation-cap me-2"></i> {% trans "Training Materials" %}
</h1>
{% if user.is_authenticated %}
<a href="{% url 'training_create' %}" class="btn btn-main-action">
<i class="fas fa-plus me-1"></i> {% trans "Add New Material" %}
</a>
{% endif %}
</div>
<div class="list-header-card"> <div class="card mb-4 shadow-sm no-hover">
<div class="d-flex justify-content-between align-items-center flex-wrap"> <div class="card-body">
<h1 class="h3 mb-0"> <div class="row">
<i class="fas fa-graduation-cap me-2"></i> <div class="col-md-6">
{% trans "Training Materials" %} <label for="search" class="form-label small text-muted">{% trans "Search by Title or Creator" %}</label>
</h1> <div class="input-group input-group-lg mb-3">
<div class="d-flex gap-3 align-items-center mt-2 mt-md-0"> <form method="get" action="" class="w-100">
{% include "includes/search_form.html" with search_query=search_query %}
<div class="order-3 order-md-1"> </form>
{% include "includes/search_form.html" with search_query=search_query %}
</div> </div>
</div>
<div class="col-md-6 d-flex align-items-end">
{# Additional Filters can go here if needed #}
</div>
</div>
</div>
</div>
{% if materials %}
<div id="training-materials-list">
{# View Switcher - list_id must match the container ID #}
{% include "includes/_list_view_switcher.html" with list_id="training-materials-list" %}
{% if user.is_authenticated %} {# Card View (Default) - Must have 'row' class for grid layout #}
<a href="{% url 'training_create' %}" class="btn btn-main-action btn-sm order-1 order-md-2" title="{% trans 'Add New Material' %}"> <div class="card-view active row">
<i class="fas fa-plus"></i> {% for material in materials %}
<span class="d-none d-sm-inline">{% trans "Add New Material" %}</span> <div class="col-md-6 col-lg-4 mb-4">
</a> <div class="card training-card h-100 shadow-sm">
{% endif %} <div class="card-body d-flex flex-column">
<div class="d-flex justify-content-between align-items-start mb-2">
<h5 class="card-title flex-grow-1 me-3">{{ material.title }}</h5>
</div>
<p class="card-text text-muted small">
<i class="fas fa-user-edit"></i> {% trans "Created By" %}: {{ material.created_by.username|default:"Anonymous" }}<br>
<i class="fas fa-calendar-alt"></i> {% trans "Created On" %}: {{ material.created_at|date:"M d, Y" }}
</p>
<div class="mt-auto pt-2 border-top">
<div class="d-flex gap-2">
<a href="{% url 'training_detail' material.pk %}" class="btn btn-sm btn-main-action">
<i class="fas fa-eye"></i> {% trans "View" %}
</a>
{% if user.is_authenticated and material.created_by == user %}
<a href="{% url 'training_update' material.pk %}" class="btn btn-sm btn-outline-secondary">
<i class="fas fa-edit"></i> {% trans "Edit" %}
</a>
<button type="button" class="btn btn-outline-danger btn-sm" title="{% trans 'Delete' %}"
data-bs-toggle="modal" data-bs-target="#deleteModal" {# Assuming standard Bootstrap modal trigger #}
data-delete-url="{% url 'training_delete' material.pk %}"
data-item-name="{{ material.title }}">
<i class="fas fa-trash-alt"></i>
</button>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{# Table View #}
<div class="table-view">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th scope="col" style="width: 40%;">{% trans "Title" %}</th>
<th scope="col" style="width: 30%;">{% trans "Created By" %}</th>
<th scope="col" style="width: 20%;">{% trans "Created" %}</th>
<th scope="col" style="width: 10%;" class="text-end">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for material in materials %}
<tr>
<td><strong class="text-primary">{{ material.title }}</strong></td>
<td>{{ material.created_by.username|default:"Anonymous" }}</td>
<td>{{ material.created_at|date:"M d, Y" }}</td>
<td class="text-end">
<div class="btn-group btn-group-sm" role="group">
<a href="{% url 'training_detail' material.pk %}" class="btn btn-outline-primary" title="{% trans 'View' %}">
<i class="fas fa-eye"></i>
</a>
{% if user.is_authenticated and material.created_by == user %}
<a href="{% url 'training_update' material.pk %}" class="btn btn-outline-secondary" title="{% trans 'Edit' %}">
<i class="fas fa-edit"></i>
</a>
<button type="button" class="btn btn-outline-danger" title="{% trans 'Delete' %}"
data-bs-toggle="modal" data-bs-target="#deleteModal"
data-delete-url="{% url 'training_delete' material.pk %}"
data-item-name="{{ material.title }}">
<i class="fas fa-trash-alt"></i>
</button>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>
{# Pagination (Standardized to Reference) #}
{% if is_paginated %}
<nav aria-label="Page navigation" class="mt-4">
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1{% if search_query %}&q={{ search_query }}{% endif %}">First</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}{% if search_query %}&q={{ search_query }}{% endif %}">Previous</a>
</li>
{% endif %}
{% if materials %} <li class="page-item active">
<div id="training-materials-list"> <span class="page-link">{{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
{# View Switcher #} </li>
{% include "includes/_list_view_switcher.html" with list_id="training-materials-list" %}
{# Table View (Default) #} {% if page_obj.has_next %}
<div class="table-view active"> <li class="page-item">
<div class="table-responsive"> <a class="page-link" href="?page={{ page_obj.next_page_number }}{% if search_query %}&q={{ search_query }}{% endif %}">Next</a>
<table class="table table-hover align-middle mb-0"> </li>
<thead> <li class="page-item">
<tr> <a class="page-link" href="?page={{ page_obj.paginator.num_pages }}{% if search_query %}&q={{ search_query }}{% endif %}">Last</a>
<th scope="col">{% trans "Title" %}</th> </li>
<th scope="col">{% trans "Created By" %}</th> {% endif %}
<th scope="col">{% trans "Created" %}</th> </ul>
<th scope="col" class="text-center">{% trans "Actions" %}</th> </nav>
</tr> {% endif %}
</thead> {% else %}
<tbody> <div class="text-center py-5 card shadow-sm">
{% for material in materials %} <div class="card-body">
<tr> <i class="fas fa-graduation-cap fa-3x mb-3" style="color: var(--kaauh-teal-dark);"></i>
<td><strong class="text-primary">{{ material.title }}</strong></td> <h3>{% trans "No training materials found" %}</h3>
<td>{{ material.created_by.username|default:"Anonymous" }}</td> <p class="text-muted">{% trans "It looks like there are no materials yet. Start by adding one!" %}</p>
<td>{{ material.created_at|date:"M d, Y" }}</td>
<td class="text-center">
<div class="btn-group btn-group-sm" role="group">
<a href="{% url 'training_detail' material.pk %}" class="btn btn-outline-primary" title="{% trans 'View' %}">
<i class="fas fa-eye"></i>
</a>
{% if user.is_authenticated and material.created_by == user %}
<a href="{% url 'training_update' material.pk %}" class="btn btn-outline-secondary" title="{% trans 'Edit' %}">
<i class="fas fa-edit"></i>
</a>
<button type="button" class="btn btn-outline-danger" title="{% trans 'Delete' %}"
data-bs-toggle="deleteModal"
data-delete-url="{% url 'training_delete' material.pk %}"
data-item-name="{{ material.title }}">
<i class="fas fa-trash-alt"></i>
</button>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{# Card View #}
<div class="card-view">
<div class="row g-4">
{% for material in materials %}
<div class="col-md-6 col-lg-4">
<div class="card h-100">
<div class="card-header">
<h5 class="h5 mb-1">{{ material.title }}</h5>
<small class="text-white-50">{{ material.created_by.username|default:"Anonymous" }}</small>
</div>
<div class="card-body">
<p class="card-text">
<strong>{% trans "Created" %}:</strong> {{ material.created_at|date:"M d, Y" }}
</p>
</div>
<div class="card-footer">
<div class="d-flex gap-2">
<a href="{% url 'training_detail' material.pk %}" class="btn btn-sm btn-outline-primary w-100">
<i class="fas fa-eye"></i> {% trans "View" %}
</a>
{% if user.is_authenticated and material.created_by == user %}
<div class="btn-group w-100" role="group">
<a href="{% url 'training_update' material.pk %}" class="btn btn-sm btn-outline-secondary" title="{% trans 'Edit' %}">
<i class="fas fa-edit"></i>
</a>
<button type="button" class="btn btn-outline-danger btn-sm" title="{% trans 'Delete' %}"
data-bs-toggle="deleteModal"
data-delete-url="{% url 'training_delete' material.pk %}"
data-item-name="{{ material.title }}">
<i class="fas fa-trash-alt"></i>
</button>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% if is_paginated %}
<div class="card-footer bg-light border-top">
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center mb-0">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
{% endif %}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="page-item active"><span class="page-link">{{ num }}</span></li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="page-item">
<a class="page-link" href="?page={{ num }}">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% endif %}
{% else %}
<div class="text-center py-5">
<i class="fas fa-book-open fa-3x text-muted mb-3"></i>
<h5 class="mb-3">{% trans "No training materials found." %}</h5>
<p class="text-muted mb-4">{% trans "It looks like there are no materials yet. Start by adding one!" %}</p>
{% if user.is_authenticated %} {% if user.is_authenticated %}
<a href="{% url 'training_create' %}" class="btn btn-main-action"> <a href="{% url 'training_create' %}" class="btn btn-main-action mt-3">
<i class="fas fa-plus me-1"></i> <i class="fas fa-plus me-1"></i> {% trans "Create Your First Material" %}
{% trans "Create Your First Material" %}
</a> </a>
{% endif %} {% endif %}
</div> </div>
{% endif %} </div>
</div> {% endif %}
</div> </div>
{% endblock %} {% endblock %}