gg
Deploy Iddaai Frontend / build-and-deploy (push) Successful in 4m15s

This commit is contained in:
2026-05-06 17:50:02 +03:00
parent 4df27e3e6d
commit 6dadc5f613
4 changed files with 84 additions and 39 deletions
+51 -21
View File
@@ -49,7 +49,7 @@ interface PredictionCardProps {
function formatReasonFallback(reason: string): string {
if (reason.startsWith("risk:")) return formatReasonFallback(reason.slice(5));
const evMatch = reason.match(/^ev_edge_([+\-][\d.]+%)_grade_(\w)$/);
if (evMatch) return `Beklenen avantaj ${evMatch[1]} (Not ${evMatch[2]})`;
if (evMatch) return `Teorik avantaj sinyali: Not ${evMatch[2]}`;
const negMatch = reason.match(/^negative_model_edge_([+\-][\d.]+)$/);
if (negMatch) return `Model avantajı negatif (${negMatch[1]})`;
const thresholdMatch = reason.match(/^below_market_edge_threshold_([+\-]?[\d.]+)$/);
@@ -90,6 +90,24 @@ function formatProbability(value?: number, digits = 1): string {
return `${(value * 100).toFixed(digits)}%`;
}
function formatSignalScore(value?: number): string {
if (value === undefined || value === null || Number.isNaN(value)) return "-";
return `${Math.max(0, Math.min(100, value)).toFixed(0)}/100`;
}
function formatEdgeSignal(value?: number): string {
if (value === undefined || value === null || Number.isNaN(value)) return "-";
return `${value > 0 ? "+" : ""}${formatPercent(value * 100, 1)}`;
}
function getEdgePalette(value?: number): string {
if (value === undefined || value === null || Number.isNaN(value)) return "gray";
if (value <= 0) return "red";
if (value < 0.08) return "yellow";
if (value < 0.15) return "orange";
return "purple";
}
function formatOdds(value?: number | null): string {
if (!value || value <= 1.01) return "-";
return value.toFixed(2);
@@ -485,9 +503,8 @@ function PickCard({
<Badge colorPalette={confidenceBandPalette} variant="subtle">
{getConfidenceBandLabel(pick.confidence_interval?.band)}
</Badge>
<Badge colorPalette={pick.ev_edge > 0 ? "green" : "red"} variant="subtle">
EV {pick.ev_edge > 0 ? "+" : ""}
{formatPercent(pick.ev_edge * 100, 1)}
<Badge colorPalette={getEdgePalette(pick.ev_edge)} variant="subtle">
Teorik avantaj {formatEdgeSignal(pick.ev_edge)}
</Badge>
</HStack>
</VStack>
@@ -498,7 +515,7 @@ function PickCard({
label={labels.recommendedStake}
value={formatUnits(pick.stake_units || stakeFallback)}
/>
<MetricTile label={labels.playScore} value={formatPercent(pick.play_score, 0)} />
<MetricTile label={labels.playScore} value={formatSignalScore(pick.play_score)} />
<MetricTile label="Guven Araligi" value={formatInterval(pick.confidence_interval)} />
<MetricTile
label="Band"
@@ -514,12 +531,12 @@ function PickCard({
{labels.playability}
</Text>
<Text fontSize="sm" color="fg.muted">
{formatPercent(pick.play_score, 1)}
{formatSignalScore(pick.play_score)}
</Text>
</HStack>
<Bar
value={pick.play_score}
color={pick.ev_edge > 0 ? "green.400" : "orange.400"}
color={pick.ev_edge > 0 ? "blue.400" : "orange.400"}
trackBg={trackBg}
/>
</Box>
@@ -596,9 +613,8 @@ function SummaryTable({
</HStack>
<HStack gap={5} fontSize="sm">
<Text minW="48px">{formatOdds(item.odds)}</Text>
<Text minW="68px" color={item.ev_edge > 0 ? "green.500" : "red.500"} fontWeight="semibold">
{item.ev_edge > 0 ? "+" : ""}
{formatPercent(item.ev_edge * 100, 1)}
<Text minW="96px" color={`${getEdgePalette(item.ev_edge)}.500`} fontWeight="semibold">
{formatEdgeSignal(item.ev_edge)}
</Text>
<Text minW="48px">{formatPercent(item.calibrated_confidence, 0)}</Text>
<Badge colorPalette={getConfidenceBandPalette(item.confidence_interval?.band)} variant="subtle">
@@ -867,9 +883,23 @@ export default function PredictionCard({ prediction }: PredictionCardProps) {
title={uiText("summary-title", "Tahmin Ozeti")}
info={uiText(
"summary-info",
"Kullanicinin once neyi oynayacagini, sonra nedenini anlamasi icin sade ozet.",
"Model sinyallerini ve belirsizlikleri sade sekilde gosterir.",
)}
/>
<Box
p={3}
bg={useColorModeValue("orange.50", "orange.950")}
borderWidth="1px"
borderColor={useColorModeValue("orange.200", "orange.800")}
borderRadius="xl"
>
<HStack align="start" gap={2}>
<Icon as={LuShieldAlert} boxSize={4.5} color="orange.500" mt={0.5} />
<Text fontSize="sm" color="fg.muted" lineHeight="tall">
Bu bir model sinyalidir; kesin sonuç, garanti veya tutma yüzdesi değildir. Sinyal puanı maç içi varyans, kadro ve veri kalitesi nedeniyle yanılabilir.
</Text>
</HStack>
</Box>
{recommendedPick ? (
<Grid templateColumns={{ base: "1fr", xl: "1.4fr 1fr" }} gap={4}>
@@ -877,7 +907,7 @@ export default function PredictionCard({ prediction }: PredictionCardProps) {
<HStack justify="space-between" align="start" mb={4}>
<VStack align="start" gap={2}>
<Badge colorPalette="green" variant="solid" borderRadius="full">
{uiText("main-recommendation", "Ana Oneri")}
{uiText("main-recommendation", "Öne Çıkan Sinyal")}
</Badge>
<Text fontSize="2xl" fontWeight="bold">
{recommendedPick.pick}
@@ -903,13 +933,13 @@ export default function PredictionCard({ prediction }: PredictionCardProps) {
<MetricTile label={uiText("odds-label", "Oran")} value={formatOdds(recommendedPick.odds)} />
<MetricTile label="Guven Araligi" value={formatInterval(recommendedPick.confidence_interval)} />
<MetricTile
label={uiText("edge-label", "Beklenen Avantaj (Edge)")}
value={`${recommendedPick.ev_edge > 0 ? "+" : ""}${formatPercent(recommendedPick.ev_edge * 100, 1)}`}
label={uiText("edge-label", "Teorik Avantaj")}
value={formatEdgeSignal(recommendedPick.ev_edge)}
helper={uiText(
"edge-info",
"Edge, model olasiligi ile piyasa olasiligi arasindaki farktir. Pozitifse model bu orani avantajli buluyor demektir.",
"Model olasiligi ile piyasa olasiligi arasindaki teorik farktir; tutma garantisi veya kesin kazanc beklentisi degildir.",
)}
accent={recommendedPick.ev_edge > 0 ? "green.500" : "red.500"}
accent={`${getEdgePalette(recommendedPick.ev_edge)}.500`}
/>
<MetricTile
label={uiText("stake-label", "Onerilen Miktar (Stake)")}
@@ -1028,7 +1058,7 @@ export default function PredictionCard({ prediction }: PredictionCardProps) {
{recommendedPick ? (
<PickCard
pick={recommendedPick}
title={uiText("best-single-pick", "En iyi tekli secim")}
title={uiText("best-single-pick", "En güçlü sinyal")}
resolveReason={resolveReason}
palette="green"
stakeFallback={prediction.bet_advice.suggested_stake_units}
@@ -1037,8 +1067,8 @@ export default function PredictionCard({ prediction }: PredictionCardProps) {
confidence: uiText("confidence-label", "Guven"),
odds: uiText("odds-label", "Oran"),
recommendedStake: uiText("stake-label-short", "Stake"),
playScore: uiText("play-score-label", "Play Score"),
playability: uiText("playability-label", "Oynanabilirlik"),
playScore: uiText("play-score-label", "Model Sinyali"),
playability: uiText("playability-label", "Model sinyali"),
}}
/>
) : null}
@@ -1064,8 +1094,8 @@ export default function PredictionCard({ prediction }: PredictionCardProps) {
confidence: uiText("confidence-label", "Guven"),
odds: uiText("odds-label", "Oran"),
recommendedStake: uiText("stake-label-short", "Stake"),
playScore: uiText("play-score-label", "Play Score"),
playability: uiText("playability-label", "Oynanabilirlik"),
playScore: uiText("play-score-label", "Model Sinyali"),
playability: uiText("playability-label", "Model sinyali"),
}}
/>
))}