From 8fa59d9d195dc3705d346c65707ab116d74127fe Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 22 Jul 2026 14:12:55 +0200 Subject: [PATCH] feat: incorporate design elements from habbo.nl, hubbly.pw, leet.ws --- src/app/(site)/page.tsx | 963 +++++++++++++++---------------- src/app/(site)/register/page.tsx | 286 ++++----- 2 files changed, 627 insertions(+), 622 deletions(-) diff --git a/src/app/(site)/page.tsx b/src/app/(site)/page.tsx index fa14ee7e..c52e7899 100644 --- a/src/app/(site)/page.tsx +++ b/src/app/(site)/page.tsx @@ -3,458 +3,323 @@ import Link from "next/link"; import { getTranslations } from "next-intl/server"; import { cached } from "@/lib/cache"; import { formatDate } from "@/lib/format-date"; +import { avatarImageUrl } from "@/lib/format"; import { prisma } from "@/lib/prisma"; import { siteSettings } from "@/lib/services/site-settings"; export const revalidate = 120; async function getHotelData() { - const hotelName = await siteSettings.get("hotel_name", "Hotel"); - const [online, users, rooms, articles] = await Promise.all([ - cached("online_count", 10_000, () => - prisma.user.count({ where: { online: "1" } }), - ).catch(() => 0), - cached("total_users", 30_000, () => prisma.user.count()).catch(() => 0), - cached("total_rooms", 30_000, () => prisma.rooms.count()).catch(() => 0), - prisma.websiteArticles - .findMany({ orderBy: { createdAt: "desc" }, take: 3 }) - .catch(() => []), + const [hotelName, imagerRaw] = await Promise.all([ + siteSettings.get("hotel_name", "Hotel"), + siteSettings.get( + "habbo_imaging_url", + "https://www.habbo.com/habbo-imaging/avatarimage", + ), ]); - return { hotelName, online, users, rooms, articles }; + const imager = imagerRaw ?? "https://www.habbo.com/habbo-imaging/avatarimage"; + + const [online, users, rooms, articles, recentUsers, recentPhotos] = + await Promise.all([ + cached("online_count", 10_000, () => + prisma.user.count({ where: { online: "1" } }), + ).catch(() => 0), + cached("total_users", 30_000, () => prisma.user.count()).catch(() => 0), + cached("total_rooms", 30_000, () => prisma.rooms.count()).catch(() => 0), + prisma.websiteArticles + .findMany({ orderBy: { createdAt: "desc" }, take: 5 }) + .catch(() => []), + prisma.user + .findMany({ + where: { online: "1" }, + select: { username: true, look: true }, + take: 12, + }) + .catch(() => []), + prisma.cameraWeb + .findMany({ orderBy: { timestamp: "desc" }, take: 4 }) + .catch(() => []), + ]); + + return { + hotelName, + imager, + online, + users, + rooms, + articles, + recentUsers, + recentPhotos, + }; } export default async function Home() { const t = await getTranslations("header"); - const { hotelName, online, users, rooms, articles } = await getHotelData(); + const { + hotelName, + imager, + online, + users, + rooms, + articles, + recentUsers, + recentPhotos, + } = await getHotelData(); + + const featuredArticle = articles[0]; + const remainingArticles = articles.slice(1, 5); return ( -
- {/* ── 1. Lobby — full-width hero with depth ── */} -
+
+ {/* ── Hero ── */} +
-
+ /> +
+
-
-
-
- - {online.toLocaleString()} online now -
-

- Your story starts{" "} - - here - -
- at {hotelName} -

-

- {t("tagline")} -

-
- - Join for free - - arrow - - - - - - Enter hotel - -
-
-
-
+ + {online.toLocaleString()} online now +
+

+ Welcome to{" "} + + {hotelName} + +

+

+ {t("tagline")} +

+
+ + -
+ Enter {hotelName} + + + Play now + +
+
+
+ Frank +
+ {recentUsers.slice(0, 4).map((u) => ( +
Frank -
-

- Hey there! 👋 -

-

- Ready for an adventure? Create your account and let's go! -

-
+ {u.username} +
-
+ ))}
- {/* ── 2. Hotel stats — reimagined as service pillars ── */} -
-
- {[ - { - value: online.toLocaleString(), - label: "Guests online", - detail: "Exploring right now", - }, - { - value: users.toLocaleString(), - label: "Total citizens", - detail: "And counting", - }, - { - value: rooms.toLocaleString(), - label: "Rooms built", - detail: "Waiting to be explored", - }, - { - value: "∞", - label: "Adventures await", - detail: "New experiences daily", - }, - ].map((stat) => ( -
-
-
-
- {stat.value} -
-
- {stat.label} -
-
- {stat.detail} -
-
-
- ))} -
-
- - {/* ── 3. Experiences — what makes the hotel special ── */} -
-
-

+ {[ + { value: online.toLocaleString(), label: "Guests online" }, + { value: users.toLocaleString(), label: "Total citizens" }, + { value: rooms.toLocaleString(), label: "Rooms built" }, + ].map((s) => ( +
- Everything{" "} - - {hotelName} - {" "} - has to offer -

-

- From styling your avatar to building empires — it all happens here -

-
-
- {[ - { - icon: "/assets/images/icons/star.gif", - title: "Avatar studio", - desc: "Mix, match and stand out with thousands of outfits, accessories and effects.", - }, - { - icon: "/assets/images/icons/catalog.png", - title: "Furniture catalog", - desc: "Deck out your room with an ever-growing collection of furniture and decor.", - }, - { - icon: "/assets/images/icons/navigation/community.png", - title: "Community hub", - desc: "Meet people from around the world, join groups and make lifelong friends.", - }, - { - icon: "/assets/images/icons/navigation/bb.png", - title: "Battle & play", - desc: "Compete in snowstorm, battleball, and weekly events with rewards.", - }, - ].map((item) => ( + {s.value} +
+ {s.label} +
+
+ ))} +
+ + {/* ── Main content: News + Sidebar ── */} +
+ {/* News feed (left, 2/3) */} +
+ {featuredArticle && ( + -
-
-
+
+ {featuredArticle.image ? ( + ) : ( +
+ )} +
+
+

+ {featuredArticle.title} +

+

+ {formatDate(featuredArticle.createdAt, "date", "")} +

-

- {item.title} -

-

+ + )} + + {remainingArticles.length > 0 && ( +

+ {remainingArticles.map((a) => ( + - {item.desc} -

-
-
- ))} -
- - - {/* ── 4. Bulletin — latest articles ── */} - {articles.length > 0 && ( -
-
-
-

- Hotel bulletin -

-

- Stay up to date with the latest from {hotelName} -

-
- - Browse all articles - - arrow - - - - -
-
- {articles.map((a, i) => ( - -
- {a.image ? ( - - ) : ( -
- )} -
- {i === 0 && ( -
-

+ {a.image ? ( + + ) : ( +
- {a.title} -

-

- {formatDate(a.createdAt, "date", "")} -

-
- )} -
- {i > 0 && ( -
+ /> + )} +
+

- )} - - ))} -
-
- - Browse all articles → - -
-
- )} + + ))} +
+ )} - {/* ── 5. Final CTA — doors are open ── */} -
-
-
-
+
+ + {/* Sidebar (right, 1/3) */} +
+ )} + + {/* Recent photos */} + {recentPhotos.length > 0 && ( +
+

+ Recent photos +

+
+ {recentPhotos.map((p) => ( + +
+ +
+ + ))} +
+
+ )} + +
+ + {/* ── CTA ── */} +
+

+ See you inside! +

+

+ {hotelName} is free, forever. Create your account and start your + adventure today. +

+ + + Join for free +
); diff --git a/src/app/(site)/register/page.tsx b/src/app/(site)/register/page.tsx index 5de5a400..f8641872 100644 --- a/src/app/(site)/register/page.tsx +++ b/src/app/(site)/register/page.tsx @@ -4,175 +4,187 @@ import Link from "next/link"; import { getTranslations } from "next-intl/server"; import { RegisterForm } from "@/components/auth/register-form"; import { cached } from "@/lib/cache"; +import { avatarImageUrl } 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 RegisterPage() { const t = await getTranslations("header"); - const [hotelName, cfg] = await Promise.all([ + const [hotelName, imagerRaw, cfg] = await Promise.all([ siteSettings.get("hotel_name", "Hotel"), + siteSettings.get( + "habbo_imaging_url", + "https://www.habbo.com/habbo-imaging/avatarimage", + ), captchaConfig(), ]); + const imager = imagerRaw ?? "https://www.habbo.com/habbo-imaging/avatarimage"; const nonce = (await headers()).get("x-nonce") ?? undefined; - const online = await cached("online_count", 10_000, () => - prisma.user.count({ where: { online: "1" } }), - ).catch(() => 0); + const [online, recentUsers] = await Promise.all([ + cached("online_count", 10_000, () => + prisma.user.count({ where: { online: "1" } }), + ).catch(() => 0), + prisma.user + .findMany({ + where: { online: "1" }, + select: { username: true, look: true }, + take: 8, + }) + .catch(() => []), + ]); return ( -
- {/* ── Left: Reception desk ── */} -
+
+ {/* ── Left panel: Frank + avatars ── */} +
-
-
-
-
- Frank -
- -
-
- - {online.toLocaleString()} here now -
- -

- Welcome to{" "} - - {hotelName} - -

-

- {t("tagline")} -

-
- -
+ Join{" "} + - {[ - { icon: "🎨", text: "Unique avatar" }, - { icon: "🏠", text: "Your own room" }, - { icon: "💬", text: "Meet new people" }, - ].map((item) => ( -
- {item.icon} - {item.text} -
- ))} -
-
-
-
- - {/* ── Right: Check-in desk (registration form) ── */} -
-
-
-

- Create your account -

-

- It takes less than a minute. No email required. -

-
- - - + {hotelName} + +

- Already checked in?{" "} - +

+ + {online.toLocaleString()} online now +
+
+ + {recentUsers.length > 0 && ( +
+

- Sign in here - + Who's online +

+
+ {recentUsers.map((u) => ( +
+ + + {u.username} + +
+ ))} +
+
+ )} + +

+ Already have an account?{" "} + + Sign in + +

+
+ + {/* ── Right: Registration form ── */} +
+
+

+ Create your account +

+

+ Free and takes less than a minute

+
);