@autobusal/common 0.0.24 → 0.0.26
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/Gender.tsx +39 -0
- package/Seats/ChooseSeat.tsx +69 -0
- package/Seats/Preview.tsx +54 -0
- package/Seats/Unavailable.tsx +20 -0
- package/Seats/styles.ts +132 -0
- package/index.ts +4 -0
- package/package.json +1 -1
package/Gender.tsx
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
import { UseFormRegister } from 'react-hook-form';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
name: string
|
|
6
|
+
defaultValue?: number
|
|
7
|
+
t: TFunction<'common'>
|
|
8
|
+
refs: UseFormRegister<any>
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const Gender = ({ name, defaultValue, t, refs }: Props) => (
|
|
12
|
+
<div className="row">
|
|
13
|
+
<div className="list">
|
|
14
|
+
<label>
|
|
15
|
+
<input
|
|
16
|
+
type="radio"
|
|
17
|
+
value={ 0 }
|
|
18
|
+
defaultChecked={ defaultValue === undefined || defaultValue == 0 }
|
|
19
|
+
{ ...refs(name) }
|
|
20
|
+
/>
|
|
21
|
+
|
|
22
|
+
{ t('data.gender.male') }
|
|
23
|
+
</label>
|
|
24
|
+
|
|
25
|
+
<label>
|
|
26
|
+
<input
|
|
27
|
+
type="radio"
|
|
28
|
+
value={ 1 }
|
|
29
|
+
defaultChecked={ defaultValue == 1 }
|
|
30
|
+
{ ...refs(name) }
|
|
31
|
+
/>
|
|
32
|
+
|
|
33
|
+
{ t('data.gender.female') }
|
|
34
|
+
</label>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
export default Gender;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { UseFormRegister } from 'react-hook-form';
|
|
3
|
+
import { TFunction } from 'i18next';
|
|
4
|
+
import { FaCheck } from 'react-icons/fa';
|
|
5
|
+
import { MdEventSeat } from 'react-icons/md';
|
|
6
|
+
import { validate } from '@autobusal/utilities';
|
|
7
|
+
import Unavailable from './Unavailable';
|
|
8
|
+
import Preview from './Preview';
|
|
9
|
+
import { ButtonSeat } from './styles';
|
|
10
|
+
import { OccupiedData } from '@autobusal/providers/types/buses';
|
|
11
|
+
import { PersonData } from '@modules/Providers/types/persons';
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
name: string
|
|
15
|
+
type: 'from' | 'to'
|
|
16
|
+
defaultValue?: number
|
|
17
|
+
bus: OccupiedData
|
|
18
|
+
picked: number[]
|
|
19
|
+
validation: string
|
|
20
|
+
t: TFunction<'common'>
|
|
21
|
+
refs: UseFormRegister<PersonData | any>
|
|
22
|
+
onUpdate: (name: string, type: ('from' | 'to'), value: number, previous: number) => void
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const ChooseSeat = ({ name, type, defaultValue, bus, picked, validation, t, refs, onUpdate }: Props): JSX.Element => {
|
|
26
|
+
const [ show, setShow ] = useState<boolean>(false);
|
|
27
|
+
const [ selected, setSelected ] = useState<number>(defaultValue ?? 0);
|
|
28
|
+
|
|
29
|
+
// if we don't have a bus, we just show a notification
|
|
30
|
+
if (! bus.bus) {
|
|
31
|
+
return <Unavailable t={ t } />;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const onSelect = (seat: number, occupied: boolean): void => {
|
|
35
|
+
if (!occupied) {
|
|
36
|
+
onUpdate(name, type, seat, selected);
|
|
37
|
+
|
|
38
|
+
setSelected(seat);
|
|
39
|
+
|
|
40
|
+
setShow(false);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div>
|
|
46
|
+
<ButtonSeat type="button" $selected={ selected > 0 } onClick={ () => setShow(true) }>
|
|
47
|
+
{ selected > 0
|
|
48
|
+
? <><FaCheck /> { t('seats.chosen', { number: selected }) }</>
|
|
49
|
+
: <><MdEventSeat /> { t('seats.choose') }</> }
|
|
50
|
+
</ButtonSeat>
|
|
51
|
+
|
|
52
|
+
{ (show && bus.bus !== undefined) && (
|
|
53
|
+
<Preview
|
|
54
|
+
bus={ bus.bus }
|
|
55
|
+
occupied={ bus.occupied }
|
|
56
|
+
picked={ picked }
|
|
57
|
+
selected={ selected }
|
|
58
|
+
t={ t }
|
|
59
|
+
onSelect={ onSelect }
|
|
60
|
+
onClose={ () => setShow(false) }
|
|
61
|
+
/>
|
|
62
|
+
) }
|
|
63
|
+
|
|
64
|
+
<input type="hidden" defaultValue={ selected } { ...refs(name, validate(validation, t)) } />
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export default ChooseSeat;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
import { MdEventSeat } from 'react-icons/md';
|
|
3
|
+
import { Modal } from '@autobusal/common';
|
|
4
|
+
import { Seats, SeatsInner, ButtonOccupy } from './styles';
|
|
5
|
+
import { BusData } from '@autobusal/providers/types/buses';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
bus: BusData
|
|
9
|
+
occupied: number[]
|
|
10
|
+
picked: number[]
|
|
11
|
+
selected: number
|
|
12
|
+
t: TFunction<'general'>
|
|
13
|
+
onSelect: (seat: number, occupied: boolean) => void
|
|
14
|
+
onClose: () => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const Preview = ({ bus, occupied, picked, selected, t, onSelect, onClose }: Props): JSX.Element => {
|
|
18
|
+
const items = bus.seats.map((item, index) => {
|
|
19
|
+
const isOccupied = occupied.includes(item.no) || picked.includes(item.no);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<ButtonOccupy
|
|
23
|
+
key={ index }
|
|
24
|
+
type="button"
|
|
25
|
+
$top={ item.top }
|
|
26
|
+
$left={ item.left }
|
|
27
|
+
$width={ item.width }
|
|
28
|
+
$height={ item.height }
|
|
29
|
+
$selected={ selected === item.no }
|
|
30
|
+
$occupied={ isOccupied }
|
|
31
|
+
onClick={ () => onSelect(item.no, isOccupied) }
|
|
32
|
+
>
|
|
33
|
+
<MdEventSeat /> { item.no }
|
|
34
|
+
</ButtonOccupy>
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<Modal
|
|
40
|
+
width={ 1040 }
|
|
41
|
+
title={ t('seats.title', { bus: bus.name }) }
|
|
42
|
+
content={ (
|
|
43
|
+
<Seats>
|
|
44
|
+
<SeatsInner $image={ bus.image_url }>
|
|
45
|
+
{ items }
|
|
46
|
+
</SeatsInner>
|
|
47
|
+
</Seats>
|
|
48
|
+
) }
|
|
49
|
+
onClose={ onClose }
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default Preview;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
import { MdEventSeat } from 'react-icons/md';
|
|
3
|
+
import { SeatUnavailable, TitleUnavailable } from './styles';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
t: TFunction<'public'>
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const Unavailable = ({ t }: Props): JSX.Element => (
|
|
10
|
+
<SeatUnavailable>
|
|
11
|
+
<TitleUnavailable>
|
|
12
|
+
<MdEventSeat />
|
|
13
|
+
{ t('seats.unavailable.title') }
|
|
14
|
+
</TitleUnavailable>
|
|
15
|
+
|
|
16
|
+
{ t('seats.unavailable.content') }
|
|
17
|
+
</SeatUnavailable>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export default Unavailable;
|
package/Seats/styles.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const ButtonSeat = styled.button<{ $selected: boolean }>`
|
|
4
|
+
display: flex;
|
|
5
|
+
gap: 5px;
|
|
6
|
+
align-items: center;
|
|
7
|
+
padding: 4px 10px;
|
|
8
|
+
background: ${ props => props.theme.background.neutral };
|
|
9
|
+
border-radius: ${ props => props.theme.borderRadius };
|
|
10
|
+
transition: all 0.3s ease;
|
|
11
|
+
|
|
12
|
+
&:hover {
|
|
13
|
+
background: ${ props => props.theme.primary.contrast };
|
|
14
|
+
background: ${ props => props.theme.primary.normal };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
${ props => props.$selected && css`
|
|
18
|
+
color: ${ props => props.theme.font.success };
|
|
19
|
+
background: none;
|
|
20
|
+
|
|
21
|
+
&:hover {
|
|
22
|
+
color: #FFF;
|
|
23
|
+
background: ${ props => props.theme.font.success };
|
|
24
|
+
}
|
|
25
|
+
` }
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
export const Seats = styled.div`
|
|
29
|
+
overflow-x: auto;
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
export const SeatsInner = styled.div<{ $image: (string | undefined) }>`
|
|
33
|
+
position: relative;
|
|
34
|
+
width: 1000px;
|
|
35
|
+
height: 400px;
|
|
36
|
+
background: url('${ props => props.$image }');
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
export const ButtonOccupy = styled.button<{
|
|
40
|
+
$top: number
|
|
41
|
+
$left: number
|
|
42
|
+
$width: number
|
|
43
|
+
$height: number
|
|
44
|
+
$selected: boolean
|
|
45
|
+
$occupied: boolean
|
|
46
|
+
}>`
|
|
47
|
+
position: absolute;
|
|
48
|
+
top: ${ props => props.$top }px;
|
|
49
|
+
left: ${ props => props.$left }px;
|
|
50
|
+
width: ${ props => props.$width }px;
|
|
51
|
+
height: ${ props => props.$height }px;
|
|
52
|
+
display: flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
justify-content: center;
|
|
55
|
+
gap: 2px;
|
|
56
|
+
font-weight: 700;
|
|
57
|
+
font-size: ${ props => props.theme.size.l };
|
|
58
|
+
color: ${ props => props.theme.font.normal };
|
|
59
|
+
background: ${ props => props.theme.foreground.normal };
|
|
60
|
+
border-radius: ${ props => props.theme.borderRadius };
|
|
61
|
+
box-shadow: ${ props => props.theme.boxShadow };
|
|
62
|
+
transition: all 0.3s ease;
|
|
63
|
+
|
|
64
|
+
${ props => props.$selected && css`
|
|
65
|
+
color: ${ props => props.theme.primary.contrast };
|
|
66
|
+
background: ${ props => props.theme.primary.normal };
|
|
67
|
+
` }
|
|
68
|
+
|
|
69
|
+
${ props => props.$occupied && css`
|
|
70
|
+
color: #FFFFFF;
|
|
71
|
+
background: ${ props => props.theme.font.error };
|
|
72
|
+
` }
|
|
73
|
+
|
|
74
|
+
&:hover {
|
|
75
|
+
opacity: .8;
|
|
76
|
+
}
|
|
77
|
+
`;
|
|
78
|
+
|
|
79
|
+
export const SeatUnavailable = styled.div`
|
|
80
|
+
font-size: ${ props => props.theme.size.xs };
|
|
81
|
+
font-weight: 400;
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
export const TitleUnavailable = styled.div`
|
|
85
|
+
display: flex;
|
|
86
|
+
gap: 6px;
|
|
87
|
+
align-items: center;
|
|
88
|
+
font-weight: 700;
|
|
89
|
+
font-size: ${ props => props.theme.size.s };
|
|
90
|
+
`;
|
|
91
|
+
|
|
92
|
+
// // @todo: finish adding a seat
|
|
93
|
+
// // .choose-seats {
|
|
94
|
+
// // .bus-plan {
|
|
95
|
+
// // .bus-seat {
|
|
96
|
+
// // .remove {
|
|
97
|
+
// // position: absolute;
|
|
98
|
+
// // top: 0;
|
|
99
|
+
// // right: 0;
|
|
100
|
+
// // z-index: 1;
|
|
101
|
+
// // width: 20px;
|
|
102
|
+
// // height: 20px;
|
|
103
|
+
// // background-color: $red;
|
|
104
|
+
// // border-radius: $border-radius - 3;
|
|
105
|
+
|
|
106
|
+
// // svg {
|
|
107
|
+
// // margin: 1px auto 0;
|
|
108
|
+
// // font-size: $size-info;
|
|
109
|
+
// // color: $white;
|
|
110
|
+
// // }
|
|
111
|
+
// // }
|
|
112
|
+
// // }
|
|
113
|
+
|
|
114
|
+
// // .add-seat {
|
|
115
|
+
// // display: block;
|
|
116
|
+
// // position: absolute;
|
|
117
|
+
// // z-index: 5;
|
|
118
|
+
// // bottom: -58px;
|
|
119
|
+
// // width: 50px;
|
|
120
|
+
// // height: 38px;
|
|
121
|
+
// // text-align: center;
|
|
122
|
+
// // line-height: 32px;
|
|
123
|
+
// // color: $blue;
|
|
124
|
+
// // border: 3px solid $blue;
|
|
125
|
+
// // border-radius: 100px;
|
|
126
|
+
|
|
127
|
+
// // svg {
|
|
128
|
+
// // margin: 0;
|
|
129
|
+
// // }
|
|
130
|
+
// // }
|
|
131
|
+
// // }
|
|
132
|
+
// // }
|
package/index.ts
CHANGED
|
@@ -3,7 +3,9 @@ import BackTo from './BackTo';
|
|
|
3
3
|
import Calendar from './Calendar/Calendar';
|
|
4
4
|
import CookieNotification from './CookieNotification/CookieNotification';
|
|
5
5
|
import ChangeLanguage from './ChangeLanguage/ChangeLanguage';
|
|
6
|
+
import ChooseSeat from './Seats/ChooseSeat';
|
|
6
7
|
import { Button } from './Button/Button';
|
|
8
|
+
import Gender from './Gender';
|
|
7
9
|
import { General, Inline, Paragraph } from './Loading/Loading';
|
|
8
10
|
import Modal from './Modal/Modal';
|
|
9
11
|
import Meta from './Meta';
|
|
@@ -20,6 +22,8 @@ export {
|
|
|
20
22
|
Calendar,
|
|
21
23
|
CookieNotification,
|
|
22
24
|
ChangeLanguage,
|
|
25
|
+
ChooseSeat,
|
|
26
|
+
Gender,
|
|
23
27
|
General,
|
|
24
28
|
Inline,
|
|
25
29
|
Meta,
|