@bytebrand/fe-ui-core-autobahn 1.0.44 → 1.0.45
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/package.json +1 -1
- package/theme/md3.css +12 -8
- package/vdp/FinanceCalculator.tsx +622 -599
- package/vdp/VdpModules.tsx +42 -16
package/vdp/VdpModules.tsx
CHANGED
|
@@ -27,10 +27,29 @@ import { TranslationFunction } from '../types';
|
|
|
27
27
|
import { Button, Icon, Spec, StatChip, Stars, Switch } from '../ui';
|
|
28
28
|
import { CO2_CLASSES, CO2_COLORS, TIER_LABELS, TIER_KEYS, TIER_COLORS } from '../constants';
|
|
29
29
|
import { defaultVdpSpecs, EquipmentGroups, PriceRatingBands, VehicleDetail, VdpSpec, VdpReview } from './types';
|
|
30
|
-
import { CalcRow, EvalBar, expanderBtnStyle, FinanceMode, Slider, TermChips } from './FinanceCalculator';
|
|
30
|
+
import { CalcRow, DocCheckIcon, EvalBar, expanderBtnStyle, FinanceMode, Slider, TermChips } from './FinanceCalculator';
|
|
31
31
|
|
|
32
32
|
const eur = (n: number): string => n.toLocaleString('de-DE');
|
|
33
33
|
|
|
34
|
+
/** Figma "check with open arc" glyph (modern lucide circle-check-big; the bundled lucide
|
|
35
|
+
* 0.288 only ships the closed-circle variant, so the two paths are inlined). */
|
|
36
|
+
const ArcCheckIcon: React.FunctionComponent<{ size?: number; color?: string }> = ({ size = 20, color = 'currentColor' }) => (
|
|
37
|
+
<svg
|
|
38
|
+
width={size}
|
|
39
|
+
height={size}
|
|
40
|
+
viewBox="0 0 24 24"
|
|
41
|
+
fill="none"
|
|
42
|
+
stroke={color}
|
|
43
|
+
strokeWidth={2.4}
|
|
44
|
+
strokeLinecap="round"
|
|
45
|
+
strokeLinejoin="round"
|
|
46
|
+
style={{ flex: 'none' }}
|
|
47
|
+
>
|
|
48
|
+
<path d="M21.801 10A10 10 0 1 1 17 3.335" />
|
|
49
|
+
<path d="m9 11 3 3L22 4" />
|
|
50
|
+
</svg>
|
|
51
|
+
);
|
|
52
|
+
|
|
34
53
|
/** mileage → 'X.XXX km' (de-DE grouping). */
|
|
35
54
|
function fmtKm(n: number): string {
|
|
36
55
|
return `${n.toLocaleString('de-DE')} km`;
|
|
@@ -658,7 +677,7 @@ export const PriceHistory: React.FunctionComponent<PriceHistoryProps> = ({ vehic
|
|
|
658
677
|
background: 'var(--md-good-container)',
|
|
659
678
|
}}
|
|
660
679
|
>
|
|
661
|
-
<
|
|
680
|
+
<ArcCheckIcon size={20} color="var(--md-good)" />
|
|
662
681
|
<span style={{ fontSize: 14, fontWeight: 700, color: 'var(--md-good)' }}>
|
|
663
682
|
{t('autobahn:marketValue.belowAverage', { rating: t('autobahn:tier.' + vehicle.rating), amount: eur(delta) })}
|
|
664
683
|
</span>
|
|
@@ -1250,7 +1269,8 @@ export const VdpBottomBar: React.FunctionComponent<VdpBottomBarProps> = ({
|
|
|
1250
1269
|
)}
|
|
1251
1270
|
</div>
|
|
1252
1271
|
<div className="bb-cta-row">
|
|
1253
|
-
<Button variant="filled"
|
|
1272
|
+
<Button variant="filled" style={{ flex: 1, minWidth: 0 }} onClick={onSecure}>
|
|
1273
|
+
<DocCheckIcon />
|
|
1254
1274
|
{t('autobahn:bottom.secureOffer')}
|
|
1255
1275
|
</Button>
|
|
1256
1276
|
{/* padding:0 — icon-only button (same fix as the sheet's phone button) */}
|
|
@@ -1679,14 +1699,16 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
|
|
|
1679
1699
|
|
|
1680
1700
|
<div className="fin-price-grid">
|
|
1681
1701
|
<div className="fin-rate-main">
|
|
1682
|
-
{/* Figma Kaufen sheet: struck list price on its own line ABOVE the big figure */}
|
|
1683
|
-
{isCash && listPrice != null && listPrice > price && (
|
|
1684
|
-
<s className="num fin-rate-old" style={{ display: 'block' }}>{eur(listPrice)} €</s>
|
|
1685
|
-
)}
|
|
1686
1702
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
1687
1703
|
<span className="num" style={{ fontSize: 34, fontWeight: 800, color: 'var(--md-primary)', letterSpacing: '-1px' }}>
|
|
1688
1704
|
{isCash ? eur(price) : eur(Math.round(monthly))} €
|
|
1689
1705
|
</span>
|
|
1706
|
+
{/* Figma Kaufen sheet: struck list price top-right beside the big figure */}
|
|
1707
|
+
{isCash && listPrice != null && listPrice > price && (
|
|
1708
|
+
<span className="fin-rate-unit" style={{ alignSelf: 'flex-start', paddingTop: 2 }}>
|
|
1709
|
+
<s className="num fin-rate-old">{eur(listPrice)} €</s>
|
|
1710
|
+
</span>
|
|
1711
|
+
)}
|
|
1690
1712
|
{!isCash && (
|
|
1691
1713
|
<>
|
|
1692
1714
|
<span className="md-label-sm" style={{ fontWeight: 600, alignSelf: 'flex-end', paddingBottom: 5 }}>
|
|
@@ -1733,7 +1755,8 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
|
|
|
1733
1755
|
/>
|
|
1734
1756
|
)}
|
|
1735
1757
|
<div style={{ display: 'flex', gap: 10 }}>
|
|
1736
|
-
<Button variant="filled" size="lg"
|
|
1758
|
+
<Button variant="filled" size="lg" style={{ flex: 1 }} onClick={onSecure}>
|
|
1759
|
+
<DocCheckIcon />
|
|
1737
1760
|
{t('autobahn:finance.secureOffer')}
|
|
1738
1761
|
</Button>
|
|
1739
1762
|
{/* padding:0 — mbtn--lg's 30px side padding left no room for the icon at width 52 */}
|
|
@@ -1814,7 +1837,8 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
|
|
|
1814
1837
|
)}
|
|
1815
1838
|
|
|
1816
1839
|
<div style={{ display: 'flex', gap: 10 }}>
|
|
1817
|
-
<Button variant="filled" size="lg"
|
|
1840
|
+
<Button variant="filled" size="lg" style={{ flex: 1 }} onClick={onSecure}>
|
|
1841
|
+
<DocCheckIcon />
|
|
1818
1842
|
{t('autobahn:finance.requestRate')}
|
|
1819
1843
|
</Button>
|
|
1820
1844
|
{/* padding:0 — mbtn--lg's 30px side padding left no room for the icon at width 52 */}
|
|
@@ -2091,13 +2115,14 @@ export const VdpEnquiry: React.FunctionComponent<VdpEnquiryProps> = ({
|
|
|
2091
2115
|
const emailOk = /.+@.+\..+/.test(email);
|
|
2092
2116
|
const phoneDigits = (areaCode + phoneNum).replace(/\D/g, '');
|
|
2093
2117
|
const phoneOk = phoneDigits.length >= 6;
|
|
2094
|
-
//
|
|
2095
|
-
//
|
|
2118
|
+
// Round 13 (explicit spec): PHONE is always required (the * sits on the country-code field),
|
|
2119
|
+
// e-mail is always optional (validated only when filled); names + consent always required.
|
|
2096
2120
|
const valid =
|
|
2097
2121
|
firstName.trim().length > 0 &&
|
|
2098
2122
|
lastName.trim().length > 0 &&
|
|
2099
2123
|
consent &&
|
|
2100
|
-
|
|
2124
|
+
phoneOk &&
|
|
2125
|
+
(email.trim().length === 0 || emailOk);
|
|
2101
2126
|
|
|
2102
2127
|
const submit = async (): Promise<void> => {
|
|
2103
2128
|
if (!valid || sending) return;
|
|
@@ -2213,8 +2238,8 @@ export const VdpEnquiry: React.FunctionComponent<VdpEnquiryProps> = ({
|
|
|
2213
2238
|
<input
|
|
2214
2239
|
style={enquiryInput}
|
|
2215
2240
|
type="email"
|
|
2216
|
-
//
|
|
2217
|
-
placeholder={
|
|
2241
|
+
// e-mail is always optional (round 13) — no required-*
|
|
2242
|
+
placeholder={t('autobahn:enquiry.form.emailPlaceholder').replace(/\*$/, '')}
|
|
2218
2243
|
value={email}
|
|
2219
2244
|
onChange={(e) => setEmail(e.target.value)}
|
|
2220
2245
|
aria-label={t('autobahn:enquiry.contactMethod.email')}
|
|
@@ -2222,7 +2247,8 @@ export const VdpEnquiry: React.FunctionComponent<VdpEnquiryProps> = ({
|
|
|
2222
2247
|
</div>
|
|
2223
2248
|
{/* Land (+49 fixed) · Vorwahl · Telefonnummer — outlined float-label row (Figma) */}
|
|
2224
2249
|
<div className="enq-phone-row" style={{ marginTop: 16 }}>
|
|
2225
|
-
|
|
2250
|
+
{/* phone is always required — the * sits on the country-code field (round 13) */}
|
|
2251
|
+
<FloatField label={t('autobahn:enquiry.form.countryLabel') + '*'}>
|
|
2226
2252
|
<span className="enq-country num">
|
|
2227
2253
|
+49
|
|
2228
2254
|
<Icon name="chevron-down" size={14} color="var(--md-on-surface-variant)" />
|
|
@@ -2236,7 +2262,7 @@ export const VdpEnquiry: React.FunctionComponent<VdpEnquiryProps> = ({
|
|
|
2236
2262
|
aria-label={t('autobahn:enquiry.form.areaCodeLabel')}
|
|
2237
2263
|
/>
|
|
2238
2264
|
</FloatField>
|
|
2239
|
-
<FloatField label={t('autobahn:enquiry.form.phoneNumberLabel')
|
|
2265
|
+
<FloatField label={t('autobahn:enquiry.form.phoneNumberLabel')}>
|
|
2240
2266
|
<input
|
|
2241
2267
|
type="tel"
|
|
2242
2268
|
value={phoneNum}
|