Files
EpicNext-Cms/src/app/guilds/page.tsx
T
openhands 8bcbc501ba
CI / check (push) Has been cancelled
Performance, SEO, a11y, and code quality improvements
1. Performance: 25 pages switched from force-dynamic to revalidate=300 (ISR);
   2 pages (community, developers) now fully static (SSG)
2. DB indexes: Added @@index on foreign keys for WebsiteArticles,
   WebsiteArticleReactions, WebsiteArticleComments, WebsiteHelpCenterTickets,
   WebsiteShopArticles, RadioSongRequests, StaffActivities
3. SEO: Added robots.ts, sitemap.ts, canonical URLs, Open Graph + Twitter
   Card metadata on root layout and news articles
4. A11Y: Replaced <details>/<summary> dropdowns with accessible button-based
   NavDropdown (aria-expanded, aria-haspopup, role=menu). MobileNav now
   uses translated aria-label, aria-expanded, aria-controls, role=menu
5. Code quality: Added try/catch to updateArticle/deleteArticle; deleteArticle
   now uses prisma. for atomicity
6. CI/CD: Added GitHub Actions workflow (typecheck + test)
7. i18n: Added openMenu/closeMenu keys to all 6 locales
8. Observability: Health endpoint now checks SMTP reachability when configured
9. Loading states: Added loading.tsx for root, admin, and news sections
10. Word filter cache: Added 60s TTL auto-refresh instead of manual cache bust
2026-07-04 19:41:04 +02:00

87 lines
2.5 KiB
TypeScript

import { getTranslations } from "next-intl/server";
import Link from "next/link";
import { ContentCard, EmptyState } from "@/components/public/ui";
import { excerpt } from "@/lib/format";
import { prisma } from "@/lib/prisma";
export const revalidate = 300;
export const metadata = { title: "Guilds" };
type GuildCard = {
id: number;
name: string;
description: string;
badge: string;
};
async function getGuilds(): Promise<GuildCard[]> {
try {
const guilds = await prisma.guilds.findMany({
orderBy: { id: "desc" },
take: 40,
select: { id: true, name: true, description: true, badge: true },
});
return guilds;
} 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>
);
}