Fix layout: hide site chrome for unauthenticated users, match magic-hotel layout exactly
Local Build and Deploy / deploy (push) Successful in 55s

This commit is contained in:
openhands committed 2026-07-16 20:13:04 +02:00
1 parent 9889131b03
commit 008e7d921b
3 files changed
+151 -176

No files matched your search

+19 -8
View File
@@ -4,24 +4,35 @@ import RadioPlayerGate from "@/components/public/radio-player-gate";
import { SiteFooter } from "@/components/site-footer"; import { SiteFooter } from "@/components/site-footer";
import { SiteHeader } from "@/components/site-header"; import { SiteHeader } from "@/components/site-header";
import { TopHeader } from "@/components/top-header"; import { TopHeader } from "@/components/top-header";
import { auth } from "@/lib/auth";
/** /**
* Public site chrome. Route group `(site)` keeps this off `/admin` and `/client`, * Public site chrome. Route group `(site)` keeps this off `/admin` and `/client`,
* so housekeeping is never constrained by the public max-w-7xl grid. * so housekeeping is never constrained by the public max-w-7xl grid.
*
* When the user is not authenticated, the header/nav chrome is hidden so that
* the login page acts as a full-screen landing page (magic-hotel style).
*/ */
export default function SiteLayout({ children }: { children: ReactNode }) { export default async function SiteLayout({ children }: { children: ReactNode }) {
const session = await auth();
const authenticated = !!session?.user;
return ( return (
<> <>
<TopHeader /> {authenticated && (
<SiteHeader /> <>
<Navigation /> <TopHeader />
<SiteHeader />
<Navigation />
</>
)}
<main> <main>
<div className="mx-auto mt-6 grid max-w-7xl grid-cols-12 gap-x-3 gap-y-6 px-4 py-4 md:mt-0 md:p-6"> <div className={authenticated ? "mx-auto mt-6 grid max-w-7xl grid-cols-12 gap-x-3 gap-y-6 px-4 py-4 md:mt-0 md:p-6" : ""}>
<div className="col-span-12 min-w-0">{children}</div> <div className={authenticated ? "col-span-12 min-w-0" : ""}>{children}</div>
</div> </div>
</main> </main>
<SiteFooter /> {authenticated && <SiteFooter />}
<RadioPlayerGate /> {authenticated && <RadioPlayerGate />}
</> </>
); );
} }
+62 -68
View File
@@ -1,6 +1,6 @@
"use client"; "use client";
import { LogIn, UserPlus } from "lucide-react"; import { LogIn, UserPlus, ArrowRight, User, Lock } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { signIn } from "next-auth/react"; import { signIn } from "next-auth/react";
import { type FormEvent, useState } from "react"; import { type FormEvent, useState } from "react";
@@ -49,100 +49,94 @@ export function HomeLoginForm() {
} }
return ( return (
<form onSubmit={onSubmit} className="space-y-4"> <form onSubmit={onSubmit} className="w-full flex flex-col justify-center space-y-5">
{error && ( {error && (
<div className="rounded-lg bg-red-500/10 border border-red-500/20 px-4 py-2.5"> <div className="rounded-md bg-red-500/10 border border-red-500/20 px-4 py-2.5">
<p className="text-xs font-bold text-red-400 text-center m-0"> <p className="text-xs font-bold text-red-400 text-center m-0">
{error} {error}
</p> </p>
</div> </div>
)} )}
<div className="space-y-3.5"> <div>
<label className="block text-gray-500 text-[10px] font-black uppercase tracking-widest mb-1.5 ml-1">
Username
</label>
<div className="relative group">
<div className="absolute left-3.5 top-1/2 -translate-y-1/2 text-gray-600 group-focus-within:text-[#5AB4C6] transition-colors">
<User size={16} />
</div>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Username"
className="w-full pl-10 pr-3 px-4 py-3 bg-white/5 border border-white/10 rounded-md focus:outline-none focus:border-[#5AB4C6]/50 focus:bg-[#5AB4C6]/5 text-sm text-white placeholder-gray-500 transition-all font-medium"
required
disabled={needs2fa}
/>
</div>
</div>
<div>
<label className="block text-gray-500 text-[10px] font-black uppercase tracking-widest mb-1.5 ml-1">
Password
</label>
<div className="relative group">
<div className="absolute left-3.5 top-1/2 -translate-y-1/2 text-gray-600 group-focus-within:text-[#5AB4C6] transition-colors">
<Lock size={16} />
</div>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
className="w-full pl-10 pr-3 px-4 py-3 bg-white/5 border border-white/10 rounded-md focus:outline-none focus:border-[#5AB4C6]/50 focus:bg-[#5AB4C6]/5 text-sm text-white placeholder-gray-500 transition-all font-medium"
required
disabled={needs2fa}
/>
</div>
</div>
{needs2fa && (
<div> <div>
<label className="block text-gray-500 text-[10px] font-black uppercase tracking-widest mb-1.5 ml-1"> <label className="block text-gray-500 text-[10px] font-black uppercase tracking-widest mb-1.5 ml-1">
Username 2FA Code
</label> </label>
<div className="relative group"> <div className="relative group">
<div className="absolute left-3.5 top-1/2 -translate-y-1/2 text-gray-600 group-focus-within:text-[#5AB4C6] transition-colors"> <div className="absolute left-3.5 top-1/2 -translate-y-1/2 text-gray-600 group-focus-within:text-[#5AB4C6] transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> <Lock size={16} />
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" />
<circle cx="12" cy="7" r="4" />
</svg>
</div> </div>
<input <input
type="text" type="text"
value={username} value={code}
onChange={(e) => setUsername(e.target.value)} onChange={(e) => setCode(e.target.value)}
placeholder="Username" placeholder="Enter your 2FA code"
className="w-full pl-10 pr-4 py-3 bg-white/5 border border-white/10 rounded-lg focus:outline-none focus:border-[#5AB4C6]/50 focus:bg-[#5AB4C6]/5 text-sm text-white placeholder-gray-500 transition-all font-medium" inputMode="numeric"
required autoFocus
disabled={needs2fa} className="w-full pl-10 pr-3 px-4 py-3 bg-white/5 border border-white/10 rounded-md focus:outline-none focus:border-[#5AB4C6]/50 focus:bg-[#5AB4C6]/5 text-sm text-white placeholder-gray-500 transition-all font-medium"
/> />
</div> </div>
</div> </div>
<div> )}
<label className="block text-gray-500 text-[10px] font-black uppercase tracking-widest mb-1.5 ml-1">
Password
</label>
<div className="relative group">
<div className="absolute left-3.5 top-1/2 -translate-y-1/2 text-gray-600 group-focus-within:text-[#5AB4C6] transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect width="18" height="11" x="3" y="11" rx="2" ry="2" />
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
</svg>
</div>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
className="w-full pl-10 pr-4 py-3 bg-white/5 border border-white/10 rounded-lg focus:outline-none focus:border-[#5AB4C6]/50 focus:bg-[#5AB4C6]/5 text-sm text-white placeholder-gray-500 transition-all font-medium"
required
disabled={needs2fa}
/>
</div>
</div>
{needs2fa && (
<div>
<label className="block text-gray-500 text-[10px] font-black uppercase tracking-widest mb-1.5 ml-1">
2FA Code
</label>
<div className="relative group">
<div className="absolute left-3.5 top-1/2 -translate-y-1/2 text-gray-600 group-focus-within:text-[#5AB4C6] transition-colors">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect width="18" height="11" x="3" y="11" rx="2" ry="2" />
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
</svg>
</div>
<input
type="text"
value={code}
onChange={(e) => setCode(e.target.value)}
placeholder="Enter your 2FA code"
inputMode="numeric"
autoFocus
className="w-full pl-10 pr-4 py-3 bg-white/5 border border-white/10 rounded-lg focus:outline-none focus:border-[#5AB4C6]/50 focus:bg-[#5AB4C6]/5 text-sm text-white placeholder-gray-500 transition-all font-medium"
/>
</div>
</div>
)}
</div>
<button <button
type="submit" type="submit"
disabled={pending} disabled={pending}
className="w-full group relative flex items-center justify-center gap-2 bg-[#5AB4C6] hover:bg-[#5AB4C6]/90 text-white font-black text-xs py-3.5 rounded-lg transition-all disabled:opacity-50 active:scale-[0.98] uppercase tracking-widest" className="w-full group relative flex items-center justify-center gap-2 bg-[#5AB4C6] hover:bg-[#5AB4C6] text-white font-black text-sm py-3.5 rounded-md transition-all disabled:opacity-50 active:scale-[0.98]"
> >
<LogIn size={14} /> <LogIn size={16} />
{pending ? "Please wait..." : needs2fa ? "Verify" : "Login"} <span className="uppercase tracking-widest">
{pending ? "Please wait..." : needs2fa ? "Verify" : "Sign In"}
</span>
<ArrowRight size={16} className="group-hover:translate-x-1 transition-transform" />
</button> </button>
<div className="pt-4 border-t border-white/5 text-center"> <div className="pt-5 border-t border-white/5 text-center">
<Link href="/register"> <Link href="/register">
<div className="flex items-center justify-center gap-2 w-full py-3 rounded-lg bg-white/5 border border-white/10 text-white text-[11px] font-black hover:bg-white/10 transition-all active:scale-95 uppercase tracking-wider"> <div className="flex items-center justify-center gap-2 w-full py-3 rounded-md bg-white/5 border border-white/10 text-white text-xs font-black hover:bg-white/10 transition-all active:scale-95 uppercase tracking-wider">
<UserPlus size={14} className="text-[#5AB4C6]" /> <UserPlus size={14} className="text-[#5AB4C6]" />
Create account <span>No account yet? Register now</span>
</div> </div>
</Link> </Link>
</div> </div>
+70 -100
View File
@@ -1,4 +1,4 @@
import { ArrowRight, Newspaper, Users } from "lucide-react"; import { ArrowRight, Newspaper, Users, User } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { getTranslations } from "next-intl/server"; import { getTranslations } from "next-intl/server";
@@ -10,6 +10,12 @@ import { siteSettings } from "@/lib/services/site-settings";
export default async function GuestView() { export default async function GuestView() {
const t = await getTranslations("pages.home"); const t = await getTranslations("pages.home");
const hotelName = await siteSettings.get("hotel_name", "Atom"); const hotelName = await siteSettings.get("hotel_name", "Atom");
const header = await siteSettings.get("cms_header", "/assets/images/banner.png");
const logo = await siteSettings.get("cms_logo", "");
const bannerUrl = header && header !== "/assets/images/background.png"
? header
: "/assets/images/banner.png";
const imager = const imager =
(await siteSettings.get( (await siteSettings.get(
"habbo_imaging_url", "habbo_imaging_url",
@@ -39,109 +45,73 @@ export default async function GuestView() {
}); });
} catch {} } catch {}
let onlineCount = 0;
try {
onlineCount = await prisma.user.count({ where: { online: "1" } });
} catch {}
return ( return (
<div className="flex flex-col gap-10"> <div className="flex flex-col items-center gap-8">
{/* Hero Section */} {/* Logo + Online Count */}
<div className="relative overflow-hidden rounded-3xl bg-[#09090b] border border-white/[0.06] shadow-2xl"> <div className="flex items-center gap-3">
<div className="absolute inset-0 bg-gradient-to-br from-[#5AB4C6]/5 via-transparent to-transparent" /> {logo ? (
<div className="absolute top-0 left-1/4 w-[600px] h-[600px] bg-[#5AB4C6]/5 rounded-full blur-[120px]" /> <Image
<div className="absolute -bottom-20 -right-20 w-80 h-80 bg-white/[0.03] rounded-full blur-[80px]" /> src={logo}
<div className="relative flex flex-col items-center gap-8 px-6 py-10 md:flex-row md:px-10 md:py-12"> alt={hotelName ?? "Hotel"}
{/* Left: Welcome */} width={250}
<div className="flex-1 text-center md:text-left"> height={80}
<div className="inline-flex items-center gap-2 rounded-full border border-[#5AB4C6]/20 bg-[#5AB4C6]/10 px-4 py-1.5 mb-5"> className="block"
<span className="relative flex h-2 w-2"> unoptimized
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-[#5AB4C6] opacity-80" /> />
<span className="relative inline-flex h-2 w-2 rounded-full bg-[#5AB4C6]" /> ) : (
</span> <h1 className="text-3xl font-black text-white tracking-tight m-0">
<span className="text-[10px] font-black uppercase tracking-widest text-[#5AB4C6]"> {hotelName}
Online </h1>
</span> )}
</div> <div className="flex items-center gap-2 px-4 py-4 border border-[#5AB4C6]/50 bg-[#5AB4C6]/20 rounded-md">
<div className="w-2 h-2 bg-[#5AB4C6] rounded-full animate-pulse" />
<span className="text-white font-black text-xs uppercase tracking-wider">
{onlineCount} Online
</span>
</div>
</div>
<h1 className="m-0 text-3xl font-black tracking-tight text-white md:text-4xl"> {/* Main Card: 900x500 split layout */}
Welcome to{" "} <div className="w-[900px] h-[500px] overflow-hidden rounded-md bg-white/5 backdrop-blur-2xl flex max-w-full">
<span className="text-[#5AB4C6]">{hotelName}</span> {/* Left: Login Form */}
</h1> <div className="w-1/2 flex flex-col justify-center px-10">
<p className="mt-3 max-w-md text-sm text-white/50 leading-relaxed"> <div className="mb-6 text-center">
Connect with friends, explore the hotel, and create unforgettable <h2 className="text-xl font-black text-white mb-1.5 tracking-tight uppercase">
memories. Your adventure starts here. Sign In
</p> </h2>
<div className="h-1 w-full bg-[#5AB4C6] rounded-full" />
<div className="mt-7 flex flex-wrap gap-3">
<Link href="/register">
<div className="inline-flex items-center gap-2 bg-[#5AB4C6] hover:bg-[#5AB4C6]/90 text-white font-black text-xs py-3.5 px-7 rounded-lg transition-all active:scale-[0.98] uppercase tracking-widest shadow-lg shadow-[#5AB4C6]/20">
Get Started <ArrowRight size={14} />
</div>
</Link>
<Link href="/news">
<div className="inline-flex items-center gap-2 bg-white/5 hover:bg-white/10 text-white font-black text-xs py-3.5 px-7 rounded-lg transition-all active:scale-[0.98] uppercase tracking-widest border border-white/10">
Latest News
</div>
</Link>
</div>
{/* Stats */}
<div className="mt-8 flex gap-6">
<div>
<p className="text-lg font-black text-white m-0">
{articles.length}
</p>
<p className="text-[10px] font-black uppercase tracking-widest text-white/30 m-0 mt-0.5">
Articles
</p>
</div>
<div>
<p className="text-lg font-black text-white m-0">
{recentUsers.length}+
</p>
<p className="text-[10px] font-black uppercase tracking-widest text-white/30 m-0 mt-0.5">
Active Users
</p>
</div>
</div>
</div> </div>
<HomeLoginForm />
</div>
{/* Right: Login Form */} {/* Right: Hotel Image with Overlay */}
<div className="w-full max-w-sm shrink-0"> <div className="w-1/2 relative overflow-hidden">
<div <Image
className="rounded-2xl border border-white/10 p-6" src={bannerUrl}
style={{ alt={hotelName ?? "Hotel"}
background: fill
"linear-gradient(135deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%)", className="object-cover object-center scale-110"
backdropFilter: "blur(32px)", unoptimized
WebkitBackdropFilter: "blur(32px)", />
}} <div className="absolute inset-0 bg-gradient-to-br from-[#5AB4C6] via-transparent to-[#5AB4C6]" />
> <div className="absolute inset-0 bg-black/30" />
<div className="flex items-center gap-2.5 mb-5"> <div className="absolute inset-0 flex flex-col items-center justify-center z-20">
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-[#5AB4C6]/10"> <div className="w-[140px] h-[140px] rounded-md bg-white/10 border border-white/20 backdrop-blur-xl overflow-hidden flex items-center justify-center shadow-[0_0_60px_rgba(130,204,221,0.35)]">
<svg <div className="text-white/40">
xmlns="http://www.w3.org/2000/svg" <User size={64} strokeWidth={1.5} />
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="#5AB4C6"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<rect width="18" height="11" x="3" y="11" rx="2" ry="2" />
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
</svg>
</div>
<span className="text-sm font-black text-white tracking-wide">
Sign In
</span>
</div> </div>
<HomeLoginForm />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* Articles + Sidebar */} {/* Articles + Sidebar */}
<div className="grid gap-6 lg:grid-cols-3"> <div className="w-full max-w-[900px] grid gap-6 lg:grid-cols-3 mt-6">
{/* Latest News */} {/* Latest News */}
<div className="lg:col-span-2 animate-slide-up"> <div className="lg:col-span-2 animate-slide-up">
<div className="mb-5 flex items-center justify-between"> <div className="mb-5 flex items-center justify-between">
@@ -172,7 +142,7 @@ export default async function GuestView() {
<Link <Link
key={a.slug} key={a.slug}
href={`/news/${a.slug}`} href={`/news/${a.slug}`}
className="group relative overflow-hidden rounded-2xl border border-white/10 bg-white/[0.03] transition-all duration-300 hover:bg-white/[0.06] hover:-translate-y-1" className="group relative overflow-hidden rounded-md border border-white/10 bg-white/[0.03] transition-all duration-300 hover:bg-white/[0.06] hover:-translate-y-1"
style={{ animationDelay: `${i * 100}ms` }} style={{ animationDelay: `${i * 100}ms` }}
> >
<div className="relative aspect-[16/9] overflow-hidden"> <div className="relative aspect-[16/9] overflow-hidden">
@@ -201,9 +171,9 @@ export default async function GuestView() {
<Link <Link
key={a.slug} key={a.slug}
href={`/news/${a.slug}`} href={`/news/${a.slug}`}
className="group flex items-center gap-3 rounded-xl border border-white/10 bg-white/[0.03] p-3 transition-all duration-200 hover:bg-white/[0.06] hover:-translate-y-0.5" className="group flex items-center gap-3 rounded-md border border-white/10 bg-white/[0.03] p-3 transition-all duration-200 hover:bg-white/[0.06] hover:-translate-y-0.5"
> >
<div className="relative h-16 w-16 shrink-0 overflow-hidden rounded-lg"> <div className="relative h-16 w-16 shrink-0 overflow-hidden rounded-md">
<Image <Image
src={a.image} src={a.image}
alt={a.title} alt={a.title}
@@ -236,7 +206,7 @@ export default async function GuestView() {
> >
{/* Recent Users */} {/* Recent Users */}
{recentUsers.length > 0 && ( {recentUsers.length > 0 && (
<div className="rounded-2xl border border-white/10 bg-white/[0.03] overflow-hidden"> <div className="rounded-md border border-white/10 bg-white/[0.03] overflow-hidden">
<div className="flex items-center gap-2.5 px-5 py-3.5 border-b border-white/[0.06]"> <div className="flex items-center gap-2.5 px-5 py-3.5 border-b border-white/[0.06]">
<div className="flex h-7 w-7 items-center justify-center rounded-lg bg-[#5AB4C6]/10"> <div className="flex h-7 w-7 items-center justify-center rounded-lg bg-[#5AB4C6]/10">
<Users className="h-3.5 w-3.5 text-[#5AB4C6]" /> <Users className="h-3.5 w-3.5 text-[#5AB4C6]" />
@@ -252,7 +222,7 @@ export default async function GuestView() {
href={`/u/${u.username}`} href={`/u/${u.username}`}
className="group relative flex items-center justify-center transition-all duration-200 hover:scale-110" className="group relative flex items-center justify-center transition-all duration-200 hover:scale-110"
> >
<div className="relative h-14 w-14 overflow-hidden rounded-xl border-2 border-transparent bg-white/5 transition-all duration-200 group-hover:border-[#5AB4C6]/30 group-hover:shadow-lg"> <div className="relative h-14 w-14 overflow-hidden rounded-md border-2 border-transparent bg-white/5 transition-all duration-200 group-hover:border-[#5AB4C6]/30 group-hover:shadow-lg">
<Image <Image
src={avatarImageUrl(imager, u.look, { src={avatarImageUrl(imager, u.look, {
headOnly: true, headOnly: true,
@@ -282,7 +252,7 @@ export default async function GuestView() {
{/* Quick Register CTA */} {/* Quick Register CTA */}
<Link href="/register" className="group block"> <Link href="/register" className="group block">
<div className="relative overflow-hidden rounded-2xl border border-white/10 bg-white/[0.03] px-6 py-8 text-center transition-all duration-300 hover:bg-white/[0.06] hover:-translate-y-1"> <div className="relative overflow-hidden rounded-md border border-white/10 bg-white/[0.03] px-6 py-8 text-center transition-all duration-300 hover:bg-white/[0.06] hover:-translate-y-1">
<div className="absolute -top-10 -right-10 h-32 w-32 rounded-full bg-[#5AB4C6]/10 blur-3xl" /> <div className="absolute -top-10 -right-10 h-32 w-32 rounded-full bg-[#5AB4C6]/10 blur-3xl" />
<div className="absolute -bottom-10 -left-10 h-24 w-24 rounded-full bg-[#5AB4C6]/10 blur-3xl" /> <div className="absolute -bottom-10 -left-10 h-24 w-24 rounded-full bg-[#5AB4C6]/10 blur-3xl" />
<div className="relative"> <div className="relative">
@@ -309,7 +279,7 @@ export default async function GuestView() {
<p className="mt-1 text-xs text-white/40"> <p className="mt-1 text-xs text-white/40">
Create your free account and start your adventure! Create your free account and start your adventure!
</p> </p>
<div className="mt-5 inline-flex items-center gap-2 bg-[#5AB4C6] hover:bg-[#5AB4C6]/90 text-white font-black text-xs py-3 px-6 rounded-lg transition-all active:scale-[0.98] uppercase tracking-widest shadow-lg shadow-[#5AB4C6]/20"> <div className="mt-5 inline-flex items-center gap-2 bg-[#5AB4C6] hover:bg-[#5AB4C6]/90 text-white font-black text-xs py-3 px-6 rounded-md transition-all active:scale-[0.98] uppercase tracking-widest shadow-lg shadow-[#5AB4C6]/20">
Register Now <ArrowRight className="h-3.5 w-3.5" /> Register Now <ArrowRight className="h-3.5 w-3.5" />
</div> </div>
</div> </div>