feat: favorites — type, mock data, and buttons on player/shop detail pages
This commit is contained in:
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user