From 2c9c890e6f965ee3efe8df07d80c46ec349840c1 Mon Sep 17 00:00:00 2001 From: AI-Mozi Date: Sun, 21 Apr 2024 21:13:55 +0200 Subject: [PATCH 1/5] Add edit functionality for notes --- app/db.rb | 8 ++++++ public/javascript/app.js | 15 +++++++++++ public/javascript/notes.js | 47 ++++++++++++++++++++++++++++++++++ public/stylesheets/app.css | 28 ++++++++++++++++++++ views/_notes.erb | 52 +++++++++++++++++++++++++++++++------- views/layout.erb | 1 + 6 files changed, 142 insertions(+), 9 deletions(-) diff --git a/app/db.rb b/app/db.rb index 9ecb8b6..9a7f4ff 100644 --- a/app/db.rb +++ b/app/db.rb @@ -126,6 +126,14 @@ class App < Sinatra::Base end end + put "/db/notes/:id" do + @record = Ronin::DB::Note.find_by(params[:id]) + + unless @record || @record.update(params) + halt 404 + end + end + get '/db/asns' do @pagy, @asns = pagy(Ronin::DB::ASN) diff --git a/public/javascript/app.js b/public/javascript/app.js index b8265f6..f099407 100644 --- a/public/javascript/app.js +++ b/public/javascript/app.js @@ -74,4 +74,19 @@ ready(() => { } }); }); + + (document.querySelectorAll('.dropdown') || []).forEach(dropdown => { + dropdown.addEventListener('click', function(event) { + event.stopPropagation(); + dropdown.classList.toggle('is-active'); + }); + }) + + document.addEventListener('click', function (event) { + (document.querySelectorAll('.dropdown') || []).forEach(dropdown => { + if (!dropdown.contains(event.target)) { + dropdown.classList.remove('is-active') + } + }) + }) }); diff --git a/public/javascript/notes.js b/public/javascript/notes.js index 183a320..138bb8a 100644 --- a/public/javascript/notes.js +++ b/public/javascript/notes.js @@ -6,6 +6,21 @@ const Notes = { Notes.delete(noteId, button) }); }); + + (document.querySelectorAll('.edit-note') || []).forEach(button => { + button.addEventListener('click', () => { + const noteId = button.getAttribute('data-note-id') + Notes.toggleEditForm(noteId) + }) + }); + + (document.querySelectorAll('.update-note') || []).forEach(button => { + button.addEventListener('click', () => { + const noteId = button.getAttribute('data-note-id') + Notes.toggleEditForm(noteId) + Notes.update(noteId) + }) + }); }, delete(noteId, button) { @@ -22,6 +37,38 @@ const Notes = { .catch(error => { console.error('Error:', error); }); + }, + + toggleEditForm(noteId) { + const editForm = document.getElementById(`note-edit-form-${noteId}`) + const body = document.getElementById(`note-edit-body-${noteId}`) + + if (editForm.style.display === 'none') { + body.style.display = 'none'; + editForm.style.display = 'block'; + } else { + body.style.display = 'block'; + editForm.style.display = 'none'; + } + }, + + update(noteId) { + const body = document.getElementById(`note-edit-body-${noteId}`) + const newValue = document.getElementById(`note-edit-textarea-${noteId}`).value + + fetch(`${document.location.origin}/db/notes/${noteId}?` + new URLSearchParams({ body: newValue }), { + method: 'PUT' + }) + .then(response => { + if (response.ok) { + body.textContent = newValue + } else { + console.error('Failed update a note'); + } + }) + .catch(error => { + console.error('Error:', error); + }); } }; diff --git a/public/stylesheets/app.css b/public/stylesheets/app.css index fe12d60..310f110 100644 --- a/public/stylesheets/app.css +++ b/public/stylesheets/app.css @@ -3,6 +3,7 @@ --fg-color: black; --dark-mode-bg-color: #0f0f1b; --dark-mode-fg-color: white; + --dark-mode-secondary-bg-color: #141425; } body { @@ -175,3 +176,30 @@ body.dark-mode main a { padding-top: 0; border-top: 0; } + +body.dark-mode .dropdown-content { + background-color: var(--dark-mode-secondary-bg-color); +} + +.dropdown-item { + text-decoration: none !important; + padding: 8px 32px !important; + text-align: center !important; +} + +.dropdown-item.delete-note:hover { + background-color: hsl(348, 100%, 61%) !important; + color: var(--dark-mode-fg-color) !important; +} + +.dropdown-menu { + min-width: 0 !important; +} + +.dropdown.three-dots { + cursor: pointer; +} + +.dropdown.three-dots:hover { + color: rgb(105, 105, 198) !important; +} diff --git a/views/_notes.erb b/views/_notes.erb index 5d74126..13b19b2 100644 --- a/views/_notes.erb +++ b/views/_notes.erb @@ -2,19 +2,53 @@ <% unless notes.empty? %>

