feat: make dashboard service management actionable

This commit is contained in:
zetaloop
2026-02-21 15:47:30 +08:00
parent 3a1f9c2b7f
commit 94b96ac577
3 changed files with 108 additions and 10 deletions
@@ -3,7 +3,8 @@
import { standardSchemaResolver } from "@hookform/resolvers/standard-schema"
import { ArrowLeft } from "lucide-react"
import Link from "next/link"
import { useRouter } from "next/navigation"
import { useRouter, useSearchParams } from "next/navigation"
import { useEffect } from "react"
import { useForm } from "react-hook-form"
import { z } from "zod"
import { Button } from "@/components/ui/button"
@@ -20,8 +21,12 @@ import {
import { Textarea } from "@/components/ui/textarea"
import { GameIcon } from "@/lib/game-icons"
import { mockGames } from "@/lib/mock"
import type { PlayerService } from "@/lib/types"
import { useAuthStore } from "@/store/auth"
import { useServiceStore } from "@/store/services"
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, "请输入价格"),
@@ -32,17 +37,71 @@ const serviceSchema = z.object({
export default function NewServicePage() {
const router = useRouter()
const searchParams = useSearchParams()
const serviceId = searchParams.get("serviceId")
const userId = useAuthStore((state) => state.user?.id)
const services = useServiceStore((state) => state.services)
const createService = useServiceStore((state) => state.createService)
const updateService = useServiceStore((state) => state.updateService)
const editingService = services.find((service) => service.id === serviceId)
const {
register,
handleSubmit,
setValue,
watch,
formState: { errors, isSubmitting },
} = useForm({
} = useForm<z.infer<typeof serviceSchema>>({
resolver: standardSchemaResolver(serviceSchema),
defaultValues: {
gameId: "",
title: "",
description: "",
price: "",
unit: "",
rankRange: "",
availability: "",
},
})
const onSubmit = async () => {
await new Promise((r) => setTimeout(r, 500))
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 = watch("gameId")
const selectedUnit = watch("unit")
const onSubmit = async (data: z.infer<typeof serviceSchema>) => {
const game = mockGames.find((item) => item.id === data.gameId)
if (!game) return
const payload: Omit<PlayerService, "id"> = {
playerId: editingService?.playerId ?? userId ?? "u5",
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")
}
@@ -64,7 +123,7 @@ export default function NewServicePage() {
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
<div className="space-y-2">
<Label></Label>
<Select>
<Select value={selectedGameId} onValueChange={(value) => setValue("gameId", value)}>
<SelectTrigger>
<SelectValue placeholder="选择游戏" />
</SelectTrigger>
@@ -108,7 +167,7 @@ export default function NewServicePage() {
</div>
<div className="space-y-2">
<Label htmlFor="unit"></Label>
<Select onValueChange={(value) => setValue("unit", value)}>
<Select value={selectedUnit} onValueChange={(value) => setValue("unit", value)}>
<SelectTrigger>
<SelectValue placeholder="选择单位" />
</SelectTrigger>
+9 -4
View File
@@ -1,3 +1,5 @@
"use client"
import { Edit, Plus, Trash2 } from "lucide-react"
import Link from "next/link"
import { Badge } from "@/components/ui/badge"
@@ -11,9 +13,12 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table"
import { mockServices } from "@/lib/mock"
import { useServiceStore } from "@/store/services"
export default function ServicesPage() {
const services = useServiceStore((state) => state.services)
const deleteService = useServiceStore((state) => state.deleteService)
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
@@ -43,7 +48,7 @@ export default function ServicesPage() {
</TableRow>
</TableHeader>
<TableBody>
{mockServices.map((service) => (
{services.map((service) => (
<TableRow key={service.id}>
<TableCell className="font-medium">{service.title}</TableCell>
<TableCell>
@@ -63,11 +68,11 @@ export default function ServicesPage() {
<TableCell className="text-right">
<div className="flex justify-end gap-1">
<Button variant="ghost" size="icon" asChild>
<Link href="/dashboard/services/new">
<Link href={`/dashboard/services/new?serviceId=${service.id}`}>
<Edit className="h-4 w-4" />
</Link>
</Button>
<Button variant="ghost" size="icon">
<Button variant="ghost" size="icon" onClick={() => deleteService(service.id)}>
<Trash2 className="h-4 w-4 text-destructive" />
</Button>
</div>
+34
View File
@@ -0,0 +1,34 @@
import { create } from "zustand"
import { mockServices } from "@/lib/mock"
import type { PlayerService } from "@/lib/types"
interface ServiceState {
services: PlayerService[]
createService: (service: Omit<PlayerService, "id">) => void
updateService: (serviceId: string, patch: Partial<Omit<PlayerService, "id">>) => void
deleteService: (serviceId: string) => void
}
export const useServiceStore = create<ServiceState>((set) => ({
services: mockServices,
createService: (service) =>
set((state) => ({
services: [
...state.services,
{
...service,
id: `s${Date.now()}`,
},
],
})),
updateService: (serviceId, patch) =>
set((state) => ({
services: state.services.map((service) =>
service.id === serviceId ? { ...service, ...patch } : service,
),
})),
deleteService: (serviceId) =>
set((state) => ({
services: state.services.filter((service) => service.id !== serviceId),
})),
}))