feat: Add user display in navigation

misc: styling/css cleanup
This commit is contained in:
2025-11-26 10:43:34 -05:00
parent 21b112f460
commit d671fbc130
5 changed files with 169 additions and 97 deletions

126
src/assets/styles/nav.css Normal file
View File

@@ -0,0 +1,126 @@
/* Desktop navigation - visible on medium screens and up */
.desktop-nav {
display: none;
}
@media (min-width: 768px) {
.desktop-nav {
display: flex;
}
}
/* Mobile navigation - visible below medium screens */
.mobile-nav {
display: flex;
}
@media (min-width: 768px) {
.mobile-nav {
display: none;
}
}
/* Mobile menu dropdown */
.mobile-menu-dropdown {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-in-out, opacity 0.3s ease-in-out;
opacity: 0;
}
.mobile-menu-dropdown.open {
max-height: 500px;
opacity: 1;
}
@media (min-width: 768px) {
.mobile-menu-dropdown {
display: none;
}
}
nav {
transition: background-color 0.2s ease, border-color 0.2s ease;
}
.nav-bar-row {
width: 100%;
}
@media (min-width: 768px) {
.desktop-nav {
flex: 1;
display: flex;
align-items: center;
min-width: 0;
margin-left: 2rem;
gap: clamp(0.75rem, 1.5vw, 1.25rem);
}
}
.desktop-nav-list {
display: flex;
flex: 1;
justify-content: flex-end;
align-items: center;
gap: clamp(0.75rem, 1.5vw, 1.25rem);
margin: 0;
padding: 0;
}
.desktop-nav-list li {
flex-shrink: 1;
}
.desktop-nav-list a {
white-space: nowrap;
}
.nav-user-inline {
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0.3rem 0.85rem;
border-radius: 999px;
border: 1px solid rgba(148, 163, 184, 0.4);
font-size: 0.82rem;
font-weight: 500;
color: #1e293b;
background: linear-gradient(120deg, rgba(255, 255, 255, 0.9), rgba(226, 232, 240, 0.85));
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.18), inset 0 1px 0 rgba(255, 255, 255, 0.6);
}
[data-theme="dark"] .nav-user-inline {
color: #f1f5f9;
border-color: rgba(59, 130, 246, 0.25);
background: linear-gradient(120deg, rgba(15, 23, 42, 0.85), rgba(30, 41, 59, 0.7));
box-shadow: 0 14px 35px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
.nav-user-inline__icon {
display: inline-flex;
width: 0.95rem;
height: 0.95rem;
color: inherit;
}
.nav-user-inline__icon svg {
width: 100%;
height: 100%;
}
.nav-user-inline__label {
text-transform: uppercase;
font-size: 0.65rem;
letter-spacing: 0.08em;
opacity: 0.8;
}
.nav-user-inline__name {
font-weight: 600;
white-space: nowrap;
}
.nav-user-inline--mobile {
margin: 0.75rem 0;
}

View File

@@ -6,6 +6,7 @@ import { AutoComplete } from "primereact/autocomplete";
import { authFetch } from "@/utils/authFetch"; import { authFetch } from "@/utils/authFetch";
import BreadcrumbNav from "./BreadcrumbNav"; import BreadcrumbNav from "./BreadcrumbNav";
import { API_URL, ENVIRONMENT } from "@/config"; import { API_URL, ENVIRONMENT } from "@/config";
import "./MediaRequestForm.css";
export default function MediaRequestForm() { export default function MediaRequestForm() {
const [type, setType] = useState("artist"); const [type, setType] = useState("artist");

View File

@@ -1,29 +1,22 @@
--- ---
import { metaData, API_URL } from "../config"; import { metaData, API_URL } from "../config";
import { Icon } from "astro-icon/components"; import { Icon } from "astro-icon/components";
import { requireAuthHook } from "@/hooks/requireAuthHook";
import { padlockIconSvg, userIconSvg, externalLinkIconSvg } from "@/utils/navAssets";
import "@/assets/styles/nav.css";
const isLoggedIn = Boolean(Astro.cookies.get('access_token') || Astro.cookies.get('refresh_token')); const user = await requireAuthHook(Astro);
const isLoggedIn = Boolean(user);
const userDisplayName = user?.user ?? null;
const userInitial = userDisplayName ? String(userDisplayName).charAt(0).toUpperCase() : '?';
const padlockIconSvg = `
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2.2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.5 11V9a3.5 3.5 0 1 1 7 0v2" />
<rect x="7" y="11" width="10" height="8.5" rx="1.8" />
<circle cx="12" cy="15" r="1.2" fill="currentColor" />
</svg>
`;
const externalLinkIconSvg = `
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
</svg>
`;
const navItems = [ const navItems = [
{ label: "Home", href: "/" }, { label: "Home", href: "/" },
{ label: "Radio", href: "/radio" }, { label: "Radio", href: "/radio" },
{ label: "Memes", href: "/memes" }, { label: "Memes", href: "/memes" },
{ label: "TRip", href: "/TRip", auth: true, icon: "pirate" },
{ label: "Lighting", href: "/lighting", auth: true }, { label: "Lighting", href: "/lighting", auth: true },
{ label: "TRip", href: "/TRip", auth: true, icon: "pirate" },
{ label: "Status", href: "https://status.boatson.boats", icon: "external" }, { label: "Status", href: "https://status.boatson.boats", icon: "external" },
{ label: "Git", href: "https://kode.boatson.boats", icon: "external" }, { label: "Git", href: "https://kode.boatson.boats", icon: "external" },
{ label: "Login", href: "/login", guestOnly: true }, { label: "Login", href: "/login", guestOnly: true },
@@ -102,6 +95,13 @@ const currentPath = Astro.url.pathname;
})} })}
</ul> </ul>
{isLoggedIn && userDisplayName && (
<div class="nav-user-inline" title={`Logged in as ${userDisplayName}`}>
<span class="nav-user-inline__icon" aria-hidden="true" set:html={userIconSvg}></span>
<span class="nav-user-inline__name">{userDisplayName}</span>
</div>
)}
<!-- Theme Toggle Desktop --> <!-- Theme Toggle Desktop -->
<button <button
aria-label="Toggle theme" aria-label="Toggle theme"
@@ -152,6 +152,12 @@ const currentPath = Astro.url.pathname;
id="mobile-menu" id="mobile-menu"
class="mobile-menu-dropdown md:hidden" class="mobile-menu-dropdown md:hidden"
> >
{isLoggedIn && userDisplayName && (
<div class="nav-user-inline nav-user-inline--mobile" title={`Logged in as ${userDisplayName}`}>
<span class="nav-user-inline__icon" aria-hidden="true" set:html={userIconSvg}></span>
<span class="nav-user-inline__name">{userDisplayName}</span>
</div>
)}
<ul class="flex flex-col gap-1 py-4"> <ul class="flex flex-col gap-1 py-4">
{visibleNavItems.map((item) => { {visibleNavItems.map((item) => {
const isExternal = item.href?.startsWith("http"); const isExternal = item.href?.startsWith("http");
@@ -195,84 +201,3 @@ const currentPath = Astro.url.pathname;
</div> </div>
</div> </div>
</nav> </nav>
<style>
/* Desktop navigation - visible on medium screens and up */
.desktop-nav {
display: none;
}
@media (min-width: 768px) {
.desktop-nav {
display: flex;
}
}
/* Mobile navigation - visible below medium screens */
.mobile-nav {
display: flex;
}
@media (min-width: 768px) {
.mobile-nav {
display: none;
}
}
/* Mobile menu dropdown */
.mobile-menu-dropdown {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-in-out, opacity 0.3s ease-in-out;
opacity: 0;
}
.mobile-menu-dropdown.open {
max-height: 500px;
opacity: 1;
}
@media (min-width: 768px) {
.mobile-menu-dropdown {
display: none;
}
}
nav {
transition: background-color 0.2s ease, border-color 0.2s ease;
}
.nav-bar-row {
width: 100%;
}
@media (min-width: 768px) {
.desktop-nav {
flex: 1;
display: flex;
align-items: center;
min-width: 0;
margin-left: 2rem;
gap: clamp(0.75rem, 1.5vw, 1.25rem);
}
}
.desktop-nav-list {
display: flex;
flex: 1;
justify-content: flex-end;
align-items: center;
gap: clamp(0.75rem, 1.5vw, 1.25rem);
margin: 0;
padding: 0;
}
.desktop-nav-list li {
flex-shrink: 1;
}
.desktop-nav-list a {
white-space: nowrap;
}
</style>

View File

@@ -13,7 +13,7 @@ if (!user || !user.roles.includes('lighting')) {
<Base> <Base>
<section> <section>
<div class="prose prose-neutral dark:prose-invert"> <div class="prose prose-neutral dark:prose-invert">
<Root child="Lighting" user={user} client:only="react" /> <Root child="Lighting" user?={user} client:only="react" />
</div> </div>
</section> </section>
</Base> </Base>

20
src/utils/navAssets.js Normal file
View File

@@ -0,0 +1,20 @@
export const padlockIconSvg = `
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2.2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.5 11V9a3.5 3.5 0 1 1 7 0v2" />
<rect x="7" y="11" width="10" height="8.5" rx="1.8" />
<circle cx="12" cy="15" r="1.2" fill="currentColor" />
</svg>
`;
export const userIconSvg = `
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="9" r="3.2" />
<path d="M5.5 19.5a6.5 6.5 0 0 1 13 0" />
</svg>
`;
export const externalLinkIconSvg = `
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
</svg>
`;