Commit Graph

34 Commits

Author SHA1 Message Date
jordi fita mas ac09fd77da Use redsys_environment to choose the correct URL to send the payment to 2023-10-27 17:03:50 +02:00
jordi fita mas 57b4360dfb Add error messages to the booking form
I forgot _all_ of them….
2023-10-27 12:37:09 +02:00
jordi fita mas 048b7cbf90 Add the slug to the form in the campsite type public page
The idea is that the booking form will be prefilled with the values
passed from that other mini-form, and the campsite type is implicit
due to the page where the form is located at, but i need to give it to
the booking page.

The booking page does not yet use that information.
2023-10-19 21:38:44 +02:00
jordi fita mas 302ce29e4a Add the first draft of the booking and payment forms
The form is based on the one in the current website, but in a single
page instead of split into many pages; possibly each <fieldset> should
be in a separate page/view.  The idea is for Oriol to check the design
and decide how it would be presented to the user, so i needed something
to show him first.

I hardcoded the **test** data for the customer’s Redsys account.  Is
this bad? I hope not, but i am not really, really sure.

The data sent to Redsys is just a placeholder because there are booking
details that i do not know, like what i have to do with the “teenagers”
field or the area preferences, thus i can not yet have a booking
relation.  Nevertheless, had to generate a random order number up to
12-chars in length or Redsys would refuse the payment, claiming that
the order is duplicated.

The Redsys package is based on the PHP code provided by Redsys
themselves, plus some hints at the implementations from various Go
packages that did not know why they were so complicated.

Had to grant select on table country to guest in order to show the
select with the country options.

I have changed the “Postal code” input in taxDetails for “Postcode”
because this is the spell that it is used in the current web, i did not
see a reason to change it—it is an accepted form—, and i did not want to
have inconsistencies between forms.
2023-10-19 21:37:34 +02:00
jordi fita mas 96fb253354 Show the campsite type’s calendar in an “infinite scroll” carousel
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/
2023-10-18 21:06:41 +02:00
jordi fita mas 852acaccc3 Add the calendar to the public campsite type page
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
2023-10-14 23:14:23 +02:00
jordi fita mas 27af39b296 Add the Catalonia’s Tourism Registry number to company
It is required to be displayed on the website of tourism companies of
Catalonia.
2023-10-14 21:59:36 +02:00
jordi fita mas 2002f0e959 Fix the font size and weight of campsite type headings
These are according to the design
2023-10-13 22:10:44 +02:00
jordi fita mas 9b240cf8c2 Add the booking form to type, and style page according to design 2023-10-13 21:59:45 +02:00
jordi fita mas d1b43e5062 Show “Starting from” price with campsite types that have options
In the old website, the prices where show with all the options, but in
the new design only a single price is show, that in the case of
campsites with options is the price per night of the “base” plus the
minimum options selected.
2023-10-13 20:44:24 +02:00
jordi fita mas 2e10966ad7 Add the list of features for campsite type 2023-10-13 20:30:31 +02:00
jordi fita mas 7fc87b5cb5 Add the info and facilities fields to campsite type
I wish CKEditor would have support for <dl>, but it is unlikely that
they will add any time soon[0], specially when in 2018 they already
“forgotten”[1] that this was requested back in 2007[2].

Therefore, in the demo i did it more or less like in the original
design, with “extra” <h4>, although i added them under a <h3>, to avoid
“gaps” in the outline.

[0]: https://github.com/ckeditor/ckeditor5/issues/775
[1]: https://github.com/ckeditor/ckeditor5/issues/775#issuecomment-358591747
[2]: https://dev.ckeditor.com/ticket/1333
2023-10-13 18:09:28 +02:00
jordi fita mas ebf47b5d75 Add the “spiel” for campsite types
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.
2023-10-12 18:47:08 +02:00
jordi fita mas 471ed9e870 Add the carousel for campsite types
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.
2023-10-12 17:43:23 +02:00
jordi fita mas caab52a274 Add the campground page
For now, this page only shows the map, that i had to create a new
“files” template function to include it as an extra template file.
2023-10-06 22:14:11 +02:00
jordi fita mas ae29726fa2 Add the footer site map
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.
2023-10-06 22:02:59 +02:00
jordi fita mas d5905a2277 Add the contact page, containing a map with the company location
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.
2023-10-06 21:21:00 +02:00
jordi fita mas b73c70c347 Change “singular lodges” menu item to just “campsites” 2023-10-06 18:58:24 +02:00
jordi fita mas 9293a341ef Add campsite type options, mainly for plots 2023-10-06 13:26:01 +02:00
jordi fita mas 60a488b7a0 Add the services and surroundings menu items to the public layout 2023-10-06 11:37:25 +02:00
jordi fita mas ef6a8f5aee Add the campsite type cost per season 2023-10-01 21:14:39 +02:00
jordi fita mas 44526b1efb Add the edit form for services
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>.
2023-09-25 20:10:33 +02:00
jordi fita mas afe77f2296 Add the services page
This page is more or less similar to home, in terms of database: it
has a carousel and a list of items; in this case, the definition of
campsite services.

