@autobusal/operator-routes 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.
- package/Browse.tsx +79 -0
- package/Information.tsx +46 -0
- package/Manage.tsx +238 -0
- package/Options.tsx +96 -0
- package/Seating.tsx +29 -0
- package/Template.tsx +59 -0
- package/index.ts +7 -0
- package/package.json +6 -0
- package/services.ts +84 -0
- package/utilities.tsx +117 -0
package/Browse.tsx
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
import { useSearchParams } from 'react-router-dom';
|
|
3
|
+
import { Meta, Table, Row } from '@autobusal/common';
|
|
4
|
+
import { usePage } from '@autobusal/hooks';
|
|
5
|
+
import { getParams } from '@autobusal/utilities';
|
|
6
|
+
import { Bags, Dates } from './Information';
|
|
7
|
+
import { GetRoutes } from './services';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
url: string
|
|
11
|
+
t: TFunction<'common'>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const Browse = ({ url, t }: Props): JSX.Element => {
|
|
15
|
+
const [ searchParams ] = useSearchParams();
|
|
16
|
+
|
|
17
|
+
usePage('routes');
|
|
18
|
+
|
|
19
|
+
const { data, isLoading, isFetching } = GetRoutes(getParams(searchParams, ['page', 'sort', 'order', 'q']));
|
|
20
|
+
|
|
21
|
+
const rows = data?.data.map(item => (
|
|
22
|
+
<Row
|
|
23
|
+
key={ item.id }
|
|
24
|
+
id={ item.id }
|
|
25
|
+
data={ [
|
|
26
|
+
item.name,
|
|
27
|
+
item.code,
|
|
28
|
+
item.trip.bus?.name ?? '-',
|
|
29
|
+
<Bags key={ item.id } item={ item } t={ t } />,
|
|
30
|
+
<Dates key={ item.id } item={ item } t={ t } />
|
|
31
|
+
] }
|
|
32
|
+
/>
|
|
33
|
+
));
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Meta title={ t('operator_routes.browse.title.manage', { ns: 'common' }) }>
|
|
37
|
+
<h1>{ t('operator_routes.browse.title.manage', { ns: 'common' }) }</h1>
|
|
38
|
+
|
|
39
|
+
<Table
|
|
40
|
+
url={ url }
|
|
41
|
+
columns={ [{
|
|
42
|
+
name: t('operator_routes.browse.name', { ns: 'common' }),
|
|
43
|
+
width: 30,
|
|
44
|
+
slug: 'name',
|
|
45
|
+
type: 'az'
|
|
46
|
+
}, {
|
|
47
|
+
name: t('operator_routes.browse.code', { ns: 'common' }),
|
|
48
|
+
slug: 'code',
|
|
49
|
+
width: 10
|
|
50
|
+
}, {
|
|
51
|
+
name: t('operator_routes.browse.bus', { ns: 'common' }),
|
|
52
|
+
width: 10
|
|
53
|
+
}, {
|
|
54
|
+
name: t('operator_routes.browse.bags', { ns: 'common' }),
|
|
55
|
+
width: 20
|
|
56
|
+
}, {
|
|
57
|
+
name: t('operator_routes.browse.info', { ns: 'common' }),
|
|
58
|
+
slug: 'date',
|
|
59
|
+
width: 20
|
|
60
|
+
}] }
|
|
61
|
+
rows={ rows }
|
|
62
|
+
pages={ data }
|
|
63
|
+
sorting={ {
|
|
64
|
+
slug: 'name',
|
|
65
|
+
order: 'asc'
|
|
66
|
+
} }
|
|
67
|
+
search={ searchParams.get('q') }
|
|
68
|
+
loading={ isLoading }
|
|
69
|
+
fetching={ isFetching }
|
|
70
|
+
t={ t }
|
|
71
|
+
actions={ [
|
|
72
|
+
'create', 'search', 'view'
|
|
73
|
+
] }
|
|
74
|
+
/>
|
|
75
|
+
</Meta>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export default Browse;
|
package/Information.tsx
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { RouteData } from '@autobusal/providers/types/routes';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
item?: RouteData
|
|
7
|
+
t: TFunction<'common'>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const Bags = ({ item, t }: Props): JSX.Element => (
|
|
11
|
+
<Container>
|
|
12
|
+
<div>
|
|
13
|
+
{ t('operator_routes.browse.allowed.bags_no', { ns: 'common' }) }: <strong>{ item?.trip.bags_no_display }</strong>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div>
|
|
17
|
+
{ t('operator_routes.browse.allowed.bags_weight', { ns: 'common' }) }: <strong>{ item?.trip.bags_weight_display }</strong>
|
|
18
|
+
</div>
|
|
19
|
+
</Container>
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export const Dates = ({ item, t }: Props): JSX.Element => (
|
|
23
|
+
<Container>
|
|
24
|
+
{ item?.available.start_date && (
|
|
25
|
+
<div>
|
|
26
|
+
{ t('operator_routes.browse.dates.start_date', { ns: 'common' }) }: <strong>{ item.available.start_date }</strong>
|
|
27
|
+
</div>
|
|
28
|
+
) }
|
|
29
|
+
|
|
30
|
+
{ item?.available.end_date && (
|
|
31
|
+
<div>
|
|
32
|
+
{ t('operator_routes.browse.dates.end_date', { ns: 'common' }) }: <strong>{ item.available.end_date }</strong>
|
|
33
|
+
</div>
|
|
34
|
+
) }
|
|
35
|
+
|
|
36
|
+
{ item?.available.schedule_display && (
|
|
37
|
+
<div>
|
|
38
|
+
{ t('operator_routes.browse.dates.schedule', { ns: 'common' }) }: <strong>{ item.available.schedule_display }</strong>
|
|
39
|
+
</div>
|
|
40
|
+
) }
|
|
41
|
+
</Container>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const Container = styled.div`
|
|
45
|
+
text-align: left;
|
|
46
|
+
`;
|
package/Manage.tsx
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { TFunction } from 'i18next';
|
|
3
|
+
import { useParams, useNavigate } from 'react-router-dom';
|
|
4
|
+
import { RiMoneyEuroCircleLine } from 'react-icons/ri';
|
|
5
|
+
import { FaMapMarkerAlt } from 'react-icons/fa';
|
|
6
|
+
import { IoCalendarClearOutline } from 'react-icons/io5';
|
|
7
|
+
import { Meta, BackWithTitle, Viewer } from '@autobusal/common';
|
|
8
|
+
import { usePage, useBuses, useRouteFeatures, useOperatorDrivers } from '@autobusal/hooks';
|
|
9
|
+
import { success } from '@autobusal/utilities';
|
|
10
|
+
import Template from './Template';
|
|
11
|
+
import Options from './Options';
|
|
12
|
+
import Seating from './Seating';
|
|
13
|
+
import { getType, getLanguagesExtended, getTemplates, getBusType, getSeatingType, getTransiting } from './utilities';
|
|
14
|
+
import { RouteData } from '@autobusal/providers/types/routes';
|
|
15
|
+
import { GetRoute, PostRoute, DeleteRoute } from './services';
|
|
16
|
+
|
|
17
|
+
interface Props {
|
|
18
|
+
url: string
|
|
19
|
+
t: TFunction<'common'>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const Manage = ({ url, t }: Props): JSX.Element => {
|
|
23
|
+
const [ seating, setSeating ] = useState<string>('');
|
|
24
|
+
|
|
25
|
+
const params = useParams();
|
|
26
|
+
|
|
27
|
+
const id = Number(params.id);
|
|
28
|
+
|
|
29
|
+
usePage('routes');
|
|
30
|
+
|
|
31
|
+
const navigate = useNavigate();
|
|
32
|
+
|
|
33
|
+
const { data } = GetRoute(id);
|
|
34
|
+
|
|
35
|
+
const { mutate: Update, isPending: isPendingSave } = PostRoute(id, seating);
|
|
36
|
+
|
|
37
|
+
const { mutate: Delete, isPending: isPendingDelete } = DeleteRoute();
|
|
38
|
+
|
|
39
|
+
const buses = useBuses();
|
|
40
|
+
|
|
41
|
+
const features = useRouteFeatures();
|
|
42
|
+
|
|
43
|
+
const drivers = useOperatorDrivers('id');
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
setSeating(data?.trip.seating ?? 'free');
|
|
47
|
+
}, [data]);
|
|
48
|
+
|
|
49
|
+
const onSeating = (id: string): void => {
|
|
50
|
+
setSeating(id);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const onSave = (data: RouteData): void => {
|
|
54
|
+
Update(data, {
|
|
55
|
+
onSuccess: () => {
|
|
56
|
+
success(t('operator_routes.manage.messages.saved', { ns: 'common' }));
|
|
57
|
+
|
|
58
|
+
navigate(url);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const onDelete = (): void => {
|
|
64
|
+
Delete(id, {
|
|
65
|
+
onSuccess: () => {
|
|
66
|
+
success(t('operator_routes.manage.messages.deleted', { ns: 'common' }));
|
|
67
|
+
|
|
68
|
+
navigate(url);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const title = id > 0 ? t('operator_routes.manage.title.update', { name: data?.name, ns: 'common' }) : t('operator_routes.manage.title.new', { ns: 'common' });
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<Meta title={ title }>
|
|
77
|
+
<BackWithTitle title={ title } to={ url } t={ t } />
|
|
78
|
+
|
|
79
|
+
<Viewer
|
|
80
|
+
id={ id }
|
|
81
|
+
data={ [{
|
|
82
|
+
label: t('operator_routes.manage.name', { ns: 'common' }),
|
|
83
|
+
name: 'name',
|
|
84
|
+
type: 'text',
|
|
85
|
+
value: data?.name,
|
|
86
|
+
rules: 'required|min_length:2|max_length:250'
|
|
87
|
+
}, {
|
|
88
|
+
label: t('operator_routes.manage.code', { ns: 'common' }),
|
|
89
|
+
name: 'code',
|
|
90
|
+
type: 'display',
|
|
91
|
+
value: (data?.code ?? '-')
|
|
92
|
+
}, (
|
|
93
|
+
getType(id, (data?.type ?? ''), t)
|
|
94
|
+
), {}, {
|
|
95
|
+
label: t('operator_routes.manage.seat_reservation', { ns: 'common' }),
|
|
96
|
+
type: 'component',
|
|
97
|
+
name: 'seating',
|
|
98
|
+
value: <Seating value={ seating } t={ t } onChange={ onSeating } />
|
|
99
|
+
}, {}, (
|
|
100
|
+
getBusType(buses, seating, t, data?.trip.bus?.id)
|
|
101
|
+
) , (
|
|
102
|
+
getSeatingType(seating, t, data?.trip)
|
|
103
|
+
), {} , {
|
|
104
|
+
label: t('operator_routes.manage.schedule.title', { ns: 'common' }),
|
|
105
|
+
name: 'schedule',
|
|
106
|
+
type: 'link',
|
|
107
|
+
value: (
|
|
108
|
+
<>
|
|
109
|
+
<IoCalendarClearOutline />
|
|
110
|
+
{ t('operator_routes.manage.schedule.link', { ns: 'common' }) }
|
|
111
|
+
</>
|
|
112
|
+
),
|
|
113
|
+
url: `/operator/routes/schedule/${ id }`
|
|
114
|
+
}, (
|
|
115
|
+
getTransiting(id, t, data?.type)
|
|
116
|
+
), {
|
|
117
|
+
label: t('operator_routes.manage.unavailable.title', { ns: 'common' }),
|
|
118
|
+
name: 'unavailable',
|
|
119
|
+
type: 'link',
|
|
120
|
+
value: (
|
|
121
|
+
<>
|
|
122
|
+
<IoCalendarClearOutline />
|
|
123
|
+
{ t('operator_routes.manage.unavailable.link', { ns: 'common' }) }
|
|
124
|
+
</>
|
|
125
|
+
),
|
|
126
|
+
url: `/operator/routes/unavailable/${ id }`
|
|
127
|
+
}, {}, {
|
|
128
|
+
label: t('operator_routes.manage.features', { ns: 'common' }),
|
|
129
|
+
name: 'features',
|
|
130
|
+
type: 'checkbox-list',
|
|
131
|
+
selected: data?.trip?.features ?? [],
|
|
132
|
+
values: features
|
|
133
|
+
}, {}, {
|
|
134
|
+
label: t('operator_routes.manage.bags_no', { ns: 'common' }),
|
|
135
|
+
name: 'bags_no',
|
|
136
|
+
type: 'number',
|
|
137
|
+
value: data?.trip.bags_no,
|
|
138
|
+
rules: 'required|min:1'
|
|
139
|
+
}, {
|
|
140
|
+
label: t('operator_routes.manage.bags_weight', { ns: 'common' }),
|
|
141
|
+
name: 'bags_weight',
|
|
142
|
+
type: 'number',
|
|
143
|
+
value: data?.trip.bags_weight,
|
|
144
|
+
rules: 'required|min:1'
|
|
145
|
+
}, {}, {
|
|
146
|
+
label: t('operator_routes.manage.language', { ns: 'common' }),
|
|
147
|
+
name: 'language',
|
|
148
|
+
type: 'select',
|
|
149
|
+
values: getLanguagesExtended(),
|
|
150
|
+
value: data?.language,
|
|
151
|
+
rules: 'required'
|
|
152
|
+
}, {
|
|
153
|
+
label: t('operator_routes.manage.template', { ns: 'common' }),
|
|
154
|
+
name: 'template',
|
|
155
|
+
type: 'select',
|
|
156
|
+
values: getTemplates(t),
|
|
157
|
+
value: data?.template,
|
|
158
|
+
rules: 'required'
|
|
159
|
+
}, {}, {
|
|
160
|
+
label: t('operator_routes.manage.locations.title', { ns: 'common' }),
|
|
161
|
+
type: 'link',
|
|
162
|
+
name: 'locations',
|
|
163
|
+
value: (
|
|
164
|
+
<>
|
|
165
|
+
<FaMapMarkerAlt />
|
|
166
|
+
{ t('operator_routes.manage.locations.link', { ns: 'common' }) }
|
|
167
|
+
</>
|
|
168
|
+
),
|
|
169
|
+
url: `/operator/routes/locations/${ id }`
|
|
170
|
+
}, {
|
|
171
|
+
label: t('operator_routes.manage.prices.title', { ns: 'common' }),
|
|
172
|
+
type: 'link',
|
|
173
|
+
name: 'prices',
|
|
174
|
+
value: (
|
|
175
|
+
<>
|
|
176
|
+
<RiMoneyEuroCircleLine />
|
|
177
|
+
{ t('operator_routes.manage.prices.link', { ns: 'common' }) }
|
|
178
|
+
</>
|
|
179
|
+
),
|
|
180
|
+
url: `/operator/routes/prices/${ id }`
|
|
181
|
+
}, {
|
|
182
|
+
label: t('operator_routes.manage.alternate.title', { ns: 'common' }),
|
|
183
|
+
type: 'link',
|
|
184
|
+
name: 'prices_alternates',
|
|
185
|
+
value: (
|
|
186
|
+
<>
|
|
187
|
+
<RiMoneyEuroCircleLine />
|
|
188
|
+
{ t('operator_routes.manage.alternate.link', { ns: 'common' }) }
|
|
189
|
+
</>
|
|
190
|
+
),
|
|
191
|
+
url: `/operator/routes/alternates/${ id }`
|
|
192
|
+
}, {}, {
|
|
193
|
+
label: t('operator_routes.manage.drivers', { ns: 'common' }),
|
|
194
|
+
name: 'drivers',
|
|
195
|
+
type: 'checkbox-list-all',
|
|
196
|
+
selected: data?.drivers.registered ?? [],
|
|
197
|
+
values: drivers
|
|
198
|
+
}, {}, {
|
|
199
|
+
label: t('operator_routes.manage.searchable', { ns: 'common' }),
|
|
200
|
+
name: 'searchable',
|
|
201
|
+
type: 'checkbox',
|
|
202
|
+
value: data?.searchable
|
|
203
|
+
}, {}, {
|
|
204
|
+
label: t('operator_routes.manage.policies_en', { ns: 'common' }),
|
|
205
|
+
name: 'policies_en',
|
|
206
|
+
type: 'textarea-html',
|
|
207
|
+
value: data?.policies_en
|
|
208
|
+
}, {}, {
|
|
209
|
+
label: t('operator_routes.manage.policies_sq', { ns: 'common' }),
|
|
210
|
+
name: 'policies_sq',
|
|
211
|
+
type: 'textarea-html',
|
|
212
|
+
value: data?.policies_sq
|
|
213
|
+
}, {}, {
|
|
214
|
+
label: t('operator_routes.manage.ticket.template', { ns: 'common' }),
|
|
215
|
+
type: 'component',
|
|
216
|
+
name: 'template_preview',
|
|
217
|
+
value: <Template type={ data?.template } t={ t } />
|
|
218
|
+
}, {
|
|
219
|
+
label: t('operator_routes.manage.options.title', { ns: 'common' }),
|
|
220
|
+
type: 'component',
|
|
221
|
+
name: 'options',
|
|
222
|
+
value: (
|
|
223
|
+
<Options id={ id } t={ t } />
|
|
224
|
+
)
|
|
225
|
+
}] }
|
|
226
|
+
pending={ isPendingSave || isPendingDelete }
|
|
227
|
+
t={ t }
|
|
228
|
+
actions={ [
|
|
229
|
+
'save', 'delete'
|
|
230
|
+
]}
|
|
231
|
+
onSave={ onSave }
|
|
232
|
+
onDelete={ onDelete }
|
|
233
|
+
/>
|
|
234
|
+
</Meta>
|
|
235
|
+
);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
export default Manage;
|
package/Options.tsx
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
import { useNavigate } from 'react-router-dom';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { AiOutlineCopy, AiOutlineReload } from 'react-icons/ai';
|
|
5
|
+
import { Button } from '@autobusal/common';
|
|
6
|
+
import { success } from '@autobusal/utilities';
|
|
7
|
+
import { PostCopy } from './services';
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
id: number
|
|
11
|
+
t: TFunction<'normal'>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const Options = ({ id, t }: Props): JSX.Element => {
|
|
15
|
+
const navigate = useNavigate();
|
|
16
|
+
|
|
17
|
+
const { mutate: Copy, isPending: isPendingCopy } = PostCopy(id);
|
|
18
|
+
|
|
19
|
+
const { mutate: Reverse, isPending: isPendingReverse } = PostCopy(id, true);
|
|
20
|
+
|
|
21
|
+
const onCopy = (): void => {
|
|
22
|
+
if (!window.confirm(t('operator_routes.manage.options.confirm', { ns: 'common' }))) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
Copy({}, {
|
|
27
|
+
onSuccess: (data) => {
|
|
28
|
+
success(t('operator_routes.manage.messages.copied', { ns: 'common' }));
|
|
29
|
+
|
|
30
|
+
navigate(`/operator/routes/manage/${ data.id }`);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const onReverse = (): void => {
|
|
36
|
+
if (!window.confirm(t('operator_routes.manage.options.confirm', { ns: 'common' }))) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
Reverse({}, {
|
|
41
|
+
onSuccess: (data) => {
|
|
42
|
+
success(t('operator_routes.manage.messages.reversed', { ns: 'common' }));
|
|
43
|
+
|
|
44
|
+
navigate(`/operator/routes/manage/${ data.id }`);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
if (id === 0) {
|
|
50
|
+
return (
|
|
51
|
+
<>-</>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<Container>
|
|
57
|
+
<Button
|
|
58
|
+
type="button"
|
|
59
|
+
size="small"
|
|
60
|
+
subtype="secondary"
|
|
61
|
+
loading={ isPendingCopy }
|
|
62
|
+
text={
|
|
63
|
+
<>
|
|
64
|
+
<AiOutlineCopy />
|
|
65
|
+
{ t('operator_routes.manage.options.copy', { ns: 'common' }) }
|
|
66
|
+
</>
|
|
67
|
+
}
|
|
68
|
+
noMargin
|
|
69
|
+
onClick={ onCopy }
|
|
70
|
+
/>
|
|
71
|
+
|
|
72
|
+
<Button
|
|
73
|
+
type="button"
|
|
74
|
+
size="small"
|
|
75
|
+
subtype="secondary"
|
|
76
|
+
loading={ isPendingReverse }
|
|
77
|
+
text={
|
|
78
|
+
<>
|
|
79
|
+
<AiOutlineReload />
|
|
80
|
+
{ t('operator_routes.manage.options.reverse', { ns: 'common' }) }
|
|
81
|
+
</>
|
|
82
|
+
}
|
|
83
|
+
noMargin
|
|
84
|
+
onClick={ onReverse }
|
|
85
|
+
/>
|
|
86
|
+
</Container>
|
|
87
|
+
);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const Container = styled.div`
|
|
91
|
+
display: flex;
|
|
92
|
+
flex-wrap: wrap;
|
|
93
|
+
gap: 10px;
|
|
94
|
+
`;
|
|
95
|
+
|
|
96
|
+
export default Options;
|
package/Seating.tsx
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
value?: string
|
|
6
|
+
t: TFunction<'normal'>
|
|
7
|
+
onChange: (id: string) => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const Seating = ({ value, t, onChange }: Props): JSX.Element => (
|
|
11
|
+
<Container>
|
|
12
|
+
<label>
|
|
13
|
+
<input checked={ value === 'free' } type="radio" name="seating" onChange={ () => onChange('free') } />
|
|
14
|
+
{ t('operator_routes.manage.seating.free', { ns: 'common' }) }
|
|
15
|
+
</label>
|
|
16
|
+
|
|
17
|
+
<label>
|
|
18
|
+
<input checked={ value === 'reservation' } type="radio" name="seating" onChange={ () => onChange('reservation') } />
|
|
19
|
+
{ t('operator_routes.manage.seating.reservation', { ns: 'common' }) }
|
|
20
|
+
</label>
|
|
21
|
+
</Container>
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const Container = styled.div`
|
|
25
|
+
display: flex;
|
|
26
|
+
gap: 15px;
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
export default Seating;
|
package/Template.tsx
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { TFunction } from 'i18next';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { RiEyeFill } from 'react-icons/ri';
|
|
5
|
+
import { Button, Modal } from '@autobusal/common';
|
|
6
|
+
import { GetSettings } from '@autobusal/providers/services';
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
type?: string
|
|
10
|
+
t: TFunction<'normal'>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const Template = ({ type, t }: Props): JSX.Element => {
|
|
14
|
+
const [ show, setShow ] = useState<boolean>(false);
|
|
15
|
+
|
|
16
|
+
const { data } = GetSettings();
|
|
17
|
+
|
|
18
|
+
if (!type) {
|
|
19
|
+
return (
|
|
20
|
+
<>-</>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<>
|
|
26
|
+
<Button
|
|
27
|
+
type="button"
|
|
28
|
+
subtype="secondary"
|
|
29
|
+
size="small"
|
|
30
|
+
text={
|
|
31
|
+
<>
|
|
32
|
+
<RiEyeFill />
|
|
33
|
+
{ t('operator_routes.manage.ticket.preview', { ns: 'common' }) }
|
|
34
|
+
</>
|
|
35
|
+
}
|
|
36
|
+
noMargin
|
|
37
|
+
onClick={ () => setShow(true) }
|
|
38
|
+
/>
|
|
39
|
+
|
|
40
|
+
{ show && (
|
|
41
|
+
<Modal
|
|
42
|
+
title={ t('operator_routes.manage.ticket.preview', { ns: 'common' }) }
|
|
43
|
+
width={ 900 }
|
|
44
|
+
content={
|
|
45
|
+
<Image src={ `${ data.url }/tickets/${ type }.png` } />
|
|
46
|
+
}
|
|
47
|
+
onClose={ () => setShow(false) }
|
|
48
|
+
/>
|
|
49
|
+
) }
|
|
50
|
+
</>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const Image = styled.img`
|
|
55
|
+
display: block;
|
|
56
|
+
width: 100%;
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
export default Template;
|
package/index.ts
ADDED
package/package.json
ADDED
package/services.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { UseMutationResult, UseQueryResult, UseSuspenseQueryResult, useMutation, useQuery, useSuspenseQuery } from '@tanstack/react-query';
|
|
2
|
+
import { apiClient } from '@autobusal/providers';
|
|
3
|
+
import { PaginationData } from '@autobusal/providers/types/pagination';
|
|
4
|
+
import { RouteData } from '@autobusal/providers/types/routes';
|
|
5
|
+
import { TableParamsData } from '@autobusal/providers/types/other';
|
|
6
|
+
|
|
7
|
+
export const GetRoutes = (params: TableParamsData): UseQueryResult<PaginationData<RouteData>> => (
|
|
8
|
+
useQuery({
|
|
9
|
+
queryKey: ['operator-routes', params],
|
|
10
|
+
queryFn: async () => (
|
|
11
|
+
await apiClient
|
|
12
|
+
.get('/api/routes/operator/browse', { params })
|
|
13
|
+
.then(response => (
|
|
14
|
+
response.data
|
|
15
|
+
))
|
|
16
|
+
)
|
|
17
|
+
})
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export const GetRoute = (id: number): UseSuspenseQueryResult<RouteData> => (
|
|
21
|
+
useSuspenseQuery({
|
|
22
|
+
queryKey: ['operator-routes-manage', { id }],
|
|
23
|
+
queryFn: async () => {
|
|
24
|
+
if (id === 0) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return await apiClient
|
|
29
|
+
.get('/api/routes/operator/get', {
|
|
30
|
+
params: { id }
|
|
31
|
+
})
|
|
32
|
+
.then(response => (
|
|
33
|
+
response.data
|
|
34
|
+
))
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
export const PostRoute = (id: number, seating: string): UseMutationResult<void, Error, RouteData, unknown> => (
|
|
40
|
+
useMutation({
|
|
41
|
+
mutationKey: ['operator-routes-save', { id, seating }],
|
|
42
|
+
mutationFn: async (data: RouteData) => (
|
|
43
|
+
await apiClient
|
|
44
|
+
.post('/api/routes/operator/update', {
|
|
45
|
+
...data,
|
|
46
|
+
id,
|
|
47
|
+
seating,
|
|
48
|
+
features: JSON.stringify(data.features),
|
|
49
|
+
drivers: JSON.stringify(data.drivers)
|
|
50
|
+
})
|
|
51
|
+
.then(response => (
|
|
52
|
+
response.data
|
|
53
|
+
))
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
export const DeleteRoute = (): UseMutationResult<void, Error, number, unknown> => (
|
|
59
|
+
useMutation({
|
|
60
|
+
mutationKey: ['operator-routes-delete'],
|
|
61
|
+
mutationFn: async (id: number) => (
|
|
62
|
+
await apiClient
|
|
63
|
+
.delete('/api/routes/operator/delete', {
|
|
64
|
+
params: { id }
|
|
65
|
+
})
|
|
66
|
+
.then(response => (
|
|
67
|
+
response.data
|
|
68
|
+
))
|
|
69
|
+
)
|
|
70
|
+
})
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
export const PostCopy = (id: number, reverse?: boolean): UseMutationResult<RouteData> => (
|
|
74
|
+
useMutation({
|
|
75
|
+
mutationKey: [`operator-routes-copy${ reverse && '-reverse' }`, { id }],
|
|
76
|
+
mutationFn: async () => (
|
|
77
|
+
await apiClient
|
|
78
|
+
.post(`/api/routes/operator/${ reverse ? 'reverse' : 'copy' }`, { id })
|
|
79
|
+
.then(response => (
|
|
80
|
+
response.data
|
|
81
|
+
))
|
|
82
|
+
)
|
|
83
|
+
})
|
|
84
|
+
);
|
package/utilities.tsx
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
import { IoGlobeOutline } from 'react-icons/io5';
|
|
3
|
+
import { getLanguages } from '@autobusal/utilities';
|
|
4
|
+
import { DropdownData } from '@autobusal/providers/types/other';
|
|
5
|
+
import { TripData } from '@autobusal/providers/types/routes';
|
|
6
|
+
import { ViewData } from '@autobusal/common/Viewer/types';
|
|
7
|
+
|
|
8
|
+
export const getType = (id: number, value: string, t: TFunction<'normal'>): ViewData => {
|
|
9
|
+
if (id === 0) {
|
|
10
|
+
return {
|
|
11
|
+
label: t('operator_routes.manage.type', { ns: 'common' }),
|
|
12
|
+
name: 'type',
|
|
13
|
+
type: 'select',
|
|
14
|
+
values: getOptionsTypes(t),
|
|
15
|
+
value,
|
|
16
|
+
rules: 'required|min_length:1'
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
label: t('operator_routes.manage.type', { ns: 'common' }),
|
|
22
|
+
name: 'type',
|
|
23
|
+
type: 'display',
|
|
24
|
+
value
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const getOptionsTypes = (t: TFunction<'common'>): DropdownData[] => ([
|
|
29
|
+
{
|
|
30
|
+
name: t('data.route_types.international', { ns: 'common' }),
|
|
31
|
+
id: 'international'
|
|
32
|
+
}, {
|
|
33
|
+
name: t('data.route_types.national', { ns: 'common' }),
|
|
34
|
+
id: 'national'
|
|
35
|
+
}
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
export const getLanguagesExtended = (): DropdownData[] => {
|
|
39
|
+
const languages = getLanguages();
|
|
40
|
+
|
|
41
|
+
languages.push({
|
|
42
|
+
name: 'Gr',
|
|
43
|
+
id: 'gr'
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return languages;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const getTemplates = (t: TFunction<'normal'>): DropdownData[] => ([
|
|
50
|
+
{
|
|
51
|
+
name: t('operator_routes.manage.templates.standard', { ns: 'common' }),
|
|
52
|
+
id: 'standard'
|
|
53
|
+
}
|
|
54
|
+
]);
|
|
55
|
+
|
|
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
|
+
export const getTransiting = (id: number, t: TFunction<'normal'>, type?: string): ViewData => {
|
|
96
|
+
if (type == 'national') {
|
|
97
|
+
return {
|
|
98
|
+
label: t('operator_routes.manage.transiting.title', { ns: 'common' }),
|
|
99
|
+
name: 'transiting',
|
|
100
|
+
type: 'display',
|
|
101
|
+
value: t('operator_routes.manage.transiting.cant', { ns: 'common' })
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
label: t('operator_routes.manage.transiting.title', { ns: 'common' }),
|
|
107
|
+
name: 'transiting',
|
|
108
|
+
type: 'link',
|
|
109
|
+
value: (
|
|
110
|
+
<>
|
|
111
|
+
<IoGlobeOutline />
|
|
112
|
+
{ t('operator_routes.manage.transiting.link', { ns: 'common' }) }
|
|
113
|
+
</>
|
|
114
|
+
),
|
|
115
|
+
url: `/operator/routes/transiting/${ id }`
|
|
116
|
+
};
|
|
117
|
+
};
|