import Link from "next/link"; import { notFound } from "next/navigation"; import { getTranslations } from "next-intl/server"; import { ContentCard, EmptyState, StatBlock } from "@/components/public/ui"; import { avatarImageUrl } from "@/lib/format"; import { prisma } from "@/lib/prisma"; import { formatDate } from "@/lib/format-date"; import { siteSettings } from "@/lib/services/site-settings"; export const revalidate = 300; type GuildView = { id: number; name: string; description: string; badge: string; userId: number; dateCreated: number; }; type MemberView = { userId: number; username: string; look: string; motto: string; memberSince: number; isOwner: boolean; }; export default async function GuildPage({ params, }: { params: Promise<{ id: string }>; }) { const t = await getTranslations("pages.guild"); const { id } = await params; const guildId = Number(id); if (!Number.isInteger(guildId) || guildId <= 0) notFound(); let guild: GuildView | null = null; try { guild = await prisma.guilds.findUnique({ where: { id: guildId }, select: { id: true, name: true, description: true, badge: true, userId: true, dateCreated: true, }, }); } catch { guild = null; } if (!guild) notFound(); // Members come from guilds_members; there is no relation in the schema, so we // join the users table manually by id. let memberRows: { userId: number; memberSince: number }[] = []; try { memberRows = await prisma.guildsMembers.findMany({ where: { guildId: guild.id }, orderBy: { memberSince: "asc" }, take: 200, select: { userId: true, memberSince: true }, }); } catch { memberRows = []; } const memberIds = Array.from( new Set( [guild.userId, ...memberRows.map((m) => m.userId)].filter((v) => v > 0), ), ); let users: { id: number; username: string; look: string; motto: string }[] = []; try { users = memberIds.length ? await prisma.user.findMany({ where: { id: { in: memberIds } }, select: { id: true, username: true, look: true, motto: true }, }) : []; } catch { users = []; } const imagerBase = await siteSettings.get( "habbo_imaging_url", "https://www.habbo.com/habbo-imaging/avatarimage", ); const usersById = new Map(users.map((u) => [u.id, u])); const sinceById = new Map(memberRows.map((m) => [m.userId, m.memberSince])); const guildOwnerUserId = guild.userId; const members: MemberView[] = memberIds .map((uid) => { const u = usersById.get(uid); if (!u) return null; return { userId: u.id, username: u.username, look: u.look, motto: u.motto, memberSince: sinceById.get(uid) ?? 0, isOwner: uid === guildOwnerUserId, }; }) .filter((m): m is MemberView => m !== null) // Owner first, then by join date. .sort((a, b) => { if (a.isOwner !== b.isOwner) return a.isOwner ? -1 : 1; return a.memberSince - b.memberSince; }); const created = formatDate(new Date(guild.dateCreated * 1000), "date"); return (

{t("allGuilds")}

{guild.badge ? ( // eslint-disable-next-line @next/next/no-img-element {`${guild.name} ) : null}

{guild.name || t("unnamedGuild")}

{guild.description || ( {t("noDescription")} )}

{created ? ( ) : null} {t("forumLink")} } label={t("discussion")} />
{members.length === 0 ? ( {t("noMembers")} ) : (
{members.map((m) => (
{/* eslint-disable-next-line @next/next/no-img-element */} {`${m.username}

{m.username}

{m.isOwner ? ( 👑 {t("owner")} ) : null}

{m.motto || t("noMotto")}

))}
)}
); }