@bytebrand/fe-ui-core-autobahn 1.0.44 → 1.0.46
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 +623 -599
- package/vdp/VdpModules.tsx +41 -14
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,9 +1699,12 @@ 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
|
|
1702
|
+
{/* Figma Kaufen sheet: struck list price ABOVE the price, aligned to its right
|
|
1703
|
+
edge (inline-beside wraps — the 44px phone hero fills the whole column) */}
|
|
1683
1704
|
{isCash && listPrice != null && listPrice > price && (
|
|
1684
|
-
<
|
|
1705
|
+
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
|
1706
|
+
<s className="num fin-rate-old">{eur(listPrice)} €</s>
|
|
1707
|
+
</div>
|
|
1685
1708
|
)}
|
|
1686
1709
|
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
|
1687
1710
|
<span className="num" style={{ fontSize: 34, fontWeight: 800, color: 'var(--md-primary)', letterSpacing: '-1px' }}>
|
|
@@ -1733,7 +1756,8 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
|
|
|
1733
1756
|
/>
|
|
1734
1757
|
)}
|
|
1735
1758
|
<div style={{ display: 'flex', gap: 10 }}>
|
|
1736
|
-
<Button variant="filled" size="lg"
|
|
1759
|
+
<Button variant="filled" size="lg" style={{ flex: 1 }} onClick={onSecure}>
|
|
1760
|
+
<DocCheckIcon />
|
|
1737
1761
|
{t('autobahn:finance.secureOffer')}
|
|
1738
1762
|
</Button>
|
|
1739
1763
|
{/* padding:0 — mbtn--lg's 30px side padding left no room for the icon at width 52 */}
|
|
@@ -1814,7 +1838,8 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
|
|
|
1814
1838
|
)}
|
|
1815
1839
|
|
|
1816
1840
|
<div style={{ display: 'flex', gap: 10 }}>
|
|
1817
|
-
<Button variant="filled" size="lg"
|
|
1841
|
+
<Button variant="filled" size="lg" style={{ flex: 1 }} onClick={onSecure}>
|
|
1842
|
+
<DocCheckIcon />
|
|
1818
1843
|
{t('autobahn:finance.requestRate')}
|
|
1819
1844
|
</Button>
|
|
1820
1845
|
{/* padding:0 — mbtn--lg's 30px side padding left no room for the icon at width 52 */}
|
|
@@ -2091,13 +2116,14 @@ export const VdpEnquiry: React.FunctionComponent<VdpEnquiryProps> = ({
|
|
|
2091
2116
|
const emailOk = /.+@.+\..+/.test(email);
|
|
2092
2117
|
const phoneDigits = (areaCode + phoneNum).replace(/\D/g, '');
|
|
2093
2118
|
const phoneOk = phoneDigits.length >= 6;
|
|
2094
|
-
//
|
|
2095
|
-
//
|
|
2119
|
+
// Round 13 (explicit spec): PHONE is always required (the * sits on the country-code field),
|
|
2120
|
+
// e-mail is always optional (validated only when filled); names + consent always required.
|
|
2096
2121
|
const valid =
|
|
2097
2122
|
firstName.trim().length > 0 &&
|
|
2098
2123
|
lastName.trim().length > 0 &&
|
|
2099
2124
|
consent &&
|
|
2100
|
-
|
|
2125
|
+
phoneOk &&
|
|
2126
|
+
(email.trim().length === 0 || emailOk);
|
|
2101
2127
|
|
|
2102
2128
|
const submit = async (): Promise<void> => {
|
|
2103
2129
|
if (!valid || sending) return;
|
|
@@ -2213,8 +2239,8 @@ export const VdpEnquiry: React.FunctionComponent<VdpEnquiryProps> = ({
|
|
|
2213
2239
|
<input
|
|
2214
2240
|
style={enquiryInput}
|
|
2215
2241
|
type="email"
|
|
2216
|
-
//
|
|
2217
|
-
placeholder={
|
|
2242
|
+
// e-mail is always optional (round 13) — no required-*
|
|
2243
|
+
placeholder={t('autobahn:enquiry.form.emailPlaceholder').replace(/\*$/, '')}
|
|
2218
2244
|
value={email}
|
|
2219
2245
|
onChange={(e) => setEmail(e.target.value)}
|
|
2220
2246
|
aria-label={t('autobahn:enquiry.contactMethod.email')}
|
|
@@ -2222,7 +2248,8 @@ export const VdpEnquiry: React.FunctionComponent<VdpEnquiryProps> = ({
|
|
|
2222
2248
|
</div>
|
|
2223
2249
|
{/* Land (+49 fixed) · Vorwahl · Telefonnummer — outlined float-label row (Figma) */}
|
|
2224
2250
|
<div className="enq-phone-row" style={{ marginTop: 16 }}>
|
|
2225
|
-
|
|
2251
|
+
{/* phone is always required — the * sits on the country-code field (round 13) */}
|
|
2252
|
+
<FloatField label={t('autobahn:enquiry.form.countryLabel') + '*'}>
|
|
2226
2253
|
<span className="enq-country num">
|
|
2227
2254
|
+49
|
|
2228
2255
|
<Icon name="chevron-down" size={14} color="var(--md-on-surface-variant)" />
|
|
@@ -2236,7 +2263,7 @@ export const VdpEnquiry: React.FunctionComponent<VdpEnquiryProps> = ({
|
|
|
2236
2263
|
aria-label={t('autobahn:enquiry.form.areaCodeLabel')}
|
|
2237
2264
|
/>
|
|
2238
2265
|
</FloatField>
|
|
2239
|
-
<FloatField label={t('autobahn:enquiry.form.phoneNumberLabel')
|
|
2266
|
+
<FloatField label={t('autobahn:enquiry.form.phoneNumberLabel')}>
|
|
2240
2267
|
<input
|
|
2241
2268
|
type="tel"
|
|
2242
2269
|
value={phoneNum}
|