misc
This commit is contained in:
@@ -69,7 +69,7 @@ if (import.meta.env.DEV) {
|
||||
content="index, follow, max-video-preview:-1, max-image-preview:large, max-snippet:-1"
|
||||
/>
|
||||
<Themes />
|
||||
<BaseHead title={whitelabel?.siteTitle ?? title} description={description} image={image ?? metaData.ogImage} isWhitelabel={!!whitelabel} />
|
||||
<BaseHead title={whitelabel?.siteTitle ?? title} description={description} image={image ?? metaData.ogImage} isWhitelabel={!!whitelabel} whitelabel={whitelabel} subsite={detectedSubsite} />
|
||||
<script>
|
||||
import "@scripts/lenisSmoothScroll.js";
|
||||
import "@scripts/main.jsx";
|
||||
|
||||
@@ -1,28 +1,185 @@
|
||||
---
|
||||
// Req specific subsite nav placeholder. Keeps markup minimal for now.
|
||||
import { Icon } from "astro-icon/components";
|
||||
import { API_URL } from "../../config";
|
||||
import { requireAuthHook } from "@/hooks/requireAuthHook";
|
||||
import { padlockIconSvg, userIconSvg, externalLinkIconSvg } from "@/utils/navAssets";
|
||||
import "@/assets/styles/nav.css";
|
||||
|
||||
const whitelabel = Astro.props?.whitelabel ?? null;
|
||||
const currentPath = Astro.url.pathname;
|
||||
const activeColor = "#111827";
|
||||
const subsitePathPrefix = "/subsites/req";
|
||||
|
||||
const links = [
|
||||
// Add req-specific nav items here in future
|
||||
const user = await requireAuthHook(Astro);
|
||||
const isLoggedIn = Boolean(user);
|
||||
const userDisplayName = user?.user ?? null;
|
||||
|
||||
type NavItem = {
|
||||
label: string;
|
||||
href: string;
|
||||
auth?: boolean;
|
||||
guestOnly?: boolean;
|
||||
icon?: "external" | "padlock" | "pirate" | string;
|
||||
onclick?: string;
|
||||
};
|
||||
|
||||
const baseNavItems: NavItem[] = [
|
||||
// { label: "Submit Request", href: "/" },
|
||||
];
|
||||
|
||||
const navItems: NavItem[] = isLoggedIn
|
||||
? [...baseNavItems, { label: "Logout", href: "#logout", onclick: "handleLogout()" }]
|
||||
: baseNavItems;
|
||||
|
||||
const visibleNavItems: NavItem[] = navItems.filter((item) => {
|
||||
if (item.auth && !isLoggedIn) return false;
|
||||
if (item.guestOnly && isLoggedIn) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
const normalize = (url) => (url || "/").replace(/\/+$/, "") || "/";
|
||||
const trimmedPath = currentPath.startsWith(subsitePathPrefix)
|
||||
? currentPath.slice(subsitePathPrefix.length) || "/"
|
||||
: currentPath;
|
||||
const normalizedCurrent = normalize(trimmedPath);
|
||||
---
|
||||
|
||||
<script src="/scripts/nav-controls.js" defer data-api-url={API_URL}></script>
|
||||
|
||||
<nav class="w-full px-4 sm:px-6 py-4 bg-transparent sticky top-0 z-50 backdrop-blur-sm bg-white/80 dark:bg-[#121212]/80 border-b border-neutral-200/50 dark:border-neutral-800/50">
|
||||
<div class="max-w-7xl mx-auto flex items-center justify-between">
|
||||
<a href="/" class="text-xl sm:text-2xl font-semibold" style={`color: ${whitelabel?.brandColor ?? 'var(--brand-color)'}`}>
|
||||
{whitelabel?.logoText ?? 'REQ'}
|
||||
</a>
|
||||
<ul class="flex items-center gap-4">
|
||||
<li>
|
||||
<button aria-label="Toggle theme" type="button" class="flex items-center justify-center w-8 h-8 rounded-md hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-colors" onclick="toggleTheme()">
|
||||
<Icon name="fa6-solid:circle-half-stroke" class="h-4 w-4 text-[#1c1c1c] dark:text-[#D4D4D4]" />
|
||||
<div class="max-w-7xl mx-auto">
|
||||
<div class="nav-bar-row flex items-center gap-4 justify-between">
|
||||
<a
|
||||
href="/"
|
||||
class="text-xl sm:text-2xl font-semibold header-text whitespace-nowrap hover:opacity-80 transition-opacity"
|
||||
>
|
||||
{whitelabel?.logoText ?? 'REQ'}
|
||||
</a>
|
||||
|
||||
<div class="desktop-nav flex items-center">
|
||||
<ul class="desktop-nav-list">
|
||||
{visibleNavItems.map((item) => {
|
||||
const isExternal = item.href?.startsWith("http");
|
||||
const isAuthedPath = item.auth ?? false;
|
||||
const normalizedHref = normalize(item.href);
|
||||
const isActive = !isExternal && (
|
||||
normalizedHref === '/'
|
||||
? normalizedCurrent === '/'
|
||||
: normalizedCurrent === normalizedHref || normalizedCurrent.startsWith(normalizedHref + '/')
|
||||
);
|
||||
|
||||
return (
|
||||
<li>
|
||||
<a
|
||||
href={item.href}
|
||||
class={isActive
|
||||
? "flex items-center gap-0 px-2.5 py-1.5 rounded-md text-xs font-medium transition-all duration-200 text-white"
|
||||
: "flex items-center gap-0 px-2.5 py-1.5 rounded-md text-xs font-medium transition-all duration-200 text-neutral-700 dark:text-neutral-300 hover:bg-neutral-100 dark:hover:bg-neutral-800"}
|
||||
style={isActive ? `background: ${activeColor}` : undefined}
|
||||
target={isExternal ? "_blank" : undefined}
|
||||
rel={(isExternal || isAuthedPath) ? "external" : undefined}
|
||||
onclick={item.onclick}
|
||||
>
|
||||
{item.label}
|
||||
{item.icon === "external" && (
|
||||
<span class="inline-flex ml-0.5" aria-hidden="true" set:html={externalLinkIconSvg}></span>
|
||||
)}
|
||||
{item.icon === "padlock" && (
|
||||
<span class="inline-flex" aria-hidden="true" set:html={padlockIconSvg}></span>
|
||||
)}
|
||||
{item.icon === "pirate" && (
|
||||
<span class="inline-flex ml-1" role="img" aria-label="Pirate flag">🏴☠️</span>
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
|
||||
<button
|
||||
aria-label="Toggle theme"
|
||||
type="button"
|
||||
class="flex items-center justify-center w-8 h-8 rounded-md hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-colors"
|
||||
onclick="toggleTheme()"
|
||||
>
|
||||
<Icon
|
||||
name="fa6-solid:circle-half-stroke"
|
||||
class="h-4 w-4 text-[#1c1c1c] dark:text-[#D4D4D4]"
|
||||
/>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="mobile-nav flex items-center gap-2">
|
||||
<button
|
||||
aria-label="Toggle theme"
|
||||
type="button"
|
||||
class="flex items-center justify-center w-9 h-9 rounded-lg hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-colors"
|
||||
onclick="toggleTheme()"
|
||||
>
|
||||
<Icon
|
||||
name="fa6-solid:circle-half-stroke"
|
||||
class="h-5 w-5 text-[#1c1c1c] dark:text-[#D4D4D4]"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="mobile-menu-btn"
|
||||
aria-label="Toggle menu"
|
||||
type="button"
|
||||
class="flex items-center justify-center w-9 h-9 rounded-lg hover:bg-neutral-100 dark:hover:bg-neutral-800 transition-colors"
|
||||
>
|
||||
<svg id="menu-icon" class="w-6 h-6 text-neutral-700 dark:text-neutral-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
||||
</svg>
|
||||
<svg id="close-icon" class="w-6 h-6 text-neutral-700 dark:text-neutral-300" style="display: none;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="mobile-menu"
|
||||
class="mobile-menu-dropdown md:hidden"
|
||||
>
|
||||
<ul class="flex flex-col gap-1 py-4">
|
||||
{visibleNavItems.map((item) => {
|
||||
const isExternal = item.href?.startsWith("http");
|
||||
const isAuthedPath = item.auth ?? false;
|
||||
const normalizedHref = normalize(item.href);
|
||||
const isActive = !isExternal && (
|
||||
normalizedHref === '/'
|
||||
? normalizedCurrent === '/'
|
||||
: normalizedCurrent === normalizedHref || normalizedCurrent.startsWith(normalizedHref + '/')
|
||||
);
|
||||
|
||||
return (
|
||||
<li>
|
||||
<a
|
||||
href={item.href}
|
||||
class={isActive
|
||||
? "flex items-center gap-0 px-4 py-3 rounded-lg text-base font-medium transition-all duration-200 text-white"
|
||||
: "flex items-center gap-0 px-4 py-3 rounded-lg text-base font-medium transition-all duration-200 text-neutral-700 dark:text-neutral-300 hover:bg-neutral-100 dark:hover:bg-neutral-800"}
|
||||
style={isActive ? `background: ${activeColor}` : undefined}
|
||||
target={isExternal ? "_blank" : undefined}
|
||||
rel={(isExternal || isAuthedPath) ? "external" : undefined}
|
||||
onclick={item.onclick}
|
||||
>
|
||||
<span>{item.label}</span>
|
||||
{item.icon === "external" && (
|
||||
<span class="inline-flex ml-0.5" aria-hidden="true" set:html={externalLinkIconSvg}></span>
|
||||
)}
|
||||
{item.icon === "padlock" && (
|
||||
<span class="inline-flex" aria-hidden="true" set:html={padlockIconSvg}></span>
|
||||
)}
|
||||
{item.icon === "pirate" && (
|
||||
<span class="inline-flex ml-1" role="img" aria-label="Pirate flag">🏴☠️</span>
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
Reference in New Issue
Block a user