510 lines
22 KiB
HTML
510 lines
22 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static i18n %}
|
|
{% block customCSS %}
|
|
<style>
|
|
/* -------------------------------------------------------------------------- */
|
|
/* KAAT-S Redesign CSS - Optimized Compact Detail View (Comments Left) */
|
|
/* -------------------------------------------------------------------------- */
|
|
|
|
: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 */
|
|
/* New CRM/ATS Specific Colors */
|
|
--kaauh-link: #007bff; /* Standard Blue Link */
|
|
--kaauh-link-hover: #0056b3;
|
|
--kaauh-accent-bg: #fff3cd; /* Light Yellow for Attention/Context */
|
|
--kaauh-accent-text: #664d03; /* Dark Yellow Text */
|
|
}
|
|
|
|
body {
|
|
background-color: #f0f2f5;
|
|
font-family: 'Inter', sans-serif;
|
|
}
|
|
|
|
/* ------------------ General Layout & Card Styles ------------------ */
|
|
|
|
|
|
.card {
|
|
border: none;
|
|
border-radius: 12px;
|
|
box-shadow: 0 5px 15px rgba(0,0,0,0.05), 0 2px 5px rgba(0,0,0,0.03);
|
|
margin-bottom: 1.5rem;
|
|
transition: all 0.2s ease;
|
|
}
|
|
.card:not(.no-hover):hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 10px 20px rgba(0,0,0,0.08);
|
|
}
|
|
.card-body {
|
|
padding: 1.5rem;
|
|
}
|
|
|
|
/* ------------------ Main Header & Title Styles ------------------ */
|
|
|
|
.main-title-card {
|
|
padding: 1.5rem 2rem;
|
|
background-color: white;
|
|
border-bottom: 3px solid var(--kaauh-teal);
|
|
border-radius: 12px 12px 0 0;
|
|
}
|
|
|
|
.main-title-card h1 {
|
|
color: var(--kaauh-teal-dark);
|
|
font-weight: 800;
|
|
margin: 0;
|
|
font-size: 2rem;
|
|
}
|
|
.main-title-card .heroicon {
|
|
width: 2rem;
|
|
height: 2rem;
|
|
color: var(--kaauh-teal);
|
|
}
|
|
|
|
.status-badge {
|
|
font-size: 0.75rem;
|
|
padding: 0.35em 0.8em;
|
|
border-radius: 15px;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
.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;}
|
|
|
|
/* ------------------ Detail Row & Content Styles ------------------ */
|
|
|
|
.detail-section h2 {
|
|
color: var(--kaauh-teal-dark);
|
|
font-weight: 700;
|
|
font-size: 1.25rem;
|
|
margin-bottom: 1rem;
|
|
border-bottom: 2px solid var(--kaauh-teal-light);
|
|
padding-bottom: 0.5rem;
|
|
}
|
|
|
|
.detail-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 0.75rem 0;
|
|
border-bottom: 1px dashed var(--kaauh-border);
|
|
align-items: center;
|
|
}
|
|
.detail-row:last-child {
|
|
border-bottom: none;
|
|
}
|
|
.detail-label {
|
|
font-weight: 600;
|
|
color: var(--kaauh-teal);
|
|
font-size: 0.9rem;
|
|
flex-basis: 45%;
|
|
}
|
|
.detail-value {
|
|
color: var(--kaauh-primary-text);
|
|
word-wrap: break-word;
|
|
font-weight: 500;
|
|
text-align: right;
|
|
font-size: 0.9rem;
|
|
flex-basis: 55%;
|
|
}
|
|
|
|
/* --- CRM ASSOCIATED RECORD STYLING --- */
|
|
.associated-record-card {
|
|
background-color: var(--kaauh-accent-bg);
|
|
color: var(--kaauh-accent-text);
|
|
border: 1px solid var(--kaauh-accent-text);
|
|
}
|
|
.associated-record-card a {
|
|
color: var(--kaauh-link);
|
|
font-weight: 700;
|
|
}
|
|
.associated-record-card a:hover {
|
|
color: var(--kaauh-link-hover);
|
|
}
|
|
|
|
|
|
/* ------------------ Join Info & Copy Button ------------------ */
|
|
|
|
.join-info-card {
|
|
border-left: 5px solid var(--kaauh-teal); /* Highlight join info */
|
|
}
|
|
/* Consolidated primary button style */
|
|
.btn-primary {
|
|
background-color: var(--kaauh-teal);
|
|
border-color: var(--kaauh-teal);
|
|
font-weight: 600;
|
|
padding: 0.6rem 1.25rem;
|
|
border-radius: 6px;
|
|
transition: background-color 0.2s;
|
|
}
|
|
.btn-primary:hover {
|
|
background-color: var(--kaauh-teal-dark);
|
|
border-color: var(--kaauh-teal-dark);
|
|
}
|
|
|
|
.join-url-container {
|
|
margin-top: 1rem;
|
|
}
|
|
.join-url-display {
|
|
background-color: var(--kaauh-gray-light);
|
|
border: 1px solid var(--kaauh-border);
|
|
padding: 0.5rem 0.75rem;
|
|
font-size: 0.85rem;
|
|
}
|
|
.btn-copy {
|
|
padding: 0.5rem 0.75rem;
|
|
background-color: var(--kaauh-teal-dark);
|
|
border: none;
|
|
color: white; /* Ensure copy button icon is white */
|
|
}
|
|
.btn-copy:hover {
|
|
background-color: var(--kaauh-teal);
|
|
}
|
|
|
|
/* ------------------ Footer & Actions ------------------ */
|
|
|
|
.action-bar-footer {
|
|
border-top: 1px solid var(--kaauh-border);
|
|
padding: 1rem 1.5rem;
|
|
background-color: var(--kaauh-gray-light);
|
|
border-radius: 0 0 12px 12px;
|
|
/* Explicitly use flex for layout control */
|
|
display: flex;
|
|
justify-content: space-between; /* Separate the left/right groups */
|
|
align-items: center;
|
|
}
|
|
.btn-footer-action {
|
|
font-weight: 600;
|
|
/* Made buttons smaller and consistent */
|
|
padding: 0.4rem 0.8rem;
|
|
border-radius: 6px;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
/* --- Comment Card Header Style --- */
|
|
#comments-card .card-header {
|
|
background-color: white;
|
|
color: var(--kaauh-teal-dark);
|
|
padding: 1rem 1.5rem;
|
|
font-weight: 600;
|
|
border-radius: 12px 12px 0 0;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
|
|
{% block content %}
|
|
<div class="container-fluid py-4">
|
|
|
|
{# --- TOP BAR / BACK BUTTON --- #}
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<a href="{% url 'list_meetings' %}" class="btn btn-secondary-back">
|
|
<i class="fas fa-arrow-left me-1"></i> {% trans "Back to Meetings" %}
|
|
</a>
|
|
</div>
|
|
|
|
<div class="row g-4">
|
|
|
|
{# --- LEFT COLUMN (COMMENTS & INTERNAL CONTEXT) - Takes 50% of the screen #}
|
|
<div class="col-lg-6 d-flex flex-column">
|
|
|
|
{# --- 1. INTERNAL NOTES / DESCRIPTION CARD (New CRM Feature) --- #}
|
|
{% if meeting.description %}
|
|
<div class="card no-hover mb-4 flex-shrink-0">
|
|
<div class="card-body detail-section">
|
|
<h2 class="d-flex align-items-center"><i class="fas fa-clipboard-list me-2"></i> {% trans "Internal Context" %}</h2>
|
|
<p class="text-muted small">{% trans "Meeting agenda, purpose, or interview details for internal team use." %}</p>
|
|
<div class="p-3 bg-light rounded border">
|
|
<p class="mb-0">{{ meeting.description|safe }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{# --- 2. Comments Section (Now in the Left Column) --- #}
|
|
<div class="card no-hover flex-grow-1" id="comments-card">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="card-title mb-0" style="color: var(--kaauh-teal-dark);">
|
|
<i class="fas fa-comments me-2"></i>
|
|
{% trans "Comments" %} ({{ meeting.comments.count }})
|
|
</h5>
|
|
{% if user.is_authenticated %}
|
|
<button type="button" class="btn btn-primary btn-sm"
|
|
hx-get="{% url 'add_meeting_comment' meeting.slug %}"
|
|
hx-target="#comment-section"
|
|
>
|
|
<i class="fas fa-plus me-1"></i> {% trans "Add Comment" %}
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
<div class="card-body overflow-auto">
|
|
<div id="comment-section">
|
|
{% if meeting.comments.all %}
|
|
{% for comment in meeting.comments.all|dictsortreversed:"created_at" %}
|
|
<div class="card mb-3">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<strong class="me-2">{{ comment.author.get_full_name|default:comment.author.username }}</strong>
|
|
{% if comment.author != user %}
|
|
<span class="badge bg-secondary ms-1">{% trans "Comment" %}</span>
|
|
{% endif %}
|
|
</div>
|
|
<small class="text-muted">{{ comment.created_at|date:"M d, Y P" }}</small>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="card-text">{{ comment.content|safe }}</p>
|
|
</div>
|
|
<div class="card-footer">
|
|
{% if comment.author == user or user.is_staff %}
|
|
<div class="btn-group btn-group-sm">
|
|
<button type="button" class="btn btn-outline-primary"
|
|
hx-get="{% url 'edit_meeting_comment' meeting.slug comment.id %}"
|
|
hx-target="#comment-section"
|
|
title="{% trans 'Edit Comment' %}">
|
|
<i class="fas fa-edit"></i>
|
|
</button>
|
|
<button type="button" class="btn btn-outline-danger"
|
|
hx-get="{% url 'delete_meeting_comment' meeting.slug comment.id %}"
|
|
hx-target="#comment-section"
|
|
title="{% trans 'Delete Comment' %}">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<p class="text-muted">{% trans "No comments yet. Be the first to comment!" %}</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# --- RIGHT COLUMN (MAIN DETAILS & JOIN INFO) - Takes 50% of the screen #}
|
|
<div class="col-lg-6">
|
|
<div class="d-flex flex-column h-100">
|
|
|
|
{# --- CRM ASSOCIATED RECORD CARD (Elevated Importance) --- #}
|
|
{% if meeting.interview %}
|
|
<div class="card associated-record-card mb-4 flex-shrink-0">
|
|
<div class="card-body pt-3 pb-3">
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-user-tag fa-2x me-3"></i>
|
|
<div>
|
|
<h6 class="mb-0 text-uppercase small fw-bold">{% trans "Associated Record" %}</h6>
|
|
<span class="fw-bold fs-5 me-2">
|
|
<a href="{% url 'candidate_detail' meeting.interview.candidate.slug %}" class="text-decoration-none">
|
|
{{ meeting.interview.candidate.name }}
|
|
</a>
|
|
</span>
|
|
<span class="badge bg-secondary-subtle text-secondary small fw-normal">{{ meeting.interview.job_position }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{# --- 1. MAIN DETAILS CARD --- #}
|
|
<div class="card no-hover flex-grow-1 mb-4">
|
|
{# --- CONSOLIDATED HEADER --- #}
|
|
<div class="main-title-card">
|
|
<div class="d-flex justify-content-between align-items-start">
|
|
<div class="card-header-title-group">
|
|
<h1 class="mb-1">
|
|
<svg class="heroicon me-2" 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>
|
|
<div class="d-flex align-items-center gap-3">
|
|
<span class="status-badge bg-{{ meeting.status }}">
|
|
{{ meeting.status|title }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# --- CONNECTION DETAIL BODY (Renamed from Core Details) --- #}
|
|
<div class="card-body detail-section">
|
|
<h2><i class="fas fa-calendar-alt me-2"></i> {% trans "Connection Details" %}</h2>
|
|
<div class="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 "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>
|
|
|
|
{# --- ACTION BAR AT THE BOTTOM OF THE MAIN CARD --- #}
|
|
<div class="card-footer action-bar-footer">
|
|
<div>
|
|
{# Placeholder for future left-aligned button #}
|
|
</div>
|
|
<div class="d-flex gap-2">
|
|
<a href="{% url 'update_meeting' meeting.slug %}" class="btn btn-primary btn-footer-action">
|
|
<i class="fas fa-edit me-1"></i> {% trans "Update" %}
|
|
</a>
|
|
{% if meeting.zoom_gateway_response %}
|
|
<button type="button" class="btn btn-secondary btn-footer-action" onclick="toggleGateway()">
|
|
<i class="fas fa-code me-1"></i> {% trans "API Response" %}
|
|
</button>
|
|
{% endif %}
|
|
|
|
<button type="button" class="btn btn-danger btn-footer-action ms-3" title="{% trans 'Delete' %}"
|
|
data-bs-toggle="modal" data-bs-target="#deleteModal"
|
|
data-item-name="{{ meeting.topic }}">
|
|
<i class="fas fa-trash-alt me-1"></i>
|
|
Delete
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# --- 2. JOIN INFO CARD (Separate from details, but in the same column) --- #}
|
|
{% if meeting.join_url %}
|
|
<div class="card no-hover join-info-card detail-section flex-shrink-0">
|
|
<div class="card-body">
|
|
<h2><i class="fas fa-link me-2"></i> {% trans "Join Information" %}</h2>
|
|
|
|
<a href="{{ meeting.join_url }}" class="btn btn-primary w-100 mb-4" target="_blank">
|
|
<i class="fas fa-video me-1"></i> {% trans "Join Meeting Now" %}
|
|
</a>
|
|
|
|
<div class="join-url-container">
|
|
{# Message should not be display: none; but opacity: 0; for smooth transition #}
|
|
<div id="copy-message" class="text-white rounded px-2 py-1 small fw-bold mb-2 text-center" style="opacity: 0; transition: opacity 0.3s; position: absolute; right: 0; top: -30px; background-color: var(--kaauh-success); z-index: 10;">{% trans "Copied!" %}</div>
|
|
|
|
<div class="join-url-display d-flex justify-content-between align-items-center position-relative">
|
|
<div class="text-truncate me-2">
|
|
<strong>{% trans "Join URL" %}:</strong>
|
|
<span id="meeting-join-url">{{ meeting.join_url }}</span>
|
|
</div>
|
|
<button class="btn-copy ms-2 flex-shrink-0" onclick="copyLink()" title="{% trans 'Copy URL' %}">
|
|
<i class="fas fa-copy"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{% if meeting.password %}
|
|
<div class="detail-row" style="border: none; padding-top: 1rem;">
|
|
<div class="detail-label" style="font-size: 1rem;">{% trans "Password" %}:</div>
|
|
<div class="detail-value fw-bolder text-danger">{{ meeting.password }}</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# --- API RESPONSE CARD (Full width, hidden by default) --- #}
|
|
{% if meeting.zoom_gateway_response %}
|
|
<div id="gateway-response-card" class="card mt-4" style="display: none;">
|
|
<div class="card-body">
|
|
<h3>{% trans "API Gateway Response" %}</h3>
|
|
<pre>{{ meeting.zoom_gateway_response|safe }}</pre>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
</div>
|
|
|
|
{# MODALS (KEEP OUTSIDE OF THE MAIN LAYOUT ROWS) #}
|
|
{% comment %} {% include 'modals/delete_modal.html' with item_name="Meeting" delete_url_name='delete_meeting' %} {% endcomment %}
|
|
<div class="modal fade" id="commentModal" tabindex="-1" aria-labelledby="commentModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="commentModalLabel">Add Comment</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body" id="commentModalBody">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</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';
|
|
}
|
|
}
|
|
|
|
// CopyLink function implementation (slightly improved for message placement)
|
|
function copyLink() {
|
|
const urlElement = document.getElementById('meeting-join-url');
|
|
const displayContainer = urlElement.closest('.join-url-display');
|
|
const messageElement = document.getElementById('copy-message');
|
|
const textToCopy = urlElement.textContent || urlElement.innerText;
|
|
|
|
clearTimeout(window.copyMessageTimeout);
|
|
|
|
function showMessage(success) {
|
|
messageElement.textContent = success ? '{% trans "Copied!" %}' : '{% trans "Copy Failed." %}';
|
|
messageElement.style.backgroundColor = success ? 'var(--kaauh-success)' : 'var(--kaauh-danger)';
|
|
messageElement.style.opacity = '1';
|
|
|
|
// Position the message relative to the display container
|
|
const rect = displayContainer.getBoundingClientRect();
|
|
messageElement.style.left = (rect.width / 2) - (messageElement.offsetWidth / 2) + 'px';
|
|
messageElement.style.top = '-35px';
|
|
|
|
window.copyMessageTimeout = setTimeout(() => {
|
|
messageElement.style.opacity = '0';
|
|
}, 2000);
|
|
}
|
|
|
|
if (navigator.clipboard && window.isSecureContext) {
|
|
navigator.clipboard.writeText(textToCopy).then(() => {
|
|
showMessage(true);
|
|
}).catch(err => {
|
|
console.error('Could not copy text: ', err);
|
|
fallbackCopyTextToClipboard(textToCopy, showMessage);
|
|
});
|
|
} else {
|
|
fallbackCopyTextToClipboard(textToCopy, showMessage);
|
|
}
|
|
}
|
|
|
|
function fallbackCopyTextToClipboard(text, callback) {
|
|
const textArea = document.createElement("textarea");
|
|
textArea.value = text;
|
|
|
|
textArea.style.top = "0";
|
|
textArea.style.left = "0";
|
|
textArea.style.position = "fixed";
|
|
|
|
document.body.appendChild(textArea);
|
|
textArea.focus();
|
|
textArea.select();
|
|
|
|
let success = false;
|
|
try {
|
|
success = document.execCommand('copy');
|
|
} catch (err) {
|
|
console.error('Fallback: Oops, unable to copy', err);
|
|
}
|
|
|
|
document.body.removeChild(textArea);
|
|
callback(success);
|
|
}
|
|
</script>
|
|
{% endblock %} |