import type { Args, Meta, StoryObj } from '@storybook/vue3-vite' import type { Flight } from '@/constants/tripDetails/Flight' import CpAccordion from '@/components/CpAccordion.vue' import CpTrip from '@/components/CpTrip.vue' import CpTripDetails from '@/components/CpTripDetails.vue' const directTrip = { arrivalDate: { formatted: 'Tue, 10 Jun 2026', raw: '2026-06-10', }, arrivalTime: '11:40', labels: { nonStop: 'Direct', }, dayCountAfterDeparture: 0, departureDate: { formatted: 'Tue, 10 Jun 2026', raw: '2026-06-10', }, departureTime: '09:00', destination: { iataCode: 'JFK', name: 'John F. Kennedy International Airport', }, flightDuration: { formatted: '8h 40m', raw: 'PT8H40M', }, airlines: [ { iataCode: 'AF', name: 'Air France', }, ], origin: { iataCode: 'CDG', name: 'Paris Charles de Gaulle Airport', }, } const sampleTrip = { arrivalDate: { formatted: 'Wed, 11 Jun 2026', raw: '2026-06-11', }, arrivalTime: '14:25', dayCountAfterDeparture: 1, departureDate: { formatted: 'Tue, 10 Jun 2026', raw: '2026-06-10', }, labels: { stopCount: '1 stop', layover: 'layover', transfer: 'airport change', }, hasSelfTransfer: true, stops: [ { duration: '2h 10m', iata: 'MUC', name: 'Munich', selfTransferLabel: 'Self-transfer from Franz Josef Strauss (MUC) to Augsburg - Muehlhausen (AGB)', }, ], departureTime: '08:15', destination: { iataCode: 'JFK', name: 'John F. Kennedy International Airport', }, flightDuration: { formatted: '12h 35m', raw: 'PT12H35M', }, airlines: [ { iataCode: 'AF', name: 'Air France', }, { iataCode: 'KL', name: 'KLM', }, ], origin: { iataCode: 'CDG', name: 'Paris Charles de Gaulle Airport', }, } const oneStopTrip = { arrivalDate: { formatted: 'Tue, 10 Jun 2026', raw: '2026-06-10', }, arrivalTime: '11:40', labels: { stopCount: '1 stop', layover: 'layover', }, dayCountAfterDeparture: 0, departureDate: { formatted: 'Tue, 10 Jun 2026', raw: '2026-06-10', }, departureTime: '09:00', destination: { iataCode: 'JFK', name: 'John F. Kennedy International Airport', }, flightDuration: { formatted: '8h 40m', raw: 'PT8H40M', }, airlines: [ { iataCode: 'AF', name: 'Air France', }, ], stops: [ { duration: '4h 35m', iata: 'AMS', name: 'Amsterdam', }, ], origin: { iataCode: 'CDG', name: 'Paris Charles de Gaulle Airport', }, } const twoStopTrip = { arrivalDate: { formatted: 'Thu, 12 Jun 2026', raw: '2026-06-12', }, labels: { stopCount: '2 stops', layover: 'layover', transfer: 'airport change', }, arrivalTime: '18:50', dayCountAfterDeparture: 2, departureDate: { formatted: 'Tue, 10 Jun 2026', raw: '2026-06-10', }, departureTime: '06:30', destination: { iataCode: 'LAX', name: 'Los Angeles International Airport', }, flightDuration: { formatted: '22h 20m', raw: 'PT22H20M', }, hasSelfTransfer: true, origin: { iataCode: 'CDG', name: 'Paris Charles de Gaulle Airport', }, stops: [ { duration: '4h 35m', iata: 'AMS', name: 'Amsterdam', }, { duration: '2h 10m', iata: 'MUC', name: 'Munich', selfTransferLabel: 'Self-transfer from Franz Josef Strauss (MUC) to Augsburg - Muehlhausen (AGB)', }, ], airlines: [ { iataCode: 'AF', name: 'Air France', }, { iataCode: 'KL', name: 'KLM', }, { iataCode: 'DL', name: 'Delta Airlines', }, ], } const fourStopTrip = { ...twoStopTrip, labels: { ...twoStopTrip.labels, stopCount: '4 stops', }, stops: [ ...twoStopTrip.stops, { duration: '4h 35m', iata: 'AMS', name: 'Amsterdam', }, { duration: '4h 35m', iata: 'AMS', name: 'Amsterdam', }, ], airlines: [ ...twoStopTrip.airlines, { iataCode: '1L', name: 'CitizenPlane', }, { iataCode: 'UA', name: 'United Airlines', }, ], } /** * Summary and segments below describe the same one-stop CDG → MUC → JFK * itinerary: 09:00 Paris → 10:40 Munich, 2h 10m layover, 12:50 Munich → * 16:15 New York, for 13h 15m of total travel time on 10 Jun 2026. */ const fullTrip = { arrivalDate: { formatted: 'Wed, 10 Jun 2026', raw: '2026-06-10', }, arrivalTime: '16:15', dayCountAfterDeparture: 0, departureDate: { formatted: 'Wed, 10 Jun 2026', raw: '2026-06-10', }, departureTime: '09:00', destination: { iataCode: 'JFK', name: 'John F. Kennedy International Airport', }, flightDuration: { formatted: '13h 15m', raw: 'PT13H15M', }, labels: { layover: 'layover', stopCount: '1 stop', }, airlines: [ { iataCode: 'AF', name: 'Air France', }, { iataCode: 'LH', name: 'Lufthansa', }, ], origin: { iataCode: 'CDG', name: 'Paris Charles de Gaulle Airport', }, stops: [ { duration: '2h 10m', iata: 'MUC', name: 'Munich', }, ], } /** Segment dates are GMT — `CpTripDetails` converts them to each airport's timezone. */ const fullTripFlight: Flight = { segments: [ { airline: { iata_code: 'AF', name: 'Air France' }, arrival_date: '2026-06-10T08:40:00', departure_date: '2026-06-10T07:00:00', destination: { city_name: 'Munich', iata_code: 'MUC', name: 'Franz Josef Strauss Airport', timezone: 'Europe/Berlin', }, destination_terminal: { label: 'Terminal 2', terminal_code: '2' }, flight_number: 'AF1122', origin: { city_name: 'Paris', iata_code: 'CDG', name: 'Paris Charles de Gaulle Airport', timezone: 'Europe/Paris', }, origin_terminal: { label: 'Terminal 2E', terminal_code: '2E' }, }, { airline: { iata_code: 'LH', name: 'Lufthansa' }, arrival_date: '2026-06-10T20:15:00', departure_date: '2026-06-10T10:50:00', destination: { city_name: 'New York', iata_code: 'JFK', name: 'John F. Kennedy International Airport', timezone: 'America/New_York', }, destination_terminal: { label: 'Terminal 1', terminal_code: '1' }, flight_number: 'LH410', origin: { city_name: 'Munich', iata_code: 'MUC', name: 'Franz Josef Strauss Airport', timezone: 'Europe/Berlin', }, origin_terminal: { label: 'Terminal 2', terminal_code: '2' }, }, ], } const meta = { title: 'Business/CpTrip', component: CpTrip, argTypes: { trip: { control: 'object', description: 'Flight trip data displayed in the row', }, hideAirlines: { control: 'boolean', description: 'Hides airline logos in the timeline', }, }, } satisfies Meta export default meta type Story = StoryObj const defaultRender = (args: Args) => ({ components: { CpTrip }, setup() { return { args } }, template: `
`, }) /** * Default trip row with all route fields populated. Use the controls to * experiment with each prop in isolation. */ export const Default: Story = { args: { trip: sampleTrip, }, render: defaultRender, } /** * Direct flight with no stops. The timeline shows "Direct" and a single * flight number in the footer. */ export const DirectFlight: Story = { args: { trip: directTrip, }, render: defaultRender, } /** * One-stop itinerary with a single layover. The timeline shows "1 stop" with * the stop IATA and a single flight number in the footer. */ export const OneStop: Story = { args: { trip: oneStopTrip, }, render: defaultRender, } /** * Multi-stop itinerary with two layovers. The timeline shows "2 stop" with * both stop IATAs and three flight numbers in the footer. */ export const TwoStops: Story = { args: { trip: twoStopTrip, }, render: defaultRender, } /** * Multi-stop itinerary with four layovers. The timeline shows "4 stops" with * all stop IATAs and flight numbers in the footer. */ export const FourStops: Story = { args: { trip: fourStopTrip, }, render: defaultRender, } /** * Trip row with airline logos hidden in the timeline. Useful when airline * branding is shown elsewhere on the page. */ export const HideAirlines: Story = { args: { trip: twoStopTrip, hideAirlines: true, }, render: defaultRender, } /** * A complete itinerary: `CpTrip` acts as the always-visible summary in the * accordion header, and `CpTripDetails` renders the leg-by-leg breakdown in * the expandable content. The rounded corners come from the wrapper, since * `CpAccordion` is square by design so it can be stacked in a group. */ export const FullTrip: Story = { args: { trip: fullTrip, }, render: (args: Args) => ({ components: { CpAccordion, CpTrip, CpTripDetails }, setup() { const wrapperStyle = [ 'box-sizing: border-box', 'width: 640px', 'max-width: 100%', 'overflow: hidden', 'border: 1px solid var(--cp-border-soft)', 'border-radius: var(--cp-radius-xl)', 'box-shadow: var(--cp-shadows-xs)', ].join('; ') const summaryStyle = 'padding-top: var(--cp-spacing-md);' const detailsStyle = 'border-top: 1px solid var(--cp-border-soft);' return { args, detailsStyle, fullTripFlight, summaryStyle, wrapperStyle } }, template: `
`, }), }