diff --git a/src/components/Card/card.js b/src/components/Card/card.js index 6c54345..af59e94 100644 --- a/src/components/Card/card.js +++ b/src/components/Card/card.js @@ -1,4 +1,4 @@ -import React, { useState, useContext, memo, useEffect } from "react"; +import React, { useState, memo, useEffect } from "react"; import parse from "html-react-parser"; @@ -17,46 +17,31 @@ import { Tag, } from "./styles"; -//Context -import { dataContext } from "../../context/dataContext.js"; - //Components -import { Checkbox, Col, Dropdown, Menu } from "antd"; +import { Checkbox, Row, Dropdown, Menu } from "antd"; import ModalEdit from "../Modal/modalEdit"; -const Card = ({ dataProps, index }) => { - const { todoList, setTodoList } = useContext(dataContext); +const Card = ({ dataProps, onDelete, onDone }) => { const [showModal, setShowModal] = useState(false); const [opacity, setOpacity] = useState(0); function deleteCard() { setOpacity(0); setTimeout(() => { - var newArr = todoList.slice(); - for (let i = 0; i < newArr.length; i++) { - if (newArr[i].id === dataProps.id) { - newArr.splice(i, 1); - } - } - if (newArr.length === 0) { - setTodoList(null); - } else { - setTodoList(newArr); - } + onDelete(); setOpacity(1); }, 250); } - function handleDoneClick() { - var newArr = todoList.slice(); - newArr[index].done = !newArr[index].done; - setTodoList(newArr); - } const menuOverlay = () => { return ( - setShowModal(true)}>Edit - deleteCard()}>Delete + setShowModal(true)} key={1}> + Edit + + deleteCard()} key={2}> + Delete + ); }; @@ -69,10 +54,11 @@ const Card = ({ dataProps, index }) => { return ( <> - + onDone()} > @@ -92,15 +78,12 @@ const Card = ({ dataProps, index }) => { <Tag key={index} color={data.color} /> ))} </TagContent> - <Checkbox - checked={dataProps.done} - onChange={() => handleDoneClick()} - > + <Checkbox checked={dataProps.done} onChange={() => onDone()}> Done </Checkbox> </Foot> </Container> - </Col> + </Row> <ModalEdit hideModal={() => setShowModal(false)} showModal={showModal} diff --git a/src/components/Card/index.js b/src/components/Card/index.js index b1dfa4b..8182121 100644 --- a/src/components/Card/index.js +++ b/src/components/Card/index.js @@ -1,70 +1,118 @@ import React, { useContext } from "react"; +import { Col } from "antd"; //Images import noData from "../../assets/no-data-2.png"; -//Scripts -import { filterArr } from "../../services/utils.js"; - //Context import { dataContext } from "../../context/dataContext.js"; import Card from "./card.js"; const CardContainer = () => { - const { todoList, todoFilter, tagFilter } = useContext(dataContext); + const { todoList, setTodoList, todoFilter, tagFilter } = + useContext(dataContext); - function filterTodosByTag() { - if (tagFilter[0]?.type === "done") { - return todoList.filter((el) => { - if ( - el.title.toLowerCase().indexOf(todoFilter.title.toLowerCase()) > -1 - ) { - if (el.done) { - return false; - } else return true; - } else return false; - }); - } else { - return todoList.filter((el) => { - if ( - el.title.toLowerCase().indexOf(todoFilter.title.toLowerCase()) > -1 - ) { - for (let i = 0; i < el.tags.length; i++) { - for (let j = 0; j < tagFilter.length; j++) { - if (el.tags[i].id === tagFilter[j].id) { - return true; + function filterTodo() { + if (tagFilter.length > 0) { + if (tagFilter[0]?.type === "done") { + return todoList.filter((el) => { + if ( + el.title.toLowerCase().indexOf(todoFilter.title.toLowerCase()) > -1 + ) { + if (el.done) { + return false; + } else return true; + } else return false; + }); + } else { + return todoList.filter((el) => { + if ( + el.title.toLowerCase().indexOf(todoFilter.title.toLowerCase()) > -1 + ) { + for (let i = 0; i < el.tags.length; i++) { + for (let j = 0; j < tagFilter.length; j++) { + if (el.tags[i].id === tagFilter[j].id) { + return true; + } } } } - } - return false; + return false; + }); + } + } else { + return todoList.filter(function (el) { + return ( + el.title.toLowerCase().indexOf(todoFilter.title.toLowerCase()) > -1 + ); }); } } + function deleteHandler(id) { + var newArr = todoList.slice(); + for (let i = 0; i < newArr.length; i++) { + if (newArr[i].id === id) { + newArr.splice(i, 1); + } + } + if (newArr.length === 0) { + return setTodoList(null); + } else { + return setTodoList(newArr); + } + } + + function doneHandler(id) { + var newArr = todoList.slice(); + for (let i = 0; i < newArr.length; i++) { + if (newArr[i].id === id) { + newArr[i].done = !newArr[i].done; + } + } + return setTodoList(newArr); + } + return ( <> {todoList?.length > 0 ? ( <> - {tagFilter.length > 0 ? ( - <> - {filterTodosByTag().map((data, index) => ( - <Card dataProps={data} key={index} index={index} /> - ))} - </> - ) : ( - <> - {filterArr(todoFilter.title, todoList, "title").map( - (data, index) => ( - <Card dataProps={data} key={index} index={index} /> - ) - )} - </> - )} + <Col sm={24} md={12} lg={12}> + {filterTodo().map((data, index) => { + if (index % 2 === 0) { + return ( + <Card + dataProps={data} + key={index} + onDelete={() => deleteHandler(data.id)} + onDone={() => doneHandler(data.id)} + /> + ); + } else return null; + })} + </Col> + <Col sm={24} md={12} lg={12}> + {filterTodo().map((data, index) => { + if (index % 2 !== 0) { + return ( + <Card + dataProps={data} + key={index} + onDelete={() => deleteHandler(data.id)} + onDone={() => doneHandler(data.id)} + /> + ); + } else return null; + })} + </Col> </> ) : ( <div style={{ textAlign: "center", width: "100%" }}> - <img src={noData} style={{ width: "50%", margin: "10px auto" }} alt="No data"/> + <img + src={noData} + style={{ width: "50%", margin: "10px auto" }} + alt="No data" + /> <br /> <h2 style={{ margin: 0 }}>It sims that you dont have any todo</h2> </div> diff --git a/src/components/Card/styles.js b/src/components/Card/styles.js index 631721f..17ba98e 100644 --- a/src/components/Card/styles.js +++ b/src/components/Card/styles.js @@ -11,6 +11,7 @@ export const Container = styled.div` border-radius: 6px; width: 95%; margin: 6px 0; + transition: "all .25s ease"; height: fit-content; @media (max-width: 768px) { width: 100%; diff --git a/src/components/Modal/modalEdit.js b/src/components/Modal/modalEdit.js index 07bcb41..1b98512 100644 --- a/src/components/Modal/modalEdit.js +++ b/src/components/Modal/modalEdit.js @@ -115,6 +115,7 @@ const ModalTodo = ({ hideModal, showModal, defaultData }) => { } } setTagSelector(auxArr); + // eslint-disable-next-line }, [defaultData]); return ( diff --git a/src/components/Modal/modalTodo.js b/src/components/Modal/modalTodo.js index 3f5c242..eeefbbe 100644 --- a/src/components/Modal/modalTodo.js +++ b/src/components/Modal/modalTodo.js @@ -66,7 +66,7 @@ const ModalTodo = ({ hideModal, showModal }) => { tagArr.push(tagList[tagSelector[i]]); } var date = new Date(); - var month = date.getMonth(); + var month = date.getMonth() + 1; var day = date.getDate(); var hours = date.getHours(); var minutes = date.getMinutes(); diff --git a/src/components/SideBar/Tag.js b/src/components/SideBar/Tag.js index afe9de2..1d75bdf 100644 --- a/src/components/SideBar/Tag.js +++ b/src/components/SideBar/Tag.js @@ -6,7 +6,7 @@ import { dataContext } from "../../context/dataContext"; //Minor components import { TagContent, Tag, DeleteIcon } from "./styles"; -const TagComponent = ({ data, index, type, style }) => { +const TagComponent = ({ data, index, type, style, todoAmount }) => { const [opacity, setOpacity] = useState(0); const [background, setBackground] = useState(""); const [padding, setPadding] = useState(0); @@ -73,7 +73,7 @@ const TagComponent = ({ data, index, type, style }) => { } else { setTagList(newArr); } - for (let i = 0; i < todoList.length; i++) { + for (let i = 0; i < todoList?.length; i++) { //Removing the deleted tag from all todo's for (let j = 0; j < todoList[i].tags.length; j++) { if (todoList[i].tags[j].id === deletedTagID) { @@ -104,12 +104,15 @@ const TagComponent = ({ data, index, type, style }) => { > <Tag color={data.color}>{data.text}</Tag> {type !== "CheckDone" && ( - <DeleteIcon - onClick={(e) => { - handleDelete(index); - e.stopPropagation(); - }} - /> + <div style={{ display: "flex", alignItems: "center" }}> + <div>({todoAmount[data.text]?.length})</div> + <DeleteIcon + onClick={(e) => { + handleDelete(index); + e.stopPropagation(); + }} + /> + </div> )} </TagContent> </> diff --git a/src/components/SideBar/index.js b/src/components/SideBar/index.js index 6bc244f..2cedaa5 100644 --- a/src/components/SideBar/index.js +++ b/src/components/SideBar/index.js @@ -21,7 +21,7 @@ import { } from "./styles"; const SideBar = () => { - const { tagList, todoList } = useContext(dataContext); + const { tagList, todoList, relationTagTodo } = useContext(dataContext); const [showModal, setShowModal] = useState(false); const [searchValue, setSearchValue] = useState(""); @@ -52,7 +52,7 @@ const SideBar = () => { /> </SearchContent> {filterArr(searchValue, tagList, "text").map((data, index) => ( - <TagComponent data={data} index={index} key={index} /> + <TagComponent data={data} index={index} key={index} todoAmount={relationTagTodo}/> ))} </> ) : ( diff --git a/src/components/SideBar/styles.js b/src/components/SideBar/styles.js index 45f63ed..52b24d9 100644 --- a/src/components/SideBar/styles.js +++ b/src/components/SideBar/styles.js @@ -103,6 +103,7 @@ export const Tag = styled.span` `; export const DeleteIcon = styled(Trash)` + margin-left: 13px; width: 24px; height: 24px; cursor: pointer; diff --git a/src/context/componentContext.js b/src/context/componentContext.js deleted file mode 100644 index 283b125..0000000 --- a/src/context/componentContext.js +++ /dev/null @@ -1,18 +0,0 @@ -import React, { createContext, useState } from "react"; - -export const componentContext = createContext({}); - -export default function ComponentContextProvider({ children }) { - const [showTagContent, setShowTagContent] = useState(false); - - return ( - <componentContext.Provider - value={{ - showTagContent, - setShowTagContent, - }} - > - {children} - </componentContext.Provider> - ); -} diff --git a/src/context/dataContext.js b/src/context/dataContext.js index e5f0816..9b2ecae 100644 --- a/src/context/dataContext.js +++ b/src/context/dataContext.js @@ -12,17 +12,19 @@ export default function DataContextProvider({ children }) { const [tagFilter, setTagFilter] = useState([]); const [todoFilter, setTodoFilter] = useState({ title: "" }); + const [relationTagTodo, setRelationTagTodo] = useState({}); + useEffect(() => { - //Sync the local storage + //Sync the local storage with state data if (todoList?.length > 0 || todoList == null) setTodos(todoList); }, [todoList]); useEffect(() => { - //Sync the local storage + //Sync the local storage with state data if (tagList?.length > 0 || tagList == null) setTags(tagList); }, [tagList]); useEffect(() => { - //Checking if there is a data on API + //Sync the react state with local storage data if (getTodos()) { setTodoList(getTodos()); } @@ -31,6 +33,21 @@ export default function DataContextProvider({ children }) { } }, []); + useEffect(() => { + // Inserting tags and todos relations into state ex: (tag01: [todos], tag02: [todos]) + var obj = {}; + tagList?.forEach((value) => { + obj[value.text] = []; + }); + todoList?.forEach((value) => { + value.tags.forEach((todoTags) => { + obj[todoTags.text].push(value); + }); + }); + setRelationTagTodo(obj); + //eslint-disable-next-line + }, [todoList]); + return ( <dataContext.Provider value={{ @@ -42,6 +59,7 @@ export default function DataContextProvider({ children }) { setTagFilter, todoFilter, setTodoFilter, + relationTagTodo, }} > {children} diff --git a/src/pages/Main/app.js b/src/pages/Main/app.js index cc7d733..b3dcc6f 100644 --- a/src/pages/Main/app.js +++ b/src/pages/Main/app.js @@ -8,6 +8,8 @@ import { SideBarContent, CardContent, IconContent, + TrashIcon, + ArrowDownIcon, AddIcon, Input, } from "./styles"; @@ -19,36 +21,69 @@ import { dataContext } from "../../context/dataContext.js"; import SideBar from "../../components/SideBar"; import CardContainer from "../../components/Card"; import ModalTodo from "../../components/Modal/modalTodo"; +import { Dropdown, Menu } from "antd"; import { ToastContainer } from "react-toastify"; - const App = () => { const [showModal, setShowModal] = useState(false); - const { todoFilter, setTodoFilter, tagList, setTagList } = useContext(dataContext); + const { + todoFilter, + setTodoFilter, + tagList, + setTagList, + setTodoList, + todoList, + } = useContext(dataContext); const [showTagContent, setShowTagContent] = useState(false); const screenWidth = window.innerWidth; useEffect(() => { if (tagList.length === 0 && !localStorage.getItem("tags")) { setTagList([ - {color: '#1E90FF', text: 'Work', id: '0'}, - {color: '#DC52BF', text: 'Study', id: '1'}, - {color: '#FCF33F', text: 'School', id: '2'}, - ]) + { color: "#1E90FF", text: "Work", id: "0" }, + { color: "#DC52BF", text: "Study", id: "1" }, + { color: "#FCF33F", text: "School", id: "2" }, + ]); } - }, []) - + // eslint-disable-next-line + }, []); function handleClickAdd() { window.gtag("event", "click-event", { event_category: "Open", action: "Openned todo creation modal", - label: "Todo Modal" - }) + label: "Todo Modal", + }); setShowModal(true); } + function onDeleteAll() { + setTodoList(null); + } + function onDeleteDone() { + var cloneArr = todoList.slice(); + for (let i = cloneArr.length - 1; i >= 0; i--) { + if (cloneArr[i].done) { + cloneArr.splice(i, 1); + } + } + setTodoList(cloneArr); + } + + const menuOverlay = () => { + return ( + <Menu> + <Menu.Item key={1} onClick={() => onDeleteAll()}> + Delete all + </Menu.Item> + <Menu.Item key={2} onClick={() => onDeleteDone()}> + Delete done + </Menu.Item> + </Menu> + ); + }; + return ( <> <ToastContainer /> @@ -91,7 +126,26 @@ const App = () => { }); }} /> - <AddIcon onClick={() => {handleClickAdd()}} /> + <div> + <div> + <Dropdown + overlay={menuOverlay} + trigger={["click"]} + destroyPopupOnHide={true} + arrow={true} + > + <div style={{ display: "flex", alignItems: "center" }}> + <TrashIcon /> + <ArrowDownIcon /> + </div> + </Dropdown> + </div> + <AddIcon + onClick={() => { + handleClickAdd(); + }} + /> + </div> </IconContent> <CardContent> <CardContainer /> diff --git a/src/pages/Main/styles.js b/src/pages/Main/styles.js index 51f3486..f83f981 100644 --- a/src/pages/Main/styles.js +++ b/src/pages/Main/styles.js @@ -2,6 +2,8 @@ import styled from "styled-components"; //Icons import { Add } from "@styled-icons/fluentui-system-filled/Add"; import { MenuAltLeft } from "@styled-icons/boxicons-regular/MenuAltLeft"; +import { ArrowIosDownwardOutline } from "@styled-icons/evaicons-outline/ArrowIosDownwardOutline"; +import { Trash } from "@styled-icons/bootstrap/Trash"; export const Container = styled.div` width: 100%; height: 100vh; @@ -36,10 +38,38 @@ export const IconContent = styled.div` display: flex; justify-content: space-between; align-items: center; + > div { + display: flex; + justify-content: space-between; + align-items: center; + > div { + cursor: pointer; + } + } +`; + +export const TrashIcon = styled(Trash)` + width: 32px; + height: 32px; + color: var(--subTitle); + fill: var(--subTitle); + flex-shrink: 0; + @media (max-width: 768px) { + width: 34px; + height: 34px; + } `; +export const ArrowDownIcon = styled(ArrowIosDownwardOutline)` + width: 22px; + height: 22px; + color: var(--subTitle); + fill: var(--subTitle); +`; + export const AddIcon = styled(Add)` - width: 52px; - height: 52px; + margin-left: 20px; + width: 38px; + height: 38px; color: var(--subTitle); fill: var(--subTitle); flex-shrink: 0;