Files
project-managment/templates/contributions/index.html
T
sankalp 71b700388b feat(templates): add polished login UI with Tailwind and Alpine.js
Enhances user experience with dark‑mode detection, responsive layout, and streamlined role selection workflow
2026-06-28 14:15:39 +05:45

143 lines
7.5 KiB
HTML

{% extends "base.html" %}
{% block title %}User Contributions{% endblock %}
{% block breadcrumb %}
<nav class="mb-4" aria-label="Breadcrumb">
<ol class="flex items-center gap-1.5 text-xs text-gray-500 dark:text-gray-400">
<li><a href="{% url 'dashboard' %}" class="hover:text-gray-700 dark:hover:text-gray-300">Dashboard</a></li>
<li class="text-gray-300 dark:text-gray-600">/</li>
<li class="text-gray-900 dark:text-white font-medium" aria-current="page">Contributions</li>
</ol>
</nav>
{% endblock %}
{% block content %}
<div class="mb-6 flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div>
<h1 class="text-2xl font-bold">User Contributions</h1>
<p class="text-gray-500 dark:text-gray-400 text-sm mt-1">GitHub-style contribution heatmap</p>
</div>
<div class="flex gap-2">
{% for year in available_years %}
<a href="/dashboard/contributions/?year={{ year }}" class="dk-btn-ghost text-xs px-3 py-1.5 {% if target_year == year %}bg-gray-100 dark:bg-gray-800{% endif %}">{{ year }}</a>
{% endfor %}
</div>
</div>
<div class="space-y-6">
{% for entry in contributions %}
<div class="dk-card divide-y divide-gray-200 dark:divide-gray-600">
<div class="px-6 py-4">
<div class="flex items-center gap-3">
{% with name=entry.user.get_full_name|default:entry.user.username %}
<div class="w-8 h-8 rounded-full bg-primary flex items-center justify-center text-white text-sm font-bold">{{ name|make_list|first|upper }}</div>
<div>
<p class="font-semibold text-sm">{{ name }}</p>
<p class="text-xs text-gray-400">Activity for {{ target_year }}</p>
</div>
{% endwith %}
</div>
</div>
<div class="px-6 py-4">
<div class="rounded-lg bg-gray-50 dark:bg-gray-800 p-3 shadow-sm ring-1 ring-gray-950/5 dark:ring-white/10 mb-4">
<div class="flex flex-col space-y-2 sm:flex-row sm:space-y-0 sm:space-x-4">
<div class="flex-1 text-center p-2 rounded-lg">
<p class="text-lg font-bold text-gray-900 dark:text-white">{{ entry.tickets_created }}</p>
<p class="text-xs font-medium text-gray-600 dark:text-gray-400">Tickets Created</p>
</div>
<div class="flex-1 text-center p-2 rounded-lg">
<p class="text-lg font-bold text-gray-900 dark:text-white">{{ entry.status_changes }}</p>
<p class="text-xs font-medium text-gray-600 dark:text-gray-400">Status Changes</p>
</div>
<div class="flex-1 text-center p-2 rounded-lg">
<p class="text-lg font-bold text-gray-900 dark:text-white">{{ entry.comments_made }}</p>
<p class="text-xs font-medium text-gray-600 dark:text-gray-400">Comments Made</p>
</div>
<div class="flex-1 text-center p-2 rounded-lg">
<p class="text-lg font-bold text-gray-900 dark:text-white">{{ entry.active_days }}</p>
<p class="text-xs font-medium text-gray-600 dark:text-gray-400">Active Days</p>
</div>
</div>
</div>
<div>
<div class="flex items-center justify-between mb-3">
<h4 class="text-sm font-semibold text-gray-900 dark:text-white">Daily Contributions</h4>
<div class="flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400">
<span>Less</span>
<div class="flex gap-1">
<div class="h-3 w-3 rounded-sm bg-gray-200 dark:bg-gray-800"></div>
<div class="h-3 w-3 rounded-sm" style="background-color:#9be9a8" data-dark="#0e4429"></div>
<div class="h-3 w-3 rounded-sm" style="background-color:#40c463" data-dark="#006d32"></div>
<div class="h-3 w-3 rounded-sm" style="background-color:#30a14e" data-dark="#26a641"></div>
<div class="h-3 w-3 rounded-sm" style="background-color:#216e39" data-dark="#39d353"></div>
</div>
<span>More</span>
</div>
</div>
<div class="rounded-xl bg-white dark:bg-gray-950 p-4 shadow-sm ring-1 ring-gray-950/5 dark:ring-white/10">
<div class="flex gap-[3px] overflow-x-auto">
<div class="flex flex-col gap-[3px] mr-1">
<div class="h-3 flex items-end text-[8px] text-gray-500 dark:text-gray-400 font-medium">Sun</div>
<div class="h-3 flex items-end text-[8px] text-gray-500 dark:text-gray-400 font-medium">Mon</div>
<div class="h-3 flex items-end text-[8px] text-gray-500 dark:text-gray-400 font-medium">Tue</div>
<div class="h-3 flex items-end text-[8px] text-gray-500 dark:text-gray-400 font-medium">Wed</div>
<div class="h-3 flex items-end text-[8px] text-gray-500 dark:text-gray-400 font-medium">Thu</div>
<div class="h-3 flex items-end text-[8px] text-gray-500 dark:text-gray-400 font-medium">Fri</div>
<div class="h-3 flex items-end text-[8px] text-gray-500 dark:text-gray-400 font-medium">Sat</div>
</div>
{% for week in entry.weeks %}
<div class="flex flex-col gap-[3px]">
{% for day in week %}
<div class="contribution-square h-3 w-3 rounded-sm cursor-pointer transition-all duration-150 hover:ring-2 hover:ring-primary-500/50"
style="{% if day.level == 0 %}background-color: #ebedf0{% elif day.level == 1 %}background-color: #9be9a8{% elif day.level == 2 %}background-color: #40c463{% elif day.level == 3 %}background-color: #30a14e{% else %}background-color: #216e39{% endif %}"
data-dark-style="{% if day.level == 0 %}background-color: #161b22{% elif day.level == 1 %}background-color: #0e4429{% elif day.level == 2 %}background-color: #006d32{% elif day.level == 3 %}background-color: #26a641{% else %}background-color: #39d353{% endif %}"
title="{{ day.date }}: {{ day.count }} contributions"></div>
{% endfor %}
</div>
{% empty %}
<p class="text-xs text-gray-400 py-4">No data for this period</p>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
{% empty %}
<div class="text-center py-12 text-gray-400">
<i class="fas fa-chart-bar text-4xl mb-3"></i>
<p class="text-lg font-medium">No contributions data</p>
</div>
{% endfor %}
</div>
<script>
(function() {
function updateContributionColors() {
const isDark = document.documentElement.classList.contains('dark');
document.querySelectorAll('.contribution-square').forEach(function(el) {
if (isDark) {
el.setAttribute('style', el.getAttribute('data-dark-style') || 'background-color: #161b22');
} else {
const lightStyle = el.getAttribute('style').split(';')[0] + ';';
if (!el.hasAttribute('data-light-style')) {
el.setAttribute('data-light-style', el.getAttribute('style'));
}
el.setAttribute('style', el.getAttribute('data-light-style'));
}
});
document.querySelectorAll('[data-dark]').forEach(function(el) {
if (isDark) {
el.setAttribute('style', 'background-color: ' + el.getAttribute('data-dark'));
} else {
el.setAttribute('style', el.getAttribute('data-light') ? 'background-color: ' + el.getAttribute('data-light') : el.style.cssText.split(';')[0] + ';');
}
});
}
updateContributionColors();
new MutationObserver(function(mutations) {
mutations.forEach(function(m) {
if (m.type === 'attributes' && m.attributeName === 'class') updateContributionColors();
});
}).observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
})();
</script>
{% endblock %}