@autobusal/routes-order 1.0.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.
Files changed (50) hide show
  1. package/Found/Booking/Booking.tsx +61 -0
  2. package/Found/Booking/styles.ts +50 -0
  3. package/Found/Dates/Dates.tsx +82 -0
  4. package/Found/Dates/Loading.tsx +14 -0
  5. package/Found/Dates/styles.ts +50 -0
  6. package/Found/Found.tsx +31 -0
  7. package/Found/Header/Header.tsx +21 -0
  8. package/Found/Header/styles.ts +40 -0
  9. package/Found/Orders/Orders.tsx +40 -0
  10. package/Found/Orders/styles.ts +12 -0
  11. package/Found/Route/Route.tsx +112 -0
  12. package/Found/Route/styles.ts +187 -0
  13. package/RoutesOrder.tsx +130 -0
  14. package/Step1/Step1.tsx +63 -0
  15. package/Step1/prepare.ts +38 -0
  16. package/Step2.tsx +55 -0
  17. package/Step3.tsx +56 -0
  18. package/Step4/Loading.tsx +26 -0
  19. package/Step4/Passenger/Passenger.tsx +173 -0
  20. package/Step4/Passenger/styles.ts +10 -0
  21. package/Step4/Passengers/Display.tsx +50 -0
  22. package/Step4/Passengers/Passengers.tsx +96 -0
  23. package/Step4/Passengers/styles.ts +7 -0
  24. package/Step4/Step4.tsx +96 -0
  25. package/Step4/checkAge.ts +48 -0
  26. package/Step5/Billing/Billing.tsx +161 -0
  27. package/Step5/Billing/Loading.tsx +22 -0
  28. package/Step5/Billing/styles.ts +13 -0
  29. package/Step5/Coupons/Coupons.tsx +71 -0
  30. package/Step5/Coupons/styles.ts +20 -0
  31. package/Step5/Payments/Payments.tsx +82 -0
  32. package/Step5/Payments/styles.ts +42 -0
  33. package/Step5/Step5.tsx +131 -0
  34. package/Step5/Summary/About/About.tsx +48 -0
  35. package/Step5/Summary/About/styles.ts +49 -0
  36. package/Step5/Summary/Amount/Amount.tsx +24 -0
  37. package/Step5/Summary/Amount/styles.ts +29 -0
  38. package/Step5/Summary/Summary.tsx +44 -0
  39. package/Step5/Summary/Tickets/Preview.tsx +41 -0
  40. package/Step5/Summary/Tickets/Tickets.tsx +34 -0
  41. package/Step5/Summary/Tickets/styles.ts +36 -0
  42. package/Step5/Summary/styles.ts +11 -0
  43. package/Step5/utilities.ts +24 -0
  44. package/Steps/Steps.tsx +28 -0
  45. package/Steps/styles.ts +47 -0
  46. package/index.ts +3 -0
  47. package/package.json +6 -0
  48. package/services.ts +149 -0
  49. package/styles.ts +24 -0
  50. package/types.ts +32 -0
