aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--assets/main.css70
-rw-r--r--ideas.html97
-rw-r--r--main.go98
-rw-r--r--t_idea/edit.html38
-rw-r--r--t_idea/index.html59
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>
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 @@
+<!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>