From 76a3c446da405e091260f618c08784c5b9e5c60d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borna=20Rajkovi=C4=87?= Date: Sat, 5 Aug 2023 17:50:40 +0200 Subject: [PATCH] Updated design + added documentation page --- assets/documentation.js | 110 ++++++++ assets/global.js | 68 +++++ assets/images/circle-add.svg | 5 + assets/images/close-x.svg | 4 + assets/images/done-v.svg | 4 + assets/images/edit.svg | 4 + assets/images/search.svg | 4 + assets/images/trash-delete.svg | 4 + assets/style.css | 275 ++++++++++++++++++++ dockerfile | 1 + holiday/model.go | 28 +- holiday/service.go | 8 +- main.go | 161 ++++++------ templates/admin_dashboard.gohtml | 183 ++++++------- templates/dialogs/add-holiday.gohtml | 36 +++ templates/dialogs/check-is-a-holiday.gohtml | 24 ++ templates/dialogs/delete-holiday.gohtml | 11 + templates/dialogs/edit-holiday.gohtml | 37 +++ templates/docs.gohtml | 104 -------- templates/documentation.gohtml | 133 ++++++++++ templates/error.gohtml | 5 - templates/holiday.gohtml | 44 ---- templates/index.gohtml | 139 ++++++---- templates/search.gohtml | 93 +++++-- templates/search_date.gohtml | 43 --- 25 files changed, 1080 insertions(+), 448 deletions(-) create mode 100644 assets/documentation.js create mode 100644 assets/global.js create mode 100644 assets/images/circle-add.svg create mode 100644 assets/images/close-x.svg create mode 100644 assets/images/done-v.svg create mode 100644 assets/images/edit.svg create mode 100644 assets/images/search.svg create mode 100644 assets/images/trash-delete.svg create mode 100644 assets/style.css create mode 100644 templates/dialogs/add-holiday.gohtml create mode 100644 templates/dialogs/check-is-a-holiday.gohtml create mode 100644 templates/dialogs/delete-holiday.gohtml create mode 100644 templates/dialogs/edit-holiday.gohtml delete mode 100644 templates/docs.gohtml create mode 100644 templates/documentation.gohtml delete mode 100644 templates/error.gohtml delete mode 100644 templates/holiday.gohtml delete mode 100644 templates/search_date.gohtml diff --git a/assets/documentation.js b/assets/documentation.js new file mode 100644 index 0000000..0c2658b --- /dev/null +++ b/assets/documentation.js @@ -0,0 +1,110 @@ +window.addEventListener('load', () => { + + function parseBoolean(value) { + if(value === 'true') { + return true; + } else if (value === 'false') { + return false; + } else { + return undefined; + } + } + + function generateQuery(query) { + initialQuery = `curl -H "accept: ${query['contentType']}" "https://holiday.bbr-dev.info/api/v1/holidays?country=${query['country']}` + + delete query['contentType']; + delete query['country']; + + for(const key in query) { + initialQuery += `&${key}=${query[key]}` + } + initialQuery+='"'; + + document.querySelector('#result-content').innerHTML = initialQuery; + } + + let country = document.querySelector("#country"); + let contentType = document.querySelector("#content-type"); + let stateHoliday = document.querySelector("#state-holiday"); + let religiousHoliday = document.querySelector("#religious-holiday"); + + // select which date/year/range is used + let dateSelector = document.querySelector("#date-selector"); + + let dYear = document.querySelector("#dsy-year"); + let dDate = document.querySelector("#dsd-date"); + + let dStartRange = document.querySelector("#dsr-start-range"); + let dStartRangeRequired = document.querySelector("#dsr-start-range-required"); + + let dEndRange = document.querySelector("#dsr-end-range"); + let dEndRangeRequired = document.querySelector("#dsr-end-range-required"); + + let queryGenerator = document.querySelector("#query-generator"); + + { + const query = {}; + query['country'] = country.value; + query['contentType'] = contentType.value; + if(stateHoliday.value === 'true' || stateHoliday.value === 'false') { + query['stateHoliday'] = parseBoolean(stateHoliday.value); + } + if(religiousHoliday.value === 'true' || religiousHoliday.value === 'false') { + query['religiousHoliday'] = parseBoolean(religiousHoliday.value); + } + switch(dateSelector.value) { + case 'year': + query['year'] = dYear.value; + break; + case 'date': + if(dDate.value) { + query['date'] = dDate.value; + } + break; + case 'range': + if(dStartRangeRequired.checked && dStartRange.value) { + query['startRange'] = dStartRange.value; + } + if(dEndRangeRequired.checked && dEndRange.value) { + query['endRange'] = dEndRange.value; + } + case 'all': + default: + } + + generateQuery(query); + } + + queryGenerator.addEventListener('change', event => { + const query = {}; + query['country'] = country.value; + query['contentType'] = contentType.value; + if(stateHoliday.value === 'true' || stateHoliday.value === 'false') { + query['stateHoliday'] = parseBoolean(stateHoliday.value); + } + if(religiousHoliday.value === 'true' || religiousHoliday.value === 'false') { + query['religiousHoliday'] = parseBoolean(religiousHoliday.value); + } + switch(dateSelector.value) { + case 'year': + query['year'] = dYear.value; + break; + case 'date': + if(dDate.value) { + query['date'] = dDate.value; + } + break; + case 'range': + if(dStartRangeRequired.checked && dStartRange.value) { + query['startRange'] = dStartRange.value; + } + if(dEndRangeRequired.checked && dEndRange.value) { + query['endRange'] = dEndRange.value; + } + case 'all': + default: + } + generateQuery(query); + }) +}) diff --git a/assets/global.js b/assets/global.js new file mode 100644 index 0000000..660fb8a --- /dev/null +++ b/assets/global.js @@ -0,0 +1,68 @@ +const dialogContainerId = "#dialog-container" + +window.addEventListener('load', () => { + // configure radio button groups + document.querySelectorAll("section.radio-group").forEach(el => { + const submittedInput = el.querySelector("input[type=hidden]"); + el.querySelector(`input[value="${submittedInput.value}"]`).checked = true; + + + el.querySelectorAll("input[type=radio]").forEach(rb => { + if(rb?.checked) { + submittedInput.value = rb.value; + } + rb.addEventListener('click', () => { + submittedInput.value = rb.value; + }) + }) + }) + // configure dialog buttons + document.querySelectorAll("button[data-type=dialog]").forEach(btn => { + const selector = btn.dataset.trigger; + btn.addEventListener('click', async () => { + let response = await fetch(btn.dataset.url); + if(response.ok) { + response = await response.text(); + const container = document.querySelector(dialogContainerId); + container.innerHTML = response; + const dialogReference = document.querySelector(selector); + dialogReference?.showModal(); + } else { + // todo display alert + console.log("no dialog found"); + } + }); + }); + // configure section selector + document.querySelectorAll("select[data-type=section-selector]").forEach(select => { + const sectionPrefix = select.dataset.selectorPrefix; + const options = [...select.options].map(o => o.value); + + for(let option of options) { + const querySelector = "div[data-section-id=" + sectionPrefix + "-" + option + "]"; + const section = document.querySelector(querySelector); + if(option === select.value) { + section?.classList?.remove('hide'); + } else { + section?.classList?.add('hide'); + } + } + + select.addEventListener("change", () => { + for(let option of options) { + const querySelector = "div[data-section-id=" + sectionPrefix + "-" + option + "]"; + const section = document.querySelector(querySelector); + if(option === select.value) { + section?.classList?.remove('hide'); + } else { + section?.classList?.add('hide'); + } + } + }) + }) +}) + +function closeDialog(selector) { + document.querySelector(selector).close(); + document.querySelector(dialogContainerId).innerHTML=""; +} diff --git a/assets/images/circle-add.svg b/assets/images/circle-add.svg new file mode 100644 index 0000000..6e3dfc8 --- /dev/null +++ b/assets/images/circle-add.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/assets/images/close-x.svg b/assets/images/close-x.svg new file mode 100644 index 0000000..1d0a4cd --- /dev/null +++ b/assets/images/close-x.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/assets/images/done-v.svg b/assets/images/done-v.svg new file mode 100644 index 0000000..13be0f9 --- /dev/null +++ b/assets/images/done-v.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/assets/images/edit.svg b/assets/images/edit.svg new file mode 100644 index 0000000..597a307 --- /dev/null +++ b/assets/images/edit.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/assets/images/search.svg b/assets/images/search.svg new file mode 100644 index 0000000..bd4c0b4 --- /dev/null +++ b/assets/images/search.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/assets/images/trash-delete.svg b/assets/images/trash-delete.svg new file mode 100644 index 0000000..e53609b --- /dev/null +++ b/assets/images/trash-delete.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/assets/style.css b/assets/style.css new file mode 100644 index 0000000..155b8d4 --- /dev/null +++ b/assets/style.css @@ -0,0 +1,275 @@ +/* cleanup */ + +* { + box-sizing: border-box; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + padding: 0; + margin: 0; +} + +header { + padding: 1rem 2rem; + width: 100%; + background: #666; +} +header h1 { + font-size: 1.25em; +} +header a { + color: #fff; + text-decoration: none; +} + +nav { + padding: 1rem 2rem; + width: 100%; + background: #f7f7f7; +} +nav a, nav button { + display: inline-block; + text-decoration: none; + color: #333; + font-size: 1rem; + border: none; + background: none; + padding: 0.5rem 1rem; + transition: ease-out 0.2s; +} +nav a:hover, nav button:hover { + background: #e0e0e0; + color: #000; + transition: ease-out 0.2s; +} +nav a.selected, nav button.selected { + background: #216897; + color: white; +} + +table:not(.clean) { + width: 100%; +} + +table th, table td { + text-align: left; +} +table { + border-collapse: collapse; +} +table thead * { + background: #666; + color: #fff; +} +table th, table td { + padding: 0.4rem 0.4rem; +} + +table:not(.clean) tbody tr:nth-child(2n+1) { + background: #f5f5f5; +} + +table:not(.clean) tbody tr:nth-child(2n) { + background: #e5e5e5; +} + +button { + padding: 0.25rem 1rem; +} + +.card { + padding: 1em; + background: #fff; + border-radius: 0.2rem; + border: 1px solid #bbb; +} +.card .card-title { + margin-bottom: 0.5em; +} +.card p { + margin-bottom: 0.5em; +} + +form section { + width: fit-content; +} + +/* styling */ +form section:not(.radio-group) label+:not(select) { + display: flex; +} + +form section { + padding-bottom: 0.75rem; +} + +form section > input { + width: 200px; + padding: 0.2em 0.5em; +} + + +form section > textarea { + width: 200px; + padding: 0.2em 0.5em; +} + +form section select { + display: block; + width: 200px; + border: none; + font-size: 0.9rem; + padding: 0.2em 0.5em; + background: #ddd; +} + +form section label.checkbox { + display: block; + overflow: hidden; + width: 200px; +} +form section input[type=checkbox] { + vertical-align: middle; + float: right; +} + + +section.radio-group input { + display: none; +} + +section.radio-group div label { + display: inline-block; + padding: 0.25rem 0.75rem; + background: #ddd; +} + +section.radio-group div label { + min-width: 64px; + text-align: center; + font-size: 0.9rem; +} + +section.radio-group div label:first-of-type { + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; +} + +section.radio-group div label:last-of-type { + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; +} + +section.radio-group div input:checked+label { + background: #555; + color: white; +} + +img.icon { + height: 1.5em; + width: 1.5em; +} +button.icon, a.icon { + display: flex; + align-content: baseline; + gap: 0.3em; +} + + +main { + display: flex; + flex-wrap: wrap; + max-width: 1280px; + margin: auto; +} + +.container { + max-width: 1280px; + margin: auto; +} + +section#search { + margin: 1em; + width: fit-content; +} + +.index-page { + margin: 1em auto; + align-content: center; + gap: 1em; +} +.index-page section { + flex-grow: 1; +} +.index-page section article { + margin: auto; + width: 300px; + max-width: 90vw; +} + +section#results { + margin: 1em; + flex-grow: 1; +} + +button.clean { + display: inline-block; + padding: 0.4rem; + border: none; + background: none; + text-decoration: none; + color: #000; + cursor: pointer; +} + +section#results a, section#results button { + display: inline-block; + padding: 0.4rem; + border: none; + background: none; + text-decoration: none; + color: #000; + cursor: pointer; +} +dialog { + position: fixed; + left: 50%; + top: 80px; + transform: translate(-50%, 0); + max-height: calc(100vh - 160px); + overflow-y: scroll; +} +section.actions { + padding: 0; +} +article.single-holiday { + padding-bottom: 0.5em; + margin-bottom: 1em; + border-bottom: 2px solid #a0a0a0; +} + +.optional-selector { + border: 1px solid #666; + border-radius: 0.5em; +} + +.optional-selector .header { + padding: 1em; + background: #666; + margin-bottom: 0.5em; + color: white; + border-top-left-radius: 0.5em; + border-top-right-radius: 0.5em; +} +.optional-selector > *:not(.header) { + margin: 0 1em; +} +.hide { + display: none; +} +.source-code { + padding: 1em; +} +.source-code, .source-code * { + background: #777; + color: #fff; + font-family: 'Courier New', Courier, monospace; +} \ No newline at end of file diff --git a/dockerfile b/dockerfile index 9fa7273..2f030a9 100644 --- a/dockerfile +++ b/dockerfile @@ -17,4 +17,5 @@ FROM scratch COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt COPY --from=go-build /root/holiday-api /usr/bin/holiday-api ADD templates templates/ +ADD assets assets/ ENTRYPOINT ["holiday-api"] diff --git a/holiday/model.go b/holiday/model.go index edbb4d5..b2f3a4e 100644 --- a/holiday/model.go +++ b/holiday/model.go @@ -26,6 +26,30 @@ type Search struct { Date *time.Time `form:"date" time_format:"2006-01-02"` RangeStart *time.Time `form:"range_start" time_format:"2006-01-02"` RangeEnd *time.Time `form:"range_end" time_format:"2006-01-02"` - StateHoliday *bool `form:"state_holiday,omitempty"` - ReligiousHoliday *bool `form:"religious_holiday,omitempty"` + StateHoliday string `form:"state_holiday,omitempty" binding:"omitempty"` + ReligiousHoliday string `form:"religious_holiday,omitempty" binding:"omitempty"` +} + +func (s Search) IsStateHoliday() *bool { + var value bool + if s.StateHoliday == "true" { + value = true + } else if s.StateHoliday == "false" { + value = false + } else { + return nil + } + return &value +} + +func (s Search) IsReligiousHoliday() *bool { + var value bool + if s.ReligiousHoliday == "true" { + value = true + } else if s.ReligiousHoliday == "false" { + value = false + } else { + return nil + } + return &value } diff --git a/holiday/service.go b/holiday/service.go index e102a6d..b083795 100644 --- a/holiday/service.go +++ b/holiday/service.go @@ -16,13 +16,13 @@ func (s *Service) Find(search Search, paging Paging) ([]Holiday, error) { var holidays []Holiday var err error if search.Date != nil { - holidays, err = s.findByDate(*search.Date, search.StateHoliday, search.ReligiousHoliday, search.Country) + holidays, err = s.findByDate(*search.Date, search.IsStateHoliday(), search.IsReligiousHoliday(), search.Country) } else if search.RangeStart != nil || search.RangeEnd != nil { - holidays, err = s.findForRange(getDateOrDefault(search.RangeStart, 1000), getDateOrDefault(search.RangeEnd, 3000), search.StateHoliday, search.ReligiousHoliday, search.Country) + holidays, err = s.findForRange(getDateOrDefault(search.RangeStart, 1000), getDateOrDefault(search.RangeEnd, 3000), search.IsStateHoliday(), search.IsReligiousHoliday(), search.Country) } else if search.Year != nil { - holidays, err = s.findByYear(*search.Year, search.StateHoliday, search.ReligiousHoliday, search.Country) + holidays, err = s.findByYear(*search.Year, search.IsStateHoliday(), search.IsReligiousHoliday(), search.Country) } else { - holidays, err = s.find(search.StateHoliday, search.ReligiousHoliday, search.Country) + holidays, err = s.find(search.IsStateHoliday(), search.IsReligiousHoliday(), search.Country) } if err != nil { return nil, err diff --git a/main.go b/main.go index 8eed99a..c80ca38 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ import ( "log" "net/http" "os" + "strconv" "strings" "time" ) @@ -48,6 +49,8 @@ func main() { g := gin.Default() + g.Static("assets", "assets") + loadTemplates(g) holidayService := holiday.Service{DB: client} @@ -56,24 +59,20 @@ func main() { setupAdminDashboard(g.Group("/admin"), holidayService) - g.GET("/docs", func(c *gin.Context) { - c.HTML(http.StatusOK, "docs.gohtml", nil) - }) g.GET("/", func(c *gin.Context) { - c.HTML(http.StatusOK, "index.gohtml", nil) + year := time.Now().Year() + search := holiday.Search{Country: "HR", Year: &year} + if err := c.ShouldBindQuery(&search); err != nil { + c.AbortWithError(http.StatusBadRequest, err) + return + } + holidays, _ := holidayService.Find(search, holiday.Paging{PageSize: 100}) + c.HTML(http.StatusOK, "index.gohtml", gin.H{"Search": search, "Holidays": mapHolidays(holidays).Holidays}) + }) + g.GET("/documentation", func(c *gin.Context) { + c.HTML(http.StatusOK, "documentation.gohtml", nil) }) g.GET("/search", func(c *gin.Context) { - request := holiday.Search{} - if err := c.ShouldBindQuery(&request); err != nil { - c.AbortWithError(http.StatusBadRequest, err) - return - } - search := holiday.Search{Country: request.Country, Year: request.Year} - holidays, _ := holidayService.Find(search, holiday.Paging{PageSize: 100}) - c.HTML(http.StatusOK, "search.gohtml", mapHolidays(holidays)) - }) - - g.GET("/search/date", func(c *gin.Context) { request := holiday.Search{} if err := c.ShouldBindQuery(&request); err != nil { c.AbortWithError(http.StatusBadRequest, err) @@ -81,7 +80,10 @@ func main() { } search := holiday.Search{Country: request.Country, Date: request.Date} holidays, _ := holidayService.Find(search, holiday.Paging{PageSize: 100}) - c.HTML(http.StatusOK, "search_date.gohtml", mapHolidays(holidays)) + c.HTML(http.StatusOK, "search.gohtml", gin.H{"Search": search, "Holidays": mapHolidays(holidays).Holidays}) + }) + g.GET("/dialogs/check-is-a-holiday", func(c *gin.Context) { + c.HTML(http.StatusOK, "check-is-a-holiday.gohtml", gin.H{}) }) log.Fatal(http.ListenAndServe(":5281", g)) @@ -97,53 +99,60 @@ func loadTemplates(g *gin.Engine) { } }, "deferint": func(value *int) int { return *value }, + "intpeq": func(selected *int, value int) bool { + if selected != nil { + return *selected == value + } + return false + }, }) - g.LoadHTMLFiles("templates/admin_dashboard.gohtml", "templates/holiday.gohtml", "templates/error.gohtml", "templates/index.gohtml", "templates/search.gohtml", "templates/search_date.gohtml", "templates/docs.gohtml") + g.LoadHTMLFiles( + "templates/index.gohtml", + "templates/search.gohtml", + "templates/documentation.gohtml", + + "templates/admin_dashboard.gohtml", + + "templates/dialogs/add-holiday.gohtml", + "templates/dialogs/edit-holiday.gohtml", + "templates/dialogs/delete-holiday.gohtml", + "templates/dialogs/check-is-a-holiday.gohtml", + ) } func setupAdminDashboard(adminDashboard *gin.RouterGroup, service holiday.Service) { adminDashboard.Use(gin.BasicAuth(loadAuth())) - adminDashboard.GET("/holiday", func(c *gin.Context) { - id := c.Query("id") + adminDashboard.GET("/", func(c *gin.Context) { + search := holiday.Search{Country: "HR", Year: new(int)} + *search.Year = time.Now().Year() + if err := c.ShouldBindQuery(&search); err != nil { + c.AbortWithError(http.StatusBadRequest, err) + return + } + holidays, _ := service.Find(search, holiday.Paging{PageSize: 100}) + holidayResponse := mapHolidays(holidays) - response := HolidaySingleResponse{ - Id: nil, - Country: "", - Date: DateResponse{time.Now()}, - Name: "", - Description: "", - IsStateHoliday: true, - IsReligiousHoliday: false, - } - if id != "" { - if h, err := service.FindById(uuid.Must(uuid.Parse(id))); err == nil { - response = HolidaySingleResponse{ - Id: &h.Id, - Country: h.Country, - Date: DateResponse{h.Date}, - Name: h.Name, - Description: h.Description, - IsStateHoliday: h.IsStateHoliday, - IsReligiousHoliday: h.IsReligiousHoliday, - } - } - } - c.HTML(http.StatusOK, "holiday.gohtml", response) + response := map[string]any{} + response["holidays"] = holidayResponse + response["search"] = search + + c.HTML(http.StatusOK, "admin_dashboard.gohtml", response) }) - adminDashboard.POST("/holiday", func(c *gin.Context) { + adminDashboard.POST("/holidays", func(c *gin.Context) { request := struct { Id *string `form:"id"` Name string `form:"name" binding:"required,min=1"` Description string `form:"description"` - IsStateHoliday bool `form:"stateHoliday"` - IsReligiousHoliday bool `form:"religiousHoliday"` + IsStateHoliday bool `form:"state_holiday"` + IsReligiousHoliday bool `form:"religious_holiday"` Country string `form:"country" binding:"len=2"` Date time.Time `form:"date" time_format:"2006-01-02"` }{} if err := c.ShouldBind(&request); err != nil { c.AbortWithError(http.StatusBadRequest, err) + return } hol := holiday.Holiday{ @@ -165,42 +174,42 @@ func setupAdminDashboard(adminDashboard *gin.RouterGroup, service holiday.Servic if err != nil { c.AbortWithError(http.StatusInternalServerError, err) } else { - c.Redirect(http.StatusSeeOther, "/admin/holiday?id="+hol.Id.String()) + c.Redirect(http.StatusSeeOther, "/admin?country="+request.Country+"&year="+strconv.FormatInt(int64(request.Date.Year()), 10)) } }) - - adminDashboard.POST("/holiday/delete", func(c *gin.Context) { - request := struct { - Id *string `form:"id" binding:"required"` - }{} - if err := c.ShouldBind(&request); err != nil { - c.AbortWithError(http.StatusBadRequest, err) - return - } - err := service.Delete(uuid.MustParse(*request.Id)) + adminDashboard.POST("/holidays/:id/delete", func(c *gin.Context) { + id := uuid.MustParse(c.Param("id")) + hol, err := service.FindById(id) if err != nil { - log.Printf("Failed deleting holiday: %v", err) - c.AbortWithStatus(http.StatusInternalServerError) - } else { - c.Redirect(http.StatusSeeOther, "/admin") - } - }) - - adminDashboard.GET("/", func(c *gin.Context) { - search := holiday.Search{Country: "HR", Year: new(int)} - *search.Year = time.Now().Year() - if err := c.ShouldBindQuery(&search); err != nil { - c.AbortWithError(http.StatusBadRequest, err) + c.AbortWithError(http.StatusNotFound, err) return } - holidays, _ := service.Find(search, holiday.Paging{PageSize: 100}) - holidayResponse := mapHolidays(holidays) - - response := map[string]any{} - response["holidays"] = holidayResponse - response["search"] = search - - c.HTML(http.StatusOK, "admin_dashboard.gohtml", response) + if err := service.Delete(id); err != nil { + c.AbortWithError(http.StatusInternalServerError, err) + return + } + c.Redirect(http.StatusSeeOther, "/admin?country="+hol.Country+"&year="+strconv.FormatInt(int64(hol.Date.Year()), 10)) + }) + adminDashboard.GET("/dialogs/add-holiday", func(c *gin.Context) { + c.HTML(http.StatusOK, "add-holiday.gohtml", gin.H{}) + }) + adminDashboard.GET("/dialogs/edit-holiday", func(c *gin.Context) { + id := uuid.MustParse(c.Query("id")) + hol, err := service.FindById(id) + if err != nil { + c.AbortWithError(http.StatusNotFound, err) + return + } + c.HTML(http.StatusOK, "edit-holiday.gohtml", gin.H{"Holiday": hol}) + }) + adminDashboard.GET("/dialogs/delete-holiday", func(c *gin.Context) { + id := uuid.MustParse(c.Query("id")) + hol, err := service.FindById(id) + if err != nil { + c.AbortWithError(http.StatusNotFound, err) + return + } + c.HTML(http.StatusOK, "delete-holiday.gohtml", gin.H{"Holiday": hol}) }) } diff --git a/templates/admin_dashboard.gohtml b/templates/admin_dashboard.gohtml index ad857c1..f7b1e16 100644 --- a/templates/admin_dashboard.gohtml +++ b/templates/admin_dashboard.gohtml @@ -2,104 +2,105 @@ - - Holiday-api + Holiday-api | Admin dashboard + + - -

Holiday-api

-

Welcome to admin interface for holiday api

- -
- Add a holiday -
-
-
- - - - - - - -
- - - - - - - - - - -
- -
- - - - - - - - - - - -
- - -
-
-
- - +
+
+
+

Holiday-api | Admin dashboard

+
+
+ +
+ +
+
+ - - - + + - - - {{range $entry := .holidays.Holidays}} - - - - - - - - - {{end}} - -
NameDescription DateState holidayReligious holidayStateReligious
{{$entry.Name}}{{$entry.Description}}{{$entry.Date.Format "2006-01-02"}}{{$entry.IsStateHoliday}}{{$entry.IsReligiousHoliday}} - Edit -
- - -
-
-
+ + + + {{range $entry := .holidays.Holidays}} + + {{$entry.Name}} + {{$entry.Date.Format "2006-01-02"}} + + + + + + + + {{end}} + + + + \ No newline at end of file diff --git a/templates/dialogs/add-holiday.gohtml b/templates/dialogs/add-holiday.gohtml new file mode 100644 index 0000000..9ed3728 --- /dev/null +++ b/templates/dialogs/add-holiday.gohtml @@ -0,0 +1,36 @@ + +

Create holiday

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+ + +
+
+
\ No newline at end of file diff --git a/templates/dialogs/check-is-a-holiday.gohtml b/templates/dialogs/check-is-a-holiday.gohtml new file mode 100644 index 0000000..ae9ed23 --- /dev/null +++ b/templates/dialogs/check-is-a-holiday.gohtml @@ -0,0 +1,24 @@ + +
+

Is it a holiday?

+ +
+
+
+ + +
+
+ + +
+
+ +
+
+
\ No newline at end of file diff --git a/templates/dialogs/delete-holiday.gohtml b/templates/dialogs/delete-holiday.gohtml new file mode 100644 index 0000000..044233c --- /dev/null +++ b/templates/dialogs/delete-holiday.gohtml @@ -0,0 +1,11 @@ + + +

Delete holiday

+

Are you sure you want to delete "{{.Holiday.Name}}"?

+
+
+ + +
+
+
\ No newline at end of file diff --git a/templates/dialogs/edit-holiday.gohtml b/templates/dialogs/edit-holiday.gohtml new file mode 100644 index 0000000..adaeca5 --- /dev/null +++ b/templates/dialogs/edit-holiday.gohtml @@ -0,0 +1,37 @@ + +

Edit holiday

+
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+ + +
+
+
\ No newline at end of file diff --git a/templates/docs.gohtml b/templates/docs.gohtml deleted file mode 100644 index 6b34939..0000000 --- a/templates/docs.gohtml +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - Holiday-api - - -

Holiday-api

-

Create query

- -
- - - -
- -
- -
- - - - - -
- -
- - - - - -
- -
- - - - - - - - - - - - - - - -
- -
- - -
-
- -
- -
- - - - - - - - - -
- -
- -
- - - - - - - - - -
-
- - \ No newline at end of file diff --git a/templates/documentation.gohtml b/templates/documentation.gohtml new file mode 100644 index 0000000..61f46a1 --- /dev/null +++ b/templates/documentation.gohtml @@ -0,0 +1,133 @@ + + + + + + Holiday-api + + + + + +
+
+
+

Holiday-api

+
+
+ +
+
+
+
+
+ + +
+ +
+ + +
+ +
+
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+
+
+
+

Query

+
+ Loading... +
+
+
+ + diff --git a/templates/error.gohtml b/templates/error.gohtml deleted file mode 100644 index 1d41308..0000000 --- a/templates/error.gohtml +++ /dev/null @@ -1,5 +0,0 @@ - - -

Test for error

- - \ No newline at end of file diff --git a/templates/holiday.gohtml b/templates/holiday.gohtml deleted file mode 100644 index 79c0c24..0000000 --- a/templates/holiday.gohtml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - Holiday-api holiday - - -

Holiday-api - {{if .Id}}Updating{{else}}Creating{{end}}

-

Page for creating or editing holidays

-Back -
- {{if .Id}} - - {{end}} - - - - - - - - - - - - - - - - - - - - -
- - \ No newline at end of file diff --git a/templates/index.gohtml b/templates/index.gohtml index 662bf87..92c0ff3 100644 --- a/templates/index.gohtml +++ b/templates/index.gohtml @@ -2,61 +2,96 @@ - Holiday-api + + -

Holiday-api

-

Welcome to holiday api - simple page for tracking holidays

-
-

Is a holiday?

-
- - - - - - - -
-
- -
-

Find holidays for

-
- - - - - - -
-
- +
+
+
+

Holiday-api

+
+
+ +
+ +
+ + + + + + + + + + + {{range $entry := .Holidays}} + + + + + + + {{end}} + +
NameDateStateReligious
{{$entry.Name}}{{$entry.Date.Format "2006-01-02"}}
+
+
\ No newline at end of file diff --git a/templates/search.gohtml b/templates/search.gohtml index 13f71ea..fe619dc 100644 --- a/templates/search.gohtml +++ b/templates/search.gohtml @@ -2,37 +2,76 @@ - - Holiday-api + Holiday-api | Admin dashboard + + -

Holiday-api

-

Search results

- -
- - - - - - - - - - - +
+
+
+

Holiday-api | {{.Search.Date.Format "2006-01-02"}}

+
+
+ +
+ +
+

Results

{{range $entry := .Holidays}} -
- - - - - - +
+
NameDescriptionDateState holidayReligious holiday
{{$entry.Name}}{{$entry.Description}}{{$entry.Date.Format "2006-01-02"}}{{$entry.IsStateHoliday}}{{$entry.IsReligiousHoliday}}
+ + + + + + + + + + + + + + + + + + +
Name: {{$entry.Name}}
Description: {{$entry.Description}}
Is state holiday:
Is religious holiday:
+ {{end}} - - -
+ + \ No newline at end of file diff --git a/templates/search_date.gohtml b/templates/search_date.gohtml deleted file mode 100644 index d8365f7..0000000 --- a/templates/search_date.gohtml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - Holiday-api - - -

Holiday-api

- -

{{if .Holidays}}Yes it is a holday{{else}}No it isn't a holiday{{end}}

- -

Search results

- -
- {{if .Holidays}} - - - - - - - - - - - - {{range $entry := .Holidays}} - - - - - - - - {{end}} - -
NameDescriptionDateState holidayReligious holiday
{{$entry.Name}}{{$entry.Description}}{{$entry.Date.Format "2006-01-02"}}{{$entry.IsStateHoliday}}{{$entry.IsReligiousHoliday}}
- {{end}} -
- - \ No newline at end of file