From 6eaa444421801bb220d6608c8656997b82d3ea1a Mon Sep 17 00:00:00 2001 From: Arkadi Koifman <76536506+ArkadiK94@users.noreply.github.com> Date: Thu, 30 Nov 2023 07:44:49 +0200 Subject: [PATCH] feat: notes taking app - base (#524) * [Feat] Add NoteApp Layout How: with styles Why: according to the design sample * [Feat] Add Layout for NotesSidebarHeader and NotesSidebarSearch How: html/css in react Why: progressing * [Feat] Isolate SearchInputBox component Why: so we could reuse it How: Isolate SearchInputBox to different file and use in right places * [Feat] Change the Toggle Button of Sidebar Position Why: the prev position get on elements in the main container How: change some styles * [Feat] Add NotesList in SideBar What: add notes list in SideBar How: create components for list, item style , pass data Why: need to implement * [Feat] Add Search Functionality, Add Pick Note And Show Description why: to be able to search notes. to show description of notes how: react... * [Feat] Add Search Functionality, Add Pick Note And Show Description why: to be able to search notes. to show description of notes how: react... --- src/components/CaptureTheFlag/CTF.jsx | 24 +--- src/components/CaptureTheFlag/CTFElements.jsx | 44 ------- .../Common/SearchInputBox/SearchInputBox.jsx | 13 ++ .../SearchInputBox/SearchInputBoxElements.jsx | 44 +++++++ src/components/Common/SearchInputBox/index.js | 2 + .../Dashboard/Notetaker/AddNote.jsx | 43 ------- src/components/Dashboard/Notetaker/Note.jsx | 13 -- .../Dashboard/Notetaker/NoteApp.css | 63 +-------- .../Dashboard/Notetaker/NoteApp.jsx | 120 +++++++++++++++--- .../Dashboard/Notetaker/NoteElements.jsx | 108 ++++++++++++++++ .../Dashboard/Notetaker/NoteItem.jsx | 25 ++++ .../Dashboard/Notetaker/NoteList.jsx | 16 +-- .../Dashboard/Notetaker/Old/AddNoteOld.jsx | 42 ++++++ .../Dashboard/Notetaker/Old/NoteAppOld.css | 57 +++++++++ .../Dashboard/Notetaker/Old/NoteListOld.jsx | 16 +++ .../Dashboard/Notetaker/Old/NoteOld.jsx | 13 ++ .../Dashboard/Sidebar/SidebarElements.jsx | 9 +- src/components/Explore/Explore.jsx | 16 +-- 18 files changed, 446 insertions(+), 222 deletions(-) create mode 100644 src/components/Common/SearchInputBox/SearchInputBox.jsx create mode 100644 src/components/Common/SearchInputBox/SearchInputBoxElements.jsx create mode 100644 src/components/Common/SearchInputBox/index.js delete mode 100644 src/components/Dashboard/Notetaker/AddNote.jsx delete mode 100644 src/components/Dashboard/Notetaker/Note.jsx create mode 100644 src/components/Dashboard/Notetaker/NoteElements.jsx create mode 100644 src/components/Dashboard/Notetaker/NoteItem.jsx create mode 100644 src/components/Dashboard/Notetaker/Old/AddNoteOld.jsx create mode 100644 src/components/Dashboard/Notetaker/Old/NoteAppOld.css create mode 100644 src/components/Dashboard/Notetaker/Old/NoteListOld.jsx create mode 100644 src/components/Dashboard/Notetaker/Old/NoteOld.jsx diff --git a/src/components/CaptureTheFlag/CTF.jsx b/src/components/CaptureTheFlag/CTF.jsx index 3473678f..07da5a0d 100644 --- a/src/components/CaptureTheFlag/CTF.jsx +++ b/src/components/CaptureTheFlag/CTF.jsx @@ -2,18 +2,7 @@ import React, { useEffect, useState } from "react"; import { Wrapper } from "../Dashboard/Profile/ProfileElements"; import { getAllCTFs } from "../../features/ctf/ctfSlice"; import { useDispatch, useSelector } from "react-redux"; -import { - CTFContainer, - CTFHeader, - CTFHeading, - Option, - SearchBox, - SearchContainer, - SearchDifficulty, - SearchIcon, - SearchInput, - Select, -} from "./CTFElements"; +import { CTFContainer, CTFHeader, CTFHeading, Option, SearchContainer, SearchDifficulty, Select } from "./CTFElements"; import { getUserDetail } from "../../features/userDetail/userDetailSlice"; // import { encodeURL } from "../Blogs/util"; import UnderMaintenance from "../Other/UnderMaintenance/UnderMaintenance"; @@ -23,6 +12,7 @@ import CtfChallenges from "./CTFCards/CtfChallenges"; import { RankTrophy } from "../Header/Navbar/NavbarElements"; import { RouteLink } from "../Dashboard/Sidebar/SidebarElements"; import LoadingSpinner from "../Other/MixComponents/Spinner/LoadingSpinner"; +import SearchInputBox from "../Common/SearchInputBox"; const CTF = () => { const { isApiLoading, isApiWorking } = apiStatus(); @@ -83,15 +73,7 @@ const CTF = () => { {/* */} - - - - + Type -
- {characterLimit - noteText.length} characters remaining. - -
- - ); -}; - -export default AddNote; diff --git a/src/components/Dashboard/Notetaker/Note.jsx b/src/components/Dashboard/Notetaker/Note.jsx deleted file mode 100644 index bb1d4e66..00000000 --- a/src/components/Dashboard/Notetaker/Note.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from "react"; -import { GoTrash } from 'react-icons/go'; - -const Note = ({ id, text, handleDeleteNote }) => { - return ( -
-

{text}

- handleDeleteNote(id)}>Delete Note -
- ); -}; - -export default Note; diff --git a/src/components/Dashboard/Notetaker/NoteApp.css b/src/components/Dashboard/Notetaker/NoteApp.css index 6eea7b58..c7e57c4f 100644 --- a/src/components/Dashboard/Notetaker/NoteApp.css +++ b/src/components/Dashboard/Notetaker/NoteApp.css @@ -1,62 +1,7 @@ -.note { - background-color: #242526; - color: #828282; - border: 1px solid #828282; - border-radius: 8px; - padding: 10px; - margin-bottom: 20px; - max-height: 300px; - width: 300px; - overflow-x: none; - overflow-wrap: break-word; - word-break: break-all; +.icon { + color: #646464; } - -.note textarea { - width: 100%; - height: 100%; - border: none; - background-color: #242526; - color: #828282; - font-size: 16px; - resize: none; - outline: none; - overflow-x: none; -} - - -.note button { - background-color: #828282; - color: #090909; - border: none; - padding: 8px 16px; - font-size: 16px; - border-radius: 4px; - margin-left: 30px; - cursor: pointer; -} - -.note button:hover { - background-color: #090909; - color: #828282; -} - -.note .delete-button { - margin-bottom: -5px; - margin-left: 260px; +.icon:hover { + color: #818181; cursor: pointer; } - -.note .character-limit-error { - color: red -} - -.container { - display: flex; - flex-direction: row; - align-items: flex-start; -} - -.wrapper { - overflow-y: hidden; -} \ No newline at end of file diff --git a/src/components/Dashboard/Notetaker/NoteApp.jsx b/src/components/Dashboard/Notetaker/NoteApp.jsx index b2ad6fbd..a4707211 100644 --- a/src/components/Dashboard/Notetaker/NoteApp.jsx +++ b/src/components/Dashboard/Notetaker/NoteApp.jsx @@ -1,10 +1,52 @@ -import React, { useState, useEffect } from "react"; +// import NoteListOld from "./NoteListOld"; +// import { nanoid } from "nanoid"; +import React, { useEffect, useState } from "react"; +import { RxHamburgerMenu } from "react-icons/rx"; +import { FiEdit } from "react-icons/fi"; +import { + NotesContainer, + NotesSidebarContainer, + NotesSidebarHeader, + NotesDescriptionHeader, + NotesDescription, + NotesDescriptionContainer, + NotesSidebarHeaderTitle, + SearchContainer, + DescriptionTitle, + DescriptionContent, +} from "./NoteElements"; +import SearchInputBox from "../../Common/SearchInputBox"; +import "./NoteApp.css"; import NoteList from "./NoteList"; -import { nanoid } from "nanoid"; -const NoteApp = () => { - const [notes, setNotes] = useState([]); +const DUMMY_DATA = [ + { + id: 1, + title: "Exploring JWT Security and Vulnerabilities", + description: + "JSON Web Tokens (JWT) are widely used for authentication and authorization. However, like any technology, they have potential vulnerabilities that need to be understood and addressed.", + }, + { + id: 2, + title: "Responsible Disclosure", + description: + "We take the security of our website very seriously and appreciate the contributions of security researchers to help keep our website secure. If you discover a security vulnerability, please report it to us using the contact information provided below. We ask that you please do not publicly disclose the vulnerability until we have had a chance to investigate and address the issue.", + pinned: false, + }, + { + id: 3, + title: "What is two-factor authentication and how does it work?", + description: + "I've been hearing a lot about two-factor authentication lately, but I'm not sure what it is or how it works. Can anyone explain it to me?I've been hearing a lot about two-factor authentication lately, but I'm not sure what it is or how it works. Can anyone explain it to me?I've been hearing a lot about two-factor authentication lately, but I'm not sure what it is or how it works. Can anyone explain it to me?I've been hearing a lot about two-factor authentication lately, but I'm not sure what it is or how it works. Can anyone explain it to me?", + pinned: false, + }, +]; +const NoteApp = () => { + const [notes, setNotes] = useState(DUMMY_DATA); + const [searchTerm, setSearchTerm] = useState(""); + const [filteredNotes, setFilteredNotes] = useState([]); + const [pickedNote, setPickedNote] = useState({}); useEffect(() => { const savedNotes = JSON.parse(localStorage.getItem("react-notes-app-data")); if (savedNotes !== "") { @@ -16,24 +58,66 @@ const NoteApp = () => { localStorage.setItem("react-notes-app-data", JSON.stringify(notes)); }, [notes]); - const addNote = (text) => { - const newNote = { - text, - id: nanoid(), - }; - const newNotes = [...notes, newNote]; - setNotes(newNotes); - }; + useEffect(() => { + const newFilteredNotes = notes.filter((note) => { + return ( + note?.title?.toLowerCase().includes(searchTerm?.toLowerCase()) || + note?.description?.toLowerCase().includes(searchTerm?.toLowerCase()) + ); + }); + console.log(newFilteredNotes, searchTerm); + setFilteredNotes(newFilteredNotes); + }, [searchTerm]); - const deleteNote = (id) => { - const newNotes = notes?.filter((note) => note.id !== id); - setNotes(newNotes); + const handleSearchTermChange = (e) => { + setSearchTerm(e.target.value); }; + const handlePickNote = (noteId) => { + const pickedNote = notes.find((note) => note.id === noteId); + setPickedNote(pickedNote !== -1 ? pickedNote : {}); + }; + // const addNote = (text) => { + // const newNote = { + // text, + // id: nanoid(), + // }; + // const newNotes = [...notes, newNote]; + // setNotes(newNotes); + // }; + + // const deleteNote = (id) => { + // const newNotes = notes?.filter((note) => note.id !== id); + // setNotes(newNotes); + // }; return ( -
- -
+ + + + + All Notes + + + + + + + {filteredNotes} + + + + + {pickedNote.title} + {pickedNote.description} + + + + + // ); }; diff --git a/src/components/Dashboard/Notetaker/NoteElements.jsx b/src/components/Dashboard/Notetaker/NoteElements.jsx new file mode 100644 index 00000000..f709a92b --- /dev/null +++ b/src/components/Dashboard/Notetaker/NoteElements.jsx @@ -0,0 +1,108 @@ +import styled from "styled-components"; + +export const NotesContainer = styled.div` + display: flex; + width: 100%; + height: 100vh; +`; + +export const NotesSidebarContainer = styled.div` + display: flex; + flex-direction: column; + width: 370px; + height: 100vh; + @media screen and (max-width: 800px) { + width: 35%; + } +`; + +export const NotesSidebarHeader = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 10px; + border: 2px solid #111111; + height: 3rem; +`; + +export const NotesSidebarHeaderTitle = styled.h3` + color: #fff; + text-align: center; + margin-bottom: 0; + font-family: "Roboto Mono", monospace; +`; +export const SearchContainer = styled.div` + display: flex; + border: 2px solid #111111; + background-color: #111111; + border-top: 0px; + height: 3rem; +`; + +export const NotesListContainer = styled.ul` + flex: 1; + display: flex; + flex-direction: column; + gap: 20px; + border: 2px solid #111111; + border-top: 0px; + padding: 20px 15px; + overflow-y: auto; +`; + +export const NoteItemElement = styled.li` + display: flex; + flex-direction: column; + justify-content: start; + align-items: start; + width: 100%; + padding: 10px; + gap: 5px; + background-color: #090909; + color: #f5f5f5; + &:hover { + background-color: #383838; + cursor: pointer; + } + @media screen and (max-width: 500px) { + font-size: 1.5rem; + padding: 25px; + } +`; + +export const NoteItemShortTitle = styled.h4` + font-weight: 900; +`; + +export const NoteItemShortDescription = styled.p` + font-family: "Roboto", sans-serif; + font-weight: 100; +`; + +export const NotesDescriptionContainer = styled.div` + display: flex; + flex-direction: column; + flex: 1; + height: 100vh; +`; + +export const NotesDescriptionHeader = styled.div` + border: 2px solid #111111; + border-left: 0px; + padding: 0 10px; + height: 3rem; +`; + +export const NotesDescription = styled.div` + flex: 1; + border: 2px solid #111111; + border-top: 0px; + border-left: 0px; + padding: 0 10px; + color: #f5f5f5; + padding: 20px; +`; + +export const DescriptionTitle = styled.h1``; + +export const DescriptionContent = styled.p``; diff --git a/src/components/Dashboard/Notetaker/NoteItem.jsx b/src/components/Dashboard/Notetaker/NoteItem.jsx new file mode 100644 index 00000000..0dd07565 --- /dev/null +++ b/src/components/Dashboard/Notetaker/NoteItem.jsx @@ -0,0 +1,25 @@ +import React, { useEffect, useState } from "react"; +import { NoteItemElement, NoteItemShortDescription, NoteItemShortTitle } from "./NoteElements"; + +const shortText = (text, letters) => { + const textCleanFromTags = text.replace(/<[^>]+>/g, ""); + return textCleanFromTags.length > letters ? `${textCleanFromTags.slice(0, letters)}...` : textCleanFromTags; +}; + +const NoteItem = ({ id, title, description, onPick }) => { + const [shortTitle, setShortTitle] = useState(""); + const [shortDescr, setShortDescr] = useState(""); + + useEffect(() => { + setShortTitle(shortText(title, 30)); + setShortDescr(shortText(description, 60)); + }, [title, description]); + + return ( + onPick(id)}> + {shortTitle} + {shortDescr} + + ); +}; +export default NoteItem; diff --git a/src/components/Dashboard/Notetaker/NoteList.jsx b/src/components/Dashboard/Notetaker/NoteList.jsx index 100ec532..15df315b 100644 --- a/src/components/Dashboard/Notetaker/NoteList.jsx +++ b/src/components/Dashboard/Notetaker/NoteList.jsx @@ -1,16 +1,14 @@ import React from "react"; -import Note from "./Note"; -import AddNote from "./AddNote"; +import { NotesListContainer } from "./NoteElements"; +import NoteItem from "./NoteItem"; -const NoteList = ({ id, notes, handleAddNote, handleDeleteNote }) => { +const NoteList = ({ children, onPick }) => { return ( -
- {notes?.map((note) => ( - + + {children.map((note) => ( + ))} - -
+ ); }; - export default NoteList; diff --git a/src/components/Dashboard/Notetaker/Old/AddNoteOld.jsx b/src/components/Dashboard/Notetaker/Old/AddNoteOld.jsx new file mode 100644 index 00000000..c2a5a1a6 --- /dev/null +++ b/src/components/Dashboard/Notetaker/Old/AddNoteOld.jsx @@ -0,0 +1,42 @@ +// import { useState, React } from "react"; +// import './NoteApp.css' + +// const AddNote = ({ handleAddNote }) => { +// const [noteText, setNoteText] = useState(""); +// const [characterLimit, setCharacterLimit] = useState(200); +// const [characterLimitError, setCharacterLimitError] = useState(false); + +// const handleChange = (event) => { +// setNoteText(event.target.value); +// if (noteText.trim().length > characterLimit) { +// setCharacterLimitError(true); +// } else { +// setCharacterLimitError(false); +// } +// }; + +// const handleSaveClick = () => { +// if (noteText.trim().length > 0 && noteText.trim().length < characterLimit) { +// handleAddNote(noteText); +// setNoteText(""); +// } +// }; + +// return ( +//
+// +//
+// {characterLimit - noteText.length} characters remaining. +// +//
+//
+// ); +// }; + +// export default AddNote; diff --git a/src/components/Dashboard/Notetaker/Old/NoteAppOld.css b/src/components/Dashboard/Notetaker/Old/NoteAppOld.css new file mode 100644 index 00000000..94a59680 --- /dev/null +++ b/src/components/Dashboard/Notetaker/Old/NoteAppOld.css @@ -0,0 +1,57 @@ +/* .note { + background-color: #242526; + color: #828282; + border: 1px solid #828282; + border-radius: 8px; + padding: 10px; + margin-bottom: 20px; + max-height: 300px; + width: 300px; + overflow-x: none; + overflow-wrap: break-word; + word-break: break-all; +} + +.note textarea { + width: 100%; + height: 100%; + border: none; + background-color: #242526; + color: #828282; + font-size: 16px; + resize: none; + outline: none; + overflow-x: none; +} + +.note button { + background-color: #828282; + color: #090909; + border: none; + padding: 8px 16px; + font-size: 16px; + border-radius: 4px; + margin-left: 30px; + cursor: pointer; +} + +.note button:hover { + background-color: #090909; + color: #828282; +} + +.note .delete-button { + margin-bottom: -5px; + margin-left: 260px; + cursor: pointer; +} + +.note .character-limit-error { + color: red; +} + +.container { + display: flex; + flex-direction: row; + align-items: flex-start; +} */ diff --git a/src/components/Dashboard/Notetaker/Old/NoteListOld.jsx b/src/components/Dashboard/Notetaker/Old/NoteListOld.jsx new file mode 100644 index 00000000..554f379d --- /dev/null +++ b/src/components/Dashboard/Notetaker/Old/NoteListOld.jsx @@ -0,0 +1,16 @@ +// import React from "react"; +// import Note from "./Note"; +// import AddNote from "./AddNote"; + +// const NoteListOld = ({ id, notes, handleAddNote, handleDeleteNote }) => { +// return ( +//
+// {notes?.map((note) => ( +// +// ))} +// +//
+// ); +// }; + +// export default NoteListOld; diff --git a/src/components/Dashboard/Notetaker/Old/NoteOld.jsx b/src/components/Dashboard/Notetaker/Old/NoteOld.jsx new file mode 100644 index 00000000..091b0576 --- /dev/null +++ b/src/components/Dashboard/Notetaker/Old/NoteOld.jsx @@ -0,0 +1,13 @@ +// import React from "react"; +// import { GoTrash } from 'react-icons/go'; + +// const Note = ({ id, text, handleDeleteNote }) => { +// return ( +//
+//

{text}

+// handleDeleteNote(id)}>Delete Note +//
+// ); +// }; + +// export default Note; diff --git a/src/components/Dashboard/Sidebar/SidebarElements.jsx b/src/components/Dashboard/Sidebar/SidebarElements.jsx index 63ce96c0..f23e3ba6 100644 --- a/src/components/Dashboard/Sidebar/SidebarElements.jsx +++ b/src/components/Dashboard/Sidebar/SidebarElements.jsx @@ -73,11 +73,10 @@ export const SidebarTitle = styled.h2` export const ToggleButton = styled.div` cursor: pointer; position: absolute; - right: ${(props) => (props.isOpen ? "-32px" : "-68px")}; - background-color: ${(props) => (props.isOpen ? "#000000" : "#090909")}; - padding: 10px; - border-radius: ${(props) => (props.isOpen ? "12px" : "0 12px 12px 0")}; - background-color: ${(props) => (props.isOpen ? "transparent" : "#181818")}; + top: 100px; + right: ${(props) => (props.isOpen ? "-32px" : "")}; + padding: ${(props) => (props.isOpen ? "10px" : "17px")}; + border-radius: 12px; `; export const UserProfile = styled.div` diff --git a/src/components/Explore/Explore.jsx b/src/components/Explore/Explore.jsx index cc411022..9533d9bc 100644 --- a/src/components/Explore/Explore.jsx +++ b/src/components/Explore/Explore.jsx @@ -11,7 +11,6 @@ import { import { useDispatch, useSelector } from "react-redux"; import { Wrapper } from "../Dashboard/Profile/ProfileElements"; -import { SearchBox, SearchIcon, SearchInput } from "../CaptureTheFlag/CTFElements"; import { getAllCTFs } from "../../features/ctf/ctfSlice"; import { blogReset, getAllBlogs } from "../../features/blogs/blogSlice"; import { getAllUserDetails, userDetailReset } from "../../features/userDetail/userDetailSlice"; @@ -24,6 +23,7 @@ import CtfChallenges from "../CaptureTheFlag/CTFCards/CtfChallenges"; import apiStatus from "../../features/apiStatus"; import UnderMaintenance from "../Other/UnderMaintenance/UnderMaintenance"; import LoadingSpinner from "../Other/MixComponents/Spinner/LoadingSpinner"; +import SearchInputBox from "../Common/SearchInputBox"; const Explore = () => { const dispatch = useDispatch(); @@ -138,15 +138,11 @@ const Explore = () => { - - - - + {types.map((type) => (