"use client" import { standardSchemaResolver } from "@hookform/resolvers/standard-schema" import { ArrowLeft } from "lucide-react" import Link from "next/link" import { useRouter, useSearchParams } from "next/navigation" import { useEffect } from "react" import { useForm, useWatch } from "react-hook-form" import { z } from "zod" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Textarea } from "@/components/ui/textarea" import { getGameById, listGames } from "@/lib/api" import { resolveOwnerShop } from "@/lib/domain/resolve-current-shop" import { GameIcon } from "@/lib/game-icons" import type { PlayerService } from "@/lib/types" import { useAuthStore } from "@/store/auth" import { usePlayerStore } from "@/store/players" import { useServiceStore } from "@/store/services" import { useShopStore } from "@/store/shops" const serviceSchema = z.object({ gameId: z.string().min(1, "请选择游戏"), title: z.string().min(2, "标题至少2个字符"), description: z.string().min(10, "描述至少10个字符"), price: z.string().min(1, "请输入价格"), unit: z.string().min(1, "请输入单位"), rankRange: z.string().optional(), availability: z.string().min(1, "请输入可用时间"), }) export default function NewServicePage() { const router = useRouter() const searchParams = useSearchParams() const serviceId = searchParams.get("serviceId") const userId = useAuthStore((state) => state.user?.id) const currentRole = useAuthStore((state) => state.currentRole) const shops = useShopStore((state) => state.shops) const players = usePlayerStore((state) => state.players) const services = useServiceStore((state) => state.services) const createService = useServiceStore((state) => state.createService) const updateService = useServiceStore((state) => state.updateService) const ownerShop = resolveOwnerShop(userId, shops) const scopedPlayerIds = currentRole === "player" ? userId ? [userId] : [] : currentRole === "owner" ? ownerShop ? players.filter((player) => player.shopId === ownerShop.id).map((player) => player.id) : [] : [] const scopedPlayerIdSet = new Set(scopedPlayerIds) const editingService = services.find( (service) => service.id === serviceId && scopedPlayerIdSet.has(service.playerId), ) const targetPlayerId = editingService?.playerId ?? scopedPlayerIds[0] const { register, handleSubmit, setValue, control, formState: { errors, isSubmitting }, } = useForm>({ resolver: standardSchemaResolver(serviceSchema), defaultValues: { gameId: "", title: "", description: "", price: "", unit: "", rankRange: "", availability: "", }, }) useEffect(() => { if (!editingService) return setValue("gameId", editingService.gameId) setValue("title", editingService.title) setValue("description", editingService.description) setValue("price", editingService.price.toString()) setValue("unit", editingService.unit) setValue("rankRange", editingService.rankRange ?? "") setValue("availability", editingService.availability.join("、")) }, [editingService, setValue]) const selectedGameId = useWatch({ control, name: "gameId" }) const selectedUnit = useWatch({ control, name: "unit" }) const games = listGames() if (serviceId && !editingService) { return
服务不存在或当前身份不可编辑
} if (!targetPlayerId) { return
当前身份下没有可管理的服务范围
} const onSubmit = async (data: z.infer) => { const game = getGameById(data.gameId) if (!game) return const payload: Omit = { playerId: targetPlayerId, gameId: game.id, gameName: game.name, title: data.title, description: data.description, price: Number(data.price), unit: data.unit as PlayerService["unit"], rankRange: data.rankRange?.trim() ? data.rankRange.trim() : undefined, availability: data.availability .split("、") .map((item) => item.trim()) .filter(Boolean), } if (editingService) { updateService(editingService.id, payload) } else { createService(payload) } router.push("/dashboard/services") } return (
返回服务列表 发布服务
{errors.title &&

{errors.title.message}

}