import { redirect } from "next/navigation"; import { getServerSession } from "next-auth"; import { authOptions } from "@/lib/auth"; import { isAdminEmail, listAdminEmails } from "@/lib/admin"; import { refundRepo } from "@/server/repositories/refund.repo"; import { CreateRefundForm } from "@/features/refund/components/create-refund-form"; import { AdminFilterBar } from "@/features/admin/components/admin-filter-bar"; import { RefundReviewCard, type RefundCardData, } from "@/features/refund/components/refund-review-card"; type Tab = "PENDING" | "APPROVED" | "REJECTED" | "SUCCEEDED" | "FAILED"; const TABS: { key: Tab; label: string }[] = [ { key: "PENDING", label: "Pending" }, { key: "APPROVED", label: "Disetujui" }, { key: "SUCCEEDED", label: "Selesai" }, { key: "REJECTED", label: "Ditolak" }, { key: "FAILED", label: "Gagal" }, ]; const REASON_OPTIONS = [ { value: "USER_CANCELLATION", label: "User cancel" }, { value: "ORGANIZER_CANCELLED", label: "Organizer cancel" }, { value: "TRIP_ISSUE", label: "Trip issue" }, { value: "ADMIN_ADJUSTMENT", label: "Admin adjustment" }, { value: "DISPUTE_RESOLVED", label: "Dispute resolved" }, ] as const; type ReasonValue = (typeof REASON_OPTIONS)[number]["value"]; interface PageProps { searchParams: Promise<{ tab?: string; dateFrom?: string; dateTo?: string; reviewer?: string; reason?: string; }>; } function parseDate(value: string | undefined): Date | undefined { if (!value) return undefined; const d = new Date(value); return Number.isNaN(d.getTime()) ? undefined : d; } export default async function AdminRefundsPage({ searchParams }: PageProps) { const session = await getServerSession(authOptions); if (!session?.user) redirect("/login?callbackUrl=/admin/refunds"); if (!isAdminEmail(session.user.email)) { return (

Halaman ini hanya untuk admin SeTrip.

); } const params = await searchParams; const tab: Tab = TABS.some((t) => t.key === params.tab) ? (params.tab as Tab) : "PENDING"; const reason: ReasonValue | undefined = REASON_OPTIONS.some( (r) => r.value === params.reason ) ? (params.reason as ReasonValue) : undefined; const rows = await refundRepo.listByStatus(tab, { dateFrom: parseDate(params.dateFrom), dateTo: parseDate(params.dateTo), reviewerEmail: params.reviewer || undefined, reason, }); const items: RefundCardData[] = rows.map((r) => ({ id: r.id, amount: r.amount, currency: r.currency, reason: r.reason, reportedBy: r.reportedBy, reportNote: r.reportNote, initiatedBy: r.initiatedBy, status: r.status, adminNote: r.adminNote, createdAt: r.createdAt, reviewedAt: r.reviewedAt, succeededAt: r.succeededAt, failedAt: r.failedAt, reviewedBy: r.reviewedBy, booking: { id: r.booking.id, amount: r.booking.amount, status: r.booking.status, trip: { id: r.booking.trip.id, title: r.booking.trip.title, date: r.booking.trip.date, }, user: r.booking.user, payments: r.booking.payments.map((p) => ({ id: p.id, provider: p.provider, method: p.method, amount: p.amount, status: p.status, paidAt: p.paidAt, })), }, })); return (

Review Refund Manual

Tinjau laporan refund dari peserta dan organizer. Setiap refund harus melalui approval admin sebelum dieksekusi.

{TABS.map((t) => ( {t.label} ))}
{items.length === 0 ? (

Tidak ada refund yang cocok dengan filter ini.

) : (
{items.map((r) => ( ))}
)}
); }