@autobusal/routes-order 1.11.1 → 1.12.0
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.
- package/CHANGELOG.md +13 -0
- package/Checkout/Sidebar/styles.ts +11 -3
- package/Found/Found.tsx +9 -2
- package/RoutesOrder.tsx +38 -14
- package/Step5/Coupons/Coupons.tsx +23 -17
- package/Step5/Coupons/styles.ts +40 -14
- package/Step5/Payments/Payments.tsx +2 -2
- package/Step5/Payments/styles.ts +15 -9
- package/Step5/Summary/About/About.tsx +12 -6
- package/Step5/Summary/About/styles.ts +12 -5
- package/package.json +1 -1
- package/styles.ts +66 -2
- package/Found/Booking/Booking.tsx +0 -61
- package/Found/Booking/styles.ts +0 -50
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,19 @@ All notable changes to this package are documented here. This project follows
|
|
|
6
6
|
> Note: 1.8.0 was published without a changelog entry. The gap is left as-is
|
|
7
7
|
> rather than reconstructed after the fact.
|
|
8
8
|
|
|
9
|
+
## [1.12.0] - 2026-08-01
|
|
10
|
+
|
|
11
|
+
### Removed
|
|
12
|
+
|
|
13
|
+
- **The Booking.com hotel banner.** It was unmounted from `Found` earlier on
|
|
14
|
+
2026-08-01 for sitting between the date strip and the trips - advertising a
|
|
15
|
+
hotel to somebody still deciding whether they can travel at all - and the
|
|
16
|
+
component is now deleted rather than kept dormant. It was never an
|
|
17
|
+
integration, only a hand-built `searchresults.xu.html` link built from
|
|
18
|
+
`city.booking_id`; leaving the component, the column and its admin field in
|
|
19
|
+
place made it look like one. The `routes_order.step2.booking` strings and
|
|
20
|
+
the `s-ec.bstatic.com` CSP entry go with it.
|
|
21
|
+
|
|
9
22
|
## [1.11.1] - 2026-08-01
|
|
10
23
|
|
|
11
24
|
### Changed
|
|
@@ -11,10 +11,18 @@ import styled from 'styled-components';
|
|
|
11
11
|
export const Container = styled.aside`
|
|
12
12
|
min-width: 0;
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Edited: Ferjolt Ozuni - Date: 2026-08-01
|
|
16
|
+
*
|
|
17
|
+
* 5/12 of the row rather than a fixed 340px, so on a wide screen the
|
|
18
|
+
* summary grows with the page instead of staying a narrow strip beside a
|
|
19
|
+
* very wide form. A proportion also keeps the payment options on one
|
|
20
|
+
* line at every width above the breakpoint.
|
|
21
|
+
*
|
|
22
|
+
* 1280 matches Results in ../../styles.ts, where the row layout starts.
|
|
23
|
+
*/
|
|
16
24
|
@media (min-width: 1280px) {
|
|
17
|
-
flex: 0 0
|
|
25
|
+
flex: 0 0 calc((5 / 12) * 100% - 20px);
|
|
18
26
|
position: sticky;
|
|
19
27
|
top: 15px;
|
|
20
28
|
max-height: calc(100vh - 30px);
|
package/Found/Found.tsx
CHANGED
|
@@ -2,7 +2,6 @@ import { useMemo, useState } from 'react';
|
|
|
2
2
|
import { useSearchParams } from 'react-router-dom';
|
|
3
3
|
import { TFunction } from 'i18next';
|
|
4
4
|
import Dates from './Dates/Dates';
|
|
5
|
-
import Booking from './Booking/Booking';
|
|
6
5
|
import Header from './Header/Header';
|
|
7
6
|
import Orders from './Orders/Orders';
|
|
8
7
|
import Sort from './Sort/Sort';
|
|
@@ -90,7 +89,15 @@ const Found = ({ type, loading, data, step1, preferredStop, t, onSearch, onSave
|
|
|
90
89
|
<>
|
|
91
90
|
<Dates type={ type } step1={ step1 } t={ t } onSearch={ onSearch } />
|
|
92
91
|
|
|
93
|
-
{
|
|
92
|
+
{ /* Edited: Ferjolt Ozuni - Date: 2026-08-01
|
|
93
|
+
The Booking.com banner used to sit here, between the date strip
|
|
94
|
+
and the trips, pushing the actual results below it to advertise a
|
|
95
|
+
hotel to somebody still deciding whether they can travel at all.
|
|
96
|
+
It was unmounted earlier today and the component is now removed
|
|
97
|
+
outright, along with cities.booking_id - it was never really an
|
|
98
|
+
integration, just a hand-built searchresults.xu.html link, and
|
|
99
|
+
leaving the column and its admin field behind made it look like
|
|
100
|
+
one. */ }
|
|
94
101
|
|
|
95
102
|
{ showControls && (
|
|
96
103
|
<>
|
package/RoutesOrder.tsx
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import { useParams } from 'react-router-dom';
|
|
3
3
|
import { TFunction } from 'i18next';
|
|
4
|
+
import { AiOutlineSearch, AiOutlineCaretDown, AiOutlineCaretUp } from 'react-icons/ai';
|
|
4
5
|
import { Meta } from '@autobusal/common';
|
|
6
|
+
import { useSystemTheme } from '@autobusal/hooks';
|
|
7
|
+
import { useGetSettings } from '@autobusal/providers/services';
|
|
5
8
|
import RoutesSearch from '@autobusal/routes-search';
|
|
6
9
|
import { useGetSuggestions } from '@autobusal/routes-search/services';
|
|
7
|
-
import { SearchAgain } from './styles';
|
|
10
|
+
import { SearchAgain, SearchInner, SearchToggle, SearchSummary, SearchPanel } from './styles';
|
|
8
11
|
import Step1 from './Step1/Step1';
|
|
9
12
|
import Step2 from './Step2';
|
|
10
13
|
import Step3 from './Step3';
|
|
@@ -31,6 +34,12 @@ const RoutesOrder = ({ t }: Props): JSX.Element => {
|
|
|
31
34
|
// search form (no :from/:to params yet) or if a slug doesn't resolve.
|
|
32
35
|
const { data: cities } = useGetSuggestions();
|
|
33
36
|
|
|
37
|
+
const { data: settings } = useGetSettings();
|
|
38
|
+
|
|
39
|
+
// same artwork the homepage hero uses, so the results page reads as the
|
|
40
|
+
// same site rather than a bare form on a white band
|
|
41
|
+
const mode = useSystemTheme();
|
|
42
|
+
|
|
34
43
|
const fromCity = cities.find(city => city.slug === params.from);
|
|
35
44
|
const toCity = cities.find(city => city.slug === params.to);
|
|
36
45
|
|
|
@@ -42,6 +51,7 @@ const RoutesOrder = ({ t }: Props): JSX.Element => {
|
|
|
42
51
|
? t('routes_order.meta.description_pair', { from: fromCity.name, to: toCity.name })
|
|
43
52
|
: t('routes_order.meta.description');
|
|
44
53
|
|
|
54
|
+
const [ searchOpen, setSearchOpen ] = useState<boolean>(false);
|
|
45
55
|
const [ redirected, setRedirected ] = useState<boolean>(false);
|
|
46
56
|
const [ current, setCurrent ] = useState<number>(1);
|
|
47
57
|
const [ step1, setStep1 ] = useState<RoutesSearchForm | undefined>(undefined);
|
|
@@ -114,19 +124,33 @@ const RoutesOrder = ({ t }: Props): JSX.Element => {
|
|
|
114
124
|
return (
|
|
115
125
|
<Meta title={ title } description={ description }>
|
|
116
126
|
{ showSearchAgain && step1 !== undefined && (
|
|
117
|
-
<SearchAgain
|
|
118
|
-
<
|
|
119
|
-
type={
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
<SearchAgain $background={ `${ settings.url }/home/background-${ mode }.jpg` }>
|
|
128
|
+
<SearchInner>
|
|
129
|
+
<SearchToggle type="button" aria-expanded={ searchOpen } onClick={ () => setSearchOpen(!searchOpen) }>
|
|
130
|
+
<AiOutlineSearch />
|
|
131
|
+
|
|
132
|
+
<SearchSummary>
|
|
133
|
+
{ fromCity?.name ?? step1.from } → { toCity?.name ?? step1.to } · { step1.departure }
|
|
134
|
+
</SearchSummary>
|
|
135
|
+
|
|
136
|
+
{ searchOpen ? <AiOutlineCaretUp /> : <AiOutlineCaretDown /> }
|
|
137
|
+
</SearchToggle>
|
|
138
|
+
|
|
139
|
+
<SearchPanel $open={ searchOpen }>
|
|
140
|
+
<RoutesSearch
|
|
141
|
+
type={ step1.type ?? 'departure' }
|
|
142
|
+
from={ `${ step1.from }${ step1.stop_from ? `:${ step1.stop_from }` : '' }` }
|
|
143
|
+
to={ `${ step1.to }${ step1.stop_to ? `:${ step1.stop_to }` : '' }` }
|
|
144
|
+
departure={ step1.departure }
|
|
145
|
+
_return={ step1._return ?? '' }
|
|
146
|
+
adultsNo={ step1.adults }
|
|
147
|
+
childrenNo={ step1.children }
|
|
148
|
+
babiesNo={ step1.babies }
|
|
149
|
+
t={ t }
|
|
150
|
+
onSearch={ data => { setSearchOpen(false); onStep1(data); } }
|
|
151
|
+
/>
|
|
152
|
+
</SearchPanel>
|
|
153
|
+
</SearchInner>
|
|
130
154
|
</SearchAgain>
|
|
131
155
|
) }
|
|
132
156
|
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { TFunction } from 'i18next';
|
|
2
2
|
import { useForm } from 'react-hook-form';
|
|
3
3
|
import { RiCoupon3Line } from 'react-icons/ri';
|
|
4
|
-
import { Button } from '@autobusal/common';
|
|
5
4
|
import { Validate, Display } from '@autobusal/utilities';
|
|
6
|
-
import {
|
|
7
|
-
import { Container, ContainerForm, ContainerInput } from './styles';
|
|
5
|
+
import { Container, ContainerForm, ContainerInput, Apply } from './styles';
|
|
8
6
|
import { CouponData } from '@autobusal/providers/types/orders';
|
|
9
7
|
import { FoundData } from '@autobusal/providers/types/routes';
|
|
10
8
|
import { TotalData, CouponForm } from '../../types';
|
|
@@ -46,27 +44,35 @@ const Coupons = ({ current, step2, t, total, onApplied }: Props): (JSX.Element |
|
|
|
46
44
|
return null;
|
|
47
45
|
}
|
|
48
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Edited: Ferjolt Ozuni - Date: 2026-08-01
|
|
49
|
+
*
|
|
50
|
+
* One field with the apply control tucked inside it. In the checkout
|
|
51
|
+
* sidebar this used to be a card of its own, with a heading and a
|
|
52
|
+
* full-width button, taking more room than the totals it exists to
|
|
53
|
+
* change - for something most buyers do not have.
|
|
54
|
+
*
|
|
55
|
+
* The button keeps its accessible name via aria-label and title, so
|
|
56
|
+
* dropping the visible text costs a screen reader nothing.
|
|
57
|
+
*/
|
|
49
58
|
return (
|
|
50
|
-
<Container
|
|
51
|
-
<SubTitle>
|
|
52
|
-
<RiCoupon3Line />
|
|
53
|
-
{ t('routes_order.step5.coupon.title') }
|
|
54
|
-
</SubTitle>
|
|
55
|
-
|
|
59
|
+
<Container>
|
|
56
60
|
<form onSubmit={ handleSubmit(onSubmit) }>
|
|
57
61
|
<ContainerForm>
|
|
58
62
|
<ContainerInput>
|
|
59
63
|
<input type="text" placeholder={ t('routes_order.step5.coupon.code') } { ...register('code', Validate('required|min_length:4', t)) } />
|
|
60
|
-
|
|
64
|
+
|
|
65
|
+
<Apply
|
|
66
|
+
type="submit"
|
|
67
|
+
disabled={ isPending }
|
|
68
|
+
aria-label={ t('routes_order.step5.coupon.apply') }
|
|
69
|
+
title={ t('routes_order.step5.coupon.apply') }
|
|
70
|
+
>
|
|
71
|
+
<RiCoupon3Line />
|
|
72
|
+
</Apply>
|
|
61
73
|
</ContainerInput>
|
|
62
74
|
|
|
63
|
-
|
|
64
|
-
type="submit"
|
|
65
|
-
loading={ isPending }
|
|
66
|
-
text={ t('routes_order.step5.coupon.apply') }
|
|
67
|
-
noMargin
|
|
68
|
-
maxWidth={ 175 }
|
|
69
|
-
/>
|
|
75
|
+
{ Display(errors.code) }
|
|
70
76
|
</ContainerForm>
|
|
71
77
|
</form>
|
|
72
78
|
</Container>
|
package/Step5/Coupons/styles.ts
CHANGED
|
@@ -4,24 +4,50 @@ export const Container = styled.div`
|
|
|
4
4
|
margin-top: 0;
|
|
5
5
|
`;
|
|
6
6
|
|
|
7
|
+
export const ContainerForm = styled.div`
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
gap: 4px;
|
|
11
|
+
`;
|
|
12
|
+
|
|
7
13
|
/**
|
|
8
14
|
* Edited: Ferjolt Ozuni - Date: 2026-08-01
|
|
9
15
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* in a column too narrow for either - the input came out 86px wide.
|
|
14
|
-
*
|
|
15
|
-
* flex-basis plus wrap is container-responsive without container queries:
|
|
16
|
-
* they sit side by side where there is room and stack where there is not.
|
|
16
|
+
* The apply control sits INSIDE the field rather than beside it. Beside it
|
|
17
|
+
* in a narrow sidebar, the two either fought for width or stacked into two
|
|
18
|
+
* full-width rows for something most buyers never use.
|
|
17
19
|
*/
|
|
18
|
-
export const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
export const ContainerInput = styled.div`
|
|
21
|
+
position: relative;
|
|
22
|
+
|
|
23
|
+
& > input {
|
|
24
|
+
width: 100%;
|
|
25
|
+
padding-right: 42px;
|
|
26
|
+
}
|
|
23
27
|
`;
|
|
24
28
|
|
|
25
|
-
export const
|
|
26
|
-
|
|
29
|
+
export const Apply = styled.button`
|
|
30
|
+
position: absolute;
|
|
31
|
+
top: 50%;
|
|
32
|
+
right: 6px;
|
|
33
|
+
transform: translateY(-50%);
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
justify-content: center;
|
|
37
|
+
width: 30px;
|
|
38
|
+
height: 30px;
|
|
39
|
+
font-size: ${ props => props.theme.size.l };
|
|
40
|
+
color: ${ props => props.theme.primary.contrast };
|
|
41
|
+
background: ${ props => props.theme.primary.normal };
|
|
42
|
+
border-radius: ${ props => props.theme.borderRadius };
|
|
43
|
+
transition: all 0.2s ease;
|
|
44
|
+
|
|
45
|
+
&:hover {
|
|
46
|
+
opacity: .85;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
&:disabled {
|
|
50
|
+
opacity: .5;
|
|
51
|
+
cursor: default;
|
|
52
|
+
}
|
|
27
53
|
`;
|
|
@@ -31,7 +31,7 @@ const Payments = ({ selected, isPartner, t, onChange }: Props): JSX.Element => {
|
|
|
31
31
|
|
|
32
32
|
if (isLoading) {
|
|
33
33
|
return (
|
|
34
|
-
<Container
|
|
34
|
+
<Container>
|
|
35
35
|
<Inline text={ t('routes_order.step5.actions.loading') } />
|
|
36
36
|
</Container>
|
|
37
37
|
);
|
|
@@ -39,7 +39,7 @@ const Payments = ({ selected, isPartner, t, onChange }: Props): JSX.Element => {
|
|
|
39
39
|
|
|
40
40
|
return (
|
|
41
41
|
<>
|
|
42
|
-
<Container
|
|
42
|
+
<Container>
|
|
43
43
|
<SubTitle>
|
|
44
44
|
<RiSecurePaymentLine />
|
|
45
45
|
{ t('routes_order.step5.actions.title') }
|
package/Step5/Payments/styles.ts
CHANGED
|
@@ -4,11 +4,17 @@ export const Container = styled.div`
|
|
|
4
4
|
margin-top: 0;
|
|
5
5
|
`;
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Edited: Ferjolt Ozuni - Date: 2026-08-01
|
|
9
|
+
*
|
|
10
|
+
* All options on one row inside the checkout sidebar. They are a choice of
|
|
11
|
+
* three short labels, not three products, so stacking them into tall cards
|
|
12
|
+
* gave a minor decision more visual weight than the total above it.
|
|
13
|
+
*/
|
|
7
14
|
export const ContainerPayments = styled.div`
|
|
8
15
|
display: flex;
|
|
9
16
|
flex-direction: row;
|
|
10
|
-
|
|
11
|
-
gap: 10px;
|
|
17
|
+
gap: 6px;
|
|
12
18
|
justify-content: center;
|
|
13
19
|
`;
|
|
14
20
|
|
|
@@ -17,12 +23,12 @@ export const ButtonPayment = styled.button<{ $selected: boolean }>`
|
|
|
17
23
|
flex-direction: column;
|
|
18
24
|
align-items: center;
|
|
19
25
|
justify-content: center;
|
|
20
|
-
gap:
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
padding:
|
|
26
|
+
gap: 4px;
|
|
27
|
+
// equal shares of one row, never wrapping - min-width:0 lets them shrink
|
|
28
|
+
// below their label width instead of pushing each other onto new lines
|
|
29
|
+
flex: 1 1 0;
|
|
30
|
+
min-width: 0;
|
|
31
|
+
padding: 8px 4px;
|
|
26
32
|
text-align: center;
|
|
27
33
|
font-weight: 700;
|
|
28
34
|
border: 1px solid ${ props => props.theme.background.neutral };
|
|
@@ -30,7 +36,7 @@ export const ButtonPayment = styled.button<{ $selected: boolean }>`
|
|
|
30
36
|
transition: all 0.3s ease;
|
|
31
37
|
|
|
32
38
|
& > svg {
|
|
33
|
-
font-size:
|
|
39
|
+
font-size: 22px;
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
&:hover {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TFunction } from 'i18next';
|
|
2
2
|
import { FaArrowLeft, FaArrowRight } from 'react-icons/fa';
|
|
3
3
|
import { FaCircleDot, FaLocationDot } from 'react-icons/fa6';
|
|
4
|
-
import { Container, ContainerIcon, Icon, Information, Title, Operator, Logo, Leg, Point, PointIcon, PointCity, Country, PointMeta } from './styles';
|
|
4
|
+
import { Container, ContainerIcon, Icon, Information, Title, Operator, Carrier, Logo, Leg, Point, PointIcon, PointCity, Country, PointMeta } from './styles';
|
|
5
5
|
import { RoutesSearchForm } from '@autobusal/routes-search/types';
|
|
6
6
|
import { FoundData } from '@autobusal/providers/types/routes';
|
|
7
7
|
|
|
@@ -44,9 +44,17 @@ const About = ({ type, step1, data, t }: Props): (JSX.Element | null) => {
|
|
|
44
44
|
<Information>
|
|
45
45
|
<Title>{ data.name } ({ data.code })</Title>
|
|
46
46
|
|
|
47
|
-
{
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
{ /* Edited: Ferjolt Ozuni - Date: 2026-08-01
|
|
48
|
+
Mark and name together, ranged right. The name used to sit
|
|
49
|
+
alone at the foot of the block, so the logo read as decoration
|
|
50
|
+
rather than as the operator being identified. */ }
|
|
51
|
+
<Carrier>
|
|
52
|
+
{ data.operator.logo_url && (
|
|
53
|
+
<Logo src={ data.operator.logo_url } alt={ data.operator.company } />
|
|
54
|
+
) }
|
|
55
|
+
|
|
56
|
+
<Operator>{ data.operator.company }</Operator>
|
|
57
|
+
</Carrier>
|
|
50
58
|
|
|
51
59
|
<Leg>
|
|
52
60
|
<Point>
|
|
@@ -88,8 +96,6 @@ const About = ({ type, step1, data, t }: Props): (JSX.Element | null) => {
|
|
|
88
96
|
</PointMeta>
|
|
89
97
|
</Point>
|
|
90
98
|
</Leg>
|
|
91
|
-
|
|
92
|
-
<Operator>{ data.operator.company }</Operator>
|
|
93
99
|
</Information>
|
|
94
100
|
</Container>
|
|
95
101
|
);
|
|
@@ -114,17 +114,24 @@ export const PointMeta = styled.div<{ $strong?: boolean }>`
|
|
|
114
114
|
// Edited: Ferjolt Ozuni - Date: 2026-08-01
|
|
115
115
|
// The operator's mark under the route name - who the buyer is actually
|
|
116
116
|
// travelling with, which a company name alone conveys less quickly.
|
|
117
|
+
export const Carrier = styled.div`
|
|
118
|
+
display: flex;
|
|
119
|
+
align-items: center;
|
|
120
|
+
justify-content: flex-end;
|
|
121
|
+
gap: 8px;
|
|
122
|
+
margin: 6px 0;
|
|
123
|
+
`;
|
|
124
|
+
|
|
117
125
|
export const Logo = styled.img`
|
|
118
126
|
display: block;
|
|
119
|
-
max-width:
|
|
120
|
-
max-height:
|
|
127
|
+
max-width: 90px;
|
|
128
|
+
max-height: 30px;
|
|
121
129
|
object-fit: contain;
|
|
122
|
-
object-position: left;
|
|
123
|
-
margin: 6px 0;
|
|
124
130
|
`;
|
|
125
131
|
|
|
126
132
|
export const Operator = styled.div`
|
|
127
|
-
padding-left: 10px;
|
|
128
133
|
font-size: calc(${ props => props.theme.size.s } - 1px);
|
|
134
|
+
font-weight: 700;
|
|
135
|
+
text-align: right;
|
|
129
136
|
color: ${ props => props.theme.font.faded };
|
|
130
137
|
`;
|
package/package.json
CHANGED
package/styles.ts
CHANGED
|
@@ -32,8 +32,72 @@ export const Actions = styled.div`
|
|
|
32
32
|
* your place. The form stays available above the results so an adjustment
|
|
33
33
|
* is one edit rather than a round trip.
|
|
34
34
|
*/
|
|
35
|
-
export const SearchAgain = styled.div
|
|
36
|
-
|
|
35
|
+
export const SearchAgain = styled.div<{ $background: string }>`
|
|
36
|
+
position: relative;
|
|
37
|
+
margin: -35px calc(50% - 50vw) 20px;
|
|
38
|
+
padding: 18px calc(50vw - 50%);
|
|
39
|
+
background-image: url('${ props => props.$background }');
|
|
40
|
+
background-size: cover;
|
|
41
|
+
background-repeat: no-repeat;
|
|
42
|
+
background-position: center;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Same scrim as the homepage hero, for the same reason: the artwork a
|
|
46
|
+
* brand supplies may be light or dark, and the controls on top of it must
|
|
47
|
+
* read either way.
|
|
48
|
+
*/
|
|
49
|
+
&::before {
|
|
50
|
+
content: '';
|
|
51
|
+
position: absolute;
|
|
52
|
+
inset: 0;
|
|
53
|
+
pointer-events: none;
|
|
54
|
+
background: rgba(0, 0, 0, .45);
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
export const SearchInner = styled.div`
|
|
59
|
+
position: relative;
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
export const SearchToggle = styled.button`
|
|
63
|
+
display: flex;
|
|
64
|
+
align-items: center;
|
|
65
|
+
gap: 8px;
|
|
66
|
+
padding: 6px 14px;
|
|
67
|
+
font-size: ${ props => props.theme.size.s };
|
|
68
|
+
font-weight: 700;
|
|
69
|
+
color: #FFF;
|
|
70
|
+
border: 1px solid rgba(255, 255, 255, .45);
|
|
71
|
+
border-radius: 100px;
|
|
72
|
+
transition: all 0.2s ease;
|
|
73
|
+
|
|
74
|
+
&:hover {
|
|
75
|
+
border-color: #FFF;
|
|
76
|
+
background: rgba(255, 255, 255, .12);
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
export const SearchSummary = styled.span`
|
|
81
|
+
overflow: hidden;
|
|
82
|
+
text-overflow: ellipsis;
|
|
83
|
+
white-space: nowrap;
|
|
84
|
+
`;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Edited: Ferjolt Ozuni - Date: 2026-08-01
|
|
88
|
+
*
|
|
89
|
+
* Collapsed by default: the form is there for the minority who want to
|
|
90
|
+
* change something, and left open it pushed the results themselves below
|
|
91
|
+
* the fold on the page whose whole job is showing results.
|
|
92
|
+
*/
|
|
93
|
+
export const SearchPanel = styled.div<{ $open: boolean }>`
|
|
94
|
+
display: ${ props => props.$open ? 'block' : 'none' };
|
|
95
|
+
margin-top: 14px;
|
|
96
|
+
padding: 20px;
|
|
97
|
+
background: ${ props => props.theme.header.transparent };
|
|
98
|
+
backdrop-filter: blur(25px);
|
|
99
|
+
-webkit-backdrop-filter: blur(25px);
|
|
100
|
+
border-radius: ${ props => props.theme.borderRadius };
|
|
37
101
|
`;
|
|
38
102
|
|
|
39
103
|
/**
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { TFunction } from 'i18next';
|
|
2
|
-
import { createDate } from '@autobusal/utilities';
|
|
3
|
-
import { Container, About, LinkBooking } from './styles';
|
|
4
|
-
import { RoutesSearchForm } from '@autobusal/routes-search/types';
|
|
5
|
-
import { FoundData } from '@autobusal/providers/types/routes';
|
|
6
|
-
import { useGetSettings } from '@autobusal/providers/services';
|
|
7
|
-
|
|
8
|
-
interface Props {
|
|
9
|
-
data?: FoundData[]
|
|
10
|
-
step1: RoutesSearchForm
|
|
11
|
-
t: TFunction<'public'>
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const Booking = ({ data, step1, t }: Props): (JSX.Element | null) => {
|
|
15
|
-
const { data: settings } = useGetSettings();
|
|
16
|
-
|
|
17
|
-
if (data === undefined || data?.length === 0) {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const destination = data[0].locations.to;
|
|
22
|
-
|
|
23
|
-
// stop if no booking city
|
|
24
|
-
if (destination.city.booking_id === undefined) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// stop if no booking id
|
|
29
|
-
if (settings.preferences.bookingID === undefined) {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const from = createDate(step1.departure);
|
|
34
|
-
const to = getReturnDate(step1._return, from);
|
|
35
|
-
|
|
36
|
-
const location = `https://www.booking.com/searchresults.xu.html?city=${ destination.city.booking_id }&aid=&checkin_monthday=${ from.getDate() }&checkin_month=${ from.getMonth() + 1 }&checkin_year=${ from.getFullYear() }&checkout_monthday=${ to.getDate() }&checkout_month=${ to.getMonth() + 1 }&checkout_year=${ to.getFullYear() }&no_rooms=1&group_adults=2&room1=A%2CA`;
|
|
37
|
-
|
|
38
|
-
return (
|
|
39
|
-
<Container>
|
|
40
|
-
<About>
|
|
41
|
-
{ t('routes_order.step2.booking.title', { location: destination.city.name }) }
|
|
42
|
-
<img src="https://s-ec.bstatic.com/static/img/b26logo/booking_logo_retina/22615963add19ac6b6d715a97c8d477e8b95b7ea.png" alt="Booking.com" />
|
|
43
|
-
</About>
|
|
44
|
-
|
|
45
|
-
<LinkBooking to={ location } target="_blank" rel="noopener noreferrer">{ t('routes_order.step2.booking.search') }</LinkBooking>
|
|
46
|
-
</Container>
|
|
47
|
-
);
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const getReturnDate = (_return: (string | undefined), from: Date): Date => {
|
|
51
|
-
if (_return !== undefined) {
|
|
52
|
-
return createDate(_return);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const to = new Date(from);
|
|
56
|
-
to.setDate(to.getDate() + 2);
|
|
57
|
-
|
|
58
|
-
return to;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export default Booking;
|
package/Found/Booking/styles.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { Link } from 'react-router-dom';
|
|
2
|
-
import styled from 'styled-components';
|
|
3
|
-
|
|
4
|
-
export const Container = styled.div`
|
|
5
|
-
display: flex;
|
|
6
|
-
flex-direction: column;
|
|
7
|
-
gap: 15px;
|
|
8
|
-
margin-bottom: 15px;
|
|
9
|
-
padding: 15px;
|
|
10
|
-
background: #003580;
|
|
11
|
-
border-radius: ${ props => props.theme.borderRadius };
|
|
12
|
-
|
|
13
|
-
@media (min-width: 480px) {
|
|
14
|
-
flex-direction: row;
|
|
15
|
-
align-items: center;
|
|
16
|
-
}
|
|
17
|
-
`;
|
|
18
|
-
|
|
19
|
-
export const About = styled.div`
|
|
20
|
-
color: #FFFFFF;
|
|
21
|
-
font-weight: 700;
|
|
22
|
-
|
|
23
|
-
> img {
|
|
24
|
-
display: block;
|
|
25
|
-
width: 110px;
|
|
26
|
-
margin-top: 6px;
|
|
27
|
-
}
|
|
28
|
-
`;
|
|
29
|
-
|
|
30
|
-
export const LinkBooking = styled(Link)`
|
|
31
|
-
display: inline-block;
|
|
32
|
-
max-width: 150px;
|
|
33
|
-
padding: 6px 0;
|
|
34
|
-
text-align: center;
|
|
35
|
-
font-weight: 700;
|
|
36
|
-
color: #333333;
|
|
37
|
-
background: #FFFFFF;
|
|
38
|
-
border-radius: 100px;
|
|
39
|
-
transition: all 0.3s ease;
|
|
40
|
-
|
|
41
|
-
&:hover {
|
|
42
|
-
text-decoration: none;
|
|
43
|
-
box-shadow: 0 4px 24px 0 rgba(255, 255, 255, 0.5);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
@media (min-width: 480px) {
|
|
47
|
-
flex-basis: 150px;
|
|
48
|
-
margin-left: auto;
|
|
49
|
-
}
|
|
50
|
-
`;
|