@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.
@@ -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
- <Icon name="check-circle" size={18} color="var(--md-good)" />
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" icon="file-check" style={{ flex: 1, minWidth: 0 }} onClick={onSecure}>
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 on its own line ABOVE the big figure */}
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
- <s className="num fin-rate-old" style={{ display: 'block' }}>{eur(listPrice)} €</s>
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" icon="file-check" style={{ flex: 1 }} onClick={onSecure}>
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" icon="file-check" style={{ flex: 1 }} onClick={onSecure}>
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
- // Figma round 12: the required-* follows the chosen contact channel — E-Mail* when email is
2095
- // preferred, Telefonnummer* when phone is; names + consent are always required.
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
- (pref === 'phone' ? phoneOk : emailOk);
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
- // the required-* follows the preferred channel (Figma round 12)
2217
- placeholder={pref === 'email' ? t('autobahn:enquiry.form.emailPlaceholder') : t('autobahn:enquiry.form.emailPlaceholder').replace(/\*$/, '')}
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
- <FloatField label={t('autobahn:enquiry.form.countryLabel')}>
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') + (pref === 'phone' ? '*' : '')}>
2266
+ <FloatField label={t('autobahn:enquiry.form.phoneNumberLabel')}>
2240
2267
  <input
2241
2268
  type="tel"
2242
2269
  value={phoneNum}