@autobusal/operator-routes 1.6.0 → 1.9.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 +11 -0
- package/Locations/Add/Add.tsx +18 -7
- package/Locations/utilities.tsx +37 -38
- package/Manage.tsx +1 -1
- package/Schedule/Manage.tsx +21 -2
- package/Unavailable/Manage.tsx +7 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.0 (2026-08-29)
|
|
4
|
+
|
|
5
|
+
- Route schedules and unavailability periods can be planned five years ahead, not one - the picker's ceiling was the only limit.
|
|
6
|
+
- The 'add a stop' picker is searchable: every station on the platform was one native select, so finding your own city meant scrolling hundreds of options in an order nothing sorted for you.
|
|
7
|
+
|
|
8
|
+
## 1.6.1
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **A copied or reversed route could not be opened.** The manage page read `data.drivers.registered` with the optional chain stopping one level short, so a route whose `drivers` relation was null crashed the whole page. Guarded. (Platform audit RT-09e; the backend now also copies the drivers row.)
|
|
13
|
+
|
|
3
14
|
## 1.6.0
|
|
4
15
|
|
|
5
16
|
### Added
|
package/Locations/Add/Add.tsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { TFunction } from 'i18next';
|
|
2
2
|
import { useForm } from 'react-hook-form';
|
|
3
3
|
import { FiPlus } from 'react-icons/fi';
|
|
4
|
-
import { Button } from '@autobusal/common';
|
|
5
|
-
import {
|
|
4
|
+
import { Button, SearchableSelect } from '@autobusal/common';
|
|
5
|
+
import { Display, success, notify } from '@autobusal/utilities';
|
|
6
6
|
import Loading from './Loading';
|
|
7
|
-
import {
|
|
7
|
+
import { getStopOptions } from '../utilities';
|
|
8
8
|
import { Container, Title, ContainerAdd, Choose, Notice } from './styles';
|
|
9
9
|
import { AddForm } from '../types';
|
|
10
10
|
import { useGetStops, useAddLocation } from '../services';
|
|
@@ -16,7 +16,7 @@ interface Props {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
const Add = ({ routeId, t, onReload }: Props): JSX.Element => {
|
|
19
|
-
const { register, handleSubmit, formState: { errors } } = useForm<AddForm>();
|
|
19
|
+
const { register, handleSubmit, setValue, formState: { errors } } = useForm<AddForm>();
|
|
20
20
|
|
|
21
21
|
const { data, isLoading } = useGetStops(routeId);
|
|
22
22
|
|
|
@@ -60,9 +60,20 @@ const Add = ({ routeId, t, onReload }: Props): JSX.Element => {
|
|
|
60
60
|
<form onSubmit={ handleSubmit(onSubmit) }>
|
|
61
61
|
<ContainerAdd>
|
|
62
62
|
<Choose>
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
{ /* Edited: Claude - Date: 2026-08-29: typeable. Every station on
|
|
64
|
+
the platform was listed in one native select, so an operator
|
|
65
|
+
looking for their own city scrolled hundreds of options in an
|
|
66
|
+
order nothing sorted for them. */ }
|
|
67
|
+
<SearchableSelect
|
|
68
|
+
name="group_id"
|
|
69
|
+
options={ getStopOptions(t, data) }
|
|
70
|
+
placeholder={ t('locations_manage.add_city.choose', { ns: 'common' }) }
|
|
71
|
+
empty={ t('locations_manage.add_city.empty', { ns: 'common' }) }
|
|
72
|
+
validation="required"
|
|
73
|
+
t={ t }
|
|
74
|
+
refs={ register }
|
|
75
|
+
onUpdate={ setValue }
|
|
76
|
+
/>
|
|
66
77
|
|
|
67
78
|
{ Display(errors.group_id) }
|
|
68
79
|
</Choose>
|
package/Locations/utilities.tsx
CHANGED
|
@@ -1,46 +1,45 @@
|
|
|
1
1
|
import { TFunction } from 'i18next';
|
|
2
|
-
import {
|
|
2
|
+
import { CountryData } from '@autobusal/providers/types/locations';
|
|
3
|
+
import { SearchableOption } from '@autobusal/common/SearchableSelect/types';
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The stations an operator may add to a route, as searchable options.
|
|
7
|
+
*
|
|
8
|
+
* Edited: Claude - Date: 2026-08-29
|
|
9
|
+
*
|
|
10
|
+
* Was a list of <option>/<optgroup> elements for a native select. It is a
|
|
11
|
+
* DATA list now, for SearchableSelect - every station on the platform is in
|
|
12
|
+
* here (hundreds of them, grouped by country), and until this became
|
|
13
|
+
* typeable the only way to find one's own city was to scroll the lot.
|
|
14
|
+
* `search` carries the country name too, so typing "greece" narrows to
|
|
15
|
+
* Greek stations even though the option itself shows only "city > stop".
|
|
16
|
+
*/
|
|
17
|
+
export const getStopOptions = (t: TFunction<'normal'>, countries?: CountryData[]): SearchableOption[] => {
|
|
18
|
+
const options: SearchableOption[] = [];
|
|
8
19
|
|
|
9
20
|
countries?.forEach(country => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
cities?.forEach(city => {
|
|
24
|
-
city.stops?.forEach(stop => {
|
|
25
|
-
const value = `${ city.id }#${ stop.id }`;
|
|
26
|
-
|
|
27
|
-
/*
|
|
28
|
-
* Edited: Claude - Date: 2026-08-21
|
|
29
|
-
*
|
|
30
|
-
* A stop an operator proposed is listed but DISABLED until an admin
|
|
31
|
-
* approves it. Leaving it out entirely would look like the proposal
|
|
32
|
-
* had been lost - and obtapi refuses it anyway (Locations\
|
|
33
|
-
* OperatorController::add scopes to verified()), so an enabled option
|
|
34
|
-
* would just be an error waiting to happen.
|
|
35
|
-
*/
|
|
36
|
-
const pending = stop.verified === false;
|
|
21
|
+
country.cities?.forEach(city => {
|
|
22
|
+
city.stops?.forEach(stop => {
|
|
23
|
+
/*
|
|
24
|
+
* Edited: Claude - Date: 2026-08-21
|
|
25
|
+
*
|
|
26
|
+
* A stop an operator proposed is listed but DISABLED until an admin
|
|
27
|
+
* approves it. Leaving it out entirely would look like the proposal
|
|
28
|
+
* had been lost - and obtapi refuses it anyway (Locations\
|
|
29
|
+
* OperatorController::add scopes to verified()), so an enabled option
|
|
30
|
+
* would just be an error waiting to happen.
|
|
31
|
+
*/
|
|
32
|
+
const pending = stop.verified === false;
|
|
37
33
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
{ city.name }
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
34
|
+
options.push({
|
|
35
|
+
value: `${ city.id }#${ stop.id }`,
|
|
36
|
+
label: `${ city.name } \u203a ${ stop.name }`,
|
|
37
|
+
group: country.name,
|
|
38
|
+
disabled: pending,
|
|
39
|
+
note: pending ? t('locations_manage.add_city.pending', { ns: 'common' }) : undefined,
|
|
40
|
+
search: [ country.name, city.name, stop.name ]
|
|
41
|
+
});
|
|
42
|
+
});
|
|
44
43
|
});
|
|
45
44
|
});
|
|
46
45
|
|
package/Manage.tsx
CHANGED
|
@@ -218,7 +218,7 @@ const Manage = ({ url, t }: Props): JSX.Element => {
|
|
|
218
218
|
label: t('operator_routes.manage.drivers', { ns: 'common' }),
|
|
219
219
|
name: 'drivers',
|
|
220
220
|
type: 'checkbox-list-all',
|
|
221
|
-
selected: data?.drivers
|
|
221
|
+
selected: data?.drivers?.registered ?? [],
|
|
222
222
|
values: drivers
|
|
223
223
|
}, {}, {
|
|
224
224
|
label: t('operator_routes.manage.searchable', { ns: 'common' }),
|
package/Schedule/Manage.tsx
CHANGED
|
@@ -12,6 +12,20 @@ interface Props {
|
|
|
12
12
|
t: TFunction<'common'>
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* How far ahead a route schedule may be planned.
|
|
17
|
+
*
|
|
18
|
+
* Edited: Claude - Date: 2026-08-29
|
|
19
|
+
*
|
|
20
|
+
* The picker allowed one year - the ceiling every booking calendar has -
|
|
21
|
+
* so an operator running a stable seasonal line had to come back and redo
|
|
22
|
+
* the same dates every twelve months. obtapi never capped this (its rules
|
|
23
|
+
* are date_format + after:start_date), so the limit was only ever the
|
|
24
|
+
* calendar's. Six months before the end date, `schedules:expiring` now
|
|
25
|
+
* emails the operator and raises the alert on their dashboard.
|
|
26
|
+
*/
|
|
27
|
+
const SCHEDULE_YEARS = 5;
|
|
28
|
+
|
|
15
29
|
const Manage = ({ url, t }: Props): JSX.Element => {
|
|
16
30
|
const params = useParams();
|
|
17
31
|
|
|
@@ -63,13 +77,18 @@ const Manage = ({ url, t }: Props): JSX.Element => {
|
|
|
63
77
|
name: 'start_date',
|
|
64
78
|
type: 'picker',
|
|
65
79
|
value: data?.available.start_date,
|
|
66
|
-
rules: 'required'
|
|
80
|
+
rules: 'required',
|
|
81
|
+
// Edited: Claude - Date: 2026-08-29: a schedule is planned in
|
|
82
|
+
// seasons, not in the one-year window seats are sold in - both
|
|
83
|
+
// ends of it now reach five years out (see Calendar's maxYears)
|
|
84
|
+
maxYears: SCHEDULE_YEARS
|
|
67
85
|
}, {
|
|
68
86
|
label: t('schedules_manage.end_date', { ns: 'common' }),
|
|
69
87
|
name: 'end_date',
|
|
70
88
|
type: 'picker',
|
|
71
89
|
value: data?.available.end_date,
|
|
72
|
-
rules: 'required'
|
|
90
|
+
rules: 'required',
|
|
91
|
+
maxYears: SCHEDULE_YEARS
|
|
73
92
|
}, {}, {
|
|
74
93
|
label: t('schedules_manage.schedule', { ns: 'common' }),
|
|
75
94
|
name: 'schedule',
|
package/Unavailable/Manage.tsx
CHANGED
|
@@ -80,13 +80,18 @@ const Manage = ({ url, t }: Props): JSX.Element => {
|
|
|
80
80
|
name: 'start_inactive',
|
|
81
81
|
type: 'picker',
|
|
82
82
|
value: unavailable?.start_inactive,
|
|
83
|
-
rules: 'required'
|
|
83
|
+
rules: 'required',
|
|
84
|
+
// Edited: Claude - Date: 2026-08-29: an unavailability falls INSIDE
|
|
85
|
+
// the route's schedule, which now runs up to five years out - a
|
|
86
|
+
// one-year picker could not describe a closure in year three
|
|
87
|
+
maxYears: 5
|
|
84
88
|
}, {
|
|
85
89
|
label: t('unavailable.manage.end_inactive', { ns: 'common' }),
|
|
86
90
|
name: 'end_inactive',
|
|
87
91
|
type: 'picker',
|
|
88
92
|
value: unavailable?.end_inactive,
|
|
89
|
-
rules: 'required'
|
|
93
|
+
rules: 'required',
|
|
94
|
+
maxYears: 5
|
|
90
95
|
}] }
|
|
91
96
|
fetching={ isFetching }
|
|
92
97
|
pending={ isPendingSave || isPendingDelete }
|