"use client" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { EmptyState } from "@/components/ui/empty-state" import { Input } from "@/components/ui/input" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import { inviteShopPlayer, listPlayers, listPlayersByShop, listShopInvitations, removeShopPlayer, } from "@/lib/api" import { toApiError } from "@/lib/errors" import { useMyShop } from "@/lib/hooks/use-my-shop" import { notifyInfo, notifySuccess } from "@/lib/toast" import type { Player } from "@/lib/types" import { AlertCircle, MoreHorizontal, Star, UserPlus } from "lucide-react" import Link from "next/link" import { useCallback, useEffect, useMemo, useState } from "react" const statusLabels: Record = { available: "在线", busy: "忙碌", offline: "离线", } const statusVariants: Record = { available: "default", busy: "secondary", offline: "outline", } export default function EmployeesPage() { const [search, setSearch] = useState("") const { shop, loading, error, refreshShop } = useMyShop() const [players, setPlayers] = useState([]) const [shopPlayers, setShopPlayers] = useState([]) const [pendingPlayerIds, setPendingPlayerIds] = useState>(new Set()) const [dataLoading, setDataLoading] = useState(true) const [saving, setSaving] = useState(false) const loadData = useCallback(async () => { if (!shop) return setDataLoading(true) try { const [nextPlayers, nextShopPlayers, invitations] = await Promise.all([ listPlayers(), listPlayersByShop(shop.id), listShopInvitations(shop.id), ]) setPlayers(nextPlayers) setShopPlayers(nextShopPlayers) setPendingPlayerIds( new Set( invitations .filter((invitation) => invitation.status === "pending") .map((invitation) => String(invitation.playerId)), ), ) } catch (error) { notifyInfo(toApiError(error).msg) } finally { setDataLoading(false) } }, [shop]) useEffect(() => { if (!shop) return let cancelled = false Promise.all([listPlayers(), listPlayersByShop(shop.id), listShopInvitations(shop.id)]) .then(([nextPlayers, nextShopPlayers, invitations]) => { if (cancelled) return setPlayers(nextPlayers) setShopPlayers(nextShopPlayers) setPendingPlayerIds( new Set( invitations .filter((invitation) => invitation.status === "pending") .map((invitation) => String(invitation.playerId)), ), ) }) .catch((error) => { if (cancelled) return notifyInfo(toApiError(error).msg) }) .finally(() => { if (cancelled) return setDataLoading(false) }) return () => { cancelled = true } }, [shop]) const filteredPlayers = useMemo(() => { if (!shop) return [] return shopPlayers.filter( (player) => player.shopId === shop.id && player.user.nickname.toLowerCase().includes(search.trim().toLowerCase()), ) }, [search, shop, shopPlayers]) const inviteCandidate = useMemo( () => shop ? players.find((player) => player.shopId !== shop.id && !pendingPlayerIds.has(player.id)) : undefined, [pendingPlayerIds, players, shop], ) if (loading) { return (
) } if (error) { return (
) } if (!shop) { return (
) } const handleInvite = async () => { if (!inviteCandidate) return setSaving(true) try { await inviteShopPlayer(shop.id, inviteCandidate.id) await loadData() notifySuccess("邀请已发送") } catch (error) { notifyInfo(toApiError(error).msg) } finally { setSaving(false) } } const handleRemove = async (player: Player) => { setSaving(true) try { await removeShopPlayer(shop.id, player.id) await Promise.all([loadData(), refreshShop()]) notifySuccess("打手已移除") } catch (error) { notifyInfo(toApiError(error).msg) } finally { setSaving(false) } } return (

员工管理

签约打手 ({filteredPlayers.length}) setSearch(event.target.value)} />
打手 擅长游戏 评分 完成率 状态 操作 {dataLoading ? ( ) : filteredPlayers.length === 0 ? ( ) : ( filteredPlayers.map((player) => (
{player.user.nickname[0]}

{player.user.nickname}

{player.totalOrders} 单

{player.games.map((game) => ( {game} ))}
{player.rating}
{(player.completionRate * 100).toFixed(0)}% {statusLabels[player.status]} 查看详情 调整抽成 { void handleRemove(player) }} disabled={saving} > 移除打手
)) )}
) }