'use client'; import React, { useEffect } from 'react'; import { Box, Typography, Grid, Card, CardContent, CardActions, Chip, Button, useTheme, Link, } from '@mui/material'; import { styled } from '@mui/material/styles'; import { IMod } from '@/src/types/Imod'; const ModCard = styled(Card)(({ theme }) => ({ height: '100%', display: 'flex', flexDirection: 'column', background: theme.palette.mode === 'dark' ? 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)' : 'linear-gradient(145deg, #ffffff 0%, #f5f5f5 100%)', border: `1px solid ${theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'}`, borderRadius: '16px', transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', position: 'relative', overflow: 'hidden', '&::before': { content: '""', position: 'absolute', top: 0, left: 0, right: 0, height: '3px', background: theme.palette.mode === 'dark' ? 'linear-gradient(90deg, #dee2e6 0%, #cccccc 100%)' : 'linear-gradient(90deg, #000000 0%, #333333 100%)', transform: 'scaleX(0)', transition: 'transform 0.3s ease', }, '&:hover': { boxShadow: theme.palette.mode === 'dark' ? '0 20px 40px rgba(255, 255, 255, 0.1)' : '0 20px 40px rgba(0, 0, 0, 0.2)', borderColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)', '&::before': { transform: 'scaleX(1)', }, }, })); const VersionLink = styled(Link)(({ theme }) => ({ display: 'flex', alignItems: 'center', padding: theme.spacing(1.5, 2), marginBottom: theme.spacing(1), background: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.05)' : 'rgba(0, 0, 0, 0.05)', borderRadius: '10px', textDecoration: 'none', color: 'inherit', transition: 'all 0.2s ease', border: `1px solid ${theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'}`, '&:hover': { background: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)', borderColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)', }, })); const ModTitle = styled(Typography)(({ theme }) => ({ fontWeight: 700, mb: 1, background: theme.palette.mode === 'dark' ? 'linear-gradient(135deg, #dee2e6 0%, #e0e0e0 100%)' : 'none', WebkitBackgroundClip: theme.palette.mode === 'dark' ? 'text' : 'initial', WebkitTextFillColor: theme.palette.mode === 'dark' ? '#dee2e6' : 'initial', color: theme.palette.mode === 'dark' ? '#dee2e6' : 'rgba(0, 0, 0, 0.85)', })); const VersionsChip = styled(Chip)(({ theme }) => ({ backgroundColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, 0.15)' : 'rgba(0, 0, 0, 0.1)', color: theme.palette.mode === 'dark' ? '#dee2e6' : 'rgba(0, 0, 0, 0.85)', fontWeight: 600, })); const VersionText = styled(Typography)(({ theme }) => ({ fontWeight: 600, color: theme.palette.text.secondary, })); const VersionArrow = styled(Typography)(({ theme }) => ({ color: theme.palette.text.secondary, })); const BetaVersionsLabel = styled(Typography)(({ theme }) => ({ color: 'rgba(255, 193, 7, 0.9)', fontWeight: 600, mb: 1, })); interface ModsPageClientProps { initialMods: IMod[]; } export default function ModsPageClient({ initialMods }: ModsPageClientProps) { const theme = useTheme(); // Очистка кэша сайта при заходе на главную страницу (один раз за сессию) useEffect(() => { if (!sessionStorage.getItem('cache_cleared')) { sessionStorage.setItem('cache_cleared', '1'); (async () => { try { if ('caches' in window) { const names = await caches.keys(); await Promise.all(names.map(name => caches.delete(name))); } if ('serviceWorker' in navigator) { const regs = await navigator.serviceWorker.getRegistrations(); await Promise.all(regs.map(r => r.unregister())); } } catch (e) { console.error('Cache clear failed:', e); } })(); } }, []); // Sort all mods by order, then group by name const sortedMods = [...initialMods].sort((a, b) => a.order - b.order); const mapWithModVersions = new Map(); sortedMods.forEach(mod => { const versionList = mapWithModVersions.get(mod.name); if (versionList == null) { mapWithModVersions.set(mod.name, [mod]); } else { versionList.push(mod); } }); const getLatestVersion = (modName: string) => { const sameMods = mapWithModVersions.get(modName) ?? []; // The latest version is the one with the maximum id const latest = sameMods.reduce((max, mod) => mod.id > max.id ? mod : max, sameMods[0]); const betaMods = sameMods.filter(m => m.isBeta); return { latest, allVersions: sameMods, hasBeta: betaMods.length > 0, betaVersion: betaMods[0], }; }; function ModCardComponent(modName: string) { const sameMods = mapWithModVersions.get(modName) ?? []; const { latest, hasBeta, betaVersion } = getLatestVersion(modName); return ( {modName} {sameMods.filter(m => !m.isBeta).map(mod => ( Version {mod.version} ))} {hasBeta && ( Beta versions: {betaVersion && ( Version {betaVersion.version} (Beta) )} )} ); } return ( {[...new Set(sortedMods.map(m => m.name))].map((modName) => ( {ModCardComponent(modName)} ))} ); }