Files
project-managment/templates/components/topbar.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

64 lines
9.2 KiB
HTML

<nav class="fi-topbar h-16 bg-white dark:bg-gray-950 border-b border-gray-200 dark:border-gray-600 flex items-center gap-4 px-4 md:px-6 shrink-0">
<button x-show="!sidebarOpen" @click="sidebarOpen = true; localStorage.setItem('sidebarOpen', true); document.documentElement.classList.remove('sidebar-collapsed')" x-cloak class="fi-topbar-open-sidebar-btn hidden lg:flex items-center justify-center w-10 h-10 rounded-lg text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"/></svg>
</button>
<button x-show="sidebarOpen" @click="sidebarOpen = false; localStorage.setItem('sidebarOpen', false); document.documentElement.classList.add('sidebar-collapsed')" x-cloak class="fi-topbar-close-sidebar-btn hidden lg:flex items-center justify-center w-10 h-10 rounded-lg text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg>
</button>
<button @click="mobileSidebar = true" class="lg:hidden flex items-center justify-center w-10 h-10 rounded-lg text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"/></svg>
</button>
<div class="fi-topbar-start flex items-center gap-3">
<span class="text-base font-semibold text-gray-900 dark:text-white hidden sm:inline">{{ brand_name }}</span>
</div>
<div class="fi-topbar-end ml-auto flex items-center gap-1">
<div id="notif-bell-container" x-data="{ open: false }" class="relative">
<button @click="open = !open" class="flex items-center justify-center w-10 h-10 rounded-lg text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors relative">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0"/></svg>
<span id="notif-badge" class="absolute -top-0.5 -right-0.5 w-4 h-4 bg-blue-500 text-white text-[10px] font-bold rounded-full flex items-center justify-center ring-2 ring-white dark:ring-gray-700" hx-get="/dashboard/notifications/unread-count/" hx-trigger="load, every[30s]" hx-swap="innerHTML">0</span>
</button>
<div x-show="open" @click.outside="open = false" x-cloak x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 scale-95" x-transition:enter-end="opacity-100 scale-100" class="absolute right-0 mt-2 w-80 bg-white dark:bg-gray-950 rounded-xl shadow-2xl border border-gray-200 dark:border-gray-600 z-50 max-h-96 overflow-y-auto"
hx-get="/dashboard/notifications/dropdown/" hx-trigger="load" hx-swap="innerHTML">
</div>
</div>
<div x-data="{ open: false }" class="relative">
<button @click="open = !open" class="flex items-center gap-2 py-1.5 pl-2 pr-3 rounded-lg text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors">
{% if request.user.profile_image %}
<img src="{{ request.user.profile_image.url }}" alt="{{ request.user.get_full_name|default:request.user.username }}" class="w-8 h-8 rounded-full object-cover shrink-0">
{% else %}
<div class="w-8 h-8 rounded-full flex items-center justify-center text-white text-sm font-medium shrink-0" style="background-color: var(--color-primary, #3b82f6)">{{ request.user.get_full_name|default:request.user.username|make_list|first|upper }}</div>
{% endif %}
<span class="hidden sm:inline text-sm font-medium">{{ request.user.get_full_name|default:request.user.username }}</span>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.5 8.25l-7.5 7.5-7.5-7.5"/></svg>
</button>
<div x-show="open" @click.outside="open = false" x-cloak x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 scale-95" x-transition:enter-end="opacity-100 scale-100" class="absolute right-0 mt-2 w-56 bg-white dark:bg-gray-950 rounded-xl shadow-2xl border border-gray-200 dark:border-gray-600 z-50 py-1">
<div class="px-4 py-3 border-b border-gray-100 dark:border-gray-600">
<p class="text-sm font-medium text-gray-900 dark:text-white">{{ request.user.get_full_name|default:request.user.username }}</p>
<p class="text-xs text-gray-500 dark:text-gray-400 mt-0.5">{{ request.user.email }}</p>
</div>
<a href="{% url 'user-detail' request.user.id %}" class="flex items-center gap-3 px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.75 6a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0zM4.501 20.118a7.5 7.5 0 0114.998 0A17.933 17.933 0 0112 21.75c-2.676 0-5.216-.584-7.499-1.632z"/></svg>
Profile
</a>
<a href="{% url 'ui-settings' %}" class="flex items-center gap-3 px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
Settings
</a>
<div class="border-t border-gray-100 dark:border-gray-600 my-1"></div>
<div class="px-4 py-2 flex items-center justify-between">
<span class="text-sm text-gray-700 dark:text-gray-300">Dark mode</span>
<button id="dm-btn" onclick="toggleDarkMode()" type="button" role="switch" class="relative inline-flex h-5 w-9 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none">
<span class="pointer-events-none relative inline-block h-4 w-4 rounded-full bg-white shadow-sm ring-0 transition duration-200 ease-in-out"></span>
</button>
</div>
<div class="border-t border-gray-100 dark:border-gray-600 my-1"></div>
<a href="#" @click.prevent="window.confirmAction({ title: 'Logout', message: 'Are you sure you want to log out?', confirmText: 'Logout', danger: true, onConfirm: () => { const form = document.createElement('form'); form.method = 'POST'; form.action = '{% url 'logout' %}'; const csrf = document.createElement('input'); csrf.type = 'hidden'; csrf.name = 'csrfmiddlewaretoken'; csrf.value = '{{ csrf_token }}'; form.appendChild(csrf); document.body.appendChild(form); form.submit(); } })" class="flex items-center gap-3 px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15m3 0l3-3m0 0l-3-3m3 3H9"/></svg>
Logout
</a>
</div>
</div>
</div>
</nav>