Files
wzdj/components/account/dynamic-cascade-form.tsx
v0 e267fa09f6 feat: enhance live room and planet matchmaking features
Add fan group entry on live room; update planet matching with 4 type buttons.

Co-authored-by: undefined <undefined+undefined@users.noreply.github.com>
2025-12-01 10:46:13 +00:00

205 lines
7.7 KiB
TypeScript

"use client"
import { useState, useEffect } from "react"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Label } from "@/components/ui/label"
import { Input } from "@/components/ui/input"
import { Checkbox } from "@/components/ui/checkbox"
import { GAME_CASCADE_DATA } from "@/lib/data/game-cascade-data"
type DynamicCascadeFormProps = {
gameName: string
onDataChange: (data: Record<string, any>) => void
}
export function DynamicCascadeForm({ gameName, onDataChange }: DynamicCascadeFormProps) {
const config = GAME_CASCADE_DATA[gameName]
const [selectedRegion, setSelectedRegion] = useState<string>("")
const [selectedServer, setSelectedServer] = useState<string>("")
const [selectedSubOption, setSelectedSubOption] = useState<string>("")
const [attributeValues, setAttributeValues] = useState<Record<string, any>>({})
useEffect(() => {
// Reset when game changes
setSelectedRegion("")
setSelectedServer("")
setSelectedSubOption("")
setAttributeValues({})
}, [gameName])
useEffect(() => {
// Notify parent of data changes
onDataChange({
region: selectedRegion,
server: selectedServer,
subOption: selectedSubOption,
...attributeValues,
})
}, [selectedRegion, selectedServer, selectedSubOption, attributeValues, onDataChange])
if (!config) {
return <div className="text-gray-400 text-sm"></div>
}
const currentRegion = config.regions.find((r) => r.value === selectedRegion)
const serverOptions = currentRegion?.children || []
const currentServer = serverOptions.find((s) => s.value === selectedServer)
const subOptions = currentServer?.children || []
const handleAttributeChange = (label: string, value: any) => {
setAttributeValues((prev) => ({ ...prev, [label]: value }))
}
const handleMultiSelectChange = (label: string, option: string, checked: boolean) => {
setAttributeValues((prev) => {
const current = prev[label] || []
if (checked) {
return { ...prev, [label]: [...current, option] }
} else {
return { ...prev, [label]: current.filter((v: string) => v !== option) }
}
})
}
return (
<div className="space-y-5">
{/* Region Selection */}
{config.regions.length > 0 && (
<div className="space-y-2">
<Label className="text-gray-400"></Label>
<Select
value={selectedRegion}
onValueChange={(v) => {
setSelectedRegion(v)
setSelectedServer("")
setSelectedSubOption("")
}}
>
<SelectTrigger className="bg-[#12121A] border-white/10 h-11 text-white focus:ring-1 focus:ring-blue-500">
<SelectValue placeholder="选择游戏区" />
</SelectTrigger>
<SelectContent className="bg-[#1A1A2E] border-white/10 text-white">
{config.regions.map((region) => (
<SelectItem key={region.value} value={region.value}>
{region.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
{/* Server Selection */}
{serverOptions.length > 0 && (
<div className="space-y-2">
<Label className="text-gray-400"></Label>
<Select
value={selectedServer}
onValueChange={(v) => {
setSelectedServer(v)
setSelectedSubOption("")
}}
disabled={!selectedRegion}
>
<SelectTrigger className="bg-[#12121A] border-white/10 h-11 text-white focus:ring-1 focus:ring-blue-500">
<SelectValue placeholder="选择服务器" />
</SelectTrigger>
<SelectContent className="bg-[#1A1A2E] border-white/10 text-white max-h-[300px]">
{serverOptions.map((server) => (
<SelectItem key={server.value} value={server.value}>
{server.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
{/* Sub Options */}
{subOptions.length > 0 && (
<div className="space-y-2">
<Label className="text-gray-400"></Label>
<Select value={selectedSubOption} onValueChange={setSelectedSubOption} disabled={!selectedServer}>
<SelectTrigger className="bg-[#12121A] border-white/10 h-11 text-white focus:ring-1 focus:ring-blue-500">
<SelectValue placeholder="选择属性" />
</SelectTrigger>
<SelectContent className="bg-[#1A1A2E] border-white/10 text-white">
{subOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
{/* Dynamic Attributes */}
{config.attributes.map((attr, index) => (
<div key={index} className="space-y-2">
<Label className="text-gray-400">
{attr.label}
{attr.required && <span className="text-red-400 ml-1">*</span>}
</Label>
{attr.type === "select" && attr.options && (
<Select
value={attributeValues[attr.label] || ""}
onValueChange={(v) => handleAttributeChange(attr.label, v)}
>
<SelectTrigger className="bg-[#12121A] border-white/10 h-11 text-white focus:ring-1 focus:ring-purple-500">
<SelectValue placeholder={`选择${attr.label}`} />
</SelectTrigger>
<SelectContent className="bg-[#1A1A2E] border-white/10 text-white max-h-[300px]">
{attr.options.map((option) => (
<SelectItem key={option} value={option}>
{option}
</SelectItem>
))}
</SelectContent>
</Select>
)}
{attr.type === "multiselect" && attr.options && (
<div className="bg-[#12121A] border border-white/10 rounded-lg p-4 max-h-[200px] overflow-y-auto space-y-2">
{attr.options.slice(0, 20).map((option) => (
<div key={option} className="flex items-center space-x-2">
<Checkbox
id={`${attr.label}-${option}`}
checked={(attributeValues[attr.label] || []).includes(option)}
onCheckedChange={(checked) => handleMultiSelectChange(attr.label, option, checked as boolean)}
className="border-white/20"
/>
<label htmlFor={`${attr.label}-${option}`} className="text-sm text-gray-300 cursor-pointer">
{option}
</label>
</div>
))}
{attr.options.length > 20 && <div className="text-xs text-gray-500 text-center pt-2">20...</div>}
</div>
)}
{attr.type === "input" && (
<Input
value={attributeValues[attr.label] || ""}
onChange={(e) => handleAttributeChange(attr.label, e.target.value)}
placeholder={`输入${attr.label}`}
className="bg-[#12121A] border-white/10 h-11 text-white focus:border-purple-500"
/>
)}
</div>
))}
{/* Screenshot Upload Hint */}
<div className="bg-[#12121A] border border-blue-500/20 rounded-lg p-3 mt-4">
<p className="text-xs text-blue-300 flex items-center gap-2">
<span className="text-blue-500">📸</span>
便
</p>
</div>
</div>
)
}