diff options
-rw-r--r-- | assets/main.css | 70 | ||||
-rw-r--r-- | ideas.html | 97 | ||||
-rw-r--r-- | main.go | 98 | ||||
-rw-r--r-- | t_idea/edit.html | 38 | ||||
-rw-r--r-- | t_idea/index.html | 59 |
5 files changed, 236 insertions, 126 deletions
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 @@ -<!DOCTYPE html> -<html lang="en"> -<head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>Ideas</title> - <style> - -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; -} -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; -} - - </style> -</head> -<body> - {{ with .Error }} - <p class="error">{{.}}</p> - {{ end}} - <h3>Add an idea:</h3> - <form action="/create/" method="post"> - <input name="title" type="text" placeholder="Title" required></br> - <textarea name="text" cols=50 rows=4 placeholder="Write what is in your lightbulb here." required></textarea> - </br> - <input name="author" type="text" placeholder="Your Name" required></br> - <input type="submit" value="Think"> - </form> - <hr> - <ul> - {{ range .Ideas }} - <div class="idea"> - <h2 class="title">{{.Title}}</h2> - <p class="text">{{.Text}}</p> - <p class="creation">by <span class="author">{{.Author}}</span> on <span class="date">{{.CreatedAt}}</span></p> - <form action="/delete/" method="post"> - <input type="hidden" name="title" value="{{.Title}}"> - <input type="submit" value="delete"> - </form> - </div> - {{ else }} - <p><i>No ideas here... Be the first one to think!</i></p> - {{ end }} - </ul> - <!--template--> -</body> -</html> @@ -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 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" href="/static/main.css"> + <title>Editing "{{.Title}}"</title> + <style> +textarea[name="text"] { + color: #4c4c4c; + border: dashed 1px black; + font-size: 1.25em; + font-family: monospace; +} +input[name="title"] { + color: #4c4c4c; + border: dashed 1px black; + font-weight: bold; + font-size: 2em; +} + </style> + </head> + <body> + <h3>Editing "{{.Title}}"</h3> + <hr> + <div class="idea"> + <form action="/idea/edit/" method="post"> + <input name="title" type="text" value="{{.Title}}"><br> + <textarea name="text" cols=80 rows=4 required>{{.Text}}</textarea> + <p class="creation">by <span class="author">{{.Author}}</span> on <span class="date">{{.CreatedAt}}</span></p> + <input type="submit" value="confirm"> + </form> + <form action="/ideas/" method="get"> + <input type="submit" value="cancel"> + </form> + </div> + </body> +</html> 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 @@ +<!DOCTYPE html> + <html lang="en"> + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" href="/static/main.css"> + <title>Ideas</title> + + </head> + <body> + {{ with .Error }} + <p class="error">{{.}}</p> + {{ end}} + <h3>Add an idea:</h3> + <form action="/idea/create/" method="post"> + <input name="title" type="text" placeholder="Title" required></br> + <textarea name="text" cols=50 rows=4 placeholder="Write what is in your lightbulb here." required></textarea> + </br> + <input name="author" type="text" placeholder="Your Name" required></br> + <input type="submit" value="Think"> + </form> + <hr> + <ul> + {{ range .Ideas }} + <div class="idea"> + <h2 class="title">{{.Title}}</h2> + <p class="text">{{.Text}}</p> + <p class="creation">by <span class="author">{{.Author}}</span> on <span class="date">{{.CreatedAt}}</span></p> + <form action="/idea/delete/" method="post"> + <input type="hidden" name="title" value="{{.Title}}"> + <input type="submit" value="delete"> + </form> + <button class="edit" data-title="{{.Title}}">edit</button> + </div> + {{ else }} + <p><i>No ideas here... Be the first one to think!</i></p> + {{ end }} + </ul> + <!--template--> + </body> + <script> + let dels = document.querySelectorAll("form[action=\"/idea/delete/\"]") + for (el of dels) { + el.addEventListener("submit", function(e) { + e.preventDefault() + if (confirm("are you sure?") === true) { + this.submit() + } + }) + } + let eels = document.querySelectorAll("button.edit") + for (el of eels) { + el.onclick = function(e) { + location.href = "/idea/edit?t=" + el.getAttribute("data-title") + } + } + + </script> +</html> |