@autobusal/routes-search 1.3.3 → 1.4.1

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/RoutesSearch.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { useState, useEffect, useCallback } from 'react';
1
+ import { useState, useEffect, useCallback, useRef } from 'react';
2
2
  import { TFunction } from 'i18next';
3
3
  import { useForm } from 'react-hook-form';
4
4
  import { FaArrowUp, FaHistory } from 'react-icons/fa';
@@ -9,7 +9,7 @@ import { getCities } from './utilities';
9
9
  import Picker from './Picker';
10
10
  import { Options, Columns, Column, Item, Title, Action, PickerField } from './styles';
11
11
  import { RoutesSearchForm } from './types';
12
- import { useGetSuggestions } from './services';
12
+ import { useGetSuggestions, useSuggestedOrigin } from './services';
13
13
 
14
14
  interface Props {
15
15
  type: 'departure' | 'return'
@@ -20,11 +20,21 @@ interface Props {
20
20
  adultsNo: number
21
21
  childrenNo: number
22
22
  babiesNo: number
23
+ /**
24
+ * Edited: Claude - Date: 2026-08-29
25
+ *
26
+ * Open "From" on a departure city near the visitor instead of the
27
+ * brand's configured default. Only for a FRESH form (the homepage, the
28
+ * bare /bus-lines page) - never for the collapsed form on a results
29
+ * page, where the pair on screen is the one being searched and must not
30
+ * be quietly rewritten under the visitor.
31
+ */
32
+ suggest?: boolean
23
33
  t: TFunction<'common'>
24
34
  onSearch: (data: RoutesSearchForm) => void
25
35
  }
26
36
 
27
- const RoutesSearch = ({ type, from, to, departure, _return, adultsNo, childrenNo, babiesNo, t, onSearch }: Props): JSX.Element => {
37
+ const RoutesSearch = ({ type, from, to, departure, _return, adultsNo, childrenNo, babiesNo, suggest = false, t, onSearch }: Props): JSX.Element => {
28
38
  const [ selected, setSelected ] = useState<'departure' | 'return'>(type);
29
39
 
30
40
  // Edited: Ferjolt Ozuni - Date: 2026-08-01
@@ -54,6 +64,41 @@ const RoutesSearch = ({ type, from, to, departure, _return, adultsNo, childrenNo
54
64
 
55
65
  const { data: CitiesData } = useGetSuggestions();
56
66
 
67
+ /**
68
+ * Where the visitor most likely wants to leave from.
69
+ *
70
+ * Edited: Claude - Date: 2026-08-29
71
+ *
72
+ * Applied ONCE and only while the field is untouched: it is a better
73
+ * opening guess than the brand default, not a correction of anybody's
74
+ * typing. The remount-to-reseed trick is the map picker's, above -
75
+ * Autocomplete is uncontrolled, so changing its defaultValue needs a new
76
+ * key.
77
+ */
78
+ const { data: suggestedOrigin } = useSuggestedOrigin(suggest);
79
+
80
+ const touchedFrom = useRef<boolean>(false);
81
+ const appliedSuggestion = useRef<boolean>(false);
82
+
83
+ useEffect(() => {
84
+ if (!suggest || appliedSuggestion.current || touchedFrom.current) {
85
+ return;
86
+ }
87
+
88
+ if (!suggestedOrigin || suggestedOrigin === from) {
89
+ return;
90
+ }
91
+
92
+ appliedSuggestion.current = true;
93
+
94
+ setPicked(previous => ({ ...previous, from: suggestedOrigin }));
95
+
96
+ setValue('from', suggestedOrigin);
97
+
98
+ onDeparture(suggestedOrigin);
99
+ // eslint-disable-next-line react-hooks/exhaustive-deps
100
+ }, [ suggestedOrigin ]);
101
+
57
102
  /**
58
103
  * Tell the date picker which clock this journey starts on.
59
104
  *
@@ -158,7 +203,12 @@ const RoutesSearch = ({ type, from, to, departure, _return, adultsNo, childrenNo
158
203
  validation="required"
159
204
  t={ t }
160
205
  refs={ register }
161
- onUpdate={ (_, value) => { setValue('from', value); onDeparture(String(value)); } }
206
+ onUpdate={ (_, value) => {
207
+ touchedFrom.current = true;
208
+
209
+ setValue('from', value);
210
+ onDeparture(String(value));
211
+ } }
162
212
  />
163
213
 
164
214
  <Picker t={ t } onPick={ (slug) => onPick('from', slug) } />
package/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "name": "@autobusal/routes-search",
3
- "version": "1.3.3",
3
+ "version": "1.4.1",
4
4
  "author": "Ferjolt Ozuni",
5
5
  "type": "module",
6
- "main": "index.ts"
6
+ "main": "index.ts",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ }
7
10
  }
package/services.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { useSuspenseQuery, UseSuspenseQueryResult } from '@tanstack/react-query';
1
+ import { useQuery, useSuspenseQuery, UseQueryResult, UseSuspenseQueryResult } from '@tanstack/react-query';
2
2
  import { apiClient } from '@autobusal/providers';
3
3
  import { CityData } from '@autobusal/providers/types/locations';
4
4
 
@@ -13,4 +13,37 @@ export const useGetSuggestions = (): UseSuspenseQueryResult<CityData[]> => (
13
13
  ))
14
14
  )
15
15
  })
16
- );
16
+ );
17
+
18
+ /**
19
+ * The departure city to open the form on, for THIS visitor.
20
+ *
21
+ * Claude - Date: 2026-08-29
22
+ *
23
+ * obtapi answers with the origin serving the most destinations in the
24
+ * visitor's own country - see Libraries\Geo\Origin, and note the
25
+ * precision is the COUNTRY: the geo database is country-level, so this is
26
+ * "somewhere you can actually leave from near you", not "your city".
27
+ *
28
+ * A plain useQuery, not a suspense one: the form must render immediately
29
+ * with the brand's default and improve on it when the answer arrives -
30
+ * suspending the whole search box on a geolocation lookup would be a far
31
+ * worse trade than a slightly-late pre-fill. Null (no database, a private
32
+ * address, a country we sell nothing from) simply leaves the default.
33
+ */
34
+ export const useSuggestedOrigin = (enabled: boolean): UseQueryResult<string | null> => (
35
+ useQuery({
36
+ queryKey: ['search-suggested-origin'],
37
+ enabled,
38
+ // the answer only changes with the brand's inventory, and the visitor's
39
+ // country does not change mid-session
40
+ staleTime: 1000 * 60 * 30,
41
+ retry: false,
42
+ queryFn: async () => (
43
+ await apiClient
44
+ .get('/api/geo/origin')
45
+ .then(response => response?.data?.from ?? null)
46
+ .catch(() => null)
47
+ )
48
+ })
49
+ );
package/styles.ts CHANGED
@@ -4,12 +4,32 @@ export const Options = styled.div`
4
4
  display: flex;
5
5
  gap: 15px;
6
6
  margin-bottom: 20px;
7
+
8
+ /*
9
+ * Claude - 2026-09-16 (audit): 44px controls under a thumb. The trip-type
10
+ * buttons are the shared 32px "medium", the fields below are the theme's
11
+ * 38px and the search button its 38px default; common is not this
12
+ * package's to edit, so the phone sizes are set from the containers.
13
+ * min-height beats the global height, and the desktop form is untouched.
14
+ */
15
+ @media (max-width: 639px) {
16
+ & > button {
17
+ height: 44px;
18
+ }
19
+ }
7
20
  `;
8
21
 
9
22
  export const Columns = styled.div`
10
23
  display: flex;
11
24
  flex-wrap: wrap;
12
25
  gap: 15px;
26
+
27
+ // the 44px phone fields - see Options above
28
+ @media (max-width: 639px) {
29
+ input:not([type='checkbox']):not([type='radio']), select {
30
+ min-height: 44px;
31
+ }
32
+ }
13
33
  `;
14
34
 
15
35
  export const Column = styled.div`
@@ -43,6 +63,13 @@ export const Action = styled.div`
43
63
  justify-content: center;
44
64
  align-items: end;
45
65
 
66
+ // the 44px phone search button - see Options above
67
+ @media (max-width: 639px) {
68
+ & > button {
69
+ height: 44px;
70
+ }
71
+ }
72
+
46
73
  @media (min-width: 640px) {
47
74
  justify-content: right;
48
75
  }
@@ -60,6 +87,11 @@ export const PickerContainer = styled.div`
60
87
  height: 38px;
61
88
  display: flex;
62
89
  align-items: center;
90
+
91
+ // the pin stays centred in the taller phone field - see Options above
92
+ @media (max-width: 639px) {
93
+ height: 44px;
94
+ }
63
95
  `;
64
96
 
65
97
  export const PickerField = styled.div`
package/CHANGELOG.md DELETED
@@ -1,41 +0,0 @@
1
- # Changelog
2
-
3
- ## 1.3.3 (2026-08-29)
4
-
5
- - A city with a single station lists that station instead of an identical "All Stations" entry beside it; the submitted value stays the city's slug, so URLs are unchanged.
6
-
7
- ## 1.2.0
8
-
9
- **The date picker follows the departure city.** Choosing a "from" city now sets the picker's floor to that city's timezone, and a pre-filled form (a shared link, or the search form kept on the results page) sets it on mount rather than waiting for a keystroke.
10
-
11
- An unknown city *clears* the zone rather than keeping the last one — silently flooring a new search on the previous journey's clock is the subtler version of the same bug.
12
-
13
- All notable changes to `@autobusal/routes-search` are documented here. This
14
- project adheres to [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
15
- [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
16
-
17
- ## [1.1.1] - 2026-08-01
18
-
19
- ### Fixed
20
-
21
- - **The return date could be set before the departure date.** The picker
22
- floored at today, so an invalid pair was selectable and only refused by
23
- an alert after pressing search. It now floors at whatever departure
24
- currently holds, and follows it when that changes.
25
-
26
- ## [1.1.0] - 2026-07-25
27
-
28
- ### Fixed
29
-
30
- - **Recovered as canonical source.** This package was published to npm and
31
- in active use, but had no source anywhere in this monorepo (no
32
- `packages/routes-search` directory, no git history) - the only copy of its
33
- code existed as the published npm artifact. Recreated here,
34
- byte-for-byte identical to the published 1.1.0 tarball (verified via
35
- `diff` against the installed `node_modules/@autobusal/routes-search`), so it
36
- can be edited and republished like every other package instead of
37
- requiring reverse-engineering from `node_modules` or the npm tarball.
38
- History prior to this point is not recoverable - no git log,
39
- changelog, or other record of it exists.
40
-
41
- Authored by Ferjolt Ozuni.