Bo Nix better win the damn super bowl dude he gives me anxiety every time I watch him
This commit is contained in:
211
templates/base.html
Normal file
211
templates/base.html
Normal file
@@ -0,0 +1,211 @@
|
||||
<!doctype html>
|
||||
<html lang="en" class="h-full bg-slate-950">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
<title>{{ title or app_brand }}</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
brand: {
|
||||
50: '#eff6ff',
|
||||
100: '#dbeafe',
|
||||
600: '#2563eb',
|
||||
700: '#1d4ed8'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Every box prints on its own page */
|
||||
@media print {
|
||||
.print-block {
|
||||
page-break-before: always !important;
|
||||
break-before: page !important;
|
||||
}
|
||||
|
||||
/* remove first blank page */
|
||||
.print-block:first-child {
|
||||
page-break-before: avoid !important;
|
||||
break-before: avoid !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Base print reset */
|
||||
@media print {
|
||||
html, body {
|
||||
background: #ffffff !important;
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
/* Hide app chrome */
|
||||
header,
|
||||
nav,
|
||||
footer,
|
||||
.fixed,
|
||||
#mobileNav,
|
||||
#navToggle {
|
||||
display: none !important;
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
/* Remove Tailwind background classes */
|
||||
.bg-slate-950,
|
||||
.bg-slate-900\/60,
|
||||
.bg-slate-950\/60 {
|
||||
background: #ffffff !important;
|
||||
}
|
||||
|
||||
.text-slate-100,
|
||||
.text-slate-200,
|
||||
.text-slate-300,
|
||||
.text-slate-400 {
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
/* Force links to print black */
|
||||
a {
|
||||
color: #000000 !important;
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
|
||||
/* Expand content full width */
|
||||
main {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
/* Page breaks for nice sections */
|
||||
.print-section {
|
||||
page-break-before: always;
|
||||
}
|
||||
|
||||
/* Avoid truncation */
|
||||
.no-break {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body class="h-full text-slate-100 pt-[env(safe-area-inset-top)] pb-[env(safe-area-inset-bottom)]">
|
||||
<div class="min-h-screen">
|
||||
|
||||
<!-- Topbar -->
|
||||
<header class="sticky top-0 z-50 backdrop-blur bg-slate-950/70 border-b border-slate-800">
|
||||
<div class="max-w-6xl mx-auto px-4">
|
||||
<div class="h-14 flex items-center justify-between">
|
||||
<!-- Brand -->
|
||||
<a href="{{ url_for('dashboard') }}" class="flex items-center gap-2">
|
||||
<div class="h-7 w-7 rounded bg-brand-600/20 border border-brand-600/30 grid place-items-center font-bold">SC</div>
|
||||
<span class="font-semibold tracking-wide">{{ app_brand }}</span>
|
||||
</a>
|
||||
{% if current_user.is_authenticated %}
|
||||
<!-- Desktop nav -->
|
||||
<nav class="hidden md:flex items-center gap-2 text-sm">
|
||||
<a class="px-3 py-1.5 rounded-lg border border-slate-700 hover:border-slate-500" href="{{ url_for('dashboard') }}">Home</a>
|
||||
<a class="px-3 py-1.5 rounded-lg border border-slate-700 hover:border-slate-500" href="{{ url_for('availability') }}">Your Availability</a>
|
||||
<a class="px-3 py-1.5 rounded-lg border border-slate-700 hover:border-slate-500" href="{{ url_for('request_off') }}">Request Off</a>
|
||||
<a class="px-3 py-1.5 rounded-lg border border-slate-700 hover:border-slate-500" href="{{ url_for('secret_santa') }}">Secret Santa</a>
|
||||
{% if current_user.role == 'admin' %}
|
||||
<a class="px-3 py-1.5 rounded-lg border border-slate-700 hover:border-slate-500" href="{{ url_for('admin_users') }}">Admin</a>
|
||||
{% endif %}
|
||||
<form method="post" action="{{ url_for('logout') }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<button class="px-3 py-1.5 rounded-lg bg-brand-600 text-white hover:bg-brand-700" type="submit">Logout</button>
|
||||
</form>
|
||||
</nav>
|
||||
<!-- Mobile menu -->
|
||||
<button id="navToggle" class="md:hidden inline-flex items-center justify-center h-9 w-9 rounded-lg border border-slate-700 hover:border-slate-500" aria-label="Open menu" aria-expanded="false" aria-controls="mobileNav">
|
||||
<svg id="navIconOpen" class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path stroke-linecap="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg>
|
||||
<svg id="navIconClose" class="h-5 w-5 hidden" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path stroke-linecap="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Mobile nav panel -->
|
||||
<div id="mobileNav" class="md:hidden max-w-6xl mx-auto px-4 pb-3 hidden">
|
||||
<div class="grid gap-2 text-sm">
|
||||
<a class="px-3 py-2 rounded-lg border border-slate-700 hover:border-slate-500" href="{{ url_for('dashboard') }}">Home</a>
|
||||
<a class="px-3 py-2 rounded-lg border border-slate-700 hover:border-slate-500" href="{{ url_for('availability') }}">Availability</a>
|
||||
<a class="px-3 py-2 rounded-lg border border-slate-700 hover:border-slate-500" href="{{ url_for('request_off') }}">Request Off</a>
|
||||
<a class="px-3 py-2 rounded-lg border border-slate-700 hover:border-slate-500" href="{{ url_for('secret_santa') }}">Secret Santa</a>
|
||||
{% if current_user.role == 'admin' %}
|
||||
<a class="px-3 py-2 rounded-lg border border-slate-700 hover:border-slate-500" href="{{ url_for('admin_users') }}">Admin</a>
|
||||
{% endif %}
|
||||
<form method="post" action="{{ url_for('logout') }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<button class="px-3 py-2 rounded-lg bg-brand-600 text-white hover:bg-brand-700" type="submit">Logout</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main content -->
|
||||
<main class="max-w-6xl mx-auto px-4 pt-6 pb-[6.5rem]"> <!-- ⬅️ room for bottom nav + iOS inset -->
|
||||
{% with msgs = get_flashed_messages(with_categories=true) %}
|
||||
{% if msgs %}
|
||||
<div class="space-y-2 mb-4">
|
||||
{% for cat,msg in msgs %}
|
||||
<div class="rounded-lg px-3 py-2 text-sm border {{ 'bg-emerald-500/15 border-emerald-700 text-emerald-200' if cat=='ok' else 'bg-red-500/15 border-red-700 text-red-200' }}">{{ msg }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{{ content|safe }}
|
||||
</main>
|
||||
|
||||
<!-- Bottom nav -->
|
||||
{% if current_user.is_authenticated %}
|
||||
<nav class="fixed bottom-0 inset-x-0 z-50 bg-[#111827] border-t border-gray-800 md:hidden pb-[env(safe-area-inset-bottom)]">
|
||||
<div class="flex justify-around text-sm text-gray-400">
|
||||
<a href="{{ url_for('dashboard') }}" class="flex flex-col items-center justify-center py-2 px-3 {% if 'dashboard' in request.path %}text-white{% endif %}">
|
||||
<svg class="w-5 h-5 mb-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-width="2" d="M3 12l2-2 7-7 7 7 2 2"/></svg>
|
||||
Home
|
||||
</a>
|
||||
<a href="{{ url_for('availability') }}" class="flex flex-col items-center justify-center py-2 px-3 {% if 'availability' in request.path %}text-white{% endif %}">
|
||||
<svg class="w-5 h-5 mb-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-width="2" d="M8 7V3m8 4V3M3 11h18"/></svg>
|
||||
Availability
|
||||
</a>
|
||||
<a href="{{ url_for('request_off') }}" class="flex flex-col items-center justify-center py-2 px-3 {% if 'request' in request.path %}text-white{% endif %}">
|
||||
<svg class="w-5 h-5 mb-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-width="2" d="M9 5v6m4 0h6"/></svg>
|
||||
Requests
|
||||
</a>
|
||||
<a href="{{ url_for('secret_santa') }}" class="flex flex-col items-center justify-center py-2 px-3 {% if 'secret' in request.path %}text-white{% endif %}">
|
||||
<svg class="w-5 h-5 mb-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-width="2" d="M12 14l9-5-9-5-9 5 9 5z"/></svg>
|
||||
Gifts
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
<footer class="text-xs text-center text-slate-500 py-6">© {{ app_brand }}</footer>
|
||||
</div>
|
||||
|
||||
<!-- Nav toggle -->
|
||||
<script>
|
||||
(function () {
|
||||
const btn = document.getElementById('navToggle');
|
||||
const menu = document.getElementById('mobileNav');
|
||||
const openI = document.getElementById('navIconOpen');
|
||||
const closeI = document.getElementById('navIconClose');
|
||||
if (!btn || !menu) return;
|
||||
btn.addEventListener('click', () => {
|
||||
const isHidden = menu.classList.contains('hidden');
|
||||
menu.classList.toggle('hidden');
|
||||
btn.setAttribute('aria-expanded', String(isHidden));
|
||||
openI.classList.toggle('hidden', !isHidden);
|
||||
closeI.classList.toggle('hidden', isHidden);
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user