Redesign login, register, me pages with premium layout and i18n
This commit is contained in:
1 parent
063ac9ec40
commit
1ffd46713a
7 files changed
+621
-279
No files matched your search
+89
-38
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user