feat(disputes): migrate disputes and reviews to backend API
This commit is contained in:
@@ -4,29 +4,56 @@ import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Textarea } from "@/components/ui/textarea"
|
||||
import { getOrderById, listReviewsByOrder } from "@/lib/api"
|
||||
import { submitReview } from "@/lib/api/reviews"
|
||||
import { notifyInfo, notifySuccess } from "@/lib/toast"
|
||||
import { useAuthStore } from "@/store/auth"
|
||||
import { useOrderStore } from "@/store/orders"
|
||||
import { useReviewStore } from "@/store/reviews"
|
||||
import { ArrowLeft, Lock, Star } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { use, useMemo, useState } from "react"
|
||||
import { use, useEffect, useState } from "react"
|
||||
|
||||
export default function ReviewPage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = use(params)
|
||||
const order = useOrderStore((state) => state.orders.find((item) => item.id === id))
|
||||
const userId = useAuthStore((state) => state.user?.id)
|
||||
const allReviews = useReviewStore((state) => state.reviews)
|
||||
// The selector returns the raw store array and useMemo derives the subset.
|
||||
// This keeps useSyncExternalStore snapshots stable across render checks.
|
||||
// Inline filter inside the selector creates a new array reference each call
|
||||
// and can cause infinite re-render loops in Zustand v5 (pmndrs/zustand#3155).
|
||||
const reviews = useMemo(() => allReviews.filter((item) => item.orderId === id), [allReviews, id])
|
||||
|
||||
const [order, setOrder] = useState<Awaited<ReturnType<typeof getOrderById>>>()
|
||||
const [reviews, setReviews] = useState<Awaited<ReturnType<typeof listReviewsByOrder>>>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [rating, setRating] = useState(0)
|
||||
const [hoverRating, setHoverRating] = useState(0)
|
||||
const [content, setContent] = useState("")
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
setLoading(true)
|
||||
|
||||
Promise.all([getOrderById(id), Promise.resolve(listReviewsByOrder(id))])
|
||||
.then(([nextOrder, nextReviews]) => {
|
||||
if (cancelled) return
|
||||
setOrder(nextOrder)
|
||||
setReviews(nextReviews)
|
||||
})
|
||||
.catch(() => {
|
||||
if (cancelled) return
|
||||
setOrder(undefined)
|
||||
setReviews([])
|
||||
})
|
||||
.finally(() => {
|
||||
if (cancelled) return
|
||||
setLoading(false)
|
||||
})
|
||||
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [id])
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="container mx-auto py-8 px-4 text-center text-muted-foreground">加载中...</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!order) {
|
||||
return (
|
||||
<div className="container mx-auto py-8 px-4 text-center text-muted-foreground">
|
||||
@@ -143,18 +170,23 @@ export default function ReviewPage({ params }: { params: Promise<{ id: string }>
|
||||
return
|
||||
}
|
||||
|
||||
const decision = submitReview({
|
||||
orderId: id,
|
||||
rating,
|
||||
content,
|
||||
Promise.resolve(
|
||||
submitReview({
|
||||
orderId: id,
|
||||
rating,
|
||||
content,
|
||||
}),
|
||||
).then((decision) => {
|
||||
if (decision.ok) {
|
||||
notifySuccess("评价已提交")
|
||||
Promise.resolve(listReviewsByOrder(id)).then((nextReviews) => {
|
||||
setReviews(nextReviews)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
notifyInfo(decision.error.msg)
|
||||
})
|
||||
|
||||
if (decision.ok) {
|
||||
notifySuccess("评价已提交")
|
||||
return
|
||||
}
|
||||
|
||||
notifyInfo(decision.error.msg)
|
||||
}}
|
||||
>
|
||||
提交评价
|
||||
|
||||
Reference in New Issue
Block a user