From 3c5794845af27ccbee6ffdc80e804fdefd58e30d Mon Sep 17 00:00:00 2001 From: Unbewohnte Date: Sat, 18 Jan 2025 18:40:16 +0300 Subject: [PATCH] FEATURE: Edit ToDo text and Due fields on ToDo modal view --- pages/category.html | 77 +++++++++++++++++++++++++++++++++-------- src/db/todo.go | 48 +++++++++++++++++++++++++ src/db/verification.go | 4 +-- src/server/endpoints.go | 4 +-- 4 files changed, 115 insertions(+), 18 deletions(-) diff --git a/pages/category.html b/pages/category.html index bd6a49a..0fbea35 100644 --- a/pages/category.html +++ b/pages/category.html @@ -57,14 +57,28 @@ @@ -128,7 +142,7 @@ {{ range .Todos }} {{ if not .IsDone }} - + {{ if not .Image }} @@ -164,7 +178,7 @@ {{ range .Todos }} {{ if .IsDone }} - + {{ if not .Image }} @@ -262,19 +276,22 @@ async function drop(event) { // Update todo's group ID let result = await updateTodo(todoId, { - text: draggedTodo.getElementsByClassName("todo-text")[0].innerText, groupId: Number(targetGroupId), - dueUnix: Number(draggedTodo.getElementsByClassName("todo-due-unix")[0].innerText), - image: Array.from(draggedTodo.getElementsByClassName("todo-image")[0].src, char => char.charCodeAt(0)) }); window.location.reload(); } -function openTodoModal(id, text, created, due, completionTime, image) { - document.getElementById('modalTodoText').innerText = text; + +let viewedTodoID; +function openTodoModal(id, text, created, due, completionTime, image, editable) { + viewedTodoID = id; + + document.getElementById('modalTodoTextDisplay').innerText = text; + document.getElementById('modalTodoTextInput').value = text; document.getElementById('modalTodoCreated').innerText = created; - document.getElementById('modalTodoDue').innerText = due; + document.getElementById('modalTodoDueDisplay').innerText = due; + document.getElementById('modalTodoDueInput').value = due; document.getElementById('modalTodoCompletionTime').innerText = completionTime; let img = document.getElementById('modalTodoImage'); @@ -285,10 +302,42 @@ function openTodoModal(id, text, created, due, completionTime, image) { img.style.display = 'none'; } + let editButton = document.getElementById("editButton"); + if (editable) { + // Show "Edit" button + editButton.style.display = "inline"; + } else { + editButton.style.display = "none"; + } + const todoModal = new bootstrap.Modal(document.getElementById('todoModal')); todoModal.show(); } +async function saveEditedTodo() { + const updatedText = document.getElementById('modalTodoTextInput').value; + const updatedDue = document.getElementById('modalTodoDueInput').value; + document.getElementById('modalTodoTextDisplay').innerText = updatedText; + document.getElementById('modalTodoDueDisplay').innerText = updatedDue; + const updatedDueUnix = Date.parse(updatedDue) / 1000; + + toggleEditMode(false); + + await updateTodo(viewedTodoID, {"text":updatedText, "dueUnix":updatedDueUnix, "isDone":false}); + + window.location.reload(); +} + + +function toggleEditMode(isEditing) { + document.getElementById('modalTodoTextDisplay').style.display = isEditing ? 'none' : 'inline'; + document.getElementById('modalTodoTextInput').style.display = isEditing ? 'inline' : 'none'; + document.getElementById('modalTodoDueDisplay').style.display = isEditing ? 'none' : 'inline'; + document.getElementById('modalTodoDueInput').style.display = isEditing ? 'inline' : 'none'; + document.getElementById('editButton').style.display = isEditing ? 'none' : 'inline'; + document.getElementById('saveButton').style.display = isEditing ? 'inline' : 'none'; +} + document.addEventListener('DOMContentLoaded', async function() { document.getElementById("newTodoText").focus(); @@ -331,7 +380,7 @@ document.addEventListener('DOMContentLoaded', async function() { // Make a request let response = await postNewTodo( - {text: newTodoText, groupId: Number(groupId), dueUnix: Number(dueTimeStamp), image: canvasImage} + {"text": newTodoText, "groupId": Number(groupId), "dueUnix": Number(dueTimeStamp), "image": canvasImage} ); if (response.ok) { location.reload(); diff --git a/src/db/todo.go b/src/db/todo.go index 0527889..cef986a 100644 --- a/src/db/todo.go +++ b/src/db/todo.go @@ -19,7 +19,9 @@ package db import ( + "bytes" "database/sql" + "strings" "time" ) @@ -157,6 +159,52 @@ func (db *DB) UpdateTodo(todoID uint64, updatedTodo Todo) error { return err } +// Updates all changed fields which are not nil-valued in a ToDo and retains all unchanged ones +func (db *DB) UpdateTodoSoft(todoID uint64, updatedTodo Todo) error { + originalTodo, err := db.GetTodo(todoID) + if err != nil { + return err + } + + args := []interface{}{} + updates := []string{} + if (updatedTodo.GroupID != originalTodo.GroupID) && updatedTodo.GroupID != 0 { + updates = append(updates, "group_id=?") + args = append(args, updatedTodo.GroupID) + } + if (updatedTodo.DueUnix != originalTodo.DueUnix) && updatedTodo.DueUnix != 0 { + updates = append(updates, "due_unix=?") + args = append(args, updatedTodo.DueUnix) + } + if (updatedTodo.Text != originalTodo.Text) && updatedTodo.Text != "" { + updates = append(updates, "text=?") + args = append(args, updatedTodo.Text) + } + if updatedTodo.IsDone != originalTodo.IsDone { + updates = append(updates, "is_done=?") + args = append(args, updatedTodo.IsDone) + } + if (updatedTodo.CompletionTimeUnix != originalTodo.CompletionTimeUnix) && updatedTodo.CompletionTimeUnix != 0 { + updates = append(updates, "completion_time_unix=?") + args = append(args, updatedTodo.CompletionTimeUnix) + } + if !bytes.Equal(updatedTodo.Image, originalTodo.Image) && updatedTodo.Image != nil { + updates = append(updates, "image=?") + args = append(args, updatedTodo.Image) + } + + if len(updates) == 0 { + return nil + } + + query := "UPDATE todos SET " + strings.Join(updates, ", ") + " WHERE id=?" + args = append(args, todoID) + + _, err = db.Exec(query, args...) + + return err +} + // Searches and retrieves TODO groups created by the user func (db *DB) GetAllUserTodoGroups(email string) ([]*TodoGroup, error) { var todoGroups []*TodoGroup diff --git a/src/db/verification.go b/src/db/verification.go index acd1db7..37eec69 100644 --- a/src/db/verification.go +++ b/src/db/verification.go @@ -8,8 +8,8 @@ type Verification struct { ID uint64 `json:"id"` Email string `json:"email"` Code string `json:"code"` - IssuedUnix uint64 `json:"issued_unix"` - LifeSeconds uint64 `json:"life_seconds"` + IssuedUnix uint64 `json:"issuedUnix"` + LifeSeconds uint64 `json:"lifeSeconds"` } func NewVerification(email string, code string, issuedUnix uint64, lifeSeconds uint64) *Verification { diff --git a/src/server/endpoints.go b/src/server/endpoints.go index 34dd59f..aa1e220 100644 --- a/src/server/endpoints.go +++ b/src/server/endpoints.go @@ -421,8 +421,8 @@ func (s *Server) EndpointTodoUpdate(w http.ResponseWriter, req *http.Request) { return } - // Update. (Creation date, owner username and an ID do not change) - err = s.db.UpdateTodo(todoID, updatedTodo) + // Update + err = s.db.UpdateTodoSoft(todoID, updatedTodo) if err != nil { logger.Warning("[Server] Failed to update TODO: %s", err) http.Error(w, "Failed to update", http.StatusBadRequest)