public: add conditional sign in / sign up button in navbar

This commit is contained in:
Carl-Gerhard Lindesvärd
2026-02-16 15:20:10 +01:00
parent 4f4b4a8d88
commit d54a33b5c2
2 changed files with 66 additions and 42 deletions

View File

@@ -1,15 +1,16 @@
'use client'; 'use client';
import { baseOptions } from '@/lib/layout.shared';
import { cn } from '@/lib/utils';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion';
import { MenuIcon, MoonIcon, SunIcon, XIcon } from 'lucide-react'; import { MenuIcon, MoonIcon, SunIcon, XIcon } from 'lucide-react';
import { useTheme } from 'next-themes';
import Link from 'next/link'; import Link from 'next/link';
import { useTheme } from 'next-themes';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { FeatureCardContainer } from './feature-card'; import { FeatureCardContainer } from './feature-card';
import { GithubButton } from './github-button'; import { GithubButton } from './github-button';
import { Logo } from './logo'; import { Logo } from './logo';
import { SignUpButton } from './sign-up-button';
import { Button } from './ui/button'; import { Button } from './ui/button';
import { baseOptions } from '@/lib/layout.shared';
import { cn } from '@/lib/utils';
const LINKS = [ const LINKS = [
{ {
@@ -61,18 +62,18 @@ const Navbar = () => {
return ( return (
<nav <nav
className={cn( className={cn(
'fixed top-0 z-50 w-full py-4 border-b transition-colors duration-500', 'fixed top-0 z-50 w-full border-b py-4 transition-colors duration-500',
isScrolled isScrolled
? 'bg-background border-border' ? 'border-border bg-background'
: 'bg-background/0 border-border/0', : 'border-border/0 bg-background/0'
)} )}
ref={navbarRef} ref={navbarRef}
> >
<div className="container"> <div className="container">
<div className={cn('flex justify-between items-center')}> <div className={cn('flex items-center justify-between')}>
{/* Logo */} {/* Logo */}
<div className="shrink-0"> <div className="shrink-0">
<Link href="/" className="row items-center font-medium"> <Link className="row items-center font-medium" href="/">
<Logo className="h-6" /> <Logo className="h-6" />
<span className="hidden [@media(min-width:850px)]:block"> <span className="hidden [@media(min-width:850px)]:block">
{baseOptions().nav?.title} {baseOptions().nav?.title}
@@ -82,13 +83,13 @@ const Navbar = () => {
<div className="row items-center gap-8"> <div className="row items-center gap-8">
{/* Desktop Navigation */} {/* Desktop Navigation */}
<div className="hidden md:flex items-center space-x-8 text-sm"> <div className="hidden items-center space-x-8 text-sm md:flex">
{LINKS?.map((link) => { {LINKS?.map((link) => {
return ( return (
<Link <Link
key={link.url} className="font-medium text-foreground/80 hover:text-foreground"
href={link.url!} href={link.url!}
className="text-foreground/80 hover:text-foreground font-medium" key={link.url}
> >
{link.text} {link.text}
</Link> </Link>
@@ -100,24 +101,17 @@ const Navbar = () => {
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<GithubButton /> <GithubButton />
{/* Sign in button */} {/* Sign in button */}
<Button asChild> <SignUpButton />
<Link
className="hidden md:flex"
href="https://dashboard.openpanel.dev/onboarding"
>
Sign up
</Link>
</Button>
<ThemeToggle /> <ThemeToggle />
<Button <Button
className="md:hidden -my-2" className="-my-2 md:hidden"
size="icon"
variant="ghost"
onClick={() => { onClick={() => {
setIsMobileMenuOpen((p) => !p); setIsMobileMenuOpen((p) => !p);
}} }}
size="icon"
variant="ghost"
> >
<MenuIcon className="w-4 h-4" /> <MenuIcon className="h-4 w-4" />
</Button> </Button>
</div> </div>
</div> </div>
@@ -126,26 +120,26 @@ const Navbar = () => {
<AnimatePresence> <AnimatePresence>
{isMobileMenuOpen && ( {isMobileMenuOpen && (
<motion.div <motion.div
ref={mobileMenuRef}
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }} animate={{ height: 'auto', opacity: 1 }}
className="fixed inset-0 overflow-hidden bg-background/20 p-4 backdrop-blur-lg md:hidden"
exit={{ height: 0, opacity: 0 }} exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.2 }} initial={{ height: 0, opacity: 0 }}
className="overflow-hidden bg-background/20 md:hidden backdrop-blur-lg fixed inset-0 p-4"
onClick={(e) => { onClick={(e) => {
const target = e.target as HTMLElement; const target = e.target as HTMLElement;
if (target === mobileMenuRef.current) { if (target === mobileMenuRef.current) {
setIsMobileMenuOpen(false); setIsMobileMenuOpen(false);
} }
}} }}
ref={mobileMenuRef}
transition={{ duration: 0.2 }}
> >
<FeatureCardContainer className="col text-sm divide-y divide-foreground/10 gap-0 mt-12 py-4"> <FeatureCardContainer className="col mt-12 gap-0 divide-y divide-foreground/10 py-4 text-sm">
{LINKS?.map((link) => { {LINKS?.map((link) => {
return ( return (
<Link <Link
key={link.url} className="p-4 px-0 font-semibold text-foreground/80 text-lg hover:text-foreground"
href={link.url!} href={link.url!}
className="text-foreground/80 hover:text-foreground text-lg font-semibold p-4 px-0" key={link.url}
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
> >
{link.text} {link.text}
@@ -153,12 +147,12 @@ const Navbar = () => {
); );
})} })}
</FeatureCardContainer> </FeatureCardContainer>
<div className="row gap-2 absolute top-3 right-4 items-center"> <div className="row absolute top-3 right-4 items-center gap-2">
<ThemeToggle className="flex!" /> <ThemeToggle className="flex!" />
<Button <Button
onClick={() => setIsMobileMenuOpen(false)}
size="icon" size="icon"
variant="outline" variant="outline"
onClick={() => setIsMobileMenuOpen(false)}
> >
<XIcon className="size-4" /> <XIcon className="size-4" />
</Button> </Button>
@@ -177,33 +171,33 @@ function ThemeToggle({ className }: { className?: string }) {
const theme = useTheme(); const theme = useTheme();
return ( return (
<Button <Button
size="icon"
variant="naked"
onClick={() => theme.setTheme(theme.theme === 'dark' ? 'light' : 'dark')}
className={cn( className={cn(
'relative overflow-hidden size-8 cursor-pointer hidden md:inline', 'relative hidden size-8 cursor-pointer overflow-hidden md:inline',
className, className
)} )}
onClick={() => theme.setTheme(theme.theme === 'dark' ? 'light' : 'dark')}
size="icon"
suppressHydrationWarning suppressHydrationWarning
variant="naked"
> >
<AnimatePresence mode="wait" initial={false}> <AnimatePresence initial={false} mode="wait">
{theme.theme === 'dark' || {theme.theme === 'dark' ||
(!theme.theme && theme.resolvedTheme === 'dark') ? ( (!theme.theme && theme.resolvedTheme === 'dark') ? (
<motion.div <motion.div
key="moon"
initial={{ rotate: -90, opacity: 0 }}
animate={{ rotate: 0, opacity: 1 }} animate={{ rotate: 0, opacity: 1 }}
exit={{ rotate: 90, opacity: 0 }} exit={{ rotate: 90, opacity: 0 }}
initial={{ rotate: -90, opacity: 0 }}
key="moon"
transition={{ duration: 0.2, ease: 'easeInOut' }} transition={{ duration: 0.2, ease: 'easeInOut' }}
> >
<MoonIcon className="size-4" suppressHydrationWarning /> <MoonIcon className="size-4" suppressHydrationWarning />
</motion.div> </motion.div>
) : ( ) : (
<motion.div <motion.div
key="sun"
initial={{ rotate: 90, opacity: 0 }}
animate={{ rotate: 0, opacity: 1 }} animate={{ rotate: 0, opacity: 1 }}
exit={{ rotate: -90, opacity: 0 }} exit={{ rotate: -90, opacity: 0 }}
initial={{ rotate: 90, opacity: 0 }}
key="sun"
transition={{ duration: 0.2, ease: 'easeInOut' }} transition={{ duration: 0.2, ease: 'easeInOut' }}
> >
<SunIcon className="size-4" suppressHydrationWarning /> <SunIcon className="size-4" suppressHydrationWarning />

View File

@@ -0,0 +1,30 @@
import Link from 'next/link';
import { useEffect, useState } from 'react';
import { Button } from './ui/button';
export function SignUpButton() {
const [isSignedIn, setIsSignedIn] = useState(false);
useEffect(() => {
const check = async () => {
const response = await fetch(
'https://api.openpanel.dev/trpc/auth.session'
);
const data = await response.json();
const session = data?.result?.data?.json?.session;
setIsSignedIn(session !== null);
};
check();
}, []);
return (
<Button asChild>
<Link
className="hidden md:flex"
href={`https://dashboard.openpanel.dev${isSignedIn ? '/' : '/onboarding'}`}
>
{isSignedIn ? 'Dashboard' : 'Sign up'}
</Link>
</Button>
);
}