@autobusal/routes-order 1.38.0 → 1.38.2
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/Availability/Availability.tsx +35 -1
- package/Found/Dates/Dates.tsx +3 -0
- package/RoutesOrder.tsx +4 -1
- package/Step1/Step1.tsx +5 -1
- package/Step5/Billing/Billing.tsx +18 -0
- package/package.json +1 -1
- package/services.ts +6 -1
- package/styles.ts +9 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TFunction } from 'i18next';
|
|
2
|
+
import { Link } from 'react-router-dom';
|
|
2
3
|
import { NextDay } from '../Schedule/styles';
|
|
3
4
|
import { Container, Heading, Lead, Wrap, Table, Time, Faded, Fare, Price, SeatsLeft, Book, Later, More, Updated } from './styles';
|
|
4
5
|
import { LOW_SEATS, Day, Prepared } from './service';
|
|
@@ -27,6 +28,31 @@ interface Props {
|
|
|
27
28
|
*/
|
|
28
29
|
export const VISIBLE = 7;
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Claude - 2026-09-25 (audit): the API builds each row's `url` for the
|
|
33
|
+
* partner/assistant feed - absolute, and tagged
|
|
34
|
+
* utm_source=busmagus-api&utm_medium=assistant. Rendered as-is on our own
|
|
35
|
+
* pair page, every "Book" click was a full page reload that credited our own
|
|
36
|
+
* visitors to the assistant channel. On this origin it becomes an in-app
|
|
37
|
+
* route with those two tags removed; any other origin keeps the plain link.
|
|
38
|
+
*/
|
|
39
|
+
const spaTarget = (url: string): string | null => {
|
|
40
|
+
try {
|
|
41
|
+
const parsed = new URL(url, window.location.href);
|
|
42
|
+
|
|
43
|
+
if (parsed.origin !== window.location.origin) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
parsed.searchParams.delete('utm_source');
|
|
48
|
+
parsed.searchParams.delete('utm_medium');
|
|
49
|
+
|
|
50
|
+
return parsed.pathname + parsed.search;
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
30
56
|
/**
|
|
31
57
|
* Real departures on real dates, for the next thirty days.
|
|
32
58
|
*
|
|
@@ -113,7 +139,15 @@ const Availability = ({ id, data, t }: Props): JSX.Element => {
|
|
|
113
139
|
</td>
|
|
114
140
|
|
|
115
141
|
<td>
|
|
116
|
-
{ row.url
|
|
142
|
+
{ row.url
|
|
143
|
+
? (() => {
|
|
144
|
+
const to = spaTarget(row.url);
|
|
145
|
+
|
|
146
|
+
return to
|
|
147
|
+
? <Book as={ Link } to={ to }>{ t('routes_order.availability.book') }</Book>
|
|
148
|
+
: <Book href={ row.url }>{ t('routes_order.availability.book') }</Book>;
|
|
149
|
+
})()
|
|
150
|
+
: null }
|
|
117
151
|
</td>
|
|
118
152
|
</tr>
|
|
119
153
|
)) }
|
package/Found/Dates/Dates.tsx
CHANGED
|
@@ -176,6 +176,8 @@ const Dates = ({ type, step1, t, onSearch }: Props): JSX.Element => {
|
|
|
176
176
|
size="small"
|
|
177
177
|
noMargin
|
|
178
178
|
text={ <AiOutlineCaretLeft /> }
|
|
179
|
+
// Claude - 2026-09-25 (audit): icon-only, so name it for screen readers
|
|
180
|
+
ariaLabel={ t('routes_order.step2.dates.previous') }
|
|
179
181
|
onClick={ onPrevious }
|
|
180
182
|
/>
|
|
181
183
|
|
|
@@ -186,6 +188,7 @@ const Dates = ({ type, step1, t, onSearch }: Props): JSX.Element => {
|
|
|
186
188
|
size="small"
|
|
187
189
|
noMargin
|
|
188
190
|
text={ <AiOutlineCaretRight /> }
|
|
191
|
+
ariaLabel={ t('routes_order.step2.dates.next') }
|
|
189
192
|
onClick={ onNext }
|
|
190
193
|
/>
|
|
191
194
|
</Inner>
|
package/RoutesOrder.tsx
CHANGED
|
@@ -432,7 +432,10 @@ const RoutesOrder = ({ t }: Props): JSX.Element => {
|
|
|
432
432
|
<Step1
|
|
433
433
|
value={ step1 }
|
|
434
434
|
redirected={ redirected }
|
|
435
|
-
|
|
435
|
+
// Claude - 2026-09-25 (audit): not a heading at all on the pair page -
|
|
436
|
+
// as an h2 it still sat ABOVE the facts h1, so the document outline
|
|
437
|
+
// opened on a second-level heading (an icon plus "Search Schedules")
|
|
438
|
+
heading={ subject ? 'div' : 'h1' }
|
|
436
439
|
t={ t }
|
|
437
440
|
onSave={ onStep1 }
|
|
438
441
|
/>
|
package/Step1/Step1.tsx
CHANGED
|
@@ -22,8 +22,12 @@ interface Props {
|
|
|
22
22
|
* /bus-lines/{from}/{to} page named "Search Schedules" to crawlers and
|
|
23
23
|
* screen readers alike. RoutesOrder decides, because it is the one place
|
|
24
24
|
* that knows whether the facts block is going to render.
|
|
25
|
+
*
|
|
26
|
+
* Claude - 2026-09-25 (audit): 'div' added - the pair page's form label is
|
|
27
|
+
* no heading at all now (an h2 before the page's h1 broke the outline);
|
|
28
|
+
* Title carries the heading look itself so it renders the same.
|
|
25
29
|
*/
|
|
26
|
-
heading?: 'h1' | 'h2'
|
|
30
|
+
heading?: 'h1' | 'h2' | 'div'
|
|
27
31
|
t: TFunction<'common'>
|
|
28
32
|
onSave: (data: RoutesSearchForm) => void
|
|
29
33
|
}
|
|
@@ -180,6 +180,24 @@ const Billing = ({ errors, t, refs, only }: Props): JSX.Element => {
|
|
|
180
180
|
</div>
|
|
181
181
|
) }
|
|
182
182
|
|
|
183
|
+
{ /* Claude - 2026-09-25 (Ferjolt): a PRIVATE buyer's identification for
|
|
184
|
+
the fiscal invoice - a company is identified by its VAT number
|
|
185
|
+
below, a person by a personal ID number; without one the first
|
|
186
|
+
passenger's passport stands in, so it stays optional. */ }
|
|
187
|
+
{ asked('bill_company') && !company && (
|
|
188
|
+
<div className="row">
|
|
189
|
+
<label htmlFor={ id('bill_id_number') }>{ t('routes_order.step5.billing.id_number') }</label>
|
|
190
|
+
|
|
191
|
+
<input type="text" id={ id('bill_id_number') } { ...refs('bill_id_number', Validate('max_length:50', t)) } />
|
|
192
|
+
|
|
193
|
+
<NoticeEmail>
|
|
194
|
+
<AiOutlineInfoCircle /> { t('routes_order.step5.billing.id_number_info') }
|
|
195
|
+
</NoticeEmail>
|
|
196
|
+
|
|
197
|
+
{ Display(errors.bill_id_number) }
|
|
198
|
+
</div>
|
|
199
|
+
) }
|
|
200
|
+
|
|
183
201
|
{ asked('bill_company') && company && (
|
|
184
202
|
<div className="column">
|
|
185
203
|
<div className="row">
|
package/package.json
CHANGED
package/services.ts
CHANGED
|
@@ -241,7 +241,12 @@ export const usePostOrder = (
|
|
|
241
241
|
|
|
242
242
|
if (data !== undefined) {
|
|
243
243
|
Object.keys(data).forEach(index => {
|
|
244
|
-
|
|
244
|
+
const value = data[index as keyof typeof data];
|
|
245
|
+
|
|
246
|
+
// an optional billing field the buyer left out is not sent at all
|
|
247
|
+
if (value !== undefined) {
|
|
248
|
+
order[index] = value;
|
|
249
|
+
}
|
|
245
250
|
});
|
|
246
251
|
}
|
|
247
252
|
|
package/styles.ts
CHANGED
|
@@ -10,6 +10,15 @@ export const Title = styled.h1`
|
|
|
10
10
|
* document-outline decision, not a request for a smaller heading.
|
|
11
11
|
*/
|
|
12
12
|
font-size: ${ props => props.theme.size.xxl };
|
|
13
|
+
/*
|
|
14
|
+
* Claude - 2026-09-25 (audit): the global h1-h6 rules, restated, because
|
|
15
|
+
* the pair page now renders this as a <div> (not a heading - see Step1's
|
|
16
|
+
* heading prop) and it must look exactly like the h1 it is elsewhere.
|
|
17
|
+
*/
|
|
18
|
+
margin: 0 0 20px 0;
|
|
19
|
+
font-weight: 700;
|
|
20
|
+
line-height: ${ props => props.theme.lineHeight.title };
|
|
21
|
+
font-family: ${ props => props.theme.fontFamily.title }, sans-serif;
|
|
13
22
|
`;
|
|
14
23
|
|
|
15
24
|
export const SubTitle = styled.h3`
|