@bytebrand/fe-ui-core-autobahn 1.0.60 → 1.0.61
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/cards/CarCard.tsx +55 -19
- package/cards/cardParts.tsx +268 -19
- package/cards/index.ts +4 -0
- package/cards/types.ts +7 -0
- package/constants.ts +31 -31
- package/data/toAutobahnCar.ts +13 -3
- package/locales/de/autobahn.json +32 -19
- package/locales/en/autobahn.json +31 -18
- package/package.json +1 -1
- package/theme/md3.css +537 -51
- package/utils/financeFormat.ts +62 -62
- package/utils/priceRating.ts +58 -58
- package/vdp/FinanceCalculator.tsx +127 -96
- package/vdp/VdpModules.tsx +219 -44
package/vdp/VdpModules.tsx
CHANGED
|
@@ -28,7 +28,7 @@ import { Button, Dropdown, DropdownOption, Icon, Spec, StatChip, Stars, Switch }
|
|
|
28
28
|
import { CO2_CLASSES, CO2_COLORS, TIER_LABELS, TIER_KEYS, TIER_COLORS, TIER_BACKGROUNDS } from '../constants';
|
|
29
29
|
import { defaultVdpSpecs, EquipmentGroups, PriceRatingBands, VehicleDetail, VdpSpec, VdpReview } from './types';
|
|
30
30
|
import { CalcRow, DocCheckIcon, EvalBar, expanderBtnStyle, FinanceMode, Slider, TermChips } from './FinanceCalculator';
|
|
31
|
-
import { formatEur as eur, formatPct as pct
|
|
31
|
+
import { formatEur as eur, formatPct as pct } from '../utils/financeFormat';
|
|
32
32
|
|
|
33
33
|
/** Figma "check with open arc" glyph (modern lucide circle-check-big; the bundled lucide
|
|
34
34
|
* 0.288 only ships the closed-circle variant, so the two paths are inlined). */
|
|
@@ -1957,54 +1957,76 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
|
|
|
1957
1957
|
// total, so Schlussrate + Gesamtbetrag come from the config and are omitted when unavailable —
|
|
1958
1958
|
// never a fabricated/misleading total.
|
|
1959
1959
|
const netto = netLoanAmount != null ? netLoanAmount : Math.max(0, price - down);
|
|
1960
|
-
|
|
1960
|
+
// "Rundum-Sorglos-Paket" one-time fee only counts toward the breakdown when the customer
|
|
1961
|
+
// actually has it switched on (cash mode forces it on upstream) AND a real cost is known.
|
|
1962
|
+
const hasAddServices = !!withAddServices && addServicesCost != null && addServicesCost > 0;
|
|
1963
|
+
// Grand total (Figma "Gesamtbelastung inkl. Anzahlung & Paket") — down payment + the config's
|
|
1964
|
+
// real Gesamtbetrag + the package fee. Only shown when Gesamtbetrag is known (never fabricated).
|
|
1965
|
+
const totalCost = grossAmount != null && grossAmount > 0 ? down + grossAmount + (hasAddServices ? (addServicesCost as number) : 0) : undefined;
|
|
1966
|
+
const rows: Array<[string, string, boolean?]> = [
|
|
1961
1967
|
[t('autobahn:modal.vehiclePrice'), `${eur(price)} €`],
|
|
1962
|
-
[
|
|
1968
|
+
[t('autobahn:modal.term'), t('autobahn:modal.months', { months: term, count: term })],
|
|
1963
1969
|
[t('autobahn:modal.downPayment'), `${eur(down)} €`],
|
|
1964
1970
|
...(isLease && annualMileage != null ? ([[t('autobahn:modal.annualMileage'), `${eur(annualMileage)} km`]] as Array<[string, string]>) : []),
|
|
1965
|
-
[
|
|
1971
|
+
[
|
|
1972
|
+
isLease ? t('autobahn:modal.leasingRateCount', { count: term }) : t('autobahn:modal.monthlyRateCount', { count: term }),
|
|
1973
|
+
`${eur(monthly)} €`,
|
|
1974
|
+
],
|
|
1975
|
+
...(finalInstallment != null && finalInstallment > 0 ? ([[t('autobahn:modal.finalInstallment'), `${eur(finalInstallment)} €`]] as Array<[string, string]>) : []),
|
|
1976
|
+
...(hasAddServices ? ([[t('autobahn:modal.addServicesRow'), `${eur(addServicesCost as number)} €`]] as Array<[string, string]>) : []),
|
|
1966
1977
|
...(nominalRate != null && nominalRate > 0 ? ([[t('autobahn:modal.nominalRate'), pct(nominalRate)]] as Array<[string, string]>) : []),
|
|
1967
1978
|
[t('autobahn:modal.effectiveRate'), pct(apr)],
|
|
1968
|
-
|
|
1969
|
-
[isLease ? t('autobahn:modal.leasingRate') : t('autobahn:modal.monthlyRate'), t('autobahn:modal.ratePerMonth', { amount: eur(monthly) })],
|
|
1979
|
+
[isLease ? t('autobahn:modal.leasingAmount') : t('autobahn:modal.netLoanAmount'), `${eur(netto)} €`],
|
|
1970
1980
|
...(grossAmount != null && grossAmount > 0 ? ([[isLease ? t('autobahn:modal.totalAmount') : t('autobahn:modal.totalRepayment'), `${eur(grossAmount)} €`]] as Array<[string, string]>) : []),
|
|
1981
|
+
...(totalCost != null
|
|
1982
|
+
? ([[hasAddServices ? t('autobahn:modal.totalCostWithPackage') : t('autobahn:modal.totalCost'), `${eur(totalCost)} €`, true]] as Array<
|
|
1983
|
+
[string, string, boolean]
|
|
1984
|
+
>)
|
|
1985
|
+
: []),
|
|
1986
|
+
...(listPrice != null && listPrice > 0 ? ([[t('autobahn:modal.listPrice'), `${eur(listPrice)} €`]] as Array<[string, string]>) : []),
|
|
1971
1987
|
];
|
|
1972
|
-
const breakdownRows = rows.map(([l, v], i) => (
|
|
1988
|
+
const breakdownRows = rows.map(([l, v, bold], i) => (
|
|
1973
1989
|
<div
|
|
1974
1990
|
key={l}
|
|
1975
1991
|
style={{
|
|
1976
1992
|
display: 'flex',
|
|
1977
|
-
|
|
1978
|
-
gap:
|
|
1979
|
-
padding: '
|
|
1980
|
-
|
|
1993
|
+
alignItems: 'baseline',
|
|
1994
|
+
gap: 8,
|
|
1995
|
+
padding: '7px 0',
|
|
1996
|
+
borderTop: '1px solid rgba(212, 221, 231, 0.95)',
|
|
1981
1997
|
}}
|
|
1982
1998
|
>
|
|
1983
|
-
<span
|
|
1984
|
-
|
|
1999
|
+
<span
|
|
2000
|
+
style={{
|
|
2001
|
+
fontFamily: 'Arial, Helvetica, sans-serif',
|
|
2002
|
+
fontStyle: 'normal',
|
|
2003
|
+
fontWeight: bold ? 700 : 400,
|
|
2004
|
+
fontSize: 13,
|
|
2005
|
+
lineHeight: '13px',
|
|
2006
|
+
color: '#0e2238',
|
|
2007
|
+
whiteSpace: 'nowrap',
|
|
2008
|
+
}}
|
|
2009
|
+
>
|
|
2010
|
+
{l}
|
|
2011
|
+
</span>
|
|
2012
|
+
<span
|
|
2013
|
+
className="num"
|
|
2014
|
+
style={{
|
|
2015
|
+
fontFamily: 'Arial, Helvetica, sans-serif',
|
|
2016
|
+
fontStyle: 'normal',
|
|
2017
|
+
fontWeight: bold ? 700 : 400,
|
|
2018
|
+
fontSize: 13,
|
|
2019
|
+
lineHeight: '13px',
|
|
2020
|
+
color: '#0e2238',
|
|
2021
|
+
whiteSpace: 'nowrap',
|
|
2022
|
+
marginLeft: 'auto',
|
|
2023
|
+
}}
|
|
2024
|
+
>
|
|
1985
2025
|
{v}
|
|
1986
2026
|
</span>
|
|
1987
2027
|
</div>
|
|
1988
2028
|
));
|
|
1989
2029
|
|
|
1990
|
-
// mobile sheet footnote — the Figma representative example: real figures inline
|
|
1991
|
-
// ("³ Fahrzeugpreis: … €; Effektiver Jahreszins: … %; …") followed by the PangV sentence.
|
|
1992
|
-
// Only REAL config figures are included; absent ones are simply omitted — shared logic with
|
|
1993
|
-
// the Meta AIA marketing-image renderer, see ../utils/financeFormat.ts.
|
|
1994
|
-
const exampleLine = buildFinanceExampleLine(
|
|
1995
|
-
{ price, apr, down, netLoanAmount, nominalRate, finalInstallment, grossAmount },
|
|
1996
|
-
{
|
|
1997
|
-
vehiclePrice: t('autobahn:modal.exampleVehiclePrice'),
|
|
1998
|
-
priceSuffix: t('autobahn:modal.examplePriceSuffix'),
|
|
1999
|
-
apr: t('autobahn:modal.exampleApr'),
|
|
2000
|
-
nominal: t('autobahn:modal.exampleNominal'),
|
|
2001
|
-
net: t('autobahn:modal.exampleNet'),
|
|
2002
|
-
down: t('autobahn:modal.exampleDown'),
|
|
2003
|
-
final: t('autobahn:modal.exampleFinal'),
|
|
2004
|
-
gross: t('autobahn:modal.exampleGross'),
|
|
2005
|
-
},
|
|
2006
|
-
);
|
|
2007
|
-
|
|
2008
2030
|
return (
|
|
2009
2031
|
<div className="fin-modal-scrim" onClick={onClose}>
|
|
2010
2032
|
<div
|
|
@@ -2408,7 +2430,7 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
|
|
|
2408
2430
|
</div>
|
|
2409
2431
|
|
|
2410
2432
|
{/* centered ⓘ details toggle (mockup shows no chevron) */}
|
|
2411
|
-
<button type="button" onClick={() => setShowBreakdown((v) => !v)} style={{ ...expanderBtnStyle, justifyContent: 'center', padding: '2px 0' }}>
|
|
2433
|
+
<button type="button" onClick={() => setShowBreakdown((v) => !v)} style={{ ...expanderBtnStyle, justifyContent: 'center', padding: '2px 0 20px' }}>
|
|
2412
2434
|
<span
|
|
2413
2435
|
style={{
|
|
2414
2436
|
display: 'inline-flex',
|
|
@@ -2425,27 +2447,180 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
|
|
|
2425
2447
|
{isLease ? t('autobahn:modal.leasingDetails') : t('autobahn:modal.financingDetails')}
|
|
2426
2448
|
</span>
|
|
2427
2449
|
</button>
|
|
2428
|
-
{/* mobile
|
|
2429
|
-
|
|
2450
|
+
{/* mobile "Finanzierungsdetails" toggle reveals the same row-by-row breakdown
|
|
2451
|
+
table as desktop (breakdownRows, built once above) instead of the old
|
|
2452
|
+
condensed footnote line. */}
|
|
2430
2453
|
{showBreakdown && (
|
|
2431
|
-
|
|
2454
|
+
<>
|
|
2455
|
+
<div style={{ marginBottom: 5 }}>
|
|
2456
|
+
<p
|
|
2457
|
+
style={{
|
|
2458
|
+
fontFamily: 'Arial, Helvetica, sans-serif',
|
|
2459
|
+
fontWeight: 700,
|
|
2460
|
+
fontSize: 16,
|
|
2461
|
+
lineHeight: '18px',
|
|
2462
|
+
color: '#0E2238',
|
|
2463
|
+
margin: 0,
|
|
2464
|
+
marginBottom: 5,
|
|
2465
|
+
paddingTop: 10,
|
|
2466
|
+
borderTop: '1px solid rgba(212, 221, 231, 0.95)',
|
|
2467
|
+
}}
|
|
2468
|
+
>
|
|
2469
|
+
{t('autobahn:modal.representativeExample')}
|
|
2470
|
+
</p>
|
|
2471
|
+
<p
|
|
2472
|
+
style={{
|
|
2473
|
+
fontFamily: 'Arial, Helvetica, sans-serif',
|
|
2474
|
+
fontWeight: 700,
|
|
2475
|
+
fontSize: 9,
|
|
2476
|
+
lineHeight: '12px',
|
|
2477
|
+
color: '#5B6B7F',
|
|
2478
|
+
marginBottom: 5,
|
|
2479
|
+
}}
|
|
2480
|
+
>
|
|
2481
|
+
{t('autobahn:modal.vatDisclaimer')}
|
|
2482
|
+
</p>
|
|
2483
|
+
{breakdownRows}
|
|
2484
|
+
</div>
|
|
2485
|
+
<div
|
|
2486
|
+
style={{
|
|
2487
|
+
display: 'flex',
|
|
2488
|
+
alignItems: 'flex-start',
|
|
2489
|
+
gap: 10,
|
|
2490
|
+
background: '#FAFBFD',
|
|
2491
|
+
border: '1px solid #D4DDE7',
|
|
2492
|
+
borderRadius: 6,
|
|
2493
|
+
padding: 10,
|
|
2494
|
+
marginBottom: 10,
|
|
2495
|
+
}}
|
|
2496
|
+
>
|
|
2497
|
+
<svg width="27" height="27" viewBox="0 0 27 27" fill="none" xmlns="http://www.w3.org/2000/svg" style={{ flex: 'none' }}>
|
|
2498
|
+
<path d="M2.7002 9.00001L13.5002 3.60001L24.3002 9.00001H2.7002Z" stroke="#0E2238" strokeWidth="1.6" strokeLinejoin="round" />
|
|
2499
|
+
<path d="M4.49961 10.8H22.4996M5.39961 21.6H21.5996M3.59961 24.3H23.3996" stroke="#0E2238" strokeWidth="1.6" strokeLinecap="round" />
|
|
2500
|
+
<path d="M7.2002 10.8V21.6M11.7002 10.8V21.6M15.3002 10.8V21.6M19.8002 10.8V21.6" stroke="#0E2238" strokeWidth="1.6" />
|
|
2501
|
+
</svg>
|
|
2502
|
+
<div>
|
|
2503
|
+
<p
|
|
2504
|
+
style={{
|
|
2505
|
+
fontFamily: 'Arial, Helvetica, sans-serif',
|
|
2506
|
+
fontWeight: 700,
|
|
2507
|
+
fontSize: 14,
|
|
2508
|
+
lineHeight: '13px',
|
|
2509
|
+
color: '#0E2238',
|
|
2510
|
+
margin: 0,
|
|
2511
|
+
marginBottom: 3,
|
|
2512
|
+
}}
|
|
2513
|
+
>
|
|
2514
|
+
{t('autobahn:modal.lenderLabel')}
|
|
2515
|
+
</p>
|
|
2516
|
+
<p
|
|
2517
|
+
style={{
|
|
2518
|
+
fontFamily: 'Arial, Helvetica, sans-serif',
|
|
2519
|
+
fontWeight: 400,
|
|
2520
|
+
fontSize: 12,
|
|
2521
|
+
lineHeight: '13px',
|
|
2522
|
+
color: '#0E2238',
|
|
2523
|
+
margin: 0,
|
|
2524
|
+
}}
|
|
2525
|
+
>
|
|
2526
|
+
{t('autobahn:modal.lenderName')}
|
|
2527
|
+
</p>
|
|
2528
|
+
</div>
|
|
2529
|
+
</div>
|
|
2432
2530
|
<p
|
|
2433
|
-
className="md-label-sm"
|
|
2434
2531
|
style={{
|
|
2532
|
+
fontFamily: 'Arial, Helvetica, sans-serif',
|
|
2533
|
+
fontWeight: 700,
|
|
2534
|
+
fontSize: 13.3,
|
|
2535
|
+
lineHeight: '17px',
|
|
2536
|
+
color: '#0E2238',
|
|
2435
2537
|
margin: 0,
|
|
2436
|
-
|
|
2538
|
+
marginBottom: 7,
|
|
2539
|
+
}}
|
|
2540
|
+
>
|
|
2541
|
+
{t('autobahn:modal.legalNoticesTitle')}
|
|
2542
|
+
</p>
|
|
2543
|
+
<ul style={{ margin: 0, marginBottom: 3, paddingLeft: 16, display: 'flex', flexDirection: 'column', gap: 3 }}>
|
|
2544
|
+
{[
|
|
2545
|
+
t('autobahn:modal.legalNoticeCreditworthiness'),
|
|
2546
|
+
t('autobahn:modal.legalNoticeExample'),
|
|
2547
|
+
t('autobahn:modal.legalNoticeTwoThirds'),
|
|
2548
|
+
t('autobahn:modal.legalNoticeVat'),
|
|
2549
|
+
t('autobahn:modal.legalNoticeAddServices'),
|
|
2550
|
+
].map((notice) => (
|
|
2551
|
+
<li
|
|
2552
|
+
key={notice}
|
|
2553
|
+
style={{
|
|
2554
|
+
fontFamily: 'Arial, Helvetica, sans-serif',
|
|
2555
|
+
fontWeight: 400,
|
|
2556
|
+
fontSize: 12,
|
|
2557
|
+
lineHeight: '12px',
|
|
2558
|
+
color: '#0E2238',
|
|
2559
|
+
}}
|
|
2560
|
+
>
|
|
2561
|
+
{notice}
|
|
2562
|
+
</li>
|
|
2563
|
+
))}
|
|
2564
|
+
</ul>
|
|
2565
|
+
<p
|
|
2566
|
+
style={{
|
|
2437
2567
|
fontFamily: 'Arial, Helvetica, sans-serif',
|
|
2438
2568
|
fontWeight: 400,
|
|
2439
|
-
fontSize:
|
|
2440
|
-
lineHeight: '
|
|
2441
|
-
color: '#
|
|
2569
|
+
fontSize: 12,
|
|
2570
|
+
lineHeight: '12px',
|
|
2571
|
+
color: '#0E2238',
|
|
2572
|
+
margin: 0,
|
|
2573
|
+
paddingBottom: 10,
|
|
2574
|
+
borderBottom: '1px solid rgba(212, 221, 231, 0.95)',
|
|
2442
2575
|
}}
|
|
2443
2576
|
>
|
|
2444
|
-
{
|
|
2445
|
-
<br />
|
|
2446
|
-
{isLease ? t('autobahn:modal.legalLeasing') : t('autobahn:modal.legalFinancing')}
|
|
2577
|
+
{t('autobahn:modal.listPriceFootnote')}
|
|
2447
2578
|
</p>
|
|
2448
|
-
|
|
2579
|
+
<div className="fin-sheet-cta-row" style={{ display: 'flex', gap: 7, paddingBottom: 15 }}>
|
|
2580
|
+
<Button
|
|
2581
|
+
variant="filled"
|
|
2582
|
+
size="lg"
|
|
2583
|
+
style={{
|
|
2584
|
+
width: 286,
|
|
2585
|
+
height: 50,
|
|
2586
|
+
background: '#005CFF',
|
|
2587
|
+
borderRadius: 16,
|
|
2588
|
+
justifyContent: 'flex-start',
|
|
2589
|
+
paddingLeft: 47,
|
|
2590
|
+
paddingRight: 47,
|
|
2591
|
+
fontFamily: 'Arial, Helvetica, sans-serif',
|
|
2592
|
+
fontWeight: 700,
|
|
2593
|
+
fontSize: 14,
|
|
2594
|
+
lineHeight: '16px',
|
|
2595
|
+
textAlign: 'center',
|
|
2596
|
+
color: '#FFFFFF',
|
|
2597
|
+
}}
|
|
2598
|
+
onClick={onSecure}
|
|
2599
|
+
>
|
|
2600
|
+
<span style={{ display: 'inline-flex', marginRight: 18 }}>
|
|
2601
|
+
<DocCheckIcon size={23} />
|
|
2602
|
+
</span>
|
|
2603
|
+
{t('autobahn:finance.requestRate')}
|
|
2604
|
+
</Button>
|
|
2605
|
+
{/* padding:0 — mbtn--lg's 30px side padding left no room for the icon at width 52 */}
|
|
2606
|
+
<Button
|
|
2607
|
+
variant="outlined"
|
|
2608
|
+
size="lg"
|
|
2609
|
+
icon="phone"
|
|
2610
|
+
className="fin-sheet-call-btn"
|
|
2611
|
+
style={{
|
|
2612
|
+
width: 50,
|
|
2613
|
+
height: 50,
|
|
2614
|
+
flex: 'none',
|
|
2615
|
+
padding: 0,
|
|
2616
|
+
background: '#FFFFFF',
|
|
2617
|
+
border: '1.6px solid #005CFF',
|
|
2618
|
+
borderRadius: 16,
|
|
2619
|
+
}}
|
|
2620
|
+
onClick={onCall}
|
|
2621
|
+
/>
|
|
2622
|
+
</div>
|
|
2623
|
+
</>
|
|
2449
2624
|
)}
|
|
2450
2625
|
</>
|
|
2451
2626
|
)}
|