package main import ( "embed" "html/template" "io" "log" "math/rand" "net/http" "strconv" ) //go:embed views/*.html var tmplFS embed.FS //go:embed public/* var publicFS embed.FS type Template struct { templates *template.Template } func New() *Template { templates := template.Must(template.New("").ParseFS(tmplFS, "views/*.html")) return &Template{ templates: templates, } } func (t *Template) Render(w io.Writer, name string, data any) error { tmpl := template.Must(t.templates.Clone()) tmpl = template.Must(tmpl.ParseFS(tmplFS, "views/"+name)) return tmpl.ExecuteTemplate(w, "layout.html", data) } type Todo struct { Id string Title string Completed bool } 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() { todos := make([]Todo, 0, 10) 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: renderTodos, Only: only, } err := t.Render(w, "list.html", vm) if err != nil { log.Println(err) } }) http.HandleFunc("/todo", 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 } 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)) }