Replace contact page with location

Customer does not want a contact page, but a page where they can write
the direction on how to reach the campground, with a Google map embed
instead of using Leaflet, because Google Maps shows the reviews right
in the map.

That means i had to replace the GPS locations with XML fields for the
customer to write.  In all four languages.

This time i tried a translation approach inspired by PrestaShop: instead
of opening a new page for each language, i have all languages in the
same page and use AlpineJS to show just a single language.  It is far
easier to write the translations, even though you do not have the source
text visible, specially in this section that there is no place for me
to put the language links.
This commit is contained in:
jordi fita mas 2023-12-21 21:17:04 +01:00
parent 84423166e1
commit ff6e9497b5
51 changed files with 1187 additions and 1178 deletions

View File

@ -18,10 +18,6 @@ values (52, 'localhost:8080')
, (52, 'camper.tandem.ws')
;
insert into company_geography (company_id, geog)
values (52, 'SRID=4326;POINT(2.598825853208973 42.24256146290889)')
;
insert into company_user (company_id, user_id, role)
values (52, 42, 'employee')
, (52, 43, 'admin')

View File

@ -1,8 +0,0 @@
-- Deploy camper:extension_postgis to pg
-- requires: schema_public
begin;
create extension if not exists postgis;
commit;

View File

@ -1,33 +1,33 @@
-- Deploy camper:company_geography to pg
-- requires: roles
-- Deploy camper:location to pg
-- requires: schema_camper
-- requires: roles
-- requires: company
-- requires: user_profile
-- requires: extension_postgis
begin;
set search_path to camper, public;
create table company_geography (
create table location (
company_id integer primary key references company,
geog geography not null
directions xml not null,
map_embed xml not null,
opening_dates xml not null
);
grant select on table company_geography to guest;
grant select on table company_geography to employee;
grant select, insert, update, delete on table company_geography to admin;
grant select on table location to guest;
grant select on table location to employee;
grant select, insert, update, delete on table location to admin;
alter table company_geography enable row level security;
alter table location enable row level security;
create policy guest_ok
on company_geography
on location
for select
using (true)
;
create policy insert_to_company
on company_geography
on location
for insert
with check (
company_id in (select company_id from user_profile)
@ -35,7 +35,7 @@ with check (
;
create policy update_company
on company_geography
on location
for update
using (
company_id in (select company_id from user_profile)
@ -43,7 +43,7 @@ using (
;
create policy delete_from_company
on company_geography
on location
for delete
using (
company_id in (select company_id from user_profile)

23
deploy/location_i18n.sql Normal file
View File

@ -0,0 +1,23 @@
-- Deploy camper:location_i18n to pg
-- requires: roles
-- requires: schema_camper
-- requires: location
-- requires: language
begin;
set search_path to camper, public;
create table location_i18n (
company_id integer not null references location,
lang_tag text not null references language,
directions xml not null,
opening_dates xml not null,
primary key (company_id, lang_tag)
);
grant select on table location_i18n to guest;
grant select on table location_i18n to employee;
grant select, insert, update, delete on table location_i18n to admin;
commit;

26
deploy/setup_location.sql Normal file
View File

@ -0,0 +1,26 @@
-- Deploy camper:setup_location to pg
-- requires: roles
-- requires: schema_camper
-- requires: location
begin;
set search_path to camper, public;
create or replace function setup_location(company integer, directions text, map_embed text, opening_dates text) returns void as
$$
insert into location (company_id, directions, map_embed, opening_dates)
values (company, xmlparse(content directions), xmlparse(content map_embed), xmlparse(content opening_dates))
on conflict (company_id) do update
set directions = excluded.directions
, map_embed = excluded.map_embed
, opening_dates = excluded.opening_dates
;
$$
language sql
;
revoke execute on function setup_location(integer, text, text, text) from public;
grant execute on function setup_location(integer, text, text, text) to admin;
commit;

View File

@ -0,0 +1,25 @@
-- Deploy camper:translate_location to pg
-- requires: roles
-- requires: schema_camper
-- requires: location_i18n
begin;
set search_path to camper, public;
create or replace function translate_location(company_id integer, lang_tag text, directions text, opening_dates text) returns void as
$$
insert into location_i18n (company_id, lang_tag, directions, opening_dates)
values (company_id, lang_tag, xmlparse(content coalesce(directions, '')), xmlparse(content coalesce(opening_dates, '')))
on conflict (company_id, lang_tag) do update
set directions = excluded.directions
, opening_dates = excluded.opening_dates
;
$$
language sql
;
revoke execute on function translate_location(integer, text, text, text) from public;
grant execute on function translate_location(integer, text, text, text) to admin;
commit;

View File

@ -6,6 +6,7 @@
package app
import (
"dev.tandem.ws/tandem/camper/pkg/location"
"net/http"
"dev.tandem.ws/tandem/camper/pkg/auth"
@ -26,6 +27,7 @@ type adminHandler struct {
campsite *campsite.AdminHandler
company *company.AdminHandler
home *home.AdminHandler
location *location.AdminHandler
media *media.AdminHandler
payment *booking.AdminHandler
season *season.AdminHandler
@ -37,6 +39,7 @@ func newAdminHandler(locales locale.Locales, mediaDir string) *adminHandler {
campsite: campsite.NewAdminHandler(locales),
company: company.NewAdminHandler(),
home: home.NewAdminHandler(locales),
location: location.NewAdminHandler(),
media: media.NewAdminHandler(mediaDir),
payment: booking.NewAdminHandler(),
season: season.NewAdminHandler(locales),
@ -66,6 +69,8 @@ func (h *adminHandler) Handle(user *auth.User, company *auth.Company, conn *data
h.company.Handler(user, company, conn).ServeHTTP(w, r)
case "home":
h.home.Handler(user, company, conn).ServeHTTP(w, r)
case "location":
h.location.Handler(user, company, conn).ServeHTTP(w, r)
case "media":
h.media.Handler(user, company, conn).ServeHTTP(w, r)
case "payment":

View File

@ -14,6 +14,7 @@ import (
"dev.tandem.ws/tandem/camper/pkg/database"
"dev.tandem.ws/tandem/camper/pkg/home"
httplib "dev.tandem.ws/tandem/camper/pkg/http"
"dev.tandem.ws/tandem/camper/pkg/location"
"dev.tandem.ws/tandem/camper/pkg/services"
"dev.tandem.ws/tandem/camper/pkg/template"
)
@ -22,6 +23,7 @@ type publicHandler struct {
home *home.PublicHandler
booking *booking.PublicHandler
campsite *campsite.PublicHandler
location *location.PublicHandler
services *services.PublicHandler
}
@ -30,6 +32,7 @@ func newPublicHandler() *publicHandler {
home: home.NewPublicHandler(),
booking: booking.NewPublicHandler(),
campsite: campsite.NewPublicHandler(),
location: location.NewPublicHandler(),
services: services.NewPublicHandler(),
}
}
@ -47,8 +50,8 @@ func (h *publicHandler) Handler(user *auth.User, company *auth.Company, conn *da
campgroundHandler(user, company, conn).ServeHTTP(w, r)
case "campsites":
h.campsite.Handler(user, company, conn).ServeHTTP(w, r)
case "contact":
contactHandler(user, company, conn).ServeHTTP(w, r)
case "location":
h.location.Handler(user, company, conn).ServeHTTP(w, r)
case "services":
h.services.Handler(user, company, conn).ServeHTTP(w, r)
case "surroundings":
@ -98,53 +101,3 @@ func campgroundHandler(user *auth.User, company *auth.Company, conn *database.Co
}
})
}
func contactHandler(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:
page := newContactPage()
page.MustRender(w, r, user, company, conn)
default:
httplib.MethodNotAllowed(w, r, http.MethodGet)
}
default:
http.NotFound(w, r)
}
})
}
type contactPage struct {
*template.PublicPage
CompanyGeography *geographyPoint
}
func newContactPage() *contactPage {
page := &contactPage{
PublicPage: template.NewPublicPage(),
}
return page
}
func (p *contactPage) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
p.Setup(r, user, company, conn)
row := conn.QueryRow(r.Context(), `select st_x(geog::geometry)::text, st_y(geog::geometry)::text from company_geography where company_id = $1`, company.ID)
geography := &geographyPoint{}
if err := row.Scan(&geography.Lng, &geography.Lat); err != nil {
if !database.ErrorIsNotFound(err) {
panic(err)
}
} else {
p.CompanyGeography = geography
}
template.MustRenderPublic(w, r, user, company, "contact.gohtml", p)
}
type geographyPoint struct {
Lat string
Lng string
}

View File

@ -7,19 +7,22 @@ package auth
import (
"context"
"golang.org/x/text/language"
"dev.tandem.ws/tandem/camper/pkg/database"
"dev.tandem.ws/tandem/camper/pkg/locale"
)
type Company struct {
ID int
Locales locale.Locales
ID int
DefaultLanguage language.Tag
Locales locale.Locales
}
func CompanyByHost(ctx context.Context, conn *database.Conn, host string, allLocales locale.Locales) (*Company, error) {
company := &Company{
Locales: allLocales,
Locales: allLocales,
DefaultLanguage: language.Catalan,
}
if err := conn.QueryRow(ctx, `
select company_id

View File

@ -92,3 +92,13 @@ func (c *Conn) OrderSeasons(ctx context.Context, slugs []string) error {
_, err := c.Exec(ctx, "select order_seasons($1)", slugs)
return err
}
func (tx *Tx) SetupLocation(ctx context.Context, companyID int, directions string, mapEmbed string, openingHours string) error {
_, err := tx.Exec(ctx, "select setup_location($1, $2, $3, $4)", companyID, directions, mapEmbed, openingHours)
return err
}
func (tx *Tx) TranslateLocation(ctx context.Context, companyID int, langTag language.Tag, directions string, openingHours string) error {
_, err := tx.Exec(ctx, "select translate_location($1, $2, $3, $4)", companyID, langTag, directions, openingHours)
return err
}

View File

@ -7,6 +7,7 @@ package form
import (
"database/sql/driver"
"dev.tandem.ws/tandem/camper/pkg/locale"
"net/http"
"strconv"
"strings"
@ -50,3 +51,21 @@ type L10nInput struct {
Input
Source string
}
type I18nInput map[string]*Input
func NewI18nInput(locales locale.Locales, name string) I18nInput {
input := make(I18nInput)
for lang := range locales {
input[lang.String()] = &Input{
Name: name + "." + lang.String(),
}
}
return input
}
func (input I18nInput) FillValue(r *http.Request) {
for _, inner := range input {
inner.FillValue(r)
}
}

156
pkg/location/admin.go Normal file
View File

@ -0,0 +1,156 @@
package location
import (
"context"
"golang.org/x/text/language"
"net/http"
"github.com/jackc/pgx/v4"
"dev.tandem.ws/tandem/camper/pkg/auth"
"dev.tandem.ws/tandem/camper/pkg/database"
"dev.tandem.ws/tandem/camper/pkg/form"
httplib "dev.tandem.ws/tandem/camper/pkg/http"
"dev.tandem.ws/tandem/camper/pkg/locale"
"dev.tandem.ws/tandem/camper/pkg/template"
)
type AdminHandler struct {
}
func NewAdminHandler() *AdminHandler {
return &AdminHandler{}
}
func (h *AdminHandler) Handler(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:
f := newLocationForm(company.Locales)
if err := f.FillFromDatabase(r.Context(), company, conn); err != nil {
if !database.ErrorIsNotFound(err) {
panic(err)
}
}
f.MustRender(w, r, user, company)
case http.MethodPut:
updateLocationSettings(w, r, user, company, conn)
default:
httplib.MethodNotAllowed(w, r, http.MethodGet)
}
default:
http.NotFound(w, r)
}
})
}
func updateLocationSettings(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
f := newLocationForm(company.Locales)
if ok, err := form.Handle(f, w, r, user); err != nil {
return
} else if !ok {
f.MustRender(w, r, user, company)
return
}
tx := conn.MustBegin(r.Context())
defer tx.Rollback(r.Context())
var defaultLang = company.DefaultLanguage.String()
if err := tx.SetupLocation(r.Context(), company.ID, f.Directions[defaultLang].Val, f.MapEmbed.Val, f.OpeningDates[defaultLang].Val); err != nil {
panic(err)
}
for lang := range company.Locales {
if lang == company.DefaultLanguage {
continue
}
if err := tx.TranslateLocation(r.Context(), company.ID, lang, f.Directions[lang.String()].Val, f.OpeningDates[lang.String()].Val); err != nil {
panic(err)
}
}
tx.MustCommit(r.Context())
httplib.Redirect(w, r, "/admin/location", http.StatusSeeOther)
}
type locationForm struct {
Directions form.I18nInput
MapEmbed *form.Input
OpeningDates form.I18nInput
}
func newLocationForm(locales locale.Locales) *locationForm {
return &locationForm{
Directions: form.NewI18nInput(locales, "directions"),
MapEmbed: &form.Input{
Name: "map_embed",
},
OpeningDates: form.NewI18nInput(locales, "opening_dates"),
}
}
func (f *locationForm) FillFromDatabase(ctx context.Context, company *auth.Company, conn *database.Conn) error {
var directions database.RecordArray
var openingDates database.RecordArray
err := conn.QueryRow(ctx, `
select location.directions::text
, location.map_embed::text
, location.opening_dates::text
, array_agg((lang_tag, i18n.directions::text))
, array_agg((lang_tag, i18n.opening_dates::text))
from location
left join location_i18n as i18n using (company_id)
where company_id = $1
group by location.directions::text
, location.map_embed::text
, location.opening_dates::text
`, pgx.QueryResultFormats{pgx.BinaryFormatCode}, company.ID).Scan(
&f.Directions[company.DefaultLanguage.String()].Val,
&f.MapEmbed.Val,
&f.OpeningDates[company.DefaultLanguage.String()].Val,
&directions,
&openingDates,
)
if err != nil {
return err
}
if err := fillI18nInput(f.Directions, directions); err != nil {
return err
}
if err := fillI18nInput(f.OpeningDates, openingDates); err != nil {
return err
}
return nil
}
func fillI18nInput(input form.I18nInput, array database.RecordArray) error {
for _, el := range array.Elements {
tag, err := language.Parse(el.Fields[0].Get().(string))
if err != nil {
return err
}
input[tag.String()].Val = el.Fields[1].Get().(string)
}
return nil
}
func (f *locationForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
template.MustRenderAdmin(w, r, user, company, "location.gohtml", f)
}
func (f *locationForm) Parse(r *http.Request) error {
if err := r.ParseForm(); err != nil {
return err
}
f.Directions.FillValue(r)
f.MapEmbed.FillValue(r)
f.OpeningDates.FillValue(r)
return nil
}
func (f *locationForm) Valid(l *locale.Locale) bool {
v := form.NewValidator(l)
return v.AllOK
}

67
pkg/location/public.go Normal file
View File

@ -0,0 +1,67 @@
package location
import (
"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/template"
gotemplate "html/template"
"net/http"
)
type PublicHandler struct {
}
func NewPublicHandler() *PublicHandler {
return &PublicHandler{}
}
func (h *PublicHandler) Handler(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:
page := newLocationPage()
page.MustRender(w, r, user, company, conn)
default:
httplib.MethodNotAllowed(w, r, http.MethodGet)
}
default:
http.NotFound(w, r)
}
})
}
type locationPage struct {
*template.PublicPage
Directions gotemplate.HTML
MapEmbed gotemplate.HTML
}
func newLocationPage() *locationPage {
page := &locationPage{
PublicPage: template.NewPublicPage(),
}
return page
}
func (p *locationPage) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
p.Setup(r, user, company, conn)
row := conn.QueryRow(r.Context(), `
select coalesce(i18n.directions, location.directions)::text
, map_embed::text
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)
if err := row.Scan(&p.Directions, &p.MapEmbed); err != nil {
if !database.ErrorIsNotFound(err) {
panic(err)
}
}
template.MustRenderPublic(w, r, user, company, "location.gohtml", p)
}

View File

@ -8,6 +8,7 @@ package template
import (
"context"
"fmt"
gotemplate "html/template"
"net/http"
"sort"
@ -21,6 +22,7 @@ type PublicPage struct {
LocalizedAlternates []*LocalizedAlternate
Menu *siteMenu
CompanyAddress *address
OpeningDates gotemplate.HTML
}
func NewPublicPage() *PublicPage {
@ -54,6 +56,15 @@ func (p *PublicPage) Setup(r *http.Request, user *auth.User, company *auth.Compa
`, 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 {
panic(err)
}
if err := p.CompanyAddress.FillFromDatabase(r.Context(), conn, user, company); err != nil {
panic(err)
}
@ -113,19 +124,19 @@ type address struct {
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
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,

120
po/ca.po
View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2023-12-21 17:32+0100\n"
"POT-Creation-Date: 2023-12-21 21:08+0100\n"
"PO-Revision-Date: 2023-07-22 23:45+0200\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Catalan <ca@dodds.net>\n"
@ -43,7 +43,7 @@ msgstr "Ha fallat el pagament"
#: web/templates/public/services.gohtml:6
#: web/templates/public/services.gohtml:15
#: web/templates/public/layout.gohtml:52 web/templates/public/layout.gohtml:79
#: web/templates/public/layout.gohtml:51 web/templates/public/layout.gohtml:79
#: web/templates/admin/services/index.gohtml:66
msgctxt "title"
msgid "Services"
@ -53,6 +53,14 @@ msgstr "Serveis"
msgid "The campsite offers many different services."
msgstr "El càmping disposa de diversos serveis."
#: web/templates/public/location.gohtml:6
#: web/templates/public/location.gohtml:12
#: web/templates/public/layout.gohtml:53 web/templates/public/layout.gohtml:81
#: web/templates/admin/layout.gohtml:60
msgctxt "title"
msgid "Location"
msgstr "Com arribar"
#: web/templates/public/home.gohtml:6 web/templates/public/layout.gohtml:37
#: web/templates/public/layout.gohtml:77
msgctxt "title"
@ -79,7 +87,7 @@ msgstr "Vine a gaudir!"
#: web/templates/public/home.gohtml:35
#: web/templates/public/surroundings.gohtml:6
#: web/templates/public/surroundings.gohtml:10
#: web/templates/public/layout.gohtml:53 web/templates/public/layout.gohtml:80
#: web/templates/public/layout.gohtml:52 web/templates/public/layout.gohtml:80
msgctxt "title"
msgid "Surroundings"
msgstr "Lentorn"
@ -268,14 +276,8 @@ msgctxt "title"
msgid "Campground"
msgstr "El càmping"
#: web/templates/public/contact.gohtml:6 web/templates/public/contact.gohtml:18
#: web/templates/public/layout.gohtml:54 web/templates/public/layout.gohtml:81
msgctxt "title"
msgid "Contact"
msgstr "Contacte"
#: web/templates/public/booking.gohtml:6 web/templates/public/booking.gohtml:11
#: web/templates/public/layout.gohtml:51
#: web/templates/public/layout.gohtml:54
msgctxt "title"
msgid "Booking"
msgstr "Reserva"
@ -364,18 +366,18 @@ msgid "I have read and I accept the reservation conditions"
msgstr "He llegit i accepto les condicions de reserves"
#: web/templates/public/layout.gohtml:11 web/templates/public/layout.gohtml:32
#: web/templates/public/layout.gohtml:110
#: web/templates/public/layout.gohtml:109
msgid "Campsite Montagut"
msgstr "Càmping Montagut"
#: web/templates/public/layout.gohtml:23 web/templates/admin/layout.gohtml:19
#: web/templates/public/layout.gohtml:23 web/templates/admin/layout.gohtml:20
msgid "Skip to main content"
msgstr "Salta al contingut principal"
#: web/templates/public/layout.gohtml:42 web/templates/public/layout.gohtml:88
#: web/templates/admin/campsite/index.gohtml:6
#: web/templates/admin/campsite/index.gohtml:12
#: web/templates/admin/layout.gohtml:44 web/templates/admin/layout.gohtml:75
#: web/templates/admin/layout.gohtml:45 web/templates/admin/layout.gohtml:79
msgctxt "title"
msgid "Campsites"
msgstr "Allotjaments"
@ -385,7 +387,12 @@ msgctxt "title"
msgid "Sections"
msgstr "Apartats"
#: web/templates/public/layout.gohtml:107
#: web/templates/public/layout.gohtml:99
msgctxt "title"
msgid "Opening"
msgstr "Obertura"
#: web/templates/public/layout.gohtml:106
msgid "<abbr title=\"Catalonia Tourism Registry\">RTC</abbr> <abbr title=\"Number\">#</abbr>%s"
msgstr "<abbr title=\"Número\">Núm.</abbr> <abbr title=\"Registre de Turisme de Catalunya\">RTC</abbr> %s"
@ -482,6 +489,33 @@ msgctxt "action"
msgid "Translate"
msgstr "Tradueix"
#: web/templates/admin/location.gohtml:6 web/templates/admin/location.gohtml:12
msgctxt "title"
msgid "Location Settings"
msgstr "Paràmetres de com arribar"
#: web/templates/admin/location.gohtml:17
msgctxt "input"
msgid "Directions"
msgstr "Instruccions"
#: web/templates/admin/location.gohtml:35
msgctxt "input"
msgid "Opening Dates"
msgstr "Data dobertura"
#: web/templates/admin/location.gohtml:53
msgctxt "input"
msgid "Map Embed"
msgstr "Incrustació del mapa"
#: web/templates/admin/location.gohtml:60 web/templates/admin/payment.gohtml:62
#: web/templates/admin/profile.gohtml:75
#: web/templates/admin/taxDetails.gohtml:152
msgctxt "action"
msgid "Save changes"
msgstr "Desa els canvis"
#: web/templates/admin/campsite/feature/form.gohtml:8
#: web/templates/admin/campsite/feature/form.gohtml:25
msgctxt "title"
@ -802,7 +836,7 @@ msgstr "Descripció"
#: web/templates/admin/campsite/type/index.gohtml:6
#: web/templates/admin/campsite/type/index.gohtml:12
#: web/templates/admin/layout.gohtml:41
#: web/templates/admin/layout.gohtml:42
msgctxt "title"
msgid "Campsite Types"
msgstr "Tipus dallotjaments"
@ -877,7 +911,7 @@ msgstr "Color"
#: web/templates/admin/season/index.gohtml:6
#: web/templates/admin/season/index.gohtml:12
#: web/templates/admin/layout.gohtml:47
#: web/templates/admin/layout.gohtml:48
msgctxt "title"
msgid "Seasons"
msgstr "Temporades"
@ -909,7 +943,7 @@ msgid "Cancel"
msgstr "Canceŀla"
#: web/templates/admin/payment.gohtml:6 web/templates/admin/payment.gohtml:12
#: web/templates/admin/layout.gohtml:38
#: web/templates/admin/layout.gohtml:39
msgctxt "title"
msgid "Payment Settings"
msgstr "Paràmetres de pagament"
@ -944,14 +978,8 @@ msgctxt "title"
msgid "Integration"
msgstr "Integració"
#: web/templates/admin/payment.gohtml:62 web/templates/admin/profile.gohtml:75
#: web/templates/admin/taxDetails.gohtml:152
msgctxt "action"
msgid "Save changes"
msgstr "Desa els canvis"
#: web/templates/admin/dashboard.gohtml:6
#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:72
#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:76
msgctxt "title"
msgid "Dashboard"
msgstr "Tauler"
@ -984,7 +1012,7 @@ msgid "New Service"
msgstr "Nou servei"
#: web/templates/admin/services/index.gohtml:6
#: web/templates/admin/layout.gohtml:56
#: web/templates/admin/layout.gohtml:57
msgctxt "title"
msgid "Services Page"
msgstr "Pàgina de serveis"
@ -1020,7 +1048,7 @@ msgid "Translate Service to %s"
msgstr "Traducció del servei a %s"
#: web/templates/admin/profile.gohtml:6 web/templates/admin/profile.gohtml:12
#: web/templates/admin/layout.gohtml:31
#: web/templates/admin/layout.gohtml:32
msgctxt "title"
msgid "Profile"
msgstr "Perfil"
@ -1112,29 +1140,29 @@ msgctxt "input"
msgid "Legal Disclaimer"
msgstr "Nota legal"
#: web/templates/admin/layout.gohtml:27
#: web/templates/admin/layout.gohtml:28
msgctxt "title"
msgid "User Menu"
msgstr "Menú dusuari"
#: web/templates/admin/layout.gohtml:35
#: web/templates/admin/layout.gohtml:36
msgctxt "title"
msgid "Company Settings"
msgstr "Paràmetres de lempresa"
#: web/templates/admin/layout.gohtml:50
#: web/templates/admin/layout.gohtml:51
#: web/templates/admin/media/index.gohtml:6
#: web/templates/admin/media/index.gohtml:11
msgctxt "title"
msgid "Media"
msgstr "Mèdia"
#: web/templates/admin/layout.gohtml:53 web/templates/admin/home/index.gohtml:6
#: web/templates/admin/layout.gohtml:54 web/templates/admin/home/index.gohtml:6
msgctxt "title"
msgid "Home Page"
msgstr "Pàgina dinici"
#: web/templates/admin/layout.gohtml:61
#: web/templates/admin/layout.gohtml:65
msgctxt "action"
msgid "Logout"
msgstr "Surt"
@ -1265,7 +1293,7 @@ msgstr "Lidioma escollit no és vàlid."
msgid "File must be a valid PNG or JPEG image."
msgstr "El fitxer has de ser una imatge PNG o JPEG vàlida."
#: pkg/app/admin.go:56
#: pkg/app/admin.go:59
msgid "Access forbidden"
msgstr "Accés prohibit"
@ -1534,55 +1562,55 @@ msgstr "No podeu deixar el fitxer del mèdia en blanc."
msgid "Filename can not be empty."
msgstr "No podeu deixar el nom del fitxer en blanc."
#: pkg/booking/admin.go:74
#: pkg/booking/admin.go:76
msgctxt "redsys environment"
msgid "Test"
msgstr "Proves"
#: pkg/booking/admin.go:78
#: pkg/booking/admin.go:80
msgctxt "redsys environment"
msgid "Live"
msgstr "Real"
#: pkg/booking/admin.go:87
#: pkg/booking/admin.go:89
msgctxt "redsys integration"
msgid "InSite"
msgstr "InSite"
#: pkg/booking/admin.go:91
#: pkg/booking/admin.go:93
msgctxt "redsys integration"
msgid "Redirect"
msgstr "Redirecció"
#: pkg/booking/admin.go:135
#: pkg/booking/admin.go:137
msgid "Merchant code can not be empty."
msgstr "No podeu deixar el codi del comerç en blanc."
#: pkg/booking/admin.go:136
#: pkg/booking/admin.go:138
msgid "Merchant code must be exactly nine digits long."
msgstr "El codi del comerç ha de ser de nou dígits."
#: pkg/booking/admin.go:137
#: pkg/booking/admin.go:139
msgid "Merchant code must be a number."
msgstr "El codi del comerç."
#: pkg/booking/admin.go:141
#: pkg/booking/admin.go:143
msgid "Terminal number can not be empty."
msgstr "No podeu deixar el número del terminal en blanc."
#: pkg/booking/admin.go:142
#: pkg/booking/admin.go:144
msgid "Terminal number must be a number between 1 and 999."
msgstr "El número del terminal ha de ser entre 1 i 999"
#: pkg/booking/admin.go:150
#: pkg/booking/admin.go:152
msgid "Selected environment is not valid."
msgstr "Lentorn escollit no és vàlid."
#: pkg/booking/admin.go:151
#: pkg/booking/admin.go:153
msgid "Selected integration is not valid."
msgstr "La integració escollida no és vàlida."
#: pkg/booking/admin.go:154
#: pkg/booking/admin.go:156
msgid "The merchant key is not valid."
msgstr "Aquesta clau del comerç no és vàlid."
@ -1638,6 +1666,10 @@ msgstr "El valor de %s ha de ser com a mínim %d."
msgid "%s must be at most %d."
msgstr "El valor de %s ha de ser com a màxim %d."
#~ msgctxt "title"
#~ msgid "Contact"
#~ msgstr "Contacte"
#~ msgctxt "title"
#~ msgid "Party Details"
#~ msgstr "Dades dels visitants"

120
po/es.po
View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2023-12-21 17:32+0100\n"
"POT-Creation-Date: 2023-12-21 21:08+0100\n"
"PO-Revision-Date: 2023-07-22 23:46+0200\n"
"Last-Translator: jordi fita mas <jordi@tandem.blog>\n"
"Language-Team: Spanish <es@tp.org.es>\n"
@ -43,7 +43,7 @@ msgstr "Pago fallido"
#: web/templates/public/services.gohtml:6
#: web/templates/public/services.gohtml:15
#: web/templates/public/layout.gohtml:52 web/templates/public/layout.gohtml:79
#: web/templates/public/layout.gohtml:51 web/templates/public/layout.gohtml:79
#: web/templates/admin/services/index.gohtml:66
msgctxt "title"
msgid "Services"
@ -53,6 +53,14 @@ msgstr "Servicios"
msgid "The campsite offers many different services."
msgstr "El camping dispone de varios servicios."
#: web/templates/public/location.gohtml:6
#: web/templates/public/location.gohtml:12
#: web/templates/public/layout.gohtml:53 web/templates/public/layout.gohtml:81
#: web/templates/admin/layout.gohtml:60
msgctxt "title"
msgid "Location"
msgstr "Cómo llegar"
#: web/templates/public/home.gohtml:6 web/templates/public/layout.gohtml:37
#: web/templates/public/layout.gohtml:77
msgctxt "title"
@ -79,7 +87,7 @@ msgstr "¡Ven a disfrutar!"
#: web/templates/public/home.gohtml:35
#: web/templates/public/surroundings.gohtml:6
#: web/templates/public/surroundings.gohtml:10
#: web/templates/public/layout.gohtml:53 web/templates/public/layout.gohtml:80
#: web/templates/public/layout.gohtml:52 web/templates/public/layout.gohtml:80
msgctxt "title"
msgid "Surroundings"
msgstr "El entorno"
@ -268,14 +276,8 @@ msgctxt "title"
msgid "Campground"
msgstr "El camping"
#: web/templates/public/contact.gohtml:6 web/templates/public/contact.gohtml:18
#: web/templates/public/layout.gohtml:54 web/templates/public/layout.gohtml:81
msgctxt "title"
msgid "Contact"
msgstr "Contacto"
#: web/templates/public/booking.gohtml:6 web/templates/public/booking.gohtml:11
#: web/templates/public/layout.gohtml:51
#: web/templates/public/layout.gohtml:54
msgctxt "title"
msgid "Booking"
msgstr "Reserva"
@ -364,18 +366,18 @@ msgid "I have read and I accept the reservation conditions"
msgstr "He leído y acepto las condiciones de reserva"
#: web/templates/public/layout.gohtml:11 web/templates/public/layout.gohtml:32
#: web/templates/public/layout.gohtml:110
#: web/templates/public/layout.gohtml:109
msgid "Campsite Montagut"
msgstr "Camping Montagut"
#: web/templates/public/layout.gohtml:23 web/templates/admin/layout.gohtml:19
#: web/templates/public/layout.gohtml:23 web/templates/admin/layout.gohtml:20
msgid "Skip to main content"
msgstr "Saltar al contenido principal"
#: web/templates/public/layout.gohtml:42 web/templates/public/layout.gohtml:88
#: web/templates/admin/campsite/index.gohtml:6
#: web/templates/admin/campsite/index.gohtml:12
#: web/templates/admin/layout.gohtml:44 web/templates/admin/layout.gohtml:75
#: web/templates/admin/layout.gohtml:45 web/templates/admin/layout.gohtml:79
msgctxt "title"
msgid "Campsites"
msgstr "Alojamientos"
@ -385,7 +387,12 @@ msgctxt "title"
msgid "Sections"
msgstr "Apartados"
#: web/templates/public/layout.gohtml:107
#: web/templates/public/layout.gohtml:99
msgctxt "title"
msgid "Opening"
msgstr "Apertura"
#: web/templates/public/layout.gohtml:106
msgid "<abbr title=\"Catalonia Tourism Registry\">RTC</abbr> <abbr title=\"Number\">#</abbr>%s"
msgstr "<abbr title=\"Número\">Nº</abbr> <abbr title=\"Registro de Turismo de Cataluña\">RTC</abbr> %s"
@ -482,6 +489,33 @@ msgctxt "action"
msgid "Translate"
msgstr "Traducir"
#: web/templates/admin/location.gohtml:6 web/templates/admin/location.gohtml:12
msgctxt "title"
msgid "Location Settings"
msgstr "Parámetros de cómo llegar"
#: web/templates/admin/location.gohtml:17
msgctxt "input"
msgid "Directions"
msgstr "Instrucciones"
#: web/templates/admin/location.gohtml:35
msgctxt "input"
msgid "Opening Dates"
msgstr "Fechas de apertura"
#: web/templates/admin/location.gohtml:53
msgctxt "input"
msgid "Map Embed"
msgstr "Incrustación del mapa"
#: web/templates/admin/location.gohtml:60 web/templates/admin/payment.gohtml:62
#: web/templates/admin/profile.gohtml:75
#: web/templates/admin/taxDetails.gohtml:152
msgctxt "action"
msgid "Save changes"
msgstr "Guardar los cambios"
#: web/templates/admin/campsite/feature/form.gohtml:8
#: web/templates/admin/campsite/feature/form.gohtml:25
msgctxt "title"
@ -802,7 +836,7 @@ msgstr "Descripción"
#: web/templates/admin/campsite/type/index.gohtml:6
#: web/templates/admin/campsite/type/index.gohtml:12
#: web/templates/admin/layout.gohtml:41
#: web/templates/admin/layout.gohtml:42
msgctxt "title"
msgid "Campsite Types"
msgstr "Tipos de alojamientos"
@ -877,7 +911,7 @@ msgstr "Color"
#: web/templates/admin/season/index.gohtml:6
#: web/templates/admin/season/index.gohtml:12
#: web/templates/admin/layout.gohtml:47
#: web/templates/admin/layout.gohtml:48
msgctxt "title"
msgid "Seasons"
msgstr "Temporadas"
@ -909,7 +943,7 @@ msgid "Cancel"
msgstr "Cancelar"
#: web/templates/admin/payment.gohtml:6 web/templates/admin/payment.gohtml:12
#: web/templates/admin/layout.gohtml:38
#: web/templates/admin/layout.gohtml:39
msgctxt "title"
msgid "Payment Settings"
msgstr "Parámetros de pago"
@ -944,14 +978,8 @@ msgctxt "title"
msgid "Integration"
msgstr "Integración"
#: web/templates/admin/payment.gohtml:62 web/templates/admin/profile.gohtml:75
#: web/templates/admin/taxDetails.gohtml:152
msgctxt "action"
msgid "Save changes"
msgstr "Guardar los cambios"
#: web/templates/admin/dashboard.gohtml:6
#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:72
#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:76
msgctxt "title"
msgid "Dashboard"
msgstr "Panel"
@ -984,7 +1012,7 @@ msgid "New Service"
msgstr "Nuevo servicio"
#: web/templates/admin/services/index.gohtml:6
#: web/templates/admin/layout.gohtml:56
#: web/templates/admin/layout.gohtml:57
msgctxt "title"
msgid "Services Page"
msgstr "Página de servicios"
@ -1020,7 +1048,7 @@ msgid "Translate Service to %s"
msgstr "Traducción del servicio a %s"
#: web/templates/admin/profile.gohtml:6 web/templates/admin/profile.gohtml:12
#: web/templates/admin/layout.gohtml:31
#: web/templates/admin/layout.gohtml:32
msgctxt "title"
msgid "Profile"
msgstr "Perfil"
@ -1112,29 +1140,29 @@ msgctxt "input"
msgid "Legal Disclaimer"
msgstr "Nota legal"
#: web/templates/admin/layout.gohtml:27
#: web/templates/admin/layout.gohtml:28
msgctxt "title"
msgid "User Menu"
msgstr "Menú de usuario"
#: web/templates/admin/layout.gohtml:35
#: web/templates/admin/layout.gohtml:36
msgctxt "title"
msgid "Company Settings"
msgstr "Parámetros de la empresa"
#: web/templates/admin/layout.gohtml:50
#: web/templates/admin/layout.gohtml:51
#: web/templates/admin/media/index.gohtml:6
#: web/templates/admin/media/index.gohtml:11
msgctxt "title"
msgid "Media"
msgstr "Medios"
#: web/templates/admin/layout.gohtml:53 web/templates/admin/home/index.gohtml:6
#: web/templates/admin/layout.gohtml:54 web/templates/admin/home/index.gohtml:6
msgctxt "title"
msgid "Home Page"
msgstr "Página de inicio"
#: web/templates/admin/layout.gohtml:61
#: web/templates/admin/layout.gohtml:65
msgctxt "action"
msgid "Logout"
msgstr "Salir"
@ -1265,7 +1293,7 @@ msgstr "El idioma escogido no es válido."
msgid "File must be a valid PNG or JPEG image."
msgstr "El archivo tiene que ser una imagen PNG o JPEG válida."
#: pkg/app/admin.go:56
#: pkg/app/admin.go:59
msgid "Access forbidden"
msgstr "Acceso prohibido"
@ -1534,55 +1562,55 @@ msgstr "No podéis dejar el archivo del medio en blanco."
msgid "Filename can not be empty."
msgstr "No podéis dejar el nombre del archivo en blanco."
#: pkg/booking/admin.go:74
#: pkg/booking/admin.go:76
msgctxt "redsys environment"
msgid "Test"
msgstr "Pruebas"
#: pkg/booking/admin.go:78
#: pkg/booking/admin.go:80
msgctxt "redsys environment"
msgid "Live"
msgstr "Real"
#: pkg/booking/admin.go:87
#: pkg/booking/admin.go:89
msgctxt "redsys integration"
msgid "InSite"
msgstr "InSite"
#: pkg/booking/admin.go:91
#: pkg/booking/admin.go:93
msgctxt "redsys integration"
msgid "Redirect"
msgstr "Redirección"
#: pkg/booking/admin.go:135
#: pkg/booking/admin.go:137
msgid "Merchant code can not be empty."
msgstr "No podéis dejar el código del comercio en blanco."
#: pkg/booking/admin.go:136
#: pkg/booking/admin.go:138
msgid "Merchant code must be exactly nine digits long."
msgstr "El código del comercio tiene que ser de nueve dígitos."
#: pkg/booking/admin.go:137
#: pkg/booking/admin.go:139
msgid "Merchant code must be a number."
msgstr "El código del comercio tiene que ser un número."
#: pkg/booking/admin.go:141
#: pkg/booking/admin.go:143
msgid "Terminal number can not be empty."
msgstr "No podéis dejar el número de terminal en blanco."
#: pkg/booking/admin.go:142
#: pkg/booking/admin.go:144
msgid "Terminal number must be a number between 1 and 999."
msgstr "El número de terminal tiene que ser entre 1 y 999."
#: pkg/booking/admin.go:150
#: pkg/booking/admin.go:152
msgid "Selected environment is not valid."
msgstr "El entorno escogido no es válido."
#: pkg/booking/admin.go:151
#: pkg/booking/admin.go:153
msgid "Selected integration is not valid."
msgstr "La integración escogida no es válida."
#: pkg/booking/admin.go:154
#: pkg/booking/admin.go:156
msgid "The merchant key is not valid."
msgstr "Esta clave del comercio no es válida."
@ -1638,6 +1666,10 @@ msgstr "%s tiene que ser como mínimo %d."
msgid "%s must be at most %d."
msgstr "%s tiene que ser como máximo %d"
#~ msgctxt "title"
#~ msgid "Contact"
#~ msgstr "Contacto"
#~ msgctxt "title"
#~ msgid "Party Details"
#~ msgstr "Datos de los visitantes"

120
po/fr.po
View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: camper\n"
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
"POT-Creation-Date: 2023-12-21 17:32+0100\n"
"POT-Creation-Date: 2023-12-21 21:08+0100\n"
"PO-Revision-Date: 2023-12-20 10:13+0100\n"
"Last-Translator: Oriol Carbonell <info@oriolcarbonell.cat>\n"
"Language-Team: French <traduc@traduc.org>\n"
@ -44,7 +44,7 @@ msgstr "Le paiement a échoué"
#: web/templates/public/services.gohtml:6
#: web/templates/public/services.gohtml:15
#: web/templates/public/layout.gohtml:52 web/templates/public/layout.gohtml:79
#: web/templates/public/layout.gohtml:51 web/templates/public/layout.gohtml:79
#: web/templates/admin/services/index.gohtml:66
msgctxt "title"
msgid "Services"
@ -54,6 +54,14 @@ msgstr "Services"
msgid "The campsite offers many different services."
msgstr "Le camping propose de nombreux services différents."
#: web/templates/public/location.gohtml:6
#: web/templates/public/location.gohtml:12
#: web/templates/public/layout.gohtml:53 web/templates/public/layout.gohtml:81
#: web/templates/admin/layout.gohtml:60
msgctxt "title"
msgid "Location"
msgstr "Comment nous rejoindre"
#: web/templates/public/home.gohtml:6 web/templates/public/layout.gohtml:37
#: web/templates/public/layout.gohtml:77
msgctxt "title"
@ -80,7 +88,7 @@ msgstr "Venez et profitez-en !"
#: web/templates/public/home.gohtml:35
#: web/templates/public/surroundings.gohtml:6
#: web/templates/public/surroundings.gohtml:10
#: web/templates/public/layout.gohtml:53 web/templates/public/layout.gohtml:80
#: web/templates/public/layout.gohtml:52 web/templates/public/layout.gohtml:80
msgctxt "title"
msgid "Surroundings"
msgstr "Entourage"
@ -269,14 +277,8 @@ msgctxt "title"
msgid "Campground"
msgstr "Camping"
#: web/templates/public/contact.gohtml:6 web/templates/public/contact.gohtml:18
#: web/templates/public/layout.gohtml:54 web/templates/public/layout.gohtml:81
msgctxt "title"
msgid "Contact"
msgstr "Contact"
#: web/templates/public/booking.gohtml:6 web/templates/public/booking.gohtml:11
#: web/templates/public/layout.gohtml:51
#: web/templates/public/layout.gohtml:54
msgctxt "title"
msgid "Booking"
msgstr "Reservation"
@ -365,18 +367,18 @@ msgid "I have read and I accept the reservation conditions"
msgstr "Jai lu et jaccepte les conditions de réservation"
#: web/templates/public/layout.gohtml:11 web/templates/public/layout.gohtml:32
#: web/templates/public/layout.gohtml:110
#: web/templates/public/layout.gohtml:109
msgid "Campsite Montagut"
msgstr "Camping Montagut"
#: web/templates/public/layout.gohtml:23 web/templates/admin/layout.gohtml:19
#: web/templates/public/layout.gohtml:23 web/templates/admin/layout.gohtml:20
msgid "Skip to main content"
msgstr "Passer au contenu principal"
#: web/templates/public/layout.gohtml:42 web/templates/public/layout.gohtml:88
#: web/templates/admin/campsite/index.gohtml:6
#: web/templates/admin/campsite/index.gohtml:12
#: web/templates/admin/layout.gohtml:44 web/templates/admin/layout.gohtml:75
#: web/templates/admin/layout.gohtml:45 web/templates/admin/layout.gohtml:79
msgctxt "title"
msgid "Campsites"
msgstr "Locatifs"
@ -386,7 +388,12 @@ msgctxt "title"
msgid "Sections"
msgstr "Sections"
#: web/templates/public/layout.gohtml:107
#: web/templates/public/layout.gohtml:99
msgctxt "title"
msgid "Opening"
msgstr "Ouverture"
#: web/templates/public/layout.gohtml:106
msgid "<abbr title=\"Catalonia Tourism Registry\">RTC</abbr> <abbr title=\"Number\">#</abbr>%s"
msgstr "<abbr title=\"Registre du tourisme de Catalogne\"># RTC</abbr> %s"
@ -483,6 +490,33 @@ msgctxt "action"
msgid "Translate"
msgstr "Traduire"
#: web/templates/admin/location.gohtml:6 web/templates/admin/location.gohtml:12
msgctxt "title"
msgid "Location Settings"
msgstr "Paramètres de comment nous rejoindre"
#: web/templates/admin/location.gohtml:17
msgctxt "input"
msgid "Directions"
msgstr "Directions"
#: web/templates/admin/location.gohtml:35
msgctxt "input"
msgid "Opening Dates"
msgstr "Dates douverture"
#: web/templates/admin/location.gohtml:53
msgctxt "input"
msgid "Map Embed"
msgstr "Carte intégrée"
#: web/templates/admin/location.gohtml:60 web/templates/admin/payment.gohtml:62
#: web/templates/admin/profile.gohtml:75
#: web/templates/admin/taxDetails.gohtml:152
msgctxt "action"
msgid "Save changes"
msgstr "Enregistrer les changements"
#: web/templates/admin/campsite/feature/form.gohtml:8
#: web/templates/admin/campsite/feature/form.gohtml:25
msgctxt "title"
@ -803,7 +837,7 @@ msgstr "Description"
#: web/templates/admin/campsite/type/index.gohtml:6
#: web/templates/admin/campsite/type/index.gohtml:12
#: web/templates/admin/layout.gohtml:41
#: web/templates/admin/layout.gohtml:42
msgctxt "title"
msgid "Campsite Types"
msgstr "Types demplacements de camping"
@ -878,7 +912,7 @@ msgstr "Couleur"
#: web/templates/admin/season/index.gohtml:6
#: web/templates/admin/season/index.gohtml:12
#: web/templates/admin/layout.gohtml:47
#: web/templates/admin/layout.gohtml:48
msgctxt "title"
msgid "Seasons"
msgstr "Saisons"
@ -910,7 +944,7 @@ msgid "Cancel"
msgstr "Annuler"
#: web/templates/admin/payment.gohtml:6 web/templates/admin/payment.gohtml:12
#: web/templates/admin/layout.gohtml:38
#: web/templates/admin/layout.gohtml:39
msgctxt "title"
msgid "Payment Settings"
msgstr "Paramètres de paiement"
@ -945,14 +979,8 @@ msgctxt "title"
msgid "Integration"
msgstr "Intégration"
#: web/templates/admin/payment.gohtml:62 web/templates/admin/profile.gohtml:75
#: web/templates/admin/taxDetails.gohtml:152
msgctxt "action"
msgid "Save changes"
msgstr "Enregistrer les changements"
#: web/templates/admin/dashboard.gohtml:6
#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:72
#: web/templates/admin/dashboard.gohtml:10 web/templates/admin/layout.gohtml:76
msgctxt "title"
msgid "Dashboard"
msgstr "Tableau de bord"
@ -985,7 +1013,7 @@ msgid "New Service"
msgstr "Nouveau service"
#: web/templates/admin/services/index.gohtml:6
#: web/templates/admin/layout.gohtml:56
#: web/templates/admin/layout.gohtml:57
msgctxt "title"
msgid "Services Page"
msgstr "La page des services"
@ -1021,7 +1049,7 @@ msgid "Translate Service to %s"
msgstr "Traduire le service en %s"
#: web/templates/admin/profile.gohtml:6 web/templates/admin/profile.gohtml:12
#: web/templates/admin/layout.gohtml:31
#: web/templates/admin/layout.gohtml:32
msgctxt "title"
msgid "Profile"
msgstr "Profil"
@ -1113,29 +1141,29 @@ msgctxt "input"
msgid "Legal Disclaimer"
msgstr "Avertissement juridique"
#: web/templates/admin/layout.gohtml:27
#: web/templates/admin/layout.gohtml:28
msgctxt "title"
msgid "User Menu"
msgstr "Menu utilisateur"
#: web/templates/admin/layout.gohtml:35
#: web/templates/admin/layout.gohtml:36
msgctxt "title"
msgid "Company Settings"
msgstr "Paramètres de l'entreprise"
#: web/templates/admin/layout.gohtml:50
#: web/templates/admin/layout.gohtml:51
#: web/templates/admin/media/index.gohtml:6
#: web/templates/admin/media/index.gohtml:11
msgctxt "title"
msgid "Media"
msgstr "Média"
#: web/templates/admin/layout.gohtml:53 web/templates/admin/home/index.gohtml:6
#: web/templates/admin/layout.gohtml:54 web/templates/admin/home/index.gohtml:6
msgctxt "title"
msgid "Home Page"
msgstr "Page d'accueil"
#: web/templates/admin/layout.gohtml:61
#: web/templates/admin/layout.gohtml:65
msgctxt "action"
msgid "Logout"
msgstr "Déconnexion"
@ -1266,7 +1294,7 @@ msgstr "La langue sélectionnée nest pas valide."
msgid "File must be a valid PNG or JPEG image."
msgstr "Le fichier doit être une image PNG ou JPEG valide."
#: pkg/app/admin.go:56
#: pkg/app/admin.go:59
msgid "Access forbidden"
msgstr "Accès interdit"
@ -1535,55 +1563,55 @@ msgstr "Le fichier téléchargé ne peut pas être vide."
msgid "Filename can not be empty."
msgstr "Le nom de fichier ne peut pas être vide."
#: pkg/booking/admin.go:74
#: pkg/booking/admin.go:76
msgctxt "redsys environment"
msgid "Test"
msgstr "Test"
#: pkg/booking/admin.go:78
#: pkg/booking/admin.go:80
msgctxt "redsys environment"
msgid "Live"
msgstr "Live"
#: pkg/booking/admin.go:87
#: pkg/booking/admin.go:89
msgctxt "redsys integration"
msgid "InSite"
msgstr "Insite"
#: pkg/booking/admin.go:91
#: pkg/booking/admin.go:93
msgctxt "redsys integration"
msgid "Redirect"
msgstr "Redirection"
#: pkg/booking/admin.go:135
#: pkg/booking/admin.go:137
msgid "Merchant code can not be empty."
msgstr "Le code marchand ne peut pas être vide."
#: pkg/booking/admin.go:136
#: pkg/booking/admin.go:138
msgid "Merchant code must be exactly nine digits long."
msgstr "Le code marchand doit comporter exactement neuf chiffres."
#: pkg/booking/admin.go:137
#: pkg/booking/admin.go:139
msgid "Merchant code must be a number."
msgstr "Le code du commerçant doit être un chiffre."
#: pkg/booking/admin.go:141
#: pkg/booking/admin.go:143
msgid "Terminal number can not be empty."
msgstr "Le numéro de terminal ne peut pas être vide."
#: pkg/booking/admin.go:142
#: pkg/booking/admin.go:144
msgid "Terminal number must be a number between 1 and 999."
msgstr "Le numéro de terminal doit être compris entre 1 et 999."
#: pkg/booking/admin.go:150
#: pkg/booking/admin.go:152
msgid "Selected environment is not valid."
msgstr "Lenvironnement sélectionné nest pas valide."
#: pkg/booking/admin.go:151
#: pkg/booking/admin.go:153
msgid "Selected integration is not valid."
msgstr "Lintégration sélectionnée nest pas valide."
#: pkg/booking/admin.go:154
#: pkg/booking/admin.go:156
msgid "The merchant key is not valid."
msgstr "La clé marchand nest pas valide."
@ -1638,3 +1666,7 @@ msgstr "%s doit être %d ou plus."
#, c-format
msgid "%s must be at most %d."
msgstr "%s doit être tout au plus %d."
#~ msgctxt "title"
#~ msgid "Contact"
#~ msgstr "Contact"

View File

@ -1,7 +0,0 @@
-- Revert camper:company_geography from pg
begin;
drop table if exists camper.company_geography;
commit;

View File

@ -1,7 +0,0 @@
-- Revert camper:extension_postgis from pg
begin;
drop extension if exists postgis;
commit;

7
revert/location.sql Normal file
View File

@ -0,0 +1,7 @@
-- Revert camper:location from pg
begin;
drop table if exists camper.location;
commit;

7
revert/location_i18n.sql Normal file
View File

@ -0,0 +1,7 @@
-- Revert camper:location_i18n from pg
begin;
drop table if exists camper.location_i18n;
commit;

View File

@ -0,0 +1,7 @@
-- Revert camper:setup_location from pg
begin;
drop function if exists camper.setup_location(integer, text, text, text);
commit;

View File

@ -0,0 +1,7 @@
-- Revert camper:translate_location from pg
begin;
drop function if exists camper.translate_location(integer, text, text, text);
commit;

View File

@ -97,8 +97,6 @@ campsite_type_option_cost [roles schema_camper campsite_type season campsite_typ
set_campsite_type_option_cost [roles schema_camper campsite_type_option_cost parse_price] 2023-10-05T17:41:58Z jordi fita mas <jordi@tandem.blog> # Add function to set cost of campsite type option
add_campsite_type_option [roles schema_camper campsite_type_option campsite_type] 2023-10-06T09:40:03Z jordi fita mas <jordi@tandem.blog> # Add function to create new campsite type options
edit_campsite_type_option [roles schema_camper campsite_type_option] 2023-10-06T09:51:02Z jordi fita mas <jordi@tandem.blog> # Add function to edit campsite type options
extension_postgis [schema_public] 2023-10-06T17:18:52Z jordi fita mas <jordi@tandem.blog> # Add PostGIS extension
company_geography [roles schema_camper company user_profile extension_postgis] 2023-10-06T17:53:34Z jordi fita mas <jordi@tandem.blog> # Add the relation for the GPS coordinates of companies
campsite_type_carousel [roles schema_camper campsite_type media user_profile] 2023-10-09T17:18:38Z jordi fita mas <jordi@tandem.blog> # Add the carousel for campsite types
campsite_type_carousel_i18n [roles schema_camper campsite_type_carousel language] 2023-10-09T17:45:09Z jordi fita mas <jordi@tandem.blog> # Add relation for campsite type carousel translations
add_campsite_type_carousel_slide [roles schema_camper campsite_type_carousel campsite_type] 2023-10-09T17:59:49Z jordi fita mas <jordi@tandem.blog> # Add function to create slides for the campsite type carousel
@ -126,3 +124,7 @@ order_campsite_type_carousel [schema_camper roles campsite_type campsite_type_ca
order_home_carousel [schema_camper roles home_carousel] 2023-12-20T18:30:12Z jordi fita mas <jordi@tandem.blog> # Add function to order home carousel
order_services_carousel [schema_camper roles services_carousel] 2023-12-20T18:39:18Z jordi fita mas <jordi@tandem.blog> # Add function to order services carousel
order_campsite_type_features [schema_camper roles campsite_type_feature] 2023-12-21T16:13:14Z jordi fita mas <jordi@tandem.blog> # Add function to order campsite type features
location [schema_camper roles company] 2023-12-21T17:01:28Z jordi fita mas <jordi@tandem.blog> # Add table to keep location information
location_i18n [roles schema_camper location language] 2023-12-21T17:32:50Z jordi fita mas <jordi@tandem.blog> # Add relation for location internationalization
translate_location [roles schema_camper location_i18n] 2023-12-21T17:37:47Z jordi fita mas <jordi@tandem.blog> # Add function to translate location
setup_location [roles schema_camper location] 2023-12-21T19:26:53Z jordi fita mas <jordi@tandem.blog> # Add function to setup location settings

View File

@ -1,168 +0,0 @@
-- Test company_geography
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(30);
set search_path to camper, public;
select has_table('company_geography');
select has_pk('company_geography');
select table_privs_are('company_geography', 'guest', array['SELECT']);
select table_privs_are('company_geography', 'employee', array['SELECT']);
select table_privs_are('company_geography', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('company_geography', 'authenticator', array[]::text[]);
select has_column('company_geography', 'company_id');
select col_is_pk('company_geography', 'company_id');
select col_is_fk('company_geography', 'company_id');
select fk_ok('company_geography', 'company_id', 'company', 'company_id');
select col_type_is('company_geography', 'company_id', 'integer');
select col_not_null('company_geography', 'company_id');
select col_hasnt_default('company_geography', 'company_id');
select has_column('company_geography', 'geog');
select col_type_is('company_geography', 'geog', 'geography');
select col_not_null('company_geography', 'geog');
select col_hasnt_default('company_geography', 'geog');
set client_min_messages to warning;
truncate company_geography cascade;
truncate company_host cascade;
truncate company_user cascade;
truncate company cascade;
truncate auth."user" cascade;
reset client_min_messages;
insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at)
values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
, (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
;
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, country_code, currency_code, default_lang_tag)
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 'ES', 'EUR', 'ca')
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 'FR', 'USD', 'ca')
, (8, 'Company 8', 'XX345', '', '777-777-777', 'c@c', '', '', '', '', '', '', 'DE', 'USD', 'en')
;
insert into company_user (company_id, user_id, role)
values (2, 1, 'admin')
, (4, 5, 'admin')
;
insert into company_host (company_id, host)
values (2, 'co2')
, (4, 'co4')
;
insert into company_geography (company_id, geog)
values (2, 'SRID=4326;POINT(-118.4079 33.9434)')
, (4, 'SRID=4326;POINT(2.5559 49.0083)')
;
prepare geog_data as
select company_id, st_x(geog::geometry), st_y(geog::geometry)
from company_geography
;
set role guest;
select bag_eq(
'geog_data',
$$ values (2, -118.4079, 33.9434)
, (4, 2.5559, 49.0083)
$$,
'Everyone should be able to list all geography across all companies'
);
reset role;
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2');
select lives_ok(
$$ delete from company_geography where company_id = 2 $$,
'Admin from company 2 should be able to delete geography from that company.'
);
select bag_eq(
'geog_data',
$$ values (4, 2.5559, 49.0083)
$$,
'The row should have been deleted.'
);
select lives_ok(
$$ insert into company_geography(company_id, geog) values (2, 'SRID=4326;POINT(-22.6056 63.9850)') $$,
'Admin from company 2 should be able to insert a new geography to that company.'
);
select bag_eq(
'geog_data',
$$ values (2, -22.6056, 63.9850)
, (4, 2.5559, 49.0083)
$$,
'The new row should have been added'
);
select lives_ok(
$$ update company_geography set geog = 'SRID=4326;POINT(139.7330 35.5670)' where company_id = 2 $$,
'Admin from company 2 should be able to update geography of that company.'
);
select bag_eq(
'geog_data',
$$ values (2, 139.7330, 35.5670)
, (4, 2.5559, 49.0083)
$$,
'The row should have been updated.'
);
select throws_ok(
$$ insert into company_geography (company_id, geog) values (8, 'SRID=4326;POINT(2.5559 49.0083)') $$,
'42501', 'new row violates row-level security policy for table "company_geography"',
'Admin from company 2 should NOT be able to insert new geography to another company 8.'
);
select lives_ok(
$$ update company_geography set geog = 'SRID=4326;POINT(139.7330 35.5670)' where company_id = 4 $$,
'Admin from company 2 should not be able to update geography of company 4, but no error if company_id is not changed.'
);
select bag_eq(
'geog_data',
$$ values (2, 139.7330, 35.5670)
, (4, 2.5559, 49.0083)
$$,
'No row should have been changed.'
);
select throws_ok(
$$ update company_geography set company_id = 4 where company_id = 2 $$,
'42501', 'new row violates row-level security policy for table "company_geography"',
'Admin from company 2 should NOT be able to move geography to company 4'
);
select lives_ok(
$$ delete from company_geography where company_id = 4 $$,
'Admin from company 2 should NOT be able to delete geography from company 4, but not error is thrown'
);
select bag_eq(
'geog_data',
$$ values (2, 139.7330, 35.5670)
, (4, 2.5559, 49.0083)
$$,
'No row should have been changed'
);
reset role;
select *
from finish();
rollback;

View File

@ -14,7 +14,6 @@ select extensions_are(array [
, 'pg_libphonenumber'
, 'pgtap'
, 'plpgsql'
, 'postgis'
, 'uri'
, 'vat'
]);

178
test/location.sql Normal file
View File

@ -0,0 +1,178 @@
-- Test location
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(38);
set search_path to camper, public;
select has_table('location');
select has_pk('location');
select table_privs_are('location', 'guest', array ['SELECT']);
select table_privs_are('location', 'employee', array ['SELECT']);
select table_privs_are('location', 'admin', array ['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('location', 'authenticator', array []::text[]);
select has_column('location', 'company_id');
select col_is_pk('location', 'company_id');
select col_is_fk('location', 'company_id');
select fk_ok('location', 'company_id', 'company', 'company_id');
select col_type_is('location', 'company_id', 'integer');
select col_not_null('location', 'company_id');
select col_hasnt_default('location', 'company_id');
select has_column('location', 'directions');
select col_type_is('location', 'directions', 'xml');
select col_not_null('location', 'directions');
select col_hasnt_default('location', 'directions');
select has_column('location', 'map_embed');
select col_type_is('location', 'map_embed', 'xml');
select col_not_null('location', 'map_embed');
select col_hasnt_default('location', 'map_embed');
select has_column('location', 'opening_dates');
select col_type_is('location', 'opening_dates', 'xml');
select col_not_null('location', 'opening_dates');
select col_hasnt_default('location', 'opening_dates');
set client_min_messages to warning;
truncate location cascade;
truncate company_host cascade;
truncate company_user cascade;
truncate company cascade;
truncate auth."user" cascade;
reset client_min_messages;
insert into auth."user" (user_id, email, name, password, cookie, cookie_expires_at)
values (1, 'demo@tandem.blog', 'Demo', 'test', '44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e', current_timestamp + interval '1 month')
, (5, 'admin@tandem.blog', 'Demo', 'test', '12af4c88b528c2ad4222e3740496ecbc58e76e26f087657524', current_timestamp + interval '1 month')
;
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, country_code, currency_code, default_lang_tag)
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 'ES', 'EUR', 'ca')
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 'FR', 'USD', 'ca')
, (8, 'Company 8', 'XX345', '', '777-777-777', 'c@c', '', '', '', '', '', '', 'DE', 'USD', 'en')
;
insert into company_user (company_id, user_id, role)
values (2, 1, 'admin')
, (4, 5, 'admin')
;
insert into company_host (company_id, host)
values (2, 'co2')
, (4, 'co4')
;
insert into location (company_id, directions, map_embed, opening_dates)
values (2, '<p>up</p>', '<map/>', '<p>today</p>')
, (4, '<p>down</p>', '<mapp/>', '<p>yesterday</p>')
;
prepare location_data as
select company_id, directions::text, map_embed::text, opening_dates::text
from location
;
set role guest;
select bag_eq(
'location_data',
$$ values (2, '<p>up</p>', '<map/>', '<p>today</p>')
, (4, '<p>down</p>', '<mapp/>', '<p>yesterday</p>')
$$,
'Everyone should be able to list all location across all companies'
);
reset role;
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2');
select lives_ok(
$$ delete from location where company_id = 2 $$,
'Admin from company 2 should be able to delete location from that company.'
);
select bag_eq(
'location_data',
$$ values (4, '<p>down</p>', '<mapp/>', '<p>yesterday</p>')
$$,
'The row should have been deleted.'
);
select lives_ok(
$$ insert into location(company_id, directions, map_embed, opening_dates) values (2, '<p>left</p>', '<leafletf/>', '<p>tomorrow</p>') $$,
'Admin from company 2 should be able to insert a new location to that company.'
);
select bag_eq(
'location_data',
$$ values (2, '<p>left</p>', '<leafletf/>', '<p>tomorrow</p>')
, (4, '<p>down</p>', '<mapp/>', '<p>yesterday</p>')
$$,
'The new row should have been added'
);
select lives_ok(
$$ update location set directions = '<p>up</p>', map_embed = '<map/>', opening_dates = '<p>today</p>' where company_id = 2 $$,
'Admin from company 2 should be able to update location of that company.'
);
select bag_eq(
'location_data',
$$ values (2, '<p>up</p>', '<map/>', '<p>today</p>')
, (4, '<p>down</p>', '<mapp/>', '<p>yesterday</p>')
$$,
'The row should have been updated.'
);
select throws_ok(
$$ insert into location (company_id, directions, map_embed, opening_dates) values (8, '<p/>', '<div/>', '<p/>') $$,
'42501', 'new row violates row-level security policy for table "location"',
'Admin from company 2 should NOT be able to insert new location to another company.'
);
select lives_ok(
$$ update location set opening_dates = '<p>never</p>' where company_id = 4 $$,
'Admin from company 2 should not be able to update location of company 4, but no error if company_id is not changed.'
);
select bag_eq(
'location_data',
$$ values (2, '<p>up</p>', '<map/>', '<p>today</p>')
, (4, '<p>down</p>', '<mapp/>', '<p>yesterday</p>')
$$,
'No row should have been changed.'
);
select throws_ok(
$$ update location set company_id = 4 where company_id = 2 $$,
'42501', 'new row violates row-level security policy for table "location"',
'Admin from company 2 should NOT be able to move location to company 4'
);
select lives_ok(
$$ delete from location where company_id = 4 $$,
'Admin from company 2 should NOT be able to delete location from company 4, but not error is thrown'
);
select bag_eq(
'location_data',
$$ values (2, '<p>up</p>', '<map/>', '<p>today</p>')
, (4, '<p>down</p>', '<mapp/>', '<p>yesterday</p>')
$$,
'No row should have been changed'
);
reset role;
select *
from finish();
rollback;

49
test/location_i18n.sql Normal file
View File

@ -0,0 +1,49 @@
-- Test location_i18n
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(27);
set search_path to camper, public;
select has_table('location_i18n');
select has_pk('location_i18n');
select col_is_pk('location_i18n', array['company_id', 'lang_tag']);
select table_privs_are('location_i18n', 'guest', array['SELECT']);
select table_privs_are('location_i18n', 'employee', array['SELECT']);
select table_privs_are('location_i18n', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
select table_privs_are('location_i18n', 'authenticator', array[]::text[]);
select has_column('location_i18n', 'company_id');
select col_is_fk('location_i18n', 'company_id');
select fk_ok('location_i18n', 'company_id', 'location', 'company_id');
select col_type_is('location_i18n', 'company_id', 'integer');
select col_not_null('location_i18n', 'company_id');
select col_hasnt_default('location_i18n', 'company_id');
select has_column('location_i18n', 'lang_tag');
select col_is_fk('location_i18n', 'lang_tag');
select fk_ok('location_i18n', 'lang_tag', 'language', 'lang_tag');
select col_type_is('location_i18n', 'lang_tag', 'text');
select col_not_null('location_i18n', 'lang_tag');
select col_hasnt_default('location_i18n', 'lang_tag');
select has_column('location_i18n', 'directions');
select col_type_is('location_i18n', 'directions', 'xml');
select col_not_null('location_i18n', 'directions');
select col_hasnt_default('location_i18n', 'directions');
select has_column('location_i18n', 'opening_dates');
select col_type_is('location_i18n', 'opening_dates', 'xml');
select col_not_null('location_i18n', 'opening_dates');
select col_hasnt_default('location_i18n', 'opening_dates');
select *
from finish();
rollback;

76
test/setup_location.sql Normal file
View File

@ -0,0 +1,76 @@
-- Test setup_location
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(15);
set search_path to camper, public;
select has_function('camper', 'setup_location', array['integer', 'text', 'text', 'text']);
select function_lang_is('camper', 'setup_location', array['integer', 'text', 'text', 'text'], 'sql');
select function_returns('camper', 'setup_location', array['integer', 'text', 'text', 'text'], 'void');
select isnt_definer('camper', 'setup_location', array['integer', 'text', 'text', 'text']);
select volatility_is('camper', 'setup_location', array['integer', 'text', 'text', 'text'], 'volatile');
select function_privs_are('camper', 'setup_location', array ['integer', 'text', 'text', 'text'], 'guest', array[]::text[]);
select function_privs_are('camper', 'setup_location', array ['integer', 'text', 'text', 'text'], 'employee', array[]::text[]);
select function_privs_are('camper', 'setup_location', array ['integer', 'text', 'text', 'text'], 'admin', array['EXECUTE']);
select function_privs_are('camper', 'setup_location', array ['integer', 'text', 'text', 'text'], 'authenticator', array[]::text[]);
set client_min_messages to warning;
truncate location cascade;
truncate company cascade;
reset client_min_messages;
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, country_code, currency_code, default_lang_tag)
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 'ES', 'EUR', 'ca')
, (2, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 'FR', 'USD', 'ca')
;
prepare location_data as
select company_id, directions::text, map_embed::text, opening_dates::text
from location
;
select lives_ok(
$$ select setup_location(1, '<p>Up</p>', '<map/>', '<p>Today</p>') $$,
'Should be able to setup location parameters for the first company'
);
select lives_ok(
$$ select setup_location(2, '<p>Left</p>', '<leaflet/>', '<p>Tomorrow</p>') $$,
'Should be able to setup location parameters for the second company'
);
select bag_eq(
'location_data',
$$ values (1, '<p>Up</p>', '<map/>', '<p>Today</p>'),
(2, '<p>Left</p>', '<leaflet/>', '<p>Tomorrow</p>')
$$,
'Should have inserted all location parameters'
);
select lives_ok(
$$ select setup_location(1, '<p>Down</p>', '<google/>', '<p>Never</p>') $$,
'Should be able to update location parameters for the first company'
);
select lives_ok(
$$ select setup_location(2, '<p>Right</p>', '<map/>', '<p>Yesterday</p>') $$,
'Should be able to update location parameters for the second company'
);
select bag_eq(
'location_data',
$$ values (1, '<p>Down</p>', '<google/>', '<p>Never</p>'),
(2, '<p>Right</p>', '<map/>', '<p>Yesterday</p>')
$$,
'Should have updated all location parameters'
);
select *
from finish();
rollback;

View File

@ -0,0 +1,72 @@
-- Test translate_location
set client_min_messages to warning;
create extension if not exists pgtap;
reset client_min_messages;
begin;
select plan(13);
set search_path to camper, public;
select has_function('camper', 'translate_location', array['integer', 'text', 'text', 'text']);
select function_lang_is('camper', 'translate_location', array['integer', 'text', 'text', 'text'], 'sql');
select function_returns('camper', 'translate_location', array['integer', 'text', 'text', 'text'], 'void');
select isnt_definer('camper', 'translate_location', array['integer', 'text', 'text', 'text']);
select volatility_is('camper', 'translate_location', array['integer', 'text', 'text', 'text'], 'volatile');
select function_privs_are('camper', 'translate_location', array['integer', 'text', 'text', 'text'], 'guest', array[]::text[]);
select function_privs_are('camper', 'translate_location', array['integer', 'text', 'text', 'text'], 'employee', array[]::text[]);
select function_privs_are('camper', 'translate_location', array['integer', 'text', 'text', 'text'], 'admin', array['EXECUTE']);
select function_privs_are('camper', 'translate_location', array['integer', 'text', 'text', 'text'], 'authenticator', array[]::text[]);
set client_min_messages to warning;
truncate location_i18n cascade;
truncate location cascade;
truncate company cascade;
reset client_min_messages;
insert into company (company_id, business_name, vatin, trade_name, phone, email, web, address, city, province, postal_code, rtc_number, country_code, currency_code, default_lang_tag)
values (2, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', '', 'ES', 'EUR', 'ca')
, (4, 'Company 4', 'XX234', '', '666-666-666', 'b@b', '', '', '', '', '', '', 'FR', 'USD', 'ca')
;
insert into location (company_id, directions, map_embed, opening_dates)
values (2, '<p>Up</p>', '<map/>', '<p>Tomorrow</p>')
, (4, '<p>Left</p>', '<map/>', '<p>Today</p>')
;
insert into location_i18n (company_id, lang_tag, directions, opening_dates)
values (2, 'ca', '<p>Uh?</p>', '<p>Uh?</p>')
, (4, 'ca', '<p>Uh?</p>', '<p>Uh?</p>')
;
select lives_ok(
$$ select translate_location(2, 'es', '<p>Arriba</p>', '<p>Mañana</p>') $$,
'Should be able to translate the location of the first company to a new language'
);
select lives_ok(
$$ select translate_location(2, 'ca', '<p>Dalt</p>', '<p>Demà</p>') $$,
'Should be able to overwrite a locations translation'
);
select lives_ok(
$$ select translate_location(4, 'ca', null, null) $$,
'Should be able to “translate” a location to empty strings'
);
select bag_eq(
$$ select company_id, lang_tag, directions::text, opening_dates::text from location_i18n $$,
$$ values (2, 'ca', '<p>Dalt</p>', '<p>Demà</p>')
, (2, 'es', '<p>Arriba</p>', '<p>Mañana</p>')
, (4, 'ca', '', '')
$$,
'Should have translated all locations'
);
select *
from finish();
rollback;

View File

@ -1,16 +0,0 @@
-- Verify camper:company_geography on pg
begin;
select company_id
, geog
from camper.company_geography
where false;
select 1 / count(*) from pg_class where oid = 'camper.company_geography'::regclass and relrowsecurity;
select 1 / count(*) from pg_policy where polname = 'guest_ok' and polrelid = 'camper.company_geography'::regclass;
select 1 / count(*) from pg_policy where polname = 'insert_to_company' and polrelid = 'camper.company_geography'::regclass;
select 1 / count(*) from pg_policy where polname = 'update_company' and polrelid = 'camper.company_geography'::regclass;
select 1 / count(*) from pg_policy where polname = 'delete_from_company' and polrelid = 'camper.company_geography'::regclass;
rollback;

View File

@ -1,10 +0,0 @@
-- Verify camper:extension_postgis on pg
begin;
select 1 / count(*)
from pg_extension
where extname = 'postgis'
;
rollback;

18
verify/location.sql Normal file
View File

@ -0,0 +1,18 @@
-- Verify camper:location on pg
begin;
select company_id
, directions
, map_embed
, opening_dates
from camper.location
where false;
select 1 / count(*) from pg_class where oid = 'camper.location'::regclass and relrowsecurity;
select 1 / count(*) from pg_policy where polname = 'guest_ok' and polrelid = 'camper.location'::regclass;
select 1 / count(*) from pg_policy where polname = 'insert_to_company' and polrelid = 'camper.location'::regclass;
select 1 / count(*) from pg_policy where polname = 'update_company' and polrelid = 'camper.location'::regclass;
select 1 / count(*) from pg_policy where polname = 'delete_from_company' and polrelid = 'camper.location'::regclass;
rollback;

12
verify/location_i18n.sql Normal file
View File

@ -0,0 +1,12 @@
-- Verify camper:location_i18n on pg
begin;
select company_id
, lang_tag
, directions
, opening_dates
from camper.location_i18n
where false;
rollback;

View File

@ -0,0 +1,7 @@
-- Verify camper:setup_location on pg
begin;
select has_function_privilege('camper.setup_location(integer, text, text, text)', 'execute');
rollback;

View File

@ -0,0 +1,7 @@
-- Verify camper:translate_location on pg
begin;
select has_function_privilege('camper.translate_location(integer, text, text, text)', 'execute');
rollback;

5
web/static/alpinejs@3.13.3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -365,7 +365,7 @@ label, legend {
font-style: italic;
}
fieldset + label, label + label {
fieldset + label, fieldset + fieldset, label + label:not([x-show]) {
margin-top: 1rem;
}
@ -644,9 +644,33 @@ textarea {
#arbres {
mix-blend-mode: multiply;
mix-blend-mode: multiply;
}
#zones {
mix-blend-mode: multiply;
}
#zones {
mix-blend-mode: multiply;
}
/* i18n input */
[x-cloak] {
display: none !important;
}
.lang-selector {
display: flex;
gap: .25em;
}
.lang-selector button {
min-width: auto;
padding: .15em;
margin: 0;
}
.lang-selector button[aria-pressed="true"] {
background-color: var(--camper--color--hay);
}
label[x-show] span, label[x-show] br {
display: none;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 618 B

View File

@ -1,661 +0,0 @@
/* required styles */
.leaflet-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-container,
.leaflet-pane > svg,
.leaflet-pane > canvas,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
position: absolute;
left: 0;
top: 0;
}
.leaflet-container {
overflow: hidden;
}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-user-drag: none;
}
/* Prevents IE11 from highlighting tiles in blue */
.leaflet-tile::selection {
background: transparent;
}
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
.leaflet-safari .leaflet-tile {
image-rendering: -webkit-optimize-contrast;
}
/* hack that prevents hw layers "stretching" when loading new tiles */
.leaflet-safari .leaflet-tile-container {
width: 1600px;
height: 1600px;
-webkit-transform-origin: 0 0;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container .leaflet-overlay-pane svg {
max-width: none !important;
max-height: none !important;
}
.leaflet-container .leaflet-marker-pane img,
.leaflet-container .leaflet-shadow-pane img,
.leaflet-container .leaflet-tile-pane img,
.leaflet-container img.leaflet-image-layer,
.leaflet-container .leaflet-tile {
max-width: none !important;
max-height: none !important;
width: auto;
padding: 0;
}
.leaflet-container img.leaflet-tile {
/* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
mix-blend-mode: plus-lighter;
}
.leaflet-container.leaflet-touch-zoom {
-ms-touch-action: pan-x pan-y;
touch-action: pan-x pan-y;
}
.leaflet-container.leaflet-touch-drag {
-ms-touch-action: pinch-zoom;
/* Fallback for FF which doesn't support pinch-zoom */
touch-action: none;
touch-action: pinch-zoom;
}
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
-ms-touch-action: none;
touch-action: none;
}
.leaflet-container {
-webkit-tap-highlight-color: transparent;
}
.leaflet-container a {
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
}
.leaflet-tile {
filter: inherit;
visibility: hidden;
}
.leaflet-tile-loaded {
visibility: inherit;
}
.leaflet-zoom-box {
width: 0;
height: 0;
-moz-box-sizing: border-box;
box-sizing: border-box;
z-index: 800;
}
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg {
-moz-user-select: none;
}
.leaflet-pane { z-index: 400; }
.leaflet-tile-pane { z-index: 200; }
.leaflet-overlay-pane { z-index: 400; }
.leaflet-shadow-pane { z-index: 500; }
.leaflet-marker-pane { z-index: 600; }
.leaflet-tooltip-pane { z-index: 650; }
.leaflet-popup-pane { z-index: 700; }
.leaflet-map-pane canvas { z-index: 100; }
.leaflet-map-pane svg { z-index: 200; }
.leaflet-vml-shape {
width: 1px;
height: 1px;
}
.lvml {
behavior: url(#default#VML);
display: inline-block;
position: absolute;
}
/* control positioning */
.leaflet-control {
position: relative;
z-index: 800;
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
.leaflet-top,
.leaflet-bottom {
position: absolute;
z-index: 1000;
pointer-events: none;
}
.leaflet-top {
top: 0;
}
.leaflet-right {
right: 0;
}
.leaflet-bottom {
bottom: 0;
}
.leaflet-left {
left: 0;
}
.leaflet-control {
float: left;
clear: both;
}
.leaflet-right .leaflet-control {
float: right;
}
.leaflet-top .leaflet-control {
margin-top: 10px;
}
.leaflet-bottom .leaflet-control {
margin-bottom: 10px;
}
.leaflet-left .leaflet-control {
margin-left: 10px;
}
.leaflet-right .leaflet-control {
margin-right: 10px;
}
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1;
}
.leaflet-zoom-animated {
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
}
svg.leaflet-zoom-animated {
will-change: transform;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile {
-webkit-transition: none;
-moz-transition: none;
transition: none;
}
.leaflet-zoom-anim .leaflet-zoom-hide {
visibility: hidden;
}
/* cursors */
.leaflet-interactive {
cursor: pointer;
}
.leaflet-grab {
cursor: -webkit-grab;
cursor: -moz-grab;
cursor: grab;
}
.leaflet-crosshair,
.leaflet-crosshair .leaflet-interactive {
cursor: crosshair;
}
.leaflet-popup-pane,
.leaflet-control {
cursor: auto;
}
.leaflet-dragging .leaflet-grab,
.leaflet-dragging .leaflet-grab .leaflet-interactive,
.leaflet-dragging .leaflet-marker-draggable {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
cursor: grabbing;
}
/* marker & overlays interactivity */
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-image-layer,
.leaflet-pane > svg path,
.leaflet-tile-container {
pointer-events: none;
}
.leaflet-marker-icon.leaflet-interactive,
.leaflet-image-layer.leaflet-interactive,
.leaflet-pane > svg path.leaflet-interactive,
svg.leaflet-image-layer.leaflet-interactive path {
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
/* visual tweaks */
.leaflet-container {
background: #ddd;
outline-offset: 1px;
}
.leaflet-container a {
color: #0078A8;
}
.leaflet-zoom-box {
border: 2px dotted #38f;
background: rgba(255,255,255,0.5);
}
/* general typography */
.leaflet-container {
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
font-size: 12px;
font-size: 0.75rem;
line-height: 1.5;
}
/* general toolbar styles */
.leaflet-bar {
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
border-radius: 4px;
}
.leaflet-bar a {
background-color: #fff;
border-bottom: 1px solid #ccc;
width: 26px;
height: 26px;
line-height: 26px;
display: block;
text-align: center;
text-decoration: none;
color: black;
}
.leaflet-bar a,
.leaflet-control-layers-toggle {
background-position: 50% 50%;
background-repeat: no-repeat;
display: block;
}
.leaflet-bar a:hover,
.leaflet-bar a:focus {
background-color: #f4f4f4;
}
.leaflet-bar a:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.leaflet-bar a:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
.leaflet-bar a.leaflet-disabled {
cursor: default;
background-color: #f4f4f4;
color: #bbb;
}
.leaflet-touch .leaflet-bar a {
width: 30px;
height: 30px;
line-height: 30px;
}
.leaflet-touch .leaflet-bar a:first-child {
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
.leaflet-touch .leaflet-bar a:last-child {
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
/* zoom control */
.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
font: bold 18px 'Lucida Console', Monaco, monospace;
text-indent: 1px;
}
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
font-size: 22px;
}
/* layers control */
.leaflet-control-layers {
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
background: #fff;
border-radius: 5px;
}
.leaflet-control-layers-toggle {
background-image: url(images/layers.png);
width: 36px;
height: 36px;
}
.leaflet-retina .leaflet-control-layers-toggle {
background-image: url(images/layers-2x.png);
background-size: 26px 26px;
}
.leaflet-touch .leaflet-control-layers-toggle {
width: 44px;
height: 44px;
}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display: none;
}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
display: block;
position: relative;
}
.leaflet-control-layers-expanded {
padding: 6px 10px 6px 6px;
color: #333;
background: #fff;
}
.leaflet-control-layers-scrollbar {
overflow-y: scroll;
overflow-x: hidden;
padding-right: 5px;
}
.leaflet-control-layers-selector {
margin-top: 2px;
position: relative;
top: 1px;
}
.leaflet-control-layers label {
display: block;
font-size: 13px;
font-size: 1.08333em;
}
.leaflet-control-layers-separator {
height: 0;
border-top: 1px solid #ddd;
margin: 5px -10px 5px -6px;
}
/* Default icon URLs */
.leaflet-default-icon-path { /* used only in path-guessing heuristic, see L.Icon.Default */
background-image: url(images/marker-icon.png);
}
/* attribution and scale controls */
.leaflet-container .leaflet-control-attribution {
background: #fff;
background: rgba(255, 255, 255, 0.8);
margin: 0;
}
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding: 0 5px;
color: #333;
line-height: 1.4;
}
.leaflet-control-attribution a {
text-decoration: none;
}
.leaflet-control-attribution a:hover,
.leaflet-control-attribution a:focus {
text-decoration: underline;
}
.leaflet-attribution-flag {
display: inline !important;
vertical-align: baseline !important;
width: 1em;
height: 0.6669em;
}
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
}
.leaflet-bottom .leaflet-control-scale {
margin-bottom: 5px;
}
.leaflet-control-scale-line {
border: 2px solid #777;
border-top: none;
line-height: 1.1;
padding: 2px 5px 1px;
white-space: nowrap;
-moz-box-sizing: border-box;
box-sizing: border-box;
background: rgba(255, 255, 255, 0.8);
text-shadow: 1px 1px #fff;
}
.leaflet-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
border-bottom: none;
margin-top: -2px;
}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
border-bottom: 2px solid #777;
}
.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
box-shadow: none;
}
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
border: 2px solid rgba(0,0,0,0.2);
background-clip: padding-box;
}
/* popup */
.leaflet-popup {
position: absolute;
text-align: center;
margin-bottom: 20px;
}
.leaflet-popup-content-wrapper {
padding: 1px;
text-align: left;
border-radius: 12px;
}
.leaflet-popup-content {
margin: 13px 24px 13px 20px;
line-height: 1.3;
font-size: 13px;
font-size: 1.08333em;
min-height: 1px;
}
.leaflet-popup-content p {
margin: 17px 0;
margin: 1.3em 0;
}
.leaflet-popup-tip-container {
width: 40px;
height: 20px;
position: absolute;
left: 50%;
margin-top: -1px;
margin-left: -20px;
overflow: hidden;
pointer-events: none;
}
.leaflet-popup-tip {
width: 17px;
height: 17px;
padding: 1px;
margin: -10px auto 0;
pointer-events: auto;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
background: white;
color: #333;
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
}
.leaflet-container a.leaflet-popup-close-button {
position: absolute;
top: 0;
right: 0;
border: none;
text-align: center;
width: 24px;
height: 24px;
font: 16px/24px Tahoma, Verdana, sans-serif;
color: #757575;
text-decoration: none;
background: transparent;
}
.leaflet-container a.leaflet-popup-close-button:hover,
.leaflet-container a.leaflet-popup-close-button:focus {
color: #585858;
}
.leaflet-popup-scrolled {
overflow: auto;
}
.leaflet-oldie .leaflet-popup-content-wrapper {
-ms-zoom: 1;
}
.leaflet-oldie .leaflet-popup-tip {
width: 24px;
margin: 0 auto;
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
}
.leaflet-oldie .leaflet-control-zoom,
.leaflet-oldie .leaflet-control-layers,
.leaflet-oldie .leaflet-popup-content-wrapper,
.leaflet-oldie .leaflet-popup-tip {
border: 1px solid #999;
}
/* div icon */
.leaflet-div-icon {
background: #fff;
border: 1px solid #666;
}
/* Tooltip */
/* Base styles for the element that has a tooltip */
.leaflet-tooltip {
position: absolute;
padding: 6px;
background-color: #fff;
border: 1px solid #fff;
border-radius: 3px;
color: #222;
white-space: nowrap;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
pointer-events: none;
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
.leaflet-tooltip.leaflet-interactive {
cursor: pointer;
pointer-events: auto;
}
.leaflet-tooltip-top:before,
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
position: absolute;
pointer-events: none;
border: 6px solid transparent;
background: transparent;
content: "";
}
/* Directions */
.leaflet-tooltip-bottom {
margin-top: 6px;
}
.leaflet-tooltip-top {
margin-top: -6px;
}
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-top:before {
left: 50%;
margin-left: -6px;
}
.leaflet-tooltip-top:before {
bottom: 0;
margin-bottom: -12px;
border-top-color: #fff;
}
.leaflet-tooltip-bottom:before {
top: 0;
margin-top: -12px;
margin-left: -6px;
border-bottom-color: #fff;
}
.leaflet-tooltip-left {
margin-left: -6px;
}
.leaflet-tooltip-right {
margin-left: 6px;
}
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
top: 50%;
margin-top: -6px;
}
.leaflet-tooltip-left:before {
right: 0;
margin-right: -12px;
border-left-color: #fff;
}
.leaflet-tooltip-right:before {
left: 0;
margin-left: -12px;
border-right-color: #fff;
}
/* Printing */
@media print {
/* Prevent printers from removing background-images of controls. */
.leaflet-control {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
}

File diff suppressed because one or more lines are too long

View File

@ -1152,7 +1152,7 @@ body > footer {
gap: 1rem;
}
body > footer div, .campsite_type_features, .campsite_type_detail {
body > footer > div, .campsite_type_features, .campsite_type_detail {
padding: 5rem 0;
border-top: 3px solid black;
}
@ -1168,7 +1168,7 @@ body > footer section:nth-child(3) {
padding-bottom: 2.5rem;
}
body > footer div {
body > footer > div {
display: flex;
gap: 3rem;
justify-content: space-between;

View File

@ -11,6 +11,7 @@
<link rel="stylesheet" media="screen" href="/static/camper.css">
<link rel="stylesheet" media="screen" href="/static/icons.css">
<script src="/static/sortable@1.15.1.min.js"></script>
<script src="/static/alpinejs@3.13.3.min.js"></script>
<script src="/static/htmx@1.9.3.min.js"></script>
<script type="module" src="/static/camper.js"></script>
{{ block "head" . }}{{ end }}
@ -55,6 +56,9 @@
<li>
<a href="/admin/services">{{( pgettext "Services Page" "title" )}}</a>
</li>
<li>
<a href="/admin/location">{{( pgettext "Location" "title" )}}</a>
</li>
{{- end }}
<li class="icon_logout">
<button data-hx-delete="/me/session" data-hx-headers='{ {{ CSRFHeader }} }'

View File

@ -0,0 +1,63 @@
<!--
SPDX-FileCopyrightText: 2023 jordi fita mas <jordi@tandem.blog>
SPDX-License-Identifier: AGPL-3.0-only
-->
{{ define "title" -}}
{{( pgettext "Location Settings" "title" )}}
{{- end }}
{{ define "content" -}}
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/location.locationForm*/ -}}
<form data-hx-put="/admin/location">
<h2>{{( pgettext "Location Settings" "title" )}}</h2>
{{ CSRFInput }}
<fieldset x-data="{ lang: 'ca' }">
{{ with .Directions -}}
<fieldset>
<legend>{{( pgettext "Directions" "input" )}}</legend>
<div class="lang-selector" role="toolbar">
{{ range $lang, $input := . -}}
<button :aria-pressed="lang === '{{ $lang }}'"
@click.prevent="lang = '{{ $lang }}'">{{ $lang }}</button>
{{- end }}
</div>
{{ range $lang, $input := . -}}
<label x-cloak x-show="lang === '{{ $lang }}'"><span>{{ $lang }}</span><br>
<textarea class="html"
name="{{ $input.Name }}" {{ template "error-attrs" . }}>{{ $input.Val }}</textarea><br>
</label>
{{- end }}
{{ template "error-message" . }}
</fieldset>
{{- end }}
{{ with .OpeningDates -}}
<fieldset>
<legend>{{( pgettext "Opening Dates" "input" )}}</legend>
<div class="lang-selector" role="toolbar">
{{ range $lang, $input := . -}}
<button :aria-pressed="lang === '{{ $lang }}'"
@click.prevent="lang = '{{ $lang }}'">{{ $lang }}</button>
{{- end }}
</div>
{{ range $lang, $input := . -}}
<label x-cloak x-show="lang === '{{ $lang }}'"><span>{{ $lang }}</span><br>
<textarea class="html"
name="{{ $input.Name }}" {{ template "error-attrs" . }}>{{ $input.Val }}</textarea><br>
</label>
{{- end }}
{{ template "error-message" . }}
</fieldset>
{{- end }}
{{ with .MapEmbed -}}
<label>
{{( pgettext "Map Embed" "input")}}<br>
<textarea name="{{ .Name }}" {{ template "error-attrs" . }}>{{ .Val }}</textarea><br>
{{ template "error-message" . }}
</label>
{{- end }}
</fieldset>
<footer>
<button type="submit">{{( pgettext "Save changes" "action" )}}</button>
</footer>
</form>
{{- end }}

View File

@ -1,62 +0,0 @@
<!--
SPDX-FileCopyrightText: 2023 jordi fita mas <jordi@tandem.blog>
SPDX-License-Identifier: AGPL-3.0-only
-->
{{ define "title" -}}
{{( pgettext "Contact" "title" )}}
{{- end }}
{{ define "head" -}}
<link rel="stylesheet" href="/static/leaflet@1.9.4/leaflet.css">
{{- end }}
{{ define "content" -}}
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/app.contactPage*/ -}}
<div class="contact-page">
<div class="contact-details">
<h2>{{( pgettext "Contact" "title" )}}</h2>
{{ template "companyAddress" .PublicPage.CompanyAddress }}
<p>GPS: 42º 14 43,86 / 2º 35 58,26</p>
</div>
<div id="map"></div>
</div>
{{ if .CompanyGeography -}}
<script src="/static/leaflet@1.9.4/leaflet.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
var container = document.getElementById("map");
container.style.height = '600px';
var map = L.map(container).setView(['{{ .CompanyGeography.Lat }}', '{{ .CompanyGeography.Lng }}'], '9');
L
.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png', {
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>, © <a href="https://carto.com/attributions">CARTO</a>'
})
.addTo(map)
;
map.scrollWheelZoom.disable();
var content =
{{- with .PublicPage.CompanyAddress -}}
'{{ .Address }}<br>{{ .City }} - {{ .PostalCode }}<br>{{ .Province }}'
{{- end -}}
;
L
.marker(['{{ .CompanyGeography.Lat }}', '{{ .CompanyGeography.Lng }}'])
.addTo(map)
.bindPopup(content)
;
var observer = ResizeObserver && new ResizeObserver(function () {
map.invalidateSize(true);
});
observer && observer.observe(container);
});
</script>
{{- end }}
{{- end }}

View File

@ -50,7 +50,7 @@
{{- end }}
<li><a href="/{{ currentLocale }}/services">{{( pgettext "Services" "title" )}}</a></li>
<li><a href="/{{ currentLocale }}/surroundings">{{( pgettext "Surroundings" "title" )}}</a></li>
<li><a href="/{{ currentLocale }}/contact">{{( pgettext "Contact" "title" )}}</a></li>
<li><a href="/{{ currentLocale }}/location">{{( pgettext "Location" "title" )}}</a></li>
<li class="boto-reserva"><a href="/{{ currentLocale }}/booking">{{( pgettext "Booking" "title" )}}</a></li>
{{ if .LocalizedAlternates -}}
<li class="has-submenu">{{ range .LocalizedAlternates -}}
@ -78,7 +78,7 @@
<li><a href="/{{ currentLocale }}/campground">{{( pgettext "Campground" "title" )}}</a></li>
<li><a href="/{{ currentLocale }}/services">{{( pgettext "Services" "title" )}}</a></li>
<li><a href="/{{ currentLocale }}/surroundings">{{( pgettext "Surroundings" "title" )}}</a></li>
<li><a href="/{{ currentLocale }}/contact">{{( pgettext "Contact" "title" )}}</a></li>
<li><a href="/{{ currentLocale }}/location">{{( pgettext "Location" "title" )}}</a></li>
</ul>
</section>
@ -96,9 +96,8 @@
{{- end }}
<section>
<h2>Obertura 2023</h2>
<p><strong>Camping i Safari tents:</strong><br>de 08/04 a 09/10</p>
<p><strong>Cabanes i Bungalows:</strong><br>de 08/04 a 11/12</p>
<h2>{{ (pgettext "Opening" "title" )}}</h2>
{{ .OpeningDates }}
</section>
<section>

View File

@ -0,0 +1,21 @@
<!--
SPDX-FileCopyrightText: 2023 jordi fita mas <jordi@tandem.blog>
SPDX-License-Identifier: AGPL-3.0-only
-->
{{ define "title" -}}
{{( pgettext "Location" "title" )}}
{{- end }}
{{ define "content" -}}
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/location.locationPage*/ -}}
<h2>{{( pgettext "Location" "title" )}}</h2>
<div class="contact-page">
<div class="contact-details">
{{ .Directions }}
</div>
{{ .MapEmbed }}
</div>
{{- end }}