Files
admin/src/app/(root)/coupon/page.tsx
2026-05-25 18:12:38 +08:00

159 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client"
import { Suspense, useState } from "react"
import { toast } from "sonner"
import { deleteCoupon, getPagCoupon } from "@/actions/coupon"
import { Auth } from "@/components/auth"
import { DataTable, useDataTable } from "@/components/data-table"
import { Page } from "@/components/page"
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"
import { ScopeCouponWriteAssign } from "@/lib/scopes"
import { type Coupon, getExpireType, getStatus } from "@/models/coupon"
import { formatDate } from "@/models/formatDate"
import { CreateDiscount } from "./create"
import { ReleaseCoupon } from "./release"
import { UpdateCoupon } from "./update"
export default function CouponPage() {
const table = useDataTable((page, size) => getPagCoupon({ page, size }))
return (
<Page>
<div className="flex justify-between items-stretch">
<div className="flex gap-3">
<CreateDiscount onSuccess={table.refresh} />
</div>
</div>
<Suspense>
<DataTable<Coupon>
{...table}
columns={[
{ header: "优惠券名称", accessorKey: "name" },
{ header: "优惠券数量", accessorKey: "count" },
{ header: "优惠券金额", accessorKey: "amount" },
{ header: "最低消费金额", accessorKey: "min_amount" },
{
header: "优惠券状态",
accessorKey: "status",
cell: ({ row }) => {
const { text, color } = getStatus(row.original.status, "coupon")
return <span className={color}>{text}</span>
},
},
{
header: "过期类型",
cell: ({ row }) => getExpireType(row.original.expire_type),
},
{
header: "过期时间",
accessorKey: "expire_at",
cell: ({ row }) => {
const coupon = row.original
if (coupon.expire_type === 2 && coupon.expire_in) {
return `${coupon.expire_in}`
}
if (coupon.expire_type === 1 && coupon.expire_at) {
return formatDate(row.original.expire_at)
}
return <span></span>
},
},
{
header: "创建时间",
accessorKey: "created_at",
cell: ({ row }) => formatDate(row.original.created_at),
},
{
id: "action",
meta: { pin: "right" },
header: "操作",
cell: ({ row }) => (
<div className="flex gap-2">
<UpdateCoupon
coupon={row.original}
onSuccess={table.refresh}
/>
<Auth scope={ScopeCouponWriteAssign}>
<ReleaseCoupon
coupon={row.original}
onSuccess={table.refresh}
/>
</Auth>
<DeleteCoupon
coupon={row.original}
onSuccess={table.refresh}
/>
</div>
),
},
]}
/>
</Suspense>
</Page>
)
}
function DeleteCoupon({
coupon,
onSuccess,
}: {
coupon: Coupon
onSuccess?: () => void
}) {
const [loading, setLoading] = useState(false)
const handleConfirm = async () => {
setLoading(true)
try {
const resp = await deleteCoupon(coupon.id)
if (resp.success) {
toast.success("删除成功")
onSuccess?.()
} else {
toast.error(resp.message ?? "删除失败")
}
} catch (error) {
const message = error instanceof Error ? error.message : error
toast.error(`接口请求错误: ${message}`)
} finally {
setLoading(false)
}
}
return (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button size="sm" variant="destructive" disabled={loading}>
</Button>
</AlertDialogTrigger>
<AlertDialogContent size="sm">
<AlertDialogHeader>
<AlertDialogTitle></AlertDialogTitle>
<AlertDialogDescription>
{coupon.name}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel></AlertDialogCancel>
<AlertDialogAction variant="destructive" onClick={handleConfirm}>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}