fix(housekeeping): address task 14 review round 1
This commit is contained in:
1 parent
fd68819d9b
commit
d09eaa33d6
23 files changed
+2503
-488
No files matched your search
@@ -1,34 +1,407 @@
|
||||
import { PERMS } from "@/lib/permission-slugs";
|
||||
import type { HousekeepingPageInput } from "../../../route-handlers";
|
||||
import { contentQuery } from "../queries/content-queries";
|
||||
import { ContentCommandForm } from "./content-command-form";
|
||||
import { ContentPageFrame, type ContentPageProps, parseContentListInput } from "./content-page-frame";
|
||||
import {
|
||||
type ContentCommandField,
|
||||
ContentCommandForm,
|
||||
} from "./content-command-form";
|
||||
import {
|
||||
ContentPageFrame,
|
||||
type ContentPageProps,
|
||||
parseContentListInput,
|
||||
} from "./content-page-frame";
|
||||
|
||||
export function ContentEngagementPage({ context, result, routeId }: ContentPageProps) {
|
||||
export function ContentEngagementPage({
|
||||
context,
|
||||
result,
|
||||
routeId,
|
||||
}: ContentPageProps) {
|
||||
const canEvents = context.has(PERMS.EVENTS_EDIT);
|
||||
const canPolls = context.has(PERMS.POLLS_EDIT);
|
||||
const canPrefixes = context.has(PERMS.PREFIXES_EDIT);
|
||||
return <ContentPageFrame title="Engagement" description="Manage events, polls, and community prefixes." result={result} forms={<div className="grid gap-3 lg:grid-cols-2">
|
||||
{canEvents && routeId === "content.engagement.event-types" ? <ContentCommandForm commandId="content.engagement.event-type.change" buttonLabel="Save event type" input={{ action: "update" }} fields={[{ name: "id", label: "Type ID", type: "identifier" }, { name: "name", label: "Name", type: "text", required: true, maxLength: 255 }]} /> : null}
|
||||
{canEvents && routeId?.includes("event") && routeId !== "content.engagement.event-types" ? <>
|
||||
<ContentCommandForm commandId="content.engagement.event.change" buttonLabel="Save event" input={{ action: routeId.endsWith("create") ? "create" : "update" }} fields={[{ name: "id", label: "Event ID", type: "identifier" }, { name: "title", label: "Title", type: "text", required: true, maxLength: 255 }]} />
|
||||
<ContentCommandForm commandId="content.engagement.event-prize.change" buttonLabel="Save prize" input={{ action: "create" }} fields={[{ name: "eventId", label: "Event ID", type: "identifier", required: true }, { name: "prize", label: "Prize", type: "text", required: true, maxLength: 255 }]} />
|
||||
<ContentCommandForm commandId="content.engagement.event-winner.add" buttonLabel="Add winner" input={{}} fields={[{ name: "eventId", label: "Event ID", type: "identifier", required: true }, { name: "userId", label: "User ID", type: "identifier", required: true }]} />
|
||||
</> : null}
|
||||
{canPolls && routeId?.includes("poll") ? <>
|
||||
<ContentCommandForm commandId="content.engagement.poll.change" buttonLabel="Save poll" input={{ action: routeId.endsWith("create") ? "create" : "update" }} fields={[{ name: "id", label: "Poll ID", type: "identifier" }, { name: "title", label: "Title", type: "text", required: true, maxLength: 255 }]} />
|
||||
<ContentCommandForm commandId="content.engagement.poll-question.change" buttonLabel="Save question" input={{ action: "create" }} fields={[{ name: "pollId", label: "Poll ID", type: "identifier", required: true }, { name: "question", label: "Question", type: "text", required: true, maxLength: 500 }]} />
|
||||
</> : null}
|
||||
{canPrefixes && routeId === "content.engagement.prefixes" ? <>
|
||||
<ContentCommandForm commandId="content.engagement.prefix.change" buttonLabel="Save prefix" input={{ action: "update" }} fields={[{ name: "id", label: "Prefix ID", type: "identifier" }, { name: "text", label: "Text", type: "text", required: true, maxLength: 64 }]} />
|
||||
<ContentCommandForm commandId="content.engagement.prefix-blacklist.change" buttonLabel="Update blacklist" input={{ action: "add" }} fields={[{ name: "word", label: "Word", type: "text", required: true, maxLength: 255 }]} />
|
||||
<ContentCommandForm commandId="content.engagement.prefix-settings.update" buttonLabel="Save prefix settings" input={{}} fields={[{ name: "enabled", label: "Enable prefixes", type: "checkbox" }]} />
|
||||
</> : null}
|
||||
</div>} />;
|
||||
const creatingEvent = routeId === "content.engagement.event-create";
|
||||
const creatingPoll = routeId === "content.engagement.poll-create";
|
||||
const eventFields: readonly ContentCommandField[] = [
|
||||
{
|
||||
name: "id",
|
||||
label: "Event ID",
|
||||
type: "identifier",
|
||||
required: !creatingEvent,
|
||||
},
|
||||
{
|
||||
name: "title",
|
||||
label: "Title",
|
||||
type: "text",
|
||||
required: creatingEvent,
|
||||
maxLength: 255,
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
label: "Description",
|
||||
type: "textarea",
|
||||
required: creatingEvent,
|
||||
maxLength: 20_000,
|
||||
},
|
||||
{
|
||||
name: "typeId",
|
||||
label: "Event type ID",
|
||||
type: "identifier",
|
||||
required: creatingEvent,
|
||||
},
|
||||
{ name: "roomId", label: "Room ID", type: "identifier" },
|
||||
{
|
||||
name: "startsAt",
|
||||
label: "Starts at",
|
||||
type: "text",
|
||||
required: creatingEvent,
|
||||
maxLength: 50,
|
||||
},
|
||||
{ name: "endsAt", label: "Ends at", type: "text", maxLength: 50 },
|
||||
{ name: "maxPlayers", label: "Maximum players", type: "number", min: 1 },
|
||||
{ name: "isRecurring", label: "Recurring", type: "checkbox" },
|
||||
{
|
||||
name: "recurrenceRule",
|
||||
label: "Recurrence rule",
|
||||
type: "text",
|
||||
maxLength: 255,
|
||||
},
|
||||
{
|
||||
name: "status",
|
||||
label: "Status",
|
||||
type: "select",
|
||||
options: [
|
||||
{ value: "draft", label: "Draft" },
|
||||
{ value: "published", label: "Published" },
|
||||
{ value: "cancelled", label: "Cancelled" },
|
||||
{ value: "completed", label: "Completed" },
|
||||
],
|
||||
},
|
||||
{ name: "image", label: "Image URL", type: "text", maxLength: 500 },
|
||||
];
|
||||
const pollFields: readonly ContentCommandField[] = [
|
||||
{
|
||||
name: "id",
|
||||
label: "Poll ID",
|
||||
type: "identifier",
|
||||
required: !creatingPoll,
|
||||
},
|
||||
{
|
||||
name: "title",
|
||||
label: "Title",
|
||||
type: "text",
|
||||
required: creatingPoll,
|
||||
maxLength: 255,
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
label: "Description",
|
||||
type: "textarea",
|
||||
maxLength: 2_000,
|
||||
},
|
||||
{
|
||||
name: "status",
|
||||
label: "Status",
|
||||
type: "select",
|
||||
options: [
|
||||
{ value: "draft", label: "Draft" },
|
||||
{ value: "active", label: "Active" },
|
||||
{ value: "closed", label: "Closed" },
|
||||
],
|
||||
},
|
||||
{ name: "showResults", label: "Show results", type: "checkbox" },
|
||||
{
|
||||
name: "multipleChoice",
|
||||
label: "Allow multiple choices",
|
||||
type: "checkbox",
|
||||
},
|
||||
{ name: "startsAt", label: "Starts at", type: "text", maxLength: 50 },
|
||||
{ name: "endsAt", label: "Ends at", type: "text", maxLength: 50 },
|
||||
];
|
||||
return (
|
||||
<ContentPageFrame
|
||||
title="Engagement"
|
||||
description="Manage events, polls, and community prefixes."
|
||||
result={result}
|
||||
forms={
|
||||
<div className="grid gap-3 lg:grid-cols-2">
|
||||
{canEvents && routeId === "content.engagement.event-types" ? (
|
||||
<ContentCommandForm
|
||||
commandId="content.engagement.event-type.change"
|
||||
buttonLabel="Save event type"
|
||||
input={{ action: "update" }}
|
||||
fields={[
|
||||
{
|
||||
name: "id",
|
||||
label: "Type ID",
|
||||
type: "identifier",
|
||||
required: true,
|
||||
},
|
||||
{ name: "name", label: "Name", type: "text", maxLength: 100 },
|
||||
{ name: "slug", label: "Slug", type: "text", maxLength: 100 },
|
||||
{
|
||||
name: "description",
|
||||
label: "Description",
|
||||
type: "textarea",
|
||||
maxLength: 500,
|
||||
},
|
||||
{ name: "color", label: "Color", type: "text", maxLength: 20 },
|
||||
{ name: "icon", label: "Icon", type: "text", maxLength: 50 },
|
||||
{ name: "isActive", label: "Active", type: "checkbox" },
|
||||
{
|
||||
name: "minRank",
|
||||
label: "Minimum rank",
|
||||
type: "number",
|
||||
min: 0,
|
||||
max: 7,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : null}
|
||||
{canEvents &&
|
||||
routeId?.includes("event") &&
|
||||
routeId !== "content.engagement.event-types" ? (
|
||||
<>
|
||||
<ContentCommandForm
|
||||
commandId="content.engagement.event.change"
|
||||
buttonLabel="Save event"
|
||||
input={{ action: creatingEvent ? "create" : "update" }}
|
||||
fields={eventFields}
|
||||
/>
|
||||
<ContentCommandForm
|
||||
commandId="content.engagement.event-prize.change"
|
||||
buttonLabel="Save prize"
|
||||
input={{ action: "create" }}
|
||||
fields={[
|
||||
{
|
||||
name: "eventId",
|
||||
label: "Event ID",
|
||||
type: "identifier",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "position",
|
||||
label: "Position",
|
||||
type: "number",
|
||||
min: 1,
|
||||
defaultValue: 1,
|
||||
},
|
||||
{
|
||||
name: "prizeType",
|
||||
label: "Prize type",
|
||||
type: "select",
|
||||
options: [
|
||||
{ value: "badge", label: "Badge" },
|
||||
{ value: "credits", label: "Credits" },
|
||||
{ value: "pixels", label: "Pixels" },
|
||||
{ value: "points", label: "Points" },
|
||||
{ value: "item", label: "Item" },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "badgeCode",
|
||||
label: "Badge code",
|
||||
type: "text",
|
||||
maxLength: 50,
|
||||
},
|
||||
{
|
||||
name: "credits",
|
||||
label: "Credits",
|
||||
type: "number",
|
||||
min: 0,
|
||||
defaultValue: 0,
|
||||
},
|
||||
{
|
||||
name: "pixels",
|
||||
label: "Pixels",
|
||||
type: "number",
|
||||
min: 0,
|
||||
defaultValue: 0,
|
||||
},
|
||||
{
|
||||
name: "points",
|
||||
label: "Points",
|
||||
type: "number",
|
||||
min: 0,
|
||||
defaultValue: 0,
|
||||
},
|
||||
{ name: "itemId", label: "Item ID", type: "identifier" },
|
||||
{
|
||||
name: "description",
|
||||
label: "Description",
|
||||
type: "text",
|
||||
maxLength: 255,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ContentCommandForm
|
||||
commandId="content.engagement.event-winner.add"
|
||||
buttonLabel="Add winner"
|
||||
input={{}}
|
||||
fields={[
|
||||
{
|
||||
name: "eventId",
|
||||
label: "Event ID",
|
||||
type: "identifier",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "userId",
|
||||
label: "User ID",
|
||||
type: "identifier",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "position",
|
||||
label: "Position",
|
||||
type: "number",
|
||||
min: 1,
|
||||
defaultValue: 1,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
{canPolls && routeId?.includes("poll") ? (
|
||||
<>
|
||||
<ContentCommandForm
|
||||
commandId="content.engagement.poll.change"
|
||||
buttonLabel="Save poll"
|
||||
input={{ action: creatingPoll ? "create" : "update" }}
|
||||
fields={pollFields}
|
||||
/>
|
||||
<ContentCommandForm
|
||||
commandId="content.engagement.poll-question.change"
|
||||
buttonLabel="Save question"
|
||||
input={{ action: "create" }}
|
||||
fields={[
|
||||
{
|
||||
name: "pollId",
|
||||
label: "Poll ID",
|
||||
type: "identifier",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "question",
|
||||
label: "Question",
|
||||
type: "text",
|
||||
required: true,
|
||||
maxLength: 500,
|
||||
},
|
||||
{
|
||||
name: "type",
|
||||
label: "Question type",
|
||||
type: "select",
|
||||
options: [
|
||||
{ value: "single", label: "Single choice" },
|
||||
{ value: "multiple", label: "Multiple choice" },
|
||||
{ value: "text", label: "Free text" },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "sortOrder",
|
||||
label: "Sort order",
|
||||
type: "number",
|
||||
min: 0,
|
||||
defaultValue: 0,
|
||||
},
|
||||
{
|
||||
name: "options",
|
||||
label: "Options",
|
||||
type: "textarea",
|
||||
required: true,
|
||||
maxLength: 20_000,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
{canPrefixes && routeId === "content.engagement.prefixes" ? (
|
||||
<>
|
||||
<ContentCommandForm
|
||||
commandId="content.engagement.prefix.change"
|
||||
buttonLabel="Save prefix"
|
||||
input={{ action: "update" }}
|
||||
fields={[
|
||||
{
|
||||
name: "id",
|
||||
label: "Prefix ID",
|
||||
type: "identifier",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "text",
|
||||
label: "Text",
|
||||
type: "text",
|
||||
required: true,
|
||||
maxLength: 255,
|
||||
},
|
||||
{
|
||||
name: "color",
|
||||
label: "Color",
|
||||
type: "text",
|
||||
required: true,
|
||||
maxLength: 32,
|
||||
},
|
||||
{ name: "icon", label: "Icon", type: "text", maxLength: 255 },
|
||||
{
|
||||
name: "effect",
|
||||
label: "Effect",
|
||||
type: "text",
|
||||
maxLength: 255,
|
||||
},
|
||||
{ name: "active", label: "Active", type: "checkbox" },
|
||||
]}
|
||||
/>
|
||||
<ContentCommandForm
|
||||
commandId="content.engagement.prefix-blacklist.change"
|
||||
buttonLabel="Update blacklist"
|
||||
input={{ action: "add" }}
|
||||
fields={[
|
||||
{
|
||||
name: "word",
|
||||
label: "Word",
|
||||
type: "text",
|
||||
required: true,
|
||||
maxLength: 255,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ContentCommandForm
|
||||
commandId="content.engagement.prefix-settings.update"
|
||||
buttonLabel="Save prefix settings"
|
||||
input={{}}
|
||||
fields={[
|
||||
{
|
||||
name: "settings",
|
||||
label: "Prefix settings JSON",
|
||||
type: "json",
|
||||
required: true,
|
||||
maxLength: 20_000,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export async function renderContentEngagementPage(input: HousekeepingPageInput) {
|
||||
export async function renderContentEngagementPage(
|
||||
input: HousekeepingPageInput,
|
||||
) {
|
||||
const routeId = input.match.routeId as ContentPageProps["routeId"];
|
||||
const result = await contentQuery.run(input.context, { routeId: routeId ?? "content.engagement.events", params: input.match.params, list: parseContentListInput(input.searchParams ?? {}) });
|
||||
return <ContentEngagementPage context={input.context} result={result} routeId={routeId} />;
|
||||
const result = await contentQuery.run(input.context, {
|
||||
routeId: routeId ?? "content.engagement.events",
|
||||
params: input.match.params,
|
||||
list: parseContentListInput(input.searchParams ?? {}),
|
||||
});
|
||||
return (
|
||||
<ContentEngagementPage
|
||||
context={input.context}
|
||||
result={result}
|
||||
routeId={routeId}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user