Add the services page
This page is more or less similar to home, in terms of database: it has a carousel and a list of items; in this case, the definition of campsite services. As i said early, when adding the home carousel, this carousel has its own relation and set of functions to manage slides. They are also duplicated in Go code, but i think i will need to refactor it later to a carousel package or something like that, because both relations have the exact same fields and types, so it makes no sense to have twice the same code. I already did it with the CSS and JavaScript code, mostly because it was easier to replace the `.surroundings div` selector with `.carousel`, and because that way i can have a single template that loads and initializes Slick. There is no UI to create or edit service definitions, although there are the SQL functions, because i have no more time now, and Oriol needs to check that the style is correct for that page.
This commit is contained in:
parent
8b8dda7969
commit
afe77f2296
|
@ -38,6 +38,10 @@ values (52, 'plots.avif', 'image/avif', decode('m4_esyscmd([[base64 -w0 demo/plo
|
|||
, (52, 'home_carousel6.jpg', 'image/jpeg', decode('m4_esyscmd([[base64 -w0 demo/home_carousel6.jpg]])', 'base64'))
|
||||
, (52, 'home_carousel7.jpg', 'image/jpeg', decode('m4_esyscmd([[base64 -w0 demo/home_carousel7.jpg]])', 'base64'))
|
||||
, (52, 'home_carousel8.jpg', 'image/jpeg', decode('m4_esyscmd([[base64 -w0 demo/home_carousel8.jpg]])', 'base64'))
|
||||
, (52, 'services_carousel0.avif', 'image/avif', decode('m4_esyscmd([[base64 -w0 demo/services_carousel0.avif]])', 'base64'))
|
||||
, (52, 'services_carousel1.avif', 'image/avif', decode('m4_esyscmd([[base64 -w0 demo/services_carousel1.avif]])', 'base64'))
|
||||
, (52, 'services_carousel2.avif', 'image/avif', decode('m4_esyscmd([[base64 -w0 demo/services_carousel2.avif]])', 'base64'))
|
||||
, (52, 'services_carousel3.avif', 'image/avif', decode('m4_esyscmd([[base64 -w0 demo/services_carousel3.avif]])', 'base64'))
|
||||
;
|
||||
|
||||
insert into home_carousel (media_id, caption)
|
||||
|
@ -59,6 +63,25 @@ values (66, 'en', 'Santa Margarida volcano')
|
|||
, (67, 'es', 'Piletón oscuro Sadernes')
|
||||
;
|
||||
|
||||
insert into services_carousel (media_id, caption)
|
||||
values (75, 'La Garrotxa')
|
||||
, (76, 'Tenda')
|
||||
, (77, 'Parceŀles')
|
||||
, (78, 'Hamaca')
|
||||
, (63, 'Safari Tents')
|
||||
;
|
||||
|
||||
insert into services_carousel_i18n (media_id, lang_tag, caption)
|
||||
values (76, 'en', 'Tent')
|
||||
, (76, 'es', 'Tenda')
|
||||
, (77, 'en', 'Plots')
|
||||
, (77, 'es', 'Parcelas')
|
||||
, (78, 'en', 'Hammock')
|
||||
, (78, 'es', 'Amaca')
|
||||
, (63, 'en', 'Safari Tents')
|
||||
, (63, 'es', 'Tiendas Safari')
|
||||
;
|
||||
|
||||
alter sequence campsite_type_campsite_type_id_seq restart with 72;
|
||||
insert into campsite_type (company_id, name, media_id, description)
|
||||
values (52, 'Parceŀles', 62, '')
|
||||
|
@ -77,4 +100,59 @@ values (72, 'en', 'Plots', '')
|
|||
, (75, 'es', 'Cabañas de madera', '')
|
||||
;
|
||||
|
||||
alter sequence service_service_id_seq restart with 82;
|
||||
insert into service (company_id, icon_name, name, description)
|
||||
values (52, 'information', 'Informació', '<p>A la recepció l’informarem del que pot fer des del càmping mateix o pels voltants.</p>')
|
||||
, (52, 'wifi', 'WiFi', '<p>Un 80 % de l’àrea del càmping disposa d’accés WiFi lliure.</p>')
|
||||
, (52, 'restaurant', 'Bar & Tapes', '<p>Oberts:</p><ul><li>De l’01/07 al 28/08: cada dia</li><li>D’abril a setembre: caps de setmana i ponts</li></ul>')
|
||||
, (52, 'store', 'Botiga', '<p>Oberta a diari.</p><p>Venda de pa del dia per encàrrec.</p>')
|
||||
, (52, 'wheelchair', 'Accessibilitat', '<p>Piscines i serveis del càmping adaptats a persones amb mobilitat reduïda.</p>')
|
||||
, (52, 'toilet', 'Lavabos', '<p>Ubicació central i pràctica. Nets i ben mantinguts.</p>')
|
||||
, (52, 'shower', 'Dutxa', '<p>Aigua calenta, sense fitxes.</p>')
|
||||
, (52, 'baby', 'Bany per nadons', '<p>Bany individual per nadons, amb banyera i canviador.</p>')
|
||||
, (52, 'pool', 'Piscina', '<p>Piscina per adults i piscina infantil.</p><p><em>(Piscines amb aigua salada.)</em></p>')
|
||||
, (52, 'campfire', 'Barbacoa', '<p>Trobareu una barbacoa comunitària de carbó o la possibilitat de llogar una barbacoa de gas (no es pot fer servir llenya o carbó en les parcel·les).</p>')
|
||||
, (52, 'rv', 'Estació servei per autocaravanes', '<p>Situada a l’entrada del càmping.</p>')
|
||||
, (52, 'castle', 'Zona de jocs', '<p>Una zona central pels més menuts.</p>')
|
||||
, (52, 'ball', 'Camp d’esport', '<p>Amb camp de futbol, voley, tenis-taula i espai per jugar.</p>')
|
||||
, (52, 'puzzle', 'Sala de jocs i televisió', '<p>Una sala pels dies de mal temps.</p>')
|
||||
, (52, 'washer', 'Rentadores i assecadores', '<p>Als safareigs del càmping hi ha dues rentadores i una assecadora que funcionen amb fitxes.</p>')
|
||||
, (52, 'fridge', 'Lloguer de neveres', '<p>Possibilitat de llogar neveres per estades llargues amb <a href="https://www.rentit.es/ca/portal/productes/42/">Rent It</a>.</p>')
|
||||
;
|
||||
|
||||
insert into service_i18n (service_id, lang_tag, name, description)
|
||||
values (82, 'en', 'Information', '<p>At reception we will inform you of what you can do from the campsite itself or in the surrounding area.</p>')
|
||||
, (82, 'es', 'Información', '<p>A recepción le informaremos de qué puede hacer en el camping o por los alrededores.</p>')
|
||||
, (83, 'en', 'WiFi', '<p>80 % of the campsite area has free WiFi access.</p>')
|
||||
, (83, 'es', 'WiFi', '<p>Un 80 % del área del camping dispone de acceso WiFi libre.</p>')
|
||||
, (84, 'en', 'Bar & Tapas', '<p>Open:</p><ul><li>From 07/01 to 08/28: everyday</li><li>From April to September: weekends and holidays</li></ul>')
|
||||
, (84, 'es', 'Bar & Tapas', '<p>Abierto:</p><ul><li>Del 01/07 al 28/08: cada día</li><li>De abril a setiembre: fines de semana y puentes</li></ul>')
|
||||
, (85, 'en', 'Shop', '<p>Open daily</p><p>Sale of daily bread to order.</p>')
|
||||
, (85, 'es', 'Tienda', '<p>Abierta a diario.</p><p>Venta de pan del día por encargo.</p>')
|
||||
, (86, 'en', 'Accessibility', '<p>Swimming pools and campsite services adapted to people with reduced mobility.</p>')
|
||||
, (86, 'es', 'Acesibilidad', '<p>Piscinas y servicios del camping adaptados a personas con mobilidad reducida.</p>')
|
||||
, (87, 'en', 'Toilets', '<p>Central and practical location. Clean and well maintained.</p>')
|
||||
, (87, 'es', 'Lavabos', '<p>Ubicación central y práctica. Limpios y bien mantenidos.</p>')
|
||||
, (88, 'en', 'Showers', '<p>Hot water, no tokens.</p>')
|
||||
, (88, 'es', 'Duchas', '<p>Agua caliente, sin fichas.</p>')
|
||||
, (89, 'en', 'Baby baths', '<p>Individual bathroom for babies, with bathtub and changing table.</p>')
|
||||
, (89, 'es', 'Baño para bebés', '<p>Baños individuales para bebés, con bañera y cambiador.</p>')
|
||||
, (90, 'en', 'Swimming pool', '<p>Adult pool and children’s pool.</p><p><em>(Salt water swimming pools.)</em></p>')
|
||||
, (90, 'es', 'Piscina', '<p>Piscina para adultos y piscina infantil.</p><p><em>(Piscinas con agua salada.)</em></p>')
|
||||
, (91, 'en', 'Barbecue', '<p>You will find a communal charcoal barbecue or the possibility of renting a gas barbecue (no wood or charcoal can be used on the plots).</p>')
|
||||
, (91, 'es', 'Barbacoa', '<p>Encontraréis una barbacoa comunitaria de carbón o la posibilidad de alquilar una barbacoa de gas (no se puede utilizar leña o carbón en las parcelas).</p>')
|
||||
, (92, 'en', 'RV service station', '<p>Located at the entrance of the campsite.</p>')
|
||||
, (92, 'es', 'Estación servicio para autocaravanas', '<p>Situada en la entrada del camping.</p>')
|
||||
, (93, 'en', 'Play area', '<p>A central area for the little ones.</p>')
|
||||
, (93, 'es', 'Zona de juegos', '<p>Una zona central para los más pequeños.</p>')
|
||||
, (94, 'en', 'Sports area', '<p>With football field, volleyball, table tennis and room to play.</p>')
|
||||
, (94, 'es', 'Campo de deporte', '<p>Con campo de fútbol, voley, pimpón i espacio para jugar.</p>')
|
||||
, (95, 'en', 'Games and television room', '<p>A room for bad weather days.</p>')
|
||||
, (95, 'es', 'Sala de juegos y televisión', '<p>Una sala para los días de mal tiempo.</p>')
|
||||
, (96, 'en', 'Washing machines and dryers', '<p>There are two token-operated washing machines and a dryer in the campsite’s laundry facilities.</p>')
|
||||
, (96, 'es', 'Lavadora y secadoras', '<p>A los lavaderos del camping hay dos lavadoras y una secadora que funcionana con fichas.</p>')
|
||||
, (97, 'en', 'Fridge rental', '<p>Possibility to rent refrigerators for long stays with <a href="https://www.rentit.es/en/portal/productes/42/">Rent It</a>.</p>')
|
||||
, (97, 'es', 'Alquiler de neveras', '<p>Posibilidad de alquilar neveras para estancias largas con <a href="https://www.rentit.es/es/portal/productes/42/">Rent It</a>.</p>')
|
||||
;
|
||||
|
||||
commit;
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,23 @@
|
|||
-- Deploy camper:add_service to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: service
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function add_service(company integer, icon_name text, name text, description text) returns integer as
|
||||
$$
|
||||
insert into service (company_id, icon_name, name, description)
|
||||
values (company, icon_name, name, xmlparse (content description))
|
||||
returning service_id
|
||||
;
|
||||
$$
|
||||
language sql
|
||||
;
|
||||
|
||||
revoke execute on function add_service(integer, text, text, text) from public;
|
||||
grant execute on function add_service(integer, text, text, text) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,25 @@
|
|||
-- Deploy camper:add_services_carousel_slide to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: services_carousel
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function add_services_carousel_slide(media_id integer, caption text) returns void as
|
||||
$$
|
||||
insert into services_carousel (media_id, caption)
|
||||
values (media_id, coalesce(caption, ''))
|
||||
on conflict (media_id) do update
|
||||
set caption = excluded.caption
|
||||
returning media_id
|
||||
;
|
||||
$$
|
||||
language sql
|
||||
;
|
||||
|
||||
revoke execute on function add_services_carousel_slide(integer, text) from public;
|
||||
grant execute on function add_services_carousel_slide(integer, text) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,30 @@
|
|||
-- Deploy camper:available_icons to pg
|
||||
-- requires: schema_camper
|
||||
-- requires: icon
|
||||
|
||||
begin;
|
||||
|
||||
insert into camper.icon (icon_name)
|
||||
values ('baby')
|
||||
, ('ball')
|
||||
, ('bicycle')
|
||||
, ('campfire')
|
||||
, ('castle')
|
||||
, ('fridge')
|
||||
, ('information')
|
||||
, ('kayak')
|
||||
, ('outing')
|
||||
, ('pool')
|
||||
, ('puzzle')
|
||||
, ('restaurant')
|
||||
, ('route')
|
||||
, ('rv')
|
||||
, ('shower')
|
||||
, ('store')
|
||||
, ('toilet')
|
||||
, ('washer')
|
||||
, ('wheelchair')
|
||||
, ('wifi')
|
||||
;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,25 @@
|
|||
-- Deploy camper:edit_service to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: service
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function edit_service(service_id integer, icon_name text, name text, description text) returns void as
|
||||
$$
|
||||
update service
|
||||
set icon_name = edit_service.icon_name
|
||||
, name = edit_service.name
|
||||
, description = xmlparse(content edit_service.description)
|
||||
where service_id = edit_service.service_id
|
||||
;
|
||||
$$
|
||||
language sql
|
||||
;
|
||||
|
||||
revoke execute on function edit_service(integer, text, text, text) from public;
|
||||
grant execute on function edit_service(integer, text, text, text) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,17 @@
|
|||
-- Deploy camper:icon to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table icon (
|
||||
icon_name text not null primary key
|
||||
);
|
||||
|
||||
grant select on table icon to guest;
|
||||
grant select on table icon to employee;
|
||||
grant select on table icon to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,22 @@
|
|||
-- Deploy camper:remove_services_carousel_slide to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: services_carousel
|
||||
-- requires: services_carousel_i18n
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function remove_services_carousel_slide(media_id integer) returns void as
|
||||
$$
|
||||
delete from services_carousel_i18n where media_id = $1;
|
||||
delete from services_carousel where media_id = $1;
|
||||
$$
|
||||
language sql
|
||||
;
|
||||
|
||||
revoke execute on function remove_services_carousel_slide(integer) from public;
|
||||
grant execute on function remove_services_carousel_slide (integer) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,58 @@
|
|||
-- Deploy camper:service to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: company
|
||||
-- requires: icon
|
||||
-- requires: user_profile
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table service (
|
||||
service_id serial primary key,
|
||||
company_id integer not null references company,
|
||||
icon_name text not null references icon,
|
||||
name text not null constraint name_not_empty check(length(trim(name)) > 0),
|
||||
description xml not null
|
||||
);
|
||||
|
||||
grant select on table service to guest;
|
||||
grant select on table service to employee;
|
||||
grant select, insert, update, delete on table service to admin;
|
||||
|
||||
grant usage on sequence service_service_id_seq to admin;
|
||||
|
||||
alter table service enable row level security;
|
||||
|
||||
create policy guest_ok
|
||||
on service
|
||||
for select
|
||||
using (true)
|
||||
;
|
||||
|
||||
create policy insert_to_company
|
||||
on service
|
||||
for insert
|
||||
with check (
|
||||
company_id in (select company_id from user_profile)
|
||||
)
|
||||
;
|
||||
|
||||
create policy update_company
|
||||
on service
|
||||
for update
|
||||
using (
|
||||
company_id in (select company_id from user_profile)
|
||||
)
|
||||
;
|
||||
|
||||
create policy delete_from_company
|
||||
on service
|
||||
for delete
|
||||
using (
|
||||
company_id in (select company_id from user_profile)
|
||||
)
|
||||
;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,23 @@
|
|||
-- Deploy camper:service_i18n to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: service
|
||||
-- requires: language
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table service_i18n (
|
||||
service_id integer not null references service,
|
||||
lang_tag text not null references language,
|
||||
name text not null,
|
||||
description xml not null,
|
||||
primary key (service_id, lang_tag)
|
||||
);
|
||||
|
||||
grant select on table service_i18n to guest;
|
||||
grant select on table service_i18n to employee;
|
||||
grant select, insert, update, delete on table service_i18n to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,53 @@
|
|||
-- Deploy camper:services_carousel to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_public
|
||||
-- requires: company
|
||||
-- requires: media
|
||||
-- requires: user_profile
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table services_carousel (
|
||||
media_id integer not null primary key references media,
|
||||
caption text not null
|
||||
);
|
||||
|
||||
grant select on table services_carousel to guest;
|
||||
grant select on table services_carousel to employee;
|
||||
grant select, insert, update, delete on table services_carousel to admin;
|
||||
|
||||
alter table services_carousel enable row level security;
|
||||
|
||||
create policy guest_ok
|
||||
on services_carousel
|
||||
for select
|
||||
using (true)
|
||||
;
|
||||
|
||||
create policy insert_to_company
|
||||
on services_carousel
|
||||
for insert
|
||||
with check (
|
||||
exists (select 1 from media join user_profile using (company_id) where media.media_id = services_carousel.media_id)
|
||||
)
|
||||
;
|
||||
|
||||
create policy update_company
|
||||
on services_carousel
|
||||
for update
|
||||
using (
|
||||
exists (select 1 from media join user_profile using (company_id) where media.media_id = services_carousel.media_id)
|
||||
)
|
||||
;
|
||||
|
||||
create policy delete_from_company
|
||||
on services_carousel
|
||||
for delete
|
||||
using (
|
||||
exists (select 1 from media join user_profile using (company_id) where media.media_id = services_carousel.media_id)
|
||||
)
|
||||
;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,22 @@
|
|||
-- Deploy camper:services_carousel_i18n to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: services_carousel
|
||||
-- requires: language
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create table services_carousel_i18n (
|
||||
media_id integer not null references services_carousel,
|
||||
lang_tag text not null references language,
|
||||
caption text not null,
|
||||
primary key (media_id, lang_tag)
|
||||
);
|
||||
|
||||
grant select on table services_carousel_i18n to guest;
|
||||
grant select on table services_carousel_i18n to employee;
|
||||
grant select, insert, update, delete on table services_carousel_i18n to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,25 @@
|
|||
-- Deploy camper:translate_service to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: service_i18n
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function translate_service(service_id integer, lang_tag text, name text, description text) returns void as
|
||||
$$
|
||||
insert into service_i18n (service_id, lang_tag, name, description)
|
||||
values (service_id, lang_tag, name, xmlparse(content coalesce(description, '')))
|
||||
on conflict (service_id, lang_tag) do update
|
||||
set name = excluded.name
|
||||
, description = excluded.description
|
||||
;
|
||||
$$
|
||||
language sql
|
||||
;
|
||||
|
||||
revoke execute on function translate_service(integer, text, text, text) from public;
|
||||
grant execute on function translate_service(integer, text, text, text) to admin;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,23 @@
|
|||
-- Deploy camper:translate_services_carousel_slide to pg
|
||||
-- requires: roles
|
||||
-- requires: schema_camper
|
||||
-- requires: services_carousel_i18n
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
create or replace function translate_services_carousel_slide(media_id integer, lang_tag text, caption text) returns void as
|
||||
$$
|
||||
insert into services_carousel_i18n (media_id, lang_tag, caption)
|
||||
values (media_id, lang_tag, coalesce(caption, ''))
|
||||
on conflict (media_id, lang_tag) do update
|
||||
set caption = excluded.caption
|
||||
$$
|
||||
language sql
|
||||
;
|
||||
|
||||
revoke execute on function translate_services_carousel_slide(integer, text, text) from public;
|
||||
grant execute on function translate_services_carousel_slide(integer, text, text) to admin;
|
||||
|
||||
commit;
|
|
@ -16,6 +16,7 @@ import (
|
|||
httplib "dev.tandem.ws/tandem/camper/pkg/http"
|
||||
"dev.tandem.ws/tandem/camper/pkg/locale"
|
||||
"dev.tandem.ws/tandem/camper/pkg/season"
|
||||
"dev.tandem.ws/tandem/camper/pkg/services"
|
||||
"dev.tandem.ws/tandem/camper/pkg/template"
|
||||
)
|
||||
|
||||
|
@ -24,6 +25,7 @@ type adminHandler struct {
|
|||
company *company.AdminHandler
|
||||
home *home.AdminHandler
|
||||
season *season.AdminHandler
|
||||
services *services.AdminHandler
|
||||
}
|
||||
|
||||
func newAdminHandler(locales locale.Locales) *adminHandler {
|
||||
|
@ -32,6 +34,7 @@ func newAdminHandler(locales locale.Locales) *adminHandler {
|
|||
company: company.NewAdminHandler(),
|
||||
home: home.NewAdminHandler(locales),
|
||||
season: season.NewAdminHandler(),
|
||||
services: services.NewAdminHandler(locales),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +62,8 @@ func (h *adminHandler) Handle(user *auth.User, company *auth.Company, conn *data
|
|||
h.home.Handler(user, company, conn).ServeHTTP(w, r)
|
||||
case "seasons":
|
||||
h.season.Handler(user, company, conn).ServeHTTP(w, r)
|
||||
case "services":
|
||||
h.services.Handler(user, company, conn).ServeHTTP(w, r)
|
||||
case "":
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
|
|
|
@ -13,18 +13,21 @@ 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/services"
|
||||
"dev.tandem.ws/tandem/camper/pkg/template"
|
||||
)
|
||||
|
||||
type publicHandler struct {
|
||||
home *home.PublicHandler
|
||||
campsite *campsite.PublicHandler
|
||||
services *services.PublicHandler
|
||||
}
|
||||
|
||||
func newPublicHandler() *publicHandler {
|
||||
return &publicHandler{
|
||||
home: home.NewPublicHandler(),
|
||||
campsite: campsite.NewPublicHandler(),
|
||||
services: services.NewPublicHandler(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,6 +40,8 @@ func (h *publicHandler) Handler(user *auth.User, company *auth.Company, conn *da
|
|||
h.home.Handler(user, company, conn).ServeHTTP(w, r)
|
||||
case "campsites":
|
||||
h.campsite.Handler(user, company, conn).ServeHTTP(w, r)
|
||||
case "services":
|
||||
h.services.Handler(user, company, conn).ServeHTTP(w, r)
|
||||
case "surroundings":
|
||||
surroundingsHandler(user, company, conn).ServeHTTP(w, r)
|
||||
default:
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 jordi fita mas <jfita@peritasoft.com>
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package services
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"dev.tandem.ws/tandem/camper/pkg/auth"
|
||||
"dev.tandem.ws/tandem/camper/pkg/database"
|
||||
httplib "dev.tandem.ws/tandem/camper/pkg/http"
|
||||
"dev.tandem.ws/tandem/camper/pkg/locale"
|
||||
"dev.tandem.ws/tandem/camper/pkg/template"
|
||||
)
|
||||
|
||||
type AdminHandler struct {
|
||||
locales locale.Locales
|
||||
}
|
||||
|
||||
func NewAdminHandler(locales locale.Locales) *AdminHandler {
|
||||
return &AdminHandler{locales}
|
||||
}
|
||||
|
||||
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:
|
||||
serveHomeIndex(w, r, user, company, conn)
|
||||
default:
|
||||
httplib.MethodNotAllowed(w, r, http.MethodGet)
|
||||
}
|
||||
case "slides":
|
||||
h.carouselHandler(user, company, conn).ServeHTTP(w, r)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func serveHomeIndex(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
|
||||
slides, err := collectSlideEntries(r.Context(), company, conn)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
page := &servicesIndex{
|
||||
Slides: slides,
|
||||
}
|
||||
page.MustRender(w, r, user, company)
|
||||
}
|
||||
|
||||
type servicesIndex struct {
|
||||
Slides []*slideEntry
|
||||
}
|
||||
|
||||
func (page *servicesIndex) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
|
||||
template.MustRenderAdmin(w, r, user, company, "services/index.gohtml", page)
|
||||
}
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 jordi fita mas <jfita@peritasoft.com>
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
func (h *AdminHandler) carouselHandler(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.MethodPost:
|
||||
addSlide(w, r, user, company, conn)
|
||||
default:
|
||||
httplib.MethodNotAllowed(w, r, http.MethodGet)
|
||||
}
|
||||
case "new":
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
f := newSlideForm()
|
||||
f.MustRender(w, r, user, company)
|
||||
default:
|
||||
httplib.MethodNotAllowed(w, r, http.MethodGet)
|
||||
}
|
||||
default:
|
||||
id, err := strconv.Atoi(head)
|
||||
if err != nil {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
f := newSlideForm()
|
||||
if err := f.FillFromDatabase(r.Context(), conn, id); err != nil {
|
||||
if database.ErrorIsNotFound(err) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var langTag string
|
||||
langTag, r.URL.Path = httplib.ShiftPath(r.URL.Path)
|
||||
|
||||
switch langTag {
|
||||
case "":
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
f.MustRender(w, r, user, company)
|
||||
case http.MethodPut:
|
||||
editSlide(w, r, user, company, conn, f)
|
||||
case http.MethodDelete:
|
||||
deleteSlide(w, r, user, conn, id)
|
||||
default:
|
||||
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPut)
|
||||
}
|
||||
default:
|
||||
loc, ok := h.locales.Get(langTag)
|
||||
if !ok {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
l10n := newSlideL10nForm(f, loc)
|
||||
if err := l10n.FillFromDatabase(r.Context(), conn); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
l10n.MustRender(w, r, user, company)
|
||||
case http.MethodPut:
|
||||
editSlideL10n(w, r, user, company, conn, l10n)
|
||||
default:
|
||||
httplib.MethodNotAllowed(w, r, http.MethodGet, http.MethodPut)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type carouselSlide struct {
|
||||
Media string
|
||||
Caption string
|
||||
}
|
||||
|
||||
func mustCollectCarouselSlides(ctx context.Context, company *auth.Company, conn *database.Conn, loc *locale.Locale) []*carouselSlide {
|
||||
rows, err := conn.Query(ctx, `
|
||||
select coalesce(i18n.caption, slide.caption) as l10_caption
|
||||
, media.path
|
||||
from services_carousel as slide
|
||||
join media using (media_id)
|
||||
left join services_carousel_i18n as i18n on i18n.media_id = slide.media_id and lang_tag = $1
|
||||
where media.company_id = $2
|
||||
`, loc.Language, company.ID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var carousel []*carouselSlide
|
||||
for rows.Next() {
|
||||
slide := &carouselSlide{}
|
||||
err = rows.Scan(&slide.Caption, &slide.Media)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
carousel = append(carousel, slide)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
panic(rows.Err())
|
||||
}
|
||||
|
||||
return carousel
|
||||
}
|
||||
|
||||
type slideEntry struct {
|
||||
carouselSlide
|
||||
ID int
|
||||
Translations []*translation
|
||||
}
|
||||
|
||||
type translation struct {
|
||||
Language string
|
||||
Endonym string
|
||||
Missing bool
|
||||
}
|
||||
|
||||
func collectSlideEntries(ctx context.Context, company *auth.Company, conn *database.Conn) ([]*slideEntry, error) {
|
||||
rows, err := conn.Query(ctx, `
|
||||
select media_id
|
||||
, media.path
|
||||
, caption
|
||||
, array_agg((lang_tag, endonym, not exists (select 1 from services_carousel_i18n as i18n where i18n.media_id = services_carousel.media_id and i18n.lang_tag = language.lang_tag)) order by endonym)
|
||||
from services_carousel
|
||||
join media using (media_id)
|
||||
join company using (company_id)
|
||||
, language
|
||||
where lang_tag <> default_lang_tag
|
||||
and language.selectable
|
||||
and media.company_id = $1
|
||||
group by media_id
|
||||
, media.path
|
||||
, caption
|
||||
order by caption
|
||||
`, pgx.QueryResultFormats{pgx.BinaryFormatCode}, company.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var slides []*slideEntry
|
||||
for rows.Next() {
|
||||
slide := &slideEntry{}
|
||||
var translations database.RecordArray
|
||||
if err = rows.Scan(&slide.ID, &slide.Media, &slide.Caption, &translations); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, el := range translations.Elements {
|
||||
slide.Translations = append(slide.Translations, &translation{
|
||||
el.Fields[0].Get().(string),
|
||||
el.Fields[1].Get().(string),
|
||||
el.Fields[2].Get().(bool),
|
||||
})
|
||||
}
|
||||
slides = append(slides, slide)
|
||||
}
|
||||
|
||||
return slides, nil
|
||||
}
|
||||
|
||||
func addSlide(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
|
||||
f := newSlideForm()
|
||||
editSlide(w, r, user, company, conn, f)
|
||||
}
|
||||
|
||||
func editSlide(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, f *slideForm) {
|
||||
f.process(w, r, user, company, false, func(ctx context.Context) {
|
||||
bytes := f.MustReadAllMedia()
|
||||
if bytes == nil {
|
||||
conn.MustExec(ctx, "select add_services_carousel_slide($1, $2)", f.ID, f.Caption)
|
||||
} else {
|
||||
tx := conn.MustBegin(ctx)
|
||||
defer tx.Rollback(ctx)
|
||||
f.ID = tx.MustGetInt(ctx, "select add_media($1, $2, $3, $4)", company.ID, f.Media.Filename(), f.Media.ContentType, bytes)
|
||||
tx.MustExec(ctx, "select add_services_carousel_slide($1, $2)", f.ID, f.Caption)
|
||||
tx.MustCommit(ctx)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func deleteSlide(w http.ResponseWriter, r *http.Request, user *auth.User, conn *database.Conn, id int) {
|
||||
if err := user.VerifyCSRFToken(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
conn.MustExec(r.Context(), "select remove_services_carousel_slide($1)", id)
|
||||
httplib.Redirect(w, r, "/admin/services", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
type slideForm struct {
|
||||
ID int
|
||||
Media *form.File
|
||||
Caption *form.Input
|
||||
}
|
||||
|
||||
func newSlideForm() *slideForm {
|
||||
return &slideForm{
|
||||
Media: &form.File{
|
||||
Name: "media",
|
||||
MaxSize: 1 << 20,
|
||||
},
|
||||
Caption: &form.Input{
|
||||
Name: "caption",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *slideForm) FillFromDatabase(ctx context.Context, conn *database.Conn, id int) error {
|
||||
f.ID = id
|
||||
row := conn.QueryRow(ctx, `
|
||||
select caption
|
||||
, media.path
|
||||
from services_carousel
|
||||
join media using (media_id)
|
||||
where media_id = $1
|
||||
`, id)
|
||||
return row.Scan(&f.Caption.Val, &f.Media.Val)
|
||||
}
|
||||
|
||||
func (f *slideForm) process(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, mediaRequired bool, act func(ctx context.Context)) {
|
||||
if err := f.Parse(w, r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
if err := user.VerifyCSRFToken(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if !f.Valid(user.Locale, mediaRequired) {
|
||||
if !httplib.IsHTMxRequest(r) {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
}
|
||||
f.MustRender(w, r, user, company)
|
||||
return
|
||||
}
|
||||
act(r.Context())
|
||||
httplib.Redirect(w, r, "/admin/services", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (f *slideForm) Parse(w http.ResponseWriter, r *http.Request) error {
|
||||
maxSize := f.Media.MaxSize + 1024
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxSize)
|
||||
if err := r.ParseMultipartForm(maxSize); err != nil {
|
||||
return err
|
||||
}
|
||||
f.Caption.FillValue(r)
|
||||
if err := f.Media.FillValue(r); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *slideForm) Close() error {
|
||||
return f.Media.Close()
|
||||
}
|
||||
|
||||
func (f *slideForm) Valid(l *locale.Locale, mediaRequired bool) bool {
|
||||
v := form.NewValidator(l)
|
||||
if f.HasMediaFile() {
|
||||
v.CheckImageFile(f.Media, l.GettextNoop("File must be a valid PNG or JPEG image."))
|
||||
} else {
|
||||
v.Check(f.Media, !mediaRequired, l.GettextNoop("Slide image can not be empty."))
|
||||
}
|
||||
return v.AllOK
|
||||
}
|
||||
|
||||
func (f *slideForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
|
||||
template.MustRenderAdmin(w, r, user, company, "services/carousel/form.gohtml", f)
|
||||
}
|
||||
|
||||
func (f *slideForm) HasMediaFile() bool {
|
||||
return f.Media.HasData()
|
||||
}
|
||||
|
||||
func (f *slideForm) MustReadAllMedia() []byte {
|
||||
if !f.HasMediaFile() {
|
||||
return nil
|
||||
}
|
||||
bytes, err := io.ReadAll(f.Media)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bytes
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 jordi fita mas <jfita@peritasoft.com>
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"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 slideL10nForm struct {
|
||||
Locale *locale.Locale
|
||||
ID int
|
||||
Caption *form.L10nInput
|
||||
}
|
||||
|
||||
func newSlideL10nForm(f *slideForm, loc *locale.Locale) *slideL10nForm {
|
||||
return &slideL10nForm{
|
||||
Locale: loc,
|
||||
ID: f.ID,
|
||||
Caption: f.Caption.L10nInput(),
|
||||
}
|
||||
}
|
||||
|
||||
func (l10n *slideL10nForm) FillFromDatabase(ctx context.Context, conn *database.Conn) error {
|
||||
row := conn.QueryRow(ctx, `
|
||||
select coalesce(i18n.caption, '') as l10n_caption
|
||||
from services_carousel
|
||||
left join services_carousel_i18n as i18n on services_carousel.media_id = i18n.media_id and i18n.lang_tag = $1
|
||||
where services_carousel.media_id = $2
|
||||
`, l10n.Locale.Language, l10n.ID)
|
||||
return row.Scan(&l10n.Caption.Val)
|
||||
}
|
||||
|
||||
func (l10n *slideL10nForm) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company) {
|
||||
template.MustRenderAdmin(w, r, user, company, "services/carousel/l10n.gohtml", l10n)
|
||||
}
|
||||
|
||||
func editSlideL10n(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn, l10n *slideL10nForm) {
|
||||
if err := l10n.Parse(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := user.VerifyCSRFToken(r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if !l10n.Valid(user.Locale) {
|
||||
if !httplib.IsHTMxRequest(r) {
|
||||
w.WriteHeader(http.StatusUnprocessableEntity)
|
||||
}
|
||||
l10n.MustRender(w, r, user, company)
|
||||
return
|
||||
}
|
||||
conn.MustExec(r.Context(), "select translate_services_carousel_slide($1, $2, $3)", l10n.ID, l10n.Locale.Language, l10n.Caption)
|
||||
httplib.Redirect(w, r, "/admin/services", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (l10n *slideL10nForm) Parse(r *http.Request) error {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
l10n.Caption.FillValue(r)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l10n *slideL10nForm) Valid(l *locale.Locale) bool {
|
||||
v := form.NewValidator(l)
|
||||
return v.AllOK
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 jordi fita mas <jfita@peritasoft.com>
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"dev.tandem.ws/tandem/camper/pkg/auth"
|
||||
"dev.tandem.ws/tandem/camper/pkg/database"
|
||||
httplib "dev.tandem.ws/tandem/camper/pkg/http"
|
||||
"dev.tandem.ws/tandem/camper/pkg/locale"
|
||||
"dev.tandem.ws/tandem/camper/pkg/template"
|
||||
)
|
||||
|
||||
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:
|
||||
home := newServicesPage()
|
||||
home.MustRender(w, r, user, company, conn)
|
||||
default:
|
||||
httplib.MethodNotAllowed(w, r, http.MethodGet)
|
||||
}
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type servicesPage struct {
|
||||
*template.PublicPage
|
||||
Services []*service
|
||||
Carousel []*carouselSlide
|
||||
}
|
||||
|
||||
func newServicesPage() *servicesPage {
|
||||
return &servicesPage{PublicPage: template.NewPublicPage()}
|
||||
}
|
||||
|
||||
func (p *servicesPage) MustRender(w http.ResponseWriter, r *http.Request, user *auth.User, company *auth.Company, conn *database.Conn) {
|
||||
p.Setup(r, user, company, conn)
|
||||
p.Services = mustCollectServices(r.Context(), company, conn, user.Locale)
|
||||
p.Carousel = mustCollectCarouselSlides(r.Context(), company, conn, user.Locale)
|
||||
template.MustRenderPublic(w, r, user, company, "services.gohtml", p)
|
||||
}
|
||||
|
||||
type service struct {
|
||||
IconName string
|
||||
Name string
|
||||
Description string
|
||||
}
|
||||
|
||||
func mustCollectServices(ctx context.Context, company *auth.Company, conn *database.Conn, loc *locale.Locale) []*service {
|
||||
rows, err := conn.Query(ctx, `
|
||||
select icon_name
|
||||
, coalesce(i18n.name, service.name) as l10_name
|
||||
, coalesce(i18n.description, service.description)::text as l10_description
|
||||
from service
|
||||
left join service_i18n as i18n on service.service_id = i18n.service_id and lang_tag = $1
|
||||
where service.company_id = $2
|
||||
`, loc.Language, company.ID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var items []*service
|
||||
for rows.Next() {
|
||||
item := &service{}
|
||||
err = rows.Scan(&item.IconName, &item.Name, &item.Description)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
if rows.Err() != nil {
|
||||
panic(rows.Err())
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
198
po/ca.po
198
po/ca.po
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: camper\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2023-09-16 23:43+0200\n"
|
||||
"POT-Creation-Date: 2023-09-17 03:28+0200\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"
|
||||
|
@ -18,7 +18,17 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: web/templates/public/home.gohtml:6 web/templates/public/layout.gohtml:28
|
||||
#: web/templates/public/services.gohtml:6
|
||||
#: web/templates/public/services.gohtml:15
|
||||
msgctxt "title"
|
||||
msgid "Services"
|
||||
msgstr "Serveis"
|
||||
|
||||
#: web/templates/public/services.gohtml:18
|
||||
msgid "The campsite offers many different services."
|
||||
msgstr "El càmping disposa de diversos serveis."
|
||||
|
||||
#: web/templates/public/home.gohtml:6 web/templates/public/layout.gohtml:29
|
||||
msgctxt "title"
|
||||
msgid "Home"
|
||||
msgstr "Inici"
|
||||
|
@ -37,8 +47,11 @@ msgid "Our services"
|
|||
msgstr "Els nostres serveis"
|
||||
|
||||
#: web/templates/public/home.gohtml:34
|
||||
#: web/templates/public/surroundings.gohtml:6
|
||||
#: web/templates/public/surroundings.gohtml:10
|
||||
msgctxt "title"
|
||||
msgid "Surroundings"
|
||||
msgstr "Entorn"
|
||||
msgstr "L’entorn"
|
||||
|
||||
#: web/templates/public/home.gohtml:37
|
||||
msgid "Located in <strong>Alta Garrotxa</strong>, between the <strong>Pyrenees</strong> and the <strong>Costa Brava</strong>."
|
||||
|
@ -60,12 +73,6 @@ msgstr "Descobreix l’entorn"
|
|||
msgid "Come and enjoy!"
|
||||
msgstr "Vine a gaudir!"
|
||||
|
||||
#: web/templates/public/surroundings.gohtml:6
|
||||
#: web/templates/public/surroundings.gohtml:10
|
||||
msgctxt "title"
|
||||
msgid "Surroundings"
|
||||
msgstr "L’entorn"
|
||||
|
||||
#: web/templates/public/surroundings.gohtml:13
|
||||
msgctxt "title"
|
||||
msgid "What to Do Outside the Campsite?"
|
||||
|
@ -124,8 +131,8 @@ msgstr "Caiac"
|
|||
msgid "There are several points where you can go by kayak, from sections of the Ter river as well as on the coast…."
|
||||
msgstr "Hi ha diversos punts on poder anar amb caiac, des de trams del riu Ter com també a la costa…."
|
||||
|
||||
#: web/templates/public/layout.gohtml:11 web/templates/public/layout.gohtml:23
|
||||
#: web/templates/public/layout.gohtml:58
|
||||
#: web/templates/public/layout.gohtml:11 web/templates/public/layout.gohtml:24
|
||||
#: web/templates/public/layout.gohtml:59
|
||||
msgid "Campsite Montagut"
|
||||
msgstr "Càmping Montagut"
|
||||
|
||||
|
@ -133,7 +140,7 @@ msgstr "Càmping Montagut"
|
|||
msgid "Skip to main content"
|
||||
msgstr "Salta al contingut principal"
|
||||
|
||||
#: web/templates/public/layout.gohtml:32
|
||||
#: web/templates/public/layout.gohtml:33
|
||||
msgid "Singular Lodges"
|
||||
msgstr "Allotjaments singulars"
|
||||
|
||||
|
@ -172,6 +179,7 @@ msgstr "Etiqueta"
|
|||
#: web/templates/admin/campsite/form.gohtml:71
|
||||
#: web/templates/admin/campsite/type/form.gohtml:77
|
||||
#: web/templates/admin/season/form.gohtml:65
|
||||
#: web/templates/admin/services/carousel/form.gohtml:58
|
||||
#: web/templates/admin/home/carousel/form.gohtml:58
|
||||
msgctxt "action"
|
||||
msgid "Update"
|
||||
|
@ -180,6 +188,7 @@ msgstr "Actualitza"
|
|||
#: web/templates/admin/campsite/form.gohtml:73
|
||||
#: web/templates/admin/campsite/type/form.gohtml:79
|
||||
#: web/templates/admin/season/form.gohtml:67
|
||||
#: web/templates/admin/services/carousel/form.gohtml:60
|
||||
#: web/templates/admin/home/carousel/form.gohtml:60
|
||||
msgctxt "action"
|
||||
msgid "Add"
|
||||
|
@ -250,6 +259,7 @@ msgid "Name"
|
|||
msgstr "Nom"
|
||||
|
||||
#: web/templates/admin/campsite/type/form.gohtml:59
|
||||
#: web/templates/admin/services/carousel/form.gohtml:39
|
||||
#: web/templates/admin/home/carousel/form.gohtml:39
|
||||
msgctxt "input"
|
||||
msgid "Cover image"
|
||||
|
@ -280,6 +290,7 @@ msgid "Name"
|
|||
msgstr "Nom"
|
||||
|
||||
#: web/templates/admin/campsite/type/index.gohtml:19
|
||||
#: web/templates/admin/services/index.gohtml:20
|
||||
#: web/templates/admin/home/index.gohtml:20
|
||||
msgctxt "campsite type"
|
||||
msgid "Translations"
|
||||
|
@ -297,18 +308,21 @@ msgstr "Traducció del tipus d’allotjament a %s"
|
|||
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:22
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:34
|
||||
#: web/templates/admin/services/carousel/l10n.gohtml:22
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:22
|
||||
msgid "Source:"
|
||||
msgstr "Origen:"
|
||||
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:24
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:37
|
||||
#: web/templates/admin/services/carousel/l10n.gohtml:24
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:24
|
||||
msgctxt "input"
|
||||
msgid "Translation:"
|
||||
msgstr "Traducció:"
|
||||
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:46
|
||||
#: web/templates/admin/services/carousel/l10n.gohtml:33
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:33
|
||||
msgctxt "action"
|
||||
msgid "Translate"
|
||||
|
@ -385,6 +399,85 @@ msgctxt "action"
|
|||
msgid "Login"
|
||||
msgstr "Entra"
|
||||
|
||||
#: web/templates/admin/services/carousel/form.gohtml:8
|
||||
#: web/templates/admin/services/carousel/form.gohtml:27
|
||||
#: web/templates/admin/home/carousel/form.gohtml:8
|
||||
#: web/templates/admin/home/carousel/form.gohtml:27
|
||||
msgctxt "title"
|
||||
msgid "Edit Carousel Slide"
|
||||
msgstr "Edició de la diapositiva del carrusel"
|
||||
|
||||
#: web/templates/admin/services/carousel/form.gohtml:10
|
||||
#: web/templates/admin/services/carousel/form.gohtml:29
|
||||
#: web/templates/admin/home/carousel/form.gohtml:10
|
||||
#: web/templates/admin/home/carousel/form.gohtml:29
|
||||
msgctxt "title"
|
||||
msgid "New Carousel Slide"
|
||||
msgstr "Nova diapositiva del carrusel"
|
||||
|
||||
#: web/templates/admin/services/carousel/form.gohtml:48
|
||||
#: web/templates/admin/services/carousel/l10n.gohtml:21
|
||||
#: web/templates/admin/home/carousel/form.gohtml:48
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:21
|
||||
msgctxt "input"
|
||||
msgid "Caption"
|
||||
msgstr "Llegenda"
|
||||
|
||||
#: web/templates/admin/services/carousel/l10n.gohtml:7
|
||||
#: web/templates/admin/services/carousel/l10n.gohtml:15
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:7
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:15
|
||||
msgctxt "title"
|
||||
msgid "Translate Carousel Slide to %s"
|
||||
msgstr "Traducció de la diapositiva del carrusel a %s"
|
||||
|
||||
#: web/templates/admin/services/index.gohtml:6
|
||||
#: web/templates/admin/layout.gohtml:76 web/templates/admin/home/index.gohtml:6
|
||||
msgctxt "title"
|
||||
msgid "Home Page"
|
||||
msgstr "Pàgina d’inici"
|
||||
|
||||
#: web/templates/admin/services/index.gohtml:12
|
||||
#: web/templates/admin/home/index.gohtml:12
|
||||
msgctxt "title"
|
||||
msgid "Carousel"
|
||||
msgstr "Carrusel"
|
||||
|
||||
#: web/templates/admin/services/index.gohtml:13
|
||||
#: web/templates/admin/home/index.gohtml:13
|
||||
msgctxt "action"
|
||||
msgid "Add slide"
|
||||
msgstr "Afegeix diapositiva"
|
||||
|
||||
#: web/templates/admin/services/index.gohtml:18
|
||||
#: web/templates/admin/home/index.gohtml:18
|
||||
msgctxt "header"
|
||||
msgid "Image"
|
||||
msgstr "Imatge"
|
||||
|
||||
#: web/templates/admin/services/index.gohtml:19
|
||||
#: web/templates/admin/home/index.gohtml:19
|
||||
msgctxt "header"
|
||||
msgid "Caption"
|
||||
msgstr "Llegenda"
|
||||
|
||||
#: web/templates/admin/services/index.gohtml:21
|
||||
#: web/templates/admin/home/index.gohtml:21
|
||||
msgctxt "campsite type"
|
||||
msgid "Actions"
|
||||
msgstr "Accions"
|
||||
|
||||
#: web/templates/admin/services/index.gohtml:40
|
||||
#: web/templates/admin/home/index.gohtml:40
|
||||
msgctxt "action"
|
||||
msgid "Delete"
|
||||
msgstr "Esborra"
|
||||
|
||||
#: web/templates/admin/services/index.gohtml:48
|
||||
#: web/templates/admin/home/index.gohtml:48
|
||||
msgid "No slides added yet."
|
||||
msgstr "No s’ha afegit cap diapositiva encara."
|
||||
|
||||
#: web/templates/admin/profile.gohtml:6 web/templates/admin/profile.gohtml:12
|
||||
#: web/templates/admin/layout.gohtml:29
|
||||
msgctxt "title"
|
||||
|
@ -505,68 +598,11 @@ msgctxt "action"
|
|||
msgid "Logout"
|
||||
msgstr "Surt"
|
||||
|
||||
#: web/templates/admin/layout.gohtml:76 web/templates/admin/home/index.gohtml:6
|
||||
#: web/templates/admin/layout.gohtml:79
|
||||
#, fuzzy
|
||||
msgctxt "title"
|
||||
msgid "Home Page"
|
||||
msgstr "Pàgina d’inici"
|
||||
|
||||
#: web/templates/admin/home/carousel/form.gohtml:8
|
||||
#: web/templates/admin/home/carousel/form.gohtml:27
|
||||
msgctxt "title"
|
||||
msgid "Edit Carousel Slide"
|
||||
msgstr "Edició de la diapositiva del carrusel"
|
||||
|
||||
#: web/templates/admin/home/carousel/form.gohtml:10
|
||||
#: web/templates/admin/home/carousel/form.gohtml:29
|
||||
msgctxt "title"
|
||||
msgid "New Carousel Slide"
|
||||
msgstr "Nova diapositiva del carrusel"
|
||||
|
||||
#: web/templates/admin/home/carousel/form.gohtml:48
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:21
|
||||
msgctxt "input"
|
||||
msgid "Caption"
|
||||
msgstr "Llegenda"
|
||||
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:7
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:15
|
||||
msgctxt "title"
|
||||
msgid "Translate Carousel Slide to %s"
|
||||
msgstr "Traducció de la diapositiva del carrusel a %s"
|
||||
|
||||
#: web/templates/admin/home/index.gohtml:12
|
||||
msgctxt "title"
|
||||
msgid "Carousel"
|
||||
msgstr "Carrusel"
|
||||
|
||||
#: web/templates/admin/home/index.gohtml:13
|
||||
msgctxt "action"
|
||||
msgid "Add slide"
|
||||
msgstr "Afegeix diapositiva"
|
||||
|
||||
#: web/templates/admin/home/index.gohtml:18
|
||||
msgctxt "header"
|
||||
msgid "Image"
|
||||
msgstr "Imatge"
|
||||
|
||||
#: web/templates/admin/home/index.gohtml:19
|
||||
msgctxt "header"
|
||||
msgid "Caption"
|
||||
msgstr "Llegenda"
|
||||
|
||||
#: web/templates/admin/home/index.gohtml:21
|
||||
msgctxt "campsite type"
|
||||
msgid "Actions"
|
||||
msgstr "Accions"
|
||||
|
||||
#: web/templates/admin/home/index.gohtml:40
|
||||
msgctxt "action"
|
||||
msgid "Delete"
|
||||
msgstr "Esborra"
|
||||
|
||||
#: web/templates/admin/home/index.gohtml:48
|
||||
msgid "No slides added yet."
|
||||
msgstr "No s’ha afegit cap diapositiva encara."
|
||||
msgid "Services Page"
|
||||
msgstr "Serveis"
|
||||
|
||||
#: web/templates/admin/media/index.gohtml:6
|
||||
#: web/templates/admin/media/index.gohtml:12
|
||||
|
@ -637,11 +673,12 @@ msgstr "La confirmació no es correspon amb la contrasenya."
|
|||
msgid "Selected language is not valid."
|
||||
msgstr "L’idioma escollit no és vàlid."
|
||||
|
||||
#: pkg/app/user.go:253 pkg/campsite/types/admin.go:296 pkg/home/carousel.go:287
|
||||
#: pkg/app/user.go:253 pkg/campsite/types/admin.go:296
|
||||
#: pkg/services/carousel.go:287 pkg/home/carousel.go:287
|
||||
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:47
|
||||
#: pkg/app/admin.go:50
|
||||
msgid "Access forbidden"
|
||||
msgstr "Accés prohibit"
|
||||
|
||||
|
@ -665,6 +702,10 @@ msgstr "No podeu deixar el color en blanc."
|
|||
msgid "This color is not valid. It must be like #123abc."
|
||||
msgstr "Aquest color no és vàlid. Hauria de ser similar a #123abc."
|
||||
|
||||
#: pkg/services/carousel.go:289 pkg/home/carousel.go:289
|
||||
msgid "Slide image can not be empty."
|
||||
msgstr "No podeu deixar la imatge de la diapositiva en blanc."
|
||||
|
||||
#: pkg/company/admin.go:186
|
||||
msgid "Selected country is not valid."
|
||||
msgstr "El país escollit no és vàlid."
|
||||
|
@ -729,14 +770,13 @@ msgstr "No podeu deixar el format del número de factura en blanc."
|
|||
msgid "Cross-site request forgery detected."
|
||||
msgstr "S’ha detectat un intent de falsificació de petició a llocs creuats."
|
||||
|
||||
#: pkg/home/carousel.go:289
|
||||
msgid "Slide image can not be empty."
|
||||
msgstr "No podeu deixar la imatge de la diapositiva en blanc."
|
||||
|
||||
#: pkg/media/admin.go:164
|
||||
msgid "Uploaded file can not be empty."
|
||||
msgstr "No podeu deixar el fitxer del mèdia en blanc."
|
||||
|
||||
#~ msgid "Surroundings"
|
||||
#~ msgstr "Entorn"
|
||||
|
||||
#~ msgid "Legend"
|
||||
#~ msgstr "Llegenda"
|
||||
|
||||
|
|
198
po/es.po
198
po/es.po
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: camper\n"
|
||||
"Report-Msgid-Bugs-To: jordi@tandem.blog\n"
|
||||
"POT-Creation-Date: 2023-09-16 23:55+0200\n"
|
||||
"POT-Creation-Date: 2023-09-17 03:28+0200\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"
|
||||
|
@ -18,7 +18,17 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: web/templates/public/home.gohtml:6 web/templates/public/layout.gohtml:28
|
||||
#: web/templates/public/services.gohtml:6
|
||||
#: web/templates/public/services.gohtml:15
|
||||
msgctxt "title"
|
||||
msgid "Services"
|
||||
msgstr "Servicios"
|
||||
|
||||
#: web/templates/public/services.gohtml:18
|
||||
msgid "The campsite offers many different services."
|
||||
msgstr "El camping dispone de varios servicios."
|
||||
|
||||
#: web/templates/public/home.gohtml:6 web/templates/public/layout.gohtml:29
|
||||
msgctxt "title"
|
||||
msgid "Home"
|
||||
msgstr "Inicio"
|
||||
|
@ -37,8 +47,11 @@ msgid "Our services"
|
|||
msgstr "Nuestros servicios"
|
||||
|
||||
#: web/templates/public/home.gohtml:34
|
||||
#: web/templates/public/surroundings.gohtml:6
|
||||
#: web/templates/public/surroundings.gohtml:10
|
||||
msgctxt "title"
|
||||
msgid "Surroundings"
|
||||
msgstr "Entorno"
|
||||
msgstr "El entorno"
|
||||
|
||||
#: web/templates/public/home.gohtml:37
|
||||
msgid "Located in <strong>Alta Garrotxa</strong>, between the <strong>Pyrenees</strong> and the <strong>Costa Brava</strong>."
|
||||
|
@ -60,12 +73,6 @@ msgstr "Descubre el entorno"
|
|||
msgid "Come and enjoy!"
|
||||
msgstr "¡Ven a disfrutar!"
|
||||
|
||||
#: web/templates/public/surroundings.gohtml:6
|
||||
#: web/templates/public/surroundings.gohtml:10
|
||||
msgctxt "title"
|
||||
msgid "Surroundings"
|
||||
msgstr "El entorno"
|
||||
|
||||
#: web/templates/public/surroundings.gohtml:13
|
||||
msgctxt "title"
|
||||
msgid "What to Do Outside the Campsite?"
|
||||
|
@ -125,8 +132,8 @@ msgstr "Kayak"
|
|||
msgid "There are several points where you can go by kayak, from sections of the Ter river as well as on the coast…."
|
||||
msgstr "Hay diversos puntos dónde podéis ir en kayak, desde tramos del río Ter como también en la costa…."
|
||||
|
||||
#: web/templates/public/layout.gohtml:11 web/templates/public/layout.gohtml:23
|
||||
#: web/templates/public/layout.gohtml:58
|
||||
#: web/templates/public/layout.gohtml:11 web/templates/public/layout.gohtml:24
|
||||
#: web/templates/public/layout.gohtml:59
|
||||
msgid "Campsite Montagut"
|
||||
msgstr "Camping Montagut"
|
||||
|
||||
|
@ -134,7 +141,7 @@ msgstr "Camping Montagut"
|
|||
msgid "Skip to main content"
|
||||
msgstr "Saltar al contenido principal"
|
||||
|
||||
#: web/templates/public/layout.gohtml:32
|
||||
#: web/templates/public/layout.gohtml:33
|
||||
msgid "Singular Lodges"
|
||||
msgstr "Alojamientos singulares"
|
||||
|
||||
|
@ -173,6 +180,7 @@ msgstr "Etiqueta"
|
|||
#: web/templates/admin/campsite/form.gohtml:71
|
||||
#: web/templates/admin/campsite/type/form.gohtml:77
|
||||
#: web/templates/admin/season/form.gohtml:65
|
||||
#: web/templates/admin/services/carousel/form.gohtml:58
|
||||
#: web/templates/admin/home/carousel/form.gohtml:58
|
||||
msgctxt "action"
|
||||
msgid "Update"
|
||||
|
@ -181,6 +189,7 @@ msgstr "Actualizar"
|
|||
#: web/templates/admin/campsite/form.gohtml:73
|
||||
#: web/templates/admin/campsite/type/form.gohtml:79
|
||||
#: web/templates/admin/season/form.gohtml:67
|
||||
#: web/templates/admin/services/carousel/form.gohtml:60
|
||||
#: web/templates/admin/home/carousel/form.gohtml:60
|
||||
msgctxt "action"
|
||||
msgid "Add"
|
||||
|
@ -251,6 +260,7 @@ msgid "Name"
|
|||
msgstr "Nombre"
|
||||
|
||||
#: web/templates/admin/campsite/type/form.gohtml:59
|
||||
#: web/templates/admin/services/carousel/form.gohtml:39
|
||||
#: web/templates/admin/home/carousel/form.gohtml:39
|
||||
msgctxt "input"
|
||||
msgid "Cover image"
|
||||
|
@ -281,6 +291,7 @@ msgid "Name"
|
|||
msgstr "Nombre"
|
||||
|
||||
#: web/templates/admin/campsite/type/index.gohtml:19
|
||||
#: web/templates/admin/services/index.gohtml:20
|
||||
#: web/templates/admin/home/index.gohtml:20
|
||||
msgctxt "campsite type"
|
||||
msgid "Translations"
|
||||
|
@ -298,18 +309,21 @@ msgstr "Traducción de tipo de alojamiento a %s"
|
|||
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:22
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:34
|
||||
#: web/templates/admin/services/carousel/l10n.gohtml:22
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:22
|
||||
msgid "Source:"
|
||||
msgstr "Origen:"
|
||||
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:24
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:37
|
||||
#: web/templates/admin/services/carousel/l10n.gohtml:24
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:24
|
||||
msgctxt "input"
|
||||
msgid "Translation:"
|
||||
msgstr "Traducción"
|
||||
|
||||
#: web/templates/admin/campsite/type/l10n.gohtml:46
|
||||
#: web/templates/admin/services/carousel/l10n.gohtml:33
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:33
|
||||
msgctxt "action"
|
||||
msgid "Translate"
|
||||
|
@ -386,6 +400,85 @@ msgctxt "action"
|
|||
msgid "Login"
|
||||
msgstr "Entrar"
|
||||
|
||||
#: web/templates/admin/services/carousel/form.gohtml:8
|
||||
#: web/templates/admin/services/carousel/form.gohtml:27
|
||||
#: web/templates/admin/home/carousel/form.gohtml:8
|
||||
#: web/templates/admin/home/carousel/form.gohtml:27
|
||||
msgctxt "title"
|
||||
msgid "Edit Carousel Slide"
|
||||
msgstr "Edición de la diapositiva del carrusel"
|
||||
|
||||
#: web/templates/admin/services/carousel/form.gohtml:10
|
||||
#: web/templates/admin/services/carousel/form.gohtml:29
|
||||
#: web/templates/admin/home/carousel/form.gohtml:10
|
||||
#: web/templates/admin/home/carousel/form.gohtml:29
|
||||
msgctxt "title"
|
||||
msgid "New Carousel Slide"
|
||||
msgstr "Nueva diapositiva del carrusel"
|
||||
|
||||
#: web/templates/admin/services/carousel/form.gohtml:48
|
||||
#: web/templates/admin/services/carousel/l10n.gohtml:21
|
||||
#: web/templates/admin/home/carousel/form.gohtml:48
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:21
|
||||
msgctxt "input"
|
||||
msgid "Caption"
|
||||
msgstr "Leyenda"
|
||||
|
||||
#: web/templates/admin/services/carousel/l10n.gohtml:7
|
||||
#: web/templates/admin/services/carousel/l10n.gohtml:15
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:7
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:15
|
||||
msgctxt "title"
|
||||
msgid "Translate Carousel Slide to %s"
|
||||
msgstr "Traducción de la diapositiva de carrusel a %s"
|
||||
|
||||
#: web/templates/admin/services/index.gohtml:6
|
||||
#: web/templates/admin/layout.gohtml:76 web/templates/admin/home/index.gohtml:6
|
||||
msgctxt "title"
|
||||
msgid "Home Page"
|
||||
msgstr "Página de inicio"
|
||||
|
||||
#: web/templates/admin/services/index.gohtml:12
|
||||
#: web/templates/admin/home/index.gohtml:12
|
||||
msgctxt "title"
|
||||
msgid "Carousel"
|
||||
msgstr "Carrusel"
|
||||
|
||||
#: web/templates/admin/services/index.gohtml:13
|
||||
#: web/templates/admin/home/index.gohtml:13
|
||||
msgctxt "action"
|
||||
msgid "Add slide"
|
||||
msgstr "Añadir diapositiva"
|
||||
|
||||
#: web/templates/admin/services/index.gohtml:18
|
||||
#: web/templates/admin/home/index.gohtml:18
|
||||
msgctxt "header"
|
||||
msgid "Image"
|
||||
msgstr "Imagen"
|
||||
|
||||
#: web/templates/admin/services/index.gohtml:19
|
||||
#: web/templates/admin/home/index.gohtml:19
|
||||
msgctxt "header"
|
||||
msgid "Caption"
|
||||
msgstr "Leyenda"
|
||||
|
||||
#: web/templates/admin/services/index.gohtml:21
|
||||
#: web/templates/admin/home/index.gohtml:21
|
||||
msgctxt "campsite type"
|
||||
msgid "Actions"
|
||||
msgstr "Acciones"
|
||||
|
||||
#: web/templates/admin/services/index.gohtml:40
|
||||
#: web/templates/admin/home/index.gohtml:40
|
||||
msgctxt "action"
|
||||
msgid "Delete"
|
||||
msgstr "Borrar"
|
||||
|
||||
#: web/templates/admin/services/index.gohtml:48
|
||||
#: web/templates/admin/home/index.gohtml:48
|
||||
msgid "No slides added yet."
|
||||
msgstr "No se ha añadido ninguna diapositiva todavía."
|
||||
|
||||
#: web/templates/admin/profile.gohtml:6 web/templates/admin/profile.gohtml:12
|
||||
#: web/templates/admin/layout.gohtml:29
|
||||
msgctxt "title"
|
||||
|
@ -506,68 +599,11 @@ msgctxt "action"
|
|||
msgid "Logout"
|
||||
msgstr "Salir"
|
||||
|
||||
#: web/templates/admin/layout.gohtml:76 web/templates/admin/home/index.gohtml:6
|
||||
#: web/templates/admin/layout.gohtml:79
|
||||
#, fuzzy
|
||||
msgctxt "title"
|
||||
msgid "Home Page"
|
||||
msgstr "Página de inicio"
|
||||
|
||||
#: web/templates/admin/home/carousel/form.gohtml:8
|
||||
#: web/templates/admin/home/carousel/form.gohtml:27
|
||||
msgctxt "title"
|
||||
msgid "Edit Carousel Slide"
|
||||
msgstr "Edición de la diapositiva del carrusel"
|
||||
|
||||
#: web/templates/admin/home/carousel/form.gohtml:10
|
||||
#: web/templates/admin/home/carousel/form.gohtml:29
|
||||
msgctxt "title"
|
||||
msgid "New Carousel Slide"
|
||||
msgstr "Nueva diapositiva del carrusel"
|
||||
|
||||
#: web/templates/admin/home/carousel/form.gohtml:48
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:21
|
||||
msgctxt "input"
|
||||
msgid "Caption"
|
||||
msgstr "Leyenda"
|
||||
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:7
|
||||
#: web/templates/admin/home/carousel/l10n.gohtml:15
|
||||
msgctxt "title"
|
||||
msgid "Translate Carousel Slide to %s"
|
||||
msgstr "Traducción de la diapositiva de carrusel a %s"
|
||||
|
||||
#: web/templates/admin/home/index.gohtml:12
|
||||
msgctxt "title"
|
||||
msgid "Carousel"
|
||||
msgstr "Carrusel"
|
||||
|
||||
#: web/templates/admin/home/index.gohtml:13
|
||||
msgctxt "action"
|
||||
msgid "Add slide"
|
||||
msgstr "Añadir diapositiva"
|
||||
|
||||
#: web/templates/admin/home/index.gohtml:18
|
||||
msgctxt "header"
|
||||
msgid "Image"
|
||||
msgstr "Imagen"
|
||||
|
||||
#: web/templates/admin/home/index.gohtml:19
|
||||
msgctxt "header"
|
||||
msgid "Caption"
|
||||
msgstr "Leyenda"
|
||||
|
||||
#: web/templates/admin/home/index.gohtml:21
|
||||
msgctxt "campsite type"
|
||||
msgid "Actions"
|
||||
msgstr "Acciones"
|
||||
|
||||
#: web/templates/admin/home/index.gohtml:40
|
||||
msgctxt "action"
|
||||
msgid "Delete"
|
||||
msgstr "Borrar"
|
||||
|
||||
#: web/templates/admin/home/index.gohtml:48
|
||||
msgid "No slides added yet."
|
||||
msgstr "No se ha añadido ninguna diapositiva todavía."
|
||||
msgid "Services Page"
|
||||
msgstr "Servicios"
|
||||
|
||||
#: web/templates/admin/media/index.gohtml:6
|
||||
#: web/templates/admin/media/index.gohtml:12
|
||||
|
@ -638,11 +674,12 @@ msgstr "La confirmación no se corresponde con la contraseña."
|
|||
msgid "Selected language is not valid."
|
||||
msgstr "El idioma escogido no es válido."
|
||||
|
||||
#: pkg/app/user.go:253 pkg/campsite/types/admin.go:296 pkg/home/carousel.go:287
|
||||
#: pkg/app/user.go:253 pkg/campsite/types/admin.go:296
|
||||
#: pkg/services/carousel.go:287 pkg/home/carousel.go:287
|
||||
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:47
|
||||
#: pkg/app/admin.go:50
|
||||
msgid "Access forbidden"
|
||||
msgstr "Acceso prohibido"
|
||||
|
||||
|
@ -666,6 +703,10 @@ msgstr "No podéis dejar el color en blanco."
|
|||
msgid "This color is not valid. It must be like #123abc."
|
||||
msgstr "Este color no es válido. Tiene que ser parecido a #123abc."
|
||||
|
||||
#: pkg/services/carousel.go:289 pkg/home/carousel.go:289
|
||||
msgid "Slide image can not be empty."
|
||||
msgstr "No podéis dejar la imagen de la diapositiva en blanco."
|
||||
|
||||
#: pkg/company/admin.go:186
|
||||
msgid "Selected country is not valid."
|
||||
msgstr "El país escogido no es válido."
|
||||
|
@ -730,14 +771,13 @@ msgstr "No podéis dejar el formato de número de factura en blanco."
|
|||
msgid "Cross-site request forgery detected."
|
||||
msgstr "Se ha detectado un intento de falsificación de petición en sitios cruzados."
|
||||
|
||||
#: pkg/home/carousel.go:289
|
||||
msgid "Slide image can not be empty."
|
||||
msgstr "No podéis dejar la imagen de la diapositiva en blanco."
|
||||
|
||||
#: pkg/media/admin.go:164
|
||||
msgid "Uploaded file can not be empty."
|
||||
msgstr "No podéis dejar el archivo del medio en blanco."
|
||||
|
||||
#~ msgid "Surroundings"
|
||||
#~ msgstr "Entorno"
|
||||
|
||||
#~ msgid "Legend"
|
||||
#~ msgstr "Leyenda"
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:add_service from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists camper.add_service(integer, text, text, text);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:add_services_carousel_slide from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists camper.add_services_carousel_slide(integer, text);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:available_icons from pg
|
||||
|
||||
begin;
|
||||
|
||||
delete from camper.icon;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:edit_service from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists camper.edit_service(integer, text, text, text);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:icon from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.icon;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:remove_services_carousel_slide from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists camper.remove_services_carousel_slide(integer);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:service from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.service;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:service_i18n from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.service_i18n;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:services_carousel from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.services_carousel;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:services_carousel_i18n from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop table if exists camper.services_carousel_i18n;
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:translate_service from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists camper.translate_service(integer, text, text, text);
|
||||
|
||||
commit;
|
|
@ -0,0 +1,7 @@
|
|||
-- Revert camper:translate_services_carousel_slide from pg
|
||||
|
||||
begin;
|
||||
|
||||
drop function if exists camper.translate_services_carousel_slide(integer, text, text);
|
||||
|
||||
commit;
|
12
sqitch.plan
12
sqitch.plan
|
@ -64,3 +64,15 @@ home_carousel_i18n [roles schema_camper home_carousel language] 2023-09-13T23:22
|
|||
add_home_carousel_slide [roles schema_camper home_carousel] 2023-09-14T17:49:21Z jordi fita mas <jordi@tandem.blog> # Add function to create slides for the home carousel
|
||||
translate_home_carousel_slide [roles schema_camper home_carousel_i18n] 2023-09-14T18:17:36Z jordi fita mas <jordi@tandem.blog> # Add function to translate a home carousel slider
|
||||
remove_home_carousel_slide [roles schema_camper home_carousel home_carousel_i18n] 2023-09-14T21:57:48Z jordi fita mas <jordi@tandem.blog> # Add function to remove sliders from the home carousel
|
||||
services_carousel [roles schema_public company media user_profile] 2023-09-16T22:37:47Z jordi fita mas <jordi@tandem.blog> # Add relation for services’ image carousel
|
||||
services_carousel_i18n [roles schema_camper services_carousel language] 2023-09-16T22:42:14Z jordi fita mas <jordi@tandem.blog> # Add relation for services carousel translations
|
||||
add_services_carousel_slide [roles schema_camper services_carousel] 2023-09-16T22:45:49Z jordi fita mas <jordi@tandem.blog> # Add function to create slides for the services carousel
|
||||
translate_services_carousel_slide [roles schema_camper services_carousel_i18n] 2023-09-16T22:46:43Z jordi fita mas <jordi@tandem.blog> # Add function to translate a services carousel slide
|
||||
remove_services_carousel_slide [roles schema_camper services_carousel services_carousel_i18n] 2023-09-16T22:47:54Z jordi fita mas <jordi@tandem.blog> # Add function to remove slides from the services carousel
|
||||
icon [roles schema_camper] 2023-09-16T23:11:48Z jordi fita mas <jordi@tandem.blog> # Add relation for icon
|
||||
available_icons [schema_camper icon] 2023-09-16T23:15:03Z jordi fita mas <jordi@tandem.blog> # Add the list of available icons
|
||||
service [roles schema_camper company icon user_profile] 2023-09-16T23:48:19Z jordi fita mas <jordi@tandem.blog> # Add relation of services definition
|
||||
add_service [roles schema_camper service] 2023-09-17T00:00:00Z jordi fita mas <jordi@tandem.blog> # Add function to create services
|
||||
edit_service [roles schema_camper service] 2023-09-17T00:01:16Z jordi fita mas <jordi@tandem.blog> # Add function to edit services
|
||||
service_i18n [roles schema_camper service language] 2023-09-17T00:13:42Z jordi fita mas <jordi@tandem.blog> # Add relation for service translations
|
||||
translate_service [roles schema_camper service_i18n] 2023-09-17T00:17:00Z jordi fita mas <jordi@tandem.blog> # Add function to translate a service
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
-- Test add_service
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(12);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_function('camper', 'add_service', array['integer', 'text', 'text', 'text']);
|
||||
select function_lang_is('camper', 'add_service', array['integer', 'text', 'text', 'text'], 'sql');
|
||||
select function_returns('camper', 'add_service', array['integer', 'text', 'text', 'text'], 'integer');
|
||||
select isnt_definer('camper', 'add_service', array['integer', 'text', 'text', 'text']);
|
||||
select volatility_is('camper', 'add_service', array['integer', 'text', 'text', 'text'], 'volatile');
|
||||
select function_privs_are('camper', 'add_service', array ['integer', 'text', 'text', 'text'], 'guest', array[]::text[]);
|
||||
select function_privs_are('camper', 'add_service', array ['integer', 'text', 'text', 'text'], 'employee', array[]::text[]);
|
||||
select function_privs_are('camper', 'add_service', array ['integer', 'text', 'text', 'text'], 'admin', array['EXECUTE']);
|
||||
select function_privs_are('camper', 'add_service', array ['integer', 'text', 'text', 'text'], 'authenticator', array[]::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate service 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, 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')
|
||||
;
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_service(1, 'information', 'Info', '<!-- block --><h2>This is what, exactly?</h2><!-- /block --><p>Dunno</p>') $$,
|
||||
'Should be able to add a service to the first company'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_service(2, 'toilet', 'Is this Google?', '') $$,
|
||||
'Should be able to add a service to the second company'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select company_id, icon_name, name, description::text from service $$,
|
||||
$$ values (1, 'information', 'Info', '<!-- block --><h2>This is what, exactly?</h2><!-- /block --><p>Dunno</p>')
|
||||
, (2, 'toilet', 'Is this Google?', '')
|
||||
$$,
|
||||
'Should have added all two service'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,77 @@
|
|||
-- Test add_services_carousel_slide
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(14);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_function('camper', 'add_services_carousel_slide', array['integer', 'text']);
|
||||
select function_lang_is('camper', 'add_services_carousel_slide', array['integer', 'text'], 'sql');
|
||||
select function_returns('camper', 'add_services_carousel_slide', array['integer', 'text'], 'void');
|
||||
select isnt_definer('camper', 'add_services_carousel_slide', array['integer', 'text']);
|
||||
select volatility_is('camper', 'add_services_carousel_slide', array['integer', 'text'], 'volatile');
|
||||
select function_privs_are('camper', 'add_services_carousel_slide', array['integer', 'text'], 'guest', array[]::text[]);
|
||||
select function_privs_are('camper', 'add_services_carousel_slide', array['integer', 'text'], 'employee', array[]::text[]);
|
||||
select function_privs_are('camper', 'add_services_carousel_slide', array['integer', 'text'], 'admin', array['EXECUTE']);
|
||||
select function_privs_are('camper', 'add_services_carousel_slide', array['integer', 'text'], 'authenticator', array[]::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate services_carousel_i18n cascade;
|
||||
truncate services_carousel cascade;
|
||||
truncate media 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, country_code, currency_code, default_lang_tag)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca')
|
||||
;
|
||||
|
||||
insert into media (media_id, company_id, original_filename, media_type, content)
|
||||
values (5, 1, 'text.txt', 'text/plain', 'hello, world!')
|
||||
, (6, 1, 'image.svg', 'image/svg+xml', '<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"/>')
|
||||
, (7, 1, 'cover4.xpm', 'image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};')
|
||||
;
|
||||
|
||||
insert into services_carousel (media_id, caption)
|
||||
values (5, 'Previous caption')
|
||||
;
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_services_carousel_slide(6, 'A caption') $$,
|
||||
'Should be able to add a carousel slide with a caption'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_services_carousel_slide(7, null) $$,
|
||||
'Should be able to add a carousel slide without caption'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select add_services_carousel_slide(5, 'New caption') $$,
|
||||
'Should be able to overwrite a slide with a new caption'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select media_id, caption from services_carousel $$,
|
||||
$$ values (5, 'New caption')
|
||||
, (6, 'A caption')
|
||||
, (7, '')
|
||||
$$,
|
||||
'Should have all three slides'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from services_carousel_i18n $$,
|
||||
'Should not have added any translation'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,58 @@
|
|||
-- Test edit_service
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(12);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_function('camper', 'edit_service', array['integer', 'text', 'text', 'text']);
|
||||
select function_lang_is('camper', 'edit_service', array['integer', 'text', 'text', 'text'], 'sql');
|
||||
select function_returns('camper', 'edit_service', array['integer', 'text', 'text', 'text'], 'void');
|
||||
select isnt_definer('camper', 'edit_service', array['integer', 'text', 'text', 'text']);
|
||||
select volatility_is('camper', 'edit_service', array['integer', 'text', 'text', 'text'], 'volatile');
|
||||
select function_privs_are('camper', 'edit_service', array ['integer', 'text', 'text', 'text'], 'guest', array[]::text[]);
|
||||
select function_privs_are('camper', 'edit_service', array ['integer', 'text', 'text', 'text'], 'employee', array[]::text[]);
|
||||
select function_privs_are('camper', 'edit_service', array ['integer', 'text', 'text', 'text'], 'admin', array['EXECUTE']);
|
||||
select function_privs_are('camper', 'edit_service', array ['integer', 'text', 'text', 'text'], 'authenticator', array[]::text[]);
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate service 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, country_code, currency_code, default_lang_tag)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca')
|
||||
;
|
||||
|
||||
insert into service (service_id, company_id, icon_name, name, description)
|
||||
values (5, 1, 'information', 'Service A', '<p>A</p>')
|
||||
, (6, 1, 'toilet', 'Service B', '<p>B</p>')
|
||||
;
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_service(5, 'wifi', 'Service 1', '<p>1</p>') $$,
|
||||
'Should be able to edit the first service'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select edit_service(6, 'baby', 'Service 2', '<p>2</p>') $$,
|
||||
'Should be able to edit the second service'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select service_id, icon_name, name, description::text from service $$,
|
||||
$$ values (5, 'wifi', 'Service 1', '<p>1</p>')
|
||||
, (6, 'baby', 'Service 2', '<p>2</p>')
|
||||
$$,
|
||||
'Should have updated all services.'
|
||||
);
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,30 @@
|
|||
-- Test icon
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(11);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_table('icon');
|
||||
select has_pk('icon');
|
||||
select table_privs_are('icon', 'guest', array['SELECT']);
|
||||
select table_privs_are('icon', 'employee', array['SELECT']);
|
||||
select table_privs_are('icon', 'admin', array['SELECT']);
|
||||
select table_privs_are('icon', 'authenticator', array[]::text[]);
|
||||
|
||||
select has_column('icon', 'icon_name');
|
||||
select col_is_pk('icon', 'icon_name');
|
||||
select col_type_is('icon', 'icon_name', 'text');
|
||||
select col_not_null('icon', 'icon_name');
|
||||
select col_hasnt_default('icon', 'icon_name');
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
-- Test remove_services_carousel_slide
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(12);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_function('camper', 'remove_services_carousel_slide', array['integer']);
|
||||
select function_lang_is('camper', 'remove_services_carousel_slide', array['integer'], 'sql');
|
||||
select function_returns('camper', 'remove_services_carousel_slide', array['integer'], 'void');
|
||||
select isnt_definer('camper', 'remove_services_carousel_slide', array['integer']);
|
||||
select volatility_is('camper', 'remove_services_carousel_slide', array['integer'], 'volatile');
|
||||
select function_privs_are('camper', 'remove_services_carousel_slide', array['integer'], 'guest', array[]::text[]);
|
||||
select function_privs_are('camper', 'remove_services_carousel_slide', array['integer'], 'employee', array[]::text[]);
|
||||
select function_privs_are('camper', 'remove_services_carousel_slide', array['integer'], 'admin', array['EXECUTE']);
|
||||
select function_privs_are('camper', 'remove_services_carousel_slide', array['integer'], 'authenticator', array[]::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate services_carousel_i18n cascade;
|
||||
truncate services_carousel cascade;
|
||||
truncate media 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, country_code, currency_code, default_lang_tag)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca')
|
||||
;
|
||||
|
||||
insert into media (media_id, company_id, original_filename, media_type, content)
|
||||
values (5, 1, 'text.txt', 'text/plain', 'hello, world!')
|
||||
, (6, 1, 'image.svg', 'image/svg+xml', '<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"/>')
|
||||
, (7, 1, 'cover4.xpm', 'image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};')
|
||||
;
|
||||
|
||||
insert into services_carousel (media_id, caption)
|
||||
values (5, 'Source caption')
|
||||
, (6, 'Another caption')
|
||||
, (7, 'N/A')
|
||||
;
|
||||
|
||||
insert into services_carousel_i18n (media_id, lang_tag, caption)
|
||||
values (5, 'en', 'Target caption')
|
||||
, (5, 'es', 'Target caption (spanish)')
|
||||
, (6, 'en', 'Target caption')
|
||||
, (6, 'es', 'Target caption (spanish)')
|
||||
, (7, 'en', 'Target caption')
|
||||
, (7, 'es', 'Target caption (spanish)')
|
||||
;
|
||||
|
||||
select lives_ok(
|
||||
$$ select remove_services_carousel_slide(6) $$,
|
||||
'Should be able to delete a slide'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select media_id, caption from services_carousel $$,
|
||||
$$ values (5, 'Source caption')
|
||||
, (7, 'N/A')
|
||||
$$,
|
||||
'Should have removed the slide'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select media_id, lang_tag, caption from services_carousel_i18n $$,
|
||||
$$ values (5, 'en', 'Target caption')
|
||||
, (5, 'es', 'Target caption (spanish)')
|
||||
, (7, 'en', 'Target caption')
|
||||
, (7, 'es', 'Target caption (spanish)')
|
||||
$$,
|
||||
'Should have removed the slide’s translations'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,199 @@
|
|||
-- Test service
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(51);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_table('service');
|
||||
select has_pk('service');
|
||||
select table_privs_are('service', 'guest', array['SELECT']);
|
||||
select table_privs_are('service', 'employee', array['SELECT']);
|
||||
select table_privs_are('service', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('service', 'authenticator', array[]::text[]);
|
||||
|
||||
select has_sequence('service_service_id_seq');
|
||||
select sequence_privs_are('service_service_id_seq', 'guest', array[]::text[]);
|
||||
select sequence_privs_are('service_service_id_seq', 'employee', array[]::text[]);
|
||||
select sequence_privs_are('service_service_id_seq', 'admin', array['USAGE']);
|
||||
select sequence_privs_are('service_service_id_seq', 'authenticator', array[]::text[]);
|
||||
|
||||
select has_column('service', 'service_id');
|
||||
select col_is_pk('service', 'service_id');
|
||||
select col_type_is('service', 'service_id', 'integer');
|
||||
select col_not_null('service', 'service_id');
|
||||
select col_has_default('service', 'service_id');
|
||||
select col_default_is('service', 'service_id', 'nextval(''service_service_id_seq''::regclass)');
|
||||
|
||||
select has_column('service', 'company_id');
|
||||
select col_is_fk('service', 'company_id');
|
||||
select fk_ok('service', 'company_id', 'company', 'company_id');
|
||||
select col_type_is('service', 'company_id', 'integer');
|
||||
select col_not_null('service', 'company_id');
|
||||
select col_hasnt_default('service', 'company_id');
|
||||
|
||||
select has_column('service', 'icon_name');
|
||||
select col_is_fk('service', 'icon_name');
|
||||
select fk_ok('service', 'icon_name', 'icon', 'icon_name');
|
||||
select col_type_is('service', 'icon_name', 'text');
|
||||
select col_not_null('service', 'icon_name');
|
||||
select col_hasnt_default('service', 'icon_name');
|
||||
|
||||
select has_column('service', 'name');
|
||||
select col_type_is('service', 'name', 'text');
|
||||
select col_not_null('service', 'name');
|
||||
select col_hasnt_default('service', 'name');
|
||||
|
||||
select has_column('service', 'description');
|
||||
select col_type_is('service', 'description', 'xml');
|
||||
select col_not_null('service', 'description');
|
||||
select col_hasnt_default('service', 'description');
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate service 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, 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 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 service (company_id, icon_name, name, description)
|
||||
values (2, 'information', 'Information', '')
|
||||
, (4, 'wifi', 'WiFi', '')
|
||||
;
|
||||
|
||||
prepare service_data as
|
||||
select company_id, name
|
||||
from service
|
||||
order by company_id, name;
|
||||
|
||||
set role guest;
|
||||
select bag_eq(
|
||||
'service_data',
|
||||
$$ values (2, 'Information')
|
||||
, (4, 'WiFi')
|
||||
$$,
|
||||
'Everyone should be able to list all services across all companies'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2');
|
||||
|
||||
select lives_ok(
|
||||
$$ insert into service(company_id, icon_name, name, description) values (2, 'restaurant', 'Restaurant', '') $$,
|
||||
'Admin from company 2 should be able to insert a new services to that company.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'service_data',
|
||||
$$ values (2, 'Information')
|
||||
, (2, 'Restaurant')
|
||||
, (4, 'WiFi')
|
||||
$$,
|
||||
'The new row should have been added'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ update service set name = 'Bar' where company_id = 2 and name = 'Restaurant' $$,
|
||||
'Admin from company 2 should be able to update services of that company.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'service_data',
|
||||
$$ values (2, 'Information')
|
||||
, (2, 'Bar')
|
||||
, (4, 'WiFi')
|
||||
$$,
|
||||
'The row should have been updated.'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ delete from service where company_id = 2 and name = 'Bar' $$,
|
||||
'Admin from company 2 should be able to delete services from that company.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'service_data',
|
||||
$$ values (2, 'Information')
|
||||
, (4, 'WiFi')
|
||||
$$,
|
||||
'The row should have been deleted.'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ insert into service (company_id, icon_name, name, description) values (4, 'store', 'Store', '') $$,
|
||||
'42501', 'new row violates row-level security policy for table "service"',
|
||||
'Admin from company 2 should NOT be able to insert new services to company 4.'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ update service set name = 'Nope' where company_id = 4 $$,
|
||||
'Admin from company 2 should not be able to update new services of company 4, but no error if company_id is not changed.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'service_data',
|
||||
$$ values (2, 'Information')
|
||||
, (4, 'WiFi')
|
||||
$$,
|
||||
'No row should have been changed.'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ update service set company_id = 4 where company_id = 2 $$,
|
||||
'42501', 'new row violates row-level security policy for table "service"',
|
||||
'Admin from company 2 should NOT be able to move services to company 4'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ delete from service where company_id = 4 $$,
|
||||
'Admin from company 2 should NOT be able to delete services from company 4, but not error is thrown'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'service_data',
|
||||
$$ values (2, 'Information')
|
||||
, (4, 'WiFi')
|
||||
$$,
|
||||
'No row should have been changed'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ insert into service (company_id, icon_name, name, description) values (2, 'toilet', ' ', '') $$,
|
||||
'23514', 'new row for relation "service" violates check constraint "name_not_empty"',
|
||||
'Should not be able to insert services with a blank name.'
|
||||
);
|
||||
|
||||
reset role;
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
-- Test service_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('service_i18n');
|
||||
select has_pk('service_i18n');
|
||||
select col_is_pk('service_i18n', array['service_id', 'lang_tag']);
|
||||
select table_privs_are('service_i18n', 'guest', array['SELECT']);
|
||||
select table_privs_are('service_i18n', 'employee', array['SELECT']);
|
||||
select table_privs_are('service_i18n', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('service_i18n', 'authenticator', array[]::text[]);
|
||||
|
||||
select has_column('service_i18n', 'service_id');
|
||||
select col_is_fk('service_i18n', 'service_id');
|
||||
select fk_ok('service_i18n', 'service_id', 'service', 'service_id');
|
||||
select col_type_is('service_i18n', 'service_id', 'integer');
|
||||
select col_not_null('service_i18n', 'service_id');
|
||||
select col_hasnt_default('service_i18n', 'service_id');
|
||||
|
||||
select has_column('service_i18n', 'lang_tag');
|
||||
select col_is_fk('service_i18n', 'lang_tag');
|
||||
select fk_ok('service_i18n', 'lang_tag', 'language', 'lang_tag');
|
||||
select col_type_is('service_i18n', 'lang_tag', 'text');
|
||||
select col_not_null('service_i18n', 'lang_tag');
|
||||
select col_hasnt_default('service_i18n', 'lang_tag');
|
||||
|
||||
select has_column('service_i18n', 'name');
|
||||
select col_type_is('service_i18n', 'name', 'text');
|
||||
select col_not_null('service_i18n', 'name');
|
||||
select col_hasnt_default('service_i18n', 'name');
|
||||
|
||||
select has_column('service_i18n', 'description');
|
||||
select col_type_is('service_i18n', 'description', 'xml');
|
||||
select col_not_null('service_i18n', 'description');
|
||||
select col_hasnt_default('service_i18n', 'description');
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
-- Test services_carousel
|
||||
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('services_carousel');
|
||||
select has_pk('services_carousel');
|
||||
select table_privs_are('services_carousel', 'guest', array['SELECT']);
|
||||
select table_privs_are('services_carousel', 'employee', array['SELECT']);
|
||||
select table_privs_are('services_carousel', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('services_carousel', 'authenticator', array[]::text[]);
|
||||
|
||||
select has_column('services_carousel', 'media_id');
|
||||
select col_is_pk('services_carousel', 'media_id');
|
||||
select col_is_fk('services_carousel', 'media_id');
|
||||
select fk_ok('services_carousel', 'media_id', 'media', 'media_id');
|
||||
select col_type_is('services_carousel', 'media_id', 'integer');
|
||||
select col_not_null('services_carousel', 'media_id');
|
||||
select col_hasnt_default('services_carousel', 'media_id');
|
||||
|
||||
select has_column('services_carousel', 'caption');
|
||||
select col_type_is('services_carousel', 'caption', 'text');
|
||||
select col_not_null('services_carousel', 'caption');
|
||||
select col_hasnt_default('services_carousel', 'caption');
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate services_carousel cascade;
|
||||
truncate media 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, 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 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 media (media_id, company_id, original_filename, media_type, content)
|
||||
values ( 7, 2, 'text2.txt', 'text/plain', 'content2')
|
||||
, ( 8, 2, 'text3.txt', 'text/plain', 'content3')
|
||||
, ( 9, 4, 'text4.txt', 'text/plain', 'content4')
|
||||
, (10, 4, 'text5.txt', 'text/plain', 'content5')
|
||||
;
|
||||
|
||||
insert into services_carousel (media_id, caption)
|
||||
values (7, 'Caption 7')
|
||||
, (9, 'Caption 9')
|
||||
;
|
||||
|
||||
prepare carousel_data as
|
||||
select media_id, caption
|
||||
from services_carousel
|
||||
order by media_id, caption;
|
||||
|
||||
set role guest;
|
||||
select bag_eq(
|
||||
'carousel_data',
|
||||
$$ values (7, 'Caption 7')
|
||||
, (9, 'Caption 9')
|
||||
$$,
|
||||
'Everyone should be able to list all carousel media across all companies'
|
||||
);
|
||||
reset role;
|
||||
|
||||
select set_cookie('44facbb30d8a419dfd4bfbc44a4b5539d4970148dfc84bed0e/demo@tandem.blog', 'co2');
|
||||
|
||||
select lives_ok(
|
||||
$$ insert into services_carousel(media_id, caption)
|
||||
values (8, 'Caption 8') $$,
|
||||
'Admin from company 2 should be able to insert a new carousel media to that company.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'carousel_data',
|
||||
$$ values (7, 'Caption 7')
|
||||
, (8, 'Caption 8')
|
||||
, (9, 'Caption 9')
|
||||
$$,
|
||||
'The new row should have been added'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ update services_carousel set caption = 'Caption 8.8' where media_id = 8 $$,
|
||||
'Admin from company 2 should be able to update carousel media of that company.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'carousel_data',
|
||||
$$ values (7, 'Caption 7')
|
||||
, (8, 'Caption 8.8')
|
||||
, (9, 'Caption 9')
|
||||
$$,
|
||||
'The row should have been updated.'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ delete from services_carousel where media_id = 8 $$,
|
||||
'Admin from company 2 should be able to delete carousel media from that company.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'carousel_data',
|
||||
$$ values (7, 'Caption 7')
|
||||
, (9, 'Caption 9')
|
||||
$$,
|
||||
'The row should have been deleted.'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ insert into services_carousel (media_id, caption)
|
||||
values (10, 'Caption 10') $$,
|
||||
'42501', 'new row violates row-level security policy for table "services_carousel"',
|
||||
'Admin from company 2 should NOT be able to insert new media to company 4.'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ update services_carousel set caption = 'Nope' where media_id = 9 $$,
|
||||
'Admin from company 2 should not be able to update new carousel media of company 4, but no error if media_id is not changed.'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'carousel_data',
|
||||
$$ values (7, 'Caption 7')
|
||||
, (9, 'Caption 9')
|
||||
$$,
|
||||
'No row should have been changed.'
|
||||
);
|
||||
|
||||
select throws_ok(
|
||||
$$ update services_carousel set media_id = 10 where media_id = 7 $$,
|
||||
'42501', 'new row violates row-level security policy for table "services_carousel"',
|
||||
'Admin from company 2 should NOT be able to move carousel media to company 4'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ delete from services_carousel where media_id = 9 $$,
|
||||
'Admin from company 2 should NOT be able to delete carousel media from company 4, but no error is thrown'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
'carousel_data',
|
||||
$$ values (7, 'Caption 7')
|
||||
, (9, 'Caption 9')
|
||||
$$,
|
||||
'No row should have been changed'
|
||||
);
|
||||
|
||||
reset role;
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
-- Test services_carousel_i18n
|
||||
set client_min_messages to warning;
|
||||
create extension if not exists pgtap;
|
||||
reset client_min_messages;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(23);
|
||||
|
||||
set search_path to camper, public;
|
||||
|
||||
select has_table('services_carousel_i18n');
|
||||
select has_pk('services_carousel_i18n');
|
||||
select col_is_pk('services_carousel_i18n', array['media_id', 'lang_tag']);
|
||||
select table_privs_are('services_carousel_i18n', 'guest', array['SELECT']);
|
||||
select table_privs_are('services_carousel_i18n', 'employee', array['SELECT']);
|
||||
select table_privs_are('services_carousel_i18n', 'admin', array['SELECT', 'INSERT', 'UPDATE', 'DELETE']);
|
||||
select table_privs_are('services_carousel_i18n', 'authenticator', array[]::text[]);
|
||||
|
||||
select has_column('services_carousel_i18n', 'media_id');
|
||||
select col_is_fk('services_carousel_i18n', 'media_id');
|
||||
select fk_ok('services_carousel_i18n', 'media_id', 'services_carousel', 'media_id');
|
||||
select col_type_is('services_carousel_i18n', 'media_id', 'integer');
|
||||
select col_not_null('services_carousel_i18n', 'media_id');
|
||||
select col_hasnt_default('services_carousel_i18n', 'media_id');
|
||||
|
||||
select has_column('services_carousel_i18n', 'lang_tag');
|
||||
select col_is_fk('services_carousel_i18n', 'lang_tag');
|
||||
select fk_ok('services_carousel_i18n', 'lang_tag', 'language', 'lang_tag');
|
||||
select col_type_is('services_carousel_i18n', 'lang_tag', 'text');
|
||||
select col_not_null('services_carousel_i18n', 'lang_tag');
|
||||
select col_hasnt_default('services_carousel_i18n', 'lang_tag');
|
||||
|
||||
select has_column('services_carousel_i18n', 'caption');
|
||||
select col_type_is('services_carousel_i18n', 'caption', 'text');
|
||||
select col_not_null('services_carousel_i18n', 'caption');
|
||||
select col_hasnt_default('services_carousel_i18n', 'caption');
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
-- Test translate_service
|
||||
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_service', array['integer', 'text', 'text', 'text']);
|
||||
select function_lang_is('camper', 'translate_service', array['integer', 'text', 'text', 'text'], 'sql');
|
||||
select function_returns('camper', 'translate_service', array['integer', 'text', 'text', 'text'], 'void');
|
||||
select isnt_definer('camper', 'translate_service', array['integer', 'text', 'text', 'text']);
|
||||
select volatility_is('camper', 'translate_service', array['integer', 'text', 'text', 'text'], 'volatile');
|
||||
select function_privs_are('camper', 'translate_service', array['integer', 'text', 'text', 'text'], 'guest', array[]::text[]);
|
||||
select function_privs_are('camper', 'translate_service', array['integer', 'text', 'text', 'text'], 'employee', array[]::text[]);
|
||||
select function_privs_are('camper', 'translate_service', array['integer', 'text', 'text', 'text'], 'admin', array['EXECUTE']);
|
||||
select function_privs_are('camper', 'translate_service', array['integer', 'text', 'text', 'text'], 'authenticator', array[]::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate service 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, country_code, currency_code, default_lang_tag)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca')
|
||||
;
|
||||
|
||||
insert into service (service_id, company_id, icon_name, name, description)
|
||||
values (5, 1, 'information', 'Service A', '<p>A</p>')
|
||||
, (6, 1, 'toilet', 'Service B', '<p>B</p>')
|
||||
;
|
||||
|
||||
insert into service_i18n (service_id, lang_tag, name, description)
|
||||
values (6, 'ca', 'serveib', '<p>B</p>')
|
||||
;
|
||||
|
||||
select lives_ok(
|
||||
$$ select translate_service(5, 'ca', 'Servei A', '<p>a</p>') $$,
|
||||
'Should be able to translate the first service'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select translate_service(6, 'es', 'Servicio B', '<p>b</p>') $$,
|
||||
'Should be able to translate the second service'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select translate_service(6, 'ca', 'Servei B', null) $$,
|
||||
'Should be able to overwrite the catalan translation of the second service, with no description'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select service_id, lang_tag, name, description::text from service_i18n $$,
|
||||
$$ values (5, 'ca', 'Servei A', '<p>a</p>')
|
||||
, (6, 'ca', 'Servei B', '')
|
||||
, (6, 'es', 'Servicio B', '<p>b</p>')
|
||||
$$,
|
||||
'Should have added and updated all translations.'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,78 @@
|
|||
-- Test translate_services_carousel_slide
|
||||
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_services_carousel_slide', array['integer', 'text', 'text']);
|
||||
select function_lang_is('camper', 'translate_services_carousel_slide', array['integer', 'text', 'text'], 'sql');
|
||||
select function_returns('camper', 'translate_services_carousel_slide', array['integer', 'text', 'text'], 'void');
|
||||
select isnt_definer('camper', 'translate_services_carousel_slide', array['integer', 'text', 'text']);
|
||||
select volatility_is('camper', 'translate_services_carousel_slide', array['integer', 'text', 'text'], 'volatile');
|
||||
select function_privs_are('camper', 'translate_services_carousel_slide', array['integer', 'text', 'text'], 'guest', array[]::text[]);
|
||||
select function_privs_are('camper', 'translate_services_carousel_slide', array['integer', 'text', 'text'], 'employee', array[]::text[]);
|
||||
select function_privs_are('camper', 'translate_services_carousel_slide', array['integer', 'text', 'text'], 'admin', array['EXECUTE']);
|
||||
select function_privs_are('camper', 'translate_services_carousel_slide', array['integer', 'text', 'text'], 'authenticator', array[]::text[]);
|
||||
|
||||
|
||||
set client_min_messages to warning;
|
||||
truncate services_carousel_i18n cascade;
|
||||
truncate services_carousel cascade;
|
||||
truncate media 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, country_code, currency_code, default_lang_tag)
|
||||
values (1, 'Company 2', 'XX123', '', '555-555-555', 'a@a', '', '', '', '', '', 'ES', 'EUR', 'ca')
|
||||
;
|
||||
|
||||
insert into media (media_id, company_id, original_filename, media_type, content)
|
||||
values (5, 1, 'text.txt', 'text/plain', 'hello, world!')
|
||||
, (6, 1, 'image.svg', 'image/svg+xml', '<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"/>')
|
||||
, (7, 1, 'cover4.xpm', 'image/x-xpixmap', 'static char *s[]={"1 1 1 1","a c #ffffff","a"};')
|
||||
;
|
||||
|
||||
insert into services_carousel (media_id, caption)
|
||||
values (5, 'Source caption')
|
||||
, (6, 'Another caption')
|
||||
, (7, 'N/A')
|
||||
;
|
||||
|
||||
insert into services_carousel_i18n (media_id, lang_tag, caption)
|
||||
values (5, 'en', 'Target caption')
|
||||
;
|
||||
|
||||
select lives_ok(
|
||||
$$ select translate_services_carousel_slide(5, 'ca', 'Traducció') $$,
|
||||
'Should be able to translate a carousel slide'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select translate_services_carousel_slide(6, 'es', null) $$,
|
||||
'Should be able to “translate” a carousel slide to the empty string'
|
||||
);
|
||||
|
||||
select lives_ok(
|
||||
$$ select translate_services_carousel_slide(5, 'en', 'Not anymore') $$,
|
||||
'Should be able to overwrite a slide’s translation'
|
||||
);
|
||||
|
||||
select bag_eq(
|
||||
$$ select media_id, lang_tag, caption from services_carousel_i18n $$,
|
||||
$$ values (5, 'ca', 'Traducció')
|
||||
, (5, 'en', 'Not anymore')
|
||||
, (6, 'es', '')
|
||||
$$,
|
||||
'Should have all three slides'
|
||||
);
|
||||
|
||||
|
||||
select *
|
||||
from finish();
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify camper:add_service on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('camper.add_service(integer, text, text, text)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify camper:add_services_carousel_slide on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('camper.add_services_carousel_slide(integer, text)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,28 @@
|
|||
-- Verify camper:available_icons on pg
|
||||
|
||||
begin;
|
||||
|
||||
set search_path to camper;
|
||||
|
||||
select 1 / count(*) from icon where icon_name = 'baby';
|
||||
select 1 / count(*) from icon where icon_name = 'ball';
|
||||
select 1 / count(*) from icon where icon_name = 'bicycle';
|
||||
select 1 / count(*) from icon where icon_name = 'campfire';
|
||||
select 1 / count(*) from icon where icon_name = 'castle';
|
||||
select 1 / count(*) from icon where icon_name = 'fridge';
|
||||
select 1 / count(*) from icon where icon_name = 'information';
|
||||
select 1 / count(*) from icon where icon_name = 'kayak';
|
||||
select 1 / count(*) from icon where icon_name = 'outing';
|
||||
select 1 / count(*) from icon where icon_name = 'pool';
|
||||
select 1 / count(*) from icon where icon_name = 'puzzle';
|
||||
select 1 / count(*) from icon where icon_name = 'restaurant';
|
||||
select 1 / count(*) from icon where icon_name = 'route';
|
||||
select 1 / count(*) from icon where icon_name = 'rv';
|
||||
select 1 / count(*) from icon where icon_name = 'shower';
|
||||
select 1 / count(*) from icon where icon_name = 'store';
|
||||
select 1 / count(*) from icon where icon_name = 'toilet';
|
||||
select 1 / count(*) from icon where icon_name = 'washer';
|
||||
select 1 / count(*) from icon where icon_name = 'wheelchair';
|
||||
select 1 / count(*) from icon where icon_name = 'wifi';
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify camper:edit_service on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('camper.edit_service(integer, text, text, text)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,9 @@
|
|||
-- Verify camper:icon on pg
|
||||
|
||||
begin;
|
||||
|
||||
select icon_name
|
||||
from camper.icon
|
||||
where false;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify camper:remove_services_carousel_slide on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('camper.remove_services_carousel_slide(integer)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,19 @@
|
|||
-- Verify camper:service on pg
|
||||
|
||||
begin;
|
||||
|
||||
select service_id
|
||||
, company_id
|
||||
, icon_name
|
||||
, name
|
||||
, description
|
||||
from camper.service
|
||||
where false;
|
||||
|
||||
select 1 / count(*) from pg_class where oid = 'camper.service'::regclass and relrowsecurity;
|
||||
select 1 / count(*) from pg_policy where polname = 'guest_ok' and polrelid = 'camper.service'::regclass;
|
||||
select 1 / count(*) from pg_policy where polname = 'insert_to_company' and polrelid = 'camper.service'::regclass;
|
||||
select 1 / count(*) from pg_policy where polname = 'update_company' and polrelid = 'camper.service'::regclass;
|
||||
select 1 / count(*) from pg_policy where polname = 'delete_from_company' and polrelid = 'camper.service'::regclass;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,12 @@
|
|||
-- Verify camper:service_i18n on pg
|
||||
|
||||
begin;
|
||||
|
||||
select service_id
|
||||
, lang_tag
|
||||
, name
|
||||
, description
|
||||
from camper.service_i18n
|
||||
where false;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,16 @@
|
|||
-- Verify camper:services_carousel on pg
|
||||
|
||||
begin;
|
||||
|
||||
select media_id
|
||||
, caption
|
||||
from camper.services_carousel
|
||||
where false;
|
||||
|
||||
select 1 / count(*) from pg_class where oid = 'camper.services_carousel'::regclass and relrowsecurity;
|
||||
select 1 / count(*) from pg_policy where polname = 'guest_ok' and polrelid = 'camper.services_carousel'::regclass;
|
||||
select 1 / count(*) from pg_policy where polname = 'insert_to_company' and polrelid = 'camper.services_carousel'::regclass;
|
||||
select 1 / count(*) from pg_policy where polname = 'update_company' and polrelid = 'camper.services_carousel'::regclass;
|
||||
select 1 / count(*) from pg_policy where polname = 'delete_from_company' and polrelid = 'camper.services_carousel'::regclass;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,11 @@
|
|||
-- Verify camper:services_carousel_i18n on pg
|
||||
|
||||
begin;
|
||||
|
||||
select media_id
|
||||
, lang_tag
|
||||
, caption
|
||||
from camper.services_carousel_i18n
|
||||
where false;
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify camper:translate_service on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('camper.translate_service(integer, text, text, text)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -0,0 +1,7 @@
|
|||
-- Verify camper:translate_services_carousel_slide on pg
|
||||
|
||||
begin;
|
||||
|
||||
select has_function_privilege('camper.translate_services_carousel_slide(integer, text, text)', 'execute');
|
||||
|
||||
rollback;
|
|
@ -135,6 +135,10 @@ p, h1, h2, h3, h4, h5, h6 {
|
|||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
p + p {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 4.2rem;
|
||||
font-weight: 400;
|
||||
|
@ -432,28 +436,33 @@ dl, .nature div + div, .outside_activities > div {
|
|||
background-color: var(--accent);
|
||||
}
|
||||
|
||||
.surroundings .spiel {
|
||||
.carousel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.carousel .spiel {
|
||||
font-size: 2.4rem;
|
||||
padding-right: 4rem;
|
||||
}
|
||||
|
||||
.surroundings .spiel p {
|
||||
.carousel .spiel p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.surroundings figure, .surroundings .slick-track > img {
|
||||
.carousel figure, .carousel .slick-track > img {
|
||||
margin-right: 5rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.surroundings img {
|
||||
.carousel img {
|
||||
height: 40rem;
|
||||
width: 100%;
|
||||
border-radius: 5px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.surroundings figcaption {
|
||||
.carousel figcaption {
|
||||
padding: 10px 15px;
|
||||
background: var(--clar);
|
||||
width: fit-content;
|
||||
|
@ -465,23 +474,23 @@ dl, .nature div + div, .outside_activities > div {
|
|||
font-size: 1.7rem;
|
||||
}
|
||||
|
||||
.surroundings .slick-list {
|
||||
.carousel .slick-list {
|
||||
order: 1;
|
||||
padding: 0 20% 0 0 !important;
|
||||
}
|
||||
|
||||
.surroundings .slick-track {
|
||||
.carousel .slick-track {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.surroundings .slick-slider {
|
||||
.carousel.slick-slider {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.surroundings .slick-arrow {
|
||||
.carousel .slick-arrow {
|
||||
font-size: 6rem;
|
||||
line-height: 1;
|
||||
width: 5rem;
|
||||
|
@ -492,25 +501,25 @@ dl, .nature div + div, .outside_activities > div {
|
|||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.surroundings .slick-prev.slick-arrow, .surroundings .slick-next.slick-arrow {
|
||||
.carousel .slick-prev.slick-arrow, .carousel .slick-next.slick-arrow {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.surroundings .slick-prev {
|
||||
.carousel .slick-prev {
|
||||
order: 2;
|
||||
margin: 2.5rem 4rem 0 0;
|
||||
}
|
||||
|
||||
.surroundings .slick-prev:hover {
|
||||
.carousel .slick-prev:hover {
|
||||
transform: translateX(-1.3rem);
|
||||
}
|
||||
|
||||
.surroundings .slick-next {
|
||||
.carousel .slick-next {
|
||||
order: 3;
|
||||
margin: 2.5rem 7rem 0 0;
|
||||
}
|
||||
|
||||
.surroundings .slick-next:hover {
|
||||
.carousel .slick-next:hover {
|
||||
transform: translateX(1.3rem);
|
||||
}
|
||||
|
||||
|
@ -526,7 +535,7 @@ dl {
|
|||
}
|
||||
|
||||
dl div {
|
||||
flex: 1;
|
||||
flex-basis: calc(25% - 5rem + 5rem / 4);
|
||||
}
|
||||
|
||||
dt {
|
||||
|
@ -544,32 +553,103 @@ dt {
|
|||
dl {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
del div {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.icon_baby {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M92,136a8,8,0,1,1,8-8A8,8,0,0,1,92,136Zm72-16a8,8,0,1,0,8,8A8,8,0,0,0,164,120Zm-10.13,44.62a49,49,0,0,1-51.74,0,4,4,0,0,0-4.26,6.76,57,57,0,0,0,60.26,0,4,4,0,1,0-4.26-6.76ZM228,128A100,100,0,1,1,128,28,100.11,100.11,0,0,1,228,128Zm-8,0a92.11,92.11,0,0,0-90.06-92C116.26,54.07,116,71.83,116,72a12,12,0,0,0,24,0,4,4,0,0,1,8,0,20,20,0,0,1-40,0c0-.78.16-17.31,12-35.64A92,92,0,1,0,220,128Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_ball {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M128,28A100,100,0,1,0,228,128,100.11,100.11,0,0,0,128,28Zm85,135.19a92,92,0,0,1-102.18,2.57L130.31,132h89.6A91.61,91.61,0,0,1,213,163.19ZM85.52,46.42A91.11,91.11,0,0,1,116,36.79,92,92,0,0,1,169.29,124h-39ZM219.91,124H177.29a100.06,100.06,0,0,0-46-87.93A92.11,92.11,0,0,1,219.91,124ZM78.59,50.42l21.3,36.89a100.09,100.09,0,0,0-53.16,83.77A91.92,91.92,0,0,1,78.59,50.42ZM55,183.94a92,92,0,0,1,48.87-89.7L123.38,128,78.59,205.58A92.75,92.75,0,0,1,55,183.94ZM128,220a91.37,91.37,0,0,1-42.48-10.42l21.3-36.89a100.07,100.07,0,0,0,99.1,4.16A92,92,0,0,1,128,220Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_bicycle {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M208,116a43.66,43.66,0,0,0-18.62,4.15L159,68h33a12,12,0,0,1,12,12,4,4,0,0,0,8,0,20,20,0,0,0-20-20H152a4,4,0,0,0-3.46,6L163.7,92H97L79.46,62A4,4,0,0,0,76,60H48a4,4,0,0,0,0,8H73.7L89.89,95.76,70.57,122.25A44.21,44.21,0,1,0,77,127L94.29,103.3,128.54,162a4,4,0,0,0,3.46,2,4.11,4.11,0,0,0,2-.54,4,4,0,0,0,1.44-5.48l-33.83-58h66.74l14.11,24.19A44,44,0,1,0,208,116ZM84,160a36,36,0,1,1-18.16-31.25L44.77,157.64a4,4,0,0,0,6.46,4.72l21.07-28.9A35.92,35.92,0,0,1,84,160Zm124,36a36,36,0,0,1-21.47-64.88l18,30.9a4,4,0,0,0,3.46,2,4.11,4.11,0,0,0,2-.54,4,4,0,0,0,1.44-5.48l-18-30.89A36,36,0,1,1,208,196Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_route {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M226.46,52.85a4,4,0,0,0-3.43-.73L160.47,67.76,97.79,36.42a4,4,0,0,0-2.76-.3l-64,16A4,4,0,0,0,28,56V200a4,4,0,0,0,5,3.88l62.56-15.64,62.68,31.34a4,4,0,0,0,2.76.3l64-16a4,4,0,0,0,3-3.88V56A4,4,0,0,0,226.46,52.85ZM100,46.47l56,28V209.53l-56-28ZM36,59.12l56-14V180.88l-56,14ZM220,196.88l-56,14V75.12l56-14Z"/%3E%3C/svg%3E');
|
||||
.icon_campfire {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M219.81,225.21A4,4,0,0,1,216,228a3.92,3.92,0,0,1-1.21-.19L128,200.2,41.21,227.81A3.92,3.92,0,0,1,40,228a4,4,0,0,1-1.21-7.81l76-24.19-76-24.19a4,4,0,1,1,2.42-7.62L128,191.8l86.79-27.61a4,4,0,1,1,2.42,7.62l-76,24.19,76,24.19A4,4,0,0,1,219.81,225.21ZM72,108c0-19,9.38-38.85,27.12-57.27A152,152,0,0,1,125.9,28.59a4,4,0,0,1,4.2,0,152,152,0,0,1,26.78,22.14C174.62,69.15,184,89,184,108a56,56,0,0,1-54.56,56c-.48,0-1,0-1.44,0s-1,0-1.44,0A56,56,0,0,1,72,108Zm56,48a20,20,0,0,0,20-20c0-17.39-14.37-30.53-20-35-5.63,4.48-20,17.62-20,35A20,20,0,0,0,128,156ZM80,108a48,48,0,0,0,23.28,41.13A27.83,27.83,0,0,1,100,136c0-25.84,24.73-42.63,25.78-43.33a4,4,0,0,1,4.44,0c1.05.7,25.78,17.49,25.78,43.33a27.83,27.83,0,0,1-3.28,13.13A48,48,0,0,0,176,108c0-36.37-38.49-64.76-48-71.21C118.5,43.25,80,71.68,80,108Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_outing {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M164,44.17V32a20,20,0,0,0-20-20H112A20,20,0,0,0,92,32V44.17A52.05,52.05,0,0,0,44,96V216a12,12,0,0,0,12,12H200a12,12,0,0,0,12-12V96A52.05,52.05,0,0,0,164,44.17ZM112,20h32a12,12,0,0,1,12,12V44H100V32A12,12,0,0,1,112,20Zm60,144H84V152a12,12,0,0,1,12-12h64a12,12,0,0,1,12,12Zm-88,8h56v12a4,4,0,0,0,8,0V172h24v48H84Zm120,44a4,4,0,0,1-4,4H180V152a20,20,0,0,0-20-20H96a20,20,0,0,0-20,20v68H56a4,4,0,0,1-4-4V96A44.05,44.05,0,0,1,96,52h64a44.05,44.05,0,0,1,44,44ZM148,88a4,4,0,0,1-4,4H112a4,4,0,0,1,0-8h32A4,4,0,0,1,148,88Z"/%3E%3C/svg%3E');
|
||||
.icon_castle {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M200,28H184a12,12,0,0,0-12,12V56a4,4,0,0,1-4,4H152a4,4,0,0,1-4-4V40a12,12,0,0,0-12-12H120a12,12,0,0,0-12,12V56a4,4,0,0,1-4,4H88a4,4,0,0,1-4-4V40A12,12,0,0,0,72,28H56A12,12,0,0,0,44,40V84.69a11.93,11.93,0,0,0,3.51,8.48l11.32,11.32A4,4,0,0,1,60,107.31V216a12,12,0,0,0,12,12H184a12,12,0,0,0,12-12V107.31a4,4,0,0,1,1.17-2.82l11.32-11.32A11.93,11.93,0,0,0,212,84.69V40A12,12,0,0,0,200,28ZM148,220H108V152a20,20,0,0,1,40,0ZM204,84.69a4,4,0,0,1-1.17,2.82L191.51,98.83a11.93,11.93,0,0,0-3.51,8.48V216a4,4,0,0,1-4,4H156V152a28,28,0,0,0-56,0v68H72a4,4,0,0,1-4-4V107.31a11.93,11.93,0,0,0-3.51-8.48L53.17,87.51A4,4,0,0,1,52,84.69V40a4,4,0,0,1,4-4H72a4,4,0,0,1,4,4V56A12,12,0,0,0,88,68h16a12,12,0,0,0,12-12V40a4,4,0,0,1,4-4h16a4,4,0,0,1,4,4V56a12,12,0,0,0,12,12h16a12,12,0,0,0,12-12V40a4,4,0,0,1,4-4h16a4,4,0,0,1,4,4Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_fridge {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17.57617 28.04395"%3E%3Cpath stroke-width="0" d="m15.62988,0H1.94629C.87305,0,0,.87305,0,1.94629v25.59766c0,.27637.22363.5.5.5h16.57617c.27637,0,.5-.22363.5-.5V1.94629c0-1.07324-.87305-1.94629-1.94629-1.94629ZM1.94629,1h13.68359c.52148,0,.94629.42432.94629.94629v7.41357H1V1.94629c0-.52197.42432-.94629.94629-.94629Zm-.94629,26.04395V10.35986h15.57617v16.68408H1Z"/%3E%3Cpath stroke-width="0" d="m3.64453,5.36426h2.04248c.27637,0,.5-.22363.5-.5s-.22363-.5-.5-.5h-2.04248c-.27637,0-.5.22363-.5.5s.22363.5.5.5Z"/%3E%3Cpath stroke-width="0" d="m5.68701,13.271h-2.04248c-.27637,0-.5.22363-.5.5s.22363.5.5.5h2.04248c.27637,0,.5-.22363.5-.5s-.22363-.5-.5-.5Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_information {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M140,176a4,4,0,0,1-4,4,12,12,0,0,1-12-12V128a4,4,0,0,0-4-4,4,4,0,0,1,0-8,12,12,0,0,1,12,12v40a4,4,0,0,0,4,4A4,4,0,0,1,140,176ZM124,92a8,8,0,1,0-8-8A8,8,0,0,0,124,92Zm104,36A100,100,0,1,1,128,28,100.11,100.11,0,0,1,228,128Zm-8,0a92,92,0,1,0-92,92A92.1,92.1,0,0,0,220,128Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_kayak {
|
||||
background-image: url('data:image/svg+xml,%3Csvg viewBox="0 0 29.52905 28.08545" xmlns="http://www.w3.org/2000/svg" id="uuid-87ec619a-4896-40b8-8478-aa076dee3f7c"%3E%3Cpath stroke-width="0" d="m29.46728,11.26074l-.57617-2.15186c-.08398-.31299-.24609-.59229-.45801-.78613-.26758-.24658-.59863-.3418-.90625-.25879l-3.4834.93213c-.26953.07227-.48535.26807-.6084.54932l-.5752,1.30713c-.0719.16376-.10333.34686-.10742.53485l-3.05627.81787c-.23956-3.37067-1.03204-8.79126-2.35193-10.66425l-.22363-.31836C16.58057.45752,15.69971.00049,14.76318,0h-.00049c-.94873,0-1.80566.44434-2.35156,1.21875-1.71973,2.44043-2.65771,9.75146-2.65771,12.82422,0,.24457.00812.52399.0199.81769l-3.25604.87134c-.09741-.16071-.21613-.30347-.36005-.40924l-1.15088-.84424c-.24854-.18213-.53125-.24463-.80127-.17236l-3.48389.93164c-.56689.15234-.85693.84961-.65918,1.58691l.57617,2.15137c.0835.31348.24561.59277.45654.78613.20508.18848.4458.28809.68506.28809.07471,0,.14893-.00879.22168-.02832l3.48486-.93359c.26807-.07227.48389-.26758.60693-.54883l.57568-1.30664c.07172-.16357.10309-.3465.10718-.5343l3.05591-.81793c.25531,3.52673,1.14368,8.94983,2.5791,10.98602.5459.77441,1.40332,1.21875,2.35205,1.21875.93652,0,1.81738-.45703,2.35645-1.22266l.22461-.31738c1.60352-2.27637,2.43066-9.79492,2.43066-12.50244,0-.23499-.00854-.51709-.021-.81805l3.25732-.87183c.09747.16071.21625.30347.36035.40912l1.15039.84473c.18066.13281.38086.20166.5791.20166.07422,0,.14941-.00977.22168-.0293l3.48535-.93262c.56641-.15234.85645-.84863.65918-1.58594Zm-24.24072,6.86182l-3.41602.93262c-.03516-.01465-.14844-.12109-.20654-.33691l-.57617-2.15137c-.05713-.21484-.01318-.3623-.04688-.3623h-.00098l3.4165-.93164s.00586.00293.01709.01074c.00049,0,.00098.00098.00098.00098l1.00842.74017-1.90881.5108c-.2666.07227-.4248.3457-.35352.6123.05957.22363.26172.37109.48242.37109.04297,0,.08643-.00586.12988-.0166l1.91559-.5127-.46198,1.13281Zm5.52686-4.07959c0-3.07959.93994-10.06934,2.4751-12.24805.35596-.50488.91504-.79492,1.53418-.79492.62109.00049,1.18213.2915,1.54053.79932l.22266.31689c1.17902,1.67261,1.96533,7.08936,2.18457,10.35278l-1.50305.40228c-.19397-2.55505-1.02423-4.83514-2.44324-4.83514-1.63379,0-2.48828,3.02197-2.48828,6.00684,0,.04913.00214.09814.00262.14728l-1.51538.40552c-.00574-.19403-.0097-.38147-.0097-.5528Zm2.52692-.12079c.02441-2.93951.89984-4.88605,1.48383-4.88605.53003,0,1.29828,1.6062,1.45496,4.09961l-2.93878.78644Zm2.96765.24115c-.02441,2.93933-.89978,4.88599-1.48383,4.88599-.53003,0-1.29834-1.60632-1.45496-4.09937l2.93878-.78662Zm2.52692-.12036c0,2.66846-.84961,9.94092-2.24902,11.92725l-.22363.31641c-.71387,1.01367-2.36084,1.01562-3.07373.00391-1.31152-1.86127-2.18622-7.22681-2.414-10.67285l1.50641-.4032c.19397,2.55505,1.02417,4.83484,2.44324,4.83484,1.63379,0,2.48828-3.02148,2.48828-6.00635,0-.04926-.00214-.09845-.00262-.14777l1.51514-.40558c.00659.20087.00995.38617.00995.55334Zm6.35742-1.23047s-.00684-.00293-.01855-.01123l-1.00897-.74091,1.90936-.51105c.26758-.07129.42578-.34521.35449-.6123-.07227-.26562-.3418-.42725-.6123-.35352l-1.91577.5127.46069-1.13379,3.41699-.93164c.03516.01416.14844.12012.20605.33691l.57617,2.15186c.05957.21973.01953.36816.04883.36133l-3.41699.93164Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_outing {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M164,44.17V32a20,20,0,0,0-20-20H112A20,20,0,0,0,92,32V44.17A52.05,52.05,0,0,0,44,96V216a12,12,0,0,0,12,12H200a12,12,0,0,0,12-12V96A52.05,52.05,0,0,0,164,44.17ZM112,20h32a12,12,0,0,1,12,12V44H100V32A12,12,0,0,1,112,20Zm60,144H84V152a12,12,0,0,1,12-12h64a12,12,0,0,1,12,12Zm-88,8h56v12a4,4,0,0,0,8,0V172h24v48H84Zm120,44a4,4,0,0,1-4,4H180V152a20,20,0,0,0-20-20H96a20,20,0,0,0-20,20v68H56a4,4,0,0,1-4-4V96A44.05,44.05,0,0,1,96,52h64a44.05,44.05,0,0,1,44,44ZM148,88a4,4,0,0,1-4,4H112a4,4,0,0,1,0-8h32A4,4,0,0,1,148,88Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_pool {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M88,145.39a4,4,0,0,0,4-4V124h72v19.29a4,4,0,0,0,8,0V32a4,4,0,0,0-8,0V52H92V32a4,4,0,0,0-8,0V141.39A4,4,0,0,0,88,145.39ZM92,116V92h72v24Zm72-56V84H92V60ZM28,168a4,4,0,0,1,4-4c13.21,0,20.12,4.61,26.22,8.67,5.9,3.93,11,7.33,21.78,7.33s15.88-3.4,21.78-7.33c6.09-4.06,13-8.67,26.21-8.67s20.13,4.61,26.22,8.67c5.9,3.93,11,7.33,21.79,7.33s15.88-3.4,21.78-7.33c6.1-4.06,13-8.67,26.22-8.67a4,4,0,0,1,0,8c-10.79,0-15.88,3.4-21.78,7.33-6.1,4.06-13,8.67-26.22,8.67s-20.13-4.61-26.22-8.67c-5.9-3.93-11-7.33-21.79-7.33s-15.88,3.4-21.78,7.33c-6.09,4.06-13,8.67-26.21,8.67s-20.12-4.61-26.22-8.67C47.88,175.4,42.79,172,32,172A4,4,0,0,1,28,168Zm200,40a4,4,0,0,1-4,4c-10.79,0-15.88,3.4-21.78,7.33-6.1,4.06-13,8.67-26.22,8.67s-20.13-4.61-26.22-8.67c-5.9-3.93-11-7.33-21.79-7.33s-15.88,3.4-21.78,7.33c-6.09,4.06-13,8.67-26.21,8.67s-20.12-4.61-26.22-8.67C47.88,215.4,42.79,212,32,212a4,4,0,0,1,0-8c13.21,0,20.12,4.61,26.22,8.67,5.9,3.93,11,7.33,21.78,7.33s15.88-3.4,21.78-7.33c6.09-4.06,13-8.67,26.21-8.67s20.13,4.61,26.22,8.67c5.9,3.93,11,7.33,21.79,7.33s15.88-3.4,21.78-7.33c6.1-4.06,13-8.67,26.22-8.67A4,4,0,0,1,228,208Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_puzzle {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M218.14,161.93a4,4,0,0,0-3.86-.24,24,24,0,0,1-34.23-23.25,24,24,0,0,1,34.23-20.13A4,4,0,0,0,220,114.7V72a12,12,0,0,0-12-12H167a32,32,0,1,0-62.91-10.33A32.57,32.57,0,0,0,105,60H64A12,12,0,0,0,52,72v37a32,32,0,1,0-10.33,62.91A32.28,32.28,0,0,0,52,171v37a12,12,0,0,0,12,12H208a12,12,0,0,0,12-12V165.31A4,4,0,0,0,218.14,161.93ZM212,208a4,4,0,0,1-4,4H64a4,4,0,0,1-4-4V165.31a4,4,0,0,0-1.86-3.38,4,4,0,0,0-3.85-.24,24,24,0,0,1-34.24-20.13,24,24,0,0,1,34.24-23.25A4,4,0,0,0,60,114.7V72a4,4,0,0,1,4-4h46.69a4,4,0,0,0,3.62-5.71,24,24,0,0,1,20.13-34.24,24,24,0,0,1,23.25,34.24A4,4,0,0,0,161.31,68H208a4,4,0,0,1,4,4v37a32.57,32.57,0,0,0-10.33-.94A32,32,0,1,0,212,171Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_restaurant {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M76,88V40a4,4,0,0,1,8,0V88a4,4,0,0,1-8,0ZM212,40V224a4,4,0,0,1-8,0V172H152a4,4,0,0,1-4-4,264.27,264.27,0,0,1,7.11-55.94c9.47-39.22,27.21-65.41,51.31-75.74A4,4,0,0,1,212,40Zm-8,6.46C162.25,70.33,156.81,145.75,156.1,164H204Zm-88-7.12a4,4,0,0,0-7.9,1.32l8,47.66a36,36,0,0,1-72,0l8-47.66a4,4,0,0,0-7.9-1.32l-8,48A4.89,4.89,0,0,0,36,88a44.06,44.06,0,0,0,40,43.81V224a4,4,0,0,0,8,0V131.81A44.06,44.06,0,0,0,124,88a4.89,4.89,0,0,0,0-.66Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_route {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M226.46,52.85a4,4,0,0,0-3.43-.73L160.47,67.76,97.79,36.42a4,4,0,0,0-2.76-.3l-64,16A4,4,0,0,0,28,56V200a4,4,0,0,0,5,3.88l62.56-15.64,62.68,31.34a4,4,0,0,0,2.76.3l64-16a4,4,0,0,0,3-3.88V56A4,4,0,0,0,226.46,52.85ZM100,46.47l56,28V209.53l-56-28ZM36,59.12l56-14V180.88l-56,14ZM220,196.88l-56,14V75.12l56-14Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_rv {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 29 23.832"%3E%3Cpath stroke-width="0" d="m15.6527,16.65094h-3.92455c-.68449,0-1.23939-.55489-1.23939-1.23939V6.08515c0-.68422.55467-1.2389,1.2389-1.2389h3.92406c.68476,0,1.23987.55511,1.23987,1.23987v9.32592c0,.68422-.55467,1.2389-1.2389,1.2389Zm-3.92504-10.80469c-.13139,0-.2389.1075-.2389.2389v9.32689c0,.13194.10696.2389.2389.2389h3.92406c.13248,0,.23987-.1074.23987-.23987V6.08515c0-.13194-.10696-.2389-.2389-.2389h-3.92504Z"/%3E%3Cpath stroke-width="0" d="m7.83386,11.2486H3.45423c-.68422,0-1.2389-.55467-1.2389-1.2389v-3.92455c0-.68422.55467-1.2389,1.2389-1.2389h4.38012c.68422,0,1.2389.55467,1.2389,1.2389v3.92406c0,.68449-.55489,1.23939-1.23939,1.23939ZM3.45423,5.84626c-.13194,0-.2389.10696-.2389.2389v3.92406c0,.13166.10772.23939.23939.23939h4.37914c.13166,0,.23939-.10772.23939-.23939v-3.92406c0-.13194-.10696-.2389-.2389-.2389H3.45423Z"/%3E%3Cpath stroke-width="0" d="m28.875,11.00897l-3.31604-5.19989h.18054c.72272,0,1.3103-.58594,1.3103-1.30615,0-3.03418-2.47571-4.50293-5.51849-4.50293H1.30981C.58759,0,0,.58594,0,1.30615v18.02783c0,.82843.67157,1.5,1.5,1.5h2.53625c.27747,1.91296,2.05322,3.23883,3.96619,2.9613,1.53394-.22247,2.73883-1.42737,2.9613-2.9613h7.07251c.27747,1.91296,2.05322,3.23883,3.96619,2.9613,1.53394-.22247,2.73883-1.42737,2.9613-2.9613h2.53625c.82843,0,1.5-.67157,1.5-1.5v-8c-.0014-.11981-.04572-.23511-.125-.32501Zm-1.46252-.17499h-8.41248v-4.20093c0-.44131.35776-.79907.79907-.79907h4.2334c.15027-.00006.29254.06744.38751.18378l2.99249,4.81622ZM7.5,22.83398c-1.38074,0-2.5-1.11926-2.5-2.5s1.11926-2.5,2.5-2.5,2.5,1.11932,2.5,2.5-1.11926,2.5-2.5,2.5Zm14,0c-1.38074,0-2.5-1.11926-2.5-2.5s1.11926-2.5,2.5-2.5,2.5,1.11932,2.5,2.5-1.11926,2.5-2.5,2.5Zm6.5-3.5c0,.27614-.22386.5-.5.5h-2.53625c-.27747-1.91296-2.05322-3.23877-3.96619-2.9613-1.53394.22247-2.73883,1.42737-2.9613,2.9613h-7.07251c-.27747-1.91296-2.05322-3.23877-3.96619-2.9613-1.53394.22247-2.73883,1.42737-2.9613,2.9613H1.49993c-.27613,0-.49997-.22387-.49993-.5l.00281-18.02702c.00002-.16883.13818-.30696.30701-.30696h20.2215c2.49295,0,4.51981,1.02413,4.51563,3.51052-.00028.16481-.14263.29856-.30744.29856h-6.07078c-.91711,0-1.66082.74301-1.66169,1.66012l-.00898,9.43901c0,.27637.22363.5.5.5s.5-.22363.5-.5v-4.07422h9.00195v7.5Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_shower {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M60,236a8,8,0,1,1-8-8A8,8,0,0,1,60,236Zm24-40a8,8,0,1,0,8,8A8,8,0,0,0,84,196Zm-64,0a8,8,0,1,0,8,8A8,8,0,0,0,20,196Zm32-32a8,8,0,1,0,8,8A8,8,0,0,0,52,164ZM252,40a4,4,0,0,1-4,4H219.31a4,4,0,0,0-2.82,1.17L187.73,73.93,165.86,202a12,12,0,0,1-8.17,9.44A12.09,12.09,0,0,1,154,212a12,12,0,0,1-8.46-3.52l-98-98A12,12,0,0,1,54,90.14l128-21.87,28.76-28.76A11.93,11.93,0,0,1,219.31,36H248A4,4,0,0,1,252,40ZM179.11,76.89,55.37,98a4,4,0,0,0-2.19,6.78l98,98a4,4,0,0,0,6.78-2.17Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_store {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M28,112a36,36,0,0,0,16,29.92V208a12,12,0,0,0,12,12H200a12,12,0,0,0,12-12V141.92A36,36,0,0,0,228,112l0-16a4.09,4.09,0,0,0-.13-1.1L213.5,44.7A12,12,0,0,0,202,36H54A12,12,0,0,0,42.5,44.7L28.15,94.9A4.09,4.09,0,0,0,28,96ZM50.19,46.9A4,4,0,0,1,54,44H202a4,4,0,0,1,3.84,2.9L218.7,92H37.3ZM100,100h56v12a28,28,0,0,1-56,0ZM36,112V100H92v12a28,28,0,0,1-56,0Zm168,96a4,4,0,0,1-4,4H56a4,4,0,0,1-4-4V145.94a36,36,0,0,0,44-17.48,36,36,0,0,0,64,0,36,36,0,0,0,44,17.48Zm-12-68a28,28,0,0,1-28-28V100h56v12A28,28,0,0,1,192,140Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_toilet {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M116,64a4,4,0,0,1-4,4H96a4,4,0,0,1,0-8h16A4,4,0,0,1,116,64Zm52,130.86,3.92,27.44A12,12,0,0,1,160,236H96a12,12,0,0,1-11.88-13.7L88,194.86A92.11,92.11,0,0,1,36,112a4,4,0,0,1,4-4H60V40A12,12,0,0,1,72,28H184a12,12,0,0,1,12,12v68h20a4,4,0,0,1,4,4A92.11,92.11,0,0,1,168,194.86ZM68,108H188V40a4,4,0,0,0-4-4H72a4,4,0,0,0-4,4Zm92.34,90.13a92,92,0,0,1-64.68,0L92,223.43a4,4,0,0,0,.94,3.19A3.93,3.93,0,0,0,96,228h64a3.93,3.93,0,0,0,3-1.38,4,4,0,0,0,.94-3.19ZM211.91,116H44.09a84,84,0,0,0,167.82,0Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_washer {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24.57617 28.04346"%3E%3Cpath stroke-width="0" d="m22.62988,0H1.94629C.87305,0,0,.87354,0,1.94678v25.59668c0,.27637.22363.5.5.5h23.57617c.27637,0,.5-.22363.5-.5V1.94678c0-1.07324-.87305-1.94678-1.94629-1.94678ZM1.94629,1h20.68359c.52148,0,.94629.4248.94629.94678v3.74756H1V1.94678c0-.52197.42432-.94678.94629-.94678Zm-.94629,26.04346V6.69434h22.57617v20.34912H1Z"/%3E%3Cpath stroke-width="0" d="m3.92139,4.02881h.55273c.27637,0,.5-.22363.5-.5s-.22363-.5-.5-.5h-.55273c-.27637,0-.5.22363-.5.5s.22363.5.5.5Z"/%3E%3Cpath stroke-width="0" d="m6.99805,4.02881h.55322c.27637,0,.5-.22363.5-.5s-.22363-.5-.5-.5h-.55322c-.27637,0-.5.22363-.5.5s.22363.5.5.5Z"/%3E%3Cpath stroke-width="0" d="m12.28809,8.35986c-4.63818,0-8.41162,3.77344-8.41162,8.41211,0,4.6377,3.77344,8.41113,8.41162,8.41113,4.6377,0,8.41113-3.77344,8.41113-8.41113,0-4.63867-3.77344-8.41211-8.41113-8.41211Zm0,1c3.78308,0,6.9071,2.85101,7.3515,6.51611-.02045.01282-.04352.01862-.06244.03467-.01562.01367-1.58887,1.35449-3.57617,1.35645h-.00391c-1.39941,0-2.19141-.55273-3.02979-1.1377-.83887-.58496-1.70703-1.19043-3.1167-1.19043-2.3396,0-4.15607,1.26996-4.96783,1.95667-.00067-.04156-.00629-.08191-.00629-.12366,0-4.08691,3.32471-7.41211,7.41162-7.41211Zm0,14.82324c-3.62976,0-6.6524-2.62415-7.28448-6.07397.05127-.02527.10077-.05597.1424-.09985.01953-.02051,1.99365-2.07031,4.70459-2.07031,1.0957,0,1.76709.46875,2.54492,1.01074.88477.61816,1.8877,1.31738,3.60156,1.31738h.00391c1.63293-.00134,2.9729-.72321,3.68317-1.19769-.15778,3.94849-3.40955,7.11371-7.39606,7.11371Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_wheelchair {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M176,76a28,28,0,1,0-28-28A28,28,0,0,0,176,76Zm0-48a20,20,0,1,1-20,20A20,20,0,0,1,176,28ZM164,168a60,60,0,1,1-60-60,4,4,0,0,1,0,8,52,52,0,1,0,52,52,4,4,0,0,1,8,0Zm39.09-34.54a4,4,0,0,1,.83,3.32l-16,80A4,4,0,0,1,184,220a3.44,3.44,0,0,1-.78-.08,4,4,0,0,1-3.14-4.7l15-75.22H128a4,4,0,0,1-3.47-6l22.08-38.42a84.05,84.05,0,0,0-96.06,7.61A4,4,0,0,1,45.45,97a92,92,0,0,1,108.73-6.15,4,4,0,0,1,1.29,5.34L134.91,132H200A4,4,0,0,1,203.09,133.46Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.icon_wifi {
|
||||
background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="%23000000" height="32" width="32"%3E%3Cpath d="M136,204a8,8,0,1,1-8-8A8,8,0,0,1,136,204ZM234.54,90.1a168,168,0,0,0-213.08,0,4,4,0,1,0,5.08,6.18,160,160,0,0,1,202.92,0,4,4,0,0,0,5.08-6.18Zm-32.06,35.81a120,120,0,0,0-149,0,4,4,0,0,0,5,6.27,112,112,0,0,1,139,0,4,4,0,0,0,5-6.27Zm-32.13,35.86a72,72,0,0,0-84.7,0,4,4,0,1,0,4.7,6.46,64.07,64.07,0,0,1,75.3,0,4,4,0,0,0,5.58-.87A4,4,0,0,0,170.35,161.77Z"/%3E%3C/svg%3E');
|
||||
}
|
||||
|
||||
.outside_activities {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.outside_activities h3 {
|
||||
.outside_activities h3, .campsite_services .spiel {
|
||||
font-size: calc(2.2rem + 4vw);
|
||||
font-weight: 600;
|
||||
line-height: .9em;
|
||||
}
|
||||
|
||||
.outside_activities h3 {
|
||||
margin-bottom: 10rem;
|
||||
}
|
||||
|
||||
|
@ -628,10 +708,6 @@ dt {
|
|||
width: 20%;
|
||||
}
|
||||
|
||||
.outside_activities > div:last-child p {
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.campsite_activities {
|
||||
margin-top: 10rem;
|
||||
padding-top: 10rem;
|
||||
|
@ -645,6 +721,10 @@ dt {
|
|||
margin-bottom: 5rem;
|
||||
}
|
||||
|
||||
.campsite_services.carousel .slick-track {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
footer {
|
||||
font-size: 1.5rem;
|
||||
text-align: center;
|
||||
|
|
|
@ -75,6 +75,9 @@
|
|||
<li>
|
||||
<a {{ if ne . "home"}}href="/admin/home"{{ end }}>{{( pgettext "Home Page" "title" )}}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a {{ if ne . "services"}}href="/admin/services"{{ end }}>{{( pgettext "Services Page" "title" )}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
{{- end }}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 jordi fita mas <jordi@tandem.blog>
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
{{ define "title" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/services.slideForm*/ -}}
|
||||
{{ if .ID}}
|
||||
{{( pgettext "Edit Carousel Slide" "title" )}}
|
||||
{{ else }}
|
||||
{{( pgettext "New Carousel Slide" "title" )}}
|
||||
{{ end }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/services.slideForm*/ -}}
|
||||
{{ template "settings-tabs" "services" }}
|
||||
<form
|
||||
enctype="multipart/form-data"
|
||||
{{ if .ID }}
|
||||
data-hx-put="/admin/services/slides/{{ .ID }}"
|
||||
{{ else }}
|
||||
action="/admin/services/slides" method="post"
|
||||
{{ end }}
|
||||
>
|
||||
<h2>
|
||||
{{ if .ID }}
|
||||
{{( pgettext "Edit Carousel Slide" "title" )}}
|
||||
{{ else }}
|
||||
{{( pgettext "New Carousel Slide" "title" )}}
|
||||
{{ end }}
|
||||
</h2>
|
||||
{{ CSRFInput }}
|
||||
<fieldset>
|
||||
{{ with .Media -}}
|
||||
{{ if .Val -}}
|
||||
<img src="{{ .Val }}" alt="">
|
||||
{{- end }}
|
||||
<label>
|
||||
{{( pgettext "Cover image" "input" )}}
|
||||
<input type="file" name="{{ .Name }}"
|
||||
{{ if not $.ID }}required{{ end }}
|
||||
{{ template "error-attrs" . }}><br>
|
||||
</label>
|
||||
{{ template "error-message" . }}
|
||||
{{- end }}
|
||||
{{ with .Caption -}}
|
||||
<label>
|
||||
{{( pgettext "Caption" "input")}}<br>
|
||||
<input type="text" name="{{ .Name }}" value="{{ .Val }}"
|
||||
{{ template "error-attrs" . }}><br>
|
||||
</label>
|
||||
{{ template "error-message" . }}
|
||||
{{- end }}
|
||||
</fieldset>
|
||||
<footer>
|
||||
<button type="submit">
|
||||
{{ if .ID }}
|
||||
{{( pgettext "Update" "action" )}}
|
||||
{{ else }}
|
||||
{{( pgettext "Add" "action" )}}
|
||||
{{ end }}
|
||||
</button>
|
||||
</footer>
|
||||
</form>
|
||||
{{- end }}
|
|
@ -0,0 +1,36 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 jordi fita mas <jordi@tandem.blog>
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
{{ define "title" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/services.slideL10nForm*/ -}}
|
||||
{{printf (pgettext "Translate Carousel Slide to %s" "title") .Locale.Endonym }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/services.slideL10nForm*/ -}}
|
||||
{{ template "settings-tabs" "campsiteTypes" }}
|
||||
<form data-hx-put="/admin/services/slides/{{ .ID }}/{{ .Locale.Language }}">
|
||||
<h2>
|
||||
{{printf (pgettext "Translate Carousel Slide to %s" "title") .Locale.Endonym }}
|
||||
</h2>
|
||||
{{ CSRFInput }}
|
||||
<fieldset>
|
||||
{{ with .Caption -}}
|
||||
<fieldset>
|
||||
<legend>{{( pgettext "Caption" "input")}}</legend>
|
||||
{{( gettext "Source:" )}} {{ .Source }}<br>
|
||||
<label>
|
||||
{{( pgettext "Translation:" "input" )}}
|
||||
<input type="text" name="{{ .Name }}" value="{{ .Val }}"
|
||||
{{ template "error-attrs" . }}><br>
|
||||
</label>
|
||||
{{ template "error-message" . }}
|
||||
</fieldset>
|
||||
{{- end }}
|
||||
</fieldset>
|
||||
<footer>
|
||||
<button type="submit">{{( pgettext "Translate" "action" )}}</button>
|
||||
</footer>
|
||||
</form>
|
||||
{{- end }}
|
|
@ -0,0 +1,50 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 jordi fita mas <jordi@tandem.blog>
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
{{ define "title" -}}
|
||||
{{( pgettext "Home Page" "title" )}}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/services.servicesIndex*/ -}}
|
||||
{{ template "settings-tabs" "services" }}
|
||||
<h2>{{( pgettext "Carousel" "title" )}}</h2>
|
||||
<a href="/admin/services/slides/new">{{( pgettext "Add slide" "action" )}}</a>
|
||||
{{ if .Slides -}}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{{( pgettext "Image" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Caption" "header" )}}</th>
|
||||
<th scope="col">{{( pgettext "Translations" "campsite type" )}}</th>
|
||||
<th scope="col">{{( pgettext "Actions" "campsite type" )}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ range $slide := .Slides -}}
|
||||
<tr>
|
||||
<td><a href="/admin/services/slides/{{ .ID }}"><img src="{{ .Media }}" alt=""></a></td>
|
||||
<td><a href="/admin/services/slides/{{ .ID }}">{{ .Caption }}</a></td>
|
||||
<td>
|
||||
{{ range .Translations }}
|
||||
<a
|
||||
{{ if .Missing }}
|
||||
class="missing-translation"
|
||||
{{ end }}
|
||||
href="/admin/services/slides/{{ $slide.ID }}/{{ .Language }}">{{ .Endonym }}</a>
|
||||
{{ end }}
|
||||
</td>
|
||||
<td>
|
||||
<form data-hx-delete="/admin/services/slides/{{ .ID }}" data-hx-headers='{ {{ CSRFHeader }} }'>
|
||||
<button type="submit">{{( pgettext "Delete" "action" )}}</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{- end }}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ else -}}
|
||||
<p>{{( gettext "No slides added yet." )}}</p>
|
||||
{{- end }}
|
||||
{{- end }}
|
|
@ -7,11 +7,11 @@
|
|||
{{- end }}
|
||||
|
||||
{{ define "head" -}}
|
||||
<link rel="stylesheet" media="screen" href="/static/slick@1.8.1.css">
|
||||
{{ template "carouselStyle" }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/app.homePage*/ -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/home.homePage*/ -}}
|
||||
<section class="nature">
|
||||
<div style="--background-image:url('/static/images/todd-trapani-5LHzBpiTuzQ-unsplash.jpg')">
|
||||
<h2>{{ (gettext "The pleasure of camping in the middle of nature…")}}</h2>
|
||||
|
@ -31,8 +31,8 @@
|
|||
<h2><a href="/{{ currentLocale }}/services">{{( gettext "Our services")}} <span>→</span></a></h2>
|
||||
</section>
|
||||
<section class="surroundings">
|
||||
<h2 class="sr-only">{{ (gettext "Surroundings")}}</h2>
|
||||
<div>
|
||||
<h2 class="sr-only">{{( pgettext "Surroundings" "title" )}}</h2>
|
||||
<div class="carousel">
|
||||
<div class="spiel">
|
||||
<p>{{(gettext "Located in <strong>Alta Garrotxa</strong>, between the <strong>Pyrenees</strong> and the <strong>Costa Brava</strong>.") | raw}}</p>
|
||||
<p>{{(gettext "Nearby there are the <strong>gorges of Sadernes</strong>, <strong>volcanoes</strong>, <strong>La Fageda d’en Jordà</strong>, the Jewish quarter of <strong>Besalú</strong>, the basaltic cliff of <strong>Castellfollit de la Roca</strong>… much to see and much to do.") | raw}}</p>
|
||||
|
@ -53,26 +53,5 @@
|
|||
</section>
|
||||
<p class="enjoy">{{( gettext "Come and enjoy!")}}</p>
|
||||
|
||||
<script src="/static/jquery@3.7.1.min.js"></script>
|
||||
<script src="/static/slick@1.8.1.min.js"></script>
|
||||
<script>
|
||||
jQuery(function () {
|
||||
jQuery('.surroundings > div').slick({
|
||||
slidesToShow: 2,
|
||||
slidesToScroll: 1,
|
||||
infinite: false,
|
||||
arrows: true,
|
||||
prevArrow: '<button type="button" class="slick-prev">←</button>',
|
||||
nextArrow: '<button type="button" class="slick-next">→</button>',
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 768,
|
||||
settings: {
|
||||
slidesToShow: 1,
|
||||
}
|
||||
},
|
||||
]
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{{ template "carouselInit" }}
|
||||
{{- end }}
|
||||
|
|
|
@ -62,3 +62,32 @@
|
|||
{{ define "alternateAnchor" -}}
|
||||
<a rel="alternate" href="{{ .HRef }}" hreflang="{{ .Lang }}" lang="{{ .Lang }}">{{ .Endonym }}</a>
|
||||
{{- end }}
|
||||
|
||||
{{ define "carouselStyle" -}}
|
||||
<link rel="stylesheet" media="screen" href="/static/slick@1.8.1.css">
|
||||
{{- end }}
|
||||
|
||||
{{ define "carouselInit" -}}
|
||||
<script src="/static/jquery@3.7.1.min.js"></script>
|
||||
<script src="/static/slick@1.8.1.min.js"></script>
|
||||
<script>
|
||||
jQuery(function () {
|
||||
jQuery('.carousel').slick({
|
||||
slidesToShow: 2,
|
||||
slidesToScroll: 1,
|
||||
infinite: false,
|
||||
arrows: true,
|
||||
prevArrow: '<button type="button" class="slick-prev">←</button>',
|
||||
nextArrow: '<button type="button" class="slick-next">→</button>',
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 768,
|
||||
settings: {
|
||||
slidesToShow: 1,
|
||||
}
|
||||
},
|
||||
]
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{{- end }}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: 2023 jordi fita mas <jordi@tandem.blog>
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
{{ define "title" -}}
|
||||
{{( pgettext "Services" "title" )}}
|
||||
{{- end }}
|
||||
|
||||
{{ define "head" -}}
|
||||
{{ template "carouselStyle" }}
|
||||
{{- end }}
|
||||
|
||||
{{ define "content" -}}
|
||||
{{- /*gotype: dev.tandem.ws/tandem/camper/pkg/services.servicesPage*/ -}}
|
||||
<h2>{{( pgettext "Services" "title" )}}</h2>
|
||||
<div class="campsite_services carousel">
|
||||
<div class="spiel">
|
||||
<p>{{(gettext "The campsite offers many different services.")}}</p>
|
||||
</div>
|
||||
{{ range .Carousel -}}
|
||||
{{ if .Caption -}}
|
||||
<figure>
|
||||
<img src="{{ .Media }}" alt=""/>
|
||||
<figcaption>{{ .Caption }}</figcaption>
|
||||
</figure>
|
||||
{{- else -}}
|
||||
<img src="{{ .Media }}" alt=""/>
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
</div>
|
||||
|
||||
{{ template "carouselInit" }}
|
||||
|
||||
<dl>
|
||||
{{ range .Services -}}
|
||||
<div>
|
||||
<dt class="icon_{{ .IconName }}">{{ .Name }}</dt>
|
||||
<dd>{{ .Description | raw }}</dd>
|
||||
</div>
|
||||
{{- end }}
|
||||
</dl>
|
||||
{{- end }}
|
Loading…
Reference in New Issue