diff --git a/demo/demo.sql b/demo/demo.sql index 39dd4ad..2cf9080 100644 --- a/demo/demo.sql +++ b/demo/demo.sql @@ -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ó', '
A la recepció l’informarem del que pot fer des del càmping mateix o pels voltants.
') + , (52, 'wifi', 'WiFi', 'Un 80 % de l’àrea del càmping disposa d’accés WiFi lliure.
') + , (52, 'restaurant', 'Bar & Tapes', 'Oberts:
Oberta a diari.
Venda de pa del dia per encàrrec.
') + , (52, 'wheelchair', 'Accessibilitat', 'Piscines i serveis del càmping adaptats a persones amb mobilitat reduïda.
') + , (52, 'toilet', 'Lavabos', 'Ubicació central i pràctica. Nets i ben mantinguts.
') + , (52, 'shower', 'Dutxa', 'Aigua calenta, sense fitxes.
') + , (52, 'baby', 'Bany per nadons', 'Bany individual per nadons, amb banyera i canviador.
') + , (52, 'pool', 'Piscina', 'Piscina per adults i piscina infantil.
(Piscines amb aigua salada.)
') + , (52, 'campfire', 'Barbacoa', '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).
') + , (52, 'rv', 'Estació servei per autocaravanes', 'Situada a l’entrada del càmping.
') + , (52, 'castle', 'Zona de jocs', 'Una zona central pels més menuts.
') + , (52, 'ball', 'Camp d’esport', 'Amb camp de futbol, voley, tenis-taula i espai per jugar.
') + , (52, 'puzzle', 'Sala de jocs i televisió', 'Una sala pels dies de mal temps.
') + , (52, 'washer', 'Rentadores i assecadores', 'Als safareigs del càmping hi ha dues rentadores i una assecadora que funcionen amb fitxes.
') + , (52, 'fridge', 'Lloguer de neveres', 'Possibilitat de llogar neveres per estades llargues amb Rent It.
') +; + +insert into service_i18n (service_id, lang_tag, name, description) +values (82, 'en', 'Information', 'At reception we will inform you of what you can do from the campsite itself or in the surrounding area.
') + , (82, 'es', 'Información', 'A recepción le informaremos de qué puede hacer en el camping o por los alrededores.
') + , (83, 'en', 'WiFi', '80 % of the campsite area has free WiFi access.
') + , (83, 'es', 'WiFi', 'Un 80 % del área del camping dispone de acceso WiFi libre.
') + , (84, 'en', 'Bar & Tapas', 'Open:
Abierto:
Open daily
Sale of daily bread to order.
') + , (85, 'es', 'Tienda', 'Abierta a diario.
Venta de pan del día por encargo.
') + , (86, 'en', 'Accessibility', 'Swimming pools and campsite services adapted to people with reduced mobility.
') + , (86, 'es', 'Acesibilidad', 'Piscinas y servicios del camping adaptados a personas con mobilidad reducida.
') + , (87, 'en', 'Toilets', 'Central and practical location. Clean and well maintained.
') + , (87, 'es', 'Lavabos', 'Ubicación central y práctica. Limpios y bien mantenidos.
') + , (88, 'en', 'Showers', 'Hot water, no tokens.
') + , (88, 'es', 'Duchas', 'Agua caliente, sin fichas.
') + , (89, 'en', 'Baby baths', 'Individual bathroom for babies, with bathtub and changing table.
') + , (89, 'es', 'Baño para bebés', 'Baños individuales para bebés, con bañera y cambiador.
') + , (90, 'en', 'Swimming pool', 'Adult pool and children’s pool.
(Salt water swimming pools.)
') + , (90, 'es', 'Piscina', 'Piscina para adultos y piscina infantil.
(Piscinas con agua salada.)
') + , (91, 'en', 'Barbecue', '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).
') + , (91, 'es', 'Barbacoa', '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).
') + , (92, 'en', 'RV service station', 'Located at the entrance of the campsite.
') + , (92, 'es', 'Estación servicio para autocaravanas', 'Situada en la entrada del camping.
') + , (93, 'en', 'Play area', 'A central area for the little ones.
') + , (93, 'es', 'Zona de juegos', 'Una zona central para los más pequeños.
') + , (94, 'en', 'Sports area', 'With football field, volleyball, table tennis and room to play.
') + , (94, 'es', 'Campo de deporte', 'Con campo de fútbol, voley, pimpón i espacio para jugar.
') + , (95, 'en', 'Games and television room', 'A room for bad weather days.
') + , (95, 'es', 'Sala de juegos y televisión', 'Una sala para los días de mal tiempo.
') + , (96, 'en', 'Washing machines and dryers', 'There are two token-operated washing machines and a dryer in the campsite’s laundry facilities.
') + , (96, 'es', 'Lavadora y secadoras', 'A los lavaderos del camping hay dos lavadoras y una secadora que funcionana con fichas.
') + , (97, 'en', 'Fridge rental', 'Possibility to rent refrigerators for long stays with Rent It.
') + , (97, 'es', 'Alquiler de neveras', 'Posibilidad de alquilar neveras para estancias largas con Rent It.
') +; + commit; diff --git a/demo/services_carousel0.avif b/demo/services_carousel0.avif new file mode 100644 index 0000000..7e41e80 Binary files /dev/null and b/demo/services_carousel0.avif differ diff --git a/demo/services_carousel1.avif b/demo/services_carousel1.avif new file mode 100644 index 0000000..489ed98 Binary files /dev/null and b/demo/services_carousel1.avif differ diff --git a/demo/services_carousel2.avif b/demo/services_carousel2.avif new file mode 100644 index 0000000..0e1254f Binary files /dev/null and b/demo/services_carousel2.avif differ diff --git a/demo/services_carousel3.avif b/demo/services_carousel3.avif new file mode 100644 index 0000000..6909fa7 Binary files /dev/null and b/demo/services_carousel3.avif differ diff --git a/deploy/add_service.sql b/deploy/add_service.sql new file mode 100644 index 0000000..1bc8827 --- /dev/null +++ b/deploy/add_service.sql @@ -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; diff --git a/deploy/add_services_carousel_slide.sql b/deploy/add_services_carousel_slide.sql new file mode 100644 index 0000000..c919243 --- /dev/null +++ b/deploy/add_services_carousel_slide.sql @@ -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; diff --git a/deploy/available_icons.sql b/deploy/available_icons.sql new file mode 100644 index 0000000..f585ac6 --- /dev/null +++ b/deploy/available_icons.sql @@ -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; diff --git a/deploy/edit_service.sql b/deploy/edit_service.sql new file mode 100644 index 0000000..bf1ab85 --- /dev/null +++ b/deploy/edit_service.sql @@ -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; diff --git a/deploy/icon.sql b/deploy/icon.sql new file mode 100644 index 0000000..7c06667 --- /dev/null +++ b/deploy/icon.sql @@ -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; diff --git a/deploy/remove_services_carousel_slide.sql b/deploy/remove_services_carousel_slide.sql new file mode 100644 index 0000000..daa2afe --- /dev/null +++ b/deploy/remove_services_carousel_slide.sql @@ -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; diff --git a/deploy/service.sql b/deploy/service.sql new file mode 100644 index 0000000..831991b --- /dev/null +++ b/deploy/service.sql @@ -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; diff --git a/deploy/service_i18n.sql b/deploy/service_i18n.sql new file mode 100644 index 0000000..324a905 --- /dev/null +++ b/deploy/service_i18n.sql @@ -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; diff --git a/deploy/services_carousel.sql b/deploy/services_carousel.sql new file mode 100644 index 0000000..95a421f --- /dev/null +++ b/deploy/services_carousel.sql @@ -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; diff --git a/deploy/services_carousel_i18n.sql b/deploy/services_carousel_i18n.sql new file mode 100644 index 0000000..53aed69 --- /dev/null +++ b/deploy/services_carousel_i18n.sql @@ -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; diff --git a/deploy/translate_service.sql b/deploy/translate_service.sql new file mode 100644 index 0000000..5e73288 --- /dev/null +++ b/deploy/translate_service.sql @@ -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; diff --git a/deploy/translate_services_carousel_slide.sql b/deploy/translate_services_carousel_slide.sql new file mode 100644 index 0000000..88a9ff0 --- /dev/null +++ b/deploy/translate_services_carousel_slide.sql @@ -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; diff --git a/pkg/app/admin.go b/pkg/app/admin.go index 0485027..e3b8bf2 100644 --- a/pkg/app/admin.go +++ b/pkg/app/admin.go @@ -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: diff --git a/pkg/app/public.go b/pkg/app/public.go index 99dbd0a..9b3a3fa 100644 --- a/pkg/app/public.go +++ b/pkg/app/public.go @@ -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: diff --git a/pkg/services/admin.go b/pkg/services/admin.go new file mode 100644 index 0000000..9397a43 --- /dev/null +++ b/pkg/services/admin.go @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2023 jordi fita masDunno
') $$, + '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', 'Dunno
') + , (2, 'toilet', 'Is this Google?', '') + $$, + 'Should have added all two service' +); + + +select * +from finish(); + +rollback; diff --git a/test/add_services_carousel_slide.sql b/test/add_services_carousel_slide.sql new file mode 100644 index 0000000..f35cf94 --- /dev/null +++ b/test/add_services_carousel_slide.sql @@ -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', '') + , (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; diff --git a/test/edit_service.sql b/test/edit_service.sql new file mode 100644 index 0000000..8d52d74 --- /dev/null +++ b/test/edit_service.sql @@ -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', 'A
') + , (6, 1, 'toilet', 'Service B', 'B
') +; + +select lives_ok( + $$ select edit_service(5, 'wifi', 'Service 1', '1
') $$, + 'Should be able to edit the first service' +); + +select lives_ok( + $$ select edit_service(6, 'baby', 'Service 2', '2
') $$, + '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', '1
') + , (6, 'baby', 'Service 2', '2
') + $$, + 'Should have updated all services.' +); + +select * +from finish(); + +rollback; diff --git a/test/icon.sql b/test/icon.sql new file mode 100644 index 0000000..0f2b0fc --- /dev/null +++ b/test/icon.sql @@ -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; + diff --git a/test/remove_services_carousel_slide.sql b/test/remove_services_carousel_slide.sql new file mode 100644 index 0000000..1c7b2f5 --- /dev/null +++ b/test/remove_services_carousel_slide.sql @@ -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', '') + , (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; diff --git a/test/service.sql b/test/service.sql new file mode 100644 index 0000000..f109113 --- /dev/null +++ b/test/service.sql @@ -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; + diff --git a/test/service_i18n.sql b/test/service_i18n.sql new file mode 100644 index 0000000..e37582a --- /dev/null +++ b/test/service_i18n.sql @@ -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; + diff --git a/test/services_carousel.sql b/test/services_carousel.sql new file mode 100644 index 0000000..ec02bba --- /dev/null +++ b/test/services_carousel.sql @@ -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; + diff --git a/test/services_carousel_i18n.sql b/test/services_carousel_i18n.sql new file mode 100644 index 0000000..8503b52 --- /dev/null +++ b/test/services_carousel_i18n.sql @@ -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; + diff --git a/test/translate_service.sql b/test/translate_service.sql new file mode 100644 index 0000000..8abf2cb --- /dev/null +++ b/test/translate_service.sql @@ -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', 'A
') + , (6, 1, 'toilet', 'Service B', 'B
') +; + +insert into service_i18n (service_id, lang_tag, name, description) +values (6, 'ca', 'serveib', 'B
') +; + +select lives_ok( + $$ select translate_service(5, 'ca', 'Servei A', 'a
') $$, + 'Should be able to translate the first service' +); + +select lives_ok( + $$ select translate_service(6, 'es', 'Servicio B', 'b
') $$, + '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', 'a
') + , (6, 'ca', 'Servei B', '') + , (6, 'es', 'Servicio B', 'b
') + $$, + 'Should have added and updated all translations.' +); + + +select * +from finish(); + +rollback; diff --git a/test/translate_services_carousel_slide.sql b/test/translate_services_carousel_slide.sql new file mode 100644 index 0000000..73d1e31 --- /dev/null +++ b/test/translate_services_carousel_slide.sql @@ -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', '') + , (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; diff --git a/verify/add_service.sql b/verify/add_service.sql new file mode 100644 index 0000000..5981e8d --- /dev/null +++ b/verify/add_service.sql @@ -0,0 +1,7 @@ +-- Verify camper:add_service on pg + +begin; + +select has_function_privilege('camper.add_service(integer, text, text, text)', 'execute'); + +rollback; diff --git a/verify/add_services_carousel_slide.sql b/verify/add_services_carousel_slide.sql new file mode 100644 index 0000000..4625053 --- /dev/null +++ b/verify/add_services_carousel_slide.sql @@ -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; diff --git a/verify/available_icons.sql b/verify/available_icons.sql new file mode 100644 index 0000000..3b3b5bc --- /dev/null +++ b/verify/available_icons.sql @@ -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; diff --git a/verify/edit_service.sql b/verify/edit_service.sql new file mode 100644 index 0000000..4c60aea --- /dev/null +++ b/verify/edit_service.sql @@ -0,0 +1,7 @@ +-- Verify camper:edit_service on pg + +begin; + +select has_function_privilege('camper.edit_service(integer, text, text, text)', 'execute'); + +rollback; diff --git a/verify/icon.sql b/verify/icon.sql new file mode 100644 index 0000000..85bdc4c --- /dev/null +++ b/verify/icon.sql @@ -0,0 +1,9 @@ +-- Verify camper:icon on pg + +begin; + +select icon_name +from camper.icon +where false; + +rollback; diff --git a/verify/remove_services_carousel_slide.sql b/verify/remove_services_carousel_slide.sql new file mode 100644 index 0000000..e7e4063 --- /dev/null +++ b/verify/remove_services_carousel_slide.sql @@ -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; diff --git a/verify/service.sql b/verify/service.sql new file mode 100644 index 0000000..57f9a32 --- /dev/null +++ b/verify/service.sql @@ -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; diff --git a/verify/service_i18n.sql b/verify/service_i18n.sql new file mode 100644 index 0000000..cb2289a --- /dev/null +++ b/verify/service_i18n.sql @@ -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; diff --git a/verify/services_carousel.sql b/verify/services_carousel.sql new file mode 100644 index 0000000..4fe85fa --- /dev/null +++ b/verify/services_carousel.sql @@ -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; diff --git a/verify/services_carousel_i18n.sql b/verify/services_carousel_i18n.sql new file mode 100644 index 0000000..bc3e78c --- /dev/null +++ b/verify/services_carousel_i18n.sql @@ -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; diff --git a/verify/translate_service.sql b/verify/translate_service.sql new file mode 100644 index 0000000..96aebd5 --- /dev/null +++ b/verify/translate_service.sql @@ -0,0 +1,7 @@ +-- Verify camper:translate_service on pg + +begin; + +select has_function_privilege('camper.translate_service(integer, text, text, text)', 'execute'); + +rollback; diff --git a/verify/translate_services_carousel_slide.sql b/verify/translate_services_carousel_slide.sql new file mode 100644 index 0000000..14120da --- /dev/null +++ b/verify/translate_services_carousel_slide.sql @@ -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; diff --git a/web/static/public.css b/web/static/public.css index 2e221d7..35c52a0 100644 --- a/web/static/public.css +++ b/web/static/public.css @@ -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; @@ -301,16 +305,16 @@ nav .has-submenu:hover ul, nav .has-submenu:focus-within ul { #menuShowHide:checked ~ nav { display: block; } - + #menuShowHide:checked ~ label[for="menuShowHide"]::before { content: "✕"; } - + nav ul { flex-direction: column; align-items: start; } - + nav .has-submenu ul { display: flex; position: static; @@ -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'); + 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; diff --git a/web/templates/admin/layout.gohtml b/web/templates/admin/layout.gohtml index cf850e0..b234ef9 100644 --- a/web/templates/admin/layout.gohtml +++ b/web/templates/admin/layout.gohtml @@ -75,6 +75,9 @@{{( pgettext "Image" "header" )}} | +{{( pgettext "Caption" "header" )}} | +{{( pgettext "Translations" "campsite type" )}} | +{{( pgettext "Actions" "campsite type" )}} | +
---|---|---|---|
{{ .Caption }} | ++ {{ range .Translations }} + {{ .Endonym }} + {{ end }} + | ++ + | +
{{( gettext "No slides added yet." )}}
+ {{- end }} +{{- end }} diff --git a/web/templates/public/home.gohtml b/web/templates/public/home.gohtml index db618a8..3c6b852 100644 --- a/web/templates/public/home.gohtml +++ b/web/templates/public/home.gohtml @@ -7,11 +7,11 @@ {{- end }} {{ define "head" -}} - + {{ template "carouselStyle" }} {{- end }} {{ define "content" -}} - {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/app.homePage*/ -}} + {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/home.homePage*/ -}}{{(gettext "Located in Alta Garrotxa, between the Pyrenees and the Costa Brava.") | raw}}
{{(gettext "Nearby there are the gorges of Sadernes, volcanoes, La Fageda d’en Jordà, the Jewish quarter of Besalú, the basaltic cliff of Castellfollit de la Roca… much to see and much to do.") | raw}}
@@ -53,26 +53,5 @@{{( gettext "Come and enjoy!")}}
- - - + {{ template "carouselInit" }} {{- end }} diff --git a/web/templates/public/layout.gohtml b/web/templates/public/layout.gohtml index 826931d..6e20c03 100644 --- a/web/templates/public/layout.gohtml +++ b/web/templates/public/layout.gohtml @@ -62,3 +62,32 @@ {{ define "alternateAnchor" -}} {{ .Endonym }} {{- end }} + +{{ define "carouselStyle" -}} + +{{- end }} + +{{ define "carouselInit" -}} + + + +{{- end }} diff --git a/web/templates/public/services.gohtml b/web/templates/public/services.gohtml new file mode 100644 index 0000000..a9fe6b5 --- /dev/null +++ b/web/templates/public/services.gohtml @@ -0,0 +1,42 @@ + +{{ define "title" -}} + {{( pgettext "Services" "title" )}} +{{- end }} + +{{ define "head" -}} + {{ template "carouselStyle" }} +{{- end }} + +{{ define "content" -}} + {{- /*gotype: dev.tandem.ws/tandem/camper/pkg/services.servicesPage*/ -}} +{{(gettext "The campsite offers many different services.")}}
+