2025-10-12 21:54:49 -07:00
|
|
|
import { useState } from "react";
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
import { useFetcher } from "react-router";
|
2025-10-12 21:54:49 -07:00
|
|
|
import type { Route } from "./+types/admin.data-sync";
|
2026-03-10 12:10:52 -07:00
|
|
|
|
2025-10-12 21:54:49 -07:00
|
|
|
import { findAllSports } from "~/models/sport";
|
|
|
|
|
import { findAllSportsSeasons } from "~/models/sports-season";
|
|
|
|
|
import { findAllSeasonTemplates } from "~/models/season-template";
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
import { countAllParticipants } from "~/models/participant";
|
|
|
|
|
import { countAllParticipantEVs } from "~/models/participant-expected-value";
|
2025-10-12 21:54:49 -07:00
|
|
|
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";
|
|
|
|
|
|
2026-03-10 12:10:52 -07:00
|
|
|
export function meta(): Route.MetaDescriptors {
|
|
|
|
|
return [{ title: "Data Sync - Brackt Admin" }];
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-12 21:54:49 -07:00
|
|
|
export async function loader() {
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
const [sports, sportsSeasons, templates, participantCount, evCount] =
|
|
|
|
|
await Promise.all([
|
|
|
|
|
findAllSports(),
|
|
|
|
|
findAllSportsSeasons(),
|
|
|
|
|
findAllSeasonTemplates(),
|
|
|
|
|
countAllParticipants(),
|
|
|
|
|
countAllParticipantEVs(),
|
|
|
|
|
]);
|
2025-10-12 21:54:49 -07:00
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
stats: {
|
|
|
|
|
sportsCount: sports.length,
|
|
|
|
|
sportsSeasonsCount: sportsSeasons.length,
|
|
|
|
|
templatesCount: templates.length,
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
participantCount,
|
|
|
|
|
evCount,
|
2025-10-12 21:54:49 -07:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function action({ request }: Route.ActionArgs) {
|
|
|
|
|
const formData = await request.formData();
|
|
|
|
|
const intent = formData.get("intent");
|
|
|
|
|
|
|
|
|
|
if (intent === "import") {
|
|
|
|
|
const mode = formData.get("mode") as "merge" | "replace";
|
|
|
|
|
const fileData = formData.get("fileData") as string;
|
|
|
|
|
|
|
|
|
|
if (!fileData) {
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
return { success: false, error: "No file data provided" };
|
2025-10-12 21:54:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
const { importSportsDataFromJSON } = await import(
|
|
|
|
|
"~/utils/sports-data-sync.server"
|
|
|
|
|
);
|
2025-10-12 21:54:49 -07:00
|
|
|
const result = await importSportsDataFromJSON(fileData, mode);
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
return {
|
|
|
|
|
success: true,
|
|
|
|
|
error: null,
|
|
|
|
|
message: `Import complete! Created: ${result.created}, Updated: ${result.updated}, Skipped: ${result.skipped}`,
|
2025-10-12 21:54:49 -07:00
|
|
|
};
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Import error:", error);
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: error instanceof Error ? error.message : "Import failed",
|
|
|
|
|
message: null,
|
|
|
|
|
};
|
2025-10-12 21:54:49 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
return { success: false, error: "Invalid intent", message: null };
|
2025-10-12 21:54:49 -07:00
|
|
|
}
|
|
|
|
|
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
export default function DataSync({ loaderData }: Route.ComponentProps) {
|
2025-10-12 21:54:49 -07:00
|
|
|
const { stats } = loaderData;
|
|
|
|
|
const [importMode, setImportMode] = useState<"merge" | "replace">("merge");
|
|
|
|
|
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
|
|
|
|
const [isExporting, setIsExporting] = useState(false);
|
|
|
|
|
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
const fetcher = useFetcher<typeof action>();
|
|
|
|
|
const isImporting = fetcher.state !== "idle";
|
|
|
|
|
|
2025-10-12 21:54:49 -07:00
|
|
|
const handleExport = async () => {
|
|
|
|
|
setIsExporting(true);
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch("/api/admin/export-sports-data");
|
|
|
|
|
const data = await response.json();
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
|
|
|
|
|
const blob = new Blob([JSON.stringify(data, null, 2)], {
|
|
|
|
|
type: "application/json",
|
|
|
|
|
});
|
2025-10-12 21:54:49 -07:00
|
|
|
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<HTMLInputElement>) => {
|
|
|
|
|
const file = e.target.files?.[0];
|
|
|
|
|
if (file) {
|
|
|
|
|
setSelectedFile(file);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
const handleImport = (e: React.FormEvent) => {
|
2025-10-12 21:54:49 -07:00
|
|
|
e.preventDefault();
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
|
2025-10-12 21:54:49 -07:00
|
|
|
if (!selectedFile) {
|
|
|
|
|
alert("Please select a file first");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const reader = new FileReader();
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
reader.onload = (event) => {
|
2025-10-12 21:54:49 -07:00
|
|
|
const fileData = event.target?.result as string;
|
|
|
|
|
const formData = new FormData();
|
|
|
|
|
formData.append("intent", "import");
|
|
|
|
|
formData.append("mode", importMode);
|
|
|
|
|
formData.append("fileData", fileData);
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
fetcher.submit(formData, { method: "POST" });
|
2025-10-12 21:54:49 -07:00
|
|
|
};
|
|
|
|
|
reader.readAsText(selectedFile);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="p-8">
|
|
|
|
|
<div className="max-w-4xl">
|
|
|
|
|
<div className="mb-6">
|
|
|
|
|
<h1 className="text-3xl font-bold">Data Sync</h1>
|
|
|
|
|
<p className="text-muted-foreground mt-1">
|
|
|
|
|
Import and export sports data between environments
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
{fetcher.data?.success && (
|
2026-02-20 19:26:11 -08:00
|
|
|
<div className="bg-emerald-500/15 text-emerald-400 px-4 py-3 rounded-md text-sm mb-6">
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
{fetcher.data.message || "Operation completed successfully!"}
|
2025-10-12 21:54:49 -07:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
{fetcher.data?.error && (
|
2025-10-12 21:54:49 -07:00
|
|
|
<div className="bg-destructive/15 text-destructive px-4 py-3 rounded-md text-sm mb-6">
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
{fetcher.data.error}
|
2025-10-12 21:54:49 -07:00
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
<div className="grid gap-6 md:grid-cols-5 mb-6">
|
2025-10-12 21:54:49 -07:00
|
|
|
<Card>
|
|
|
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
|
|
|
<CardTitle className="text-sm font-medium">Sports</CardTitle>
|
|
|
|
|
<Database className="h-4 w-4 text-muted-foreground" />
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<div className="text-2xl font-bold">{stats.sportsCount}</div>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
<CardTitle className="text-sm font-medium">
|
|
|
|
|
Sports Seasons
|
|
|
|
|
</CardTitle>
|
|
|
|
|
<Database className="h-4 w-4 text-muted-foreground" />
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<div className="text-2xl font-bold">
|
|
|
|
|
{stats.sportsSeasonsCount}
|
|
|
|
|
</div>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
|
|
|
<CardTitle className="text-sm font-medium">
|
|
|
|
|
Participants
|
|
|
|
|
</CardTitle>
|
2025-10-12 21:54:49 -07:00
|
|
|
<Database className="h-4 w-4 text-muted-foreground" />
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
<div className="text-2xl font-bold">
|
|
|
|
|
{stats.participantCount}
|
|
|
|
|
</div>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
|
|
|
<CardTitle className="text-sm font-medium">EV Records</CardTitle>
|
|
|
|
|
<Database className="h-4 w-4 text-muted-foreground" />
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<div className="text-2xl font-bold">{stats.evCount}</div>
|
2025-10-12 21:54:49 -07:00
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
|
|
|
<CardTitle className="text-sm font-medium">Templates</CardTitle>
|
|
|
|
|
<Database className="h-4 w-4 text-muted-foreground" />
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<div className="text-2xl font-bold">{stats.templatesCount}</div>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="grid gap-6 md:grid-cols-2">
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>Export Data</CardTitle>
|
|
|
|
|
<CardDescription>
|
|
|
|
|
Download all sports data as a JSON file
|
|
|
|
|
</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent className="space-y-4">
|
|
|
|
|
<p className="text-sm text-muted-foreground">
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
This will export all sports, sports seasons, participants,
|
|
|
|
|
participant EV/futures data, participant results, and season
|
|
|
|
|
templates.
|
2025-10-12 21:54:49 -07:00
|
|
|
</p>
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
|
2025-10-12 21:54:49 -07:00
|
|
|
<Button
|
|
|
|
|
onClick={handleExport}
|
|
|
|
|
disabled={isExporting}
|
|
|
|
|
className="w-full"
|
|
|
|
|
>
|
|
|
|
|
<Download className="mr-2 h-4 w-4" />
|
|
|
|
|
{isExporting ? "Exporting..." : "Export Data"}
|
|
|
|
|
</Button>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>Import Data</CardTitle>
|
|
|
|
|
<CardDescription>
|
|
|
|
|
Upload a JSON file to import sports data
|
|
|
|
|
</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent className="space-y-4">
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
<Label htmlFor="file">Select File</Label>
|
|
|
|
|
<input
|
|
|
|
|
id="file"
|
|
|
|
|
type="file"
|
|
|
|
|
accept=".json"
|
|
|
|
|
onChange={handleFileChange}
|
|
|
|
|
className="block w-full text-sm text-muted-foreground
|
|
|
|
|
file:mr-4 file:py-2 file:px-4
|
|
|
|
|
file:rounded-md file:border-0
|
|
|
|
|
file:text-sm file:font-semibold
|
|
|
|
|
file:bg-primary file:text-primary-foreground
|
|
|
|
|
hover:file:bg-primary/90"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
<Label>Import Mode</Label>
|
|
|
|
|
<div className="flex gap-2">
|
|
|
|
|
<Button
|
|
|
|
|
type="button"
|
|
|
|
|
variant={importMode === "merge" ? "default" : "outline"}
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => setImportMode("merge")}
|
|
|
|
|
>
|
|
|
|
|
Merge
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
type="button"
|
|
|
|
|
variant={importMode === "replace" ? "default" : "outline"}
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => setImportMode("replace")}
|
|
|
|
|
>
|
|
|
|
|
Replace
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
|
|
|
{importMode === "merge"
|
|
|
|
|
? "Updates existing records and adds new ones (safe)"
|
|
|
|
|
: "Deletes all existing data and recreates from file (destructive)"}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{importMode === "replace" ? (
|
|
|
|
|
<AlertDialog>
|
|
|
|
|
<AlertDialogTrigger asChild>
|
|
|
|
|
<Button
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
disabled={!selectedFile || isImporting}
|
2025-10-12 21:54:49 -07:00
|
|
|
variant="destructive"
|
|
|
|
|
className="w-full"
|
|
|
|
|
>
|
|
|
|
|
<Upload className="mr-2 h-4 w-4" />
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
{isImporting
|
|
|
|
|
? "Importing..."
|
|
|
|
|
: "Import Data (Replace)"}
|
2025-10-12 21:54:49 -07:00
|
|
|
</Button>
|
|
|
|
|
</AlertDialogTrigger>
|
|
|
|
|
<AlertDialogContent>
|
|
|
|
|
<AlertDialogHeader>
|
|
|
|
|
<AlertDialogTitle className="flex items-center gap-2">
|
|
|
|
|
<AlertTriangle className="h-5 w-5 text-destructive" />
|
|
|
|
|
Are you absolutely sure?
|
|
|
|
|
</AlertDialogTitle>
|
|
|
|
|
<AlertDialogDescription>
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
This will{" "}
|
|
|
|
|
<strong>delete all existing sports data</strong> and
|
|
|
|
|
replace it with the data from the file. This action
|
|
|
|
|
cannot be undone.
|
|
|
|
|
<br />
|
|
|
|
|
<br />
|
2025-10-12 21:54:49 -07:00
|
|
|
This includes:
|
|
|
|
|
<ul className="list-disc list-inside mt-2">
|
|
|
|
|
<li>All sports</li>
|
|
|
|
|
<li>All sports seasons</li>
|
|
|
|
|
<li>All participants</li>
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
<li>All participant EV/futures data</li>
|
|
|
|
|
<li>All participant results</li>
|
2025-10-12 21:54:49 -07:00
|
|
|
<li>All season templates</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</AlertDialogDescription>
|
|
|
|
|
</AlertDialogHeader>
|
|
|
|
|
<AlertDialogFooter>
|
|
|
|
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
|
|
|
<AlertDialogAction
|
|
|
|
|
onClick={handleImport}
|
|
|
|
|
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
|
|
|
>
|
|
|
|
|
Yes, Replace All Data
|
|
|
|
|
</AlertDialogAction>
|
|
|
|
|
</AlertDialogFooter>
|
|
|
|
|
</AlertDialogContent>
|
|
|
|
|
</AlertDialog>
|
|
|
|
|
) : (
|
|
|
|
|
<Button
|
|
|
|
|
onClick={handleImport}
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
disabled={!selectedFile || isImporting}
|
2025-10-12 21:54:49 -07:00
|
|
|
className="w-full"
|
|
|
|
|
>
|
|
|
|
|
<Upload className="mr-2 h-4 w-4" />
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
{isImporting ? "Importing..." : "Import Data (Merge)"}
|
2025-10-12 21:54:49 -07:00
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-02-20 19:26:11 -08:00
|
|
|
<Card className="mt-6 border-electric/30 bg-electric/10">
|
2025-10-12 21:54:49 -07:00
|
|
|
<CardHeader>
|
2026-02-20 19:26:11 -08:00
|
|
|
<CardTitle className="text-electric">How It Works</CardTitle>
|
2025-10-12 21:54:49 -07:00
|
|
|
</CardHeader>
|
2026-02-20 19:26:11 -08:00
|
|
|
<CardContent className="text-sm text-foreground/80 space-y-3">
|
2025-10-12 21:54:49 -07:00
|
|
|
<div>
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
<strong>Export:</strong> Downloads all sports data as a JSON file
|
|
|
|
|
that you can save, version control, or transfer to another
|
|
|
|
|
environment.
|
2025-10-12 21:54:49 -07:00
|
|
|
</div>
|
|
|
|
|
<div>
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
<strong>Import (Merge):</strong> Updates existing records and adds
|
|
|
|
|
new ones. Safe to use — won't delete anything.
|
2025-10-12 21:54:49 -07:00
|
|
|
</div>
|
|
|
|
|
<div>
|
Add Zod validation and expand export/import for EV and results data (#14)
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
|
|
|
<strong>Import (Replace):</strong> Deletes all existing sports
|
|
|
|
|
data and recreates it from the file. Use this for a clean slate.
|
2025-10-12 21:54:49 -07:00
|
|
|
</div>
|
2026-02-20 19:26:11 -08:00
|
|
|
<div className="pt-2 border-t border-electric/30">
|
2025-10-12 21:54:49 -07:00
|
|
|
<strong>Typical workflow:</strong>
|
|
|
|
|
<ol className="list-decimal list-inside mt-1 space-y-1">
|
|
|
|
|
<li>Export data from development</li>
|
|
|
|
|
<li>Download the JSON file</li>
|
|
|
|
|
<li>Upload it to production</li>
|
|
|
|
|
<li>Import using merge mode</li>
|
|
|
|
|
</ol>
|
|
|
|
|
</div>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|