Files
Epicnabbo-Catalogus-Updated…/src/features/housekeeping/domains/people/pages/community.tsx
T

133 lines
3.6 KiB
TypeScript

import { PERMS } from "@/lib/permission-slugs";
import {
createCorrelationId,
fail,
type HousekeepingCapabilityContext,
type HousekeepingResult,
} from "../../../foundation/contracts";
import type { HousekeepingPageInput } from "../../../route-handlers";
import {
type PeopleCommunityQueryData,
peopleCommunityQuery,
} from "../queries/community";
import { PeoplePageFrame, parsePeopleListInput } from "./page-state";
import { PeopleCommandForm } from "./people-command-form";
interface PeopleCommunityPageProps {
readonly context: HousekeepingCapabilityContext;
readonly result?: HousekeepingResult<PeopleCommunityQueryData>;
}
function empty(data: PeopleCommunityQueryData) {
return data.kind !== "guild" && data.page.items.length === 0;
}
function SearchForm() {
return (
<form method="get" className="flex gap-2">
<input name="search" maxLength={100} aria-label="Search community" />
<button type="submit">Search</button>
</form>
);
}
export function PeopleCommunityPage({
context,
result,
}: PeopleCommunityPageProps) {
return (
<PeoplePageFrame
title="Community"
description="Review online users and guild ownership."
result={result}
isEmpty={empty}
>
{(data) => {
if (data.kind === "online")
return (
<div className="space-y-3">
<SearchForm />
<ul className="rounded-lg border border-[var(--admin-border)] bg-[var(--admin-surface)] p-4">
{data.page.items.map((user) => (
<li key={user.id}>
<a href={user.href}>{user.username}</a> - {user.motto}
</li>
))}
</ul>
</div>
);
if (data.kind === "guilds")
return (
<div className="space-y-3">
<SearchForm />
<ul className="space-y-2">
{data.page.items.map((guild) => (
<li
key={guild.id}
className="rounded-lg border border-[var(--admin-border)] bg-[var(--admin-surface)] p-4"
>
<a href={guild.href}>{guild.name}</a>
<p>{guild.memberCount} members</p>
</li>
))}
</ul>
</div>
);
return (
<section className="space-y-3 rounded-lg border border-[var(--admin-border)] bg-[var(--admin-surface)] p-4">
<h2>{data.guild.name}</h2>
<p>{data.guild.description}</p>
<ul>
{data.guild.members.map((member) => (
<li key={member.id}>
<a href={member.href}>{member.username}</a>
</li>
))}
</ul>
{context.has(PERMS.USERS_EDIT) ? (
<PeopleCommandForm
commandId="people.guild.disband"
buttonLabel="Disband guild"
input={{ guildId: data.guild.id }}
requiresReason
/>
) : null}
</section>
);
}}
</PeoplePageFrame>
);
}
export async function renderPeopleCommunityPage(input: HousekeepingPageInput) {
const routeId = input.match.routeId;
const result =
routeId === "people.community.guild-detail"
? (() => {
const id = Number(input.match.params.id);
return Number.isSafeInteger(id) && id > 0
? peopleCommunityQuery.run(input.context, { routeId, id })
: Promise.resolve(
fail(
"VALIDATION",
"errors.housekeeping.validation",
createCorrelationId(),
),
);
})()
: routeId === "people.community.online" ||
routeId === "people.community.guilds"
? peopleCommunityQuery.run(input.context, {
routeId,
list: parsePeopleListInput(input.searchParams ?? {}),
})
: Promise.resolve(
fail(
"NOT_FOUND",
"errors.housekeeping.notFound",
createCorrelationId(),
),
);
return <PeopleCommunityPage context={input.context} result={await result} />;
}