Notes

- <% notes.each do |note| %> -
-
-
-

<%= note.body %>

- Created at: <%= note.created_at %> + + <% notes.each do |note| %> +
+
+
+ Created at: <%= note.created_at %> +
+ +
+ -
- +
+
+ +
+
+
<%= note.body %>
+ +
- <% end %> +
+ <% end %> <% end %>
diff --git a/views/layout.erb b/views/layout.erb index 1793711..f5ddc8f 100644 --- a/views/layout.erb +++ b/views/layout.erb @@ -8,6 +8,7 @@ + From 1549bbb09df62c1ec1ac86d5ba89803d26b462fb Mon Sep 17 00:00:00 2001 From: AI-Mozi Date: Mon, 22 Apr 2024 10:43:14 +0200 Subject: [PATCH 2/5] remove in-line styles, create js class Note, change classes names --- app/db.rb | 6 +-- public/images/three-dots.svg | 1 + public/javascript/notes.js | 93 +++++++++++++++++++----------------- public/stylesheets/app.css | 29 +++++++++-- views/_notes.erb | 22 ++++----- views/layout.erb | 1 - 6 files changed, 89 insertions(+), 63 deletions(-) create mode 100644 public/images/three-dots.svg diff --git a/app/db.rb b/app/db.rb index 9a7f4ff..59ef891 100644 --- a/app/db.rb +++ b/app/db.rb @@ -127,10 +127,10 @@ class App < Sinatra::Base end put "/db/notes/:id" do - @record = Ronin::DB::Note.find_by(params[:id]) + @record = Ronin::DB::Note.find(params[:id]) - unless @record || @record.update(params) - halt 404 + unless @record.update(body: params[:body]) + halt 400 end end diff --git a/public/images/three-dots.svg b/public/images/three-dots.svg new file mode 100644 index 0000000..e02abbc --- /dev/null +++ b/public/images/three-dots.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/javascript/notes.js b/public/javascript/notes.js index 138bb8a..ab80acf 100644 --- a/public/javascript/notes.js +++ b/public/javascript/notes.js @@ -1,35 +1,35 @@ -const Notes = { - init() { - (document.querySelectorAll('.delete-note') || []).forEach(button => { - button.addEventListener('click', () => { - const noteId = button.getAttribute('data-note-id'); - Notes.delete(noteId, button) - }); - }); +class Note { + constructor(id, noteDiv) { + this.id = id + this.noteDiv = noteDiv + } - (document.querySelectorAll('.edit-note') || []).forEach(button => { - button.addEventListener('click', () => { - const noteId = button.getAttribute('data-note-id') - Notes.toggleEditForm(noteId) - }) - }); + update() { + const body = this.noteDiv.querySelector('.note-body') + const newValue = this.noteDiv.querySelector('.textarea').value - (document.querySelectorAll('.update-note') || []).forEach(button => { - button.addEventListener('click', () => { - const noteId = button.getAttribute('data-note-id') - Notes.toggleEditForm(noteId) - Notes.update(noteId) - }) + fetch(`${document.location.origin}/db/notes/${this.id}?` + new URLSearchParams({ body: newValue }), { + method: 'PUT' + }) + .then(response => { + if (response.ok) { + body.textContent = newValue + } else { + console.error('Failed update a note'); + } + }) + .catch(error => { + console.error('Error: ', error); }); - }, + } - delete(noteId, button) { - fetch(`${document.location}/notes/${noteId}`, { + delete() { + fetch(`${document.location}/notes/${this.id}`, { method: 'DELETE' }) .then(response => { if (response.ok) { - button.parentElement.parentElement.remove(); + this.noteDiv.remove(); } else { console.error('Failed to delete note'); } @@ -37,11 +37,11 @@ const Notes = { .catch(error => { console.error('Error:', error); }); - }, + } - toggleEditForm(noteId) { - const editForm = document.getElementById(`note-edit-form-${noteId}`) - const body = document.getElementById(`note-edit-body-${noteId}`) + toggleEditForm() { + const editForm = this.noteDiv.querySelector('.note-form') + const body = this.noteDiv.querySelector('.note-body') if (editForm.style.display === 'none') { body.style.display = 'none'; @@ -50,25 +50,30 @@ const Notes = { body.style.display = 'block'; editForm.style.display = 'none'; } - }, + } +} - update(noteId) { - const body = document.getElementById(`note-edit-body-${noteId}`) - const newValue = document.getElementById(`note-edit-textarea-${noteId}`).value +const Notes = { + init() { + (document.querySelectorAll('.note-div') || []).forEach(noteDiv => { + const noteId = noteDiv.getAttribute('data-note-id') + const note = new Note(noteId, noteDiv) - fetch(`${document.location.origin}/db/notes/${noteId}?` + new URLSearchParams({ body: newValue }), { - method: 'PUT' - }) - .then(response => { - if (response.ok) { - body.textContent = newValue - } else { - console.error('Failed update a note'); - } + noteDiv.querySelector('.delete-note').addEventListener('click', () => { + note.delete() + }); + + (noteDiv.querySelectorAll('.edit-note') || []).forEach(editNoteButton => { + editNoteButton.addEventListener('click', () => { + note.toggleEditForm() + }) + }) + + noteDiv.querySelector('.update-note').addEventListener('click', () => { + note.toggleEditForm() + note.update() + }) }) - .catch(error => { - console.error('Error:', error); - }); } }; diff --git a/public/stylesheets/app.css b/public/stylesheets/app.css index 310f110..060fcff 100644 --- a/public/stylesheets/app.css +++ b/public/stylesheets/app.css @@ -4,6 +4,7 @@ --dark-mode-bg-color: #0f0f1b; --dark-mode-fg-color: white; --dark-mode-secondary-bg-color: #141425; + --danger-color: hsl(348, 100%, 61%); } body { @@ -188,7 +189,7 @@ body.dark-mode .dropdown-content { } .dropdown-item.delete-note:hover { - background-color: hsl(348, 100%, 61%) !important; + background-color: var(--danger-color) !important; color: var(--dark-mode-fg-color) !important; } @@ -196,10 +197,30 @@ body.dark-mode .dropdown-content { min-width: 0 !important; } -.dropdown.three-dots { +.icon { cursor: pointer; } -.dropdown.three-dots:hover { - color: rgb(105, 105, 198) !important; +.three-dots-icon { + min-width: 16px; + height: 16px; + background-image: url('/images/three-dots.svg'); + background-size: cover; + display: inline-block; } + +.note-header { + background-color: #3e3e67 !important; +} + +body.dark-mode .note-header { + background-color: #1c1c39 !important; +} + +.note-div { + border: 1px solid #1c1c39 !important; +} + +.note-header .created-at small { + color: white !important; +} \ No newline at end of file diff --git a/views/_notes.erb b/views/_notes.erb index 13b19b2..62fb219 100644 --- a/views/_notes.erb +++ b/views/_notes.erb @@ -4,9 +4,9 @@

Notes

<% notes.each do |note| %> -
-
-
+
class="note-div box p-0"> +
+
Created at: <%= note.created_at %>
@@ -14,13 +14,13 @@ @@ -29,18 +29,18 @@
-
<%= note.body %>
+
<%= note.body %>
-