Compare commits
No commits in common. "dc7ece7d1280734abbf5e332346494ea822887ce" and "839483242813e35e14a702c88c35f610ce6d48d7" have entirely different histories.
dc7ece7d12
...
8394832428
@ -1,11 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"go-udemy-web-1/internal/config"
|
||||
"go-udemy-web-1/internal/handlers"
|
||||
"go-udemy-web-1/internal/models"
|
||||
"go-udemy-web-1/internal/render"
|
||||
"log"
|
||||
"net/http"
|
||||
@ -23,9 +21,6 @@ var (
|
||||
|
||||
// main is the main application function
|
||||
func main() {
|
||||
// what am I going to put in the session
|
||||
gob.Register(models.Reservation{})
|
||||
|
||||
// change this to true when in production
|
||||
app.InProduction = false
|
||||
|
||||
|
@ -27,7 +27,6 @@ func routes(app *config.AppConfig) http.Handler {
|
||||
mux.Post("/availability-json", handlers.Repo.AvailabilityJSON)
|
||||
mux.Get("/make-reservation", handlers.Repo.MakeReservation)
|
||||
mux.Post("/make-reservation", handlers.Repo.PostMakeReservation)
|
||||
mux.Get("/reservation-summary", handlers.Repo.ReservationSummary)
|
||||
|
||||
fileServer := http.FileServer(http.Dir("./static/"))
|
||||
mux.Handle("/static/*", http.StripPrefix("/static", fileServer))
|
||||
|
4
go.mod
4
go.mod
@ -2,12 +2,8 @@ module go-udemy-web-1
|
||||
|
||||
go 1.21.0
|
||||
|
||||
// github.com/CloudyKit/jet --> Check this later
|
||||
|
||||
require github.com/go-chi/chi/v5 v5.0.14
|
||||
|
||||
require github.com/justinas/nosurf v1.1.1
|
||||
|
||||
require github.com/alexedwards/scs/v2 v2.8.0
|
||||
|
||||
require github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
|
||||
|
2
go.sum
2
go.sum
@ -1,7 +1,5 @@
|
||||
github.com/alexedwards/scs/v2 v2.8.0 h1:h31yUYoycPuL0zt14c0gd+oqxfRwIj6SOjHdKRZxhEw=
|
||||
github.com/alexedwards/scs/v2 v2.8.0/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/go-chi/chi/v5 v5.0.14 h1:PyEwo2Vudraa0x/Wl6eDRRW2NXBvekgfxyydcM0WGE0=
|
||||
github.com/go-chi/chi/v5 v5.0.14/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/justinas/nosurf v1.1.1 h1:92Aw44hjSK4MxJeMSyDa7jwuI9GR2J/JCQiaKvXXSlk=
|
||||
|
@ -1,12 +1,8 @@
|
||||
package forms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/asaskevich/govalidator"
|
||||
)
|
||||
|
||||
// Form creates a custom form struct, embeds a url.Values object
|
||||
@ -23,41 +19,8 @@ func New(data url.Values) *Form {
|
||||
}
|
||||
}
|
||||
|
||||
// Required checks required fields
|
||||
func (f *Form) Required(fields ...string) {
|
||||
for _, field := range fields {
|
||||
value := f.Get(field)
|
||||
if strings.TrimSpace(value) == "" {
|
||||
f.Errors.Add(field, "This field cannot be blank")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Has checks if form field is in post and not emtpy
|
||||
func (f *Form) Has(field string, r *http.Request) bool {
|
||||
x := r.Form.Get(field)
|
||||
return x != ""
|
||||
}
|
||||
|
||||
// Valid returns true if there are no errors, otherwise false
|
||||
func (f *Form) Valid() bool {
|
||||
return len(f.Errors) == 0
|
||||
}
|
||||
|
||||
// MinLength checks for string minimum length
|
||||
func (f *Form) MinLength(field string, length int, r *http.Request) bool {
|
||||
value := r.Form.Get(field)
|
||||
if len(value) < length {
|
||||
f.Errors.Add(field, fmt.Sprintf("This field must have at least %d letters", length))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsEmail checks the email address
|
||||
func (f *Form) IsEmail(field string, r *http.Request) {
|
||||
value := r.Form.Get(field)
|
||||
if !govalidator.IsEmail(value) {
|
||||
f.Errors.Add(field, "Invalid email address")
|
||||
}
|
||||
return x != ""
|
||||
}
|
||||
|
@ -69,73 +69,13 @@ func (m *Repository) Majors(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// MakeReservation is the make reservation page handler
|
||||
func (m *Repository) MakeReservation(w http.ResponseWriter, r *http.Request) {
|
||||
// For the first time render emptyReservation so that this object is
|
||||
// filled with the info when sent back.
|
||||
var emptyReservation models.Reservation
|
||||
data := make(map[string]interface{})
|
||||
|
||||
data["reservation"] = emptyReservation
|
||||
|
||||
render.RenderTemplate(w, r, "make-reservation.page.tmpl", &models.TemplateData{
|
||||
Form: forms.New(nil),
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// PostMakeReservation is the make reservation page post handler
|
||||
func (m *Repository) PostMakeReservation(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
reservation := models.Reservation{
|
||||
FirstName: r.Form.Get("first_name"),
|
||||
LastName: r.Form.Get("last_name"),
|
||||
Email: r.Form.Get("email"),
|
||||
Phone: r.Form.Get("phone"),
|
||||
}
|
||||
|
||||
form := forms.New(r.PostForm)
|
||||
|
||||
form.Required("first_name", "last_name", "email")
|
||||
form.MinLength("first_name", 2, r)
|
||||
form.IsEmail("email", r)
|
||||
|
||||
if !form.Valid() {
|
||||
data := make(map[string]interface{})
|
||||
data["reservation"] = reservation
|
||||
|
||||
render.RenderTemplate(w, r, "make-reservation.page.tmpl", &models.TemplateData{
|
||||
Data: data,
|
||||
Form: form,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
m.App.Session.Put(r.Context(), "reservation", reservation)
|
||||
|
||||
http.Redirect(w, r, "/reservation-summary", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// ReservationSummary is the reservation summary page handler
|
||||
func (m *Repository) ReservationSummary(w http.ResponseWriter, r *http.Request) {
|
||||
reservation, ok := m.App.Session.Get(r.Context(), "reservation").(models.Reservation)
|
||||
if !ok {
|
||||
log.Println("connot get item from reservation")
|
||||
m.App.Session.Put(r.Context(), "error", "Can't get reservation from session")
|
||||
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
|
||||
}
|
||||
|
||||
m.App.Session.Remove(r.Context(), "reservation")
|
||||
|
||||
data := make(map[string]interface{})
|
||||
data["reservation"] = reservation
|
||||
|
||||
render.RenderTemplate(w, r, "reservation-summary.page.tmpl", &models.TemplateData{
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
// Availability is the search for availability page handler
|
||||
|
@ -1,9 +0,0 @@
|
||||
package models
|
||||
|
||||
// Reservation holds Reservation data
|
||||
type Reservation struct {
|
||||
FirstName string
|
||||
LastName string
|
||||
Email string
|
||||
Phone string
|
||||
}
|
@ -21,9 +21,6 @@ func NewTemplates(a *config.AppConfig) {
|
||||
|
||||
// AddDefaultData adds default template data
|
||||
func AddDefaultData(td *models.TemplateData, r *http.Request) *models.TemplateData {
|
||||
td.Flash = app.Session.PopString(r.Context(), "flash")
|
||||
td.Warning = app.Session.PopString(r.Context(), "warning")
|
||||
td.Error = app.Session.PopString(r.Context(), "error")
|
||||
td.CSRFToken = nosurf.Token(r)
|
||||
return td
|
||||
}
|
||||
|
@ -116,16 +116,6 @@
|
||||
})
|
||||
}
|
||||
|
||||
{{with .Error}}
|
||||
notify("{{.}}", "error")
|
||||
{{end}}
|
||||
{{with .Warning}}
|
||||
notify("{{.}}", "warning")
|
||||
{{end}}
|
||||
{{with .Flash}}
|
||||
notify("{{.}}", "flash")
|
||||
{{end}}
|
||||
|
||||
// Prompt is out Javascript module for all alerts, notifications, and custom popup dialogs
|
||||
function Prompt() {
|
||||
let toast = function (c) {
|
||||
|
@ -5,38 +5,25 @@
|
||||
<div class="col">
|
||||
<h1 class="text-center mt-3">Make reservation</h1>
|
||||
|
||||
{{$res := index .Data "reservation"}}
|
||||
<!-- <form method="post" action="" class="needs-validation" novalidate> -->
|
||||
<form method="post" action="" class="" novalidate>
|
||||
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
||||
|
||||
<div class="form-group mt-5">
|
||||
<label for="first_name">First name:</label>
|
||||
{{with .Form.Errors.Get "first_name"}}
|
||||
<label class="text-danger">{{.}}</label>
|
||||
{{end}}
|
||||
<input type="text" name="first_name" id="first_name" class="form-control {{with .Form.Errors.Get "first_name"}} is-invalid {{end}}"
|
||||
value="{{$res.FirstName}}" required autocomplete="off">
|
||||
<input type="text" name="first_name" id="first_name" class="form-control" required
|
||||
autocomplete="off">
|
||||
</div>
|
||||
<div class="form-group mt-5">
|
||||
<label for="last_name">Last name:</label>
|
||||
{{with .Form.Errors.Get "last_name"}}
|
||||
<label class="text-danger">{{.}}</label>
|
||||
{{end}}
|
||||
<input type="text" name="last_name" id="last_name" class="form-control {{with .Form.Errors.Get "last_name"}} is-invalid {{end}}"
|
||||
value="{{$res.LastName}}" required autocomplete="off">
|
||||
<input type="text" name="last_name" id="last_name" class="form-control" required autocomplete="off">
|
||||
</div>
|
||||
<div class="form-group mt-5">
|
||||
<label for="email">Email:</label>
|
||||
{{with .Form.Errors.Get "email"}}
|
||||
<label class="text-danger">{{.}}</label>
|
||||
{{end}}
|
||||
<input type="email" name="email" id="email" class="form-control {{with .Form.Errors.Get "email"}} is-invalid {{end}}"
|
||||
value="{{$res.Email}}" required autocomplete="off">
|
||||
<input type="email" name="email" id="email" class="form-control" required autocomplete="off">
|
||||
</div>
|
||||
<div class="form-group mt-5">
|
||||
<label for="phone">Phone number:</label>
|
||||
<input type="text" name="phone" id="phone" class="form-control" value="{{$res.Phone}}" autocomplete="off">
|
||||
<input type="text" name="phone" id="phone" class="form-control" required autocomplete="off">
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
@ -1,39 +0,0 @@
|
||||
{{template "base" .}}
|
||||
{{define "content"}}
|
||||
{{$res := index .Data "reservation"}}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col mt-5">
|
||||
<h1>Reservation Summary</h1>
|
||||
|
||||
<hr>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Name: </td>
|
||||
<td>{{$res.LastName}} {{$res.FirstName}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Arrival: </td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Departure: </td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Email: </td>
|
||||
<td>{{$res.Email}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Phone: </td>
|
||||
<td>{{$res.Phone}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
Loading…
x
Reference in New Issue
Block a user