import { useMutation } from "@tanstack/react-query"; import { useSearch } from "@tanstack/react-router"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@workspace/ui/components/alert-dialog"; import { Badge } from "@workspace/ui/components/badge"; import { Button } from "@workspace/ui/components/button"; import { HoverCard, HoverCardContent, HoverCardTrigger, } from "@workspace/ui/components/hover-card"; import { Separator } from "@workspace/ui/components/separator"; import { Combobox } from "@workspace/ui/composed/combobox"; import { ProTable, type ProTableActions, } from "@workspace/ui/composed/pro-table/pro-table"; import { cn } from "@workspace/ui/lib/utils"; import { activateOrder, getOrderList, refundOrder, updateOrderStatus, } from "@workspace/ui/services/admin/order"; import { useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { Display } from "@/components/display"; import { useGlobalStore } from "@/stores/global"; import { useSubscribe } from "@/stores/subscribe"; import { formatDate } from "@/utils/common"; import { UserDetail } from "../user/user-detail"; const REFUNDED_ORDER_STATUS = 6; export default function Order() { const { t } = useTranslation("order"); const sp = useSearch({ strict: false }) as Record; const user = useGlobalStore((state) => state.user); const [confirmingOrderId, setConfirmingOrderId] = useState( null ); const initialFilters = { user_id: sp.user_id ? Number(sp.user_id) : undefined, search: sp.search || undefined, status: sp.status || undefined, subscribe_id: sp.subscribe_id || undefined, }; const statusOptions = [ { value: 1, label: t("status.1", "Pending"), className: "bg-orange-500", }, { value: 2, label: t("status.2", "Paid"), className: "bg-green-500" }, { value: 3, label: t("status.3", "Cancelled"), className: "bg-gray-500", }, { value: 4, label: t("status.4", "Closed"), className: "bg-red-500" }, { value: 5, label: t("status.5", "Completed"), className: "bg-green-500", }, { value: REFUNDED_ORDER_STATUS, label: t("status.6", "Refunded"), className: "bg-red-500", }, ]; const typeOptions = [ { value: 1, label: t("type.1", "New Purchase") }, { value: 2, label: t("type.2", "Renewal") }, { value: 3, label: t("type.3", "Reset Traffic") }, { value: 4, label: t("type.4", "Recharge") }, ]; const ref = useRef(null); const { subscribes, getSubscribeName } = useSubscribe(); const canRefundOrders = Boolean(user?.is_admin); const refundMutation = useMutation({ mutationFn: async (order: API.Order) => { await refundOrder({ id: order.id }); }, onSuccess: () => { toast.success(t("refundSuccess", "Refund completed.")); setConfirmingOrderId(null); ref.current?.refresh(); }, onError: (error) => { console.error("Refund order failed", error); setConfirmingOrderId(null); }, }); const isRefundedOrder = (order: API.Order) => order.status === REFUNDED_ORDER_STATUS || order.status_name === t("status.6", "Refunded") || order.status_name?.toLowerCase() === "refunded"; const canRefundOrder = (order: API.Order) => canRefundOrders && !isRefundedOrder(order); return ( action={ref} actions={{ render: (order) => { if (!canRefundOrder(order)) return []; const isPending = refundMutation.isPending && confirmingOrderId === order.id; return [ { setConfirmingOrderId(open ? order.id : null); }} open={confirmingOrderId === order.id} > {t("refundConfirmTitle", "Confirm refund")}

{t( "refundConfirmDescription", "This refund will immediately invalidate the user's subscription and deduct the related agent commission." )}

{t( "refundConfirmWarning", "This action cannot be repeated after the order is refunded." )}

{t("cancel", "Cancel")} { event.preventDefault(); if (isPending) return; await refundMutation.mutateAsync(order); }} > {isPending ? t("refundSubmitting", "Refunding...") : t("confirmRefund", "Confirm refund")}
, ]; }, }} columns={[ { accessorKey: "order_no", header: t("orderNumber", "Order Number"), }, { accessorKey: "type", header: t("type.0", "Type"), cell: ({ row }) => { const type = row.getValue("type") as number; return ( typeOptions.find((opt) => opt.value === type)?.label || t(`type.${type}`) ); }, }, { accessorKey: "subscribe_id", header: t("subscribe", "Subscribe"), cell: ({ row }) => { const order = row.original as API.Order; if (order.type === 4) { const type = row.getValue("type") as number; return ( typeOptions.find((opt) => opt.value === type)?.label || t(`type.${type}`) ); } const name = getSubscribeName(order.subscribe_id); const quantity = order.quantity; return name ? `${name} × ${quantity}` : ""; }, }, { accessorKey: "amount", header: t("amount", "Amount"), cell: ({ row }) => { const order = row.original as API.Order; return (
{order.trade_no && ( <>
{t("tradeNo", "Transaction Number")}
{order.trade_no} )}
  • {t("subscribePrice", "Subscription Price")}
  • {t("discount", "Discount Amount")}
  • {t("couponDiscount", "Coupon Discount")}
  • {t("feeAmount", "Fee Amount")}
  • {t("total", "Total")}
  • {t("method", "Payment Method")} {order.payment?.name || order.payment?.platform}
); }, }, { accessorKey: "payment", header: t("method", "Payment Method"), cell: ({ row }) => { const order = row.original as API.Order; return order.payment?.name || order.payment?.platform || "--"; }, }, { accessorKey: "user_id", header: t("user", "User"), cell: ({ row }) => { const order = row.original as API.Order; return ; }, }, { accessorKey: "updated_at", header: t("updateTime", "Update Time"), cell: ({ row }) => { const order = row.original as API.Order; return formatDate(order.updated_at); }, }, { accessorKey: "status", header: t("status.0", "Status"), cell: ({ row }) => { const order = row.original as API.Order; const option = statusOptions.find( (opt) => opt.value === order.status ); if ([1, 3, 4].includes(row.getValue("status"))) { return (
className={cn(option?.className)} onChange={async (value) => { await updateOrderStatus({ id: order.id, status: value, }); ref.current?.refresh(); }} options={statusOptions} placeholder={t("status.0", "Status")} value={order.status} /> {[1, 3].includes(order.status) && ( )}
); } return ( {option?.label || (order.status_name ? t(`status.${row.getValue("status")}`, order.status_name) : t(`status.${row.getValue("status")}`))} ); }, }, ]} initialFilters={initialFilters} params={[ { key: "status", placeholder: t("status.0", "Status"), options: statusOptions.map((item) => ({ label: item.label, value: String(item.value), })), }, { key: "subscribe_id", placeholder: `${t("subscribe", "Subscribe")}`, options: subscribes?.map((item) => ({ label: item.name!, value: String(item.id), })), }, { key: "search" }, { key: "user_id", placeholder: `${t("user", "User")} ID`, options: undefined, }, ]} request={async (pagination, filter) => { const { data } = await getOrderList({ ...pagination, ...filter }); return { list: data.data?.list || [], total: data.data?.total || 0, }; }} /> ); }