Skip to content

Product Templates

ProductTemplate defines what a trip IS - its route, structure, and identity. Content is written in a source locale and can be translated when creating market-specific versions.

  • Define trip itinerary with POI-based locations and multi-segment routes
  • Store base marketing content
  • Calculate trip duration automatically
  • Provide foundation for market-specific products

Table: product_templates

FieldTypePurpose
titlevarcharProduct name
subtitlevarcharMarketing tagline
skuvarcharAuto-generated: <ID>-<DAYS>
source_localevarcharContent language (e.g., en_US)
itineraryjsonbArray of arrival + day items with POIs and routes
durationintTrip length in days (auto-calculated)
highlightsjsonbKey selling points
categoriesjsonbProduct categories (beach, luxury, etc.)

Source: backend/app/Models/ProductTemplate.php

The itinerary uses a structured format with an arrival item as the entry point followed by day items with locations and routes.

[
{
"type": "arrival",
"arrival_poi_id": 123
},
{
"locations": [456, 789],
"nights": 2,
"routes": [
{"from": "Nairobi", "to": "Amboseli", "from_id": 456, "to_id": 789, "mode": "arnk"}
],
"title": "Safari Adventure",
"details": "Game drives and wildlife viewing..."
},
{
"locations": [789, 456, 321],
"nights": 1,
"routes": [
{"from": "Amboseli", "to": "Nairobi", "from_id": 789, "to_id": 456, "mode": "arnk"},
{"from": "Nairobi", "to": "Lake Naivasha", "from_id": 456, "to_id": 321, "mode": "flight"}
],
"title": "Transfer & Flight",
"details": "Morning drive to Nairobi, afternoon flight..."
}
]
TypeFieldsPurpose
Arrivaltype, arrival_poi_idEntry point to the trip (first item)
Daylocations, nights, routes, title, detailsDaily journey with overnight location

Routes use the RouteMode enum to distinguish transport types:

ModeValueDescription
FlightflightAir travel between locations
SurfacearnkGround transport (road, rail, etc.)

The arnk value follows airline terminology for “Arrival Not Known” - used for surface segments in multi-city itineraries.

Source: backend/app/Enums/RouteMode.php

ItinerarySchema is the single source of truth for itinerary structure. Use it when working with itinerary data programmatically.

use App\Support\ItinerarySchema;
// Create items
ItinerarySchema::createArrivalItem($poiId);
ItinerarySchema::createDayItem($locationIds, $nights, $routes, $title, $details);
ItinerarySchema::createRoute($from, $to, RouteMode::Flight, $fromId, $toId);
// Check item types
ItinerarySchema::isArrival($item);
ItinerarySchema::isFlightRoute($route);

Source: backend/app/Support/ItinerarySchema.php

Duration is automatically calculated from itinerary day items:

duration = total_nights + 1

Arrival items are skipped (no nights). Example: Day 1 (2 nights) + Day 2 (1 night) = 3 nights + 1 = 4 days.

Source: backend/app/Filament/Resources/ProductTemplates/Support/ItineraryCalculator.php

The itinerary enforces route continuity: each day must start where the previous day ended. When using AI generation, the service automatically inserts connecting segments to maintain a continuous route graph.

Example: If Day 1 ends in Amboseli and Day 2 starts with Nairobi, a connecting segment is added automatically.

Helper class for itinerary computations:

use App\Filament\Resources\ProductTemplates\Support\ItineraryCalculator;
ItineraryCalculator::calculateDuration($itinerary); // Total days
ItineraryCalculator::getArrivalLocation($itinerary); // Entry city name
ItineraryCalculator::getAllLocations($itinerary); // All unique location names
ItineraryCalculator::hasFlightSegments($itinerary); // Has any flights?
ItineraryCalculator::generateHotelAssignments($itinerary); // Hotel assignment array

Source: backend/app/Filament/Resources/ProductTemplates/Support/ItineraryCalculator.php

The ItineraryRepeater provides the Filament form interface:

  • First item is always arrival type (cannot be deleted)
  • POI multi-select for day locations with Google Places search
  • Auto-generated routes from location sequence
  • Route mode selection (flight/surface)
  • Duration auto-updates on change

Source: backend/app/Filament/Resources/ProductTemplates/Schemas/Components/ItineraryRepeater.php

Templates support AI-powered content generation with automatic POI resolution:

  1. Trip Parser - Paste raw trip info, AI generates itinerary with POI lookups
  2. Title-based Generation - Enter title, AI generates descriptions

The AI service:

  • Generates itinerary in the new schema format
  • Resolves location names to POI IDs via database and Google Places
  • Ensures route continuity between days
  • Handles multi-segment days (e.g., drive + flight)

Configuration: OPENROUTER_API_KEY environment variable

Source: backend/app/Services/ProductTemplateAIService.php

The FlightRouteConfigGenerator analyzes itineraries to extract flight segments for booking configuration:

  • Identifies arrival location (first international flight destination)
  • Extracts routes with mode: flight for domestic segments
  • Deduplicates consecutive stops sharing the same airport
  • Generates multi-city and separate flight options

Source: backend/app/Services/Flights/FlightRouteConfigGenerator.php

  • SKU is auto-generated on save (cannot be manually edited)
  • Duration auto-calculates from itinerary nights
  • First itinerary item must be arrival type
  • Route continuity is enforced (no gaps in route graph)
  • Templates with associated ProductByMarket records are “locked”
  • Itinerary changes propagate to market products