55 lines
4.0 KiB
TypeScript
55 lines
4.0 KiB
TypeScript
import Link from "next/link";
|
|
import { and, desc, eq, gt, isNull } from "drizzle-orm";
|
|
import { redirect } from "next/navigation";
|
|
import { connection } from "next/server";
|
|
import { getDb } from "@/db";
|
|
import { commissionCheckouts, commissionTickets } from "@/db/schema";
|
|
import { CommissionLiveRefresh } from "@/components/commission/live-refresh";
|
|
import { SiteHeader } from "@/components/public/site-header";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Card, CardAction, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { getCustomerSession } from "@/lib/auth/server";
|
|
import { CHECKOUT_DURATION_MS, checkoutNow } from "@/lib/commission/checkout-expiration";
|
|
|
|
export const instant = false;
|
|
export default async function CommissionTicketsPage() {
|
|
await connection();
|
|
const session = await getCustomerSession();
|
|
if (!session) redirect("/auth/login?next=%2Fcommission%2Ftickets");
|
|
const [tickets, pending] = await Promise.all([
|
|
getDb().select({ ticket: commissionTickets, amount: commissionCheckouts.amountBaht })
|
|
.from(commissionTickets).innerJoin(commissionCheckouts, eq(commissionTickets.checkoutId, commissionCheckouts.id))
|
|
.where(eq(commissionTickets.userId, session.user.id)).orderBy(desc(commissionTickets.updatedAt)).limit(50),
|
|
getDb().select({ id: commissionCheckouts.id, amount: commissionCheckouts.amountBaht,
|
|
createdAt: commissionCheckouts.createdAt }).from(commissionCheckouts)
|
|
.leftJoin(commissionTickets, eq(commissionTickets.checkoutId, commissionCheckouts.id))
|
|
.where(and(eq(commissionCheckouts.userId, session.user.id), isNull(commissionTickets.id),
|
|
gt(commissionCheckouts.createdAt, new Date(checkoutNow() - CHECKOUT_DURATION_MS))))
|
|
.orderBy(desc(commissionCheckouts.createdAt)).limit(20),
|
|
]);
|
|
return <div className="min-h-svh"><SiteHeader /><CommissionLiveRefresh endpoint="/api/commission/events" userId={session.user.id} />
|
|
<main className="mx-auto flex max-w-4xl flex-col gap-5 p-4 py-10 sm:p-8">
|
|
<div className="flex items-center justify-between gap-3">
|
|
<h1 className="font-heading text-3xl font-semibold">คำขอของฉัน</h1>
|
|
</div>
|
|
<p className="text-sm text-muted-foreground">แต่ละคำขอมีแชตสำหรับรับผลและพูดคุยกับทีมงาน (Ticket) สถานะแชตบอกว่ายังส่งข้อความได้หรือไม่ ส่วนความคืบหน้าของงานสอบถามได้ในแชต</p>
|
|
<Link href="/commission" className="text-sm underline">สร้างคำขอเพิ่ม</Link>
|
|
{pending.length > 0 && <h2 className="text-xl font-semibold">รอชำระเงิน</h2>}
|
|
{pending.map((checkout) => <Link key={checkout.id} href={`/commission/pay/${checkout.id}`}>
|
|
<Card><CardHeader><CardTitle>รอชำระเงิน - ฿{checkout.amount}</CardTitle></CardHeader>
|
|
<CardContent>{checkout.createdAt.toLocaleString("th-TH")}</CardContent></Card>
|
|
</Link>)}
|
|
<h2 className="text-xl font-semibold">คำขอที่ชำระเงินแล้ว</h2>
|
|
{tickets.length === 0 && <p>ยังไม่มีคำขอที่ชำระเงินแล้ว</p>}
|
|
{tickets.map(({ ticket, amount }) => <Link key={ticket.id} href={`/commission/tickets/${ticket.id}`}>
|
|
<Card><CardHeader><CardTitle>{ticket.title || `Ticket #${ticket.id.slice(0, 8)}`}</CardTitle>
|
|
<CardAction><Badge variant={ticket.status === "open" ? "outline" : "destructive"}
|
|
className={ticket.status === "open" ? "border-success/30 bg-success/10 text-success" : undefined}>
|
|
{ticket.status === "open" ? "แชตเปิดอยู่" : "แชตปิดแล้ว"}
|
|
</Badge></CardAction></CardHeader>
|
|
<CardContent>฿{amount} - {ticket.updatedAt.toLocaleString("th-TH")}</CardContent></Card>
|
|
</Link>)}
|
|
</main>
|
|
</div>;
|
|
}
|