@autobusal/operator-routes 1.10.4 → 1.10.5

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.
@@ -54,6 +54,7 @@ const Browse = ({ id, type, data, name, t, onReload }: Props): JSX.Element => {
54
54
  id={ id }
55
55
  type={ type }
56
56
  group={ group }
57
+ others={ groups.length > 1 }
57
58
  name={ name }
58
59
  t={ t }
59
60
  onReload={ onReload }
@@ -3,18 +3,21 @@ import { TFunction } from 'i18next';
3
3
  import { useForm } from 'react-hook-form';
4
4
  import { BiEditAlt } from 'react-icons/bi';
5
5
  import { IoArrowDown } from 'react-icons/io5';
6
- import { FaRegCopy } from 'react-icons/fa';
6
+ import { FaRegCopy, FaArrowDown } from 'react-icons/fa';
7
7
  import { Button } from '@autobusal/common';
8
8
  import { Validate, Display, success } from '@autobusal/utilities';
9
9
  import { Group as Container, Header, Origin, Country, Tools, Dirty, Copy, Columns, Column, Fill, Row, Destination, Changed } from './styles';
10
10
  import { FareColumn, GroupForm, OriginGroup } from '../types';
11
11
  import { COLUMNS, field, toForm, toPrices } from '../utilities';
12
- import { usePostPrices, usePostCopy } from '../services';
12
+ import { usePostPrices, usePostCopy, usePostSpread } from '../services';
13
13
 
14
14
  interface Props {
15
15
  id: number
16
16
  type: 'normal' | 'alternate'
17
17
  group: OriginGroup
18
+
19
+ /** whether the route has more than this one origin - nothing to spread to otherwise */
20
+ others: boolean
18
21
  name: string
19
22
  t: TFunction<'normal'>
20
23
  onReload: () => void
@@ -35,7 +38,7 @@ interface Props {
35
38
  * that changed carry a dot - on a twelve-destination group, pressing Save
36
39
  * should not be a guess about what you edited near the top.
37
40
  */
38
- const Group = ({ id, type, group, name, t, onReload }: Props): JSX.Element => {
41
+ const Group = ({ id, type, group, others, name, t, onReload }: Props): JSX.Element => {
39
42
  const defaults = useMemo(() => toForm(group.prices), [ group.prices ]);
40
43
 
41
44
  const { register, handleSubmit, setValue, getValues, reset, formState: { errors, isDirty, dirtyFields } } = useForm<GroupForm>({
@@ -54,6 +57,7 @@ const Group = ({ id, type, group, name, t, onReload }: Props): JSX.Element => {
54
57
 
55
58
  const { mutate: Save, isPending } = usePostPrices(id, type, name);
56
59
  const { mutate: CopyPrices, isPending: copying } = usePostCopy(id, type, name);
60
+ const { mutate: Spread, isPending: spreading } = usePostSpread(id, type, name);
57
61
 
58
62
  const city = group.from.city;
59
63
 
@@ -115,6 +119,29 @@ const Group = ({ id, type, group, name, t, onReload }: Props): JSX.Element => {
115
119
  });
116
120
  };
117
121
 
122
+ /**
123
+ * Hand this origin's fares to every other origin, destination by
124
+ * destination.
125
+ *
126
+ * Claude - 2026-08-30. The confirm spells out what it will do rather than
127
+ * asking "are you sure": this overwrites every other group on the route and
128
+ * there is no undo, so the sentence has to be the thing the operator checks
129
+ * before agreeing, not a formality they click through.
130
+ */
131
+ const onSpread = (): void => {
132
+ if (!window.confirm(t('prices_manage.group.spread_ask', { city: city.name, ns: 'common' }))) {
133
+ return;
134
+ }
135
+
136
+ Spread({ location_from: group.from.id }, {
137
+ onSuccess: () => {
138
+ success(t('prices_manage.messages.spread', { city: city.name, ns: 'common' }));
139
+
140
+ onReload();
141
+ }
142
+ });
143
+ };
144
+
118
145
  /**
119
146
  * Fill one column down the group from its first destination.
120
147
  *
@@ -157,6 +184,18 @@ const Group = ({ id, type, group, name, t, onReload }: Props): JSX.Element => {
157
184
  </Copy>
158
185
  ) }
159
186
 
187
+ { others && (
188
+ <Copy
189
+ type="button"
190
+ disabled={ spreading }
191
+ onClick={ onSpread }
192
+ title={ t('prices_manage.group.spread_hint', { ns: 'common' }) }
193
+ >
194
+ <FaArrowDown />
195
+ { t('prices_manage.group.spread', { ns: 'common' }) }
196
+ </Copy>
197
+ ) }
198
+
160
199
  <Button
161
200
  type="submit"
162
201
  active={ isDirty }
@@ -72,6 +72,33 @@ export const usePostPrices = (id: number, type: ('normal' | 'alternate'), name:
72
72
  })
73
73
  );
74
74
 
75
+ /**
76
+ * Give every other origin this one's fares, matched destination by
77
+ * destination.
78
+ *
79
+ * Claude - 2026-08-30. Not the same question as usePostCopy below: that one
80
+ * flattens a set of fares to a single figure, this one carries the SHAPE of
81
+ * an origin across - whatever it charges to Thessaloniki becomes what every
82
+ * other stop charges to Thessaloniki.
83
+ */
84
+ export const usePostSpread = (id: number, type: ('normal' | 'alternate'), name: string): UseMutationResult<{ copied: number }, Error, { location_from: number }, unknown> => (
85
+ useMutation({
86
+ mutationKey: ['operator-prices-spread', { id, type, name }],
87
+ mutationFn: async (data: { location_from: number }) => (
88
+ await apiClient
89
+ .post('/api/prices/operator/copy-origins', {
90
+ ...data,
91
+ id,
92
+ type,
93
+ name
94
+ })
95
+ .then(response => (
96
+ response.data
97
+ ))
98
+ )
99
+ })
100
+ );
101
+
75
102
  /**
76
103
  * Copy the first fare over the rest.
77
104
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autobusal/operator-routes",
3
- "version": "1.10.4",
3
+ "version": "1.10.5",
4
4
  "author": "Ferjolt Ozuni",
5
5
  "type": "module",
6
6
  "main": "index.ts"