diff --git a/ao3 webpages/src/pages/notes.jsx b/ao3 webpages/src/pages/notes.jsx index 66e71af..b6f9669 100644 --- a/ao3 webpages/src/pages/notes.jsx +++ b/ao3 webpages/src/pages/notes.jsx @@ -1,7 +1,6 @@ -import { useState } from 'react' -import { Dialog } from '@headlessui/react' +import { useState, useRef, useEffect } from 'react' -const demoNotes = [ +const initialNotes = [ { id: '1', title: 'The Shoebox Project', tags: ['Humor', 'Drama'], completed: false }, { id: '2', title: 'Twist and Shout', tags: ['Angst', 'Historical', 'Tragedy'], completed: false }, { id: '3', title: 'Everyday Love in Stockholm', tags: ['Humor', 'Hurt', 'Loki'], completed: false }, @@ -11,157 +10,234 @@ const demoNotes = [ ] export default function Component() { - const [notes, setNotes] = useState(demoNotes) + const [notes, setNotes] = useState(() => { + const savedNotes = localStorage.getItem('notes') + return savedNotes ? JSON.parse(savedNotes) : initialNotes + }) const [isModalOpen, setIsModalOpen] = useState(false) - const [newNote, setNewNote] = useState({ title: '', tags: '' }) + const [newNote, setNewNote] = useState({ title: '', tags: [] }) + const [currentTag, setCurrentTag] = useState('') const [searchTag, setSearchTag] = useState('') + const [selectedTags, setSelectedTags] = useState([]) + const modalRef = useRef(null) + + useEffect(() => { + localStorage.setItem('notes', JSON.stringify(notes)) + }, [notes]) const addNote = () => { + if (newNote.title.trim() === '') return const newNoteWithId = { ...newNote, id: Date.now().toString(), - tags: newNote.tags.split(',').map(tag => tag.trim()), completed: false } - setNotes([...notes, newNoteWithId]) - setNewNote({ title: '', tags: '' }) + setNotes(prevNotes => [...prevNotes, newNoteWithId]) + setNewNote({ title: '', tags: [] }) + setCurrentTag('') setIsModalOpen(false) } const toggleNoteCompletion = (id) => { - setNotes(notes.map(note => + setNotes(prevNotes => prevNotes.map(note => note.id === id ? { ...note, completed: !note.completed } : note )) } const deleteNote = (id) => { - setNotes(notes.filter(note => note.id !== id)) + setNotes(prevNotes => prevNotes.filter(note => note.id !== id)) + } + + const handleTagInput = (e) => { + const value = e.target.value + if (value.endsWith(' ') && value.trim() !== '') { + setNewNote(prev => ({ + ...prev, + tags: [...prev.tags, currentTag.trim()] + })) + setCurrentTag('') + } else { + setCurrentTag(value) + } + } + + const removeTag = (tagToRemove) => { + setNewNote(prev => ({ + ...prev, + tags: prev.tags.filter(tag => tag !== tagToRemove) + })) } const allTags = Array.from(new Set(notes.flatMap(note => note.tags))) - return ( -
-

Notes

- -
-
-

Manage your Reading list :

-
setIsModalOpen(true)}> -
-
- - Add Name -
- + + useEffect(() => { + const handleClickOutside = (event) => { + if (modalRef.current && !modalRef.current.contains(event.target)) { + setIsModalOpen(false) + } + } + + document.addEventListener('mousedown', handleClickOutside) + return () => { + document.removeEventListener('mousedown', handleClickOutside) + } + }, []) + + const toggleSelectedTag = (tag) => { + setSelectedTags(prevTags => + prevTags.includes(tag) + ? prevTags.filter(t => t !== tag) + : [...prevTags, tag] + ) + } + + const sortedNotes = [...notes].sort((a, b) => { + const aMatchCount = a.tags.filter(tag => + selectedTags.includes(tag) || tag.toLowerCase().includes(searchTag.toLowerCase()) + ).length + const bMatchCount = b.tags.filter(tag => + selectedTags.includes(tag) || tag.toLowerCase().includes(searchTag.toLowerCase()) + ).length + if (aMatchCount !== bMatchCount) { + return bMatchCount - aMatchCount // Sort by match count first + } + // If match counts are equal, maintain original order + return notes.indexOf(a) - notes.indexOf(b) + }) + + const renderNotes = (completed) => { + return sortedNotes + .filter(note => note.completed === completed) + .map(note => ( +
+
+
+ toggleNoteCompletion(note.id)} + /> + {note.title}
-

#Add Tags

+ deleteNote(note.id)}>Delete
+

+ {note.tags.map(tag => ( + + #{tag} + + ))} +

+
+ )) + } - {notes.filter(note => !note.completed).map(note => ( -
+ return ( +
+
+

Notes

+ +
+
+

Manage your Reading list :

+
setIsModalOpen(true)}>
- toggleNoteCompletion(note.id)} - /> - {note.title} + + Add Name
- deleteNote(note.id)}>Delete + +
-

- {note.tags.map(tag => `#${tag}`).join(' ')} -

+

#Add Tags

- ))} -

Completed :

- {notes.filter(note => note.completed).map(note => ( -
-
-
- toggleNoteCompletion(note.id)} - /> - {note.title} -
- deleteNote(note.id)}>Delete + {renderNotes(false)} + +

Completed :

+ {renderNotes(true)} +
+ +
+

Manage Tags :

+
+
+ setSearchTag(e.target.value)} + /> + 🔍 +
+
+ {allTags.map(tag => ( + toggleSelectedTag(tag)} + > + #{tag} + + ))}
-

- {note.tags.map(tag => `#${tag}`).join(' ')} -

- ))} +
+
-
-

Manage Tags :

-
-
+ {isModalOpen && ( +
+
+

Add a new Note :

+
setSearchTag(e.target.value)} + placeholder="Add name" + className="w-full p-2 border rounded-md pr-8" + value={newNote.title} + onChange={(e) => setNewNote({ ...newNote, title: e.target.value })} /> - 🔍 + ✏️
-
- {allTags.map(tag => ( - - #{tag} - - ))} +
+
+ {newNote.tags.map(tag => ( + + #{tag} + + + ))} +
+
-
-
-
- - setIsModalOpen(false)} className="fixed inset-0 z-10 overflow-y-auto"> -
- -
-

Add New Note

- setNewNote({ ...newNote, title: e.target.value })} - /> - setNewNote({ ...newNote, tags: e.target.value })} - /> -
+
-
-
+ )}
) -} +} \ No newline at end of file