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>  | 
