@autobusal/routes-order 1.26.0 → 1.27.0

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,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.27.0
4
+
5
+ **`reloadDocument` is gone — the results follow the URL.**
6
+
7
+ The search lives on `Step1`, which is **not mounted** once results are on screen. So a link that changed the address bar was read by nobody: the URL said one journey and the results underneath still showed another. That is why the empty-search suggestions threw away the whole application on every click.
8
+
9
+ `RoutesOrder` now watches the search spec the URL describes and re-runs when it changes. Keyed on **the spec**, not on a "have we searched yet" flag — a flag can only answer once, a key re-answers whenever the question changes and stays quiet while it does not. It cannot loop either: writing state does not alter the URL the key derives from. The first search is still Step1's, so it does not run twice.
10
+
11
+ Choosing a different search also clears the previously chosen coach. Leaving it set would let the step guards resolve to a checkout for a route the buyer never picked.
12
+
13
+ Verified client-side: navigating from Tirana → Thessaloniki (10 trips) to Durres → Tirana updated the header and the results with the page never reloading.
14
+
3
15
  ## 1.26.0
4
16
 
5
17
  **Funnel events on every step**: the result list, the coach chosen, checkout reached, the Flexible Ticket ticked or unticked, and the value stashed for the purchase event.
@@ -44,7 +44,6 @@ const Empty = ({ step1, data, loading, t }: Props): JSX.Element => {
44
44
  const city = (item: NearbyCity, side: 'from' | 'to'): JSX.Element => (
45
45
  <Option
46
46
  key={ item.city.id }
47
- reloadDocument
48
47
  to={ side === 'from'
49
48
  ? link(item.city.slug, step1.to, step1.departure)
50
49
  : link(step1.from, item.city.slug, step1.departure) }
@@ -56,7 +55,7 @@ const Empty = ({ step1, data, loading, t }: Props): JSX.Element => {
56
55
  );
57
56
 
58
57
  const day = (item: FoundDay): JSX.Element => (
59
- <Option key={ item.id } reloadDocument to={ link(step1.from, step1.to, item.day) }>
58
+ <Option key={ item.id } to={ link(step1.from, step1.to, item.day) }>
60
59
  <OptionName>{ item.day }</OptionName>
61
60
  <OptionMeta>{ t('routes_order.step2.empty.available') }</OptionMeta>
62
61
  { item.from_price_display && <OptionPrice>{ item.from_price_display }</OptionPrice> }
@@ -44,17 +44,16 @@ export const Options = styled.div`
44
44
  `;
45
45
 
46
46
  /**
47
- * Edited: Ferjolt Ozuni - Date: 2026-08-01
47
+ * Edited: Ferjolt Ozuni - Date: 2026-08-05
48
48
  *
49
- * These links carry `reloadDocument` at the call site. RoutesOrder holds
50
- * the search in component state and Step1 only kicks off a search when it
51
- * has not already redirected, so a client-side navigation changes the URL
52
- * and nothing else - the old, empty result set just stays on screen.
49
+ * Plain client-side links now. They used to force a full document reload,
50
+ * because the search lived on a Step1 that is not mounted once results are
51
+ * on screen - so a navigation changed the address bar and nothing else, and
52
+ * the old empty result set simply stayed there.
53
53
  *
54
- * A full load re-enters the flow from the URL exactly as arriving from a
55
- * search engine does. Teaching RoutesOrder to resync its state from
56
- * changing params would be the tidier fix, but that is the main booking
57
- * flow and this is a dead-end page: not worth the risk for the gain.
54
+ * RoutesOrder now follows the URL's search spec, which is the tidier fix
55
+ * the note here previously judged not worth the risk. It became worth it
56
+ * when the same root cause turned up a third time.
58
57
  */
59
58
  export const Option = styled(Link)`
60
59
  display: flex;
package/RoutesOrder.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { useState } from 'react';
1
+ import { useState, useEffect, useMemo } from 'react';
2
2
  import { useParams, useSearchParams } from 'react-router-dom';
3
3
  import { TFunction } from 'i18next';
4
4
  import { AiOutlineSearch, AiOutlineCaretDown, AiOutlineCaretUp } from 'react-icons/ai';
@@ -13,6 +13,7 @@ import { useGetSuggestions } from '@autobusal/routes-search/services';
13
13
  import { SearchAgain, SearchInner, SearchToggle, SearchSummary, SearchPanel } from './styles';
14
14
  import Sections from './Sections/Sections';
15
15
  import Step1 from './Step1/Step1';
16
+ import prepare from './Step1/prepare';
16
17
  import Step2 from './Step2';
17
18
  import Step3 from './Step3';
18
19
  import Checkout from './Checkout/Checkout';
@@ -125,6 +126,53 @@ const RoutesOrder = ({ t }: Props): JSX.Element => {
125
126
  }, { replace });
126
127
  };
127
128
 
129
+ /**
130
+ * Follow the URL when it describes a different search.
131
+ *
132
+ * Edited: Ferjolt Ozuni - Date: 2026-08-05
133
+ *
134
+ * THE SEARCH LIVES ON STEP 1, WHICH IS NOT MOUNTED once results are on
135
+ * screen - `showStep1` is false from step 2 onwards. So a link that
136
+ * changed the address bar was read by nobody: the URL said one journey
137
+ * and the results underneath still showed another. That is why the
138
+ * empty-search suggestions carried `reloadDocument`, throwing away the
139
+ * whole application to do what a route change should have done by itself.
140
+ *
141
+ * Keyed on the SPEC the URL describes rather than on a "have we searched
142
+ * yet" flag. A flag can only ever answer once; a key re-answers whenever
143
+ * the question changes and stays quiet while it does not, which is the
144
+ * behaviour that was actually wanted. It also cannot loop: writing state
145
+ * does not alter the URL the key is derived from.
146
+ *
147
+ * The FIRST search is still Step1's - it owns the empty state a visitor
148
+ * arrives at, and duplicating that here would run it twice.
149
+ */
150
+ const urlSpec = useMemo(
151
+ () => prepare(undefined, params, searchParams, settings.preferences.defaultLocations),
152
+ [params, searchParams, settings.preferences.defaultLocations]
153
+ );
154
+
155
+ // Only a URL that actually carries a complete search - the bare
156
+ // /bus-lines form describes nothing to follow.
157
+ const urlKey = params.departure !== undefined ? JSON.stringify(urlSpec) : null;
158
+
159
+ useEffect(() => {
160
+ if (urlKey === null || step1 === undefined || urlKey === JSON.stringify(step1)) {
161
+ return;
162
+ }
163
+
164
+ setStep1(urlSpec);
165
+
166
+ // the previous journey's chosen coach is not on offer for a different
167
+ // search, and leaving it set would let the guards resolve to a checkout
168
+ // for a route the buyer never picked
169
+ setStep2(undefined);
170
+ setStep3(undefined);
171
+
172
+ goto(null, true);
173
+ // eslint-disable-next-line react-hooks/exhaustive-deps
174
+ }, [urlKey]);
175
+
128
176
  const onSearchDate = (type: ('departure' | '_return'), day: string): void => {
129
177
  if (step1 !== undefined) {
130
178
  setStep1({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autobusal/routes-order",
3
- "version": "1.26.0",
3
+ "version": "1.27.0",
4
4
  "author": "Ferjolt Ozuni",
5
5
  "type": "module",
6
6
  "main": "index.ts"