Compare commits
No commits in common. "839483242813e35e14a702c88c35f610ce6d48d7" and "0ca6de59fd15a5fa9585783de7bfd134a7e5cafa" have entirely different histories.
8394832428
...
0ca6de59fd
@ -2,9 +2,9 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go-udemy-web-1/internal/config"
|
||||
"go-udemy-web-1/internal/handlers"
|
||||
"go-udemy-web-1/internal/render"
|
||||
"go-udemy-web-1/pkg/config"
|
||||
"go-udemy-web-1/pkg/handlers"
|
||||
"go-udemy-web-1/pkg/render"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
@ -1,8 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"go-udemy-web-1/internal/config"
|
||||
"go-udemy-web-1/internal/handlers"
|
||||
"go-udemy-web-1/pkg/config"
|
||||
"go-udemy-web-1/pkg/handlers"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
@ -22,11 +22,8 @@ func routes(app *config.AppConfig) http.Handler {
|
||||
mux.Get("/contact", handlers.Repo.Contact)
|
||||
mux.Get("/generals-quarters", handlers.Repo.Generals)
|
||||
mux.Get("/majors-suite", handlers.Repo.Majors)
|
||||
mux.Get("/availability", handlers.Repo.Availability)
|
||||
mux.Post("/availability", handlers.Repo.PostAvailability)
|
||||
mux.Post("/availability-json", handlers.Repo.AvailabilityJSON)
|
||||
mux.Get("/reservation", handlers.Repo.Reservation)
|
||||
mux.Get("/make-reservation", handlers.Repo.MakeReservation)
|
||||
mux.Post("/make-reservation", handlers.Repo.PostMakeReservation)
|
||||
|
||||
fileServer := http.FileServer(http.Dir("./static/"))
|
||||
mux.Handle("/static/*", http.StripPrefix("/static", fileServer))
|
||||
|
@ -1,18 +0,0 @@
|
||||
package forms
|
||||
|
||||
type errors map[string][]string
|
||||
|
||||
// Add adds an error message for a given form field
|
||||
func (e errors) Add(field, message string) {
|
||||
e[field] = append(e[field], message)
|
||||
}
|
||||
|
||||
// Get returns the first error message
|
||||
func (e errors) Get(field string) string {
|
||||
es := e[field]
|
||||
|
||||
if len(es) == 0 {
|
||||
return ""
|
||||
}
|
||||
return es[0]
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package forms
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// Form creates a custom form struct, embeds a url.Values object
|
||||
type Form struct {
|
||||
url.Values
|
||||
Errors errors
|
||||
}
|
||||
|
||||
// New initializes a form struct
|
||||
func New(data url.Values) *Form {
|
||||
return &Form{
|
||||
data,
|
||||
errors(map[string][]string{}),
|
||||
}
|
||||
}
|
||||
|
||||
// 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 != ""
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go-udemy-web-1/internal/config"
|
||||
"go-udemy-web-1/internal/forms"
|
||||
"go-udemy-web-1/internal/models"
|
||||
"go-udemy-web-1/internal/render"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Repo the repository used by the handlers
|
||||
var Repo *Repository
|
||||
|
||||
// Repository is the repository type
|
||||
type Repository struct {
|
||||
App *config.AppConfig
|
||||
}
|
||||
|
||||
// NewRepo creates a new repository
|
||||
func NewRepo(a *config.AppConfig) *Repository {
|
||||
return &Repository{
|
||||
App: a,
|
||||
}
|
||||
}
|
||||
|
||||
// NewHandlers sets the repository for the handlers
|
||||
func NewHandlers(r *Repository) {
|
||||
Repo = r
|
||||
}
|
||||
|
||||
// Home is the home page handler
|
||||
func (m *Repository) Home(w http.ResponseWriter, r *http.Request) {
|
||||
remoteIP := r.RemoteAddr
|
||||
m.App.Session.Put(r.Context(), "remote_ip", remoteIP)
|
||||
|
||||
render.RenderTemplate(w, r, "home.page.tmpl", &models.TemplateData{})
|
||||
}
|
||||
|
||||
// About is the about page handler
|
||||
func (m *Repository) About(w http.ResponseWriter, r *http.Request) {
|
||||
// perform some logic
|
||||
stringMap := make(map[string]string)
|
||||
stringMap["test"] = "Hello world!"
|
||||
|
||||
remoteIP := m.App.Session.GetString(r.Context(), "remote_ip")
|
||||
stringMap["remote_ip"] = remoteIP
|
||||
|
||||
// send the data to the template
|
||||
render.RenderTemplate(w, r, "about.page.tmpl", &models.TemplateData{StringMap: stringMap})
|
||||
}
|
||||
|
||||
// Contact is the contact page handler
|
||||
func (m *Repository) Contact(w http.ResponseWriter, r *http.Request) {
|
||||
render.RenderTemplate(w, r, "contact.page.tmpl", &models.TemplateData{})
|
||||
}
|
||||
|
||||
// Generals is the General's Quarters page handler
|
||||
func (m *Repository) Generals(w http.ResponseWriter, r *http.Request) {
|
||||
render.RenderTemplate(w, r, "generals.page.tmpl", &models.TemplateData{})
|
||||
}
|
||||
|
||||
// Majors is the Major's Suite page handler
|
||||
func (m *Repository) Majors(w http.ResponseWriter, r *http.Request) {
|
||||
render.RenderTemplate(w, r, "majors.page.tmpl", &models.TemplateData{})
|
||||
}
|
||||
|
||||
// MakeReservation is the make reservation page handler
|
||||
func (m *Repository) MakeReservation(w http.ResponseWriter, r *http.Request) {
|
||||
render.RenderTemplate(w, r, "make-reservation.page.tmpl", &models.TemplateData{
|
||||
Form: forms.New(nil),
|
||||
})
|
||||
}
|
||||
|
||||
// PostMakeReservation is the make reservation page post handler
|
||||
func (m *Repository) PostMakeReservation(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Availability is the search for availability page handler
|
||||
func (m *Repository) Availability(w http.ResponseWriter, r *http.Request) {
|
||||
render.RenderTemplate(w, r, "availability.page.tmpl", &models.TemplateData{})
|
||||
}
|
||||
|
||||
// PostAvailability is the search for availability page handler
|
||||
func (m *Repository) PostAvailability(w http.ResponseWriter, r *http.Request) {
|
||||
start := r.Form.Get("start")
|
||||
end := r.Form.Get("end")
|
||||
fmt.Fprintf(w, "Posted to search availability from %s to %s", start, end)
|
||||
}
|
||||
|
||||
type responseJSON struct {
|
||||
OK string `json:"ok"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// AvailabilityJSON is the search for availability page handler
|
||||
func (m *Repository) AvailabilityJSON(w http.ResponseWriter, r *http.Request) {
|
||||
resp := responseJSON{
|
||||
OK: "true",
|
||||
Message: "Available!",
|
||||
}
|
||||
|
||||
out, err := json.MarshalIndent(resp, "", " ")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
w.Write(out)
|
||||
}
|
74
pkg/handlers/handlers.go
Normal file
74
pkg/handlers/handlers.go
Normal file
@ -0,0 +1,74 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"go-udemy-web-1/pkg/config"
|
||||
"go-udemy-web-1/pkg/models"
|
||||
"go-udemy-web-1/pkg/render"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Repo the repository used by the handlers
|
||||
var Repo *Repository
|
||||
|
||||
// Repository is the repository type
|
||||
type Repository struct {
|
||||
App *config.AppConfig
|
||||
}
|
||||
|
||||
// NewRepo creates a new repository
|
||||
func NewRepo(a *config.AppConfig) *Repository {
|
||||
return &Repository{
|
||||
App: a,
|
||||
}
|
||||
}
|
||||
|
||||
// NewHandlers sets the repository for the handlers
|
||||
func NewHandlers(r *Repository) {
|
||||
Repo = r
|
||||
}
|
||||
|
||||
// Home is the home page handler
|
||||
func (m *Repository) Home(w http.ResponseWriter, r *http.Request) {
|
||||
remoteIP := r.RemoteAddr
|
||||
m.App.Session.Put(r.Context(), "remote_ip", remoteIP)
|
||||
|
||||
render.RenderTemplate(w, "home.page.tmpl", &models.TemplateData{})
|
||||
}
|
||||
|
||||
// About is the about page handler
|
||||
func (m *Repository) About(w http.ResponseWriter, r *http.Request) {
|
||||
// perform some logic
|
||||
stringMap := make(map[string]string)
|
||||
stringMap["test"] = "Hello world!"
|
||||
|
||||
remoteIP := m.App.Session.GetString(r.Context(), "remote_ip")
|
||||
stringMap["remote_ip"] = remoteIP
|
||||
|
||||
// send the data to the template
|
||||
render.RenderTemplate(w, "about.page.tmpl", &models.TemplateData{StringMap: stringMap})
|
||||
}
|
||||
|
||||
// Contact is the contact page handler
|
||||
func (m *Repository) Contact(w http.ResponseWriter, r *http.Request) {
|
||||
render.RenderTemplate(w, "contact.page.tmpl", &models.TemplateData{})
|
||||
}
|
||||
|
||||
// Generals is the General's Quarters page handler
|
||||
func (m *Repository) Generals(w http.ResponseWriter, r *http.Request) {
|
||||
render.RenderTemplate(w, "generals.page.tmpl", &models.TemplateData{})
|
||||
}
|
||||
|
||||
// Majors is the Major's Suite page handler
|
||||
func (m *Repository) Majors(w http.ResponseWriter, r *http.Request) {
|
||||
render.RenderTemplate(w, "majors.page.tmpl", &models.TemplateData{})
|
||||
}
|
||||
|
||||
// MakeReservation is the make reservation page handler
|
||||
func (m *Repository) MakeReservation(w http.ResponseWriter, r *http.Request) {
|
||||
render.RenderTemplate(w, "make-reservation.page.tmpl", &models.TemplateData{})
|
||||
}
|
||||
|
||||
// MakeReservation is the make reservation page handler
|
||||
func (m *Repository) Reservation(w http.ResponseWriter, r *http.Request) {
|
||||
render.RenderTemplate(w, "reservation.page.tmpl", &models.TemplateData{})
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
package models
|
||||
|
||||
import "go-udemy-web-1/internal/forms"
|
||||
|
||||
// TemplateData holds data sent from handlers to templates
|
||||
type TemplateData struct {
|
||||
StringMap map[string]string
|
||||
@ -12,5 +10,4 @@ type TemplateData struct {
|
||||
Flash string
|
||||
Warning string
|
||||
Error string
|
||||
Form *forms.Form
|
||||
}
|
@ -2,14 +2,12 @@ package render
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go-udemy-web-1/internal/config"
|
||||
"go-udemy-web-1/internal/models"
|
||||
"go-udemy-web-1/pkg/config"
|
||||
"go-udemy-web-1/pkg/models"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/justinas/nosurf"
|
||||
)
|
||||
|
||||
var app *config.AppConfig
|
||||
@ -20,13 +18,12 @@ func NewTemplates(a *config.AppConfig) {
|
||||
}
|
||||
|
||||
// AddDefaultData adds default template data
|
||||
func AddDefaultData(td *models.TemplateData, r *http.Request) *models.TemplateData {
|
||||
td.CSRFToken = nosurf.Token(r)
|
||||
func AddDefaultData(td *models.TemplateData) *models.TemplateData {
|
||||
return td
|
||||
}
|
||||
|
||||
// RenderTemplate renders a HTML template file
|
||||
func RenderTemplate(w http.ResponseWriter, r *http.Request, tmpl string, td *models.TemplateData) {
|
||||
func RenderTemplate(w http.ResponseWriter, tmpl string, td *models.TemplateData) {
|
||||
var tc map[string]*template.Template
|
||||
if app.UseCache {
|
||||
// get the template cache from the app config
|
||||
@ -45,7 +42,7 @@ func RenderTemplate(w http.ResponseWriter, r *http.Request, tmpl string, td *mod
|
||||
// written successfully
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
td = AddDefaultData(td, r)
|
||||
td = AddDefaultData(td)
|
||||
|
||||
err := t.Execute(buf, td)
|
||||
if err != nil {
|
@ -45,7 +45,7 @@
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/availability">Book Now</a>
|
||||
<a class="nav-link" href="/reservation">Book Now</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/contact">Contact</a>
|
||||
@ -177,21 +177,25 @@
|
||||
title = "",
|
||||
} = c;
|
||||
|
||||
const {value: result} = await Swal.fire({
|
||||
const {value: formValues} = await Swal.fire({
|
||||
title: title,
|
||||
html: msg,
|
||||
backdrop: false,
|
||||
focusConfirm: false,
|
||||
showCancelButton: true,
|
||||
willOpen: () => {
|
||||
if (c.willOpen !== undefined) {
|
||||
c.willOpen();
|
||||
}
|
||||
console.log("test")
|
||||
const elem = document.getElementById('reservation-dates-modal')
|
||||
const rp = new DateRangePicker(elem, {
|
||||
"format": "yyyy-mm-dd",
|
||||
showOnFocus: true,
|
||||
});
|
||||
},
|
||||
didOpen: () => {
|
||||
if (c.didOpen !== undefined) {
|
||||
c.didOpen();
|
||||
}
|
||||
return [
|
||||
document.getElementById('start').removeAttribute("disabled"),
|
||||
document.getElementById('end').removeAttribute("disabled"),
|
||||
]
|
||||
},
|
||||
preConfirm: () => {
|
||||
return [
|
||||
@ -201,14 +205,8 @@
|
||||
},
|
||||
})
|
||||
|
||||
if (c.callback !== undefined) {
|
||||
if (result &&
|
||||
result.dismiss !== Swal.DismissReason.cancel &&
|
||||
result !== "") {
|
||||
c.callback(result);
|
||||
} else {
|
||||
c.callback(false);
|
||||
}
|
||||
if (formValues) {
|
||||
Swal.fire(JSON.stringify(formValues))
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,6 +218,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
// document.getElementById("dummy").addEventListener('click', () => {
|
||||
// notify("This is a message", "success");
|
||||
// // notifyModal("title", "text", "success", "confirm");
|
||||
// // Prompt().toast({msg: "Prompt Test"})
|
||||
// Prompt().success({msg: "Success!"})
|
||||
// // Prompt().error({msg: "Ooops"})
|
||||
//
|
||||
// let html = `
|
||||
// <form action="reservation.html" method="get" novalidate class="needs-validation">
|
||||
// <div id="reservation-dates-modal" class="row">
|
||||
// <div class="col mb-3">
|
||||
// <input disabled required type="text" class="form-control" name="start" id="start" placeholder="Arrival">
|
||||
// </div>
|
||||
// <div class="col mb-3">
|
||||
// <input disabled required type="text" class="form-control" name="end" id="end" placeholder="Departure">
|
||||
// </div>
|
||||
// </div>
|
||||
// </form>
|
||||
// `;
|
||||
// Prompt().custom({title: "Choose your dates", msg: html})
|
||||
// });
|
||||
</script>
|
||||
|
||||
{{block "js" .}}
|
||||
|
@ -23,60 +23,8 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="d-flex justify-content-center py-3">
|
||||
<button id="check-availability" class="btn btn-success">Check Availability</button>
|
||||
<a href="/make-reservation-gq" class="btn btn-success">Check Availability</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{define "js"}}
|
||||
<script>
|
||||
document.getElementById("check-availability").addEventListener('click', () => {
|
||||
let html = `
|
||||
<form id="check-availability-form" action="/availability-json" method="post" novalidate class="needs-validation">
|
||||
<div id="reservation-dates-modal" class="row">
|
||||
<div class="col mb-3">
|
||||
<input disabled required type="text" class="form-control" name="start" id="start" placeholder="Arrival">
|
||||
</div>
|
||||
<div class="col mb-3">
|
||||
<input disabled required type="text" class="form-control" name="end" id="end" placeholder="Departure">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
`;
|
||||
Prompt().custom({
|
||||
title: "Choose your dates",
|
||||
msg: html,
|
||||
willOpen: () => {
|
||||
const elem = document.getElementById('reservation-dates-modal')
|
||||
const rp = new DateRangePicker(elem, {
|
||||
"format": "yyyy-mm-dd",
|
||||
showOnFocus: true,
|
||||
});
|
||||
},
|
||||
didOpen: () => {
|
||||
return [
|
||||
document.getElementById('start').removeAttribute("disabled"),
|
||||
document.getElementById('end').removeAttribute("disabled"),
|
||||
]
|
||||
},
|
||||
callback: (result) => {
|
||||
console.log(result);
|
||||
|
||||
const formElem = document.getElementById("check-availability-form");
|
||||
let formData = new FormData(formElem);
|
||||
formData.append("csrf_token", "{{.CSRFToken}}");
|
||||
|
||||
fetch('/availability-json', {
|
||||
method: "post",
|
||||
body: formData,
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log(data);
|
||||
})
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
|
@ -23,27 +23,8 @@
|
||||
|
||||
<div class="row">
|
||||
<div class="d-flex justify-content-center py-3">
|
||||
<button id="check-availability" class="btn btn-success">Check Availability</button>
|
||||
<a href="/make-reservation-ms" class="btn btn-success">Check Availability</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{define "js"}}
|
||||
<script>
|
||||
document.getElementById("check-availability").addEventListener('click', () => {
|
||||
let html = `
|
||||
<form action="reservation.html" method="get" novalidate class="needs-validation">
|
||||
<div id="reservation-dates-modal" class="row">
|
||||
<div class="col mb-3">
|
||||
<input disabled required type="text" class="form-control" name="start" id="start" placeholder="Arrival">
|
||||
</div>
|
||||
<div class="col mb-3">
|
||||
<input disabled required type="text" class="form-control" name="end" id="end" placeholder="Departure">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
`;
|
||||
Prompt().custom({title: "Choose your dates", msg: html})
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
|
@ -5,9 +5,7 @@
|
||||
<div class="col">
|
||||
<h1 class="text-center mt-3">Make reservation</h1>
|
||||
|
||||
<!-- <form method="post" action="" class="needs-validation" novalidate> -->
|
||||
<form method="post" action="" class="" novalidate>
|
||||
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
||||
<form method="post" action="" class="needs-validation" novalidate>
|
||||
<div class="form-group mt-5">
|
||||
<label for="first_name">First name:</label>
|
||||
<input type="text" name="first_name" id="first_name" class="form-control" required
|
||||
|
@ -7,8 +7,7 @@
|
||||
<div class="col-md-6">
|
||||
<h1 class="text-center mt-5">Search for Availability</h1>
|
||||
|
||||
<form action="/availability" method="post" novalidate class="needs-validation">
|
||||
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
|
||||
<form action="reservation.html" method="get" novalidate class="needs-validation">
|
||||
<div id="reservation-dates" class="row">
|
||||
<div class="col mb-3">
|
||||
<input required type="text" class="form-control" name="start" placeholder="Arrival">
|
Loading…
x
Reference in New Issue
Block a user