@autobusal/routes-order 1.34.2 → 1.34.3
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,3 +1,7 @@
|
|
|
1
|
+
## 1.34.3 - 2026-08-29
|
|
2
|
+
|
|
3
|
+
- Staff sellers (agent/subagent/operator/employee/admin) get an OPTIONAL passenger email box at checkout, so a counter-sold ticket reaches the passenger instead of only the seller and operator (audit A4-05, owner decision). obtapi already read {type}{i}_email/_notify_email; it now validates them too.
|
|
4
|
+
|
|
1
5
|
## 1.34.2 - 2026-08-29
|
|
2
6
|
|
|
3
7
|
- With more than one card gateway available, each Credit Card button carries its processor's name - they used to render byte-identical (audit A0-06).
|
package/Checkout/Checkout.tsx
CHANGED
|
@@ -442,6 +442,14 @@ const Checkout = ({ values, step1, step2, step3, isPartner, t, onStore, onBack }
|
|
|
442
442
|
values={ values }
|
|
443
443
|
step1={ step1 }
|
|
444
444
|
fields={ fields }
|
|
445
|
+
/*
|
|
446
|
+
* Claude - 2026-08-29 (audit A4-05, owner decision): only staff
|
|
447
|
+
* are asked. They are the sellers with no billing block - see
|
|
448
|
+
* `gaps` above - so this optional box is the only way a
|
|
449
|
+
* counter-sold ticket reaches the passenger directly instead of
|
|
450
|
+
* landing solely with the agent and the operator.
|
|
451
|
+
*/
|
|
452
|
+
askEmail={ UserData !== undefined && ['agent', 'subagent', 'operator', 'employee', 'admin'].includes(UserData.type) }
|
|
445
453
|
busFrom={ busFrom }
|
|
446
454
|
busTo={ busTo }
|
|
447
455
|
saved={ saved }
|
|
@@ -15,6 +15,15 @@ interface Props {
|
|
|
15
15
|
number: number
|
|
16
16
|
values?: PersonData
|
|
17
17
|
fields: string[]
|
|
18
|
+
/**
|
|
19
|
+
* Claude - 2026-08-29 (audit A4-05, owner decision): staff selling at the
|
|
20
|
+
* counter get an OPTIONAL email box per passenger. They are the only
|
|
21
|
+
* sellers with no billing block (an agent or operator writes no
|
|
22
|
+
* orders_contact row), so without it a walk-in customer's ticket reached
|
|
23
|
+
* only the agent and the operator and had to be Re-Sent by hand. Optional
|
|
24
|
+
* on purpose: a counter sale often has no email to give.
|
|
25
|
+
*/
|
|
26
|
+
askEmail?: boolean
|
|
18
27
|
withReturn: boolean
|
|
19
28
|
busFrom: OccupiedData
|
|
20
29
|
busTo: OccupiedData
|
|
@@ -47,7 +56,7 @@ interface Props {
|
|
|
47
56
|
// telegram render ONLY when the route requires them - and are then
|
|
48
57
|
// required. Name and date of birth stay mandatory always. `fields` is the
|
|
49
58
|
// union of both legs on a return trip (see Step4).
|
|
50
|
-
const Passenger = ({ type, number, values, fields, withReturn, busFrom, busTo, pickedFrom, pickedTo, saved, savedMax, seatsOptional, seatsFee, t, errors, refs, onSeatSelect, onUpdate }: Props): JSX.Element => {
|
|
59
|
+
const Passenger = ({ type, number, values, fields, askEmail, withReturn, busFrom, busTo, pickedFrom, pickedTo, saved, savedMax, seatsOptional, seatsFee, t, errors, refs, onSeatSelect, onUpdate }: Props): JSX.Element => {
|
|
51
60
|
const name = `${ type }${ number }`;
|
|
52
61
|
|
|
53
62
|
const names = {
|
|
@@ -65,7 +74,13 @@ const Passenger = ({ type, number, values, fields, withReturn, busFrom, busTo, p
|
|
|
65
74
|
// Edited: Claude - Date: 2026-08-20
|
|
66
75
|
// "Remember this passenger" - obtapi reads `{type}{i}_save` alongside
|
|
67
76
|
// the `_fn` / `_pass` fields this form already posts.
|
|
68
|
-
save: `${ name }_save
|
|
77
|
+
save: `${ name }_save`,
|
|
78
|
+
|
|
79
|
+
// the counter-sale email (audit A4-05). obtapi already reads
|
|
80
|
+
// `{type}{i}_email` + `_notify_email` - see Orders\Make\Tickets - it
|
|
81
|
+
// was only the web form that never offered them.
|
|
82
|
+
email: `${ name }_email`,
|
|
83
|
+
notifyEmail: `${ name }_notify_email`
|
|
69
84
|
};
|
|
70
85
|
|
|
71
86
|
const has = (field: string): boolean => fields.includes(field);
|
|
@@ -340,6 +355,31 @@ const Passenger = ({ type, number, values, fields, withReturn, busFrom, busTo, p
|
|
|
340
355
|
</div>
|
|
341
356
|
) }
|
|
342
357
|
|
|
358
|
+
{ askEmail && (
|
|
359
|
+
<div className="column">
|
|
360
|
+
<div className="row">
|
|
361
|
+
<label htmlFor={ id(names.email) }>{ t('routes_order.step4.email') }</label>
|
|
362
|
+
|
|
363
|
+
<input
|
|
364
|
+
type="email"
|
|
365
|
+
key={ `${ names.email }-${ applied }` }
|
|
366
|
+
id={ id(names.email) }
|
|
367
|
+
defaultValue={ start(names.email, undefined) }
|
|
368
|
+
{ ...refs(names.email, Validate('email|max_length:255', t)) }
|
|
369
|
+
/>
|
|
370
|
+
|
|
371
|
+
{ Display(errors[names.email]) }
|
|
372
|
+
|
|
373
|
+
{ /* the ticket goes to this address as well as to the seller -
|
|
374
|
+
obtapi gates the per-passenger copy on `_notify_email`, so
|
|
375
|
+
an address typed here has to arrive with the flag set */ }
|
|
376
|
+
<input type="hidden" value="1" { ...refs(names.notifyEmail) } />
|
|
377
|
+
|
|
378
|
+
<Notice>{ t('routes_order.step4.email_notice') }</Notice>
|
|
379
|
+
</div>
|
|
380
|
+
</div>
|
|
381
|
+
) }
|
|
382
|
+
|
|
343
383
|
{ (has('whatsapp') || has('telegram')) && (
|
|
344
384
|
<div className="column">
|
|
345
385
|
{ has('whatsapp') && (
|
|
@@ -8,6 +8,8 @@ interface Props {
|
|
|
8
8
|
type: 'adult' | 'child' | 'baby'
|
|
9
9
|
amount: number
|
|
10
10
|
fields: string[]
|
|
11
|
+
/** staff sellers are offered an optional passenger email (audit A4-05) */
|
|
12
|
+
askEmail?: boolean
|
|
11
13
|
withReturn: boolean
|
|
12
14
|
values?: PersonData
|
|
13
15
|
busFrom: OccupiedData
|
|
@@ -25,7 +27,7 @@ interface Props {
|
|
|
25
27
|
onUpdate: (name: string, value: (string | number)) => void
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
const Display = ({ type, amount, fields, withReturn, values, busFrom, busTo, pickedFrom, pickedTo, saved, savedMax, seatsOptional, seatsFee, t, errors, refs, onSeatSelect, onUpdate }: Props): JSX.Element[] => {
|
|
30
|
+
const Display = ({ type, amount, fields, askEmail, withReturn, values, busFrom, busTo, pickedFrom, pickedTo, saved, savedMax, seatsOptional, seatsFee, t, errors, refs, onSeatSelect, onUpdate }: Props): JSX.Element[] => {
|
|
29
31
|
const passengers: JSX.Element[] = [];
|
|
30
32
|
|
|
31
33
|
for (let i = 1; i <= amount; i++) {
|
|
@@ -35,6 +37,7 @@ const Display = ({ type, amount, fields, withReturn, values, busFrom, busTo, pic
|
|
|
35
37
|
type={ type }
|
|
36
38
|
number={ i }
|
|
37
39
|
fields={ fields }
|
|
40
|
+
askEmail={ askEmail }
|
|
38
41
|
withReturn={ withReturn }
|
|
39
42
|
values={ values }
|
|
40
43
|
busFrom={ busFrom }
|
|
@@ -11,6 +11,8 @@ interface Props {
|
|
|
11
11
|
values?: PersonData
|
|
12
12
|
step1: RoutesSearchForm
|
|
13
13
|
fields: string[]
|
|
14
|
+
/** staff sellers are offered an optional passenger email (audit A4-05) */
|
|
15
|
+
askEmail?: boolean
|
|
14
16
|
busFrom: OccupiedData
|
|
15
17
|
busTo: OccupiedData
|
|
16
18
|
/**
|
|
@@ -38,7 +40,7 @@ interface Props {
|
|
|
38
40
|
onUpdate: (name: string, value: (string | number)) => void
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
const Passengers = ({ values, step1, fields, busFrom, busTo, saved, savedMax, seatsOptional, seatsFee, onPickedCount, t, errors, refs, onUpdate }: Props): JSX.Element => {
|
|
43
|
+
const Passengers = ({ values, step1, fields, askEmail, busFrom, busTo, saved, savedMax, seatsOptional, seatsFee, onPickedCount, t, errors, refs, onUpdate }: Props): JSX.Element => {
|
|
42
44
|
const [ pickedFrom, setPickedFrom ] = useState<number[]>([]);
|
|
43
45
|
const [ pickedTo, setPickedTo ] = useState<number[]>([]);
|
|
44
46
|
|
|
@@ -84,6 +86,7 @@ const Passengers = ({ values, step1, fields, busFrom, busTo, saved, savedMax, se
|
|
|
84
86
|
type="adult"
|
|
85
87
|
amount={ step1.adults }
|
|
86
88
|
fields={ fields }
|
|
89
|
+
askEmail={ askEmail }
|
|
87
90
|
withReturn={ step1.type === 'return' }
|
|
88
91
|
values={ values }
|
|
89
92
|
busFrom={ busFrom }
|
|
@@ -105,6 +108,7 @@ const Passengers = ({ values, step1, fields, busFrom, busTo, saved, savedMax, se
|
|
|
105
108
|
type="child"
|
|
106
109
|
amount={ step1.children }
|
|
107
110
|
fields={ fields }
|
|
111
|
+
askEmail={ askEmail }
|
|
108
112
|
withReturn={ step1.type === 'return' }
|
|
109
113
|
values={ values }
|
|
110
114
|
busFrom={ busFrom }
|
|
@@ -126,6 +130,7 @@ const Passengers = ({ values, step1, fields, busFrom, busTo, saved, savedMax, se
|
|
|
126
130
|
type="baby"
|
|
127
131
|
amount={ step1.babies }
|
|
128
132
|
fields={ fields }
|
|
133
|
+
askEmail={ askEmail }
|
|
129
134
|
withReturn={ step1.type === 'return' }
|
|
130
135
|
values={ values }
|
|
131
136
|
busFrom={ busFrom }
|