133 lines
3.6 KiB
TypeScript
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} />;
|
|
}
|