mirror of
https://github.com/JetSprow/J-Board-Lite.git
synced 2026-05-01 01:14:10 +05:30
feat: add admin wallet recharge order actions
This commit is contained in:
@@ -8,9 +8,10 @@ import {
|
||||
DataTableHeaderRow,
|
||||
DataTableRow,
|
||||
} from "@/components/shared/data-table";
|
||||
import { OrderStatusBadge } from "@/components/shared/domain-badges";
|
||||
import { StatusBadge, type StatusTone } from "@/components/shared/status-badge";
|
||||
import { getPaymentProviderName } from "@/services/payment/catalog";
|
||||
import { formatDateShort } from "@/lib/utils";
|
||||
import { RechargeOrderActions } from "../recharge-order-actions";
|
||||
import type { AdminRechargeOrderRow } from "../orders-data";
|
||||
|
||||
interface RechargeOrdersTableProps {
|
||||
@@ -22,9 +23,28 @@ function formatAmount(amount: { toString(): string }) {
|
||||
}
|
||||
|
||||
function getPaymentLabel(provider: string | null) {
|
||||
if (provider === "manual") return "手动确认";
|
||||
return provider ? getPaymentProviderName(provider) : "未选择支付";
|
||||
}
|
||||
|
||||
const rechargeStatusLabels: Record<AdminRechargeOrderRow["status"], string> = {
|
||||
PENDING: "待支付",
|
||||
PAID: "已入账",
|
||||
CANCELLED: "已取消",
|
||||
REFUNDED: "已退款",
|
||||
};
|
||||
|
||||
function getRechargeStatusTone(status: AdminRechargeOrderRow["status"]): StatusTone {
|
||||
if (status === "PAID") return "success";
|
||||
if (status === "PENDING") return "warning";
|
||||
if (status === "CANCELLED") return "neutral";
|
||||
return "danger";
|
||||
}
|
||||
|
||||
function RechargeStatusBadge({ status }: { status: AdminRechargeOrderRow["status"] }) {
|
||||
return <StatusBadge tone={getRechargeStatusTone(status)}>{rechargeStatusLabels[status]}</StatusBadge>;
|
||||
}
|
||||
|
||||
export function RechargeOrdersTable({ rechargeOrders }: RechargeOrdersTableProps) {
|
||||
return (
|
||||
<DataTableShell
|
||||
@@ -38,7 +58,7 @@ export function RechargeOrdersTable({ rechargeOrders }: RechargeOrdersTableProps
|
||||
<p className="break-all text-sm font-semibold">{order.user.email}</p>
|
||||
<p className="mt-1 text-xs text-muted-foreground">{order.user.name || "未设置昵称"}</p>
|
||||
</div>
|
||||
<OrderStatusBadge status={order.status} />
|
||||
<RechargeStatusBadge status={order.status} />
|
||||
</div>
|
||||
<div className="rounded-lg bg-muted/25 p-3">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
@@ -50,10 +70,11 @@ export function RechargeOrdersTable({ rechargeOrders }: RechargeOrdersTableProps
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-muted-foreground">{formatDateShort(order.createdAt)}</p>
|
||||
</div>
|
||||
<RechargeOrderActions orderId={order.id} status={order.status} />
|
||||
</article>
|
||||
))}
|
||||
>
|
||||
<DataTable aria-label="充值订单列表" className="min-w-[980px]">
|
||||
<DataTable aria-label="充值订单列表" className="min-w-[1120px]">
|
||||
<DataTableHead>
|
||||
<DataTableHeaderRow>
|
||||
<DataTableHeadCell>用户</DataTableHeadCell>
|
||||
@@ -62,6 +83,7 @@ export function RechargeOrdersTable({ rechargeOrders }: RechargeOrdersTableProps
|
||||
<DataTableHeadCell>状态</DataTableHeadCell>
|
||||
<DataTableHeadCell>备注</DataTableHeadCell>
|
||||
<DataTableHeadCell>时间</DataTableHeadCell>
|
||||
<DataTableHeadCell className="text-right">操作</DataTableHeadCell>
|
||||
</DataTableHeaderRow>
|
||||
</DataTableHead>
|
||||
<DataTableBody>
|
||||
@@ -81,7 +103,7 @@ export function RechargeOrdersTable({ rechargeOrders }: RechargeOrdersTableProps
|
||||
</div>
|
||||
</DataTableCell>
|
||||
<DataTableCell>
|
||||
<OrderStatusBadge status={order.status} />
|
||||
<RechargeStatusBadge status={order.status} />
|
||||
</DataTableCell>
|
||||
<DataTableCell className="max-w-64 whitespace-normal break-words text-xs text-muted-foreground">
|
||||
{order.note || order.paymentRef || "—"}
|
||||
@@ -89,6 +111,9 @@ export function RechargeOrdersTable({ rechargeOrders }: RechargeOrdersTableProps
|
||||
<DataTableCell className="whitespace-nowrap text-muted-foreground">
|
||||
{formatDateShort(order.createdAt)}
|
||||
</DataTableCell>
|
||||
<DataTableCell>
|
||||
<RechargeOrderActions orderId={order.id} status={order.status} />
|
||||
</DataTableCell>
|
||||
</DataTableRow>
|
||||
))}
|
||||
</DataTableBody>
|
||||
|
||||
84
src/app/(admin)/admin/orders/recharge-order-actions.tsx
Normal file
84
src/app/(admin)/admin/orders/recharge-order-actions.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { CheckCircle2, Trash2, XCircle } from "lucide-react";
|
||||
import {
|
||||
cancelAdminWalletRecharge,
|
||||
confirmAdminWalletRecharge,
|
||||
deleteAdminWalletRecharge,
|
||||
} from "@/actions/admin/recharge-orders";
|
||||
import { ConfirmActionButton } from "@/components/shared/confirm-action-button";
|
||||
|
||||
type RechargeOrderStatus = "PENDING" | "PAID" | "CANCELLED" | "REFUNDED";
|
||||
|
||||
export function RechargeOrderActions({
|
||||
orderId,
|
||||
status,
|
||||
}: {
|
||||
orderId: string;
|
||||
status: RechargeOrderStatus;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const isPending = status === "PENDING";
|
||||
const isPaid = status === "PAID";
|
||||
|
||||
return (
|
||||
<div className="flex flex-wrap justify-start gap-2 lg:justify-end">
|
||||
{isPending && (
|
||||
<>
|
||||
<ConfirmActionButton
|
||||
title="确认这笔充值?"
|
||||
description="会立即把充值金额入账到用户钱包,并将订单标记为已入账。"
|
||||
confirmLabel="确认入账"
|
||||
successMessage="充值订单已确认入账"
|
||||
errorMessage="确认充值订单失败"
|
||||
size="sm"
|
||||
onConfirm={async () => {
|
||||
await confirmAdminWalletRecharge(orderId);
|
||||
}}
|
||||
onSuccess={() => router.refresh()}
|
||||
>
|
||||
<CheckCircle2 className="size-3.5" />
|
||||
确认
|
||||
</ConfirmActionButton>
|
||||
<ConfirmActionButton
|
||||
title="取消这笔充值?"
|
||||
description="取消后用户不能继续支付这笔充值订单,钱包余额不会变化。"
|
||||
confirmLabel="取消充值"
|
||||
successMessage="充值订单已取消"
|
||||
errorMessage="取消充值订单失败"
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
onConfirm={async () => {
|
||||
await cancelAdminWalletRecharge(orderId);
|
||||
}}
|
||||
onSuccess={() => router.refresh()}
|
||||
>
|
||||
<XCircle className="size-3.5" />
|
||||
取消
|
||||
</ConfirmActionButton>
|
||||
</>
|
||||
)}
|
||||
<ConfirmActionButton
|
||||
title="删除这条充值记录?"
|
||||
description={
|
||||
isPaid
|
||||
? "只删除充值订单记录,不回滚已入账余额,钱包流水会保留。"
|
||||
: "删除后这条充值记录不可恢复,不会改变用户钱包余额。"
|
||||
}
|
||||
confirmLabel="删除记录"
|
||||
successMessage="充值记录已删除"
|
||||
errorMessage="删除充值记录失败"
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
onConfirm={async () => {
|
||||
await deleteAdminWalletRecharge(orderId);
|
||||
}}
|
||||
onSuccess={() => router.refresh()}
|
||||
>
|
||||
<Trash2 className="size-3.5" />
|
||||
删除
|
||||
</ConfirmActionButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user