Files
Game_Calendar_FE/src/components/features/calendar/game-calendar.tsx
Harun CAN 1f123b9f65 main
2026-01-30 04:48:46 +03:00

165 lines
7.2 KiB
TypeScript

'use client';
import { Link } from '@/i18n/navigation';
import { Box, Grid, Heading, Text, VStack, Badge, Flex, Image as ChakraImage, SimpleGrid } from '@chakra-ui/react';
import { useState } from 'react';
import { format, startOfMonth, endOfMonth, eachDayOfInterval, isSameMonth, isSameDay, addMonths, subMonths, getDay } from 'date-fns';
import { enUS, tr } from 'date-fns/locale';
import { useLocale } from 'next-intl';
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa';
import { IconButton } from '@chakra-ui/react';
import { FilterBar, FilterState } from './filter-bar';
// import { Game, Event } from '@prisma/client'; // Removed dependency
// Note: In a real monorepo we'd share types. For now we will mock or use any.
interface CalendarProps {
games: any[]; // Replace with correct type
events: any[];
}
export function GameCalendar({ games = [], events = [] }: CalendarProps) {
const [currentDate, setCurrentDate] = useState(new Date());
const [filters, setFilters] = useState<FilterState>({
search: '',
platforms: ['PC', 'PS5', 'Xbox', 'Switch'],
showEvents: true
});
const locale = useLocale();
const dateLocale = locale === 'tr' ? tr : enUS;
const monthStart = startOfMonth(currentDate);
const monthEnd = endOfMonth(monthStart);
const daysInMonth = eachDayOfInterval({ start: monthStart, end: monthEnd });
// Add padding days for start of month
const startDayOfWeek = getDay(monthStart); // 0 (Sun) to 6 (Sat)
// Adjust for Monday start if needed. Let's assume standard Sunday start for grid.
const paddingDays = Array.from({ length: startDayOfWeek });
const nextMonth = () => setCurrentDate(addMonths(currentDate, 1));
const prevMonth = () => setCurrentDate(subMonths(currentDate, 1));
const getItemsForDay = (date: Date) => {
// Filter Games
const dayGames = games.filter(g => {
if (!g.releaseDate || !isSameDay(new Date(g.releaseDate), date)) return false;
// Search filter
if (filters.search && !g.title.toLowerCase().includes(filters.search.toLowerCase())) return false;
// Platform filter (if game has platforms)
if (g.platforms && g.platforms.length > 0) {
const hasPlatform = g.platforms.some((p: string) => filters.platforms.includes(p) || filters.platforms.some(fp => p.includes(fp)));
if (!hasPlatform) return false;
}
return true;
});
// Filter Events
const dayEvents = filters.showEvents ? events.filter(e => {
if (!isSameDay(new Date(e.startTime), date)) return false;
if (filters.search && !e.title.toLowerCase().includes(filters.search.toLowerCase())) return false;
return true;
}) : [];
return [...dayGames.map(g => ({ ...g, type: 'game' })), ...dayEvents.map(e => ({ ...e, type: 'event' }))];
};
return (
<Box w="full" bg="whiteAlpha.300" rounded="xl" p={6} backdropFilter="blur(10px)" border="1px solid" borderColor="whiteAlpha.200">
{/* Header */}
<Flex justify="space-between" align="center" mb={6}>
<Heading size="lg" color="white">
{format(currentDate, 'MMMM yyyy', { locale: dateLocale })}
</Heading>
<Flex gap={2}>
<IconButton aria-label="Previous month" onClick={prevMonth} variant="ghost" color="white">
<FaChevronLeft />
</IconButton>
<IconButton aria-label="Next month" onClick={nextMonth} variant="ghost" color="white">
<FaChevronRight />
</IconButton>
</Flex>
</Flex>
{/* Filters */}
<FilterBar filters={filters} onFilterChange={setFilters} />
{/* Days Header */}
<SimpleGrid columns={7} mb={2}>
{['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'].map(day => (
<Text key={day} textAlign="center" color="gray.400" fontWeight="bold" fontSize="sm">
{day}
</Text>
))}
</SimpleGrid>
{/* Calendar Grid */}
<SimpleGrid columns={7} gap={1} minH="500px">
{paddingDays.map((_, i) => (
<Box key={`padding-${i}`} bg="transparent" />
))}
{daysInMonth.map((date) => {
const items = getItemsForDay(date);
const isToday = isSameDay(date, new Date());
return (
<Box
key={date.toString()}
bg={isToday ? 'primary.900' : 'whiteAlpha.200'}
border="1px solid"
borderColor={isToday ? 'primary.500' : 'whiteAlpha.100'}
rounded="md"
p={2}
minH="100px"
transition="all 0.2s"
_hover={{ bg: 'whiteAlpha.200', transform: 'scale(1.02)', zIndex: 1 }}
cursor="pointer"
position="relative"
overflow="hidden"
>
<Text fontSize="sm" color={isToday ? 'primary.300' : 'gray.400'} mb={1} fontWeight={isToday ? 'bold' : 'normal'}>
{format(date, 'd')}
</Text>
<VStack align="stretch" gap={1}>
{items.map((item: any, idx) => {
const badge = (
<Badge
key={`${item.id}-${idx}`}
size="sm"
variant="solid"
colorPalette={item.type === 'game' ? 'blue' : 'purple'}
truncate
fontSize="xs"
px={1}
cursor="pointer"
_hover={{ opacity: 0.8 }}
>
{item.title}
</Badge>
);
if (item.type === 'game') {
return (
<Link key={`${item.id}-${idx}`} href={`/games/${item.slug}`}>
{badge}
</Link>
);
}
return badge;
})}
</VStack>
{/* Background image effect for heavy days? Optional polish */}
</Box>
);
})}
</SimpleGrid>
</Box >
);
}