@autobusal/routes-order 1.37.4 → 1.37.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.
- package/Facts/questions.ts +39 -8
- package/Found/Route/Route.tsx +40 -1
- package/Found/Route/styles.ts +43 -1
- package/package.json +1 -1
package/Facts/questions.ts
CHANGED
|
@@ -170,17 +170,40 @@ const questions = (facts: FactsData, t: TFunction<'common'>): Question[] => {
|
|
|
170
170
|
});
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
/**
|
|
174
|
+
* DIRECT MEANS NO CHANGE OF BUS. That is the question being asked.
|
|
175
|
+
*
|
|
176
|
+
* Edited: Claude - Date: 2026-09-16
|
|
177
|
+
*
|
|
178
|
+
* This was answered out of `facts.direct` alone, which counts services
|
|
179
|
+
* with no intermediate STOPS - so it said "No. Every scheduled service
|
|
180
|
+
* calls at stops along the way" on 201 of the 202 published pairs, under
|
|
181
|
+
* a timetable showing a single coach running the whole route. Every bus
|
|
182
|
+
* in the Balkans calls somewhere, so the answer was structurally always
|
|
183
|
+
* no. A traveller reads that as "you have to change buses", and so does
|
|
184
|
+
* an assistant: ChatGPT quoted the contradiction back to us against the
|
|
185
|
+
* Thessaloniki-Tirana page.
|
|
186
|
+
*
|
|
187
|
+
* Nothing in Libraries\Routes\Search ever joins two routes into one
|
|
188
|
+
* journey, so every service that can be booked here is one bus end to
|
|
189
|
+
* end, and yes is not a generous reading - it is the fact.
|
|
190
|
+
*
|
|
191
|
+
* The stop count keeps its own word, NON-STOP, and its own sentence. It
|
|
192
|
+
* is the rarer and more valuable fact, still counted out of the total
|
|
193
|
+
* rather than claimed for the whole pair, but it no longer decides
|
|
194
|
+
* whether the pair has a direct bus at all.
|
|
195
|
+
*/
|
|
175
196
|
if (facts.departures > 0 && !dead) {
|
|
176
197
|
all.push({
|
|
177
198
|
id: 'direct',
|
|
178
199
|
q: t('routes_order.schema.direct_q', cities),
|
|
179
|
-
a: facts.direct ===
|
|
180
|
-
? t('routes_order.schema.
|
|
181
|
-
: (facts.direct
|
|
182
|
-
? t('routes_order.schema.
|
|
183
|
-
:
|
|
200
|
+
a: facts.direct === facts.departures
|
|
201
|
+
? t('routes_order.schema.direct_a_nonstop_all', cities)
|
|
202
|
+
: (facts.direct > 0
|
|
203
|
+
? t('routes_order.schema.direct_a_nonstop_some', { ...cities, count: facts.direct, total: facts.departures })
|
|
204
|
+
: (facts.departures === 1
|
|
205
|
+
? t('routes_order.schema.direct_a_through_single', cities)
|
|
206
|
+
: t('routes_order.schema.direct_a_through', { ...cities, total: facts.departures })))
|
|
184
207
|
});
|
|
185
208
|
}
|
|
186
209
|
|
|
@@ -190,9 +213,17 @@ const questions = (facts: FactsData, t: TFunction<'common'>): Question[] => {
|
|
|
190
213
|
all.push({
|
|
191
214
|
id: 'overnight',
|
|
192
215
|
q: t('routes_order.schema.overnight_q', cities),
|
|
216
|
+
// Edited: Claude - Date: 2026-09-16
|
|
217
|
+
// "1 of the 2 scheduled services ... travel overnight" was published
|
|
218
|
+
// on 40 pairs, in the visible FAQ and in the JSON-LD both. Its own
|
|
219
|
+
// key rather than an i18next plural, for the reason spelled out
|
|
220
|
+
// above departures_a_single: the eight Slavic locales here do not
|
|
221
|
+
// share English's one rule.
|
|
193
222
|
a: facts.overnight === facts.departures
|
|
194
223
|
? t('routes_order.schema.overnight_a_all', cities)
|
|
195
|
-
:
|
|
224
|
+
: (facts.overnight === 1
|
|
225
|
+
? t('routes_order.schema.overnight_a_single', { ...cities, total: facts.departures })
|
|
226
|
+
: t('routes_order.schema.overnight_a', { ...cities, count: facts.overnight, total: facts.departures }))
|
|
196
227
|
});
|
|
197
228
|
}
|
|
198
229
|
|
package/Found/Route/Route.tsx
CHANGED
|
@@ -8,9 +8,20 @@ import { formatDuration, splitPrice } from '../refine';
|
|
|
8
8
|
import { Button, RouteFeature, Information, Rating } from '@autobusal/common';
|
|
9
9
|
import { useGetSettings } from '@autobusal/providers/services';
|
|
10
10
|
import { createDate, getDayParts } from '@autobusal/utilities';
|
|
11
|
-
import { Container, ButtonDetails, SpecialOffer, FavoriteStop, Trip, Company, Location, Stop, Iso, Time, Price, Amount, Decimals, Currency, PriceNotice, LinkCompany, CodeCompany, Details, Detail, ButtonPolicy, TitleDetail, FeaturesItems, Leaves, Day, NextDay, Standard, Stops, StopsItems, StopRow, StopTime, StopDay, StopBorder } from './styles';
|
|
11
|
+
import { Container, ButtonDetails, SpecialOffer, FavoriteStop, Trip, Company, Location, Stop, Iso, Time, Price, Amount, Decimals, Currency, PriceNotice, SeatsLeft, LinkCompany, CodeCompany, Details, Detail, ButtonPolicy, TitleDetail, FeaturesItems, Leaves, Day, NextDay, Standard, Stops, StopsItems, StopRow, StopTime, StopDay, StopBorder } from './styles';
|
|
12
12
|
import { FoundData } from '@autobusal/providers/types/routes';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* At or below this many seats, the count is emphasised.
|
|
16
|
+
*
|
|
17
|
+
* Edited: Claude - Date: 2026-09-16
|
|
18
|
+
*
|
|
19
|
+
* A coach carries roughly fifty, so single figures are the point at which
|
|
20
|
+
* the number changes what somebody does about it rather than merely
|
|
21
|
+
* describing the bus.
|
|
22
|
+
*/
|
|
23
|
+
const LOW_SEATS = 5;
|
|
24
|
+
|
|
14
25
|
// neutral placeholder so a missing/broken operator logo doesn't render the
|
|
15
26
|
// browser's broken-image icon in the route results
|
|
16
27
|
const LOGO_FALLBACK = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='60' viewBox='0 0 120 60'%3E%3Crect width='120' height='60' rx='6' fill='%23e5e7eb'/%3E%3Cpath d='M42 22h36v14a2 2 0 0 1-2 2h-2a3 3 0 0 1-6 0H52a3 3 0 0 1-6 0h-2a2 2 0 0 1-2-2z' fill='%239ca3af'/%3E%3C/svg%3E";
|
|
@@ -78,6 +89,21 @@ const Route = ({ data, type, date, passengers, t, onSave, children }: Props): JS
|
|
|
78
89
|
? `${ import.meta.env.VITE_API_OBT }/api/external/go/${ data.external.offer_id }`
|
|
79
90
|
: undefined;
|
|
80
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Seats still on sale on this coach.
|
|
94
|
+
*
|
|
95
|
+
* Edited: Claude - Date: 2026-09-16
|
|
96
|
+
*
|
|
97
|
+
* Typed rather than truthy-checked: 0 is a real answer from obtapi and it
|
|
98
|
+
* means a full bus, which never reaches a result list - and a `!` test
|
|
99
|
+
* would have hidden the field on exactly the coach it mattered most on if
|
|
100
|
+
* one ever did. An external-provider offer (Mode A) carries no trip of
|
|
101
|
+
* ours to count, so it simply shows nothing.
|
|
102
|
+
*/
|
|
103
|
+
const seats = typeof data.trip?.available_seats === 'number' && data.trip.available_seats > 0
|
|
104
|
+
? data.trip.available_seats
|
|
105
|
+
: null;
|
|
106
|
+
|
|
81
107
|
// Edited: Ferjolt Ozuni - Date: 2026-08-03
|
|
82
108
|
// Only today's results carry this - obtapi sends null on every other date,
|
|
83
109
|
// so there is no "in 26 days" badge to suppress here.
|
|
@@ -296,6 +322,19 @@ const Route = ({ data, type, date, passengers, t, onSave, children }: Props): JS
|
|
|
296
322
|
{ details ? <HiChevronUp /> : <HiChevronDown /> }
|
|
297
323
|
{ t('routes_order.step2.route.details') }
|
|
298
324
|
</ButtonDetails>
|
|
325
|
+
|
|
326
|
+
{ /* Claude - 2026-09-16 (Ferjolt: "show somewhere on each search
|
|
327
|
+
result the number of available/bookable seats per route").
|
|
328
|
+
|
|
329
|
+
Last in source, and ordered by class rather than by position -
|
|
330
|
+
see Price in ./styles. It sits inside the fare cell because
|
|
331
|
+
how many are left is a fact about this fare on this coach, and
|
|
332
|
+
it is read at the same moment as the price. */ }
|
|
333
|
+
{ seats !== null && (
|
|
334
|
+
<SeatsLeft className="seats-left" $low={ seats <= LOW_SEATS }>
|
|
335
|
+
{ t('routes_order.step2.route.seats_left', { count: seats }) }
|
|
336
|
+
</SeatsLeft>
|
|
337
|
+
) }
|
|
299
338
|
</Price>
|
|
300
339
|
</Trip>
|
|
301
340
|
|
package/Found/Route/styles.ts
CHANGED
|
@@ -510,6 +510,17 @@ export const Price = styled.div`
|
|
|
510
510
|
flex: 0 0 auto;
|
|
511
511
|
}
|
|
512
512
|
|
|
513
|
+
/*
|
|
514
|
+
* Claude - 2026-09-16: last line of the fare cell on a phone, on a row of
|
|
515
|
+
* its own under the note and the toggle. By class for the same reason as
|
|
516
|
+
* the two above - a dynamic-price drop renders an extra child and shifts
|
|
517
|
+
* every nth-child index in this block by one.
|
|
518
|
+
*/
|
|
519
|
+
& > .seats-left {
|
|
520
|
+
order: 5;
|
|
521
|
+
flex: 1 1 100%;
|
|
522
|
+
}
|
|
523
|
+
|
|
513
524
|
@media (min-width: 768px) {
|
|
514
525
|
flex: 0 0 165px;
|
|
515
526
|
flex-direction: column;
|
|
@@ -530,7 +541,8 @@ export const Price = styled.div`
|
|
|
530
541
|
* just now inside the price cell rather than under the whole card.
|
|
531
542
|
*/
|
|
532
543
|
& > .price-notice,
|
|
533
|
-
& > .details-toggle
|
|
544
|
+
& > .details-toggle,
|
|
545
|
+
& > .seats-left {
|
|
534
546
|
order: initial;
|
|
535
547
|
flex-basis: auto;
|
|
536
548
|
}
|
|
@@ -970,3 +982,33 @@ export const Day = styled.span`
|
|
|
970
982
|
white-space: nowrap;
|
|
971
983
|
color: ${ props => props.theme.primary.normal };
|
|
972
984
|
`;
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* How many seats are still on sale, under the fare.
|
|
988
|
+
*
|
|
989
|
+
* Edited: Claude - Date: 2026-09-16
|
|
990
|
+
*
|
|
991
|
+
* Ferjolt asked for the seat count on every result. obtapi has always sent
|
|
992
|
+
* it (trip.available_seats) and nothing on the card ever showed it, so the
|
|
993
|
+
* only way to find out how full a coach was, was to try to book it.
|
|
994
|
+
*
|
|
995
|
+
* The string is "Seats left: 49", a label and a number, rather than
|
|
996
|
+
* "49 seats left" - this codebase carries no i18next plural rules, and a
|
|
997
|
+
* counted noun that has to agree in fifteen languages is how "1 stops"
|
|
998
|
+
* happened in the timetable. A colon sidesteps the agreement entirely and
|
|
999
|
+
* translates cleanly into all of them.
|
|
1000
|
+
*
|
|
1001
|
+
* Low stock is emphasised rather than coloured red on its own: it is a real
|
|
1002
|
+
* fact about the bus, not a countdown we invented to hurry anyone.
|
|
1003
|
+
*/
|
|
1004
|
+
export const SeatsLeft = styled.span<{ $low: boolean }>`
|
|
1005
|
+
font-size: ${ props => props.theme.size.xxs };
|
|
1006
|
+
line-height: 1.25;
|
|
1007
|
+
text-align: left;
|
|
1008
|
+
font-weight: ${ props => (props.$low ? 700 : 400) };
|
|
1009
|
+
color: ${ props => (props.$low ? props.theme.font.error : props.theme.font.faded) };
|
|
1010
|
+
|
|
1011
|
+
@media (min-width: 768px) {
|
|
1012
|
+
text-align: center;
|
|
1013
|
+
}
|
|
1014
|
+
`;
|