import { useState, useEffect, Suspense, lazy } from "react"; import { useNavigate } from "react-router"; import LoadingDisplay from "../components/LoadingDisplay"; import { useAuth } from "../contexts/AuthContext"; import SessionExpiredPopup from "../components/SessionExpiredPopup"; import type { SupportedGame } from "../types/game"; import { uploadScore } from "../utils/scoreUpload"; import { NavBar } from "../components/NavBar"; import { EamusementUserscriptCard } from "../components/modals/EamusementUserscriptModal"; import { FlowerUserscriptCard } from "../components/modals/FlowerUserscriptModal"; import TaikoDonderHirobaModal from "../components/modals/TaikoDonderHirobaModal"; const JsonUploadModal = lazy(() => import("../components/modals/JsonUploadModal")); const EamusementUserscriptModal = lazy(() => import("../components/modals/EamusementUserscriptModal")); const DivaNetModal = lazy(() => import("../components/modals/DivaNetModal")); const MusicDiverModal = lazy(() => import("../components/modals/MusicDiverModal")); const FlowerUserscriptModal = lazy(() => import("../components/modals/FlowerUserscriptModal")); type ModalType = 'json' | 'dancerush' | 'dancearound' | 'divanet' | 'musicdiver' | 'nostalgia' | 'reflecbeat' | 'taiko'; const Import = () => { const { user, isLoading, logout } = useAuth(); const navigate = useNavigate(); const [selectedGame, setSelectedGame] = useState(""); const [openModal, setOpenModal] = useState(null); const [supportedGames, setSupportedGames] = useState([]); const [gamesLoading, setGamesLoading] = useState(true); const [uploadStatus, setUploadStatus] = useState<{ type: "success" | "error" | null; message: string; }>({ type: null, message: "" }); useEffect(() => { const fetchSupportedGames = async () => { try { const response = await fetch( import.meta.env.VITE_API_URL + "/supportedGames", ); if (!response.ok) { throw new Error("Failed to fetch supported games"); } const data = await response.json(); setSupportedGames(data); } catch (error) { console.error("Failed to fetch supported games:", error); setUploadStatus({ type: "error", message: "Failed to load supported games. Please refresh the page.", }); } finally { setGamesLoading(false); } }; fetchSupportedGames(); }, []); const handleLogout = async () => { try { await logout(); navigate("/"); } catch (error) { console.error("Logout failed:", error); alert("Network error during logout. Please try again."); } }; // has to be any as this is a dynamic trackerm with dynamic score formats // eslint-disable-next-line @typescript-eslint/no-explicit-any const handleJsonUpload = async (data: any) => { try { console.log("Uploading data for game:", selectedGame, data); const result = await uploadScore({ meta: { game: data.meta.game, service: data.meta.service, playtype: data.meta.playtype, }, scores: data.scores, }); setUploadStatus({ type: "success", message: `Successfully imported ${result.scoreCount} score(s) for ${supportedGames.find((g) => g.internalName === data.meta.game)?.formattedName || data.meta.game}`, }); setTimeout(() => { setUploadStatus({ type: null, message: "" }); }, 5000); } catch (error) { console.error("Upload failed:", error); setUploadStatus({ type: "error", message: error instanceof Error ? error.message : "Failed to import data. Please try again.", }); } }; const JsonUploadCard = () => (

Batch-Manual Upload

Upload your game data from a Mirage compatible JSON file

); const renderImportOptions = () => { switch (selectedGame) { case "dancerush": return ( <> setOpenModal('dancerush')} /> ); case "dancearound": return ( <> setOpenModal('dancearound')} /> ); case "diva": return ( <> {}} game={supportedGames.find((g) => g.internalName === selectedGame)} renderAsCard={() => setOpenModal('divanet')} /> ); case "musicdiver": return ( <> {}} game={supportedGames.find((g) => g.internalName === selectedGame)} renderAsCard={() => setOpenModal('musicdiver')} /> ); case "nostalgia": return ( <> setOpenModal('nostalgia')} /> ); case "reflecbeat": return ( <> setOpenModal('reflecbeat')} /> ); case "taiko": return ( <> {}} game={supportedGames.find((g) => g.internalName === selectedGame)} renderAsCard={() => setOpenModal('taiko')} /> ); default: return ; } }; if (isLoading) { return ( ); } if (!user) { return ; } return (
{/* Navigation */} {/* Main Content */}
{/* Header */}

Import Data

Import your game scores and progress from various sources

{/* Status Message */} {uploadStatus.type && (

{uploadStatus.message}

)} {/* Game Selection Card */}

Select Game

Choose the game you want to import data for

{gamesLoading ? (
Loading games...
) : ( )}
{/* Import Options */} {selectedGame && (

Import Options

{renderImportOptions()}
)}
{/* Modals wrapped in Suspense */} Loading...
}> {openModal === 'json' && ( setOpenModal(null)} onUpload={handleJsonUpload} game={ supportedGames.find((g) => g.internalName === selectedGame) ?.formattedName || "" } /> )} {openModal === 'dancerush' && ( setOpenModal(null)} mainGameName="DANCERUSH" userPage="https://p.eagate.573.jp/game/dan/1st/top/entrance.html" importPage="https://p.eagate.573.jp/payment/p/ex_select_course.html" scripts={[{ name: "e-amusement Recently Played Score Export Userscript (Last 20 Played)", url: "https://github.com/pinapelz/Mirage/raw/refs/heads/main/scripts/dancerush/eamuse/dancerush_play_history.user.js" }]} /> )} {openModal === 'dancearound' && ( setOpenModal(null)} mainGameName="DANCE aROUND" userPage="https://p.eagate.573.jp/game/around/1st/top/index.html" importPage="https://p.eagate.573.jp/game/around/1st/top/index.html#play_hist" scripts={[{ name: "e-amusement Recently Played Score Export Userscript (Last 20 Played)", url: "https://github.com/pinapelz/Mirage/raw/refs/heads/main/scripts/dancearound/eamuse/dancearound_play_history.user.js" }]} /> )} {openModal === 'divanet' && ( setOpenModal(null)} game={ supportedGames.find((g) => g.internalName === selectedGame) || undefined } /> )} {openModal === 'musicdiver' && ( setOpenModal(null)} game={ supportedGames.find((g) => g.internalName === selectedGame) || undefined } /> )} {openModal === 'nostalgia' && ( setOpenModal(null)} mainGameName="NOSTALGIA" userPage="https://projectflower.eu" importPage="https://projectflower.eu/game/nostalgia/54827307" scripts={[{ name: "Flower Play History (Exports only the page you are on)", url: "https://github.com/pinapelz/Mirage/raw/refs/heads/main/scripts/nostalgia/flower/nostalgia_flower_scraper.user.js" }]} /> )} {openModal === 'reflecbeat' && ( setOpenModal(null)} mainGameName="REFLEC BEAT" userPage="https://projectflower.eu" importPage="https://projectflower.eu/game/rb/profile/21363050" scripts={[{ name: "Flower Play History (Exports only the page you are on)", url: "https://github.com/pinapelz/Mirage/raw/refs/heads/main/scripts/reflecbeat/flower/reflecbeat_flower_scraper.user.js" }]} /> )} {openModal === 'taiko' && ( setOpenModal(null)} game={ supportedGames.find((g) => g.internalName === selectedGame) || undefined } /> )} ); }; export default Import;