Architecture
Technical architecture and component structure of Manage My Booking.
Technology stack
| Component | Technology |
|---|---|
| Framework | Next.js 14 (App Router) |
| Language | TypeScript 5.4.5 |
| Styling | Tailwind CSS 4.0 + Material-UI 5.13 |
| Data fetching | GraphQL (graphql-request + Apollo Client) |
| Internationalisation | i18next 22.5 + react-i18next 12.3 |
| Deployment | SST 3.5 (AWS Lambda + CloudFront) |
| Runtime | Node.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:
- Extracts the booking ID from the URL parameters.
- Calls the GraphQL API via
getBookingWithBrand()with React'scache()for request deduplication. - Validates the response — returns 404 if the booking is not found or has no brand.
- Extracts currency (defaults to GBP), language (defaults to en-GB), and brand configuration.
- Generates page metadata (title, favicon) from the brand.
- 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:
- CSS custom properties —
ServerThemeStylesconverts brand hex colours to HSL and injects them as--header-backgroundand--header-foregroundCSS variables. - MUI Theme —
BrandedThemeProviderfrom@holibob-packages/ui-corecreates a Material-UI theme from brand colours, fonts, and spacing.
Consumer permissions
The booking form is configured as a read-only consumer view:
| Permission | Value |
|---|---|
| canCreate | false |
| canRemove | false |
| canForceCancel | false |
| canPreviewFiles | false |
| isConsumerFacing | true |
| isHolibobAdmin | false |
Booking cancellation is the only interactive action and is controlled by the entity configuration (showCancelEntireBookingButton).