Visit Stoney Campgrounds to see this website in production!
Designs
I've split the page layout into three sections: top, middle and bottom. Laravel makes it easy to create layouts and re-usable components. The top section contains the header and navbar. The middle section contains the main content of the page. The bottom section contains the footer.
Design 2021
Each page has too much blank space, not enough words and not enough UI.
The easy approach is to create large elements that can display some information or render large images to fill whitespace.
Unfortunately, there wasn't many photos or videos which forced the majority of content on each page to be text.
After some time, I drone footage which took a panoramic still of the area. The drone captured slowly and steadily which made it satisfying to watch. I immediately set it as a hero image for the homepage.
Padding was greatly increased on all elements along with Font Awesome icons to help comprehend blocks of text with just a few quick glances.
Design 2022
After a year of being in production, a few features have been modified, deprecated and added.
- A new button in the far right of the navbar was added. It toggles the website theme between dark and light mode.
- The CSS framework switched from Bootstrap to Tailwind making transitions, animations and styling so much easier.
- Rules page had a redesign making it easier to find the content you want using groups, icons and headers.
- /campsites now redirects to /map to stay consistent with back-end naming schemes.
- Various layout changes were made to lessen the amount of space wasted on unncessary elements.
Design 2024
The website has been updated to use the latest version of Laravel and Tailwind CSS. The website is now fully responsive and has a new design.
A shape divider got added near the bottom of the hero banner to help transition into the next section. A subtle vignette is overlayed on the hero image to help the text stand out and to transition the image into the shape divider.
A weather widget got added to the homepage. It displays a 14 day forecast for the area, clicking on a date will toggle showing the evening or morning forecast for that day.
A fire ban detection system got added. It uses the albertafirebans API to view certain levels of provincal fire bans and checks if any are nearby via calculating distance between two sets of georcoordinates which are Stoney Campground's location and each fire ban location.
The reservation system has been given an overhaul in an attempt to make it more user friendly and extremely straight forward.
The /amenities page has been consolidated into the /reserve page. The amenities page was a list of all the amenities available at the campground. It was a short and sweet page and therefore it was decided to merge the two pages to make it easier for users to find the information they need.
Another shape divider is used to separate the amenities section from the reservation form. It's placed at the top with icons relating to each amenity and a short description of each. This was added in hopes of reducing the amount of customer support requests regarding common questions about Stoney Campgrounds. In the end, it only helped a little.
The first name and last name fields for guests have been removed from the reservation form. These fields were always optional and even though that was never explicitly stated, customers would always leave them blank.
Instead of providing a first and last name, customers are now required to select a number of guests staying at a selected campsite. This doubles down as a way to determine if the "Large Group Fee" is applicable.
Previously, a third-party library was used for selecting reservation dates. There was issues with the behavior and interaction in both the front and back-end. The library was removed and settled for the native date picker. Native date pickers are themed to match the operating system of the device anyways. Any modern browser will have a date picker that is easy to use and understand.
The reserve system has been updated to show an interactive map of Stoney Campgrounds. This is to help guests understand where their campsite is located in relation to the rest of the campground.
It also helps guests understand the size of the Stoney Campgrounds and the amount of space they have to work with at their campsite. It shows where the garbage bins, showers, washrooms and the office building are located.
This makes it easier for guests to select campsites that are close to the amenities they want to use. As more campsites are added, some have amenities that others don't. To help guests understand this, a legend has been added to the campsite selector. Each campsite has a list of icons that represents the amenities available at that campsite.
Development
Stripe Integration
Payments are processed using a third party service, Stripe. They make it easy to create a checkout system using various types of payment methods. The website uses Stripe to create a checkout session after calculating the total cost of the reservation.
Stripe requires to checkout system to be created on the server side, but redirecting to the checkout page has to be done on the client side. Without knowing about their redirectToCheckout fuction, I originally had the server submit the reservation form which caused a page reload, the server passes a Stripe checkout URL to a new page, and this new page uses JavaScript to redirect the window to the checkout URL. That's a lot of steps for a simple checkout process.
Stripe Integration Update 2022
After discovering the redirectToCheckoutfunction, the checkout system has been reworked to use the fetch API. The server creates a Stripe checkout session and forwards the ID via JSON response.
After this update, a lot of the back-end functions were updated to use the fetch API. This made the website faster and more responsive.
Campsite Availability
My first concern was UX/UI. I mean, how would you display over 300 selectable campsites? When you think about reserving a campsite you think about what you want to see. Some have more trees than others. How would you structure all of this information on a single page?
Before any work on the front-end, I like to work on the back-end first. At the very least I need a functioning reservation system to test different situations and see how the front-end would handle it.
I've never made a website for camping specifically so ideas come after the situation arises. I'll need to start with these tasks and fill in the gaps.
- A database server
- Objects for campsites, customers and reservations
- Functions to check campsite availability
- Functions to calculate the total cost of a reservation
- Functions to manage reservations
- A runtime-generated receipt email
A name is needed to identify the customer. An email is used to send a receipt. The phone number is used for emergencies or issues with the reservation.transaction_id
was unknown at the time. I settled for a string because it could be anything and I didn't want to limit the possibilities.reservations.campground_id
is a string concatenation of campgrounds.section
and campgrounds.number
.
There was unknowns about how campsites were going to be labeled and I had to think of a way to make it easy to query and display certain datasets without indexing via substrings or regex. Therefore campgrounds
has two columns, and reservations
has a single column. The complexity of queries towards selecting campsites from reservations
is miniscule.
Determining if a reservation is possible means finding other reservations conflicting with a choice of arrival and departure dates. The results from this query are other customers who have reservations that overlap with the requested dates. The campsites selected by these reservations are declared as unavailable.
When the arrival date overlaps with the departure date of other reservations, the campsites from existing reservations are declared as unavailable. This is also the case for the departure date conflicting with arrival dates of existing reservations.
To visualize this, imagine a table that contains reservations for certain dates. There is 2 existing reservations represented by red boxes, and a requested reservation represented by the outline box that intersects both red boxes.
The requested arrival date is January 3, which conflicts with the first reservation's departure date (January 3).
At the same time, the requested departure date (January 6) conflicts with the second reservation's arrival date.
Using this data, we can get the existing reservations selected campsites and declare them unavailable to the customer.