Redesign backend layout: dark sidebar + slim topbar
Moves from a floating-card sidebar to a full-height dark sidebar (bg-neutral) with the club logo at the top and logged-in user pinned to the bottom. The topbar becomes a slim bar containing only the mobile hamburger, notifications, and logout — no duplicate logo. - base.html: DaisyUI drawer with h-screen/overflow-hidden so the sidebar is sticky and the content column scrolls independently. The #content div is now the white card (bg-base-100 rounded-xl) so HTMX innerHTML swaps stay inside it. - member_filter.html: replaces the .action_bar component with a clean flex row (title + Add button) above a plain filter form. Table and mobile card list are unchanged. - styles.css: adds .sidebar-nav scoped overrides for menu link hover/active colours on the dark sidebar; reduces h1.page-title bottom margin from mb-12 to mb-4. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,144 +4,127 @@
|
||||
{% load django_htmx %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
|
||||
<html lang="en" class="bg-base-200">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta viewport="width=device-width, initial-scale=1.0">
|
||||
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>
|
||||
{% if config.TF_CLUB_NAME != "TeamForge" %}{{ config.TF_CLUB_NAME }} - {% endif %}TeamForge
|
||||
</title>
|
||||
|
||||
<title>
|
||||
{% if config.TF_CLUB_NAME != "TeamForge" %}{{ config.TF_CLUB_NAME }} - {% endif %}TeamForge
|
||||
</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js@11.1.0/public/assets/styles/choices.min.css"/>
|
||||
<script src="https://cdn.jsdelivr.net/npm/choices.js@11.1.0/public/assets/scripts/choices.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js@11.1.0/public/assets/styles/choices.min.css"/>
|
||||
<script src="https://cdn.jsdelivr.net/npm/choices.js@11.1.0/public/assets/scripts/choices.min.js"></script>
|
||||
<link href="{% static "css/fontawesome.css" %}" rel="stylesheet"/>
|
||||
<link href="{% static "css/solid.css" %}" rel="stylesheet"/>
|
||||
<link href="{% static "css/regular.css" %}" rel="stylesheet"/>
|
||||
<link href="{% static "css/brands.css" %}" rel="stylesheet"/>
|
||||
|
||||
<link href="{% static "css/fontawesome.css" %}" rel="stylesheet"/>
|
||||
<link href="{% static "css/solid.css" %}" rel="stylesheet"/>
|
||||
<link href="{% static "css/regular.css" %}" rel="stylesheet"/>
|
||||
<link href="{% static "css/brands.css" %}" rel="stylesheet"/>
|
||||
{% tailwind_css %}
|
||||
{% htmx_script %}
|
||||
</head>
|
||||
|
||||
{% tailwind_css %}
|
||||
{% htmx_script %}
|
||||
<body hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
|
||||
|
||||
<style>
|
||||
.navbar-shrink {
|
||||
height: 3rem !important;
|
||||
transition: height 0.2s ease;
|
||||
}
|
||||
<!--
|
||||
DaisyUI drawer layout:
|
||||
- drawer-side = dark sidebar (always visible on lg, off-canvas drawer on mobile)
|
||||
- drawer-content = topbar + scrollable page content + footer
|
||||
|
||||
.navbar-normal {
|
||||
height: 6rem;
|
||||
transition: height 0.2s ease;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
h-screen + overflow-hidden on .drawer keeps the viewport fixed.
|
||||
overflow-y-auto on .drawer-content lets the right column scroll independently.
|
||||
sticky top-0 on the topbar header keeps it pinned within that scrolling column.
|
||||
-->
|
||||
<div class="drawer lg:drawer-open h-screen overflow-hidden">
|
||||
<input type="checkbox" id="sidebar-toggle" class="drawer-toggle">
|
||||
|
||||
<body class="flex flex-col h-dvh overflow-hidden" hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'>
|
||||
<!-- NAVBAR -->
|
||||
<header id="mainNavbar" class="navbar-shrink navbar bg-base-100 sticky top-0 z-50 shadow">
|
||||
<div class="flex-none lg:hidden">
|
||||
<!-- Mobile sidebar toggle -->
|
||||
<label for="sidebar-toggle" class="btn btn-square btn-ghost">
|
||||
<i class="fa-solid fa-bars text-xl"></i>
|
||||
</label>
|
||||
</div>
|
||||
<!-- ═══ SIDEBAR ═══════════════════════════════════════════════════════ -->
|
||||
<div class="drawer-side z-50">
|
||||
<label for="sidebar-toggle" aria-label="close sidebar" class="drawer-overlay"></label>
|
||||
|
||||
<div class="flex-1 flex items-center gap-3 pl-2">
|
||||
<img src="{% static config.TF_CLUB_LOGO %}" class="hidden lg:inline h-10 {% if config.TF_CLUB_LOGO != "teamforge/logo.png" %}mask mask-circle{% endif %}" alt="{{ config.TF_CLUB_NAME }} logo">
|
||||
<span class="text-xl font-bold font-jersey">{{ config.TF_CLUB_NAME }}</span>
|
||||
</div>
|
||||
<aside class="w-64 min-h-full bg-neutral text-neutral-content flex flex-col">
|
||||
|
||||
<div class="flex flex-row items-center gap-1 lg:gap-4 pr-2">
|
||||
<!-- Notifications -->
|
||||
<button class="btn btn-ghost btn-circle">
|
||||
<i class="fa-solid fa-bell text-xl"></i>
|
||||
</button>
|
||||
|
||||
<!-- Avatar -->
|
||||
<div class="dropdown dropdown-end">
|
||||
{% avatar first_name=request.user.first_name last_name=request.user.last_name button=True %}
|
||||
<ul tabindex="-1" class="menu menu-sm dropdown-content bg-base-100 rounded-box z-1 mt-3 w-52 p-2 shadow">
|
||||
<li>
|
||||
<a class="justify-between">
|
||||
Profile
|
||||
<span class="badge">New</span>
|
||||
</a>
|
||||
</li>
|
||||
<li><a>Settings</a></li>
|
||||
<li><a>Logout</a></li>
|
||||
</ul>
|
||||
<!-- Brand / club identity -->
|
||||
<div class="flex items-center gap-3 px-5 py-5 shrink-0 border-b border-white/10">
|
||||
<img src="{% static config.TF_CLUB_LOGO %}"
|
||||
class="h-9 w-9 shrink-0 object-cover {% if config.TF_CLUB_LOGO != "teamforge/logo.png" %}mask mask-circle{% else %}mask mask-squircle{% endif %}"
|
||||
alt="{{ config.TF_CLUB_NAME }} logo">
|
||||
<span class="text-lg font-bold font-jersey leading-tight">{{ config.TF_CLUB_NAME }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Login/Logout -->
|
||||
{% if user.is_authenticated %}
|
||||
<a href="" class="btn btn-outline btn-sm hidden lg:flex">Logout</a>
|
||||
{% else %}
|
||||
<a href="" class="btn btn-outline btn-sm hidden lg:flex">Login</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="drawer lg:drawer-open flex-1 min-h-0">
|
||||
<!-- Hidden checkbox for mobile sidebar -->
|
||||
<input type="checkbox" id="sidebar-toggle" class="drawer-toggle">
|
||||
|
||||
<!-- SIDEBAR -->
|
||||
<aside class="drawer-side z-60 lg:z-auto min-w-fit h-full lg:h-fit">
|
||||
<label for="sidebar-toggle" class="drawer-overlay"></label>
|
||||
|
||||
<div class="w-64 bg-base-100 border-r p-4 h-full lg:h-fit lg:border lg:border-base-300 lg:m-4 lg:mr-2 lg:rounded-xl">
|
||||
<ul class="menu w-full">
|
||||
<!-- Navigation — flex-1 + overflow-y-auto so long nav lists scroll
|
||||
sidebar-nav scopes the dark-bg colour overrides in styles.css -->
|
||||
<nav class="flex-1 overflow-y-auto px-3 py-4 sidebar-nav">
|
||||
<ul class="menu w-full p-0 gap-0.5">
|
||||
{% block sidebar %}{% endblock sidebar %}
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
</nav>
|
||||
|
||||
<!-- MAIN CONTENT-->
|
||||
<div class="drawer-content flex w-full overflow-y-auto">
|
||||
<main class="bg-base-100 border border-base-300 rounded-xl m-4 ml-2 p-6 w-full h-fit">
|
||||
{% include "backend/partials/messages.html" %}
|
||||
|
||||
<div id="content">
|
||||
{% block content %}
|
||||
<h1 class="text-3xl font-bold">Welcome!</h1>
|
||||
<p>This is your main content area.</p>
|
||||
{% endblock %}
|
||||
<!-- Logged-in user pinned to bottom of sidebar -->
|
||||
<div class="flex items-center gap-3 px-4 py-4 shrink-0 border-t border-white/10">
|
||||
{% avatar first_name=request.user.first_name last_name=request.user.last_name %}
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-sm font-semibold text-neutral-content truncate">{{ request.user.get_full_name }}</p>
|
||||
<p class="text-xs text-neutral-content/50 truncate">{{ request.user.email }}</p>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
</div>
|
||||
<!-- ════════════════════════════════════════════════════════════════════ -->
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer class="footer footer-center p-4 bg-neutral text-neutral-content">
|
||||
<p>© {% now "Y" %} TeamForge — All rights reserved.</p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
document.body.addEventListener("menuHighlight", (event) => {
|
||||
const active = event.detail.value;
|
||||
document.querySelectorAll(".menu-item").forEach(el => {
|
||||
el.classList.toggle("menu-active", el.dataset.menu === active);
|
||||
});
|
||||
<!-- ═══ MAIN COLUMN ══════════════════════════════════════════════════ -->
|
||||
<div class="drawer-content flex flex-col overflow-y-auto bg-base-200">
|
||||
|
||||
<!-- Slim top bar -->
|
||||
<header class="navbar bg-base-100 border-b border-base-300 sticky top-0 z-30 shrink-0 min-h-14 px-4 gap-2 shadow-xs">
|
||||
<!-- Mobile only: open sidebar -->
|
||||
<label for="sidebar-toggle" class="btn btn-ghost btn-square btn-sm lg:hidden">
|
||||
<i class="fa-solid fa-bars"></i>
|
||||
</label>
|
||||
|
||||
<div class="flex-1"></div>
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
<button class="btn btn-ghost btn-circle btn-sm" aria-label="Notifications">
|
||||
<i class="fa-solid fa-bell"></i>
|
||||
</button>
|
||||
{% if user.is_authenticated %}
|
||||
<a href="" class="btn btn-ghost btn-sm">Logout</a>
|
||||
{% else %}
|
||||
<a href="" class="btn btn-ghost btn-sm">Login</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Page content — the white card is the #content div itself so
|
||||
HTMX swaps (innerHTML) stay inside the card without re-wrapping -->
|
||||
<main class="flex-1 p-4 lg:p-6">
|
||||
<div id="content" class="bg-base-100 rounded-xl p-4 lg:p-6 min-h-full">
|
||||
{% include "backend/partials/messages.html" %}
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="text-center text-xs text-base-content/30 py-3 shrink-0">
|
||||
© {% now "Y" %} TeamForge — All rights reserved.
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
<!-- ════════════════════════════════════════════════════════════════════ -->
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Fired by HTMXViewMixin's HX-Trigger header to highlight the active menu item
|
||||
document.body.addEventListener("menuHighlight", (event) => {
|
||||
const active = event.detail.value;
|
||||
document.querySelectorAll(".menu-item").forEach(el => {
|
||||
el.classList.toggle("menu-active", el.dataset.menu === active);
|
||||
});
|
||||
|
||||
{% comment %}// Shrinking navbar on scroll
|
||||
const navbar = document.getElementById("mainNavbar");
|
||||
let lastScroll = 0; window.addEventListener("scroll", () => {
|
||||
const current = window.scrollY;
|
||||
|
||||
if (current > lastScroll && current > 50) {
|
||||
navbar.classList.add("navbar-shrink");
|
||||
navbar.classList.remove("navbar-normal");
|
||||
} else {
|
||||
navbar.classList.add("navbar-normal");
|
||||
navbar.classList.remove("navbar-shrink");
|
||||
}
|
||||
|
||||
lastScroll = current;
|
||||
});{% endcomment %}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user