Files
juip-web/src/admin/coupon/page.tsx

171 lines
6.3 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.
import { CheckCircle, Clock, Gift, Tag, Ticket, XCircle } from "lucide-react"
import { useMemo } from "react"
import { fetchMyCoupons } from "@/api/coupon"
import Page from "@/components/page"
import { Badge } from "@/components/ui/badge"
import { Card, CardContent } from "@/components/ui/card"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { useList } from "@/lib/hooks/useList"
import type { MyCouponItem } from "@/lib/models/coupon"
import { formatDate } from "@/lib/utils"
const COUPON_TYPE_DISCOUNT = 1
function CouponCard({ item }: { item: MyCouponItem }) {
const isDiscount = item.Coupon.CouponType === COUPON_TYPE_DISCOUNT
const isUsed = item.IsUsed
return (
<Card
className={`relative overflow-hidden transition-shadow hover:shadow-md ${isUsed ? "opacity-60" : ""}`}
>
<CardContent className="p-4">
<div className="flex items-start gap-4">
<div className="flex min-w-20 flex-col items-center justify-center rounded-lg bg-primary/10 p-3">
{isDiscount ? (
<>
<span className="text-2xl font-bold text-primary">
{item.Coupon.CouponValue}
</span>
<span className="text-xs text-muted-foreground">折</span>
</>
) : (
<>
<span className="text-xs text-muted-foreground">¥</span>
<span className="text-2xl font-bold text-primary">
{item.Coupon.CouponValue}
</span>
</>
)}
</div>
<div className="flex-1 space-y-1">
<div className="flex items-center gap-2">
<h4 className="font-medium">{item.Coupon.Name}</h4>
{isUsed ? (
<Badge variant="secondary" className="text-xs">
<CheckCircle className="mr-1 h-3 w-3" />
已使用
</Badge>
) : item.IsExpired ? (
<Badge variant="secondary" className="text-xs">
<XCircle className="mr-1 h-3 w-3" />
已过期
</Badge>
) : (
<Badge variant="default" className="text-xs">
<Ticket className="mr-1 h-3 w-3" />
可使用
</Badge>
)}
</div>
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-muted-foreground">
<span className="flex items-center gap-1">
<Tag className="h-3 w-3" />
{item.Coupon.AllowMinAmount > 0
? `满${item.Coupon.AllowMinAmount}元可用`
: "无限制"}
</span>
<span className="flex items-center gap-1">
<Clock className="h-3 w-3" />
{formatDate(item.Orgin.StartTime)} -{" "}
{formatDate(item.Orgin.EndTime)}
</span>
<span className="flex items-center gap-1">
<Gift className="h-3 w-3" />
{item.Orgin.Remark}
</span>
</div>
</div>
</div>
{isUsed && (
<div className="absolute right-4 top-4">
<XCircle className="h-6 w-6 text-muted-foreground/50" />
</div>
)}
</CardContent>
</Card>
)
}
export default function CouponPage() {
const { data: coupons, status } = useList(fetchMyCoupons)
const availableCoupons = useMemo(
() => coupons.filter(item => !item.IsUsed && !item.IsExpired),
[coupons],
)
const usedCoupons = useMemo(
() => coupons.filter(item => item.IsUsed),
[coupons],
)
return (
<Page>
<div className="bg-white rounded-lg p-4">
<div className="space-y-6">
<div className="flex items-center justify-between">
<h3 className="text-lg font-semibold">我的优惠券</h3>
<Badge variant="secondary" className="text-sm">
共 {coupons.length} 张
</Badge>
</div>
<Tabs defaultValue="available" className="w-full">
<TabsList className="grid w-full max-w-md grid-cols-2">
<TabsTrigger value="available">
可使用 ({availableCoupons.length})
</TabsTrigger>
<TabsTrigger value="used">
已使用 ({usedCoupons.length})
</TabsTrigger>
</TabsList>
<TabsContent value="available" className="mt-4">
{status === "load" ? (
<div className="flex flex-col items-center justify-center py-12 text-muted-foreground">
<p>加载中...</p>
</div>
) : status === "fail" ? (
<div className="flex flex-col items-center justify-center py-12 text-muted-foreground">
<XCircle className="mb-4 h-12 w-12 opacity-50" />
<p>加载失败,请刷新重试</p>
</div>
) : availableCoupons.length === 0 ? (
<div className="flex flex-col items-center justify-center py-12 text-muted-foreground">
<Ticket className="mb-4 h-12 w-12 opacity-50" />
<p>暂无可用优惠券</p>
<p className="text-sm">快去参与活动领取吧</p>
</div>
) : (
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{availableCoupons.map(item => (
<CouponCard key={item.Orgin.Id} item={item} />
))}
</div>
)}
</TabsContent>
<TabsContent value="used" className="mt-4">
{usedCoupons.length === 0 ? (
<div className="flex flex-col items-center justify-center py-12 text-muted-foreground">
<CheckCircle className="mb-4 h-12 w-12 opacity-50" />
<p>暂无已使用的优惠券</p>
</div>
) : (
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{usedCoupons.map(item => (
<CouponCard key={item.Orgin.Id} item={item} />
))}
</div>
)}
</TabsContent>
</Tabs>
</div>
</div>
</Page>
)
}