@autobusal/operator-routes 1.5.1 → 1.6.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
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.6.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **The stops taken off a route can be put back.** Withdrawal never destroyed
|
|
8
|
+
anything - the stop's row, its price rows and the fares on them all survive
|
|
9
|
+
- and both the API endpoint and this package's `useRestoreLocation` hook
|
|
10
|
+
had existed since it shipped. Nothing rendered them: the locations response
|
|
11
|
+
filters on `active`, so a withdrawn stop was never mentioned and no screen
|
|
12
|
+
could offer to bring one back. From the operator's side a withdrawal was
|
|
13
|
+
invisible and permanent, which is the one thing it was built not to be.
|
|
14
|
+
The section appears only when the route has something to restore.
|
|
15
|
+
|
|
16
|
+
## 1.5.2
|
|
17
|
+
|
|
18
|
+
### Removed
|
|
19
|
+
|
|
20
|
+
- **The confirm dialog in front of removing a stop.** It asked the question
|
|
21
|
+
the withdrawal panel already asks - the panel names the stop, lists
|
|
22
|
+
everybody still travelling through it and has its own button - and it
|
|
23
|
+
answered one part wrongly, warning that the fares would have to be entered
|
|
24
|
+
again. That stopped being true when calibration began reconciling price
|
|
25
|
+
rows instead of rebuilding them: measured on a ten-stop route, withdrawing
|
|
26
|
+
a stop keeps fourteen of sixteen fares and drops only the two legs through
|
|
27
|
+
the stop itself.
|
|
28
|
+
|
|
3
29
|
## 1.3.3
|
|
4
30
|
|
|
5
31
|
### Fixed
|
|
@@ -24,11 +24,29 @@ const Actions = ({ item, loading, t, onMove, onDelete }: Props): JSX.Element =>
|
|
|
24
24
|
onMove(order);
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
/*
|
|
28
|
+
* Edited: Claude - Date: 2026-08-22
|
|
29
|
+
*
|
|
30
|
+
* No window.confirm here any more. Removing a stop opens the withdrawal
|
|
31
|
+
* panel, which names the stop, lists everybody still travelling through
|
|
32
|
+
* it, and has its own explicit button - a far better confirmation than a
|
|
33
|
+
* browser dialog, and the dialog now stood in front of it saying "YOU WILL
|
|
34
|
+
* NEED TO ADD THE PRICES AGAIN", which stopped being true when
|
|
35
|
+
* Locations\Calibration started reconciling price rows instead of
|
|
36
|
+
* rebuilding them. Measured: withdrawing a stop from a ten-stop route kept
|
|
37
|
+
* fourteen of sixteen fares, losing only the two legs through the stop
|
|
38
|
+
* itself.
|
|
39
|
+
*
|
|
40
|
+
* So it was a second gate that asked the same question twice and got the
|
|
41
|
+
* answer wrong once. The panel asks it properly.
|
|
42
|
+
*
|
|
43
|
+
* `locations_manage.actions.delete` is deliberately still in obtapi's
|
|
44
|
+
* language files. obtapi deploys BEFORE the frontends, so for the length
|
|
45
|
+
* of that window the previous bundle is still calling it - and a missing
|
|
46
|
+
* key renders as the key itself, which is what the dialog showed when it
|
|
47
|
+
* was removed. It now says something true and is simply read by nobody
|
|
48
|
+
* once this bundle lands.
|
|
49
|
+
*/
|
|
32
50
|
|
|
33
51
|
return (
|
|
34
52
|
<ContainerActions>
|
|
@@ -80,7 +98,7 @@ const Actions = ({ item, loading, t, onMove, onDelete }: Props): JSX.Element =>
|
|
|
80
98
|
</>
|
|
81
99
|
}
|
|
82
100
|
noMargin
|
|
83
|
-
onClick={
|
|
101
|
+
onClick={ onDelete }
|
|
84
102
|
/>
|
|
85
103
|
</Group>
|
|
86
104
|
</ContainerActions>
|
package/Locations/Manage.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import { usePage, useBreadcrumbs } from '@autobusal/hooks';
|
|
|
5
5
|
import Add from './Add/Add';
|
|
6
6
|
import Propose from './Propose/Propose';
|
|
7
7
|
import Browse from './Browse/Browse';
|
|
8
|
+
import Withdrawn from './Withdrawn/Withdrawn';
|
|
8
9
|
import { useGetLocations, useGetStops } from './services';
|
|
9
10
|
|
|
10
11
|
interface Props {
|
|
@@ -61,6 +62,13 @@ const Manage = ({ url, t }: Props): JSX.Element => {
|
|
|
61
62
|
<Report margin={ 15 } question={ t('locations_manage.report', { ns: 'common' }) } type="location" t={ t } />
|
|
62
63
|
|
|
63
64
|
<Browse routeId={ id } data={ data?.locations } t={ t } onReload={ onReload } />
|
|
65
|
+
|
|
66
|
+
{ /* Claude - 2026-08-22: the stops taken off this route. Withdrawal
|
|
67
|
+
keeps everything - the row, the price rows, the fares - so it is
|
|
68
|
+
reversible, and the endpoint and hook to reverse it already
|
|
69
|
+
existed. Nothing rendered them, so a withdrawn stop was invisible
|
|
70
|
+
and permanent from here. Renders nothing when there are none. */ }
|
|
71
|
+
<Withdrawn routeId={ id } data={ data?.withdrawn } t={ t } onReload={ onReload } />
|
|
64
72
|
</Meta>
|
|
65
73
|
);
|
|
66
74
|
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { TFunction } from 'i18next';
|
|
2
|
+
import { FiRotateCcw } from 'react-icons/fi';
|
|
3
|
+
import { success } from '@autobusal/utilities';
|
|
4
|
+
import { LocationData } from '@autobusal/providers/types/locations';
|
|
5
|
+
import { useRestoreLocation } from '../services';
|
|
6
|
+
import { Section, Lead, Row, Where, Restore } from './styles';
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
routeId: number
|
|
10
|
+
data?: LocationData[]
|
|
11
|
+
t: TFunction<'normal'>
|
|
12
|
+
onReload: () => void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The stops taken off this route, and the way to put one back.
|
|
17
|
+
*
|
|
18
|
+
* Claude - Date: 2026-08-22
|
|
19
|
+
*
|
|
20
|
+
* Withdrawal never destroys anything - the stop's row, its price rows and
|
|
21
|
+
* the fares on them all survive, which is what lets a ticket sold through it
|
|
22
|
+
* still say where its passenger boarded. That makes it reversible by
|
|
23
|
+
* construction, and obtapi has had the endpoint and this package has had the
|
|
24
|
+
* `useRestoreLocation` hook since the day withdrawal shipped.
|
|
25
|
+
*
|
|
26
|
+
* Nothing rendered them. `locations` filters on `active`, so the response
|
|
27
|
+
* never mentioned a withdrawn stop and no screen could offer to bring one
|
|
28
|
+
* back: from the operator's side a withdrawal was invisible and permanent,
|
|
29
|
+
* which is the one thing it was built not to be. This is the missing half.
|
|
30
|
+
*
|
|
31
|
+
* Shown only when there is something to show - an operator who has never
|
|
32
|
+
* withdrawn a stop should not be told about a concept they have not met.
|
|
33
|
+
*/
|
|
34
|
+
const Withdrawn = ({ routeId, data, t, onReload }: Props): JSX.Element | null => {
|
|
35
|
+
const { mutate: Restoring, isPending } = useRestoreLocation(routeId);
|
|
36
|
+
|
|
37
|
+
if (!data?.length) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const restore = (id: number): void => {
|
|
42
|
+
Restoring(id, {
|
|
43
|
+
onSuccess: () => {
|
|
44
|
+
success(t('locations_manage.withdrawn.restored', { ns: 'common' }));
|
|
45
|
+
|
|
46
|
+
onReload();
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<Section className="box">
|
|
53
|
+
<h4>{ t('locations_manage.withdrawn.title', { ns: 'common' }) }</h4>
|
|
54
|
+
|
|
55
|
+
<Lead>{ t('locations_manage.withdrawn.lead', { ns: 'common' }) }</Lead>
|
|
56
|
+
|
|
57
|
+
{ data.map(item => (
|
|
58
|
+
<Row key={ item.id }>
|
|
59
|
+
<Where>
|
|
60
|
+
<strong>{ item.city?.name }</strong>
|
|
61
|
+
<span>{ item.stop?.name }</span>
|
|
62
|
+
</Where>
|
|
63
|
+
|
|
64
|
+
<Restore type="button" disabled={ isPending } onClick={ () => restore(item.id) }>
|
|
65
|
+
<FiRotateCcw />
|
|
66
|
+
{ t('locations_manage.withdrawn.restore', { ns: 'common' }) }
|
|
67
|
+
</Restore>
|
|
68
|
+
</Row>
|
|
69
|
+
)) }
|
|
70
|
+
</Section>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export default Withdrawn;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const Section = styled.div`
|
|
4
|
+
margin-bottom: 15px;
|
|
5
|
+
`;
|
|
6
|
+
|
|
7
|
+
export const Lead = styled.p`
|
|
8
|
+
margin-bottom: 12px;
|
|
9
|
+
font-size: ${ props => props.theme.size.xs };
|
|
10
|
+
opacity: .8;
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
export const Row = styled.div`
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-wrap: wrap;
|
|
16
|
+
align-items: center;
|
|
17
|
+
justify-content: space-between;
|
|
18
|
+
gap: 10px;
|
|
19
|
+
padding: 8px 0;
|
|
20
|
+
border-top: 1px solid ${ props => props.theme.background.neutral };
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
export const Where = styled.div`
|
|
24
|
+
font-size: ${ props => props.theme.size.xs };
|
|
25
|
+
min-width: 0;
|
|
26
|
+
|
|
27
|
+
strong {
|
|
28
|
+
display: block;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
span {
|
|
32
|
+
display: block;
|
|
33
|
+
opacity: .7;
|
|
34
|
+
word-break: break-word;
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
export const Restore = styled.button`
|
|
39
|
+
background: none;
|
|
40
|
+
border: 1px solid ${ props => props.theme.background.neutral };
|
|
41
|
+
border-radius: 3px;
|
|
42
|
+
padding: 5px 12px;
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
font-size: ${ props => props.theme.size.xs };
|
|
45
|
+
|
|
46
|
+
svg {
|
|
47
|
+
margin-right: 6px;
|
|
48
|
+
vertical-align: -2px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
&:disabled {
|
|
52
|
+
opacity: .5;
|
|
53
|
+
cursor: default;
|
|
54
|
+
}
|
|
55
|
+
`;
|