@@ -0,0 +1,71 @@
1
+ import { TFunction } from 'i18next';
2
+ import { useForm } from 'react-hook-form';
3
+ import { RiCoupon3Line } from 'react-icons/ri';
4
+ import { Button } from '@autobusal/common';
5
+ import { Validate, Display } from '@autobusal/utilities';
6
+ import { SubTitle } from '../../styles';
7
+ import { Container, ContainerForm, ContainerInput } from './styles';
8
+ import { CouponData } from '@autobusal/providers/types/orders';
9
+ import { FoundData } from '@autobusal/providers/types/routes';
10
+ import { TotalData, CouponForm } from '../../types';
11
+ import { GetCoupon } from '../../services';
12
+
13
+ interface Props {
14
+ current?: CouponData
15
+ step2: FoundData
16
+ total: TotalData
17
+ t: TFunction<'common'>
18
+ onApplied: (data: CouponData) => void
19
+ }
20
+
21
+ const Coupons = ({ current, step2, t, total, onApplied }: Props): (JSX.Element | null) => {
22
+ const { register, handleSubmit, formState: { errors } } = useForm<CouponForm>();
23
+
24
+ const { mutate: SearchCoupons, isPending } = GetCoupon(step2.id, total.value);
25
+
26
+ const onSubmit = (data: CouponForm): void => {
27
+ SearchCoupons(data, {
28
+ onSuccess: (data) => {
29
+ if (data.used) {
30
+ onApplied(data);
31
+
32
+ return;
33
+ }
34
+
35
+ alert(t('routes_order.step5.coupon.error'));
36
+ }
37
+ });
38
+ };
39
+
40
+ if (current) {
41
+ return null;
42
+ }
43
+
44
+ return (
45
+ <Container className="box">
46
+ <SubTitle>
47
+ <RiCoupon3Line />
48
+ { t('routes_order.step5.coupon.title') }
49
+ </SubTitle>
50
+
51
+ <form onSubmit={ handleSubmit(onSubmit) }>
52
+ <ContainerForm>
53
+ <ContainerInput>
54
+ <input type="text" placeholder={ t('routes_order.step5.coupon.code') } { ...register('code', Validate('required|min_length:4', t)) } />
55
+ { Display(errors.code) }
56
+ </ContainerInput>
57
+
58
+ <Button
59
+ type="submit"
60
+ loading={ isPending }
61
+ text={ t('routes_order.step5.coupon.apply') }
62
+ noMargin
63
+ maxWidth={ 175 }
64
+ />
65
+ </ContainerForm>
66
+ </form>
67
+ </Container>
68
+ );
69
+ };
70
+
71
+ export default Coupons;
@@ -0,0 +1,20 @@
1
+ import styled from 'styled-components';
2
+
3
+ export const Container = styled.div`
4
+ margin-top: 25px;
5
+ `;
6
+
7
+ export const ContainerForm = styled.div`
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: 15px;
11
+ max-width: 450px;
12
+
13
+ @media (min-width: 480px) {
14
+ flex-direction: row;
15
+ }
16
+ `;
17
+
18
+ export const ContainerInput = styled.div`
19
+ flex: 1;
20
+ `;
@@ -0,0 +1,82 @@
1
+ import { useEffect } from 'react';
2
+ import { TFunction } from 'i18next';
3
+ import { RiSecurePaymentLine } from 'react-icons/ri';
4
+ import { FaRegHandPaper, FaMoneyBill } from 'react-icons/fa';
5
+ import { AiOutlineCreditCard } from 'react-icons/ai';
6
+ import { Inline } from '@autobusal/common';
7
+ import { SubTitle } from '../../styles';
8
+ import { Container, ContainerPayments, ButtonPayment } from './styles';
9
+ import { GetPayments } from '../../services';
10
+
11
+ interface Props {
12
+ selected: string
13
+ isPartner: boolean
14
+ t: TFunction<'common'>
15
+ onChange: (action: string) => void
16
+ }
17
+
18
+ const Payments = ({ selected, isPartner, t, onChange }: Props): JSX.Element => {
19
+ const { data, isLoading, isSuccess } = GetPayments();
20
+
21
+ useEffect(() => {
22
+ if (isSuccess) {
23
+ const found = Object.keys(data).find(item => {
24
+ return data[item as keyof typeof data] === true;
25
+ });
26
+
27
+ // we assign the first available action
28
+ onChange(found ?? '');
29
+ }
30
+ }, [isSuccess, data, onChange]);
31
+
32
+ if (isLoading) {
33
+ return (
34
+ <Container className="box">
35
+ <Inline text={ t('routes_order.step5.actions.loading') } />
36
+ </Container>
37
+ );
38
+ }
39
+
40
+ return (
41
+ <>
42
+ <Container className="box">
43
+ <SubTitle>
44
+ <RiSecurePaymentLine />
45
+ { t('routes_order.step5.actions.title') }
46
+ </SubTitle>
47
+
48
+ <ContainerPayments>
49
+ { (data?.reserve && !isPartner) && (
50
+ <ButtonPayment type="button" $selected={ selected === 'reserve' } onClick={ () => onChange('reserve') }>
51
+ <FaRegHandPaper />
52
+ { t('routes_order.step5.actions.reserve') }
53
+ </ButtonPayment>
54
+ ) }
55
+
56
+ { (data?.funds && !isPartner) && (
57
+ <ButtonPayment type="button" $selected={ selected === 'funds' } onClick={ () => onChange('funds') }>
58
+ <FaMoneyBill />
59
+ { t('routes_order.step5.actions.funds') }
60
+ </ButtonPayment>
61
+ ) }
62
+
63
+ { (data?.funds && !isPartner) && (
64
+ <ButtonPayment type="button" $selected={ selected === 'cash' } onClick={ () => onChange('cash') }>
65
+ <FaMoneyBill />
66
+ { t('routes_order.step5.actions.cash') }
67
+ </ButtonPayment>
68
+ ) }
69
+
70
+ { (data?.bkt || isPartner) && (
71
+ <ButtonPayment type="button" $selected={ selected === 'bkt' } onClick={ () => onChange('bkt') }>
72
+ <AiOutlineCreditCard />
73
+ { t('routes_order.step5.actions.card') }
74
+ </ButtonPayment>
75
+ ) }
76
+ </ContainerPayments>
77
+ </Container>
78
+ </>
79
+ );
80
+ };
81
+
82
+ export default Payments;
@@ -0,0 +1,42 @@
1
+ import styled, { css } from 'styled-components';
2
+
3
+ export const Container = styled.div`
4
+ margin-top: 25px;
5
+ `;
6
+
7
+ export const ContainerPayments = styled.div`
8
+ display: flex;
9
+ flex-direction: row;
10
+ flex-wrap: wrap;
11
+ gap: 15px;
12
+ justify-content: center;
13
+ `;
14
+
15
+ export const ButtonPayment = styled.button<{ $selected: boolean }>`
16
+ display: flex;
17
+ flex-direction: column;
18
+ align-items: center;
19
+ justify-content: center;
20
+ gap: 10px;
21
+ flex-basis: 150px;
22
+ padding: 10px;
23
+ text-align: center;
24
+ font-weight: 700;
25
+ border: 1px solid ${ props => props.theme.background.neutral };
26
+ border-radius: ${ props => props.theme.borderRadius };
27
+ transition: all 0.3s ease;
28
+
29
+ & > svg {
30
+ font-size: 35px;
31
+ }
32
+
33
+ &:hover {
34
+ background-color: ${ props => props.theme.background.neutral };
35
+ }
36
+
37
+ ${ props => props.$selected && css`
38
+ color: ${ props => props.theme.primary.contrast } !important;
39
+ background-color: ${ props => props.theme.primary.normal } !important;
40
+ border-color: ${ props => props.theme.primary.normal };
41
+ ` }
42
+ `;
@@ -0,0 +1,131 @@
1
+ import { useState } from 'react';
2
+ import { TFunction } from 'i18next';
3
+ import { useForm } from 'react-hook-form';
4
+ import { useNavigate } from 'react-router-dom';
5
+ import { RiShoppingCartLine } from 'react-icons/ri';
6
+ import { AiOutlineCaretLeft } from 'react-icons/ai';
7
+ import { MdDone } from 'react-icons/md';
8
+ import { Button } from '@autobusal/common';
9
+ import Steps from '../Steps/Steps';
10
+ import Summary from './Summary/Summary';
11
+ import Coupons from './Coupons/Coupons';
12
+ import Billing from './Billing/Billing';
13
+ import Payments from './Payments/Payments';
14
+ import { getTotal, convertCoupon } from './utilities';
15
+ import { Title, Actions } from '../styles';
16
+ import { PersonData, BillingData } from '@autobusal/providers/types/persons';
17
+ import { FoundData } from '@autobusal/providers/types/routes';
18
+ import { CouponData } from '@autobusal/providers/types/orders';
19
+ import { RoutesSearchForm } from '@autobusal/routes-search/types';
20
+ import { TotalData } from '../types';
21
+ import { useUserStore } from '@autobusal/providers/stores/user';
22
+ import { PostOrder } from '../services';
23
+
24
+ interface Props {
25
+ step1: RoutesSearchForm
26
+ step2: FoundData
27
+ step3?: FoundData
28
+ step4: PersonData
29
+ isPartner?: boolean
30
+ t: TFunction<'common'>
31
+ onBack: (step: number) => void
32
+ }
33
+
34
+ const Step5 = ({ step1, step2, step3, step4, isPartner, t, onBack }: Props): JSX.Element => {
35
+ const [ coupon, setCoupon ] = useState<CouponData | undefined>(undefined);
36
+ const [ total, setTotal ] = useState<TotalData>(
37
+ getTotal(step2, step3)
38
+ );
39
+ const [ action, setAction ] = useState<string | undefined>(undefined);
40
+
41
+ const { data: UserData } = useUserStore();
42
+
43
+ const navigate = useNavigate();
44
+
45
+ const { register, handleSubmit, formState: { errors } } = useForm<BillingData>();
46
+
47
+ const { mutate: OrderSend, isPending } = PostOrder(step1, step2, step3, step4, coupon, action);
48
+
49
+ const onApplyCoupon = (data: CouponData): void => {
50
+ setTotal(
51
+ convertCoupon(data)
52
+ );
53
+
54
+ setCoupon(data);
55
+ };
56
+
57
+ const onAction = (action: string): void => {
58
+ setAction(action);
59
+ };
60
+
61
+ const onSendForm = async (): Promise<void> => {
62
+ await handleSubmit(onSubmit)();
63
+ };
64
+
65
+ const onSubmit = (data: BillingData): void => {
66
+ OrderSend(data, {
67
+ onSuccess: (data) => {
68
+ if (data === undefined) {
69
+ return;
70
+ }
71
+
72
+ const hash = step1.type === 'return' ? data.return : data.departure;
73
+
74
+ navigate(`/orders/process/${ action }/${ hash }`);
75
+ }
76
+ });
77
+ };
78
+
79
+ return (
80
+ <>
81
+ <Title>
82
+ <RiShoppingCartLine />
83
+ { t('routes_order.step5.title') }
84
+ </Title>
85
+
86
+ <Steps value={ 5 } />
87
+
88
+ <Summary
89
+ step1={ step1 }
90
+ step2={ step2 }
91
+ step3={ step3 }
92
+ step4={ step4 }
93
+ coupon={ coupon }
94
+ total={ total }
95
+ t={ t }
96
+ />
97
+
98
+ <Coupons current={ coupon } step2={ step2 } total={ total } t={ t } onApplied={ onApplyCoupon } />
99
+
100
+ { UserData === undefined && <Billing t={ t } errors={ errors } refs={ register } /> }
101
+
102
+ <Payments
103
+ selected={ action ?? '' }
104
+ isPartner={ isPartner ?? false }
105
+ t={ t }
106
+ onChange={ onAction }
107
+ />
108
+
109
+ <Actions>
110
+ <Button
111
+ type="button"
112
+ subtype="secondary"
113
+ text={ <><AiOutlineCaretLeft /> { t('routes_order.step5.back') }</> }
114
+ noMargin
115
+ onClick={ () => onBack(4) }
116
+ />
117
+
118
+ <Button
119
+ loading={isPending}
120
+ noMargin
121
+ text={ (
122
+ <><MdDone />{ t('routes_order.step5.next') }</>
123
+ ) }
124
+ onClick={ onSendForm }
125
+ />
126
+ </Actions>
127
+ </>
128
+ );
129
+ };
130
+
131
+ export default Step5;
@@ -0,0 +1,48 @@
1
+ import { TFunction } from 'i18next';
2
+ import { FaArrowLeft, FaArrowRight } from 'react-icons/fa';
3
+ import { Container, ContainerIcon, Icon, Information, Title, SubTitle, Operator } from './styles';
4
+ import { RoutesSearchForm } from '@autobusal/routes-search/types';
5
+ import { FoundData } from '@autobusal/providers/types/routes';
6
+
7
+ interface Props {
8
+ type: 'departure' | 'return'
9
+ step1: RoutesSearchForm
10
+ data?: FoundData
11
+ t: TFunction<'public'>
12
+ }
13
+
14
+ const About = ({ type, step1, data, t }: Props): (JSX.Element | null) => {
15
+ const icon = type === 'departure' ? <FaArrowRight /> : <FaArrowLeft />;
16
+
17
+ if (!data) {
18
+ return null;
19
+ }
20
+
21
+ const details = type === 'departure' ? {
22
+ date: step1.departure,
23
+ hour: data.locations.from.departure,
24
+ stop: data.locations.from.stop?.name,
25
+ city: data.locations.from.city.name
26
+ } : {
27
+ date: step1._return,
28
+ hour: data.locations.to.arrival,
29
+ stop: data.locations.to.stop?.name,
30
+ city: data.locations.to.city.name
31
+ };
32
+
33
+ return (
34
+ <Container>
35
+ <ContainerIcon>
36
+ <Icon>{ icon }</Icon>
37
+ </ContainerIcon>
38
+
39
+ <Information>
40
+ <Title>{ data.name } ({ data.code })</Title>
41
+ <SubTitle>{ t(`routes_order.step5.summary.${ type }`, details) }</SubTitle>
42
+ <Operator>{ data.operator.company }</Operator>
43
+ </Information>
44
+ </Container>
45
+ );
46
+ };
47
+
48
+ export default About;
@@ -0,0 +1,49 @@
1
+ import styled from 'styled-components';
2
+
3
+ export const Container = styled.div`
4
+ flex: 1;
5
+ display: flex;
6
+ `;
7
+
8
+ export const ContainerIcon = styled.div`
9
+ display: flex;
10
+ align-items: center;
11
+ flex-basis: 54px;
12
+ `;
13
+
14
+ export const Icon = styled.div`
15
+ display: flex;
16
+ align-items: center;
17
+ justify-content: center;
18
+ width: 40px;
19
+ height: 40px;
20
+ font-size: ${ props => props.theme.size.xxl };
21
+ color: ${ props => props.theme.primary.contrast };
22
+ background: ${ props => props.theme.primary.normal };
23
+ border-radius: 100px;
24
+ `;
25
+
26
+ export const Information = styled.div`
27
+ flex: 1;
28
+ `;
29
+
30
+ export const Title = styled.h2`
31
+ margin: 0;
32
+ font-size: ${ props => props.theme.size.l };
33
+ text-transform: uppercase;
34
+ `;
35
+
36
+ export const SubTitle = styled.div`
37
+ display: inline-block;
38
+ padding: 4px 10px;
39
+ margin: 6px 0;
40
+ font-size: calc(${ props => props.theme.size.s } - 1px);
41
+ background: ${ props => props.theme.background.neutral };
42
+ border-radius: ${ props => props.theme.borderRadius };
43
+ `;
44
+
45
+ export const Operator = styled.div`
46
+ padding-left: 10px;
47
+ font-size: calc(${ props => props.theme.size.s } - 1px);
48
+ color: ${ props => props.theme.font.faded };
49
+ `;
@@ -0,0 +1,24 @@
1
+ import { TFunction } from 'i18next';
2
+ import { Container, Value, Code } from './styles';
3
+
4
+ interface Props {
5
+ type: 'total' | 'discount'
6
+ value: string
7
+ code?: string
8
+ t: TFunction<'common'>
9
+ }
10
+
11
+ const Amount = ({ type, value, code, t }: Props): JSX.Element => (
12
+ <Container $type={ type }>
13
+ { t(`routes_order.step5.summary.${ type }`) }
14
+
15
+ { code && <Code>{ code }</Code> }
16
+
17
+ <Value>
18
+ { type === 'discount' && '-' }
19
+ { value }
20
+ </Value>
21
+ </Container>
22
+ );
23
+
24
+ export default Amount;
@@ -0,0 +1,29 @@
1
+ import styled, { css } from 'styled-components';
2
+
3
+ export const Container = styled.div<{ $type: 'total' | 'discount' }>`
4
+ display: flex;
5
+ align-items: center;
6
+ padding: 10px 15px;
7
+ margin-top: 15px;
8
+ text-transform: uppercase;
9
+ font-weight: 700;
10
+ background: ${ props => props.theme.background.neutral };
11
+ border-radius: ${ props => props.theme.borderRadius };
12
+
13
+ ${ props => props.$type === 'discount' && css`
14
+ color: ${ props => props.theme.font.error };
15
+ ` }
16
+ `;
17
+
18
+ export const Value = styled.strong`
19
+ margin-left: auto;
20
+ `;
21
+
22
+ export const Code = styled.span`
23
+ padding: 3px 5px;
24
+ margin-left: 6px;
25
+ color: #FFF;
26
+ font-size: ${ props => props.theme.size.xs };
27
+ background-color: ${ props => props.theme.font.error };
28
+ border-radius: ${ props => props.theme.borderRadius };
29
+ `;
@@ -0,0 +1,44 @@
1
+ import { TFunction } from 'i18next';
2
+ import { AiOutlineInfoCircle } from 'react-icons/ai';
3
+ import About from './About/About';
4
+ import Tickets from './Tickets/Tickets';
5
+ import Amount from './Amount/Amount';
6
+ import { SubTitle } from '../../styles';
7
+ import { Information } from './styles';
8
+ import { RoutesSearchForm } from '@autobusal/routes-search/types';
9
+ import { FoundData } from '@autobusal/providers/types/routes';
10
+ import { PersonData } from '@autobusal/providers/types/persons';
11
+ import { CouponData } from '@autobusal/providers/types/orders';
12
+ import { TotalData } from '../../types';
13
+
14
+ interface Props {
15
+ step1: RoutesSearchForm
16
+ step2: FoundData
17
+ step3?: FoundData
18
+ step4: PersonData
19
+ coupon?: CouponData
20
+ total: TotalData
21
+ t: TFunction<'common'>
22
+ }
23
+
24
+ const Summary = ({ step1, step2, step3, step4, coupon, total, t }: Props): JSX.Element => (
25
+ <div className="box">
26
+ <SubTitle>
27
+ <AiOutlineInfoCircle />
28
+ { t('routes_order.step5.summary.title') }
29
+ </SubTitle>
30
+
31
+ <Information>
32
+ <About type="departure" step1={ step1 } data={ step2 } t={ t } />
33
+ <About type="return" step1={ step1 } data={ step3 } t={ t } />
34
+ </Information>
35
+
36
+ <Tickets step1={ step1 } step4={ step4 } t={ t } />
37
+
38
+ { coupon && <Amount type="discount" code={ coupon.code } value={ coupon.discount } t={ t } /> }
39
+
40
+ <Amount type="total" value={ total.display } t={ t } />
41
+ </div>
42
+ );
43
+
44
+ export default Summary;
@@ -0,0 +1,41 @@
1
+ import { TFunction } from 'i18next';
2
+ import { MdEventSeat } from 'react-icons/md';
3
+ import { Person, Seats } from './styles';
4
+ import { PersonData } from '@autobusal/providers/types/persons';
5
+
6
+ interface Props {
7
+ type: 'adult' | 'child' | 'baby'
8
+ data: PersonData
9
+ number: number
10
+ t: TFunction<'public'>
11
+ }
12
+
13
+ const Preview = ({ type, data, number, t }: Props): JSX.Element[] => {
14
+ const persons: JSX.Element[] = [];
15
+
16
+ for (let i = 1; i <= number; i++) {
17
+ const name = `${ type }${ number }`;
18
+
19
+ const names = {
20
+ firstName: `${ name }_fn`,
21
+ lastName: `${ name }_ln`,
22
+ seatDeparture: `${ name }_sd`,
23
+ seatReturn: `${ name }_sr`
24
+ };
25
+
26
+ persons.push(
27
+ <Person key={ i }>
28
+ { data[names.firstName] } { data[names.lastName] } ({ t(`data.persons.${ type }.singular`) })
29
+
30
+ <Seats>
31
+ { t('routes_order.step5.summary.seats.departure') }: <MdEventSeat /> { data[names.seatDeparture] }
32
+ { data[names.seatReturn] && <>, { t('routes_order.step5.summary.seats.return') }: <MdEventSeat /> { data[names.seatReturn] }</> }
33
+ </Seats>
34
+ </Person>
35
+ );
36
+ }
37
+
38
+ return persons;
39
+ };
40
+
41
+ export default Preview;
@@ -0,0 +1,34 @@
1
+ import { TFunction } from 'i18next';
2
+ import Preview from './Preview';
3
+ import { Container, About, List } from './styles';
4
+ import { RoutesSearchForm } from '@autobusal/routes-search/types';
5
+ import { PersonData } from '@autobusal/providers/types/persons';
6
+
7
+ interface Props {
8
+ step1: RoutesSearchForm
9
+ step4: PersonData
10
+ t: TFunction<'public'>
11
+ }
12
+
13
+ const Tickets = ({ step1, step4, t }: Props): JSX.Element => {
14
+ const total = step1.adults + step1.children + step1.babies;
15
+
16
+ return (
17
+ <Container>
18
+ <About>
19
+ { t('routes_order.step5.summary.tickets') } ({ total }):
20
+ { step1.adults > 0 && ` ${ step1.adults } x ${ t('data.persons.adult.plural') }` }
21
+ { step1.children > 0 && `, ${ step1.children } x ${ t('data.persons.child.plural') }` }
22
+ { step1.babies > 0 && `${ step1.babies } x ${ t('data.persons.baby.plural') }` }
23
+ </About>
24
+
25
+ <List>
26
+ <Preview type="adult" data={ step4 } number={ step1.adults } t={ t } />
27
+ <Preview type="child" data={ step4 } number={ step1.children } t={ t } />
28
+ <Preview type="baby" data={ step4 } number={ step1.babies } t={ t } />
29
+ </List>
30
+ </Container>
31
+ );
32
+ };
33
+
34
+ export default Tickets;
@@ -0,0 +1,36 @@
1
+ import styled from 'styled-components';
2
+
3
+ export const Container = styled.div`
4
+ padding: 10px 15px;
5
+ margin: 15px 0;
6
+ border: 1px solid ${ props => props.theme.background.neutral };
7
+ border-radius: ${ props => props.theme.borderRadius };
8
+ `;
9
+
10
+ export const About = styled.div`
11
+ padding-bottom: 8px;
12
+ font-weight: 700;
13
+ border-bottom: 1px solid ${ props => props.theme.background.neutral };
14
+ `;
15
+
16
+ export const List = styled.div`
17
+ display: flex;
18
+ flex-direction: column;
19
+ gap: 10px;
20
+ margin-top: 8px;
21
+ font-size: ${ props => props.theme.size.s };
22
+ `;
23
+
24
+ export const Person = styled.div`
25
+ font-weight: 700;
26
+ `;
27
+
28
+ export const Seats = styled.div`
29
+ display: flex;
30
+ align-items: center;
31
+ gap: 2px;
32
+ margin-top: 2px;
33
+ font-weight: 400;
34
+ color: ${ props => props.theme.font.faded };
35
+ font-size: ${ props => props.theme.size.xs };
36
+ `;
@@ -0,0 +1,11 @@
1
+ import styled from 'styled-components';
2
+
3
+ export const Information = styled.div`
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: 15px;
7
+
8
+ @media (min-width: 768px) {
9
+ flex-direction: row;
10
+ }
11
+ `;