StepsContainer Component Documentation
Overview
StepsContainer is a multi-step wizard component that manages the itinerary creation flow. It supports both a standard 5-step flow and special destination flows with customizable steps (e.g., Maldives SIS with only 2 steps).
Table of Contents
- Architecture Overview
- Special Destination Configuration
- Step Flow
- State Management
- Step Handlers System
- Navigation System
- Search Operations
- Completion Flow
- UI Layout
- Key Functions Reference
- Data Flow Diagrams
Architecture Overview
graph TD
A[StepsContainer] --> B[Form Management]
A --> C[Context Integration]
A --> D[Step Configuration]
B --> E[Ant Design Form Instance]
C --> F[ItineraryComposerContext]
C --> G[DataContext]
C --> H[AppContext]
D --> I[Standard Flow: 5 Steps]
D --> J[Special Flow: Configurable]
I --> K[CX → Routes → Flights → Hotels → Activities]
J --> L[CX → Flights → Done]
Key Dependencies
Contexts Used:
useItineraryComposerContext- Step data, navigation, and persistenceuseDataContext- Hotel/flight search state, IndexedDB operationsuseAppContext- User information (name, role)
Custom Hooks:
useRequestRegistry- Manages async API requests and prevents duplicates
Step Components:
InitialDetails- Customer and trip detailsRoutes- City-wise itinerary planningFlights- Flight selection and searchHotels- Hotel selection and searchActivities- Additional activities and extras
Special Destination Configuration
Configuration System
Location: Top of StepsContainer.jsx
Configuration Object: SPECIAL_DESTINATION_CONFIG
Defines custom flows for specific destinations. Each entry contains:
steps- Array of STEP constants to displayskipValidation- Array of STEP constants to skip during validation
Helper Functions
isSpecialDestination(destination)
- Checks if a destination requires special handling
- Returns boolean
getSpecialConfig(destination)
- Retrieves configuration for a special destination
- Returns config object or null
How It Works
The component watches the destination form field using Form.useWatch():
- When destination changes to a configured special destination (e.g.,
"maldives_sis") - The
stepsarray is automatically filtered to show only configured steps - UI adapts reactively without page refresh
- "Done" button appears on the last configured step
Key Variables:
currentDestination- Currently selected destinationspecialConfig- Retrieved configuration objectisSpecialFlow- Boolean flag indicating special flow is active
Step Flow
Standard Flow (5 Steps)
graph LR
A[1. CX Details] --> B[2. Routes]
B --> C[3. Flights]
C --> D[4. Hotels]
D --> E[5. Activities]
E --> F[Done]
style A fill:#e1f5ff
style B fill:#e1f5ff
style C fill:#fff4e1
style D fill:#ffe1e1
style E fill:#e1ffe1
style F fill:#f0f0f0
Special Flow Example (Maldives SIS)
graph LR
A[1. CX Details] --> B[2. Flights]
B --> C[Done]
style A fill:#e1f5ff
style B fill:#fff4e1
style C fill:#f0f0f0
Step Constants
Defined in @/utils/itineraryComposerUtils:
STEP.CX = 1- Customer detailsSTEP.ROUTES = 2- City itinerarySTEP.FLIGHTS = 3- Flight selectionSTEP.HOTELS = 4- Hotel selectionSTEP.ACTIVITIES = 5- Activities & extras
Step Arrays
allSteps - Memo containing all 5 possible steps with:
key- Unique identifiertitle- Display namecontent- React component to renderstep- STEP constant for filtering
steps - Computed array based on special destination:
- Filters
allStepsifisSpecialFlowis true - Otherwise returns all steps
- Used for rendering Steps component and navigation
State Management
Local State
form - Ant Design form instance
- Manages all form fields across all steps
- Provides validation, getValue, setValue methods
saving - Boolean loading state
- Controls button loading indicators during navigation
routesState - Object containing:
isNightsValid- Whether total nights allocation is correctnightsRemaining- How many nights left to allocatetotalNights- Total nights from CX stepsumNights- Sum of allocated nightsitinerary- Array of city objects with nights
Context State
From ItineraryComposerContext:
itData- Complete step data structuresetCurrentStep- Function to change active stepupdateStepData- Function to save step dataupdateShared- Function to update shared statecheckForCxChangesAndSave- Handles customer changesloading- Global loading statesetLoading- Set global loading
From DataContext:
setFetchingStatus- Hotel search status updatessetHotelsFromIndexedDb- Load cached hotelstimestampData/setTimestampData- Cache managementgetItenerary- Fetch itinerary datasetFetchingFlightData- Flight search state
Computed Values
currentStepIndex - Zero-based index of current step (itData.currentStep - 1)
totalNights - Total nights from CX step data
dealId - Extracted from route param id by splitting on "_"
isLastStep - True when on the final step
routesValue - Computed from either saved Routes data or shared route state
Step Handlers System
Handler Structure
stepHandlers object contains handlers for each STEP constant.
Each handler can define:
fields(form, idx)- Returns array of field names to validatevalues(form, idx)- Returns object of values to persistbeforeSave(form, idx)- Optional pre-validation logic, return true to prevent saveafterSave({ itData, routesState, updateShared, values })- Optional post-save side effects
Handler Breakdown
[STEP.CX] - Customer Details
- Validates: customer fields, destination, dates, pax counts
- Persists: All customer and trip parameters
- After Save: Calls
checkForCxChangesAndSave()
[STEP.ROUTES] - City Itinerary
- Validates: Nothing (uses state instead of form)
- Persists:
routesStateobject - Before Save: Checks
routesState.isNightsValid, shows Modal.error if invalid - After Save: Builds route using
buildRoute(), creates hotel entries, stores in IndexedDB
[STEP.FLIGHTS] - Flight Selection
- Validates: Dynamic flight fields via
buildFlightFieldNames() - Persists: Flight data via
buildFlightsValue() - Before Save: Ensures either flights exist or
flightBookedByGuestis true - After Save: Triggers
handleSearchFlights()
[STEP.HOTELS] - Hotel Selection
- Validates:
hotelsfield array - Persists: Hotel data via
buildHotelsValue() - After Save: Triggers
handleSearchHotels()
default - Fallback
- Uses
STEP_FIELDSmapping for field names - Generic getValue approach
Navigation System
Navigation Flow
stateDiagram-v2
[*] --> CheckDirection
CheckDirection --> Forward: Moving Forward
CheckDirection --> Backward: Moving Backward
Forward --> ValidateCurrent
ValidateCurrent --> PreventSave: beforeSave returns true
ValidateCurrent --> ValidateFields: beforeSave passes
ValidateFields --> SaveData
SaveData --> RunAfterSave
RunAfterSave --> Navigate
Backward --> Navigate
PreventSave --> [*]
Navigate --> [*]
Key Navigation Functions
validateAndSaveCurrent(manualIndex)
- Validates and saves the current step
- Can optionally validate a specific step via
manualIndexparameter - Runs through: beforeSave → validateFields → getValue → updateStepData → afterSave
- Returns boolean indicating success
goToIndex(targetIdx, isMaldives)
- Navigates to a specific step index
- Only validates if moving forward
isMaldivesparameter skips actual navigation (used for validation-only in special flows)- Returns validation result
handleNext()
- Validates current step
- Moves to next step if validation passes
- Sets
savingstate during process
handlePrev()
- Moves to previous step without validation
- Simply calls
setCurrentStep(itData.currentStep - 1)
onStepChange(targetIdx)
- Handles clicking on step indicators in Steps component
- Checks
canNavigateByClick()before allowing navigation - Navigates via
goToIndex()
canNavigateByClick(targetIdx)
- Returns true if step is clickable
- Allows: backward navigation OR completed steps
isStepCompleted(idx)
- Checks if step has
savedAttimestamp - Used to determine clickability
Search Operations
Flight Search Flow
Function: handleSearchFlights()
graph TD
A[handleSearchFlights] --> B[Get form values]
B --> C[Build TBO payloads]
C --> D[Build Google payloads]
D --> E[Create unique request ID]
E --> F{Same request active?}
F -->|Yes| G[Skip - return early]
F -->|No| H[Cancel other requests]
H --> I[Create new request signal]
I --> J[Register request]
J --> K[runFlightSearchAndPersist]
Process:
- Gets all form values
- Builds TBO payloads via
buildTboFlightPayloads() - Builds Google payloads via
buildGoogleFlightPayloads() - Creates unique request identifier via
createUniqueObjPerRequestFlights() - Checks if same search already active via
isRequestActive() - Cancels other active requests via
cancelFlightRequests() - Creates new request with abort signal
- Executes search via
runFlightSearchAndPersist()
Duplicate Prevention: Uses unique object based on flight parameters to skip identical searches
Hotel Search Flow
Function: handleSearchHotels()
graph TD
A[handleSearchHotels] --> B[Get form values]
B --> C[Build search payload]
C --> D[Create unique request ID]
D --> E{Same request active?}
E -->|Yes| F[Skip - return early]
E -->|No| G[Cancel other requests]
G --> H[Seed IndexedDB tabs]
H --> I[Create request signal]
I --> J[Set status: searching]
J --> K[searchAndPersistHotels]
K --> L{Success?}
L -->|Yes| M[Update IndexedDB]
L -->|No - AbortError| N[Silent fail]
L -->|No - Other| O[Set status: error]
Process:
- Gets form values
- Builds search payload via
buildHotelSearchPayload() - Creates unique request identifier via
createUniqueObjPerRequestHotels() - Checks if same search already active
- Cancels other active requests via
cancelHotelRequests() - Seeds IndexedDB tabs via
seedTabsToIndexedDB() - Sets
fetchingStatusto "searching" - Executes search via
searchAndPersistHotels() - Handles AbortError separately from other errors
Key Difference: Hotels use IndexedDB for per-city caching
Completion Flow
Standard Completion
Function: handleComplete()
sequenceDiagram
participant User
participant Component
participant API
participant Backend
User->>Component: Click Done
Component->>Component: Validate flights (if special flow)
Component->>API: checkPendingRequests()
Component->>Component: convertStepsDataToItineraryFormat()
Component->>Backend: saveItineraryAndFlights()
Backend-->>Component: Success
Component->>Component: initializeFetchingFlightData()
Component->>Component: getItenerary()
Component->>Component: navigate to /edit
Steps:
- Sets global loading state
- Special Flow Only: Validates flights step manually via
goToIndex(STEP.FLIGHTS, true) - Waits for pending API requests via
checkPendingRequests() - Converts all step data via
convertStepsDataToItineraryFormat()(passesisSpecialFlowflag) - Saves to backend via
saveItineraryAndFlights() - Handles errors with message.error()
- Initializes flight fetching state with
initializeFetchingFlightData() - Reloads itinerary via
getItenerary() - Navigates to
/edit/${id}
Special Flow Handling
For special destinations like maldives_sis:
- Manually validates the flights step before conversion
- Passes
isSpecialFlowflag toconvertStepsDataToItineraryFormat() - Converter skips validation for missing steps (Routes, Hotels, Activities)
Key Variable: isSpecialFlow - Ensures proper data conversion without errors
UI Layout
Layout Overview
graph TB
subgraph Main["StepsContainer Layout"]
TB[Top Bar - Close Button]
SI[Steps Indicator Card]
subgraph Content["Content Area - Row with 2 Columns"]
subgraph Left["Left Column - 18/24 width"]
RD[Route Display Card<br/>Conditional: currentStepIndex > 1 AND !isSpecialFlow]
SC[Step Content Card<br/>Dynamic form based on current step]
end
subgraph Right["Right Column - 6/24 width"]
NP[Notes Panel<br/>Persistent notes and comments]
end
end
Footer[Fixed Footer<br/>Previous | Quick Plan | Next/Done]
end
TB --> SI
SI --> Content
Content --> Footer
style TB fill:#f0f0f0
style SI fill:#e1f5ff
style RD fill:#fff4e1
style SC fill:#e1ffe1
style NP fill:#ffe1e1
style Footer fill:#f0f0f0
Layout Structure Details
graph TD
A[Full Container] --> B[Close Button - Absolute Position]
A --> C[Steps Card]
A --> D[Content Row]
A --> E[Spacer Div]
A --> F[Fixed Footer]
D --> G[Col 18/24 - Main]
D --> H[Col 6/24 - Notes]
G --> I{Step Index > 1?}
I -->|Yes & !Special| J[Route Display Card]
I -->|No or Special| K[Skip Route Display]
G --> L[Step Content Card]
L --> M[Form - vertical layout]
M --> N[Dynamic Step Component]
N --> O{Current Step}
O -->|Step 1| P[InitialDetails]
O -->|Step 2| Q[Routes]
O -->|Step 3| R[Flights]
O -->|Step 4| S[Hotels]
O -->|Step 5| T[Activities]
F --> U[Previous Button]
F --> V[Quick Plan Button]
F --> W{isLastStep?}
W -->|No| X[Next Button]
W -->|Yes| Y[Done Button]
style A fill:#e1f5ff
style D fill:#fff4e1
style F fill:#ffe1e1
style N fill:#e1ffe1
Conditional Rendering
Route Display Card:
- Shows when
currentStepIndex > 1 - Hidden in special flows (
!isSpecialFlow)
Next vs Done Button:
- Shows "Next" when
!isLastStep - Shows "Done" when
isLastStep
Loading Overlay:
- Shows full-screen loader when
loadingis true - Contains Spin component
Key Functions Reference
Form Management
| Function | Purpose |
|---|---|
form.getFieldsValue() |
Retrieves form field values |
form.setFieldsValue() |
Sets form field values |
form.validateFields(names) |
Validates specific fields |
Form.useWatch(field, form) |
Watches field changes reactively |
Step Navigation
| Function | Purpose |
|---|---|
validateAndSaveCurrent(manualIndex) |
Validates and saves current/specific step |
goToIndex(targetIdx, isMaldives) |
Navigates to specific step index |
handleNext() |
Moves to next step with validation |
handlePrev() |
Moves to previous step |
handleComplete() |
Finalizes and saves entire itinerary |
handleClose() |
Closes wizard and returns to edit page |
onStepChange(targetIdx) |
Handles step indicator clicks |
Validation Helpers
| Function | Purpose |
|---|---|
isStepCompleted(idx) |
Checks if step has been saved |
canNavigateByClick(targetIdx) |
Determines if step is clickable |
Search Operations
| Function | Purpose |
|---|---|
handleSearchFlights() |
Executes flight API search |
handleSearchHotels() |
Executes hotel API search |
Utilities
| Function | Purpose |
|---|---|
addDays(dateString, days) |
Adds days to ISO date string |
isSpecialDestination(destination) |
Checks if destination is special |
getSpecialConfig(destination) |
Gets special destination config |
Data Flow Diagrams
Form Synchronization on Load
sequenceDiagram
participant Context
participant Component
participant Form
Context->>Component: itData.steps updated
Component->>Component: useEffect triggered
Component->>Component: Merge all step data
Component->>Form: setFieldsValue(allFormData)
Form->>Form: Fields populated
Trigger: useEffect watching itData.steps
Process: Reduces all step data into single object, sets form values
Validation and Save Flow
sequenceDiagram
participant User
participant Component
participant Handler
participant Context
User->>Component: Click Next
Component->>Component: validateAndSaveCurrent()
Component->>Handler: Get handler for step
Handler->>Component: Run beforeSave()
Component->>Component: validateFields()
Component->>Component: getFieldsValue()
Component->>Context: updateStepData()
Component->>Handler: Run afterSave()
Component->>Context: setCurrentStep()
Special Flow Detection
graph TD
A[User selects destination] --> B[Form.useWatch triggers]
B --> C{Is special destination?}
C -->|Yes| D[getSpecialConfig]
C -->|No| E[Use all steps]
D --> F[Filter steps array]
F --> G[Render filtered steps]
E --> G
Routes to Hotels Data Flow
graph TD
A[Routes Step] --> B[User allocates nights]
B --> C[setRoutesState]
C --> D{isNightsValid?}
D -->|No| E[Show error modal]
D -->|Yes| F[Save Routes]
F --> G[buildRoute]
G --> H[Create hotelsData array]
H --> I[updateStepData HOTELS]
H --> J[updateShared route]
H --> K[storeHotelsInIndexedDB]
Complete Flow Decision Tree
graph TD
A[Click Done] --> B{isSpecialFlow?}
B -->|Yes| C[Validate flights step]
B -->|No| D[Check pending requests]
C --> D
D --> E[Convert steps to format]
E --> F[Save to backend]
F --> G{Success?}
G -->|Yes| H[Initialize flight data]
G -->|No| I[Show error]
H --> J[Navigate to edit]
I --> K[Stay on page]
Component Interaction
Context Connections
graph TB
SC[StepsContainer] --> F[Form Instance]
SC --> IC[ItineraryComposerContext]
SC --> DC[DataContext]
SC --> AC[AppContext]
F --> ID[InitialDetails]
F --> R[Routes]
F --> FL[Flights]
F --> H[Hotels]
F --> A[Activities]
IC --> ICD[itData]
IC --> SCS[setCurrentStep]
IC --> USD[updateStepData]
IC --> US[updateShared]
DC --> SFS[setFetchingStatus]
DC --> SHFI[setHotelsFromIndexedDb]
DC --> GI[getItenerary]
AC --> N[name]
AC --> RO[role]
style SC fill:#e1f5ff
style IC fill:#fff4e1
style DC fill:#ffe1e1
style AC fill:#e1ffe1
Effect Hooks
Form Data Sync Effect
Trigger: Changes to itData.steps
Purpose: Merges all step data into form when component mounts or step data updates
Process: Reduces step data → Sets form values
Auto-Redirect Effect
Trigger: Changes to itData.currentStep
Purpose: Redirects to edit page if step exceeds 5 (failsafe)
Condition: itData.currentStep > 5
Error Handling
Validation Errors
- Handled by Ant Design Form
- Shows inline field errors
- Prevents navigation
Business Logic Errors
- Shows Modal.error with descriptive messages
- Returns boolean to prevent save
API Errors
- Catches in try-catch blocks
- Shows message.error
- Handles AbortError separately
Adding New Features
Adding a New Step
- Define STEP constant in
itineraryComposerUtils - Create step component
- Add to
allStepsarray with proper structure - Add handler to
stepHandlersobject - Update
convertStepsDataToItineraryFormatif needed
Adding a New Special Destination
- Add entry to
SPECIAL_DESTINATION_CONFIG - Define which steps to show in
stepsarray - Define which steps to skip in
skipValidationarray - Update backend converter to handle missing step data
Modifying Search Behavior
- Locate search function (
handleSearchFlightsorhandleSearchHotels) - Modify payload builders (e.g.,
buildTboFlightPayloads) - Update unique request identifier logic if needed
- Adjust search persistence function parameters
Important Variables
Configuration
SPECIAL_DESTINATION_CONFIG- Special destination flow definitionsstepHandlers- Step validation and persistence logicallSteps- Complete step definitionssteps- Filtered steps (respects special flow)
State
form- Ant Design form instancesaving- Button loading stateroutesState- Routes step validation statecurrentStepIndex- Zero-based active stepisSpecialFlow- Boolean for special destinationisLastStep- Boolean for last step detection
Context Values
itData- Complete step data structureloading- Global loading statetimestampData- Cache timestamps for searches
Computed
currentDestination- Active destination valuespecialConfig- Retrieved special configdealId- Extracted from route paramstotalNights- From CX step data
Best Practices
- Always use step constants - Never hardcode step numbers
- Check special flow state - Use
isSpecialFlowfor conditional logic - Use manualIndex parameter - When validating specific steps outside normal flow
- Handle abort errors - Distinguish between AbortError and actual failures
- Update both handlers and converters - When adding/modifying steps
- Test special flows separately - Different validation paths
- Monitor form sync - Check useEffect dependencies when debugging