As i said early, when adding the home carousel, this carousel has its
own relation and set of functions to manage slides.  They are also
duplicated in Go code, but i think i will need to refactor it later to
a carousel package or something like that, because both relations have
the exact same fields and types, so it makes no sense to have twice the
same code.

I already did it with the CSS and JavaScript code, mostly because it was
easier to replace the `.surroundings div` selector with `.carousel`, and
because that way i can have a single template that loads and initializes
Slick.

There is no UI to create or edit service definitions, although there are
the SQL functions, because i have no more time now, and Oriol needs to
check that the style is correct for that page.
2023-09-17 03:42:16 +02:00
jordi fita mas 8b8dda7969 Add the surroundings static 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).
2023-09-17 00:11:39 +02:00
jordi fita mas f746c82b46 Make home page’s carousel manageable via the database
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.
2023-09-15 01:05:38 +02:00
jordi fita mas 9306acaec3 Add checkbox and style for a mobile “hamburger” menu 2023-09-11 05:43:36 +02:00
jordi fita mas b7e130fed2 Integrate lodges’ and languages’ submenus to the main menu
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.
2023-09-11 05:13:57 +02:00
jordi fita mas e4053cd844 Change home’s texts to English and add Catalan and Spanish translations 2023-09-11 04:20:21 +02:00
jordi fita mas da127124a1 Add cover media to campsite types
This is the image that is shown at the home page, and maybe other pages
in the future.  We can not use a static file because this image can be
changed by the customer, not us; just like name and description.

I decided to keep the actual media content in the database, but to copy
this file out to the file system the first time it is accessed. This is
because we are going to replicate the database to a public instance that
must show exactly the same image, but the customer will update the image
from the private instance, behind a firewall.  We could also synchronize
the folder where they upload the images, the same way we will replicate,
but i thought that i would make the whole thing a little more brittle:
this way if it can replicate the update of the media, it is impossible
to not have its contents; dumping it to a file is to improve subsequent
requests to the same media.

I use the hex representation of the media’s hash as the URL to the
resource, because PostgreSQL’s base64 is not URL save (i.e., it uses
RFC2045’s charset that includes the forward slash[0]), and i did not
feel necessary write a new function just to slightly reduce the URLs’
length.

Before checking if the file exists, i make sure that the given hash is
an hex string, like i do for UUID, otherwise any other check is going
to fail for sure.  I moved out hex.Valid function from UUID to check for
valid hex values, but the actual hash check is inside app/media because
i doubt it will be used outside that module.

[0]: https://datatracker.ietf.org/doc/html/rfc2045#section-6.8
2023-09-10 03:04:18 +02:00
jordi fita mas 1f9668104e Add the first test for the front end design
As previously stated, web made the design with an external tool and
had to “convert” it to proper CSS and HTML markup.

Unfortunately, the original design uses slick, that requires jQuery;
i can’t do anything about it now.

Disabled most of the menu and language switcher because it is not in the
design yet.
2023-09-05 04:40:48 +02:00
jordi fita mas c0f532df4e Add the pages section
For now, this is almost identical to the campsite types, but this
section is for purely informational pages that have no other relation
to the database than “belongs to the same company”.

Part of #33.
2023-08-08 20:09:57 +02:00
jordi fita mas d117ce5027 Add public page for campsite type, and function to edit them
Had to export and move PublicPage struct to template because i can not
import app from campsites/types: app already imports campsite for the
http handler, and it, in turn, imports the types package for its own
http handler; an import loop.

Also had to replace PublicPage.MustRender with a Setup function because
the page passed down to html/template was the PublicPage struct, not
whatever struct embeds it.  I was thinking more of Java inheritance here
rather than struct embedding.
2023-08-08 02:45:54 +02:00
jordi fita mas 9a8ef8ce9f Add the language switched to the public layout
The language switcher needs the same information as languageLinks
needed, namely the list of locales and the current Path, to construct
the URI to all alternate versions.  However, in this case i need access
to this data in the template context, to build the list of links.

At first i use request’s context to hold the list of available locales
from application, and it worked, possibly without ill-effects, but i
realized that i was doing it just to avoid a new parameter.  Or, more
precise, an _explicit_ parameter; the context was used to skip the
inner functions between app and template.MustRenderPublic, but the
parameter was there all the same.

Finally, i thought that some handler might want to filter the list of
locales to show only the ones that it has a translation of.  In that
case, i would need to extract the locales from the context, filter it,
and create a new request with the updated context.  That made little
sense, and made me add the explicit locales parameter.

Since now the template has the same data as languageLinks, there is
little point of having the link in the HTTP response headers, and added
the <link> elements to <head>.

I thought that maybe i could avoid these <links> as they give the exact
same data as the language switch, but Google says nothing of using
regular anchors to gather information about localized versions of the
document[0], thus i opted to be conservative.  One can reason that the
<head> has more weight for Google, as most sites with user-generated
content, which could contain these anchors, rarely allow users to edit
the <head>.

[0]: https://developers.google.com/search/docs/specialty/international/localized-versions
2023-08-06 05:53:52 +02:00
jordi fita mas e128680e9a Split templates and handlers into admin and public
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
2023-08-05 03:42:37 +02:00