message queryset #44
@ -1,4 +1,4 @@
|
||||
# Generated by Django 5.2.6 on 2025-12-02 10:27
|
||||
# Generated by Django 5.2.7 on 2025-12-02 14:21
|
||||
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
# Generated by Django 5.2.7 on 2025-12-02 10:28
|
||||
|
||||
import django.db.models.deletion
|
||||
import django_ckeditor_5.fields
|
||||
import django_extensions.db.fields
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('recruitment', '0005_merge_20251202_1308'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='EmailContent',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('subject', models.CharField(max_length=255, verbose_name='Subject')),
|
||||
('message', django_ckeditor_5.fields.CKEditor5Field(verbose_name='Message Body')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Email Content',
|
||||
'verbose_name_plural': 'Email Contents',
|
||||
},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='interview',
|
||||
name='details_url',
|
||||
field=models.JSONField(blank=True, null=True),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Note',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created at')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated at')),
|
||||
('slug', django_extensions.db.fields.RandomCharField(blank=True, editable=False, length=8, unique=True, verbose_name='Slug')),
|
||||
('note_type', models.CharField(choices=[('Feedback', 'Candidate Feedback'), ('Logistics', 'Logistical Note'), ('General', 'General Comment')], default='Feedback', max_length=50, verbose_name='Note Type')),
|
||||
('content', django_ckeditor_5.fields.CKEditor5Field(verbose_name='Content/Feedback')),
|
||||
('application', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='recruitment.application', verbose_name='Application')),
|
||||
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interview_notes', to=settings.AUTH_USER_MODEL, verbose_name='Author')),
|
||||
('interview', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='recruitment.interview', verbose_name='Scheduled Interview')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Interview Note',
|
||||
'verbose_name_plural': 'Interview Notes',
|
||||
'ordering': ['created_at'],
|
||||
},
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='InterviewNote',
|
||||
),
|
||||
]
|
||||
@ -4,95 +4,90 @@
|
||||
{% block title %}{{ message.subject }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="container-fluid py-4">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<!-- Message Header -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">
|
||||
{{ message.subject }}
|
||||
{% if message.parent_message %}
|
||||
<span class="badge bg-secondary ms-2">{% trans "Reply" %}</span>
|
||||
{% endif %}
|
||||
</h5>
|
||||
<div class="btn-group" role="group">
|
||||
<a href="{% url 'message_reply' message.id %}" class="btn btn-outline-info">
|
||||
<i class="fas fa-reply"></i> {% trans "Reply" %}
|
||||
</a>
|
||||
{% if message.recipient == request.user %}
|
||||
<a href="{% url 'message_mark_unread' message.id %}"
|
||||
class="btn btn-outline-warning"
|
||||
hx-swap="outerHTML"
|
||||
hx-post="{% url 'message_mark_unread' message.id %}">
|
||||
<i class="fas fa-envelope"></i> {% trans "Mark Unread" %}
|
||||
<div class="card shadow-sm mb-4 border-0">
|
||||
<div class="card-header bg-gradient border-0 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-start gap-3">
|
||||
<div>
|
||||
<h4 class="mb-2 fw-bold">{{ message.subject }}</h4>
|
||||
{% if message.parent_message %}
|
||||
<span class="badge bg-info">{% trans "Reply" %}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="btn-group flex-wrap" role="group">
|
||||
<a href="{% url 'message_reply' message.id %}" class="btn btn-sm btn-main-action">
|
||||
<i class="fas fa-reply me-1"></i> {% trans "Reply" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
<button type="button"
|
||||
class="btn btn-danger btn-lg"
|
||||
hx-post="{% url 'message_delete' message.id %}"
|
||||
hx-confirm="{% trans 'Are you sure you want to permanently delete this message? This action cannot be undone.' %}"
|
||||
hx-redirect="{% url 'message_list' %}"
|
||||
onclick="this.disabled=true;"
|
||||
title="{% trans 'Delete Message' %}">
|
||||
<i class="fas fa-trash me-1"></i> {% trans "Delete Message" %}
|
||||
</button>
|
||||
<a href="{% url 'message_list' %}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i> {% trans "Back to Messages" %}
|
||||
</a>
|
||||
{% if message.recipient == request.user %}
|
||||
<button type="button" class="btn btn-sm btn-outline-warning"
|
||||
hx-post="{% url 'message_mark_unread' message.id %}"
|
||||
hx-swap="outerHTML">
|
||||
<i class="fas fa-envelope me-1"></i> {% trans "Mark Unread" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
<button type="button" class="btn btn-sm btn-outline-danger"
|
||||
hx-post="{% url 'message_delete' message.id %}"
|
||||
hx-confirm="{% trans 'Are you sure you want to permanently delete this message? This action cannot be undone.' %}"
|
||||
hx-redirect="{% url 'message_list' %}"
|
||||
onclick="this.disabled=true;">
|
||||
<i class="fas fa-trash me-1"></i> {% trans "Delete" %}
|
||||
</button>
|
||||
<a href="{% url 'message_list' %}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left me-1"></i> {% trans "Back" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row mb-3">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<strong>{% trans "From:" %}</strong>
|
||||
<span class="text-primary">{{ message.sender.get_full_name|default:message.sender.username }}</span>
|
||||
<small class="text-muted d-block mb-1">{% trans "From:" %}</small>
|
||||
<span class="fw-semibold">{{ message.sender.get_full_name|default:message.sender.username }}</span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<strong>{% trans "To:" %}</strong>
|
||||
<span class="text-primary">{{ message.recipient.get_full_name|default:message.recipient.username }}</span>
|
||||
<small class="text-muted d-block mb-1">{% trans "To:" %}</small>
|
||||
<span class="fw-semibold">{{ message.recipient.get_full_name|default:message.recipient.username }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<strong>{% trans "Type:" %}</strong>
|
||||
<span class="badge bg-{{ message.message_type|lower }}">
|
||||
{{ message.get_message_type_display }}
|
||||
<small class="text-muted d-block mb-1">{% trans "Type:" %}</small>
|
||||
<span class="badge bg-primary-theme">
|
||||
{{message.get_message_type_display}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<strong>{% trans "Status:" %}</strong>
|
||||
<small class="text-muted d-block mb-1">{% trans "Status:" %}</small>
|
||||
{% if message.is_read %}
|
||||
<span class="badge bg-success">{% trans "Read" %}</span>
|
||||
{% if message.read_at %}
|
||||
<small class="text-muted">({{ message.read_at|date:"M d, Y H:i" }})</small>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="badge bg-warning">{% trans "Unread" %}</span>
|
||||
<span class="badge bg-warning text-dark">{% trans "Unread" %}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6">
|
||||
<strong>{% trans "Created:" %}</strong>
|
||||
<small class="text-muted d-block mb-1">{% trans "Created:" %}</small>
|
||||
<span>{{ message.created_at|date:"M d, Y H:i" }}</span>
|
||||
</div>
|
||||
{% if message.job %}
|
||||
<div class="col-md-6">
|
||||
<strong>{% trans "Related Job:" %}</strong>
|
||||
<a href="{% url 'job_detail' message.job.slug %}" class="text-primary">
|
||||
<div class="col-md-6">
|
||||
<small class="text-muted d-block mb-1">{% trans "Related Job:" %}</small>
|
||||
<a href="{% url 'job_detail' message.job.slug %}" class="fw-semibold text-decoration-none text-primary-theme">
|
||||
{{ message.job.title }}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if message.parent_message %}
|
||||
<div class="alert alert-info">
|
||||
<div class="alert alert-info mt-3 mb-0 border-0">
|
||||
<strong>{% trans "In reply to:" %}</strong>
|
||||
<a href="{% url 'message_detail' message.parent_message.id %}">
|
||||
<a href="{% url 'message_detail' message.parent_message.id %}" class="text-decoration-none">
|
||||
{{ message.parent_message.subject }}
|
||||
</a>
|
||||
<small class="text-muted d-block">
|
||||
<small class="text-muted d-block mt-2">
|
||||
{% trans "From" %} {{ message.parent_message.sender.get_full_name|default:message.parent_message.sender.username }}
|
||||
{% trans "on" %} {{ message.parent_message.created_at|date:"M d, Y H:i" }}
|
||||
</small>
|
||||
@ -102,10 +97,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Message Content -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0">{% trans "Message" %}</h6>
|
||||
</div>
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-body">
|
||||
<div class="message-content">
|
||||
{{ message.content|linebreaks }}
|
||||
@ -115,15 +107,15 @@
|
||||
|
||||
<!-- Message Thread (if this is a reply and has replies) -->
|
||||
{% if message.replies.all %}
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-header bg-light border-0">
|
||||
<h6 class="mb-0">
|
||||
<i class="fas fa-comments"></i> {% trans "Replies" %} ({{ message.replies.count }})
|
||||
<i class="fas fa-comments text-primary"></i> {% trans "Replies" %} <span class="badge bg-primary">{{ message.replies.count }}</span>
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% for reply in message.replies.all %}
|
||||
<div class="border-start ps-3 mb-3">
|
||||
<div class="message-reply mb-3 p-3 bg-light rounded">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div>
|
||||
<strong>{{ reply.sender.get_full_name|default:reply.sender.username }}</strong>
|
||||
@ -135,14 +127,12 @@
|
||||
{{ reply.get_message_type_display }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="reply-content">
|
||||
<div class="reply-content mb-3">
|
||||
{{ reply.content|linebreaks }}
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<a href="{% url 'message_reply' reply.id %}" class="btn btn-sm btn-outline-info">
|
||||
<i class="fas fa-reply"></i> {% trans "Reply to this" %}
|
||||
</a>
|
||||
</div>
|
||||
<a href="{% url 'message_reply' reply.id %}" class="btn btn-sm btn-outline-primary">
|
||||
<i class="fas fa-reply me-1"></i> {% trans "Reply" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@ -153,31 +143,37 @@
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
{% block customCSS %}
|
||||
<style>
|
||||
.message-content {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
line-height: 1.6;
|
||||
padding: 1rem;
|
||||
padding: 1.5rem;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 0.375rem;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.reply-content {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
line-height: 1.5;
|
||||
font-size: 0.9rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.border-start {
|
||||
border-left: 3px solid #0d6efd;
|
||||
|
||||
|
||||
.message-reply:hover {
|
||||
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
|
||||
.ps-3 {
|
||||
padding-left: 1rem;
|
||||
.bg-gradient {
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #ffffff 100%);
|
||||
}
|
||||
|
||||
.btn-group .btn {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user