Files
2026-04-25 21:45:32 +08:00

271 lines
9.1 KiB
TypeScript

"use client"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { EmptyState } from "@/components/ui/empty-state"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Separator } from "@/components/ui/separator"
import { Textarea } from "@/components/ui/textarea"
import { addShopAnnouncement, deleteShopAnnouncement, updateShop } from "@/lib/api"
import { toApiError } from "@/lib/errors"
import { useMyShop } from "@/lib/hooks/use-my-shop"
import { notifyInfo, notifySuccess } from "@/lib/toast"
import type { Shop } from "@/lib/types"
import { AlertCircle, DollarSign, Edit, ExternalLink, ListOrdered, Star, Users } from "lucide-react"
import Link from "next/link"
import { useState } from "react"
export default function ShopManagementPage() {
const { shop, setShop, loading, error, refreshShop } = useMyShop()
if (loading) {
return (
<div className="container mx-auto max-w-6xl px-4 py-8">
<EmptyState title="店铺信息加载中" icon={ListOrdered} />
</div>
)
}
if (error) {
return (
<div className="container mx-auto max-w-6xl px-4 py-8">
<EmptyState title="店铺信息加载失败" description={error} icon={AlertCircle} />
</div>
)
}
if (!shop) {
return (
<div className="container mx-auto max-w-6xl px-4 py-8">
<EmptyState title="当前账号没有可管理的店铺" icon={ListOrdered} />
</div>
)
}
return (
<ShopManagementContent key={shop.id} shop={shop} setShop={setShop} refreshShop={refreshShop} />
)
}
function ShopManagementContent({
shop,
setShop,
refreshShop,
}: {
shop: Shop
setShop: (shop: Shop | null) => void
refreshShop: () => Promise<Shop | null>
}) {
const [name, setName] = useState(shop.name)
const [description, setDescription] = useState(shop.description)
const [saving, setSaving] = useState(false)
const handleSave = async () => {
setSaving(true)
try {
const nextShop = await updateShop(shop.id, {
name,
description,
commissionType: shop.commissionType,
commissionValue: shop.commissionValue,
allowMultiShop: shop.allowMultiShop,
allowIndependentOrders: shop.allowIndependentOrders,
dispatchMode: shop.dispatchMode,
})
setShop(nextShop)
notifySuccess("店铺信息已保存")
} catch (error) {
notifyInfo(toApiError(error).msg)
} finally {
setSaving(false)
}
}
const handleAddAnnouncement = async () => {
const next = window.prompt("", "")
if (next === null) return
const value = next.trim()
if (!value) return
setSaving(true)
try {
await addShopAnnouncement(shop.id, value)
await refreshShop()
notifySuccess("公告已添加")
} catch (error) {
notifyInfo(toApiError(error).msg)
} finally {
setSaving(false)
}
}
const handleDeleteAnnouncement = async (index: number) => {
setSaving(true)
try {
await deleteShopAnnouncement(shop.id, index)
await refreshShop()
notifySuccess("公告已删除")
} catch (error) {
notifyInfo(toApiError(error).msg)
} finally {
setSaving(false)
}
}
return (
<div className="container mx-auto max-w-6xl px-4 py-8 space-y-8">
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold"></h1>
<Button variant="outline" size="sm" asChild>
<Link href={`/shop/${shop.id}`}>
<ExternalLink className="mr-1 h-4 w-4" />
</Link>
</Button>
</div>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
<Card className="border-border/80 shadow-sm">
<CardHeader className="flex flex-row items-center justify-between pb-2">
<CardTitle className="text-sm font-medium"></CardTitle>
<ListOrdered className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{shop.totalOrders}</div>
</CardContent>
</Card>
<Card className="border-border/80 shadow-sm">
<CardHeader className="flex flex-row items-center justify-between pb-2">
<CardTitle className="text-sm font-medium"></CardTitle>
<Star className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{shop.rating}</div>
</CardContent>
</Card>
<Card className="border-border/80 shadow-sm">
<CardHeader className="flex flex-row items-center justify-between pb-2">
<CardTitle className="text-sm font-medium"></CardTitle>
<Users className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{shop.playerCount}</div>
</CardContent>
</Card>
<Card className="border-border/80 shadow-sm">
<CardHeader className="flex flex-row items-center justify-between pb-2">
<CardTitle className="text-sm font-medium"></CardTitle>
<DollarSign className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">
{shop.commissionType === "percentage"
? `${shop.commissionValue}%`
: `¥${shop.commissionValue}`}
</div>
</CardContent>
</Card>
</div>
<Card className="border-border/80 shadow-sm">
<CardHeader>
<CardTitle className="text-base"></CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="shop-name"></Label>
<Input id="shop-name" value={name} onChange={(event) => setName(event.target.value)} />
</div>
<div className="space-y-2">
<Label htmlFor="shop-desc"></Label>
<Textarea
id="shop-desc"
value={description}
onChange={(event) => setDescription(event.target.value)}
rows={3}
/>
</div>
<Button
onClick={() => {
void handleSave()
}}
disabled={saving}
>
<Edit className="mr-1 h-4 w-4" />
</Button>
</CardContent>
</Card>
<Card className="border-border/80 shadow-sm">
<CardHeader>
<CardTitle className="text-base"></CardTitle>
</CardHeader>
<CardContent className="space-y-3">
{shop.announcements.length > 0 ? (
shop.announcements.map((announcement, index) => (
<div
key={announcement}
className="flex items-center justify-between rounded-md border border-border/60 p-3"
>
<span className="text-sm">{announcement}</span>
<Button
variant="ghost"
size="sm"
onClick={() => {
void handleDeleteAnnouncement(index)
}}
disabled={saving}
>
</Button>
</div>
))
) : (
<EmptyState title="暂无公告" className="min-h-[180px] border-dashed" />
)}
<Separator />
<Button
variant="outline"
size="sm"
onClick={() => {
void handleAddAnnouncement()
}}
disabled={saving}
>
</Button>
</CardContent>
</Card>
<Card className="border-border/80 shadow-sm">
<CardHeader>
<CardTitle className="text-base"></CardTitle>
</CardHeader>
<CardContent className="space-y-3 text-sm">
<div className="flex items-center justify-between">
<span className="text-muted-foreground"></span>
<Badge variant="outline">
{shop.dispatchMode === "manual" ? "手动派单" : "自动派单"}
</Badge>
</div>
<div className="flex items-center justify-between">
<span className="text-muted-foreground"></span>
<Badge variant={shop.allowMultiShop ? "default" : "secondary"}>
{shop.allowMultiShop ? "是" : "否"}
</Badge>
</div>
<div className="flex items-center justify-between">
<span className="text-muted-foreground"></span>
<Badge variant={shop.allowIndependentOrders ? "default" : "secondary"}>
{shop.allowIndependentOrders ? "是" : "否"}
</Badge>
</div>
</CardContent>
</Card>
</div>
)
}