It seems that we have to highlight the map zones too. On the previous
website, they had a mouseover effect that displayed a tooltip, but here
we can not do that because we use the mouse to select accommodations.
This is just a test to see whether Oriol likes how it is shown, thus the
red is likely to change to something else more pleasant to look at.
This does nothing but bring useless work to the browser, that has to
move the SVG group to the top for nothing, as the accommodations do not
overlap anything else.
There are many installations on the map that are drawn with rect
instead of path. I could transform them with Inkscape, but i finally
found out about DOMMatrix, that helps me a lot to convert points, as
it can parse the values of the `transform` attribute.
I also saw that SVGGraphicElements has getCTM() and getScreenCTM(), but
neither of them worked for me: the resulting transformation was “shrunk”
on the map (i.e., like it was scaled down).
This is more or less the same as the campsites, as public information
goes, but for buildings and other amenities that the camping provides
that are not campsites.
A small page with a brief description, carousel, and feature list of
each individual accommodation.
Most of the relations and functions for carousel and features are like
the ones for campsite types, but i had to use the accommodation’s label
to find them, because they do not have slugs; i did not even though
these would be public, and they already have a label, although not
unique for all companies, like UUID slugs are.
I can not use <a> in that map because Leaflet handles the mouse over
before the anchors sees it, thus it is impossible to click on them; i
have to use a Leaflet layer.
Fortunately, i can just use the <path>’s coordinates as
the polygon points, because with CRS.Simple the coordinates map to
pixel, except for the reversed Y/latitude coordinate. Unfortunately,
<path> coordinates are not straightforward to get: I have to follow the
drawing coordinates, taking into account the current transformation
(CTM), and keeping the last point around for relative coordinates.
Bézier curves are simplified to a straight line from start to end.
There is one single accommodation that started with a relative move
command (m), which apparently have to be treated as an absolute
move (M), but subsequent pairs are treated as relative coordinates[0].
It was easier for me to convert that relative move to absolute and add
a relative lineto command (l) to the next pair.
For now, all i do is highlight the accommodation and zoom it on click,
because i do not know how i should the accommodation’s information.
[0]: https://www.w3.org/TR/SVG11/paths.html#PathDataMovetoCommands
In the map i added in e3503187d, paths around each accommodation
inherited the fill and stroke from the group, thus i could just override
that fill at the anchor level, but the current map sets the fill to each
accommodation’s path, party because the text is not a path too, partly
because Affinity is a visual tool only and does not give a shit about
mark up.
If we keep the text in a group, however, we can set the fill of the area
using CSS too, although it is not nice due to `!important`, but still.
There was a plot, however, #93, that had the area in a group too, and
i had to remove that group manually.
It seems that the prefix got removed in one of the edits.
Also, Affinity does not give a fuck to what classes we give to the
elements, and just removes them, thus .guest-only no longer matches, and
had to hide the layers by id. Hope they hold this time.
There is no kayaking, canoe, or raft icon in Font Awesome[0], so i redid
the kayak icon in more or less the same style, but shittier, of course.
Oriol also asked me to add the sailboat, that may replace the use of
kayak.
[0]: https://github.com/FortAwesome/Font-Awesome/issues/10772
We were using what was very clearly a campfire as the icon for the
barbecue service, when we first replaced it with a Font Awesome icon
we chose an icon that was neither campfire nor barbecue (flame-curved),
but now Oriol wanted one that left no doubt it was a barbecue.
Instead of replacing the campfire SVG with that of the barbecue, i have
chosen a campfire image from Font Awesome for our icon, and added a
separate icon for that service.
Previously, the only type with options was plots, that was always the
third fieldset, which was the wrong thing to hardcode, but it was done
in a hurry. Now there are more types with options, so we have to do it
properly.
I tried to use ../, and ../../ to make the routes relatives, but it
would not work well because the same page would have two URLs, one
ending with slash and another without, and the relative links would be
different on each case.
I need to have a single top-level element in order to serialize to XML
when returning the editor’s content back to the text area, because,
unlike PostgreSQL, XMLSerializer only works with XML documents.
I added a <div> because this is the least “semantic” block element there
is, but Oriol seems to have trouble in services when trying to align
services using a grid due to this extra div.
Now i still use the div to serialize to XML, but then remove that
top-level element from the output string by stripping out its label. I
can not be sure that all browsers would serialize the <div> in the
same way, thus i can not use fixed indices in call to substring.
This is mostly because it is required for the “Digital Kit”, but it also
works in our favor because now i can version the URL to the static
resources.
Go 1.18 adds the info from git if the package is build from a git
repository, but this is not the case in OBS, so i instead relay on a
constant for the version number. This constant is “updated” by Debian’s
rules, mostly due to the discussion in [0].
[0]: https://github.com/golang/go/issues/22706
Without the position relative, the booking button is no longer on top
of the carousel depending on the screen resolution, and Oriol wants less
gap between the carrousel and the next section.
I misunderstood Oriol, and what he really wanted was to have the arrows
on top of the image, not just move the arrows from below to above. Thus,
I no longer need the carrousel to be a flex because the buttons are now
absolutely position (slick.css already sets the container to relative
position).
These arrows need to be visually inside a single container, to have a
white background, but Slick adds the two arrows separately. I had to
move them close together, remove the radius from the “common edge”, and
use padding to “move” the arrows, rather than translation, in order to
avoid showing a gap.
Oriol did not like that this carousel was different from the rest, and
wanted to have it like the rest, but showing only one slide at a time,
like the customer wants.
He also wanted the arrows for **all** carousels to be in the top-right
corner instead of bottom-right, mostly because the customer complains
that she does not see the arrows if they are on the bottom, and Oriol
does not like the arrows to the sides.
This is a separate carousel from the one displayed at the bottom with
location info; it is, i suppose, a carousel for the hero image.
For the database, it works exactly as the home carousel, but on the
front had to use AlpineJS instead of Slick because it needs to show a
text popping up from the bottom when the slide is show, something i do
not know how to do in Slick.
It now makes no sense to have the carousel inside the “nature” section,
because the heading is no longer in there, and moved it out into a new
“hero” div.
Since i now have two carousels in home, i had to add additional
attributes to carousel.AdminHandler to know which URL to point to when
POSTing, PUTting, or redirecting.
Customer does not want the new “masonry-like” design of the surroundings
page, and wants the same style they already had: a regular list with
text and photo, alternating the photo’s side.
And, of course, they want to be able to add and edit them themselves. It
is like another carousel, but with an additional rich-text description.
The photos that we had in that page are no longer of use.
Had to create a custom build of CKEditor with the following plugins
added-in:
* Autoformat
* Block quote
* Bold
* General HTML Support
* Heading
* Image
* Image caption
* Image resize
* Image style
* Image toolbar
* Image upload
* Indent
* Italic
* Link
* Link image
* List
* Media embed
* Simple upload adapter
* Source editing
* Table
* Table toolbar
* Text transformation
The important bit is the “Simple uploader adapter”, that i modified to
upload the file as `media` instead of the default `upload` (i.e.,
modified ckeditor.js to replace "upload" with "media").
I also had to add the CSRF header somewhere in the HTML document for
JavaScript to be able to retrieve it and pass it to the uploader
adapter, or i would have to disable CSRF validation in that form, which
i did not like at all.
The pets icon is just the same as the notpet but without the diagonal
bar.
The price is hardcoded in the query for now, as there is no field
in the campsite type.
It is better for mobile users, as they can zoom and pan the map in their
small screens.
Had to increase header’s z-index or the zoom controls would be on top
of it.
Customer does not want a contact page, but a page where they can write
the direction on how to reach the campground, with a Google map embed
instead of using Leaflet, because Google Maps shows the reviews right
in the map.
That means i had to replace the GPS locations with XML fields for the
customer to write. In all four languages.
This time i tried a translation approach inspired by PrestaShop: instead
of opening a new page for each language, i have all languages in the
same page and use AlpineJS to show just a single language. It is far
easier to write the translations, even though you do not have the source
text visible, specially in this section that there is no place for me
to put the language links.
Customer does not want the next slide to show partially; either it shows
theo whole slide, or not at all.
Had to remove the min-width for campsite type’s spiel, or it would make
the whole thing fall over, i do not know why; possibly because slick
could not reduce the width to its expected value.
I use Sortable, exactly like HTMx’s sorting example does[0]. Had to
export the slug or ID of some entries to be able to add it in the hidden
input.
For forms that use ID instead of slug, had to use an input name other
than “id” because otherwise the swap would fail due to bug #1496[1]. It
is apparently fixed in a recent version of HTMx, but i did not want to
update for fear of behaviour changes.
[0]: https://htmx.org/examples/sortable/
[1]: https://github.com/bigskysoftware/htmx/issues/1496
Oriol does not want to waste so much vertical space for the calendar,
and wants it to show in a carousel, initially with only 6 months, and
loading the next three each time the user scrolls past the last.
I now use HTMx in the public page too for this auto-loading behavior,
based on their “infinite scroll” example[0].
Had to put the /calendar URI inside campsites because in the
calendar.gohtml i do not know the current type’s UUID, and can not use
a relative URL to “add subdirectories”, because the type does not end
with a slash.
Had to change season.CollectCalendar to expect the first month and a
number of months to show, to be able to load only 6 or 3 months after
the current, for the initial carousel content, or after the last month
of the carousel.
[0]: https://htmx.org/examples/infinite-scroll/
I had to export the Calendar type from Season to use it from
campsite/types, and also renamed them because season.SeasonCalendar is
a bit redundant compared to just season.Calendar.
I still have not added the HTMx code to switch year because i am not
sure whether Oriol will want to show a whole year or just half a year.
The calculation for the text color taking into account the contrast with
the background is from [0].
[0]: https://www.smashingmagazine.com/2020/07/css-techniques-legibility/#foreground-contrast
This is the text that introduces the carousel; it is not a spiel, but
this is what i call it.
It turns out that this text needs to have paragraphs and headings, much
like home’s slider, rather than the one in services page, thus no need
to change its font size or to align all items in the carousel in the
middle.
I can not reuse the carousel package because these carousels need the
campsite site’s slug as a first parameters: i can not have a relation
per campsite type, as i do in home and services pages, because the
campsite types are added by administration types; even if i had a
single relation for slides of home and services pages, these would go
in a different relation due to the foreign key to campsite type.
What i could reuse, however, is the Slide and SlideEntry types from
that package, although i had to export carousel.Translation to be usable
from the types package. I should change that to use locale.Translation,
but this was the easier option, or i would need to change the queries
and templates for carousel package too.
Besides that, they work exactly like the slides in home and services
pages.
I created a common template to show the company address in the footer
and the contact page, and then i realized Go did not like to output my
phone URL in the anchor without having the tel: schema in the template.
I then removed that variable and now the URL is created with tel: and
the phone number with its spaces removed.
I was not sure whether to use PostGIS to store the GPS location of the
company, as i am sure i will only use that point just to show the map.
However, just in case, it is not a big deal.
There is no way to change that from the administration pages for now,
because of time constraints, and it is very unlikely that they will
change the campgrounds’ location in the near future.
The location is in a separate table because i did not want to have to
change every test file, to be honest, but this also makes the map
“optional” without the need for NULL values.
I added the contact address to every public page because the new design
adds it to the footer, so i will be needing it everywhere, just like the
menu.
This calendar is supposed to be edited by admin users, but do not yet
have the complete JavaScript code to do so, thus for now i have made it
read-only.
This one has an input to select the icon. It makes no sense to choose
an icon only by name, thus a <select> is not appropriate, and had to
use a hidden input with a row of button to choose the icon from. This
works now only because there are very few icons; we’ll need to choose
a different approach when there are many more icons.
Since now the icons have to be defined in CSS for both the public and
admin sections, i had to split it into a separate file that both sites
can use. I considered the option to “include” that CSS with m4, like
i do for images in demo.sql, but it made everything too complicated
(e.g., having to call make for each change in the CSS), and decided to
load that CSS in a separate <link>.
I decided to use a custom attribute for the campsite label in the SVG
because i was a bit wary of reusing ‘id’ for that, specially given that
most labels are number only and XML can not have IDs starting with a
number.
In fact, at least Inkscape and Affinity solve the problem by having an
additional foreign attribute to keep the “group label” in without that
restriction (‘inkscape:label’ and ‘serif:id’, respectively), thus i
thought of doing the same, but with a namespace that i control and be
independent of the design program.
However, it seems that Affinity does not have a way of editing the XML
attributes like Inkscape does[0], thus there is no way of adding or
editing that value from there; i can not ask Oriol to edit the SVG file
in a text editor each time.
We have agreed to reuse the ‘id’ attribute to contain the campsite’s
label by using a specific prefix, that we checked is editable without
issue in Affinity’s UI.
[0]: https://forum.affinity.serif.com/index.php?/topic/24318-xml-data/&do=findComment&comment=115609
I am using the US terms for campground and campsite, that’s why the
relation is called ‘campsite’ instead of ‘pitch’, but i used the wrong
terminology in the SVG map because the customer uses the UK term and
call themselves campsite, so i mixed things.
It is now the campground map and each individual area is a campsite,
as i have been using all along.
I did not remember that the file was loaded as a module, so i have
to export it in order to use from the <script> in media. That <script>
needs to be also a module and explicitly import the function; since it
is already loaded, the browser does not load the file again.