import { useState } from "react"; import type { Route } from "./+types/admin.data-sync"; import { findAllSports } from "~/models/sport"; import { findAllSportsSeasons } from "~/models/sports-season"; import { findAllSeasonTemplates } from "~/models/season-template"; import { Button } from "~/components/ui/button"; import { Label } from "~/components/ui/label"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "~/components/ui/card"; import { Download, Upload, AlertTriangle, Database } from "lucide-react"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "~/components/ui/alert-dialog"; export async function loader() { const [sports, sportsSeasons, templates] = await Promise.all([ findAllSports(), findAllSportsSeasons(), findAllSeasonTemplates(), ]); return { stats: { sportsCount: sports.length, sportsSeasonsCount: sportsSeasons.length, templatesCount: templates.length, }, }; } export async function action({ request }: Route.ActionArgs) { const formData = await request.formData(); const intent = formData.get("intent"); if (intent === "export") { // Export will be handled client-side via download return { success: true }; } if (intent === "import") { const mode = formData.get("mode") as "merge" | "replace"; const fileData = formData.get("fileData") as string; if (!fileData) { return { error: "No file data provided" }; } try { // Import the data (we'll create a server-side function for this) const { importSportsDataFromJSON } = await import("~/utils/sports-data-sync.server"); const result = await importSportsDataFromJSON(fileData, mode); return { success: true, message: `Import complete! Created: ${result.created}, Updated: ${result.updated}, Skipped: ${result.skipped}` }; } catch (error) { console.error("Import error:", error); return { error: error instanceof Error ? error.message : "Import failed" }; } } return { error: "Invalid intent" }; } export default function DataSync({ loaderData, actionData }: Route.ComponentProps) { const { stats } = loaderData; const [importMode, setImportMode] = useState<"merge" | "replace">("merge"); const [selectedFile, setSelectedFile] = useState(null); const [isExporting, setIsExporting] = useState(false); const handleExport = async () => { setIsExporting(true); try { const response = await fetch("/api/admin/export-sports-data"); const data = await response.json(); // Create download const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `sports-data-export-${new Date().toISOString().split("T")[0]}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } catch (error) { console.error("Export failed:", error); alert("Export failed. Please try again."); } finally { setIsExporting(false); } }; const handleFileChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { setSelectedFile(file); } }; const handleImport = async (e: React.FormEvent) => { e.preventDefault(); if (!selectedFile) { alert("Please select a file first"); return; } const reader = new FileReader(); reader.onload = async (event) => { const fileData = event.target?.result as string; const formData = new FormData(); formData.append("intent", "import"); formData.append("mode", importMode); formData.append("fileData", fileData); // Submit via form const form = document.createElement("form"); form.method = "POST"; form.style.display = "none"; const intentInput = document.createElement("input"); intentInput.name = "intent"; intentInput.value = "import"; form.appendChild(intentInput); const modeInput = document.createElement("input"); modeInput.name = "mode"; modeInput.value = importMode; form.appendChild(modeInput); const dataInput = document.createElement("input"); dataInput.name = "fileData"; dataInput.value = fileData; form.appendChild(dataInput); document.body.appendChild(form); form.submit(); }; reader.readAsText(selectedFile); }; return (

Data Sync

Import and export sports data between environments

{actionData?.success && (
{actionData.message || "Operation completed successfully!"}
)} {actionData?.error && (
{actionData.error}
)}
Sports
{stats.sportsCount}
Sports Seasons
{stats.sportsSeasonsCount}
Templates
{stats.templatesCount}
Export Data Download all sports data as a JSON file

This will export all sports, sports seasons, participants, and season templates.

Import Data Upload a JSON file to import sports data

{importMode === "merge" ? "Updates existing records and adds new ones (safe)" : "Deletes all existing data and recreates from file (destructive)"}

{importMode === "replace" ? ( Are you absolutely sure? This will delete all existing sports data and replace it with the data from the file. This action cannot be undone.

This includes:
  • All sports
  • All sports seasons
  • All participants
  • All season templates
Cancel Yes, Replace All Data
) : ( )}
How It Works
Export: Downloads all sports data as a JSON file that you can save, version control, or transfer to another environment.
Import (Merge): Updates existing records and adds new ones. Safe to use - won't delete anything.
Import (Replace): Deletes all existing sports data and recreates it from the file. Use this for a clean slate.
Typical workflow:
  1. Export data from development
  2. Download the JSON file
  3. Upload it to production
  4. Import using merge mode
); }