Add option to show/hide completed items
This commit is contained in:
parent
d14d1ec468
commit
e96bbdb0dd
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
test.db
|
||||
todo-web
|
||||
|
6
db/db.go
6
db/db.go
@ -102,7 +102,7 @@ func GetAllTodos() ([]types.Todo, error) {
|
||||
func GetOverdueTodos() ([]types.Todo, error) {
|
||||
var todos []types.Todo
|
||||
|
||||
rows, err := db.Query("SELECT * FROM items WHERE due < ? AND due IS NOT NULL ORDER BY due", time.Now().Unix())
|
||||
rows, err := db.Query("SELECT * FROM items WHERE due < ? AND due IS NOT NULL ORDER BY completed, due", time.Now().Unix())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -136,7 +136,7 @@ func GetTodayTodos() ([]types.Todo, error) {
|
||||
now := time.Now()
|
||||
year, month, day := now.Date()
|
||||
today := time.Date(year, month, day, 0, 0, 0, 0, time.Local)
|
||||
rows, err := db.Query("SELECT * FROM items WHERE (start <= ? OR start IS NULL) AND (due >= ? OR due IS NULL) ORDER BY due NULLS LAST", today.Unix(), now.Unix())
|
||||
rows, err := db.Query("SELECT * FROM items WHERE (start <= ? OR start IS NULL) AND (due >= ? OR due IS NULL) ORDER BY completed, due NULLS LAST", today.Unix(), now.Unix())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -169,7 +169,7 @@ func GetUpcomingTodos() ([]types.Todo, error) {
|
||||
|
||||
year, month, day := time.Now().Date()
|
||||
today := time.Date(year, month, day, 0, 0, 0, 0, time.Local)
|
||||
rows, err := db.Query("SELECT * FROM items WHERE start > ? ORDER BY start", today.Unix())
|
||||
rows, err := db.Query("SELECT * FROM items WHERE start > ? ORDER BY completed, start", today.Unix())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
7
main.go
7
main.go
@ -15,6 +15,7 @@ func main() {
|
||||
db_path := flag.String("db", "./test.db", "Path to the sqlite3 database")
|
||||
bind_port := flag.Int("p", 8080, "Port to bind to")
|
||||
bind_addr := flag.String("a", "0.0.0.0", "Address to bind to")
|
||||
static_dir := flag.String("static", "./static", "Path to static files")
|
||||
noFront := flag.Bool("no-frontend", false, "Disable the frontend endpoints")
|
||||
a := false; noBack := &a // flag.Bool("no-backend", false, "Disable the backend endpoints")
|
||||
|
||||
@ -28,7 +29,7 @@ func main() {
|
||||
}
|
||||
|
||||
if !*noFront {
|
||||
addFrontendEndpoints(mux)
|
||||
addFrontendEndpoints(mux, *static_dir)
|
||||
}
|
||||
|
||||
if !*noBack {
|
||||
@ -50,7 +51,7 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func addFrontendEndpoints(mux *http.ServeMux) {
|
||||
func addFrontendEndpoints(mux *http.ServeMux, static_path string) {
|
||||
fmt.Println("Frontend enabled")
|
||||
|
||||
mux.HandleFunc("/", Error404)
|
||||
@ -64,7 +65,7 @@ func addFrontendEndpoints(mux *http.ServeMux) {
|
||||
mux.HandleFunc("PUT /update", pages.UpdateItem)
|
||||
mux.HandleFunc("POST /new", pages.CreateItem)
|
||||
|
||||
fileServer := http.FileServer(http.Dir("./static"))
|
||||
fileServer := http.FileServer(http.Dir(static_path))
|
||||
mux.Handle("/css/", fileServer)
|
||||
mux.Handle("/js/", fileServer)
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
|
||||
templ RootPage() {
|
||||
<!Doctype HTML>
|
||||
<html lang="en-US">
|
||||
<html lang="en-US" data-show-completed="false">
|
||||
<head>
|
||||
<title>Todo</title>
|
||||
|
||||
@ -30,7 +30,10 @@ templ RootPage() {
|
||||
<a id="new-button" href="#create-item">New</a>
|
||||
</div>
|
||||
<div id="nav-center"></div>
|
||||
<div id="nav-right"></div>
|
||||
<div id="nav-right">
|
||||
<label for="show-completed">Show completed</label>
|
||||
<input id="show-completed" type="checkbox" name="show-completed"/>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
@ -32,7 +32,7 @@ func RootPage() templ.Component {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype HTML><html lang=\"en-US\"><head><title>Todo</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><link rel=\"stylesheet\" href=\"/css/styles.css\"><script src=\"/js/script.js\"></script><script src=\"/js/lib/htmx.min.js\"></script><!-- Font Awesome --><script src=\"https://kit.fontawesome.com/469cdddb31.js\" crossorigin=\"anonymous\"></script></head><body><nav><div id=\"nav-container\"><div id=\"nav-left\"><a id=\"new-button\" href=\"#create-item\">New</a></div><div id=\"nav-center\"></div><div id=\"nav-right\"></div></div></nav><div id=\"main-content\"><div id=\"lists\"><div id=\"overdue-list\" class=\"todo-list\"><div class=\"todo-list-title\">Overdue</div><div class=\"todo-list-items\" hx-get=\"/overdue\" hx-trigger=\"load\" hx-swap=\"outerHTML\"></div><div class=\"new-item\"></div></div><div id=\"today-list\" class=\"todo-list\"><div class=\"todo-list-title\">Today</div><div class=\"todo-list-items\" hx-get=\"/today\" hx-trigger=\"load\" hx-swap=\"outerHTML\"></div><div class=\"new-item\"></div></div><div id=\"upcoming-list\" class=\"todo-list\"><div class=\"todo-list-title\">Upcoming</div><div class=\"todo-list-items\" hx-get=\"/upcoming\" hx-trigger=\"load\" hx-swap=\"outerHTML\"></div><div class=\"new-item\"></div></div></div><form id=\"create-item\" hx-post=\"/new\" hx-swap=\"none\"><div class=\"form-title\">Create new Todo</div><div class=\"form-container\"><div class=\"form-column\"><label for=\"name\">Name</label><br><input id=\"create-item-name\" type=\"text\" name=\"name\"><br><label for=\"start\">Start</label><br><input id=\"create-item-start\" name=\"start\" type=\"datetime-local\"><br><label for=\"due\">Due</label><br><input id=\"create-item-due\" name=\"due\" type=\"datetime-local\"></div><div class=\"form-column\"></div></div><div class=\"form-button-container\"><button id=\"create-save\" class=\"form-save-button button\" type=\"submit\">Save</button> <a class=\"form-close-button button\" href=\"#\">Close</a></div></form><form id=\"edit-item\" data-id=\"\" hx-put=\"/update\" hx-swap=\"outerHTML\"><div class=\"form-title\">Edit Todo</div><div class=\"form-container\"><div class=\"form-column\"><label for=\"name\">Name</label><br><input id=\"edit-item-name\" type=\"text\" name=\"name\"><br><label for=\"start\">Start</label><br><input id=\"edit-item-start\" name=\"start\" type=\"datetime-local\"><br><label for=\"due\">Due</label><br><input id=\"edit-item-due\" name=\"due\" type=\"datetime-local\"></div><div class=\"form-column\"></div></div><div class=\"form-button-container\"><button id=\"edit-save\" class=\"form-save-button button\" type=\"submit\">Save</button> <a class=\"form-close-button button\" href=\"#\">Close</a></div></form></div></body></html>")
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype HTML><html lang=\"en-US\" data-show-completed=\"false\"><head><title>Todo</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><link rel=\"stylesheet\" href=\"/css/styles.css\"><script src=\"/js/script.js\"></script><script src=\"/js/lib/htmx.min.js\"></script><!-- Font Awesome --><script src=\"https://kit.fontawesome.com/469cdddb31.js\" crossorigin=\"anonymous\"></script></head><body><nav><div id=\"nav-container\"><div id=\"nav-left\"><a id=\"new-button\" href=\"#create-item\">New</a></div><div id=\"nav-center\"></div><div id=\"nav-right\"><label for=\"show-completed\">Show completed</label> <input id=\"show-completed\" type=\"checkbox\" name=\"show-completed\"></div></div></nav><div id=\"main-content\"><div id=\"lists\"><div id=\"overdue-list\" class=\"todo-list\"><div class=\"todo-list-title\">Overdue</div><div class=\"todo-list-items\" hx-get=\"/overdue\" hx-trigger=\"load\" hx-swap=\"outerHTML\"></div><div class=\"new-item\"></div></div><div id=\"today-list\" class=\"todo-list\"><div class=\"todo-list-title\">Today</div><div class=\"todo-list-items\" hx-get=\"/today\" hx-trigger=\"load\" hx-swap=\"outerHTML\"></div><div class=\"new-item\"></div></div><div id=\"upcoming-list\" class=\"todo-list\"><div class=\"todo-list-title\">Upcoming</div><div class=\"todo-list-items\" hx-get=\"/upcoming\" hx-trigger=\"load\" hx-swap=\"outerHTML\"></div><div class=\"new-item\"></div></div></div><form id=\"create-item\" hx-post=\"/new\" hx-swap=\"none\"><div class=\"form-title\">Create new Todo</div><div class=\"form-container\"><div class=\"form-column\"><label for=\"name\">Name</label><br><input id=\"create-item-name\" type=\"text\" name=\"name\"><br><label for=\"start\">Start</label><br><input id=\"create-item-start\" name=\"start\" type=\"datetime-local\"><br><label for=\"due\">Due</label><br><input id=\"create-item-due\" name=\"due\" type=\"datetime-local\"></div><div class=\"form-column\"></div></div><div class=\"form-button-container\"><button id=\"create-save\" class=\"form-save-button button\" type=\"submit\">Save</button> <a class=\"form-close-button button\" href=\"#\">Close</a></div></form><form id=\"edit-item\" data-id=\"\" hx-put=\"/update\" hx-swap=\"outerHTML\"><div class=\"form-title\">Edit Todo</div><div class=\"form-container\"><div class=\"form-column\"><label for=\"name\">Name</label><br><input id=\"edit-item-name\" type=\"text\" name=\"name\"><br><label for=\"start\">Start</label><br><input id=\"edit-item-start\" name=\"start\" type=\"datetime-local\"><br><label for=\"due\">Due</label><br><input id=\"edit-item-due\" name=\"due\" type=\"datetime-local\"></div><div class=\"form-column\"></div></div><div class=\"form-button-container\"><button id=\"edit-save\" class=\"form-save-button button\" type=\"submit\">Save</button> <a class=\"form-close-button button\" href=\"#\">Close</a></div></form></div></body></html>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -75,7 +75,7 @@ func TodoItem(item types.Todo) templ.Component {
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("item-%d", item.Id))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 113, Col: 45}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 116, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -88,7 +88,7 @@ func TodoItem(item types.Todo) templ.Component {
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", item.Id))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 113, Col: 105}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 116, Col: 105}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -101,7 +101,7 @@ func TodoItem(item types.Todo) templ.Component {
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", item.Start))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 113, Col: 150}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 116, Col: 150}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -114,7 +114,7 @@ func TodoItem(item types.Todo) templ.Component {
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", item.Due))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 113, Col: 191}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 116, Col: 191}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -137,7 +137,7 @@ func TodoItem(item types.Todo) templ.Component {
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(string(templ.URL(fmt.Sprintf("/set/%d", item.Id))))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 114, Col: 137}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 117, Col: 137}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -150,7 +150,7 @@ func TodoItem(item types.Todo) templ.Component {
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(item.Text)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 115, Col: 42}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 118, Col: 42}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -163,7 +163,7 @@ func TodoItem(item types.Todo) templ.Component {
|
||||
var templ_7745c5c3_Var9 string
|
||||
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/delete/%d", item.Id))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 118, Col: 101}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 121, Col: 101}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -202,7 +202,7 @@ func OobTodoItem(targetSelector string, item types.Todo) templ.Component {
|
||||
var templ_7745c5c3_Var11 string
|
||||
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%s:%s", "afterend", targetSelector))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 124, Col: 71}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 127, Col: 71}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -249,7 +249,7 @@ func TodoList(fillerText string, items []types.Todo) templ.Component {
|
||||
var templ_7745c5c3_Var13 string
|
||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", len(items)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 130, Col: 80}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 133, Col: 80}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
@ -262,7 +262,7 @@ func TodoList(fillerText string, items []types.Todo) templ.Component {
|
||||
var templ_7745c5c3_Var14 string
|
||||
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(fillerText)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 131, Col: 45}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `pages/templates/root.templ`, Line: 134, Col: 45}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
|
@ -13,6 +13,7 @@ nav {
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#new-button {
|
||||
@ -107,6 +108,11 @@ nav {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
[data-show-completed="false"]
|
||||
.todo-item:has(input[type="checkbox"]:checked) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.todo-text {
|
||||
flex: 1;
|
||||
text-wrap: nowrap;
|
||||
|
@ -66,6 +66,11 @@ function on_load() {
|
||||
overdue_items.setAttribute("data-item-count", parseInt(overdue_items.getAttribute("data-item-count")) + 1);
|
||||
});
|
||||
|
||||
let checkbox = document.getElementById("show-completed");
|
||||
checkbox.addEventListener("change", function(evt) {
|
||||
document.documentElement.setAttribute("data-show-completed", checkbox.checked);
|
||||
});
|
||||
|
||||
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"));
|
||||
|
Loading…
Reference in New Issue
Block a user