jetsetgo_departure-selection v1.0.9
jetsetgo_departure-selection
A highly customizable React component for handling travel departure and return journey selections.
Installation
npm install jetsetgo_departure-selection
Quick Start
import { DepartureSelection } from 'jetsetgo_departure-selection';
function App() {
return (
<DepartureSelection
initialData={{
departure: outboundServices,
return: returnServices
}}
uiConfig={{
continueToReturnText: "Continue to Return Journey",
finalContinueText: "Complete Booking",
backToOutboundText: "Back to Outbound",
backText: "Back",
outboundTitle: "Select Outbound Journey",
returnTitle: "Select Return Journey",
pageTitle: "Choose your journey",
pageSubtitle: "Select your preferred travel times",
outboundButtonMode: "continue" // 'continue' | 'complete'
}}
onComplete={handleComplete}
onDateSelect={handleDateSelect}
onServiceSelect={handleServiceSelect}
onContextChange={handleContextChange}
/>
);
}
Event Communication
The component provides a comprehensive event system to keep the parent application informed about user interactions.
Date Selection Events
Triggered when the user selects a new date:
interface DateSelectEvent {
date: string; // The newly selected date (YYYY-MM-DD)
previousDate: string; // The previously selected date
context: 'outbound' | 'return'; // Current selection context
}
// Example usage
const handleDateSelect = (event: DateSelectEvent) => {
console.log('Date changed:', event.date);
console.log('Previous date:', event.previousDate);
console.log('Current context:', event.context);
};
Service Selection Events
Emitted when a user selects a service:
interface ServiceSelectEvent {
serviceId: number; // ID of the selected service
context: 'outbound' | 'return'; // Whether this is outbound or return
service: Service; // Full service object
}
// Example usage
const handleServiceSelect = (event: ServiceSelectEvent) => {
console.log('Selected service:', event.serviceId);
console.log('Selection context:', event.context);
console.log('Service details:', event.service);
};
Context Change Events
Triggered when switching between outbound and return selection:
interface ContextChangeEvent {
previousContext: 'outbound' | 'return';
nextContext: 'outbound' | 'return';
selectedOutboundId: number | null;
selectedReturnId: number | null;
}
// Example usage
const handleContextChange = (event: ContextChangeEvent) => {
console.log('Switching from', event.previousContext, 'to', event.nextContext);
console.log('Current selections:', {
outbound: event.selectedOutboundId,
return: event.selectedReturnId
});
};
Completion Event
Triggered when the user completes their selection:
interface CompletionData {
selectedDepartureId: number | null;
selectedReturnId: number | null;
departureService: Service | null;
returnService: Service | null;
date: string;
totalCost: number;
}
// Example usage
const handleComplete = (data: CompletionData) => {
console.log('Selection completed:', data);
};
Features
Dynamic Loading States
The component supports loading states for data updates, commonly used when fetching new data after date changes:
function BookingApp() {
const [isLoading, setIsLoading] = useState(false);
const [loadingContext, setLoadingContext] = useState<'outbound' | 'return' | 'both'>('both');
const [services, setServices] = useState({
departure: initialDepartureServices,
return: initialReturnServices
});
const handleDateSelect = async (event: DateSelectEvent) => {
// 1. Start loading state for the relevant section
setIsLoading(true);
setLoadingContext(event.context);
try {
// 2. Fetch new data for the selected date
const newServices = await fetchServicesForDate(event.date);
// 3. Update the services data
setServices(newServices);
} catch (error) {
console.error('Failed to fetch services:', error);
} finally {
// 4. Stop loading state
setIsLoading(false);
}
};
return (
<DepartureSelection
initialData={services}
isLoading={isLoading}
loadingContext={loadingContext}
onDateSelect={handleDateSelect}
// ... other props
/>
);
}
When isLoading
is true:
- A loading overlay appears over the specified section(s)
- The UI remains interactive but selections are disabled
- Smooth transitions handle the loading state changes
- The date selector remains accessible
Data Persistence
The component includes built-in state persistence:
// Export current selections to JSON
const handleExport = () => {
const data = exportSelections();
// Save data to file/storage
};
// Import saved selections
const handleImport = (savedData: string) => {
importSelections(savedData);
};
Props
Required Props
Prop | Type | Description |
---|---|---|
initialData | { departure: Service[]; return: Service[]; } | The outbound and return services to display |
onComplete | (data: CompletionData) => void | Callback when selection is complete |
uiConfig | UIConfig | Configuration for UI text and labels |
Optional Props
Prop | Type | Description |
---|---|---|
selectedOutboundId | number \| null | Pre-selected outbound service ID |
selectedReturnId | number \| null | Pre-selected return service ID |
currentContext | 'outbound' \| 'return' | Initial selection context |
onDateSelect | (event: DateSelectEvent) => void | Date selection callback |
onServiceSelect | (event: ServiceSelectEvent) => void | Service selection callback |
onContextChange | (event: ContextChangeEvent) => void | Context change callback |
onBack | () => void | Callback for back button click |
isLoading | boolean | Whether the component is loading new data |
loadingContext | 'outbound' \| 'return' \| 'both' | Which section is loading |
UI Configuration
The uiConfig
prop allows customization of all text elements:
interface UIConfig {
continueToReturnText: string; // Text for continue to return button
finalContinueText: string; // Text for final continue button
backToOutboundText: string; // Text for back to outbound button
backText: string; // Text for back button
outboundTitle: string; // Title for outbound selection
returnTitle: string; // Title for return selection
pageTitle: string; // Main page title
pageSubtitle: string; // Page subtitle
outboundButtonMode: 'continue' | 'complete'; // Controls which button appears in outbound context
}
Outbound Button Mode
The outboundButtonMode
configuration controls which button appears after selecting an outbound service. Available options:
Option | Description |
---|---|
'continue' | (Default) Shows the "Continue to Return Journey" button (continueToReturnText ). Use this for return journeys where users need to select both outbound and return services. |
'complete' | Shows the "Complete Booking" button (finalContinueText ). Use this for one-way journeys where only an outbound service selection is needed. |
Example usage:
// For a one-way journey where return selection is not needed
<DepartureSelection
uiConfig={{
...otherConfig,
outboundButtonMode: "complete", // Shows "Complete Booking" button
finalContinueText: "Complete Booking"
}}
// ... other props
/>
// For a return journey where both selections are required
<DepartureSelection
uiConfig={{
...otherConfig,
outboundButtonMode: "continue", // Shows "Continue to Return Journey" button
continueToReturnText: "Continue to Return Journey"
}}
// ... other props
/>
Service Data Structure
Services should follow this structure:
interface Service {
service_id: number;
can_accept: string;
resource_name: string;
route_name: string;
departing_from: string;
travelling_to: string;
departure_time: string;
arrival_time: string;
departure_date: string;
total_cost: number;
pats: Pat[];
flags: Flag[];
}
Styling
The component uses Tailwind CSS classes by default. You can override styles by:
- Using the provided class names
- Using CSS modules (coming soon)
- Using styled-components (coming soon)
Browser Support
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
Utility Functions
The component exports several utility functions for working with dates and services:
Date Utilities
import { getDefaultDate, generateDateRange, shiftDates } from 'jetsetgo_departure-selection';
// Get the default date from services
const defaultDate = getDefaultDate(services);
// Generate a range of dates (default: 1 day before and after)
const dateRange = generateDateRange('2024-01-15', 2);
// Shift dates forward or backward
const newDates = shiftDates(currentDates, 'forward');
Service Utilities
import { sortServices, filterServices } from 'jetsetgo_departure-selection';
// Sort services by departure time or price
const sortedServices = sortServices(services, 'departure');
// Filter services based on availability
const availableServices = filterServices(services, false);
Type Exports
The package exports TypeScript types for all major interfaces:
import type {
Service,
Pat,
Flag,
DepartureSelectionProps,
UIConfig,
DateSelectEvent,
ServiceSelectEvent,
ContextChangeEvent,
DepartureSelections
} from 'jetsetgo_departure-selection';
// Example usage
const config: UIConfig = {
continueToReturnText: "Next",
finalContinueText: "Complete",
backToOutboundText: "Back",
backText: "Previous",
outboundTitle: "Select Departure",
returnTitle: "Select Return",
pageTitle: "Travel Selection",
pageSubtitle: "Choose your journey",
outboundButtonMode: "continue" // 'continue' | 'complete'
};
State Communication Overview The component communicates state changes through three main callback props:
onContextChange: Tracks the current view context and selections onServiceSelect: Reports individual service selections onDateSelect: Reports date selection changes Tracking Context State The onContextChange callback is triggered in these scenarios:
interface ContextChangeEvent { previousContext: 'outbound' | 'return'; nextContext: 'outbound' | 'return'; selectedOutboundId: number | null; selectedReturnId: number | null; } Triggered when:
Component mounts (initial load) User switches between outbound/return views Service selections change Example usage:
function ParentApp() { const handleContextChange = (event: ContextChangeEvent) => { console.log('Current view:', event.nextContext); console.log('Selected outbound:', event.selectedOutboundId); console.log('Selected return:', event.selectedReturnId); };
return ( <DepartureSelection onContextChange={handleContextChange} // ... other props /> ); } Tracking Service Selections The onServiceSelect callback provides detailed service information:
interface ServiceSelectEvent { serviceId: number; context: 'outbound' | 'return'; service: Service; // Full service object } Triggered when:
User selects an outbound service User selects a return service Example:
function ParentApp() { const handleServiceSelect = (event: ServiceSelectEvent) => { console.log('Service selected in context:', event.context); console.log('Selected service:', event.service); };
return ( <DepartureSelection onServiceSelect={handleServiceSelect} // ... other props /> ); } Tracking Date Changes The onDateSelect callback reports date changes:
interface DateSelectEvent { date: string; previousDate: string; context: 'outbound' | 'return'; } Triggered when:
User selects a new date User navigates dates using arrows Example:
function ParentApp() { const handleDateSelect = (event: DateSelectEvent) => { console.log('New date:', event.date); console.log('Previous date:', event.previousDate); console.log('Current context:', event.context); };
return ( <DepartureSelection onDateSelect={handleDateSelect} // ... other props /> ); } Complete Parent App Example Here's a complete example showing how to track all state changes:
function ParentApp() { const currentContext, setCurrentContext = useState<'outbound' | 'return'>('outbound'); const selections, setSelections = useState({ outboundId: null as number | null, returnId: null as number | null, currentDate: null as string | null });
const handleContextChange = (event: ContextChangeEvent) => { setCurrentContext(event.nextContext); setSelections(prev => ({ ...prev, outboundId: event.selectedOutboundId, returnId: event.selectedReturnId })); };
const handleServiceSelect = (event: ServiceSelectEvent) => { setSelections(prev => ({ ...prev,
[event.context === 'outbound' ? 'outboundId' : 'returnId']: event.serviceId
}));
};
const handleDateSelect = (event: DateSelectEvent) => { setSelections(prev => ({ ...prev, currentDate: event.date })); };
return ( <DepartureSelection onContextChange={handleContextChange} onServiceSelect={handleServiceSelect} onDateSelect={handleDateSelect} currentContext={currentContext} selectedOutboundId={selections.outboundId} selectedReturnId={selections.returnId} // ... other required props /> ); } This setup provides complete visibility into the component's internal state and user interactions.