Skip to content

Travel

Online bookings for a travel agency, with the price worked out for it

A travel agency in Ploiești with departures from four cities. Its brochure site became one that takes booking requests, works out the total itself, Early Booking included, and builds its offers and calendar pages from the departures.

Client
4 Seasons Holidays
Sector
Travel
Published

The problem

The site showed the tours but could not take a booking: the only routes in were the telephone and WhatsApp. Per-person totals and Early Booking prices were worked out by hand, and "what leaves this month?" was answered by whoever went through the tours one by one.

The solution

On every tour page, a booking request for adults, children and infants that shows the total at once and applies the Early Booking price until the date set for it. The request lands in the admin, with an email to the agency and a confirmation to the customer. Departures feed /oferte, filtered by category, destination, price and month, and /calendar, grouped by month, with nobody writing either. The admin is in Romanian, with filters on every list.

The architecture

Laravel moved from 11 to 13 and Livewire from 3 to 4, with the admin on Filament. Uploads run through an automatic image pipeline: a WebP display image of at most 800px and a thumbnail of at most 300px, generated at upload. The contact button decides server-side, on Bucharest time, whether to offer the telephone or WhatsApp, so every visitor and every cached page get the same answer.

The outcome

Online booking requests with totals and Early Booking worked out automatically; offers and calendar generated from departures; 66 business tests.

In detail

The departures decided the shape of this. A tour is not a page: it has departures, each with its own date, and a price that changes when Early Booking runs out. A page editor can hold text about a tour; it cannot hold the tour. So everything starts from the departures — the booking request, the offers page and the calendar all read the same data, and the agency enters it once.

The booking request is the piece with the largest effect. The customer says how many adults, children and infants are travelling and the total appears at once, with the Early Booking price applied for as long as it is valid. Once the date passes, the reduced price goes by itself, without anybody having to remember to take it down. The request is saved in the admin, as a booking and as a lead, and the emails go out in the same moment: one to the agency, one confirming to the customer.

/oferte and /calendar answer two different questions. /oferte is for someone looking: every upcoming departure, filtered by category, destination, price and month, with a link that keeps the filters. /calendar is for someone who wants to see the months ahead: departures in order, grouped by month. Neither is written by hand. A new tour appears in both the moment it has a departure.

The contact button looks like a small thing and is the one piece of logic here that had to be defended. Deciding in the browser whether the office is open is cheap to write and wrong twice: it trusts a clock the visitor controls, and it cannot be cached. Evaluated server-side in Europe/Bucharest, the answer is the same for everyone — the telephone while somebody is there to pick it up, WhatsApp when there is not.

Two major versions in one step is a decision people are right to be wary of without a safety net. The net is the 66 business tests, and they are not framework tests; they cover what the agency would notice if it broke — what /oferte and /calendar show, what the filters do, how a booking is saved, which contact route the page offers at a given hour. They are what made the upgrade a thing that could be checked rather than a thing that had to be hoped about.

Stack

  • Laravel 13
  • Livewire 4
  • Filament
  • Intervention Image
  • Docker

Capability

  • Online booking
  • Admin
  • Platform modernisation
Work

Contact

What do you want to build?

Discuss your project