import { useState, useEffect } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { Game, Source } from "../items"; import { apiFetch } from "./api"; import type { ToastType } from "./Toast"; interface Props { onShowToast?: (message: string, type?: ToastType) => void; } export function EditGame({ onShowToast }: Props) { const { title } = useParams<{ title: string }>(); const navigate = useNavigate(); const [game, setGame] = useState(null); const [newTitle, setNewTitle] = useState(""); const [source, setSource] = useState(Source.STEAM); const [minPlayers, setMinPlayers] = useState(1); const [maxPlayers, setMaxPlayers] = useState(1); const [price, setPrice] = useState(0); const [remoteId, setRemoteId] = useState(0); const [isSubmitting, setIsSubmitting] = useState(false); const [remoteIdError, setRemoteIdError] = useState(""); const [loading, setLoading] = useState(true); useEffect(() => { if (!title) return; apiFetch(`/api/game/${encodeURIComponent(title)}`) .then(async (res) => { if (!res.ok) throw new Error("Game not found"); return Game.decode(new Uint8Array(await res.arrayBuffer())); }) .then((data) => { setGame(data); setNewTitle(data.title); setSource(data.source); setMinPlayers(data.minPlayers); setMaxPlayers(data.maxPlayers); setPrice(data.price); setRemoteId(data.remoteId); setLoading(false); }) .catch((err) => { console.error(err); onShowToast?.("Failed to load game", "error"); setLoading(false); }); }, [title, onShowToast]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (remoteId === 0) { setRemoteIdError("Remote ID must be greater than 0"); return; } if (!newTitle.trim()) { onShowToast?.("Game title is required", "error"); return; } setRemoteIdError(""); setIsSubmitting(true); const updatedGame = { title: newTitle.trim(), source, minPlayers, maxPlayers, price, remoteId, }; try { const encoded = Game.encode(updatedGame).finish(); const res = await apiFetch("/api/game", { method: "PATCH", headers: { "Content-Type": "application/octet-stream", }, body: encoded, }); if (res.ok) { onShowToast?.("Game updated successfully!", "success"); navigate(`/game/${encodeURIComponent(newTitle.trim())}`); } else { onShowToast?.("Failed to update game. Please try again.", "error"); } } catch (err) { console.error(err); onShowToast?.("An error occurred while updating the game.", "error"); } finally { setIsSubmitting(false); } }; if (loading) return
Loading...
; if (!game) return
Game not found
; const formCardStyles: React.CSSProperties = { background: "linear-gradient(135deg, var(--secondary-bg) 0%, var(--secondary-alt-bg) 100%)", borderRadius: "20px", padding: "0", maxWidth: "520px", margin: "0 auto", boxShadow: "0 20px 40px rgba(0, 0, 0, 0.3), 0 0 0 1px var(--border-color)", overflow: "hidden", }; const formHeaderStyles: React.CSSProperties = { background: "linear-gradient(135deg, var(--accent-color) 0%, var(--secondary-accent) 100%)", padding: "1.5rem 2rem", display: "flex", alignItems: "center", gap: "1rem", }; const formBodyStyles: React.CSSProperties = { padding: "2rem", }; const sectionStyles: React.CSSProperties = { marginBottom: "1.5rem", }; const sectionTitleStyles: React.CSSProperties = { fontSize: "0.75rem", textTransform: "uppercase", letterSpacing: "0.1em", color: "var(--text-muted)", marginBottom: "1rem", display: "flex", alignItems: "center", gap: "0.5rem", }; const inputGroupStyles: React.CSSProperties = { position: "relative", marginBottom: "1rem", }; const labelStyles: React.CSSProperties = { fontSize: "0.85rem", color: "var(--text-muted)", marginBottom: "0.5rem", display: "block", fontWeight: 500, }; const inputStyles: React.CSSProperties = { width: "100%", padding: "0.875rem 1rem", backgroundColor: "var(--tertiary-bg)", border: "2px solid var(--border-color)", borderRadius: "12px", color: "var(--text-color)", fontSize: "1rem", transition: "all 0.2s ease", boxSizing: "border-box", }; const gridStyles: React.CSSProperties = { display: "grid", gridTemplateColumns: "1fr 1fr", gap: "1rem", }; const dividerStyles: React.CSSProperties = { height: "1px", background: "linear-gradient(90deg, transparent, var(--border-color), transparent)", margin: "1.5rem 0", }; const buttonStyles: React.CSSProperties = { padding: "1rem", background: "linear-gradient(135deg, var(--accent-color) 0%, var(--secondary-accent) 100%)", border: "none", borderRadius: "12px", color: "white", fontSize: "1rem", fontWeight: 600, cursor: isSubmitting ? "not-allowed" : "pointer", transition: "all 0.3s ease", display: "flex", alignItems: "center", justifyContent: "center", gap: "0.5rem", opacity: isSubmitting ? 0.7 : 1, transform: isSubmitting ? "none" : undefined, }; const cancelStyles: React.CSSProperties = { ...buttonStyles, background: "var(--tertiary-bg)", color: "var(--text-color)", border: "2px solid var(--border-color)", }; return (
✏️

Edit Game

Update game information

📝 Basic Information
setNewTitle(e.target.value)} required placeholder="Enter game title..." style={inputStyles} className="add-game-input" />
👥 Player Count
setMinPlayers(Number(e.target.value))} min="1" style={inputStyles} className="add-game-input" />
setMaxPlayers(Number(e.target.value))} min="1" style={inputStyles} className="add-game-input" />
💰 Additional Details
setPrice(Math.ceil(Number(e.target.value.replace(',', '.'))))} min="0" step="1" style={inputStyles} className="add-game-input" />
{ setRemoteId(Number(e.target.value)); setRemoteIdError(""); }} min="0" style={{ ...inputStyles, borderColor: remoteIdError ? "#f44336" : undefined, }} className="add-game-input" /> {remoteIdError && (
{remoteIdError}
)}
); }