import React, { useState, useEffect } from "react"; import { toast } from "react-toastify"; import { Button } from "@mui/joy"; // Dropdown not used in this form; removed to avoid unused-import warnings import { AutoComplete } from "primereact/autocomplete"; import { InputText } from "primereact/inputtext"; declare global { interface Window { _t?: string; } } type MediaType = 'movie' | 'tv' | string; interface SearchItem { label: string; year?: string; mediaType?: MediaType; poster_path?: string | null; overview?: string; title?: string; type?: string; requester?: string; } interface SubmittedRequest { title: string; year: string; type: string; requester: string; poster_path?: string | null; } export default function ReqForm() { const [type, setType] = useState(""); const [title, setTitle] = useState(""); const [year, setYear] = useState(""); const [requester, setRequester] = useState(""); const [selectedItem, setSelectedItem] = useState(null); const [selectedOverview, setSelectedOverview] = useState(""); const [selectedTitle, setSelectedTitle] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); const [suggestions, setSuggestions] = useState([]); const [posterLoading, setPosterLoading] = useState(true); const [submittedRequest, setSubmittedRequest] = useState(null); // Track successful submission const [csrfToken, setCsrfToken] = useState(null); // Get CSRF token from window global on mount useEffect(() => { if (typeof window !== 'undefined' && window._t) { setCsrfToken(window._t); } }, []); useEffect(() => { if (title !== selectedTitle) { if (selectedOverview) setSelectedOverview(""); if (selectedItem) setSelectedItem(null); if (type) setType(""); if (selectedTitle) setSelectedTitle(""); setPosterLoading(true); } }, [title, selectedTitle, selectedOverview, selectedItem, type]); const searchTitles = async (event: { query: string }) => { const query = event.query; if (query.length < 2) { setSuggestions([]); return; } try { const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`); if (!response.ok) { throw new Error(`API error: ${response.status}`); } const data: SearchItem[] = await response.json(); setSuggestions(data); } catch (error) { console.error('Error fetching suggestions:', error); setSuggestions([]); } }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!title.trim()) { toast.error("Please fill in the required fields."); return; } if (!csrfToken) { toast.error("Security token not loaded. Please refresh the page."); return; } setIsSubmitting(true); try { const response = await fetch('/api/submit', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ title, year, type, requester, csrfToken }), }); const responseData = await response.json().catch(() => ({})); if (!response.ok) { const errorMessage = responseData.error || 'Submission failed'; toast.error(errorMessage); // If CSRF token error, require page reload if (response.status === 403) { toast.error('Please refresh the page and try again.'); } return; } // Save the new CSRF token from the response for future submissions if (responseData.csrfToken) { setCsrfToken(responseData.csrfToken); } // Store submitted request info for success view setSubmittedRequest({ title, year, type, requester, poster_path: selectedItem?.poster_path ?? null, }); } catch (error) { console.error('Submission error:', error); toast.error("Failed to submit request. Please try again."); } finally { setIsSubmitting(false); } }; const resetForm = () => { setType(""); setTitle(""); setYear(""); setRequester(""); setSelectedOverview(""); setSelectedTitle(""); setSelectedItem(null); setSubmittedRequest(null); setPosterLoading(true); // Token was already refreshed from the submit response }; const attachScrollFix = () => { setTimeout(() => { const panel = document.querySelector(".p-autocomplete-panel"); const items = panel?.querySelector(".p-autocomplete-items"); if (items) { items.style.maxHeight = "200px"; items.style.overflowY = "auto"; items.style.overscrollBehavior = "contain"; const wheelHandler = (e: WheelEvent) => { const delta = e.deltaY; const atTop = items.scrollTop === 0; const atBottom = items.scrollTop + items.clientHeight >= items.scrollHeight; if ((delta < 0 && atTop) || (delta > 0 && atBottom)) { e.preventDefault(); } else { e.stopPropagation(); } }; items.removeEventListener("wheel", wheelHandler); items.addEventListener("wheel", wheelHandler, { passive: false }); } }, 0); }; const formatMediaType = (mediaTypeValue: MediaType | undefined) => { if (!mediaTypeValue) return ""; if (mediaTypeValue === "tv") return "TV Series"; if (mediaTypeValue === "movie") return "Movie"; return mediaTypeValue.charAt(0).toUpperCase() + mediaTypeValue.slice(1); }; const selectedTypeLabel = formatMediaType(selectedItem?.mediaType || type); // Success view after submission if (submittedRequest) { const typeLabel = formatMediaType(submittedRequest.type); return (

Request Submitted!

Your request has been received and is pending review.

{submittedRequest.poster_path && ( Poster )}

{submittedRequest.title}

{submittedRequest.year && `${submittedRequest.year} ยท `} {typeLabel || 'Media'}

{submittedRequest.requester && (

Requested by: {submittedRequest.requester}

)}
); } return (

Request Movies/TV

Submit your request for review

{ // Handle both string input and object selection const val = (e as any).target?.value ?? e.value; setTitle(typeof val === 'string' ? val : (val as SearchItem | undefined)?.label || ''); }} onSelect={(e: { value: SearchItem }) => { const item = e.value; setType(item.mediaType === 'tv' ? 'tv' : 'movie'); setTitle(item.label); setSelectedTitle(item.label); setSelectedItem(item); if (item.year) setYear(item.year); setSelectedOverview(item.overview || ""); }} placeholder="Enter movie or TV title" title="Enter movie or TV show title" className="w-full" inputClassName="w-full border border-neutral-200 dark:border-neutral-700 rounded-xl px-4 py-3 bg-white dark:bg-neutral-900/50 focus:border-blue-500 dark:focus:border-blue-400 focus:ring-2 focus:ring-blue-500/20 transition-all outline-none" panelClassName="rounded-xl overflow-hidden" field="label" autoComplete="off" onShow={attachScrollFix} itemTemplate={(item) => (
{item.label} {item.year && ({item.year})} {item.mediaType === 'tv' ? 'TV' : 'Movie'}
)} /> {selectedItem && selectedTypeLabel && (
Selected type: {selectedTypeLabel}
)}
{selectedOverview && (

{selectedOverview}

{selectedItem?.poster_path && (
{posterLoading && (
)} Poster setPosterLoading(false)} onError={() => setPosterLoading(false)} />
)}
)}
setYear(e.target.value)} placeholder="e.g. 2023" className="w-full border border-neutral-200 dark:border-neutral-700 rounded-xl px-4 py-3 bg-white dark:bg-neutral-900/50 focus:border-blue-500 dark:focus:border-blue-400 focus:ring-2 focus:ring-blue-500/20 transition-all outline-none" />
setRequester(e.target.value)} placeholder="Who is requesting this?" className="w-full border border-neutral-200 dark:border-neutral-700 rounded-xl px-4 py-3 bg-white dark:bg-neutral-900/50 focus:border-blue-500 dark:focus:border-blue-400 focus:ring-2 focus:ring-blue-500/20 transition-all outline-none" />
); }