fix(dashboard): scope owner and service views by resolved shop

This commit is contained in:
zetaloop
2026-02-22 17:14:52 +08:00
parent 1f2dc1434b
commit 1dfcd3927d
11 changed files with 269 additions and 70 deletions
@@ -20,10 +20,13 @@ import {
} 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, "请选择游戏"),
@@ -40,10 +43,28 @@ export default function NewServicePage() {
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 editingService = services.find((service) => service.id === serviceId)
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,
@@ -78,12 +99,20 @@ export default function NewServicePage() {
const selectedUnit = useWatch({ control, name: "unit" })
const games = listGames()
if (serviceId && !editingService) {
return <div className="text-sm text-muted-foreground"></div>
}
if (!targetPlayerId) {
return <div className="text-sm text-muted-foreground"></div>
}
const onSubmit = async (data: z.infer<typeof serviceSchema>) => {
const game = getGameById(data.gameId)
if (!game) return
const payload: Omit<PlayerService, "id"> = {
playerId: editingService?.playerId ?? userId ?? "u5",
playerId: targetPlayerId,
gameId: game.id,
gameName: game.name,
title: data.title,
+36 -2
View File
@@ -13,11 +13,38 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table"
import { resolveOwnerShop } from "@/lib/domain/resolve-current-shop"
import { useAuthStore } from "@/store/auth"
import { usePlayerStore } from "@/store/players"
import { useServiceStore } from "@/store/services"
import { useShopStore } from "@/store/shops"
export default function ServicesPage() {
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 deleteService = useServiceStore((state) => state.deleteService)
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 scopedServices = services.filter((service) => scopedPlayerIdSet.has(service.playerId))
if (currentRole !== "player" && currentRole !== "owner") {
return <div className="text-sm text-muted-foreground"></div>
}
return (
<div className="space-y-6">
@@ -48,7 +75,7 @@ export default function ServicesPage() {
</TableRow>
</TableHeader>
<TableBody>
{services.map((service) => (
{scopedServices.map((service) => (
<TableRow key={service.id}>
<TableCell className="font-medium">{service.title}</TableCell>
<TableCell>
@@ -72,7 +99,14 @@ export default function ServicesPage() {
<Edit className="h-4 w-4" />
</Link>
</Button>
<Button variant="ghost" size="icon" onClick={() => deleteService(service.id)}>
<Button
variant="ghost"
size="icon"
onClick={() => {
if (!scopedPlayerIdSet.has(service.playerId)) return
deleteService(service.id)
}}
>
<Trash2 className="h-4 w-4 text-destructive" />
</Button>
</div>