Frontend Overview
The Volare frontend is built with Astro 5 and Tailwind CSS v4, providing optimal performance and SEO-friendly travel product pages.
Technology Stack
Section titled “Technology Stack”| Component | Technology | Version |
|---|---|---|
| Framework | Astro | 5.x |
| Styling | Tailwind CSS | 4.x |
| Runtime | Node.js | 18+ |
| Package Manager | pnpm | 8+ |
| Deployment | Cloudflare Pages | - |
Architecture
Section titled “Architecture”Frontend├── Pages (File-based routing)│ ├── Static pages (Astro components)│ ├── Dynamic routes ([market], [slug])│ └── API endpoints (.ts files)├── Layouts (Shared structure)│ └── MainLayout.astro├── Components (Reusable UI)│ └── Feature-specific components└── Shared (Utilities & config)Key Features
Section titled “Key Features”- Zero JavaScript by default - Astro components ship no JS unless needed
- File-based routing - Pages map directly to URLs
- Dynamic routes - Market and product slug parameters
- SSR/SSG flexibility - Server or static rendering per page
- Tailwind CSS v4 - Utility-first styling with CSS variables
Project Structure
Section titled “Project Structure”frontend/├── src/│ ├── components/ # Reusable Astro components│ ├── features/ # Feature-specific components│ ├── layouts/ # Page layouts│ │ └── MainLayout.astro│ ├── pages/ # File-based routing│ │ ├── index.astro│ │ ├── health.ts│ │ ├── [market]/ # Dynamic market routes│ │ └── products/│ ├── shared/ # Shared utilities│ └── styles/ # Global styles├── public/ # Static assets├── astro.config.mjs # Astro configuration└── package.jsonRouting
Section titled “Routing”Static Pages
Section titled “Static Pages”src/pages/index.astro -> /src/pages/about.astro -> /aboutsrc/pages/contact.astro -> /contactDynamic Routes
Section titled “Dynamic Routes”src/pages/[market]/index.astro -> /{market}src/pages/[market]/[...path].astro -> /{market}/{tourPath}/{slug} -> /{market}/{lang}/{tourPath}/{slug}src/pages/[market]/booking/[reference].astro -> /{market}/booking/{reference}Product Detail Routes
Section titled “Product Detail Routes”Product pages use localized tour path slugs from market configuration:
| Pattern | Example | Description |
|---|---|---|
/{market}/{tourPath}/{slug} | /es/circuito/to-maldivas | Default language |
/{market}/{lang}/{tourPath}/{slug} | /es/ca/circuit/to-maldivas | Specific language |
The tourPath segment is configured per-market and language (e.g., “circuito” for Spanish, “circuit” for Catalan).
Preview Mode: Product pages accept ?preview=<token> to display draft products using signed API URLs.
Source: frontend/src/pages/[market]/[...path].astro
API Endpoints
Section titled “API Endpoints”src/pages/health.ts -> /health (JSON response)Astro Components
Section titled “Astro Components”Basic Component
Section titled “Basic Component”---// Component script (runs at build time)interface Props { title: string; description?: string;}
const { title, description } = Astro.props;---
<article class="product-card"> <h2>{title}</h2> {description && <p>{description}</p>}</article>
<style> .product-card { @apply p-4 rounded-lg shadow-md; }</style>Layout Usage
Section titled “Layout Usage”---import MainLayout from '../layouts/MainLayout.astro';---
<MainLayout title="Page Title"> <main> <h1>Content here</h1> </main></MainLayout>Styling with Tailwind CSS v4
Section titled “Styling with Tailwind CSS v4”Configuration
Section titled “Configuration”Tailwind v4 uses CSS-first configuration:
@import "tailwindcss";
@theme { --color-primary: #3b82f6; --color-secondary: #64748b;}Usage in Components
Section titled “Usage in Components”<div class="bg-primary text-white p-4 rounded-lg"> <h1 class="text-2xl font-bold">Title</h1> <p class="text-secondary">Description</p></div>Development
Section titled “Development”Commands
Section titled “Commands”# Start development serverpnpm dev
# Build for productionpnpm build
# Preview production buildpnpm previewDevelopment Server
Section titled “Development Server”The frontend runs at http://localhost:4321 by default.
API Integration
Section titled “API Integration”Fetching from Backend
Section titled “Fetching from Backend”---const market = Astro.params.market;const lang = 'en';
const response = await fetch( `${import.meta.env.API_URL}/api/${market}/${lang}/products`);const { data: products } = await response.json();---
<ul> {products.map((product) => ( <li> <a href={`/${market}/products/${product.url_slug}`}> {product.title} </a> </li> ))}</ul>Image Optimization
Section titled “Image Optimization”Use Astro’s <Image /> component for optimized images:
---import { Image } from 'astro:assets';import heroImage from '../assets/hero.jpg';---
<Image src={heroImage} alt="Hero image" width={1200} height={600}/>Meta Tags
Section titled “Meta Tags”---// In layout or pageconst { title, description } = Astro.props;---
<head> <title>{title} | Volare Travel</title> <meta name="description" content={description} /> <meta property="og:title" content={title} /> <meta property="og:description" content={description} /></head>Structured Data
Section titled “Structured Data”<script type="application/ld+json"> {JSON.stringify({ "@context": "https://schema.org", "@type": "Product", "name": product.title, "description": product.description })}</script>Checkout Flow
Section titled “Checkout Flow”The checkout flow guides users through customizing their trip after selecting an offer.
Flow Steps
Section titled “Flow Steps”Flights → Hotels → Activities → Transfers → Travelers → Summary & Payment → Confirmation| Step | Route | Description |
|---|---|---|
| Flights | /{market}/checkout/{offerId}/flights | Select flight options |
| Hotels | /{market}/checkout/{offerId}/hotels | Select hotel accommodations |
| Activities | /{market}/checkout/{offerId}/activities | Select optional activity upgrades per day |
| Transfers | /{market}/checkout/{offerId}/transfers | Select optional transfer upgrades per day |
| Travelers | /{market}/checkout/{offerId}/travelers | Enter traveler information (names, documents, contacts) |
| Summary | /{market}/checkout/{offerId}/summary | Review booking and complete payment |
| Confirmation | /{market}/confirmation/{reference} | Booking confirmed, trip details and next steps |
Architecture
Section titled “Architecture”Checkout pages share a common architecture:
- Astro page - Server-rendered wrapper passing props to React
- React page component - Handles state, API calls, navigation
- Selector components - Day-by-day selection UI
- Shared components -
StickyHeader,NavigationFooter,HelpWidget
src/features/checkout/├── types/ # TypeScript interfaces│ ├── checkout.ts # Shared types (OfferSummary, Currency, Labels)│ ├── activity.ts # Activity selection types│ ├── transfer.ts # Transfer selection types│ ├── traveler.ts # Traveler data types│ └── summary.ts # Summary page types├── components/│ ├── ActivitySelectionPage.tsx│ ├── TransferSelectionPage.tsx│ ├── TravelerDataPage.tsx│ ├── SummaryPaymentPage.tsx│ ├── ActivitySelector/ # Day sections, cards│ ├── TransferSelector/ # Day sections, cards│ ├── TravelerForm/ # Traveler input forms│ ├── Summary/ # Summary page components│ ├── StickyHeader.tsx # Trip summary header│ ├── NavigationFooter.tsx # Back/Continue buttons│ └── HelpWidget.tsx # Support contact└── api/ └── checkoutApi.ts # Session persistence
src/features/confirmation/├── components/│ ├── ConfirmationPage.tsx # Main page component│ ├── HeroColumn.tsx # Left column with destination image│ ├── DetailsColumn.tsx # Right column with booking details│ ├── ChecklistItem.tsx # Individual checklist items│ └── ActionButtons.tsx # Download/share actions└── types/ └── confirmation.ts # Confirmation page typesState Management
Section titled “State Management”Each selection page uses the reducer pattern:
Map<number, string>tracks one selection per day (dayNumber → optionId)- Selections persist to backend session via checkout API
- Previous selections restore on page load
Session Persistence
Section titled “Session Persistence”Selections save to the checkout session (backend API):
// Activity selections{ activity_id: number, day_number: number, price: number }[]
// Transfer selections{ transfer_id: number, day_number: number, price: number }[]Source: frontend/src/features/checkout/
Deployment
Section titled “Deployment”The frontend deploys to Cloudflare Pages:
# Deploy to stagingpnpm deploy-stagingBuild output is in dist/ directory.