Updated user pages
|
@ -1,3 +1,4 @@
|
|||
.idea/**
|
||||
holiday-api
|
||||
.env
|
||||
**/.DS_Store
|
||||
|
|
After Width: | Height: | Size: 236 KiB |
|
@ -0,0 +1,203 @@
|
|||
body {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
main {
|
||||
background: #f3ebe6;
|
||||
width: 640px;
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #e0e0e0;
|
||||
background: white;
|
||||
margin: auto;
|
||||
padding: 16px 32px;
|
||||
}
|
||||
|
||||
.background-image {
|
||||
min-width: 100vw;
|
||||
min-height: 100vh;
|
||||
|
||||
max-width: 100vw;
|
||||
max-height: 100vh;
|
||||
|
||||
background: url("/assets/background.jpg");
|
||||
background-position: bottom;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Quicksand;
|
||||
src: url(fonts/Quicksand-Light.ttf);
|
||||
font-weight: 100 200;
|
||||
}
|
||||
@font-face {
|
||||
font-family: Quicksand;
|
||||
src: url(fonts/Quicksand-Medium.ttf);
|
||||
font-weight: 300 300;
|
||||
}
|
||||
@font-face {
|
||||
font-family: Quicksand;
|
||||
src: url(fonts/Quicksand-Regular.ttf);
|
||||
font-weight: 400 500;
|
||||
}
|
||||
@font-face {
|
||||
font-family: Quicksand;
|
||||
src: url(fonts/Quicksand-Bold.ttf);
|
||||
font-weight: 800 900;
|
||||
}
|
||||
@font-face {
|
||||
font-family: Quicksand;
|
||||
src: url(fonts/Quicksand-SemiBold.ttf);
|
||||
font-weight: 600 700;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: Quicksand, Arial, sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
h1 a {
|
||||
all: unset;
|
||||
}
|
||||
|
||||
|
||||
.form-field {
|
||||
width: 100%;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.form-field label {
|
||||
display: block;
|
||||
color: #999;
|
||||
}
|
||||
.form-field input {
|
||||
width: 300px;
|
||||
border: none;
|
||||
border-bottom: 1px solid #bbb;
|
||||
|
||||
font-size: 20px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.form-field input:focus {
|
||||
color: cadetblue;
|
||||
border-bottom: 1px solid cadetblue;
|
||||
}
|
||||
|
||||
.form-field input:not(focus) {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-field label {
|
||||
display: block;
|
||||
color: #999;
|
||||
}
|
||||
.form-field select {
|
||||
all: unset;
|
||||
width: 100%;
|
||||
border: none;
|
||||
border-bottom: 1px solid #bbb;
|
||||
|
||||
font-size: 20px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.form-field select:focus {
|
||||
color: cadetblue;
|
||||
border-bottom: 1px solid cadetblue;
|
||||
}
|
||||
|
||||
.form-field select:not(focus) {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-field:focus-within label {
|
||||
color: cadetblue;
|
||||
}
|
||||
|
||||
|
||||
.button.secondary, button.primary {
|
||||
padding: 12px 16px;
|
||||
background-color: cadetblue;
|
||||
font-family: Quicksand, Arial, sans-serif;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 1.2rem;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.button.secondary, button.primary:hover {
|
||||
filter: contrast(140%);
|
||||
}
|
||||
|
||||
.button.secondary, button.secondary {
|
||||
padding: 12px 16px;
|
||||
background-color: white;
|
||||
font-family: Quicksand, Arial, sans-serif;
|
||||
border: 1px solid cadetblue;
|
||||
color: cadetblue;
|
||||
font-size: 1.2rem;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.button.secondary:hover, button.secondary:hover {
|
||||
filter: contrast(140%);
|
||||
}
|
||||
|
||||
/*index.html*/
|
||||
|
||||
.search-dialog {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 480px) {
|
||||
.search-dialog {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 10vw;
|
||||
transform: translateY(-50%);
|
||||
width: 380px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
/*results.html*/
|
||||
.results-dialog {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
|
||||
.results-dialog table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.results-dialog table th, .results-dialog table td {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 480px) {
|
||||
.results-dialog {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
width: 600px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.results td {
|
||||
padding: 0.4em 0;
|
||||
}
|
|
@ -1,6 +1,19 @@
|
|||
const dialogContainerId = "#dialog-container"
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
document.querySelectorAll(".dropdown").forEach(el => {
|
||||
const action = el.querySelector(".dropdown-action");
|
||||
const content = el.querySelector(".dropdown-content");
|
||||
|
||||
console.log(action, content, el.offsetWidth, content.offsetWidth);
|
||||
|
||||
action.addEventListener('click', () => {
|
||||
content.classList.toggle('selected');
|
||||
console.log(content.offsetWidth - action.offsetWidth);
|
||||
content.style.marginLeft = `-${(content.offsetWidth - action.offsetWidth)}px`;
|
||||
})
|
||||
})
|
||||
|
||||
// configure radio button groups
|
||||
document.querySelectorAll("section.radio-group").forEach(el => {
|
||||
const submittedInput = el.querySelector("input[type=hidden]");
|
||||
|
@ -23,7 +36,13 @@ window.addEventListener('load', () => {
|
|||
let response = await fetch(btn.dataset.url);
|
||||
if(response.ok) {
|
||||
response = await response.text();
|
||||
const container = document.querySelector(dialogContainerId);
|
||||
let container = document.querySelector(dialogContainerId);
|
||||
if(container == null) {
|
||||
const node = document.createElement('div');
|
||||
node.id = "dialog-container";
|
||||
document.querySelector("body").appendChild(node);
|
||||
container = document.querySelector(dialogContainerId);
|
||||
}
|
||||
container.innerHTML = response;
|
||||
const dialogReference = document.querySelector(selector);
|
||||
dialogReference?.showModal();
|
||||
|
|
Before Width: | Height: | Size: 795 B After Width: | Height: | Size: 791 B |
Before Width: | Height: | Size: 807 B After Width: | Height: | Size: 804 B |
Before Width: | Height: | Size: 602 B After Width: | Height: | Size: 599 B |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 696 B After Width: | Height: | Size: 693 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
@ -1,5 +1,11 @@
|
|||
/* cleanup */
|
||||
|
||||
:root {
|
||||
--primary: #9222dd;
|
||||
--primary-darker: #6400a2;
|
||||
--primary-contrast: #eee;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
|
@ -10,22 +16,26 @@
|
|||
header {
|
||||
padding: 1rem 2rem;
|
||||
width: 100%;
|
||||
background: #666;
|
||||
background: var(--primary);
|
||||
color: var(--primary-contrast);
|
||||
}
|
||||
header h1 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
header a {
|
||||
color: #fff;
|
||||
color: var(--primary-contrast);
|
||||
text-decoration: none;
|
||||
}
|
||||
button {
|
||||
all: unset;
|
||||
padding: 0.25em 0.5em;
|
||||
background: #dddddd;
|
||||
border: solid 1px #cccccc;
|
||||
color: var(--primary-contrast);
|
||||
background: var(--primary);
|
||||
border: solid 1px var(--primary-darker);
|
||||
border-radius: 0.25em;
|
||||
font-size: 0.875em;
|
||||
|
||||
--text: var(--primary-contrast);
|
||||
}
|
||||
select {
|
||||
all: unset;
|
||||
|
@ -63,6 +73,10 @@ nav a.selected, nav button.selected {
|
|||
color: white;
|
||||
}
|
||||
|
||||
table {
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
|
||||
table:not(.clean) {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -71,7 +85,8 @@ table th, table td {
|
|||
text-align: left;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
/*border-collapse: collapse;*/
|
||||
}
|
||||
table thead * {
|
||||
background: #666;
|
||||
|
@ -184,9 +199,9 @@ section.radio-group div input:checked+label {
|
|||
color: white;
|
||||
}
|
||||
|
||||
img.icon {
|
||||
height: 1.5em;
|
||||
width: 1.5em;
|
||||
img.icon, button.icon svg {
|
||||
height: 1.2em;
|
||||
width: 1.2em;
|
||||
}
|
||||
button.icon, a.icon {
|
||||
display: flex;
|
||||
|
@ -194,6 +209,11 @@ button.icon, a.icon {
|
|||
gap: 0.3em;
|
||||
}
|
||||
|
||||
button.icon svg, button.icon svg * {
|
||||
fill: var(--text, var(--primary-contrast));
|
||||
color: var(--text, var(--primary-contrast));
|
||||
stroke: var(--text, var(--primary-contrast));
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
|
|
19
handlers.go
|
@ -1,12 +1,15 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"holiday-api/holiday"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -26,9 +29,9 @@ func getHolidays(service holiday.HolidayService) gin.HandlerFunc {
|
|||
|
||||
holidays, err := service.Find(search, paging)
|
||||
if err != nil {
|
||||
render(c, http.StatusNotFound, ErrorResponse{Created: time.Now(), Message: "failed fetching holidays"})
|
||||
render(c, http.StatusNotFound, ErrorResponse{Created: time.Now(), Message: "failed fetching holidays"}, nil)
|
||||
} else {
|
||||
render(c, http.StatusOK, mapHolidays(holidays))
|
||||
render(c, http.StatusOK, mapHolidays(holidays), search.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +57,18 @@ type HolidayResponse struct {
|
|||
Holidays []HolidayItemResponse `json:"holidays"`
|
||||
}
|
||||
|
||||
func (h HolidayResponse) CSV() []byte {
|
||||
buffer := bytes.Buffer{}
|
||||
csvWriter := csv.NewWriter(&buffer)
|
||||
csvWriter.Write([]string{"id", "date", "name", "description", "isStateHoliday", "isReligiousHoliday"})
|
||||
|
||||
for _, item := range h.Holidays {
|
||||
csvWriter.Write([]string{item.Id.String(), item.Date.String(), item.Name, item.Description, strconv.FormatBool(item.IsStateHoliday), strconv.FormatBool(item.IsReligiousHoliday)})
|
||||
}
|
||||
csvWriter.Flush()
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
type HolidayItemResponse struct {
|
||||
XMLName xml.Name `json:"-" xml:"Holiday"`
|
||||
Id uuid.UUID `json:"id" xml:"id,attr"`
|
||||
|
|
|
@ -34,6 +34,7 @@ type Search struct {
|
|||
RangeEnd *time.Time `form:"range_end" time_format:"2006-01-02"`
|
||||
StateHoliday string `form:"state_holiday,omitempty" binding:"omitempty"`
|
||||
ReligiousHoliday string `form:"religious_holiday,omitempty" binding:"omitempty"`
|
||||
Type *string `form:"type,omitempty" binding:"omitempty"`
|
||||
}
|
||||
|
||||
func (s Search) IsStateHoliday() *bool {
|
||||
|
|
24
main.go
|
@ -62,16 +62,20 @@ func main() {
|
|||
setupAdminDashboard(g.Group("/admin"), holidayService, countryService, yearService)
|
||||
|
||||
g.GET("/", func(c *gin.Context) {
|
||||
year := time.Now().Year()
|
||||
search := holiday.Search{Country: "HR", Year: &year}
|
||||
search := holiday.Search{Country: "HR", Year: nil}
|
||||
if err := c.ShouldBindQuery(&search); err != nil {
|
||||
c.AbortWithError(http.StatusBadRequest, err)
|
||||
return
|
||||
}
|
||||
holidays, _ := holidayService.Find(search, holiday.Paging{PageSize: 100})
|
||||
|
||||
countries, _ := countryService.Find()
|
||||
years, _ := yearService.Find()
|
||||
c.HTML(http.StatusOK, "index.gohtml", gin.H{"Years": years, "Countries": countries, "Search": search, "Holidays": mapHolidays(holidays).Holidays})
|
||||
if search.Year == nil {
|
||||
c.HTML(http.StatusOK, "index.gohtml", gin.H{"Years": years, "Countries": countries, "Search": search})
|
||||
return
|
||||
}
|
||||
holidays, _ := holidayService.Find(search, holiday.Paging{PageSize: 100})
|
||||
c.HTML(http.StatusOK, "results.gohtml", gin.H{"Years": years, "Countries": countries, "Search": search, "Holidays": mapHolidays(holidays).Holidays})
|
||||
})
|
||||
g.GET("/documentation", func(c *gin.Context) {
|
||||
countries, _ := countryService.Find()
|
||||
|
@ -113,9 +117,11 @@ func loadTemplates(g *gin.Engine) {
|
|||
}
|
||||
return false
|
||||
},
|
||||
"importSvg": IncludeHTML,
|
||||
})
|
||||
g.LoadHTMLFiles(
|
||||
"templates/index.gohtml",
|
||||
"templates/results.gohtml",
|
||||
"templates/search.gohtml",
|
||||
"templates/documentation.gohtml",
|
||||
|
||||
|
@ -132,6 +138,16 @@ func loadTemplates(g *gin.Engine) {
|
|||
)
|
||||
}
|
||||
|
||||
func IncludeHTML(path string) template.HTML {
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Println("includeHTML - error reading file: %v", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return template.HTML(string(b))
|
||||
}
|
||||
|
||||
func setupAdminDashboard(adminDashboard *gin.RouterGroup, service holiday.HolidayService, countryService holiday.CountryService, yearService holiday.YearService) {
|
||||
adminDashboard.Use(gin.BasicAuth(loadAuth()))
|
||||
|
||||
|
|
28
render.go
|
@ -4,12 +4,34 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func render[T any](c *gin.Context, status int, response T) {
|
||||
switch c.GetHeader("accept") {
|
||||
type CSV interface {
|
||||
CSV() []byte
|
||||
}
|
||||
|
||||
func render(c *gin.Context, status int, response any, contentType *string) {
|
||||
value := c.GetHeader("accept")
|
||||
if contentType != nil {
|
||||
switch *contentType {
|
||||
case "xml":
|
||||
value = "text/xml"
|
||||
case "json":
|
||||
value = "application/json"
|
||||
case "csv":
|
||||
value = "text/csv"
|
||||
}
|
||||
}
|
||||
switch value {
|
||||
case "text/csv":
|
||||
if csvResponse, ok := response.(CSV); ok {
|
||||
c.Data(200, value+"; charset=utf-8", csvResponse.CSV())
|
||||
} else {
|
||||
c.Header("content-type", "application/json; charset=utf-8")
|
||||
c.JSON(status, response)
|
||||
}
|
||||
case "application/xml":
|
||||
fallthrough
|
||||
case "text/xml":
|
||||
c.Header("content-type", c.GetHeader("accept")+"; charset=utf-8")
|
||||
c.Header("content-type", value+"; charset=utf-8; header=present;")
|
||||
c.XML(status, response)
|
||||
case "application/json":
|
||||
fallthrough
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
</section>
|
||||
<section class="actions">
|
||||
<button class="icon primary" type="submit">
|
||||
<img class="icon" src="/assets/images/search.svg">
|
||||
{{ importSvg "/assets/icons/search.svg"}}
|
||||
<span>Search</span>
|
||||
</button>
|
||||
</section>
|
||||
|
@ -85,11 +85,11 @@
|
|||
<tr>
|
||||
<td>{{$entry.Name}}</td>
|
||||
<td>{{$entry.Date.Format "2006-01-02"}}</td>
|
||||
<td><img class="icon" src="{{if $entry.IsStateHoliday}}/assets/images/done-v.svg{{else}}/assets/images/close-x.svg{{end}}"></td>
|
||||
<td><img class="icon" src="{{if $entry.IsReligiousHoliday}}/assets/images/done-v.svg{{else}}/assets/images/close-x.svg{{end}}"></td>
|
||||
<td><img class="icon" src="{{if $entry.IsStateHoliday}}/assets/icons/done-v.svg{{else}}/assets/icons/close-x.svg{{end}}"></td>
|
||||
<td><img class="icon" src="{{if $entry.IsReligiousHoliday}}/assets/icons/done-v.svg{{else}}/assets/icons/close-x.svg{{end}}"></td>
|
||||
<td>
|
||||
<button data-type="dialog" data-trigger="#update-card" data-url="/admin/dialogs/edit-holiday?id={{$entry.Id}}" class="icon"><img class="icon" src="/assets/images/edit.svg"></button>
|
||||
<button data-type="dialog" data-trigger="#delete-card" data-url="/admin/dialogs/delete-holiday?id={{$entry.Id}}" class="icon"><img class="icon" src="/assets/images/trash-delete.svg"></button>
|
||||
<button data-type="dialog" data-trigger="#update-card" data-url="/admin/dialogs/edit-holiday?id={{$entry.Id}}" class="icon"><img class="icon" src="/assets/icons/edit.svg"></button>
|
||||
<button data-type="dialog" data-trigger="#delete-card" data-url="/admin/dialogs/delete-holiday?id={{$entry.Id}}" class="icon"><img class="icon" src="/assets/icons/trash-delete.svg"></button>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
|
|
@ -46,8 +46,8 @@
|
|||
<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>
|
||||
<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/icons/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/icons/trash-delete.svg"></button>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<dialog class="card" data-closeable id="check-is-a-holiday">
|
||||
<div style="display: flex;">
|
||||
<h2 style="margin-right: 1em;">Is it a holiday?</h2>
|
||||
<button onclick="closeDialog('#check-is-a-holiday')" class="clean icon"><img class="icon" src="/assets/images/close-x.svg"></button>
|
||||
<button onclick="closeDialog('#check-is-a-holiday')" class="clean icon"><img class="icon" src="/assets/icons/close-x.svg"></button>
|
||||
</div>
|
||||
<form method="get" action="/search">
|
||||
<section>
|
||||
|
|
|
@ -1,95 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Holiday-api</title>
|
||||
<link rel="stylesheet" href="/assets/style.css">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
||||
<link rel="stylesheet" href="/assets/global.css">
|
||||
<script src="/assets/global.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="dialog-container"></div>
|
||||
<header>
|
||||
<section class="container">
|
||||
<div class="background-image">
|
||||
<article class="dialog search-dialog">
|
||||
<h1><a href="/">Holiday-api</a></h1>
|
||||
</section>
|
||||
</header>
|
||||
<nav>
|
||||
<section class="container">
|
||||
<a class="selected" href="#">Search</a>
|
||||
<a href="/documentation">Documentation</a>
|
||||
<button data-type="dialog" data-trigger="#check-is-a-holiday" data-url="/dialogs/check-is-a-holiday">Check is a holiday</button>
|
||||
</section>
|
||||
</nav>
|
||||
<main>
|
||||
<section id="search">
|
||||
<article class="card">
|
||||
<form action="/" method="get">
|
||||
<section>
|
||||
<label for="country">Country:</label>
|
||||
<select id="country" name="country">
|
||||
{{range $entry := .Countries}}
|
||||
<option {{if eq $.Search.Country $entry.IsoName}}selected{{end}} value="{{$entry.IsoName}}">{{$entry.Name}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</section>
|
||||
<section>
|
||||
<label for="year">Year:</label>
|
||||
<select id="year" name="year">
|
||||
{{range $entry := .Years}}
|
||||
<option {{if intpeq $.Search.Year $entry}}selected{{end}} value="{{$entry}}">{{$entry}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</section>
|
||||
<section class="radio-group">
|
||||
<label>Is state holiday:</label>
|
||||
<div>
|
||||
<input type="radio" value="true" name="sh" id="sh_true"><label for="sh_true">True
|
||||
</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>
|
||||
</div>
|
||||
<input type="hidden" value="{{.Search.StateHoliday}}" name="state_holiday">
|
||||
</section>
|
||||
<section class="radio-group">
|
||||
<label>Is religious holiday:</label>
|
||||
<div>
|
||||
<input type="radio" value="true" name="rh" id="rh_true"><label for="rh_true">True
|
||||
</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>
|
||||
</div>
|
||||
<input type="hidden" value="{{.Search.ReligiousHoliday}}" name="religious_holiday">
|
||||
</section>
|
||||
<section class="actions">
|
||||
<button class="icon primary" type="submit">
|
||||
<img class="icon" src="/assets/images/search.svg">
|
||||
<span>Search</span>
|
||||
</button>
|
||||
</section>
|
||||
</form>
|
||||
</article>
|
||||
</section>
|
||||
<section id="results">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Date</th>
|
||||
<th>State</th>
|
||||
<th>Religious</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $entry := .Holidays}}
|
||||
<tr>
|
||||
<td>{{$entry.Name}}</td>
|
||||
<td>{{$entry.Date.Format "2006-01-02"}}</td>
|
||||
<td><img class="icon" src="{{if $entry.IsStateHoliday}}/assets/images/done-v.svg{{else}}/assets/images/close-x.svg{{end}}"></td>
|
||||
<td><img class="icon" src="{{if $entry.IsReligiousHoliday}}/assets/images/done-v.svg{{else}}/assets/images/close-x.svg{{end}}"></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</main>
|
||||
<form action="/">
|
||||
<div class="form-field">
|
||||
<label for="year">Za godinu:</label>
|
||||
<select id="year" name="year">
|
||||
{{range $entry := .Years}}
|
||||
<option {{if intpeq $.Search.Year $entry}}selected{{end}} value="{{$entry}}">{{$entry}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="button-actions">
|
||||
<button class="primary">Pretraži</button>
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,88 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Holiday-api | {{deferint $.Search.Year}}</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
||||
<link rel="stylesheet" href="/assets/global.css">
|
||||
<script src="/assets/global.js"></script>
|
||||
|
||||
<style>
|
||||
.dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.dropdown-content {
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color: #f9f9f9;
|
||||
min-width: 160px;
|
||||
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.dropdown-content.selected {
|
||||
display: block;
|
||||
}
|
||||
.dropdown-content a {
|
||||
all: unset;
|
||||
display: block;
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
.dropdown-content a:hover {
|
||||
background: rgba(95, 158, 160, 0.2);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="background-image" style="position: fixed;"></div>
|
||||
<article class="dialog results-dialog">
|
||||
<section style="margin-bottom: 1em;">
|
||||
<h1><a href="/">Holiday-api</a> | {{deferint $.Search.Year}}</h1>
|
||||
<form style="display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; gap: 1em" action="/">
|
||||
<div style="flex-grow: 1;"></div>
|
||||
<div class="form-field" style="width: auto;" >
|
||||
<label for="year">Za godinu:</label>
|
||||
<select id="year" name="year" style="width: 150px">
|
||||
{{range $entry := .Years}}
|
||||
<option {{if intpeq $.Search.Year $entry}}selected{{end}} value="{{$entry}}">{{$entry}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="button-actions">
|
||||
<button class="primary">Pretraži</button>
|
||||
</div>
|
||||
<div class="button-actions" style="margin-left: auto">
|
||||
<div class="dropdown">
|
||||
<button type="button" class="secondary dropdown-action">Preuzmi</button>
|
||||
<div class="dropdown-content">
|
||||
<a target="_blank" href="/api/v1/holidays?country=HR&type=csv&year={{deferint $.Search.Year}}">Kao csv</a>
|
||||
<a target="_blank" href="/api/v1/holidays?country=HR&type=json&year={{deferint $.Search.Year}}">Kao json</a>
|
||||
<a target="_blank" href="/api/v1/holidays?country=HR&type=xml&year={{deferint $.Search.Year}}">Kao xml</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<table class="results">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Ime</th>
|
||||
<th>Datum</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $entry := .Holidays}}
|
||||
<tr>
|
||||
<td>{{$entry.Name}}</td>
|
||||
<td>{{$entry.Date.Format "02.01.2006."}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
|
@ -60,11 +60,11 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<th>Is state holiday: </th>
|
||||
<td><img class="icon" src="{{if $entry.IsStateHoliday}}/assets/images/done-v.svg{{else}}/assets/images/close-x.svg{{end}}"></td>
|
||||
<td><img class="icon" src="{{if $entry.IsStateHoliday}}/assets/icons/done-v.svg{{else}}/assets/images/close-x.svg{{end}}"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Is religious holiday: </th>
|
||||
<td><img class="icon" src="{{if $entry.IsReligiousHoliday}}/assets/images/done-v.svg{{else}}/assets/images/close-x.svg{{end}}"></td>
|
||||
<td><img class="icon" src="{{if $entry.IsReligiousHoliday}}/assets/icons/done-v.svg{{else}}/assets/images/close-x.svg{{end}}"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|