Skip to content
Snippets Groups Projects
Commit 0eb2ae48 authored by gbe's avatar gbe
Browse files

Add template for detailed view

parent a94b81f9
No related branches found
No related tags found
No related merge requests found
package main
import (
"errors"
"html/template"
"log"
"net/http"
"strconv"
)
var detailsTemplate = template.Must(template.ParseFS(templateFS, "templates/base.tpl", "templates/details.tpl"))
func (h Handler) details(w http.ResponseWriter, r *http.Request) {
log.Println("handling", r.Method, r.URL, "from", r.RemoteAddr)
if r.Method != "GET" {
httpError(w, "bad method", errors.New(r.Method), http.StatusMethodNotAllowed)
return
}
args := r.URL.Query()
log.Println("args", args)
if args.Get("id") == "" {
httpError(w, "bad request", errors.New("empty id"), http.StatusBadRequest)
return
}
id, err := strconv.Atoi(args.Get("id"))
if err != nil {
httpError(w, "can't parse id", err, http.StatusBadRequest)
return
}
v, err := LoadVino(r.Context(), h.db, id, false)
if err != nil {
httpError(w, "can't load wine data", err, http.StatusInternalServerError)
return
}
data := struct {
Wine Vino
}{v}
err = detailsTemplate.ExecuteTemplate(w, "details.tpl", data)
if err != nil {
log.Println("can't execute details template:", err)
return
}
}
package main
import (
"errors"
"fmt"
"html/template"
"io/ioutil"
"log"
"net/http"
"strconv"
)
var indexTemplate = template.Must(template.ParseFS(templateFS, "templates/base.tpl", "templates/index.tpl"))
func (h Handler) index(w http.ResponseWriter, r *http.Request) {
log.Println("handling", r.Method, r.URL, "from", r.RemoteAddr)
if r.Method == "GET" {
wines, err := ListWines(r.Context(), h.db)
if err != nil {
httpError(w, "can't list wines", err, http.StatusInternalServerError)
return
}
w.Header().Add("content-type", "text/html")
data := struct {
Wines []Vino
}{wines}
err = indexTemplate.ExecuteTemplate(w, "index.tpl", data)
if err != nil {
log.Println("can't execute index template:", err)
}
return
}
if r.Method != "POST" {
httpError(w, "invalid method", errors.New(r.Method), http.StatusMethodNotAllowed)
return
}
name := r.FormValue("name")
if name == "" {
httpError(w, "bad name", errors.New("empty or missing"), http.StatusBadRequest)
return
}
if len(name) > 80 {
httpError(w, "bad name", errors.New("name too long, max length is 80"), http.StatusBadRequest)
return
}
ratingVal := r.FormValue("rating")
var (
rating int
err error
)
if ratingVal != "" {
rating, err = strconv.Atoi(ratingVal)
if err != nil {
httpError(w, "can't convert rating", err, http.StatusBadRequest)
return
}
}
picFile, picHdr, err := r.FormFile("picture")
if err != nil {
httpError(w, "can't open picture file", err, http.StatusInternalServerError)
return
}
defer picFile.Close()
log.Println("picture", picHdr.Size, picHdr.Header.Get("Content-Type"))
picData, err := ioutil.ReadAll(picFile)
if err != nil {
httpError(w, "can't read picture file", err, http.StatusInternalServerError)
return
}
vino := Vino{
Name: name,
Rating: rating,
Picture: picData,
}
err = vino.Store(r.Context(), h.db)
if err != nil {
httpError(w, fmt.Sprintf("can't store wine %q", vino), err, http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/", http.StatusSeeOther) // TODO: Is this the correct status?
}
......@@ -2,7 +2,6 @@ package main
import (
"embed"
"html/template"
"log"
"net/http"
......@@ -12,11 +11,14 @@ import (
//go:embed templates/*.tpl
var templateFS embed.FS
var templates = template.Must(template.ParseFS(templateFS, "templates/base.tpl", "templates/index.tpl"))
//go:embed static/*
var staticFS embed.FS
type Handler struct {
db *sqlx.DB
}
func httpError(w http.ResponseWriter, msg string, err error, status int) {
log.Println(msg+":", err)
http.Error(w, msg+": "+err.Error(), status)
......
{{ define "base" }}
<!doctype html>
<html>
<head>
<style type="text/css">
span.todo {
background: red;
}
</style>
<title>In Vino Veritas {{ block "title" . }}{{ end }}</title>
</head>
<body>
{{ block "content" . }}
TODO
{{ end }}
</body>
</html>
{{ end }}
\ No newline at end of file
{{ template "base" . }}
{{ define "title" }}Details for {{ .Wine.Name }}{{ end }}
{{ define "content" }}
{{ with .Wine }}
{{ .ID }} - {{ .Name }} - {{ .Rating }}
{{ end }}
{{ end }}
\ No newline at end of file
......@@ -19,7 +19,7 @@
{{ range .Wines }}
<tr>
<td>{{ .ID }}</td>
<td><a href="/details/{{ .ID }}">{{ .Name }}</a></td>
<td><a href="/details?id={{ .ID }}">{{ .Name }}</a></td>
<td>{{ .Rating }}</td>
</tr>
{{ end }}
......
......@@ -16,6 +16,29 @@ type Vino struct {
Picture []byte `db:"picture"`
}
func LoadVino(ctx context.Context, db *sqlx.DB, id int, withImage bool) (Vino, error) {
cols := []string{"rowid", "name", "rating"}
if withImage {
cols = append(cols, "picture")
}
query, args, err := squirrel.Select(cols...).
From("wines").
Where(squirrel.Eq{"rowid": id}).
ToSql()
if err != nil {
return Vino{}, err
}
var v Vino
err = db.GetContext(ctx, &v, query, args...)
if err != nil {
return v, err
}
return v, nil
}
func (v Vino) String() string {
return fmt.Sprintf("{Name: %q, Rating: %d}", v.Name, v.Rating)
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment