From f9beab0bc6a1bfe830bdfe44b9d66bf066fe85ec Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Sat, 27 Jul 2024 23:25:47 +0200 Subject: [PATCH 01/25] =?UTF-8?q?=D0=9A=D0=BE=D0=BB=D0=B8=D1=87=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=B2=D0=BE=20=D0=B6=D0=B8=D0=B7=D0=BD=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .prettierrc.js | 1 + src/components/Cards/Cards.jsx | 6 +++++- src/context/context.jsx | 9 +++++++++ src/index.js | 5 ++++- src/pages/SelectLevelPage/SelectLevelPage.jsx | 5 +++++ 5 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 src/context/context.jsx diff --git a/.prettierrc.js b/.prettierrc.js index 65e18f5ff..28033a2b6 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -7,4 +7,5 @@ module.exports = { bracketSpacing: true, arrowParens: "avoid", htmlWhitespaceSensitivity: "ignore", + endOfLine: "auto", }; diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 7526a56c8..9f1402bb0 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -1,10 +1,11 @@ import { shuffle } from "lodash"; -import { useEffect, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import { generateDeck } from "../../utils/cards"; import styles from "./Cards.module.css"; import { EndGameModal } from "../../components/EndGameModal/EndGameModal"; import { Button } from "../../components/Button/Button"; import { Card } from "../../components/Card/Card"; +import { EasyContext } from "../../context/context"; // Игра закончилась const STATUS_LOST = "STATUS_LOST"; @@ -41,6 +42,8 @@ function getTimerValue(startDate, endDate) { * previewSeconds - сколько секунд пользователь будет видеть все карты открытыми до начала игры */ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { + const { tries, isEasyMode } = useContext(EasyContext); + // console.log(isEasyMode); // В cards лежит игровое поле - массив карт и их состояние открыта\закрыта const [cards, setCards] = useState([]); // Текущий статус игры @@ -196,6 +199,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { )} {status === STATUS_IN_PROGRESS ? : null} + {isEasyMode && Количество жизней: {tries}}
diff --git a/src/context/context.jsx b/src/context/context.jsx new file mode 100644 index 000000000..ff14509f5 --- /dev/null +++ b/src/context/context.jsx @@ -0,0 +1,9 @@ +import { createContext, useState } from "react"; + +export const EasyContext = createContext(false); + +export const EasyProvider = ({ children }) => { + const [tries, setTries] = useState(3); + const [isEasyMode, setEasyMode] = useState(false); + return {children}; +}; diff --git a/src/index.js b/src/index.js index f689c5f0b..871d7d979 100644 --- a/src/index.js +++ b/src/index.js @@ -3,10 +3,13 @@ import ReactDOM from "react-dom/client"; import "./index.css"; import { RouterProvider } from "react-router-dom"; import { router } from "./router"; +import { EasyProvider } from "./context/context"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( - + + + , ); diff --git a/src/pages/SelectLevelPage/SelectLevelPage.jsx b/src/pages/SelectLevelPage/SelectLevelPage.jsx index 758942e51..bc67a8964 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.jsx +++ b/src/pages/SelectLevelPage/SelectLevelPage.jsx @@ -1,7 +1,10 @@ import { Link } from "react-router-dom"; import styles from "./SelectLevelPage.module.css"; +import { useContext } from "react"; +import { EasyContext } from "../../context/context"; export function SelectLevelPage() { + const { isEasyMode, setEasyMode } = useContext(EasyContext); return (
@@ -23,6 +26,8 @@ export function SelectLevelPage() { + + setEasyMode(e.target.checked)} />
); From d520b6a9ba98f9a374c1eabe6b05579a70383b3a Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Sat, 27 Jul 2024 23:42:52 +0200 Subject: [PATCH 02/25] =?UTF-8?q?=D1=81=D1=87=D0=B5=D1=82=D1=87=D0=B8?= =?UTF-8?q?=D0=BA=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE=D0=BA=20=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=D0=B5=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Cards/Cards.jsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 9f1402bb0..638f2081b 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -42,7 +42,7 @@ function getTimerValue(startDate, endDate) { * previewSeconds - сколько секунд пользователь будет видеть все карты открытыми до начала игры */ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { - const { tries, isEasyMode } = useContext(EasyContext); + const { tries, setTries, isEasyMode } = useContext(EasyContext); // console.log(isEasyMode); // В cards лежит игровое поле - массив карт и их состояние открыта\закрыта const [cards, setCards] = useState([]); @@ -76,6 +76,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setGameEndDate(null); setTimer(getTimerValue(null, null)); setStatus(STATUS_PREVIEW); + setTries(3); // Сброс количества попыток при перезапуске игры } /** @@ -130,7 +131,12 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { // "Игрок проиграл", т.к на поле есть две открытые карты без пары if (playerLost) { - finishGame(STATUS_LOST); + setTries(prevTries => prevTries - 1); + if (tries - 1 <= 0) { + finishGame(STATUS_LOST); + } else { + setCards(cards.map(card => ({ ...card, open: false }))); // Закрываем карты без пары + } return; } From eb2074e0c4d6c4a4ce60a3cbefe29fdefe35317a Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Sun, 28 Jul 2024 00:08:03 +0200 Subject: [PATCH 03/25] =?UTF-8?q?=D0=A1=D1=87=D0=B5=D1=82=D1=87=D0=B8?= =?UTF-8?q?=D0=BA=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE=D0=BA=20=D0=B2=20Readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 9b90842c4..6c9382a59 100644 --- a/README.md +++ b/README.md @@ -44,3 +44,15 @@ https://skypro-web-developer.github.io/react-memo/ Запускает eslint проверку кода, эта же команда запускается перед каждым коммитом. Если не получается закоммитить, попробуйте запустить эту команду и исправить все ошибки и предупреждения. + +## Счётчик ошибок + +В игре реализован счётчик ошибок, который показывает количество оставшихся попыток. + +### Формат отображения + +- "Осталось попыток: X" + +### Обоснование выбора + +Этот формат позволяет игрокам легко понять, сколько попыток у них осталось, без отвлечения от игры. Использование красного цвета привлекает внимание к этой важной информации. From 6aafae893bd237626b7102390cde9da7a456b52f Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Sun, 28 Jul 2024 00:14:47 +0200 Subject: [PATCH 04/25] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BE=D1=86=D0=B5=D0=BD=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=B2=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 6c9382a59..0eaa6b95a 100644 --- a/README.md +++ b/README.md @@ -56,3 +56,10 @@ https://skypro-web-developer.github.io/react-memo/ ### Обоснование выбора Этот формат позволяет игрокам легко понять, сколько попыток у них осталось, без отвлечения от игры. Использование красного цвета привлекает внимание к этой важной информации. + +## Оценка времени работы + +- **Инициализация игры**: O(n) +- **Обработка клика по карте**: O(1) +- **Проверка выигрыша**: O(n) +- **Перемешивание карт**: O(n log n) From 588034058d562f842e46c41dafe2e12003ba2b38 Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Sun, 28 Jul 2024 22:46:35 +0200 Subject: [PATCH 05/25] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=B8=D0=B3=D1=80=D0=B0=D1=82=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/SelectLevelPage/SelectLevelPage.jsx | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/src/pages/SelectLevelPage/SelectLevelPage.jsx b/src/pages/SelectLevelPage/SelectLevelPage.jsx index bc67a8964..32cd46ed1 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.jsx +++ b/src/pages/SelectLevelPage/SelectLevelPage.jsx @@ -1,33 +1,41 @@ -import { Link } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; import styles from "./SelectLevelPage.module.css"; -import { useContext } from "react"; +import { useContext, useState } from "react"; import { EasyContext } from "../../context/context"; export function SelectLevelPage() { const { isEasyMode, setEasyMode } = useContext(EasyContext); + const navigate = useNavigate(); + const [selectedLevel, setSelectedLevel] = useState(null); + + const startGame = () => { + if (selectedLevel !== null) { + navigate(`/game/${selectedLevel}`); + } else { + alert("Пожалуйста, выберите уровень сложности"); + } + }; + return (

Выбери сложность

    -
  • - - 1 - -
  • -
  • - - 2 - -
  • -
  • - - 3 - -
  • + {[3, 6, 9].map(level => ( +
  • setSelectedLevel(level)} + > + {level / 3} +
  • + ))}
- + setEasyMode(e.target.checked)} /> +
); From 625062a520e93116888922bbed310b59428c8670 Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Mon, 29 Jul 2024 00:28:57 +0200 Subject: [PATCH 06/25] =?UTF-8?q?=D0=A1=D1=82=D0=B8=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=20=D0=B2=D1=8B=D0=B1=D0=BE=D1=80=20?= =?UTF-8?q?=D1=81=D0=BB=D0=BE=D0=B6=D0=BD=D0=BE=D1=81=D1=82=D0=B8=20=D0=B8?= =?UTF-8?q?=20=D1=80=D0=B5=D0=B6=D0=B8=D0=BC=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/index.html | 3 + src/pages/SelectLevelPage/SelectLevelPage.jsx | 11 ++- .../SelectLevelPage.module.css | 89 +++++++++++++++++-- 3 files changed, 94 insertions(+), 9 deletions(-) diff --git a/public/index.html b/public/index.html index c0103cf10..f8f3a611a 100644 --- a/public/index.html +++ b/public/index.html @@ -2,6 +2,9 @@ + + + diff --git a/src/pages/SelectLevelPage/SelectLevelPage.jsx b/src/pages/SelectLevelPage/SelectLevelPage.jsx index 32cd46ed1..7857a929a 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.jsx +++ b/src/pages/SelectLevelPage/SelectLevelPage.jsx @@ -31,8 +31,15 @@ export function SelectLevelPage() { ))} - - setEasyMode(e.target.checked)} /> +
+ + setEasyMode(e.target.checked)} + className={styles.easyModeCheckbox} + /> +
diff --git a/src/pages/SelectLevelPage/SelectLevelPage.module.css b/src/pages/SelectLevelPage/SelectLevelPage.module.css index 390ac0def..e4f9b7594 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.module.css +++ b/src/pages/SelectLevelPage/SelectLevelPage.module.css @@ -1,27 +1,34 @@ .container { + font-family: 'Roboto', sans-serif; + width: 100%; min-height: 100%; display: flex; align-items: center; justify-content: center; -} + } .modal { - width: 480px; - height: 459px; - border-radius: 12px; background: #c2f5ff; display: flex; flex-direction: column; justify-content: center; align-items: center; + + width: 480px; + height: 529px; + top: 122px; + left: 272px; + gap: 0px; + border-radius: 12px; + opacity: 0px; } .title { color: #004980; text-align: center; font-variant-numeric: lining-nums proportional-nums; - font-family: StratosSkyeng; + font-family: Roboto; font-size: 40px; font-style: normal; font-weight: 400; @@ -33,7 +40,16 @@ flex-direction: row; gap: 26px; margin-top: 48px; - margin-bottom: 28px; + margin-bottom: 28px; + + color: #0080C1; + text-align: center; + font-variant-numeric: lining-nums proportional-nums; + font-family: Roboto; + font-size: 40px; + font-style: normal; + font-weight: 400; + line-height: 48px; } .level { @@ -46,12 +62,28 @@ border-radius: 12px; background: #fff; + + cursor: pointer; + padding: 10px; + margin: 5px; + border: 2px solid transparent; + transition: border-color 0.1s, background-color 0.1s; +} + +.level:hover { + border-color: #aaa; +} + +.selected { + text-color: #004980; + border-color: #B1B1B1; + background-color: #ddd; } .levelLink { color: #0080c1; text-align: center; - font-family: StratosSkyeng; + font-family: Roboto; font-size: 64px; font-style: normal; font-weight: 400; @@ -62,3 +94,46 @@ .levelLink:visited { color: #0080c1; } + +.labelName { + display: flex; + flex-direction: row-reverse; + + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: center; + gap: 10px; +} + +.easyModeCheckbox { + width: 30px; + height: 29px; + top: 439px; + left: 353px; + gap: 0px; + border-radius: 4px; + border: none; + opacity: 0px; +} + +.startButton { + margin-top: 28px; + margin-bottom: 28px; + width: 276px; + height: 45px; + top: 508px; + left: 375px; + border-radius: 12px; + border: none; + opacity: 0px; + background-color: #7AC100; + + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: center; + color: #FFFFFF; +} \ No newline at end of file From 51353728084e90d8adb3ea1cd3885855a5847968 Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Mon, 29 Jul 2024 20:47:01 +0200 Subject: [PATCH 07/25] =?UTF-8?q?=D0=B4=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B0=20=D0=B4=D0=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Cards/Cards.jsx | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 638f2081b..19345a27b 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -131,11 +131,29 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { // "Игрок проиграл", т.к на поле есть две открытые карты без пары if (playerLost) { - setTries(prevTries => prevTries - 1); - if (tries - 1 <= 0) { - finishGame(STATUS_LOST); + if (isEasyMode) { + setTries(prevTries => prevTries - 1); + if (tries - 1 <= 0) { + finishGame(STATUS_LOST); + } else { + // Закрываем только карты без пары + setTimeout(() => { + setCards( + nextCards.map(card => + openCardsWithoutPair.some(openCard => openCard.id === card.id) ? { ...card, open: false } : card, + ), + ); + }, 1000); // Задержка в 1 секунду, чтобы игрок успел увидеть вторую карту + } } else { - setCards(cards.map(card => ({ ...card, open: false }))); // Закрываем карты без пары + // Сложный режим: закрываем только карты без пары, не уменьшаем количество попыток + setTimeout(() => { + setCards( + nextCards.map(card => + openCardsWithoutPair.some(openCard => openCard.id === card.id) ? { ...card, open: false } : card, + ), + ); + }, 1000); // Задержка в 1 секунду, чтобы игрок успел увидеть вторую карту } return; } From 09db74485215a4312d44d4dcc2726086f63b168d Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Fri, 2 Aug 2024 09:12:44 +0200 Subject: [PATCH 08/25] =?UTF-8?q?=D0=A1=D0=BB=D0=BE=D0=B6=D0=BD=D1=8B?= =?UTF-8?q?=D0=B9=20=D1=80=D0=B5=D0=B6=D0=B8=D0=BC=20=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=B0=D0=B5=D1=82=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Cards/Cards.jsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 19345a27b..cf07f677a 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -147,17 +147,10 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { } } else { // Сложный режим: закрываем только карты без пары, не уменьшаем количество попыток - setTimeout(() => { - setCards( - nextCards.map(card => - openCardsWithoutPair.some(openCard => openCard.id === card.id) ? { ...card, open: false } : card, - ), - ); - }, 1000); // Задержка в 1 секунду, чтобы игрок успел увидеть вторую карту + finishGame(STATUS_LOST); // Завершаем игру при одной ошибке в сложном режиме } return; } - // ... игра продолжается }; From 1215258215264e1cde9c40b182e514fad3f54530 Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Fri, 2 Aug 2024 09:51:28 +0200 Subject: [PATCH 09/25] =?UTF-8?q?=D0=BA=D0=B0=D1=81=D1=82=D0=BE=D0=BC?= =?UTF-8?q?=D0=BD=D1=8B=D0=B9=20=D1=87=D0=B5=D0=BA=D0=B1=D0=BE=D0=BA=D1=81?= =?UTF-8?q?=20=D0=BD=D0=B5=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=B5?= =?UTF-8?q?=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/SelectLevelPage/SelectLevelPage.jsx | 4 +- .../SelectLevelPage.module.css | 60 ++++++++++++------- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/src/pages/SelectLevelPage/SelectLevelPage.jsx b/src/pages/SelectLevelPage/SelectLevelPage.jsx index 7857a929a..b72d43332 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.jsx +++ b/src/pages/SelectLevelPage/SelectLevelPage.jsx @@ -32,13 +32,15 @@ export function SelectLevelPage() { ))}
- setEasyMode(e.target.checked)} className={styles.easyModeCheckbox} /> + + Легкий режим (3 жизни)
+ +
+
Позиция
+
Пользователь
+
Время
+
+ {leaderArray.map((player, index) => ( + + ))} + + + ); +} diff --git a/src/pages/LeaderBoardPage/LeaderBoardPage.module.css b/src/pages/LeaderBoardPage/LeaderBoardPage.module.css new file mode 100644 index 000000000..10caa7776 --- /dev/null +++ b/src/pages/LeaderBoardPage/LeaderBoardPage.module.css @@ -0,0 +1,116 @@ +@import url("https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"); +@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"); + +.leader_board_container { + width: 944px; + margin: 0 auto; + padding: 26px; + padding-top: 22px; + box-sizing: border-box; +} + +.leader_board_container_top { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 40px; +} + +.leader_board_container_middle { + display: grid; + grid-template-columns: repeat(8, 1fr); + align-items: center; + background-color: #ffffff; + border-radius: 12px; + padding: 12px 40px 12px 20px; + margin-bottom: 15px; +} + +.leader_board_container_middle_left { + display: flex; + justify-content: space-between; + width: 400px; +} +.leader_board_container_middle_div1 { + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: left; + color: #999999; + grid-column: span 2; +} +.leader_board_container_middle_div2 { + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: left; + color: #999999; + grid-column: span 5; +} +.leader_board_container_middle_div3 { + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: left; + color: #999999; + grid-column: span 1; +} + +.leader_board_container_middle_div1_b { + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: left; + color: #000000; + grid-column: span 2; +} +.leader_board_container_middle_div2_b { + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: left; + color: #000000; + grid-column: span 5; +} +.leader_board_container_middle_div3_b { + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: left; + color: #000000; + grid-column: span 1; +} + +.leader_board_container_h1 { + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + color: #ffffff; +} + +.leader_board_container_button { + background-color: #7ac100; + width: 246px; + height: 50px; + border-radius: 12px; + border-width: 0px; + opacity: 0px; + color: #ffffff; + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: center; +} + +.leader_board_container_button:hover { + cursor: pointer; + background-color: #97de1e; +} \ No newline at end of file diff --git a/src/pages/SelectLevelPage/SelectLevelPage.jsx b/src/pages/SelectLevelPage/SelectLevelPage.jsx index ada5661e3..f3aca9169 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.jsx +++ b/src/pages/SelectLevelPage/SelectLevelPage.jsx @@ -2,6 +2,7 @@ import { useNavigate } from "react-router-dom"; import styles from "./SelectLevelPage.module.css"; import { useContext, useState } from "react"; import { EasyContext } from "../../context/context"; +import { Link } from "react-router-dom"; export function SelectLevelPage() { const { isEasyMode, setEasyMode } = useContext(EasyContext); @@ -58,6 +59,9 @@ export function SelectLevelPage() { + + Перейти к лидерборду + ); diff --git a/src/router.js b/src/router.js index da6e94b51..80f1488f9 100644 --- a/src/router.js +++ b/src/router.js @@ -1,6 +1,7 @@ import { createBrowserRouter } from "react-router-dom"; import { GamePage } from "./pages/GamePage/GamePage"; import { SelectLevelPage } from "./pages/SelectLevelPage/SelectLevelPage"; +import { LeaderBoardPage } from "./pages/LeaderBoardPage/LeaderBoardPage"; export const router = createBrowserRouter( [ @@ -12,10 +13,14 @@ export const router = createBrowserRouter( path: "/game/:pairsCount", element: , }, + { + path: "/game/:leaderboard", + element: , + }, ], /** * basename нужен для корректной работы в gh pages * он же указан в homepage package.json и в index.html */ - { basename: "/react-memo" }, + { basename: "/react-memo" } ); From f7f532dce94d27a17e7e8665a5bc7cdb899aed1c Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Sun, 4 Aug 2024 22:00:55 +0200 Subject: [PATCH 14/25] =?UTF-8?q?=D0=B4=D0=BE=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Cards/Cards.jsx | 4 ---- src/components/Cards/Cards.module.css | 16 ---------------- .../SelectLevelPage/SelectLevelPage.module.css | 10 ++++++++++ src/router.js | 2 +- 4 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index d19041f94..8b4b23ae3 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -230,10 +230,6 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { /> ))} - {/*
-
-
SuperHeroes
-
*/} {isGameEnded ? (
Date: Mon, 26 Aug 2024 23:22:47 +0200 Subject: [PATCH 15/25] =?UTF-8?q?=D0=B4=D0=B7-=20=D0=BF=D0=BE=20=D0=BB?= =?UTF-8?q?=D0=B8=D0=B4=D0=B5=D1=80=D0=B1=D0=BE=D1=80=D0=B4=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api.js | 2 +- src/components/Cards/Cards.jsx | 3 + src/components/EndGameModal/EndGameModal.jsx | 29 +++++-- .../EndGameModal/EndGameModal.module.css | 83 ++++++++++++++++++- src/pages/LeaderBoardPage/LeaderBoardPage.jsx | 19 +++-- src/pages/SelectLevelPage/SelectLevelPage.jsx | 2 +- src/router.js | 2 +- 7 files changed, 120 insertions(+), 20 deletions(-) diff --git a/src/api.js b/src/api.js index 70db9cce3..e38bd3c95 100644 --- a/src/api.js +++ b/src/api.js @@ -6,5 +6,5 @@ export async function getPlayersList() { }); const data = await response.json(); - return data; + return data.leaders; } diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 8b4b23ae3..adfef6dfe 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -42,6 +42,7 @@ function getTimerValue(startDate, endDate) { * previewSeconds - сколько секунд пользователь будет видеть все карты открытыми до начала игры */ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { + const isHardMode = pairsCount === 9; const { tries, setTries, isEasyMode } = useContext(EasyContext); // console.log(isEasyMode); // В cards лежит игровое поле - массив карт и их состояние открыта\закрыта @@ -234,6 +235,8 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) {
{imgAlt} -

{title}

+ {title &&

{title}

} + {lossTitle &&

{lossTitle}

} + {isLeader && ( +
+

Вы попали на Лидерборд!

+ + {/* */} +
+ )}

Затраченное время:

- {gameDurationMinutes.toString().padStart("2", "0")}.{gameDurationSeconds.toString().padStart("2", "0")} + {gameDurationMinutes.toString().padStart("2", "0")}:{gameDurationSeconds.toString().padStart("2", "0")}
+ + + Перейти к лидерборду +
); } diff --git a/src/components/EndGameModal/EndGameModal.module.css b/src/components/EndGameModal/EndGameModal.module.css index 9368cb8b5..83b29a296 100644 --- a/src/components/EndGameModal/EndGameModal.module.css +++ b/src/components/EndGameModal/EndGameModal.module.css @@ -1,6 +1,6 @@ .modal { width: 480px; - height: 459px; + height: 643px; border-radius: 12px; background: #c2f5ff; display: flex; @@ -46,6 +46,85 @@ font-style: normal; font-weight: 400; line-height: 72px; - margin-bottom: 40px; } + +.input { + height: 45px; + width: 276px; + border-radius: 8px; + background-color: #ffffff; + color: #999999; + padding: 0 16px; + border: none; + margin-bottom: 10px; + margin-top: 20px; + + &::placeholder { + line-height: 32px; + font-size: 24px; + font-weight: 400; + text-align: center; + color: #999999; + } + + &:invalid { + border: 2px solid red; + } +} + +.modalContainer { + position: relative; + width: 250px; + margin: 0 auto; +} + +.leaderboardModal { + font-family: 'Roboto', sans-serif; + font-weight: 400; + font-size: 40px; + line-height: 48px; + color: #004980; + width: 276px; + height: 96px; + top: 210px; + left: 0; + text-align: center; + margin: 20; +} + +.LeaderBoardLink { + margin-top: 20px; + color: #004980; + font-family: Roboto; + font-size: 18px; + font-weight: 400; + line-height: 32px; + text-align: left; +} + +/* .submitButton { + height: 45px; + width: 276px; + background-color: #004980; + color: #ffffff; + border: none; + border-radius: 8px; + font-size: 18px; + font-weight: 600; + cursor: pointer; + text-align: center; + + &:hover { + background-color: #003366; + } + + &:active { + background-color: #002244; + } + + &:disabled { + background-color: #cccccc; + cursor: not-allowed; + } +} */ diff --git a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx index b87d4bdeb..0e0734cf0 100644 --- a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx +++ b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx @@ -2,19 +2,19 @@ import { useState, useEffect } from "react"; import { getPlayersList } from "../../api"; import { LeaderBoardPlayer } from "../../components/LeaderBoardPlayer/LeaderBoardPlayer"; import styles from "./LeaderBoardPage.module.css"; +import { useNavigate } from "react-router-dom"; export function LeaderBoardPage() { const [leaderArray, setLeaderArray] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const navigate = useNavigate(); useEffect(() => { const fetchData = async () => { try { const data = await getPlayersList(); - const filteredData = data.leaders - .filter(player => player.difficulty === 3) // Учитываем только 3-й уровень сложности - .sort((a, b) => a.time - b.time); // Сортируем по возрастанию времени + const filteredData = data.sort((a, b) => a.time - b.time).slice(0, 10); setLeaderArray(filteredData); } catch (err) { setError("Не удалось загрузить данные"); @@ -26,6 +26,10 @@ export function LeaderBoardPage() { fetchData(); }, []); + const startGame = () => { + navigate(`/game/9`); + }; + if (loading) return
Загрузка...
; if (error) return
{error}
; @@ -34,19 +38,20 @@ export function LeaderBoardPage() {

Лидерборд

- +
Позиция
Пользователь
-
Время
+
Время
{leaderArray.map((player, index) => ( ))} diff --git a/src/pages/SelectLevelPage/SelectLevelPage.jsx b/src/pages/SelectLevelPage/SelectLevelPage.jsx index f3aca9169..a7e0d3df6 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.jsx +++ b/src/pages/SelectLevelPage/SelectLevelPage.jsx @@ -59,7 +59,7 @@ export function SelectLevelPage() { - + Перейти к лидерборду
diff --git a/src/router.js b/src/router.js index 8b1f416a2..c80542c76 100644 --- a/src/router.js +++ b/src/router.js @@ -14,7 +14,7 @@ export const router = createBrowserRouter( element: , }, { - path: "/game/:leaderboard", + path: "/leaderboard", element: , }, ], From eecd65acc3fc671139cbc333bad3b77b56e38574 Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Mon, 26 Aug 2024 23:35:02 +0200 Subject: [PATCH 16/25] =?UTF-8?q?=D0=B4=D0=BE=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 0eaa6b95a..b635bdcf0 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,12 @@ https://skypro-web-developer.github.io/react-memo/ ## Оценка времени работы - **Инициализация игры**: O(n) + - Игра инициализируется на основе количества карт. Чем больше карт, тем больше времени потребуется для их инициализации. - **Обработка клика по карте**: O(1) + - Время на обработку клика по карте фиксированное, так как это простое действие — переворот карты. +- **Проверка совпадений**: O(1) + - Проверка совпадения двух карт осуществляется мгновенно, так как это сравнение двух значений. - **Проверка выигрыша**: O(n) + - Для проверки выигрыша необходимо пройти по всем картам, чтобы убедиться, что все пары найдены. - **Перемешивание карт**: O(n log n) + - Перемешивание карт реализовано с использованием алгоритма, временная сложность которого составляет O(n log n), что характерно для эффективных алгоритмов сортировки. \ No newline at end of file From aac17871ba7e52620036f58dacbd54244bf26bb3 Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Thu, 29 Aug 2024 22:17:33 +0200 Subject: [PATCH 17/25] =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8C=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D1=8F=D0=B5=D1=82=D1=81=D1=8F=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=BB=D0=B8=D0=B1=D0=B5=D1=80=D0=B1=D0=BE=D1=80=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api.js | 20 ++++++ src/components/EndGameModal/EndGameModal.jsx | 45 ++++++++++-- .../EndGameModal/EndGameModal.module.css | 69 ++++++++++--------- .../SelectLevelPage.module.css | 4 +- 4 files changed, 100 insertions(+), 38 deletions(-) diff --git a/src/api.js b/src/api.js index e38bd3c95..d2229b95d 100644 --- a/src/api.js +++ b/src/api.js @@ -8,3 +8,23 @@ export async function getPlayersList() { const data = await response.json(); return data.leaders; } + +export async function updateLeaderboard(name, score, time) { + try { + const response = await fetch(API_URL, { + method: "POST", + body: JSON.stringify({ name, score, time }), // Добавляем поле time в запрос + }); + + if (!response.ok) { + const errorText = await response.text(); + console.error("Ошибка при обновлении лидерборда:", response.status, errorText); + throw new Error(`Не удалось обновить лидерборд: ${response.status} ${errorText}`); + } + + console.log("Лидерборд успешно обновлён"); + } catch (error) { + console.error("Ошибка сети или другая ошибка:", error); + throw new Error("Не удалось обновить лидерборд"); + } +} diff --git a/src/components/EndGameModal/EndGameModal.jsx b/src/components/EndGameModal/EndGameModal.jsx index 788145f15..8bcd8c299 100644 --- a/src/components/EndGameModal/EndGameModal.jsx +++ b/src/components/EndGameModal/EndGameModal.jsx @@ -2,7 +2,9 @@ import styles from "./EndGameModal.module.css"; import { Button } from "../Button/Button"; import deadImageUrl from "./images/dead.png"; import celebrationImageUrl from "./images/celebration.png"; -import { Link } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; +import { useState } from "react"; +import { updateLeaderboard } from "../../api"; export function EndGameModal({ isWon, level, isHardMode, gameDurationSeconds, gameDurationMinutes, onClick }) { const title = isWon && level <= 2 ? "Вы победили!" : ""; @@ -10,6 +12,35 @@ export function EndGameModal({ isWon, level, isHardMode, gameDurationSeconds, ga const lossTitle = !isWon ? "Вы проиграли!" : ""; const imgSrc = isWon ? celebrationImageUrl : deadImageUrl; const imgAlt = isWon ? "celebration emodji" : "dead emodji"; + const navigate = useNavigate(); + const [username, setUsername] = useState(""); + + const handleSubmit = async () => { + console.log("HandleSubmit вызван"); + + if (username.trim()) { + const score = 100; + const timeInSeconds = gameDurationMinutes * 60 + gameDurationSeconds; + + console.log("Имя пользователя перед отправкой:", username); + console.log("Тело запроса:", JSON.stringify({ name: username, score, time: timeInSeconds })); + + try { + await updateLeaderboard(username, score, timeInSeconds); // Ждём завершения обновления лидерборда + navigate("/leaderboard"); // Переход после успешного обновления + } catch (error) { + console.error("Ошибка при обновлении лидерборда:", error); + alert("Не удалось обновить лидерборд, попробуйте снова."); + } + } else { + alert("Введите имя пользователя перед отправкой!"); + } + }; + + const handleInputChange = event => { + setUsername(event.target.value); + console.log("Текущий username:", event.target.value); + }; return (
@@ -19,10 +50,16 @@ export function EndGameModal({ isWon, level, isHardMode, gameDurationSeconds, ga {isLeader && (

Вы попали на Лидерборд!

- - {/* */} +
)}

Затраченное время:

diff --git a/src/components/EndGameModal/EndGameModal.module.css b/src/components/EndGameModal/EndGameModal.module.css index 83b29a296..d6cc4c100 100644 --- a/src/components/EndGameModal/EndGameModal.module.css +++ b/src/components/EndGameModal/EndGameModal.module.css @@ -35,7 +35,7 @@ font-style: normal; font-weight: 400; line-height: 32px; - + margin-top: 20px; margin-bottom: 10px; } @@ -49,16 +49,23 @@ margin-bottom: 40px; } -.input { +.input, .submitButton { + box-sizing: border-box; height: 45px; width: 276px; border-radius: 8px; + border: none; +} + +.input { background-color: #ffffff; color: #999999; padding: 0 16px; - border: none; margin-bottom: 10px; margin-top: 20px; + font-size: 18px; + line-height: 45px; + text-align: center; &::placeholder { line-height: 32px; @@ -69,7 +76,31 @@ } &:invalid { - border: 2px solid red; + border: 1px solid red; + } +} + +.submitButton { + background-color: #004980; + color: #ffffff; + border: none; + border-radius: 8px; + font-size: 18px; + font-weight: 600; + cursor: pointer; + text-align: center; + + &:hover { + background-color: #003366; + } + + &:active { + background-color: #002244; + } + + &:disabled { + background-color: #cccccc; + cursor: not-allowed; } } @@ -95,36 +126,10 @@ .LeaderBoardLink { margin-top: 20px; - color: #004980; + color: #565EEF; font-family: Roboto; font-size: 18px; font-weight: 400; line-height: 32px; text-align: left; -} - -/* .submitButton { - height: 45px; - width: 276px; - background-color: #004980; - color: #ffffff; - border: none; - border-radius: 8px; - font-size: 18px; - font-weight: 600; - cursor: pointer; - text-align: center; - - &:hover { - background-color: #003366; - } - - &:active { - background-color: #002244; - } - - &:disabled { - background-color: #cccccc; - cursor: not-allowed; - } -} */ +} \ No newline at end of file diff --git a/src/pages/SelectLevelPage/SelectLevelPage.module.css b/src/pages/SelectLevelPage/SelectLevelPage.module.css index fcba45ce7..76e121b9e 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.module.css +++ b/src/pages/SelectLevelPage/SelectLevelPage.module.css @@ -46,10 +46,10 @@ text-align: center; font-variant-numeric: lining-nums proportional-nums; font-family: Roboto; - font-size: 40px; + font-size: 64px; font-style: normal; font-weight: 400; - line-height: 48px; + line-height: 72px; } .level { From 3707581ed424ac76268849e6e097e6181d6aa295 Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Sat, 7 Sep 2024 17:54:38 +0200 Subject: [PATCH 18/25] =?UTF-8?q?=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=81=D1=83=D0=BF=D0=B5=D1=80=D1=81=D0=B8=D0=BB?= =?UTF-8?q?=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 17 ++- src/api.js | 4 +- src/components/Cards/Cards.jsx | 63 +++++------ src/pages/LeaderBoardPage/LeaderBoardPage.jsx | 22 ++-- .../LeaderBoardPage.module.css | 101 +++++++++++------- 5 files changed, 127 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index b635bdcf0..d3d6259a8 100644 --- a/README.md +++ b/README.md @@ -68,4 +68,19 @@ https://skypro-web-developer.github.io/react-memo/ - **Проверка выигрыша**: O(n) - Для проверки выигрыша необходимо пройти по всем картам, чтобы убедиться, что все пары найдены. - **Перемешивание карт**: O(n log n) - - Перемешивание карт реализовано с использованием алгоритма, временная сложность которого составляет O(n log n), что характерно для эффективных алгоритмов сортировки. \ No newline at end of file + - Перемешивание карт реализовано с использованием алгоритма, временная сложность которого составляет O(n log n), что характерно для эффективных алгоритмов сортировки. + + Время на выполнение ДЗ №1: + + - пларируемое - 12 часов + - фактическое - 15 часов + +Время на выполнение ДЗ №2: + + - пларируемое - 12 часов + - фактическое - 14 часов + +Время на выполнение ДЗ №3: + + - пларируемое - 12 часов + - фактическое - \ No newline at end of file diff --git a/src/api.js b/src/api.js index d2229b95d..3db768936 100644 --- a/src/api.js +++ b/src/api.js @@ -9,11 +9,11 @@ export async function getPlayersList() { return data.leaders; } -export async function updateLeaderboard(name, score, time) { +export async function updateLeaderboard(name, score, time, achievements) { try { const response = await fetch(API_URL, { method: "POST", - body: JSON.stringify({ name, score, time }), // Добавляем поле time в запрос + body: JSON.stringify({ name, score, time, achievements }), // Добавляем поле time в запрос }); if (!response.ok) { diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index adfef6dfe..efd85b646 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -1,11 +1,12 @@ import { shuffle } from "lodash"; -import { useContext, useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { generateDeck } from "../../utils/cards"; import styles from "./Cards.module.css"; import { EndGameModal } from "../../components/EndGameModal/EndGameModal"; import { Button } from "../../components/Button/Button"; import { Card } from "../../components/Card/Card"; -import { EasyContext } from "../../context/context"; +import { useEasyContext } from "../../hook/useEasyContext"; // Импортируем хук +import { updateLeaderboard } from "../../api"; // Игра закончилась const STATUS_LOST = "STATUS_LOST"; @@ -22,11 +23,9 @@ function getTimerValue(startDate, endDate) { seconds: 0, }; } - if (endDate === null) { endDate = new Date(); } - const diffInSecconds = Math.floor((endDate.getTime() - startDate.getTime()) / 1000); const minutes = Math.floor(diffInSecconds / 60); const seconds = diffInSecconds % 60; @@ -36,35 +35,45 @@ function getTimerValue(startDate, endDate) { }; } -/** - * Основной компонент игры, внутри него находится вся игровая механика и логика. - * pairsCount - сколько пар будет в игре - * previewSeconds - сколько секунд пользователь будет видеть все карты открытыми до начала игры - */ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const isHardMode = pairsCount === 9; - const { tries, setTries, isEasyMode } = useContext(EasyContext); - // console.log(isEasyMode); - // В cards лежит игровое поле - массив карт и их состояние открыта\закрыта + const { tries, setTries, isEasyMode } = useEasyContext(); // Используем наш хук const [cards, setCards] = useState([]); - // Текущий статус игры const [status, setStatus] = useState(STATUS_PREVIEW); - - // Дата начала игры const [gameStartDate, setGameStartDate] = useState(null); - // Дата конца игры const [gameEndDate, setGameEndDate] = useState(null); - - // Стейт для таймера, высчитывается в setInteval на основе gameStartDate и gameEndDate const [timer, setTimer] = useState({ seconds: 0, minutes: 0, }); + const [achievements, setAchievements] = useState([]); + + const handleGameEnd = isWon => { + const playerName = "Player"; // Получаем имя игрока (в будущем можно получить из контекста или пропсов) + const totalTime = timer.minutes * 60 + timer.seconds; // Подсчет времени + + // Добавляем ачивку за выигрыш + if (isWon) { + setAchievements(prev => [...prev, "win"]); + } + + // Обновляем лидерборд + updateLeaderboard(playerName, isWon ? 1 : 0, totalTime, achievements) + .then(() => { + console.log("Результаты игры успешно отправлены"); + }) + .catch(error => { + console.error("Ошибка при отправке результатов игры:", error); + }); + }; + function finishGame(status = STATUS_LOST) { setGameEndDate(new Date()); setStatus(status); + handleGameEnd(status === STATUS_WON); } + function startGame() { const startDate = new Date(); setGameEndDate(null); @@ -72,21 +81,16 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setTimer(getTimerValue(startDate, null)); setStatus(STATUS_IN_PROGRESS); } + function resetGame() { setGameStartDate(null); setGameEndDate(null); setTimer(getTimerValue(null, null)); setStatus(STATUS_PREVIEW); - setTries(3); // Сброс количества попыток при перезапуске игры + setTries(3); + setAchievements([]); } - /** - * Обработка основного действия в игре - открытие карты. - * После открытия карты игра может пепереходит в следующие состояния - * - "Игрок выиграл", если на поле открыты все карты - * - "Игрок проиграл", если на поле есть две открытые карты без пары - * - "Игра продолжается", если не случилось первых двух условий - */ const openCard = clickedCard => { // Если карта уже открыта, то ничего не делаем if (clickedCard.open) { @@ -137,22 +141,19 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { if (tries - 1 <= 0) { finishGame(STATUS_LOST); } else { - // Закрываем только карты без пары setTimeout(() => { setCards( nextCards.map(card => openCardsWithoutPair.some(openCard => openCard.id === card.id) ? { ...card, open: false } : card, ), ); - }, 1000); // Задержка в 1 секунду, чтобы игрок успел увидеть вторую карту + }, 1000); } } else { - // Сложный режим: закрываем только карты без пары, не уменьшаем количество попыток - finishGame(STATUS_LOST); // Завершаем игру при одной ошибке в сложном режиме + finishGame(STATUS_LOST); } return; } - // ... игра продолжается }; const isGameEnded = status === STATUS_LOST || status === STATUS_WON; diff --git a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx index 0e0734cf0..bc0396bc7 100644 --- a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx +++ b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx @@ -3,6 +3,7 @@ import { getPlayersList } from "../../api"; import { LeaderBoardPlayer } from "../../components/LeaderBoardPlayer/LeaderBoardPlayer"; import styles from "./LeaderBoardPage.module.css"; import { useNavigate } from "react-router-dom"; +import cn from "classnames"; export function LeaderBoardPage() { const [leaderArray, setLeaderArray] = useState([]); @@ -11,6 +12,7 @@ export function LeaderBoardPage() { const navigate = useNavigate(); useEffect(() => { + setLoading(true); const fetchData = async () => { try { const data = await getPlayersList(); @@ -35,24 +37,26 @@ export function LeaderBoardPage() { return ( <> -
-
-

Лидерборд

-
-
-
Позиция
-
Пользователь
-
Время
+
+
Позиция
+
Пользователь
+
Достижения
+
Время
{leaderArray.map((player, index) => ( ))}
diff --git a/src/pages/LeaderBoardPage/LeaderBoardPage.module.css b/src/pages/LeaderBoardPage/LeaderBoardPage.module.css index 10caa7776..f3fece55a 100644 --- a/src/pages/LeaderBoardPage/LeaderBoardPage.module.css +++ b/src/pages/LeaderBoardPage/LeaderBoardPage.module.css @@ -1,7 +1,7 @@ @import url("https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"); @import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"); -.leader_board_container { +.leaderboardContainer { width: 944px; margin: 0 auto; padding: 26px; @@ -9,64 +9,95 @@ box-sizing: border-box; } -.leader_board_container_top { +.leaderboardHeader { display: flex; justify-content: space-between; align-items: center; + flex-direction: row; + flex-wrap: wrap; margin-bottom: 40px; } -.leader_board_container_middle { - display: grid; - grid-template-columns: repeat(8, 1fr); - align-items: center; - background-color: #ffffff; +.leaderboardTitle{ + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + color: #ffffff; +} + +.leaderboardButton { + background-color: #7ac100; + width: 246px; + height: 50px; border-radius: 12px; - padding: 12px 40px 12px 20px; - margin-bottom: 15px; + border-width: 0px; + opacity: 0px; + color: #ffffff; + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: center; } -.leader_board_container_middle_left { +.leadrboardButton:hover { + cursor: pointer; + background-color: #97de1e; +} + +.leaderboardSection { display: flex; - justify-content: space-between; - width: 400px; + gap: 15px; + background-color: #FFFFFF; + border-radius: 12px; + display: flex; + padding: 16px 20px; + justify-content: space-between; } -.leader_board_container_middle_div1 { + +.leaderbordText { + color: rgb(153, 153, 153); + font-family: Roboto; + font-size: 24px; + font-weight: 400; + +} +.leaderboardPosition { + width: 178px; font-family: Roboto; font-size: 24px; font-weight: 400; line-height: 32px; text-align: left; color: #999999; - grid-column: span 2; } -.leader_board_container_middle_div2 { +.leaderboardUser { + width: 227px; font-family: Roboto; font-size: 24px; font-weight: 400; line-height: 32px; text-align: left; color: #999999; - grid-column: span 5; } -.leader_board_container_middle_div3 { +.leaderboardAchievement { font-family: Roboto; font-size: 24px; font-weight: 400; line-height: 32px; text-align: left; color: #999999; - grid-column: span 1; } -.leader_board_container_middle_div1_b { +.leaderboardTime { + width: 194px; font-family: Roboto; font-size: 24px; font-weight: 400; line-height: 32px; text-align: left; - color: #000000; - grid-column: span 2; + color: #999999; } .leader_board_container_middle_div2_b { font-family: Roboto; @@ -86,31 +117,27 @@ color: #000000; grid-column: span 1; } - -.leader_board_container_h1 { +.leader_board_container_middle_div4 { font-family: Roboto; font-size: 24px; font-weight: 400; line-height: 32px; - color: #ffffff; + text-align: left; + color: #999999; + grid-column: span 4; /* Можно изменить в зависимости от ширины нужной колонки */ } -.leader_board_container_button { - background-color: #7ac100; - width: 246px; - height: 50px; - border-radius: 12px; - border-width: 0px; - opacity: 0px; - color: #ffffff; +.leader_board_container_middle_div4_b { font-family: Roboto; font-size: 24px; font-weight: 400; line-height: 32px; - text-align: center; + text-align: left; + color: #000000; + grid-column: span 4; /* Можно изменить в зависимости от ширины нужной колонки */ } -.leader_board_container_button:hover { - cursor: pointer; - background-color: #97de1e; -} \ No newline at end of file + + + + From d0cf4a7216a3916da3efb597029e77839920d878 Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Sun, 8 Sep 2024 13:37:32 +0200 Subject: [PATCH 19/25] =?UTF-8?q?=D0=90=D1=87=D0=B8=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 15 +++ package.json | 1 + public/card_insight.png | Bin 0 -> 5803 bytes public/magic_ball.svg | 17 ++++ public/magic_ball_empty.svg | 33 +++++++ public/puzzle.svg | 18 ++++ public/puzzle_empty.svg | 20 ++++ src/components/Ball/Ball.jsx | 11 +++ src/components/Ball/Ball.module.css | 34 +++++++ src/components/Cards/Cards.jsx | 67 ++++++++++--- src/components/Cards/Cards.module.css | 44 ++++++++- .../LeaderBoardPlayer/LeaderBoardPlayer.jsx | 45 ++++++++- .../LeaderBoardPlayer.module.css | 93 ++++++++++-------- src/components/Puzzle/Puzzle.jsx | 11 +++ src/components/Puzzle/Puzzle.module.css | 34 +++++++ src/pages/LeaderBoardPage/LeaderBoardPage.jsx | 2 +- .../LeaderBoardPage.module.css | 54 +++------- 17 files changed, 393 insertions(+), 106 deletions(-) create mode 100644 public/card_insight.png create mode 100644 public/magic_ball.svg create mode 100644 public/magic_ball_empty.svg create mode 100644 public/puzzle.svg create mode 100644 public/puzzle_empty.svg create mode 100644 src/components/Ball/Ball.jsx create mode 100644 src/components/Ball/Ball.module.css create mode 100644 src/components/Puzzle/Puzzle.jsx create mode 100644 src/components/Puzzle/Puzzle.module.css diff --git a/package-lock.json b/package-lock.json index edaf5083f..662339d85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "classnames": "^2.3.2", + "date-fns": "^3.6.0", "gh-pages": "^6.0.0", "lodash": "^4.17.21", "react": "^18.2.0", @@ -6785,6 +6786,15 @@ "node": ">=10" } }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -23136,6 +23146,11 @@ "whatwg-url": "^8.0.0" } }, + "date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==" + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", diff --git a/package.json b/package.json index e9b7a089e..061aa6e4b 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "classnames": "^2.3.2", + "date-fns": "^3.6.0", "gh-pages": "^6.0.0", "lodash": "^4.17.21", "react": "^18.2.0", diff --git a/public/card_insight.png b/public/card_insight.png new file mode 100644 index 0000000000000000000000000000000000000000..aeddd26071add9d8b4c7033fa7d768770653cfb3 GIT binary patch literal 5803 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 + + Created with Pixso. + + + + + + + + + + + + + + diff --git a/public/magic_ball_empty.svg b/public/magic_ball_empty.svg new file mode 100644 index 000000000..8de6a592f --- /dev/null +++ b/public/magic_ball_empty.svg @@ -0,0 +1,33 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/puzzle.svg b/public/puzzle.svg new file mode 100644 index 000000000..0b1729123 --- /dev/null +++ b/public/puzzle.svg @@ -0,0 +1,18 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + diff --git a/public/puzzle_empty.svg b/public/puzzle_empty.svg new file mode 100644 index 000000000..b49cb5c68 --- /dev/null +++ b/public/puzzle_empty.svg @@ -0,0 +1,20 @@ + + + Created with Pixso. + + + + + + + + + + + + + + + + + diff --git a/src/components/Ball/Ball.jsx b/src/components/Ball/Ball.jsx new file mode 100644 index 000000000..c67978f70 --- /dev/null +++ b/src/components/Ball/Ball.jsx @@ -0,0 +1,11 @@ +import styles from "./Ball.module.css"; + +export function Ball() { + return ( +
+
+

Игра пройдена в сложном режиме

+
+
+ ); +} diff --git a/src/components/Ball/Ball.module.css b/src/components/Ball/Ball.module.css new file mode 100644 index 000000000..93850aee7 --- /dev/null +++ b/src/components/Ball/Ball.module.css @@ -0,0 +1,34 @@ +.popup { + position: absolute; + top: -100px; +} + +.popupContent { + position: relative; + background-color: #C2F5FF; + border-radius: 8px; + padding: 15px 20px; + width: 212px; + text-align: center; + box-sizing: border-box; +} + +.popupContent::after { + content: ""; + position: absolute; + bottom: -20px; + left: 20px; + width: 0; + height: 0; + border-right: 25px solid transparent; + border-top: 20px solid #C2F5FF; + border-bottom: 5px solid transparent; +} + +.popupText { + color: rgb(0, 73, 128); + font-family: StratosSkyeng; + font-size: 18px; + font-weight: 400; + text-align: center; +} \ No newline at end of file diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index efd85b646..1693a3325 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -1,11 +1,11 @@ import { shuffle } from "lodash"; -import { useEffect, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import { generateDeck } from "../../utils/cards"; import styles from "./Cards.module.css"; import { EndGameModal } from "../../components/EndGameModal/EndGameModal"; import { Button } from "../../components/Button/Button"; import { Card } from "../../components/Card/Card"; -import { useEasyContext } from "../../hook/useEasyContext"; // Импортируем хук +import { EasyContext } from "../../context/context"; import { updateLeaderboard } from "../../api"; // Игра закончилась @@ -37,7 +37,7 @@ function getTimerValue(startDate, endDate) { export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const isHardMode = pairsCount === 9; - const { tries, setTries, isEasyMode } = useEasyContext(); // Используем наш хук + const { tries, setTries, isEasyMode } = useContext(EasyContext); const [cards, setCards] = useState([]); const [status, setStatus] = useState(STATUS_PREVIEW); const [gameStartDate, setGameStartDate] = useState(null); @@ -49,17 +49,22 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const [achievements, setAchievements] = useState([]); + const [isRevealed, setIsRevealed] = useState(false); // Состояние для отображения всех карт + const [canUsePower, setCanUsePower] = useState(true); // Состояние для использования суперсилы + const [isPaused, setIsPaused] = useState(false); // Состояние паузы таймера + const [usedSuperpower, setUsedSuperpower] = useState(false); + const handleGameEnd = isWon => { - const playerName = "Player"; // Получаем имя игрока (в будущем можно получить из контекста или пропсов) + const playerName = "Player"; const totalTime = timer.minutes * 60 + timer.seconds; // Подсчет времени // Добавляем ачивку за выигрыш - if (isWon) { + if (isWon && !usedSuperpower) { setAchievements(prev => [...prev, "win"]); } // Обновляем лидерборд - updateLeaderboard(playerName, isWon ? 1 : 0, totalTime, achievements) + updateLeaderboard(playerName, isWon ? 1 : 0, totalTime, achievements, usedSuperpower) .then(() => { console.log("Результаты игры успешно отправлены"); }) @@ -73,7 +78,6 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setStatus(status); handleGameEnd(status === STATUS_WON); } - function startGame() { const startDate = new Date(); setGameEndDate(null); @@ -81,7 +85,6 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setTimer(getTimerValue(startDate, null)); setStatus(STATUS_IN_PROGRESS); } - function resetGame() { setGameStartDate(null); setGameEndDate(null); @@ -89,6 +92,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setStatus(STATUS_PREVIEW); setTries(3); setAchievements([]); + setCanUsePower(true); // Сброс возможности использования суперсилы } const openCard = clickedCard => { @@ -158,6 +162,21 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const isGameEnded = status === STATUS_LOST || status === STATUS_WON; + // Логика для активации суперсилы "Прозрение" + const handleReveal = () => { + if (canUsePower) { + setIsRevealed(true); + setIsPaused(true); // Останавливаем таймер + setCanUsePower(false); // Суперсила используется только один раз + setUsedSuperpower(true); + + setTimeout(() => { + setIsRevealed(false); + setIsPaused(false); // Возобновляем таймер через 5 секунд + }, 5000); // 5 секундное действие + } + }; + // Игровой цикл useEffect(() => { // В статусах кроме превью доп логики не требуется @@ -186,13 +205,15 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { // Обновляем значение таймера в интервале useEffect(() => { + if (isPaused) return; + const intervalId = setInterval(() => { setTimer(getTimerValue(gameStartDate, gameEndDate)); }, 300); return () => { clearInterval(intervalId); }; - }, [gameStartDate, gameEndDate]); + }, [gameStartDate, gameEndDate, isPaused]); return (
@@ -217,16 +238,36 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { )}
- {status === STATUS_IN_PROGRESS ? : null} - {isEasyMode && Количество жизней: {tries}} -
+ {isEasyMode && ( + + + + + {tries} + + )} + {status === STATUS_IN_PROGRESS ? ( + <> +
+ eye_perk +
{isRevealed}
+
+ + + ) : null} +
{cards.map(card => ( openCard(card)} - open={status !== STATUS_IN_PROGRESS ? true : card.open} + open={isRevealed || status !== STATUS_IN_PROGRESS ? true : card.open} suit={card.suit} rank={card.rank} /> diff --git a/src/components/Cards/Cards.module.css b/src/components/Cards/Cards.module.css index 00a758ff7..972e996a2 100644 --- a/src/components/Cards/Cards.module.css +++ b/src/components/Cards/Cards.module.css @@ -67,13 +67,47 @@ font-style: normal; font-weight: 400; line-height: 32px; - margin-bottom: -12px; } -.superModal { - display: none; +.insight { + display: flex; + gap: 15px; + position: relative; + border: none; +} + +.cardInsight { + background-color: #C2F5FF; + border: none; + cursor: pointer; + width: 68px; + height: 68px; + radius: 50px; + opacity: 40; +} + +.counterInsight { position: absolute; - left: 0; - top: 0; + background-color: #C2F5FF; + border-radius: 100px; + + padding: 5px 10px; + font-family: Roboto; + top: 45px; + left: 45px; +} + + +.attemptСounter { + display: flex; + gap: 10px; + color: red; + font-variant-numeric: lining-nums proportional-nums; + font-family: Roboto; + font-size: 30px; + font-style: normal; + font-weight: 400; + line-height: 30px; + padding-bottom: 10px; } \ No newline at end of file diff --git a/src/components/LeaderBoardPlayer/LeaderBoardPlayer.jsx b/src/components/LeaderBoardPlayer/LeaderBoardPlayer.jsx index d1e3f24eb..12aeb07ad 100644 --- a/src/components/LeaderBoardPlayer/LeaderBoardPlayer.jsx +++ b/src/components/LeaderBoardPlayer/LeaderBoardPlayer.jsx @@ -1,11 +1,46 @@ +import { useState } from "react"; import styles from "./LeaderBoardPlayer.module.css"; +import { Puzzle } from "../Puzzle/Puzzle"; +import { Ball } from "../Ball/Ball"; + +export function LeaderBoardPlayer({ position, name, achievements = [], time }) { + const [isBallVisible, setIsBallVisible] = useState(false); + const [isPuzzleVisible, setIsPuzzleVisible] = useState(false); + + const handlePuzzleMouseEnter = () => { + setIsPuzzleVisible(true); + }; + const handlePuzzleMouseLeave = () => { + setIsPuzzleVisible(false); + }; + const handleBallMouseEnter = () => { + setIsBallVisible(true); + }; + const handleBallMouseLeave = () => { + setIsBallVisible(false); + }; -export function LeaderBoardPlayer({ name, time, position }) { return ( -
-
{position}
-
{name}
-
{time}
+
+
{position}
+
{name}
+
+ puzzle + ball + {isPuzzleVisible && } + {isBallVisible && } +
+
{time}
); } diff --git a/src/components/LeaderBoardPlayer/LeaderBoardPlayer.module.css b/src/components/LeaderBoardPlayer/LeaderBoardPlayer.module.css index 27c6f8b19..55698575c 100644 --- a/src/components/LeaderBoardPlayer/LeaderBoardPlayer.module.css +++ b/src/components/LeaderBoardPlayer/LeaderBoardPlayer.module.css @@ -1,39 +1,54 @@ -.leader_board_container_middle { - display: grid; - grid-template-columns: repeat(8, 1fr); - align-items: center; - background-color: #ffffff; - border-radius: 12px; - padding: 12px 40px 12px 20px; - margin-bottom: 15px; - } - - - .leader_board_container_middle_div1_b { - font-family: Roboto; - font-size: 24px; - font-weight: 400; - line-height: 32px; - text-align: left; - color: #000000; - grid-column: span 2; - } - .leader_board_container_middle_div2_b { - font-family: Roboto; - font-size: 24px; - font-weight: 400; - line-height: 32px; - text-align: left; - color: #000000; - grid-column: span 5; - } - .leader_board_container_middle_div3_b { - font-family: Roboto; - font-size: 24px; - font-weight: 400; - line-height: 32px; - text-align: left; - color: #000000; - grid-column: span 1; - } - \ No newline at end of file +.leaderboardPlayerSection { + display: flex; + gap: 15px; + background-color: #FFFFFF; + border-radius: 12px; + padding: 16px 20px; + justify-content: space-between; + gap: 66px; + position: relative; +} + +.leaderboardPlayerText { + color: #000000; + font-family: Roboto; + font-size: 24px; + font-weight: 400; +} + +.leaderboardPlayerIcons { + display: flex; + width: 194px; + gap: 6px; + position: relative; +} + +.leaderboardPlayerPosition { + width: 178px; + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: left; + color: ##000000; +} + +.leaderboardPlayerUser { + width: 227px; + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: left; + color: ##000000; +} + +.leaderboardPlayerTime { + width: 102px; + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: left; + color: ##000000; +} diff --git a/src/components/Puzzle/Puzzle.jsx b/src/components/Puzzle/Puzzle.jsx new file mode 100644 index 000000000..2f0196956 --- /dev/null +++ b/src/components/Puzzle/Puzzle.jsx @@ -0,0 +1,11 @@ +import styles from "./Puzzle.module.css"; + +export function Puzzle() { + return ( +
+
+

Игра пройдена без супер-сил

+
+
+ ); +} diff --git a/src/components/Puzzle/Puzzle.module.css b/src/components/Puzzle/Puzzle.module.css new file mode 100644 index 000000000..9e167ed8c --- /dev/null +++ b/src/components/Puzzle/Puzzle.module.css @@ -0,0 +1,34 @@ +.popup { + position: absolute; + top: -100px; + left: 35px; +} + +.popupContent { + text-align: center; + background-color: #C2F5FF; + border-radius: 8px; + padding: 15px 20px; + position: relative; + width: 174px; +} + +.popupText { + color: rgb(0, 73, 128); + font-family: StratosSkyeng; + font-size: 18px; + font-weight: 400; + text-align: center; +} + +.popupContent::after { + content: ""; + position: absolute; + bottom: -20px; + left: 20px; + width: 0; + height: 0; + border-right: 25px solid transparent; + border-top: 20px solid #C2F5FF; + border-bottom: 5px solid transparent; +} \ No newline at end of file diff --git a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx index bc0396bc7..dd7f5d77f 100644 --- a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx +++ b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx @@ -55,7 +55,7 @@ export function LeaderBoardPage() { key={player.id} name={player.name} time={`${Math.floor(player.time / 60)}:${(player.time % 60).toString().padStart(2, "0")}`} - position={index + 1} + position={`# ${index + 1}`} achievements={player.achievements} /> ))} diff --git a/src/pages/LeaderBoardPage/LeaderBoardPage.module.css b/src/pages/LeaderBoardPage/LeaderBoardPage.module.css index f3fece55a..be8782fbc 100644 --- a/src/pages/LeaderBoardPage/LeaderBoardPage.module.css +++ b/src/pages/LeaderBoardPage/LeaderBoardPage.module.css @@ -2,6 +2,9 @@ @import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap"); .leaderboardContainer { + display: flex; + flex-direction: column; + gap: 15px; width: 944px; margin: 0 auto; padding: 26px; @@ -15,7 +18,7 @@ align-items: center; flex-direction: row; flex-wrap: wrap; - margin-bottom: 40px; + margin-bottom: 20px; } .leaderboardTitle{ @@ -32,7 +35,7 @@ height: 50px; border-radius: 12px; border-width: 0px; - opacity: 0px; + opacity: 1px; color: #ffffff; font-family: Roboto; font-size: 24px; @@ -41,19 +44,19 @@ text-align: center; } -.leadrboardButton:hover { +.leaderboardButton:hover { cursor: pointer; background-color: #97de1e; } .leaderboardSection { - display: flex; - gap: 15px; background-color: #FFFFFF; border-radius: 12px; display: flex; padding: 16px 20px; - justify-content: space-between; + justify-content: space-between; + gap: 66px; + position: relative; } .leaderbordText { @@ -96,46 +99,11 @@ font-size: 24px; font-weight: 400; line-height: 32px; - text-align: left; - color: #999999; -} -.leader_board_container_middle_div2_b { - font-family: Roboto; - font-size: 24px; - font-weight: 400; - line-height: 32px; - text-align: left; - color: #000000; - grid-column: span 5; -} -.leader_board_container_middle_div3_b { - font-family: Roboto; - font-size: 24px; - font-weight: 400; - line-height: 32px; - text-align: left; - color: #000000; - grid-column: span 1; -} -.leader_board_container_middle_div4 { - font-family: Roboto; - font-size: 24px; - font-weight: 400; - line-height: 32px; - text-align: left; + text-align: center; color: #999999; - grid-column: span 4; /* Можно изменить в зависимости от ширины нужной колонки */ } -.leader_board_container_middle_div4_b { - font-family: Roboto; - font-size: 24px; - font-weight: 400; - line-height: 32px; - text-align: left; - color: #000000; - grid-column: span 4; /* Можно изменить в зависимости от ширины нужной колонки */ -} + From 8f49272f1b898c02ec640a12537e3271b5d90deb Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Sun, 8 Sep 2024 14:10:31 +0200 Subject: [PATCH 20/25] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F=20=D1=81=20=D0=B0=D1=87?= =?UTF-8?q?=D0=B8=D0=B2=D0=BA=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api.js | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/api.js b/src/api.js index 3db768936..21e6cd4db 100644 --- a/src/api.js +++ b/src/api.js @@ -1,4 +1,4 @@ -const API_URL = "https://wedev-api.sky.pro/api/leaderboard"; +const API_URL = "https://wedev-api.sky.pro/api/v2/leaderboard"; export async function getPlayersList() { const response = await fetch(API_URL, { @@ -9,20 +9,40 @@ export async function getPlayersList() { return data.leaders; } -export async function updateLeaderboard(name, score, time, achievements) { +// Получить ID достижений +function getAchievementIds(achievements) { + if (!Array.isArray(achievements)) { + return []; + } + return achievements; +} + +export async function updateLeaderboard(name, time, achievements) { + const achievementIds = getAchievementIds(achievements); + + const requestBody = JSON.stringify({ + name: name || "Пользователь", + time, + achievements: achievementIds, + }); + + console.log("Отправляем запрос на обновление лидерборда:"); + console.log("Тело запроса:", requestBody); + try { const response = await fetch(API_URL, { method: "POST", - body: JSON.stringify({ name, score, time, achievements }), // Добавляем поле time в запрос + body: requestBody, }); + const responseBody = await response.text(); + console.log("Ответ от сервера:", responseBody); + if (!response.ok) { const errorText = await response.text(); console.error("Ошибка при обновлении лидерборда:", response.status, errorText); throw new Error(`Не удалось обновить лидерборд: ${response.status} ${errorText}`); } - - console.log("Лидерборд успешно обновлён"); } catch (error) { console.error("Ошибка сети или другая ошибка:", error); throw new Error("Не удалось обновить лидерборд"); From 78aca3aecf191c9d5bc5f93a7ffc8c19497c30ad Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Sun, 8 Sep 2024 19:20:24 +0200 Subject: [PATCH 21/25] Kursovaya --- src/components/Cards/Cards.jsx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 1693a3325..6d8b834bd 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -63,8 +63,16 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setAchievements(prev => [...prev, "win"]); } + // Логируем достижения перед отправкой + console.log("Текущие достижения перед отправкой:", achievements); + + const updatedAchievements = achievements.filter(a => { + // Логика для фильтрации достижений + return true; // Пример, оставляем все достижения + }); + // Обновляем лидерборд - updateLeaderboard(playerName, isWon ? 1 : 0, totalTime, achievements, usedSuperpower) + updateLeaderboard(playerName, isWon ? 1 : 0, totalTime, updatedAchievements) .then(() => { console.log("Результаты игры успешно отправлены"); }) @@ -170,6 +178,15 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setCanUsePower(false); // Суперсила используется только один раз setUsedSuperpower(true); + // Обновляем достижения, если суперсила использована + setAchievements(prevAchievements => { + return prevAchievements.filter(achievement => { + // Логика для фильтрации достижений, если суперсила использована + // Например, очищаем достижения, если нужно + return false; // Пример, убираем все достижения + }); + }); + setTimeout(() => { setIsRevealed(false); setIsPaused(false); // Возобновляем таймер через 5 секунд From b622c54ec8db012a746a002fa3037d620d9b446b Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Sun, 8 Sep 2024 19:23:19 +0200 Subject: [PATCH 22/25] =?UTF-8?q?=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d3d6259a8..f9812dd99 100644 --- a/README.md +++ b/README.md @@ -83,4 +83,4 @@ https://skypro-web-developer.github.io/react-memo/ Время на выполнение ДЗ №3: - пларируемое - 12 часов - - фактическое - \ No newline at end of file + - фактическое - 13 часов \ No newline at end of file From bf428614b9f8893fd9ee3709caabe935a79a53aa Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Tue, 10 Sep 2024 21:39:32 +0200 Subject: [PATCH 23/25] =?UTF-8?q?=D0=90=D1=87=D0=B8=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=B2=20=D0=B4=D0=BE=D1=81=D1=82=D0=B8=D0=B6=D0=B5=D0=BD=D1=8F?= =?UTF-8?q?=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D1=8F=D1=8E=D1=82=D1=81?= =?UTF-8?q?=D1=8F=20=D0=B2=D1=81=D0=B5=D0=B3=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api.js | 4 ---- src/components/Cards/Cards.jsx | 8 ++----- src/components/EndGameModal/EndGameModal.jsx | 24 ++++++++++--------- .../LeaderBoardPlayer/LeaderBoardPlayer.jsx | 7 ++++-- 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/api.js b/src/api.js index 21e6cd4db..28588bfe0 100644 --- a/src/api.js +++ b/src/api.js @@ -26,9 +26,6 @@ export async function updateLeaderboard(name, time, achievements) { achievements: achievementIds, }); - console.log("Отправляем запрос на обновление лидерборда:"); - console.log("Тело запроса:", requestBody); - try { const response = await fetch(API_URL, { method: "POST", @@ -40,7 +37,6 @@ export async function updateLeaderboard(name, time, achievements) { if (!response.ok) { const errorText = await response.text(); - console.error("Ошибка при обновлении лидерборда:", response.status, errorText); throw new Error(`Не удалось обновить лидерборд: ${response.status} ${errorText}`); } } catch (error) { diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 6d8b834bd..f38756724 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -63,9 +63,6 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setAchievements(prev => [...prev, "win"]); } - // Логируем достижения перед отправкой - console.log("Текущие достижения перед отправкой:", achievements); - const updatedAchievements = achievements.filter(a => { // Логика для фильтрации достижений return true; // Пример, оставляем все достижения @@ -181,9 +178,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { // Обновляем достижения, если суперсила использована setAchievements(prevAchievements => { return prevAchievements.filter(achievement => { - // Логика для фильтрации достижений, если суперсила использована - // Например, очищаем достижения, если нужно - return false; // Пример, убираем все достижения + return false; }); }); @@ -299,6 +294,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { gameDurationSeconds={timer.seconds} gameDurationMinutes={timer.minutes} onClick={resetGame} + achievements={achievements} />
) : null} diff --git a/src/components/EndGameModal/EndGameModal.jsx b/src/components/EndGameModal/EndGameModal.jsx index 8bcd8c299..62354fc08 100644 --- a/src/components/EndGameModal/EndGameModal.jsx +++ b/src/components/EndGameModal/EndGameModal.jsx @@ -6,28 +6,31 @@ import { Link, useNavigate } from "react-router-dom"; import { useState } from "react"; import { updateLeaderboard } from "../../api"; -export function EndGameModal({ isWon, level, isHardMode, gameDurationSeconds, gameDurationMinutes, onClick }) { +export function EndGameModal({ + isWon, + level, + isHardMode, + gameDurationSeconds, + gameDurationMinutes, + onClick, + achievements, +}) { const title = isWon && level <= 2 ? "Вы победили!" : ""; const isLeader = isWon && isHardMode && level === 3; const lossTitle = !isWon ? "Вы проиграли!" : ""; const imgSrc = isWon ? celebrationImageUrl : deadImageUrl; const imgAlt = isWon ? "celebration emodji" : "dead emodji"; const navigate = useNavigate(); + const [username, setUsername] = useState(""); const handleSubmit = async () => { - console.log("HandleSubmit вызван"); - if (username.trim()) { - const score = 100; - const timeInSeconds = gameDurationMinutes * 60 + gameDurationSeconds; - - console.log("Имя пользователя перед отправкой:", username); - console.log("Тело запроса:", JSON.stringify({ name: username, score, time: timeInSeconds })); + const time = gameDurationMinutes * 60 + gameDurationSeconds; try { - await updateLeaderboard(username, score, timeInSeconds); // Ждём завершения обновления лидерборда - navigate("/leaderboard"); // Переход после успешного обновления + await updateLeaderboard(username, time, achievements); + navigate("/leaderboard"); } catch (error) { console.error("Ошибка при обновлении лидерборда:", error); alert("Не удалось обновить лидерборд, попробуйте снова."); @@ -39,7 +42,6 @@ export function EndGameModal({ isWon, level, isHardMode, gameDurationSeconds, ga const handleInputChange = event => { setUsername(event.target.value); - console.log("Текущий username:", event.target.value); }; return ( diff --git a/src/components/LeaderBoardPlayer/LeaderBoardPlayer.jsx b/src/components/LeaderBoardPlayer/LeaderBoardPlayer.jsx index 12aeb07ad..aeb1bfd25 100644 --- a/src/components/LeaderBoardPlayer/LeaderBoardPlayer.jsx +++ b/src/components/LeaderBoardPlayer/LeaderBoardPlayer.jsx @@ -20,19 +20,22 @@ export function LeaderBoardPlayer({ position, name, achievements = [], time }) { setIsBallVisible(false); }; + const hasPuzzle = achievements.includes(1); + const hasBall = achievements.includes(2); + return (
{position}
{name}
puzzle ball Date: Thu, 12 Sep 2024 00:38:38 +0200 Subject: [PATCH 24/25] =?UTF-8?q?=D0=A2=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20?= =?UTF-8?q?=D0=B0=D1=87=D0=B8=D0=B2=D0=BA=D0=B8=20=D0=B2=D0=BE=D0=BE=D0=B1?= =?UTF-8?q?=D1=89=D0=B5=20=D0=BD=D0=B5=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D1=8F=D1=8E=D1=82=D1=81=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api.js | 17 +++++++------ src/components/Cards/Cards.jsx | 25 ++++++++----------- src/components/EndGameModal/EndGameModal.jsx | 17 +++++++++++-- src/context/LeadersContext.jsx | 12 +++++++++ src/hooks/useLeaders.jsx | 6 +++++ src/pages/LeaderBoardPage/LeaderBoardPage.jsx | 2 +- 6 files changed, 53 insertions(+), 26 deletions(-) create mode 100644 src/context/LeadersContext.jsx create mode 100644 src/hooks/useLeaders.jsx diff --git a/src/api.js b/src/api.js index 28588bfe0..d26ba048a 100644 --- a/src/api.js +++ b/src/api.js @@ -10,20 +10,21 @@ export async function getPlayersList() { } // Получить ID достижений -function getAchievementIds(achievements) { - if (!Array.isArray(achievements)) { - return []; - } - return achievements; -} +// function getAchievementIds(achievements) { +// if (!Array.isArray(achievements)) { +// return []; +// } +// return achievements; +// } export async function updateLeaderboard(name, time, achievements) { - const achievementIds = getAchievementIds(achievements); + // const achievementIds = getAchievementIds(achievements); + // console.log("Achievements IDs in updateLeaderboard:", achievementIds); const requestBody = JSON.stringify({ name: name || "Пользователь", time, - achievements: achievementIds, + achievements, }); try { diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index f38756724..e59c394b0 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -57,25 +57,26 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const handleGameEnd = isWon => { const playerName = "Player"; const totalTime = timer.minutes * 60 + timer.seconds; // Подсчет времени + const cardAchievements = [...achievements]; - // Добавляем ачивку за выигрыш + if (isWon && isHardMode) { + cardAchievements.push(1); // Ачивка за сложный уровень + } + // Победа без использования суперсил if (isWon && !usedSuperpower) { - setAchievements(prev => [...prev, "win"]); + cardAchievements.push(2); // Ачивка за победу без суперсил } - const updatedAchievements = achievements.filter(a => { - // Логика для фильтрации достижений - return true; // Пример, оставляем все достижения - }); - // Обновляем лидерборд - updateLeaderboard(playerName, isWon ? 1 : 0, totalTime, updatedAchievements) + updateLeaderboard(playerName, isWon ? 1 : 0, totalTime, cardAchievements) .then(() => { console.log("Результаты игры успешно отправлены"); }) .catch(error => { console.error("Ошибка при отправке результатов игры:", error); }); + + console.log("Achievements IDs:", cardAchievements); }; function finishGame(status = STATUS_LOST) { @@ -98,6 +99,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setTries(3); setAchievements([]); setCanUsePower(true); // Сброс возможности использования суперсилы + setUsedSuperpower(false); } const openCard = clickedCard => { @@ -175,13 +177,6 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setCanUsePower(false); // Суперсила используется только один раз setUsedSuperpower(true); - // Обновляем достижения, если суперсила использована - setAchievements(prevAchievements => { - return prevAchievements.filter(achievement => { - return false; - }); - }); - setTimeout(() => { setIsRevealed(false); setIsPaused(false); // Возобновляем таймер через 5 секунд diff --git a/src/components/EndGameModal/EndGameModal.jsx b/src/components/EndGameModal/EndGameModal.jsx index 62354fc08..6855786e1 100644 --- a/src/components/EndGameModal/EndGameModal.jsx +++ b/src/components/EndGameModal/EndGameModal.jsx @@ -14,6 +14,7 @@ export function EndGameModal({ gameDurationMinutes, onClick, achievements, + hasUsedSuperPower, }) { const title = isWon && level <= 2 ? "Вы победили!" : ""; const isLeader = isWon && isHardMode && level === 3; @@ -21,13 +22,25 @@ export function EndGameModal({ const imgSrc = isWon ? celebrationImageUrl : deadImageUrl; const imgAlt = isWon ? "celebration emodji" : "dead emodji"; const navigate = useNavigate(); - - const [username, setUsername] = useState(""); + const [username, setUsername] = useState({ + name: "", + time: gameDurationSeconds, + achievements: achievements, + }); const handleSubmit = async () => { if (username.trim()) { const time = gameDurationMinutes * 60 + gameDurationSeconds; + if (isHardMode && !achievements.includes(1)) { + achievements.push(1); + } + + if (!hasUsedSuperPower && !achievements.includes(2)) { + achievements.push(2); + } + console.log("Achievements IDs before sending:", achievements); + try { await updateLeaderboard(username, time, achievements); navigate("/leaderboard"); diff --git a/src/context/LeadersContext.jsx b/src/context/LeadersContext.jsx new file mode 100644 index 000000000..128d1bd38 --- /dev/null +++ b/src/context/LeadersContext.jsx @@ -0,0 +1,12 @@ +import { createContext, useState } from "react"; + +export const LeadersContext = createContext(); + +export function LeadersProvider({ children }) { + const [leaders, setLeaders] = useState([]); + const [isLeader, setIsLeader] = useState(false); + + return ( + {children} + ); +} diff --git a/src/hooks/useLeaders.jsx b/src/hooks/useLeaders.jsx new file mode 100644 index 000000000..3ea44c599 --- /dev/null +++ b/src/hooks/useLeaders.jsx @@ -0,0 +1,6 @@ +import { useContext } from "react"; +import { LeadersContext } from "../context/LeadersContext"; + +export function useLeaders() { + return useContext(LeadersContext); +} diff --git a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx index dd7f5d77f..204bbd768 100644 --- a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx +++ b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx @@ -16,7 +16,7 @@ export function LeaderBoardPage() { const fetchData = async () => { try { const data = await getPlayersList(); - const filteredData = data.sort((a, b) => a.time - b.time).slice(0, 10); + const filteredData = data.sort((a, b) => a.time - b.time); setLeaderArray(filteredData); } catch (err) { setError("Не удалось загрузить данные"); From 635d2cea79cf7f41cfe1cac81283bc5e4eadc5c0 Mon Sep 17 00:00:00 2001 From: Karina Rozenberga Korneva Date: Sat, 14 Sep 2024 19:25:34 +0200 Subject: [PATCH 25/25] =?UTF-8?q?=D0=90=D1=87=D0=B8=D0=B2=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D1=8F=D1=8E=D1=82=D1=81?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Cards/Cards.jsx | 28 +-------------- src/components/Cards/Cards.module.css | 3 -- src/components/EndGameModal/EndGameModal.jsx | 34 +++++++++---------- .../LeaderBoardPlayer/LeaderBoardPlayer.jsx | 14 ++++---- src/pages/LeaderBoardPage/LeaderBoardPage.jsx | 2 +- 5 files changed, 26 insertions(+), 55 deletions(-) diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index e59c394b0..a89697f56 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -6,7 +6,6 @@ import { EndGameModal } from "../../components/EndGameModal/EndGameModal"; import { Button } from "../../components/Button/Button"; import { Card } from "../../components/Card/Card"; import { EasyContext } from "../../context/context"; -import { updateLeaderboard } from "../../api"; // Игра закончилась const STATUS_LOST = "STATUS_LOST"; @@ -54,35 +53,9 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const [isPaused, setIsPaused] = useState(false); // Состояние паузы таймера const [usedSuperpower, setUsedSuperpower] = useState(false); - const handleGameEnd = isWon => { - const playerName = "Player"; - const totalTime = timer.minutes * 60 + timer.seconds; // Подсчет времени - const cardAchievements = [...achievements]; - - if (isWon && isHardMode) { - cardAchievements.push(1); // Ачивка за сложный уровень - } - // Победа без использования суперсил - if (isWon && !usedSuperpower) { - cardAchievements.push(2); // Ачивка за победу без суперсил - } - - // Обновляем лидерборд - updateLeaderboard(playerName, isWon ? 1 : 0, totalTime, cardAchievements) - .then(() => { - console.log("Результаты игры успешно отправлены"); - }) - .catch(error => { - console.error("Ошибка при отправке результатов игры:", error); - }); - - console.log("Achievements IDs:", cardAchievements); - }; - function finishGame(status = STATUS_LOST) { setGameEndDate(new Date()); setStatus(status); - handleGameEnd(status === STATUS_WON); } function startGame() { const startDate = new Date(); @@ -290,6 +263,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { gameDurationMinutes={timer.minutes} onClick={resetGame} achievements={achievements} + hasUsedSuperPower={usedSuperpower} />
) : null} diff --git a/src/components/Cards/Cards.module.css b/src/components/Cards/Cards.module.css index 972e996a2..72e51a7b1 100644 --- a/src/components/Cards/Cards.module.css +++ b/src/components/Cards/Cards.module.css @@ -28,14 +28,11 @@ .header { display: flex; justify-content: space-between; - align-items: end; margin-bottom: 35px; } .timer { display: flex; - align-items: end; - color: #fff; font-family: StratosSkyeng; font-size: 64px; diff --git a/src/components/EndGameModal/EndGameModal.jsx b/src/components/EndGameModal/EndGameModal.jsx index 6855786e1..88bd89b9b 100644 --- a/src/components/EndGameModal/EndGameModal.jsx +++ b/src/components/EndGameModal/EndGameModal.jsx @@ -22,30 +22,28 @@ export function EndGameModal({ const imgSrc = isWon ? celebrationImageUrl : deadImageUrl; const imgAlt = isWon ? "celebration emodji" : "dead emodji"; const navigate = useNavigate(); - const [username, setUsername] = useState({ - name: "", - time: gameDurationSeconds, - achievements: achievements, - }); + const [username, setUsername] = useState(""); - const handleSubmit = async () => { - if (username.trim()) { - const time = gameDurationMinutes * 60 + gameDurationSeconds; + const handleGameEnd = async () => { + const totalTime = gameDurationMinutes * 60 + gameDurationSeconds; + const cardAchievements = [...achievements]; - if (isHardMode && !achievements.includes(1)) { - achievements.push(1); - } + if (isWon && isHardMode && !cardAchievements.includes(1)) { + console.log(achievements); + cardAchievements.push(1); // Ачивка за сложный уровень + } - if (!hasUsedSuperPower && !achievements.includes(2)) { - achievements.push(2); - } - console.log("Achievements IDs before sending:", achievements); + if (isWon && !hasUsedSuperPower && !cardAchievements.includes(2)) { + cardAchievements.push(2); // Ачивка за победу без суперсил + } + if (username.trim()) { try { - await updateLeaderboard(username, time, achievements); + await updateLeaderboard(username, totalTime, cardAchievements); + console.log("Результаты игры успешно отправлены"); navigate("/leaderboard"); } catch (error) { - console.error("Ошибка при обновлении лидерборда:", error); + console.error("Ошибка при отправке результатов игры:", error); alert("Не удалось обновить лидерборд, попробуйте снова."); } } else { @@ -72,7 +70,7 @@ export function EndGameModal({ value={username} onChange={handleInputChange} /> -
diff --git a/src/components/LeaderBoardPlayer/LeaderBoardPlayer.jsx b/src/components/LeaderBoardPlayer/LeaderBoardPlayer.jsx index aeb1bfd25..6a251f87e 100644 --- a/src/components/LeaderBoardPlayer/LeaderBoardPlayer.jsx +++ b/src/components/LeaderBoardPlayer/LeaderBoardPlayer.jsx @@ -23,22 +23,24 @@ export function LeaderBoardPlayer({ position, name, achievements = [], time }) { const hasPuzzle = achievements.includes(1); const hasBall = achievements.includes(2); + console.log("Achievements for player", name, achievements); + return (
{position}
{name}
puzzle ball {isPuzzleVisible && } {isBallVisible && } diff --git a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx index 204bbd768..dd7f5d77f 100644 --- a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx +++ b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx @@ -16,7 +16,7 @@ export function LeaderBoardPage() { const fetchData = async () => { try { const data = await getPlayersList(); - const filteredData = data.sort((a, b) => a.time - b.time); + const filteredData = data.sort((a, b) => a.time - b.time).slice(0, 10); setLeaderArray(filteredData); } catch (err) { setError("Не удалось загрузить данные");