Ship product gaps: register/vote pages, friend add/accept/decline/remove, email verify TTL, captcha on login/forgot, soft-fail user actions, SSE abort/shared client, Commando Centrum error toasts, admin delete for events/polls, and IT/NL i18n fills. Co-authored-by: Cursor <[email protected]>
256 lines
7.2 KiB
TypeScript
256 lines
7.2 KiB
TypeScript
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import { headers } from "next/headers";
|
|
import { getTranslations } from "next-intl/server";
|
|
import { HomeLoginForm } from "@/components/auth/home-login-form";
|
|
import { Reveal } from "@/components/motion-reveal";
|
|
import { avatarImageUrl, excerpt } from "@/lib/format";
|
|
import { prisma } from "@/lib/prisma";
|
|
import { captchaConfig } from "@/lib/services/captcha";
|
|
import { siteSettings } from "@/lib/services/site-settings";
|
|
|
|
export default async function GuestView() {
|
|
const t = await getTranslations("pages.home");
|
|
const _hotelName = await siteSettings.get("hotel_name", "Atom");
|
|
const imager =
|
|
(await siteSettings.get(
|
|
"habbo_imaging_url",
|
|
"https://www.habbo.com/habbo-imaging/avatarimage",
|
|
)) ?? "";
|
|
const cfg = await captchaConfig();
|
|
const nonce = (await headers()).get("x-nonce") ?? undefined;
|
|
|
|
let articles: Array<{
|
|
slug: string;
|
|
title: string;
|
|
shortStory: string;
|
|
image: string;
|
|
}> = [];
|
|
try {
|
|
articles = await prisma.websiteArticles.findMany({
|
|
orderBy: { createdAt: "desc" },
|
|
take: 8,
|
|
select: { slug: true, title: true, shortStory: true, image: true },
|
|
});
|
|
} catch {}
|
|
|
|
let recentUsers: Array<{ username: string; look: string }> = [];
|
|
try {
|
|
recentUsers = await prisma.user.findMany({
|
|
orderBy: { lastOnline: "desc" },
|
|
take: 12,
|
|
select: { username: true, look: true },
|
|
});
|
|
} catch {}
|
|
|
|
return (
|
|
<div className="flex flex-col gap-4 md:flex-row">
|
|
<Reveal className="flex w-full flex-col gap-4 md:w-1/3 md:flex-shrink-0">
|
|
<div
|
|
className="overflow-hidden rounded-lg border"
|
|
style={{
|
|
borderColor:
|
|
"color-mix(in srgb, var(--color-text-muted) 22%, transparent)",
|
|
}}
|
|
>
|
|
<div className="relative h-[120px] overflow-hidden md:h-[150px]">
|
|
<div
|
|
className="absolute inset-0"
|
|
style={{
|
|
backgroundImage: "url(/assets/images/kasja_mepage_header.png)",
|
|
backgroundSize: "cover",
|
|
backgroundPosition: "center 30%",
|
|
backgroundRepeat: "no-repeat",
|
|
}}
|
|
/>
|
|
<div
|
|
className="absolute inset-0"
|
|
style={{
|
|
background:
|
|
"linear-gradient(135deg, rgba(0,0,0,0.65) 0%, rgba(0,0,0,0.15) 50%, rgba(0,0,0,0.5) 100%)",
|
|
}}
|
|
/>
|
|
<div
|
|
className="absolute -bottom-8 right-4 w-32 h-32 rounded-full opacity-[0.12] blur-[60px]"
|
|
style={{ background: "var(--color-primary)" }}
|
|
/>
|
|
</div>
|
|
<div
|
|
className="p-3"
|
|
style={{ backgroundColor: "var(--color-surface)" }}
|
|
>
|
|
<HomeLoginForm
|
|
captcha={{
|
|
provider: cfg.provider,
|
|
siteKey: cfg.siteKey || undefined,
|
|
field: cfg.field || undefined,
|
|
}}
|
|
nonce={nonce}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<a href="/register" className="relative block">
|
|
<div
|
|
className="inline-block overflow-hidden rounded-lg border"
|
|
style={{
|
|
borderColor:
|
|
"color-mix(in srgb, var(--color-text-muted) 22%, transparent)",
|
|
}}
|
|
>
|
|
<Image
|
|
src="/assets/images/EnterHubbly.png"
|
|
alt="Register"
|
|
width={300}
|
|
height={100}
|
|
className="block"
|
|
unoptimized
|
|
/>
|
|
</div>
|
|
<span
|
|
className="pointer-events-none absolute inset-0 flex items-center text-4xl font-extrabold text-white text-shadow"
|
|
style={{ marginLeft: "15px" }}
|
|
>
|
|
REGISTER NOW
|
|
</span>
|
|
</a>
|
|
|
|
{recentUsers.length > 0 && (
|
|
<div
|
|
className="overflow-hidden rounded-lg border"
|
|
style={{
|
|
borderColor:
|
|
"color-mix(in srgb, var(--color-text-muted) 22%, transparent)",
|
|
backgroundColor: "var(--color-surface)",
|
|
}}
|
|
>
|
|
<div
|
|
className="flex h-12 w-full items-center justify-center bg-repeat"
|
|
style={{
|
|
background:
|
|
"linear-gradient(140deg, #0A1F0A 0%, #0F2D0F 20%, #153C15 40%, #1B4A1B 60%, #205820 75%, #1A4A1A 90%, #143814 100%)",
|
|
}}
|
|
>
|
|
<h2 className="px-3 text-xl font-bold text-white">
|
|
Recent Users
|
|
</h2>
|
|
</div>
|
|
<div className="grid grid-cols-4 sm:grid-cols-6 gap-3 p-3">
|
|
{recentUsers.map((u) => (
|
|
<div
|
|
key={u.username}
|
|
className="relative overflow-visible rounded-md p-0 shadow-lg"
|
|
style={{
|
|
width: "50px",
|
|
height: "50px",
|
|
justifySelf: "center",
|
|
backgroundColor: "var(--color-surface)",
|
|
}}
|
|
>
|
|
<Image
|
|
src={avatarImageUrl(imager, u.look, {
|
|
headOnly: true,
|
|
direction: 3,
|
|
})}
|
|
alt={u.username}
|
|
width={50}
|
|
height={62}
|
|
style={{
|
|
position: "absolute",
|
|
top: "-12px",
|
|
left: "50%",
|
|
transform: "translateX(-50%)",
|
|
maxWidth: "none",
|
|
maxHeight: "none",
|
|
}}
|
|
unoptimized
|
|
/>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</Reveal>
|
|
|
|
<Reveal delay={0.15} className="flex w-full flex-col gap-4 md:w-2/3">
|
|
<div
|
|
className="overflow-hidden rounded-lg border"
|
|
style={{
|
|
borderColor:
|
|
"color-mix(in srgb, var(--color-text-muted) 22%, transparent)",
|
|
backgroundColor: "var(--color-surface)",
|
|
}}
|
|
>
|
|
<div
|
|
className="flex h-12 w-full items-center justify-center"
|
|
style={{
|
|
background:
|
|
"linear-gradient(140deg, #1a3a5c 0%, #1e4a6e 20%, #2a5a80 40%, #3a6a90 60%, #4a7aa0 75%, #3a6a90 90%, #2a5a80 100%)",
|
|
}}
|
|
>
|
|
<h2 className="text-xl font-bold text-white">
|
|
{t("latestNews") || "Latest News"}
|
|
</h2>
|
|
</div>
|
|
<div className="p-4">
|
|
{articles.length === 0 ? (
|
|
<p
|
|
className="py-8 text-center"
|
|
style={{
|
|
color:
|
|
"var(--color-text-muted-readable, var(--color-text-muted))",
|
|
}}
|
|
>
|
|
{t("noArticles")}
|
|
</p>
|
|
) : (
|
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
{articles.map((a) => (
|
|
<Link
|
|
key={a.slug}
|
|
href={`/news/${a.slug}`}
|
|
className="group relative w-full overflow-hidden rounded-lg transition-all duration-200"
|
|
style={{
|
|
height: "215px",
|
|
backgroundColor: "var(--color-surface)",
|
|
border:
|
|
"1px solid color-mix(in srgb, var(--color-text-muted) 15%, transparent)",
|
|
}}
|
|
>
|
|
<div className="relative h-full w-full overflow-hidden rounded-lg">
|
|
<Image
|
|
src={a.image}
|
|
alt={a.title}
|
|
fill
|
|
className="rounded-lg object-cover transition-all duration-300 group-hover:scale-105"
|
|
unoptimized
|
|
/>
|
|
<div
|
|
className="absolute left-0 w-full p-2"
|
|
style={{
|
|
bottom: "-2px",
|
|
background:
|
|
"linear-gradient(to top, rgba(0,0,0,0.85) 0%, transparent 100%)",
|
|
}}
|
|
>
|
|
<h3 className="truncate text-lg font-semibold text-white text-shadow-sm">
|
|
{a.title}
|
|
</h3>
|
|
{a.shortStory && (
|
|
<p className="truncate text-xs font-normal text-white text-shadow-sm">
|
|
{excerpt(a.shortStory, 80)}
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</Reveal>
|
|
</div>
|
|
);
|
|
}
|