This commit is contained in:
2025-12-19 13:45:30 -05:00
parent 823c8b52b3
commit 7b3862c43a
21 changed files with 2405 additions and 373 deletions

View File

@@ -5,19 +5,46 @@ import { Button } from "@mui/joy";
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);
const [type, setType] = useState<string>("");
const [title, setTitle] = useState<string>("");
const [year, setYear] = useState<string>("");
const [requester, setRequester] = useState<string>("");
const [selectedItem, setSelectedItem] = useState<SearchItem | null>(null);
const [selectedOverview, setSelectedOverview] = useState<string>("");
const [selectedTitle, setSelectedTitle] = useState<string>("");
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
const [suggestions, setSuggestions] = useState<SearchItem[]>([]);
const [posterLoading, setPosterLoading] = useState<boolean>(true);
const [submittedRequest, setSubmittedRequest] = useState<SubmittedRequest | null>(null); // Track successful submission
const [csrfToken, setCsrfToken] = useState<string | null>(null);
// Get CSRF token from window global on mount
useEffect(() => {
@@ -36,7 +63,7 @@ export default function ReqForm() {
}
}, [title, selectedTitle, selectedOverview, selectedItem, type]);
const searchTitles = async (event) => {
const searchTitles = async (event: { query: string }) => {
const query = event.query;
if (query.length < 2) {
setSuggestions([]);
@@ -48,7 +75,7 @@ export default function ReqForm() {
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
const data: SearchItem[] = await response.json();
setSuggestions(data);
} catch (error) {
console.error('Error fetching suggestions:', error);
@@ -56,7 +83,7 @@ export default function ReqForm() {
}
};
const handleSubmit = async (e) => {
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!title.trim()) {
toast.error("Please fill in the required fields.");
@@ -101,7 +128,7 @@ export default function ReqForm() {
year,
type,
requester,
poster_path: selectedItem?.poster_path,
poster_path: selectedItem?.poster_path ?? null,
});
} catch (error) {
console.error('Submission error:', error);
@@ -126,13 +153,13 @@ export default function ReqForm() {
const attachScrollFix = () => {
setTimeout(() => {
const panel = document.querySelector(".p-autocomplete-panel");
const items = panel?.querySelector(".p-autocomplete-items");
const panel = document.querySelector<HTMLElement>(".p-autocomplete-panel");
const items = panel?.querySelector<HTMLElement>(".p-autocomplete-items");
if (items) {
items.style.maxHeight = "200px";
items.style.overflowY = "auto";
items.style.overscrollBehavior = "contain";
const wheelHandler = (e) => {
const wheelHandler = (e: WheelEvent) => {
const delta = e.deltaY;
const atTop = items.scrollTop === 0;
const atBottom = items.scrollTop + items.clientHeight >= items.scrollHeight;
@@ -148,7 +175,7 @@ export default function ReqForm() {
}, 0);
};
const formatMediaType = (mediaTypeValue) => {
const formatMediaType = (mediaTypeValue: MediaType | undefined) => {
if (!mediaTypeValue) return "";
if (mediaTypeValue === "tv") return "TV Series";
if (mediaTypeValue === "movie") return "Movie";
@@ -239,16 +266,17 @@ export default function ReqForm() {
delay={300}
onChange={(e) => {
// Handle both string input and object selection
const val = e.target?.value ?? e.value;
setTitle(typeof val === 'string' ? val : val?.label || '');
const val = (e as any).target?.value ?? e.value;
setTitle(typeof val === 'string' ? val : (val as SearchItem | undefined)?.label || '');
}}
onSelect={(e) => {
setType(e.value.mediaType === 'tv' ? 'tv' : 'movie');
setTitle(e.value.label);
setSelectedTitle(e.value.label);
setSelectedItem(e.value);
if (e.value.year) setYear(e.value.year);
setSelectedOverview(e.value.overview || "");
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"