Files
EpicNext-Cms/src/features/housekeeping/domains/content/pages/engagement.tsx
T

408 lines
10 KiB
TypeScript

import { PERMS } from "@/lib/permission-slugs";
import type { HousekeepingPageInput } from "../../../route-handlers";
import { contentQuery } from "../queries/content-queries";
import {
type ContentCommandField,
ContentCommandForm,
} from "./content-command-form";
import {
ContentPageFrame,
type ContentPageProps,
parseContentListInput,
} from "./content-page-frame";
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);
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,
) {
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}
/>
);
}