js -> ts
This commit is contained in:
@@ -46,7 +46,8 @@ export interface RootProps {
|
||||
|
||||
export default function Root({ child, user = undefined, ...props }: RootProps): React.ReactElement {
|
||||
window.toast = toast;
|
||||
const theme = document.documentElement.getAttribute("data-theme")
|
||||
const theme = document.documentElement.getAttribute("data-theme") ?? null;
|
||||
const toastTheme = theme ?? undefined;
|
||||
const loggedIn = props.loggedIn ?? Boolean(user);
|
||||
usePrimeReactThemeSwitcher(theme);
|
||||
// Avoid adding the Player island for subsite requests. We expose a
|
||||
@@ -81,7 +82,7 @@ export default function Root({ child, user = undefined, ...props }: RootProps):
|
||||
let mounted = true;
|
||||
if (wantPlayer) {
|
||||
if (import.meta.env.DEV) { try { console.debug('[AppLayout] dynamic-import: requesting AudioPlayer'); } catch (e) { } }
|
||||
import('./AudioPlayer.jsx')
|
||||
import('./Radio.js')
|
||||
.then((mod) => {
|
||||
if (!mounted) return;
|
||||
// set the component factory
|
||||
@@ -102,7 +103,7 @@ export default function Root({ child, user = undefined, ...props }: RootProps):
|
||||
return (
|
||||
<PrimeReactProvider>
|
||||
<CustomToastContainer
|
||||
theme={theme}
|
||||
theme={toastTheme}
|
||||
newestOnTop={true}
|
||||
closeOnClick={true} />
|
||||
<JoyUIRootIsland>
|
||||
@@ -114,22 +115,22 @@ export default function Root({ child, user = undefined, ...props }: RootProps):
|
||||
Work in progress... bugs are to be expected.
|
||||
</Alert> */}
|
||||
{child == "LoginPage" && (<LoginPage {...props} loggedIn={loggedIn} />)}
|
||||
{child == "LyricSearch" && (<LyricSearch {...props} client:only="react" />)}
|
||||
{child == "LyricSearch" && (<LyricSearch />)}
|
||||
{child == "Player" && !isSubsite && PlayerComp && (
|
||||
<Suspense fallback={null}>
|
||||
<PlayerComp client:only="react" user={user} />
|
||||
<PlayerComp user={user} />
|
||||
</Suspense>
|
||||
)}
|
||||
{child == "Memes" && <Memes client:only="react" />}
|
||||
{child == "Memes" && <Memes />}
|
||||
{child == "DiscordLogs" && (
|
||||
<Suspense fallback={<div style={{ padding: '2rem', textAlign: 'center' }}>Loading...</div>}>
|
||||
<DiscordLogs client:only="react" />
|
||||
<DiscordLogs />
|
||||
</Suspense>
|
||||
)}
|
||||
{child == "qs2.MediaRequestForm" && <MediaRequestForm client:only="react" />}
|
||||
{child == "qs2.RequestManagement" && <RequestManagement client:only="react" />}
|
||||
{child == "ReqForm" && <ReqForm {...props} client:only="react" />}
|
||||
{child == "Lighting" && <Lighting key={window.location.pathname + Math.random()} client:only="react" />}
|
||||
{child == "qs2.MediaRequestForm" && <MediaRequestForm />}
|
||||
{child == "qs2.RequestManagement" && <RequestManagement />}
|
||||
{child == "ReqForm" && <ReqForm />}
|
||||
{child == "Lighting" && <Lighting key={window.location.pathname + Math.random()} />}
|
||||
</JoyUIRootIsland>
|
||||
</PrimeReactProvider>
|
||||
);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,11 +4,13 @@
|
||||
* This is a minimal version that avoids importing from shared modules
|
||||
* that would pull in heavy CSS (AppLayout, Components, etc.)
|
||||
*/
|
||||
import React, { Suspense, lazy, ReactNode } from 'react';
|
||||
import React, { Suspense, lazy } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { ToastContainer, toast } from 'react-toastify';
|
||||
import { CssVarsProvider } from "@mui/joy";
|
||||
import { CacheProvider, EmotionCache } from "@emotion/react";
|
||||
import { CacheProvider } from "@emotion/react";
|
||||
import createCache from "@emotion/cache";
|
||||
import type { EmotionCache } from "@emotion/cache";
|
||||
import { PrimeReactProvider } from "primereact/api";
|
||||
|
||||
// Import only minimal CSS - no theme CSS, no primeicons
|
||||
|
||||
1349
src/components/TRip/MediaRequestForm.jsx
Normal file
1349
src/components/TRip/MediaRequestForm.jsx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user