diff --git a/app/components/draft/AvailableParticipantsSection.tsx b/app/components/draft/AvailableParticipantsSection.tsx index 05addc2..53d5824 100644 --- a/app/components/draft/AvailableParticipantsSection.tsx +++ b/app/components/draft/AvailableParticipantsSection.tsx @@ -193,7 +193,7 @@ export function AvailableParticipantsSection({ {participant.sport.name} - {participant.expectedValue} + {participant.expectedValue ? participant.expectedValue.toFixed(1) : "—"} {hasTeam && ( diff --git a/app/models/draft-utils.ts b/app/models/draft-utils.ts index 91c4e27..9a202f5 100644 --- a/app/models/draft-utils.ts +++ b/app/models/draft-utils.ts @@ -294,10 +294,12 @@ export async function getTopAvailableParticipant( allParticipants.push(...seasonParticipants); } - // Sort by EV desc, then name + // Sort by EV desc, then name (expectedValue is a decimal string from DB) allParticipants.sort((a, b) => { - if (b.expectedValue !== a.expectedValue) { - return b.expectedValue - a.expectedValue; + const evA = parseFloat(String(a.expectedValue)) || 0; + const evB = parseFloat(String(b.expectedValue)) || 0; + if (evB !== evA) { + return evB - evA; } return a.name.localeCompare(b.name); }); diff --git a/app/models/participant-expected-value.ts b/app/models/participant-expected-value.ts index bb455c2..a4f3296 100644 --- a/app/models/participant-expected-value.ts +++ b/app/models/participant-expected-value.ts @@ -6,7 +6,7 @@ */ import { database } from "~/database/context"; -import { participantExpectedValues } from "~/database/schema"; +import { participantExpectedValues, participants } from "~/database/schema"; import { eq, and } from "drizzle-orm"; import type { ProbabilityDistribution, ScoringRules } from "~/services/ev-calculator"; import { calculateEV, validateProbabilities, normalizeProbabilities } from "~/services/ev-calculator"; @@ -95,6 +95,8 @@ export async function upsertParticipantEV( const now = new Date(); + let result: ParticipantEV; + if (existing.length > 0) { // Update existing const updated = await db @@ -117,7 +119,7 @@ export async function upsertParticipantEV( .where(eq(participantExpectedValues.id, existing[0].id)) .returning(); - return updated[0]; + result = updated[0]; } else { // Create new const created = await db @@ -141,8 +143,16 @@ export async function upsertParticipantEV( }) .returning(); - return created[0]; + result = created[0]; } + + // Sync calculated EV to participants table for draft room ranking + await db + .update(participants) + .set({ expectedValue: expectedValue.toString(), updatedAt: now }) + .where(eq(participants.id, participantId)); + + return result; } /** @@ -211,6 +221,12 @@ export async function deleteParticipantEV( eq(participantExpectedValues.sportsSeasonId, sportsSeasonId) ) ); + + // Reset participant EV to 0 + await db + .update(participants) + .set({ expectedValue: "0", updatedAt: new Date() }) + .where(eq(participants.id, participantId)); } /** @@ -270,16 +286,23 @@ export async function recalculateEV( const newEV = calculateEV(probabilities, newScoringRules); const db = database(); + const now = new Date(); const updated = await db .update(participantExpectedValues) .set({ expectedValue: newEV.toString(), - calculatedAt: new Date(), - updatedAt: new Date(), + calculatedAt: now, + updatedAt: now, }) .where(eq(participantExpectedValues.id, existing.id)) .returning(); + // Sync recalculated EV to participants table + await db + .update(participants) + .set({ expectedValue: newEV.toString(), updatedAt: now }) + .where(eq(participants.id, participantId)); + return updated[0]; } diff --git a/app/routes/admin.sports-seasons.$id.participants.tsx b/app/routes/admin.sports-seasons.$id.participants.tsx index e5caeaa..0b3c665 100644 --- a/app/routes/admin.sports-seasons.$id.participants.tsx +++ b/app/routes/admin.sports-seasons.$id.participants.tsx @@ -79,7 +79,7 @@ export async function action({ request, params }: Route.ActionArgs) { name, shortName: null, externalId: null, - expectedValue: 0, + expectedValue: "0", }); } @@ -103,7 +103,7 @@ export async function action({ request, params }: Route.ActionArgs) { name: name.trim(), shortName: null, externalId: null, - expectedValue: 0, + expectedValue: "0", }); return { success: true }; diff --git a/app/routes/leagues/$leagueId.draft.$seasonId.tsx b/app/routes/leagues/$leagueId.draft.$seasonId.tsx index c4bd547..5eb0700 100644 --- a/app/routes/leagues/$leagueId.draft.$seasonId.tsx +++ b/app/routes/leagues/$leagueId.draft.$seasonId.tsx @@ -144,6 +144,12 @@ export async function loader(args: any) { desc(schema.participants.expectedValue), asc(schema.participants.name) ); + + // Parse decimal expectedValue (Drizzle returns strings for decimal columns) + availableParticipants = availableParticipants.map((p: any) => ({ + ...p, + expectedValue: parseFloat(p.expectedValue) || 0, + })); } // Load user's team queue if they have a team @@ -1054,7 +1060,7 @@ export default function DraftRoom() { {participant.sport.name} - {participant.expectedValue} + {participant.expectedValue ? participant.expectedValue.toFixed(1) : "—"}