reduce amount of modal useStates + add Flower import modals (RB + nostop)

This commit is contained in:
Pinapelz
2025-10-26 15:19:05 -07:00
parent babaaa631d
commit 016a4f266b
2 changed files with 243 additions and 27 deletions
@@ -0,0 +1,169 @@
interface UserScript {
name: string;
url: string;
}
interface FlowerUserscriptModalProps {
isOpen: boolean;
onClose: () => void;
mainGameName: string;
userPage: string;
importPage: string;
scripts: UserScript[];
}
interface FlowerUserscriptCardProps {
mainGameName: string;
onClick: () => void;
}
export const FlowerUserscriptCard = ({ mainGameName, onClick }: FlowerUserscriptCardProps) => {
return (
<div className="bg-slate-800 rounded-lg border border-slate-700 p-6 hover:border-violet-500 transition-colors">
<div className="w-12 h-12 bg-blue-600/20 rounded-lg flex items-center justify-center mb-4">
<svg
className="w-6 h-6 text-blue-400"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
/>
</svg>
</div>
<h4 className="text-white font-semibold mb-2">
{mainGameName} Flower Play History (Userscript)
</h4>
<p className="text-slate-400 text-sm mb-4">
Import playdata from cabinets on the Flower network
</p>
<button
onClick={onClick}
className="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-3 sm:px-4 rounded-md text-sm sm:text-base font-medium transition-colors"
>
Export Flower Data
</button>
</div>
)
}
const FlowerUserscriptModal = ({
isOpen,
onClose,
mainGameName,
userPage,
importPage,
scripts,
}: FlowerUserscriptModalProps) => {
if (!isOpen) return null;
const handleClose = () => {
onClose();
};
if(mainGameName === undefined){
return "Sorry, due to some extreme error the game you're looking for doesn't seem to exist..."
}
return (
<div className="fixed inset-0 z-50 overflow-y-auto">
{/* Backdrop */}
<div
className="fixed inset-0 bg-black/50 backdrop-blur-sm transition-opacity"
onClick={handleClose}
/>
{/* Modal */}
<div className="flex min-h-full items-center justify-center p-4">
<div className="relative bg-slate-900 rounded-lg border border-slate-700 w-full max-w-xl p-6 shadow-xl">
{/* Header */}
<div className="mb-6">
<h3 className="text-xl font-bold text-white mb-2">
Import {mainGameName} Flower Play Data
</h3>
<p className="text-slate-400 text-sm">
Follow the instructions below to import your data
</p>
</div>
{/* Warning */}
<div className="mb-6 rounded-md bg-blue-500/10 border border-blue-500/20 p-3">
<p className="text-sm text-blue-400">
You will need your ACCESS CODE to register on Flower for the first time! <br/><br/>
Do this by tapping your Amusement IC card at any machine on the Flower network and copying
down the code displayed on the screen.
<br/><br/>
This is likely not the same code as the one on the back of your card.
</p>
</div>
{/* Instructions */}
<div className="mb-4 rounded-md bg-slate-800 border border-slate-700 p-4">
<h4 className="text-sm font-semibold text-slate-300 mb-2">
Instructions:
</h4>
<ol className="text-sm text-slate-400 space-y-1 list-decimal list-inside">
<li>
Log into the{" "}
<a
className="font-bold hover:underline"
href={userPage}>
{mainGameName} Project Flower page
</a>
</li>
<li>
Navigate to the{" "}
<a
className="font-bold hover:underline"
href={importPage}>
{mainGameName} Play History Page
</a>{" "}
</li>
<li>
Install the relevant userscript (use a browser extension such as{" "}
<a
className="font-bold hover:underline"
href="https://www.tampermonkey.net/">Tampermonkey</a>)
</li>
{/* Additional Info */}
<div className="my-2 rounded-md bg-blue-500/10 border border-blue-500/20 p-3">
<p className="text-sm text-blue-400">
{scripts.map(userscript => (
<a
href={userscript.url}
className="font-bold underline"
>
{userscript.name}
</a>
))}
</p>
</div>
<li>
A button will appear on the page that you can click to start the scraping process.
</li>
<li>
Upload the resulting JSON file into Mirage using the
Batch-Manual Upload functionality
</li>
<li>Verify that all data has been imported correctly</li>
</ol>
</div>
{/* Actions */}
<div className="flex justify-center">
<button
onClick={handleClose}
className="px-6 py-2 bg-violet-600 hover:bg-violet-700 text-white rounded-md font-medium transition-colors"
>
Got it
</button>
</div>
</div>
</div>
</div>
);
};
export default FlowerUserscriptModal;
+74 -27
View File
@@ -6,21 +6,22 @@ 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";
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';
const Import = () => {
const { user, isLoading, logout } = useAuth();
const navigate = useNavigate();
const [selectedGame, setSelectedGame] = useState("");
const [isJsonModalOpen, setIsJsonModalOpen] = useState(false);
const [isDancerushModalOpen, setIsDancerushModalOpen] = useState(false);
const [isDanceAroundModalOpen, setIsDanceAroundModalOpen] = useState(false);
const [isDivaNetModalOpen, setIsDivaNetModalOpen] = useState(false);
const [isMusicDiverModalOpen, setIsMusicDiverModalOpen] = useState(false);
const [openModal, setOpenModal] = useState<ModalType | null>(null);
const [supportedGames, setSupportedGames] = useState<SupportedGame[]>([]);
const [gamesLoading, setGamesLoading] = useState(true);
const [uploadStatus, setUploadStatus] = useState<{
@@ -120,7 +121,7 @@ const Import = () => {
Upload your game data from a Mirage compatible JSON file
</p>
<button
onClick={() => setIsJsonModalOpen(true)}
onClick={() => setOpenModal('json')}
className="w-full bg-violet-600 hover:bg-violet-700 text-white py-2 px-3 sm:px-4 rounded-md text-sm sm:text-base font-medium transition-colors"
>
Upload JSON
@@ -136,7 +137,7 @@ const Import = () => {
<JsonUploadCard />
<EamusementUserscriptCard
mainGameName="DANCERUSH"
onClick={() => setIsDancerushModalOpen(true)}
onClick={() => setOpenModal('dancerush')}
/>
</>
);
@@ -145,8 +146,8 @@ const Import = () => {
<>
<JsonUploadCard />
<EamusementUserscriptCard
mainGameName="DANCE aROUND"
onClick={() => setIsDanceAroundModalOpen(true)}
mainGameName="DANCE aROUND"
onClick={() => setOpenModal('dancearound')}
/>
</>
);
@@ -158,7 +159,7 @@ const Import = () => {
isOpen={false}
onClose={() => {}}
game={supportedGames.find((g) => g.internalName === selectedGame)}
renderAsCard={() => setIsDivaNetModalOpen(true)}
renderAsCard={() => setOpenModal('divanet')}
/>
</>
);
@@ -170,7 +171,27 @@ const Import = () => {
isOpen={false}
onClose={() => {}}
game={supportedGames.find((g) => g.internalName === selectedGame)}
renderAsCard={() => setIsMusicDiverModalOpen(true)}
renderAsCard={() => setOpenModal('musicdiver')}
/>
</>
);
case "nostalgia":
return (
<>
<JsonUploadCard />
<FlowerUserscriptCard
mainGameName="NOSTALGIA"
onClick={() => setOpenModal('nostalgia')}
/>
</>
);
case "reflecbeat":
return (
<>
<JsonUploadCard />
<FlowerUserscriptCard
mainGameName="REFLEC BEAT"
onClick={() => setOpenModal('reflecbeat')}
/>
</>
);
@@ -282,10 +303,10 @@ const Import = () => {
{/* Modals wrapped in Suspense */}
<Suspense fallback={<div>Loading...</div>}>
{isJsonModalOpen && (
{openModal === 'json' && (
<JsonUploadModal
isOpen={isJsonModalOpen}
onClose={() => setIsJsonModalOpen(false)}
isOpen={true}
onClose={() => setOpenModal(null)}
onUpload={handleJsonUpload}
game={
supportedGames.find((g) => g.internalName === selectedGame)
@@ -293,23 +314,23 @@ const Import = () => {
}
/>
)}
{isDancerushModalOpen && (
{openModal === 'dancerush' && (
<EamusementUserscriptModal
isOpen={isDancerushModalOpen}
onClose={() => setIsDancerushModalOpen(false)}
isOpen={true}
onClose={() => setOpenModal(null)}
mainGameName="DANCERUSH"
userPage="https://p.eagate.573.jp/game/dan/1st/top/entrance.html"
importPage="https://p.eagate.573.jp/game/dan/1st/top/index.html#play_his"
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/dancerush_play_history.user.js"
}]}
/>
)}
{isDanceAroundModalOpen && (
{openModal === 'dancearound' && (
<EamusementUserscriptModal
isOpen={isDanceAroundModalOpen}
onClose={() => setIsDanceAroundModalOpen(false)}
isOpen={true}
onClose={() => 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"
@@ -319,26 +340,52 @@ const Import = () => {
}]}
/>
)}
{isDivaNetModalOpen && (
{openModal === 'divanet' && (
<DivaNetModal
isOpen={isDivaNetModalOpen}
onClose={() => setIsDivaNetModalOpen(false)}
isOpen={true}
onClose={() => setOpenModal(null)}
game={
supportedGames.find((g) => g.internalName === selectedGame) ||
undefined
}
/>
)}
{isMusicDiverModalOpen && (
{openModal === 'musicdiver' && (
<MusicDiverModal
isOpen={isMusicDiverModalOpen}
onClose={() => setIsMusicDiverModalOpen(false)}
isOpen={true}
onClose={() => setOpenModal(null)}
game={
supportedGames.find((g) => g.internalName === selectedGame) ||
undefined
}
/>
)}
{openModal === 'nostalgia' && (
<FlowerUserscriptModal
isOpen={true}
onClose={() => 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/nostalgia_flower_scraper.user.js"
}]}
/>
)}
{openModal === 'reflecbeat' && (
<FlowerUserscriptModal
isOpen={true}
onClose={() => 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/reflecbeat_flower_scraper.user.js"
}]}
/>
)}
</Suspense>
</div>
);