@odynn/awayz-flights 0.9.3 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/package.json +3 -4
  2. package/lib/components/BookingOption/BookingOption.tsx +0 -240
  3. package/lib/components/BookingOption/_styles.booking-option.scss +0 -69
  4. package/lib/components/FlightItinerary/FlightItinerary.tsx +0 -290
  5. package/lib/components/FlightItinerary/_styles.flight-itinerary.scss +0 -102
  6. package/lib/components/FlightResult/FlightResult.stories.tsx +0 -495
  7. package/lib/components/FlightResult/FlightResult.tsx +0 -612
  8. package/lib/components/FlightResult/_styles.flight-result.scss +0 -408
  9. package/lib/components/FlightResult/mocks/DefaultFlightData.args.json +0 -645
  10. package/lib/components/FlightResult/mocks/NoCabinClassFlightData.args.json +0 -310
  11. package/lib/components/FlightResult/mocks/index.ts +0 -4
  12. package/lib/components/HorizontalScroller/HorizontalScroller.tsx +0 -118
  13. package/lib/components/HorizontalScroller/_styles.horizontal-scroller.scss +0 -79
  14. package/lib/components/index.ts +0 -2
  15. package/lib/constants/endpoints.ts +0 -13
  16. package/lib/enums/EPaymentType.ts +0 -4
  17. package/lib/enums/index.ts +0 -1
  18. package/lib/hooks/index.ts +0 -10
  19. package/lib/hooks/useAirportSearch/useAirportSearch.ts +0 -24
  20. package/lib/hooks/useAirportSearch/useAirportSearch.types.ts +0 -12
  21. package/lib/hooks/useFlightSearch/useFlightSearch.ts +0 -488
  22. package/lib/hooks/useFlightSearch/useFlightSearch.types.ts +0 -254
  23. package/lib/main.ts +0 -51
  24. package/lib/services/flights/FlightsService.ts +0 -171
  25. package/lib/services/flights/FlightsService.types.ts +0 -244
  26. package/lib/services/wallet/WalletService.ts +0 -60
  27. package/lib/services/wallet/WalletService.types.ts +0 -159
  28. package/lib/stores/useFlightStore.ts +0 -35
  29. package/lib/styles/_animations.scss +0 -366
  30. package/lib/styles/_colours.scss +0 -20
  31. package/lib/styles/_fonts.scss +0 -18
  32. package/lib/styles/_layout.scss +0 -88
  33. package/lib/styles/_mixins.scss +0 -34
  34. package/lib/styles/_shared.scss +0 -116
  35. package/lib/styles/index.scss +0 -4
  36. package/lib/types/ECabinClass.ts +0 -46
  37. package/lib/types/enums.ts +0 -10
  38. package/lib/types/index.ts +0 -1
  39. package/lib/utils/flightDateUtils.ts +0 -20
  40. package/lib/utils/flightUtils.ts +0 -563
  41. package/lib/utils/index.ts +0 -1
