@@ -19,7 +19,12 @@ import { useSession } from "next-auth/react";
|
||||
import { useColorModeValue } from "@/components/ui/color-mode";
|
||||
import { SlideUp, FadeIn } from "@/components/motion";
|
||||
import { useTeamById, useTeamMatches } from "@/lib/api/leagues/use-hooks";
|
||||
import { LuArrowLeft, LuCalendar, LuTrophy, LuChevronDown } from "react-icons/lu";
|
||||
import {
|
||||
LuArrowLeft,
|
||||
LuCalendar,
|
||||
LuTrophy,
|
||||
LuChevronDown,
|
||||
} from "react-icons/lu";
|
||||
import type { MatchResponseDto } from "@/lib/api/matches/types";
|
||||
import { useState, useMemo, useCallback } from "react";
|
||||
import { LoginModal } from "@/components/auth/login-modal";
|
||||
@@ -29,7 +34,8 @@ import { LoginModal } from "@/components/auth/login-modal";
|
||||
// ─────────────────────────────────────────────────
|
||||
|
||||
function getMatchTimestamp(match: MatchResponseDto): number {
|
||||
const raw = typeof match.mstUtc === "string" ? Number(match.mstUtc) : match.mstUtc;
|
||||
const raw =
|
||||
typeof match.mstUtc === "string" ? Number(match.mstUtc) : match.mstUtc;
|
||||
return Number.isFinite(raw) ? raw : 0;
|
||||
}
|
||||
|
||||
@@ -39,19 +45,32 @@ function getMatchStatus(match: MatchResponseDto): string {
|
||||
|
||||
function isMatchFinished(match: MatchResponseDto): boolean {
|
||||
const status = getMatchStatus(match);
|
||||
return status === "FT" || status === "FINISHED" || status === "POSTGAME" || status === "POST_GAME";
|
||||
return (
|
||||
status === "FT" ||
|
||||
status === "FINISHED" ||
|
||||
status === "POSTGAME" ||
|
||||
status === "POST_GAME"
|
||||
);
|
||||
}
|
||||
|
||||
function isMatchLive(match: MatchResponseDto): boolean {
|
||||
const status = getMatchStatus(match);
|
||||
return status === "LIVE" || status === "INPROGRESS" || status === "IN_PROGRESS";
|
||||
return (
|
||||
status === "LIVE" || status === "INPROGRESS" || status === "IN_PROGRESS"
|
||||
);
|
||||
}
|
||||
|
||||
function getTeamSideName(team: MatchResponseDto["homeTeam"] | MatchResponseDto["awayTeam"], fallback?: unknown): string {
|
||||
function getTeamSideName(
|
||||
team: MatchResponseDto["homeTeam"] | MatchResponseDto["awayTeam"],
|
||||
fallback?: unknown,
|
||||
): string {
|
||||
return String(team?.name || fallback || "");
|
||||
}
|
||||
|
||||
function getTeamSideLogo(team: MatchResponseDto["homeTeam"] | MatchResponseDto["awayTeam"], fallback?: unknown): string {
|
||||
function getTeamSideLogo(
|
||||
team: MatchResponseDto["homeTeam"] | MatchResponseDto["awayTeam"],
|
||||
fallback?: unknown,
|
||||
): string {
|
||||
return String(team?.logo || fallback || "");
|
||||
}
|
||||
|
||||
@@ -74,7 +93,10 @@ const SEASONS = (() => {
|
||||
const currentYear = new Date().getFullYear();
|
||||
const currentMonth = new Date().getMonth() + 1;
|
||||
const startYear = currentMonth >= 8 ? currentYear : currentYear - 1;
|
||||
return Array.from({ length: 5 }, (_, i) => `${startYear - i}-${startYear - i + 1}`);
|
||||
return Array.from(
|
||||
{ length: 5 },
|
||||
(_, i) => `${startYear - i}-${startYear - i + 1}`,
|
||||
);
|
||||
})();
|
||||
|
||||
// ─────────────────────────────────────────────────
|
||||
@@ -97,7 +119,11 @@ export default function TeamDetailContent() {
|
||||
data: matchesResponse,
|
||||
isLoading: matchesLoading,
|
||||
isFetching: matchesFetching,
|
||||
} = useTeamMatches(teamId, { page: currentPage, limit: 20, season: activeSeason });
|
||||
} = useTeamMatches(teamId, {
|
||||
page: currentPage,
|
||||
limit: 20,
|
||||
season: activeSeason,
|
||||
});
|
||||
|
||||
const cardBg = useColorModeValue("white", "gray.800");
|
||||
const borderColor = useColorModeValue("gray.100", "gray.700");
|
||||
@@ -109,20 +135,30 @@ export default function TeamDetailContent() {
|
||||
const team = teamWrapper?.data as Record<string, unknown> | undefined;
|
||||
|
||||
// matchesResponse = { success, status, message, data: { data: [...], total, page, limit, totalPages } }
|
||||
const paginationWrapper = matchesResponse as Record<string, unknown> | undefined;
|
||||
const paginationData = paginationWrapper?.data as Record<string, unknown> | undefined;
|
||||
const matches: MatchResponseDto[] = (Array.isArray(paginationData?.data) ? paginationData.data : paginationData?.data ? [] : []) as MatchResponseDto[];
|
||||
const paginationWrapper = matchesResponse as
|
||||
| Record<string, unknown>
|
||||
| undefined;
|
||||
const paginationData = paginationWrapper?.data as
|
||||
| Record<string, unknown>
|
||||
| undefined;
|
||||
const matches: MatchResponseDto[] = (
|
||||
Array.isArray(paginationData?.data)
|
||||
? paginationData.data
|
||||
: paginationData?.data
|
||||
? []
|
||||
: []
|
||||
) as MatchResponseDto[];
|
||||
const totalPages = (paginationData?.totalPages as number) ?? 1;
|
||||
const totalMatches = (paginationData?.total as number) ?? 0;
|
||||
|
||||
// Separate past and upcoming matches
|
||||
const pastMatches = useMemo(
|
||||
() => matches.filter((m) => isMatchFinished(m)),
|
||||
[matches]
|
||||
[matches],
|
||||
);
|
||||
const upcomingMatches = useMemo(
|
||||
() => matches.filter((m) => !isMatchFinished(m)),
|
||||
[matches]
|
||||
[matches],
|
||||
);
|
||||
|
||||
// Pagination handlers
|
||||
@@ -169,7 +205,9 @@ export default function TeamDetailContent() {
|
||||
if (!team) {
|
||||
return (
|
||||
<Flex justify="center" py={20} direction="column" align="center" gap={4}>
|
||||
<Text color="fg.muted" fontSize="lg">Takım bulunamadı</Text>
|
||||
<Text color="fg.muted" fontSize="lg">
|
||||
Takım bulunamadı
|
||||
</Text>
|
||||
<Button variant="outline" onClick={() => router.back()}>
|
||||
<LuArrowLeft /> Geri
|
||||
</Button>
|
||||
@@ -181,13 +219,24 @@ export default function TeamDetailContent() {
|
||||
<SlideUp>
|
||||
<Box>
|
||||
{/* Back Button */}
|
||||
<Button variant="ghost" size="sm" mb={4} onClick={() => router.back()} gap={1.5}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
mb={4}
|
||||
onClick={() => router.back()}
|
||||
gap={1.5}
|
||||
>
|
||||
<LuArrowLeft />
|
||||
Geri
|
||||
</Button>
|
||||
|
||||
{/* Team Header */}
|
||||
<Card.Root bg={cardBg} borderColor={borderColor} borderRadius="xl" mb={6}>
|
||||
<Card.Root
|
||||
bg={cardBg}
|
||||
borderColor={borderColor}
|
||||
borderRadius="xl"
|
||||
mb={6}
|
||||
>
|
||||
<Card.Body>
|
||||
<HStack gap={6} justify="center" align="center">
|
||||
{(team as Record<string, unknown>).logo ? (
|
||||
@@ -206,7 +255,9 @@ export default function TeamDetailContent() {
|
||||
justify="center"
|
||||
>
|
||||
<Text fontSize="3xl" fontWeight="bold" color="primary.fg">
|
||||
{String((team as Record<string, unknown>).name || "T").charAt(0)}
|
||||
{String(
|
||||
(team as Record<string, unknown>).name || "T",
|
||||
).charAt(0)}
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
@@ -249,7 +300,11 @@ export default function TeamDetailContent() {
|
||||
cardBg={cardBg}
|
||||
borderColor={borderColor}
|
||||
statusBadge={getStatusBadge(match)}
|
||||
onClick={() => session ? router.push(`/matches/${match.id}`) : setLoginModalOpen(true)}
|
||||
onClick={() =>
|
||||
session
|
||||
? router.push(`/matches/${match.id}`)
|
||||
: setLoginModalOpen(true)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</VStack>
|
||||
@@ -260,7 +315,13 @@ export default function TeamDetailContent() {
|
||||
{/* Past Matches — Season Grouped */}
|
||||
<FadeIn>
|
||||
<Box>
|
||||
<Flex align="center" justify="space-between" mb={4} flexWrap="wrap" gap={2}>
|
||||
<Flex
|
||||
align="center"
|
||||
justify="space-between"
|
||||
mb={4}
|
||||
flexWrap="wrap"
|
||||
gap={2}
|
||||
>
|
||||
<Heading as="h2" size="lg">
|
||||
📊 Geçmiş Maçlar
|
||||
</Heading>
|
||||
@@ -320,7 +381,11 @@ export default function TeamDetailContent() {
|
||||
cardBg={cardBg}
|
||||
borderColor={borderColor}
|
||||
statusBadge={getStatusBadge(match)}
|
||||
onClick={() => session ? router.push(`/matches/${match.id}`) : setLoginModalOpen(true)}
|
||||
onClick={() =>
|
||||
session
|
||||
? router.push(`/matches/${match.id}`)
|
||||
: setLoginModalOpen(true)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</VStack>
|
||||
@@ -357,7 +422,9 @@ export default function TeamDetailContent() {
|
||||
key={pageNum}
|
||||
size="sm"
|
||||
variant={pageNum === currentPage ? "solid" : "ghost"}
|
||||
bg={pageNum === currentPage ? seasonActiveBg : undefined}
|
||||
bg={
|
||||
pageNum === currentPage ? seasonActiveBg : undefined
|
||||
}
|
||||
color={pageNum === currentPage ? "white" : undefined}
|
||||
borderRadius="full"
|
||||
minW="36px"
|
||||
@@ -407,7 +474,13 @@ interface MatchRowProps {
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
function MatchRow({ match, cardBg, borderColor, statusBadge, onClick }: MatchRowProps) {
|
||||
function MatchRow({
|
||||
match,
|
||||
cardBg,
|
||||
borderColor,
|
||||
statusBadge,
|
||||
onClick,
|
||||
}: MatchRowProps) {
|
||||
const hoverBg = useColorModeValue("gray.50", "gray.700");
|
||||
const matchTimestamp = getMatchTimestamp(match);
|
||||
const homeTeamName = getTeamSideName(match.homeTeam, match.homeTeamName);
|
||||
@@ -436,17 +509,34 @@ function MatchRow({ match, cardBg, borderColor, statusBadge, onClick }: MatchRow
|
||||
{homeTeamName}
|
||||
</Text>
|
||||
{homeTeamLogo ? (
|
||||
<Image src={homeTeamLogo} alt="" boxSize="24px" objectFit="contain" flexShrink={0} />
|
||||
<Image
|
||||
src={homeTeamLogo}
|
||||
alt=""
|
||||
boxSize="24px"
|
||||
objectFit="contain"
|
||||
flexShrink={0}
|
||||
/>
|
||||
) : (
|
||||
<Flex boxSize="24px" bg="primary.subtle" borderRadius="full" align="center" justify="center" flexShrink={0}>
|
||||
<Text fontSize="xs" fontWeight="bold">{homeTeamName?.charAt(0)}</Text>
|
||||
<Flex
|
||||
boxSize="24px"
|
||||
bg="primary.subtle"
|
||||
borderRadius="full"
|
||||
align="center"
|
||||
justify="center"
|
||||
flexShrink={0}
|
||||
>
|
||||
<Text fontSize="xs" fontWeight="bold">
|
||||
{homeTeamName?.charAt(0)}
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
{/* Score / VS */}
|
||||
<VStack gap={0} flexShrink={0} minW="60px">
|
||||
{hasScore && match.scoreHome !== undefined && match.scoreHome !== null ? (
|
||||
{hasScore &&
|
||||
match.scoreHome !== undefined &&
|
||||
match.scoreHome !== null ? (
|
||||
<Text fontSize="md" fontWeight="900">
|
||||
{match.scoreHome} - {match.scoreAway}
|
||||
</Text>
|
||||
@@ -468,10 +558,25 @@ function MatchRow({ match, cardBg, borderColor, statusBadge, onClick }: MatchRow
|
||||
{/* Away Team */}
|
||||
<HStack gap={2} flex={1}>
|
||||
{awayTeamLogo ? (
|
||||
<Image src={awayTeamLogo} alt="" boxSize="24px" objectFit="contain" flexShrink={0} />
|
||||
<Image
|
||||
src={awayTeamLogo}
|
||||
alt=""
|
||||
boxSize="24px"
|
||||
objectFit="contain"
|
||||
flexShrink={0}
|
||||
/>
|
||||
) : (
|
||||
<Flex boxSize="24px" bg="primary.subtle" borderRadius="full" align="center" justify="center" flexShrink={0}>
|
||||
<Text fontSize="xs" fontWeight="bold">{awayTeamName?.charAt(0)}</Text>
|
||||
<Flex
|
||||
boxSize="24px"
|
||||
bg="primary.subtle"
|
||||
borderRadius="full"
|
||||
align="center"
|
||||
justify="center"
|
||||
flexShrink={0}
|
||||
>
|
||||
<Text fontSize="xs" fontWeight="bold">
|
||||
{awayTeamName?.charAt(0)}
|
||||
</Text>
|
||||
</Flex>
|
||||
)}
|
||||
<Text fontSize="sm" fontWeight="600" truncate>
|
||||
@@ -483,7 +588,11 @@ function MatchRow({ match, cardBg, borderColor, statusBadge, onClick }: MatchRow
|
||||
{/* Status + League */}
|
||||
<HStack gap={2} flexShrink={0} ml={3}>
|
||||
{leagueLabel && (
|
||||
<Text fontSize="2xs" color="fg.muted" display={{ base: "none", md: "block" }}>
|
||||
<Text
|
||||
fontSize="2xs"
|
||||
color="fg.muted"
|
||||
display={{ base: "none", md: "block" }}
|
||||
>
|
||||
{leagueLabel}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
@@ -69,7 +69,13 @@ export default function TeamsContent() {
|
||||
<Spinner size="lg" color="primary.500" />
|
||||
</Flex>
|
||||
) : query.length < 2 ? (
|
||||
<Flex justify="center" py={16} direction="column" align="center" gap={3}>
|
||||
<Flex
|
||||
justify="center"
|
||||
py={16}
|
||||
direction="column"
|
||||
align="center"
|
||||
gap={3}
|
||||
>
|
||||
<Text fontSize="5xl">⚽</Text>
|
||||
<Text color="fg.muted" fontSize="lg">
|
||||
Aramak istediğiniz takımın adını yazın
|
||||
@@ -117,7 +123,11 @@ export default function TeamsContent() {
|
||||
align="center"
|
||||
justify="center"
|
||||
>
|
||||
<Text fontSize="xl" fontWeight="bold" color="primary.fg">
|
||||
<Text
|
||||
fontSize="xl"
|
||||
fontWeight="bold"
|
||||
color="primary.fg"
|
||||
>
|
||||
{team.name?.charAt(0) || "T"}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
Reference in New Issue
Block a user