From eec06f0be956615aaa911dc052f3423b5202fa25 Mon Sep 17 00:00:00 2001 From: anguseva25 Date: Sun, 7 Jul 2024 00:34:28 +0300 Subject: [PATCH 01/13] changed SelectLevelPage.jsx and the line ends --- .gitignore | 1 + src/context/GameSettingsContext.js | 18 +++++++ src/index.js | 5 +- src/pages/GamePage/GamePage.jsx | 4 +- src/pages/SelectLevelPage/SelectLevelPage.jsx | 53 ++++++++++++++++--- .../SelectLevelPage.module.css | 7 +++ src/router.js | 4 ++ 7 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 src/context/GameSettingsContext.js diff --git a/.gitignore b/.gitignore index 4d29575de..76c22382d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ /build # misc +.idea .DS_Store .env.local .env.development.local diff --git a/src/context/GameSettingsContext.js b/src/context/GameSettingsContext.js new file mode 100644 index 000000000..27c524736 --- /dev/null +++ b/src/context/GameSettingsContext.js @@ -0,0 +1,18 @@ +import { createContext, useState } from "react"; + +export const GameSettingsContext = createContext(null); + +export const GameSettingsProvider = ({ children }) => { + const [levelMode, setLevelMode] = useState(1); + const [liteVersion, setLiteVersion] = useState(false); + + function getTriesCount() { + return liteVersion ? 3 : 0; + } + + return ( + + {children} + + ); +}; diff --git a/src/index.js b/src/index.js index f689c5f0b..10664b4cc 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 { GameSettingsProvider } from "./context/GameSettingsContext"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( - + + + , ); diff --git a/src/pages/GamePage/GamePage.jsx b/src/pages/GamePage/GamePage.jsx index a4be871db..4e64c13f9 100644 --- a/src/pages/GamePage/GamePage.jsx +++ b/src/pages/GamePage/GamePage.jsx @@ -2,8 +2,8 @@ import { useParams } from "react-router-dom"; import { Cards } from "../../components/Cards/Cards"; -export function GamePage() { - const { pairsCount } = useParams(); +export function GamePage({ otherPairsCount }) { + const { pairsCount = otherPairsCount } = useParams(); return ( <> diff --git a/src/pages/SelectLevelPage/SelectLevelPage.jsx b/src/pages/SelectLevelPage/SelectLevelPage.jsx index 758942e51..85a08e0ec 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.jsx +++ b/src/pages/SelectLevelPage/SelectLevelPage.jsx @@ -1,28 +1,67 @@ -import { Link } from "react-router-dom"; +import { useContext } from "react"; +import { useNavigate } from "react-router-dom"; import styles from "./SelectLevelPage.module.css"; +import { Button } from "../../components/Button/Button"; +import { GameSettingsContext } from "../../context/GameSettingsContext"; export function SelectLevelPage() { + const navigate = useNavigate(); + const { levelMode, setLevelMode, liteVersion, setLiteVersion } = useContext(GameSettingsContext); + + function handleLiteChange() { + console.log(liteVersion); + setLiteVersion(prev => !prev); + } + + function startGame() { + navigate(`/game/${levelMode * 3}`); + } + return (

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

