diff --git a/pkg/season/admin.go b/pkg/season/admin.go index a2268e5..5ab5e38 100644 --- a/pkg/season/admin.go +++ b/pkg/season/admin.go @@ -9,6 +9,9 @@ import ( "context" "fmt" "net/http" + "net/url" + "strconv" + "strings" "time" "github.com/jackc/pgtype" @@ -56,10 +59,12 @@ func (h *AdminHandler) Handler(user *auth.User, company *auth.Company, conn *dat } case "range": switch r.Method { + case http.MethodGet: + serveSeasonCalendar(w, r, user, company, conn) case http.MethodPut: updateSeasonCalendar(w, r, user, company, conn) default: - httplib.MethodNotAllowed(w, r, http.MethodGet) + httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPut) } default: if !uuid.Valid(head) { @@ -91,7 +96,7 @@ func serveSeasonIndex(w http.ResponseWriter, r *http.Request, user *auth.User, c if err != nil { panic(err) } - calendar, err := collectSeasonCalendar(r.Context(), company, conn, 2023) + calendar, err := collectSeasonCalendar(r.Context(), company, conn, getCalendarYear(r.URL.Query())) if err != nil { panic(err) } @@ -103,6 +108,16 @@ func serveSeasonIndex(w http.ResponseWriter, r *http.Request, user *auth.User, c page.MustRender(w, r, user, company) } +func getCalendarYear(query url.Values) int { + yearStr := strings.TrimSpace(query.Get("year")) + if yearStr != "" { + if year, err := strconv.Atoi(yearStr); err == nil { + return year + } + } + return time.Now().Year() +} + func collectSeasonEntries(ctx context.Context, company *auth.Company, conn *database.Conn) ([]*seasonEntry, error) { rows, err := conn.Query(ctx, ` select slug @@ -167,7 +182,9 @@ func collectSeasonCalendar(ctx context.Context, company *auth.Company, conn *dat var month *seasonMonth var week seasonWeek - calendar := &seasonCalendar{} + calendar := &seasonCalendar{ + Year: year, + } weekday := int(time.Monday) for rows.Next() { day := &seasonDay{} @@ -213,6 +230,7 @@ func collectSeasonCalendar(ctx context.Context, company *auth.Company, conn *dat } type seasonCalendar struct { + Year int Months []*seasonMonth Form *calendarForm } @@ -327,6 +345,11 @@ func (f *seasonForm) MustRender(w http.ResponseWriter, r *http.Request, user *au template.MustRenderAdmin(w, r, user, company, "season/form.gohtml", f) } +func serveSeasonCalendar(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) { + f := newCalendarForm(r.Context(), company, conn) + f.MustRender(w, r, user, company, conn, getCalendarYear(r.URL.Query())) +} + func updateSeasonCalendar(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) { f := newCalendarForm(r.Context(), company, conn) if ok, err := form.Handle(f, w, r, user); err != nil { @@ -346,7 +369,7 @@ func updateSeasonCalendar(w http.ResponseWriter, r *http.Request, user *auth.Use f.StartDate.Val = "" f.EndDate.Val = "" } - f.MustRender(w, r, user, company, conn) + f.MustRender(w, r, user, company, conn, getCalendarYear(r.Form)) } type calendarForm struct { @@ -427,8 +450,8 @@ func (f *calendarForm) Valid(l *locale.Locale) bool { return v.AllOK } -func (f *calendarForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) { - calendar, err := collectSeasonCalendar(r.Context(), company, conn, 2023) +func (f *calendarForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, year int) { + calendar, err := collectSeasonCalendar(r.Context(), company, conn, year) if err != nil { panic(err) } diff --git a/pkg/template/render.go b/pkg/template/render.go index 3a89e82..ca645e0 100644 --- a/pkg/template/render.go +++ b/pkg/template/render.go @@ -78,6 +78,12 @@ func mustRenderLayout(w io.Writer, user *auth.User, company *auth.Company, templ "queryEscape": func(s string) string { return url.QueryEscape(s) }, + "inc": func(i int) int { + return i + 1 + }, + "dec": func(i int) int { + return i - 1 + }, }) templates = append(templates, "form.gohtml") files := make([]string, len(templates)) diff --git a/web/static/camper.css b/web/static/camper.css index fb8defe..4418d6b 100644 --- a/web/static/camper.css +++ b/web/static/camper.css @@ -228,14 +228,14 @@ body > a[href="#content"]:focus { } /* header */ -header { +body > header { display: flex; justify-content: space-between; align-items: center; background-color: var(--camper--header--background-color); } -header, body > nav a { +body > header, body > nav a { padding: 0 3rem; } @@ -497,7 +497,38 @@ textarea { background-color: #ffeeaa; } -.season-calendar { +/* calendar */ +.season-calendar button { + display: flex; + gap: 1em; + border: none; + cursor: pointer; +} + +.season-calendar form button:first-child, .season-calendar > header button { + min-width: 0; +} + +.season-calendar > header { + display: flex; + gap: 2rem; + justify-content: center; + align-items: center; +} + +.season-calendar > header button:first-of-type { + order: -1; +} + +.season-calendar > header button:first-of-type::before { + content: "←"; +} + +.season-calendar > header button:last-of-type::before { + content: "→"; +} + +.season-calendar > div { display: grid; grid-template-columns: repeat(3, auto); grid-auto-rows: 1fr; @@ -507,11 +538,11 @@ textarea { } @media (max-width: 48rem) { - .season-calendar { + .season-calendar > div { display: flex; flex-direction: column; } - + .season-calendar table { width: 100%; } @@ -553,22 +584,14 @@ textarea { height: .8rem; } -.season-calendar form button { - display: flex; - gap: 1em; - border: none; - cursor: pointer; -} - .season-calendar form button:first-child { - min-width: 0; position: absolute; top: 0; right: 0; background-color: transparent; } -.season-calendar form button:hover, .season-calendar form button:first-child:hover { +.season-calendar form button:hover, .season-calendar form button:first-child:hover { background-color: var(--camper--color--hay); } diff --git a/web/templates/admin/season/calendar.gohtml b/web/templates/admin/season/calendar.gohtml index ce33880..2b44d5b 100644 --- a/web/templates/admin/season/calendar.gohtml +++ b/web/templates/admin/season/calendar.gohtml @@ -1,58 +1,69 @@ -
+
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/season.seasonCalendar*/ -}} - {{ range .Months -}} - - - - - - - - - - - - - - - {{ range .Weeks }} +
+

{{ .Year }}

+ + +
+
+ {{ range .Months -}} +
{{ pgettext .Name "month" }}
{{(pgettext "Mon" "day" )}}{{(pgettext "Tue" "day" )}}{{(pgettext "Wed" "day" )}}{{(pgettext "Thu" "day" )}}{{(pgettext "Fri" "day" )}}{{(pgettext "Sat" "day" )}}{{(pgettext "Sun" "day" )}}
+ + - {{- range . }} - - {{- end }} + + + + + + + - {{- end }} - -
{{ pgettext .Name "month" }}
- {{- if .Color -}} - - {{- end -}} - {{(pgettext "Mon" "day" )}}{{(pgettext "Tue" "day" )}}{{(pgettext "Wed" "day" )}}{{(pgettext "Thu" "day" )}}{{(pgettext "Fri" "day" )}}{{(pgettext "Sat" "day" )}}{{(pgettext "Sun" "day" )}}
- {{- end }} - {{ with .Form }} - -
- {{ CSRFInput }} - {{ with .StartDate }}{{ end }} - {{ with .EndDate }}{{ end }} -
- - {{ range .Seasons -}} - - {{- end }} -
-
-
- {{ end }} + + + {{ range .Weeks }} + + {{- range . }} + + {{- if .Color -}} + + {{- end -}} + + {{- end }} + + {{- end }} + + + {{- end }} + {{ with .Form }} + +
+ {{ CSRFInput }} + + {{ with .StartDate }}{{ end }} + {{ with .EndDate }}{{ end }} + +
+
+ {{ end }} +
- +