feat: favorites — type, mock data, and buttons on player/shop detail pages

This commit is contained in:
zetaloop
2026-02-20 18:35:14 +08:00
parent 1938f9041d
commit 91d1694bcd
5 changed files with 63 additions and 2 deletions
+6 -1
View File
@@ -1,6 +1,7 @@
import { CheckCircle, Clock, MapPin, MessageSquare, ShoppingBag, Star } from "lucide-react" import { CheckCircle, Clock, MapPin, MessageSquare, ShoppingBag, Star } from "lucide-react"
import Link from "next/link" import Link from "next/link"
import { notFound } from "next/navigation" import { notFound } from "next/navigation"
import { FavoriteButton } from "@/components/favorite-button"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge" import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
@@ -14,7 +15,7 @@ import {
} from "@/components/ui/card" } from "@/components/ui/card"
import { Separator } from "@/components/ui/separator" import { Separator } from "@/components/ui/separator"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { mockPlayers, mockReviews, mockServices } from "@/lib/mock-data" import { mockFavorites, mockPlayers, mockReviews, mockServices } from "@/lib/mock-data"
export default async function PlayerDetailPage({ params }: { params: Promise<{ id: string }> }) { export default async function PlayerDetailPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params const { id } = await params
@@ -29,6 +30,9 @@ export default async function PlayerDetailPage({ params }: { params: Promise<{ i
player.services && player.services.length > 0 player.services && player.services.length > 0
? player.services ? player.services
: mockServices.filter((s) => s.playerId === player.id) : mockServices.filter((s) => s.playerId === player.id)
const isFavorited = mockFavorites.some(
(f) => f.userId === "u1" && f.targetType === "player" && f.targetId === player.id,
)
return ( return (
<div className="container mx-auto py-8 px-4 max-w-5xl"> <div className="container mx-auto py-8 px-4 max-w-5xl">
@@ -72,6 +76,7 @@ export default async function PlayerDetailPage({ params }: { params: Promise<{ i
</Button> </Button>
</Link> </Link>
)} )}
<FavoriteButton initialFavorited={isFavorited} />
</div> </div>
<div className="bg-muted/50 p-4 rounded-lg"> <div className="bg-muted/50 p-4 rounded-lg">
+6 -1
View File
@@ -2,11 +2,12 @@ import { Gamepad2, Megaphone, ShoppingBag, Star, Users } from "lucide-react"
import Image from "next/image" import Image from "next/image"
import Link from "next/link" import Link from "next/link"
import { notFound } from "next/navigation" import { notFound } from "next/navigation"
import { FavoriteButton } from "@/components/favorite-button"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge" import { Badge } from "@/components/ui/badge"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Separator } from "@/components/ui/separator" import { Separator } from "@/components/ui/separator"
import { mockPlayers, mockReviews, mockServices, mockShops } from "@/lib/mock-data" import { mockFavorites, mockPlayers, mockReviews, mockServices, mockShops } from "@/lib/mock-data"
interface PageProps { interface PageProps {
params: Promise<{ id: string }> params: Promise<{ id: string }>
@@ -24,6 +25,9 @@ export default async function ShopPage({ params }: PageProps) {
const playerIds = shopPlayers.map((p) => p.id) const playerIds = shopPlayers.map((p) => p.id)
const shopServices = mockServices.filter((s) => playerIds.includes(s.playerId)) const shopServices = mockServices.filter((s) => playerIds.includes(s.playerId))
const shopReviews = mockReviews.filter((r) => playerIds.includes(r.toUserId)) const shopReviews = mockReviews.filter((r) => playerIds.includes(r.toUserId))
const isFavorited = mockFavorites.some(
(f) => f.userId === "u1" && f.targetType === "shop" && f.targetId === shop.id,
)
const sortedSections = [...shop.templateConfig.sections] const sortedSections = [...shop.templateConfig.sections]
.filter((s) => s.enabled) .filter((s) => s.enabled)
@@ -98,6 +102,7 @@ export default async function ShopPage({ params }: PageProps) {
{shop.owner.bio || "暂无介绍"} {shop.owner.bio || "暂无介绍"}
</p> </p>
</div> </div>
<FavoriteButton initialFavorited={isFavorited} />
</div> </div>
</div> </div>
</CardContent> </CardContent>
+25
View File
@@ -0,0 +1,25 @@
"use client"
import { Heart } from "lucide-react"
import { useState } from "react"
import { Button } from "@/components/ui/button"
interface FavoriteButtonProps {
initialFavorited: boolean
}
export function FavoriteButton({ initialFavorited }: FavoriteButtonProps) {
const [favorited, setFavorited] = useState(initialFavorited)
return (
<Button
variant={favorited ? "default" : "outline"}
size="sm"
className="gap-1.5"
onClick={() => setFavorited(!favorited)}
>
<Heart className={`h-4 w-4 ${favorited ? "fill-current" : ""}`} />
{favorited ? "已收藏" : "收藏"}
</Button>
)
}
+18
View File
@@ -3,6 +3,7 @@ import type {
ChatSession, ChatSession,
Comment, Comment,
Dispute, Dispute,
Favorite,
Game, Game,
Notification, Notification,
Order, Order,
@@ -567,3 +568,20 @@ export const mockTransactions: WalletTransaction[] = [
export const currentUser = mockUsers[0] export const currentUser = mockUsers[0]
export const walletBalance = 430 export const walletBalance = 430
export const mockFavorites: Favorite[] = [
{
id: "fav1",
userId: "u1",
targetType: "player",
targetId: "u2",
createdAt: "2025-02-16T10:00:00",
},
{
id: "fav2",
userId: "u1",
targetType: "shop",
targetId: "shop1",
createdAt: "2025-02-17T14:00:00",
},
]
+8
View File
@@ -192,3 +192,11 @@ export interface WalletTransaction {
description: string description: string
createdAt: string createdAt: string
} }
export interface Favorite {
id: string
userId: string
targetType: "player" | "shop"
targetId: string
createdAt: string
}