gg
Deploy Iddaai Frontend / build-and-deploy (push) Successful in 2m36s

This commit is contained in:
2026-06-01 01:09:04 +03:00
parent 5df5145104
commit 9540ff9d2e
4 changed files with 47 additions and 15 deletions
@@ -373,6 +373,7 @@ export default function DashboardContent() {
borderRadius="full" borderRadius="full"
> >
{Math.round( {Math.round(
pred.main_pick.unified_score ??
pred.main_pick.calibrated_confidence ?? pred.main_pick.calibrated_confidence ??
pred.main_pick.confidence, pred.main_pick.confidence,
)} )}
+37 -13
View File
@@ -643,7 +643,7 @@ function PickCard({
<SimpleGrid columns={2} gap={3} minW={{ base: "full", md: "320px" }}> <SimpleGrid columns={2} gap={3} minW={{ base: "full", md: "320px" }}>
<MetricTile <MetricTile
label={labels.confidence} label={labels.confidence}
value={formatPercent(pick.calibrated_confidence, 0)} value={formatPercent(pick.unified_score ?? pick.calibrated_confidence, 0)}
/> />
<MetricTile label={labels.odds} value={formatOdds(pick.odds)} /> <MetricTile label={labels.odds} value={formatOdds(pick.odds)} />
<MetricTile <MetricTile
@@ -742,7 +742,7 @@ function SummaryTable({
right.signal_tier || "PASS", right.signal_tier || "PASS",
); );
if (leftIndex !== rightIndex) return leftIndex - rightIndex; if (leftIndex !== rightIndex) return leftIndex - rightIndex;
return right.calibrated_confidence - left.calibrated_confidence; return (right.unified_score ?? right.calibrated_confidence) - (left.unified_score ?? left.calibrated_confidence);
}) })
.map((item) => ( .map((item) => (
<Flex <Flex
@@ -778,6 +778,13 @@ function SummaryTable({
</HStack> </HStack>
<HStack gap={5} fontSize="sm"> <HStack gap={5} fontSize="sm">
<Text minW="48px">{formatOdds(item.odds)}</Text> <Text minW="48px">{formatOdds(item.odds)}</Text>
<Text minW="64px" color="fg.muted">
{formatPercent(
(item.model_probability ?? 0) * 100,
0,
)}{" "}
{getUiText(ui, "probability-short", "olasılık")}
</Text>
<Text <Text
minW="96px" minW="96px"
color={`${getEdgePalette(item.ev_edge)}.500`} color={`${getEdgePalette(item.ev_edge)}.500`}
@@ -786,7 +793,7 @@ function SummaryTable({
{formatEdgeSignal(item.ev_edge)} {formatEdgeSignal(item.ev_edge)}
</Text> </Text>
<Text minW="48px"> <Text minW="48px">
{formatPercent(item.calibrated_confidence, 0)} {formatPercent(item.unified_score ?? item.calibrated_confidence, 0)}
</Text> </Text>
<Badge <Badge
colorPalette={getConfidenceBandPalette( colorPalette={getConfidenceBandPalette(
@@ -881,9 +888,18 @@ function MarketBoardSection({
if (!marketBoard || !Object.keys(marketBoard).length) return null; if (!marketBoard || !Object.keys(marketBoard).length) return null;
// Key by market:pick so each card resolves the summary row for the EXACT
// outcome it displays (graph pick). Keying by market alone collided on
// multi-row markets (MS 1/X/2) and surfaced the wrong odds + confidence.
const summaryByMarket = new Map( const summaryByMarket = new Map(
(betSummary || []).map((item) => [item.market, item]), (betSummary || []).map((item) => [`${item.market}:${item.pick}`, item]),
); );
// Fallback: first row of a market, for cards whose pick has no exact row.
for (const item of betSummary || []) {
if (!summaryByMarket.has(item.market)) {
summaryByMarket.set(item.market, item);
}
}
const orderedEntries = Object.entries(marketBoard).sort(([left], [right]) => { const orderedEntries = Object.entries(marketBoard).sort(([left], [right]) => {
const leftIndex = MARKET_ORDER.indexOf(left); const leftIndex = MARKET_ORDER.indexOf(left);
const rightIndex = MARKET_ORDER.indexOf(right); const rightIndex = MARKET_ORDER.indexOf(right);
@@ -899,9 +915,18 @@ function MarketBoardSection({
<SimpleGrid columns={{ base: 1, xl: 2 }} gap={4}> <SimpleGrid columns={{ base: 1, xl: 2 }} gap={4}>
{orderedEntries.map(([market, entry]) => { {orderedEntries.map(([market, entry]) => {
if (!entry?.probs) return null; if (!entry?.probs) return null;
const summary = summaryByMarket.get(market); const summary =
summaryByMarket.get(`${market}:${entry.pick}`) ??
summaryByMarket.get(market);
const interval = const interval =
summary?.confidence_interval || entry.confidence_interval; summary?.confidence_interval || entry.confidence_interval;
// Hit probability == the dominant (green) bar in the graph below,
// so the headline never contradicts the distribution it sits on.
const pickProbPct =
Math.max(
0,
...Object.values(entry.probs).map((p) => Number(p) || 0),
) * 100;
return ( return (
<Box <Box
key={market} key={market}
@@ -951,25 +976,25 @@ function MarketBoardSection({
variant="subtle" variant="subtle"
borderRadius="full" borderRadius="full"
> >
{entry.pick} ({formatPercent(entry.confidence, 0)}) {entry.pick} ({formatPercent(pickProbPct, 0)})
</Badge> </Badge>
) : null} ) : null}
</Flex> </Flex>
<SimpleGrid columns={3} gap={2} mb={3}> <SimpleGrid columns={3} gap={2} mb={3}>
<MetricTile <MetricTile
label={getUiText(ui, "hit-probability", "Tutma Olasılığı")} label={getUiText(ui, "hit-probability", "Tutma Olasılığı")}
value={formatPercent(entry.confidence, 0)} value={formatPercent(pickProbPct, 0)}
accent="green.500" accent="green.500"
/> />
<MetricTile <MetricTile
label={getUiText( label={getUiText(
ui, ui,
"calibrated-confidence", "unified-confidence",
"Kalibre Güven", "Güven Skoru",
)} )}
value={ value={
summary summary
? formatPercent(summary.calibrated_confidence, 0) ? formatPercent(summary.unified_score ?? summary.calibrated_confidence, 0)
: "-" : "-"
} }
accent={summary?.playable ? "green.500" : "orange.500"} accent={summary?.playable ? "green.500" : "orange.500"}
@@ -999,8 +1024,7 @@ function MarketBoardSection({
<Bar <Bar
value={probability * 100} value={probability * 100}
color={ color={
entry.pick === outcome || (Number(probability) || 0) * 100 >= pickProbPct - 1e-6
entry.pick?.toUpperCase() === outcome.toUpperCase()
? "green.400" ? "green.400"
: "blue.400" : "blue.400"
} }
@@ -1349,7 +1373,7 @@ export default function PredictionCard({ prediction }: PredictionCardProps) {
<MetricTile <MetricTile
label={uiText("confidence-label", "Güven")} label={uiText("confidence-label", "Güven")}
value={formatPercent( value={formatPercent(
recommendedPick.calibrated_confidence, recommendedPick.unified_score ?? recommendedPick.calibrated_confidence,
0, 0,
)} )}
/> />
@@ -193,6 +193,7 @@ export default function PredictionsContent() {
color="primary.fg" color="primary.fg"
> >
{Math.round( {Math.round(
pred.main_pick.unified_score ??
pred.main_pick.calibrated_confidence ?? pred.main_pick.calibrated_confidence ??
pred.main_pick.confidence, pred.main_pick.confidence,
)} )}
+6
View File
@@ -90,6 +90,8 @@ export interface EngineBreakdownDto {
export type BetGrade = "A" | "B" | "C" | "PASS"; export type BetGrade = "A" | "B" | "C" | "PASS";
export type SignalTier = "CORE" | "VALUE" | "LEAN" | "LONGSHOT" | "PASS"; export type SignalTier = "CORE" | "VALUE" | "LEAN" | "LONGSHOT" | "PASS";
export type UnifiedScoreLabel = "very_reliable" | "reliable" | "moderate" | "low";
export interface MatchPickDto { export interface MatchPickDto {
market: string; market: string;
pick: string; pick: string;
@@ -98,6 +100,8 @@ export interface MatchPickDto {
odds: number; odds: number;
raw_confidence: number; raw_confidence: number;
calibrated_confidence: number; calibrated_confidence: number;
unified_score?: number;
unified_score_label?: UnifiedScoreLabel;
min_required_confidence: number; min_required_confidence: number;
edge: number; edge: number;
ev_edge: number; ev_edge: number;
@@ -158,6 +162,8 @@ export interface MatchBetSummaryItemDto {
pick: string; pick: string;
raw_confidence: number; raw_confidence: number;
calibrated_confidence: number; calibrated_confidence: number;
unified_score?: number;
unified_score_label?: UnifiedScoreLabel;
bet_grade: BetGrade; bet_grade: BetGrade;
playable: boolean; playable: boolean;
stake_units: number; stake_units: number;