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.
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.
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.
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>.
This page is more or less similar to home, in terms of database: it
has a carousel and a list of items; in this case, the definition of
campsite services.
As i said early, when adding the home carousel, this carousel has its
own relation and set of functions to manage slides. They are also
duplicated in Go code, but i think i will need to refactor it later to
a carousel package or something like that, because both relations have
the exact same fields and types, so it makes no sense to have twice the
same code.
I already did it with the CSS and JavaScript code, mostly because it was
easier to replace the `.surroundings div` selector with `.carousel`, and
because that way i can have a single template that loads and initializes
Slick.
There is no UI to create or edit service definitions, although there are
the SQL functions, because i have no more time now, and Oriol needs to
check that the style is correct for that page.
This page is “highly stylized”, with a masonry-like grid, that i did not
know how to generate automatically from data defined in PostgreSQL,
therefore with Oriol we agreed to have this one as a static page and
we will see what we can do if the customer asks to be able to change
it.
I was a bit undecided on whether the icons in the bottom part of the
page should be defined in the CSS or with style="" and CSS variables,
like i do for the campsite type in the home page.
At first i thought that it should use CSS variables, mostly for
coherence: if another section of the web does it for its background
image, why no this one. The difference is that the home page is
dynamically created from the database, while this page is static and we
know what icons we need, thus it makes more sense to move it to the
stylesheet file, because then it will be downloaded by user agents that
actually want to use it (e.g., browsers, but not Braille terminals).
I debated with myself whether to create the home_carousel relation or
rather if it would be better to have a single carousel relation for all
pages. However, i thought that it would be actually harder to maintain
a single relation because i would need an additional column to tell one
carrousel from another, and what would that column be? An enum? A
foreign key to another relation? home_carousel carries no such issues.
I was starting to duplicate logic all over the packages, such as the
way to encode media paths or “localization” (l10n) input fields.
Therefore, i refactorized them.
In the case of media path, i added a function that accepts rows of
media, because always need the same columns from the row, and it was
yet another repetition if i needed to pass them all the time. Plus,
these kind of functions can be called as `table.function`, that make
them look like columns from the table; if PostgreSQL implemented virtual
generated columns, i would have used that instead.
I am not sure whether that media_path function can be immutable. An
immutable function is “guaranteed to return the same results given the
same arguments forever”, which would be true if the inputs where the
hash and the original_filename columns, instead of the whole rows, but
i left it as static because i did not know whether PostgreSQL interprets
the “same row but with different values” as a different input. That is,
whether PostgreSQL’s concept of row is the actual tuple or the space
that has a rowid, irrespective of contents; in the latter case, the
function can not be immutable. Just to be in the safe side, i left it
stable.
The home page was starting to grow a bit too much inside the app
package, new that it has its own admin handler, and moved it all to a
separate package.
There is a big difference between the item that has the submenu for
lodges and languages: languages is a link to the “alternate” version of
the page, while the lodges has no page to link to. Therefore, one is an
anchor while the other is a button, to make a semantic difference, but
both have the exact same appearance here.