1.0.9 • Published 5 months ago

jetsetgo_departure-selection v1.0.9

Weekly downloads
-
License
MIT
Repository
-
Last release
5 months ago

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

PropTypeDescription
initialData{ departure: Service[]; return: Service[]; }The outbound and return services to display
onComplete(data: CompletionData) => voidCallback when selection is complete
uiConfigUIConfigConfiguration for UI text and labels

Optional Props

PropTypeDescription
selectedOutboundIdnumber \| nullPre-selected outbound service ID
selectedReturnIdnumber \| nullPre-selected return service ID
currentContext'outbound' \| 'return'Initial selection context
onDateSelect(event: DateSelectEvent) => voidDate selection callback
onServiceSelect(event: ServiceSelectEvent) => voidService selection callback
onContextChange(event: ContextChangeEvent) => voidContext change callback
onBack() => voidCallback for back button click
isLoadingbooleanWhether 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:

OptionDescription
'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:

  1. Using the provided class names
  2. Using CSS modules (coming soon)
  3. 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.

1.0.9

5 months ago

1.0.8

5 months ago

1.0.7

5 months ago

1.0.6

5 months ago

1.0.5

5 months ago

1.0.4

6 months ago

1.0.3

6 months ago

1.0.2

6 months ago

1.0.1

6 months ago

1.0.0

6 months ago