candidate and job timeline and ui fix #13
Binary file not shown.
Binary file not shown.
@ -1011,6 +1011,7 @@ def submit_form(request, template_id):
|
||||
)
|
||||
submission.applicant_email = email.display_value
|
||||
submission.save()
|
||||
time=timezone.now()
|
||||
Candidate.objects.create(
|
||||
first_name=first_name.display_value,
|
||||
last_name=last_name.display_value,
|
||||
@ -1019,6 +1020,7 @@ def submit_form(request, template_id):
|
||||
address=address.display_value,
|
||||
resume=resume.get_file if resume.is_file else None,
|
||||
job=submission.template.job,
|
||||
|
||||
)
|
||||
return redirect('application_success')
|
||||
|
||||
|
||||
@ -22,7 +22,18 @@
|
||||
--stage-interview: #ffc107; /* Warning Yellow */
|
||||
--stage-offer: #28a745; /* Success Green */
|
||||
--stage-inactive: #6c757d; /* Secondary Gray */
|
||||
}
|
||||
|
||||
--kaauh-teal: #00636e; /* Primary Theme / Active Stage */
|
||||
--kaauh-teal-light: #4bb3be; /* For active glow */
|
||||
--color-created: #6c757d; /* Muted Initial State */
|
||||
--color-active: #00636e; /* Teal for Active Flow */
|
||||
--color-posted: #17a2b8; /* Info Blue for External Posting */
|
||||
--color-closed: #ffc107; /* Warning Yellow for Soft End/Review */
|
||||
--color-archived: #343a40; /* Darkest for Final Storage */
|
||||
--color-canceled: #dc3545; /* Red for Negative/Canceled */
|
||||
--color-line-default: #e9ecef; /* Light Gray for all inactive markers */
|
||||
}
|
||||
|
||||
|
||||
/* Primary Color Overrides */
|
||||
.text-primary { color: var(--kaauh-teal) !important; }
|
||||
@ -160,6 +171,132 @@
|
||||
border: 1px solid;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.job-timeline-container {
|
||||
padding: 10px 0;
|
||||
overflow-x: auto;
|
||||
margin: 10px 0;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.job-timeline {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
list-style: none;
|
||||
padding: 0 10px;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
min-width: 1000px;
|
||||
}
|
||||
|
||||
/* 🛑 NO CONNECTING LINE: Removed .job-timeline::before and .timeline-stage.is-complete + .timeline-stage::before */
|
||||
|
||||
/* ------------------ Stage & Marker Basics ------------------ */
|
||||
|
||||
.timeline-stage {
|
||||
flex: 1 1 auto;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.status-marker {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
|
||||
/* DEFAULT (Inactive/Not Current Status) */
|
||||
background-color: white;
|
||||
border: 3px solid var(--color-line-default);
|
||||
color: var(--color-line-default);
|
||||
|
||||
font-size: 18px;
|
||||
line-height: 34px;
|
||||
z-index: 3;
|
||||
transition: all 0.3s ease, box-shadow 0.3s ease;
|
||||
box-shadow: 0 0 0 2px white;
|
||||
}
|
||||
|
||||
/* ------------------ ACTIVE/CURRENT Status Indicator ------------------ */
|
||||
|
||||
/* The 'is-active' class now completely defines the current status color and highlight */
|
||||
|
||||
.timeline-stage.is-active .status-marker {
|
||||
transform: translateX(-50%) scale(1.15); /* Keep the pop effect */
|
||||
color: white; /* Default text color for all active markers */
|
||||
}
|
||||
|
||||
/* 1. CREATED (Active) */
|
||||
.timeline-stage[data-stage="created"].is-active .status-marker {
|
||||
background-color: var(--color-created);
|
||||
border-color: var(--color-created);
|
||||
box-shadow: 0 0 0 4px rgba(108, 117, 125, 0.5);
|
||||
}
|
||||
|
||||
/* 2. MADE ACTIVE (Active) */
|
||||
.timeline-stage[data-stage="active"].is-active .status-marker {
|
||||
background-color: var(--color-active);
|
||||
border-color: var(--color-active);
|
||||
box-shadow: 0 0 0 4px var(--kaauh-teal-light);
|
||||
}
|
||||
|
||||
/* 3. POSTED TO LINKEDIN (Active) */
|
||||
.timeline-stage[data-stage="posted"].is-active .status-marker {
|
||||
background-color: var(--color-posted);
|
||||
border-color: var(--color-posted);
|
||||
box-shadow: 0 0 0 4px rgba(23, 162, 184, 0.5);
|
||||
}
|
||||
|
||||
/* 4. CANCELED (Active/Terminal) */
|
||||
.timeline-stage[data-stage="canceled"].is-active .status-marker {
|
||||
background-color: var(--color-canceled);
|
||||
border-color: var(--color-canceled);
|
||||
box-shadow: 0 0 0 4px rgba(220, 53, 69, 0.5);
|
||||
}
|
||||
|
||||
/* 5. CLOSED (Active/Terminal) */
|
||||
.timeline-stage[data-stage="closed"].is-active .status-marker {
|
||||
background-color: var(--color-closed);
|
||||
border-color: var(--color-closed);
|
||||
color: #343a40; /* Dark icon for contrast on yellow */
|
||||
box-shadow: 0 0 0 4px rgba(255, 193, 7, 0.5);
|
||||
}
|
||||
|
||||
/* 6. ARCHIVED (Active/Terminal) */
|
||||
.timeline-stage[data-stage="archived"].is-active .status-marker {
|
||||
background-color: var(--color-archived);
|
||||
border-color: var(--color-archived);
|
||||
box-shadow: 0 0 0 4px rgba(52, 58, 64, 0.5);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------ Text Labels ------------------ */
|
||||
|
||||
.status-label {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: #343a40;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* Highlight text label for the active status */
|
||||
.timeline-stage.is-active .status-label {
|
||||
color: var(--kaauh-teal);
|
||||
}
|
||||
|
||||
.status-date {
|
||||
font-size: 0.75rem;
|
||||
color: #6c757d;
|
||||
}
|
||||
</style>
|
||||
|
||||
{% endblock %}
|
||||
@ -168,8 +305,50 @@
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="container-fluid py-4">
|
||||
<div class="job-timeline-container shadow-sm">
|
||||
<div class="card-body">
|
||||
<ul class="job-timeline">
|
||||
|
||||
<li class="timeline-stage" data-stage="created">
|
||||
<div class="status-marker"><i class="fas fa-hammer"></i></div>
|
||||
<div class="status-label">Created</div>
|
||||
<div class="status-date">Jan 1</div>
|
||||
</li>
|
||||
|
||||
<li class="timeline-stage" data-stage="active">
|
||||
<div class="status-marker"><i class="fas fa-play-circle"></i></div>
|
||||
<div class="status-label">Made Active</div>
|
||||
<div class="status-date">Jan 5</div>
|
||||
</li>
|
||||
|
||||
<li class="timeline-stage" data-stage="posted">
|
||||
<div class="status-marker"><i class="fab fa-linkedin"></i></div>
|
||||
<div class="status-label">Posted to LinkedIn</div>
|
||||
<div class="status-date">Jan 6</div>
|
||||
</li>
|
||||
|
||||
<li class="timeline-stage is-active" data-stage="canceled">
|
||||
<div class="status-marker"><i class="fas fa-times-circle"></i></div>
|
||||
<div class="status-label">Canceled</div>
|
||||
<div class="status-date">Jan 15</div>
|
||||
</li>
|
||||
|
||||
<li class="timeline-stage" data-stage="closed">
|
||||
<div class="status-marker"><i class="fas fa-lock"></i></div>
|
||||
<div class="status-label">Closed</div>
|
||||
<div class="status-date"></div>
|
||||
</li>
|
||||
|
||||
<li class="timeline-stage" data-stage="archived">
|
||||
<div class="status-marker"><i class="fas fa-archive"></i></div>
|
||||
<div class="status-label">Archived</div>
|
||||
<div class="status-date"></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{% url 'dashboard' %}" class="text-secondary">Home</a></li>
|
||||
|
||||
@ -290,7 +290,7 @@
|
||||
{% for job in jobs %}
|
||||
<tr>
|
||||
<td class="fw-medium text-primary-theme">
|
||||
{{ job.title }}
|
||||
<a href="{% url 'job_detail' job.slug %}" class="text-decoration-none">{{ job.title }}</a>
|
||||
<br>
|
||||
<small class="text-muted">{{ job.pk }} / </small>
|
||||
<span class="badge bg-{{ job.status }} status-badge">{{ job.status }}</span>
|
||||
@ -345,7 +345,7 @@
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<h5 class="card-title mb-0">{{ job.title }}</h5>
|
||||
<h5 class="card-title mb-0"><a href="{% url 'job_detail' job.slug %}" class="text-decoration-none text-primary-theme">{{ job.title }}</a></h5>
|
||||
<span class="badge bg-{{ job.status }} status-badge">{{ job.status }}</span>
|
||||
</div>
|
||||
<p class="text-muted small mb-3">ID: {{ job.pk }} | Source: {{ job.get_source }}</p>
|
||||
|
||||
@ -1,306 +1,476 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% extends 'base.html' %}
|
||||
{% load static i18n %}
|
||||
|
||||
{% block title %}{% trans "Meeting Details" %} - {{ block.super }}{% endblock %}
|
||||
|
||||
{% block customCSS %}
|
||||
<style>
|
||||
/* UI Variables for the KAAT-S Theme (Consistent with list_meetings.html) */
|
||||
:root {
|
||||
--kaauh-teal: #00636e;
|
||||
--kaauh-teal-dark: #004a53;
|
||||
--kaauh-border: #eaeff3;
|
||||
--kaauh-primary-text: #343a40;
|
||||
--kaauh-gray-light: #f8f9fa;
|
||||
}
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* KAAT-S Redesign CSS */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/* Main Container and Card Styling */
|
||||
.container {
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
.card {
|
||||
border: 1px solid var(--kaauh-border);
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
|
||||
background-color: white;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.card:not(.no-hover):hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(0,0,0,0.1);
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
.card.no-hover:hover {
|
||||
transform: none;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.06);
|
||||
}
|
||||
:root {
|
||||
--kaauh-teal: #00636e; /* Primary Brand Teal */
|
||||
--kaauh-teal-dark: #004a53; /* Darker Teal for Text/Hover */
|
||||
--kaauh-teal-light: #e0f7f9; /* Lightest Teal for background accents */
|
||||
--kaauh-border: #e9ecef; /* Soft Border Gray */
|
||||
--kaauh-primary-text: #212529; /* Dark Text */
|
||||
--kaauh-secondary-text: #6c757d;/* Muted Text */
|
||||
--kaauh-gray-light: #f8f9fa; /* Card Header/Footer Background */
|
||||
--kaauh-success: #198754; /* Success Green */
|
||||
--kaauh-danger: #dc3545; /* Danger Red */
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #f0f2f5; /* Off-white page background */
|
||||
font-family: 'Inter', sans-serif; /* Use a modern font stack */
|
||||
}
|
||||
|
||||
/* Header Card Styling */
|
||||
.card-header {
|
||||
background-color: var(--kaauh-gray-light);
|
||||
border-bottom: 1px solid var(--kaauh-border);
|
||||
padding: 1.5rem;
|
||||
border-radius: 0.75rem 0.75rem 0 0; /* Match top border radius of card */
|
||||
}
|
||||
.card-header h1 {
|
||||
color: var(--kaauh-teal-dark);
|
||||
font-weight: 700;
|
||||
margin: 0 0 0.5rem 0; /* Space below title */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.card-header .heroicon {
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
color: var(--kaauh-teal);
|
||||
}
|
||||
.card-header .btn-secondary {
|
||||
background-color: var(--kaauh-teal);
|
||||
border-color: var(--kaauh-teal);
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.card-header .btn-secondary:hover {
|
||||
background-color: var(--kaauh-teal-dark);
|
||||
border-color: var(--kaauh-teal-dark);
|
||||
color: white;
|
||||
}
|
||||
/* ------------------ General Layout & Card Styles ------------------ */
|
||||
|
||||
/* Status Badge Styling (from list_meetings.html) */
|
||||
.status-badge {
|
||||
font-size: 0.8rem;
|
||||
padding: 0.4em 0.8em;
|
||||
border-radius: 0.4rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.7px;
|
||||
}
|
||||
.bg-waiting { background-color: #ffc107 !important; color: var(--kaauh-primary-text) !important;}
|
||||
.bg-started { background-color: var(--kaauh-teal) !important; color: white !important;}
|
||||
.bg-ended { background-color: #dc3545 !important; color: white !important;}
|
||||
.container {
|
||||
width:auto;
|
||||
padding: 3rem 1.5rem;
|
||||
}
|
||||
.card {
|
||||
border: none; /* Remove default border */
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.08), 0 4px 10px rgba(0,0,0,0.05); /* Deep, soft shadow */
|
||||
background-color: white;
|
||||
margin-bottom: 2.5rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.card:not(.no-hover):hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 15px 40px rgba(0,0,0,0.1), 0 6px 15px rgba(0,0,0,0.08);
|
||||
}
|
||||
.card.no-hover:hover {
|
||||
transform: none;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.08), 0 4px 10px rgba(0,0,0,0.05);
|
||||
}
|
||||
.card-body {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
/* ------------------ Header & Title Styles ------------------ */
|
||||
|
||||
/* Detail Row Styling */
|
||||
.detail-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-bottom: 1px solid var(--kaauh-border);
|
||||
}
|
||||
.detail-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.detail-label {
|
||||
font-weight: 600;
|
||||
color: var(--kaauh-teal-dark);
|
||||
min-width: 40%; /* Ensure labels align */
|
||||
}
|
||||
.detail-value {
|
||||
text-align: right;
|
||||
color: var(--kaauh-primary-text);
|
||||
word-wrap: break-word; /* Long URLs or text wrap */
|
||||
max-width: 60%; /* Ensure values align */
|
||||
}
|
||||
.card-header {
|
||||
background-color: var(--kaauh-gray-light);
|
||||
border-bottom: 1px solid var(--kaauh-border);
|
||||
padding: 2rem;
|
||||
border-radius: 12px 12px 0 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start; /* Align title group to the top */
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.card-header-title-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.card-header h1 {
|
||||
color: var(--kaauh-teal-dark);
|
||||
font-weight: 800; /* Extra bold for prominence */
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
.card-header .heroicon {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
color: var(--kaauh-teal);
|
||||
}
|
||||
.card-header .btn-secondary-back {
|
||||
/* Subtle Back Button */
|
||||
align-self: flex-start;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: var(--kaauh-secondary-text);
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.card-header .btn-secondary-back:hover {
|
||||
color: var(--kaauh-teal);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Card Title Styling within content cards */
|
||||
.card h2 {
|
||||
color: var(--kaauh-teal-dark);
|
||||
font-weight: 600;
|
||||
padding: 1.5rem 1.5rem 0.75rem;
|
||||
margin: 0;
|
||||
border-bottom: 1px solid var(--kaauh-border);
|
||||
}
|
||||
/* ------------------ Status Badge Styles ------------------ */
|
||||
|
||||
/* Join URL Styling */
|
||||
.join-url-display {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid var(--kaauh-border);
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.75rem 1rem;
|
||||
margin-top: 0.75rem;
|
||||
word-break: break-all; /* Force long URLs to break */
|
||||
}
|
||||
.status-badge {
|
||||
font-size: 0.85rem;
|
||||
padding: 0.5em 1em;
|
||||
border-radius: 20px; /* Pill shape */
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
.bg-waiting { background-color: #ffc107 !important; color: var(--kaauh-primary-text) !important;}
|
||||
.bg-started { background-color: var(--kaauh-teal) !important; color: white !important;}
|
||||
.bg-ended { background-color: var(--kaauh-danger) !important; color: white !important;}
|
||||
|
||||
/* Actions Styling */
|
||||
.actions {
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem; /* Space between buttons */
|
||||
justify-content: flex-start; /* Align buttons to the left */
|
||||
}
|
||||
.btn-primary {
|
||||
background-color: var(--kaauh-teal);
|
||||
border-color: var(--kaauh-teal);
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background-color: var(--kaauh-teal-dark);
|
||||
border-color: var(--kaauh-teal-dark);
|
||||
color: white;
|
||||
}
|
||||
.btn-danger {
|
||||
background-color: #dc3545;
|
||||
border-color: #dc3545;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.btn-danger:hover {
|
||||
background-color: #c82333;
|
||||
border-color: #bd2130;
|
||||
color: white;
|
||||
}
|
||||
.btn-secondary {
|
||||
background-color: #6c757d;
|
||||
border-color: #6c757d;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background-color: #5a6268;
|
||||
border-color: #545b62;
|
||||
color: white;
|
||||
}
|
||||
/* ------------------ Detail Row & Content Styles ------------------ */
|
||||
|
||||
/* API Response Styling */
|
||||
#gateway-response {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
#gateway-response .card {
|
||||
border-left: 4px solid var(--kaauh-teal); /* Indicate it's different content */
|
||||
}
|
||||
#gateway-response pre {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid var(--kaauh-border);
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
margin-top: 0.75rem;
|
||||
overflow-x: auto; /* For long JSON lines */
|
||||
font-size: 0.9rem;
|
||||
color: var(--kaauh-primary-text);
|
||||
}
|
||||
.card h2 {
|
||||
color: var(--kaauh-teal-dark);
|
||||
font-weight: 700;
|
||||
padding: 1.5rem 2rem 1rem;
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
border-bottom: 1px solid var(--kaauh-border);
|
||||
}
|
||||
|
||||
.detail-row-group {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(150px, 40%) 1fr;
|
||||
padding: 1rem 2rem;
|
||||
border-bottom: 1px solid var(--kaauh-border);
|
||||
align-items: center;
|
||||
}
|
||||
.detail-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.detail-label {
|
||||
font-weight: 600;
|
||||
color: var(--kaauh-teal-dark);
|
||||
text-align: left;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.detail-value {
|
||||
text-align: right;
|
||||
color: var(--kaauh-primary-text);
|
||||
word-wrap: break-word;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ------------------ Join Info & Copy Button ------------------ */
|
||||
|
||||
.join-info-card .card-body {
|
||||
padding-top: 2rem;
|
||||
}
|
||||
.btn-primary {
|
||||
background-color: var(--kaauh-teal);
|
||||
border-color: var(--kaauh-teal);
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background-color: var(--kaauh-teal-dark);
|
||||
border-color: var(--kaauh-teal-dark);
|
||||
box-shadow: 0 4px 8px rgba(0, 99, 110, 0.3);
|
||||
}
|
||||
|
||||
.join-url-container {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
margin-top: 1.5rem;
|
||||
position: relative;
|
||||
padding: 1rem 0; /* Add padding for clear space around the copy area */
|
||||
}
|
||||
.join-url-display {
|
||||
flex-grow: 1;
|
||||
background-color: var(--kaauh-gray-light);
|
||||
border: 1px solid var(--kaauh-border);
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem 1rem;
|
||||
word-break: break-all;
|
||||
font-size: 0.9rem;
|
||||
color: var(--kaauh-secondary-text);
|
||||
font-family: monospace; /* Monospace for links/code */
|
||||
}
|
||||
.join-url-display strong {
|
||||
color: var(--kaauh-teal-dark);
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
.btn-copy {
|
||||
flex-shrink: 0;
|
||||
background-color: var(--kaauh-teal-dark); /* Darker teal for a clean utility look */
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 0.75rem 1rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.btn-copy:hover {
|
||||
background-color: var(--kaauh-teal);
|
||||
}
|
||||
.btn-copy i {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
/* 🎯 Copy Message Pill Style */
|
||||
#copy-message {
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
right: 0;
|
||||
background-color: var(--kaauh-success);
|
||||
color: white;
|
||||
padding: 0.2rem 0.6rem;
|
||||
border-radius: 20px; /* Pill shape */
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s ease-in-out;
|
||||
z-index: 10;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* ------------------ Footer & Actions ------------------ */
|
||||
|
||||
.card-footer {
|
||||
border-top: 1px solid var(--kaauh-border);
|
||||
padding: 1.5rem 2rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
justify-content: flex-start;
|
||||
background-color: var(--kaauh-gray-light);
|
||||
border-radius: 0 0 12px 12px;
|
||||
}
|
||||
.btn-danger {
|
||||
background-color: var(--kaauh-danger);
|
||||
border-color: var(--kaauh-danger);
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.btn-danger:hover {
|
||||
background-color: #c82333;
|
||||
border-color: #bd2130;
|
||||
box-shadow: 0 4px 8px rgba(220, 53, 69, 0.3);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background-color: #6c757d;
|
||||
border-color: #6c757d;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background-color: #5a6268;
|
||||
border-color: #545b62;
|
||||
}
|
||||
|
||||
/* ------------------ API Response Styling ------------------ */
|
||||
#gateway-response-card {
|
||||
border-left: 5px solid var(--kaauh-teal); /* Prominent left border */
|
||||
}
|
||||
#gateway-response-card .card-body {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
#gateway-response-card h3 {
|
||||
color: var(--kaauh-teal-dark);
|
||||
font-weight: 700;
|
||||
font-size: 1.35rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
#gateway-response-card pre {
|
||||
background-color: #fff;
|
||||
border: 1px solid var(--kaauh-border);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
font-size: 0.8rem;
|
||||
color: var(--kaauh-primary-text);
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="container py-4">
|
||||
<div class="container">
|
||||
|
||||
<div class="card no-hover">
|
||||
<div class="card-header">
|
||||
<h1>
|
||||
<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>
|
||||
{{ meeting.topic }}
|
||||
</h1>
|
||||
<span class="status-badge bg-{{ meeting.status }}">
|
||||
{{ meeting.status|title }}
|
||||
</span>
|
||||
<a href="{% url 'list_meetings' %}" class="btn btn-secondary mt-3">{% trans "Back to Meetings" %}</a>
|
||||
<div class="card-header-title-group">
|
||||
<h1>
|
||||
<svg class="heroicon" viewBox="0 0 24 24" fill="none" xmlns="[http://www.w3.org/2000/svg](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>
|
||||
{{ meeting.topic }}
|
||||
</h1>
|
||||
<span class="status-badge bg-{{ meeting.status }}">
|
||||
{{ meeting.status|title }}
|
||||
</span>
|
||||
</div>
|
||||
<a href="{% url 'list_meetings' %}" class="btn btn-secondary-back">
|
||||
<i class="fas fa-arrow-left"></i> {% trans "Back to Meetings" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="card no-hover">
|
||||
<h2>{% trans "Meeting Information" %}</h2>
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">{% trans "Meeting ID" %}:</div>
|
||||
<div class="detail-value">{{ meeting.meeting_id }}</div>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">{% trans "Topic" %}:</div>
|
||||
<div class="detail-value">{{ meeting.topic }}</div>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<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="detail-row">
|
||||
<div class="detail-label">{% trans "Duration" %}:</div>
|
||||
<div class="detail-value">{{ meeting.duration }} {% trans "minutes" %}</div>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">{% trans "Timezone" %}:</div>
|
||||
<div class="detail-value">{{ meeting.timezone|default:"UTC" }}</div>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">{% trans "Host Email" %}:</div>
|
||||
<div class="detail-value">{{ meeting.host_email|default:"N/A" }}</div>
|
||||
<div class="card-body detail-row-group">
|
||||
<div class="detail-row"><div class="detail-label">{% trans "Meeting ID" %}:</div><div class="detail-value">{{ meeting.meeting_id }}</div></div>
|
||||
<div class="detail-row"><div class="detail-label">{% trans "Topic" %}:</div><div class="detail-value">{{ meeting.topic }}</div></div>
|
||||
<div class="detail-row"><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="detail-row"><div class="detail-label">{% trans "Duration" %}:</div><div class="detail-value">{{ meeting.duration }} {% trans "minutes" %}</div></div>
|
||||
<div class="detail-row"><div class="detail-label">{% trans "Timezone" %}:</div><div class="detail-value">{{ meeting.timezone|default:"UTC" }}</div></div>
|
||||
<div class="detail-row"><div class="detail-label">{% trans "Host Email" %}:</div><div class="detail-value">{{ meeting.host_email|default:"N/A" }}</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if meeting.join_url %}
|
||||
<div class="card no-hover">
|
||||
<div class="card no-hover join-info-card">
|
||||
<h2>{% trans "Join Information" %}</h2>
|
||||
<a href="{{ meeting.join_url }}" class="btn btn-primary" target="_blank">{% trans "Join Meeting" %}</a>
|
||||
{% comment %} <div class="join-url-display">
|
||||
<strong>{% trans "Join URL:" %}</strong> {{ meeting.join_url }}
|
||||
</div> {% endcomment %}
|
||||
{% if meeting.password %}
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">{% trans "Password" %}: {{ meeting.password }}</div>
|
||||
<div class="detail-value"></div>
|
||||
<div class="card-body">
|
||||
<a href="{{ meeting.join_url }}" class="btn btn-primary" target="_blank">
|
||||
<i class="fas fa-video"></i> {% trans "Join Meeting Now" %}
|
||||
</a>
|
||||
|
||||
<div class="join-url-container">
|
||||
<div id="copy-message" style="opacity: 0;">{% trans "Copied!" %}</div>
|
||||
|
||||
<div class="join-url-display" id="join-url-display">
|
||||
<strong>{% trans "Join URL" %}:</strong> <span id="meeting-join-url">{{ meeting.join_url }}</span>
|
||||
</div>
|
||||
|
||||
<button class="btn-copy" onclick="copyLink()">
|
||||
<i class="fas fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if meeting.password %}
|
||||
<div class="detail-row" style="border: none; padding: 1rem 0 0 0;">
|
||||
<div class="detail-label" style="font-size: 1rem;">{% trans "Password" %}:</div>
|
||||
<div class="detail-value" style="font-weight: 700; color: var(--kaauh-danger);">{{ meeting.password }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card no-hover">
|
||||
<h2>{% trans "Settings" %}</h2>
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">{% trans "Participant Video" %}:</div>
|
||||
<div class="detail-value">{{ meeting.participant_video|yesno:"Yes,No" }}</div>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">{% trans "Join Before Host" %}:</div>
|
||||
<div class="detail-value">{{ meeting.join_before_host|yesno:"Yes,No" }}</div>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">{% trans "Mute Upon Entry" %}:</div>
|
||||
<div class="detail-value">{{ meeting.mute_upon_entry|yesno:"Yes,No" }}</div>
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
<div class="detail-label">{% trans "Waiting Room" %}:</div>
|
||||
<div class="detail-value">{{ meeting.waiting_room|yesno:"Yes,No" }}</div>
|
||||
<div class="card-body detail-row-group">
|
||||
<div class="detail-row"><div class="detail-label">{% trans "Participant Video" %}:</div><div class="detail-value">{{ meeting.participant_video|yesno:"Yes,No" }}</div></div>
|
||||
<div class="detail-row"><div class="detail-label">{% trans "Join Before Host" %}:</div><div class="detail-value">{{ meeting.join_before_host|yesno:"Yes,No" }}</div></div>
|
||||
<div class="detail-row"><div class="detail-label">{% trans "Mute Upon Entry" %}:</div><div class="detail-value">{{ meeting.mute_upon_entry|yesno:"Yes,No" }}</div></div>
|
||||
<div class="detail-row"><div class="detail-label">{% trans "Waiting Room" %}:</div><div class="detail-value">{{ meeting.waiting_room|yesno:"Yes,No" }}</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card no-hover">
|
||||
<div class="actions">
|
||||
<div class="card-footer">
|
||||
<a href="{% url 'update_meeting' meeting.slug %}" class="btn btn-primary">
|
||||
<i class="fas fa-edit"></i> {% trans "Update Meeting" %}
|
||||
</a>
|
||||
|
||||
{% if meeting.zoom_gateway_response %}
|
||||
<a href="#" class="btn btn-secondary" onclick="toggleGateway()">{% trans "View API Response" %}</a>
|
||||
<button type="button" class="btn btn-secondary" onclick="toggleGateway()">
|
||||
<i class="fas fa-code"></i> {% trans "View API Response" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
<a href="{% url 'update_meeting' meeting.slug %}" class="btn btn-primary">{% trans "Update Meeting" %}</a>
|
||||
|
||||
<form method="post" action="{% url 'delete_meeting' meeting.pk %}" style="display: inline;">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-danger" onclick="return confirm('{% trans "Are you sure?" %}')">{% trans "Delete Meeting" %}</button>
|
||||
<button type="submit" class="btn btn-danger" onclick="return confirm('{% trans "Are you sure you want to delete this meeting? This action is permanent." %}')">
|
||||
<i class="fas fa-trash-alt"></i> {% trans "Delete Meeting" %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if meeting.zoom_gateway_response %}
|
||||
{% comment %} <div id="gateway-response">
|
||||
<div class="card">
|
||||
<h3>{% trans "Zoom API Response" %}</h3>
|
||||
<div id="gateway-response-card" class="card" style="display: none;">
|
||||
<div class="card-body">
|
||||
<h3>{% trans "API Gateway Response" %}</h3>
|
||||
<pre>{{ meeting.zoom_gateway_response|safe }}</pre>
|
||||
</div>
|
||||
</div> {% endcomment %}
|
||||
<script>
|
||||
function toggleGateway() {
|
||||
const element = document.getElementById('gateway-response');
|
||||
if (element.style.display === 'none' || !element.style.display) {
|
||||
element.style.display = 'block';
|
||||
} else {
|
||||
element.style.display = 'none';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block customJS %}
|
||||
<script>
|
||||
function toggleGateway() {
|
||||
const element = document.getElementById('gateway-response-card');
|
||||
if (element.style.display === 'none' || element.style.display === '') {
|
||||
element.style.display = 'block';
|
||||
} else {
|
||||
element.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function copyLink() {
|
||||
const urlElement = document.getElementById('meeting-join-url');
|
||||
const messageElement = document.getElementById('copy-message');
|
||||
const textToCopy = urlElement.textContent || urlElement.innerText;
|
||||
|
||||
// Clear any existing message
|
||||
clearTimeout(window.copyMessageTimeout);
|
||||
|
||||
// Function to show the message
|
||||
function showMessage(success) {
|
||||
messageElement.textContent = success ? '{% trans "Copied!" %}' : '{% trans "Copy Failed." %}';
|
||||
messageElement.style.backgroundColor = success ? 'var(--kaauh-success)' : 'var(--kaauh-danger)';
|
||||
messageElement.style.opacity = '1';
|
||||
|
||||
// Hide the message after 2 seconds
|
||||
window.copyMessageTimeout = setTimeout(() => {
|
||||
messageElement.style.opacity = '0';
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// Use the modern clipboard API
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
navigator.clipboard.writeText(textToCopy).then(() => {
|
||||
showMessage(true); // Show success message
|
||||
}).catch(err => {
|
||||
console.error('Could not copy text: ', err);
|
||||
fallbackCopyTextToClipboard(textToCopy, showMessage); // Try fallback on failure
|
||||
});
|
||||
} else {
|
||||
// Fallback for older browsers
|
||||
fallbackCopyTextToClipboard(textToCopy, showMessage);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback function for older browsers
|
||||
function fallbackCopyTextToClipboard(text, callback) {
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = text;
|
||||
|
||||
textArea.style.top = "0";
|
||||
textArea.style.left = "0";
|
||||
textArea.style.position = "fixed";
|
||||
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
let success = false;
|
||||
try {
|
||||
success = document.execCommand('copy');
|
||||
} catch (err) {
|
||||
console.error('Fallback: Oops, unable to copy', err);
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
callback(success); // Call the message function with the result
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@ -369,14 +369,53 @@
|
||||
<i class="far fa-clock ms-2 me-1"></i> {{ candidate.created_at|date:"h:i A" }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% if candidate.exam_date %}
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-icon timeline-bg-applied"><i class="fas fa-clipboard-check"></i></div>
|
||||
<div class="timeline-content">
|
||||
<p class="timeline-stage fw-bold mb-0 ms-2">{% trans "Exam" %}</p>
|
||||
<small class="text-muted">
|
||||
<i class="far fa-calendar-alt me-1"></i> {{ candidate.exam_date|date:"M d, Y" }}
|
||||
<span class="ms-2">|</span>
|
||||
<i class="far fa-clock ms-2 me-1"></i> {{ candidate.exam_date|date:"h:i A" }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{# Fallback if no history is explicitly logged #}
|
||||
{% if not candidate_stage_history %}
|
||||
<p class="text-muted mt-3 mb-0 ps-3">
|
||||
{% trans "Detailed stage history logs are currently unavailable." %}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if candidate.interview_date %}
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-icon timeline-bg-applied"><i class="fas fas fa-comments"></i></div>
|
||||
<div class="timeline-content">
|
||||
<p class="timeline-stage fw-bold mb-0 ms-2">{% trans "Interview" %}</p>
|
||||
<small class="text-muted">
|
||||
<i class="far fa-calendar-alt me-1"></i> {{ candidate.interview_date|date:"M d, Y" }}
|
||||
<span class="ms-2">|</span>
|
||||
<i class="far fa-clock ms-2 me-1"></i> {{ candidate.interview_date|date:"h:i A" }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if candidate.offer_date %}
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-icon timeline-bg-applied"><i class="fas fas fa-handshake"></i></div>
|
||||
<div class="timeline-content">
|
||||
<p class="timeline-stage fw-bold mb-0 ms-2">{% trans "Offer" %}</p>
|
||||
<small class="text-muted">
|
||||
<i class="far fa-calendar-alt me-1"></i> {{ candidate.offer_date|date:"M d, Y" }}
|
||||
<span class="ms-2">|</span>
|
||||
<i class="far fa-clock ms-2 me-1"></i> {{ candidate.offer_date|date:"h:i A" }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@ -14,6 +14,13 @@
|
||||
--kaauh-gray-light: #f8f9fa; /* Added for hover/background consistency */
|
||||
}
|
||||
|
||||
/* Primary Color Overrides */
|
||||
.text-primary-theme { color: var(--kaauh-teal) !important; }
|
||||
.bg-primary-theme { background-color: var(--kaauh-teal) !important; }
|
||||
.text-success { color: var(--kaauh-success) !important; }
|
||||
.text-danger { color: var(--kaauh-danger) !important; }
|
||||
.text-info { color: #17a2b8 !important; }
|
||||
|
||||
/* Enhanced Card Styling (Consistent) */
|
||||
.card {
|
||||
border: 1px solid var(--kaauh-border);
|
||||
@ -208,10 +215,10 @@
|
||||
<tbody>
|
||||
{% for candidate in candidates %}
|
||||
<tr>
|
||||
<td class="fw-medium">{{ candidate.name }}</td>
|
||||
<td class="fw-medium"><a href="{% url 'candidate_detail' candidate.slug %}" class="text-decoration-none link-secondary">{{ candidate.name }}<a></td>
|
||||
<td>{{ candidate.email }}</td>
|
||||
<td>{{ candidate.phone }}</td>
|
||||
<td> <span class="badge bg-primary">{{ candidate.job.title }}</span></td>
|
||||
<td> <span class="badge bg-primary"><a href="{% url 'job_detail' candidate.job.slug %}" class="text-decoration-none text-white">{{ candidate.job.title }}</a></span></td>
|
||||
<td>
|
||||
<span class="badge bg-primary">
|
||||
{{ candidate.stage }}
|
||||
@ -250,14 +257,14 @@
|
||||
<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>
|
||||
<h5 class="card-title flex-grow-1 me-3"><a href="{% url 'candidate_detail' candidate.slug %}" class="text-decoration-none text-primary-theme ">{{ candidate.name }}</a></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>
|
||||
<i class="fas fa-briefcase"></i> <span class="badge bg-primary"><a href="{% url 'job_detail' candidate.job.slug %}" class="text-decoration-none text-white">{{ candidate.job.title }}</a></span>
|
||||
</p>
|
||||
|
||||
<div class="mt-auto pt-2 border-top">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user