Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add edit functionality for notes #101

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/db.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
1 change: 1 addition & 0 deletions public/images/three-dots.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions public/javascript/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
}
})
})
});
79 changes: 67 additions & 12 deletions public/javascript/notes.js
Original file line number Diff line number Diff line change
@@ -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');
}
Expand All @@ -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();
});
49 changes: 49 additions & 0 deletions public/stylesheets/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}
52 changes: 43 additions & 9 deletions views/_notes.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,53 @@

<% unless notes.empty? %>
<h3>Notes</h3>
<% notes.each do |note| %>
<div class="box">
<div class="columns is-full">
<div class="column">
<h4 class="mb-0"><%= note.body %></h4>
<small>Created at: <%= note.created_at %></small>

<% notes.each do |note| %>
<div data-note-id=<%=hattr note.id %> class="note-div box p-0">
<div class="columns note-header m-0 p-1">
<div class="column created-at is-half px-3 py-0">
<small class="is-size-7">Created at: <%=h note.created_at %></small>
</div>

<div class="column has-text-right p-0 pr-1 mt-0">
<div class="dropdown three-dots is-right">
<div class="dropdown-trigger">
<span class="icon is-small">
<div class="three-dots-icon"></div>
</span>
</div>
<div class="dropdown-menu" id="dropdown-menu6" role="menu">
<div class="dropdown-content">
<a class="dropdown-item py-2 edit-note">Edit</a>
<a class="dropdown-item py-2 delete-note has-text-danger">Delete</a>
</div>
</div>
</div>
<div class="column is-one-fifth has-text-right">
<button class="button is-danger is-small delete-note " data-note-id=<%= note.id %>>X</button>
</div>
</div>

<div class="columns is-full">
<div class="column">
<div class="note-body mb-0 p-4"><%=h note.body %></div>

<div class="note-form" style="display: none">
<div class="control">
<div class="media-content">
<div class="field mb-0 p-2">
<textarea class="textarea" name="body" placeholder="Edit a note..."><%=h note.body %></textarea>
</div>

<div class="field is-flex is-justify-content-flex-end px-2 pb-2">
<button class="edit-note button is-small is-danger mr-2">Discard</button>
<button class="update-note button is-small is-primary">Save</button>
</div>
</div>
</div>
</div>
</div>
</div>
<% end %>
</div>
<% end %>
<% end %>

<div class="control mt-4">
Expand Down
Loading