Added dynamic support for countries

This commit is contained in:
Borna Rajković 2023-08-05 21:34:09 +02:00
parent 381444a838
commit 47b9939ead
17 changed files with 296 additions and 69 deletions

View File

@ -229,6 +229,7 @@ section#results a, section#results button {
color: #000; color: #000;
cursor: pointer; cursor: pointer;
} }
dialog { dialog {
position: fixed; position: fixed;
left: 50%; left: 50%;

View File

@ -1,14 +1,34 @@
CREATE TABLE IF NOT EXISTS "country"
(
id uuid,
iso_name char(2) UNIQUE NOT NULL,
name varchar(45) NOT NULL,
primary key (id)
);
CREATE TABLE IF NOT EXISTS "holiday" CREATE TABLE IF NOT EXISTS "holiday"
( (
id uuid, id uuid,
country char(2), country char(2) NOT NULL,
date date, date date NOT NULL,
name varchar(64), name varchar(64) NOT NULL,
description varchar(512), description varchar(512),
is_state boolean, is_state boolean NOT NULL,
is_religious boolean is_religious boolean NOT NULL,
primary key (id),
constraint fk_country_id foreign key (country)
references country(iso_name) on delete cascade on update cascade
); );
INSERT INTO "country" (id, iso_name, name)
VALUES
('096ca6c4-5c04-47a4-0063-4b4cc6f6b671', 'HR', 'Croatia'),
('096ca6c4-5c04-47a4-0063-4b4cc6f6b672', 'US', 'USA'),
('096ca6c4-5c04-47a4-0063-4b4cc6f6b673', 'FR', 'France'),
('096ca6c4-5c04-47a4-0063-4b4cc6f6b674', 'GB', 'Great Britain');
INSERT INTO "holiday" (id, country, date, name, description, is_state, is_religious) INSERT INTO "holiday" (id, country, date, name, description, is_state, is_religious)
VALUES VALUES
('096ca6c4-5c04-47a4-b363-4b4cc6f6b671', 'HR', '2023-01-01', 'Nova godina', '', true, false), ('096ca6c4-5c04-47a4-b363-4b4cc6f6b671', 'HR', '2023-01-01', 'Nova godina', '', true, false),

View File

@ -10,7 +10,7 @@ import (
"time" "time"
) )
func getHolidays(service holiday.Service) gin.HandlerFunc { func getHolidays(service holiday.HolidayService) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
paging := holiday.Paging{PageSize: 50} paging := holiday.Paging{PageSize: 50}
if err := c.ShouldBindQuery(&paging); err != nil { if err := c.ShouldBindQuery(&paging); err != nil {

View File

@ -0,0 +1,40 @@
package holiday
import (
"github.com/google/uuid"
"github.com/jmoiron/sqlx"
)
type CountryService struct {
DB *sqlx.DB
}
func (s *CountryService) FindById(id uuid.UUID) (Country, error) {
var country Country
return country, s.DB.Get(&country, `SELECT * FROM "country" WHERE "id" = $1;`, id)
}
func (s *CountryService) Find() ([]Country, error) {
var countries []Country
return countries, s.DB.Select(&countries, `SELECT * FROM "country";`)
}
func (s *CountryService) Update(country Country) (Country, error) {
_, err := s.DB.Exec(`UPDATE country SET "iso_name" = $2, "name" = $3 WHERE "id" = $1`,
&country.Id, &country.IsoName, &country.Name,
)
return country, err
}
func (s *CountryService) Create(country Country) (Country, error) {
country.Id = uuid.Must(uuid.NewRandom())
_, err := s.DB.Exec(`INSERT INTO country (id, iso_name, name) values ($1, $2, $3)`,
&country.Id, &country.IsoName, &country.Name,
)
return country, err
}
func (s *CountryService) Delete(id uuid.UUID) error {
_, err := s.DB.Exec(`DELETE FROM country WHERE "id" = $1`, &id)
return err
}

View File

@ -8,11 +8,11 @@ import (
"time" "time"
) )
type Service struct { type HolidayService struct {
DB *sqlx.DB DB *sqlx.DB
} }
func (s *Service) Find(search Search, paging Paging) ([]Holiday, error) { func (s *HolidayService) Find(search Search, paging Paging) ([]Holiday, error) {
var holidays []Holiday var holidays []Holiday
var err error var err error
if search.Date != nil { if search.Date != nil {
@ -30,32 +30,32 @@ func (s *Service) Find(search Search, paging Paging) ([]Holiday, error) {
return s.paginate(holidays, paging), err return s.paginate(holidays, paging), err
} }
func (s *Service) FindById(id uuid.UUID) (Holiday, error) { func (s *HolidayService) FindById(id uuid.UUID) (Holiday, error) {
var holiday Holiday var holiday Holiday
return holiday, s.DB.Get(&holiday, `SELECT * FROM "holiday" WHERE "id" = $1;`, id) return holiday, s.DB.Get(&holiday, `SELECT * FROM "holiday" WHERE "id" = $1;`, id)
} }
func (s *Service) findByDate(date time.Time, isState *bool, isReligious *bool, country string) ([]Holiday, error) { func (s *HolidayService) findByDate(date time.Time, isState *bool, isReligious *bool, country string) ([]Holiday, error) {
var holidays []Holiday var holidays []Holiday
return holidays, s.DB.Select(&holidays, `SELECT * FROM "holiday" WHERE "date" = $1 AND country = $2 `+s.filter(isState, isReligious)+";", date, country) return holidays, s.DB.Select(&holidays, `SELECT * FROM "holiday" WHERE "date" = $1 AND country = $2 `+s.filter(isState, isReligious)+";", date, country)
} }
func (s *Service) findForRange(rangeStart time.Time, rangeEnd time.Time, isState *bool, isReligious *bool, country string) ([]Holiday, error) { func (s *HolidayService) findForRange(rangeStart time.Time, rangeEnd time.Time, isState *bool, isReligious *bool, country string) ([]Holiday, error) {
var holidays []Holiday var holidays []Holiday
return holidays, s.DB.Select(&holidays, `SELECT * FROM "holiday" WHERE "date" BETWEEN $1 AND $2 AND country = $3`+s.filter(isState, isReligious)+";", rangeStart, rangeEnd, country) return holidays, s.DB.Select(&holidays, `SELECT * FROM "holiday" WHERE "date" BETWEEN $1 AND $2 AND country = $3`+s.filter(isState, isReligious)+";", rangeStart, rangeEnd, country)
} }
func (s *Service) findByYear(year int, isState *bool, isReligious *bool, country string) ([]Holiday, error) { func (s *HolidayService) findByYear(year int, isState *bool, isReligious *bool, country string) ([]Holiday, error) {
var holidays []Holiday var holidays []Holiday
return holidays, s.DB.Select(&holidays, `SELECT * FROM "holiday" WHERE extract(year from "date") = $1 AND country = $2 `+s.filter(isState, isReligious)+";", year, country) return holidays, s.DB.Select(&holidays, `SELECT * FROM "holiday" WHERE extract(year from "date") = $1 AND country = $2 `+s.filter(isState, isReligious)+";", year, country)
} }
func (s *Service) find(isState *bool, isReligious *bool, country string) ([]Holiday, error) { func (s *HolidayService) find(isState *bool, isReligious *bool, country string) ([]Holiday, error) {
var holidays []Holiday var holidays []Holiday
return holidays, s.DB.Select(&holidays, `SELECT * FROM "holiday" WHERE country = $1 `+s.filter(isState, isReligious)+";", country) return holidays, s.DB.Select(&holidays, `SELECT * FROM "holiday" WHERE country = $1 `+s.filter(isState, isReligious)+";", country)
} }
func (s *Service) paginate(holidays []Holiday, paging Paging) []Holiday { func (s *HolidayService) paginate(holidays []Holiday, paging Paging) []Holiday {
start := paging.Page * paging.PageSize start := paging.Page * paging.PageSize
end := (paging.Page + 1) * paging.PageSize end := (paging.Page + 1) * paging.PageSize
if end < len(holidays) { if end < len(holidays) {
@ -67,7 +67,7 @@ func (s *Service) paginate(holidays []Holiday, paging Paging) []Holiday {
} }
} }
func (s *Service) filter(isState *bool, isReligious *bool) string { func (s *HolidayService) filter(isState *bool, isReligious *bool) string {
var filters []string var filters []string
if isState != nil { if isState != nil {
filters = append(filters, "is_state = "+strconv.FormatBool(*isState)) filters = append(filters, "is_state = "+strconv.FormatBool(*isState))
@ -82,14 +82,14 @@ func (s *Service) filter(isState *bool, isReligious *bool) string {
} }
} }
func (s *Service) Update(holiday Holiday) (Holiday, error) { func (s *HolidayService) Update(holiday Holiday) (Holiday, error) {
_, err := s.DB.Exec(`UPDATE holiday SET "country" = $1, "name" = $2, "description" = $3, "date" = $4, "is_state"=$5, "is_religious"=$6 WHERE "id" = $7`, _, err := s.DB.Exec(`UPDATE holiday SET "country" = $1, "name" = $2, "description" = $3, "date" = $4, "is_state"=$5, "is_religious"=$6 WHERE "id" = $7`,
&holiday.Country, &holiday.Name, &holiday.Description, &holiday.Date, &holiday.IsStateHoliday, &holiday.IsReligiousHoliday, &holiday.Id, &holiday.Country, &holiday.Name, &holiday.Description, &holiday.Date, &holiday.IsStateHoliday, &holiday.IsReligiousHoliday, &holiday.Id,
) )
return holiday, err return holiday, err
} }
func (s *Service) Create(holiday Holiday) (Holiday, error) { func (s *HolidayService) Create(holiday Holiday) (Holiday, error) {
holiday.Id = uuid.Must(uuid.NewRandom()) holiday.Id = uuid.Must(uuid.NewRandom())
_, err := s.DB.Exec(`INSERT INTO holiday (id, country, name, description, date, is_state, is_religious) values ($1, $2, $3, $4, $5, $6, $7)`, _, err := s.DB.Exec(`INSERT INTO holiday (id, country, name, description, date, is_state, is_religious) values ($1, $2, $3, $4, $5, $6, $7)`,
&holiday.Id, &holiday.Country, &holiday.Name, &holiday.Description, &holiday.Date, &holiday.IsStateHoliday, &holiday.IsReligiousHoliday, &holiday.Id, &holiday.Country, &holiday.Name, &holiday.Description, &holiday.Date, &holiday.IsStateHoliday, &holiday.IsReligiousHoliday,
@ -97,7 +97,7 @@ func (s *Service) Create(holiday Holiday) (Holiday, error) {
return holiday, err return holiday, err
} }
func (s *Service) Delete(id uuid.UUID) error { func (s *HolidayService) Delete(id uuid.UUID) error {
_, err := s.DB.Exec(`DELETE FROM holiday WHERE "id" = $1`, &id) _, err := s.DB.Exec(`DELETE FROM holiday WHERE "id" = $1`, &id)
return err return err
} }

View File

@ -15,6 +15,12 @@ type Holiday struct {
IsReligiousHoliday bool `db:"is_religious"` IsReligiousHoliday bool `db:"is_religious"`
} }
type Country struct {
Id uuid.UUID `db:"id"`
IsoName string `db:"iso_name"`
Name string `db:"name"`
}
type Paging struct { type Paging struct {
PageSize int `form:"page_size" binging:"min=0"` PageSize int `form:"page_size" binging:"min=0"`
Page int `form:"page" binging:"min=0"` Page int `form:"page" binging:"min=0"`

101
main.go
View File

@ -53,11 +53,12 @@ func main() {
loadTemplates(g) loadTemplates(g)
holidayService := holiday.Service{DB: client} holidayService := holiday.HolidayService{DB: client}
countryService := holiday.CountryService{DB: client}
g.GET("/api/v1/holidays", getHolidays(holidayService)) g.GET("/api/v1/holidays", getHolidays(holidayService))
setupAdminDashboard(g.Group("/admin"), holidayService) setupAdminDashboard(g.Group("/admin"), holidayService, countryService)
g.GET("/", func(c *gin.Context) { g.GET("/", func(c *gin.Context) {
year := time.Now().Year() year := time.Now().Year()
@ -67,10 +68,12 @@ func main() {
return return
} }
holidays, _ := holidayService.Find(search, holiday.Paging{PageSize: 100}) holidays, _ := holidayService.Find(search, holiday.Paging{PageSize: 100})
c.HTML(http.StatusOK, "index.gohtml", gin.H{"Search": search, "Holidays": mapHolidays(holidays).Holidays}) countries, _ := countryService.Find()
c.HTML(http.StatusOK, "index.gohtml", gin.H{"Countries": countries, "Search": search, "Holidays": mapHolidays(holidays).Holidays})
}) })
g.GET("/documentation", func(c *gin.Context) { g.GET("/documentation", func(c *gin.Context) {
c.HTML(http.StatusOK, "documentation.gohtml", nil) countries, _ := countryService.Find()
c.HTML(http.StatusOK, "documentation.gohtml", gin.H{"Countries": countries})
}) })
g.GET("/search", func(c *gin.Context) { g.GET("/search", func(c *gin.Context) {
request := holiday.Search{} request := holiday.Search{}
@ -80,10 +83,12 @@ func main() {
} }
search := holiday.Search{Country: request.Country, Date: request.Date} search := holiday.Search{Country: request.Country, Date: request.Date}
holidays, _ := holidayService.Find(search, holiday.Paging{PageSize: 100}) holidays, _ := holidayService.Find(search, holiday.Paging{PageSize: 100})
c.HTML(http.StatusOK, "search.gohtml", gin.H{"Search": search, "Holidays": mapHolidays(holidays).Holidays}) countries, _ := countryService.Find()
c.HTML(http.StatusOK, "search.gohtml", gin.H{"Countries": countries, "Search": search, "Holidays": mapHolidays(holidays).Holidays})
}) })
g.GET("/dialogs/check-is-a-holiday", func(c *gin.Context) { g.GET("/dialogs/check-is-a-holiday", func(c *gin.Context) {
c.HTML(http.StatusOK, "check-is-a-holiday.gohtml", gin.H{}) countries, _ := countryService.Find()
c.HTML(http.StatusOK, "check-is-a-holiday.gohtml", gin.H{"Countries": countries})
}) })
log.Fatal(http.ListenAndServe(":5281", g)) log.Fatal(http.ListenAndServe(":5281", g))
@ -112,15 +117,19 @@ func loadTemplates(g *gin.Engine) {
"templates/documentation.gohtml", "templates/documentation.gohtml",
"templates/admin_dashboard.gohtml", "templates/admin_dashboard.gohtml",
"templates/countries.gohtml",
"templates/dialogs/add-holiday.gohtml", "templates/dialogs/add-holiday.gohtml",
"templates/dialogs/edit-holiday.gohtml", "templates/dialogs/edit-holiday.gohtml",
"templates/dialogs/delete-holiday.gohtml", "templates/dialogs/delete-holiday.gohtml",
"templates/dialogs/check-is-a-holiday.gohtml", "templates/dialogs/check-is-a-holiday.gohtml",
"templates/dialogs/edit-country.gohtml",
"templates/dialogs/delete-country.gohtml",
) )
} }
func setupAdminDashboard(adminDashboard *gin.RouterGroup, service holiday.Service) { func setupAdminDashboard(adminDashboard *gin.RouterGroup, service holiday.HolidayService, countryService holiday.CountryService) {
adminDashboard.Use(gin.BasicAuth(loadAuth())) adminDashboard.Use(gin.BasicAuth(loadAuth()))
adminDashboard.GET("/", func(c *gin.Context) { adminDashboard.GET("/", func(c *gin.Context) {
@ -132,14 +141,15 @@ func setupAdminDashboard(adminDashboard *gin.RouterGroup, service holiday.Servic
} }
holidays, _ := service.Find(search, holiday.Paging{PageSize: 100}) holidays, _ := service.Find(search, holiday.Paging{PageSize: 100})
holidayResponse := mapHolidays(holidays) holidayResponse := mapHolidays(holidays)
countries, _ := countryService.Find()
response := map[string]any{} response := map[string]any{}
response["holidays"] = holidayResponse response["Holidays"] = holidayResponse
response["search"] = search response["Search"] = search
response["Countries"] = countries
c.HTML(http.StatusOK, "admin_dashboard.gohtml", response) c.HTML(http.StatusOK, "admin_dashboard.gohtml", response)
}) })
adminDashboard.POST("/holidays", func(c *gin.Context) { adminDashboard.POST("/holidays", func(c *gin.Context) {
request := struct { request := struct {
Id *string `form:"id"` Id *string `form:"id"`
@ -190,8 +200,56 @@ func setupAdminDashboard(adminDashboard *gin.RouterGroup, service holiday.Servic
} }
c.Redirect(http.StatusSeeOther, "/admin?country="+hol.Country+"&year="+strconv.FormatInt(int64(hol.Date.Year()), 10)) c.Redirect(http.StatusSeeOther, "/admin?country="+hol.Country+"&year="+strconv.FormatInt(int64(hol.Date.Year()), 10))
}) })
adminDashboard.GET("/countries", func(c *gin.Context) {
countries, _ := countryService.Find()
c.HTML(http.StatusOK, "countries.gohtml", gin.H{"Countries": countries})
})
adminDashboard.POST("/countries", func(c *gin.Context) {
request := struct {
Id *string `form:"id"`
IsoName string `form:"iso_name" binding:"required,min=2,max=2"`
Name string `form:"name" binding:"required,min=1,max=45"`
}{}
if err := c.ShouldBind(&request); err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
country := holiday.Country{
IsoName: request.IsoName,
Name: request.Name,
}
var err error
if request.Id != nil {
country.Id = uuid.MustParse(*request.Id)
country, err = countryService.Update(country)
} else {
country, err = countryService.Create(country)
}
if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
} else {
c.Redirect(http.StatusSeeOther, "/admin/countries")
}
})
adminDashboard.POST("/countries/:id/delete", func(c *gin.Context) {
id := uuid.MustParse(c.Param("id"))
_, err := countryService.FindById(id)
if err != nil {
c.AbortWithError(http.StatusNotFound, err)
return
}
if err := countryService.Delete(id); err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}
c.Redirect(http.StatusSeeOther, "/admin/countries")
})
adminDashboard.GET("/dialogs/add-holiday", func(c *gin.Context) { adminDashboard.GET("/dialogs/add-holiday", func(c *gin.Context) {
c.HTML(http.StatusOK, "add-holiday.gohtml", gin.H{}) countries, _ := countryService.Find()
c.HTML(http.StatusOK, "add-holiday.gohtml", gin.H{"Countries": countries})
}) })
adminDashboard.GET("/dialogs/edit-holiday", func(c *gin.Context) { adminDashboard.GET("/dialogs/edit-holiday", func(c *gin.Context) {
id := uuid.MustParse(c.Query("id")) id := uuid.MustParse(c.Query("id"))
@ -200,7 +258,17 @@ func setupAdminDashboard(adminDashboard *gin.RouterGroup, service holiday.Servic
c.AbortWithError(http.StatusNotFound, err) c.AbortWithError(http.StatusNotFound, err)
return return
} }
c.HTML(http.StatusOK, "edit-holiday.gohtml", gin.H{"Holiday": hol}) countries, _ := countryService.Find()
c.HTML(http.StatusOK, "edit-holiday.gohtml", gin.H{"Countries": countries, "Holiday": hol})
})
adminDashboard.GET("/dialogs/edit-country", func(c *gin.Context) {
id := uuid.MustParse(c.Query("id"))
country, err := countryService.FindById(id)
if err != nil {
c.AbortWithError(http.StatusNotFound, err)
return
}
c.HTML(http.StatusOK, "edit-country.gohtml", gin.H{"Country": country})
}) })
adminDashboard.GET("/dialogs/delete-holiday", func(c *gin.Context) { adminDashboard.GET("/dialogs/delete-holiday", func(c *gin.Context) {
id := uuid.MustParse(c.Query("id")) id := uuid.MustParse(c.Query("id"))
@ -211,6 +279,15 @@ func setupAdminDashboard(adminDashboard *gin.RouterGroup, service holiday.Servic
} }
c.HTML(http.StatusOK, "delete-holiday.gohtml", gin.H{"Holiday": hol}) c.HTML(http.StatusOK, "delete-holiday.gohtml", gin.H{"Holiday": hol})
}) })
adminDashboard.GET("/dialogs/delete-country", func(c *gin.Context) {
id := uuid.MustParse(c.Query("id"))
country, err := countryService.FindById(id)
if err != nil {
c.AbortWithError(http.StatusNotFound, err)
return
}
c.HTML(http.StatusOK, "delete-country.gohtml", gin.H{"Country": country})
})
} }
func loadAuth() map[string]string { func loadAuth() map[string]string {

View File

@ -17,6 +17,7 @@
<nav> <nav>
<section class="container"> <section class="container">
<a class="selected" href="#">Search</a> <a class="selected" href="#">Search</a>
<a href="/admin/countries">Countries</a>
<button data-type="dialog" data-trigger="#create-card" data-url="/admin/dialogs/add-holiday">Add new holiday</button> <button data-type="dialog" data-trigger="#create-card" data-url="/admin/dialogs/add-holiday">Add new holiday</button>
</section> </section>
</nav> </nav>
@ -27,23 +28,22 @@
<section> <section>
<label for="country">Country:</label> <label for="country">Country:</label>
<select id="country" name="country"> <select id="country" name="country">
<option value="HR" {{if eq .search.Country "HR"}}selected{{end}}>Croatia</option> {{range $entry := .Countries}}
<option value="GB" {{if eq .search.Country "GB"}}selected{{end}}>Great Britain</option> <option {{if eq $.Search.Country $entry.IsoName}}selected{{end}} value="{{$entry.IsoName}}">{{$entry.Name}}</option>
<option value="US" {{if eq .search.Country "US"}}selected{{end}}>USA</option> {{end}}
<option value="FR" {{if eq .search.Country "FR"}}selected{{end}}>France</option>
</select> </select>
</section> </section>
<section> <section>
<label for="year">Year:</label> <label for="year">Year:</label>
<select id="year" name="year"> <select id="year" name="year">
<option value="2020" {{if eq (deferint .search.Year) 2020}}selected{{end}}>2020</option> <option value="2020" {{if eq (deferint .Search.Year) 2020}}selected{{end}}>2020</option>
<option value="2021" {{if eq (deferint .search.Year) 2021}}selected{{end}}>2021</option> <option value="2021" {{if eq (deferint .Search.Year) 2021}}selected{{end}}>2021</option>
<option value="2022" {{if eq (deferint .search.Year) 2022}}selected{{end}}>2022</option> <option value="2022" {{if eq (deferint .Search.Year) 2022}}selected{{end}}>2022</option>
<option value="2023" {{if eq (deferint .search.Year) 2023}}selected{{end}}>2023</option> <option value="2023" {{if eq (deferint .Search.Year) 2023}}selected{{end}}>2023</option>
<option value="2024" {{if eq (deferint .search.Year) 2024}}selected{{end}}>2024</option> <option value="2024" {{if eq (deferint .Search.Year) 2024}}selected{{end}}>2024</option>
<option value="2025" {{if eq (deferint .search.Year) 2025}}selected{{end}}>2025</option> <option value="2025" {{if eq (deferint .Search.Year) 2025}}selected{{end}}>2025</option>
<option value="2026" {{if eq (deferint .search.Year) 2026}}selected{{end}}>2026</option> <option value="2026" {{if eq (deferint .Search.Year) 2026}}selected{{end}}>2026</option>
<option value="2027" {{if eq (deferint .search.Year) 2027}}selected{{end}}>2027</option> <option value="2027" {{if eq (deferint .Search.Year) 2027}}selected{{end}}>2027</option>
</select> </select>
</section> </section>
<section class="radio-group"> <section class="radio-group">
@ -53,7 +53,7 @@
</label><input type="radio" value="false" name="sh" id="sh_false"><label for="sh_false">False </label><input type="radio" value="false" name="sh" id="sh_false"><label for="sh_false">False
</label><input type="radio" value="" name="sh" id="sh_any"><label for="sh_any">All</label> </label><input type="radio" value="" name="sh" id="sh_any"><label for="sh_any">All</label>
</div> </div>
<input type="hidden" value="{{.search.StateHoliday}}" name="state_holiday"> <input type="hidden" value="{{.Search.StateHoliday}}" name="state_holiday">
</section> </section>
<section class="radio-group"> <section class="radio-group">
<label>Is religious holiday:</label> <label>Is religious holiday:</label>
@ -62,7 +62,7 @@
</label><input type="radio" value="false" name="rh" id="rh_false"><label for="rh_false">False </label><input type="radio" value="false" name="rh" id="rh_false"><label for="rh_false">False
</label><input type="radio" value="" name="rh" id="rh_any"><label for="rh_any">All</label> </label><input type="radio" value="" name="rh" id="rh_any"><label for="rh_any">All</label>
</div> </div>
<input type="hidden" value="{{.search.ReligiousHoliday}}" name="religious_holiday"> <input type="hidden" value="{{.Search.ReligiousHoliday}}" name="religious_holiday">
</section> </section>
<section class="actions"> <section class="actions">
<button class="icon primary" type="submit"> <button class="icon primary" type="submit">
@ -86,7 +86,7 @@
</thead> </thead>
<tbody> <tbody>
{{range $entry := .holidays.Holidays}} {{range $entry := .Holidays.Holidays}}
<tr> <tr>
<td>{{$entry.Name}}</td> <td>{{$entry.Name}}</td>
<td>{{$entry.Date.Format "2006-01-02"}}</td> <td>{{$entry.Date.Format "2006-01-02"}}</td>

View File

@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Holiday-api | Admin dashboard</title>
<link rel="stylesheet" href="/assets/style.css">
<script src="/assets/global.js"></script>
</head>
<body>
<div id="dialog-container"></div>
<header>
<section class="container">
<h1><a href="/">Holiday-api | Countries</a></h1>
</section>
</header>
<nav>
<section class="container">
<a href="/admin">Search</a>
<a class="selected" href="#">Countries</a>
<button data-type="dialog" data-trigger="#create-card" data-url="/admin/dialogs/add-holiday">Add new holiday</button>
</section>
</nav>
<main>
<section style="margin: 1em; flex-grow: 1">
<form action="/admin/countries" method="post">
<table style="width: 100%">
<thead>
<tr>
<th>Iso name</th>
<th>Name</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td><section><input minlength="2" maxlength="2" required id="iso_name" type="text" name="iso_name"></section></td>
<td><section><input minlength="1" maxlength="45" required id="name" type="text" name="name"></section></td>
<td><button>Create country</button></td>
</tr>
{{range $entry := .Countries}}
<tr>
<td>{{$entry.IsoName}}</td>
<td>{{$entry.Name}}</td>
<td>
<button type="button" data-type="dialog" data-trigger="#update-card" data-url="/admin/dialogs/edit-country?id={{$entry.Id}}" class="clean icon"><img class="icon" src="/assets/images/edit.svg"></button>
<button type="button" data-type="dialog" data-trigger="#delete-card" data-url="/admin/dialogs/delete-country?id={{$entry.Id}}" class="clean icon"><img class="icon" src="/assets/images/trash-delete.svg"></button>
</td>
</tr>
{{end}}
</tbody>
</table>
</form>
</section>
</main>
</body>
</html>

View File

@ -4,10 +4,9 @@
<section> <section>
<label for="country">Country:</label> <label for="country">Country:</label>
<select id="country" required name="country"> <select id="country" required name="country">
<option value="HR">Croatia</option> {{range $entry := .Countries}}
<option value="US">United states</option> <option value="{{$entry.IsoName}}">{{$entry.Name}}</option>
<option value="FR">France</option> {{end}}
<option value="GB">Great Britain</option>
</select> </select>
</section> </section>
<section> <section>

View File

@ -7,10 +7,9 @@
<section> <section>
<label for="country">Country:</label> <label for="country">Country:</label>
<select id="country" name="country"> <select id="country" name="country">
<option value="HR">Croatia</option> {{range $entry := .Countries}}
<option value="US">United states</option> <option value="{{$entry.IsoName}}">{{$entry.Name}}</option>
<option value="FR">France</option> {{end}}
<option value="GB">Great Britain</option>
</select> </select>
</section> </section>
<section> <section>

View File

@ -0,0 +1,11 @@
<dialog class="card" id="delete-card">
<h3 class="card-title">Delete country</h3>
<p>Are you sure you want to delete "{{.Country.Name}}"?<br>All holidays for given country will be deleted!</p>
<form method="post" action="/admin/countries/{{.Country.Id}}/delete">
<section class="actions">
<button type="submit">Delete</button>
<button type="button" onclick="closeDialog('#delete-card')">Cancel</button>
</section>
</form>
</dialog>

View File

@ -0,0 +1,18 @@
<dialog class="card" id="update-card">
<h3 class="card-title">Edit country</h3>
<form method="post" action="/admin/countries">
<input type="hidden" name="id" value="{{.Country.Id}}">
<section>
<label for="name">Iso name:</label>
<input required minlength="2" maxlength="2" id="iso_name" value="{{.Country.IsoName}}" name="iso_name" type="text">
</section>
<section>
<label for="name">Name:</label>
<input required minlength="1" maxlength="45" id="name" value="{{.Country.Name}}" name="name" type="text">
</section>
<section class="actions">
<button type="submit">Update</button>
<button type="button" onclick="closeDialog('#update-card')">Cancel</button>
</section>
</form>
</dialog>

View File

@ -5,10 +5,9 @@
<section> <section>
<label for="country">Country:</label> <label for="country">Country:</label>
<select id="country" required name="country"> <select id="country" required name="country">
<option {{if eq .Holiday.Country "HR"}}selected{{end}} value="HR">Croatia</option> {{range $entry := .Countries}}
<option {{if eq .Holiday.Country "US"}}selected{{end}} value="US">United states</option> <option {{if eq $.Holiday.Country $entry.IsoName}}selected{{end}} value="{{$entry.IsoName}}">{{$entry.Name}}</option>
<option {{if eq .Holiday.Country "FR"}}selected{{end}} value="FR">France</option> {{end}}
<option {{if eq .Holiday.Country "GB"}}selected{{end}} value="GB">Great Britain</option>
</select> </select>
</section> </section>
<section> <section>

View File

@ -29,10 +29,9 @@
<section> <section>
<label for="country">Country</label> <label for="country">Country</label>
<select id="country" name="country"> <select id="country" name="country">
<option value="HR">Croatia</option> {{range $entry := .Countries}}
<option value="GB">Great Britain</option> <option value="{{$entry.IsoName}}">{{$entry.Name}}</option>
<option value="US">USA</option> {{end}}
<option value="FR">France</option>
</select> </select>
</section> </section>

View File

@ -28,10 +28,9 @@
<section> <section>
<label for="country">Country:</label> <label for="country">Country:</label>
<select id="country" name="country"> <select id="country" name="country">
<option {{if eq .Search.Country "HR"}}selected{{end}} value="HR">Croatia</option> {{range $entry := .Countries}}
<option {{if eq .Search.Country "US"}}selected{{end}} value="US">United states</option> <option {{if eq $.Search.Country $entry.IsoName}}selected{{end}} value="{{$entry.IsoName}}">{{$entry.Name}}</option>
<option {{if eq .Search.Country "FR"}}selected{{end}} value="FR">France</option> {{end}}
<option {{if eq .Search.Country "GB"}}selected{{end}} value="GB">Great Britain</option>
</select> </select>
</section> </section>
<section> <section>

View File

@ -29,10 +29,9 @@
<section> <section>
<label for="country">Country:</label> <label for="country">Country:</label>
<select id="country" name="country"> <select id="country" name="country">
<option {{if eq .Search.Country "HR"}}selected{{end}} value="HR">Croatia</option> {{range $entry := .Countries}}
<option {{if eq .Search.Country "US"}}selected{{end}} value="US">United states</option> <option {{if eq $.Search.Country $entry.IsoName}}selected{{end}} value="{{$entry.IsoName}}">{{$entry.Name}}</option>
<option {{if eq .Search.Country "FR"}}selected{{end}} value="FR">France</option> {{end}}
<option {{if eq .Search.Country "GB"}}selected{{end}} value="GB">Great Britain</option>
</select> </select>
</section> </section>
<section> <section>