diff --git a/front/package-lock.json b/front/package-lock.json index 9fb04d8..351b44a 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -39,6 +39,7 @@ "react-dom": "^18.3.1", "react-hook-form": "7.52.0", "react-jwt": "^1.2.2", + "react-material-ui-carousel": "^3.4.2", "react-redux": "^9.2.0", "react-router": "^7.1.1", "react-router-dom": "^6.25.1", @@ -9501,6 +9502,52 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/framer-motion": { + "version": "4.1.17", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-4.1.17.tgz", + "integrity": "sha512-thx1wvKzblzbs0XaK2X0G1JuwIdARcoNOW7VVwjO8BUltzXPyONGAElLu6CiCScsOQRI7FIk/45YTFtJw5Yozw==", + "license": "MIT", + "dependencies": { + "framesync": "5.3.0", + "hey-listen": "^1.0.8", + "popmotion": "9.3.6", + "style-value-types": "4.1.4", + "tslib": "^2.1.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": ">=16.8 || ^17.0.0", + "react-dom": ">=16.8 || ^17.0.0" + } + }, + "node_modules/framer-motion/node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/framer-motion/node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "license": "MIT", + "optional": true + }, + "node_modules/framesync": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-5.3.0.tgz", + "integrity": "sha512-oc5m68HDO/tuK2blj7ZcdEBRx3p1PjrgHazL8GYEpvULhrtGIFbQArN6cQS2QhW8mitffaB+VYzMjDqBxxQeoA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -9912,6 +9959,12 @@ "he": "bin/he" } }, + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==", + "license": "MIT" + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -14158,6 +14211,18 @@ "node": ">=4" } }, + "node_modules/popmotion": { + "version": "9.3.6", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-9.3.6.tgz", + "integrity": "sha512-ZTbXiu6zIggXzIliMi8LGxXBF5ST+wkpXGEjeTUDUOCdSQ356hij/xjeUdv0F8zCQNeqB1+PR5/BB+gC+QLAPw==", + "license": "MIT", + "dependencies": { + "framesync": "5.3.0", + "hey-listen": "^1.0.8", + "style-value-types": "4.1.4", + "tslib": "^2.1.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -15900,6 +15965,29 @@ "react": "^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-material-ui-carousel": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/react-material-ui-carousel/-/react-material-ui-carousel-3.4.2.tgz", + "integrity": "sha512-jUbC5aBWqbbbUOOdUe3zTVf4kMiZFwKJqwhxzHgBfklaXQbSopis4iWAHvEOLcZtSIJk4JAGxKE0CmxDoxvUuw==", + "license": "MIT", + "dependencies": { + "@emotion/react": "^11.7.1", + "@emotion/styled": "^11.6.0", + "@mui/icons-material": "^5.4.1", + "@mui/material": "^5.4.1", + "@mui/system": "^5.4.1", + "framer-motion": "^4.1.17" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "@mui/icons-material": "^5.0.0", + "@mui/material": "^5.0.0", + "@mui/system": "^5.0.0", + "react": "^17.0.1 || ^18.0.0", + "react-dom": "^17.0.2 || ^18.0.0" + } + }, "node_modules/react-redux": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", @@ -17465,6 +17553,16 @@ "webpack": "^5.0.0" } }, + "node_modules/style-value-types": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-4.1.4.tgz", + "integrity": "sha512-LCJL6tB+vPSUoxgUBt9juXIlNJHtBMy8jkXzUJSBzeHWdBu6lhzHqCvLVkXFGsFIlNa2ln1sQHya/gzaFmB2Lg==", + "license": "MIT", + "dependencies": { + "hey-listen": "^1.0.8", + "tslib": "^2.1.0" + } + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", diff --git a/front/package.json b/front/package.json index e9f1aff..878e2f2 100644 --- a/front/package.json +++ b/front/package.json @@ -34,6 +34,7 @@ "react-dom": "^18.3.1", "react-hook-form": "7.52.0", "react-jwt": "^1.2.2", + "react-material-ui-carousel": "^3.4.2", "react-redux": "^9.2.0", "react-router": "^7.1.1", "react-router-dom": "^6.25.1", diff --git a/front/public/assets/avatar.png b/front/public/assets/avatar.png index 2baa6c8..bbb69de 100644 Binary files a/front/public/assets/avatar.png and b/front/public/assets/avatar.png differ diff --git a/front/public/assets/cocktails/0uc4v0yzaqgrpjviyxgokdrju014xxjr.jpg b/front/public/assets/cocktails/0uc4v0yzaqgrpjviyxgokdrju014xxjr.jpg deleted file mode 100644 index 2602efa..0000000 Binary files a/front/public/assets/cocktails/0uc4v0yzaqgrpjviyxgokdrju014xxjr.jpg and /dev/null differ diff --git a/front/public/assets/cocktails/1723685244471_bulletin.jpg b/front/public/assets/cocktails/1723685244471_bulletin.jpg deleted file mode 100644 index 8155467..0000000 Binary files a/front/public/assets/cocktails/1723685244471_bulletin.jpg and /dev/null differ diff --git a/front/public/assets/cocktails/herosim.jpg b/front/public/assets/cocktails/herosim.jpg deleted file mode 100644 index 65a09e7..0000000 Binary files a/front/public/assets/cocktails/herosim.jpg and /dev/null differ diff --git a/front/public/assets/cocktails/morangeyto.jpg b/front/public/assets/cocktails/morangeyto.jpg deleted file mode 100644 index 974cfa1..0000000 Binary files a/front/public/assets/cocktails/morangeyto.jpg and /dev/null differ diff --git a/front/public/assets/cocktails/morangeyto_c.jpg b/front/public/assets/cocktails/morangeyto_c.jpg deleted file mode 100644 index e229fbc..0000000 Binary files a/front/public/assets/cocktails/morangeyto_c.jpg and /dev/null differ diff --git a/front/public/assets/helmet_icon_153945.svg b/front/public/assets/helmet_icon_153945.svg new file mode 100644 index 0000000..125f2cf --- /dev/null +++ b/front/public/assets/helmet_icon_153945.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/front/public/assets/ingredients/Aperol.png b/front/public/assets/ingredients/Aperol.png deleted file mode 100644 index e456296..0000000 Binary files a/front/public/assets/ingredients/Aperol.png and /dev/null differ diff --git a/front/public/assets/sponsors/kanistra.png b/front/public/assets/sponsors/kanistra.png new file mode 100644 index 0000000..a7bcbcd Binary files /dev/null and b/front/public/assets/sponsors/kanistra.png differ diff --git a/front/public/assets/sponsors/newDiffer.svg b/front/public/assets/sponsors/newDiffer.svg new file mode 100644 index 0000000..29b99f0 --- /dev/null +++ b/front/public/assets/sponsors/newDiffer.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + diff --git a/front/public/assets/sponsors/riostroy.svg b/front/public/assets/sponsors/riostroy.svg new file mode 100644 index 0000000..841e94c --- /dev/null +++ b/front/public/assets/sponsors/riostroy.svg @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + / + + + \ No newline at end of file diff --git a/front/public/assets/sponsors/sgaz.png b/front/public/assets/sponsors/sgaz.png new file mode 100644 index 0000000..8026304 Binary files /dev/null and b/front/public/assets/sponsors/sgaz.png differ diff --git a/front/public/assets/sponsors/sslm.png b/front/public/assets/sponsors/sslm.png new file mode 100644 index 0000000..c80cd8a Binary files /dev/null and b/front/public/assets/sponsors/sslm.png differ diff --git a/front/public/assets/logo_ulyanka-1.svg b/front/public/assets/sponsors/ulyanka.svg similarity index 90% rename from front/public/assets/logo_ulyanka-1.svg rename to front/public/assets/sponsors/ulyanka.svg index 25acf61..e1085b3 100644 --- a/front/public/assets/logo_ulyanka-1.svg +++ b/front/public/assets/sponsors/ulyanka.svg @@ -1,7 +1,9 @@ - + - + + - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + - - - - - - - + - + - + - + - + - - - - + - + diff --git a/front/public/assets/sponsors/woodGrand.svg b/front/public/assets/sponsors/woodGrand.svg new file mode 100644 index 0000000..7d261c2 --- /dev/null +++ b/front/public/assets/sponsors/woodGrand.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/front/public/regular/Pokrovsk_Cup_2026.pdf b/front/public/regular/Pokrovsk_Cup_2026.pdf new file mode 100644 index 0000000..8f093a6 Binary files /dev/null and b/front/public/regular/Pokrovsk_Cup_2026.pdf differ diff --git a/front/src/app/App.js b/front/src/app/App.js index 03af354..2ae3da5 100644 --- a/front/src/app/App.js +++ b/front/src/app/App.js @@ -15,7 +15,7 @@ function App() { return ( // Провайдер времени - {/*Провайдер уведомлений*/} + {/* Провайдер уведомлений*/} {/*Провайдер авторизации*/} diff --git a/front/src/app/pages/calendar/CalendarPage.js b/front/src/app/pages/calendar/CalendarPage.js index 2c64b09..215832f 100644 --- a/front/src/app/pages/calendar/CalendarPage.js +++ b/front/src/app/pages/calendar/CalendarPage.js @@ -1,403 +1,23 @@ -import React, { useState } from 'react'; -import { - Container, - Typography, - Box, - Drawer, - List, - ListItem, - ListItemText, - Chip, - Select, - MenuItem, - FormControl, - InputLabel, - Button, - Divider, - useMediaQuery, - useTheme, - Paper, -} from '@mui/material'; -import { CalendarToday as CalendarIcon, FilterList as FilterIcon } from '@mui/icons-material'; -import { DateCalendar } from '@mui/x-date-pickers/DateCalendar'; -import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; -import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; -import ruLocale from 'date-fns/locale/ru'; - -const CHAMPIONSHIP_STAGES = [ - { - id: 1, - title: 'SWC Зимний чемпионат', - stage: '2‑й этап', - date: '2026-02-08', - class: 'Юниоры', - status: 'Идёт', - }, - { - id: 2, - title: 'Honda Winter Cup', - stage: '1‑й этап', - date: '2026-01-31', - class: 'Pro', - status: 'Регистрация открыта', - }, - { - id: 3, - title: 'Кубок Покровска (онлайн)', - stage: '1‑й этап', - date: '2026-02-01', - class: 'Симулятор A', - status: 'Предрегистрация', - }, -]; +import React from 'react'; +import {Container,} from '@mui/material'; +import {CalendarContent} from "../../../components/calendar/CalendarContent"; +import {Footer} from "../../../components/core/Footer"; +import CalendarHeader from "../../../components/calendar/CalendarHeader"; const CalendarPage = () => { - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('sm')); - - const [viewDate, setViewDate] = useState(new Date()); // Дата для отображения месяца - const [selectedClass, setSelectedClass] = useState('Все'); - const [showFilters, setShowFilters] = useState(false); - const [openDrawer, setOpenDrawer] = useState(false); - - // Фильтрованные этапы для текущего месяца/класса - const filteredStages = CHAMPIONSHIP_STAGES.filter((stage) => { - const stageDate = new Date(stage.date); - const isSameMonth = stageDate.getMonth() === viewDate.getMonth(); - const isSameYear = stageDate.getFullYear() === viewDate.getFullYear(); - const classMatch = selectedClass === 'Все' || stage.class === selectedClass; - return isSameMonth && isSameYear && classMatch; - }); - - // Группируем этапы по дате - const stagesByDate = filteredStages.reduce((acc, stage) => { - const dayKey = new Date(stage.date).toDateString(); - acc[dayKey] = acc[dayKey] || []; - acc[dayKey].push(stage); - return acc; - }, {}); - - // Содержимое drawer для мобильных - const drawerContent = ( - - - Этапы на {viewDate.toLocaleDateString('ru-RU', { day: 'numeric', month: 'long', year: 'numeric' })} - - {filteredStages.length === 0 ? ( - - Нет этапов в этот день - - ) : ( - - {filteredStages.map((stage) => ( - - - - {stage.title} - - - {stage.stage} · {stage.class} - - - - - - - ))} - - )} - - ); + // const [selectedClass, setSelectedClass] = useState('Все'); return ( - + {/* Шапка */} - - - - Календарь чемпионатов - - - {/* Фильтры */} - - } - onClick={() => setShowFilters(!showFilters)} - size="small" - > - Фильтры - - {showFilters && ( - - Класс - - - )} - - + {/* Основной контент */} - - {/* Календарь */} - - - { - // Не меняем viewDate при клике на день — только навигация по месяцам - if (newValue.getMonth() !== viewDate.getMonth()) { - setViewDate(newValue); - } - }} - showDaysOutsideCurrentMonth - skipDisabledDateSelection - sx={{ - height: '100%', - '.MuiDayCalendar-dayButton': { - minHeight: { xs: 80, sm: 100 }, - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'flex-start', - p: 1, - }, - '.MuiPickersDay-root': { - fontSize: { xs: '1rem', sm: '1.1rem' }, - }, - // Подсветка дней с событиями - '&.MuiPickersDay-root[data-selected="true"]': { - backgroundColor: 'primary.main', - color: 'white', - }, - '&.MuiPickersDay-root:has(.event-chip)': { - border: '2px solid', - borderColor: 'primary.main', - '&:hover': { - borderColor: 'primary.dark', - } - } - }} - dayRenderer={(params) => { - const dayKey = params.day.toDateString(); - const events = stagesByDate[dayKey] || []; - - - return ( - - - {params.day.getDate()} - - {events.length > 0 && ( - - {events.map((event, idx) => ( - - ))} - - )} - - ); - }} - /> - - - - {/* Список этапов (только на десктопе) */} - {!isMobile && ( - - - - - Этапы на {viewDate.toLocaleDateString('ru-RU', { month: 'long', year: 'numeric' })} - - {filteredStages.length === 0 ? ( - - Нет этапов в выбранном периоде - - ) : ( - filteredStages.map((stage) => ( - - - {stage.title} - - - {stage.stage} · {stage.class} - - - - - - {new Date(stage.date).toLocaleDateString('ru-RU', { - day: 'numeric', - month: 'long', - weekday: 'short', - })} - - - )) - )} - - - - )} - - - {/* Drawer для мобильных */} - {isMobile && ( - setOpenDrawer(false)} - PaperProps={{ sx: { borderRadius: '16px 16px 0 0' } }} - > - {drawerContent} - - )} - - {/* Кнопка для открытия drawer на мобильных */} - {isMobile && filteredStages.length > 0 && ( - - - - )} + {/* Подвал */} - - - © 2026 КартХолл. Календарь соревнований. - +
); -}; - +} export default CalendarPage; diff --git a/front/src/app/pages/championship/ChampionshipPage.js b/front/src/app/pages/championship/ChampionshipPage.js index 090e28c..fd6419c 100644 --- a/front/src/app/pages/championship/ChampionshipPage.js +++ b/front/src/app/pages/championship/ChampionshipPage.js @@ -12,7 +12,7 @@ import { CircularProgress } from '@mui/material'; import { Launch as LaunchIcon, Download as DownloadIcon } from '@mui/icons-material'; -import { useParams } from 'react-router-dom'; +import { useParams, Link } from 'react-router-dom'; // Импортируем константы (из предыдущего файла) import { MOCK_STAGES, STAGE_STATUSES } from '../../../data/constants'; @@ -32,7 +32,7 @@ const ChampionshipPage = () => { try { // В реальном проекте тут будет fetch(`/api/championships/${id}`) // Для примера фильтруем MOCK_STAGES по id этапа (упрощённо) - const stagesForChampionship = MOCK_STAGES.filter(stage => stage.id === Number(id)); + const stagesForChampionship = MOCK_STAGES.filter(stage => stage.stage.id === Number(id)); if (stagesForChampionship.length === 0) { setError(true); @@ -40,10 +40,10 @@ const ChampionshipPage = () => { // Формируем данные чемпионата на основе первого подходящего этапа const stage = stagesForChampionship[0]; setChampionship({ - id: stage.id, + id: stage.stage.id, title: stage.title, description: stage.description || 'Информация о чемпионате отсутствует.', - regulationsUrl: '/dummy-regulations.pdf', // Условный URL регламента + regulationsUrl: '/regular/Pokrovsk_Cup_2026.pdf', // Условный URL регламента stages: MOCK_STAGES.filter(s => s.title === stage.title) // Все этапы этого чемпионата }); } @@ -89,7 +89,7 @@ const ChampionshipPage = () => { }); return ( - + {/* Заголовок и основная информация */} {championship.title} @@ -120,18 +120,21 @@ const ChampionshipPage = () => { {sortedStages.map((stage) => ( window.location.replace("/stages/" + stage.id)} + onClick={() => window.location.replace()} > - {stage.stage} + {stage.stage.name} { return ( - + {/* Заголовок и кнопка создания */} - - - Все чемпионаты - - - - - + {/* Список чемпионатов */} - - {CHAMPIONSHIPS.map((champ) => ( - - - - - - - {champ.title} - - - - {champ.status} - - - - {champ.season} - - - - - - {/* Таблица с основными данными */} -
- - - Этапы - {champ.stages} - - - Классы - - {champ.classes.join(', ')} - - - - Начало - {champ.startDate} - - - Конец - {champ.endDate} - - -
- - - {/* Действия */} - - - - - - - - - - - - ))} - - - {/* Сообщение, если чемпионатов нет */} - {CHAMPIONSHIPS.length === 0 && ( - - - Пока нет ни одного чемпионата. Создайте первый! - - - - )} - + {/* Подвал */} - - - © 2026 КартХолл. Управление чемпионатами. - +