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

140 lines
3.7 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 PeopleSupportQueryData,
peopleSupportQuery,
} from "../queries/support";
import { PeoplePageFrame } from "./page-state";
import { PeopleCommandForm } from "./people-command-form";
interface Props {
readonly context: HousekeepingCapabilityContext;
readonly result?: HousekeepingResult<PeopleSupportQueryData>;
}
export function PeopleTicketDetailPage({ context, result }: Props) {
const canEdit = context.hasAny(PERMS.TICKETS_EDIT, PERMS.MOD_TICKETS_EDIT);
return (
<PeoplePageFrame
title="Ticket"
description="Review the conversation and coordinate the support response."
result={result}
isEmpty={(data) => data.kind !== "ticket"}
>
{(data) =>
data.kind === "ticket" ? (
<article className="space-y-4">
<header>
<h2>{data.ticket.subject}</h2>
<p>
{data.ticket.status} · {data.ticket.priority}
</p>
</header>
<ul className="space-y-2">
{data.ticket.messages.map((message) => (
<li
key={message.id}
className="rounded border border-[var(--admin-border)] p-3"
>
<strong>
{message.username ?? `User #${message.userId}`}
</strong>
<p>{message.message}</p>
</li>
))}
</ul>
{canEdit ? (
<div className="grid gap-3 lg:grid-cols-2">
<PeopleCommandForm
commandId="people.ticket.reply"
buttonLabel="Reply"
input={{ ticketId: data.ticket.id }}
fields={[
{
name: "message",
label: "Message",
type: "text",
required: true,
maxLength: 5000,
},
]}
/>
<PeopleCommandForm
commandId="people.ticket.assign"
buttonLabel="Assign"
input={{ ticketId: data.ticket.id }}
fields={[
{
name: "assigneeId",
label: "Assignee",
type: "select",
options: data.ticket.staff.map((staff) => ({
value: staff.id,
label: staff.username,
})),
},
]}
/>
<PeopleCommandForm
commandId="people.ticket.status"
buttonLabel="Change status"
input={{ ticketId: data.ticket.id }}
fields={[
{
name: "status",
label: "Status",
type: "select",
options: ["open", "in_progress", "waiting", "closed"].map(
(value) => ({ value, label: value }),
),
},
]}
/>
<PeopleCommandForm
commandId="people.ticket.priority"
buttonLabel="Change priority"
input={{ ticketId: data.ticket.id }}
fields={[
{
name: "priority",
label: "Priority",
type: "select",
options: ["low", "normal", "high", "urgent"].map(
(value) => ({ value, label: value }),
),
},
]}
/>
</div>
) : null}
</article>
) : null
}
</PeoplePageFrame>
);
}
export async function renderPeopleTicketDetailPage(
input: HousekeepingPageInput,
) {
const id = Number(input.match.params.id);
const result =
Number.isSafeInteger(id) && id > 0
? await peopleSupportQuery.run(input.context, {
routeId: "people.support.ticket-detail",
id,
})
: fail(
"VALIDATION",
"errors.housekeeping.validation",
createCorrelationId(),
);
return <PeopleTicketDetailPage context={input.context} result={result} />;
}