246 lines
13 KiB
HTML
246 lines
13 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static i18n %}
|
|
|
|
{% block title %}{% trans "Notifications" %} - {{ block.super }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="px-4 py-6">
|
|
<!-- Header -->
|
|
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-start gap-4 mb-6">
|
|
<div class="flex-1">
|
|
<h1 class="text-3xl font-bold text-temple-dark mb-1 flex items-center gap-2">
|
|
<i data-lucide="bell" class="w-8 h-8"></i>
|
|
{% trans "Notifications" %}
|
|
</h1>
|
|
<p class="text-gray-600">
|
|
{% blocktrans count count=total_notifications %}
|
|
{{ count }} notification
|
|
{% plural %}
|
|
{{ count }} notifications
|
|
{% endblocktrans %}
|
|
{% if unread_notifications %}
|
|
<span class="font-semibold text-temple-red">({{ unread_notifications }} unread)</span>
|
|
{% endif %}
|
|
</p>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
{% if unread_notifications %}
|
|
<a href="{% url 'notification_mark_all_read' %}"
|
|
class="inline-flex items-center gap-2 border border-gray-300 text-gray-700 hover:bg-gray-50 px-4 py-2 rounded-lg text-sm font-medium transition">
|
|
<i data-lucide="check-double" class="w-4 h-4"></i>
|
|
<span class="hidden sm:inline">{% trans "Mark All Read" %}</span>
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Filters -->
|
|
<div class="bg-white rounded-xl shadow-md border border-gray-200 mb-6">
|
|
<div class="p-6">
|
|
<form method="get" class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<div>
|
|
<label for="status_filter" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{% trans "Status" %}
|
|
</label>
|
|
<select name="status" id="status_filter"
|
|
class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-temple-red focus:border-transparent transition text-gray-900">
|
|
<option value="">{% trans "All Status" %}</option>
|
|
<option value="unread" {% if status_filter == 'unread' %}selected{% endif %}>{% trans "Unread" %}</option>
|
|
<option value="read" {% if status_filter == 'read' %}selected{% endif %}>{% trans "Read" %}</option>
|
|
<option value="sent" {% if status_filter == 'sent' %}selected{% endif %}>{% trans "Sent" %}</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label for="type_filter" class="block text-sm font-semibold text-gray-700 mb-2">
|
|
{% trans "Type" %}
|
|
</label>
|
|
<select name="type" id="type_filter"
|
|
class="w-full px-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-temple-red focus:border-transparent transition text-gray-900">
|
|
<option value="">{% trans "All Types" %}</option>
|
|
<option value="in_app" {% if type_filter == 'in_app' %}selected{% endif %}>{% trans "In-App" %}</option>
|
|
<option value="email" {% if type_filter == 'email' %}selected{% endif %}>{% trans "Email" %}</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-semibold text-gray-700 mb-2"> </label>
|
|
<div class="flex gap-2">
|
|
<button type="submit"
|
|
class="flex-1 bg-temple-red hover:bg-red-800 text-white font-semibold px-4 py-2.5 rounded-lg transition shadow-md hover:shadow-lg flex items-center justify-center gap-2">
|
|
<i data-lucide="filter" class="w-4 h-4"></i>
|
|
{% trans "Filter" %}
|
|
</button>
|
|
<a href="{% url 'notification_list' %}"
|
|
class="flex-1 border border-gray-300 text-gray-700 hover:bg-gray-50 px-4 py-2.5 rounded-lg transition flex items-center justify-center gap-2">
|
|
<i data-lucide="x" class="w-4 h-4"></i>
|
|
{% trans "Clear" %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistics -->
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
|
<div class="bg-white rounded-xl shadow-md border border-temple-red p-6 text-center">
|
|
<h3 class="text-3xl font-bold text-temple-red mb-1">{{ total_notifications }}</h3>
|
|
<p class="text-gray-600">{% trans "Total Notifications" %}</p>
|
|
</div>
|
|
<div class="bg-white rounded-xl shadow-md border border-yellow-500 p-6 text-center">
|
|
<h3 class="text-3xl font-bold text-yellow-600 mb-1">{{ unread_notifications }}</h3>
|
|
<p class="text-gray-600">{% trans "Unread" %}</p>
|
|
</div>
|
|
<div class="bg-white rounded-xl shadow-md border border-blue-500 p-6 text-center">
|
|
<h3 class="text-3xl font-bold text-blue-600 mb-1">{{ email_notifications }}</h3>
|
|
<p class="text-gray-600">{% trans "Email Notifications" %}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Notifications List -->
|
|
{% if page_obj %}
|
|
<div class="bg-white rounded-xl shadow-md border border-gray-200">
|
|
<div class="divide-y divide-gray-200">
|
|
{% for notification in page_obj %}
|
|
<div class="p-4 hover:bg-gray-50 transition {% if notification.status == 'PENDING' %}bg-blue-50{% endif %}">
|
|
<div class="flex justify-between items-start gap-4">
|
|
<div class="flex-1 min-w-0">
|
|
<div class="flex items-center flex-wrap gap-2 mb-2">
|
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
|
{% if notification.status == 'PENDING' %}bg-yellow-100 text-yellow-800
|
|
{% elif notification.status == 'READ' %}bg-green-100 text-green-800
|
|
{% else %}bg-gray-100 text-gray-800{% endif %}">
|
|
{{ notification.get_status_display }}
|
|
</span>
|
|
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-200 text-gray-800">
|
|
{{ notification.get_notification_type_display }}
|
|
</span>
|
|
<span class="text-xs text-gray-500">{{ notification.created_at|date:"Y-m-d H:i" }}</span>
|
|
</div>
|
|
<h5 class="text-base font-semibold mb-1">
|
|
<a href="{% url 'notification_detail' notification.id %}"
|
|
class="text-gray-900 hover:text-temple-red transition {% if notification.status == 'PENDING' %}font-bold{% endif %}">
|
|
{{ notification.message|truncatewords:15 }}
|
|
</a>
|
|
</h5>
|
|
{% if notification.related_meeting %}
|
|
<p class="text-sm text-gray-500">
|
|
<i data-lucide="video" class="w-3 h-3 inline mr-1"></i>
|
|
{% trans "Related to meeting:" %} {{ notification.related_meeting.topic }}
|
|
</p>
|
|
{% endif %}
|
|
</div>
|
|
<div class="flex flex-col gap-2">
|
|
{% if notification.status == 'PENDING' %}
|
|
<a href="{% url 'notification_mark_read' notification.id %}"
|
|
class="p-2 text-green-600 hover:bg-green-50 rounded-lg transition"
|
|
title="{% trans 'Mark as read' %}">
|
|
<i data-lucide="check" class="w-4 h-4"></i>
|
|
</a>
|
|
{% else %}
|
|
<a href="{% url 'notification_mark_unread' notification.id %}"
|
|
class="p-2 text-gray-600 hover:bg-gray-100 rounded-lg transition"
|
|
title="{% trans 'Mark as unread' %}">
|
|
<i data-lucide="mail" class="w-4 h-4"></i>
|
|
</a>
|
|
{% endif %}
|
|
<a href="{% url 'notification_delete' notification.id %}"
|
|
class="p-2 text-red-600 hover:bg-red-50 rounded-lg transition"
|
|
title="{% trans 'Delete notification' %}">
|
|
<i data-lucide="trash-2" class="w-4 h-4"></i>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
{% if page_obj.has_other_pages %}
|
|
<nav aria-label="{% trans 'Notifications pagination' %}" class="mt-6">
|
|
<ul class="flex justify-center items-center gap-2">
|
|
{% if page_obj.has_previous %}
|
|
<li>
|
|
<a class="px-3 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition"
|
|
href="?page={{ page_obj.previous_page_number }}&status={{ status_filter }}&type={{ type_filter }}">
|
|
<i data-lucide="chevron-left" class="w-4 h-4"></i>
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
|
|
{% for num in page_obj.paginator.page_range %}
|
|
{% if page_obj.number == num %}
|
|
<li>
|
|
<span class="px-4 py-2 bg-temple-red text-white rounded-lg font-semibold">{{ num }}</span>
|
|
</li>
|
|
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
|
|
<li>
|
|
<a class="px-4 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition"
|
|
href="?page={{ num }}&status={{ status_filter }}&type={{ type_filter }}">{{ num }}</a>
|
|
</li>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{% if page_obj.has_next %}
|
|
<li>
|
|
<a class="px-3 py-2 border border-gray-300 rounded-lg hover:bg-gray-50 transition"
|
|
href="?page={{ page_obj.next_page_number }}&status={{ status_filter }}&type={{ type_filter }}">
|
|
<i data-lucide="chevron-right" class="w-4 h-4"></i>
|
|
</a>
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
</nav>
|
|
{% endif %}
|
|
{% else %}
|
|
<div class="text-center py-12 bg-white rounded-xl shadow-md border border-gray-200">
|
|
<i data-lucide="bell-off" class="w-16 h-16 text-gray-400 mx-auto mb-4"></i>
|
|
<h3 class="text-xl font-semibold text-gray-600 mb-2">{% trans "No notifications found" %}</h3>
|
|
<p class="text-gray-500 mb-4">
|
|
{% if status_filter or type_filter %}
|
|
{% trans "Try adjusting your filters to see more notifications." %}
|
|
{% else %}
|
|
{% trans "You don't have any notifications yet." %}
|
|
{% endif %}
|
|
</p>
|
|
{% if status_filter or type_filter %}
|
|
<a href="{% url 'notification_list' %}"
|
|
class="inline-flex items-center gap-2 bg-temple-red hover:bg-red-800 text-white font-semibold px-6 py-2.5 rounded-xl transition shadow-md hover:shadow-lg">
|
|
<i data-lucide="x" class="w-4 h-4"></i>
|
|
{% trans "Clear Filters" %}
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block customJS %}
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Initialize Lucide icons
|
|
lucide.createIcons();
|
|
|
|
/*
|
|
// Auto-refresh notifications every 30 seconds
|
|
setInterval(function() {
|
|
fetch('/api/notification-count/')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
// Update notification badge if it exists
|
|
const badge = document.querySelector('.notification-badge');
|
|
if (badge) {
|
|
badge.textContent = data.count;
|
|
if (data.count > 0) {
|
|
badge.classList.remove('hidden');
|
|
} else {
|
|
badge.classList.add('hidden');
|
|
}
|
|
}
|
|
})
|
|
.catch(error => console.error('Error fetching notifications:', error));
|
|
}, 30000);
|
|
*/
|
|
});
|
|
</script>
|
|
{% endblock %} |