Files
EpicNext-Cms/src/app/(site)/guilds/page.tsx
T
openhands dc8fb8a6ed feat: speed up admin clone import and enable Cache Components
- Clone import: defer FurnitureData.json writes and append all entries in a
  single batched write instead of one read-modify-write per item, removing
  the main serialization bottleneck for large batches.
- Clone import: raise SSE batch concurrency cap from 5 to 10 and bump the
  clone client/route default from 2 to 6.
- Add a flush hook to runSseBatch so callers can batch deferred work before
  batch_complete is emitted, and surface flush errors as an error event.
- Enable Next.js Cache Components (instant: false opt-out) and silence the
  related build warnings in next.config.ts.
- Switch isomorphic-dompurify to dompurify and refresh dependencies.
2026-08-05 11:10:16 +02:00

92 lines
2.4 KiB
TypeScript

import { desc } from "drizzle-orm";
import Link from "next/link";
import { getTranslations } from "next-intl/server";
import { ContentCard, EmptyState } from "@/components/public/ui";
import { db, Guilds } from "@/lib/db";
import { excerpt } from "@/lib/format";
// TODO: Cache Components adoption. Refactor this route so this opt-out can be removed.
// See: https://nextjs.org/docs/app/guides/migrating-to-cache-components
export const instant = false;
export const metadata = { title: "Guilds" };
type GuildCard = {
id: number;
name: string;
description: string;
badge: string;
};
async function getGuilds(): Promise<GuildCard[]> {
try {
return await db
.select({
id: Guilds.id,
name: Guilds.name,
description: Guilds.description,
badge: Guilds.badge,
})
.from(Guilds)
.orderBy(desc(Guilds.id))
.limit(40);
} catch {
return [];
}
}
export default async function GuildsPage() {
const t = await getTranslations("pages.guilds");
const guilds = await getGuilds();
return (
<main style={{ display: "grid", gap: "1.5rem" }}>
<ContentCard icon="🚪" title={t("title")} subtitle={t("subtitle")} />
<ContentCard padded={guilds.length === 0}>
{guilds.length === 0 ? (
<EmptyState icon="🚪">{t("empty")}</EmptyState>
) : (
<div className="card-grid sm-2 lg-3" style={{ padding: "1rem" }}>
{guilds.map((g) => (
<Link
key={g.id}
href={`/guilds/${g.id}`}
className="card hover"
style={{
display: "flex",
gap: "0.85rem",
alignItems: "flex-start",
color: "inherit",
textDecoration: "none",
}}
>
{g.badge ? (
// eslint-disable-next-line @next/next/no-img-element
<img
src={`https://habbo-stories-content.s3.amazonaws.com/badge-part-images/${g.badge}.gif`}
alt={`${g.name} badge`}
width={40}
height={40}
style={{ flex: "0 0 auto" }}
/>
) : null}
<div style={{ minWidth: 0 }}>
<h3 style={{ margin: "0 0 0.25rem", fontSize: "1rem" }}>
{g.name || t("unnamedGuild")}
</h3>
<p className="muted" style={{ margin: 0 }}>
{g.description
? excerpt(g.description, 120)
: t("noDescription")}
</p>
</div>
</Link>
))}
</div>
)}
</ContentCard>
</main>
);
}