Holibob Docs

Architecture

Technical architecture and component structure of Manage My Booking.

Technology stack

ComponentTechnology
FrameworkNext.js 14 (App Router)
LanguageTypeScript 5.4.5
StylingTailwind CSS 4.0 + Material-UI 5.13
Data fetchingGraphQL (graphql-request + Apollo Client)
Internationalisationi18next 22.5 + react-i18next 12.3
DeploymentSST 3.5 (AWS Lambda + CloudFront)
RuntimeNode.js 22.x

Application structure

The application follows the Next.js App Router convention with a single dynamic route:

src/
├── app/
│   ├── layout.tsx                  # Root layout with metadata
│   ├── page.tsx                    # Home page (returns 404)
│   ├── globals.css                 # Global styles with Tailwind
│   └── [bookingId]/
│       └── page.tsx                # Main booking page (Server Component)
├── components/
│   ├── BookingManageContent.tsx    # Booking management UI wrapper
│   ├── Header.tsx                  # Brand-aware header with logo
│   └── ServerThemeStyles.tsx       # Runtime CSS variable injection
├── contexts/
│   ├── ApolloProvider.tsx          # GraphQL client setup
│   ├── ThemeProvider.tsx           # Brand-based MUI theming
│   ├── CurrencyProvider.tsx        # Currency context
│   ├── LanguageProvider.tsx        # Language/i18next context
│   └── ViewerProvider.tsx          # Brand and entity configuration
├── graphql/
│   ├── getBookingWithBrand.ts      # Server-side data fetching
│   ├── graphqlRequest.ts           # GraphQL SDK
│   ├── holibobHeaders.ts           # Header builder utility
│   └── queries/
│       └── BookingWithBrand.graphql
├── config/
│   ├── config.ts                   # App configuration (Zod-validated)
│   └── i18n.ts                     # i18next initialisation
└── utils/
    └── getLanguageFromId.ts        # Language lookup

Page flow

The [bookingId]/page.tsx is a React Server Component. On each request it:

  1. Extracts the booking ID from the URL parameters.
  2. Calls the GraphQL API via getBookingWithBrand() with React's cache() for request deduplication.
  3. Validates the response — returns 404 if the booking is not found or has no brand.
  4. Extracts currency (defaults to GBP), language (defaults to en-GB), and brand configuration.
  5. Generates page metadata (title, favicon) from the brand.
  6. Renders the content wrapped in the provider stack.

Provider stack

The booking page wraps content in nested providers:

ServerThemeStyles  (CSS variables for brand colours)
└── Header  (brand logo and header colour)
    └── ThemeProvider  (MUI theme from brand)
        └── ApolloProvider  (GraphQL client)
            └── CurrencyProvider
                └── LanguageProvider  (i18next + MUI LocalizationProvider)
                    └── ViewerProvider  (brand + entity config)
                        └── BookingManageContent  (booking form UI)

Data fetching

The application uses a single GraphQL query — BookingWithBrand — which fetches:

  • Booking fields: id, reference, state, languageId, createdAt, saleCurrency
  • Brand fields: name, colours (header, footer, primary, secondary, button with 5 shades each), fonts, spacing (airiness, roundness), assets (headerLogo, footerLogo, faviconImage)

Server-side fetching uses graphql-request. Client-side interactivity (booking form) uses Apollo Client.

Theming

Brand styling is applied through two mechanisms:

  1. CSS custom propertiesServerThemeStyles converts brand hex colours to HSL and injects them as --header-background and --header-foreground CSS variables.
  2. MUI ThemeBrandedThemeProvider from @holibob-packages/ui-core creates a Material-UI theme from brand colours, fonts, and spacing.

Consumer permissions

The booking form is configured as a read-only consumer view:

PermissionValue
canCreatefalse
canRemovefalse
canForceCancelfalse
canPreviewFilesfalse
isConsumerFacingtrue
isHolibobAdminfalse

Booking cancellation is the only interactive action and is controlled by the entity configuration (showCancelEntireBookingButton).