From d0e24c5b1ebc0ef2d2a5b1922e2436392ed67797 Mon Sep 17 00:00:00 2001 From: ax1lebafer Date: Thu, 27 Jun 2024 00:04:58 +0400 Subject: [PATCH 01/18] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BB=20=D0=B2=D1=8B=D0=B1=D0=BE=D1=80=20?= =?UTF-8?q?=D1=83=D1=80=D0=BE=D0=B2=D0=BD=D0=B5=D0=B9=20=D0=B8=20=D0=BA?= =?UTF-8?q?=D0=BD=D0=BE=D0=BF=D0=BA=D1=83=20"=D0=A1=D1=82=D0=B0=D1=80?= =?UTF-8?q?=D1=82"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 ++ src/components/Cards/Cards.module.css | 4 +- src/pages/SelectLevelPage/SelectLevelPage.jsx | 44 +++++++---- .../SelectLevelPage.module.css | 74 ++++++++++++++++++- 4 files changed, 108 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 9b90842c4..76964a1db 100644 --- a/README.md +++ b/README.md @@ -44,3 +44,8 @@ https://skypro-web-developer.github.io/react-memo/ Запускает eslint проверку кода, эта же команда запускается перед каждым коммитом. Если не получается закоммитить, попробуйте запустить эту команду и исправить все ошибки и предупреждения. + +### Затраченное время + +Продполагаемое время: 8 часов вместе с ознакомлением кода +Фактически затраченное время: 1 час 35 минут \ No newline at end of file diff --git a/src/components/Cards/Cards.module.css b/src/components/Cards/Cards.module.css index 000c5006c..4f17cd060 100644 --- a/src/components/Cards/Cards.module.css +++ b/src/components/Cards/Cards.module.css @@ -28,13 +28,13 @@ .header { display: flex; justify-content: space-between; - align-items: end; + align-items: flex-end; margin-bottom: 35px; } .timer { display: flex; - align-items: end; + align-items: flex-end; color: #fff; font-family: StratosSkyeng; diff --git a/src/pages/SelectLevelPage/SelectLevelPage.jsx b/src/pages/SelectLevelPage/SelectLevelPage.jsx index 758942e51..8bf85ae82 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.jsx +++ b/src/pages/SelectLevelPage/SelectLevelPage.jsx @@ -1,28 +1,40 @@ -import { Link } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; import styles from "./SelectLevelPage.module.css"; +import { useState } from "react"; export function SelectLevelPage() { + const [selectedLevel, setSelectedLevel] = useState(null); + const navigate = useNavigate(); + + const handleCheckboxChange = level => { + setSelectedLevel(level); + }; + + const handleStartClick = () => { + if (selectedLevel !== null) { + navigate(`/game/${selectedLevel}`); + } else { + alert("Выберите уровень перед началом игры"); + } + }; + return (

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

    -
  • - - 1 - -
  • -
  • - - 2 - -
  • -
  • - - 3 - -
  • + {[3, 6, 9].map((level, index) => ( +
  • + +
  • + ))}
