Compare commits

..

No commits in common. "350620ca675274394916aef62e78ba4293321787" and "f4bf7ff723706d4097b29889947e07504a87dc60" have entirely different histories.

7 changed files with 32 additions and 55 deletions

View file

@ -23,7 +23,6 @@ describe("QualifyingPointsStandings", () => {
totalQualifyingPoints: "14.67",
eventsScored: 1,
finalRanking: null,
globalRank: 1,
participant: { id: "participant-1", name: "Player One" },
},
{
@ -31,7 +30,6 @@ describe("QualifyingPointsStandings", () => {
totalQualifyingPoints: "0.50",
eventsScored: 1,
finalRanking: null,
globalRank: 2,
participant: { id: "participant-2", name: "Player Two" },
},
{
@ -39,7 +37,6 @@ describe("QualifyingPointsStandings", () => {
totalQualifyingPoints: "0.43",
eventsScored: 1,
finalRanking: null,
globalRank: 3,
participant: { id: "participant-3", name: "Player Three" },
},
]}

View file

@ -82,7 +82,7 @@ describe("SportsSection", () => {
await user.click(screen.getByText("2025 MLB Season"));
expect(mlbCheckbox).toBeChecked();
}, 15000);
});
it("submits the currently selected sports seasons after toggling", async () => {
const user = userEvent.setup();

View file

@ -23,7 +23,6 @@ interface QPStanding {
totalQualifyingPoints: string;
eventsScored: number;
finalRanking: number | null;
globalRank: number;
participant: {
id: string;
name: string;
@ -71,11 +70,26 @@ export function QualifyingPointsStandings({
const ownershipMap = new Map(teamOwnerships.map((o) => [o.participantId, o]));
const userParticipantSet = new Set(userParticipantIds);
// Use the pre-computed global rank from the loader so displayed ranks reflect the full
// participant field even when the array has been filtered down to drafted/points-earning rows.
const rankedStandings: Array<QPStanding & { currentRank: number }> = standings.map(
(standing) => ({ ...standing, currentRank: standing.globalRank })
);
// Assign current ranks with tie handling
const rankedStandings: Array<QPStanding & { currentRank: number }> = [];
let previousQP = -1;
let previousRankStart = -1;
for (let index = 0; index < standings.length; index++) {
const standing = standings[index];
const currentQP = parseFloat(standing.totalQualifyingPoints);
let currentRank: number;
if (index > 0 && Math.abs(currentQP - previousQP) < 0.001) {
currentRank = previousRankStart;
} else {
currentRank = index + 1;
}
rankedStandings.push({ ...standing, currentRank });
previousRankStart = currentRank;
previousQP = currentQP;
}
const tiedCountByRank = new Map<number, number>();
for (const s of rankedStandings) {

View file

@ -64,7 +64,6 @@ interface QPStanding {
totalQualifyingPoints: string;
eventsScored: number;
finalRanking: number | null;
globalRank: number;
participant: {
id: string;
name: string;

View file

@ -25,25 +25,11 @@ export async function loader({ params }: Route.LoaderArgs) {
const events = await getScoringEventsForSportsSeason(params.id);
// For qualifying sports seasons, get QP standings with global ranks attached
// For qualifying sports seasons, get QP standings
let qpStandings = null;
const scoringRules = null;
if (sportsSeason.scoringPattern === "qualifying_points") {
const standings = await getQPStandings(params.id);
let prevQP = -1;
let prevRankStart = 1;
qpStandings = standings.map((s, index) => {
const qp = parseFloat(s.totalQualifyingPoints);
let rank: number;
if (index > 0 && Math.abs(qp - prevQP) < 0.001) {
rank = prevRankStart;
} else {
rank = index + 1;
prevRankStart = rank;
}
prevQP = qp;
return { ...s, globalRank: rank };
});
qpStandings = await getQPStandings(params.id);
}
// Tournaments for this sport that don't already have a scoring event in this season.

View file

@ -155,7 +155,7 @@ export async function loader(args: Route.LoaderArgs) {
let participantPoints: { participantId: string; points: number }[] = [];
let partialScoreParticipantIds: string[] = [];
let seasonStandings: SeasonStanding[] = [];
type QPStanding = Awaited<ReturnType<typeof getQPStandings>>[number] & { globalRank: number };
type QPStanding = Awaited<ReturnType<typeof getQPStandings>>[number];
let qpStandings: QPStanding[] = [];
// Group standings for group-stage events (e.g. FIFA World Cup)
@ -293,28 +293,9 @@ export async function loader(args: Route.LoaderArgs) {
},
}));
} else if (scoringPattern === "qualifying_points") {
// Fetch qualifying points standings
const standings = await getQPStandings(sportsSeasonId);
// Compute global ranks with tie handling across the full field before filtering,
// so the displayed rank numbers remain correct after undrafted/zero-QP rows are removed.
let prevQP = -1;
let prevRankStart = 1;
const withGlobalRank = standings.map((s, index) => {
const qp = parseFloat(s.totalQualifyingPoints);
let rank: number;
if (index > 0 && Math.abs(qp - prevQP) < 0.001) {
rank = prevRankStart;
} else {
rank = index + 1;
prevRankStart = rank;
}
prevQP = qp;
return { ...s, globalRank: rank };
});
qpStandings = withGlobalRank.filter(
(s) =>
parseFloat(s.totalQualifyingPoints) > 0 ||
ownershipMap.has(s.participant.id)
);
qpStandings = standings;
}
// Fetch event schedule for all patterns (upcoming + recent)

View file

@ -114,7 +114,7 @@ describe("WorldCupSimulator", () => {
mockDb.query.tournamentGroups.findMany.mockResolvedValue([]);
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
const sim = new WorldCupSimulator(20);
const sim = new WorldCupSimulator(100);
await expect(sim.simulate("season-1")).rejects.toThrow("No participants found");
});
@ -125,7 +125,7 @@ describe("WorldCupSimulator", () => {
mockDb.query.tournamentGroups.findMany.mockResolvedValue([]);
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
const sim = new WorldCupSimulator(20);
const sim = new WorldCupSimulator(100);
const results = await sim.simulate("season-1");
expect(results).toHaveLength(48);
const ids = new Set(results.map((r) => r.participantId));
@ -141,7 +141,7 @@ describe("WorldCupSimulator", () => {
mockDb.query.tournamentGroups.findMany.mockResolvedValue([]);
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
const sim = new WorldCupSimulator(20);
const sim = new WorldCupSimulator(100);
const results = await sim.simulate("season-1");
const sumFirst = results.reduce((s, r) => s + r.probabilities.probFirst, 0);
@ -162,7 +162,7 @@ describe("WorldCupSimulator", () => {
mockDb.query.tournamentGroups.findMany.mockResolvedValue([]);
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
const sim = new WorldCupSimulator(50);
const sim = new WorldCupSimulator(100);
const results = await sim.simulate("season-1");
// probFirst + probSecond + probThird + probFourth should cover all probability mass
@ -218,7 +218,7 @@ describe("WorldCupSimulator", () => {
mockDb.query.tournamentGroups.findMany.mockResolvedValue([group, ...remainingGroups]);
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
const sim = new WorldCupSimulator(20);
const sim = new WorldCupSimulator(100);
const results = await sim.simulate("season-1");
const p3Result = results.find((r) => r.participantId === "p3");
@ -238,7 +238,7 @@ describe("WorldCupSimulator", () => {
mockDb.query.tournamentGroups.findMany.mockResolvedValue([]);
mockDb.query.playoffMatches.findMany.mockResolvedValue([]);
const sim = new WorldCupSimulator(20);
const sim = new WorldCupSimulator(100);
const results = await sim.simulate("season-1");
for (const r of results) {