@autobusal/common 1.33.1 → 1.33.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 +11 -0
- package/Meta.tsx +31 -4
- package/RouteItem/RouteItem.tsx +16 -1
- package/RouteItem/styles.ts +6 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.33.3
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`RouteItem` shows a "from" price** next to the route name/code - the lowest fare across the route's own `prices`, using the row's own pre-formatted `adult_display` (same currency/decimal convention as everywhere else). Both `browse` and `view` modes get it; a route with no prices at all shows nothing, same as it always could.
|
|
8
|
+
|
|
9
|
+
## 1.33.2
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **`Meta` now defaults `og:image`/`twitter:image` to the label's own homepage hero photo** (`{settings.url}/home/background-light.jpg`) when a page doesn't pass its own `image`. Most of the ~136 call sites never had a share-preview image at all, so a link pasted into a chat assistant or social post previewed with nothing. A page with something more specific (an operator's logo, a blog post's own photo) still passes its own `image` and wins - this only fills the gap.
|
|
3
14
|
|
|
4
15
|
## 1.33.1
|
|
5
16
|
|
package/Meta.tsx
CHANGED
|
@@ -237,11 +237,38 @@ const useAlternates = (noIndex: boolean, explicitUrl?: string): { locale: string
|
|
|
237
237
|
];
|
|
238
238
|
};
|
|
239
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Edited: Ferjolt Ozuni - Date: 2026-08-11
|
|
242
|
+
*
|
|
243
|
+
* A default og:image/twitter:image for whichever of Meta's ~136 call
|
|
244
|
+
* sites never passes one - route detail, Help, blog index, and most
|
|
245
|
+
* static pages had NO share-preview image at all, so a link pasted into
|
|
246
|
+
* a chat assistant or social post previewed with nothing. Rather than
|
|
247
|
+
* hunt down every call site individually, the fallback lives here: the
|
|
248
|
+
* same hero photo the homepage itself uses (`home/background-light.jpg`,
|
|
249
|
+
* already uploaded per-label - see RoutesOrder.tsx's identical asset
|
|
250
|
+
* path), which every label already has. The DARK variant is deliberately
|
|
251
|
+
* not used here regardless of the visitor's own theme - a share preview
|
|
252
|
+
* is generated by whatever unpacks the URL server-side, not by this
|
|
253
|
+
* visitor's browser, so there is no "current theme" to match; picking
|
|
254
|
+
* one consistently is more correct than guessing.
|
|
255
|
+
*
|
|
256
|
+
* A page with something more specific to show (an operator's logo, a
|
|
257
|
+
* blog post's own photo) still passes its own `image` and wins - this
|
|
258
|
+
* only fills the gap when nothing more specific exists.
|
|
259
|
+
*/
|
|
260
|
+
const useDefaultImage = (explicit?: string): (string | undefined) => {
|
|
261
|
+
const { data: settings } = useGetSettings();
|
|
262
|
+
|
|
263
|
+
return explicit ?? (settings ? `${ settings.url }/home/background-light.jpg` : undefined);
|
|
264
|
+
};
|
|
265
|
+
|
|
240
266
|
const Meta = ({ title, keywords, description, image, url, type = 'website', noIndex = false, children }: Props): JSX.Element => {
|
|
241
267
|
const fullTitle = withBrand(title);
|
|
242
268
|
const canonicalUrl = useCanonicalUrl(url);
|
|
243
269
|
const location = useLocation();
|
|
244
270
|
const robots = noIndex ? 'noindex, nofollow' : 'index, follow';
|
|
271
|
+
const resolvedImage = useDefaultImage(image);
|
|
245
272
|
|
|
246
273
|
const alternates = useAlternates(noIndex, url);
|
|
247
274
|
|
|
@@ -255,13 +282,13 @@ const Meta = ({ title, keywords, description, image, url, type = 'website', noIn
|
|
|
255
282
|
{ selector: 'meta[property="og:type"]', attr: 'content', value: type },
|
|
256
283
|
{ selector: 'meta[property="og:title"]', attr: 'content', value: fullTitle },
|
|
257
284
|
{ selector: 'meta[property="og:description"]', attr: 'content', value: description },
|
|
258
|
-
{ selector: 'meta[property="og:image"]', attr: 'content', value:
|
|
285
|
+
{ selector: 'meta[property="og:image"]', attr: 'content', value: resolvedImage },
|
|
259
286
|
{ selector: 'meta[property="og:url"]', attr: 'content', value: canonicalUrl },
|
|
260
287
|
{ selector: 'meta[property="og:site_name"]', attr: 'content', value: brand },
|
|
261
288
|
{ selector: 'meta[name="twitter:card"]', attr: 'content', value: 'summary_large_image' },
|
|
262
289
|
{ selector: 'meta[name="twitter:title"]', attr: 'content', value: fullTitle },
|
|
263
290
|
{ selector: 'meta[name="twitter:description"]', attr: 'content', value: description },
|
|
264
|
-
{ selector: 'meta[name="twitter:image"]', attr: 'content', value:
|
|
291
|
+
{ selector: 'meta[name="twitter:image"]', attr: 'content', value: resolvedImage }
|
|
265
292
|
]);
|
|
266
293
|
usePageviewTracking(location.pathname + location.search, fullTitle);
|
|
267
294
|
|
|
@@ -284,7 +311,7 @@ const Meta = ({ title, keywords, description, image, url, type = 'website', noIn
|
|
|
284
311
|
<meta property="og:type" content={ type } />
|
|
285
312
|
<meta property="og:title" content={ fullTitle } />
|
|
286
313
|
{ description && <meta property="og:description" content={ description } /> }
|
|
287
|
-
{
|
|
314
|
+
{ resolvedImage && <meta property="og:image" content={ resolvedImage } /> }
|
|
288
315
|
<meta property="og:url" content={ canonicalUrl } />
|
|
289
316
|
{ brand && <meta property="og:site_name" content={ brand } /> }
|
|
290
317
|
|
|
@@ -292,7 +319,7 @@ const Meta = ({ title, keywords, description, image, url, type = 'website', noIn
|
|
|
292
319
|
<meta name="twitter:card" content="summary_large_image" />
|
|
293
320
|
<meta name="twitter:title" content={ fullTitle } />
|
|
294
321
|
{ description && <meta name="twitter:description" content={ description } /> }
|
|
295
|
-
{
|
|
322
|
+
{ resolvedImage && <meta name="twitter:image" content={ resolvedImage } /> }
|
|
296
323
|
|
|
297
324
|
{ children }
|
|
298
325
|
</>
|
package/RouteItem/RouteItem.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import { useState } from 'react';
|
|
|
2
2
|
import { TFunction } from 'i18next';
|
|
3
3
|
import { HiOutlineInformationCircle, HiOutlineClock, HiArrowCircleRight } from 'react-icons/hi';
|
|
4
4
|
import More from './More/More';
|
|
5
|
-
import { Container, About, Image, Logo, Information, Name, Code, ButtonMore, Locations, Location, City, Iso, Date, Arrow, Order, LinkOrder } from './styles';
|
|
5
|
+
import { Container, About, Image, Logo, Information, Name, Code, Price, ButtonMore, Locations, Location, City, Iso, Date, Arrow, Order, LinkOrder } from './styles';
|
|
6
6
|
import { RouteData } from '@autobusal/providers/types/routes';
|
|
7
7
|
|
|
8
8
|
interface Props {
|
|
@@ -16,6 +16,19 @@ const RouteItem = ({ type, route, t }: Props): JSX.Element => {
|
|
|
16
16
|
|
|
17
17
|
const lastLocation = route.locations.length - 1;
|
|
18
18
|
|
|
19
|
+
// Edited: Ferjolt Ozuni - Date: 2026-08-11
|
|
20
|
+
// The cheapest fare on this route, same "from" semantics as everywhere
|
|
21
|
+
// else a lowest price is shown (Facts.price_from, results-page cards) -
|
|
22
|
+
// `prices` is one row per from/to leg the route sells, not one row for
|
|
23
|
+
// the whole journey, so the honest "from" figure is the minimum across
|
|
24
|
+
// all of them. Uses the row's OWN `adult_display` rather than
|
|
25
|
+
// reformatting `adult` itself - the server already applied this label's
|
|
26
|
+
// currency symbol/decimal convention, and re-deriving that here would
|
|
27
|
+
// risk disagreeing with every other price on the site.
|
|
28
|
+
const cheapest = route.prices.length > 0
|
|
29
|
+
? route.prices.reduce((min, price) => (price.adult < min.adult ? price : min))
|
|
30
|
+
: null;
|
|
31
|
+
|
|
19
32
|
return (
|
|
20
33
|
<Container className="box">
|
|
21
34
|
<About>
|
|
@@ -30,6 +43,8 @@ const RouteItem = ({ type, route, t }: Props): JSX.Element => {
|
|
|
30
43
|
|
|
31
44
|
<Code>{ route.code }</Code>
|
|
32
45
|
|
|
46
|
+
{ cheapest && <Price>{ t('route_item.price_from', { price: cheapest.adult_display }) }</Price> }
|
|
47
|
+
|
|
33
48
|
<ButtonMore type="button" onClick={ () => setShow(!show) }>
|
|
34
49
|
<HiOutlineInformationCircle />
|
|
35
50
|
{ t('route_item.info') }
|
package/RouteItem/styles.ts
CHANGED
|
@@ -60,6 +60,12 @@ export const Code = styled.div`
|
|
|
60
60
|
font-size: ${ props => props.theme.size.xs };
|
|
61
61
|
`;
|
|
62
62
|
|
|
63
|
+
export const Price = styled.div`
|
|
64
|
+
font-weight: 700;
|
|
65
|
+
color: ${ props => props.theme.primary.normal };
|
|
66
|
+
font-size: ${ props => props.theme.size.s };
|
|
67
|
+
`;
|
|
68
|
+
|
|
63
69
|
export const ButtonMore = styled.button`
|
|
64
70
|
display: flex;
|
|
65
71
|
align-items: center;
|