325 lines
17 KiB
HTML
325 lines
17 KiB
HTML
{% extends "layouts/base.html" %}
|
|
{% load i18n static %}
|
|
|
|
{% block title %}{% trans "Appreciation" %} - PX360{% endblock %}
|
|
|
|
{% block content %}
|
|
<!-- Header -->
|
|
<header class="mb-6">
|
|
<div class="flex justify-between items-start">
|
|
<div>
|
|
<h1 class="text-2xl font-bold text-navy flex items-center gap-3">
|
|
<i data-lucide="heart" class="w-7 h-7 text-rose-500"></i>
|
|
{% trans "Appreciation" %}
|
|
</h1>
|
|
<p class="text-sm text-slate mt-1">{% trans "Send appreciation to colleagues and celebrate achievements" %}</p>
|
|
</div>
|
|
<a href="{% url 'appreciation:appreciation_send' %}" class="bg-navy text-white px-5 py-2.5 rounded-xl text-sm font-bold shadow-lg shadow-navy/20 hover:bg-blue flex items-center gap-2 transition">
|
|
<i data-lucide="send" class="w-4 h-4"></i> {% trans "Send Appreciation" %}
|
|
</a>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Stats Cards -->
|
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
|
|
<div class="bg-white p-5 rounded-2xl shadow-sm border border-slate-100">
|
|
<div class="flex items-center gap-4">
|
|
<div class="p-3 bg-blue-50 rounded-xl">
|
|
<i data-lucide="inbox" class="w-6 h-6 text-blue-500"></i>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs font-bold text-slate uppercase tracking-wider mb-1">{% trans "Received" %}</p>
|
|
<p class="text-2xl font-black text-navy" id="receivedCount">{{ stats.received }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="bg-white p-5 rounded-2xl shadow-sm border border-slate-100">
|
|
<div class="flex items-center gap-4">
|
|
<div class="p-3 bg-green-50 rounded-xl">
|
|
<i data-lucide="send" class="w-6 h-6 text-green-500"></i>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs font-bold text-slate uppercase tracking-wider mb-1">{% trans "Sent" %}</p>
|
|
<p class="text-2xl font-black text-navy" id="sentCount">{{ stats.sent }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="bg-white p-5 rounded-2xl shadow-sm border border-slate-100">
|
|
<div class="flex items-center gap-4">
|
|
<div class="p-3 bg-amber-50 rounded-xl">
|
|
<i data-lucide="award" class="w-6 h-6 text-amber-500"></i>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs font-bold text-slate uppercase tracking-wider mb-1">{% trans "Badges Earned" %}</p>
|
|
<p class="text-2xl font-black text-navy" id="badgesCount">{{ stats.badges_earned }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="bg-white p-5 rounded-2xl shadow-sm border border-slate-100">
|
|
<div class="flex items-center gap-4">
|
|
<div class="p-3 bg-purple-50 rounded-xl">
|
|
<i data-lucide="trophy" class="w-6 h-6 text-purple-500"></i>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs font-bold text-slate uppercase tracking-wider mb-1">{% trans "Leaderboard" %}</p>
|
|
<a href="{% url 'appreciation:leaderboard_view' %}" class="text-sm font-bold text-navy hover:text-blue transition flex items-center gap-1">
|
|
{% trans "View" %} <i data-lucide="arrow-right" class="w-4 h-4"></i>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
|
<a href="{% url 'appreciation:leaderboard_view' %}" class="group">
|
|
<div class="bg-white p-5 rounded-2xl shadow-sm border border-amber-200 hover:border-amber-300 hover:shadow-md transition">
|
|
<div class="flex items-center gap-4">
|
|
<div class="p-3 bg-amber-50 rounded-xl group-hover:bg-amber-100 transition">
|
|
<i data-lucide="trophy" class="w-6 h-6 text-amber-500"></i>
|
|
</div>
|
|
<div>
|
|
<h3 class="font-bold text-navy">{% trans "Leaderboard" %}</h3>
|
|
<p class="text-sm text-slate">{% trans "See top performers" %}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
<a href="{% url 'appreciation:my_badges_view' %}" class="group">
|
|
<div class="bg-white p-5 rounded-2xl shadow-sm border border-blue-200 hover:border-blue-300 hover:shadow-md transition">
|
|
<div class="flex items-center gap-4">
|
|
<div class="p-3 bg-blue-50 rounded-xl group-hover:bg-blue-100 transition">
|
|
<i data-lucide="award" class="w-6 h-6 text-blue-500"></i>
|
|
</div>
|
|
<div>
|
|
<h3 class="font-bold text-navy">{% trans "My Badges" %}</h3>
|
|
<p class="text-sm text-slate">{% trans "View earned badges" %}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
<a href="{% url 'appreciation:appreciation_send' %}" class="group">
|
|
<div class="bg-white p-5 rounded-2xl shadow-sm border border-green-200 hover:border-green-300 hover:shadow-md transition">
|
|
<div class="flex items-center gap-4">
|
|
<div class="p-3 bg-green-50 rounded-xl group-hover:bg-green-100 transition">
|
|
<i data-lucide="send" class="w-6 h-6 text-green-500"></i>
|
|
</div>
|
|
<div>
|
|
<h3 class="font-bold text-navy">{% trans "Send Appreciation" %}</h3>
|
|
<p class="text-sm text-slate">{% trans "Share appreciation" %}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Filter Bar -->
|
|
<div class="bg-white rounded-2xl shadow-sm border-2 border-slate-200 mb-6">
|
|
<div class="px-6 py-4 border-b-2 border-slate-200 flex justify-between items-center bg-gradient-to-r from-slate-50 to-slate-100">
|
|
<h3 class="font-bold text-navy flex items-center gap-2">
|
|
<i data-lucide="filter" class="w-5 h-5 text-navy"></i>
|
|
{% trans "Filters" %}
|
|
</h3>
|
|
</div>
|
|
<div class="p-5">
|
|
<form method="get" class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
<div>
|
|
<label class="block text-xs font-bold text-slate uppercase mb-2">{% trans "View" %}</label>
|
|
<select id="tab" name="tab" onchange="this.form.submit()"
|
|
class="w-full px-4 py-2.5 border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-navy focus:border-transparent transition">
|
|
<option value="received" {% if current_tab == 'received' %}selected{% endif %}>
|
|
{% trans "My Appreciations" %}
|
|
</option>
|
|
<option value="sent" {% if current_tab == 'sent' %}selected{% endif %}>
|
|
{% trans "Sent by Me" %}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs font-bold text-slate uppercase mb-2">{% trans "Status" %}</label>
|
|
<select id="status" name="status" onchange="this.form.submit()"
|
|
class="w-full px-4 py-2.5 border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-navy focus:border-transparent transition">
|
|
<option value="">{% trans "All Status" %}</option>
|
|
{% for choice in status_choices %}
|
|
<option value="{{ choice.0 }}" {% if filters.status == choice.0 %}selected{% endif %}>
|
|
{{ choice.1 }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs font-bold text-slate uppercase mb-2">{% trans "Category" %}</label>
|
|
<select id="category" name="category" onchange="this.form.submit()"
|
|
class="w-full px-4 py-2.5 border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-navy focus:border-transparent transition">
|
|
<option value="">{% trans "All Categories" %}</option>
|
|
{% for cat in categories %}
|
|
<option value="{{ cat.id }}" {% if filters.category == cat.id|stringformat:"s" %}selected{% endif %}>
|
|
{{ cat.name_en }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs font-bold text-slate uppercase mb-2">{% trans "Search" %}</label>
|
|
<div class="flex gap-2">
|
|
<input type="text" id="search" name="search"
|
|
value="{{ filters.search|default:'' }}" placeholder="{% trans 'Search messages...' %}"
|
|
class="flex-1 px-4 py-2.5 border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-navy focus:border-transparent transition">
|
|
<button type="submit" class="bg-navy text-white px-4 py-2.5 rounded-xl hover:bg-blue transition">
|
|
<i data-lucide="search" class="w-4 h-4"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
<div class="mt-4 pt-4 border-t border-slate-100">
|
|
<a href="{% url 'appreciation:appreciation_list' %}" class="text-sm font-bold text-slate hover:text-navy transition flex items-center gap-2">
|
|
<i data-lucide="rotate-ccw" class="w-4 h-4"></i> {% trans "Clear Filters" %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Appreciations List -->
|
|
<div class="bg-white rounded-2xl shadow-sm border-2 border-slate-200 overflow-hidden">
|
|
<div class="px-6 py-4 border-b-2 border-slate-200 bg-gradient-to-r from-slate-50 to-slate-100">
|
|
<h3 class="font-bold text-navy flex items-center gap-2">
|
|
<i data-lucide="heart" class="w-5 h-5 text-rose-500"></i>
|
|
{% trans "Appreciations" %}
|
|
</h3>
|
|
</div>
|
|
<div class="p-6">
|
|
{% if page_obj %}
|
|
<div class="space-y-3">
|
|
{% for appreciation in page_obj %}
|
|
<a href="{% url 'appreciation:appreciation_detail' appreciation.id %}"
|
|
class="block p-4 rounded-xl border border-slate-100 hover:border-navy/30 hover:bg-light transition group">
|
|
<div class="flex justify-between items-start gap-4">
|
|
<div class="flex-1 min-w-0">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
{% if appreciation.category %}
|
|
<span class="inline-flex items-center px-2.5 py-1 rounded-lg text-xs font-bold bg-blue-100 text-blue-700">
|
|
<i data-lucide="{{ appreciation.category.icon|default:'heart' }}" class="w-3 h-3 mr-1"></i>
|
|
{{ appreciation.category.name_en }}
|
|
</span>
|
|
{% endif %}
|
|
<h4 class="font-bold text-navy">
|
|
{% if current_tab == 'received' %}
|
|
{{ appreciation.sender.get_full_name }}
|
|
{% else %}
|
|
{{ appreciation.recipient_name }}
|
|
{% endif %}
|
|
</h4>
|
|
</div>
|
|
<p class="text-sm text-slate mb-2 line-clamp-2">{{ appreciation.message_en|truncatewords:15 }}</p>
|
|
<div class="flex items-center gap-3 text-xs text-slate">
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="clock" class="w-3 h-3"></i>
|
|
{{ appreciation.sent_at|date:"F j, Y, g:i A" }}
|
|
</span>
|
|
{% if appreciation.department %}
|
|
<span class="flex items-center gap-1">
|
|
<i data-lucide="building" class="w-3 h-3"></i>
|
|
{{ appreciation.department.name }}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="text-right">
|
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-bold {% if appreciation.status == 'acknowledged' %}bg-green-100 text-green-700{% else %}bg-blue-100 text-blue-700{% endif %}">
|
|
{{ appreciation.get_status_display }}
|
|
</span>
|
|
<div class="text-xs text-slate mt-1">
|
|
{{ appreciation.get_visibility_display }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
{% if page_obj.has_other_pages %}
|
|
<div class="mt-6 pt-6 border-t border-slate-100 flex justify-center">
|
|
<div class="flex items-center gap-2">
|
|
{% if page_obj.has_previous %}
|
|
<a href="?page={{ page_obj.previous_page_number }}&tab={{ current_tab }}{% if filters.urlencode %}&{{ filters.urlencode }}{% endif %}"
|
|
class="px-3 py-2 rounded-lg border border-slate-200 text-slate hover:bg-light transition">
|
|
{% trans "Previous" %}
|
|
</a>
|
|
{% else %}
|
|
<span class="px-3 py-2 rounded-lg border border-slate-200 text-gray-300 cursor-not-allowed">
|
|
{% trans "Previous" %}
|
|
</span>
|
|
{% endif %}
|
|
|
|
{% for num in page_obj.paginator.page_range %}
|
|
{% if page_obj.number == num %}
|
|
<span class="px-4 py-2 rounded-lg bg-navy text-white font-bold">{{ num }}</span>
|
|
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
|
|
<a href="?page={{ num }}&tab={{ current_tab }}{% if filters.urlencode %}&{{ filters.urlencode }}{% endif %}"
|
|
class="px-4 py-2 rounded-lg border border-slate-200 text-slate hover:bg-light transition">
|
|
{{ num }}
|
|
</a>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{% if page_obj.has_next %}
|
|
<a href="?page={{ page_obj.next_page_number }}&tab={{ current_tab }}{% if filters.urlencode %}&{{ filters.urlencode }}{% endif %}"
|
|
class="px-3 py-2 rounded-lg border border-slate-200 text-slate hover:bg-light transition">
|
|
{% trans "Next" %}
|
|
</a>
|
|
{% else %}
|
|
<span class="px-3 py-2 rounded-lg border border-slate-200 text-gray-300 cursor-not-allowed">
|
|
{% trans "Next" %}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% else %}
|
|
<div class="text-center py-12">
|
|
<div class="w-16 h-16 bg-rose-50 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
<i data-lucide="heart" class="w-8 h-8 text-rose-400"></i>
|
|
</div>
|
|
<h4 class="text-lg font-bold text-navy mb-2">
|
|
{% if current_tab == 'received' %}
|
|
{% trans "No appreciations received yet" %}
|
|
{% else %}
|
|
{% trans "No appreciations sent yet" %}
|
|
{% endif %}
|
|
</h4>
|
|
<p class="text-slate mb-4">{% trans "Start sharing appreciation with your colleagues!" %}</p>
|
|
<a href="{% url 'appreciation:appreciation_send' %}" class="bg-navy text-white px-5 py-2.5 rounded-xl text-sm font-bold hover:bg-blue transition inline-flex items-center gap-2">
|
|
<i data-lucide="send" class="w-4 h-4"></i> {% trans "Send Your First Appreciation" %}
|
|
</a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
{{ block.super }}
|
|
<script>
|
|
// Refresh summary statistics periodically (every 30 seconds)
|
|
function refreshSummary() {
|
|
fetch("{% url 'appreciation:appreciation_summary_ajax' %}")
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
document.getElementById('receivedCount').textContent = data.total_received || 0;
|
|
document.getElementById('sentCount').textContent = data.total_sent || 0;
|
|
document.getElementById('badgesCount').textContent = data.badges_earned || 0;
|
|
})
|
|
.catch(error => {
|
|
console.error('Error refreshing summary:', error);
|
|
});
|
|
}
|
|
|
|
// Refresh summary every 30 seconds
|
|
setInterval(refreshSummary, 30000);
|
|
|
|
// Initial summary refresh after page load
|
|
setTimeout(refreshSummary, 1000);
|
|
</script>
|
|
{% endblock %}
|