campingmontagut/pkg/template/page.go

186 lines
4.5 KiB
Go
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* SPDX-FileCopyrightText: 2023 jordi fita mas <jfita@peritasoft.com>
* SPDX-License-Identifier: AGPL-3.0-only
*/
package template
import (
"context"
"fmt"
gotemplate "html/template"
"net/http"
"sort"
"time"
"dev.tandem.ws/tandem/camper/pkg/auth"
"dev.tandem.ws/tandem/camper/pkg/database"
httplib "dev.tandem.ws/tandem/camper/pkg/http"
"dev.tandem.ws/tandem/camper/pkg/locale"
)
type PublicPage struct {
LocalizedAlternates []*LocalizedAlternate
Menu *siteMenu
CompanyAddress *address
WeatherForecast *WeatherForecast
OpeningDates gotemplate.HTML
}
func NewPublicPage() *PublicPage {
return &PublicPage{
CompanyAddress: &address{},
WeatherForecast: &WeatherForecast{},
}
}
func (p *PublicPage) Setup(r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
schema := httplib.Protocol(r)
authority := httplib.Host(r)
_, path := httplib.ShiftPath(r.RequestURI)
for _, l := range company.Locales {
p.LocalizedAlternates = append(p.LocalizedAlternates, &LocalizedAlternate{
Lang: l.Language.String(),
Endonym: l.Endonym,
HRef: fmt.Sprintf("%s://%s/%s%s", schema, authority, l.Language, path),
})
}
sort.Slice(p.LocalizedAlternates, func(i, j int) bool { return p.LocalizedAlternates[i].Lang < p.LocalizedAlternates[j].Lang })
p.Menu = &siteMenu{
CampsiteTypes: mustCollectMenuItems(r.Context(), conn, user.Locale, `
select coalesce(i18n.name, campsite_type.name) as l10n_name
, '/campsites/types/' || slug
from campsite_type
left join campsite_type_i18n as i18n on campsite_type.campsite_type_id = i18n.campsite_type_id and i18n.lang_tag = $1
where company_id = $2
and active
order by position, l10n_name
`, user.Locale.Language, company.ID),
}
if err := conn.QueryRow(r.Context(), `
select coalesce(i18n.opening_dates, location.opening_dates)
from location
left join location_i18n as i18n on location.company_id = i18n.company_id and i18n.lang_tag = $1
where location.company_id = $2
`, user.Locale.Language, company.ID).Scan(&p.OpeningDates); err != nil {
if !database.ErrorIsNotFound(err) {
panic(err)
}
}
if err := p.CompanyAddress.FillFromDatabase(r.Context(), conn, user, company); err != nil {
panic(err)
}
if err := p.WeatherForecast.FillFromDatabase(r.Context(), conn); err != nil {
panic(err)
}
}
type LocalizedAlternate struct {
Lang string
HRef string
Endonym string
}
type siteMenu struct {
CampsiteTypes []*menuItem
}
type menuItem struct {
Label string
HRef string
}
func mustCollectMenuItems(ctx context.Context, conn *database.Conn, loc *locale.Locale, sql string, args ...interface{}) []*menuItem {
rows, err := conn.Query(ctx, sql, args...)
if err != nil {
panic(err)
}
defer rows.Close()
localePath := "/" + loc.Language.String()
var items []*menuItem
for rows.Next() {
item := &menuItem{}
err = rows.Scan(&item.Label, &item.HRef)
if err != nil {
panic(err)
}
item.HRef = localePath + item.HRef
items = append(items, item)
}
if rows.Err() != nil {
panic(rows.Err())
}
return items
}
type address struct {
TradeName string
Address string
PostalCode string
Province string
City string
Country string
Phone string
Email string
RTCNumber string
}
func (addr *address) FillFromDatabase(ctx context.Context, conn *database.Conn, user *auth.User, company *auth.Company) error {
row := conn.QueryRow(ctx, `
select trade_name
, address
, postal_code
, province
, city
, coalesce(country_i18n.name, country.name) as country_name
, phone::text
, email::text
, rtc_number
from company
join country using (country_code)
left join country_i18n on country.country_code = country_i18n.country_code and country_i18n.lang_tag = $2
where company_id = $1
`, company.ID, user.Locale.Language)
return row.Scan(
&addr.TradeName,
&addr.Address,
&addr.PostalCode,
&addr.Province,
&addr.City,
&addr.Country,
&addr.Phone,
&addr.Email,
&addr.RTCNumber,
)
}
type WeatherForecast struct {
WeatherConditionId string
DayTemperature string
MinTemperature string
ForecastedAt time.Time
}
func (fc *WeatherForecast) FillFromDatabase(ctx context.Context, conn *database.Conn) error {
row := conn.QueryRow(ctx, `
select weather_condition_id
, ceil(day_temperature) || '°'
, ceil(min_temperature) || '° C'
, forecasted_at
from weather_forecast
limit 1
`)
return row.Scan(
&fc.WeatherConditionId,
&fc.DayTemperature,
&fc.MinTemperature,
&fc.ForecastedAt,
)
}