feat(helpdesk): add helpdesk pages
Add the helpdesk list page and ticket detail page routes. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
40f23e8774
commit
14d92bf277
2 changed files with 130 additions and 0 deletions
83
src/app/(frontend)/helpdesk/[id]/page.tsx
Normal file
83
src/app/(frontend)/helpdesk/[id]/page.tsx
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import config from "@payload-config";
|
||||
import { headers as nextHeaders } from "next/headers";
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { getPayload } from "payload";
|
||||
import type { Ticket } from "@/payload-types";
|
||||
import { TicketDetail } from "@/components/frontend/helpdesk/TicketDetail";
|
||||
import hasRoles from "@/utils/access-control/hasRoles";
|
||||
import { ArrowLeftIcon, LifeBuoyIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
export const metadata = {
|
||||
title: "Ticket — Polaris Task Force",
|
||||
};
|
||||
|
||||
interface TicketPageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function TicketPage({ params }: TicketPageProps) {
|
||||
const { id } = await params;
|
||||
const payload = await getPayload({ config });
|
||||
const { user } = await payload.auth({
|
||||
headers: await nextHeaders(),
|
||||
canSetHeaders: false,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
redirect("/login");
|
||||
}
|
||||
|
||||
const ticket = await payload
|
||||
.findByID({
|
||||
collection: "tickets",
|
||||
id: Number(id),
|
||||
depth: 2,
|
||||
})
|
||||
.catch(() => null);
|
||||
|
||||
if (!ticket) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const typedTicket = ticket as unknown as Ticket;
|
||||
const isStaff = hasRoles(["admin", "developer"], user);
|
||||
const assigneeOptions = isStaff
|
||||
? (
|
||||
await payload.find({
|
||||
collection: "users",
|
||||
sort: "username",
|
||||
limit: 100,
|
||||
depth: 0,
|
||||
})
|
||||
).docs.map((staffUser) => ({
|
||||
id: staffUser.id,
|
||||
label: staffUser.payloadDisplayName || staffUser.username,
|
||||
}))
|
||||
: [];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6 p-5">
|
||||
<div className="flex flex-col gap-3">
|
||||
<Link
|
||||
href="/helpdesk"
|
||||
className="flex w-fit items-center gap-1 text-sm text-muted-foreground transition-colors hover:text-primary"
|
||||
>
|
||||
<ArrowLeftIcon className="size-3" />
|
||||
Helpdesk
|
||||
</Link>
|
||||
<div className="flex items-center gap-2">
|
||||
<LifeBuoyIcon className="size-5 text-muted-foreground" />
|
||||
<h1 className="text-lg font-semibold">Ticket #{typedTicket.id}</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TicketDetail
|
||||
ticket={typedTicket}
|
||||
currentUserId={user.id}
|
||||
isStaff={isStaff}
|
||||
assigneeOptions={assigneeOptions}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
47
src/app/(frontend)/helpdesk/page.tsx
Normal file
47
src/app/(frontend)/helpdesk/page.tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import config from "@payload-config";
|
||||
import { headers as nextHeaders } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
import { getPayload } from "payload";
|
||||
import type { Ticket } from "@/payload-types";
|
||||
import { TicketsView } from "@/components/frontend/helpdesk/TicketsView";
|
||||
import hasRoles from "@/utils/access-control/hasRoles";
|
||||
import { LifeBuoyIcon } from "lucide-react";
|
||||
|
||||
export const metadata = {
|
||||
title: "Helpdesk — Polaris Task Force",
|
||||
};
|
||||
|
||||
export default async function HelpdeskPage() {
|
||||
const payload = await getPayload({ config });
|
||||
const { user } = await payload.auth({
|
||||
headers: await nextHeaders(),
|
||||
canSetHeaders: false,
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
redirect("/login");
|
||||
}
|
||||
|
||||
const ticketsRes = await payload.find({
|
||||
collection: "tickets",
|
||||
sort: "-updatedAt",
|
||||
limit: 200,
|
||||
depth: 2,
|
||||
});
|
||||
const tickets = ticketsRes.docs as unknown as Ticket[];
|
||||
const isStaff = hasRoles(["admin", "developer"], user);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6 p-5">
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<LifeBuoyIcon className="size-5 text-muted-foreground" />
|
||||
<h1 className="text-lg font-semibold">Helpdesk</h1>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">Submit and track support requests.</p>
|
||||
</div>
|
||||
|
||||
<TicketsView tickets={tickets} currentUserId={user.id} isStaff={isStaff} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in a new issue