From 07ace53ba2423f1b7e62477549ba62a30e79ff47 Mon Sep 17 00:00:00 2001 From: Raymaekers Luca Date: Sun, 29 Sep 2024 13:10:56 +0200 Subject: Added edit functionality Added a new edit functionality and page with styling. Other minor changes: - Added assets folder served under /static/ - Created a new template edit.html - grouped ideas.html and edit.html under t_idea - Changed router to use mux and organized routes - Added more checks on empty titles - Added javascript to t_idea/index.html for intuitive button functionality --- assets/main.css | 70 +++++++++++++++++++++++++++++++++++++++ ideas.html | 97 ------------------------------------------------------ main.go | 98 +++++++++++++++++++++++++++++++++++++++---------------- t_idea/edit.html | 38 +++++++++++++++++++++ t_idea/index.html | 59 +++++++++++++++++++++++++++++++++ 5 files changed, 236 insertions(+), 126 deletions(-) create mode 100644 assets/main.css delete mode 100644 ideas.html create mode 100644 t_idea/edit.html create mode 100644 t_idea/index.html diff --git a/assets/main.css b/assets/main.css new file mode 100644 index 0000000..f6869bb --- /dev/null +++ b/assets/main.css @@ -0,0 +1,70 @@ +body { + margin-left: 1em; +} +.idea { + border: solid 2px black; + border-radius: 5px; + padding: 4px; + margin-bottom: 1em; + width: 50%; +} +.title { + margin-left: 1em; + margin-bottom: 0; + display: inline; +} +.rating { + display: inline; + margin-left: 1em; +} +.text { + margin-left: .5em; + margin-top: .5em; + font-family: monospace; + font-size: 1.25em; +} +.creation { + margin-bottom: .3em; +} +.author { + font-style: italic; +} +.date { + color: #bcbcbc; +} +.error { + color: red; +} +.idea form { + display: inline; +} +textarea[name="text"] { + border: solid 2px black; + border-radius: 5px; + margin-top: .3em; + margin-bottom: .3em; + resize: none; +} +h3 { + margin-bottom: 0; +} +input[type="text"]{ + border-radius: 3px; + border: solid 1px black; +} +input[name="author"] { + margin-bottom: .3em; +} +a.edit { + padding: 1px 6px; + border: 1px buttonborder solid black; + border-radius: 3px; + color: buttontext; + background-color: buttonface; + text-decoration: none; + /*color: black;*/ + /*border: solid 2px black;*/ + /*padding: 4px;*/ + /*border-radius: 2px;*/ + /*text-decoration: none;*/ +} diff --git a/ideas.html b/ideas.html deleted file mode 100644 index de8aa2b..0000000 --- a/ideas.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - Ideas - - - - {{ with .Error }} -

{{.}}

- {{ end}} -

Add an idea:

-
-
- -
-
- -
-
- - - - diff --git a/main.go b/main.go index 329fc34..b8aef22 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( _ "embed" "encoding/gob" "errors" + "fmt" "html/template" "io" "log" @@ -23,21 +24,25 @@ var Version string // layout for how the date should be output in html var DateLayout string = "02/01/2006 on 15:04" -// template for /ideas -// -//go:embed ideas.html -var ideas_html string +// template for ideas html +var ( + //go:embed t_idea/index.html + ideas_html string + //go:embed t_idea/edit.html + idea_edit_html string +) var Ideas []Idea // ToDo's -// - [x] Add a post -// - [x] Remove a post -// - [ ] work with funcmaps in templates +// - [ ] Create a Server out of this so you can run multiple instances // - [ ] Put a reaction on a post +// - [x] edit a post // - [x] Store ideas to a file (encoder/gob) // - [x] Change the date format printing // - [x] outsource removing the posts to a separate cli tool +// - [x] Add a post +// - [x] Remove a post // Represents an idea // CreatedAt is a formatted date string @@ -157,21 +162,27 @@ func main() { if err != nil { log.Fatalln(err) } + _, err = tmpl.New("edit").Parse(idea_edit_html) + if err != nil { + log.Fatalln(err) + } // TODO (Luca): Make the app more interactive by using websockets instead, // such that adding, editing or commenting on an idea does not require to // refresh the page. // Another approach would be to use htmx? + // - http.HandleFunc("/ideas/", func(w http.ResponseWriter, r *http.Request) { + mux := http.NewServeMux() + + fs := http.FileServer(http.Dir("assets/")) + mux.Handle("/static/", http.StripPrefix("/static/", fs)) + + mux.HandleFunc("GET /ideas/", func(w http.ResponseWriter, r *http.Request) { tmpl.Execute(w, PageData{Ideas, ""}) }) - http.HandleFunc("/create/", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Redirect(w, r, "/ideas/", http.StatusMovedPermanently) - return - } + mux.HandleFunc("POST /idea/create/", func(w http.ResponseWriter, r *http.Request) { i := Idea{ Title: r.FormValue("title"), Author: r.FormValue("author"), @@ -195,20 +206,53 @@ func main() { http.Redirect(w, r, "/ideas/", http.StatusMovedPermanently) }) - http.HandleFunc("/edit/", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Redirect(w, r, "/ideas/", http.StatusMovedPermanently) + mux.HandleFunc("GET /idea/edit/", func(w http.ResponseWriter, r *http.Request) { + t := r.URL.Query().Get("t") + if t == "" { + tmpl.Execute(w, PageData{Ideas, "You must provide a title."}) return } - http.Redirect(w, r, "/ideas/", http.StatusMovedPermanently) + + for _, i := range Ideas { + if i.Title == t { + tmpl.ExecuteTemplate(w, "edit", i) + return + } + } + tmpl.Execute(w, PageData{Ideas, "No idea with title '" + t + "'."}) }) - http.HandleFunc("/delete/", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Redirect(w, r, "/ideas/", http.StatusMovedPermanently) + mux.HandleFunc("POST /idea/edit/", func(w http.ResponseWriter, r *http.Request) { + t := r.FormValue("title") + if t == "" { + tmpl.Execute(w, PageData{Ideas, "You must provide a title."}) return } + + var i *Idea + for j := range Ideas { + if Ideas[j].Title == t { + i = &Ideas[j] + break + } + } + if i.Title == "" { + tmpl.Execute(w, PageData{Ideas, "No idea with title '" + t + "'."}) + } + + i.Title = r.FormValue("title") + i.Text = r.FormValue("text") + log.Printf("Edited '%s'\n", i.Title) + + http.Redirect(w, r, "/ideas/", http.StatusMovedPermanently) + }) + + mux.HandleFunc("POST /idea/delete/", func(w http.ResponseWriter, r *http.Request) { t := r.FormValue("title") + if t == "" { + tmpl.Execute(w, PageData{Ideas, "You must provide a title."}) + return + } for i, v := range Ideas { if t == v.Title { log.Println("Deleted:", v.Title) @@ -217,19 +261,15 @@ func main() { return } } - tmpl.Execute(w, PageData{Ideas, "No idea with name '" + t + "'."}) + tmpl.Execute(w, PageData{Ideas, "No idea with title '" + t + "'."}) }) - http.HandleFunc("/comment/", func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Redirect(w, r, "/ideas/", http.StatusMovedPermanently) - return - } - http.Redirect(w, r, "/ideas/", http.StatusMovedPermanently) + mux.HandleFunc("POST /comment/create/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Not implemented yet.") }) - log.Println("Listening on http://localhost:8080") - err = http.ListenAndServe(":8080", nil) + log.Println("Listening on http://localhost:8080/ideas") + err = http.ListenAndServe(":8080", mux) if err != nil { log.Fatalln(err) } diff --git a/t_idea/edit.html b/t_idea/edit.html new file mode 100644 index 0000000..cae7bd2 --- /dev/null +++ b/t_idea/edit.html @@ -0,0 +1,38 @@ + + + + + + + Editing "{{.Title}}" + + + +

Editing "{{.Title}}"

+
+
+
+
+ +

by {{.Author}} on {{.CreatedAt}}

+ +
+
+ +
+
+ + diff --git a/t_idea/index.html b/t_idea/index.html new file mode 100644 index 0000000..b219ba3 --- /dev/null +++ b/t_idea/index.html @@ -0,0 +1,59 @@ + + + + + + + Ideas + + + + {{ with .Error }} +

{{.}}

+ {{ end}} +

Add an idea:

+
+
+ +
+
+ +
+
+ + + + + -- cgit v1.2.3