diff --git a/pkg/campsite/public.go b/pkg/campsite/public.go index 9bb4bf0..2b469d1 100644 --- a/pkg/campsite/public.go +++ b/pkg/campsite/public.go @@ -7,11 +7,14 @@ package campsite import ( "net/http" + "time" "dev.tandem.ws/tandem/camper/pkg/auth" "dev.tandem.ws/tandem/camper/pkg/campsite/types" "dev.tandem.ws/tandem/camper/pkg/database" httplib "dev.tandem.ws/tandem/camper/pkg/http" + "dev.tandem.ws/tandem/camper/pkg/season" + "dev.tandem.ws/tandem/camper/pkg/template" ) type PublicHandler struct { @@ -32,6 +35,33 @@ func (h *PublicHandler) Handler(user *auth.User, company *auth.Company, conn *da switch head { case "types": h.types.Handler(user, company, conn).ServeHTTP(w, r) + case "calendar": + h.calendarHandler(user, company, conn).ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +func (h *PublicHandler) calendarHandler(user *auth.User, company *auth.Company, conn *database.Conn) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var head string + head, r.URL.Path = httplib.ShiftPath(r.URL.Path) + switch head { + case "": + switch r.Method { + case http.MethodGet: + year := season.GetCalendarYear(r.URL.Query()) + month := season.GetCalendarMonth(r.URL.Query()) + date := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC).AddDate(0, 1, 0) + calendar, err := season.CollectSeasonCalendar(r.Context(), company, conn, date.Year(), date.Month(), 3) + if err != nil { + panic(err) + } + template.MustRenderPublicNoLayout(w, r, user, company, "campsite/calendar.gohtml", calendar) + default: + httplib.MethodNotAllowed(w, r, http.MethodGet) + } default: http.NotFound(w, r) } diff --git a/pkg/campsite/types/public.go b/pkg/campsite/types/public.go index 0017f87..2dda5ad 100644 --- a/pkg/campsite/types/public.go +++ b/pkg/campsite/types/public.go @@ -94,7 +94,8 @@ func newPublicPage(ctx context.Context, company *auth.Company, conn *database.Co if err != nil { return nil, err } - calendar, err := season.CollectSeasonCalendar(ctx, company, conn, time.Now().Year()) + now := time.Now() + calendar, err := season.CollectSeasonCalendar(ctx, company, conn, now.Year(), now.Month(), 6) if err != nil { return nil, err } diff --git a/pkg/media/admin.go b/pkg/media/admin.go index 46a1a7a..3cf9c54 100644 --- a/pkg/media/admin.go +++ b/pkg/media/admin.go @@ -197,11 +197,11 @@ func newMediaPickerWithUploadForm(f *uploadForm, query url.Values) *mediaPicker } func (picker *mediaPicker) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) { - template.MustRenderNoLayout(w, r, user, company, "media/picker.gohtml", picker) + template.MustRenderAdminNoLayout(w, r, user, company, "media/picker.gohtml", picker) } func (picker *mediaPicker) MustRenderField(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) { - template.MustRenderNoLayout(w, r, user, company, "media/field.gohtml", picker.Field) + template.MustRenderAdminNoLayout(w, r, user, company, "media/field.gohtml", picker.Field) } func uploadMedia(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) { diff --git a/pkg/season/admin.go b/pkg/season/admin.go index 85dc484..26c276e 100644 --- a/pkg/season/admin.go +++ b/pkg/season/admin.go @@ -124,7 +124,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, getCalendarYear(r.URL.Query())) + calendar, err := CollectSeasonCalendar(r.Context(), company, conn, GetCalendarYear(r.URL.Query()), time.January, 12) if err != nil { panic(err) } @@ -136,7 +136,7 @@ func serveSeasonIndex(w http.ResponseWriter, r *http.Request, user *auth.User, c page.MustRender(w, r, user, company) } -func getCalendarYear(query url.Values) int { +func GetCalendarYear(query url.Values) int { yearStr := strings.TrimSpace(query.Get("year")) if yearStr != "" { if year, err := strconv.Atoi(yearStr); err == nil { @@ -146,6 +146,16 @@ func getCalendarYear(query url.Values) int { return time.Now().Year() } +func GetCalendarMonth(query url.Values) time.Month { + monthStr := strings.TrimSpace(query.Get("month")) + if monthStr != "" { + if month, err := strconv.Atoi(monthStr); err == nil { + return time.Month(month) + } + } + return time.Now().Month() +} + func collectSeasonEntries(ctx context.Context, company *auth.Company, conn *database.Conn) ([]*seasonEntry, error) { rows, err := conn.Query(ctx, ` select '/admin/seasons/' || season.slug @@ -213,9 +223,9 @@ var longMonthNames = []string{ locale.PgettextNoop("December", "month"), } -func CollectSeasonCalendar(ctx context.Context, company *auth.Company, conn *database.Conn, year int) (*Calendar, error) { - firstDay := time.Date(year, time.January, 1, 0, 0, 0, 0, time.UTC) - lastDay := time.Date(year, time.December, 31, 23, 59, 59, 0, time.UTC) +func CollectSeasonCalendar(ctx context.Context, company *auth.Company, conn *database.Conn, year int, firstMonth time.Month, monthCount int) (*Calendar, error) { + firstDay := time.Date(year, firstMonth, 1, 0, 0, 0, 0, time.UTC) + lastDay := firstDay.AddDate(0, monthCount, 0).Add(-1 * time.Second) rows, err := conn.Query(ctx, ` select t.day::date , to_color(coalesce(color, $1)) as color @@ -248,6 +258,7 @@ func CollectSeasonCalendar(ctx context.Context, company *auth.Company, conn *dat calendar.Months = append(calendar.Months, month) } month = &Month{ + Year: day.Date.Year(), Month: dayMonth, Name: longMonthNames[dayMonth-1], } @@ -283,6 +294,7 @@ type Calendar struct { } type Month struct { + Year int Month time.Month Name string Weeks []Week @@ -394,7 +406,7 @@ func (f *seasonForm) MustRender(w http.ResponseWriter, r *http.Request, user *au 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())) + 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) { @@ -416,7 +428,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, getCalendarYear(r.Form)) + f.MustRender(w, r, user, company, conn, GetCalendarYear(r.Form)) } type calendarForm struct { @@ -498,10 +510,10 @@ func (f *calendarForm) Valid(l *locale.Locale) bool { } 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) + calendar, err := CollectSeasonCalendar(r.Context(), company, conn, year, time.January, 12) if err != nil { panic(err) } calendar.Form = f - template.MustRenderNoLayout(w, r, user, company, "season/calendar.gohtml", calendar) + template.MustRenderAdminNoLayout(w, r, user, company, "season/calendar.gohtml", calendar) } diff --git a/pkg/template/render.go b/pkg/template/render.go index 3d882eb..b6347db 100644 --- a/pkg/template/render.go +++ b/pkg/template/render.go @@ -14,6 +14,7 @@ import ( "path" "strconv" "strings" + "time" "dev.tandem.ws/tandem/camper/pkg/auth" httplib "dev.tandem.ws/tandem/camper/pkg/http" @@ -44,7 +45,7 @@ func MustRenderAdminFiles(w io.Writer, r *http.Request, user *auth.User, company mustRenderLayout(w, user, company, adminTemplateFile, data, filenames...) } -func MustRenderNoLayout(w io.Writer, r *http.Request, user *auth.User, company *auth.Company, filename string, data interface{}) { +func MustRenderAdminNoLayout(w io.Writer, r *http.Request, user *auth.User, company *auth.Company, filename string, data interface{}) { mustRenderLayout(w, user, company, adminTemplateFile, data, filename) } @@ -53,6 +54,10 @@ func MustRenderPublic(w io.Writer, r *http.Request, user *auth.User, company *au mustRenderLayout(w, user, company, publicTemplateFile, data, layout, filename) } +func MustRenderPublicNoLayout(w io.Writer, r *http.Request, user *auth.User, company *auth.Company, filename string, data interface{}) { + mustRenderLayout(w, user, company, publicTemplateFile, data, filename) +} + func MustRenderPublicFiles(w io.Writer, r *http.Request, user *auth.User, company *auth.Company, data interface{}, filenames ...string) { layout := "layout.gohtml" filenames = append([]string{layout}, filenames...) @@ -95,6 +100,18 @@ func mustRenderLayout(w io.Writer, user *auth.User, company *auth.Company, templ "dec": func(i int) int { return i - 1 }, + "int": func(v interface{}) int { + switch v := v.(type) { + case int: + return v + case time.Weekday: + return int(v) + case time.Month: + return int(v) + default: + panic(fmt.Errorf("Could not convert to integer")) + } + }, "hexToDec": func(s string) int { num, _ := strconv.ParseInt(s, 16, 0) return int(num) diff --git a/web/static/public.css b/web/static/public.css index 33ff9c4..3eb4c93 100644 --- a/web/static/public.css +++ b/web/static/public.css @@ -835,14 +835,10 @@ dt { .campsite_type_calendar > header { display: flex; gap: 2rem; - justify-content: center; + justify-content: space-between; align-items: center; } -.campsite_type_calendar > header button:first-of-type { - order: -1; -} - .campsite_type_calendar > header button:first-of-type::before { content: "←"; } @@ -852,27 +848,20 @@ dt { } .campsite_type_calendar > div { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(27rem, 1fr)); - grid-auto-rows: 1fr; - justify-content: center; - align-items: start; + display: flex; + flex-wrap: nowrap; gap: 1em; -} - -@media (max-width: 48rem) { - .campsite_type_calendar > div { - display: flex; - flex-direction: column; - } - - .campsite_type_calendar table { - width: 100%; - } + overflow-x: scroll; + scroll-behavior: smooth; + scrollbar-width: none; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; } .campsite_type_calendar table { border-collapse: collapse; + flex: 0 0 27rem; + height: 100%; } .campsite_type_calendar td { diff --git a/web/templates/public/campsite/calendar.gohtml b/web/templates/public/campsite/calendar.gohtml index 4817d27..09233a1 100644 --- a/web/templates/public/campsite/calendar.gohtml +++ b/web/templates/public/campsite/calendar.gohtml @@ -2,48 +2,42 @@ SPDX-FileCopyrightText: 2023 jordi fita mas SPDX-License-Identifier: AGPL-3.0-only --> -
- {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/season.Calendar*/ -}} -
-

{{ .Year }}

- - -
-
- {{ range .Months -}} - - - - - - - - - - - - - - - {{ range .Weeks }} - - {{- range . }} - - {{- end }} - +{{ $last := dec (len .Months)}} +{{ range $index, $month := .Months -}} + {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/season.Month */ -}} +
{{ pgettext .Name "month" }}
{{(pgettext "Mon" "day" )}}{{(pgettext "Tue" "day" )}}{{(pgettext "Wed" "day" )}}{{(pgettext "Thu" "day" )}}{{(pgettext "Fri" "day" )}}{{(pgettext "Sat" "day" )}}{{(pgettext "Sun" "day" )}}
- {{- if .Color -}} - - {{- end -}} -
+ + + + + + + + + + + + + + {{ range .Weeks }} + + {{- range . }} + {{- end }} - -
{{ pgettext .Name "month" }} {{ .Year }}
{{(pgettext "Mon" "day" )}}{{(pgettext "Tue" "day" )}}{{(pgettext "Wed" "day" )}}{{(pgettext "Thu" "day" )}}{{(pgettext "Fri" "day" )}}{{(pgettext "Sat" "day" )}}{{(pgettext "Sun" "day" )}}
+ {{- if .Color -}} + + {{- end -}} +
+ {{- end }} -
-
+ + +{{- end }} diff --git a/web/templates/public/campsite/type.gohtml b/web/templates/public/campsite/type.gohtml index 4ece1c5..d90f140 100644 --- a/web/templates/public/campsite/type.gohtml +++ b/web/templates/public/campsite/type.gohtml @@ -82,7 +82,17 @@ {{- end }} - {{ template "calendar.gohtml" .Calendar }} +
+ {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/season.Calendar*/ -}} +
+

{{( pgettext "Calendar" "title" )}}

+ + +
+
+ {{ template "calendar.gohtml" .Calendar }} +
+
{{ with .Features -}}
@@ -112,4 +122,20 @@ {{ template "carouselInit" }} + + {{- end }} diff --git a/web/templates/public/layout.gohtml b/web/templates/public/layout.gohtml index b42f510..a36e7ac 100644 --- a/web/templates/public/layout.gohtml +++ b/web/templates/public/layout.gohtml @@ -16,6 +16,7 @@ {{ range .LocalizedAlternates -}} {{ end }} + {{- block "head" . }}{{ end }}