Files
juwan-frontend/app/(order)/order/new/page.tsx
T

227 lines
8.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client"
import { ArrowLeft, CheckCircle, CreditCard, ShieldCheck } from "lucide-react"
import Link from "next/link"
import { useRouter, useSearchParams } from "next/navigation"
import { useState } from "react"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Separator } from "@/components/ui/separator"
import { Textarea } from "@/components/ui/textarea"
import { getPlayerById, getServiceById } from "@/lib/api"
import { notifySuccess } from "@/lib/toast"
import { useRequireAuth } from "@/lib/use-require-auth"
import { useAuthStore } from "@/store/auth"
import { useChatStore } from "@/store/chat"
import { useOrderStore } from "@/store/orders"
import { useWalletStore } from "@/store/wallet"
export default function NewOrderPage() {
const router = useRouter()
const searchParams = useSearchParams()
const { requireAuth } = useRequireAuth()
const createOrder = useOrderStore((state) => state.createOrder)
const ensureOrderSession = useChatStore((state) => state.ensureOrderSession)
const balance = useWalletStore((state) => state.balance)
const deductBalance = useWalletStore((state) => state.deductBalance)
const serviceId = searchParams.get("serviceId")
const service = serviceId ? getServiceById(serviceId) : undefined
const player = service ? getPlayerById(service.playerId) : undefined
const [quantity, setQuantity] = useState(1)
const [note, setNote] = useState("")
const [submitted, setSubmitted] = useState(false)
if (!service || !player) {
return (
<div className="container mx-auto py-8 px-4 text-center text-muted-foreground">
</div>
)
}
const totalPrice = service.price * quantity
if (submitted) {
return (
<div className="container mx-auto py-8 px-4 max-w-lg text-center space-y-4">
<CheckCircle className="h-12 w-12 mx-auto text-green-500" />
<h2 className="text-xl font-bold"></h2>
<p className="text-sm text-muted-foreground">
</p>
<div className="flex gap-2 justify-center">
<Button asChild>
<Link href="/orders"></Link>
</Button>
<Button variant="outline" asChild>
<Link href={`/player/${player.id}`}></Link>
</Button>
</div>
</div>
)
}
return (
<div className="container mx-auto py-8 px-4 max-w-2xl">
<Link
href={`/player/${player.id}`}
className="inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground mb-4"
>
<ArrowLeft className="h-4 w-4" />
</Link>
<h1 className="text-2xl font-bold mb-6"></h1>
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="text-base"></CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center gap-3">
<Avatar className="h-10 w-10">
<AvatarImage src={player.user.avatar} />
<AvatarFallback>{player.user.nickname[0]}</AvatarFallback>
</Avatar>
<div>
<p className="font-medium">{player.user.nickname}</p>
<p className="text-xs text-muted-foreground">
{player.shopName ? `${player.shopName} · ` : ""}
{service.gameName}
</p>
</div>
</div>
<Separator />
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-muted-foreground"></span>
<span>{service.title}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground"></span>
<span>
¥{service.price}/{service.unit}
</span>
</div>
{service.rankRange && (
<div className="flex justify-between">
<span className="text-muted-foreground"></span>
<span>{service.rankRange}</span>
</div>
)}
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-base"></CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="quantity">{service.unit}</Label>
<Input
id="quantity"
type="number"
min={1}
max={99}
value={quantity}
onChange={(e) => setQuantity(Math.max(1, Number.parseInt(e.target.value, 10) || 1))}
/>
</div>
<div className="space-y-2">
<Label htmlFor="note"></Label>
<Textarea
id="note"
placeholder="例如:希望晚上8点后开始"
value={note}
onChange={(e) => setNote(e.target.value)}
rows={3}
/>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-base"></CardTitle>
</CardHeader>
<CardContent className="space-y-3">
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">
{service.title} × {quantity}
</span>
<span>¥{totalPrice}</span>
</div>
<Separator />
<div className="flex justify-between font-medium">
<span></span>
<span className="text-lg text-primary">¥{totalPrice}</span>
</div>
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<CreditCard className="h-4 w-4" />
<span>: ¥{balance.toFixed(2)}</span>
{balance < totalPrice && <span className="text-destructive"></span>}
{balance < totalPrice && (
<Button variant="outline" size="sm" asChild>
<Link href="/wallet"></Link>
</Button>
)}
</div>
<div className="flex items-center gap-2 text-xs text-muted-foreground">
<ShieldCheck className="h-3.5 w-3.5" />
<span></span>
</div>
</CardContent>
</Card>
<Button
className="w-full"
size="lg"
disabled={balance < totalPrice}
onClick={() =>
requireAuth(async () => {
await new Promise((resolve) => setTimeout(resolve, 500))
const authUser = useAuthStore.getState().user
if (!authUser) return
const order = createOrder({
consumerId: authUser.id,
consumerName: authUser.nickname,
playerId: player.id,
playerName: player.user.nickname,
shopId: player.shopId,
shopName: player.shopName,
service,
totalPrice,
note,
status: "pending_accept",
})
const paid = deductBalance(order.id, totalPrice)
if (!paid) {
return
}
ensureOrderSession(order)
setSubmitted(true)
notifySuccess("下单成功")
setTimeout(() => {
router.push(`/order/${order.id}`)
}, 800)
})
}
>
¥{totalPrice}
</Button>
</div>
</div>
)
}