feat : big update
CI / Verify (push) Successful in 3m14s
CI / Build immutable images and deploy (push) Successful in 4m0s

This commit is contained in:
2026-10-01 18:37:15 +07:00 Unverified
parent c5037154cb
commit 0022625edb
76 changed files with 18671 additions and 29 deletions
+17
View File
@@ -0,0 +1,17 @@
import Link from "next/link";
import { notFound } from "next/navigation";
import { connection } from "next/server";
import { AdminHeader } from "@/components/admin/admin-header";
import { CommissionTicketDetail } from "@/components/commission/ticket-detail";
import { getAdminSession } from "@/lib/auth/server";
export const instant = false;
export default async function AdminCommissionTicketPage({ params }: PageProps<"/admin/commission/[id]">) {
await connection();
if (!(await getAdminSession())) notFound();
const { id } = await params;
return <div className="min-h-svh"><AdminHeader /><main className="mx-auto flex max-w-6xl flex-col gap-6 p-4 py-10 sm:p-8">
<Link href="/admin/commission" className="text-sm underline">กลับไปหน้า Ticket ทั้งหมด</Link>
<CommissionTicketDetail id={id} admin />
</main></div>;
}
+44
View File
@@ -0,0 +1,44 @@
import Link from "next/link";
import { desc, eq } from "drizzle-orm";
import { redirect } from "next/navigation";
import { connection } from "next/server";
import { getDb } from "@/db";
import { commissionCheckouts, commissionTickets, users } from "@/db/schema";
import { AdminHeader } from "@/components/admin/admin-header";
import { CommissionLiveRefresh } from "@/components/commission/live-refresh";
import { Badge } from "@/components/ui/badge";
import { Card, CardAction, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { getAdminSession } from "@/lib/auth/server";
export const instant = false;
export default async function AdminCommissionPage({ searchParams }: PageProps<"/admin/commission">) {
await connection();
if (!(await getAdminSession())) redirect("/admin/login");
const query = await searchParams;
const rawPage = Number(query.page ?? 1);
const page = Number.isInteger(rawPage) && rawPage > 0 ? Math.min(rawPage, 10000) : 1;
const tickets = await getDb().select({ ticket: commissionTickets, amount: commissionCheckouts.amountBaht,
customer: users.name }).from(commissionTickets)
.innerJoin(commissionCheckouts, eq(commissionTickets.checkoutId, commissionCheckouts.id))
.innerJoin(users, eq(commissionTickets.userId, users.id))
.orderBy(desc(commissionTickets.status), desc(commissionTickets.updatedAt), desc(commissionTickets.id))
.limit(51).offset((page - 1) * 50);
return <div className="min-h-svh"><AdminHeader /><CommissionLiveRefresh endpoint="/api/commission/events?scope=admin" />
<main className="mx-auto flex max-w-5xl flex-col gap-5 p-4 py-10 sm:p-8">
<h1 className="font-heading text-3xl font-semibold">Commission tickets</h1>
{tickets.length === 0 && <p>ยังไม่มี Ticket</p>}
{tickets.slice(0, 50).map(({ ticket, amount, customer }) => <Link key={ticket.id} href={`/admin/commission/${ticket.id}`}>
<Card><CardHeader><CardTitle>{ticket.title || `#${ticket.id.slice(0, 8)}`} - {customer}</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>)}
<nav className="flex gap-4 text-sm" aria-label="Ticket pages">
{page > 1 && <Link href={`/admin/commission?page=${page - 1}`} className="underline">ก่อนหน้า</Link>}
{tickets.length > 50 && <Link href={`/admin/commission?page=${page + 1}`} className="underline">ถัดไป</Link>}
</nav>
</main>
</div>;
}