diff --git a/app/db.rb b/app/db.rb index 0919e82..1c3a282 100644 --- a/app/db.rb +++ b/app/db.rb @@ -137,6 +137,14 @@ class App < Sinatra::Base end end + put "/db/notes/:id" do + @record = Ronin::DB::Note.find(params[:id]) + + unless @record.update(body: params[:body]) + halt 400 + end + end + get '/db/asns' do @pagy, @asns = pagy(Ronin::DB::ASN) 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/app.js b/public/javascript/app.js index b570ddd..f099407 100644 --- a/public/javascript/app.js +++ b/public/javascript/app.js @@ -57,4 +57,36 @@ ready(() => { $notification.parentNode.removeChild($notification); }); }); + + (document.querySelectorAll('.advanced > a.advanced-toggle') || []).forEach(($toggle) => { + const $advanced = $toggle.parentNode; + + $advanced.classList.add('hidden'); + + $toggle.addEventListener('click', (e) => { + if ($advanced.classList.contains('hidden')) + { + $advanced.classList.remove('hidden'); + } + else + { + $advanced.classList.add('hidden'); + } + }); + }); + + (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 8f81f64..9300bb2 100644 --- a/public/javascript/notes.js +++ b/public/javascript/notes.js @@ -1,20 +1,50 @@ -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(noteDiv) { + this.noteDiv = noteDiv; + this.id = noteDiv.getAttribute('data-note-id'); + + (noteDiv.querySelectorAll('.edit-note') || []).forEach(editNoteButton => { + editNoteButton.addEventListener('click', () => { + this.toggleEditForm() + }) + }) + + noteDiv.querySelector('.delete-note').addEventListener('click', () => { + this.delete() + }) + + noteDiv.querySelector('.update-note').addEventListener('click', () => { + this.toggleEditForm() + this.update() + }) + } + + update() { + const body = this.noteDiv.querySelector('.note-body') + const newValue = this.noteDiv.querySelector('.textarea').value + + 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.parentElement.remove(); + this.noteDiv.remove(); } else { console.error('Failed to delete note'); } @@ -23,6 +53,31 @@ const Notes = { console.error('Error:', error); }); } + + toggleEditForm() { + const editForm = this.noteDiv.querySelector('.note-form') + const body = this.noteDiv.querySelector('.note-body') + + if (editForm.style.display === 'none') { + body.style.display = 'none'; + editForm.style.display = 'block'; + } else { + body.style.display = 'block'; + editForm.style.display = 'none'; + } + } +} + +class Notes { + constructor() { + this.notes = []; + + (document.querySelectorAll('.note-div') || []).forEach(noteDiv => { + this.notes.push(new Note(noteDiv)) + }) + } }; -ready(Notes.init); +ready(() => { + new Notes(); +}); diff --git a/public/stylesheets/app.css b/public/stylesheets/app.css index 023943a..0781677 100644 --- a/public/stylesheets/app.css +++ b/public/stylesheets/app.css @@ -3,6 +3,8 @@ --fg-color: black; --dark-mode-bg-color: #0f0f1b; --dark-mode-fg-color: white; + --dark-mode-secondary-bg-color: #141425; + --danger-color: hsl(348, 100%, 61%); } body { @@ -223,3 +225,50 @@ body.dark-mode .content .tabs ul li a:hover { color: var(--dark-mode-bg-color); background-color: var(--dark-mode-fg-color); } + +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: var(--danger-color) !important; + color: var(--dark-mode-fg-color) !important; +} + +.dropdown-menu { + min-width: 0 !important; +} + +.icon { + cursor: pointer; +} + +.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 5d74126..c8910ef 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| %> +
class="note-div box p-0"> +
+
+ Created at: <%=h note.created_at %> +
+ +
+ -
- +
+
+ +
+
+
<%=h note.body %>
+ +
- <% end %> +
+ <% end %> <% end %>