Files
admin/src/app/(root)/coupon/page.tsx

189 lines
5.7 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 { format } from "date-fns"
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 } from "@/models/coupon"
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 status = row.original.status
if (status === 0) {
return <span className="text-yellow-600"></span>
}
if (status === 1) {
return <span className="text-green-600"></span>
}
return <span>-</span>
},
},
{
header: "过期类型",
accessorFn: row => {
switch (row.expire_type) {
case 0:
return "不过期"
case 1:
return "固定日期"
case 2:
return "相对日期"
default:
return ""
}
},
},
{
header: "过期时长(天)",
accessorKey: "expire_in",
},
{
header: "过期时间",
accessorKey: "expire_at",
cell: ({ row }) => {
const coupon = row.original
if (coupon.expire_type === 2 && coupon.expire_in) {
const expireDate = new Date(coupon.created_at)
expireDate.setDate(expireDate.getDate() + coupon.expire_in)
return format(expireDate, "yyyy-MM-dd HH:mm:ss")
}
if (coupon.expire_type === 1 && coupon.expire_at) {
return format(
new Date(coupon.expire_at),
"yyyy-MM-dd HH:mm:ss",
)
}
return <span></span>
},
},
{
header: "创建时间",
accessorKey: "created_at",
cell: ({ row }) =>
format(
new Date(row.original.created_at),
"yyyy-MM-dd HH:mm:ss",
),
},
{
id: "action",
meta: { pin: "right" },
header: "操作",
cell: ({ row }) => (
<div className="flex gap-2">
<UpdateCoupon
coupon={row.original}
onSuccess={table.refresh}
/>
<DeleteCoupon
coupon={row.original}
onSuccess={table.refresh}
/>
<Auth scope={ScopeCouponWriteAssign}>
<ReleaseCoupon
coupon={row.original}
onSuccess={table.refresh}
/>
</Auth>
</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>
)
}