@autobusal/operator-routes 1.0.9 → 1.0.10
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/Manage.tsx +13 -21
- package/Schedule/Manage.tsx +4 -3
- package/Seating.tsx +139 -14
- package/Transiting/Manage.tsx +2 -1
- package/Unavailable/Manage.tsx +2 -1
- package/package.json +1 -1
- package/services.ts +3 -2
- package/types.ts +6 -0
- package/utilities.tsx +0 -40
package/Manage.tsx
CHANGED
|
@@ -5,13 +5,14 @@ import { RiMoneyEuroCircleLine } from 'react-icons/ri';
|
|
|
5
5
|
import { FaMapMarkerAlt } from 'react-icons/fa';
|
|
6
6
|
import { IoCalendarClearOutline } from 'react-icons/io5';
|
|
7
7
|
import { Meta, BackWithTitle, Viewer } from '@autobusal/common';
|
|
8
|
-
import { usePage,
|
|
8
|
+
import { usePage, useRouteFeatures, useOperatorDrivers } from '@autobusal/hooks';
|
|
9
9
|
import { success } from '@autobusal/utilities';
|
|
10
10
|
import Template from './Template';
|
|
11
11
|
import Options from './Options';
|
|
12
12
|
import Seating from './Seating';
|
|
13
|
-
import { getType, getLanguagesExtended, getTemplates,
|
|
13
|
+
import { getType, getLanguagesExtended, getTemplates, getTransiting } from './utilities';
|
|
14
14
|
import { RouteData } from '@autobusal/providers/types/routes';
|
|
15
|
+
import { SeatingOptionsData } from './types';
|
|
15
16
|
import { useGetRoute, usePostRoute, useDeleteRoute } from './services';
|
|
16
17
|
|
|
17
18
|
interface Props {
|
|
@@ -20,7 +21,7 @@ interface Props {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
const Manage = ({ url, t }: Props): JSX.Element => {
|
|
23
|
-
const [ seating, setSeating ] = useState<
|
|
24
|
+
const [ seating, setSeating ] = useState<SeatingOptionsData | undefined>(undefined);
|
|
24
25
|
|
|
25
26
|
const params = useParams();
|
|
26
27
|
|
|
@@ -30,26 +31,16 @@ const Manage = ({ url, t }: Props): JSX.Element => {
|
|
|
30
31
|
|
|
31
32
|
const navigate = useNavigate();
|
|
32
33
|
|
|
33
|
-
const { data } = useGetRoute(id);
|
|
34
|
+
const { data, isFetching } = useGetRoute(id);
|
|
34
35
|
|
|
35
36
|
const { mutate: Update, isPending: isPendingSave } = usePostRoute(id, seating);
|
|
36
37
|
|
|
37
38
|
const { mutate: Delete, isPending: isPendingDelete } = useDeleteRoute();
|
|
38
39
|
|
|
39
|
-
const buses = useBuses();
|
|
40
|
-
|
|
41
40
|
const features = useRouteFeatures();
|
|
42
41
|
|
|
43
42
|
const drivers = useOperatorDrivers('id');
|
|
44
43
|
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
setSeating(data?.trip.seating ?? 'free');
|
|
47
|
-
}, [data]);
|
|
48
|
-
|
|
49
|
-
const onSeating = (id: string): void => {
|
|
50
|
-
setSeating(id);
|
|
51
|
-
};
|
|
52
|
-
|
|
53
44
|
const onSave = (data: RouteData): void => {
|
|
54
45
|
Update(data, {
|
|
55
46
|
onSuccess: () => {
|
|
@@ -69,7 +60,11 @@ const Manage = ({ url, t }: Props): JSX.Element => {
|
|
|
69
60
|
}
|
|
70
61
|
});
|
|
71
62
|
};
|
|
72
|
-
|
|
63
|
+
|
|
64
|
+
const onSeatingUpdated = (options: SeatingOptionsData): void => {
|
|
65
|
+
setSeating(options);
|
|
66
|
+
};
|
|
67
|
+
|
|
73
68
|
const title = id > 0 ? t('operator_routes.manage.title.update', { name: data?.name, ns: 'common' }) : t('operator_routes.manage.title.new', { ns: 'common' });
|
|
74
69
|
|
|
75
70
|
return (
|
|
@@ -95,12 +90,8 @@ const Manage = ({ url, t }: Props): JSX.Element => {
|
|
|
95
90
|
label: t('operator_routes.manage.seat_reservation', { ns: 'common' }),
|
|
96
91
|
type: 'component',
|
|
97
92
|
name: 'seating',
|
|
98
|
-
value: <Seating
|
|
99
|
-
}, {},
|
|
100
|
-
getBusType(buses, seating, t, data?.trip.bus?.id)
|
|
101
|
-
) , (
|
|
102
|
-
getSeatingType(seating, t, data?.trip)
|
|
103
|
-
), {} , {
|
|
93
|
+
value: <Seating trip={ data?.trip } t={ t } onUpdated={ onSeatingUpdated } />
|
|
94
|
+
}, {} , {
|
|
104
95
|
label: t('operator_routes.manage.schedule.title', { ns: 'common' }),
|
|
105
96
|
name: 'schedule',
|
|
106
97
|
type: 'link',
|
|
@@ -223,6 +214,7 @@ const Manage = ({ url, t }: Props): JSX.Element => {
|
|
|
223
214
|
<Options id={ id } t={ t } />
|
|
224
215
|
)
|
|
225
216
|
}] }
|
|
217
|
+
fetching={ isFetching }
|
|
226
218
|
pending={ isPendingSave || isPendingDelete }
|
|
227
219
|
t={ t }
|
|
228
220
|
actions={ [
|
package/Schedule/Manage.tsx
CHANGED
|
@@ -23,7 +23,7 @@ const Manage = ({ url, t }: Props): JSX.Element => {
|
|
|
23
23
|
|
|
24
24
|
const navigate = useNavigate();
|
|
25
25
|
|
|
26
|
-
const { data } = useGetSchedule(id);
|
|
26
|
+
const { data, isFetching } = useGetSchedule(id);
|
|
27
27
|
|
|
28
28
|
const { mutate: Update, isPending } = usePostSchedule(id);
|
|
29
29
|
|
|
@@ -34,7 +34,7 @@ const Manage = ({ url, t }: Props): JSX.Element => {
|
|
|
34
34
|
const onSave = (data: AvailableData): void => {
|
|
35
35
|
Update(data, {
|
|
36
36
|
onSuccess: () => {
|
|
37
|
-
success(t('schedules_manage.
|
|
37
|
+
success(t('schedules_manage.messages.saved', { ns: 'common' }));
|
|
38
38
|
|
|
39
39
|
navigate(url);
|
|
40
40
|
}
|
|
@@ -42,7 +42,7 @@ const Manage = ({ url, t }: Props): JSX.Element => {
|
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
const title = t('schedules_manage.title', { name: data?.name, ns: 'common' });
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
return (
|
|
47
47
|
<Meta title={ title }>
|
|
48
48
|
<BackWithTitle title={ title } to={ url } t={ t } />
|
|
@@ -68,6 +68,7 @@ const Manage = ({ url, t }: Props): JSX.Element => {
|
|
|
68
68
|
values: getSchedules(t),
|
|
69
69
|
selected: data?.available.schedule ?? []
|
|
70
70
|
}] }
|
|
71
|
+
fetching={ isFetching }
|
|
71
72
|
pending={ isPending }
|
|
72
73
|
t={ t }
|
|
73
74
|
actions={ [
|
package/Seating.tsx
CHANGED
|
@@ -1,29 +1,154 @@
|
|
|
1
|
+
import { useState, useEffect, ChangeEvent } from 'react';
|
|
1
2
|
import { TFunction } from 'i18next';
|
|
3
|
+
import { useBuses } from '@autobusal/hooks';
|
|
4
|
+
import { Display } from '@autobusal/common/Viewer/styles';
|
|
2
5
|
import styled from 'styled-components';
|
|
6
|
+
import { TripData } from '@autobusal/providers/types/routes';
|
|
7
|
+
import { SeatingOptionsData } from './types';
|
|
3
8
|
|
|
4
9
|
interface Props {
|
|
5
|
-
|
|
10
|
+
trip?: TripData
|
|
6
11
|
t: TFunction<'normal'>
|
|
7
|
-
|
|
12
|
+
onUpdated: (options: SeatingOptionsData) => void
|
|
8
13
|
}
|
|
9
14
|
|
|
10
|
-
const Seating = ({
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
const Seating = ({ trip, t, onUpdated }: Props): JSX.Element => {
|
|
16
|
+
const [ data, setData ] = useState<SeatingOptionsData>({
|
|
17
|
+
seating: trip?.seating ?? 'free',
|
|
18
|
+
bus_id: trip?.bus?.id,
|
|
19
|
+
blocked_seats: Number(trip?.blocked_seats),
|
|
20
|
+
available_seats: Number(trip?.available_seats)
|
|
21
|
+
});
|
|
22
|
+
const [ type, setType ] = useState<string>(data.seating);
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
const buses = useBuses();
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
onUpdated(data);
|
|
28
|
+
}, [data]);
|
|
29
|
+
|
|
30
|
+
const onSeatingChange = (type: string): void => {
|
|
31
|
+
const newData = { ...data };
|
|
32
|
+
|
|
33
|
+
newData.seating = type;
|
|
34
|
+
|
|
35
|
+
if (type === 'free') {
|
|
36
|
+
newData.bus_id = undefined;
|
|
37
|
+
newData.blocked_seats = 0;
|
|
38
|
+
} else {
|
|
39
|
+
newData.bus_id = Number(buses[0].id);
|
|
40
|
+
newData.available_seats = 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
setType(type);
|
|
44
|
+
setData(newData);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const onBusChange = (event: ChangeEvent<HTMLSelectElement>): void => {
|
|
48
|
+
setData({
|
|
49
|
+
...data,
|
|
50
|
+
bus_id: Number(event.target.value)
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const onSeatsChange = (event: ChangeEvent<HTMLInputElement>, type: string): void => {
|
|
55
|
+
const seatsNo = Number(event.target.value);
|
|
56
|
+
|
|
57
|
+
if (type === 'blocked_seats') {
|
|
58
|
+
setData({
|
|
59
|
+
...data,
|
|
60
|
+
blocked_seats: seatsNo
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (type === 'available_seats') {
|
|
65
|
+
setData({
|
|
66
|
+
...data,
|
|
67
|
+
available_seats: seatsNo
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const busOptions = buses.map(bus => (
|
|
73
|
+
<option key={ bus.id } value={ bus.id }>
|
|
74
|
+
{ bus.name }
|
|
75
|
+
</option>
|
|
76
|
+
));
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<>
|
|
80
|
+
<div className="row">
|
|
81
|
+
<Container>
|
|
82
|
+
<label>
|
|
83
|
+
<input checked={ type === 'free' } type="radio" name="seating" onChange={ () => onSeatingChange('free') } />
|
|
84
|
+
{ t('operator_routes.manage.seating.free', { ns: 'common' }) }
|
|
85
|
+
</label>
|
|
86
|
+
|
|
87
|
+
<label>
|
|
88
|
+
<input checked={ type === 'reservation' } type="radio" name="seating" onChange={ () => onSeatingChange('reservation') } />
|
|
89
|
+
{ t('operator_routes.manage.seating.reservation', { ns: 'common' }) }
|
|
90
|
+
</label>
|
|
91
|
+
</Container>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<Column className="column">
|
|
95
|
+
<div className="row">
|
|
96
|
+
{ type === 'reservation' && (
|
|
97
|
+
<>
|
|
98
|
+
{ t('operator_routes.manage.bus.reservation', { ns: 'common' }) }
|
|
99
|
+
|
|
100
|
+
<select defaultValue={ trip?.bus?.id } onChange={ onBusChange }>
|
|
101
|
+
{ busOptions }
|
|
102
|
+
</select>
|
|
103
|
+
</>
|
|
104
|
+
) }
|
|
105
|
+
|
|
106
|
+
{ type === 'free' && (
|
|
107
|
+
<>
|
|
108
|
+
{ t('operator_routes.manage.bus.title', { ns: 'common' }) }
|
|
109
|
+
|
|
110
|
+
<Display className="row-data">{ t('operator_routes.manage.bus.free', { ns: 'common' }) }</Display>
|
|
111
|
+
</>
|
|
112
|
+
) }
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div className="row row-nomargin">
|
|
116
|
+
{ type === 'reservation' && (
|
|
117
|
+
<>
|
|
118
|
+
{ t('operator_routes.manage.seats.blocked', { ns: 'common' }) }
|
|
119
|
+
|
|
120
|
+
<input type="number" name="blocked_seats" defaultValue={ data.blocked_seats } onChange={ (event) => onSeatsChange(event, 'blocked_seats') } />
|
|
121
|
+
</>
|
|
122
|
+
) }
|
|
123
|
+
|
|
124
|
+
{ type === 'free' && (
|
|
125
|
+
<>
|
|
126
|
+
{ t('operator_routes.manage.seats.available', { ns: 'common' }) }
|
|
127
|
+
|
|
128
|
+
<input type="number" name="available_seats" defaultValue={ data.available_seats } onChange={ (event) => onSeatsChange(event, 'available_seats') } />
|
|
129
|
+
</>
|
|
130
|
+
) }
|
|
131
|
+
</div>
|
|
132
|
+
</Column>
|
|
133
|
+
</>
|
|
134
|
+
);
|
|
135
|
+
};
|
|
23
136
|
|
|
24
137
|
const Container = styled.div`
|
|
25
138
|
display: flex;
|
|
26
139
|
gap: 15px;
|
|
27
140
|
`;
|
|
28
141
|
|
|
142
|
+
const Column = styled.div`
|
|
143
|
+
& > div:first-of-type {
|
|
144
|
+
margin-bottom: 15px;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@media (min-width: 540px) {
|
|
148
|
+
& > div:first-of-type {
|
|
149
|
+
margin-bottom: 0;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
`;
|
|
153
|
+
|
|
29
154
|
export default Seating;
|
package/Transiting/Manage.tsx
CHANGED
|
@@ -24,7 +24,7 @@ const Manage = ({ url, t }: Props): JSX.Element => {
|
|
|
24
24
|
|
|
25
25
|
const { data: CountriesData, isLoading } = useCountries('available');
|
|
26
26
|
|
|
27
|
-
const { data, refetch } = useGetTransiting(id);
|
|
27
|
+
const { data, isFetching, refetch } = useGetTransiting(id);
|
|
28
28
|
|
|
29
29
|
const { mutate: Update, isPending: isPendingSave } = usePostTransiting(id);
|
|
30
30
|
|
|
@@ -79,6 +79,7 @@ const Manage = ({ url, t }: Props): JSX.Element => {
|
|
|
79
79
|
value: 0,
|
|
80
80
|
rules: 'required|min:1'
|
|
81
81
|
}] }
|
|
82
|
+
fetching={ isFetching }
|
|
82
83
|
pending={ isPendingSave || isPendingDelete }
|
|
83
84
|
t={ t }
|
|
84
85
|
actions={ [
|
package/Unavailable/Manage.tsx
CHANGED
|
@@ -23,7 +23,7 @@ const Manage = ({ url, t }: Props): JSX.Element => {
|
|
|
23
23
|
|
|
24
24
|
const navigate = useNavigate();
|
|
25
25
|
|
|
26
|
-
const { data } = useGetUnavailable(routeId, id);
|
|
26
|
+
const { data, isFetching } = useGetUnavailable(routeId, id);
|
|
27
27
|
|
|
28
28
|
const { mutate: Update, isPending: isPendingSave } = usePostUnavailable(routeId, id);
|
|
29
29
|
|
|
@@ -76,6 +76,7 @@ const Manage = ({ url, t }: Props): JSX.Element => {
|
|
|
76
76
|
value: unavailable?.end_inactive,
|
|
77
77
|
rules: 'required'
|
|
78
78
|
}] }
|
|
79
|
+
fetching={ isFetching }
|
|
79
80
|
pending={ isPendingSave || isPendingDelete }
|
|
80
81
|
t={ t }
|
|
81
82
|
actions={ [
|
package/package.json
CHANGED
package/services.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { apiClient } from '@autobusal/providers';
|
|
|
3
3
|
import { PaginationData } from '@autobusal/providers/types/pagination';
|
|
4
4
|
import { RouteData } from '@autobusal/providers/types/routes';
|
|
5
5
|
import { TableParamsData } from '@autobusal/providers/types/other';
|
|
6
|
+
import { SeatingOptionsData } from './types';
|
|
6
7
|
|
|
7
8
|
export const useGetRoutes = (params: TableParamsData): UseQueryResult<PaginationData<RouteData>> => (
|
|
8
9
|
useQuery({
|
|
@@ -36,7 +37,7 @@ export const useGetRoute = (id: number): UseSuspenseQueryResult<RouteData> => (
|
|
|
36
37
|
})
|
|
37
38
|
);
|
|
38
39
|
|
|
39
|
-
export const usePostRoute = (id: number, seating
|
|
40
|
+
export const usePostRoute = (id: number, seating?: SeatingOptionsData): UseMutationResult<void, Error, RouteData, unknown> => (
|
|
40
41
|
useMutation({
|
|
41
42
|
mutationKey: ['operator-routes-save', { id, seating }],
|
|
42
43
|
mutationFn: async (data: RouteData) => (
|
|
@@ -44,7 +45,7 @@ export const usePostRoute = (id: number, seating: string): UseMutationResult<voi
|
|
|
44
45
|
.post('/api/routes/operator/update', {
|
|
45
46
|
...data,
|
|
46
47
|
id,
|
|
47
|
-
seating,
|
|
48
|
+
...seating,
|
|
48
49
|
features: JSON.stringify(data.features),
|
|
49
50
|
drivers: JSON.stringify(data.drivers)
|
|
50
51
|
})
|
package/types.ts
ADDED
package/utilities.tsx
CHANGED
|
@@ -2,7 +2,6 @@ import { TFunction } from 'i18next';
|
|
|
2
2
|
import { IoGlobeOutline } from 'react-icons/io5';
|
|
3
3
|
import { getLanguages } from '@autobusal/utilities';
|
|
4
4
|
import { DropdownData } from '@autobusal/providers/types/other';
|
|
5
|
-
import { TripData } from '@autobusal/providers/types/routes';
|
|
6
5
|
import { ViewData } from '@autobusal/common/Viewer/types';
|
|
7
6
|
|
|
8
7
|
export const getType = (id: number, value: string, t: TFunction<'normal'>): ViewData => {
|
|
@@ -53,45 +52,6 @@ export const getTemplates = (t: TFunction<'normal'>): DropdownData[] => ([
|
|
|
53
52
|
}
|
|
54
53
|
]);
|
|
55
54
|
|
|
56
|
-
export const getBusType = (buses: DropdownData[], seating: string, t: TFunction<'normal'>, value?: number): ViewData => {
|
|
57
|
-
if (seating === 'free') {
|
|
58
|
-
return {
|
|
59
|
-
label: t('operator_routes.manage.bus.reservation', { ns: 'common' }),
|
|
60
|
-
name: 'bus_id',
|
|
61
|
-
type: 'display',
|
|
62
|
-
value: t('operator_routes.manage.bus.free', { ns: 'common' })
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
label: t('operator_routes.manage.bus.title', { ns: 'common' }),
|
|
68
|
-
name: 'bus_id',
|
|
69
|
-
type: 'select',
|
|
70
|
-
values: buses,
|
|
71
|
-
value,
|
|
72
|
-
rules: 'required|min:1'
|
|
73
|
-
};
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
export const getSeatingType = (seating: string, t: TFunction<'normal'>, trip?: TripData): ViewData => {
|
|
77
|
-
if (seating === 'free') {
|
|
78
|
-
return {
|
|
79
|
-
label: t('operator_routes.manage.seats.available', { ns: 'common' }),
|
|
80
|
-
name: 'available_seats',
|
|
81
|
-
type: 'number',
|
|
82
|
-
value: trip?.available_seats,
|
|
83
|
-
rules: 'required|min:1'
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
label: t('operator_routes.manage.seats.blocked', { ns: 'common' }),
|
|
89
|
-
name: 'blocked_seats',
|
|
90
|
-
type: 'number',
|
|
91
|
-
value: trip?.blocked_seats
|
|
92
|
-
};
|
|
93
|
-
};
|
|
94
|
-
|
|
95
55
|
export const getTransiting = (id: number, t: TFunction<'normal'>, type?: string): ViewData => {
|
|
96
56
|
if (type == 'national') {
|
|
97
57
|
return {
|