package/package.json CHANGED
@@ -1,12 +1,11 @@
1
1
  {
2
2
  "name": "@odynn/awayz-flights",
3
- "version": "0.9.3",
3
+ "version": "0.9.4",
4
4
  "type": "module",
5
5
  "files": [
6
- "dist",
7
- "lib"
6
+ "dist"
8
7
  ],
9
- "main": "lib/main.ts",
8
+ "main": "dist/main.js",
10
9
  "types": "./dist/lib/main.d.ts",
11
10
  "scripts": {
12
11
  "test": "echo \"NOTE: Please run tests from the root\"",
@@ -1,240 +0,0 @@
1
- import {
2
- CashValue,
3
- ClientPointsValue,
4
- EAmountsDisplayFeature,
5
- EInvalidAmountDisplayOption,
6
- EToolTipPosition,
7
- useFeatureFlags
8
- } from '@odynn/awayz-core';
9
- import { useQuery } from '@tanstack/react-query';
10
- import {
11
- commaSeparatedNumber,
12
- DEFAULT_CURRENCY,
13
- ISegment,
14
- ISlice,
15
- pluralise,
16
- toTitleCase
17
- } from '@type-op/shared';
18
- import React, { useState } from 'react';
19
- import { FaTimes } from 'react-icons/fa';
20
- import {
21
- FaCheck,
22
- FaChevronDown,
23
- FaChevronUp,
24
- FaSuitcase
25
- } from 'react-icons/fa6';
26
- import { EPaymentType } from '../../enums';
27
- import {
28
- EBaggage,
29
- IFlightPaymentOptionBaggage,
30
- IOfferConditions
31
- } from '../../hooks/useFlightSearch/useFlightSearch.types';
32
- import { formatCabinClass, getAirlineProgram } from '../../utils/flightUtils';
33
- import './_styles.booking-option.scss';
34
-
35
- interface IBookingOptionProps {
36
- type: EPaymentType;
37
- title: string;
38
- cashValue?: {
39
- amount: number;
40
- currency: string;
41
- };
42
- milesValue?: number;
43
- cashFee?: {
44
- amount: number;
45
- currency: string;
46
- };
47
- setSelected: () => void;
48
- conditions?: IOfferConditions;
49
- slices?: ISlice[];
50
- baggages?: IFlightPaymentOptionBaggage[];
51
- program?: string;
52
- }
53
-
54
- const BookingOption = ({
55
- type,
56
- title,
57
- cashValue,
58
- milesValue,
59
- cashFee,
60
- setSelected,
61
- conditions,
62
- slices,
63
- baggages,
64
- program
65
- }: IBookingOptionProps) => {
66
- const { featureFlags } = useFeatureFlags();
67
- const [showMoreOptions, setShowMoreOptions] = useState(false);
68
-
69
- const refundable = conditions?.refundBeforeDeparture;
70
-
71
- const changeable = conditions?.changeBeforeDeparture;
72
-
73
- const additionalOptionsCount =
74
- (refundable?.allowed ? 1 : 0) +
75
- (changeable?.allowed ? 1 : 0) +
76
- (slices?.[0]?.segments?.length || 0);
77
-
78
- const { data: programDetails } = useQuery({
79
- queryKey: ['airlineProgram', program],
80
- queryFn: () => getAirlineProgram(program!),
81
- enabled: !!program
82
- });
83
-
84
- return (
85
- <div
86
- className={`flight-booking-option`}
87
- onClick={(e) => e.stopPropagation()}
88
- >
89
- <div className='rate-details'>
90
- {type === EPaymentType.POINTS && (
91
- <img src={programDetails?.programLogo} />
92
- )}
93
- <p className='fare-brand-name'>{toTitleCase(title)}</p>
94
- </div>
95
- {type === EPaymentType.CASH && (
96
- <div className={`fare-comparison-content`}>
97
- {baggages?.map((item, idx: number) => (
98
- <React.Fragment key={idx}>
99
- {item.type === EBaggage.CHECKED && (
100
- <div className='fare-option checked'>
101
- <p>Checked bags:</p>
102
- <p>{item.quantity}</p>
103
- </div>
104
- )}
105
- {item.type === EBaggage.CARRY_ON && (
106
- <div className='fare-option'>
107
- <p>Carry-on bags:</p>
108
- <p>{item.quantity}</p>
109
- </div>
110
- )}
111
- {item.type === null && (
112
- <div className='fare-option'>
113
- <FaSuitcase />
114
- <p>N/A</p>
115
- </div>
116
- )}
117
- </React.Fragment>
118
- ))}
119
- <div className='fare-option refundable'>
120
- Refundable:{' '}
121
- {refundable?.allowed ? (
122
- <FaCheck className='yes' />
123
- ) : (
124
- <FaTimes className='no' />
125
- )}
126
- </div>
127
- <div className='fare-option changeable'>
128
- Changeable:{' '}
129
- {changeable?.allowed ? (
130
- <FaCheck className='yes' />
131
- ) : (
132
- <FaTimes className='no' />
133
- )}
134
- </div>
135
- <div
136
- className='fare-option show-more-toggle'
137
- onClick={(e) => {
138
- e.stopPropagation();
139
- setShowMoreOptions(!showMoreOptions);
140
- }}
141
- >
142
- <p>
143
- {showMoreOptions
144
- ? `Hide ${pluralise(additionalOptionsCount, 'option')}`
145
- : `+${additionalOptionsCount} ${pluralise(additionalOptionsCount, 'option')}`}
146
- </p>
147
- <p>{showMoreOptions ? <FaChevronUp /> : <FaChevronDown />}</p>
148
- </div>
149
- {showMoreOptions && (
150
- <>
151
- {refundable?.allowed && (
152
- <div className='fare-option'>
153
- Refundable Penalty:{' '}
154
- <CashValue
155
- amount={refundable.penaltyAmount || 0}
156
- currency={refundable.penaltyCurrency || ''}
157
- zeroDisplayOption={
158
- EInvalidAmountDisplayOption.DISPLAY_AS_ZERO_WITH_CURRENCY
159
- }
160
- position={EToolTipPosition.LEFT}
161
- />
162
- </div>
163
- )}
164
- {changeable?.allowed && (
165
- <div className='fare-option'>
166
- Changeable Penalty:{' '}
167
- <CashValue
168
- amount={changeable.penaltyAmount || 0}
169
- currency={changeable.penaltyCurrency || ''}
170
- zeroDisplayOption={
171
- EInvalidAmountDisplayOption.DISPLAY_AS_ZERO_WITH_CURRENCY
172
- }
173
- position={EToolTipPosition.LEFT}
174
- />
175
- </div>
176
- )}
177
- {slices?.[0].segments?.map((segment: ISegment) => (
178
- <div
179
- className='fare-option checked'
180
- key={`${segment.origin.iataCode}-${segment.destination.iataCode}`}
181
- >
182
- <p>
183
- {segment.origin.iataCode} - {segment.destination.iataCode}:
184
- </p>
185
- <div>
186
- <img src={segment.operatingCarrier.logo} />
187
- <p>{formatCabinClass(segment.passengers[0].cabinClass)}</p>
188
- </div>
189
- </div>
190
- ))}
191
- </>
192
- )}
193
- </div>
194
- )}
195
- <button
196
- onClick={(e) => {
197
- e.stopPropagation();
198
- setSelected();
199
- }}
200
- >
201
- {type === EPaymentType.CASH && !!cashValue ? (
202
- <span>
203
- <CashValue
204
- amount={cashValue.amount}
205
- currency={cashValue.currency}
206
- position={EToolTipPosition.LEFT}
207
- zeroDisplayOption={
208
- EInvalidAmountDisplayOption.DISPLAY_AS_ZERO_WITH_CURRENCY
209
- }
210
- decimalPlaces={2}
211
- />
212
- {featureFlags.amountsDisplayFeature ===
213
- EAmountsDisplayFeature.CASH_AND_CLIENT_POINTS && (
214
- <>
215
- {' / '}
216
- <ClientPointsValue
217
- currency={cashValue.currency}
218
- cashAmount={cashValue.amount}
219
- />
220
- </>
221
- )}
222
- </span>
223
- ) : (
224
- <>
225
- {`${commaSeparatedNumber(milesValue)} miles + `}
226
- <CashValue
227
- amount={cashFee?.amount || 0}
228
- currency={cashFee?.currency || DEFAULT_CURRENCY}
229
- position={EToolTipPosition.LEFT}
230
- customDisplay='No fee'
231
- zeroDisplayOption={EInvalidAmountDisplayOption.CUSTOM}
232
- />
233
- </>
234
- )}
235
- </button>
236
- </div>
237
- );
238
- };
239
-
240
- export default BookingOption;
@@ -1,69 +0,0 @@
1
- .flight-booking-option {
2
- display: flex;
3
- flex-direction: column;
4
- border: $border;
5
- border-radius: $border-radius;
6
- cursor: default;
7
- user-select: none;
8
- transition: all 0.3s ease;
9
- padding: $padding;
10
- min-width: 270px;
11
- gap: $padding;
12
- height: fit-content;
13
-
14
- .fare-option {
15
- display: flex;
16
- justify-content: space-between;
17
- border-bottom: $border;
18
- padding: $padding-sm 0;
19
-
20
- &.show-more-toggle {
21
- cursor: pointer;
22
-
23
- &:hover {
24
- background-color: colour.$background;
25
- }
26
- }
27
-
28
- .yes {
29
- color: colour.$primary;
30
- }
31
-
32
- .no {
33
- color: colour.$danger;
34
- }
35
-
36
- > div {
37
- display: flex;
38
- gap: $padding-sm;
39
-
40
- img {
41
- height: 20px;
42
- }
43
- }
44
- }
45
-
46
- .rate-details {
47
- > img {
48
- height: 30px;
49
- max-width: 70%;
50
- }
51
-
52
- .fare-brand-name {
53
- font-weight: font.$heavy;
54
- }
55
- }
56
-
57
- button {
58
- border-radius: $border-radius-pill;
59
- padding: $padding-sm $padding;
60
- border: none;
61
- background-color: colour.$primary;
62
- color: colour.$foreground;
63
- font-weight: font.$heavy;
64
- cursor: pointer;
65
- display: flex;
66
- align-items: center;
67
- justify-content: center;
68
- }
69
- }
@@ -1,290 +0,0 @@
1
- import {
2
- calculateTimeDifference,
3
- getCountryNameByCode,
4
- getTimeString,
5
- getTimezoneOffsetOrCode,
6
- ISegment
7
- } from '@type-op/shared';
8
- import {
9
- DUFFEL_ASSET_PATH,
10
- EDateFormats,
11
- EDateKeys
12
- } from '@type-op/shared/constants';
13
- import moment from 'moment';
14
- import React, { useMemo } from 'react';
15
- import { useTranslation } from 'react-i18next';
16
- import { FaCalendar, FaClock } from 'react-icons/fa';
17
- import { IAirportLocation } from '../../hooks/useFlightSearch/useFlightSearch.types';
18
- import './_styles.flight-itinerary.scss';
19
-
20
- interface FlightItineraryProps {
21
- origin: IAirportLocation;
22
- destination: IAirportLocation;
23
- itinerary: ISegment[];
24
- onDisableRenderItinerary?: () => void;
25
- isMultiDayFlight?: boolean;
26
- }
27
-
28
- const FlightItinerary = ({
29
- itinerary,
30
- origin,
31
- destination,
32
- onDisableRenderItinerary,
33
- isMultiDayFlight
34
- }: FlightItineraryProps) => {
35
- const getCabinClass = () => {
36
- const passenger = itinerary[0]?.passengers?.[0];
37
- return passenger?.cabinClassMarketingName ?? passenger?.cabinClass;
38
- };
39
- const layovers = useMemo(() => {
40
- const layovers = [];
41
-
42
- for (let i = 0; i < itinerary.length; i++) {
43
- const segment = itinerary[i];
44
- const nextSegment = itinerary[i + 1];
45
-
46
- if (!nextSegment) {
47
- break;
48
- }
49
- const layover = {
50
- arrival: {
51
- airport: segment.destination?.name,
52
- city:
53
- segment.destination?.cityName ?? segment.destination?.city?.name,
54
- country: getCountryNameByCode(segment.destination?.iataCountryCode),
55
- time: segment.arrivingAt,
56
- arrivalTimeZone: segment.destination.timeZone
57
- },
58
- departure: {
59
- airport: nextSegment.origin?.name,
60
- city: nextSegment.origin?.cityName ?? nextSegment.origin?.city?.name,
61
- country: getCountryNameByCode(segment.destination?.iataCountryCode),
62
- time: nextSegment.departingAt,
63
- airline: nextSegment.operatingCarrier.name,
64
- airlineCode: nextSegment.operatingCarrier.iataCode,
65
- aircraft: nextSegment.aircraft?.name,
66
- flightNumber: nextSegment.operatingCarrierFlightNumber,
67
- cabin:
68
- nextSegment.passengers[0].cabinClassMarketingName ??
69
- nextSegment.passengers[0].cabinClass ??
70
- getCabinClass(),
71
- departureTimeZone: nextSegment.origin.timeZone
72
- },
73
- layoverTime: calculateTimeDifference({
74
- // we need to swap around the departure and arrival times
75
- departureAt: segment.arrivingAt,
76
- departureTimeZone: segment.destination.timeZone,
77
- arrivalAt: nextSegment.departingAt,
78
- arrivalTimeZone: nextSegment.origin.timeZone
79
- })
80
- };
81
- // TODO: get the ASAPI to give us what
82
- // we need to calculate the layover time
83
- if (layover.layoverTime <= 0) {
84
- // if we can't calculate the layover time, we'll just
85
- onDisableRenderItinerary?.();
86
- }
87
- layovers.push(layover);
88
- }
89
-
90
- return layovers;
91
- }, [itinerary]);
92
-
93
- return (
94
- <div className='flight-itinerary'>
95
- <Departure
96
- airport={itinerary[0].origin.name}
97
- city={origin.city}
98
- country={getCountryNameByCode(origin.country)}
99
- departureAt={itinerary[0].departingAt}
100
- departureTimeZone={itinerary[0].origin.timeZone}
101
- arrivalAt={itinerary[0].arrivingAt}
102
- arrivalTimeZone={itinerary[0].destination.timeZone}
103
- airline={itinerary[0].operatingCarrier.name}
104
- airlineCode={itinerary[0].operatingCarrier.iataCode}
105
- aircraft={itinerary[0].aircraft?.name}
106
- flightNumber={itinerary[0].operatingCarrierFlightNumber}
107
- isMultiDayFlight={isMultiDayFlight}
108
- />
109
- {layovers.map((layover, index) => (
110
- <React.Fragment key={layover.departure.flightNumber || index}>
111
- <Arrival
112
- airport={layover.arrival.airport}
113
- city={layover.arrival.city}
114
- country={layover.arrival.country}
115
- time={layover.arrival.time}
116
- isLayover
117
- layoverTime={getTimeString(layover.layoverTime)}
118
- arrivalTimeZone={getTimezoneOffsetOrCode(
119
- layover.arrival.arrivalTimeZone,
120
- layover.arrival.time
121
- )}
122
- />
123
- <Departure
124
- airport={layover.departure?.airport}
125
- city={layover.departure?.city}
126
- country={layover.departure?.country}
127
- departureAt={layover.departure.time}
128
- departureTimeZone={layover.departure.departureTimeZone}
129
- // we need to get the next segment's arrival time when
130
- // it's a layover
131
- arrivalAt={itinerary[index + 1].arrivingAt}
132
- // we need to get the next segment's arrival timezone when
133
- // it's a layover
134
- arrivalTimeZone={itinerary[index + 1].destination.timeZone}
135
- airline={layover.departure?.airline}
136
- airlineCode={layover.departure?.airlineCode}
137
- aircraft={layover.departure?.aircraft}
138
- flightNumber={layover.departure?.flightNumber}
139
- />
140
- </React.Fragment>
141
- ))}
142
- <Arrival
143
- airport={itinerary.getLast()?.destination?.name}
144
- city={destination.city}
145
- country={getCountryNameByCode(destination.country)}
146
- time={itinerary.getLast()?.arrivingAt}
147
- arrivalTimeZone={getTimezoneOffsetOrCode(
148
- itinerary.getLast()?.destination.timeZone,
149
- itinerary.getLast()?.arrivingAt
150
- )}
151
- isMultiDayFlight={isMultiDayFlight}
152
- />
153
- </div>
154
- );
155
- };
156
-
157
- interface IDepartureProps {
158
- airport?: string;
159
- city?: string;
160
- country?: string;
161
- airline?: string;
162
- airlineCode?: string;
163
- aircraft?: string;
164
- flightNumber?: string;
165
- testTimezone?: string;
166
- departureAt: string;
167
- departureTimeZone: string;
168
- arrivalAt: string;
169
- arrivalTimeZone: string;
170
- isMultiDayFlight?: boolean;
171
- }
172
-
173
- const Departure = ({
174
- airport,
175
- city,
176
- country,
177
- airline,
178
- airlineCode,
179
- flightNumber,
180
- aircraft,
181
- departureAt,
182
- departureTimeZone,
183
- arrivalAt,
184
- arrivalTimeZone,
185
- isMultiDayFlight
186
- }: IDepartureProps) => {
187
- const { t } = useTranslation();
188
- const timeTravelled = calculateTimeDifference({
189
- departureAt,
190
- departureTimeZone,
191
- arrivalAt,
192
- arrivalTimeZone
193
- });
194
-
195
- return (
196
- <div className='departure'>
197
- <p>
198
- {moment(departureAt).format(
199
- t(EDateKeys.TIME_ONLY, EDateFormats.FlightTime)
200
- )}{' '}
201
- <span className='time-zone'>
202
- ({getTimezoneOffsetOrCode(departureTimeZone, departureAt)})
203
- </span>
204
- <br />
205
- {isMultiDayFlight ? (
206
- <div className='travel-time'>
207
- <FaCalendar />
208
- <span>
209
- Departure:{' '}
210
- {moment(departureAt).format(
211
- t(EDateKeys.DISPLAY_MEDIUM, EDateFormats.StringDateFormat)
212
- )}
213
- </span>
214
- </div>
215
- ) : (
216
- <></>
217
- )}
218
- <div className='travel-time'>
219
- <FaClock />
220
- <span>Travel Time: {getTimeString(timeTravelled)}</span>
221
- </div>
222
- </p>
223
- <div>
224
- <span>
225
- {airport}, {city}, {country}
226
- </span>
227
- <img src={`${DUFFEL_ASSET_PATH}${airlineCode}.svg`} alt={airline} />
228
- <p>
229
- <strong>Aircraft:</strong> {aircraft}
230
- </p>
231
- <p>
232
- <strong>Flight:</strong> {airlineCode} {flightNumber}
233
- </p>
234
- </div>
235
- </div>
236
- );
237
- };
238
-
239
- interface IArrivalProps {
240
- airport?: string;
241
- city?: string;
242
- country?: string;
243
- time?: string;
244
- isLayover?: boolean;
245
- layoverTime?: string;
246
- arrivalTimeZone?: string;
247
- isMultiDayFlight?: boolean;
248
- }
249
-
250
- const Arrival = ({
251
- airport,
252
- city,
253
- country,
254
- time,
255
- isLayover,
256
- layoverTime,
257
- arrivalTimeZone,
258
- isMultiDayFlight
259
- }: IArrivalProps) => {
260
- const { t } = useTranslation('dates');
261
- return (
262
- <div className={`arrival ${isLayover ? 'layover' : ''}`}>
263
- <p>
264
- {moment(time).format(t(EDateKeys.TIME_ONLY, EDateFormats.FlightTime))}{' '}
265
- <span className='time-zone'>({arrivalTimeZone})</span>
266
- {isMultiDayFlight && !isLayover ? (
267
- <div className='travel-time'>
268
- <FaCalendar />
269
- <span>
270
- Arrival:{' '}
271
- {moment(time).format(
272
- t(EDateKeys.DISPLAY_MEDIUM, EDateFormats.StringDateFormat)
273
- )}
274
- </span>
275
- </div>
276
- ) : (
277
- <></>
278
- )}
279
- </p>
280
- <div>
281
- <p>
282
- {isLayover ? 'Layover in' : ''} {airport}, {city}, {country}
283
- </p>
284
- <i>{isLayover ? layoverTime : ''}</i>
285
- </div>
286
- </div>
287
- );
288
- };
289
-
290
- export default FlightItinerary;
@@ -1,102 +0,0 @@
1
- .flight-itinerary {
2
- display: flex;
3
- flex-direction: column;
4
-
5
- .layover {
6
- position: relative;
7
- padding-bottom: calc($padding * 3);
8
-
9
- &::before {
10
- content: '';
11
- position: absolute;
12
- top: 0;
13
- bottom: 0;
14
- left: calc($padding / 2 + 1px);
15
- width: 3px;
16
- background: repeating-linear-gradient(0deg, #d8e3ea 0 5px, #0000 0 8px);
17
- z-index: 7;
18
- }
19
-
20
- > div {
21
- > p,
22
- > i {
23
- color: #a9b2b7;
24
- }
25
- }
26
- }
27
-
28
- .departure,
29
- .arrival {
30
- display: flex;
31
- gap: calc($padding * 2);
32
-
33
- > p {
34
- position: relative;
35
- margin-left: calc($padding * 2);
36
-
37
- &::before {
38
- content: '';
39
- border: 2px solid #d8e3ea;
40
- height: 1rem;
41
- width: 1rem;
42
- border-radius: 50%;
43
- position: absolute;
44
- left: calc($padding * -2);
45
- top: -0.2rem;
46
- z-index: 10;
47
- background-color: colour.$foreground;
48
- }
49
-
50
- .time-zone {
51
- color: colour.$text-light;
52
- font-size: font.$small;
53
- }
54
- .travel-time {
55
- color: colour.$text-light;
56
- font-size: font.$small;
57
- display: flex;
58
- flex-direction: row;
59
- margin-top: $padding;
60
- gap: calc($padding/4);
61
- align-items: center;
62
- }
63
- }
64
- }
65
-
66
- .departure {
67
- padding-bottom: calc($padding * 2);
68
- position: relative;
69
-
70
- &::before {
71
- content: '';
72
- position: absolute;
73
- top: 0;
74
- bottom: 0;
75
- left: calc($padding / 2 + 1px);
76
- width: 2px;
77
- background-color: #d8e3ea;
78
- z-index: 7;
79
- }
80
-
81
- div {
82
- flex: 1;
83
- display: flex;
84
- flex-direction: column;
85
- gap: 4px;
86
- position: relative;
87
- border-radius: $border-radius;
88
-
89
- span {
90
- font-weight: 500;
91
- }
92
- img {
93
- max-width: 150px;
94
- max-height: 25px;
95
- align-self: flex-start;
96
- background-color: colour.$light;
97
- padding: $padding-xs $padding-sm;
98
- border-radius: calc($border-radius / 2);
99
- }
100
- }
101
- }
102
- }