@autobusal/common 1.23.0 → 1.25.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,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.25.0
4
+
5
+ **New `setDepartureZone(zone)` — the date picker floors on today WHERE THE COACH LEAVES.**
6
+
7
+ `setServerToday` was already an improvement on the buyer's device clock, but it is still the wrong clock: it is *our* date, in Tirana, and a coach leaving Bari has nothing to do with it. Only the departure city can say whether the 4th is still today for that journey.
8
+
9
+ Falls back to `serverToday`, then to the device, so the bare search form (no journey chosen yet) behaves exactly as before. An unresolvable zone returns null and falls back too — a picker that refuses to open because a timezone lookup failed would be far worse than one flooring a day out.
10
+
11
+ ## 1.24.0
12
+
13
+ **`Policy` is now `Information`.**
14
+
15
+ - `Policy` → `Information`, `useGetPolicy` → `useGetInformation`, and the endpoint behind it moves from `/api/routes/policy` to `/api/routes/information`. **Breaking**: update the import.
16
+ - `RouteItem`'s `ReadPolicy` → `ReadInformation`, reading `route_item.information`.
17
+ - Translation keys: `policy.title` → `information.title`, `route_item.policy` → `route_item.information`.
18
+
19
+ The rename follows the Flexible Ticket model, where what a ticket permits comes from the add-on and the operator's own windows — not from this box.
20
+
3
21
  All notable changes to `@autobusal/common` are documented here. This project follows
