diff --git a/main.go b/main.go index 0b64c42..d61b407 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,9 @@ import ( "html/template" "io" "log" + "math/rand" "net/http" + "strconv" ) //go:embed views/*.html @@ -40,6 +42,19 @@ type Todo struct { type ViewModel struct { Todos []Todo + Only string +} + +func filterSlice[T any](xs []T, pred func(T) bool) []T { + result := make([]T, 0, len(xs)) + for _, element := range xs { + if pred(element) { + result = append(result, element) + } + + } + + return result } func main() { @@ -48,8 +63,21 @@ func main() { t := New() http.Handle("/public/", http.FileServer(http.FS(publicFS))) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + q := r.URL.Query() + renderTodos := todos + only := q.Get("only") + + if only == "active" { + renderTodos = filterSlice(todos, func(todo Todo) bool { return !todo.Completed }) + } + + if only == "completed" { + renderTodos = filterSlice(todos, func(todo Todo) bool { return todo.Completed }) + } + vm := ViewModel{ - Todos: todos, + Todos: renderTodos, + Only: only, } err := t.Render(w, "list.html", vm) if err != nil { @@ -69,12 +97,36 @@ func main() { title := r.Form.Get("title") todos = append(todos, Todo{ + Id: strconv.Itoa(rand.Int()), Title: title, }) w.Header().Set("Location", "/") w.WriteHeader(http.StatusSeeOther) }) + + http.HandleFunc("/toggle", func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + if err := r.ParseForm(); err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + + id := r.Form.Get("id") + for i, todo := range todos { + if todo.Id == id { + todo.Completed = !todo.Completed + todos[i] = todo + } + } + + w.Header().Set("Location", "/") + w.WriteHeader(http.StatusSeeOther) + }) log.Print("Running on 8080") log.Fatal(http.ListenAndServe(":8080", nil)) } diff --git a/public/todo-app.css b/public/todo-app.css index fcc3da5..0c21271 100644 --- a/public/todo-app.css +++ b/public/todo-app.css @@ -192,6 +192,11 @@ body { appearance: none; } +.todo-list li .toggle > button { + width: 40px; + height: 40px; +} + .todo-list li .toggle { opacity: 0; } @@ -206,7 +211,7 @@ body { background-position: center left; } -.todo-list li .toggle:checked + label { +.todo-list li.completed .toggle + label { background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%2359A193%22%20stroke-width%3D%223%22%2F%3E%3Cpath%20fill%3D%22%233EA390%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22%2F%3E%3C%2Fsvg%3E'); } diff --git a/views/_task.html b/views/_task.html index 8827738..d12d0a0 100644 --- a/views/_task.html +++ b/views/_task.html @@ -1,6 +1,11 @@