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.
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.
Seasons have a color to show on the calendar. I need them in HTML format
(e.g., #123abc) in order to set as value to `<input type="color">`, but
i did not want to save them as text in the database, as integers are
better representations of colors—in fact, that’s what the HTML syntax
also is: an integer.
I think the best would be to create an extension that adds an HTML color
type, with functions to convert from many representations (e.g., CSS’
rgb or even color names) to integer and back. However, that’s a lot of
work and i can satisfy Camper’s needs with just a couple of functions
and a domain.
To show the color on the index, at first tried to use a read-only
`<input type="color">`, but seems that this type of input can not be
read-only and must be disabled instead. However, i do not know whether
it makes sense to have a disabled input outside a form “just” to show
a color; i suspect it does not. Thus, at the end i use SVG with a
single circle, which is better that a 50%-rounded div with a background
color, even if the result is the same—SVG **is** intended for showing
pictures, which is this case.
I realized that tax details, campsite types, and campsites pages are all
part of the settings of the company, in the sense that all of them are
set up by a company administrator, and should be under the same item in
the user menu.
The template for these tabs is in the same layout.gohtml file because
i did not want to repeat the tabs everywhere it were used, or i would
forget some of them when adding new tabs, and did not want to add a new
file just for that.
It is inside the “user menu” only because this is where Numerus has the
same option, although it makes less sense in this case, because Numerus
is geared toward individual freelancers while Camper is for companies.
But, since it is easy to change afterward, this will do for now.
However, it should be only shown to admin users, because regular
employees have no UPDATE privilege on the company relation. Thus, the
need for a new template function to check if the user is admin.
Part of #17.
I need to check that the user is an employee (or admin) in
administration handlers, but i do not want to do it for each handler,
because i am bound to forget it. Thus, i added the /admin sub-path for
these resources.
The public-facing web is the rest of the resources outside /admin, but
for now there is only home, to test whether it works as expected or not.
The public-facing web can not relay on the user’s language settings, as
the guest user has no way to set that. I would be happy to just use the
Accept-Language header for that, but apparently Google does not use that
header[0], and they give four alternatives: a country-specific domain,
a subdomain with a generic top-level domain (gTLD), subdirectories with
a gTLD, or URL parameters (e.g., site.com?loc=de).
Of the four, Google does not recommend URL parameters, and the customer
is already using subdirectories with the current site, therefor that’s
what i have chosen.
Google also tells me that it is a very good idea to have links between
localized version of the same resources, either with <link> elements,
Link HTTP response headers, or a sitemap file[1]; they are all
equivalent in the eyes of Google.
I have choosen the Link response headers way, because for that i can
simply “augment” ResponseHeader to automatically add these headers when
the response status is 2xx, otherwise i would need to pass down the
original URL path until it reaches the template.
Even though Camper is supposed to be a “generic”, multi-company
application, i think i will stick to the easiest route and write the
templates for just the “first” customer.
[0]: https://developers.google.com/search/docs/specialty/international/managing-multi-regional-sites
[1]: https://developers.google.com/search/docs/specialty/international/localized-versions