4
22
  [Keep a Changelog](https://keepachangelog.com/) and [Semantic Versioning](https://semver.org/).
5
23
 
@@ -13,6 +13,26 @@ import { CalendarLimit, DatesLimit, CalendarData } from '../types';
13
13
  */
14
14
  let serverToday: Date | null = null;
15
15
 
16
+ /**
17
+ * The timezone the journey STARTS in, when we know it.
18
+ *
19
+ * Edited: Ferjolt Ozuni - Date: 2026-08-03
20
+ *
21
+ * `serverToday` was already an improvement on the buyer's device clock, but
22
+ * it is still the wrong clock - it is OUR date, in Tirana, and a coach
23
+ * leaving Bari has nothing to do with it. Only the departure city can say
24
+ * whether the 4th is still today for that journey.
25
+ *
26
+ * Null until a departure city is chosen, which is why serverToday remains
27
+ * the fallback rather than being replaced: on the bare search form there is
28
+ * no journey yet to take a clock from.
29
+ */
30
+ let departureZone: string | null = null;
31
+
32
+ export const setDepartureZone = (zone?: string | null): void => {
33
+ departureZone = zone ?? null;
34
+ };
35
+
16
36
  export const setServerToday = (value?: string): void => {
17
37
  if (!value) {
18
38
  return;
@@ -40,8 +60,8 @@ export const getLimits = (type: ('picker' | 'dob' | 'date')): CalendarLimit => {
40
60
  //
41
61
  // A date of birth genuinely is about the person in front of the screen,
42
62
  // so that one keeps the local clock.
43
- const today = (type === 'picker' && serverToday)
44
- ? new Date(serverToday.getTime())
63
+ const today = type === 'picker'
64
+ ? (departureToday() ?? (serverToday ? new Date(serverToday.getTime()) : new Date()))
45
65
  : new Date();
46
66
 
47
67
  today.setHours(0, 0, 0, 0);
@@ -91,6 +111,40 @@ export const getLimits = (type: ('picker' | 'dob' | 'date')): CalendarLimit => {
91
111
  };
92
112
  };
93
113
 
114
+ /**
115
+ * Today, as the departure city reckons it.
116
+ *
117
+ * Edited: Ferjolt Ozuni - Date: 2026-08-03
118
+ *
119
+ * en-CA because it formats as YYYY-MM-DD, which parses unambiguously - the
120
+ * one thing a date string in this codebase has repeatedly failed to do.
121
+ * Built from the PARTS rather than parsed from the string, so the result is
122
+ * a local midnight on that calendar day rather than a UTC instant that can
123
+ * land on the day before.
124
+ *
125
+ * Returns null when there is no zone or the runtime rejects it, and every
126
+ * caller falls back - a picker that refuses to open because a timezone
127
+ * lookup failed would be far worse than one flooring on the wrong day.
128
+ */
129
+ const departureToday = (): Date | null => {
130
+ if (!departureZone) {
131
+ return null;
132
+ }
133
+
134
+ try {
135
+ const [ year, month, day ] = new Intl.DateTimeFormat('en-CA', {
136
+ timeZone: departureZone,
137
+ year: 'numeric',
138
+ month: '2-digit',
139
+ day: '2-digit'
140
+ }).format(new Date()).split('-').map(Number);
141
+
142
+ return new Date(year, month - 1, day, 0, 0, 0, 0);
143
+ } catch {
144
+ return null;
145
+ }
146
+ };
147
+
94
148
  export const getCalendar = (value: string): CalendarData => {
95
149
  const prepared = createDate(value);
96
150
 
@@ -1,7 +1,7 @@
1
1
  import { TFunction } from 'i18next';
2
2
  import { sanitizeHtml } from '@autobusal/utilities';
3
3
  import Modal from '../Modal/Modal';
4
- import { useGetPolicy } from './services';
4
+ import { useGetInformation } from './services';
5
5
 
6
6
  interface Props {
7
7
  id: number
@@ -9,20 +9,20 @@ interface Props {
9
9
  onClose: () => void
10
10
  }
11
11
 
12
- const Policy = ({ id, t, onClose }: Props): JSX.Element => {
13
- const { data, isFetching } = useGetPolicy(id);
12
+ const Information = ({ id, t, onClose }: Props): JSX.Element => {
13
+ const { data, isFetching } = useGetInformation(id);
14
14
 
15
15
  return (
16
16
  <Modal
17
17
  loading={ isFetching }
18
18
  width={ 750 }
19
- title={ t('policy.title') }
19
+ title={ t('information.title') }
20
20
  content={
21
- <div dangerouslySetInnerHTML={{ __html: sanitizeHtml(data?.policies) }} />
21
+ <div dangerouslySetInnerHTML={{ __html: sanitizeHtml(data?.information) }} />
22
22
  }
23
23
  onClose={ onClose }
24
24
  />
25
25
  );
26
26
  };
27
27
 
28
- export default Policy;
28
+ export default Information;
@@ -1,13 +1,13 @@
1
1
  import { useQuery, UseQueryResult } from '@tanstack/react-query';
2
2
  import { apiClient } from '@autobusal/providers';
3
- import { PolicyData } from '@autobusal/providers/types/routes';
3
+ import { InformationData } from '@autobusal/providers/types/routes';
4
4
 
5
- export const useGetPolicy = (id: number): UseQueryResult<PolicyData> => (
5
+ export const useGetInformation = (id: number): UseQueryResult<InformationData> => (
6
6
  useQuery({
7
- queryKey: ['route-policy', { id }],
7
+ queryKey: ['route-information', { id }],
8
8
  queryFn: async () => (
9
9
  await apiClient
10
- .get('/api/routes/policy', {
10
+ .get('/api/routes/information', {
11
11
  params: { id }
12
12
  })
13
13
  .then(response => (
@@ -1,5 +1,5 @@
1
1
  import { TFunction } from 'i18next';
2
- import ReadPolicy from './ReadPolicy';
2
+ import ReadInformation from './ReadInformation';
3
3
  import Schedule from './Schedule';
4
4
  import Transiting from './Transiting';
5
5
  import Douane from './Douane';
@@ -25,7 +25,7 @@ const More = ({ route, t }: Props): JSX.Element => (
25
25
  </Details>
26
26
 
27
27
  <Policy>
28
- <ReadPolicy id={ route.id } t={ t } />
28
+ <ReadInformation id={ route.id } t={ t } />
29
29
  </Policy>
30
30
  </Container>
31
31
  );
@@ -1,7 +1,7 @@
1
1
  import { useState } from 'react';
2
2
  import { TFunction } from 'i18next';
3
3
  import { IoDocumentText } from 'react-icons/io5';
4
- import Policy from '../../Policy/Policy';
4
+ import Information from '../../Information/Information';
5
5
  import { ButtonPolicy } from './styles';
6
6
 
7
7
  interface Props {
@@ -9,19 +9,19 @@ interface Props {
9
9
  t: TFunction<'public'>
10
10
  }
11
11
 
12
- const ReadPolicy = ({ id, t }: Props): JSX.Element => {
12
+ const ReadInformation = ({ id, t }: Props): JSX.Element => {
13
13
  const [ view, setView ] = useState<boolean>(false);
14
14
 
15
15
  return (
16
16
  <>
17
17
  <ButtonPolicy type="button" onClick={ () => setView(!view) }>
18
18
  <IoDocumentText />
19
- { t('route_item.policy') }
19
+ { t('route_item.information') }
20
20
  </ButtonPolicy>
21
21
 
22
- { view && <Policy id={ id } t={ t } onClose={ () => setView(false) } /> }
22
+ { view && <Information id={ id } t={ t } onClose={ () => setView(false) } /> }
23
23
  </>
24
24
  );
25
25
  };
26
26
 
27
- export default ReadPolicy;
27
+ export default ReadInformation;
package/index.ts CHANGED
@@ -26,8 +26,8 @@ import Paragraph from './Loading/Paragraph';
26
26
  import Passengers from './Passengers/Passengers';
27
27
  import Password from './Password/Password';
28
28
  import Rating from './Rating/Rating';
29
- import { setServerToday } from './Calendar/utilities/settings';
30
- import Policy from './Policy/Policy';
29
+ import { setServerToday, setDepartureZone } from './Calendar/utilities/settings';
30
+ import Information from './Information/Information';
31
31
  import Report from './Report/Report';
32
32
  import RouteFeature from './RouteFeature/RouteFeature';
33
33
  import Required from './Required/Required';
@@ -77,7 +77,8 @@ export {
77
77
  Password,
78
78
  Rating,
79
79
  setServerToday,
80
- Policy,
80
+ setDepartureZone,
81
+ Information,
81
82
  Report,
82
83
  RouteFeature,
83
84
  Required,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autobusal/common",
3
- "version": "1.23.0",
3
+ "version": "1.25.0",
4
4
  "author": "Ferjolt Ozuni",
5
5
  "type": "module",
6
6
  "main": "index.ts"