diff --git a/README.md b/README.md index f768e33..3963513 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,42 @@ Currently, two official plugins are available: - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +Чек-лист +- Стартовый экран — экран «Вход». Пользователю предлагается ввести почту и пароль, если он уже зарегистрирован, и нажать «Войти». +Если пользователь еще не зарегистрирован, ему необходимо нажать «Регистрируйтесь здесь». + +- На экране регистрации пользователь придумывает имя, почту и пароль. После этого он нажимает «Зарегистрироваться». +Если пользователь понял, что у него уже есть аккаунт, он может нажать «Войдите здесь» и вернуться на экран «Вход». +После регистрации пользователь снова попадает на стартовый экран «Вход», где вводит логин и пароль заново. + +- При вводе неверных данных или если пользователь не заполнил одно из полей вылезает сообщение об ошибке. + +- После заполнения данных и нажатия на кнопку «Войти» пользователь попадает на главный экран канбан-доски. + +- Канбан состоит из карточек с задачами, которые расположены под теми колонками, в каком статусе выполнения они находятся. +Карточка задачи содержит в себе категорию, название задачи и срок ее исполнения. Также на карточке в правом верхнем углу присутствуют три точки, при нажатии на которые раскрывается подробный просмотр задачи. + +- При загрузки страницы появляется лоадер "Подождите, страница загружается". + +- При нажатии на «Окошко пользователя» пользователю раскрывается окно, где отображаются его имя и почта, а также есть возможность выхода из аккаунта. + +- При нажатии на «Выйти» пользователю раскрывается окно с подтверждением выхода из аккаунта. При нажатии «Да, выйти» происходит выход. При нажатии «Нет, остаться» на экране остается канбан, а окно «Выйти из аккаунта» закрывается. При наведении и нажатии на кнопку с обводкой она заливается цветом. + +- При нажатии на кнопку «Создать новую задачу» пользователю раскрывается окно с созданием задачи. Задний фон с канбаном при открытии окна затемняется. + +- Окно создание задачи содержит в себе поля для заполнения: «Название задачи», «Описание задачи». Также есть возможность выбора категории и постановки срока исполнения. +После заполнения информации необходимо нажать на кнопку «Создать задачу» — тогда карточка с задачей падает на доску канбана. + +- Реализация календаря. Конечный срок исполнения выбирается нажатием на необходимую дату. Сегодняшний день выделен начертанием bold. +Мы хотим поставить другой срок исполнения, тогда чтобы выбрать это число, мы кликаем на него. Ниже календаря, где написано «Выберите срок исполнения», автоматически проставляется выбранный конечный срок по задаче. + +- Категория выбирается с помощью клика на необходимый выбор. Активная выбранная категория отображается ярко, а невыбранные категории имеют прозрачность. + +- При нажатии на карточку с задачей открывается окошко с более подробным просмотром данной задачи, где пользователь может увидеть описание задачи, срок исполнения и статус. +Данные поля неактивны для клика и изменения , пока пользователь не нажмет «Редактировать задачу». Также присутствует возможность удаления задачи. +Если пользователь нажимает «Удалить задачу», задача исчезает с канбан-доски. При нажатии на кнопку «Закрыть» окно с просмотром задачи закрывается, и пользователь снова видит канбан. + +- При нажатии на кнопку «Редактировать задачу» у пользователя появляется возможность взаимодействия с полями «Статус», «Описание задачи» и «Даты». +Объекты, которые выбраны, отображаются цветом 94A6BE. + diff --git a/src/App.css b/src/App.css index 89c2da5..8f36c91 100644 --- a/src/App.css +++ b/src/App.css @@ -83,7 +83,7 @@ body { width: 100%; height: 100%; font-family: "Roboto", Arial, Helvetica, sans-serif; - color: #000000; + /* color: #000000; */ } /* .wrapper { diff --git a/src/api.js b/src/api.js index d5af58a..1a13640 100644 --- a/src/api.js +++ b/src/api.js @@ -62,18 +62,21 @@ export async function changeTodo({ } //Удаление задачи -export async function DeleteTodo({ _id }) { - const response = await fetch(baseHost + "/" + _id, { +export async function deleteTodo({ _id, token }) { + const response = await fetch(baseHost + `/${_id}`, { method: "DELETE", headers: { Authorization: `Bearer ${token}`, }, }); - if (response.ok) { - throw new Error("Ошибка удаления задачи"); - } - return await response.json(); + if (!response.status === 201) { + throw new Error("Не удалось удалить задачу, попробуйте позже"); +} +const data = await response.json(); +return data; } + + diff --git a/src/components/Calendar/Calendar.jsx b/src/components/Calendar/Calendar.jsx index 5fa11bc..5a3f5eb 100644 --- a/src/components/Calendar/Calendar.jsx +++ b/src/components/Calendar/Calendar.jsx @@ -1,21 +1,25 @@ import { format } from "date-fns"; -import { DayPicker } from "react-day-picker"; + import "react-day-picker/dist/style.css"; import ru from "date-fns/locale/ru"; +import { CalendarCustom, Calendaric, CategoriesP, ChooseDate } from "./Calendar.styled"; export default function Calendar({selectedDate,setSelectedDate }) { - let footer =

Пожалуйста, выберите дату.

; + let footer = Выберите срок исполнения; if (selectedDate) { - footer =

Вы выбрали {format(selectedDate, "PP", { locale: ru })}

; + footer = Вы выбрали {format(selectedDate, "PP", { locale: ru })}; } return ( - + + Даты + + ); } diff --git a/src/components/Calendar/Calendar.styled.js b/src/components/Calendar/Calendar.styled.js new file mode 100644 index 0000000..75fa3fd --- /dev/null +++ b/src/components/Calendar/Calendar.styled.js @@ -0,0 +1,30 @@ +import styled from "styled-components"; +import { DayPicker } from 'react-day-picker'; + + +export const Calendaric = styled.div` + margin-bottom: 20px; + font-weight: 400; + font-size: 14px; + line-height: 1px; + color: #94a6be; + letter-spacing: -0.14px; + +`; +export const CategoriesP = styled.p` + margin-bottom: 14px; + margin-left: 20px; + color: #000; + font-size: 14px; + font-weight: 600; + line-height: 14px; + white-space: nowrap; +`; +export const ChooseDate = styled.p` + margin-left: 7px; + margin-top: 10px; +`; +export const CalendarCustom = styled(DayPicker)` + --rdp-cell-size: 30px; + --rdp-caption-font-size: 14px; +`; \ No newline at end of file diff --git a/src/components/popups/PopBrowse/PopBrowse.jsx b/src/components/popups/PopBrowse/PopBrowse.jsx index e3ae0ea..4080769 100644 --- a/src/components/popups/PopBrowse/PopBrowse.jsx +++ b/src/components/popups/PopBrowse/PopBrowse.jsx @@ -1,24 +1,45 @@ -import { Link, useParams } from "react-router-dom"; +import { Link, useNavigate, useParams } from "react-router-dom"; import { appRoutes } from "../../../lib/appRoutes"; import Calendar from "../../Calendar/Calendar"; import * as S from "./PopBrowse.styled"; -import { useTask } from "../../../hooks/useUser"; -import * as St from "../../Card/Card.styled"; +import { useUser } from "../../../hooks/useUser"; +import { useTask } from "../../../hooks/useTask"; + import { topicHeader } from "../../../lib/topic"; +import { deleteTodo } from "../../../api"; function PopBrowse() { const { id } = useParams(); - const{task} = useTask(); + const { task, setTask } = useTask(); + const { user } = useUser(); const currentTask = task.find((element) => id === element._id); + const navigate = useNavigate(); + + const deleteTask = () => { + deleteTodo({ + token: user.token, + _id: id, + }) + .then((data) => { + setTask(data.tasks); + navigate(appRoutes.MAIN); + }) + .catch((error) => { + alert(error); + }); + }; + return ( - Название задачи:{currentTask.title} - - {currentTask.topic} - + + Название задачи:{currentTask.title} + + + {currentTask.topic} + Статус @@ -26,18 +47,6 @@ function PopBrowse() { Без статуса - {/*
-

Нужно сделать

-
-
-

В работе

-
-
-

Тестирование

-
-
-

Готово

-
*/}
@@ -50,50 +59,27 @@ function PopBrowse() { id="textArea01" readOnly="" placeholder="Введите описание задачи..." - defaultValue={currentTask.description} + defaultValue={currentTask.description} /> - {/*
-

Категория

-
-

Web Design

-
-
*/} Редактировать задачу - Удалить задачу + + Удалить задачу + Закрыть - {/* */}
diff --git a/src/components/popups/PopBrowse/PopBrowse.styled.js b/src/components/popups/PopBrowse/PopBrowse.styled.js index 6a23d95..3ec6773 100644 --- a/src/components/popups/PopBrowse/PopBrowse.styled.js +++ b/src/components/popups/PopBrowse/PopBrowse.styled.js @@ -1,5 +1,6 @@ import { styled } from "styled-components"; import {hover01, hover03} from "../../../styled/common/Common.styled"; +import { topicStyles } from "../../../lib/topic"; export const PopBrowse = styled.div` width: 100%; @@ -31,6 +32,40 @@ export const PopBrowseContainer = styled.div` justify-content: flex-start; } `; + +export const CategoriesTheme = styled.div` + display: inline-block; + width: auto; + height: 30px; + padding: 8px 20px; + border-radius: 24px; + margin-right: 7px; + opacity: 0.4; + /* background-color: #ffe4c2; */ + /* color: #ff6d00; */ + opacity: 1 !important; + display: block; + background-color: ${({ $themeColor }) => + topicStyles[$themeColor]?.backgroundColor || "#94a6be"}; + + + color: ${({ $themeColor }) => topicStyles[$themeColor]?.color || "#ffffff"}; + +`; +export const CategoriesThemeP = styled.p` + /* background-color: #ffe4c2; */ + /* color: #ff6d00; */ + font-size: 14px; + font-weight: 600; + line-height: 14px; + white-space: nowrap; + +`; + + + + + export const PopBrowseBlock = styled.div` display: block; margin: 0 auto; diff --git a/src/components/popups/PopEditCard/PopEditCard.jsx b/src/components/popups/PopEditCard/PopEditCard.jsx index cfea510..21756b6 100644 --- a/src/components/popups/PopEditCard/PopEditCard.jsx +++ b/src/components/popups/PopEditCard/PopEditCard.jsx @@ -1,20 +1,49 @@ -import { Link, useParams } from "react-router-dom"; +import { Link, useNavigate, useParams } from "react-router-dom"; import { appRoutes } from "../../../lib/appRoutes"; import Calendar from "../../Calendar/Calendar"; -import * as S from "../PopBrowse/PopBrowse.styled"; +import * as S from "../PopEditCard/PopEditCard.styled"; +import { useTask } from "../../../hooks/useTask"; +import { topicHeader } from "../../../lib/topic"; +import { deleteTodo } from "../../../api"; +import { useUser } from "../../../hooks/useUser"; function PopEditCard() { const { id } = useParams(); + const{task, setTask} = useTask(); + const currentTask = task.find((element) => id === element._id); + const navigate = useNavigate(); + const {user} = useUser(); + + + + const deleteTask = () => { + deleteTodo({ + token:user.token, + _id: id + }) + .then((data) => { + setTask(data.tasks); + navigate(appRoutes.MAIN); + }) + .catch((error) => { + alert (error); + }) + } + + + + + return ( - Название задачи:{id} -
-

Web Design

-
+ Название задачи:{currentTask.title} + + {currentTask.topic} +
Статус @@ -22,18 +51,18 @@ function PopEditCard() { Без статуса -
-

Нужно сделать

-
-
-

В работе

-
-
-

Тестирование

-
-
-

Готово

-
+ + Нужно сделать + + + В работе + + + Тестирование + + + Готово +
@@ -46,50 +75,33 @@ function PopEditCard() { id="textArea01" readOnly="" placeholder="Введите описание задачи..." - // defaultValue={""} + defaultValue={currentTask.description} /> -
-

Категория

-
-

Web Design

-
-
- + - - Редактировать задачу - - Удалить задачу - + + + Сохранить + - - Закрыть - - - + + Отменить + + + Удалить задачу + + + + + Закрыть + + +
diff --git a/src/components/popups/PopEditCard/PopEditCard.styled.js b/src/components/popups/PopEditCard/PopEditCard.styled.js new file mode 100644 index 0000000..87dd4e2 --- /dev/null +++ b/src/components/popups/PopEditCard/PopEditCard.styled.js @@ -0,0 +1,276 @@ +import { styled } from "styled-components"; +import { hover01, hover03 } from "../../../styled/common/Common.styled"; +import { topicStyles } from "../../../lib/topic"; + + + + +export const PopBrowse = styled.div` + width: 100%; + height: 100%; + min-width: 375px; + min-height: 100vh; + position: absolute; + top: 0; + left: 0; + z-index: 7; + + @media screen and (max-width: 660px) { + top: 70px; + } +`; +export const PopBrowseContainer = styled.div` + width: 100%; + height: 100%; + min-height: 100vh; + padding: 0 16px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background: rgba(0, 0, 0, 0.4); + + @media screen and (max-width: 660px) { + padding: 0; + justify-content: flex-start; + } +`; +export const PopBrowseBlock = styled.div` + display: block; + margin: 0 auto; + background-color: #ffffff; + max-width: 630px; + width: 100%; + padding: 40px 30px 38px; + border-radius: 10px; + border: 0.7px solid #d4dbe5; + position: relative; + + @media screen and (max-width: 495px) { + padding: 20px 16px 32px; + } + + @media screen and (max-width: 660px) { + border-radius: 0; + } +`; +export const PopBrowseContent = styled.div` + display: block; + text-align: left; +`; +export const PopBrowseTopBlock = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 18px; +`; +export const PopBrowseTtl = styled.h3` + color: #000000; + font-size: 20px; + font-weight: 600; + line-height: 24px; +`; +export const CategoriesTheme = styled.div` + display: inline-block; + width: auto; + height: 30px; + padding: 8px 20px; + border-radius: 24px; + margin-right: 7px; + opacity: 0.4; + /* background-color: #ffe4c2; */ + /* color: #ff6d00; */ + opacity: 1 !important; + display: block; + background-color: ${({ $themeColor }) => + topicStyles[$themeColor]?.backgroundColor || "#94a6be"}; + + + color: ${({ $themeColor }) => topicStyles[$themeColor]?.color || "#ffffff"}; + +`; +export const CategoriesThemeP = styled.p` + /* background-color: #ffe4c2; */ + /* color: #ff6d00; */ + font-size: 14px; + font-weight: 600; + line-height: 14px; + white-space: nowrap; + +`; + +export const PopBrowseStatus = styled.div` + margin-bottom: 11px; +`; +export const StatusPSubttl = styled.p` + margin-bottom: 14px; + color: #000; + font-size: 14px; + font-weight: 600; + line-height: 1; +`; +export const StatusThemes = styled.div` + display: flex; + flex-wrap: wrap; + align-items: flex-start; + justify-content: flex-start; +`; +export const StatusThemeHide = styled.div` + border-radius: 24px; + border: 0.7px solid rgba(148, 166, 190, 0.4); + color: #94a6be; + padding: 11px 14px 10px; + margin-right: 7px; + margin-bottom: 7px; + display: block; +`; +export const StatusThemeP = styled.p` + font-size: 14px; + line-height: 1; + letter-spacing: -0.14px; + background: #94a6be; + color: #ffffff; +`; +export const StatusTheme = styled.div` + border-radius: 24px; + border: 0.7px solid rgba(148, 166, 190, 0.4); + color: #94a6be; + padding: 11px 14px 10px; + margin-right: 7px; + margin-bottom: 7px; + background: #94a6be; + color: #ffffff; +`; + +export const StatusThemeHideP = styled.p` + font-size: 14px; + line-height: 1; + letter-spacing: -0.14px; +`; +export const PopBrowseWrap = styled.div` + display: flex; + align-items: flex-start; + justify-content: space-between; + /* display: block; */ +`; +export const PopBrowseForm = styled.form` + max-width: 370px; + width: 100%; + display: block; + margin-bottom: 20px; +`; +export const FormBrowseBlock = styled.div` + display: flex; + flex-direction: column; +`; +export const Subttl = styled.label` + color: #000; + font-size: 14px; + font-weight: 600; + line-height: 1; +`; +export const FormBrowseArea = styled.textarea` + max-width: 370px; + width: 100%; + outline: none; + padding: 14px; + background: #eaeef6; + border: 0.7px solid rgba(148, 166, 190, 0.4); + border-radius: 8px; + font-size: 14px; + line-height: 1; + letter-spacing: -0.14px; + margin-top: 14px; + height: 200px; + ::placeholder { + font-weight: 400; + font-size: 14px; + line-height: 1px; + color: #94a6be; + letter-spacing: -0.14px; + } +`; +export const PopBrowseBtnEditHide = styled.div` + display: flex; + flex-wrap: wrap; + align-items: flex-start; + justify-content: space-between; + display: block; +`; + +export const BtnGroup = styled.div` + margin-right: 8px; + width: 100%; + margin-right: 0px; + display: flex; + flex-direction: row; + justify-content: space-between; +`; + +export const BtnBrowse = styled.div` + display: flex; + margin-right: 8px; + gap: 8px; + height: 30px; +`; +export const BtnEditButton = styled.button` + border-radius: 4px; + border: 0.7px solid var(--palette-navy-60, #565eef); + outline: none; + background: transparent; + color: #565eef; + margin-bottom: 10px; + padding: 0 14px; + height: 30px; + margin-right: 8px; + ${hover03} +`; +export const BtnBgA = styled.span` +color: #565eef; +`; +export const BtnEditButtonBtmBor = styled.button` + height: 30px; + margin-bottom: 10px; + padding: 0 14px; + border-radius: 4px; + background: #565eef; + border: none; + outline: none; + color: #ffffff; + border-radius: 4px; + border: 0.7px solid var(--palette-navy-60, #565EEF); + outline: none; + background: transparent; + color: #565EEF; + ${hover03} +`; +export const BtnBrowseClose = styled.button` + border-radius: 4px; + background: #565eef; + border: none; + outline: none; + color: #ffffff; + margin-bottom: 10px; + margin-right: 8px; + padding: 0 14px; + height: 30px; + ${hover01} +`; + +export const BtnBrowseEditBtnBor = styled.button` + border-radius: 4px; + border: 0.7px solid var(--palette-navy-60, #565eef); + outline: none; + background: transparent; + color: #565eef; + margin-bottom: 10px; + padding: 0 14px; + height: 30px; + margin-right: 8px; + ${hover03} +`; + + + + + diff --git a/src/components/popups/PopNewCard/PopNewCard.jsx b/src/components/popups/PopNewCard/PopNewCard.jsx index 14d97dc..8877454 100644 --- a/src/components/popups/PopNewCard/PopNewCard.jsx +++ b/src/components/popups/PopNewCard/PopNewCard.jsx @@ -3,7 +3,8 @@ import Calendar from "../../Calendar/Calendar"; import * as S from "./PopNewCard.styled"; import { Link, useNavigate } from "react-router-dom"; import { postTodo } from "../../../api"; -import { useTask, useUser } from "../../../hooks/useUser"; +import {useUser } from "../../../hooks/useUser"; +import { useTask} from "../../../hooks/useTask"; import { appRoutes } from "../../../lib/appRoutes"; function PopNewCard() { diff --git a/src/hooks/useTask.js b/src/hooks/useTask.js new file mode 100644 index 0000000..c6e501e --- /dev/null +++ b/src/hooks/useTask.js @@ -0,0 +1,6 @@ +import { useContext } from "react"; +import { TaskContext } from "../contexts/task"; + +export function useTask() { + return useContext(TaskContext) +} \ No newline at end of file diff --git a/src/hooks/useUser.js b/src/hooks/useUser.js index d2fe5e8..472943e 100644 --- a/src/hooks/useUser.js +++ b/src/hooks/useUser.js @@ -1,11 +1,8 @@ import { useContext } from "react"; import { UserContext } from "../contexts/user"; -import { TaskContext } from "../contexts/task"; + export function useUser() { return useContext(UserContext); } -export function useTask() { - return useContext(TaskContext) -} \ No newline at end of file diff --git a/src/pages/MainPage/MainPage.jsx b/src/pages/MainPage/MainPage.jsx index cc8f880..91b9ebf 100644 --- a/src/pages/MainPage/MainPage.jsx +++ b/src/pages/MainPage/MainPage.jsx @@ -5,7 +5,8 @@ import Column from "../../components/Column/Column"; import MainContent from "../../components/MainContent/MainContent"; import { Outlet } from "react-router-dom"; import { getTodos } from "../../api"; -import { useTask, useUser } from "../../hooks/useUser"; +import {useUser } from "../../hooks/useUser"; +import { useTask} from "../../hooks/useTask"; import { Wrapper } from "../../styled/common/Common.styled"; const statusList = [