Redesign login, register, me pages with premium layout and i18n
Deploy / release (push) Skipped
Deploy / deploy (push) Successful in 2m7s

This commit is contained in:
openhands committed 2026-07-22 14:42:01 +02:00
1 parent 063ac9ec40
commit 1ffd46713a
7 files changed
+621 -279

No files matched your search

+89 -38
View File
@@ -33,7 +33,7 @@ async function getHotelData() {
.findMany({
where: { online: "1" },
select: { username: true, look: true },
take: 12,
take: 30,
})
.catch(() => []),
prisma.cameraWeb
@@ -54,7 +54,8 @@ async function getHotelData() {
}
export default async function Home() {
const t = await getTranslations("header");
const tp = await getTranslations("pages.home");
const th = await getTranslations("header");
const {
hotelName,
imager,
@@ -68,6 +69,21 @@ export default async function Home() {
return (
<div className="flex flex-col gap-16 pb-16">
<style>{`
@keyframes train-scroll {
0% { transform: translateX(0); }
100% { transform: translateX(-50%); }
}
.train-track {
animation: train-scroll 40s linear infinite;
display: flex;
gap: 0.75rem;
width: max-content;
}
.train-track:hover {
animation-play-state: paused;
}
`}</style>
{/* ── 1. Welcome section ── */}
<div className="flex flex-col lg:flex-row items-center gap-10 lg:gap-16">
<div className="flex-1 text-center lg:text-left">
@@ -82,20 +98,13 @@ export default async function Home() {
}}
>
<span className="w-2 h-2 rounded-full bg-green-500" />
{online.toLocaleString()} online
{th("online", { count: online, hotel: hotelName ?? "Hotel" })}
</div>
<h1
className="text-4xl md:text-5xl lg:text-6xl font-black leading-[1.05] tracking-tight mb-4"
style={{ color: "var(--color-text-readable)" }}
>
Welcome to{" "}
<span
style={{
color: "var(--color-primary-readable, var(--color-primary))",
}}
>
{hotelName}
</span>
{tp("welcome", { hotelName: hotelName ?? "Hotel" })}
</h1>
<p
className="text-base md:text-lg leading-relaxed max-w-xl mb-8"
@@ -104,7 +113,7 @@ export default async function Home() {
"var(--color-text-muted-readable, var(--color-text-muted))",
}}
>
{t("tagline")}
{th("tagline")}
</p>
<div className="flex flex-wrap gap-3 justify-center lg:justify-start">
<Link
@@ -124,7 +133,7 @@ export default async function Home() {
height={20}
unoptimized
/>
Enter {hotelName}
{tp("enterHotel", { hotelName: hotelName ?? "Hotel" })}
</Link>
<Link
href="/client"
@@ -135,7 +144,7 @@ export default async function Home() {
color: "var(--color-text-readable)",
}}
>
Play now
{th("nitroClient")}
</Link>
</div>
</div>
@@ -181,7 +190,7 @@ export default async function Home() {
{u.username}
</p>
<p className="text-[10px] font-semibold text-green-500">
Online
{tp("statsOnline")}
</p>
</div>
</div>
@@ -193,9 +202,9 @@ export default async function Home() {
{/* ── 2. Stats ── */}
<div className="grid grid-cols-3 gap-4">
{[
{ value: online.toLocaleString(), label: "Online now", icon: "🟢" },
{ value: users.toLocaleString(), label: "Total users", icon: "👤" },
{ value: rooms.toLocaleString(), label: "Rooms", icon: "🏠" },
{ value: online.toLocaleString(), label: tp("statsOnline"), icon: "🟢" },
{ value: users.toLocaleString(), label: tp("statsCitizens"), icon: "👤" },
{ value: rooms.toLocaleString(), label: tp("statsRooms"), icon: "🏠" },
].map((s) => (
<div
key={s.label}
@@ -222,28 +231,70 @@ export default async function Home() {
))}
</div>
{/* ── 3. Features ── */}
{/* ── 3. User train ── */}
{recentUsers.length > 0 && (
<div
className="rounded-2xl border overflow-hidden py-3"
style={{
background: "var(--color-surface)",
borderColor: "color-mix(in srgb, var(--color-text-muted) 10%, transparent)",
}}
>
<div className="train-track">
{[...recentUsers, ...recentUsers].map((u, i) => (
<div
key={`${u.username}-${i}`}
className="flex flex-col items-center gap-0.5 px-2 py-1.5 rounded-xl transition-all duration-200 hover:scale-110"
style={{
background: "color-mix(in srgb, var(--color-primary) 4%, transparent)",
}}
>
<Image
src={avatarImageUrl(imager, u.look, {
headOnly: true,
direction: 2,
size: "s",
})}
alt={u.username}
width={34}
height={42}
className="rounded-md"
unoptimized
/>
<span
className="text-[9px] font-bold truncate max-w-[54px] text-center leading-tight"
style={{ color: "var(--color-text-readable)" }}
>
{u.username}
</span>
</div>
))}
</div>
</div>
)}
{/* ── 4. Features ── */}
<div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-4">
{[
{
img: "/assets/images/icons/star.gif",
title: "Avatar",
desc: "Create your unique look with thousands of outfits",
title: tp("featuresAvatar"),
desc: tp("featuresAvatarDesc"),
},
{
img: "/assets/images/icons/catalog.png",
title: "Furniture",
desc: "Decorate rooms with hundreds of items",
title: tp("featuresFurniture"),
desc: tp("featuresFurnitureDesc"),
},
{
img: "/assets/images/icons/navigation/community.png",
title: "Community",
desc: "Make friends and join groups",
title: tp("featuresCommunity"),
desc: tp("featuresCommunityDesc"),
},
{
img: "/assets/images/icons/navigation/bb.png",
title: "Games",
desc: "Compete in battles and events",
title: tp("featuresGames"),
desc: tp("featuresGamesDesc"),
},
].map((f) => (
<div
@@ -280,7 +331,7 @@ export default async function Home() {
))}
</div>
{/* ── 4. News + Sidebar ── */}
{/* ── 5. News + Sidebar ── */}
<div className="grid lg:grid-cols-12 gap-6">
<div className="lg:col-span-8 space-y-4">
<div className="flex items-center justify-between">
@@ -288,14 +339,14 @@ export default async function Home() {
className="text-xl font-black tracking-tight"
style={{ color: "var(--color-text-readable)" }}
>
News
{tp("latestNews")}
</h2>
<Link
href="/news"
className="text-sm font-bold hover:underline"
style={{ color: "var(--color-primary)" }}
>
View all →
{tp("allNews")}
</Link>
</div>
{articles.length > 0 ? (
@@ -349,7 +400,7 @@ export default async function Home() {
</div>
) : (
<p className="text-sm" style={{ color: "var(--color-text-muted)" }}>
No news yet.
{tp("noArticles")}
</p>
)}
</div>
@@ -369,7 +420,7 @@ export default async function Home() {
style={{ color: "var(--color-text-muted)" }}
>
<span className="w-2 h-2 rounded-full bg-green-500" />
Online now
{tp("recentUsers")}
</h3>
<div className="grid grid-cols-3 gap-2">
{recentUsers.slice(0, 9).map((u) => (
@@ -418,7 +469,7 @@ export default async function Home() {
className="text-xs font-bold uppercase tracking-widest mb-3"
style={{ color: "var(--color-text-muted)" }}
>
Photos
{tp("recentPhotos")}
</h3>
<div className="grid grid-cols-2 gap-2">
{recentPhotos.map((p) => (
@@ -461,13 +512,13 @@ export default async function Home() {
className="text-xs font-bold"
style={{ color: "var(--color-text-muted)" }}
>
See you inside!
{tp("joinNow", { hotelName: hotelName ?? "Hotel" })}
</p>
</div>
</aside>
</div>
{/* ── 5. CTA ── */}
{/* ── 6. CTA ── */}
<div
className="rounded-2xl border text-center px-6 py-10"
style={{
@@ -480,13 +531,13 @@ export default async function Home() {
className="text-2xl md:text-3xl font-black tracking-tight mb-2"
style={{ color: "var(--color-text-readable)" }}
>
Ready to join?
{tp("joinNow", { hotelName: hotelName ?? "Hotel" })}
</h2>
<p
className="text-sm mb-6 max-w-sm mx-auto"
style={{ color: "var(--color-text-muted)" }}
>
Create your free account and start your adventure today.
{tp("welcomeBody")}
</p>
<Link
href="/register"
@@ -505,7 +556,7 @@ export default async function Home() {
height={20}
unoptimized
/>
Create free account
{tp("register")}
</Link>
</div>
</div>