237 lines
7.9 KiB
TypeScript
Executable File
237 lines
7.9 KiB
TypeScript
Executable File
"use client";
|
|
|
|
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import { useRouter } from "next/navigation";
|
|
import { usePathname } from "next/navigation";
|
|
import { useEffect, useState } from "react";
|
|
import { Moon, Sun } from "lucide-react";
|
|
import { useTheme } from "next-themes";
|
|
import { DEMO_AUTH_EMAIL_COOKIE, DEMO_AUTH_ROLE_COOKIE } from "@/lib/auth/demoAuth";
|
|
import { supabaseBrowser } from "@/lib/supabase/browser";
|
|
import { Button } from "@/components/ui/button";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
type NavLink = {
|
|
href: string;
|
|
label: string;
|
|
};
|
|
|
|
const navLinks: NavLink[] = [
|
|
{ href: "/courses", label: "Formación Académica" },
|
|
{ href: "/case-studies", label: "Casos prácticos" },
|
|
{ href: "/practice", label: "Retos" },
|
|
{ href: "/eventos", label: "Eventos" },
|
|
{ href: "/noticias", label: "Noticias" },
|
|
{ href: "/comunidad", label: "Comunidad" },
|
|
{ href: "/sobre-acve", label: "Sobre ACVE" },
|
|
];
|
|
|
|
export default function Navbar() {
|
|
const pathname = usePathname();
|
|
const router = useRouter();
|
|
const [userEmail, setUserEmail] = useState<string | null>(null);
|
|
const [isTeacher, setIsTeacher] = useState(false);
|
|
const [mounted, setMounted] = useState(false);
|
|
const { resolvedTheme, setTheme } = useTheme();
|
|
|
|
useEffect(() => {
|
|
const client = supabaseBrowser();
|
|
if (!client) {
|
|
const cookieMap = new Map(
|
|
document.cookie
|
|
.split(";")
|
|
.map((entry) => entry.trim())
|
|
.filter(Boolean)
|
|
.map((entry) => {
|
|
const [key, ...rest] = entry.split("=");
|
|
return [key, decodeURIComponent(rest.join("="))] as const;
|
|
}),
|
|
);
|
|
|
|
const email = cookieMap.get(DEMO_AUTH_EMAIL_COOKIE) ?? null;
|
|
const role = cookieMap.get(DEMO_AUTH_ROLE_COOKIE) ?? "";
|
|
setUserEmail(email);
|
|
setIsTeacher(role === "teacher");
|
|
return;
|
|
}
|
|
|
|
let mounted = true;
|
|
|
|
const fetchSession = async () => {
|
|
const {
|
|
data: { user },
|
|
} = await client.auth.getUser();
|
|
if (!mounted) return;
|
|
const email = user?.email ?? null;
|
|
setUserEmail(email);
|
|
if (!user) {
|
|
setIsTeacher(false);
|
|
return;
|
|
}
|
|
try {
|
|
const res = await fetch("/api/auth/session");
|
|
if (!mounted) return;
|
|
const data = await res.json();
|
|
setIsTeacher(data.isTeacher === true);
|
|
} catch {
|
|
if (!mounted) return;
|
|
setIsTeacher(false);
|
|
}
|
|
};
|
|
|
|
fetchSession();
|
|
|
|
const {
|
|
data: { subscription },
|
|
} = client.auth.onAuthStateChange(() => {
|
|
fetchSession();
|
|
});
|
|
|
|
return () => {
|
|
mounted = false;
|
|
subscription.unsubscribe();
|
|
};
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
setMounted(true);
|
|
}, []);
|
|
|
|
const handleLogout = async () => {
|
|
const loginSwitchUrl = "/auth/login?switchUser=1&redirectTo=/courses";
|
|
document.cookie = `${DEMO_AUTH_EMAIL_COOKIE}=; path=/; max-age=0`;
|
|
document.cookie = `${DEMO_AUTH_ROLE_COOKIE}=; path=/; max-age=0`;
|
|
|
|
const client = supabaseBrowser();
|
|
if (!client) {
|
|
setUserEmail(null);
|
|
setIsTeacher(false);
|
|
router.replace(loginSwitchUrl);
|
|
router.refresh();
|
|
return;
|
|
}
|
|
|
|
await client.auth.signOut();
|
|
setUserEmail(null);
|
|
setIsTeacher(false);
|
|
router.replace(loginSwitchUrl);
|
|
router.refresh();
|
|
};
|
|
|
|
const isDark = resolvedTheme === "dark";
|
|
const loginHref = userEmail ? "/profile" : "/auth/login";
|
|
const loginLabel = userEmail ? "Mi cuenta" : "Ingresa";
|
|
|
|
const isNavActive = (href: string) => {
|
|
return pathname === href || pathname?.startsWith(`${href}/`);
|
|
};
|
|
|
|
return (
|
|
<header className="sticky top-0 z-50 border-b border-border/70 bg-background/90 backdrop-blur supports-[backdrop-filter]:bg-background/70">
|
|
<div className="border-b border-border/60 bg-card/80">
|
|
<div className="mx-auto flex w-full max-w-[1300px] items-center gap-3 px-4 py-2 text-xs text-muted-foreground md:px-6">
|
|
<div className="hidden items-center gap-4 sm:flex">
|
|
<Link className="hover:text-foreground" href="/auth/signup">
|
|
Únete
|
|
</Link>
|
|
<Link className="hover:text-foreground" href={loginHref}>
|
|
{loginLabel}
|
|
</Link>
|
|
<Link className="hover:text-foreground" href="/#footer-contact">
|
|
Contáctanos
|
|
</Link>
|
|
</div>
|
|
|
|
<div className="ml-auto flex items-center gap-2">
|
|
<Button
|
|
aria-label={isDark ? "Cambiar a modo claro" : "Cambiar a modo oscuro"}
|
|
className="rounded-full"
|
|
size="icon"
|
|
type="button"
|
|
variant="ghost"
|
|
onClick={() => setTheme(isDark ? "light" : "dark")}
|
|
>
|
|
{mounted && isDark ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
|
|
</Button>
|
|
|
|
{userEmail ? (
|
|
<>
|
|
<span className="hidden max-w-44 truncate px-2 text-muted-foreground md:inline-block">{userEmail}</span>
|
|
<Button className="rounded-full" size="sm" variant="outline" asChild>
|
|
<Link href="/profile">Mi cuenta</Link>
|
|
</Button>
|
|
<Button className="rounded-full" size="sm" type="button" variant="ghost" onClick={handleLogout}>
|
|
Salir
|
|
</Button>
|
|
</>
|
|
) : (
|
|
<Button className="rounded-full" size="sm" variant="outline" asChild>
|
|
<Link href="/auth/login">Ingresa</Link>
|
|
</Button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mx-auto w-full max-w-[1300px] px-4 md:px-6">
|
|
<div className="flex items-center justify-between gap-4 py-4">
|
|
<Link className="flex items-center gap-3" href="/">
|
|
<div className="rounded-xl bg-primary/10 p-1.5 shadow-sm ring-1 ring-primary/20">
|
|
<Image alt="ACVE logo" className="h-10 w-10 rounded-lg object-cover" height={40} src="/images/logo.png" width={40} />
|
|
</div>
|
|
<div className="leading-tight">
|
|
<div className="text-2xl font-bold tracking-tight text-primary md:text-3xl">ACVE Centro de Estudios</div>
|
|
<div className="text-xs text-muted-foreground md:text-sm">Empowering Lawyer one word at a time</div>
|
|
</div>
|
|
</Link>
|
|
|
|
<div className="flex items-center gap-2">
|
|
{isTeacher ? (
|
|
<Button className="hidden rounded-full md:inline-flex" size="sm" variant="outline" asChild>
|
|
<Link href="/teacher">Panel docente</Link>
|
|
</Button>
|
|
) : null}
|
|
<Button className="rounded-full px-5" asChild>
|
|
<Link href="/auth/signup">¡Inscríbete!</Link>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<nav className="hidden items-center gap-1 border-t border-border/70 py-3 text-sm lg:flex">
|
|
{navLinks.map((link) => {
|
|
const isActive = isNavActive(link.href);
|
|
return (
|
|
<Button
|
|
key={link.href}
|
|
className={cn("rounded-full px-4 font-semibold", !isActive && "text-muted-foreground hover:text-foreground")}
|
|
variant={isActive ? "default" : "ghost"}
|
|
asChild
|
|
>
|
|
<Link href={link.href}>{link.label}</Link>
|
|
</Button>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
<nav className="flex gap-2 overflow-x-auto border-t border-border/70 pb-3 pt-2 text-sm lg:hidden">
|
|
{navLinks.map((link) => {
|
|
const isActive = isNavActive(link.href);
|
|
return (
|
|
<Button
|
|
key={link.href}
|
|
className="whitespace-nowrap rounded-full px-4"
|
|
size="sm"
|
|
variant={isActive ? "default" : "outline"}
|
|
asChild
|
|
>
|
|
<Link href={link.href}>{link.label}</Link>
|
|
</Button>
|
|
);
|
|
})}
|
|
</nav>
|
|
</div>
|
|
</header>
|
|
);
|
|
}
|