+ +
); diff --git a/src/pages/SelectLevelPage/SelectLevelPage.module.css b/src/pages/SelectLevelPage/SelectLevelPage.module.css index 390ac0def..8063c25a9 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.module.css +++ b/src/pages/SelectLevelPage/SelectLevelPage.module.css @@ -62,3 +62,10 @@ .levelLink:visited { color: #0080c1; } + +.liteMode { + color: #004980; + font-family: "StratosSkyeng", sans-serif; + font-size: 18px; + margin-bottom: 48px; +} diff --git a/src/router.js b/src/router.js index da6e94b51..570b1b6f8 100644 --- a/src/router.js +++ b/src/router.js @@ -8,6 +8,10 @@ export const router = createBrowserRouter( path: "/", element: , }, + { + path: "/game", + element: , + }, { path: "/game/:pairsCount", element: , From 22a7117b78ef6c29474ae8d981318056869f873c Mon Sep 17 00:00:00 2001 From: anguseva25 Date: Tue, 9 Jul 2024 22:41:44 +0300 Subject: [PATCH 02/13] hw1 is ready --- README.md | 5 +++ src/components/Button/Button.module.css | 2 +- src/components/Card/Card.module.css | 4 +-- src/components/Cards/Cards.jsx | 34 ++++++++++++++++--- src/components/Cards/Cards.module.css | 12 +++++-- .../EndGameModal/EndGameModal.module.css | 6 ++-- src/pages/SelectLevelPage/SelectLevelPage.jsx | 12 +++---- .../SelectLevelPage.module.css | 13 +++++-- 8 files changed, 68 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 9b90842c4..8e8441d1a 100644 --- a/README.md +++ b/README.md @@ -44,3 +44,8 @@ https://skypro-web-developer.github.io/react-memo/ Запускает eslint проверку кода, эта же команда запускается перед каждым коммитом. Если не получается закоммитить, попробуйте запустить эту команду и исправить все ошибки и предупреждения. + +## Первая домашняя работа (оценка) + +Предполагаемое время: 8 часов. +Фактическое время: 5 часов. diff --git a/src/components/Button/Button.module.css b/src/components/Button/Button.module.css index 5d3f1f80e..29e36d8eb 100644 --- a/src/components/Button/Button.module.css +++ b/src/components/Button/Button.module.css @@ -10,7 +10,7 @@ font-variant-numeric: lining-nums proportional-nums; /* Pres → Caption S */ - font-family: StratosSkyeng; + font-family: "StratosSkyeng", sans-serif; font-size: 24px; font-style: normal; font-weight: 400; diff --git a/src/components/Card/Card.module.css b/src/components/Card/Card.module.css index 86c3fbb5b..03390dec4 100644 --- a/src/components/Card/Card.module.css +++ b/src/components/Card/Card.module.css @@ -40,7 +40,7 @@ color: #000; font-variant-numeric: lining-nums proportional-nums; - font-family: StratosSkyeng; + font-family: "StratosSkyeng", sans-serif; font-size: 24px; font-style: normal; font-weight: 400; @@ -75,7 +75,7 @@ /* Анимация переворота */ /* весь контейнер поддерживает перспективу */ .flipContainer { - perspective: 1000; + perspective: 1000px; } .flipContainer:hover .flipper { diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 7526a56c8..4fae4e2b4 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 { EndGameModal } from "../EndGameModal/EndGameModal"; +import { Button } from "../Button/Button"; +import { Card } from "../Card/Card"; +import { GameSettingsContext } from "../../context/GameSettingsContext"; // Игра закончилась const STATUS_LOST = "STATUS_LOST"; @@ -57,6 +58,10 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { minutes: 0, }); + const { liteVersion, getTriesCount } = useContext(GameSettingsContext); + const [triesCount, setTriesCount] = useState(getTriesCount()); + const [openedCards, setOpenedCards] = useState([]); + function finishGame(status = STATUS_LOST) { setGameEndDate(new Date()); setStatus(status); @@ -67,12 +72,17 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setGameStartDate(startDate); setTimer(getTimerValue(startDate, null)); setStatus(STATUS_IN_PROGRESS); + + setTriesCount(getTriesCount()); } function resetGame() { setGameStartDate(null); setGameEndDate(null); setTimer(getTimerValue(null, null)); setStatus(STATUS_PREVIEW); + + setTriesCount(getTriesCount()); + setOpenedCards([]); } /** @@ -127,11 +137,23 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { // "Игрок проиграл", т.к на поле есть две открытые карты без пары if (playerLost) { + if (liteVersion && triesCount > 1) { + setTriesCount(triesCount - 1); + const prevCards = cards.map(card => ({ + ...card, + open: card.open && openedCards.some(openCard => card.id === openCard.id), + })); + setTimeout(() => setCards(prevCards), 500); + return; + } + finishGame(STATUS_LOST); return; } // ... игра продолжается + + if (openCardsWithoutPair.length === 0) setOpenedCards(openCards); }; const isGameEnded = status === STATUS_LOST || status === STATUS_WON; @@ -153,6 +175,8 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { return shuffle(generateDeck(pairsCount, 10)); }); + setTriesCount(getTriesCount()); + const timerId = setTimeout(() => { startGame(); }, previewSeconds * 1000); @@ -210,6 +234,8 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { ))} + {liteVersion &&

{`You have a ${triesCount} tries.....`}

} + {isGameEnded ? ( diff --git a/src/pages/SelectLevelPage/SelectLevelPage.module.css b/src/pages/SelectLevelPage/SelectLevelPage.module.css index 8063c25a9..341662d0a 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.module.css +++ b/src/pages/SelectLevelPage/SelectLevelPage.module.css @@ -21,7 +21,7 @@ color: #004980; text-align: center; font-variant-numeric: lining-nums proportional-nums; - font-family: StratosSkyeng; + font-family: "StratosSkyeng", sans-serif; font-size: 40px; font-style: normal; font-weight: 400; @@ -51,12 +51,21 @@ .levelLink { color: #0080c1; text-align: center; - font-family: StratosSkyeng; + font-family: "StratosSkyeng", sans-serif; font-size: 64px; font-style: normal; font-weight: 400; line-height: 72px; text-decoration: none; + + &:hover { + font-weight: 600; + transform: scale(1.05); + } + + &.selected { + color: #c10080 !important; + } } .levelLink:visited { From 30be8eb16c4837316259325e33687fc3e1068679 Mon Sep 17 00:00:00 2001 From: anguseva25 Date: Tue, 9 Jul 2024 22:52:23 +0300 Subject: [PATCH 03/13] hw1 is ready --- src/components/Cards/Cards.jsx | 4 ++-- src/context/GameSettingsContext.js | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 4fae4e2b4..1ff266d40 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -58,7 +58,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { minutes: 0, }); - const { liteVersion, getTriesCount } = useContext(GameSettingsContext); + const { liteVersion, getTriesCount, printTriesText } = useContext(GameSettingsContext); const [triesCount, setTriesCount] = useState(getTriesCount()); const [openedCards, setOpenedCards] = useState([]); @@ -234,7 +234,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { ))} - {liteVersion &&

{`You have a ${triesCount} tries.....`}

} + {liteVersion &&

{printTriesText(triesCount)}

} {isGameEnded ? (
diff --git a/src/context/GameSettingsContext.js b/src/context/GameSettingsContext.js index 27c524736..92b5ce20b 100644 --- a/src/context/GameSettingsContext.js +++ b/src/context/GameSettingsContext.js @@ -10,8 +10,22 @@ export const GameSettingsProvider = ({ children }) => { return liteVersion ? 3 : 0; } + function printTriesText(count) { + switch (count) { + case 3: + return "У вас есть 3 попытки"; + case 2: + return "У вас осталось 2 попытки"; + case 1: + default: + return "У вас осталась последняя попытка"; + } + } + return ( - + {children} ); From 3a26bc4451cc22020d98ff23ecaa453c25625d11 Mon Sep 17 00:00:00 2001 From: anguseva25 Date: Fri, 12 Jul 2024 22:54:53 +0300 Subject: [PATCH 04/13] checkPair was added --- src/components/Cards/Cards.jsx | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 1ff266d40..35dc3d74d 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -46,6 +46,8 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const [cards, setCards] = useState([]); // Текущий статус игры const [status, setStatus] = useState(STATUS_PREVIEW); + // Статус активности проверки открытой пары карт + const [checkPair, setCheckPair] = useState(false); // Дата начала игры const [gameStartDate, setGameStartDate] = useState(null); @@ -60,7 +62,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const { liteVersion, getTriesCount, printTriesText } = useContext(GameSettingsContext); const [triesCount, setTriesCount] = useState(getTriesCount()); - const [openedCards, setOpenedCards] = useState([]); + const [openedCard, setOpenedCard] = useState(null); function finishGame(status = STATUS_LOST) { setGameEndDate(new Date()); @@ -81,8 +83,9 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setTimer(getTimerValue(null, null)); setStatus(STATUS_PREVIEW); + setCheckPair(false); setTriesCount(getTriesCount()); - setOpenedCards([]); + setOpenedCard(null); } /** @@ -94,9 +97,14 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { */ const openCard = clickedCard => { // Если карта уже открыта, то ничего не делаем - if (clickedCard.open) { + if (clickedCard.open || checkPair) { return; } + + if (openedCard) { + setCheckPair(true); + } + // Игровое поле после открытия кликнутой карты const nextCards = cards.map(card => { if (card.id !== clickedCard.id) { @@ -141,9 +149,13 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setTriesCount(triesCount - 1); const prevCards = cards.map(card => ({ ...card, - open: card.open && openedCards.some(openCard => card.id === openCard.id), + open: card.open && card.id !== openedCard.id && card.id !== clickedCard.id, })); - setTimeout(() => setCards(prevCards), 500); + setTimeout(() => { + setCards(prevCards); + setOpenedCard(null); + setCheckPair(false); + }, 500); return; } @@ -153,7 +165,12 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { // ... игра продолжается - if (openCardsWithoutPair.length === 0) setOpenedCards(openCards); + if (openCardsWithoutPair.length === 0) { + setOpenedCard(null); + setCheckPair(false); + } else if (openCardsWithoutPair.length === 1) { + setOpenedCard(clickedCard); + } }; const isGameEnded = status === STATUS_LOST || status === STATUS_WON; From fa56af9ada477d7ce89b7684d56c8ee5931a1dc0 Mon Sep 17 00:00:00 2001 From: anguseva25 Date: Mon, 15 Jul 2024 21:22:02 +0300 Subject: [PATCH 05/13] created LeaderBoardPage --- .eslintrc.json | 3 +- src/api/LeadersAPI.js | 30 +++++++++ src/components/Card/Card.module.css | 4 +- src/context/LeaderBoardContext.js | 18 ++++++ .../LeaderBoardPage/LeadeBoardPage.module.css | 63 +++++++++++++++++++ src/pages/LeaderBoardPage/LeaderBoardPage.jsx | 23 +++++++ src/pages/SelectLevelPage/SelectLevelPage.jsx | 7 ++- .../SelectLevelPage.module.css | 21 ++++++- src/router.js | 5 ++ 9 files changed, 167 insertions(+), 7 deletions(-) create mode 100644 src/api/LeadersAPI.js create mode 100644 src/context/LeaderBoardContext.js create mode 100644 src/pages/LeaderBoardPage/LeadeBoardPage.module.css create mode 100644 src/pages/LeaderBoardPage/LeaderBoardPage.jsx diff --git a/.eslintrc.json b/.eslintrc.json index e37e1e072..d9932d762 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,7 +4,6 @@ "rules": { "camelcase": ["error", { "properties": "never" }], "prettier/prettier": "error", - "eqeqeq": ["error", "always"], - "no-unused-vars": ["error"] + "eqeqeq": ["error", "always"] } } diff --git a/src/api/LeadersAPI.js b/src/api/LeadersAPI.js new file mode 100644 index 000000000..30bbc9f3a --- /dev/null +++ b/src/api/LeadersAPI.js @@ -0,0 +1,30 @@ +const topLeadersLink = "https://wedev-api.sky.pro/api/leaderboard"; + +export const getRequest = () => { + return fetch(topLeadersLink, { + method: "GET", + }).then(response => { + if (!response.ok) { + throw new Error("Что-то я заплутал..."); + } + if (response.status === 400) { + throw new Error("Полученные данные не в формате JSON"); + } + return response.json(); + }); +}; + +export const postRequest = ({}) => { + return fetch(topLeadersLink, { + method: "POST", + body: JSON.stringify(), + }).then(response => { + if (!response.ok) { + throw new Error("Что-то пошло не так"); + } + if (response.status === 400) { + throw new Error("Полученные данные не в формате JSON!"); + } + return response.json(); + }); +}; diff --git a/src/components/Card/Card.module.css b/src/components/Card/Card.module.css index 03390dec4..75202fbf9 100644 --- a/src/components/Card/Card.module.css +++ b/src/components/Card/Card.module.css @@ -79,8 +79,8 @@ } .flipContainer:hover .flipper { - transition: 0.2s; - transform: rotateY(35deg); + /*transition: 0.2s;*/ + /*transform: rotateY(35deg);*/ } .flipContainer.flip .flipper { diff --git a/src/context/LeaderBoardContext.js b/src/context/LeaderBoardContext.js new file mode 100644 index 000000000..8ae7dc6e2 --- /dev/null +++ b/src/context/LeaderBoardContext.js @@ -0,0 +1,18 @@ +import { createContext, useEffect, useState } from "react"; +import { getRequest } from "../api/LeadersAPI"; + +export const LeadersContext = createContext(null); +export const leadersTimeSorting = leaders => [...leaders].sort((a, b) => a.time - b.time); + +export const LeadersProvider = ({ children }) => { + const [leaders, setLeaders] = useState([]); + + useEffect(() => { + getRequest().then(leaders => { + const sortingLeaders = leadersTimeSorting(leaders.leaders); + setLeaders(sortingLeaders.splice(1, 10)); + }); + }, []); + + return {children}; +}; diff --git a/src/pages/LeaderBoardPage/LeadeBoardPage.module.css b/src/pages/LeaderBoardPage/LeadeBoardPage.module.css new file mode 100644 index 000000000..ee33361a7 --- /dev/null +++ b/src/pages/LeaderBoardPage/LeadeBoardPage.module.css @@ -0,0 +1,63 @@ +.container { + padding-left: calc(50% - 472px); + padding-right: calc(50% - 472px); +} + +.header { + display: flex; + justify-content: space-between; + padding-top: 50px; + padding-bottom: 50px; +} + +.title { + font-family: Roboto; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: left; + color: #ffffff; +} + +.mainBox { + width: 944px; + height: 64px; + display: flex; + gap: 66px; + box-sizing: border-box; + padding-top: 16px; + margin-bottom: 16px; + font-size: 24px; + font-family: StratosSkyeng; + background-color: #fff; + border-radius: 12px; +} + +.usersPosition { + width: 178px; + /*font-family: Poppins;*/ + font-size: 24px; + font-weight: 400; + line-height: 32px; + margin-left: 20px; +} + +.nameUser { + width: 300px; + /*font-family: Poppins;*/ + font-size: 24px; + font-weight: 400; + line-height: 32px; + margin-left: 20px; +} + +.timeRecord { + width: 130px; + /*font-family: Poppins;*/ + font-size: 24px; + font-weight: 400; + line-height: 32px; + margin-left: 220px; + box-sizing: border-box; +} + diff --git a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx new file mode 100644 index 000000000..74f8c6b1f --- /dev/null +++ b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx @@ -0,0 +1,23 @@ +import { Link } from "react-router-dom"; +import { Button } from "../../components/Button/Button"; +import styles from "./LeadeBoardPage.module.css"; + +export const LeaderBoardPage = () => { + return ( +
+
+

Лидерборд

+ + + +
+
+
+

Позиция

+

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

+

Время

+
+
+
+ ); +}; diff --git a/src/pages/SelectLevelPage/SelectLevelPage.jsx b/src/pages/SelectLevelPage/SelectLevelPage.jsx index ca7bb5b7e..07bc06995 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.jsx +++ b/src/pages/SelectLevelPage/SelectLevelPage.jsx @@ -1,5 +1,5 @@ import { useContext } from "react"; -import { useNavigate } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import styles from "./SelectLevelPage.module.css"; import { Button } from "../../components/Button/Button"; import { GameSettingsContext } from "../../context/GameSettingsContext"; @@ -58,10 +58,13 @@ export function SelectLevelPage() { + + Перейти к лидерборду +
); diff --git a/src/pages/SelectLevelPage/SelectLevelPage.module.css b/src/pages/SelectLevelPage/SelectLevelPage.module.css index 341662d0a..4c002f49d 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.module.css +++ b/src/pages/SelectLevelPage/SelectLevelPage.module.css @@ -75,6 +75,25 @@ .liteMode { color: #004980; font-family: "StratosSkyeng", sans-serif; - font-size: 18px; + /*font-size: 18px;*/ margin-bottom: 48px; + /* font-family: Roboto; */ + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: center; + +} + +.link { + color: #004980; + text-align: center; + font-variant-numeric: lining-nums proportional-nums; + font-family: StratosSkyeng; + font-size: 20px; + font-style: normal; + font-weight: 400; + line-height: 24px; + margin-top: 18px; + } diff --git a/src/router.js b/src/router.js index 570b1b6f8..acb1ff86b 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( [ @@ -16,6 +17,10 @@ export const router = createBrowserRouter( path: "/game/:pairsCount", element: , }, + { + path: "/leaderboard", + element: , + }, ], /** * basename нужен для корректной работы в gh pages From 232f5c93c4ee39de90c03fdd13f96d938bbda548 Mon Sep 17 00:00:00 2001 From: anguseva25 Date: Tue, 16 Jul 2024 20:02:29 +0300 Subject: [PATCH 06/13] post results --- src/api/LeadersAPI.js | 4 +- src/components/Cards/Cards.jsx | 1 + src/components/EndGameModal/EndGameModal.jsx | 45 +++++++++++++++++-- .../EndGameModal/EndGameModal.module.css | 12 +++++ src/context/LeaderBoardContext.js | 11 ++++- src/index.js | 5 ++- .../LeaderBoardPage/LeadeBoardPage.module.css | 10 ++++- src/pages/LeaderBoardPage/LeaderBoardPage.jsx | 20 +++++++-- .../SelectLevelPage.module.css | 3 +- 9 files changed, 94 insertions(+), 17 deletions(-) diff --git a/src/api/LeadersAPI.js b/src/api/LeadersAPI.js index 30bbc9f3a..f65e17b16 100644 --- a/src/api/LeadersAPI.js +++ b/src/api/LeadersAPI.js @@ -14,10 +14,10 @@ export const getRequest = () => { }); }; -export const postRequest = ({}) => { +export const postRequest = record => { return fetch(topLeadersLink, { method: "POST", - body: JSON.stringify(), + body: JSON.stringify(record), }).then(response => { if (!response.ok) { throw new Error("Что-то пошло не так"); diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 35dc3d74d..055ef5e6b 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -257,6 +257,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) {
setLeaders(data.leaders)); + } + + function handleSubmit() { + sendStatics(); + + onClick(); + } + + function handleLinkToLeaderboard(e) { + e.preventDefault(); + sendStatics(); + navigate("/leaderboard"); + } + return ( ); } diff --git a/src/components/EndGameModal/EndGameModal.module.css b/src/components/EndGameModal/EndGameModal.module.css index 5018bdf41..3b33b3ac4 100644 --- a/src/components/EndGameModal/EndGameModal.module.css +++ b/src/components/EndGameModal/EndGameModal.module.css @@ -49,3 +49,15 @@ margin-bottom: 40px; } + +.link { + color: #004980; + text-align: center; + font-variant-numeric: lining-nums proportional-nums; + font-family: "StratosSkyeng", sans-serif; + font-size: 20px; + font-style: normal; + font-weight: 400; + line-height: 24px; + margin-top: 18px; +} diff --git a/src/context/LeaderBoardContext.js b/src/context/LeaderBoardContext.js index 8ae7dc6e2..c864bc3c7 100644 --- a/src/context/LeaderBoardContext.js +++ b/src/context/LeaderBoardContext.js @@ -2,7 +2,14 @@ import { createContext, useEffect, useState } from "react"; import { getRequest } from "../api/LeadersAPI"; export const LeadersContext = createContext(null); -export const leadersTimeSorting = leaders => [...leaders].sort((a, b) => a.time - b.time); +export const leadersTimeSorting = leaders => leaders.sort((a, b) => a.time - b.time); + +async function updateData() { + getRequest().then(leaders => { + const sortingLeaders = leadersTimeSorting(leaders.leaders); + //setLeaders(sortingLeaders.splice(0, 10)); + }); +} export const LeadersProvider = ({ children }) => { const [leaders, setLeaders] = useState([]); @@ -10,7 +17,7 @@ export const LeadersProvider = ({ children }) => { useEffect(() => { getRequest().then(leaders => { const sortingLeaders = leadersTimeSorting(leaders.leaders); - setLeaders(sortingLeaders.splice(1, 10)); + setLeaders(sortingLeaders.splice(0, 10)); }); }, []); diff --git a/src/index.js b/src/index.js index 10664b4cc..41e0be5da 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 { GameSettingsProvider } from "./context/GameSettingsContext"; +import { LeadersProvider } from "./context/LeaderBoardContext"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( - + + + , ); diff --git a/src/pages/LeaderBoardPage/LeadeBoardPage.module.css b/src/pages/LeaderBoardPage/LeadeBoardPage.module.css index ee33361a7..312c7a514 100644 --- a/src/pages/LeaderBoardPage/LeadeBoardPage.module.css +++ b/src/pages/LeaderBoardPage/LeadeBoardPage.module.css @@ -11,7 +11,7 @@ } .title { - font-family: Roboto; + font-family: "StratosSkyeng", sans-serif; font-size: 24px; font-weight: 400; line-height: 32px; @@ -28,7 +28,7 @@ padding-top: 16px; margin-bottom: 16px; font-size: 24px; - font-family: StratosSkyeng; + font-family: "StratosSkyeng", sans-serif; background-color: #fff; border-radius: 12px; } @@ -40,6 +40,7 @@ font-weight: 400; line-height: 32px; margin-left: 20px; + /*color: #999999;*/ } .nameUser { @@ -49,6 +50,7 @@ font-weight: 400; line-height: 32px; margin-left: 20px; + /*color: #999999;*/ } .timeRecord { @@ -59,5 +61,9 @@ line-height: 32px; margin-left: 220px; box-sizing: border-box; + /*color: #999999;*/ } +.headerLine { + color: #999999; +} diff --git a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx index 74f8c6b1f..7ea1ed842 100644 --- a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx +++ b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx @@ -1,22 +1,34 @@ import { Link } from "react-router-dom"; import { Button } from "../../components/Button/Button"; import styles from "./LeadeBoardPage.module.css"; +import { useContext } from "react"; +import { LeadersContext } from "../../context/LeaderBoardContext"; +import cn from "classnames"; export const LeaderBoardPage = () => { + const { leaders } = useContext(LeadersContext); + return (

Лидерборд

- +
-

Позиция

-

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

-

Время

+

Позиция

+

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

+

Время

+ {leaders.map((leader, index) => ( +
+

{index + 1}

+

{leader.name}

+

{leader.time}

+
+ ))}
); diff --git a/src/pages/SelectLevelPage/SelectLevelPage.module.css b/src/pages/SelectLevelPage/SelectLevelPage.module.css index 4c002f49d..a824c2460 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.module.css +++ b/src/pages/SelectLevelPage/SelectLevelPage.module.css @@ -89,11 +89,10 @@ color: #004980; text-align: center; font-variant-numeric: lining-nums proportional-nums; - font-family: StratosSkyeng; + font-family: "StratosSkyeng", sans-serif; font-size: 20px; font-style: normal; font-weight: 400; line-height: 24px; margin-top: 18px; - } From 5073b9736deb11d8e3c6f6723fab4bb44c0b9e52 Mon Sep 17 00:00:00 2001 From: anguseva25 Date: Tue, 16 Jul 2024 21:32:11 +0300 Subject: [PATCH 07/13] added the 'hasAchievement' conditions --- src/components/Cards/Cards.jsx | 2 +- src/components/EndGameModal/EndGameModal.jsx | 9 ++++- .../EndGameModal/EndGameModal.module.css | 34 ++++++++++++++++--- .../SelectLevelPage.module.css | 6 ++-- 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 055ef5e6b..d60186bc2 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -257,7 +257,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) {
= 9 && !liteVersion} gameDurationSeconds={timer.seconds} gameDurationMinutes={timer.minutes} onClick={resetGame} diff --git a/src/components/EndGameModal/EndGameModal.jsx b/src/components/EndGameModal/EndGameModal.jsx index be56118a4..cd997de95 100644 --- a/src/components/EndGameModal/EndGameModal.jsx +++ b/src/components/EndGameModal/EndGameModal.jsx @@ -49,7 +49,14 @@ export function EndGameModal({ isWon, hasAchievement, gameDurationSeconds, gameD
{imgAlt}

{title}

- {hasAchievement && } + {hasAchievement && ( + + )}

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

{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 3b33b3ac4..3e1e7db33 100644 --- a/src/components/EndGameModal/EndGameModal.module.css +++ b/src/components/EndGameModal/EndGameModal.module.css @@ -1,6 +1,7 @@ .modal { width: 480px; - height: 459px; + /*height: 459px;*/ + height: 634px; border-radius: 12px; background: #c2f5ff; display: flex; @@ -23,7 +24,7 @@ font-style: normal; font-weight: 400; line-height: 48px; - + text-align: center; margin-bottom: 28px; } @@ -55,9 +56,34 @@ text-align: center; font-variant-numeric: lining-nums proportional-nums; font-family: "StratosSkyeng", sans-serif; - font-size: 20px; + font-size: 18px; font-style: normal; font-weight: 400; - line-height: 24px; + line-height: 32px; margin-top: 18px; } + +.input{ + width: 276px; + height: 45px; + top: 334px; + left: 374px; + border-radius: 10px; + background: #ffffff; + font-family: "StratosSkyeng", sans-serif; + font-size: 24px; + font-weight: 400; + line-height: 32px; + text-align: center; + border: none; + outline: none; + margin-bottom: 20px; +} + +.placeholder{ + text-align: center; + font-size: 24px; + font-weight: 400; + line-height: 32px; + font-family: "StratosSkyeng", sans-serif; +} \ No newline at end of file diff --git a/src/pages/SelectLevelPage/SelectLevelPage.module.css b/src/pages/SelectLevelPage/SelectLevelPage.module.css index a824c2460..db3a776e7 100644 --- a/src/pages/SelectLevelPage/SelectLevelPage.module.css +++ b/src/pages/SelectLevelPage/SelectLevelPage.module.css @@ -75,9 +75,7 @@ .liteMode { color: #004980; font-family: "StratosSkyeng", sans-serif; - /*font-size: 18px;*/ margin-bottom: 48px; - /* font-family: Roboto; */ font-size: 24px; font-weight: 400; line-height: 32px; @@ -90,9 +88,9 @@ text-align: center; font-variant-numeric: lining-nums proportional-nums; font-family: "StratosSkyeng", sans-serif; - font-size: 20px; + font-size: 18px; font-style: normal; font-weight: 400; - line-height: 24px; + line-height: 32px; margin-top: 18px; } From b23d024dc4fbc22513f08751e5024aae5e4375e0 Mon Sep 17 00:00:00 2001 From: anguseva25 Date: Tue, 16 Jul 2024 21:37:07 +0300 Subject: [PATCH 08/13] added time for hw in readme.md file --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 8e8441d1a..dc4b12ebf 100644 --- a/README.md +++ b/README.md @@ -49,3 +49,8 @@ https://skypro-web-developer.github.io/react-memo/ Предполагаемое время: 8 часов. Фактическое время: 5 часов. + +## Вторая домашняя работа (оценка) + +Предполагаемое время: 8 часов. +Фактическое время: 12 часов. From 6ec69bd91e79e1b98bed9b417174dcbf7f437032 Mon Sep 17 00:00:00 2001 From: anguseva25 Date: Thu, 18 Jul 2024 21:00:52 +0300 Subject: [PATCH 09/13] correction: added btnEnter, time format in leaderBoard --- src/components/Card/Card.module.css | 4 +-- src/components/Cards/Cards.jsx | 2 ++ src/components/EndGameModal/EndGameModal.jsx | 28 +++++++++++----- .../EndGameModal/EndGameModal.module.css | 32 +++++++++++++++++-- src/pages/LeaderBoardPage/LeaderBoardPage.jsx | 9 +++++- 5 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/components/Card/Card.module.css b/src/components/Card/Card.module.css index 75202fbf9..03390dec4 100644 --- a/src/components/Card/Card.module.css +++ b/src/components/Card/Card.module.css @@ -79,8 +79,8 @@ } .flipContainer:hover .flipper { - /*transition: 0.2s;*/ - /*transform: rotateY(35deg);*/ + transition: 0.2s; + transform: rotateY(35deg); } .flipContainer.flip .flipper { diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index d60186bc2..4e65acd9a 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -206,6 +206,8 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { // Обновляем значение таймера в интервале useEffect(() => { const intervalId = setInterval(() => { + if (status === STATUS_LOST || status === STATUS_WON) return; + setTimer(getTimerValue(gameStartDate, gameEndDate)); }, 300); return () => { diff --git a/src/components/EndGameModal/EndGameModal.jsx b/src/components/EndGameModal/EndGameModal.jsx index cd997de95..18afba0a1 100644 --- a/src/components/EndGameModal/EndGameModal.jsx +++ b/src/components/EndGameModal/EndGameModal.jsx @@ -13,6 +13,7 @@ export function EndGameModal({ isWon, hasAchievement, gameDurationSeconds, gameD const { setLeaders } = useContext(LeadersContext); const navigate = useNavigate(); const [inputValue, setInputValue] = useState(""); + const [dataIsSent, setDataIsSent] = useState(false); const title = isWon && hasAchievement ? "Вы попали на Лидерборд!" : isWon ? "Вы победили!" : "Вы проиграли!"; const imgSrc = isWon ? celebrationImageUrl : deadImageUrl; @@ -30,7 +31,10 @@ export function EndGameModal({ isWon, hasAchievement, gameDurationSeconds, gameD postRequest({ name: username, time: gameDurationMinutes * 60 + gameDurationSeconds, - }).then(data => setLeaders(data.leaders)); + }).then(data => { + setLeaders(data.leaders); + setDataIsSent(true); + }); } function handleSubmit() { @@ -41,7 +45,7 @@ export function EndGameModal({ isWon, hasAchievement, gameDurationSeconds, gameD function handleLinkToLeaderboard(e) { e.preventDefault(); - sendStatics(); + // sendStatics(); navigate("/leaderboard"); } @@ -50,12 +54,20 @@ export function EndGameModal({ isWon, hasAchievement, gameDurationSeconds, gameD {imgAlt}

{title}

{hasAchievement && ( - +
+ + {dataIsSent || ( + + )} +
)}

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

diff --git a/src/components/EndGameModal/EndGameModal.module.css b/src/components/EndGameModal/EndGameModal.module.css index 3e1e7db33..7cda1a6ff 100644 --- a/src/components/EndGameModal/EndGameModal.module.css +++ b/src/components/EndGameModal/EndGameModal.module.css @@ -63,7 +63,7 @@ margin-top: 18px; } -.input{ +.input { width: 276px; height: 45px; top: 334px; @@ -77,13 +77,39 @@ text-align: center; border: none; outline: none; - margin-bottom: 20px; } -.placeholder{ +.placeholder { text-align: center; font-size: 24px; font-weight: 400; line-height: 32px; font-family: "StratosSkyeng", sans-serif; +} + +.addNameUser { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 20px; +} + +.btnAddUser { + height: 48px; + border-radius: 12px; + background: #7ac100; + color: #fff; + text-align: center; + font-variant-numeric: lining-nums proportional-nums; + font-family: "StratosSkyeng", sans-serif; + font-size: 24px; + font-style: normal; + font-weight: 400; + line-height: 32px; + border: none; + cursor: pointer; + + &:disabled { + background: cadetblue; + } } \ No newline at end of file diff --git a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx index 7ea1ed842..78df2e51f 100644 --- a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx +++ b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx @@ -5,6 +5,13 @@ import { useContext } from "react"; import { LeadersContext } from "../../context/LeaderBoardContext"; import cn from "classnames"; +function formatTime(seconds) { + const minutes = Math.floor(seconds / 60); + seconds = seconds % 60; + + return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`; +} + export const LeaderBoardPage = () => { const { leaders } = useContext(LeadersContext); @@ -26,7 +33,7 @@ export const LeaderBoardPage = () => {

{index + 1}

{leader.name}

-

{leader.time}

+

{formatTime(leader.time)}

))} From 989c3883c53729f4075bbab5af7f21af5dcf8a61 Mon Sep 17 00:00:00 2001 From: anguseva25 Date: Thu, 18 Jul 2024 21:30:07 +0300 Subject: [PATCH 10/13] correction: sending name by btn Enter --- src/components/EndGameModal/EndGameModal.jsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/components/EndGameModal/EndGameModal.jsx b/src/components/EndGameModal/EndGameModal.jsx index 18afba0a1..701240006 100644 --- a/src/components/EndGameModal/EndGameModal.jsx +++ b/src/components/EndGameModal/EndGameModal.jsx @@ -23,6 +23,14 @@ export function EndGameModal({ isWon, hasAchievement, gameDurationSeconds, gameD setInputValue(e.target.value); } + function handleKeyDown(e) { + if (e.key === "Enter") { + sendStatics(); + e.preventDefault(); + e.stopPropagation(); + } + } + function sendStatics() { const username = inputValue.trim(); @@ -60,6 +68,7 @@ export function EndGameModal({ isWon, hasAchievement, gameDurationSeconds, gameD placeholder={"Пользователь"} value={inputValue} onChange={handleChangeUsername} + onKeyDown={handleKeyDown} readOnly={dataIsSent} /> {dataIsSent || ( From 0e7c881ac4b023d5b72e9bbc3e89217b563d28c6 Mon Sep 17 00:00:00 2001 From: anguseva25 Date: Fri, 19 Jul 2024 22:05:14 +0300 Subject: [PATCH 11/13] correction: validation blanc text --- src/components/Cards/Cards.jsx | 2 +- src/components/EndGameModal/EndGameModal.jsx | 38 +++++++++++-------- .../EndGameModal/EndGameModal.module.css | 23 ++++++++++- src/context/LeaderBoardContext.js | 2 +- 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 4e65acd9a..7221ecd47 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -259,7 +259,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) {
= 9 && !liteVersion} + hasAchievement={pairsCount >= 9} gameDurationSeconds={timer.seconds} gameDurationMinutes={timer.minutes} onClick={resetGame} diff --git a/src/components/EndGameModal/EndGameModal.jsx b/src/components/EndGameModal/EndGameModal.jsx index 701240006..83caa19bb 100644 --- a/src/components/EndGameModal/EndGameModal.jsx +++ b/src/components/EndGameModal/EndGameModal.jsx @@ -14,6 +14,7 @@ export function EndGameModal({ isWon, hasAchievement, gameDurationSeconds, gameD const navigate = useNavigate(); const [inputValue, setInputValue] = useState(""); const [dataIsSent, setDataIsSent] = useState(false); + const [error, setError] = useState(""); const title = isWon && hasAchievement ? "Вы попали на Лидерборд!" : isWon ? "Вы победили!" : "Вы проиграли!"; const imgSrc = isWon ? celebrationImageUrl : deadImageUrl; @@ -34,7 +35,10 @@ export function EndGameModal({ isWon, hasAchievement, gameDurationSeconds, gameD function sendStatics() { const username = inputValue.trim(); - if (!username) return; + if (!username) { + setError("Введите имя"); + return; + } postRequest({ name: username, @@ -42,6 +46,7 @@ export function EndGameModal({ isWon, hasAchievement, gameDurationSeconds, gameD }).then(data => { setLeaders(data.leaders); setDataIsSent(true); + setError(""); }); } @@ -62,20 +67,23 @@ export function EndGameModal({ isWon, hasAchievement, gameDurationSeconds, gameD {imgAlt}

{title}

{hasAchievement && ( -
- - {dataIsSent || ( - - )} +
+
+ + {dataIsSent || ( + + )} +
+ {error &&

{error}

}
)}

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

diff --git a/src/components/EndGameModal/EndGameModal.module.css b/src/components/EndGameModal/EndGameModal.module.css index 7cda1a6ff..25be85b97 100644 --- a/src/components/EndGameModal/EndGameModal.module.css +++ b/src/components/EndGameModal/EndGameModal.module.css @@ -77,6 +77,10 @@ text-align: center; border: none; outline: none; + + &:read-only { + background: lightcyan; + } } .placeholder { @@ -87,11 +91,19 @@ font-family: "StratosSkyeng", sans-serif; } +.inputData { + margin-bottom: 20px; + + p { + text-align: center; + } +} + .addNameUser { display: flex; align-items: center; gap: 8px; - margin-bottom: 20px; + margin-bottom: 6px; } .btnAddUser { @@ -112,4 +124,11 @@ &:disabled { background: cadetblue; } -} \ No newline at end of file +} + +.error { + color: lightcoral; + font-family: "StratosSkyeng", sans-serif; + font-size: 18px; + font-weight: 700; +} diff --git a/src/context/LeaderBoardContext.js b/src/context/LeaderBoardContext.js index c864bc3c7..0d102173a 100644 --- a/src/context/LeaderBoardContext.js +++ b/src/context/LeaderBoardContext.js @@ -19,7 +19,7 @@ export const LeadersProvider = ({ children }) => { const sortingLeaders = leadersTimeSorting(leaders.leaders); setLeaders(sortingLeaders.splice(0, 10)); }); - }, []); + }); return {children}; }; From f10030c0f8bd6192d0254c0294545cef4dc7f5d5 Mon Sep 17 00:00:00 2001 From: anguseva25 Date: Sat, 20 Jul 2024 22:24:35 +0300 Subject: [PATCH 12/13] kursach is done --- src/api/LeadersAPI.js | 2 +- .../AchievementsImages/AchievementsImages.jsx | 62 +++++++++++++++++++ .../AchievementsImages.module.css | 39 ++++++++++++ .../AchievementsImages/images/hardLevel.svg | 15 +++++ .../images/hardLevelGrey.svg | 17 +++++ .../AchievementsImages/images/superPower.svg | 14 +++++ .../images/superPowerGrey.svg | 30 +++++++++ src/components/Cards/Cards.jsx | 33 ++++++++-- src/components/Cards/Cards.module.css | 5 ++ src/components/EndGameModal/EndGameModal.jsx | 7 ++- src/components/Hints/Hints.jsx | 47 ++++++++++++++ src/components/Hints/Hints.module.css | 55 ++++++++++++++++ src/components/Hints/images/eye.svg | 34 ++++++++++ src/components/Hints/images/randomCards.svg | 13 ++++ src/pages/LeaderBoardPage/LeaderBoardPage.jsx | 9 ++- ....module.css => LeaderBoardPage.module.css} | 24 +++---- 16 files changed, 384 insertions(+), 22 deletions(-) create mode 100644 src/components/AchievementsImages/AchievementsImages.jsx create mode 100644 src/components/AchievementsImages/AchievementsImages.module.css create mode 100644 src/components/AchievementsImages/images/hardLevel.svg create mode 100644 src/components/AchievementsImages/images/hardLevelGrey.svg create mode 100644 src/components/AchievementsImages/images/superPower.svg create mode 100644 src/components/AchievementsImages/images/superPowerGrey.svg create mode 100644 src/components/Hints/Hints.jsx create mode 100644 src/components/Hints/Hints.module.css create mode 100644 src/components/Hints/images/eye.svg create mode 100644 src/components/Hints/images/randomCards.svg rename src/pages/LeaderBoardPage/{LeadeBoardPage.module.css => LeaderBoardPage.module.css} (76%) diff --git a/src/api/LeadersAPI.js b/src/api/LeadersAPI.js index f65e17b16..b6c065b3f 100644 --- a/src/api/LeadersAPI.js +++ b/src/api/LeadersAPI.js @@ -1,4 +1,4 @@ -const topLeadersLink = "https://wedev-api.sky.pro/api/leaderboard"; +const topLeadersLink = "https://wedev-api.sky.pro/api/v2/leaderboard"; export const getRequest = () => { return fetch(topLeadersLink, { diff --git a/src/components/AchievementsImages/AchievementsImages.jsx b/src/components/AchievementsImages/AchievementsImages.jsx new file mode 100644 index 000000000..f17e3eed4 --- /dev/null +++ b/src/components/AchievementsImages/AchievementsImages.jsx @@ -0,0 +1,62 @@ +import { useState } from "react"; +import styles from "./AchievementsImages.module.css"; + +import hardLevelImage from "./images/hardLevel.svg"; +import superPowerImage from "./images/superPower.svg"; +import hardLevelGreyImage from "./images/hardLevelGrey.svg"; +import superPowerGreyImage from "./images/superPowerGrey.svg"; + +export function HardLevelImg({ disabled }) { + const [isOpen, setOpen] = useState(false); + + function mouseHover(value) { + if (disabled) return; + + setOpen(value); + } + + return ( +
+ {!disabled && isOpen && ( +

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

+ )} + hard level mouseHover(true)} + onMouseOut={() => mouseHover(false)} + /> +
+ ); +} + +export function SuperPowerImg({ disabled }) { + const [isOpen, setOpen] = useState(false); + + function mouseHover(value) { + if (disabled) return; + + setOpen(value); + } + + return ( +
+ {!disabled && isOpen && ( +

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

+ )} + hard level mouseHover(true)} + onMouseOut={() => mouseHover(false)} + /> +
+ ); +} diff --git a/src/components/AchievementsImages/AchievementsImages.module.css b/src/components/AchievementsImages/AchievementsImages.module.css new file mode 100644 index 000000000..74883c6aa --- /dev/null +++ b/src/components/AchievementsImages/AchievementsImages.module.css @@ -0,0 +1,39 @@ + +.cntHint { + position: relative; + + img { + display: block; + position: relative; + z-index: 1; + } +} + +.cntInsight { + background: #C2F5FF; + border-radius: 12px; + position: absolute; + bottom: 45px; + left: 3px; + padding: 25px 20px 20px; + width: 222px; + z-index: 1; + box-sizing: border-box; + font-family: "StratosSkyeng", sans-serif; + font-size: 18px; + font-weight: 400; + line-height: 24px; + text-align: center; + color: #004980; + + &:after { + content: ""; + width: 20px; + height: 14px; + position: absolute; + bottom: -9px; + left: 14px; + background: #C2F5FF; + clip-path: polygon(0% 100%, 0% 0%, 100% 0%); + } +} diff --git a/src/components/AchievementsImages/images/hardLevel.svg b/src/components/AchievementsImages/images/hardLevel.svg new file mode 100644 index 000000000..d101cef6d --- /dev/null +++ b/src/components/AchievementsImages/images/hardLevel.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/components/AchievementsImages/images/hardLevelGrey.svg b/src/components/AchievementsImages/images/hardLevelGrey.svg new file mode 100644 index 000000000..9538c3d7d --- /dev/null +++ b/src/components/AchievementsImages/images/hardLevelGrey.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/components/AchievementsImages/images/superPower.svg b/src/components/AchievementsImages/images/superPower.svg new file mode 100644 index 000000000..c74a8b83d --- /dev/null +++ b/src/components/AchievementsImages/images/superPower.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/components/AchievementsImages/images/superPowerGrey.svg b/src/components/AchievementsImages/images/superPowerGrey.svg new file mode 100644 index 000000000..0557f5282 --- /dev/null +++ b/src/components/AchievementsImages/images/superPowerGrey.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Cards/Cards.jsx b/src/components/Cards/Cards.jsx index 7221ecd47..b325c8a59 100644 --- a/src/components/Cards/Cards.jsx +++ b/src/components/Cards/Cards.jsx @@ -6,6 +6,7 @@ import { EndGameModal } from "../EndGameModal/EndGameModal"; import { Button } from "../Button/Button"; import { Card } from "../Card/Card"; import { GameSettingsContext } from "../../context/GameSettingsContext"; +import { InsightHint } from "../Hints/Hints"; // Игра закончилась const STATUS_LOST = "STATUS_LOST"; @@ -14,6 +15,7 @@ const STATUS_WON = "STATUS_WON"; const STATUS_IN_PROGRESS = "STATUS_IN_PROGRESS"; // Начало игры: игрок видит все карты в течении нескольких секунд const STATUS_PREVIEW = "STATUS_PREVIEW"; +const STATUS_FROZEN = "STATUS_FROZEN"; function getTimerValue(startDate, endDate) { if (!startDate && !endDate) { @@ -60,6 +62,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { minutes: 0, }); + const [hasHint, setHasHint] = useState(true); const { liteVersion, getTriesCount, printTriesText } = useContext(GameSettingsContext); const [triesCount, setTriesCount] = useState(getTriesCount()); const [openedCard, setOpenedCard] = useState(null); @@ -86,6 +89,18 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { setCheckPair(false); setTriesCount(getTriesCount()); setOpenedCard(null); + setHasHint(true); + } + + function handleInsightClick() { + setStatus(STATUS_FROZEN); + + setTimeout(() => { + setStatus(STATUS_IN_PROGRESS); + setHasHint(false); + + gameStartDate.setMilliseconds(gameStartDate.getMilliseconds() + 5000); + }, 5000); } /** @@ -175,6 +190,9 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { const isGameEnded = status === STATUS_LOST || status === STATUS_WON; + const achievements = + status !== STATUS_WON ? [] : pairsCount >= 9 && hasHint ? [1, 2] : pairsCount >= 9 ? [1] : hasHint ? [2] : []; + // Игровой цикл useEffect(() => { // В статусах кроме превью доп логики не требуется @@ -206,14 +224,14 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { // Обновляем значение таймера в интервале useEffect(() => { const intervalId = setInterval(() => { - if (status === STATUS_LOST || status === STATUS_WON) return; + if (status === STATUS_LOST || status === STATUS_WON || status === STATUS_FROZEN) return; setTimer(getTimerValue(gameStartDate, gameEndDate)); }, 300); return () => { clearInterval(intervalId); }; - }, [gameStartDate, gameEndDate]); + }, [gameStartDate, gameEndDate, status]); return (
@@ -238,7 +256,12 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { )}
- {status === STATUS_IN_PROGRESS ? : null} + {status === STATUS_IN_PROGRESS || status === STATUS_FROZEN ? ( + <> + + + + ) : null}
@@ -246,7 +269,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) { openCard(card)} - open={status !== STATUS_IN_PROGRESS ? true : card.open} + open={status !== STATUS_IN_PROGRESS || status === STATUS_FROZEN ? true : card.open} suit={card.suit} rank={card.rank} /> @@ -259,7 +282,7 @@ export function Cards({ pairsCount = 3, previewSeconds = 5 }) {
= 9} + achievements={achievements} gameDurationSeconds={timer.seconds} gameDurationMinutes={timer.minutes} onClick={resetGame} diff --git a/src/components/Cards/Cards.module.css b/src/components/Cards/Cards.module.css index 12693cbca..48bd7f9a5 100644 --- a/src/components/Cards/Cards.module.css +++ b/src/components/Cards/Cards.module.css @@ -1,9 +1,12 @@ .container { width: 672px; + height: 100vh; margin: 0 auto; padding: 26px; padding-top: 22px; box-sizing: border-box; + + overflow: hidden; } .cards { @@ -23,6 +26,8 @@ justify-content: center; align-items: center; background: #004980c0; + + clear: both; } .header { diff --git a/src/components/EndGameModal/EndGameModal.jsx b/src/components/EndGameModal/EndGameModal.jsx index 83caa19bb..fd8c07179 100644 --- a/src/components/EndGameModal/EndGameModal.jsx +++ b/src/components/EndGameModal/EndGameModal.jsx @@ -9,14 +9,14 @@ import { useContext, useState } from "react"; import { LeadersContext } from "../../context/LeaderBoardContext"; import { useNavigate } from "react-router-dom"; -export function EndGameModal({ isWon, hasAchievement, gameDurationSeconds, gameDurationMinutes, onClick }) { +export function EndGameModal({ isWon, achievements, gameDurationSeconds, gameDurationMinutes, onClick }) { const { setLeaders } = useContext(LeadersContext); const navigate = useNavigate(); const [inputValue, setInputValue] = useState(""); const [dataIsSent, setDataIsSent] = useState(false); const [error, setError] = useState(""); - const title = isWon && hasAchievement ? "Вы попали на Лидерборд!" : isWon ? "Вы победили!" : "Вы проиграли!"; + const title = isWon ? "Вы попали на Лидерборд!" : "Вы проиграли!"; const imgSrc = isWon ? celebrationImageUrl : deadImageUrl; const imgAlt = isWon ? "celebration emodji" : "dead emodji"; @@ -43,6 +43,7 @@ export function EndGameModal({ isWon, hasAchievement, gameDurationSeconds, gameD postRequest({ name: username, time: gameDurationMinutes * 60 + gameDurationSeconds, + achievements: achievements, }).then(data => { setLeaders(data.leaders); setDataIsSent(true); @@ -66,7 +67,7 @@ export function EndGameModal({ isWon, hasAchievement, gameDurationSeconds, gameD
{imgAlt}

{title}

- {hasAchievement && ( + {isWon && (
+ {isOpen && ( + <> +
+
+

Прозрение

+

+ На 5 секунд показываются все карты. Таймер длительности игры на это время останавливается. +

+
+ + )} + {"eye"} mouseHover(true)} + onMouseOut={() => mouseHover(false)} + /> +
+ ); +} diff --git a/src/components/Hints/Hints.module.css b/src/components/Hints/Hints.module.css new file mode 100644 index 000000000..c129f3733 --- /dev/null +++ b/src/components/Hints/Hints.module.css @@ -0,0 +1,55 @@ + +.cntHint { + position: relative; + + img { + position: relative; + z-index: 1; + } +} + +.disabled { + opacity: 0.4; +} + +.bckColor { + position: absolute; + width: 200vw; + height: 200vh; + background: #004980; + opacity: 0.6; + z-index: 1; + transform: translate(-50%, -50%); +} + +.cntInsight { + display: flex; + flex-direction: column; + align-items: center; + background: #C2F5FF; + border-radius: 12px; + position: absolute; + top: 84px; + left: -77px; + padding: 25px 20px 20px; + width: 222px; + z-index: 1; + box-sizing: border-box; + font-family: "StratosSkyeng", sans-serif; + color: #004980; +} + +.insightTitle { + font-size: 18px; + font-weight: 700; + line-height: 24px; + text-align: center; + margin-bottom: 10px; +} + +.insightDescription { + font-size: 18px; + font-weight: 400; + line-height: 24px; + text-align: center; +} diff --git a/src/components/Hints/images/eye.svg b/src/components/Hints/images/eye.svg new file mode 100644 index 000000000..40a1796f4 --- /dev/null +++ b/src/components/Hints/images/eye.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Hints/images/randomCards.svg b/src/components/Hints/images/randomCards.svg new file mode 100644 index 000000000..40c7ebfc2 --- /dev/null +++ b/src/components/Hints/images/randomCards.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx index 78df2e51f..e5c03359d 100644 --- a/src/pages/LeaderBoardPage/LeaderBoardPage.jsx +++ b/src/pages/LeaderBoardPage/LeaderBoardPage.jsx @@ -1,10 +1,12 @@ import { Link } from "react-router-dom"; import { Button } from "../../components/Button/Button"; -import styles from "./LeadeBoardPage.module.css"; +import styles from "./LeaderBoardPage.module.css"; import { useContext } from "react"; import { LeadersContext } from "../../context/LeaderBoardContext"; import cn from "classnames"; +import { HardLevelImg, SuperPowerImg } from "../../components/AchievementsImages/AchievementsImages"; + function formatTime(seconds) { const minutes = Math.floor(seconds / 60); seconds = seconds % 60; @@ -27,12 +29,17 @@ export const LeaderBoardPage = () => {

Позиция

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

+

Достижения

Время

{leaders.map((leader, index) => (

{index + 1}

{leader.name}

+
+ + +

{formatTime(leader.time)}

))} diff --git a/src/pages/LeaderBoardPage/LeadeBoardPage.module.css b/src/pages/LeaderBoardPage/LeaderBoardPage.module.css similarity index 76% rename from src/pages/LeaderBoardPage/LeadeBoardPage.module.css rename to src/pages/LeaderBoardPage/LeaderBoardPage.module.css index 312c7a514..a420b84b7 100644 --- a/src/pages/LeaderBoardPage/LeadeBoardPage.module.css +++ b/src/pages/LeaderBoardPage/LeaderBoardPage.module.css @@ -23,9 +23,10 @@ width: 944px; height: 64px; display: flex; + align-items: center; gap: 66px; box-sizing: border-box; - padding-top: 16px; + padding-inline: 20px; margin-bottom: 16px; font-size: 24px; font-family: "StratosSkyeng", sans-serif; @@ -35,35 +36,34 @@ .usersPosition { width: 178px; - /*font-family: Poppins;*/ font-size: 24px; font-weight: 400; line-height: 32px; - margin-left: 20px; - /*color: #999999;*/ } .nameUser { - width: 300px; - /*font-family: Poppins;*/ + width: 227px; font-size: 24px; font-weight: 400; line-height: 32px; - margin-left: 20px; - /*color: #999999;*/ +} + +.achievUser { + width: 194px; + display: flex; + gap: 6px; } .timeRecord { - width: 130px; - /*font-family: Poppins;*/ + width: 92px; font-size: 24px; font-weight: 400; line-height: 32px; - margin-left: 220px; box-sizing: border-box; - /*color: #999999;*/ } .headerLine { color: #999999; } + + From 5796ee855bc9a1ab1f1a0fcc54b0fadcb7a82fa4 Mon Sep 17 00:00:00 2001 From: anguseva25 Date: Sun, 21 Jul 2024 09:10:01 +0300 Subject: [PATCH 13/13] added time in README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index dc4b12ebf..3b8b6827c 100644 --- a/README.md +++ b/README.md @@ -54,3 +54,8 @@ https://skypro-web-developer.github.io/react-memo/ Предполагаемое время: 8 часов. Фактическое время: 12 часов. + +## Курсовая работа работа (оценка) + +Предполагаемое время: 9 часов. +Фактическое время: 10 часов.