first
Deploy Iddaai Frontend / build-and-deploy (push) Successful in 4m0s

This commit is contained in:
2026-04-16 13:36:34 +03:00
parent de5e145c4e
commit fc7a1ba567
218 changed files with 32370 additions and 0 deletions
+85
View File
@@ -0,0 +1,85 @@
import { apiRequest } from "@/lib/api/api-service";
import type { SportType } from "@/lib/api/matches/types";
import { ApiResponse } from "@/types/api-response";
import {
MatchPredictionDto,
UpcomingPredictionsDto,
ValueBetDto,
PredictionHistoryResponseDto,
AIHealthDto,
SmartCouponRequestDto,
SmartCouponResponseDto,
} from "./types";
/**
* Predictions Service
* Backend: /api/predictions/*
*/
const getPrediction = (matchId: string) => {
return apiRequest<ApiResponse<MatchPredictionDto>>({
url: `/predictions/${matchId}`,
client: "core",
method: "get",
});
};
const generatePrediction = (body: { matchId: string; sport?: SportType }) => {
return apiRequest<ApiResponse<MatchPredictionDto>>({
url: "/predictions/generate",
client: "core",
method: "post",
data: body,
});
};
const getUpcoming = () => {
return apiRequest<ApiResponse<UpcomingPredictionsDto>>({
url: "/predictions/upcoming",
client: "core",
method: "get",
});
};
const getValueBets = () => {
return apiRequest<ApiResponse<ValueBetDto[]>>({
url: "/predictions/value-bets",
client: "core",
method: "get",
});
};
const getHistory = () => {
return apiRequest<ApiResponse<PredictionHistoryResponseDto>>({
url: "/predictions/history",
client: "core",
method: "get",
});
};
const checkHealth = () => {
return apiRequest<ApiResponse<AIHealthDto>>({
url: "/predictions/health",
client: "core",
method: "get",
});
};
const generateSmartCoupon = (body: SmartCouponRequestDto) => {
return apiRequest<ApiResponse<SmartCouponResponseDto>>({
url: "/predictions/smart-coupon",
client: "core",
method: "post",
data: body,
});
};
export const predictionsService = {
getPrediction,
generatePrediction,
getUpcoming,
getValueBets,
getHistory,
checkHealth,
generateSmartCoupon,
};
+230
View File
@@ -0,0 +1,230 @@
import type { SportType } from "@/lib/api/matches/types";
// ========================
// Sub-DTOs for MatchPredictionDto
// ========================
export interface MatchInfoDto {
match_id: string;
match_name: string;
home_team: string;
away_team: string;
league: string;
match_date_ms: number;
league_id?: string | null;
is_top_league?: boolean;
sport?: SportType;
[key: string]: unknown;
}
export interface DataQualityDto {
label: "HIGH" | "MEDIUM" | "LOW";
score: number;
home_lineup_count: number;
away_lineup_count: number;
lineup_source: string;
flags: string[];
[key: string]: unknown;
}
export interface ConfidenceIntervalDto {
lower: number;
upper: number;
width: number;
band: "HIGH" | "MEDIUM" | "LOW";
threshold_met: boolean;
}
export interface RiskDto {
level: "LOW" | "MEDIUM" | "HIGH" | "EXTREME";
score: number;
is_surprise_risk: boolean;
surprise_type: string | null;
surprise_score?: number;
surprise_comment?: string | null;
surprise_reasons?: string[];
warnings: string[];
}
export interface EngineBreakdownDto {
team: number;
player: number;
odds: number;
referee: number;
[key: string]: unknown;
}
export type BetGrade = "A" | "B" | "C" | "PASS";
export type SignalTier = "CORE" | "VALUE" | "LEAN" | "LONGSHOT" | "PASS";
export interface MatchPickDto {
market: string;
pick: string;
probability: number;
confidence: number;
odds: number;
raw_confidence: number;
calibrated_confidence: number;
min_required_confidence: number;
edge: number;
ev_edge: number;
implied_prob: number;
play_score: number;
playable: boolean;
bet_grade: BetGrade;
stake_units: number;
decision_reasons: string[];
confidence_interval?: ConfidenceIntervalDto;
signal_tier?: SignalTier;
}
export interface MatchBetAdviceDto {
playable: boolean;
suggested_stake_units: number;
reason: string;
confidence_band?: "HIGH" | "MEDIUM" | "LOW";
min_confidence_for_play?: number;
signal_tier?: SignalTier;
[key: string]: unknown;
}
export interface MatchBetSummaryItemDto {
market: string;
pick: string;
raw_confidence: number;
calibrated_confidence: number;
bet_grade: BetGrade;
playable: boolean;
stake_units: number;
play_score: number;
ev_edge: number;
implied_prob: number;
odds: number;
reasons: string[];
confidence_interval?: ConfidenceIntervalDto;
signal_tier?: SignalTier;
}
export interface AggressivePickDto {
market: string;
pick: string;
probability: number;
confidence: number;
odds: number | null;
confidence_interval?: ConfidenceIntervalDto;
}
export interface ScenarioTop5ItemDto {
score: string;
prob: number;
}
export interface ScorePredictionDto {
ft: string;
ht: string;
xg_home: number;
xg_away: number;
xg_total: number;
}
// ========================
// Main Prediction DTOs
// ========================
export interface MarketBoardEntryDto {
pick?: string;
confidence?: number;
confidence_band?: "HIGH" | "MEDIUM" | "LOW";
confidence_interval?: ConfidenceIntervalDto;
playable?: boolean;
probs?: Record<string, number>;
[key: string]: unknown;
}
export interface MatchPredictionDto {
model_version: string;
match_info: MatchInfoDto;
data_quality: DataQualityDto;
risk: RiskDto;
engine_breakdown: EngineBreakdownDto;
main_pick: MatchPickDto | null;
bet_advice: MatchBetAdviceDto;
bet_summary: MatchBetSummaryItemDto[];
supporting_picks: MatchPickDto[];
aggressive_pick: AggressivePickDto | null;
scenario_top5: ScenarioTop5ItemDto[];
score_prediction: ScorePredictionDto;
market_board: Record<string, MarketBoardEntryDto>;
reasoning_factors: string[];
ai_commentary?: string | null;
}
export interface ValueBetDto {
matchId: string;
matchName: string;
betType: string;
prediction: string;
confidence: number;
odd: number;
expectedValue: number;
}
export interface UpcomingPredictionsDto {
count: number;
matches: MatchPredictionDto[];
modelVersion: string;
}
export interface PredictionHistoryStatsDto {
totalPredictions: number;
correctPredictions: number;
accuracy: number;
[key: string]: unknown;
}
export interface PredictionHistoryResponseDto {
stats: PredictionHistoryStatsDto;
history: Record<string, unknown>[];
}
export interface AIHealthDto {
status: string;
modelLoaded: boolean;
predictionServiceReady: boolean;
}
// ========================
// Smart Coupon Request DTO
// ========================
export type CouponStrategy =
| "SAFE"
| "BALANCED"
| "AGGRESSIVE"
| "VALUE"
| "MIRACLE";
export interface SmartCouponRequestDto {
matchIds: string[];
strategy?: CouponStrategy;
maxMatches?: number;
minConfidence?: number;
}
export interface SmartCouponResponseDto {
coupon: {
items: Array<{
matchId: string;
matchName: string;
market: string;
pick: string;
odd: number;
confidence: number;
[key: string]: unknown;
}>;
totalOdd: number;
strategy: CouponStrategy;
[key: string]: unknown;
};
[key: string]: unknown;
}
+65
View File
@@ -0,0 +1,65 @@
import { useQuery, useMutation } from "@tanstack/react-query";
import type { SportType } from "@/lib/api/matches/types";
import { predictionsService } from "./service";
import type { SmartCouponRequestDto } from "./types";
export const PredictionsQueryKeys = {
all: ["predictions"] as const,
detail: (matchId: string) =>
[...PredictionsQueryKeys.all, "detail", matchId] as const,
upcoming: () => [...PredictionsQueryKeys.all, "upcoming"] as const,
valueBets: () => [...PredictionsQueryKeys.all, "valueBets"] as const,
history: () => [...PredictionsQueryKeys.all, "history"] as const,
health: () => [...PredictionsQueryKeys.all, "health"] as const,
};
export const usePrediction = (matchId: string) => {
return useQuery({
queryKey: PredictionsQueryKeys.detail(matchId),
queryFn: () => predictionsService.getPrediction(matchId),
enabled: !!matchId,
});
};
export const useGeneratePrediction = () => {
return useMutation({
mutationFn: (body: { matchId: string; sport?: SportType }) =>
predictionsService.generatePrediction(body),
});
};
export const useUpcomingPredictions = () => {
return useQuery({
queryKey: PredictionsQueryKeys.upcoming(),
queryFn: () => predictionsService.getUpcoming(),
});
};
export const useValueBets = () => {
return useQuery({
queryKey: PredictionsQueryKeys.valueBets(),
queryFn: () => predictionsService.getValueBets(),
});
};
export const usePredictionHistory = () => {
return useQuery({
queryKey: PredictionsQueryKeys.history(),
queryFn: () => predictionsService.getHistory(),
});
};
export const useAIHealth = () => {
return useQuery({
queryKey: PredictionsQueryKeys.health(),
queryFn: () => predictionsService.checkHealth(),
refetchInterval: 30_000, // Auto-refresh every 30s
});
};
export const useGenerateSmartCoupon = () => {
return useMutation({
mutationFn: (body: SmartCouponRequestDto) =>
predictionsService.generateSmartCoupon(body),
});
};