@autobusal/common 1.24.0 → 1.25.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/CHANGELOG.md +18 -0
- package/Calendar/utilities/settings.ts +56 -2
- package/SeoOverride/SeoOverride.tsx +17 -10
- package/SeoOverride/styles.ts +27 -34
- package/index.ts +2 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.25.1
|
|
4
|
+
|
|
5
|
+
**The SEO override wears the site's own form clothes.**
|
|
6
|
+
|
|
7
|
+
`SeoOverride` declared its own `Field`/`Label`/`Input`/`Textarea` and restated the global field styling with *different* values — transparent instead of `inputs.background`, a primary-coloured border, its own font size — then sat on the bare page rather than a card. On a country or city screen that made the two SEO fields the only ones on screen not matching the form directly beneath them, which is the opposite of what its own comment claimed.
|
|
8
|
+
|
|
9
|
+
It uses `.box` and `.row` now, the site's own card and field classes, with plain inputs so `GlobalStyles` reaches them exactly as it reaches every `Viewer` row. Being controlled is the only thing that genuinely differs from a Viewer row, and that is a state concern, not a visual one. The textarea keeps a single rule, for height: `GlobalStyles` gives every text input 38px, right for one line and useless for a description.
|
|
10
|
+
|
|
11
|
+
Reaches the label SEO tab too, which had the same problem. Checked it does not land inside another card — `admin-label`'s `Options` is the tab strip, not a wrapper around the tab bodies.
|
|
12
|
+
|
|
13
|
+
## 1.25.0
|
|
14
|
+
|
|
15
|
+
**New `setDepartureZone(zone)` — the date picker floors on today WHERE THE COACH LEAVES.**
|
|
16
|
+
|
|
17
|
+
`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.
|
|
18
|
+
|
|
19
|
+
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.
|
|
20
|
+
|
|
3
21
|
## 1.24.0
|
|
4
22
|
|
|
5
23
|
**`Policy` is now `Information`.**
|
|
@@ -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 =
|
|
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,6 +1,6 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import { TFunction } from 'i18next';
|
|
3
|
-
import { ContainerLocales, LocaleButton, Notice,
|
|
3
|
+
import { Container, ContainerLocales, LocaleButton, Notice, Textarea } from './styles';
|
|
4
4
|
import { useGetSettings } from '@autobusal/providers/services';
|
|
5
5
|
|
|
6
6
|
export interface SeoEntry {
|
|
@@ -70,8 +70,15 @@ const SeoOverride = ({ value, t, onChange }: Props): JSX.Element => {
|
|
|
70
70
|
return !!(entry?.title?.trim() || entry?.description?.trim());
|
|
71
71
|
};
|
|
72
72
|
|
|
73
|
+
// Edited: Ferjolt Ozuni - Date: 2026-08-05
|
|
74
|
+
// `.box` and `.row` are the site's own card and field classes, and the
|
|
75
|
+
// inputs are left plain so GlobalStyles reaches them - the same shape
|
|
76
|
+
// Viewer/Item renders. Being controlled is the only way this differs from
|
|
77
|
+
// a Viewer row, and that is a state concern, not a visual one; styling
|
|
78
|
+
// them separately is what made the two SEO fields the odd ones out on
|
|
79
|
+
// every country and city screen.
|
|
73
80
|
return (
|
|
74
|
-
|
|
81
|
+
<Container className="box">
|
|
75
82
|
<ContainerLocales>
|
|
76
83
|
{ available.map(language => (
|
|
77
84
|
<LocaleButton
|
|
@@ -88,27 +95,27 @@ const SeoOverride = ({ value, t, onChange }: Props): JSX.Element => {
|
|
|
88
95
|
|
|
89
96
|
<Notice>{ t('seo_override.notice', { ns: 'common' }) }</Notice>
|
|
90
97
|
|
|
91
|
-
<
|
|
92
|
-
|
|
98
|
+
<div className="row">
|
|
99
|
+
{ t('seo_override.title', { ns: 'common' }) }
|
|
93
100
|
|
|
94
|
-
<
|
|
101
|
+
<input
|
|
95
102
|
type="text"
|
|
96
103
|
maxLength={ 250 }
|
|
97
104
|
value={ current.title ?? '' }
|
|
98
105
|
onChange={ (event) => update('title', event.target.value) }
|
|
99
106
|
/>
|
|
100
|
-
</
|
|
107
|
+
</div>
|
|
101
108
|
|
|
102
|
-
<
|
|
103
|
-
|
|
109
|
+
<div className="row row-nomargin">
|
|
110
|
+
{ t('seo_override.description', { ns: 'common' }) }
|
|
104
111
|
|
|
105
112
|
<Textarea
|
|
106
113
|
maxLength={ 500 }
|
|
107
114
|
value={ current.description ?? '' }
|
|
108
115
|
onChange={ (event) => update('description', event.target.value) }
|
|
109
116
|
/>
|
|
110
|
-
</
|
|
111
|
-
|
|
117
|
+
</div>
|
|
118
|
+
</Container>
|
|
112
119
|
);
|
|
113
120
|
};
|
|
114
121
|
|
package/SeoOverride/styles.ts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import styled, { css } from 'styled-components';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* The editor's own card.
|
|
5
|
+
*
|
|
6
|
+
* Ferjolt Ozuni - Date: 2026-08-05
|
|
7
|
+
* `.box` is the site's card surface (GlobalStyles), the same one Viewer
|
|
8
|
+
* draws itself on. This block sits directly above a Viewer on the country
|
|
9
|
+
* and city screens, so on the bare page background it read as loose
|
|
10
|
+
* furniture that happened to be above the form rather than part of it -
|
|
11
|
+
* which is exactly backwards, since the form's Save button is what submits
|
|
12
|
+
* these fields.
|
|
13
|
+
*/
|
|
14
|
+
export const Container = styled.div`
|
|
15
|
+
margin-bottom: 20px;
|
|
16
|
+
`;
|
|
17
|
+
|
|
3
18
|
export const ContainerLocales = styled.div`
|
|
4
19
|
display: flex;
|
|
5
20
|
flex-wrap: wrap;
|
|
@@ -47,42 +62,20 @@ export const Notice = styled.p`
|
|
|
47
62
|
`;
|
|
48
63
|
|
|
49
64
|
/**
|
|
50
|
-
*
|
|
65
|
+
* Height, and nothing else.
|
|
51
66
|
*
|
|
52
|
-
* Ferjolt Ozuni - Date: 2026-08-
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
67
|
+
* Ferjolt Ozuni - Date: 2026-08-05
|
|
68
|
+
* How a field looks - fill, border, radius, weight, colour, width - belongs
|
|
69
|
+
* to GlobalStyles, which already styles `textarea` alongside every text
|
|
70
|
+
* input on the site. This file used to restate all of it with DIFFERENT
|
|
71
|
+
* values (transparent fill, a primary-coloured border, its own font size),
|
|
72
|
+
* which is the whole reason these two fields were the only ones on the
|
|
73
|
+
* screen that did not match the form beneath them. The single thing
|
|
74
|
+
* GlobalStyles cannot get right here is the height: 38px is correct for a
|
|
75
|
+
* one-line input and useless for a description.
|
|
57
76
|
*/
|
|
58
|
-
export const Field = styled.div`
|
|
59
|
-
margin-bottom: 15px;
|
|
60
|
-
`;
|
|
61
|
-
|
|
62
|
-
export const Label = styled.label`
|
|
63
|
-
display: block;
|
|
64
|
-
margin-bottom: 5px;
|
|
65
|
-
font-weight: 600;
|
|
66
|
-
font-size: ${ props => props.theme.size.m };
|
|
67
|
-
`;
|
|
68
|
-
|
|
69
|
-
const field = css`
|
|
70
|
-
width: 100%;
|
|
71
|
-
padding: 10px;
|
|
72
|
-
border: 1px solid ${ props => props.theme.primary.neutral };
|
|
73
|
-
border-radius: ${ props => props.theme.borderRadius };
|
|
74
|
-
background: transparent;
|
|
75
|
-
color: ${ props => props.theme.font.normal };
|
|
76
|
-
font-family: inherit;
|
|
77
|
-
font-size: ${ props => props.theme.size.m };
|
|
78
|
-
`;
|
|
79
|
-
|
|
80
|
-
export const Input = styled.input`
|
|
81
|
-
${ field }
|
|
82
|
-
`;
|
|
83
|
-
|
|
84
77
|
export const Textarea = styled.textarea`
|
|
85
|
-
|
|
86
|
-
|
|
78
|
+
height: 90px;
|
|
79
|
+
padding: 8px 10px;
|
|
87
80
|
resize: vertical;
|
|
88
81
|
`;
|
package/index.ts
CHANGED
|
@@ -26,7 +26,7 @@ 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';
|
|
29
|
+
import { setServerToday, setDepartureZone } from './Calendar/utilities/settings';
|
|
30
30
|
import Information from './Information/Information';
|
|
31
31
|
import Report from './Report/Report';
|
|
32
32
|
import RouteFeature from './RouteFeature/RouteFeature';
|
|
@@ -77,6 +77,7 @@ export {
|
|
|
77
77
|
Password,
|
|
78
78
|
Rating,
|
|
79
79
|
setServerToday,
|
|
80
|
+
setDepartureZone,
|
|
80
81
|
Information,
|
|
81
82
|
Report,
|
|
82
83
|
RouteFeature,
|