@jotul/jotul-widgets 1.0.5 → 1.2.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/README.md +21 -1
- package/dist/JotulWidget.css +1 -1
- package/dist/JotulWidget.d.ts +2 -2
- package/dist/JotulWidget.js +166 -36
- package/dist/api.d.ts +2 -2
- package/dist/api.js +13 -2
- package/dist/components/FindDealerDrawerWidget.d.ts +39 -0
- package/dist/components/FindDealerDrawerWidget.js +318 -0
- package/dist/components/ProductPageWidget.d.ts +11 -2
- package/dist/components/ProductPageWidget.js +215 -41
- package/dist/components/product-page/CampaignStatus.d.ts +6 -0
- package/dist/components/product-page/CampaignStatus.js +48 -0
- package/dist/components/product-page/DealerList.d.ts +4 -2
- package/dist/components/product-page/DealerList.js +12 -4
- package/dist/components/product-page/StatusBanner.d.ts +1 -1
- package/dist/components/product-page/StatusBanner.js +3 -1
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +4 -0
- package/dist/i18n/locales/cz.json +5 -1
- package/dist/i18n/locales/de.json +5 -1
- package/dist/i18n/locales/en.json +5 -1
- package/dist/i18n/locales/fi.json +5 -1
- package/dist/i18n/locales/fr.json +5 -1
- package/dist/i18n/locales/nl.json +5 -1
- package/dist/i18n/locales/no.json +5 -1
- package/dist/i18n/locales/pl.json +5 -1
- package/dist/i18n/locales/se.json +5 -1
- package/dist/i18n/widgetStrings.d.ts +4 -0
- package/dist/images/dealer-pin-ildstedet.svg +31 -0
- package/dist/images/dealer-pin.svg +8 -3
- package/dist/images/ildstedet-dealer.svg +702 -0
- package/dist/index.d.ts +1 -1
- package/dist/types.d.ts +26 -5
- package/dist/utils/cssColor.d.ts +5 -0
- package/dist/utils/cssColor.js +31 -0
- package/dist/utils/dealerMapClustering.d.ts +28 -0
- package/dist/utils/dealerMapClustering.js +71 -0
- package/dist/utils/loadLeafletMarkerCluster.d.ts +5 -0
- package/dist/utils/loadLeafletMarkerCluster.js +20 -0
- package/dist/utils/markerClusterIconHtml.d.ts +2 -0
- package/dist/utils/markerClusterIconHtml.js +7 -0
- package/dist/utils.d.ts +3 -1
- package/dist/utils.js +10 -0
- package/package.json +5 -2
|
@@ -1,18 +1,38 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import 'leaflet/dist/leaflet.css';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
3
|
+
import 'leaflet.markercluster/dist/MarkerCluster.css';
|
|
4
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
5
|
+
import { icon, latLng } from 'leaflet';
|
|
5
6
|
import { MapContainer, Marker, TileLayer, Tooltip, useMap } from 'react-leaflet';
|
|
6
7
|
import { DealerCardSkeleton } from './DealerCardSkeleton';
|
|
7
8
|
import { DealerList } from './product-page/DealerList';
|
|
9
|
+
import { CampaignStatus } from './product-page/CampaignStatus';
|
|
8
10
|
import { InquiryForm } from './product-page/InquiryForm';
|
|
9
11
|
import { LocationSearch } from './product-page/LocationSearch';
|
|
10
12
|
import { StatusBanner } from './product-page/StatusBanner';
|
|
11
13
|
import { ArrowRightIcon } from '../icons/ArrowRightIcon';
|
|
12
14
|
import dealerPin from '../images/dealer-pin.svg';
|
|
13
15
|
import dealerPinExclusive from '../images/dealer-pin-exclusive.svg';
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
+
import dealerPinIldstedet from '../images/dealer-pin-ildstedet.svg';
|
|
17
|
+
import { MARKER_CLUSTER_LINK_KM, MARKER_CLUSTER_MIN_GROUP, partitionDealersForMarkerClusterPlugin, } from '../utils/dealerMapClustering';
|
|
18
|
+
import { loadLeafletMarkerCluster } from '../utils/loadLeafletMarkerCluster';
|
|
19
|
+
import { markerClusterCountIconHtml } from '../utils/markerClusterIconHtml';
|
|
20
|
+
import { JOTUL_BRAND_PRIMARY_HEX, R10 } from '../constants';
|
|
21
|
+
const OSM_MINIMAL_TILE_URL = 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png';
|
|
22
|
+
function mapPointsBoundsKey(points, defaultCenter) {
|
|
23
|
+
const body = points.length === 0
|
|
24
|
+
? ''
|
|
25
|
+
: [...points]
|
|
26
|
+
.sort((a, b) => a.latitude !== b.latitude
|
|
27
|
+
? a.latitude - b.latitude
|
|
28
|
+
: a.longitude !== b.longitude
|
|
29
|
+
? a.longitude - b.longitude
|
|
30
|
+
: a.dealerName.localeCompare(b.dealerName))
|
|
31
|
+
.map((p) => `${p.dealerName}\t${p.latitude}\t${p.longitude}`)
|
|
32
|
+
.join('\n');
|
|
33
|
+
const dc = defaultCenter != null ? `${defaultCenter[0]},${defaultCenter[1]}` : '';
|
|
34
|
+
return `${body}|${dc}`;
|
|
35
|
+
}
|
|
16
36
|
function readNumber(value) {
|
|
17
37
|
if (typeof value === 'number' && Number.isFinite(value))
|
|
18
38
|
return value;
|
|
@@ -53,10 +73,11 @@ function getDealerMapPoint(dealer) {
|
|
|
53
73
|
function toAssetSrc(value) {
|
|
54
74
|
return typeof value === 'string' ? value : value.src;
|
|
55
75
|
}
|
|
56
|
-
function createDealerPinIcon(isExclusive, active) {
|
|
76
|
+
function createDealerPinIcon(isExclusive, active, market) {
|
|
57
77
|
const size = active ? 48 : 42;
|
|
58
78
|
const width = Math.round((16 / 20) * size);
|
|
59
|
-
const
|
|
79
|
+
const exclusivePin = market === 'NO' ? dealerPinIldstedet : dealerPinExclusive;
|
|
80
|
+
const pinUrl = isExclusive ? toAssetSrc(exclusivePin) : toAssetSrc(dealerPin);
|
|
60
81
|
return icon({
|
|
61
82
|
iconUrl: pinUrl,
|
|
62
83
|
iconRetinaUrl: pinUrl,
|
|
@@ -66,7 +87,7 @@ function createDealerPinIcon(isExclusive, active) {
|
|
|
66
87
|
className: 'jwi-map-pin',
|
|
67
88
|
});
|
|
68
89
|
}
|
|
69
|
-
function FitMapBounds({ points, defaultCenter, }) {
|
|
90
|
+
function FitMapBounds({ boundsKey, points, defaultCenter, }) {
|
|
70
91
|
const map = useMap();
|
|
71
92
|
useEffect(() => {
|
|
72
93
|
const all = points.map((p) => [p.latitude, p.longitude]);
|
|
@@ -75,11 +96,13 @@ function FitMapBounds({ points, defaultCenter, }) {
|
|
|
75
96
|
if (all.length === 0)
|
|
76
97
|
return;
|
|
77
98
|
if (all.length === 1) {
|
|
78
|
-
map.setView(all[0], 12, { animate:
|
|
99
|
+
map.setView(all[0], 12, { animate: false });
|
|
79
100
|
return;
|
|
80
101
|
}
|
|
81
|
-
|
|
82
|
-
|
|
102
|
+
// No animation — must complete before FocusActiveDealer centers the selected pin (see FindDealerDrawerWidget).
|
|
103
|
+
map.fitBounds(all, { padding: [50, 50], animate: false, maxZoom: 19 });
|
|
104
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- boundsKey encodes points + defaultCenter; avoid refit on array identity churn
|
|
105
|
+
}, [boundsKey, map]);
|
|
83
106
|
return null;
|
|
84
107
|
}
|
|
85
108
|
function FocusActiveDealer({ point }) {
|
|
@@ -87,34 +110,149 @@ function FocusActiveDealer({ point }) {
|
|
|
87
110
|
useEffect(() => {
|
|
88
111
|
if (point == null)
|
|
89
112
|
return;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
});
|
|
113
|
+
const target = latLng(point.latitude, point.longitude);
|
|
114
|
+
map.setView(target, Math.max(map.getZoom(), 13), { animate: true });
|
|
93
115
|
}, [map, point]);
|
|
94
116
|
return null;
|
|
95
117
|
}
|
|
96
|
-
|
|
118
|
+
function ClusteredMapMarkers({ points, pointsBoundsKey, clusterThemeKey, clusterBrandFill, clusterBrandLabel, market, activeDealerName, onSelectDealer, onUnavailable, }) {
|
|
119
|
+
const map = useMap();
|
|
120
|
+
const markersByDealerRef = useRef(new Map());
|
|
121
|
+
const pointsRef = useRef(points);
|
|
122
|
+
const activeNameRef = useRef(activeDealerName);
|
|
123
|
+
const onSelectRef = useRef(onSelectDealer);
|
|
124
|
+
const onUnavailableRef = useRef(onUnavailable);
|
|
125
|
+
pointsRef.current = points;
|
|
126
|
+
activeNameRef.current = activeDealerName;
|
|
127
|
+
onSelectRef.current = onSelectDealer;
|
|
128
|
+
onUnavailableRef.current = onUnavailable;
|
|
129
|
+
function applyPinsForSelection() {
|
|
130
|
+
const active = activeNameRef.current;
|
|
131
|
+
for (const p of pointsRef.current) {
|
|
132
|
+
const marker = markersByDealerRef.current.get(p.dealerName);
|
|
133
|
+
if (marker != null) {
|
|
134
|
+
marker.setIcon(createDealerPinIcon(p.isExclusive, active === p.dealerName, market));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
applyPinsForSelection();
|
|
140
|
+
}, [activeDealerName]);
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
let disposed = false;
|
|
143
|
+
markersByDealerRef.current.clear();
|
|
144
|
+
let clusterGroup = null;
|
|
145
|
+
let plainGroup = null;
|
|
146
|
+
async function mountClusters() {
|
|
147
|
+
try {
|
|
148
|
+
const leafletModule = await import('leaflet');
|
|
149
|
+
if (disposed)
|
|
150
|
+
return;
|
|
151
|
+
const { clustered, standalone } = partitionDealersForMarkerClusterPlugin(pointsRef.current, {
|
|
152
|
+
linkKm: MARKER_CLUSTER_LINK_KM,
|
|
153
|
+
minGroupSize: MARKER_CLUSTER_MIN_GROUP,
|
|
154
|
+
});
|
|
155
|
+
const leafletMaybeDefault = leafletModule;
|
|
156
|
+
const leaf = (leafletMaybeDefault.default ??
|
|
157
|
+
leafletMaybeDefault);
|
|
158
|
+
if (typeof leaf.featureGroup !== 'function') {
|
|
159
|
+
onUnavailableRef.current?.();
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
function makeMarker(point) {
|
|
163
|
+
const marker = leaf.marker([point.latitude, point.longitude], {
|
|
164
|
+
icon: createDealerPinIcon(point.isExclusive, activeNameRef.current === point.dealerName, market),
|
|
165
|
+
});
|
|
166
|
+
marker.on('click', () => {
|
|
167
|
+
onSelectRef.current?.({
|
|
168
|
+
dealerName: point.dealerName,
|
|
169
|
+
latitude: point.latitude,
|
|
170
|
+
longitude: point.longitude,
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
marker.bindTooltip(point.dealerName);
|
|
174
|
+
markersByDealerRef.current.set(point.dealerName, marker);
|
|
175
|
+
return marker;
|
|
176
|
+
}
|
|
177
|
+
if (standalone.length > 0) {
|
|
178
|
+
plainGroup = leaf.featureGroup();
|
|
179
|
+
for (const point of standalone) {
|
|
180
|
+
plainGroup.addLayer(makeMarker(point));
|
|
181
|
+
}
|
|
182
|
+
plainGroup.addTo(map);
|
|
183
|
+
}
|
|
184
|
+
if (clustered.length > 0) {
|
|
185
|
+
await loadLeafletMarkerCluster(leaf);
|
|
186
|
+
if (disposed)
|
|
187
|
+
return;
|
|
188
|
+
if (typeof leaf.markerClusterGroup !== 'function') {
|
|
189
|
+
onUnavailableRef.current?.();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
clusterGroup = leaf.markerClusterGroup({
|
|
193
|
+
showCoverageOnHover: false,
|
|
194
|
+
spiderfyOnMaxZoom: true,
|
|
195
|
+
spiderLegPolylineOptions: {
|
|
196
|
+
weight: 1.75,
|
|
197
|
+
color: clusterBrandFill,
|
|
198
|
+
opacity: 0.38,
|
|
199
|
+
},
|
|
200
|
+
iconCreateFunction: (cluster) => {
|
|
201
|
+
const n = cluster.getChildCount();
|
|
202
|
+
return leaf.divIcon({
|
|
203
|
+
html: markerClusterCountIconHtml(n, clusterBrandFill, clusterBrandLabel),
|
|
204
|
+
className: '',
|
|
205
|
+
iconSize: leaf.point(40, 40),
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
maxClusterRadius: (zoom) => zoom <= 6 ? 140 : zoom <= 8 ? 125 : zoom <= 10 ? 100 : zoom <= 11 ? 72 : 45,
|
|
209
|
+
disableClusteringAtZoom: 12,
|
|
210
|
+
chunkedLoading: true,
|
|
211
|
+
});
|
|
212
|
+
for (const point of clustered) {
|
|
213
|
+
clusterGroup.addLayer(makeMarker(point));
|
|
214
|
+
}
|
|
215
|
+
clusterGroup.addTo(map);
|
|
216
|
+
}
|
|
217
|
+
applyPinsForSelection();
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
onUnavailableRef.current?.();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
void mountClusters();
|
|
224
|
+
return () => {
|
|
225
|
+
disposed = true;
|
|
226
|
+
if (clusterGroup != null) {
|
|
227
|
+
clusterGroup.clearLayers();
|
|
228
|
+
clusterGroup.removeFrom(map);
|
|
229
|
+
}
|
|
230
|
+
if (plainGroup != null) {
|
|
231
|
+
plainGroup.clearLayers();
|
|
232
|
+
plainGroup.removeFrom(map);
|
|
233
|
+
}
|
|
234
|
+
markersByDealerRef.current.clear();
|
|
235
|
+
};
|
|
236
|
+
}, [map, pointsBoundsKey, clusterThemeKey]);
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
export function ProductPageWidget({ t, buttonStyling, borderStyling, market, isSearching, locationError, searchResult, mapSearchResult, inquiryValues, inquiryError, isInquirySubmitted, selectedDealerName, isManualSearchEnabled, query, suggestions, suggestionsOpen, isSuggestionsLoading, onQueryChange, onQuerySubmit, onSuggestionSelect, onDismissSuggestions, onInquiryClose, onInquirySubmit, onInquiryFieldChange, onStartInquiry, onMapDealerSelect, onClosePopup, }) {
|
|
97
240
|
const dealers = (searchResult?.dealers ?? []);
|
|
98
|
-
const
|
|
241
|
+
const mapDealers = (mapSearchResult?.dealers ?? dealers);
|
|
242
|
+
const total = dealers.length;
|
|
99
243
|
const inquiryFormOpen = inquiryValues != null;
|
|
100
|
-
const [viewMode, setViewMode] = useState('list');
|
|
101
244
|
const [activeDealerName, setActiveDealerName] = useState(null);
|
|
102
245
|
const [mobileMapExpanded, setMobileMapExpanded] = useState(false);
|
|
103
246
|
const [isMobileViewport, setIsMobileViewport] = useState(false);
|
|
247
|
+
const [visibleDealerCount, setVisibleDealerCount] = useState(10);
|
|
248
|
+
const [clusterUnavailable, setClusterUnavailable] = useState(false);
|
|
104
249
|
useEffect(() => {
|
|
105
|
-
if (viewMode !== 'map') {
|
|
106
|
-
setMobileMapExpanded(false);
|
|
107
|
-
}
|
|
108
|
-
}, [viewMode]);
|
|
109
|
-
useEffect(() => {
|
|
110
|
-
if (viewMode !== 'map')
|
|
111
|
-
return;
|
|
112
250
|
const previous = document.body.style.overflow;
|
|
113
251
|
document.body.style.overflow = 'hidden';
|
|
114
252
|
return () => {
|
|
115
253
|
document.body.style.overflow = previous;
|
|
116
254
|
};
|
|
117
|
-
}, [
|
|
255
|
+
}, []);
|
|
118
256
|
useEffect(() => {
|
|
119
257
|
if (typeof window === 'undefined')
|
|
120
258
|
return;
|
|
@@ -124,7 +262,23 @@ export function ProductPageWidget({ t, buttonStyling, isSearching, locationError
|
|
|
124
262
|
media.addEventListener('change', update);
|
|
125
263
|
return () => media.removeEventListener('change', update);
|
|
126
264
|
}, []);
|
|
127
|
-
|
|
265
|
+
useEffect(() => {
|
|
266
|
+
setVisibleDealerCount(10);
|
|
267
|
+
}, [dealers]);
|
|
268
|
+
const visibleDealers = useMemo(() => dealers.slice(0, Math.max(10, visibleDealerCount)), [dealers, visibleDealerCount]);
|
|
269
|
+
useEffect(() => {
|
|
270
|
+
if (activeDealerName == null)
|
|
271
|
+
return;
|
|
272
|
+
const activeIndex = dealers.findIndex((dealer) => getDealerName(dealer) === activeDealerName);
|
|
273
|
+
if (activeIndex < 0 || activeIndex < visibleDealerCount)
|
|
274
|
+
return;
|
|
275
|
+
const nextCount = Math.ceil((activeIndex + 1) / 10) * 10;
|
|
276
|
+
setVisibleDealerCount(nextCount);
|
|
277
|
+
}, [activeDealerName, dealers, visibleDealerCount]);
|
|
278
|
+
const mapPoints = useMemo(() => mapDealers.map((dealer) => getDealerMapPoint(dealer)).filter((v) => v != null), [mapDealers]);
|
|
279
|
+
useEffect(() => {
|
|
280
|
+
setClusterUnavailable(false);
|
|
281
|
+
}, [mapPoints]);
|
|
128
282
|
const defaultCenter = useMemo(() => {
|
|
129
283
|
const first = mapPoints[0];
|
|
130
284
|
if (first != null)
|
|
@@ -135,22 +289,41 @@ export function ProductPageWidget({ t, buttonStyling, isSearching, locationError
|
|
|
135
289
|
return null;
|
|
136
290
|
return [latitude, longitude];
|
|
137
291
|
}, [mapPoints, searchResult?.origin?.latitude, searchResult?.origin?.longitude]);
|
|
292
|
+
const mapBoundsKey = useMemo(() => mapPointsBoundsKey(mapPoints, defaultCenter), [mapPoints, defaultCenter]);
|
|
138
293
|
useEffect(() => {
|
|
139
294
|
setActiveDealerName(dealers.length > 0 ? getDealerName(dealers[0]) : null);
|
|
140
295
|
}, [dealers]);
|
|
141
296
|
const activeMapPoint = useMemo(() => mapPoints.find((p) => p.dealerName === activeDealerName) ?? null, [activeDealerName, mapPoints]);
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
297
|
+
const mapClusterTheme = useMemo(() => {
|
|
298
|
+
const fill = buttonStyling?.backgroundColor?.trim() || JOTUL_BRAND_PRIMARY_HEX;
|
|
299
|
+
const label = buttonStyling?.textColor?.trim() || '#ffffff';
|
|
300
|
+
return {
|
|
301
|
+
fill,
|
|
302
|
+
label,
|
|
303
|
+
key: `${fill}\0${label}`,
|
|
304
|
+
};
|
|
305
|
+
}, [buttonStyling?.backgroundColor, buttonStyling?.textColor]);
|
|
306
|
+
const mapCanvas = (_jsxs(MapContainer, { center: defaultCenter ?? [59.9139, 10.7522], zoom: 6, className: "jwi-h-full jwi-w-full", zoomControl: true, children: [_jsx(TileLayer, { attribution: '\u00A9 <a href="https://www.openstreetmap.org/copyright" target="_blank" rel="noreferrer">OpenStreetMap</a> contributors \u00A9 <a href="https://carto.com/attributions" target="_blank" rel="noreferrer">CARTO</a>', url: OSM_MINIMAL_TILE_URL, maxZoom: 19 }), _jsx(FitMapBounds, { boundsKey: mapBoundsKey, points: mapPoints, defaultCenter: defaultCenter }), _jsx(FocusActiveDealer, { point: activeMapPoint }), _jsx(ClusteredMapMarkers, { points: mapPoints, pointsBoundsKey: mapBoundsKey, clusterThemeKey: mapClusterTheme.key, clusterBrandFill: mapClusterTheme.fill, clusterBrandLabel: mapClusterTheme.label, market: market, activeDealerName: activeDealerName, onUnavailable: () => setClusterUnavailable(true), onSelectDealer: (dealer) => {
|
|
307
|
+
setActiveDealerName(dealer.dealerName);
|
|
308
|
+
onMapDealerSelect?.(dealer);
|
|
309
|
+
} }), clusterUnavailable &&
|
|
310
|
+
mapPoints.map((point) => {
|
|
311
|
+
const isActive = activeDealerName === point.dealerName;
|
|
312
|
+
return (_jsx(Marker, { position: [point.latitude, point.longitude], icon: createDealerPinIcon(point.isExclusive, isActive, market), eventHandlers: {
|
|
313
|
+
click: () => {
|
|
314
|
+
setActiveDealerName(point.dealerName);
|
|
315
|
+
onMapDealerSelect?.({
|
|
316
|
+
dealerName: point.dealerName,
|
|
317
|
+
latitude: point.latitude,
|
|
318
|
+
longitude: point.longitude,
|
|
319
|
+
});
|
|
320
|
+
},
|
|
321
|
+
}, children: _jsx(Tooltip, { children: point.dealerName }) }, `${point.dealerName}-${point.latitude}-${point.longitude}`));
|
|
322
|
+
})] }));
|
|
323
|
+
const showInquiryInMapPopup = inquiryFormOpen && searchResult?.ok;
|
|
324
|
+
const showInquirySuccessScreen = isInquirySubmitted && !inquiryFormOpen;
|
|
325
|
+
const canShowMore = visibleDealerCount < dealers.length;
|
|
326
|
+
const showMoreButton = canShowMore ? (_jsx("button", { type: "button", onClick: () => setVisibleDealerCount((count) => Math.min(count + 10, dealers.length)), className: "jwi-mt-3 jwi-inline-flex jwi-w-full jwi-cursor-pointer jwi-items-center jwi-justify-center jwi-rounded-md jwi-border jwi-border-[#d8d2c7] jwi-bg-white jwi-px-3 jwi-py-2 jwi-text-sm jwi-font-medium jwi-text-[#111111]", children: "Show more" })) : null;
|
|
154
327
|
const handleDealerFocus = (dealerName) => {
|
|
155
328
|
setActiveDealerName(dealerName);
|
|
156
329
|
};
|
|
@@ -159,10 +332,11 @@ export function ProductPageWidget({ t, buttonStyling, isSearching, locationError
|
|
|
159
332
|
onClosePopup();
|
|
160
333
|
return;
|
|
161
334
|
}
|
|
162
|
-
setViewMode('list');
|
|
163
335
|
};
|
|
164
|
-
|
|
165
|
-
return (_jsx("div", { className: "jwi-flex jwi-w-full jwi-flex-col jwi-gap-
|
|
336
|
+
function renderSuccessPanel() {
|
|
337
|
+
return (_jsx("div", { className: "jwi-flex jwi-w-full jwi-items-center jwi-justify-center jwi-bg-white jwi-p-2", children: _jsxs("div", { className: `jwi-flex jwi-w-full jwi-max-w-[520px] jwi-flex-col jwi-items-center jwi-gap-4 ${R10} jwi-bg-white jwi-p-8 jwi-text-center`, children: [_jsx("div", { className: "jwi-text-2xl jwi-font-semibold jwi-text-[#111111]", children: t.inquiryThankYouTitle }), _jsx("div", { className: "jwi-text-sm jwi-leading-6 jwi-text-[#333333]", children: t.inquirySentSuccess }), _jsx("button", { type: "button", onClick: closeMapPopup, className: "jwi-mt-2 jwi-inline-flex jwi-cursor-pointer jwi-items-center jwi-justify-center jwi-rounded-md jwi-border jwi-border-[#d8d2c7] jwi-bg-white jwi-px-5 jwi-py-2.5 jwi-text-sm jwi-font-medium jwi-text-[#111111]", children: t.goBack })] }) }));
|
|
166
338
|
}
|
|
167
|
-
return (_jsxs(_Fragment, { children: [
|
|
339
|
+
return (_jsxs(_Fragment, { children: [!isMobileViewport && (_jsx(_Fragment, { children: _jsx("div", { className: "jwi-fixed jwi-inset-0 jwi-z-[9999] jwi-flex jwi-items-center jwi-justify-center jwi-bg-black/45 jwi-p-4", onClick: closeMapPopup, children: _jsxs("div", { className: `jwi-relative jwi-flex jwi-scale-100 jwi-flex-col jwi-overflow-hidden ${R10} jwi-bg-white jwi-shadow-[0_20px_60px_rgba(0,0,0,0.25)] jwi-transition-all ${showInquirySuccessScreen
|
|
340
|
+
? 'jwi-h-auto jwi-w-[min(92vw,620px)]'
|
|
341
|
+
: 'jwi-h-[min(85vh,860px)] jwi-w-[min(96vw,1200px)] md:jwi-flex-row'}`, onClick: (event) => event.stopPropagation(), children: [_jsx("button", { type: "button", onClick: closeMapPopup, className: "jwi-absolute jwi-right-3 jwi-top-3 jwi-z-[1200] jwi-inline-flex jwi-h-9 jwi-w-9 jwi-cursor-pointer jwi-items-center jwi-justify-center jwi-rounded-full jwi-border jwi-border-[#d8d2c7] jwi-bg-white jwi-text-xl jwi-leading-none jwi-text-[#111111] jwi-shadow-[0_2px_8px_rgba(0,0,0,0.12)]", "aria-label": t.closeMap, children: "\u00D7" }), showInquirySuccessScreen ? (renderSuccessPanel()) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "jwi-flex jwi-h-full jwi-min-h-0 jwi-w-full jwi-flex-col jwi-overflow-hidden md:jwi-w-[48%] md:jwi-border-r md:jwi-border-[#ece8df]", children: _jsx("div", { className: "jwi-flex jwi-h-full jwi-min-h-0 jwi-w-full jwi-flex-col jwi-gap-3 jwi-overflow-hidden jwi-bg-white jwi-p-6", children: showInquiryInMapPopup ? (_jsx(InquiryForm, { t: t, buttonStyling: buttonStyling, inquiryValues: inquiryValues, inquiryError: inquiryError, embedded: true, onInquiryClose: onInquiryClose, onInquirySubmit: onInquirySubmit, onInquiryFieldChange: onInquiryFieldChange })) : (_jsxs(_Fragment, { children: [_jsx(LocationSearch, { t: t, isManualSearchEnabled: isManualSearchEnabled, query: query, suggestions: suggestions, suggestionsOpen: suggestionsOpen, isSuggestionsLoading: isSuggestionsLoading, onQueryChange: onQueryChange, onQuerySubmit: onQuerySubmit, onSuggestionSelect: onSuggestionSelect, onDismissSuggestions: onDismissSuggestions }), _jsx(CampaignStatus, { campaign: searchResult?.campaign, t: t }), locationError != null && !isManualSearchEnabled && (_jsx(StatusBanner, { tone: "error", children: locationError })), searchResult?.ok === false && (_jsx(StatusBanner, { tone: "error", children: searchResult.error ?? '' })), isSearching ? (_jsxs("div", { className: "jwi-flex jwi-flex-col", children: [_jsx("div", { className: "jwi-w-full jwi-flex-shrink-0 jwi-border-b jwi-border-[#e6e1d7] jwi-pb-3", children: _jsx("div", { className: "jwi-h-5 jwi-w-48 jwi-animate-pulse jwi-rounded-full jwi-bg-[#ece8df]" }) }), _jsxs("div", { className: "jwi-mt-3 jwi-mr-[-12px] jwi-flex jwi-h-full jwi-flex-col jwi-gap-4 jwi-overflow-y-auto jwi-pr-[12px]", children: [_jsx(DealerCardSkeleton, {}), _jsx(DealerCardSkeleton, {}), _jsx(DealerCardSkeleton, {})] })] })) : (_jsxs(_Fragment, { children: [searchResult?.ok && (_jsx(DealerList, { dealers: visibleDealers, total: total, selectedDealerName: selectedDealerName, activeDealerName: activeDealerName, fitAvailableHeight: true, autoScrollToActive: true, maxHeightClassName: "jwi-max-h-none", t: t, buttonStyling: buttonStyling, borderStyling: borderStyling, market: market, onStartInquiry: onStartInquiry, onSelectDealer: handleDealerFocus })), showMoreButton] }))] })) }) }), _jsx("div", { className: "jwi-relative jwi-h-[45%] jwi-w-full md:jwi-h-full md:jwi-w-[52%]", children: mapCanvas })] }))] }) }) })), isMobileViewport && (_jsx("div", { className: "jwi-fixed jwi-inset-0 jwi-z-[9999] jwi-bg-white", children: _jsxs("div", { className: "jwi-relative jwi-flex jwi-h-full jwi-flex-col", children: [_jsx("div", { className: "jwi-flex jwi-justify-end jwi-bg-white jwi-px-4 jwi-pt-3", children: _jsx("button", { type: "button", onClick: closeMapPopup, className: "jwi-inline-flex jwi-h-9 jwi-w-9 jwi-cursor-pointer jwi-items-center jwi-justify-center jwi-rounded-full jwi-border jwi-border-[#d8d2c7] jwi-bg-white jwi-text-xl jwi-leading-none jwi-text-[#111111]", "aria-label": t.closeMap, children: "\u00D7" }) }), _jsx("div", { className: "jwi-min-h-0 jwi-flex-1 jwi-overflow-y-auto jwi-bg-white jwi-p-4 jwi-pb-24", children: showInquirySuccessScreen ? (renderSuccessPanel()) : showInquiryInMapPopup ? (_jsx(InquiryForm, { t: t, buttonStyling: buttonStyling, inquiryValues: inquiryValues, inquiryError: inquiryError, embedded: true, onInquiryClose: onInquiryClose, onInquirySubmit: onInquirySubmit, onInquiryFieldChange: onInquiryFieldChange })) : (_jsxs(_Fragment, { children: [_jsx(LocationSearch, { t: t, isManualSearchEnabled: isManualSearchEnabled, query: query, suggestions: suggestions, suggestionsOpen: suggestionsOpen, isSuggestionsLoading: isSuggestionsLoading, onQueryChange: onQueryChange, onQuerySubmit: onQuerySubmit, onSuggestionSelect: onSuggestionSelect, onDismissSuggestions: onDismissSuggestions }), _jsx(CampaignStatus, { campaign: searchResult?.campaign, t: t }), locationError != null && !isManualSearchEnabled && (_jsx(StatusBanner, { tone: "error", children: locationError })), searchResult?.ok === false && (_jsx(StatusBanner, { tone: "error", children: searchResult.error ?? '' })), isSearching ? (_jsxs("div", { className: "jwi-flex jwi-flex-col", children: [_jsx("div", { className: "jwi-w-full jwi-flex-shrink-0 jwi-border-b jwi-border-[#e6e1d7] jwi-pb-3", children: _jsx("div", { className: "jwi-h-5 jwi-w-48 jwi-animate-pulse jwi-rounded-full jwi-bg-[#ece8df]" }) }), _jsxs("div", { className: "jwi-mt-3 jwi-flex jwi-flex-col jwi-gap-4", children: [_jsx(DealerCardSkeleton, {}), _jsx(DealerCardSkeleton, {}), _jsx(DealerCardSkeleton, {})] })] })) : (_jsxs(_Fragment, { children: [searchResult?.ok && (_jsx(DealerList, { dealers: visibleDealers, total: total, selectedDealerName: selectedDealerName, activeDealerName: activeDealerName, maxHeightClassName: "jwi-max-h-none", autoScrollToActive: true, enableInternalScroll: false, t: t, buttonStyling: buttonStyling, borderStyling: borderStyling, market: market, onStartInquiry: onStartInquiry, onSelectDealer: handleDealerFocus })), showMoreButton] }))] })) }), !showInquiryInMapPopup && !showInquirySuccessScreen && !mobileMapExpanded && (_jsxs("button", { type: "button", onClick: () => setMobileMapExpanded((open) => !open), className: "jwi-absolute jwi-inset-x-0 jwi-bottom-0 jwi-z-20 jwi-flex jwi-h-14 jwi-items-center jwi-justify-center jwi-gap-2 jwi-rounded-t-[16px] jwi-border-t jwi-border-[#e6e1d7] jwi-bg-white jwi-text-sm jwi-font-semibold jwi-text-[#111111] jwi-shadow-[0_-6px_20px_rgba(0,0,0,0.12)]", children: [t.openMap, _jsx("span", { className: "jwi-inline-flex", style: { transform: 'rotate(-90deg)' }, children: _jsx(ArrowRightIcon, { className: "jwi-h-4 jwi-w-4 jwi-shrink-0" }) })] })), mobileMapExpanded && !showInquiryInMapPopup && !showInquirySuccessScreen && (_jsxs("div", { className: "jwi-absolute jwi-inset-x-0 jwi-bottom-0 jwi-z-30 jwi-h-[78vh] jwi-overflow-hidden jwi-rounded-t-[16px] jwi-bg-white jwi-shadow-[0_-12px_36px_rgba(0,0,0,0.22)]", children: [_jsxs("button", { type: "button", onClick: () => setMobileMapExpanded(false), className: "jwi-flex jwi-h-12 jwi-w-full jwi-items-center jwi-justify-center jwi-gap-2 jwi-border-b jwi-border-[#e6e1d7] jwi-bg-white jwi-text-sm jwi-font-semibold jwi-text-[#111111]", children: [t.closeMapMobile, _jsx("span", { className: "jwi-inline-flex", style: { transform: 'rotate(90deg)' }, children: _jsx(ArrowRightIcon, { className: "jwi-h-4 jwi-w-4 jwi-shrink-0" }) })] }), _jsx("div", { className: "jwi-h-[calc(78vh-48px)] jwi-w-full", children: mapCanvas })] }))] }) }))] }));
|
|
168
342
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { WidgetStrings } from '../../i18n/widgetStrings';
|
|
2
|
+
import type { DealerSearchResponse } from '../../types';
|
|
3
|
+
export declare function CampaignStatus({ campaign, t, }: {
|
|
4
|
+
campaign: DealerSearchResponse['campaign'];
|
|
5
|
+
t: WidgetStrings;
|
|
6
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
function formatCountdown(milliseconds, t) {
|
|
4
|
+
const totalSeconds = Math.max(0, Math.floor(milliseconds / 1000));
|
|
5
|
+
const days = Math.floor(totalSeconds / 86400);
|
|
6
|
+
const hours = Math.floor((totalSeconds % 86400) / 3600);
|
|
7
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
8
|
+
const seconds = totalSeconds % 60;
|
|
9
|
+
return t.campaignCountdownTime
|
|
10
|
+
.replace('{days}', String(days))
|
|
11
|
+
.replace('{hours}', String(hours))
|
|
12
|
+
.replace('{minutes}', String(minutes))
|
|
13
|
+
.replace('{seconds}', String(seconds));
|
|
14
|
+
}
|
|
15
|
+
export function CampaignStatus({ campaign, t, }) {
|
|
16
|
+
const [remaining, setRemaining] = useState(null);
|
|
17
|
+
const fromTime = campaign?.from ? Date.parse(campaign.from) : Number.NaN;
|
|
18
|
+
const toTime = campaign?.to ? Date.parse(campaign.to) : Number.NaN;
|
|
19
|
+
const hasCountdown = Number.isFinite(fromTime) && Number.isFinite(toTime);
|
|
20
|
+
const title = campaign?.name?.trim();
|
|
21
|
+
const salesMessage = campaign?.salesMessage?.trim();
|
|
22
|
+
const hasContent = Boolean(title || salesMessage);
|
|
23
|
+
const bannerClassName = 'jwi-w-full jwi-flex-shrink-0 jwi-rounded-[12px] jwi-border jwi-border-[#b8d8aa] jwi-bg-[#eff9e9] jwi-px-4 jwi-py-3 jwi-text-center jwi-text-[#16330f] jwi-shadow-[0_6px_16px_rgba(22,51,15,0.08)]';
|
|
24
|
+
const titleClassName = 'jwi-text-base jwi-font-semibold jwi-leading-tight';
|
|
25
|
+
const salesMessageClassName = title
|
|
26
|
+
? 'jwi-mt-1 jwi-text-sm jwi-font-medium jwi-leading-snug'
|
|
27
|
+
: 'jwi-text-sm jwi-font-medium jwi-leading-snug';
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!hasCountdown)
|
|
30
|
+
return;
|
|
31
|
+
const update = () => setRemaining(toTime - Date.now());
|
|
32
|
+
const initial = window.setTimeout(update, 0);
|
|
33
|
+
const timer = window.setInterval(update, 1000);
|
|
34
|
+
return () => {
|
|
35
|
+
window.clearTimeout(initial);
|
|
36
|
+
window.clearInterval(timer);
|
|
37
|
+
};
|
|
38
|
+
}, [hasCountdown, toTime]);
|
|
39
|
+
if (!hasCountdown || remaining == null) {
|
|
40
|
+
if (!hasContent)
|
|
41
|
+
return null;
|
|
42
|
+
return (_jsxs("div", { className: bannerClassName, children: [title && _jsx("div", { className: titleClassName, children: title }), salesMessage && _jsx("div", { className: salesMessageClassName, children: salesMessage })] }));
|
|
43
|
+
}
|
|
44
|
+
if (remaining <= 0) {
|
|
45
|
+
return (_jsxs("div", { className: bannerClassName, children: [_jsx("div", { className: "jwi-text-xs jwi-font-semibold jwi-uppercase jwi-tracking-[0.06em]", children: t.campaignEnded }), title && _jsx("div", { className: `jwi-mt-1 ${titleClassName}`, children: title }), salesMessage && _jsx("div", { className: "jwi-mt-1 jwi-text-sm jwi-font-medium jwi-leading-snug", children: salesMessage })] }));
|
|
46
|
+
}
|
|
47
|
+
return (_jsxs("div", { className: bannerClassName, children: [title && _jsx("div", { className: titleClassName, children: title }), salesMessage && _jsx("div", { className: salesMessageClassName, children: salesMessage }), _jsx("div", { className: hasContent ? 'jwi-mt-2 jwi-text-xs jwi-font-medium md:jwi-text-sm' : 'jwi-text-xs jwi-font-medium md:jwi-text-sm', children: t.campaignEndsIn.replace('{time}', formatCountdown(remaining, t)) })] }));
|
|
48
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import type { WidgetStrings } from '../../i18n/widgetStrings';
|
|
3
|
-
import type { DealerRecord, JotulWidgetButtonStyling } from '../../types';
|
|
3
|
+
import type { DealerRecord, JotulWidgetButtonStyling, JotulWidgetBorderStyling } from '../../types';
|
|
4
4
|
type DealerListProps = {
|
|
5
5
|
dealers: DealerRecord[];
|
|
6
6
|
total: number;
|
|
@@ -13,8 +13,10 @@ type DealerListProps = {
|
|
|
13
13
|
headerRight?: ReactNode;
|
|
14
14
|
t: WidgetStrings;
|
|
15
15
|
buttonStyling?: JotulWidgetButtonStyling;
|
|
16
|
+
borderStyling?: JotulWidgetBorderStyling;
|
|
17
|
+
market?: string;
|
|
16
18
|
onStartInquiry: (dealerName: string) => void;
|
|
17
19
|
onSelectDealer?: (dealerName: string) => void;
|
|
18
20
|
};
|
|
19
|
-
export declare function DealerList({ dealers, total, selectedDealerName, activeDealerName, maxHeightClassName, fitAvailableHeight, autoScrollToActive, enableInternalScroll, headerRight, t, buttonStyling, onStartInquiry, onSelectDealer, }: DealerListProps): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export declare function DealerList({ dealers, total, selectedDealerName, activeDealerName, maxHeightClassName, fitAvailableHeight, autoScrollToActive, enableInternalScroll, headerRight, t, buttonStyling, borderStyling, market, onStartInquiry, onSelectDealer, }: DealerListProps): import("react/jsx-runtime").JSX.Element;
|
|
20
22
|
export {};
|
|
@@ -5,8 +5,9 @@ import { ArrowRightIcon } from '../../icons/ArrowRightIcon';
|
|
|
5
5
|
import { TelephoneIcon } from '../../icons/TelephoneIcon';
|
|
6
6
|
import { getWidgetPrimaryButtonPresentation } from '../../utils/widgetPrimaryButtonPresentation';
|
|
7
7
|
import exclusiveDealerBadge from '../../images/jotul-exclusive-dealer.png';
|
|
8
|
+
import ildstedetDealer from '../../images/ildstedet-dealer.svg';
|
|
8
9
|
import { asText, formatDistance, getDealerAddressLines, getDealerKey, getDealerName, } from '../../utils';
|
|
9
|
-
export function DealerList({ dealers, total, selectedDealerName, activeDealerName = null, maxHeightClassName = 'jwi-max-h-[min(60vh,480px)]', fitAvailableHeight = false, autoScrollToActive = false, enableInternalScroll = true, headerRight, t, buttonStyling, onStartInquiry, onSelectDealer, }) {
|
|
10
|
+
export function DealerList({ dealers, total, selectedDealerName, activeDealerName = null, maxHeightClassName = 'jwi-max-h-[min(60vh,480px)]', fitAvailableHeight = false, autoScrollToActive = false, enableInternalScroll = true, headerRight, t, buttonStyling, borderStyling, market, onStartInquiry, onSelectDealer, }) {
|
|
10
11
|
const cardRefs = useRef({});
|
|
11
12
|
useEffect(() => {
|
|
12
13
|
if (!autoScrollToActive || !activeDealerName)
|
|
@@ -16,6 +17,7 @@ export function DealerList({ dealers, total, selectedDealerName, activeDealerNam
|
|
|
16
17
|
return;
|
|
17
18
|
card.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
18
19
|
}, [activeDealerName, autoScrollToActive]);
|
|
20
|
+
const defaultBorderColor = borderStyling?.borderColor ?? '#e6e1d7';
|
|
19
21
|
const inquiryCta = getWidgetPrimaryButtonPresentation(buttonStyling, 'panel', {
|
|
20
22
|
extraClassName: 'jwi-w-full jwi-max-w-full jwi-shrink-0 jwi-justify-between jwi-gap-2 md:jwi-max-w-[220px]',
|
|
21
23
|
});
|
|
@@ -33,9 +35,15 @@ export function DealerList({ dealers, total, selectedDealerName, activeDealerNam
|
|
|
33
35
|
dealer.exclusive === '1';
|
|
34
36
|
return (_jsxs("div", { ref: (element) => {
|
|
35
37
|
cardRefs.current[dealerName] = element;
|
|
36
|
-
}, className: `jwi-w-full jwi-cursor-pointer ${R10} jwi-border jwi-bg-white jwi-p-4 jwi-shadow-[0_1px_2px_rgba(17,17,17,0.03)] ${isActiveDealer ? 'jwi-border-[#ef2b18]' : '
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
}, className: `jwi-w-full jwi-cursor-pointer ${R10} jwi-border jwi-bg-white jwi-p-4 jwi-shadow-[0_1px_2px_rgba(17,17,17,0.03)] ${isActiveDealer ? 'jwi-border-[#ef2b18]' : ''}`, style: !isActiveDealer && defaultBorderColor !== '#e6e1d7'
|
|
39
|
+
? { borderColor: defaultBorderColor }
|
|
40
|
+
: undefined, onClick: () => onSelectDealer?.(dealerName), children: [_jsxs("div", { className: "jwi-flex jwi-items-start jwi-justify-between jwi-gap-3", children: [_jsx("div", { className: "jwi-min-w-0 jwi-max-w-[calc(100%-5rem)] jwi-pr-1", children: _jsxs("div", { className: "jwi-flex jwi-items-center jwi-gap-2", children: [isExclusive && (() => {
|
|
41
|
+
const badgeImage = market === 'NO' ? ildstedetDealer : exclusiveDealerBadge;
|
|
42
|
+
const badgeAlt = market === 'NO' ? 'Ildstedet dealer' : 'Jotul exclusive dealer';
|
|
43
|
+
return (_jsx("img", { src: typeof badgeImage === 'string'
|
|
44
|
+
? badgeImage
|
|
45
|
+
: badgeImage.src, alt: badgeAlt, className: "jwi-h-auto jwi-w-[40px] jwi-shrink-0 jwi-rounded-full jwi-m-0" }));
|
|
46
|
+
})(), _jsx("h3", { className: "jwi-m-0 jwi-text-sm md:jwi-text-base jwi-font-semibold jwi-leading-snug jwi-text-[#111111]", children: dealerName })] }) }), distance && (_jsx("div", { className: `jwi-shrink-0 jwi-whitespace-nowrap ${R10} jwi-bg-[#fbf3db] jwi-px-2.5 jwi-py-1 jwi-text-xs jwi-font-medium jwi-leading-none jwi-text-[#111111]`, children: distance }))] }), addressLines.length > 0 && (_jsx("div", { className: "jwi-mt-3 jwi-flex jwi-flex-col jwi-gap-0.5 jwi-text-sm jwi-leading-snug jwi-text-[#111111]", children: addressLines.map((line) => (_jsx("div", { children: line }, line))) })), _jsxs("div", { className: "jwi-mt-4 jwi-flex jwi-flex-col jwi-gap-3 md:jwi-flex-row md:jwi-items-center md:jwi-justify-between", children: [_jsxs("button", { type: "button", onClick: (event) => {
|
|
39
47
|
event.stopPropagation();
|
|
40
48
|
onStartInquiry(dealerName);
|
|
41
49
|
}, className: inquiryCta.className, style: inquiryCta.style, children: [_jsx("span", { children: isSelectedDealer ? t.sendInquiryEditing : t.sendInquiryCta }), _jsx(ArrowRightIcon, { className: "jwi-h-[18px] jwi-w-[18px] jwi-shrink-0" })] }), phone && (_jsxs("a", { href: `tel:${phone.replace(/\s+/g, '')}`, onClick: (event) => event.stopPropagation(), className: "jwi-inline-flex jwi-min-w-0 jwi-items-center jwi-gap-2 jwi-text-sm jwi-font-normal jwi-tabular-nums jwi-text-[#111111] hover:jwi-underline", children: [_jsx(TelephoneIcon, {}), _jsx("span", { className: "jwi-break-all", children: phone })] }))] })] }, getDealerKey(dealer, index)));
|
|
@@ -3,6 +3,8 @@ import { R10 } from '../../constants';
|
|
|
3
3
|
export function StatusBanner({ tone, children }) {
|
|
4
4
|
const toneClass = tone === 'success'
|
|
5
5
|
? 'jwi-border-[#b7e5c2] jwi-bg-[#eefbf2] jwi-text-[#1b5e20]'
|
|
6
|
-
:
|
|
6
|
+
: tone === 'info'
|
|
7
|
+
? 'jwi-border-[#e6e1d7] jwi-bg-[#fbf3db] jwi-text-[#111111]'
|
|
8
|
+
: 'jwi-border-[#f0c7c2] jwi-bg-[#fff3f1] jwi-text-[#8f2d21]';
|
|
7
9
|
return (_jsx("div", { className: `jwi-w-full jwi-flex-shrink-0 ${R10} jwi-border jwi-px-4 jwi-py-3 jwi-text-sm jwi-leading-[1.4] ${toneClass}`, children: children }));
|
|
8
10
|
}
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/** Primary Jotul brand red (CTAs, active list border, map clusters). */
|
|
2
|
+
export declare const JOTUL_BRAND_PRIMARY_HEX = "#ef2b18";
|
|
3
|
+
/** Slightly darker red for hover / cluster ring. */
|
|
4
|
+
export declare const JOTUL_BRAND_PRIMARY_DARK_HEX = "#d92817";
|
|
1
5
|
/** 10px radius for widget chrome (matches design spec). */
|
|
2
6
|
export declare const R10 = "jwi-rounded-[10px]";
|
|
3
7
|
export declare const FIND_DEALER_BUTTON_CLASS = "jwi-inline-flex jwi-w-full jwi-min-h-[56px] jwi-cursor-pointer jwi-items-center jwi-justify-center jwi-rounded-[10px] jwi-border-0 jwi-bg-[#ef2b18] jwi-px-7 jwi-text-base jwi-font-medium jwi-text-white hover:jwi-bg-[#d92817] disabled:jwi-cursor-wait disabled:hover:jwi-bg-[#ef2b18]";
|
package/dist/constants.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/** Primary Jotul brand red (CTAs, active list border, map clusters). */
|
|
2
|
+
export const JOTUL_BRAND_PRIMARY_HEX = '#ef2b18';
|
|
3
|
+
/** Slightly darker red for hover / cluster ring. */
|
|
4
|
+
export const JOTUL_BRAND_PRIMARY_DARK_HEX = '#d92817';
|
|
1
5
|
/** 10px radius for widget chrome (matches design spec). */
|
|
2
6
|
export const R10 = 'jwi-rounded-[10px]';
|
|
3
7
|
export const FIND_DEALER_BUTTON_CLASS = `jwi-inline-flex jwi-w-full jwi-min-h-[56px] jwi-cursor-pointer jwi-items-center jwi-justify-center ${R10} jwi-border-0 jwi-bg-[#ef2b18] jwi-px-7 jwi-text-base jwi-font-medium jwi-text-white hover:jwi-bg-[#d92817] disabled:jwi-cursor-wait disabled:hover:jwi-bg-[#ef2b18]`;
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"find": "Najít",
|
|
18
18
|
"finding": "Vyhledávání …",
|
|
19
19
|
"inquirySentSuccess": "Váš požadavek byl odeslán.",
|
|
20
|
+
"inquiryThankYouTitle": "Dekujeme za vas pozadavek",
|
|
20
21
|
"goBack": "Zpět",
|
|
21
22
|
"sendInquiryTitle": "Poptávka",
|
|
22
23
|
"sendInquiryTitleWithProduct": "Poptávka na {product}",
|
|
@@ -37,5 +38,8 @@
|
|
|
37
38
|
"mapView": "Mapa",
|
|
38
39
|
"closeMap": "Zavřít mapu",
|
|
39
40
|
"openMap": "Otevřít mapu",
|
|
40
|
-
"closeMapMobile": "Zavřít mapu"
|
|
41
|
+
"closeMapMobile": "Zavřít mapu",
|
|
42
|
+
"campaignEndsIn": "Campaign ends in {time}",
|
|
43
|
+
"campaignCountdownTime": "{days} days {hours} hours {minutes} minutes and {seconds} seconds",
|
|
44
|
+
"campaignEnded": "Campaign ended"
|
|
41
45
|
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"find": "Suchen",
|
|
18
18
|
"finding": "Suche …",
|
|
19
19
|
"inquirySentSuccess": "Ihre Anfrage wurde gesendet.",
|
|
20
|
+
"inquiryThankYouTitle": "Vielen Dank fur Ihre Anfrage",
|
|
20
21
|
"goBack": "Zurück",
|
|
21
22
|
"sendInquiryTitle": "Anfrage",
|
|
22
23
|
"sendInquiryTitleWithProduct": "Anfrage zu {product}",
|
|
@@ -37,5 +38,8 @@
|
|
|
37
38
|
"mapView": "Karte",
|
|
38
39
|
"closeMap": "Karte schließen",
|
|
39
40
|
"openMap": "Karte öffnen",
|
|
40
|
-
"closeMapMobile": "Karte schließen"
|
|
41
|
+
"closeMapMobile": "Karte schließen",
|
|
42
|
+
"campaignEndsIn": "Kampagne endet in {time}",
|
|
43
|
+
"campaignCountdownTime": "{days} Tage {hours} Stunden {minutes} Minuten und {seconds} Sekunden",
|
|
44
|
+
"campaignEnded": "Kampagne beendet"
|
|
41
45
|
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"find": "Locate",
|
|
18
18
|
"finding": "Searching …",
|
|
19
19
|
"inquirySentSuccess": "Your request has been sent.",
|
|
20
|
+
"inquiryThankYouTitle": "Thank you for your inquiry",
|
|
20
21
|
"goBack": "Back",
|
|
21
22
|
"sendInquiryTitle": "Request",
|
|
22
23
|
"sendInquiryTitleWithProduct": "Request regarding {product}",
|
|
@@ -37,5 +38,8 @@
|
|
|
37
38
|
"mapView": "Map",
|
|
38
39
|
"closeMap": "Close map",
|
|
39
40
|
"openMap": "Open map",
|
|
40
|
-
"closeMapMobile": "Close map"
|
|
41
|
+
"closeMapMobile": "Close map",
|
|
42
|
+
"campaignEndsIn": "Campaign ends in {time}",
|
|
43
|
+
"campaignCountdownTime": "{days} days {hours} hours {minutes} minutes and {seconds} seconds",
|
|
44
|
+
"campaignEnded": "Campaign ended"
|
|
41
45
|
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"find": "Etsi",
|
|
18
18
|
"finding": "Haetaan …",
|
|
19
19
|
"inquirySentSuccess": "Pyyntösi on lähetetty.",
|
|
20
|
+
"inquiryThankYouTitle": "Kiitos yhteydenotostasi",
|
|
20
21
|
"goBack": "Takaisin",
|
|
21
22
|
"sendInquiryTitle": "Pyyntö",
|
|
22
23
|
"sendInquiryTitleWithProduct": "Pyyntö tuotteesta {product}",
|
|
@@ -37,5 +38,8 @@
|
|
|
37
38
|
"mapView": "Kartta",
|
|
38
39
|
"closeMap": "Sulje kartta",
|
|
39
40
|
"openMap": "Avaa kartta",
|
|
40
|
-
"closeMapMobile": "Sulje kartta"
|
|
41
|
+
"closeMapMobile": "Sulje kartta",
|
|
42
|
+
"campaignEndsIn": "Campaign ends in {time}",
|
|
43
|
+
"campaignCountdownTime": "{days} days {hours} hours {minutes} minutes and {seconds} seconds",
|
|
44
|
+
"campaignEnded": "Campaign ended"
|
|
41
45
|
}
|