+
); diff --git a/src/pages/SelectLevelPage/SelectLevelPage.module.css b/src/pages/SelectLevelPage/SelectLevelPage.module.css index 390ac0def..9468b4e5d 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.module.css +++ b/src/pages/SelectLevelPage/SelectLevelPage.module.css @@ -33,7 +33,7 @@ flex-direction: row; gap: 26px; margin-top: 48px; - margin-bottom: 28px; + margin-bottom: 67px; } .level { @@ -46,6 +46,19 @@ border-radius: 12px; background: #fff; + + box-sizing: border-box; + cursor: pointer; + align-items: center; +} + +.level:hover { + border: 2px solid blue; +} + +.selected { + border: 2px solid blue; + background: #ffe0a6; } .levelLink { @@ -62,3 +75,62 @@ .levelLink:visited { color: #0080c1; } + +.checkboxButton { + user-select: none; + position: relative; + box-sizing: border-box; + cursor: pointer; +} + +.checkboxButton input[type=checkbox] { + z-index: -1; + opacity: 0; + display: block; + width: 0; + height: 0; + box-sizing: border-box; + +} + +.checkboxButton span { + display: inline-block; + cursor: pointer; + padding: 0px 10px; + /* border: 1px solid #999; */ + border-radius: 4px; + transition: background 0.2s ease; + + color: #0080c1; + text-align: center; + font-family: StratosSkyeng; + font-size: 64px; + font-style: normal; + font-weight: 400; + line-height: 72px; + text-decoration: none; +} + +.checkboxButton input[type=checkbox]:checked + span { + background: #ffe0a6; +} + +.buttonStart { + width: 246px; + height: 48px; + font-family: StratosSkyeng; + font-size: 24px; + font-weight: 400; + line-height: 32px; + letter-spacing: 0%; + background: #7ac100; + outline: none; + cursor: pointer; + border: none; + border-radius: 12px; + color: #fff; +} + +.buttonStart:hover { + background: #7ac100cc; +} \ No newline at end of file From fd5c4387461e2cae81b1d50a56f07513a77ee033 Mon Sep 17 00:00:00 2001 From: ax1lebafer Date: Fri, 28 Jun 2024 00:00:41 +0400 Subject: [PATCH 02/18] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BB=20=D0=BB=D0=B5=D0=B3=D0=BA=D0=B8=D0=B9?= =?UTF-8?q?=20=D1=80=D0=B5=D0=B6=D0=B8=D0=BC=20=D0=B8=D0=B3=D1=80=D1=8B=20?= =?UTF-8?q?=D1=81=203=20=D0=BF=D0=BE=D0=BF=D1=8B=D1=82=D0=BA=D0=B0=D0=BC?= =?UTF-8?q?=D0=B8=20=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BB=20=D0=B2=D1=8B=D0=B1=D0=BE=D1=80=20=D1=8D=D1=82=D0=BE?= =?UTF-8?q?=D0=B3=D0=BE=20=D1=80=D0=B5=D0=B6=D0=B8=D0=BC=D0=B0=20=D0=BF?= =?UTF-8?q?=D0=BE=D1=81=D1=80=D0=B5=D0=B4=D1=81=D1=82=D0=B2=D0=BE=D0=BC=20?= =?UTF-8?q?=D0=BA=D0=BB=D0=B8=D0=BA=D0=B0=20=D0=BD=D0=B0=20=D1=87=D0=B5?= =?UTF-8?q?=D0=BA=D0=B1=D0=BE=D0=BA=D1=81=20=D0=98=D1=81=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D0=BB=20useContext=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B4=D0=B0=D1=87=D0=B8?= =?UTF-8?q?=20=D0=BD=D0=B5=D0=BE=D0=B1=D1=85=D0=BE=D0=B4=D0=B8=D0=BC=D1=8B?= =?UTF-8?q?=D1=85=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B2=20=D0=BA?= =?UTF-8?q?=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=D1=8B=20=D0=A1?= =?UTF-8?q?=D1=82=D0=B8=D0=BB=D0=B8=D0=B7=D0=BE=D0=B2=D0=B0=D0=BB=20=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D1=81=D1=82=D0=BA=D1=83=20=D0=9D=D0=B0=D1=87=D0=B0?= =?UTF-8?q?=D0=BB=20=D0=BF=D0=B8=D1=81=D0=B0=D1=82=D1=8C=20=D0=BB=D0=BE?= =?UTF-8?q?=D0=B3=D0=B8=D0=BA=D1=83=20=D0=B4=D0=BB=D1=8F=20=D0=BB=D0=B5?= =?UTF-8?q?=D0=B3=D0=BA=D0=BE=D0=B3=D0=BE=20=D1=80=D0=B5=D0=B6=D0=B8=D0=BC?= =?UTF-8?q?=D0=B0=20=D0=B8=D0=B3=D1=80=D1=8B=20(=D0=B2=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D1=86=D0=B5=D1=81=D1=81=D0=B5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/components/Cards/Cards.jsx | 21 +++++++++++++++++-- src/components/Cards/Cards.module.css | 10 +++++++++ src/contexts/EasyModeContext.jsx | 9 ++++++++ src/hooks/useEasyMode.jsx | 6 ++++++ src/index.js | 5 ++++- src/pages/SelectLevelPage/SelectLevelPage.jsx | 10 +++++++++ .../SelectLevelPage.module.css | 21 ++++++++++++++++--- 8 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 src/contexts/EasyModeContext.jsx create mode 100644 src/hooks/useEasyMode.jsx diff --git a/README.md b/README.md index 76964a1db..9a8be9730 100644 --- a/README.md +++ b/README.md @@ -48,4 +48,4 @@ https://skypro-web-developer.github.io/react-memo/ ### Затраченное время Продполагаемое время: 8 часов вместе с ознакомлением кода -Фактически затраченное время: 1 час 35 минут \ No newline at end of file +Фактически затраченное время: 3 часа 25 минут \ No newline at end of file diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 7526a56c8..ac43301df 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -5,6 +5,7 @@ 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 { useEasyMode } from "../../hooks/useEasyMode"; // Игра закончилась const STATUS_LOST = "STATUS_LOST"; @@ -41,6 +42,9 @@ function getTimerValue(startDate, endDate) { * previewSeconds - сколько секунд пользователь будет видеть все карты открытыми до начала игры */ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { + const { isEasyMode } = useEasyMode(); + const [lives, setLives] = useState(isEasyMode ? 3 : 1); + // В cards лежит игровое поле - массив карт и их состояние открыта\закрыта const [cards, setCards] = useState([]); // Текущий статус игры @@ -127,8 +131,13 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { // "Игрок проиграл", т.к на поле есть две открытые карты без пары if (playerLost) { - finishGame(STATUS_LOST); - return; + if (!isEasyMode) { + finishGame(STATUS_LOST); + return; + } else { + // Функция закрытия карточек + setLives(prevState => prevState - 1); + } } // ... игра продолжается @@ -172,6 +181,12 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { }; }, [gameStartDate, gameEndDate]); + useEffect(() => { + if (lives === 0) { + finishGame(STATUS_LOST); + } + }, [lives]); + return (
@@ -210,6 +225,8 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { ))}
+ {isEasyMode && Осталось попыток: {lives}} + {isGameEnded ? (
{children}; +} diff --git a/src/hooks/useEasyMode.jsx b/src/hooks/useEasyMode.jsx new file mode 100644 index 000000000..05873ce6a --- /dev/null +++ b/src/hooks/useEasyMode.jsx @@ -0,0 +1,6 @@ +import { useContext } from "react"; +import { EasyModeContext } from "../contexts/EasyModeContext"; + +export function useEasyMode() { + return useContext(EasyModeContext); +} diff --git a/src/index.js b/src/index.js index f689c5f0b..8eaaddb18 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 { EasyModeProvider } from "./contexts/EasyModeContext"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( - + + + , ); diff --git a/src/pages/SelectLevelPage/SelectLevelPage.jsx b/src/pages/SelectLevelPage/SelectLevelPage.jsx index 8bf85ae82..2a3d0af60 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.jsx +++ b/src/pages/SelectLevelPage/SelectLevelPage.jsx @@ -1,10 +1,16 @@ import { useNavigate } from "react-router-dom"; import styles from "./SelectLevelPage.module.css"; import { useState } from "react"; +import { useEasyMode } from "../../hooks/useEasyMode"; export function SelectLevelPage() { const [selectedLevel, setSelectedLevel] = useState(null); const navigate = useNavigate(); + const { isEasyMode, setIsEasyMode } = useEasyMode(); + + const handleEasyModeChange = event => { + setIsEasyMode(event.target.checked); + }; const handleCheckboxChange = level => { setSelectedLevel(level); @@ -32,6 +38,10 @@ export function SelectLevelPage() { ))} + diff --git a/src/pages/SelectLevelPage/SelectLevelPage.module.css b/src/pages/SelectLevelPage/SelectLevelPage.module.css index 9468b4e5d..2792f66e1 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.module.css +++ b/src/pages/SelectLevelPage/SelectLevelPage.module.css @@ -33,7 +33,7 @@ flex-direction: row; gap: 26px; margin-top: 48px; - margin-bottom: 67px; + margin-bottom: 30px; } .level { @@ -111,8 +111,23 @@ text-decoration: none; } -.checkboxButton input[type=checkbox]:checked + span { - background: #ffe0a6; +.checkboxButton input[type=checkbox]:checked+span { + background: #ffe0a6; +} + +.checkboxMode { + font-family: StratosSkyeng; + font-size: 24px; + font-style: normal; + font-weight: 400; + margin-bottom: 25px; +} + +.checkboxMode input[type=checkbox] { + display: inline-block; + width: 20px; + height: 20px; + margin-right: 7px; } .buttonStart { From 9b7962a9df53641ed8eaa6199681e5e35d52ef11 Mon Sep 17 00:00:00 2001 From: ax1lebafer Date: Sun, 30 Jun 2024 23:09:59 +0400 Subject: [PATCH 03/18] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BE=D0=B1=D0=BD=D1=83=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=20=D1=81=D1=87=D0=B5=D1=82=D1=87=D0=B8=D0=BA=D0=B0=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=BF=D1=8B=D1=82=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/components/Cards/Cards.jsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9a8be9730..af49308c5 100644 --- a/README.md +++ b/README.md @@ -48,4 +48,4 @@ https://skypro-web-developer.github.io/react-memo/ ### Затраченное время Продполагаемое время: 8 часов вместе с ознакомлением кода -Фактически затраченное время: 3 часа 25 минут \ No newline at end of file +Фактически затраченное время: 5 часов 40 минут \ No newline at end of file diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index ac43301df..15293a5ee 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -77,6 +77,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setGameEndDate(null); setTimer(getTimerValue(null, null)); setStatus(STATUS_PREVIEW); + setLives(isEasyMode ? 3 : 1); } /** From 7d0f1096d3f57d3d9d329b26b20dd8f08d41e80c Mon Sep 17 00:00:00 2001 From: ax1lebafer Date: Sun, 30 Jun 2024 23:34:03 +0400 Subject: [PATCH 04/18] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BB=20=D0=B7=D0=B0=D0=BA=D1=80=D1=8B=D1=82?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BD=D0=B5=D1=83=D0=B3=D0=B0=D0=B4=D0=B0=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D1=85=20=D0=BA=D0=B0=D1=80=D1=82,=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=20=D1=8D=D1=82=D0=BE=D0=BC=20=D1=83=D0=B3=D0=B0=D0=B4?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D1=8B=D0=B5=20=D0=BA=D0=B0=D1=80=D1=82=D0=BE?= =?UTF-8?q?=D1=87=D0=BA=D0=B8=20=D0=BE=D1=81=D1=82=D0=B0=D1=8E=D1=82=D1=81?= =?UTF-8?q?=D1=8F=20=D0=BE=D1=82=D0=BA=D1=80=D1=8B=D1=82=D1=8B=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/components/Cards/Cards.jsx | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index af49308c5..c030d77e6 100644 --- a/README.md +++ b/README.md @@ -48,4 +48,4 @@ https://skypro-web-developer.github.io/react-memo/ ### Затраченное время Продполагаемое время: 8 часов вместе с ознакомлением кода -Фактически затраченное время: 5 часов 40 минут \ No newline at end of file +Фактически затраченное время: 6 часов 40 минут \ No newline at end of file diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 15293a5ee..bc0a915a8 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -36,6 +36,16 @@ function getTimerValue(startDate, endDate) { }; } +function closeUnmatchedCards(setCards, openCardsWithoutPair) { + setTimeout(() => { + setCards(currentCards => + currentCards.map(card => + openCardsWithoutPair.some(openCard => openCard.id === card.id) ? { ...card, open: false } : card, + ), + ); + }, 1000); +} + /** * Основной компонент игры, внутри него находится вся игровая механика и логика. * pairsCount - сколько пар будет в игре @@ -138,9 +148,9 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { } else { // Функция закрытия карточек setLives(prevState => prevState - 1); + closeUnmatchedCards(setCards, openCardsWithoutPair); } } - // ... игра продолжается }; From 7b9d722e29553162527ebcd75d0525996848558f Mon Sep 17 00:00:00 2001 From: ax1lebafer Date: Wed, 3 Jul 2024 18:00:56 +0400 Subject: [PATCH 05/18] =?UTF-8?q?=D0=92=D1=8B=D0=BD=D0=B5=D1=81=20=D0=B7?= =?UTF-8?q?=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=D0=BF?= =?UTF-8?q?=D1=8B=D1=82=D0=BE=D0=BA=20=D0=B2=20=D0=BA=D0=BE=D0=BD=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=BD=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Cards/Cards.jsx | 5 +++-- src/const.js | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index bc0a915a8..bc1d525e2 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -6,6 +6,7 @@ import { EndGameModal } from "../../components/EndGameModal/EndGameModal"; import { Button } from "../../components/Button/Button"; import { Card } from "../../components/Card/Card"; import { useEasyMode } from "../../hooks/useEasyMode"; +import { DEFAULT_MODE_LIVES, EASY_MODE_LIVES } from "../../const"; // Игра закончилась const STATUS_LOST = "STATUS_LOST"; @@ -53,7 +54,7 @@ function closeUnmatchedCards(setCards, openCardsWithoutPair) { */ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const { isEasyMode } = useEasyMode(); - const [lives, setLives] = useState(isEasyMode ? 3 : 1); + const [lives, setLives] = useState(isEasyMode ? EASY_MODE_LIVES : DEFAULT_MODE_LIVES); // В cards лежит игровое поле - массив карт и их состояние открыта\закрыта const [cards, setCards] = useState([]); @@ -87,7 +88,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setGameEndDate(null); setTimer(getTimerValue(null, null)); setStatus(STATUS_PREVIEW); - setLives(isEasyMode ? 3 : 1); + setLives(isEasyMode ? EASY_MODE_LIVES : DEFAULT_MODE_LIVES); } /** diff --git a/src/const.js b/src/const.js index 9a101d0b9..b8feddd8d 100644 --- a/src/const.js +++ b/src/const.js @@ -2,3 +2,5 @@ export const SPADES_SUIT = "SPADES"; export const CROSS_SUIT = "CROSS"; export const DIAMONDS_SUIT = "DIAMONDS"; export const HEARTS_SUIT = "HEARTS"; +export const EASY_MODE_LIVES = 3; +export const DEFAULT_MODE_LIVES = 1; From 70fe5d4510f90827234b0ca0ffbf1c4913e13553 Mon Sep 17 00:00:00 2001 From: ax1lebafer Date: Wed, 3 Jul 2024 19:15:29 +0400 Subject: [PATCH 06/18] =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0?= =?UTF-8?q?=D0=BB=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8=20API,=20?= =?UTF-8?q?=D0=BD=D0=B0=D1=87=D0=B0=D0=BB=20=D0=B4=D0=B5=D0=BB=D0=B0=D1=82?= =?UTF-8?q?=D1=8C=20=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=83=20?= =?UTF-8?q?=D0=BB=D0=B8=D0=B4=D0=B5=D1=80=D0=B1=D0=BE=D1=80=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 ++- src/api.js | 31 ++++++++++++ src/components/Leaderboard/Leaderboard.jsx | 29 +++++++++++ .../Leaderboard/Leaderboard.module.css | 48 +++++++++++++++++++ .../LeaderboardPage/LeaderboardPages.jsx | 5 ++ src/pages/SelectLevelPage/SelectLevelPage.jsx | 5 +- .../SelectLevelPage.module.css | 9 ++++ src/router.js | 5 ++ 8 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 src/api.js create mode 100644 src/components/Leaderboard/Leaderboard.jsx create mode 100644 src/components/Leaderboard/Leaderboard.module.css create mode 100644 src/pages/LeaderboardPage/LeaderboardPages.jsx diff --git a/README.md b/README.md index c030d77e6..12add2a7c 100644 --- a/README.md +++ b/README.md @@ -47,5 +47,10 @@ https://skypro-web-developer.github.io/react-memo/ ### Затраченное время +1 спринт: Продполагаемое время: 8 часов вместе с ознакомлением кода -Фактически затраченное время: 6 часов 40 минут \ No newline at end of file +Фактически затраченное время: 6 часов 40 минут + +2 спринт: +Продполагаемое время: 5 часов +Фактически затраченное время: 1 час 10 минут diff --git a/src/api.js b/src/api.js new file mode 100644 index 000000000..2183ebba8 --- /dev/null +++ b/src/api.js @@ -0,0 +1,31 @@ +const host = "https://wedev-api.sky.pro/api/leaderboard"; + +export async function getLeaders() { + const response = await fetch(host, { + method: "GET", + }); + + if (!response.ok) { + throw new Error("Ошибка сервера"); + } + + const data = await response.json(); + return data; +} + +export async function postLeader({ name, time }) { + const response = await fetch(host, { + method: "POST", + body: JSON.stringify({ + name: name, + time: time, + }), + }); + + if (!response.ok) { + throw new Error("Ошибка сервера"); + } + + const data = await response.json(); + return data; +} diff --git a/src/components/Leaderboard/Leaderboard.jsx b/src/components/Leaderboard/Leaderboard.jsx new file mode 100644 index 000000000..1ab43f646 --- /dev/null +++ b/src/components/Leaderboard/Leaderboard.jsx @@ -0,0 +1,29 @@ +import { Button } from "../Button/Button"; +import styles from "./Leaderboard.module.css"; + +export function Leaderboard() { + return ( +
+
+

Лидерборд

+ +
+
+
+
+

Позиция

+

Пользователь

+
+

Время

+
+
+
+

#1

+

ab98awj_918mlz1lavfh_ru

+
+

01:30

+
+
+
+ ); +} diff --git a/src/components/Leaderboard/Leaderboard.module.css b/src/components/Leaderboard/Leaderboard.module.css new file mode 100644 index 000000000..1c71a4013 --- /dev/null +++ b/src/components/Leaderboard/Leaderboard.module.css @@ -0,0 +1,48 @@ +.leaderboard { + padding-left: calc(50% - 475px); + padding-right: calc(50% - 475px); + padding-top: 50px; +} + +.header { + display: flex; + justify-content: space-between; + align-items: center; + flex-direction: row; + flex-wrap: wrap; + margin-bottom: 40px; +} + +.headerTitle { + color: rgb(255, 255, 255); + font-family: StratosSkyeng; + font-size: 24px; + font-weight: 400; +} + +.section { + display: flex; + gap: 15px; + flex-direction: column; +} + +.sectionTop { + background-color: #FFFFFF; + border-radius: 12px; + display: flex; + padding: 16px 20px; + justify-content: space-between; +} + +.sectionTop div { + display: flex; + justify-content: space-between; + width: 400px; +} + +.sectionText { + color: rgb(153, 153, 153); + font-family: StratosSkyeng; + font-size: 24px; + font-weight: 400; +} \ No newline at end of file diff --git a/src/pages/LeaderboardPage/LeaderboardPages.jsx b/src/pages/LeaderboardPage/LeaderboardPages.jsx new file mode 100644 index 000000000..2e1a73a1a --- /dev/null +++ b/src/pages/LeaderboardPage/LeaderboardPages.jsx @@ -0,0 +1,5 @@ +import { Leaderboard } from "../../components/Leaderboard/Leaderboard"; + +export function LeaderboardPage() { + return ; +} diff --git a/src/pages/SelectLevelPage/SelectLevelPage.jsx b/src/pages/SelectLevelPage/SelectLevelPage.jsx index 2a3d0af60..cc7316844 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.jsx +++ b/src/pages/SelectLevelPage/SelectLevelPage.jsx @@ -1,4 +1,4 @@ -import { useNavigate } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import styles from "./SelectLevelPage.module.css"; import { useState } from "react"; import { useEasyMode } from "../../hooks/useEasyMode"; @@ -45,6 +45,9 @@ export function SelectLevelPage() { + + Перейти к лидерборду +
); diff --git a/src/pages/SelectLevelPage/SelectLevelPage.module.css b/src/pages/SelectLevelPage/SelectLevelPage.module.css index 2792f66e1..d54bb61b8 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.module.css +++ b/src/pages/SelectLevelPage/SelectLevelPage.module.css @@ -148,4 +148,13 @@ .buttonStart:hover { background: #7ac100cc; +} + +.leaderboardLink { + color: rgb(0, 73, 128); + font-family: StratosSkyeng; + font-size: 18px; + font-weight: 400; + line-height: 32px; + margin-top: 18px; } \ No newline at end of file diff --git a/src/router.js b/src/router.js index da6e94b51..d19418f24 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/LeaderboardPages"; export const router = createBrowserRouter( [ @@ -12,6 +13,10 @@ export const router = createBrowserRouter( path: "/game/:pairsCount", element: , }, + { + path: "/leaderboard", + element: , + }, ], /** * basename нужен для корректной работы в gh pages From c7894e8cbb0a40e2a6482b811c6482201e44bf24 Mon Sep 17 00:00:00 2001 From: ax1lebafer Date: Fri, 5 Jul 2024 23:01:46 +0400 Subject: [PATCH 07/18] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=83=20=D0=BB=D0=B8?= =?UTF-8?q?=D0=B4=D0=B5=D1=80=D0=B1=D0=BE=D1=80=D0=B4=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- package-lock.json | 15 +++++++ package.json | 1 + src/components/Leaderboard/Leaderboard.jsx | 41 +++++++++++++------ .../Leaderboard/Leaderboard.module.css | 14 ++++++- .../LeaderboardRow/LeaderboardRow.jsx | 23 +++++++++++ .../LeaderboardRow/LeaderboardRow.module.css | 32 +++++++++++++++ src/contexts/LeadersContext.jsx | 9 ++++ src/hooks/useLeaders.jsx | 6 +++ src/index.js | 9 ++-- ...aderboardPages.jsx => LeaderboardPage.jsx} | 0 src/router.js | 2 +- 12 files changed, 135 insertions(+), 19 deletions(-) create mode 100644 src/components/LeaderboardRow/LeaderboardRow.jsx create mode 100644 src/components/LeaderboardRow/LeaderboardRow.module.css create mode 100644 src/contexts/LeadersContext.jsx create mode 100644 src/hooks/useLeaders.jsx rename src/pages/LeaderboardPage/{LeaderboardPages.jsx => LeaderboardPage.jsx} (100%) diff --git a/README.md b/README.md index 12add2a7c..3f971d3f9 100644 --- a/README.md +++ b/README.md @@ -53,4 +53,4 @@ https://skypro-web-developer.github.io/react-memo/ 2 спринт: Продполагаемое время: 5 часов -Фактически затраченное время: 1 час 10 минут +Фактически затраченное время: 2 час 40 минут 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/src/components/Leaderboard/Leaderboard.jsx b/src/components/Leaderboard/Leaderboard.jsx index 1ab43f646..f4531a927 100644 --- a/src/components/Leaderboard/Leaderboard.jsx +++ b/src/components/Leaderboard/Leaderboard.jsx @@ -1,28 +1,43 @@ +import { Link } from "react-router-dom"; import { Button } from "../Button/Button"; +import { LeaderboardRow } from "../LeaderboardRow/LeaderboardRow"; import styles from "./Leaderboard.module.css"; +import cn from "classnames"; +import { useEffect } from "react"; +import { getLeaders } from "../../api"; +import { useLeaders } from "../../hooks/useLeaders"; export function Leaderboard() { + const { leaders, setLeaders } = useLeaders(); + + useEffect(() => { + getLeaders().then(response => { + const sortedLeaders = response.leaders.sort((a, b) => a.time - b.time); + setLeaders(sortedLeaders); + }); + }, [setLeaders]); + + console.log(leaders); + return (

Лидерборд

- + + +
-
-
-

Позиция

-

Пользователь

-
-

Время

-
-
+
-

#1

-

ab98awj_918mlz1lavfh_ru

+

Позиция

+

Пользователь

-

01:30

-
+

Время

+
+ {leaders.map((leader, index) => ( + + ))} ); diff --git a/src/components/Leaderboard/Leaderboard.module.css b/src/components/Leaderboard/Leaderboard.module.css index 1c71a4013..8ca32f0e1 100644 --- a/src/components/Leaderboard/Leaderboard.module.css +++ b/src/components/Leaderboard/Leaderboard.module.css @@ -37,7 +37,7 @@ .sectionTop div { display: flex; justify-content: space-between; - width: 400px; + gap: 66px; } .sectionText { @@ -45,4 +45,16 @@ font-family: StratosSkyeng; font-size: 24px; font-weight: 400; +} + +.textPosition { + width: 178px; +} + +.textUser { + width: 324px; +} + +.textTime { + width: 102px; } \ No newline at end of file diff --git a/src/components/LeaderboardRow/LeaderboardRow.jsx b/src/components/LeaderboardRow/LeaderboardRow.jsx new file mode 100644 index 000000000..49f77f8f5 --- /dev/null +++ b/src/components/LeaderboardRow/LeaderboardRow.jsx @@ -0,0 +1,23 @@ +import styles from "./LeaderboardRow.module.css"; +import cn from "classnames"; +import { format } from "date-fns"; + +function formatSeconds(seconds) { + const date = new Date(0); + date.setSeconds(seconds); + return date; +} + +export function LeaderboardRow({ position, userName, time }) { + const formattedTime = format(formatSeconds(time), "mm:ss"); + + return ( +
+
+

{position}

+

{userName}

+
+

{formattedTime}

+
+ ); +} diff --git a/src/components/LeaderboardRow/LeaderboardRow.module.css b/src/components/LeaderboardRow/LeaderboardRow.module.css new file mode 100644 index 000000000..d7ddee69c --- /dev/null +++ b/src/components/LeaderboardRow/LeaderboardRow.module.css @@ -0,0 +1,32 @@ +.sectionTop { + background-color: #FFFFFF; + border-radius: 12px; + display: flex; + padding: 16px 20px; + justify-content: space-between; +} + +.sectionTop div { + display: flex; + justify-content: space-between; + gap: 66px; +} + +.sectionText { + color: #000000; + font-family: StratosSkyeng; + font-size: 24px; + font-weight: 400; +} + +.textPosition { + width: 178px; +} + +.textUser { + width: 324px; +} + +.textTime { + width: 102px; +} \ No newline at end of file diff --git a/src/contexts/LeadersContext.jsx b/src/contexts/LeadersContext.jsx new file mode 100644 index 000000000..9eb35d348 --- /dev/null +++ b/src/contexts/LeadersContext.jsx @@ -0,0 +1,9 @@ +import { createContext, useState } from "react"; + +export const LeadersContext = createContext(); + +export function LeadersProvider({ children }) { + const [leaders, setLeaders] = useState([]); + + return {children}; +} diff --git a/src/hooks/useLeaders.jsx b/src/hooks/useLeaders.jsx new file mode 100644 index 000000000..c6bbc94a5 --- /dev/null +++ b/src/hooks/useLeaders.jsx @@ -0,0 +1,6 @@ +import { useContext } from "react"; +import { LeadersContext } from "../contexts/LeadersContext"; + +export function useLeaders() { + return useContext(LeadersContext); +} diff --git a/src/index.js b/src/index.js index 8eaaddb18..02b8a6de9 100644 --- a/src/index.js +++ b/src/index.js @@ -4,12 +4,15 @@ import "./index.css"; import { RouterProvider } from "react-router-dom"; import { router } from "./router"; import { EasyModeProvider } from "./contexts/EasyModeContext"; +import { LeadersProvider } from "./contexts/LeadersContext"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( - - - + + + + + , ); diff --git a/src/pages/LeaderboardPage/LeaderboardPages.jsx b/src/pages/LeaderboardPage/LeaderboardPage.jsx similarity index 100% rename from src/pages/LeaderboardPage/LeaderboardPages.jsx rename to src/pages/LeaderboardPage/LeaderboardPage.jsx diff --git a/src/router.js b/src/router.js index d19418f24..ecd8d90ae 100644 --- a/src/router.js +++ b/src/router.js @@ -1,7 +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/LeaderboardPages"; +import { LeaderboardPage } from "./pages/LeaderboardPage/LeaderboardPage"; export const router = createBrowserRouter( [ From 8609b4768bd6b0a70edec452e9e88ff6c69a9885 Mon Sep 17 00:00:00 2001 From: ax1lebafer Date: Fri, 5 Jul 2024 23:10:19 +0400 Subject: [PATCH 08/18] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D0=BE=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=83=D1=81=D1=82=D1=8B=D1=85=20?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=BE=D0=BA=20=D0=B2=20=D0=B8=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D1=82=D0=B5=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/components/Leaderboard/Leaderboard.jsx | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3f971d3f9..1c7d027f6 100644 --- a/README.md +++ b/README.md @@ -53,4 +53,4 @@ https://skypro-web-developer.github.io/react-memo/ 2 спринт: Продполагаемое время: 5 часов -Фактически затраченное время: 2 час 40 минут +Фактически затраченное время: 2 час 50 минут diff --git a/src/components/Leaderboard/Leaderboard.jsx b/src/components/Leaderboard/Leaderboard.jsx index f4531a927..ac2764d7c 100644 --- a/src/components/Leaderboard/Leaderboard.jsx +++ b/src/components/Leaderboard/Leaderboard.jsx @@ -12,7 +12,12 @@ export function Leaderboard() { useEffect(() => { getLeaders().then(response => { - const sortedLeaders = response.leaders.sort((a, b) => a.time - b.time); + const sortedLeaders = response.leaders + .map(leader => ({ + ...leader, + name: leader.name.trim() === "" ? "Пользователь" : leader.name, + })) + .sort((a, b) => a.time - b.time); setLeaders(sortedLeaders); }); }, [setLeaders]); From d5253c83cdc744125b0506d21fd8646feafd79f5 Mon Sep 17 00:00:00 2001 From: ax1lebafer Date: Sat, 6 Jul 2024 02:29:31 +0400 Subject: [PATCH 09/18] =?UTF-8?q?=D0=9D=D0=B0=D1=87=D0=B0=D0=BB=20=D1=80?= =?UTF-8?q?=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE=D0=B2=D1=8B=D0=B2=D0=B0?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D1=83=20=D0=B4?= =?UTF-8?q?=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=B2=20=D0=BB=D0=B8=D0=B4=D0=B5=D1=80=D0=B1=D0=BE?= =?UTF-8?q?=D1=80=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/components/EndGameModal/EndGameModal.jsx | 39 ++++++++++++++++++- .../EndGameModal/EndGameModal.module.css | 31 ++++++++++++++- src/components/Leaderboard/Leaderboard.jsx | 18 +-------- src/pages/SelectLevelPage/SelectLevelPage.jsx | 18 ++++++++- 5 files changed, 86 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 1c7d027f6..4dd7fc451 100644 --- a/README.md +++ b/README.md @@ -53,4 +53,4 @@ https://skypro-web-developer.github.io/react-memo/ 2 спринт: Продполагаемое время: 5 часов -Фактически затраченное время: 2 час 50 минут +Фактически затраченное время: 4 часа 30 минут diff --git a/src/components/EndGameModal/EndGameModal.jsx b/src/components/EndGameModal/EndGameModal.jsx index 722394833..f2c474512 100644 --- a/src/components/EndGameModal/EndGameModal.jsx +++ b/src/components/EndGameModal/EndGameModal.jsx @@ -4,18 +4,55 @@ import { Button } from "../Button/Button"; import deadImageUrl from "./images/dead.png"; import celebrationImageUrl from "./images/celebration.png"; +import { useState } from "react"; +import { useLeaders } from "../../hooks/useLeaders"; export function EndGameModal({ isWon, gameDurationSeconds, gameDurationMinutes, onClick }) { - const title = isWon ? "Вы победили!" : "Вы проиграли!"; + let title = isWon ? "Вы победили!" : "Вы проиграли!"; const imgSrc = isWon ? celebrationImageUrl : deadImageUrl; const imgAlt = isWon ? "celebration emodji" : "dead emodji"; + const [newLeader, setNewLeader] = useState({ + name: "", + time: gameDurationSeconds, + }); + const { leaders } = useLeaders(); + + console.log(leaders); + + if (gameDurationMinutes * 60 + gameDurationSeconds < leaders[2].time && isWon) { + title = "Вы попали на лидерборд!"; + } + + let isLeader = gameDurationMinutes * 60 + gameDurationSeconds < leaders[2].time && isWon; + + function handleInputChange(event) { + const { name, value } = event.target; + setNewLeader({ + ...newLeader, + [name]: value, + }); + } + return (
{imgAlt}

{title}

+ {isLeader && ( +
+ + +
+ )}

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

{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..6a70b1c3d 100644 --- a/src/components/EndGameModal/EndGameModal.module.css +++ b/src/components/EndGameModal/EndGameModal.module.css @@ -1,12 +1,13 @@ .modal { width: 480px; - height: 459px; + /* height: 459px; */ border-radius: 12px; background: #c2f5ff; display: flex; flex-direction: column; justify-content: center; align-items: center; + padding: 40px 0 48px 0; } .image { @@ -27,6 +28,32 @@ margin-bottom: 28px; } +.leaderboardInfo { + display: flex; + flex-direction: column; + align-items: center; +} + +.nameInput { + width: 276px; + box-sizing: border-box; + outline: none; + border: none; + padding: 5px 10px; + border-radius: 10px; + height: 45px; +} + +.nameInput::placeholder { + color: rgb(153, 153, 153); + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + letter-spacing: 0%; + text-align: center; +} + .description { color: #000; font-variant-numeric: lining-nums proportional-nums; @@ -48,4 +75,4 @@ line-height: 72px; margin-bottom: 40px; -} +} \ No newline at end of file diff --git a/src/components/Leaderboard/Leaderboard.jsx b/src/components/Leaderboard/Leaderboard.jsx index ac2764d7c..6c5f20c1b 100644 --- a/src/components/Leaderboard/Leaderboard.jsx +++ b/src/components/Leaderboard/Leaderboard.jsx @@ -3,26 +3,10 @@ import { Button } from "../Button/Button"; import { LeaderboardRow } from "../LeaderboardRow/LeaderboardRow"; import styles from "./Leaderboard.module.css"; import cn from "classnames"; -import { useEffect } from "react"; -import { getLeaders } from "../../api"; import { useLeaders } from "../../hooks/useLeaders"; export function Leaderboard() { - const { leaders, setLeaders } = useLeaders(); - - useEffect(() => { - getLeaders().then(response => { - const sortedLeaders = response.leaders - .map(leader => ({ - ...leader, - name: leader.name.trim() === "" ? "Пользователь" : leader.name, - })) - .sort((a, b) => a.time - b.time); - setLeaders(sortedLeaders); - }); - }, [setLeaders]); - - console.log(leaders); + const { leaders } = useLeaders(); return (
diff --git a/src/pages/SelectLevelPage/SelectLevelPage.jsx b/src/pages/SelectLevelPage/SelectLevelPage.jsx index cc7316844..bd947784d 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.jsx +++ b/src/pages/SelectLevelPage/SelectLevelPage.jsx @@ -1,7 +1,9 @@ import { Link, useNavigate } from "react-router-dom"; import styles from "./SelectLevelPage.module.css"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { useEasyMode } from "../../hooks/useEasyMode"; +import { useLeaders } from "../../hooks/useLeaders"; +import { getLeaders } from "../../api"; export function SelectLevelPage() { const [selectedLevel, setSelectedLevel] = useState(null); @@ -24,6 +26,20 @@ export function SelectLevelPage() { } }; + const { setLeaders } = useLeaders(); + + useEffect(() => { + getLeaders().then(response => { + const sortedLeaders = response.leaders + .map(leader => ({ + ...leader, + name: leader.name.trim() === "" ? "Пользователь" : leader.name, + })) + .sort((a, b) => a.time - b.time); + setLeaders(sortedLeaders); + }); + }, [setLeaders]); + return (
From 011d9bbc5ce51dcb7cbeb1bb880e988c7c2d5ba9 Mon Sep 17 00:00:00 2001 From: ax1lebafer Date: Mon, 8 Jul 2024 01:02:22 +0400 Subject: [PATCH 10/18] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BB=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D0=BA=D1=83=20=D0=BD=D0=B0=20=D1=80=D0=B0=D0=B7=D0=BC=D0=B5?= =?UTF-8?q?=D1=89=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B2=20=D0=BB=D0=B8=D0=B4?= =?UTF-8?q?=D0=B5=D1=80=D0=B1=D0=BE=D1=80=D0=B4=20=D0=B8=D0=B3=D1=80=D0=BE?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B8=20=D0=BF=D1=83=D0=B1=D0=BB=D0=B8=D0=BA?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8E=20=D0=B2=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Button/Button.jsx | 4 +- src/components/Cards/Cards.jsx | 1 + src/components/EndGameModal/EndGameModal.jsx | 48 +++++++++++++++---- .../EndGameModal/EndGameModal.module.css | 24 ++++++++-- src/components/Leaderboard/Leaderboard.jsx | 35 ++++++++++++-- .../Leaderboard/Leaderboard.module.css | 7 +++ src/contexts/LeadersContext.jsx | 5 +- src/pages/SelectLevelPage/SelectLevelPage.jsx | 20 ++------ 8 files changed, 109 insertions(+), 35 deletions(-) diff --git a/src/components/Button/Button.jsx b/src/components/Button/Button.jsx index 3d4618a88..3ed74c087 100644 --- a/src/components/Button/Button.jsx +++ b/src/components/Button/Button.jsx @@ -1,8 +1,8 @@ import styles from "./Button.module.css"; -export function Button({ children, onClick }) { +export function Button({ children, onClick, disabled }) { return ( - ); diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index bc1d525e2..5396d6a02 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -243,6 +243,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) {
{ + const isInLeaderboard = + leaders.length > 0 && newLeader.time < leaders[leaders.length - 1].time && isWon && pairsCount === 9; + + if (isInLeaderboard) { + setIsLeader(true); + } + }, [leaders, newLeader.time, isWon, pairsCount, setIsLeader]); - if (gameDurationMinutes * 60 + gameDurationSeconds < leaders[2].time && isWon) { + if (isLeader) { title = "Вы попали на лидерборд!"; } - let isLeader = gameDurationMinutes * 60 + gameDurationSeconds < leaders[2].time && isWon; - function handleInputChange(event) { const { name, value } = event.target; setNewLeader({ @@ -36,6 +46,22 @@ export function EndGameModal({ isWon, gameDurationSeconds, gameDurationMinutes, }); } + function handleSaveLeader() { + setIsLoading(true); + + postLeader({ name: newLeader.name, time: newLeader.time }) + .then(response => { + setLeaders(response.leaders); + navigate("/leaderboard"); + }) + .catch(error => { + alert(error); + }) + .finally(() => { + setIsLoading(false); + }); + } + return (
{imgAlt} @@ -50,7 +76,9 @@ export function EndGameModal({ isWon, gameDurationSeconds, gameDurationMinutes, placeholder="Пользователь" onChange={handleInputChange} /> - +
)}

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

@@ -59,6 +87,10 @@ export function EndGameModal({ isWon, gameDurationSeconds, gameDurationMinutes,
+ + + Перейти к лидерборду +
); } diff --git a/src/components/EndGameModal/EndGameModal.module.css b/src/components/EndGameModal/EndGameModal.module.css index 6a70b1c3d..e76386dee 100644 --- a/src/components/EndGameModal/EndGameModal.module.css +++ b/src/components/EndGameModal/EndGameModal.module.css @@ -24,6 +24,7 @@ font-style: normal; font-weight: 400; line-height: 48px; + text-align: center; margin-bottom: 28px; } @@ -35,25 +36,32 @@ } .nameInput { - width: 276px; + width: 246px; box-sizing: border-box; outline: none; border: none; padding: 5px 10px; border-radius: 10px; height: 45px; + font-size: 24px; + margin-bottom: 20px; + text-align: center; } .nameInput::placeholder { color: rgb(153, 153, 153); - font-family: Roboto; + font-family: StratosSkyeng; font-size: 24px; font-weight: 400; - line-height: 32px; + /* line-height: 32px; */ letter-spacing: 0%; text-align: center; } +.saveButton { + font-size: 18px; +} + .description { color: #000; font-variant-numeric: lining-nums proportional-nums; @@ -64,6 +72,7 @@ line-height: 32px; margin-bottom: 10px; + margin-top: 10px; } .time { @@ -75,4 +84,13 @@ line-height: 72px; margin-bottom: 40px; +} + +.leaderboardLink { + color: rgb(0, 73, 128); + font-family: StratosSkyeng; + font-size: 18px; + font-weight: 400; + line-height: 32px; + margin-top: 18px; } \ No newline at end of file diff --git a/src/components/Leaderboard/Leaderboard.jsx b/src/components/Leaderboard/Leaderboard.jsx index 6c5f20c1b..ef929219f 100644 --- a/src/components/Leaderboard/Leaderboard.jsx +++ b/src/components/Leaderboard/Leaderboard.jsx @@ -4,9 +4,31 @@ import { LeaderboardRow } from "../LeaderboardRow/LeaderboardRow"; import styles from "./Leaderboard.module.css"; import cn from "classnames"; import { useLeaders } from "../../hooks/useLeaders"; +import { useEffect, useState } from "react"; +import { getLeaders } from "../../api"; export function Leaderboard() { - const { leaders } = useLeaders(); + const { leaders, setLeaders } = useLeaders(); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + setIsLoading(true); + + getLeaders() + .then(response => { + const sortedLeaders = response.leaders + .map(leader => ({ + ...leader, + name: leader.name.trim() === "" ? "Пользователь" : leader.name, + })) + .sort((a, b) => a.time - b.time) + .slice(0, 10); + setLeaders(sortedLeaders); + }) + .finally(() => { + setIsLoading(false); + }); + }, [setLeaders]); return (
@@ -24,9 +46,14 @@ export function Leaderboard() {

Время

- {leaders.map((leader, index) => ( - - ))} + {isLoading && Загрузка...} + {!isLoading && ( + <> + {leaders.map((leader, index) => ( + + ))} + + )}
); diff --git a/src/components/Leaderboard/Leaderboard.module.css b/src/components/Leaderboard/Leaderboard.module.css index 8ca32f0e1..6acf486fa 100644 --- a/src/components/Leaderboard/Leaderboard.module.css +++ b/src/components/Leaderboard/Leaderboard.module.css @@ -57,4 +57,11 @@ .textTime { width: 102px; +} + +.loader { + font-size: 24px; + color: #FFFFFF; + font-family: StratosSkyeng; + text-align: center; } \ No newline at end of file diff --git a/src/contexts/LeadersContext.jsx b/src/contexts/LeadersContext.jsx index 9eb35d348..128d1bd38 100644 --- a/src/contexts/LeadersContext.jsx +++ b/src/contexts/LeadersContext.jsx @@ -4,6 +4,9 @@ export const LeadersContext = createContext(); export function LeadersProvider({ children }) { const [leaders, setLeaders] = useState([]); + const [isLeader, setIsLeader] = useState(false); - return {children}; + return ( + {children} + ); } diff --git a/src/pages/SelectLevelPage/SelectLevelPage.jsx b/src/pages/SelectLevelPage/SelectLevelPage.jsx index bd947784d..e4795425f 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.jsx +++ b/src/pages/SelectLevelPage/SelectLevelPage.jsx @@ -1,9 +1,9 @@ import { Link, useNavigate } from "react-router-dom"; import styles from "./SelectLevelPage.module.css"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import { useEasyMode } from "../../hooks/useEasyMode"; -import { useLeaders } from "../../hooks/useLeaders"; -import { getLeaders } from "../../api"; +// import { useLeaders } from "../../hooks/useLeaders"; +// import { getLeaders } from "../../api"; export function SelectLevelPage() { const [selectedLevel, setSelectedLevel] = useState(null); @@ -26,20 +26,6 @@ export function SelectLevelPage() { } }; - const { setLeaders } = useLeaders(); - - useEffect(() => { - getLeaders().then(response => { - const sortedLeaders = response.leaders - .map(leader => ({ - ...leader, - name: leader.name.trim() === "" ? "Пользователь" : leader.name, - })) - .sort((a, b) => a.time - b.time); - setLeaders(sortedLeaders); - }); - }, [setLeaders]); - return (
From f05c96ab5d4b1caee592115cb5e2ecff9b9a4a3d Mon Sep 17 00:00:00 2001 From: ax1lebafer Date: Mon, 8 Jul 2024 01:03:03 +0400 Subject: [PATCH 11/18] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B7=D0=B0=D1=82=D1=80=D0=B0=D1=87=D0=B5=D0=BD=D0=BD?= =?UTF-8?q?=D0=BE=D0=B5=20=D0=B2=D1=80=D0=B5=D0=BC=D1=8F?= 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 4dd7fc451..9b29162f4 100644 --- a/README.md +++ b/README.md @@ -53,4 +53,4 @@ https://skypro-web-developer.github.io/react-memo/ 2 спринт: Продполагаемое время: 5 часов -Фактически затраченное время: 4 часа 30 минут +Фактически затраченное время: 6 часов 30 минут From a4162c08544181c9fbb422e90300b03bf80075ea Mon Sep 17 00:00:00 2001 From: ax1lebafer Date: Tue, 9 Jul 2024 23:08:12 +0400 Subject: [PATCH 12/18] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B8=D0=BA=D0=BE=D0=BD=D0=BA=D0=B8=20=D0=9D=D0=B5?= =?UTF-8?q?=D0=BC=D0=BD=D0=BE=D0=B3=D0=BE=20=D1=81=D1=82=D0=B8=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=D0=BB=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D1=82=D1=8B=20=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0?= =?UTF-8?q?=D0=BB=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=BF=D0=BE=D0=BF=D0=B0=D0=BF=D0=B0=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=20=D0=BD=D0=B0=D0=B2=D0=B5=D0=B4=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B8=20=D0=BD=D0=B0=20=D0=B8=D0=BA=D0=BE=D0=BD=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++ public/magic_ball.svg | 17 +++++++ public/magic_ball_empty.svg | 33 +++++++++++++ public/puzzle.svg | 18 +++++++ public/puzzle_empty.svg | 20 ++++++++ src/api.js | 2 +- src/components/Leaderboard/Leaderboard.jsx | 9 ++-- .../Leaderboard/Leaderboard.module.css | 11 ++--- .../LeaderboardRow/LeaderboardRow.jsx | 23 +++++++-- .../LeaderboardRow/LeaderboardRow.module.css | 15 +++--- src/components/ModalPuzzle/ModalPuzzle.jsx | 15 ++++++ .../ModalPuzzle/ModalPuzzle.module.css | 48 +++++++++++++++++++ 12 files changed, 194 insertions(+), 21 deletions(-) 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/ModalPuzzle/ModalPuzzle.jsx create mode 100644 src/components/ModalPuzzle/ModalPuzzle.module.css diff --git a/README.md b/README.md index 9b29162f4..84aab3f0e 100644 --- a/README.md +++ b/README.md @@ -54,3 +54,7 @@ https://skypro-web-developer.github.io/react-memo/ 2 спринт: Продполагаемое время: 5 часов Фактически затраченное время: 6 часов 30 минут + +3 спринт +Продполагаемое время: 8 часов +Фактически затраченное время: 1 час 30 минут \ No newline at end of file diff --git a/public/magic_ball.svg b/public/magic_ball.svg new file mode 100644 index 000000000..78d4894ef --- /dev/null +++ b/public/magic_ball.svg @@ -0,0 +1,17 @@ + + + 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/api.js b/src/api.js index 2183ebba8..fbf5c3e20 100644 --- a/src/api.js +++ b/src/api.js @@ -1,4 +1,4 @@ -const host = "https://wedev-api.sky.pro/api/leaderboard"; +const host = "https://wedev-api.sky.pro/api/v2/leaderboard"; export async function getLeaders() { const response = await fetch(host, { diff --git a/src/components/Leaderboard/Leaderboard.jsx b/src/components/Leaderboard/Leaderboard.jsx index ef929219f..5f73d02cc 100644 --- a/src/components/Leaderboard/Leaderboard.jsx +++ b/src/components/Leaderboard/Leaderboard.jsx @@ -30,6 +30,8 @@ export function Leaderboard() { }); }, [setLeaders]); + console.log(leaders); + return (
@@ -40,10 +42,9 @@ export function Leaderboard() {
-
-

Позиция

-

Пользователь

-
+

Позиция

+

Пользователь

+

Достижения

Время

{isLoading && Загрузка...} diff --git a/src/components/Leaderboard/Leaderboard.module.css b/src/components/Leaderboard/Leaderboard.module.css index 6acf486fa..e4474c6a2 100644 --- a/src/components/Leaderboard/Leaderboard.module.css +++ b/src/components/Leaderboard/Leaderboard.module.css @@ -32,11 +32,6 @@ display: flex; padding: 16px 20px; justify-content: space-between; -} - -.sectionTop div { - display: flex; - justify-content: space-between; gap: 66px; } @@ -52,7 +47,11 @@ } .textUser { - width: 324px; + width: 227px; +} + +.textAchievement { + width: 194px; } .textTime { diff --git a/src/components/LeaderboardRow/LeaderboardRow.jsx b/src/components/LeaderboardRow/LeaderboardRow.jsx index 49f77f8f5..a794c98da 100644 --- a/src/components/LeaderboardRow/LeaderboardRow.jsx +++ b/src/components/LeaderboardRow/LeaderboardRow.jsx @@ -1,6 +1,8 @@ import styles from "./LeaderboardRow.module.css"; import cn from "classnames"; import { format } from "date-fns"; +import { ModalPuzzle } from "../ModalPuzzle/ModalPuzzle"; +import { useState } from "react"; function formatSeconds(seconds) { const date = new Date(0); @@ -8,14 +10,27 @@ function formatSeconds(seconds) { return date; } -export function LeaderboardRow({ position, userName, time }) { +export function LeaderboardRow({ position, userName, achievement, time }) { + const [isPopupVisible, setIsPopupVisible] = useState(false); + const formattedTime = format(formatSeconds(time), "mm:ss"); + const handleMouseEnter = () => { + setIsPopupVisible(true); + }; + + const handleMouseLeave = () => { + setIsPopupVisible(false); + }; + return (
-
-

{position}

-

{userName}

+

{position}

+

{userName}

+
+ puzzle + ball + {isPopupVisible && }

{formattedTime}

diff --git a/src/components/LeaderboardRow/LeaderboardRow.module.css b/src/components/LeaderboardRow/LeaderboardRow.module.css index d7ddee69c..068c09762 100644 --- a/src/components/LeaderboardRow/LeaderboardRow.module.css +++ b/src/components/LeaderboardRow/LeaderboardRow.module.css @@ -4,12 +4,8 @@ display: flex; padding: 16px 20px; justify-content: space-between; -} - -.sectionTop div { - display: flex; - justify-content: space-between; gap: 66px; + position: relative; } .sectionText { @@ -19,12 +15,19 @@ font-weight: 400; } +.sectionIcons { + display: flex; + width: 194px; + gap: 6px; + position: relative; +} + .textPosition { width: 178px; } .textUser { - width: 324px; + width: 227px; } .textTime { diff --git a/src/components/ModalPuzzle/ModalPuzzle.jsx b/src/components/ModalPuzzle/ModalPuzzle.jsx new file mode 100644 index 000000000..c61ba7b3b --- /dev/null +++ b/src/components/ModalPuzzle/ModalPuzzle.jsx @@ -0,0 +1,15 @@ +import styles from "./ModalPuzzle.module.css"; + +export function ModalPuzzle() { + return ( +
+
+
+
+
Игра пройдена без супер-сил
+
+
+
+
+ ); +} diff --git a/src/components/ModalPuzzle/ModalPuzzle.module.css b/src/components/ModalPuzzle/ModalPuzzle.module.css new file mode 100644 index 000000000..141c420e7 --- /dev/null +++ b/src/components/ModalPuzzle/ModalPuzzle.module.css @@ -0,0 +1,48 @@ +.fff { + position: absolute; + width: 174px; + top: -100px; + left: 35px; +} + +.popupContainer { + display: flex; + justify-content: center; + align-items: center; + position: relative; +} + +.popup { + display: flex; + align-items: center; + position: relative; + border-radius: 8px; +} + +.popupContent { + display: flex; + align-items: center; + background-color: #C2F5FF; + border-radius: 8px; + padding: 15px 20px; + position: relative; +} + +.popupText { + font-size: 18px; + color: #002B6E; + text-align: center; + font-family: StratosSkyeng; +} + +.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 From 5fb55fdce60b4e48450ce572002a3781597e6713 Mon Sep 17 00:00:00 2001 From: ax1lebafer Date: Wed, 10 Jul 2024 22:05:35 +0400 Subject: [PATCH 13/18] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BB=20=D0=BF=D0=BE=D0=BF=D0=B0=D0=BF=D1=8B?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=B4=D1=81=D0=BA=D0=B0=D0=B7=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../LeaderboardRow/LeaderboardRow.jsx | 37 +++++++++++++++---- src/components/ModalBall/ModalBall.jsx | 11 ++++++ src/components/ModalBall/ModalBall.module.css | 34 +++++++++++++++++ src/components/ModalPuzzle/ModalPuzzle.jsx | 10 ++--- .../ModalPuzzle/ModalPuzzle.module.css | 26 +++---------- 6 files changed, 84 insertions(+), 36 deletions(-) create mode 100644 src/components/ModalBall/ModalBall.jsx create mode 100644 src/components/ModalBall/ModalBall.module.css diff --git a/README.md b/README.md index 84aab3f0e..4bf7e1400 100644 --- a/README.md +++ b/README.md @@ -57,4 +57,4 @@ https://skypro-web-developer.github.io/react-memo/ 3 спринт Продполагаемое время: 8 часов -Фактически затраченное время: 1 час 30 минут \ No newline at end of file +Фактически затраченное время: 1 час 50 минут \ No newline at end of file diff --git a/src/components/LeaderboardRow/LeaderboardRow.jsx b/src/components/LeaderboardRow/LeaderboardRow.jsx index a794c98da..508e8322d 100644 --- a/src/components/LeaderboardRow/LeaderboardRow.jsx +++ b/src/components/LeaderboardRow/LeaderboardRow.jsx @@ -3,6 +3,7 @@ import cn from "classnames"; import { format } from "date-fns"; import { ModalPuzzle } from "../ModalPuzzle/ModalPuzzle"; import { useState } from "react"; +import { ModalBall } from "../ModalBall/ModalBall"; function formatSeconds(seconds) { const date = new Date(0); @@ -11,16 +12,25 @@ function formatSeconds(seconds) { } export function LeaderboardRow({ position, userName, achievement, time }) { - const [isPopupVisible, setIsPopupVisible] = useState(false); + const [isPopupPuzzleVisible, setIsPopupPuzzleVisible] = useState(false); + const [isPopupBallVisible, setIsPopupBallVisible] = useState(false); const formattedTime = format(formatSeconds(time), "mm:ss"); - const handleMouseEnter = () => { - setIsPopupVisible(true); + const handleBallMouseEnter = () => { + setIsPopupBallVisible(true); }; - const handleMouseLeave = () => { - setIsPopupVisible(false); + const handleBallMouseLeave = () => { + setIsPopupBallVisible(false); + }; + + const handlePuzzleMouseEnter = () => { + setIsPopupPuzzleVisible(true); + }; + + const handlePuzzleMouseLeave = () => { + setIsPopupPuzzleVisible(false); }; return ( @@ -28,9 +38,20 @@ export function LeaderboardRow({ position, userName, achievement, time }) {

{position}

{userName}

- puzzle - ball - {isPopupVisible && } + puzzle + ball + {isPopupBallVisible && } + {isPopupPuzzleVisible && }

{formattedTime}

diff --git a/src/components/ModalBall/ModalBall.jsx b/src/components/ModalBall/ModalBall.jsx new file mode 100644 index 000000000..64ac199e4 --- /dev/null +++ b/src/components/ModalBall/ModalBall.jsx @@ -0,0 +1,11 @@ +import styles from "./ModalBall.module.css"; + +export function ModalBall() { + return ( +
+
+

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

+
+
+ ); +} diff --git a/src/components/ModalBall/ModalBall.module.css b/src/components/ModalBall/ModalBall.module.css new file mode 100644 index 000000000..93850aee7 --- /dev/null +++ b/src/components/ModalBall/ModalBall.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/ModalPuzzle/ModalPuzzle.jsx b/src/components/ModalPuzzle/ModalPuzzle.jsx index c61ba7b3b..64a5fd622 100644 --- a/src/components/ModalPuzzle/ModalPuzzle.jsx +++ b/src/components/ModalPuzzle/ModalPuzzle.jsx @@ -2,13 +2,9 @@ import styles from "./ModalPuzzle.module.css"; export function ModalPuzzle() { return ( -
-
-
-
-
Игра пройдена без супер-сил
-
-
+
+
+

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

); diff --git a/src/components/ModalPuzzle/ModalPuzzle.module.css b/src/components/ModalPuzzle/ModalPuzzle.module.css index 141c420e7..9e167ed8c 100644 --- a/src/components/ModalPuzzle/ModalPuzzle.module.css +++ b/src/components/ModalPuzzle/ModalPuzzle.module.css @@ -1,38 +1,24 @@ -.fff { +.popup { position: absolute; - width: 174px; top: -100px; left: 35px; } -.popupContainer { - display: flex; - justify-content: center; - align-items: center; - position: relative; -} - -.popup { - display: flex; - align-items: center; - position: relative; - border-radius: 8px; -} - .popupContent { - display: flex; - align-items: center; + 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; - color: #002B6E; + font-weight: 400; text-align: center; - font-family: StratosSkyeng; } .popupContent::after { From a7118dd40cb1631db67781b0920e38ac43281ee0 Mon Sep 17 00:00:00 2001 From: ax1lebafer Date: Wed, 10 Jul 2024 22:16:27 +0400 Subject: [PATCH 14/18] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=B8=D0=BA=D0=BE=D0=BD=D0=BE=D0=BA=20=D0=B0?= =?UTF-8?q?=D1=87=D0=B8=D0=B2=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- src/components/Leaderboard/Leaderboard.jsx | 8 +++++++- src/components/LeaderboardRow/LeaderboardRow.jsx | 6 +++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4bf7e1400..3ed89d6b1 100644 --- a/README.md +++ b/README.md @@ -57,4 +57,4 @@ https://skypro-web-developer.github.io/react-memo/ 3 спринт Продполагаемое время: 8 часов -Фактически затраченное время: 1 час 50 минут \ No newline at end of file +Фактически затраченное время: 2 час 00 минут \ No newline at end of file diff --git a/src/components/Leaderboard/Leaderboard.jsx b/src/components/Leaderboard/Leaderboard.jsx index 5f73d02cc..2d6a8dd55 100644 --- a/src/components/Leaderboard/Leaderboard.jsx +++ b/src/components/Leaderboard/Leaderboard.jsx @@ -51,7 +51,13 @@ export function Leaderboard() { {!isLoading && ( <> {leaders.map((leader, index) => ( - + ))} )} diff --git a/src/components/LeaderboardRow/LeaderboardRow.jsx b/src/components/LeaderboardRow/LeaderboardRow.jsx index 508e8322d..b204494a9 100644 --- a/src/components/LeaderboardRow/LeaderboardRow.jsx +++ b/src/components/LeaderboardRow/LeaderboardRow.jsx @@ -11,7 +11,7 @@ function formatSeconds(seconds) { return date; } -export function LeaderboardRow({ position, userName, achievement, time }) { +export function LeaderboardRow({ position, userName, achievements, time }) { const [isPopupPuzzleVisible, setIsPopupPuzzleVisible] = useState(false); const [isPopupBallVisible, setIsPopupBallVisible] = useState(false); @@ -39,13 +39,13 @@ export function LeaderboardRow({ position, userName, achievement, time }) {

{userName}

puzzle ball Date: Wed, 10 Jul 2024 23:41:32 +0400 Subject: [PATCH 15/18] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BB=201=20=D0=BF=D0=B5=D1=80=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/card_perk.svg | 15 +++++++++ src/components/Cards/Cards.jsx | 38 +++++++++++++++++++++- src/components/Cards/Cards.module.css | 21 ++++++++++++ src/components/Leaderboard/Leaderboard.jsx | 2 -- 4 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 public/card_perk.svg diff --git a/public/card_perk.svg b/public/card_perk.svg new file mode 100644 index 000000000..88ab0b523 --- /dev/null +++ b/public/card_perk.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 5396d6a02..f7e79a75e 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -55,6 +55,8 @@ function closeUnmatchedCards(setCards, openCardsWithoutPair) { export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const { isEasyMode } = useEasyMode(); const [lives, setLives] = useState(isEasyMode ? EASY_MODE_LIVES : DEFAULT_MODE_LIVES); + const [perkUses, setPerkUses] = useState(0); + const [counterPerk, setCounterPerk] = useState(2); // В cards лежит игровое поле - массив карт и их состояние открыта\закрыта const [cards, setCards] = useState([]); @@ -89,6 +91,32 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setTimer(getTimerValue(null, null)); setStatus(STATUS_PREVIEW); setLives(isEasyMode ? EASY_MODE_LIVES : DEFAULT_MODE_LIVES); + setPerkUses(0); + setCounterPerk(2); + } + + function usePerk() { + if (perkUses >= 2) return; + setPerkUses(prev => prev + 1); + setCounterPerk(prev => prev - 1); + + const closedCards = cards.filter(card => !card.open); + if (closedCards.length < 2) return; + + const randomCardIndex = Math.floor(Math.random() * closedCards.length); + const randomCard = closedCards[randomCardIndex]; + + const matchingCard = cards.find( + card => card.suit === randomCard.suit && card.rank === randomCard.rank && card.id !== randomCard.id, + ); + + if (matchingCard) { + setCards(currentCards => + currentCards.map(card => + card.id === randomCard.id || card.id === matchingCard.id ? { ...card, open: true } : card, + ), + ); + } } /** @@ -222,7 +250,15 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { )}
- {status === STATUS_IN_PROGRESS ? : null} + {status === STATUS_IN_PROGRESS ? ( + <> +
+ eye_perk +
{counterPerk}
+
+ + + ) : null}
diff --git a/src/components/Cards/Cards.module.css b/src/components/Cards/Cards.module.css index 6c5c42904..673c60bc1 100644 --- a/src/components/Cards/Cards.module.css +++ b/src/components/Cards/Cards.module.css @@ -60,6 +60,7 @@ margin-top: 34px; margin-bottom: 10px; } + .previewDescription { font-size: 18px; line-height: 18px; @@ -80,3 +81,23 @@ margin-bottom: -12px; } + +.perks { + display: flex; + gap: 15px; + position: relative; +} + +.cardPerk { + cursor: pointer; +} + +.counterPerk { + position: absolute; + background-color: orange; + border-radius: 100px; + padding: 5px 10px; + font-family: StratosSkyeng; + top: 45px; + left: 45px; +} \ No newline at end of file diff --git a/src/components/Leaderboard/Leaderboard.jsx b/src/components/Leaderboard/Leaderboard.jsx index 2d6a8dd55..dd2704eba 100644 --- a/src/components/Leaderboard/Leaderboard.jsx +++ b/src/components/Leaderboard/Leaderboard.jsx @@ -30,8 +30,6 @@ export function Leaderboard() { }); }, [setLeaders]); - console.log(leaders); - return (
From 04bc60bf7067f0d534256565a2aa8b236f3f1b81 Mon Sep 17 00:00:00 2001 From: ax1lebafer Date: Thu, 11 Jul 2024 00:11:21 +0400 Subject: [PATCH 16/18] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BB=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B2=20=D0=BB=D0=B8=D0=B4=D0=B5?= =?UTF-8?q?=D1=80=D0=B1=D0=BE=D1=80=D0=B4=20=D1=81=20=D0=B0=D1=87=D0=B8?= =?UTF-8?q?=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 | 3 ++- src/components/Cards/Cards.jsx | 6 ++++++ src/components/EndGameModal/EndGameModal.jsx | 5 +++-- src/components/LeaderboardRow/LeaderboardRow.jsx | 4 ++-- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/api.js b/src/api.js index fbf5c3e20..a5145f3ec 100644 --- a/src/api.js +++ b/src/api.js @@ -13,12 +13,13 @@ export async function getLeaders() { return data; } -export async function postLeader({ name, time }) { +export async function postLeader({ name, time, achievements }) { const response = await fetch(host, { method: "POST", body: JSON.stringify({ name: name, time: time, + achievements: achievements, }), }); diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index f7e79a75e..ef5474b1f 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -57,6 +57,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const [lives, setLives] = useState(isEasyMode ? EASY_MODE_LIVES : DEFAULT_MODE_LIVES); const [perkUses, setPerkUses] = useState(0); const [counterPerk, setCounterPerk] = useState(2); + const [achievements, setAchievements] = useState(isEasyMode ? [1] : []); // В cards лежит игровое поле - массив карт и их состояние открыта\закрыта const [cards, setCards] = useState([]); @@ -100,6 +101,10 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setPerkUses(prev => prev + 1); setCounterPerk(prev => prev - 1); + if (perkUses === 0) { + setAchievements([...achievements, 2]); + } + const closedCards = cards.filter(card => !card.open); if (closedCards.length < 2) return; @@ -283,6 +288,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 1e4cc5669..4fc6e1870 100644 --- a/src/components/EndGameModal/EndGameModal.jsx +++ b/src/components/EndGameModal/EndGameModal.jsx @@ -9,7 +9,7 @@ import { useLeaders } from "../../hooks/useLeaders"; import { Link, useNavigate } from "react-router-dom"; import { postLeader } from "../../api"; -export function EndGameModal({ isWon, pairsCount, gameDurationSeconds, gameDurationMinutes, onClick }) { +export function EndGameModal({ isWon, pairsCount, gameDurationSeconds, gameDurationMinutes, onClick, achievements }) { const { leaders, setLeaders, isLeader, setIsLeader } = useLeaders(); const navigate = useNavigate(); const [isLoading, setIsLoading] = useState(false); @@ -23,6 +23,7 @@ export function EndGameModal({ isWon, pairsCount, gameDurationSeconds, gameDurat const [newLeader, setNewLeader] = useState({ name: "", time: gameDurationSeconds, + achievements: achievements, }); useEffect(() => { @@ -49,7 +50,7 @@ export function EndGameModal({ isWon, pairsCount, gameDurationSeconds, gameDurat function handleSaveLeader() { setIsLoading(true); - postLeader({ name: newLeader.name, time: newLeader.time }) + postLeader({ name: newLeader.name, time: newLeader.time, achievements: newLeader.achievements }) .then(response => { setLeaders(response.leaders); navigate("/leaderboard"); diff --git a/src/components/LeaderboardRow/LeaderboardRow.jsx b/src/components/LeaderboardRow/LeaderboardRow.jsx index b204494a9..685f3475f 100644 --- a/src/components/LeaderboardRow/LeaderboardRow.jsx +++ b/src/components/LeaderboardRow/LeaderboardRow.jsx @@ -39,13 +39,13 @@ export function LeaderboardRow({ position, userName, achievements, time }) {

{userName}

puzzle ball Date: Thu, 11 Jul 2024 00:12:14 +0400 Subject: [PATCH 17/18] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B7=D0=B0=D1=82=D1=80=D0=B0=D1=87=D0=B5=D0=BD=D0=BD?= =?UTF-8?q?=D0=BE=D0=B5=20=D0=B2=D1=80=D0=B5=D0=BC=D1=8F?= 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 3ed89d6b1..c5c684c4c 100644 --- a/README.md +++ b/README.md @@ -57,4 +57,4 @@ https://skypro-web-developer.github.io/react-memo/ 3 спринт Продполагаемое время: 8 часов -Фактически затраченное время: 2 час 00 минут \ No newline at end of file +Фактически затраченное время: 4 час 20 минут From cbaf639f7841af7f0e3d300a90fb3322f72df6f8 Mon Sep 17 00:00:00 2001 From: ax1lebafer Date: Tue, 30 Jul 2024 21:45:28 +0400 Subject: [PATCH 18/18] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20?= =?UTF-8?q?=D0=B4=D0=B5=D0=BF=D0=BB=D0=BE=D0=B9?= 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 c5c684c4c..1ab530ac3 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ В этом репозитории реализован MVP карточкой игры "Мемо" по [тех.заданию](./docs/mvp-spec.md) Проект задеплоен на gh pages: -https://skypro-web-developer.github.io/react-memo/ +https://ax1lebafer.github.io/react-memory/ ## Разработка