main
This commit is contained in:
149
src/components/features/calendar/game-calendar.tsx
Normal file
149
src/components/features/calendar/game-calendar.tsx
Normal file
@@ -0,0 +1,149 @@
|
||||
'use client';
|
||||
|
||||
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.50" rounded="xl" p={6} backdropFilter="blur(10px)" border="1px solid" borderColor="whiteAlpha.100">
|
||||
{/* 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.50'}
|
||||
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) => (
|
||||
<Badge
|
||||
key={`${item.id}-${idx}`}
|
||||
size="sm"
|
||||
variant="solid"
|
||||
colorPalette={item.type === 'game' ? 'blue' : 'purple'}
|
||||
truncate
|
||||
fontSize="xs"
|
||||
px={1}
|
||||
>
|
||||
{item.title}
|
||||
</Badge>
|
||||
))}
|
||||
</VStack>
|
||||
|
||||
{/* Background image effect for heavy days? Optional polish */}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</SimpleGrid>
|
||||
</Box >
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user