OZZAK V2 public front end

- +Foundation: layout, header, footer, mobile menu and tab bar
- +Home, films, film, cinema and news (/actu) pages
- +Natural language search: LLM with structured output, parameterised PostgreSQL query
- +Safeguards: LLM-free autocomplete, rate limit, circuit breaker, reCAPTCHA, fallback search
- +V2 checkout flow: payment, pending validation, thank-you page
- +City selection for anonymous visitors, with a single location rule
- +SEO redirects for the old films and cinema URLs
- +News SEO: NewsArticle JSON-LD, canonical, hreflang, Open Graph, server rendering
- ~Back end adjusted when the front end needed it
- ~Pages converted from the Next.js reference to Symfony and Twig
After the member area, OZZAK is rebuilding its whole public front end for V2. I built this entire front end, from the home page to the purchase confirmation page, and worked on the back end whenever it was needed.
Approach
The UI reference is a Next.js version, which I convert into Symfony pages and Twig components (Symfony UX). Components follow atomic design: atoms, molecules, organisms and blocks. Each page has its own ticket, with a sub-ticket for every new component. Pages use the V2 toolchain, AssetMapper and importmap, instead of Webpack Encore.
What I built
- The foundation: layout, header, footer, mobile menu and the app tab bar.
- The home page: search, poster, daily showtime and article sliders, sign-up block.
- The films, film and cinema pages: trailer, sharing, date picker, showtimes by cinema, language and “Near me” filters, SEO redirects for the old URLs.
- City selection for anonymous visitors: a dialog on desktop, a drawer on mobile, with the city kept in session while browsing.
- The news section (/actu) and its SEO: NewsArticle and BreadcrumbList structured data, title and description from the CMS, canonical URLs with 301 redirects, fr-FR / fr-BE hreflang, article Open Graph tags, srcset and preloaded hero image, server-rendered article body and links to film pages.
- The checkout flow: payment, pending validation and thank-you page.
Natural language search
The home page search bar understands sentences such as “a comedy tonight near me”. The LLM is only a last resort. Autocomplete and exact film or cinema matches lead straight to the right page, without any LLM call.
For a free-form request, Claude Haiku fills a closed schema of criteria: genre, cinema, city, date, time. The model receives the current date, the time zone, the list of genres and the visitor’s location. It never writes SQL: the application builds a parameterised PostgreSQL query that applies the existing visibility rules.
The call is protected by a per-IP quota, a short timeout, a circuit breaker and reCAPTCHA v3. When the LLM is unavailable, a keyword search on the catalogue takes over. Tests use a fake extractor and never call the API.
Back end
Payment was already decoupled from the view (order validation workflow, Mangopay handlers, payment endpoints). I rewrote the presentation layer on top of these services, and adjusted the back end when the new pages required it. For example, the visitor location now follows a single rule (city chosen in session, then the profile postcode, then national content), owned by one service used by both the home page and the header.