From d14d1ec4687c5085516de66d199735892107679f Mon Sep 17 00:00:00 2001 From: Cameron Reed Date: Mon, 19 Aug 2024 23:26:47 -0600 Subject: [PATCH] Editing and better sorting --- db/db.go | 5 + main.go | 1 + pages/templates/root.templ | 70 +++++++++---- pages/templates/root_templ.go | 184 +++++++++++++++++++++------------- pages/webapi.go | 58 +++++++++++ static/css/styles.css | 22 ++-- static/js/script.js | 106 ++++++++++++++++---- 7 files changed, 323 insertions(+), 123 deletions(-) diff --git a/db/db.go b/db/db.go index 9eacf9e..7d88799 100644 --- a/db/db.go +++ b/db/db.go @@ -197,6 +197,11 @@ func GetUpcomingTodos() ([]types.Todo, error) { return todos, nil } +func UpdateTodo(newValues types.Todo) error { + _, err := db.Exec("UPDATE items SET start=?, due=?, text=? WHERE id=?", toNullInt64(newValues.Start), toNullInt64(newValues.Due), newValues.Text, newValues.Id) + return err; +} + func SetCompleted(id int, completed bool) error { _, err := db.Exec("UPDATE items SET completed=? WHERE id=?", completed, id) return err diff --git a/main.go b/main.go index a91ec56..3bc259a 100644 --- a/main.go +++ b/main.go @@ -61,6 +61,7 @@ func addFrontendEndpoints(mux *http.ServeMux) { mux.HandleFunc("/upcoming", pages.UpcomingFragment) mux.HandleFunc("DELETE /delete/{id}", pages.DeleteItem) mux.HandleFunc("PATCH /set/{id}", pages.SetItemCompleted) + mux.HandleFunc("PUT /update", pages.UpdateItem) mux.HandleFunc("POST /new", pages.CreateItem) fileServer := http.FileServer(http.Dir("./static")) diff --git a/pages/templates/root.templ b/pages/templates/root.templ index 5cc2cd1..ed0c2b4 100644 --- a/pages/templates/root.templ +++ b/pages/templates/root.templ @@ -54,28 +54,50 @@ templ RootPage() {
-
Create new Todo
-
-
-
- -
- -
- -
- -
- -
- -
+
Create new Todo
+
+
+ +
+ +
+ +
+ +
+ +
+
-
+
-
- - Close +
+ + Close +
+ + +
+
Edit Todo
+
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+
+ + Close
@@ -83,12 +105,16 @@ templ RootPage() { } +script edit(item_id string) { + edit(item_id) +} + templ TodoItem(item types.Todo) { -
+
{ item.Text }
- +
diff --git a/pages/templates/root_templ.go b/pages/templates/root_templ.go index 5a5b5e4..f17a91f 100644 --- a/pages/templates/root_templ.go +++ b/pages/templates/root_templ.go @@ -1,14 +1,12 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.2.663 +// templ: version: v0.2.747 package templates //lint:file-ignore SA4006 This context is only used if a nested component is present. import "github.com/a-h/templ" -import "context" -import "io" -import "bytes" +import templruntime "github.com/a-h/templ/runtime" import ( "fmt" @@ -17,11 +15,16 @@ import ( ) func RootPage() templ.Component { - return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { - templ_7745c5c3_Buffer = templ.GetBuffer() - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() } ctx = templ.InitializeContext(ctx) templ_7745c5c3_Var1 := templ.GetChildren(ctx) @@ -29,23 +32,35 @@ func RootPage() templ.Component { templ_7745c5c3_Var1 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Todo
Overdue
Today
Upcoming
Create new Todo





Close
") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Todo
Overdue
Today
Upcoming
Create new Todo





Close
Edit Todo





Close
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - if !templ_7745c5c3_IsBuffer { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) - } return templ_7745c5c3_Err }) } +func edit(item_id string) templ.ComponentScript { + return templ.ComponentScript{ + Name: `__templ_edit_2f09`, + Function: `function __templ_edit_2f09(item_id){edit(item_id) +}`, + Call: templ.SafeScript(`__templ_edit_2f09`, item_id), + CallInline: templ.SafeScriptInline(`__templ_edit_2f09`, item_id), + } +} + func TodoItem(item types.Todo) templ.Component { - return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { - templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { - templ_7745c5c3_Buffer = templ.GetBuffer() - defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() } ctx = templ.InitializeContext(ctx) templ_7745c5c3_Var2 := templ.GetChildren(ctx) @@ -53,29 +68,55 @@ func TodoItem(item types.Todo) templ.Component { templ_7745c5c3_Var2 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
now { + targetSelector = "#upcoming-list > .new-item" + } + + templates.OobTodoItem(targetSelector, todo).Render(r.Context(), w) +} diff --git a/static/css/styles.css b/static/css/styles.css index dde4db7..76d19a7 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -133,7 +133,7 @@ nav { cursor: pointer; } -#create-item { +form { position: absolute; left: 50%; top: 50%; @@ -152,35 +152,35 @@ nav { padding: 30px; } -#create-item:target { +form:target { top: 48%; visibility: visible; opacity: 1; transition: visibility 0s, top .5s, opacity .5s linear; } -#create-item-title { +.form-title { font-size: 2em; font-weight: bold; margin-top: -10px; } -#create-item-form-container { +.form-container { display: flex; flex: 1; flex-direction: row; } -.create-item-form-column { +.form-column { flex: 1; padding: 20px; } -.create-item-form-column input { +.form-column input { margin-bottom: 20px; } -#create-item-button-container { +.form-button-container { display: flex; flex-direction: row; align-items: center; @@ -189,7 +189,7 @@ nav { gap: 20px; } -.button { +form .button { padding: 10px 20px; text-decoration: none; border: 2px solid; @@ -200,15 +200,15 @@ nav { cursor: pointer; } -.button:hover { +form .button:hover { background-color: #ddd; } -#create-item-close-button { +.form-close-button { color: initial; } -#create-item-save-button { +.form-save-button { color: blue; border-color: blue; } diff --git a/static/js/script.js b/static/js/script.js index bb495c7..db5f811 100644 --- a/static/js/script.js +++ b/static/js/script.js @@ -1,34 +1,61 @@ -document.addEventListener("keyup", (event) => { - if (event.key === "Escape") { - window.location.hash = ''; - } -}); - function on_load() { - let start_input = document.getElementById("create-item-form-start"); - let due_input = document.getElementById("create-item-form-due"); + let create_start_input = document.getElementById("create-item-start"); + let create_due_input = document.getElementById("create-item-due"); let create_form = document.getElementById("create-item"); create_form.addEventListener("htmx:configRequest", function(evt) { - evt.detail.parameters["start"] = start_input.value ? start_input.valueAsNumber / 1000 : 0; - evt.detail.parameters["due"] = due_input.value ? due_input.valueAsNumber / 1000 : 0; + evt.detail.parameters["start"] = create_start_input.value ? create_start_input.valueAsNumber / 1000 : 0; + evt.detail.parameters["due"] = create_due_input.value ? create_due_input.valueAsNumber / 1000 : 0; }); create_form.addEventListener("htmx:afterRequest", function(evt) { if (evt.detail.successful) { window.location.hash = ''; - evt.detail.elt.reset(); + create_form.reset(); + } + }); + + let edit_start_input = document.getElementById("edit-item-start"); + let edit_due_input = document.getElementById("edit-item-due"); + + let edit_form = document.getElementById("edit-item"); + + edit_form.addEventListener("htmx:configRequest", function(evt) { + let target = document.getElementById(edit_form.getAttribute("data-id")); + + evt.detail.parameters["id"] = parseInt(target.getAttribute("data-idnum")); + evt.detail.parameters["start"] = edit_start_input.value ? edit_start_input.valueAsNumber / 1000 : 0; + evt.detail.parameters["due"] = edit_due_input.value ? edit_due_input.valueAsNumber / 1000 : 0; + }); + + edit_form.addEventListener("htmx:afterRequest", function(evt) { + if (evt.detail.successful) { + window.location.hash = ''; + edit_form.reset(); + } + }); + + edit_form.addEventListener("htmx:beforeSwap", function(evt) { + let target = document.getElementById(edit_form.getAttribute("data-id")); + if (target === null || target === undefined) { + evt.detail.shouldSwap = false; + } else { + evt.detail.target = target; } }); document.querySelector("#overdue-list > .new-item").addEventListener("htmx:oobBeforeSwap", function(evt) { let overdue_items = document.querySelector("#overdue-list > .todo-list-items"); let due = parseInt(evt.detail.fragment.firstElementChild.getAttribute("data-due")); + let id = parseInt(evt.detail.fragment.firstElementChild.getAttribute("data-idnum")); let target = overdue_items.children[overdue_items.children.length - 1]; for (let i = 1; i < overdue_items.children.length; i++) { - if (parseInt(overdue_items.children[i].getAttribute("data-due")) > due) { + let other_due = parseInt(today_items.children[i].getAttribute("data-due")); + let other_id = parseInt(today_items.children[i].getAttribute("data-idnum")); + + if (other_due > due || (other_due === due && other_id > id)) { target = overdue_items.children[i - 1]; break; } @@ -42,14 +69,16 @@ function on_load() { document.querySelector("#today-list > .new-item").addEventListener("htmx:oobBeforeSwap", function(evt) { let today_items = document.querySelector("#today-list > .todo-list-items"); let due = parseInt(evt.detail.fragment.firstElementChild.getAttribute("data-due")); + let id = parseInt(evt.detail.fragment.firstElementChild.getAttribute("data-idnum")); let target = today_items.children[today_items.children.length - 1]; - if (due !== 0) { - for (let i = 1; i < today_items.children.length; i++) { - if (parseInt(today_items.children[i].getAttribute("data-due")) > due) { - target = today_items.children[i - 1]; - break; - } + for (let i = 1; i < today_items.children.length; i++) { + let other_due = parseInt(today_items.children[i].getAttribute("data-due")); + let other_id = parseInt(today_items.children[i].getAttribute("data-idnum")); + + if ((other_due > due && due !== 0) || (other_due === due && other_id > id)) { + target = today_items.children[i - 1]; + break; } } @@ -61,10 +90,14 @@ function on_load() { document.querySelector("#upcoming-list > .new-item").addEventListener("htmx:oobBeforeSwap", function(evt) { let upcoming_items = document.querySelector("#upcoming-list > .todo-list-items"); let start = parseInt(evt.detail.fragment.firstElementChild.getAttribute("data-start")); + let id = parseInt(evt.detail.fragment.firstElementChild.getAttribute("data-idnum")); let target = upcoming_items.children[0]; for (let i = 1; i < upcoming_items.children.length; i++) { - if (parseInt(upcoming_items.children[i].getAttribute("data-start")) > start) { + let other_start = parseInt(today_items.children[i].getAttribute("data-start")); + let other_id = parseInt(today_items.children[i].getAttribute("data-idnum")); + + if (other_start > start || (other_start === start && other_id > id)) { target = upcoming_items.children[i - 1]; break; } @@ -77,9 +110,44 @@ function on_load() { } + if (document.readyState === "completed") { on_load(); } else { document.addEventListener("DOMContentLoaded", on_load); } + + +document.addEventListener("keyup", (event) => { + if (event.key === "Escape") { + window.location.hash = ''; + } +}); + + + +function edit(item_id) { + let item = document.getElementById(item_id); + let start = parseInt(item.getAttribute("data-start")); + let due = parseInt(item.getAttribute("data-due")); + let text = item.querySelector(".todo-text").textContent; + + let edit_form = document.getElementById("edit-item"); + document.getElementById("edit-item-name").value = text; + if (start != 0) { + document.getElementById("edit-item-start").valueAsNumber = start * 1000; + } else { + document.getElementById("edit-item-start").value = null; + } + if (due != 0) { + document.getElementById("edit-item-due").valueAsNumber = due * 1000; + } else { + document.getElementById("edit-item-due").value = null; + } + //document.getElementByid("edit-item-start").value = new Date(start * 100).toISOString().slice(0, 16); + //document.getElementByid("edit-item-due").value = new Date(due * 100).toISOString().slice(0, 16); + edit_form.setAttribute("data-id", item_id); + window.location.hash = "edit-item"; +} +