@jotul/jotul-widgets 1.2.5 → 2.0.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 +112 -28
- package/dist/JotulWidget.css +1 -1
- package/dist/JotulWidget.d.ts +1 -1
- package/dist/JotulWidget.js +325 -132
- package/dist/analytics/WidgetTrackingContext.d.ts +14 -0
- package/dist/analytics/WidgetTrackingContext.js +5 -0
- package/dist/analytics/gtm.d.ts +7 -0
- package/dist/analytics/gtm.js +17 -0
- package/dist/analytics/widgetTracking.d.ts +54 -0
- package/dist/analytics/widgetTracking.js +144 -0
- package/dist/api.d.ts +27 -1
- package/dist/api.js +74 -0
- package/dist/components/FindDealerDrawerWidget.d.ts +7 -4
- package/dist/components/FindDealerDrawerWidget.js +17 -14
- package/dist/components/InquiryField.d.ts +3 -1
- package/dist/components/InquiryField.js +19 -2
- package/dist/components/InquirySelectField.d.ts +13 -0
- package/dist/components/InquirySelectField.js +5 -0
- package/dist/components/ProductPageWidget.d.ts +7 -4
- package/dist/components/ProductPageWidget.js +12 -14
- package/dist/components/TurnstileField.d.ts +7 -0
- package/dist/components/TurnstileField.js +48 -0
- package/dist/components/WarrantyFormWidget.d.ts +12 -0
- package/dist/components/WarrantyFormWidget.js +98 -0
- package/dist/components/product-page/DealerList.d.ts +1 -1
- package/dist/components/product-page/DealerList.js +14 -6
- package/dist/components/product-page/InquiryForm.d.ts +6 -2
- package/dist/components/product-page/InquiryForm.js +21 -3
- package/dist/constants/turnstile.d.ts +8 -0
- package/dist/constants/turnstile.js +19 -0
- package/dist/hooks/useTurnstileSiteKey.d.ts +1 -0
- package/dist/hooks/useTurnstileSiteKey.js +38 -0
- package/dist/i18n/locales/cz.json +34 -1
- package/dist/i18n/locales/de.json +34 -1
- package/dist/i18n/locales/en.json +34 -1
- package/dist/i18n/locales/fi.json +34 -1
- package/dist/i18n/locales/fr.json +34 -1
- package/dist/i18n/locales/nl.json +34 -1
- package/dist/i18n/locales/no.json +34 -1
- package/dist/i18n/locales/pl.json +34 -1
- package/dist/i18n/locales/se.json +34 -1
- package/dist/i18n/widgetStrings.d.ts +33 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/turnstile.d.ts +18 -0
- package/dist/turnstile.js +31 -0
- package/dist/types.d.ts +56 -0
- package/dist/utils/inquiryCategories.d.ts +8 -0
- package/dist/utils/inquiryCategories.js +24 -0
- package/dist/utils/inquirySubmit.d.ts +24 -0
- package/dist/utils/inquirySubmit.js +35 -0
- package/dist/utils/urlDealerId.d.ts +2 -0
- package/dist/utils/urlDealerId.js +5 -0
- package/dist/utils/usMarket.d.ts +2 -0
- package/dist/utils/usMarket.js +9 -0
- package/dist/utils/warrantyForm.d.ts +38 -0
- package/dist/utils/warrantyForm.js +80 -0
- package/dist/utils.d.ts +10 -1
- package/dist/utils.js +46 -3
- package/package.json +5 -2
|
@@ -19,7 +19,7 @@ import { MARKER_CLUSTER_LINK_KM, MARKER_CLUSTER_MIN_GROUP, partitionDealersForMa
|
|
|
19
19
|
import { loadLeafletMarkerCluster } from '../utils/loadLeafletMarkerCluster';
|
|
20
20
|
import { markerClusterCountIconHtml } from '../utils/markerClusterIconHtml';
|
|
21
21
|
import { JOTUL_BRAND_PRIMARY_HEX, R10 } from '../constants';
|
|
22
|
-
import { isExclusiveDealer } from '../utils';
|
|
22
|
+
import { isExclusiveDealer, getDealerName } from '../utils';
|
|
23
23
|
const OSM_MINIMAL_TILE_URL = 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png';
|
|
24
24
|
const EMPTY_DEALERS = [];
|
|
25
25
|
function mapPointsBoundsKey(points, defaultCenter) {
|
|
@@ -46,11 +46,7 @@ function readNumber(value) {
|
|
|
46
46
|
}
|
|
47
47
|
return null;
|
|
48
48
|
}
|
|
49
|
-
function
|
|
50
|
-
const raw = dealer.name;
|
|
51
|
-
return typeof raw === 'string' && raw.trim() ? raw.trim() : 'Unknown dealer';
|
|
52
|
-
}
|
|
53
|
-
function getDealerMapPoint(dealer) {
|
|
49
|
+
function getDealerMapPoint(dealer, unknownDealerLabel) {
|
|
54
50
|
const latitude = readNumber(dealer.latitude);
|
|
55
51
|
const longitude = readNumber(dealer.longitude);
|
|
56
52
|
if (latitude == null || longitude == null)
|
|
@@ -58,7 +54,7 @@ function getDealerMapPoint(dealer) {
|
|
|
58
54
|
const isExclusive = isExclusiveDealer(dealer);
|
|
59
55
|
return {
|
|
60
56
|
dealer,
|
|
61
|
-
dealerName: getDealerName(dealer),
|
|
57
|
+
dealerName: getDealerName(dealer, unknownDealerLabel),
|
|
62
58
|
latitude,
|
|
63
59
|
longitude,
|
|
64
60
|
isExclusive,
|
|
@@ -231,7 +227,7 @@ function ClusteredMapMarkers({ points, pointsBoundsKey, clusterThemeKey, cluster
|
|
|
231
227
|
}, [map, pointsBoundsKey, clusterThemeKey]);
|
|
232
228
|
return null;
|
|
233
229
|
}
|
|
234
|
-
export function ProductPageWidget({ t, buttonStyling, borderStyling, markets, scope, isSearching, locationError, searchResult, mapSearchResult, inquiryValues, inquiryError, isInquirySubmitted, selectedDealerName, isManualSearchEnabled, query, suggestions, suggestionsOpen, isSuggestionsLoading, onQueryChange, onQuerySubmit, onSuggestionSelect, onDismissSuggestions, onInquiryClose, onInquirySubmit, onInquiryFieldChange, onStartInquiry, onMapDealerSelect, onClosePopup, }) {
|
|
230
|
+
export function ProductPageWidget({ t, buttonStyling, borderStyling, markets, scope, isSearching, locationError, searchResult, mapSearchResult, inquiryValues, inquiryError, isSubmittingInquiry = false, turnstileSiteKey, turnstileResetKey = 0, isInquirySubmitted, selectedDealerName, isManualSearchEnabled, query, suggestions, suggestionsOpen, isSuggestionsLoading, onQueryChange, onQuerySubmit, onSuggestionSelect, onDismissSuggestions, onInquiryClose, onInquirySubmit, onInquiryFieldChange, onStartInquiry, onMapDealerSelect, onClosePopup, }) {
|
|
235
231
|
const rawDealers = (searchResult?.dealers ?? EMPTY_DEALERS);
|
|
236
232
|
const dealers = useMemo(() => (scope === 'ildstedet' ? rawDealers.filter(isExclusiveDealer) : rawDealers), [rawDealers, scope]);
|
|
237
233
|
const rawMapDealers = (mapSearchResult?.dealers ?? rawDealers);
|
|
@@ -266,13 +262,15 @@ export function ProductPageWidget({ t, buttonStyling, borderStyling, markets, sc
|
|
|
266
262
|
useEffect(() => {
|
|
267
263
|
if (activeDealerName == null)
|
|
268
264
|
return;
|
|
269
|
-
const activeIndex = dealers.findIndex((dealer) => getDealerName(dealer) === activeDealerName);
|
|
265
|
+
const activeIndex = dealers.findIndex((dealer) => getDealerName(dealer, t.unknownDealer) === activeDealerName);
|
|
270
266
|
if (activeIndex < 0 || activeIndex < visibleDealerCount)
|
|
271
267
|
return;
|
|
272
268
|
const nextCount = Math.ceil((activeIndex + 1) / 10) * 10;
|
|
273
269
|
setVisibleDealerCount(nextCount);
|
|
274
270
|
}, [activeDealerName, dealers, visibleDealerCount]);
|
|
275
|
-
const mapPoints = useMemo(() => mapDealers
|
|
271
|
+
const mapPoints = useMemo(() => mapDealers
|
|
272
|
+
.map((dealer) => getDealerMapPoint(dealer, t.unknownDealer))
|
|
273
|
+
.filter((v) => v != null), [mapDealers, t.unknownDealer]);
|
|
276
274
|
useEffect(() => {
|
|
277
275
|
setClusterUnavailable(false);
|
|
278
276
|
}, [mapPoints]);
|
|
@@ -288,8 +286,8 @@ export function ProductPageWidget({ t, buttonStyling, borderStyling, markets, sc
|
|
|
288
286
|
}, [mapPoints, searchResult?.origin?.latitude, searchResult?.origin?.longitude]);
|
|
289
287
|
const mapBoundsKey = useMemo(() => mapPointsBoundsKey(mapPoints, defaultCenter), [mapPoints, defaultCenter]);
|
|
290
288
|
useEffect(() => {
|
|
291
|
-
setActiveDealerName(dealers.length > 0 ? getDealerName(dealers[0]) : null);
|
|
292
|
-
}, [dealers]);
|
|
289
|
+
setActiveDealerName(dealers.length > 0 ? getDealerName(dealers[0], t.unknownDealer) : null);
|
|
290
|
+
}, [dealers, t.unknownDealer]);
|
|
293
291
|
const activeMapPoint = useMemo(() => mapPoints.find((p) => p.dealerName === activeDealerName) ?? null, [activeDealerName, mapPoints]);
|
|
294
292
|
const mapClusterTheme = useMemo(() => {
|
|
295
293
|
const fill = buttonStyling?.backgroundColor?.trim() || JOTUL_BRAND_PRIMARY_HEX;
|
|
@@ -320,7 +318,7 @@ export function ProductPageWidget({ t, buttonStyling, borderStyling, markets, sc
|
|
|
320
318
|
const showInquiryInMapPopup = inquiryFormOpen && searchResult?.ok;
|
|
321
319
|
const showInquirySuccessScreen = isInquirySubmitted && !inquiryFormOpen;
|
|
322
320
|
const canShowMore = visibleDealerCount < dealers.length;
|
|
323
|
-
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:
|
|
321
|
+
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: t.showMore })) : null;
|
|
324
322
|
const handleDealerFocus = (dealerName) => {
|
|
325
323
|
setActiveDealerName(dealerName);
|
|
326
324
|
};
|
|
@@ -335,5 +333,5 @@ export function ProductPageWidget({ t, buttonStyling, borderStyling, markets, sc
|
|
|
335
333
|
}
|
|
336
334
|
return (_jsxs(_Fragment, { children: [!isMobileViewport && (_jsx(_Fragment, { children: _jsx("div", { className: "jwi-fixed jwi-inset-0 jwi-z-[2147483647] 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
|
|
337
335
|
? 'jwi-h-auto jwi-w-[min(92vw,620px)]'
|
|
338
|
-
: '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_2s_ease-in-out_infinite] 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, markets: markets, 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-[2147483647] 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_2s_ease-in-out_infinite] 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, markets: markets, 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 })] }))] }) }))] }));
|
|
336
|
+
: '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, isSubmitting: isSubmittingInquiry, turnstileSiteKey: turnstileSiteKey, turnstileResetKey: turnstileResetKey, 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_2s_ease-in-out_infinite] 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, markets: markets, 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-[2147483647] 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, isSubmitting: isSubmittingInquiry, turnstileSiteKey: turnstileSiteKey, turnstileResetKey: turnstileResetKey, 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_2s_ease-in-out_infinite] 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, markets: markets, 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 })] }))] }) }))] }));
|
|
339
337
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type TurnstileFieldProps = {
|
|
2
|
+
siteKey: string;
|
|
3
|
+
resetKey?: number;
|
|
4
|
+
onTokenChange: (token: string | null) => void;
|
|
5
|
+
};
|
|
6
|
+
export declare function TurnstileField({ siteKey, resetKey, onTokenChange }: TurnstileFieldProps): import("react/jsx-runtime").JSX.Element | null;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { loadTurnstileScript } from '../turnstile';
|
|
4
|
+
export function TurnstileField({ siteKey, resetKey = 0, onTokenChange }) {
|
|
5
|
+
const containerRef = useRef(null);
|
|
6
|
+
const widgetIdRef = useRef(null);
|
|
7
|
+
const onTokenChangeRef = useRef(onTokenChange);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
onTokenChangeRef.current = onTokenChange;
|
|
10
|
+
}, [onTokenChange]);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const trimmedSiteKey = siteKey.trim();
|
|
13
|
+
if (!trimmedSiteKey) {
|
|
14
|
+
onTokenChangeRef.current(null);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
let cancelled = false;
|
|
18
|
+
void loadTurnstileScript()
|
|
19
|
+
.then(() => {
|
|
20
|
+
if (cancelled || containerRef.current == null || window.turnstile == null) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (widgetIdRef.current) {
|
|
24
|
+
window.turnstile.remove(widgetIdRef.current);
|
|
25
|
+
widgetIdRef.current = null;
|
|
26
|
+
}
|
|
27
|
+
widgetIdRef.current = window.turnstile.render(containerRef.current, {
|
|
28
|
+
sitekey: trimmedSiteKey,
|
|
29
|
+
theme: 'light',
|
|
30
|
+
size: 'flexible',
|
|
31
|
+
callback: (token) => onTokenChangeRef.current(token),
|
|
32
|
+
'expired-callback': () => onTokenChangeRef.current(null),
|
|
33
|
+
'error-callback': () => onTokenChangeRef.current(null),
|
|
34
|
+
});
|
|
35
|
+
})
|
|
36
|
+
.catch(() => onTokenChangeRef.current(null));
|
|
37
|
+
return () => {
|
|
38
|
+
cancelled = true;
|
|
39
|
+
if (widgetIdRef.current && window.turnstile) {
|
|
40
|
+
window.turnstile.remove(widgetIdRef.current);
|
|
41
|
+
widgetIdRef.current = null;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}, [siteKey, resetKey]);
|
|
45
|
+
if (!siteKey.trim())
|
|
46
|
+
return null;
|
|
47
|
+
return _jsx("div", { ref: containerRef, className: "jwi-mt-4 jwi-min-h-[65px]" });
|
|
48
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { JotulWidgetStyling } from '../types';
|
|
2
|
+
export type WarrantyFormWidgetProps = {
|
|
3
|
+
endpoint?: string;
|
|
4
|
+
className?: string;
|
|
5
|
+
locale?: string;
|
|
6
|
+
/** ISO 3166-1 alpha-2 market code (singular). */
|
|
7
|
+
market?: string;
|
|
8
|
+
styling?: JotulWidgetStyling;
|
|
9
|
+
turnstileSiteKey?: string;
|
|
10
|
+
turnstileConfigEndpoint?: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function WarrantyFormWidget({ endpoint, className, locale: localeProp, market: marketProp, styling, turnstileSiteKey: turnstileSiteKeyProp, turnstileConfigEndpoint, }: WarrantyFormWidgetProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
4
|
+
import { submitWarranty } from '../api';
|
|
5
|
+
import { R10 } from '../constants';
|
|
6
|
+
import { InquiryField } from './InquiryField';
|
|
7
|
+
import { TurnstileField } from './TurnstileField';
|
|
8
|
+
import { DEFAULT_WIDGET_LOCALE_TAG, resolveWidgetUiLocale, WIDGET_STRINGS, } from '../i18n/widgetStrings';
|
|
9
|
+
import { useTurnstileSiteKey } from '../hooks/useTurnstileSiteKey';
|
|
10
|
+
import { getWidgetPrimaryButtonPresentation } from '../utils/widgetPrimaryButtonPresentation';
|
|
11
|
+
import { isUsWarrantyMarket } from '../utils/usMarket';
|
|
12
|
+
import { createWarrantyFormValues, validateWarrantyFormValues, warrantyFormValuesToApiPayload, } from '../utils/warrantyForm';
|
|
13
|
+
export function WarrantyFormWidget({ endpoint = '/api/jotul/warranty', className, locale: localeProp, market: marketProp, styling, turnstileSiteKey: turnstileSiteKeyProp, turnstileConfigEndpoint, }) {
|
|
14
|
+
const normalizedMarket = useMemo(() => {
|
|
15
|
+
const trimmed = marketProp?.trim().toUpperCase();
|
|
16
|
+
return trimmed && /^[A-Z]{2}$/.test(trimmed) ? trimmed : undefined;
|
|
17
|
+
}, [marketProp]);
|
|
18
|
+
const resolvedUiLocale = useMemo(() => resolveWidgetUiLocale(localeProp, normalizedMarket), [localeProp, normalizedMarket]);
|
|
19
|
+
const showCounty = isUsWarrantyMarket(normalizedMarket, localeProp);
|
|
20
|
+
const t = WIDGET_STRINGS[resolvedUiLocale];
|
|
21
|
+
const resolvedTurnstileSiteKey = useTurnstileSiteKey(turnstileSiteKeyProp, turnstileConfigEndpoint);
|
|
22
|
+
const [values, setValues] = useState(createWarrantyFormValues);
|
|
23
|
+
const [formError, setFormError] = useState(null);
|
|
24
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
25
|
+
const [isSubmitted, setIsSubmitted] = useState(false);
|
|
26
|
+
const [turnstileToken, setTurnstileToken] = useState(null);
|
|
27
|
+
const [turnstileResetKey, setTurnstileResetKey] = useState(0);
|
|
28
|
+
const handleTurnstileTokenChange = useCallback((token) => {
|
|
29
|
+
setTurnstileToken(token);
|
|
30
|
+
if (token) {
|
|
31
|
+
setFormError((current) => current === t.formValidationCaptcha ? null : current);
|
|
32
|
+
}
|
|
33
|
+
}, [t.formValidationCaptcha]);
|
|
34
|
+
const submitButton = getWidgetPrimaryButtonPresentation(styling?.button, 'panel', {
|
|
35
|
+
extraClassName: 'jwi-mt-4 jwi-w-full',
|
|
36
|
+
});
|
|
37
|
+
const fieldClassName = `jwi-w-full ${R10} jwi-border jwi-border-[#d8d2c7] jwi-bg-white jwi-px-4 jwi-py-3 jwi-text-sm jwi-leading-[1.4] jwi-text-[#111111] jwi-outline-none placeholder:jwi-text-[#767676] focus:jwi-border-[#111111]`;
|
|
38
|
+
const updateField = useCallback((key, value) => {
|
|
39
|
+
setValues((current) => ({ ...current, [key]: value }));
|
|
40
|
+
}, []);
|
|
41
|
+
const handleSubmit = useCallback(async () => {
|
|
42
|
+
const validationError = validateWarrantyFormValues(values, {
|
|
43
|
+
market: normalizedMarket,
|
|
44
|
+
locale: localeProp,
|
|
45
|
+
});
|
|
46
|
+
if (validationError) {
|
|
47
|
+
if (validationError.type === 'invalid_email') {
|
|
48
|
+
setFormError(t.formValidationEmail);
|
|
49
|
+
}
|
|
50
|
+
else if (validationError.field === 'consent') {
|
|
51
|
+
setFormError(t.formValidationConsent);
|
|
52
|
+
}
|
|
53
|
+
else if (validationError.field === 'installerName') {
|
|
54
|
+
setFormError(t.formValidationInstaller);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
setFormError(t.formValidationRequired);
|
|
58
|
+
}
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (resolvedTurnstileSiteKey && !turnstileToken) {
|
|
62
|
+
setFormError(t.formValidationCaptcha);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
setFormError(null);
|
|
66
|
+
setIsSubmitting(true);
|
|
67
|
+
try {
|
|
68
|
+
const result = await submitWarranty(warrantyFormValuesToApiPayload(values, {
|
|
69
|
+
turnstileToken,
|
|
70
|
+
market: normalizedMarket,
|
|
71
|
+
domain: typeof window !== 'undefined' ? window.location.hostname : undefined,
|
|
72
|
+
}), { endpoint });
|
|
73
|
+
if (result.ok !== true) {
|
|
74
|
+
setFormError(typeof result.error === 'string' ? result.error : t.genericWidgetError);
|
|
75
|
+
setTurnstileResetKey((current) => current + 1);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
setIsSubmitted(true);
|
|
79
|
+
setTurnstileResetKey((current) => current + 1);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
setFormError(t.genericWidgetError);
|
|
83
|
+
setTurnstileResetKey((current) => current + 1);
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
setIsSubmitting(false);
|
|
87
|
+
}
|
|
88
|
+
}, [endpoint, localeProp, normalizedMarket, resolvedTurnstileSiteKey, t, turnstileToken, values]);
|
|
89
|
+
const shellClass = 'jwi-box-border jwi-flex jwi-w-full jwi-max-w-[540px] jwi-flex-col jwi-font-sans jwi-text-[#111111]';
|
|
90
|
+
const rootClass = className != null && className !== '' ? `${shellClass} ${className}` : shellClass;
|
|
91
|
+
if (isSubmitted) {
|
|
92
|
+
return (_jsx("div", { className: rootClass, children: _jsxs("div", { className: `jwi-flex jwi-w-full jwi-flex-col jwi-items-center jwi-gap-4 ${R10} jwi-border jwi-border-[#e6e1d7] jwi-bg-white jwi-p-8 jwi-text-center`, children: [_jsx("div", { className: "jwi-text-2xl jwi-font-semibold jwi-text-[#111111]", children: t.warrantySuccessTitle }), _jsx("div", { className: "jwi-text-sm jwi-leading-6 jwi-text-[#333333]", children: t.warrantySuccessMessage })] }) }));
|
|
93
|
+
}
|
|
94
|
+
return (_jsx("div", { className: rootClass, children: _jsxs("form", { onSubmit: (event) => {
|
|
95
|
+
event.preventDefault();
|
|
96
|
+
void handleSubmit();
|
|
97
|
+
}, className: `jwi-w-full ${R10} jwi-border jwi-border-[#e6e1d7] jwi-bg-white jwi-p-4 jwi-shadow-[0_1px_2px_rgba(17,17,17,0.03)]`, children: [_jsx("h3", { className: "jwi-m-0 jwi-text-base jwi-font-semibold jwi-leading-snug jwi-text-[#111111]", children: t.warrantyCustomerSectionTitle }), _jsxs("div", { className: "jwi-mt-4 jwi-flex jwi-flex-col jwi-gap-3", children: [_jsxs("div", { className: "jwi-grid jwi-grid-cols-1 jwi-gap-3 md:jwi-grid-cols-2", children: [_jsx(InquiryField, { label: t.fieldFirstName, value: values.firstName, onChange: (value) => updateField('firstName', value) }), _jsx(InquiryField, { label: t.fieldLastName, value: values.lastName, onChange: (value) => updateField('lastName', value) })] }), _jsx(InquiryField, { label: t.fieldAddress, value: values.address, onChange: (value) => updateField('address', value) }), _jsxs("div", { className: "jwi-grid jwi-grid-cols-1 jwi-gap-3 md:jwi-grid-cols-2", children: [_jsx(InquiryField, { label: t.fieldZipcode, value: values.zipcode, onChange: (value) => updateField('zipcode', value) }), _jsx(InquiryField, { label: t.fieldCity, value: values.city, onChange: (value) => updateField('city', value) })] }), showCounty ? (_jsx(InquiryField, { label: t.fieldCounty, value: values.county, onChange: (value) => updateField('county', value) })) : null, _jsx(InquiryField, { label: t.fieldPhone, type: "tel", value: values.phone, onChange: (value) => updateField('phone', value) }), _jsx(InquiryField, { label: t.fieldEmail, type: "email", value: values.email, onChange: (value) => updateField('email', value) })] }), _jsx("h3", { className: "jwi-m-0 jwi-mt-6 jwi-text-base jwi-font-semibold jwi-leading-snug jwi-text-[#111111]", children: t.warrantyProductSectionTitle }), _jsxs("div", { className: "jwi-mt-4 jwi-flex jwi-flex-col jwi-gap-3", children: [_jsxs("label", { className: "jwi-flex jwi-flex-col jwi-gap-1.5", children: [_jsx("span", { className: "jwi-text-sm jwi-font-medium jwi-text-[#111111]", children: t.fieldProductName }), _jsx("input", { type: "text", value: values.productName, onChange: (event) => updateField('productName', event.target.value), className: fieldClassName }), _jsx("span", { className: "jwi-text-xs jwi-leading-[1.4] jwi-text-[#767676]", children: t.fieldProductNameHint })] }), _jsxs("label", { className: "jwi-flex jwi-flex-col jwi-gap-1.5", children: [_jsx("span", { className: "jwi-text-sm jwi-font-medium jwi-text-[#111111]", children: t.fieldPurchaseDate }), _jsx("input", { type: "date", value: values.purchaseDate, onChange: (event) => updateField('purchaseDate', event.target.value), className: fieldClassName })] }), _jsx(InquiryField, { label: t.fieldPurchasedFromDealer, value: values.purchasedFromDealer, onChange: (value) => updateField('purchasedFromDealer', value) }), _jsxs("fieldset", { className: "jwi-m-0 jwi-border-0 jwi-p-0", children: [_jsx("legend", { className: "jwi-mb-2 jwi-text-sm jwi-font-medium jwi-text-[#111111]", children: t.fieldSelfInstalled }), _jsxs("div", { className: "jwi-flex jwi-flex-col jwi-gap-2", children: [_jsxs("label", { className: "jwi-inline-flex jwi-cursor-pointer jwi-items-center jwi-gap-2 jwi-text-sm jwi-text-[#111111]", children: [_jsx("input", { type: "radio", name: "selfInstalled", value: "yes", checked: values.selfInstalled === 'yes', onChange: () => updateField('selfInstalled', 'yes') }), t.fieldSelfInstalledYes] }), _jsxs("label", { className: "jwi-inline-flex jwi-cursor-pointer jwi-items-center jwi-gap-2 jwi-text-sm jwi-text-[#111111]", children: [_jsx("input", { type: "radio", name: "selfInstalled", value: "no", checked: values.selfInstalled === 'no', onChange: () => updateField('selfInstalled', 'no') }), t.fieldSelfInstalledNo] })] })] }), values.selfInstalled === 'no' ? (_jsx(InquiryField, { label: t.fieldInstallerName, value: values.installerName, onChange: (value) => updateField('installerName', value) })) : null, _jsxs("label", { className: "jwi-flex jwi-cursor-pointer jwi-items-start jwi-gap-2", children: [_jsx("input", { type: "checkbox", checked: values.consent, onChange: (event) => updateField('consent', event.target.checked), className: "jwi-mt-1" }), _jsx("span", { className: "jwi-text-sm jwi-leading-[1.4] jwi-text-[#111111]", children: t.fieldConsent })] })] }), formError ? (_jsx("div", { className: `jwi-mt-4 ${R10} jwi-border jwi-border-[#f0c7c2] jwi-bg-[#fff3f1] jwi-px-4 jwi-py-3 jwi-text-sm jwi-leading-[1.4] jwi-text-[#8f2d21]`, children: formError })) : null, resolvedTurnstileSiteKey ? (_jsx(TurnstileField, { siteKey: resolvedTurnstileSiteKey, resetKey: turnstileResetKey, onTokenChange: handleTurnstileTokenChange })) : null, _jsx("button", { type: "submit", disabled: isSubmitting, className: submitButton.className, style: submitButton.style, children: isSubmitting ? t.warrantySubmitting : t.warrantySubmitCta })] }) }));
|
|
98
|
+
}
|
|
@@ -15,7 +15,7 @@ type DealerListProps = {
|
|
|
15
15
|
buttonStyling?: JotulWidgetButtonStyling;
|
|
16
16
|
borderStyling?: JotulWidgetBorderStyling;
|
|
17
17
|
markets?: string[];
|
|
18
|
-
onStartInquiry: (
|
|
18
|
+
onStartInquiry: (dealer: DealerRecord) => void;
|
|
19
19
|
onSelectDealer?: (dealerName: string) => void;
|
|
20
20
|
};
|
|
21
21
|
export declare function DealerList({ dealers, total, selectedDealerName, activeDealerName, maxHeightClassName, fitAvailableHeight, autoScrollToActive, enableInternalScroll, headerRight, t, buttonStyling, borderStyling, markets, onStartInquiry, onSelectDealer, }: DealerListProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -6,8 +6,10 @@ import { TelephoneIcon } from '../../icons/TelephoneIcon';
|
|
|
6
6
|
import { getWidgetPrimaryButtonPresentation } from '../../utils/widgetPrimaryButtonPresentation';
|
|
7
7
|
import exclusiveDealerBadge from '../../images/jotul-exclusive-dealer.png';
|
|
8
8
|
import ildstedetDealer from '../../images/ildstedet-dealer.svg';
|
|
9
|
-
import { asText, formatDistance, getDealerAddressLines, getDealerKey, getDealerName, isExclusiveDealer, } from '../../utils';
|
|
9
|
+
import { asText, formatDistance, getDealerAddressLines, getDealerId, getDealerKey, getDealerName, isExclusiveDealer, } from '../../utils';
|
|
10
|
+
import { useWidgetTracking } from '../../analytics/WidgetTrackingContext';
|
|
10
11
|
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, markets, onStartInquiry, onSelectDealer, }) {
|
|
12
|
+
const tracking = useWidgetTracking();
|
|
11
13
|
const cardRefs = useRef({});
|
|
12
14
|
useEffect(() => {
|
|
13
15
|
if (!autoScrollToActive || !activeDealerName)
|
|
@@ -33,18 +35,24 @@ export function DealerList({ dealers, total, selectedDealerName, activeDealerNam
|
|
|
33
35
|
const isExclusive = isExclusiveDealer(dealer);
|
|
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]' : ''}`, style: isActiveDealer && activeBorderColor
|
|
38
|
+
}, className: `jwi-w-full jwi-cursor-pointer ${R10} jwi-border jwi-border-[#e6e1d7] jwi-bg-white jwi-p-4 jwi-shadow-[0_1px_2px_rgba(17,17,17,0.03)] ${isActiveDealer ? 'jwi-border-[#ef2b18]' : ''}`, style: isActiveDealer && activeBorderColor
|
|
37
39
|
? { borderColor: activeBorderColor }
|
|
38
|
-
: undefined, onClick: () =>
|
|
40
|
+
: undefined, onClick: () => {
|
|
41
|
+
onSelectDealer?.(dealerName);
|
|
42
|
+
tracking?.trackDealerSelect(getDealerId(dealer), dealerName);
|
|
43
|
+
}, 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 && (() => {
|
|
39
44
|
const isNorway = markets?.includes('NO') ?? false;
|
|
40
45
|
const badgeImage = isNorway ? ildstedetDealer : exclusiveDealerBadge;
|
|
41
|
-
const badgeAlt = isNorway ?
|
|
46
|
+
const badgeAlt = isNorway ? t.ildstedetDealerBadgeAlt : t.exclusiveDealerBadgeAlt;
|
|
42
47
|
return (_jsx("img", { src: typeof badgeImage === 'string'
|
|
43
48
|
? badgeImage
|
|
44
49
|
: badgeImage.src, alt: badgeAlt, className: "jwi-h-auto jwi-w-[40px] jwi-shrink-0 jwi-rounded-full jwi-m-0" }));
|
|
45
50
|
})(), _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) => {
|
|
46
51
|
event.stopPropagation();
|
|
47
|
-
onStartInquiry(
|
|
48
|
-
}, 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) =>
|
|
52
|
+
onStartInquiry(dealer);
|
|
53
|
+
}, 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) => {
|
|
54
|
+
event.stopPropagation();
|
|
55
|
+
tracking?.trackDealerPhoneClick(getDealerId(dealer), dealerName);
|
|
56
|
+
}, 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)));
|
|
49
57
|
}) }), _jsx("div", { "aria-hidden": true, className: "jwi-pointer-events-none jwi-absolute jwi-bottom-0 jwi-left-0 jwi-right-0 jwi-h-10 jwi-bg-gradient-to-t jwi-from-white jwi-to-transparent" })] })] }));
|
|
50
58
|
}
|
|
@@ -5,10 +5,14 @@ type InquiryFormProps = {
|
|
|
5
5
|
buttonStyling?: JotulWidgetButtonStyling;
|
|
6
6
|
inquiryValues: InquiryFormValues;
|
|
7
7
|
inquiryError: string | null;
|
|
8
|
+
isSubmitting?: boolean;
|
|
9
|
+
turnstileSiteKey?: string;
|
|
10
|
+
turnstileResetKey?: number;
|
|
8
11
|
embedded?: boolean;
|
|
12
|
+
showBackButton?: boolean;
|
|
9
13
|
onInquiryClose: () => void;
|
|
10
|
-
onInquirySubmit: () => void;
|
|
14
|
+
onInquirySubmit: (turnstileToken: string | null) => void;
|
|
11
15
|
onInquiryFieldChange: (key: keyof InquiryFormValues, value: string) => void;
|
|
12
16
|
};
|
|
13
|
-
export declare function InquiryForm({ t, buttonStyling, inquiryValues, inquiryError, embedded, onInquiryClose, onInquirySubmit, onInquiryFieldChange, }: InquiryFormProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare function InquiryForm({ t, buttonStyling, inquiryValues, inquiryError, isSubmitting, turnstileSiteKey, turnstileResetKey, embedded, showBackButton, onInquiryClose, onInquirySubmit, onInquiryFieldChange, }: InquiryFormProps): import("react/jsx-runtime").JSX.Element;
|
|
14
18
|
export {};
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useState } from 'react';
|
|
2
3
|
import { InquiryField } from '../InquiryField';
|
|
4
|
+
import { InquirySelectField } from '../InquirySelectField';
|
|
5
|
+
import { TurnstileField } from '../TurnstileField';
|
|
3
6
|
import { R10 } from '../../constants';
|
|
4
7
|
import { ArrowRightIcon } from '../../icons/ArrowRightIcon';
|
|
5
8
|
import { getWidgetPrimaryButtonPresentation } from '../../utils/widgetPrimaryButtonPresentation';
|
|
6
|
-
|
|
9
|
+
import { inquiryCategoryOptions } from '../../utils/inquiryCategories';
|
|
10
|
+
export function InquiryForm({ t, buttonStyling, inquiryValues, inquiryError, isSubmitting = false, turnstileSiteKey, turnstileResetKey = 0, embedded = false, showBackButton = true, onInquiryClose, onInquirySubmit, onInquiryFieldChange, }) {
|
|
11
|
+
const [turnstileToken, setTurnstileToken] = useState(null);
|
|
12
|
+
const [captchaError, setCaptchaError] = useState(null);
|
|
13
|
+
const handleTurnstileTokenChange = useCallback((token) => {
|
|
14
|
+
setTurnstileToken(token);
|
|
15
|
+
if (token)
|
|
16
|
+
setCaptchaError(null);
|
|
17
|
+
}, []);
|
|
7
18
|
const submitButton = getWidgetPrimaryButtonPresentation(buttonStyling, 'panel', {
|
|
8
19
|
extraClassName: 'jwi-mt-4 jwi-w-full',
|
|
9
20
|
});
|
|
@@ -11,10 +22,17 @@ export function InquiryForm({ t, buttonStyling, inquiryValues, inquiryError, emb
|
|
|
11
22
|
const title = trimmedProductName !== ''
|
|
12
23
|
? t.sendInquiryTitleWithProduct.replace('{product}', trimmedProductName)
|
|
13
24
|
: t.sendInquiryTitle;
|
|
25
|
+
const displayError = inquiryError ?? captchaError;
|
|
26
|
+
const turnstileEnabled = Boolean(turnstileSiteKey?.trim());
|
|
14
27
|
return (_jsxs("form", { onSubmit: (event) => {
|
|
15
28
|
event.preventDefault();
|
|
16
|
-
|
|
29
|
+
if (turnstileEnabled && !turnstileToken) {
|
|
30
|
+
setCaptchaError(t.formValidationCaptcha);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
setCaptchaError(null);
|
|
34
|
+
onInquirySubmit(turnstileToken);
|
|
17
35
|
}, className: `jwi-w-full ${R10} jwi-bg-white jwi-p-4 ${embedded
|
|
18
36
|
? ''
|
|
19
|
-
: 'jwi-border jwi-border-[#e6e1d7] jwi-shadow-[0_1px_2px_rgba(17,17,17,0.03)]'}`, children: [_jsxs("button", { type: "button", onClick: onInquiryClose, className: "jwi-mb-3 jwi-inline-flex jwi-w-fit jwi-cursor-pointer jwi-items-center jwi-gap-1.5 jwi-border-0 jwi-bg-transparent jwi-p-0 jwi-text-left jwi-text-sm jwi-font-medium jwi-text-[#767676] hover:jwi-text-[#444444]", children: [_jsx("span", { className: "jwi-inline-flex jwi-shrink-0", style: { transform: 'scaleX(-1)' }, "aria-hidden": true, children: _jsx(ArrowRightIcon, { className: "jwi-h-[14px] jwi-w-[14px]" }) }), t.goBack] }), _jsx("input", { type: "hidden", name: "product", value: inquiryValues.productName }), _jsx("input", { type: "hidden", name: "dealer", value: inquiryValues.dealerName }), _jsxs("div", { className: "jwi-min-w-0", children: [_jsx("h3", { className: "jwi-m-0 jwi-text-base jwi-font-semibold jwi-leading-snug jwi-text-[#111111]", children: title }), _jsx("p", { className: "jwi-m-0 jwi-mt-2 jwi-text-sm jwi-leading-[1.4] jwi-text-[#767676]", children: t.dealerWillContact.replace('{dealer}', inquiryValues.dealerName) })] }), _jsxs("div", { className: "jwi-mt-4 jwi-flex jwi-flex-col jwi-gap-3", children: [_jsx(InquiryField, { label: t.fieldName, value: inquiryValues.name, onChange: (value) => onInquiryFieldChange('name', value) }), _jsx(InquiryField, { label: t.fieldEmail, type: "email", value: inquiryValues.email, onChange: (value) => onInquiryFieldChange('email', value) }), _jsx(InquiryField, { label: t.fieldPhone, type: "tel", value: inquiryValues.phone, onChange: (value) => onInquiryFieldChange('phone', value) }), _jsx(InquiryField, { label: t.fieldComment, multiline: true, value: inquiryValues.comment, onChange: (value) => onInquiryFieldChange('comment', value) })] }),
|
|
37
|
+
: 'jwi-border jwi-border-[#e6e1d7] jwi-shadow-[0_1px_2px_rgba(17,17,17,0.03)]'}`, children: [showBackButton ? (_jsxs("button", { type: "button", onClick: onInquiryClose, className: "jwi-mb-3 jwi-inline-flex jwi-w-fit jwi-cursor-pointer jwi-items-center jwi-gap-1.5 jwi-border-0 jwi-bg-transparent jwi-p-0 jwi-text-left jwi-text-sm jwi-font-medium jwi-text-[#767676] hover:jwi-text-[#444444]", children: [_jsx("span", { className: "jwi-inline-flex jwi-shrink-0", style: { transform: 'scaleX(-1)' }, "aria-hidden": true, children: _jsx(ArrowRightIcon, { className: "jwi-h-[14px] jwi-w-[14px]" }) }), t.goBack] })) : null, _jsx("input", { type: "hidden", name: "product", value: inquiryValues.productName }), _jsx("input", { type: "hidden", name: "dealer", value: inquiryValues.dealerName }), _jsxs("div", { className: "jwi-min-w-0", children: [_jsx("h3", { className: "jwi-m-0 jwi-text-base jwi-font-semibold jwi-leading-snug jwi-text-[#111111]", children: title }), _jsx("p", { className: "jwi-m-0 jwi-mt-2 jwi-text-sm jwi-leading-[1.4] jwi-text-[#767676]", children: t.dealerWillContact.replace('{dealer}', inquiryValues.dealerName) })] }), _jsxs("div", { className: "jwi-mt-4 jwi-flex jwi-flex-col jwi-gap-3", children: [_jsx(InquirySelectField, { label: t.fieldRequestCategory, value: inquiryValues.requestCategory, options: inquiryCategoryOptions(t), onChange: (value) => onInquiryFieldChange('requestCategory', value) }), _jsx(InquiryField, { label: t.fieldName, fieldName: "name", value: inquiryValues.name, onChange: (value) => onInquiryFieldChange('name', value) }), _jsx(InquiryField, { label: t.fieldEmail, fieldName: "email", type: "email", value: inquiryValues.email, onChange: (value) => onInquiryFieldChange('email', value) }), _jsx(InquiryField, { label: t.fieldPhone, fieldName: "phone", type: "tel", value: inquiryValues.phone, onChange: (value) => onInquiryFieldChange('phone', value) }), _jsx(InquiryField, { label: t.fieldComment, fieldName: "comment", multiline: true, value: inquiryValues.comment, onChange: (value) => onInquiryFieldChange('comment', value) })] }), turnstileEnabled && turnstileSiteKey ? (_jsx(TurnstileField, { siteKey: turnstileSiteKey, resetKey: turnstileResetKey, onTokenChange: handleTurnstileTokenChange })) : null, displayError && (_jsx("div", { className: `jwi-mt-4 ${R10} jwi-border jwi-border-[#f0c7c2] jwi-bg-[#fff3f1] jwi-px-4 jwi-py-3 jwi-text-sm jwi-leading-[1.4] jwi-text-[#8f2d21]`, children: displayError })), _jsx("button", { type: "submit", className: submitButton.className, style: submitButton.style, disabled: isSubmitting, children: isSubmitting ? t.warrantySubmitting : t.sendInquiryCta })] }));
|
|
20
38
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Public Turnstile site-key endpoint on api.jotul.com (no auth; site keys are public). */
|
|
2
|
+
export declare const JOTUL_TURNSTILE_CONFIG_URL = "https://api.jotul.com/api/jotul/turnstile-config";
|
|
3
|
+
/**
|
|
4
|
+
* Where the widget loads the Turnstile site key from.
|
|
5
|
+
* Partners do not configure this — only Jotul sets keys on api.jotul.com.
|
|
6
|
+
* Uses same-origin config on localhost so local docs/dev work without CORS.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveTurnstileConfigEndpoint(override?: string): string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Public Turnstile site-key endpoint on api.jotul.com (no auth; site keys are public). */
|
|
2
|
+
export const JOTUL_TURNSTILE_CONFIG_URL = 'https://api.jotul.com/api/jotul/turnstile-config';
|
|
3
|
+
/**
|
|
4
|
+
* Where the widget loads the Turnstile site key from.
|
|
5
|
+
* Partners do not configure this — only Jotul sets keys on api.jotul.com.
|
|
6
|
+
* Uses same-origin config on localhost so local docs/dev work without CORS.
|
|
7
|
+
*/
|
|
8
|
+
export function resolveTurnstileConfigEndpoint(override) {
|
|
9
|
+
const trimmed = override?.trim();
|
|
10
|
+
if (trimmed)
|
|
11
|
+
return trimmed;
|
|
12
|
+
if (typeof window !== 'undefined') {
|
|
13
|
+
const { hostname, protocol, host } = window.location;
|
|
14
|
+
if (hostname === 'localhost' || hostname === '127.0.0.1') {
|
|
15
|
+
return `${protocol}//${host}/api/jotul/turnstile-config`;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return JOTUL_TURNSTILE_CONFIG_URL;
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useTurnstileSiteKey(explicitSiteKey?: string, configEndpoint?: string): string | undefined;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { resolveTurnstileConfigEndpoint } from '../constants/turnstile';
|
|
3
|
+
export function useTurnstileSiteKey(explicitSiteKey, configEndpoint) {
|
|
4
|
+
const trimmedExplicit = explicitSiteKey?.trim();
|
|
5
|
+
const resolvedEndpoint = resolveTurnstileConfigEndpoint(configEndpoint);
|
|
6
|
+
const [fetchedSiteKey, setFetchedSiteKey] = useState();
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (trimmedExplicit) {
|
|
9
|
+
setFetchedSiteKey(undefined);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
let cancelled = false;
|
|
13
|
+
void fetch(resolvedEndpoint, {
|
|
14
|
+
method: 'GET',
|
|
15
|
+
headers: { Accept: 'application/json' },
|
|
16
|
+
cache: 'no-store',
|
|
17
|
+
})
|
|
18
|
+
.then(async (response) => {
|
|
19
|
+
if (!response.ok)
|
|
20
|
+
return null;
|
|
21
|
+
return (await response.json());
|
|
22
|
+
})
|
|
23
|
+
.then((data) => {
|
|
24
|
+
if (cancelled)
|
|
25
|
+
return;
|
|
26
|
+
const siteKey = data?.siteKey?.trim();
|
|
27
|
+
setFetchedSiteKey(siteKey || undefined);
|
|
28
|
+
})
|
|
29
|
+
.catch(() => {
|
|
30
|
+
if (!cancelled)
|
|
31
|
+
setFetchedSiteKey(undefined);
|
|
32
|
+
});
|
|
33
|
+
return () => {
|
|
34
|
+
cancelled = true;
|
|
35
|
+
};
|
|
36
|
+
}, [trimmedExplicit, resolvedEndpoint]);
|
|
37
|
+
return trimmedExplicit || fetchedSiteKey;
|
|
38
|
+
}
|
|
@@ -26,12 +26,45 @@
|
|
|
26
26
|
"fieldEmail": "E-mail",
|
|
27
27
|
"fieldPhone": "Telefon",
|
|
28
28
|
"fieldComment": "Komentář",
|
|
29
|
-
"
|
|
29
|
+
"dealerNotFound": "Prodejce nebyl nalezen.",
|
|
30
|
+
"fieldRequestCategory": "Kategorie poptávky",
|
|
31
|
+
"requestCategoryPriceQuote": "Cenová nabídka",
|
|
32
|
+
"requestCategoryProductHelp": "Pomoc s produktem / dotazy",
|
|
33
|
+
"requestCategoryService": "Servis",
|
|
34
|
+
"requestCategoryOther": "Jiné",
|
|
35
|
+
"warrantyCustomerSectionTitle": "Informace o zákazníkovi",
|
|
36
|
+
"warrantyProductSectionTitle": "Informace o produktu",
|
|
37
|
+
"fieldFirstName": "Jméno",
|
|
38
|
+
"fieldLastName": "Příjmení",
|
|
39
|
+
"fieldAddress": "Adresa",
|
|
40
|
+
"fieldCity": "Město",
|
|
41
|
+
"fieldZipcode": "PSČ",
|
|
42
|
+
"fieldCounty": "Okres",
|
|
43
|
+
"fieldProductName": "Název produktu",
|
|
44
|
+
"fieldProductNameHint": "Například Jøtul F 602",
|
|
45
|
+
"fieldPurchaseDate": "Datum nákupu",
|
|
46
|
+
"fieldPurchasedFromDealer": "Zakoupeno u prodejce",
|
|
47
|
+
"fieldSelfInstalled": "Instalovali jste produkt sami?",
|
|
48
|
+
"fieldSelfInstalledYes": "Ano",
|
|
49
|
+
"fieldSelfInstalledNo": "Ne",
|
|
50
|
+
"fieldInstallerName": "Pokud ne, kdo produkt instaloval?",
|
|
51
|
+
"fieldConsent": "Souhlasím s odesláním mých údajů společnosti Jøtul pro informační a obchodní účely. Mám právo na nahlížení a výmaz svých údajů",
|
|
52
|
+
"warrantySubmitCta": "Odeslat",
|
|
53
|
+
"warrantySubmitting": "Odesílání …",
|
|
54
|
+
"warrantySuccessTitle": "Děkujeme za registraci",
|
|
55
|
+
"warrantySuccessMessage": "Registrace záruky byla odeslána. Zkontrolujte e-mail pro potvrzení.",
|
|
56
|
+
"formValidationConsent": "Pro pokračování musíte udělit souhlas.",
|
|
57
|
+
"formValidationInstaller": "Uveďte, kdo produkt instaloval.",
|
|
58
|
+
"formValidationCaptcha": "Dokončete bezpečnostní kontrolu.",
|
|
59
|
+
"formValidationRequired": "Vyplňte všechna povinná pole.",
|
|
30
60
|
"formValidationEmail": "Zadejte platnou e-mailovou adresu.",
|
|
31
61
|
"dealersNearYou": "{count} prodejců v okolí",
|
|
32
62
|
"unknownDealer": "Neznámý prodejce",
|
|
33
63
|
"sendInquiryCta": "Odeslat poptávku",
|
|
34
64
|
"sendInquiryEditing": "Úprava poptávky",
|
|
65
|
+
"showMore": "Zobrazit další",
|
|
66
|
+
"exclusiveDealerBadgeAlt": "Jotul exclusive prodejce",
|
|
67
|
+
"ildstedetDealerBadgeAlt": "Ildstedet prodejce",
|
|
35
68
|
"readyDealerFinder": "JotulWidget připraven: dealerFinder",
|
|
36
69
|
"readyWarrantyForm": "JotulWidget připraven: warrantyForm",
|
|
37
70
|
"listView": "Seznam",
|
|
@@ -26,12 +26,45 @@
|
|
|
26
26
|
"fieldEmail": "E-Mail",
|
|
27
27
|
"fieldPhone": "Telefon",
|
|
28
28
|
"fieldComment": "Kommentar",
|
|
29
|
-
"
|
|
29
|
+
"dealerNotFound": "Händler nicht gefunden.",
|
|
30
|
+
"fieldRequestCategory": "Anfragekategorie",
|
|
31
|
+
"requestCategoryPriceQuote": "Preisangebot",
|
|
32
|
+
"requestCategoryProductHelp": "Produkthilfe / Fragen",
|
|
33
|
+
"requestCategoryService": "Service",
|
|
34
|
+
"requestCategoryOther": "Sonstiges",
|
|
35
|
+
"warrantyCustomerSectionTitle": "Kundeninformation",
|
|
36
|
+
"warrantyProductSectionTitle": "Produktinformation",
|
|
37
|
+
"fieldFirstName": "Vorname",
|
|
38
|
+
"fieldLastName": "Nachname",
|
|
39
|
+
"fieldAddress": "Adresse",
|
|
40
|
+
"fieldCity": "Stadt",
|
|
41
|
+
"fieldZipcode": "Postleitzahl",
|
|
42
|
+
"fieldCounty": "Bundesland",
|
|
43
|
+
"fieldProductName": "Produktname",
|
|
44
|
+
"fieldProductNameHint": "Zum Beispiel Jøtul F 602",
|
|
45
|
+
"fieldPurchaseDate": "Kaufdatum",
|
|
46
|
+
"fieldPurchasedFromDealer": "Gekauft beim Händler",
|
|
47
|
+
"fieldSelfInstalled": "Haben Sie das Produkt selbst installiert?",
|
|
48
|
+
"fieldSelfInstalledYes": "Ja",
|
|
49
|
+
"fieldSelfInstalledNo": "Nein",
|
|
50
|
+
"fieldInstallerName": "Wenn nein, wer hat das Produkt installiert?",
|
|
51
|
+
"fieldConsent": "Ich stimme zu, dass meine Informationen zu Informations- und Werbezwecken an Jøtul gesendet werden. Ich habe das Recht, meine Informationen einzusehen und zu löschen",
|
|
52
|
+
"warrantySubmitCta": "Senden",
|
|
53
|
+
"warrantySubmitting": "Wird gesendet …",
|
|
54
|
+
"warrantySuccessTitle": "Vielen Dank für Ihre Registrierung",
|
|
55
|
+
"warrantySuccessMessage": "Ihre Garantieregistrierung wurde gesendet. Prüfen Sie Ihre E-Mail zur Bestätigung.",
|
|
56
|
+
"formValidationConsent": "Sie müssen zustimmen, um fortzufahren.",
|
|
57
|
+
"formValidationInstaller": "Geben Sie an, wer das Produkt installiert hat.",
|
|
58
|
+
"formValidationCaptcha": "Bitte schließen Sie die Sicherheitsprüfung ab.",
|
|
59
|
+
"formValidationRequired": "Bitte füllen Sie alle Pflichtfelder aus.",
|
|
30
60
|
"formValidationEmail": "Bitte geben Sie eine gültige E-Mail-Adresse ein.",
|
|
31
61
|
"dealersNearYou": "{count} Händler in Ihrer Nähe",
|
|
32
62
|
"unknownDealer": "Unbekannter Händler",
|
|
33
63
|
"sendInquiryCta": "Anfrage senden",
|
|
34
64
|
"sendInquiryEditing": "Anfrage bearbeiten",
|
|
65
|
+
"showMore": "Mehr anzeigen",
|
|
66
|
+
"exclusiveDealerBadgeAlt": "Jotul Exclusive-Händler",
|
|
67
|
+
"ildstedetDealerBadgeAlt": "Ildstedet-Händler",
|
|
35
68
|
"readyDealerFinder": "JotulWidget bereit: dealerFinder",
|
|
36
69
|
"readyWarrantyForm": "JotulWidget bereit: warrantyForm",
|
|
37
70
|
"listView": "Liste",
|