fix(public): distinguish unavailable data from empty results and missing pages
This commit is contained in:
1 parent
db4acbb46e
commit
445ef13846
16 files changed
+608
-169
No files matched your search
+94
-69
@@ -4,6 +4,8 @@ import { headers } from "next/headers";
|
||||
import Image from "next/image";
|
||||
import { redirect } from "next/navigation";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { PublicLoadError } from "@/components/public/load-error";
|
||||
import { publicReadFailure } from "@/lib/services/public-read";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Home",
|
||||
@@ -53,34 +55,36 @@ async function getHotelData() {
|
||||
.from(User)
|
||||
.where(eq(User.online, "1"))
|
||||
.then((rows) => rows[0]?.total ?? 0),
|
||||
).catch(() => 0),
|
||||
).catch(publicReadFailure("home.online")),
|
||||
cached("total_users", 300_000, () =>
|
||||
db
|
||||
.select({ total: count() })
|
||||
.from(User)
|
||||
.then((rows) => rows[0]?.total ?? 0),
|
||||
).catch(() => 0),
|
||||
).catch(publicReadFailure("home.users")),
|
||||
cached("total_rooms", 300_000, () =>
|
||||
db
|
||||
.select({ total: count() })
|
||||
.from(Rooms)
|
||||
.then((rows) => rows[0]?.total ?? 0),
|
||||
).catch(() => 0),
|
||||
getNewsList(4),
|
||||
).catch(publicReadFailure("home.rooms")),
|
||||
getNewsList(4, { throwOnError: true }).catch(
|
||||
publicReadFailure("home.news"),
|
||||
),
|
||||
cached("home_online_users", 15_000, () =>
|
||||
db
|
||||
.select({ username: User.username, look: User.look })
|
||||
.from(User)
|
||||
.where(eq(User.online, "1"))
|
||||
.limit(12),
|
||||
).catch(() => []),
|
||||
).catch(publicReadFailure("home.recent-users")),
|
||||
cached("home_recent_photos", 60_000, () =>
|
||||
db
|
||||
.select({ id: CameraWeb.id, url: CameraWeb.url })
|
||||
.from(CameraWeb)
|
||||
.orderBy(desc(CameraWeb.timestamp))
|
||||
.limit(4),
|
||||
).catch(() => []),
|
||||
).catch(publicReadFailure("home.photos")),
|
||||
]);
|
||||
|
||||
return {
|
||||
@@ -100,6 +104,7 @@ export default async function Home() {
|
||||
if (session?.user?.id) redirect("/me");
|
||||
|
||||
const tp = await getTranslations("pages.home");
|
||||
const te = await getTranslations("publicData");
|
||||
const th = await getTranslations("header");
|
||||
const {
|
||||
hotelName,
|
||||
@@ -251,7 +256,11 @@ export default async function Home() {
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75" />
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-primary" />
|
||||
</span>
|
||||
<LiveOnlineCount initial={online} hotelName={hotelName} />
|
||||
{online === null ? (
|
||||
<span>{te("unavailable")}</span>
|
||||
) : (
|
||||
<LiveOnlineCount initial={online} hotelName={hotelName} />
|
||||
)}
|
||||
</div>
|
||||
<h1
|
||||
className="text-2xl font-black leading-[1.05] tracking-tight mb-2 sm:text-3xl md:text-5xl lg:text-6xl"
|
||||
@@ -368,7 +377,7 @@ export default async function Home() {
|
||||
live: true,
|
||||
},
|
||||
{
|
||||
value: articles.length,
|
||||
value: articles?.length ?? null,
|
||||
label: tp("statsArticles"),
|
||||
icon: "/assets/images/icons/article.gif",
|
||||
live: false,
|
||||
@@ -379,7 +388,9 @@ export default async function Home() {
|
||||
className="relative p-3 sm:p-5 text-center transition-all duration-300 hover:-translate-y-1 hover:shadow-lg"
|
||||
>
|
||||
<div className="mb-0.5 text-2xl sm:text-3xl font-black tracking-tight md:text-4xl">
|
||||
{s.live ? (
|
||||
{s.value === null ? (
|
||||
<span className="text-base">{te("unavailable")}</span>
|
||||
) : s.live ? (
|
||||
<LiveOnlineCounter initial={s.value} />
|
||||
) : (
|
||||
<AnimatedCounter value={s.value} />
|
||||
@@ -415,36 +426,42 @@ export default async function Home() {
|
||||
/>
|
||||
</SurfaceCard>
|
||||
|
||||
{recentUsers.length > 0 && (
|
||||
<SurfaceCard
|
||||
title={tp("recentUsers")}
|
||||
icon="/assets/images/icons/navigation/goody.png"
|
||||
actionHref="/community"
|
||||
actionLabel={tp("allUsers")}
|
||||
bodyClassName="p-3.5 sm:p-4"
|
||||
>
|
||||
<div className="grid grid-cols-4 gap-1.5 sm:gap-2">
|
||||
{recentUsers.map((u) => (
|
||||
<div
|
||||
key={u.username}
|
||||
className="flex flex-col items-center gap-0.5 rounded-lg px-1 py-1.5 transition-colors hover:bg-[color-mix(in_srgb,var(--color-primary)_5%,transparent)]"
|
||||
>
|
||||
<UserAvatarThumbnail
|
||||
figure={u.look}
|
||||
alt={u.username}
|
||||
options={{ direction: 2 }}
|
||||
className="rounded-md"
|
||||
/>
|
||||
<span
|
||||
className="max-w-[64px] truncate text-center text-[9px] sm:text-[10px] font-bold leading-tight"
|
||||
style={{ color: "var(--color-text-readable)" }}
|
||||
>
|
||||
{u.username}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{recentUsers === null ? (
|
||||
<SurfaceCard title={tp("recentUsers")} bodyClassName="p-4">
|
||||
<PublicLoadError href="/" />
|
||||
</SurfaceCard>
|
||||
) : (
|
||||
recentUsers.length > 0 && (
|
||||
<SurfaceCard
|
||||
title={tp("recentUsers")}
|
||||
icon="/assets/images/icons/navigation/goody.png"
|
||||
actionHref="/community"
|
||||
actionLabel={tp("allUsers")}
|
||||
bodyClassName="p-3.5 sm:p-4"
|
||||
>
|
||||
<div className="grid grid-cols-4 gap-1.5 sm:gap-2">
|
||||
{recentUsers.map((u) => (
|
||||
<div
|
||||
key={u.username}
|
||||
className="flex flex-col items-center gap-0.5 rounded-lg px-1 py-1.5 transition-colors hover:bg-[color-mix(in_srgb,var(--color-primary)_5%,transparent)]"
|
||||
>
|
||||
<UserAvatarThumbnail
|
||||
figure={u.look}
|
||||
alt={u.username}
|
||||
options={{ direction: 2 }}
|
||||
className="rounded-md"
|
||||
/>
|
||||
<span
|
||||
className="max-w-[64px] truncate text-center text-[9px] sm:text-[10px] font-bold leading-tight"
|
||||
style={{ color: "var(--color-text-readable)" }}
|
||||
>
|
||||
{u.username}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</SurfaceCard>
|
||||
)
|
||||
)}
|
||||
</Reveal>
|
||||
|
||||
@@ -456,7 +473,9 @@ export default async function Home() {
|
||||
actionHref="/news"
|
||||
actionLabel={tp("allNews")}
|
||||
>
|
||||
{articles.length > 0 ? (
|
||||
{articles === null ? (
|
||||
<PublicLoadError href="/" />
|
||||
) : articles.length > 0 ? (
|
||||
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-4">
|
||||
{articles.slice(0, 4).map((a) => (
|
||||
<Link
|
||||
@@ -517,36 +536,42 @@ export default async function Home() {
|
||||
</div>
|
||||
|
||||
{/* ── Recent photos ── */}
|
||||
{recentPhotos.length > 0 && (
|
||||
<Reveal>
|
||||
<SurfaceCard
|
||||
title={tp("recentPhotos")}
|
||||
icon="/assets/images/icons/camera.png"
|
||||
>
|
||||
<div className="mx-auto grid max-w-md grid-cols-4 gap-2">
|
||||
{recentPhotos.slice(0, 4).map((p) => (
|
||||
<Link
|
||||
key={p.id}
|
||||
href="/photos"
|
||||
className="block aspect-square overflow-hidden rounded-xl border transition-all duration-200 hover:shadow-md hover:scale-[1.02]"
|
||||
style={{
|
||||
borderColor:
|
||||
"color-mix(in srgb, var(--color-text-muted) 8%, transparent)",
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src={p.url}
|
||||
alt=""
|
||||
width={200}
|
||||
height={200}
|
||||
className="h-full w-full object-cover"
|
||||
unoptimized
|
||||
/>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</SurfaceCard>
|
||||
</Reveal>
|
||||
{recentPhotos === null ? (
|
||||
<SurfaceCard title={tp("recentPhotos")} bodyClassName="p-4">
|
||||
<PublicLoadError href="/" />
|
||||
</SurfaceCard>
|
||||
) : (
|
||||
recentPhotos.length > 0 && (
|
||||
<Reveal>
|
||||
<SurfaceCard
|
||||
title={tp("recentPhotos")}
|
||||
icon="/assets/images/icons/camera.png"
|
||||
>
|
||||
<div className="mx-auto grid max-w-md grid-cols-4 gap-2">
|
||||
{recentPhotos.slice(0, 4).map((p) => (
|
||||
<Link
|
||||
key={p.id}
|
||||
href="/photos"
|
||||
className="block aspect-square overflow-hidden rounded-xl border transition-all duration-200 hover:shadow-md hover:scale-[1.02]"
|
||||
style={{
|
||||
borderColor:
|
||||
"color-mix(in srgb, var(--color-text-muted) 8%, transparent)",
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src={p.url}
|
||||
alt=""
|
||||
width={200}
|
||||
height={200}
|
||||
className="h-full w-full object-cover"
|
||||
unoptimized
|
||||
/>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</SurfaceCard>
|
||||
</Reveal>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user