@autobusal/routes-order 1.33.2 → 1.33.4
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 +12 -0
- package/Checkout/Checkout.tsx +17 -1
- package/Step5/Payments/Payments.tsx +11 -3
- package/Step5/utilities.ts +5 -0
- package/package.json +1 -1
- package/services.ts +11 -3
- package/types.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.33.4
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Complimentary in the web checkout.** An operator/employee selling on their OWN operator's route is offered the zero-cost Complimentary option the mobile app already had - the route-aware flag comes from `/api/funds/allowed?route=...` and the ownership rule is re-checked server-side at payment time. (Audit decision BOOK-07; needs obtapi >= 2.4.37.)
|
|
8
|
+
|
|
9
|
+
## 1.33.3
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **Checkout swallowed every booking error.** A 4xx from order creation - a seat taken while the form was open, the unpaid-hold limit, a route gone unavailable - left the buyer on the filled form with no message. It now surfaces the API's message. (Audit BOOK-13-c.)
|
|
14
|
+
|
|
3
15
|
## 1.33.2
|
|
4
16
|
|
|
5
17
|
### Fixed
|
package/Checkout/Checkout.tsx
CHANGED
|
@@ -27,6 +27,7 @@ import { useGetSettings } from '@autobusal/providers/services';
|
|
|
27
27
|
import { usePostOrder, useGetFlex, useGetPayments, useGetSavedPassengers } from '../services';
|
|
28
28
|
import { addon as commerceAddon, beginCheckout, stash } from '@autobusal/providers/Setup/commerce';
|
|
29
29
|
import { item as commerceItem } from '../commerce';
|
|
30
|
+
import { error as notifyError } from '@autobusal/utilities';
|
|
30
31
|
|
|
31
32
|
interface Props {
|
|
32
33
|
values?: PersonData
|
|
@@ -113,7 +114,7 @@ const Checkout = ({ values, step1, step2, step3, isPartner, t, onStore, onBack }
|
|
|
113
114
|
* answers it from cache - this reads the answer, it does not fetch it a
|
|
114
115
|
* second time.
|
|
115
116
|
*/
|
|
116
|
-
const { data: paymentsData } = useGetPayments();
|
|
117
|
+
const { data: paymentsData } = useGetPayments(step2.id);
|
|
117
118
|
|
|
118
119
|
const singleMethod = getAvailablePayments(paymentsData, isPartner ?? false).length === 1;
|
|
119
120
|
|
|
@@ -351,6 +352,20 @@ const Checkout = ({ values, step1, step2, step3, isPartner, t, onStore, onBack }
|
|
|
351
352
|
);
|
|
352
353
|
|
|
353
354
|
navigate(`/orders/process/${ action }/${ hash }`);
|
|
355
|
+
},
|
|
356
|
+
/*
|
|
357
|
+
* Edited: Claude - Date: 2026-08-22 (audit finding BOOK-13-c)
|
|
358
|
+
*
|
|
359
|
+
* Surface why the booking was refused. Every 4xx from order creation -
|
|
360
|
+
* a seat taken while the form was open, the unpaid-hold limit, a route
|
|
361
|
+
* that just went unavailable - left the buyer on the filled form with
|
|
362
|
+
* no message, clicking Pay again. The API's own message says exactly
|
|
363
|
+
* what happened.
|
|
364
|
+
*/
|
|
365
|
+
onError: (submitError: unknown) => {
|
|
366
|
+
const message = (submitError as { response?: { data?: { message?: string } } })?.response?.data?.message;
|
|
367
|
+
|
|
368
|
+
notifyError(message ?? t('routes_order.checkout.error', { ns: 'common', defaultValue: 'We could not complete your booking. Please try again.' }));
|
|
354
369
|
}
|
|
355
370
|
});
|
|
356
371
|
};
|
|
@@ -432,6 +447,7 @@ const Checkout = ({ values, step1, step2, step3, isPartner, t, onStore, onBack }
|
|
|
432
447
|
<Payments
|
|
433
448
|
selected={ action ?? '' }
|
|
434
449
|
isPartner={ isPartner ?? false }
|
|
450
|
+
routeId={ step2.id }
|
|
435
451
|
t={ t }
|
|
436
452
|
onChange={ onAction }
|
|
437
453
|
/>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
2
|
import { TFunction } from 'i18next';
|
|
3
3
|
import { RiSecurePaymentLine } from 'react-icons/ri';
|
|
4
|
-
import { FaRegHandPaper, FaMoneyBill } from 'react-icons/fa';
|
|
4
|
+
import { FaRegHandPaper, FaMoneyBill, FaGift } from 'react-icons/fa';
|
|
5
5
|
import { AiOutlineCreditCard } from 'react-icons/ai';
|
|
6
6
|
import { Inline } from '@autobusal/common';
|
|
7
7
|
import { SubTitle } from '../../styles';
|
|
@@ -12,12 +12,13 @@ import { getAvailablePayments } from '../utilities';
|
|
|
12
12
|
interface Props {
|
|
13
13
|
selected: string
|
|
14
14
|
isPartner: boolean
|
|
15
|
+
routeId?: number | string
|
|
15
16
|
t: TFunction<'common'>
|
|
16
17
|
onChange: (action: string) => void
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
const Payments = ({ selected, isPartner, t, onChange }: Props): JSX.Element | null => {
|
|
20
|
-
const { data, isLoading, isSuccess } = useGetPayments();
|
|
20
|
+
const Payments = ({ selected, isPartner, routeId, t, onChange }: Props): JSX.Element | null => {
|
|
21
|
+
const { data, isLoading, isSuccess } = useGetPayments(routeId);
|
|
21
22
|
|
|
22
23
|
const available = getAvailablePayments(data, isPartner);
|
|
23
24
|
|
|
@@ -90,6 +91,13 @@ const Payments = ({ selected, isPartner, t, onChange }: Props): JSX.Element | nu
|
|
|
90
91
|
</ButtonPayment>
|
|
91
92
|
) }
|
|
92
93
|
|
|
94
|
+
{ (data?.complimentary && !isPartner) && (
|
|
95
|
+
<ButtonPayment type="button" $selected={ selected === 'complimentary' } onClick={ () => onChange('complimentary') }>
|
|
96
|
+
<FaGift />
|
|
97
|
+
{ t('routes_order.step5.actions.complimentary') }
|
|
98
|
+
</ButtonPayment>
|
|
99
|
+
) }
|
|
100
|
+
|
|
93
101
|
{ (data?.bkt || isPartner) && (
|
|
94
102
|
<ButtonPayment type="button" $selected={ selected === 'bkt' } onClick={ () => onChange('bkt') }>
|
|
95
103
|
<AiOutlineCreditCard />
|
package/Step5/utilities.ts
CHANGED
|
@@ -51,6 +51,11 @@ export const getAvailablePayments = (data?: ActionData, isPartner = false): stri
|
|
|
51
51
|
if (data.reserve && !isPartner) { available.push('reserve'); }
|
|
52
52
|
if (data.funds && !isPartner) { available.push('funds', 'cash'); }
|
|
53
53
|
|
|
54
|
+
// Claude - 2026-08-22 (audit decision BOOK-07): an operator/employee on
|
|
55
|
+
// their own operator's route pays nothing - the flag is route-aware
|
|
56
|
+
// (services.ts) and re-checked server-side at payment time.
|
|
57
|
+
if (data.complimentary && !isPartner) { available.push('complimentary'); }
|
|
58
|
+
|
|
54
59
|
if (data.bkt || isPartner) { available.push('bkt'); }
|
|
55
60
|
if (data.stripe || isPartner) { available.push('stripe'); }
|
|
56
61
|
if (data.mollie || isPartner) { available.push('mollie'); }
|
package/package.json
CHANGED
package/services.ts
CHANGED
|
@@ -222,12 +222,20 @@ export const useGetCoupon = (routeId: number, total: number): UseMutationResult<
|
|
|
222
222
|
})
|
|
223
223
|
);
|
|
224
224
|
|
|
225
|
-
|
|
225
|
+
/*
|
|
226
|
+
* Claude - 2026-08-22 (audit decision BOOK-07): asks with the route being
|
|
227
|
+
* sold, so the API can offer Complimentary to an operator/employee on
|
|
228
|
+
* their own operator's route. External routes carry string ids and can
|
|
229
|
+
* never be own inventory, so those simply don't send the parameter.
|
|
230
|
+
*/
|
|
231
|
+
export const useGetPayments = (routeId?: number | string): UseQueryResult<ActionData> => (
|
|
226
232
|
useQuery({
|
|
227
|
-
queryKey: ['allowed-funds'],
|
|
233
|
+
queryKey: ['allowed-funds', { routeId }],
|
|
228
234
|
queryFn: async () => (
|
|
229
235
|
await apiClient
|
|
230
|
-
.get('/api/funds/allowed'
|
|
236
|
+
.get('/api/funds/allowed', {
|
|
237
|
+
params: typeof routeId === 'number' ? { route: routeId } : {}
|
|
238
|
+
})
|
|
231
239
|
.then(response => (
|
|
232
240
|
response.data
|
|
233
241
|
))
|