feat: AI commentary skeleton loading - separate async endpoint
Deploy Iddaai Frontend / build-and-deploy (push) Successful in 2m20s

This commit is contained in:
2026-05-17 16:46:53 +03:00
parent e744a62fc2
commit 71a6ed320c
8 changed files with 228 additions and 40 deletions
@@ -20,7 +20,7 @@ import { useState } from "react";
import { useColorModeValue } from "@/components/ui/color-mode";
import { SlideUp, FadeIn } from "@/components/motion";
import { useMatchDetails } from "@/lib/api/matches/use-hooks";
import { usePrediction } from "@/lib/api/predictions/use-hooks";
import { usePrediction, useAiCommentary } from "@/lib/api/predictions/use-hooks";
import { useGetMe } from "@/lib/api/users/use-hooks";
import { useQueryClient } from "@tanstack/react-query";
import { UsersQueryKeys } from "@/lib/api/users/use-hooks";
@@ -32,6 +32,7 @@ import {
LuRefreshCw,
LuUser,
LuSparkles,
LuBrain,
LuInfo,
LuChevronDown,
LuChevronUp,
@@ -142,6 +143,11 @@ export default function MatchDetailContent() {
isFetching: isPredFetching,
} = usePrediction(matchId);
const { data: commentaryData, isLoading: commentaryLoading } = useAiCommentary(
matchId,
!!predictionData?.data, // prediction yüklendikten sonra başlat
);
const [officialsOpen, setOfficialsOpen] = useState(false);
const [activeTab, setActiveTab] = useState<string>("all");
@@ -762,7 +768,33 @@ export default function MatchDetailContent() {
<Skeleton h="80px" borderRadius="xl" />
</VStack>
) : prediction ? (
<PredictionCard prediction={prediction} />
<>
<PredictionCard prediction={prediction} />
{/* AI Uzman Yorumu — prediction yüklendikten sonra ayrıca çekiliyor */}
<Card.Root bg={cardBg} borderColor={borderColor} borderRadius="2xl">
<Card.Body gap={3}>
<Flex align="center" gap={2}>
<LuBrain size={16} />
<Text fontWeight="semibold" fontSize="sm">AI Uzman Yorumu</Text>
</Flex>
{commentaryLoading ? (
<VStack align="stretch" gap={2}>
<Skeleton h="16px" borderRadius="md" />
<Skeleton h="16px" borderRadius="md" w="90%" />
<Skeleton h="16px" borderRadius="md" w="75%" />
</VStack>
) : commentaryData?.data?.commentary ? (
<Text fontSize="sm" color="fg.muted" lineHeight="1.7">
{commentaryData.data.commentary}
</Text>
) : (
<Text fontSize="sm" color="fg.subtle">
Yorum üretilemedi.
</Text>
)}
</Card.Body>
</Card.Root>
</>
) : (
<Card.Root borderColor={borderColor} borderRadius="xl">
<Card.Body>
@@ -1627,23 +1627,6 @@ export default function PredictionCard({ prediction }: PredictionCardProps) {
)}
ui={ui}
/>
{(prediction as unknown as Record<string, unknown>).ai_expert_commentary ? (
<Card.Root bg={cardBg} borderColor={borderColor} borderRadius="2xl">
<Card.Body gap={3}>
<SectionTitle
icon={LuBrain}
title={uiText("ai-expert-commentary-title", "AI Uzman Yorumu")}
info={uiText(
"ai-expert-commentary-info",
"Yapay zekanın maç verilerini okuyarak ürettiği uzman bahis analizi.",
)}
/>
<Text fontSize="sm" color="fg.muted" lineHeight="1.7">
{String((prediction as unknown as Record<string, unknown>).ai_expert_commentary)}
</Text>
</Card.Body>
</Card.Root>
) : null}
{prediction.match_commentary?.headline ||
prediction.match_commentary?.summary ? (
<Card.Root bg={cardBg} borderColor={borderColor} borderRadius="2xl">
+9
View File
@@ -65,6 +65,14 @@ const checkHealth = () => {
});
};
const getCommentary = (matchId: string) => {
return apiRequest<ApiResponse<{ commentary: string | null }>>({
url: `/predictions/${matchId}/commentary`,
client: "core",
method: "get",
});
};
const generateSmartCoupon = (body: SmartCouponRequestDto) => {
return apiRequest<ApiResponse<SmartCouponResponseDto>>({
url: "/predictions/smart-coupon",
@@ -82,4 +90,5 @@ export const predictionsService = {
getHistory,
checkHealth,
generateSmartCoupon,
getCommentary,
};
+12
View File
@@ -7,6 +7,8 @@ export const PredictionsQueryKeys = {
all: ["predictions"] as const,
detail: (matchId: string) =>
[...PredictionsQueryKeys.all, "detail", matchId] as const,
commentary: (matchId: string) =>
[...PredictionsQueryKeys.all, "commentary", matchId] as const,
upcoming: () => [...PredictionsQueryKeys.all, "upcoming"] as const,
valueBets: () => [...PredictionsQueryKeys.all, "valueBets"] as const,
history: () => [...PredictionsQueryKeys.all, "history"] as const,
@@ -21,6 +23,16 @@ export const usePrediction = (matchId: string) => {
});
};
export const useAiCommentary = (matchId: string, enabled: boolean) => {
return useQuery({
queryKey: PredictionsQueryKeys.commentary(matchId),
queryFn: () => predictionsService.getCommentary(matchId),
enabled,
staleTime: 10 * 60 * 1000, // 10 dakika cache
retry: 1,
});
};
export const useGeneratePrediction = () => {
return useMutation({
mutationFn: (body: { matchId: string; sport?: SportType }) =>