|
| 1 | +{% extends "base.html" %} |
| 2 | +{% load static %} |
| 3 | + |
| 4 | +{% block title %}Notifications{% endblock title %} |
| 5 | + |
| 6 | +{% block content %} |
| 7 | +<div class="container mx-auto px-4 py-8 max-w-3xl"> |
| 8 | + <div class="flex items-center justify-between mb-6"> |
| 9 | + <h1 class="text-2xl font-bold text-gray-900 dark:text-white"> |
| 10 | + <i class="fas fa-bell mr-2 text-teal-500"></i> Notifications |
| 11 | + {% if unread_count %} |
| 12 | + <span class="ml-2 text-sm font-medium bg-red-100 text-red-700 dark:bg-red-900 dark:text-red-200 px-2 py-0.5 rounded-full"> |
| 13 | + {{ unread_count }} unread |
| 14 | + </span> |
| 15 | + {% endif %} |
| 16 | + </h1> |
| 17 | + {% if unread_count %} |
| 18 | + <form method="post" action="{% url 'mark_notifications_read' %}"> |
| 19 | + {% csrf_token %} |
| 20 | + <input type="hidden" name="filter_type" value="{{ filter_type }}"> |
| 21 | + <button type="submit" |
| 22 | + class="text-sm px-4 py-2 bg-teal-600 hover:bg-teal-700 text-white rounded-lg transition-colors duration-200"> |
| 23 | + Mark all as read |
| 24 | + </button> |
| 25 | + </form> |
| 26 | + {% endif %} |
| 27 | + </div> |
| 28 | + |
| 29 | + <!-- Filter Tabs --> |
| 30 | + <div class="flex gap-2 mb-6 flex-wrap"> |
| 31 | + <a href="{% url 'notification_list' %}" |
| 32 | + class="px-3 py-1.5 rounded-full text-sm font-medium transition-colors duration-200 |
| 33 | + {% if not filter_type %}bg-teal-600 text-white{% else %}bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600{% endif %}"> |
| 34 | + All |
| 35 | + </a> |
| 36 | + {% for type, label in notification_types %} |
| 37 | + <a href="{% url 'notification_list' %}?type={{ type }}" |
| 38 | + class="px-3 py-1.5 rounded-full text-sm font-medium transition-colors duration-200 |
| 39 | + {% if filter_type == type %}bg-teal-600 text-white{% else %}bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600{% endif %}"> |
| 40 | + {{ label }} |
| 41 | + </a> |
| 42 | + {% endfor %} |
| 43 | + </div> |
| 44 | + |
| 45 | + <!-- Notification List --> |
| 46 | + <div class="space-y-3"> |
| 47 | + {% for notification in page_obj %} |
| 48 | + <div class="flex items-start gap-4 p-4 rounded-lg border transition-colors duration-200 |
| 49 | + {% if not notification.read %}bg-teal-50 dark:bg-teal-900/20 border-teal-200 dark:border-teal-800{% else %}bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700{% endif %}"> |
| 50 | + <!-- Icon --> |
| 51 | + <div class="flex-shrink-0 mt-0.5"> |
| 52 | + {% if notification.notification_type == "success" %} |
| 53 | + <i class="fas fa-check-circle text-green-500 text-lg"></i> |
| 54 | + {% elif notification.notification_type == "warning" %} |
| 55 | + <i class="fas fa-exclamation-triangle text-yellow-500 text-lg"></i> |
| 56 | + {% elif notification.notification_type == "error" %} |
| 57 | + <i class="fas fa-times-circle text-red-500 text-lg"></i> |
| 58 | + {% else %} |
| 59 | + <i class="fas fa-info-circle text-blue-500 text-lg"></i> |
| 60 | + {% endif %} |
| 61 | + </div> |
| 62 | + <!-- Content --> |
| 63 | + <div class="flex-1 min-w-0"> |
| 64 | + <div class="flex items-center justify-between gap-2"> |
| 65 | + <p class="font-semibold text-gray-900 dark:text-white text-sm">{{ notification.title }}</p> |
| 66 | + <span class="text-xs text-gray-400 dark:text-gray-500 whitespace-nowrap"> |
| 67 | + {{ notification.created_at|timesince }} ago |
| 68 | + </span> |
| 69 | + </div> |
| 70 | + <p class="text-sm text-gray-600 dark:text-gray-300 mt-1">{{ notification.message }}</p> |
| 71 | + </div> |
| 72 | + <!-- Mark as read --> |
| 73 | + {% if not notification.read %} |
| 74 | + <form method="post" action="{% url 'mark_single_notification_read' notification.id %}"> |
| 75 | + {% csrf_token %} |
| 76 | + <input type="hidden" name="filter_type" value="{{ filter_type }}"> |
| 77 | + <button type="submit" |
| 78 | + title="Mark as read" |
| 79 | + aria-label="Mark notification as read" |
| 80 | + class="flex-shrink-0 text-teal-500 hover:text-teal-700 transition-colors duration-200"> |
| 81 | + <i class="fas fa-check text-sm"></i> |
| 82 | + </button> |
| 83 | + </form> |
| 84 | + {% endif %} |
| 85 | + </div> |
| 86 | + {% empty %} |
| 87 | + <div class="text-center py-16 bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700"> |
| 88 | + <i class="fas fa-bell-slash text-4xl text-gray-300 dark:text-gray-600 mb-4"></i> |
| 89 | + <p class="text-gray-500 dark:text-gray-400 font-medium">No notifications yet</p> |
| 90 | + <p class="text-sm text-gray-400 dark:text-gray-500 mt-1">You're all caught up!</p> |
| 91 | + </div> |
| 92 | + {% endfor %} |
| 93 | + </div> |
| 94 | + <!-- Pagination --> |
| 95 | + {% if page_obj.has_other_pages %} |
| 96 | + <div class="flex justify-center gap-2 mt-6"> |
| 97 | + {% if page_obj.has_previous %} |
| 98 | + <a href="?page={{ page_obj.previous_page_number }}{% if filter_type %}&type={{ filter_type }}{% endif %}" |
| 99 | + class="px-4 py-2 text-sm bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600"> |
| 100 | + Previous |
| 101 | + </a> |
| 102 | + {% endif %} |
| 103 | + <span class="px-4 py-2 text-sm text-gray-500 dark:text-gray-400"> |
| 104 | + Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }} |
| 105 | + </span> |
| 106 | + {% if page_obj.has_next %} |
| 107 | + <a href="?page={{ page_obj.next_page_number }}{% if filter_type %}&type={{ filter_type }}{% endif %}" |
| 108 | + class="px-4 py-2 text-sm bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600"> |
| 109 | + Next |
| 110 | + </a> |
| 111 | + {% endif %} |
| 112 | + </div> |
| 113 | + {% endif %} |
| 114 | +</div> |
| 115 | +{% endblock content %} |
0 commit comments