"use client" import { useState } from "react" import Image from "next/image" import { useRouter } from "next/navigation" import { AnimatePresence, motion } from "motion/react" import { useSession } from "@/lib/auth-client" import type { GameSettingCategory } from "@/lib/db/schema/performanceEntries" import { ThumbsUpIcon, ThumbsDownIcon, FlagIcon, TrashIcon, ChevronLeftIcon, ChevronRightIcon, ShieldCheckIcon, XIcon, UserIcon, ShareIcon, PencilIcon, } from "lucide-react" import { TiptapRenderer } from "@/components/tiptap-renderer" interface Preset { id: string gameId?: string hardwareSlug: string hardwareName: string upvotes: number downvotes: number settingsJson: GameSettingCategory[] | null settingsCount: number fpsAvg: number | null fpsLow: number | null fpsHigh: number | null fpsOnePercentLow: number | null upscalerType: string | null upscalerVersion: string | null frameGenMethod: string | null protonVersion: string | null osVersion: string | null launchOptions: string | null loadTimeSsd: number | null loadTimeSd: number | null tdpWatts: number | null youtubeVideoId: string | null screenshots: Array<{ id: string; url: string; width: number; height: number }> | null hardwareWattHours: number | null hardwareDeviceType: string | null customSystem: boolean userNotes: string | null userId: string userName: string | null userImage: string | null verifiedAt: string | null isPinned: boolean pinnedAt: string | null createdAt: string } interface PresetDetailModalProps { preset: Preset gameId: string onClose: () => void onDelete: (presetId: string) => void onReport: ( presetId: string, reason: "inaccurate" | "spam" | "inappropriate" | "other", details?: string, ) => void hasReported: boolean } function formatDate(value: string | null): string { if (!value) return "—" return new Date(value).toLocaleDateString() } function formatValue(value: string | number | boolean): string { if (typeof value === "boolean") return value ? "On" : "Off" return String(value) } export function PresetDetailModal({ preset, gameId, onClose, onDelete, onReport, hasReported, }: PresetDetailModalProps) { const router = useRouter() const { data: session } = useSession() const [activeCategoryIndex, setActiveCategoryIndex] = useState(0) const [showDeleteConfirm, setShowDeleteConfirm] = useState(false) const [showReportForm, setShowReportForm] = useState(false) const [reportReason, setReportReason] = useState< "inaccurate" | "spam" | "inappropriate" | "other" >("inaccurate") const [reportDetails, setReportDetails] = useState("") const [copied, setCopied] = useState(false) const [userVote, setUserVote] = useState<"up" | "down" | null>(null) const [localUpvotes, setLocalUpvotes] = useState(preset.upvotes) const [localDownvotes, setLocalDownvotes] = useState(preset.downvotes) const [mobileTab, setMobileTab] = useState<"details" | "settings">("settings") const isOwner = session?.user?.id === preset.userId const isAdmin = session?.user?.role === "admin" const isAuthenticated = !!session?.user const handleShare = () => { const url = `${window.location.origin}/game/${gameId}?preset=${preset.id}` navigator.clipboard.writeText(url) setCopied(true) setTimeout(() => setCopied(false), 2000) } const categories = preset.settingsJson ?? [] const hasCategories = categories.length > 0 const currentCategory = hasCategories ? categories[activeCategoryIndex] : null const goPrevCategory = () => { setActiveCategoryIndex((prev) => (prev > 0 ? prev - 1 : categories.length - 1)) } const goNextCategory = () => { setActiveCategoryIndex((prev) => (prev < categories.length - 1 ? prev + 1 : 0)) } const handleUpvote = async () => { if (!isAuthenticated || userVote === "up") return try { const res = await fetch(`/api/performance/${preset.id}/upvote`, { method: "POST" }) if (res.ok) { if (userVote === "down") setLocalDownvotes((d) => d - 1) setLocalUpvotes((u) => u + 1) setUserVote("up") } } catch (err) { console.error("Failed to upvote:", err) } } const handleDownvote = async () => { if (!isAuthenticated || userVote === "down") return try { const res = await fetch(`/api/performance/${preset.id}/downvote`, { method: "POST" }) if (res.ok) { if (userVote === "up") setLocalUpvotes((u) => u - 1) setLocalDownvotes((d) => d + 1) setUserVote("down") } } catch (err) { console.error("Failed to downvote:", err) } } const handleReportSubmit = () => { onReport( preset.id, reportReason, reportDetails.trim() || undefined, ) setShowReportForm(false) setReportDetails("") } const handleTogglePin = async () => { const method = preset.isPinned ? "DELETE" : "POST" try { const res = await fetch(`/api/performance/${preset.id}/pin`, { method }) if (res.ok) { router.refresh() } } catch (err) { console.error("Failed to toggle pin:", err) } } return ( <> {/* Backdrop */} {/* Modal container */} {/* Modal card */} e.stopPropagation()} > {/* Header */}

{preset.hardwareName}

{/* Mobile tabs */}
{/* Two-column body */}
{/* Left panel */}
{/* User info */}
{preset.userImage ? ( {preset.userName ) : ( )}
{preset.userName || "Anonymous"} {preset.verifiedAt && ( )}
{formatDate(preset.createdAt)}
{/* Actions */}
{isAdmin && ( )} {(isOwner || isAdmin) && ( <> {!showDeleteConfirm ? ( ) : (
Are you sure?
)} )} {(isOwner || isAdmin) && ( )} {session && !hasReported && ( <> {!showReportForm ? ( ) : (