@bytebrand/fe-ui-core-autobahn 1.0.14 → 1.0.15
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/locales/de/autobahn.json +15 -3
- package/locales/en/autobahn.json +15 -3
- package/package.json +1 -1
- package/vdp/FinanceCalculator.tsx +139 -78
- package/vdp/VdpModules.tsx +321 -64
package/locales/de/autobahn.json
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"termSelection": "Laufzeit",
|
|
57
57
|
"headerTitle": "Dein Angebot",
|
|
58
58
|
"adjustRate": "Rate anpassen",
|
|
59
|
-
"secureOffer": "Angebot
|
|
59
|
+
"secureOffer": "Angebot anfragen",
|
|
60
60
|
"trustLine": "Unverbindlich · kostenlos · 14 Tage Rückgaberecht",
|
|
61
61
|
"downPaymentLabel": "€ Anzahlung",
|
|
62
62
|
"monthsLabel": "{{months}} Monat",
|
|
@@ -84,7 +84,16 @@
|
|
|
84
84
|
"kmPerYear": "{{value}} km/Jahr",
|
|
85
85
|
"perMonth": "/mtl.",
|
|
86
86
|
"leasingDetails": "Leasingdetails",
|
|
87
|
-
"financingDetails": "Finanzierungsdetails"
|
|
87
|
+
"financingDetails": "Finanzierungsdetails",
|
|
88
|
+
"personalConsultation": "Persönliche Beratung",
|
|
89
|
+
"personalConsultationSub": "Wir sind für Sie da",
|
|
90
|
+
"financingLeasingPossible": "Finanzierung & Leasing möglich",
|
|
91
|
+
"requestRate": "Rate anfragen",
|
|
92
|
+
"callCta": "Anrufen",
|
|
93
|
+
"finalInstallmentToggle": "Mit Schlussrate",
|
|
94
|
+
"addServicesToggle": "Rundum-Sorglos-Paket",
|
|
95
|
+
"addServicesPerMonth": "+{{value}} € / Monat",
|
|
96
|
+
"addServicesOnce": "+{{value}} €"
|
|
88
97
|
},
|
|
89
98
|
"specTable": {
|
|
90
99
|
"title": "Fahrzeugdaten",
|
|
@@ -603,7 +612,10 @@
|
|
|
603
612
|
"totalAmount": "Gesamtbetrag",
|
|
604
613
|
"totalRepayment": "Gesamtrückzahlungsbetrag",
|
|
605
614
|
"legalLeasing": "Unverbindliches Leasingbeispiel (Kilometerleasing). Bonität vorausgesetzt. Dieses freibleibende Angebot wird vermittelt durch die auto.de GmbH, Max-Planck-Str. 19, 06796 Sandersdorf-Brehna, die als ungebundener Vermittler nicht beratend tätig ist. Irrtümer vorbehalten. Preise ggf. inkl. MwSt.",
|
|
606
|
-
"legalFinancing": "Repräsentatives Berechnungsbeispiel gem. § 17a Abs. 4 PAngV, das 2/3 aller Kunden — Bonität vorausgesetzt — erhalten. Für Ihre Finanzierungswünsche stellen wir eine Bonitätsanfrage. Dieses freibleibende Angebot der Santander Consumer Bank AG, Santander-Platz 1, 41061 Mönchengladbach, wird vermittelt durch die auto.de GmbH, Max-Planck-Str. 19, 06796 Sandersdorf-Brehna, die als ungebundener Vermittler nicht beratend tätig ist. Irrtümer vorbehalten. Preise ggf. inkl. MwSt."
|
|
615
|
+
"legalFinancing": "Repräsentatives Berechnungsbeispiel gem. § 17a Abs. 4 PAngV, das 2/3 aller Kunden — Bonität vorausgesetzt — erhalten. Für Ihre Finanzierungswünsche stellen wir eine Bonitätsanfrage. Dieses freibleibende Angebot der Santander Consumer Bank AG, Santander-Platz 1, 41061 Mönchengladbach, wird vermittelt durch die auto.de GmbH, Max-Planck-Str. 19, 06796 Sandersdorf-Brehna, die als ungebundener Vermittler nicht beratend tätig ist. Irrtümer vorbehalten. Preise ggf. inkl. MwSt.",
|
|
616
|
+
"sheetTitleFinancing": "Kredit-Rate anpassen",
|
|
617
|
+
"sheetTitleLeasing": "Leasing-Rate anpassen",
|
|
618
|
+
"sheetTitleCash": "Kaufen - Details"
|
|
607
619
|
},
|
|
608
620
|
"ratings": {
|
|
609
621
|
"showLess": "Weniger anzeigen",
|
package/locales/en/autobahn.json
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"termSelection": "Term",
|
|
57
57
|
"headerTitle": "Your offer",
|
|
58
58
|
"adjustRate": "Adjust rate",
|
|
59
|
-
"secureOffer": "
|
|
59
|
+
"secureOffer": "Request offer",
|
|
60
60
|
"trustLine": "Non-binding · free · 14-day return right",
|
|
61
61
|
"downPaymentLabel": "€ down payment",
|
|
62
62
|
"monthsLabel": "{{months}} month",
|
|
@@ -84,7 +84,16 @@
|
|
|
84
84
|
"kmPerYear": "{{value}} km/year",
|
|
85
85
|
"perMonth": "/mo.",
|
|
86
86
|
"leasingDetails": "Leasing details",
|
|
87
|
-
"financingDetails": "Financing details"
|
|
87
|
+
"financingDetails": "Financing details",
|
|
88
|
+
"personalConsultation": "Personal advice",
|
|
89
|
+
"personalConsultationSub": "We're here for you",
|
|
90
|
+
"financingLeasingPossible": "Financing & leasing available",
|
|
91
|
+
"requestRate": "Request rate",
|
|
92
|
+
"callCta": "Call",
|
|
93
|
+
"finalInstallmentToggle": "With final installment",
|
|
94
|
+
"addServicesToggle": "All-round care package",
|
|
95
|
+
"addServicesPerMonth": "+€{{value}} / month",
|
|
96
|
+
"addServicesOnce": "+€{{value}}"
|
|
88
97
|
},
|
|
89
98
|
"specTable": {
|
|
90
99
|
"title": "Vehicle data",
|
|
@@ -603,7 +612,10 @@
|
|
|
603
612
|
"totalAmount": "Total amount",
|
|
604
613
|
"totalRepayment": "Total repayment amount",
|
|
605
614
|
"legalLeasing": "Non-binding leasing example (mileage leasing). Subject to creditworthiness. This non-binding offer is brokered by auto.de GmbH, Max-Planck-Str. 19, 06796 Sandersdorf-Brehna, which acts as a non-tied intermediary and does not provide advice. Errors excepted. Prices incl. VAT where applicable.",
|
|
606
|
-
"legalFinancing": "Representative calculation example pursuant to § 17a (4) PAngV, obtained by 2/3 of all customers — subject to creditworthiness. For your financing requests we will submit a credit inquiry. This non-binding offer from Santander Consumer Bank AG, Santander-Platz 1, 41061 Mönchengladbach, is brokered by auto.de GmbH, Max-Planck-Str. 19, 06796 Sandersdorf-Brehna, which acts as a non-tied intermediary and does not provide advice. Errors excepted. Prices incl. VAT where applicable."
|
|
615
|
+
"legalFinancing": "Representative calculation example pursuant to § 17a (4) PAngV, obtained by 2/3 of all customers — subject to creditworthiness. For your financing requests we will submit a credit inquiry. This non-binding offer from Santander Consumer Bank AG, Santander-Platz 1, 41061 Mönchengladbach, is brokered by auto.de GmbH, Max-Planck-Str. 19, 06796 Sandersdorf-Brehna, which acts as a non-tied intermediary and does not provide advice. Errors excepted. Prices incl. VAT where applicable.",
|
|
616
|
+
"sheetTitleFinancing": "Adjust credit rate",
|
|
617
|
+
"sheetTitleLeasing": "Adjust leasing rate",
|
|
618
|
+
"sheetTitleCash": "Purchase - Details"
|
|
607
619
|
},
|
|
608
620
|
"ratings": {
|
|
609
621
|
"showLess": "Show less",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bytebrand/fe-ui-core-autobahn",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"description": "auto.de md3 (Autobahn) design system — UI primitives, cards, chrome, VDP modules, theme CSS and locales. Ships raw TypeScript source (like fe-ui-core before it); each consumer's bundler compiles it.",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -158,8 +158,11 @@ export interface FinanceCalculatorProps {
|
|
|
158
158
|
onSecure?: () => void;
|
|
159
159
|
/** secondary CTA click (phone). */
|
|
160
160
|
onCall?: () => void;
|
|
161
|
-
/** open the Finanzierungs-/Leasingdetails popup (full breakdown + legal). Omit to hide the link. */
|
|
161
|
+
/** open the Finanzierungs-/Leasingdetails popup (full breakdown + legal, expanded). Omit to hide the link. */
|
|
162
162
|
onShowDetails?: () => void;
|
|
163
|
+
/** mobile only: open the "Rate anpassen" / "Details anzeigen" sheet (collapsed/default view).
|
|
164
|
+
* Omit to hide the trigger row (falls back to the desktop inline expander). */
|
|
165
|
+
onOpenAdjust?: () => void;
|
|
163
166
|
/** mode segment changed. */
|
|
164
167
|
onModeChange: (mode: FinanceMode) => void;
|
|
165
168
|
/** down-payment slider changed. */
|
|
@@ -204,7 +207,7 @@ const DetailsLink: React.FunctionComponent<{ label: string; onClick: () => void
|
|
|
204
207
|
);
|
|
205
208
|
|
|
206
209
|
/** "Rate anpassen" / "Details anzeigen" expander toggle button. */
|
|
207
|
-
const expanderBtnStyle: React.CSSProperties = {
|
|
210
|
+
export const expanderBtnStyle: React.CSSProperties = {
|
|
208
211
|
width: '100%',
|
|
209
212
|
background: 'none',
|
|
210
213
|
border: 0,
|
|
@@ -243,6 +246,7 @@ const FinanceCalculator: React.FunctionComponent<FinanceCalculatorProps> = ({
|
|
|
243
246
|
onSecure,
|
|
244
247
|
onCall,
|
|
245
248
|
onShowDetails,
|
|
249
|
+
onOpenAdjust,
|
|
246
250
|
onModeChange,
|
|
247
251
|
onDownChange,
|
|
248
252
|
onTermChange,
|
|
@@ -364,100 +368,157 @@ const FinanceCalculator: React.FunctionComponent<FinanceCalculatorProps> = ({
|
|
|
364
368
|
</div>
|
|
365
369
|
</div>
|
|
366
370
|
|
|
367
|
-
{/*
|
|
368
|
-
<div className="
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
{
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
<
|
|
381
|
-
|
|
382
|
-
{t('autobahn:finance.
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, marginTop: 8 }}>
|
|
387
|
-
<div>
|
|
388
|
-
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
|
|
389
|
-
<span className="md-title-sm">{t('autobahn:finance.downPaymentSliderLabel')}</span>
|
|
390
|
-
<span className="num" style={{ fontWeight: 700 }}>
|
|
391
|
-
{eur(down)} €
|
|
392
|
-
</span>
|
|
393
|
-
</div>
|
|
394
|
-
<Slider
|
|
395
|
-
min={downMin}
|
|
396
|
-
max={downMax}
|
|
397
|
-
step={downStep}
|
|
398
|
-
value={down}
|
|
399
|
-
onChange={onDownChange}
|
|
400
|
-
fmt={(v) => `${eur(Number(v))} €`}
|
|
401
|
-
/>
|
|
371
|
+
{/* ============================== DESKTOP (≥601px) — unchanged ============================== */}
|
|
372
|
+
<div className="hide-mobile">
|
|
373
|
+
{/* RATE EDIT (Rate anpassen) — between the rate and the request CTA */}
|
|
374
|
+
<div className="fin-adjust">
|
|
375
|
+
<button onClick={() => setAdjust((a) => !a)} style={expanderBtnStyle}>
|
|
376
|
+
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 7 }}>
|
|
377
|
+
<Icon name={isCash ? 'list' : 'sliders-horizontal'} size={16} color="var(--md-primary)" />
|
|
378
|
+
{isCash ? t('autobahn:finance.detailsShow') : t('autobahn:finance.adjustRate')}
|
|
379
|
+
</span>
|
|
380
|
+
<Icon name={adjust ? 'chevron-up' : 'chevron-down'} size={18} color="var(--md-primary)" />
|
|
381
|
+
</button>
|
|
382
|
+
{adjust &&
|
|
383
|
+
(isCash ? (
|
|
384
|
+
<div style={{ marginTop: 4 }}>
|
|
385
|
+
<CalcRow label={t('autobahn:finance.paymentLabel')}>{t('autobahn:finance.paymentValue')}</CalcRow>
|
|
386
|
+
<CalcRow label={t('autobahn:finance.warrantyLabel')}>{t('autobahn:finance.warrantyValue')}</CalcRow>
|
|
387
|
+
<CalcRow label={t('autobahn:finance.deliveryLabel')} last>
|
|
388
|
+
{t('autobahn:finance.deliveryValue')}
|
|
389
|
+
</CalcRow>
|
|
402
390
|
</div>
|
|
403
|
-
|
|
391
|
+
) : (
|
|
392
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, marginTop: 8 }}>
|
|
404
393
|
<div>
|
|
405
394
|
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
|
|
406
|
-
<span className="md-title-sm">{t('autobahn:finance.
|
|
395
|
+
<span className="md-title-sm">{t('autobahn:finance.downPaymentSliderLabel')}</span>
|
|
407
396
|
<span className="num" style={{ fontWeight: 700 }}>
|
|
408
|
-
{eur(
|
|
397
|
+
{eur(down)} €
|
|
409
398
|
</span>
|
|
410
399
|
</div>
|
|
411
400
|
<Slider
|
|
412
|
-
min={
|
|
413
|
-
max={
|
|
414
|
-
step={
|
|
415
|
-
value={
|
|
416
|
-
onChange={
|
|
417
|
-
fmt={(v) => `${eur(Number(v))}
|
|
401
|
+
min={downMin}
|
|
402
|
+
max={downMax}
|
|
403
|
+
step={downStep}
|
|
404
|
+
value={down}
|
|
405
|
+
onChange={onDownChange}
|
|
406
|
+
fmt={(v) => `${eur(Number(v))} €`}
|
|
418
407
|
/>
|
|
419
408
|
</div>
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
409
|
+
{annualMileage != null && onAnnualMileageChange && (
|
|
410
|
+
<div>
|
|
411
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
|
|
412
|
+
<span className="md-title-sm">{t('autobahn:finance.annualMileageLabel')}</span>
|
|
413
|
+
<span className="num" style={{ fontWeight: 700 }}>
|
|
414
|
+
{eur(annualMileage)} km
|
|
415
|
+
</span>
|
|
416
|
+
</div>
|
|
417
|
+
<Slider
|
|
418
|
+
min={annualMileageMin}
|
|
419
|
+
max={annualMileageMax}
|
|
420
|
+
step={annualMileageStep}
|
|
421
|
+
value={annualMileage}
|
|
422
|
+
onChange={onAnnualMileageChange}
|
|
423
|
+
fmt={(v) => `${eur(Number(v))} km`}
|
|
424
|
+
/>
|
|
425
|
+
</div>
|
|
426
|
+
)}
|
|
427
|
+
<div>
|
|
428
|
+
<div className="md-title-sm" style={{ marginBottom: 8 }}>
|
|
429
|
+
{t('autobahn:finance.termSelection')}
|
|
430
|
+
</div>
|
|
431
|
+
<TermChips options={terms} value={term} onChange={onTermChange} t={t} />
|
|
432
|
+
</div>
|
|
433
|
+
<div>
|
|
434
|
+
<CalcRow label={t('autobahn:finance.purchasePrice')}>{eur(price)} €</CalcRow>
|
|
435
|
+
<CalcRow label={t('autobahn:finance.warrantyLabel')}>{t('autobahn:finance.warrantyValue')}</CalcRow>
|
|
436
|
+
<CalcRow label={t('autobahn:finance.deliveryLabel')} last>
|
|
437
|
+
{t('autobahn:finance.deliveryValue')}
|
|
438
|
+
</CalcRow>
|
|
424
439
|
</div>
|
|
425
|
-
<TermChips options={terms} value={term} onChange={onTermChange} t={t} />
|
|
426
|
-
</div>
|
|
427
|
-
<div>
|
|
428
|
-
<CalcRow label={t('autobahn:finance.purchasePrice')}>{eur(price)} €</CalcRow>
|
|
429
|
-
<CalcRow label={t('autobahn:finance.warrantyLabel')}>{t('autobahn:finance.warrantyValue')}</CalcRow>
|
|
430
|
-
<CalcRow label={t('autobahn:finance.deliveryLabel')} last>
|
|
431
|
-
{t('autobahn:finance.deliveryValue')}
|
|
432
|
-
</CalcRow>
|
|
433
440
|
</div>
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
</div>
|
|
441
|
+
))}
|
|
442
|
+
</div>
|
|
437
443
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
</Button>
|
|
442
|
-
{/* phone CTA — touch only (hidden on desktop via .fin-call) */}
|
|
443
|
-
<div className="fin-call">
|
|
444
|
-
<Button variant="outlined" block icon="phone" onClick={onCall}>
|
|
445
|
-
{phone}
|
|
444
|
+
{/* REQUEST CTA */}
|
|
445
|
+
<Button variant="filled" block size="lg" icon="shield-check" style={{ marginTop: 14 }} onClick={onSecure}>
|
|
446
|
+
{t('autobahn:finance.secureOffer')}
|
|
446
447
|
</Button>
|
|
448
|
+
{/* phone CTA — touch only (hidden on desktop via .fin-call) */}
|
|
449
|
+
<div className="fin-call">
|
|
450
|
+
<Button variant="outlined" block icon="phone" onClick={onCall}>
|
|
451
|
+
{phone}
|
|
452
|
+
</Button>
|
|
453
|
+
</div>
|
|
454
|
+
|
|
455
|
+
{/* auto.de-ID in front of the Finanzierungs-/Leasingdetails link — one row, under everything */}
|
|
456
|
+
{(autoDeId || (!isCash && onShowDetails)) && (
|
|
457
|
+
<div className="fin-info">
|
|
458
|
+
{autoDeId && <span className="fin-id">auto.de-ID {autoDeId}</span>}
|
|
459
|
+
{!isCash && onShowDetails && (
|
|
460
|
+
<DetailsLink
|
|
461
|
+
label={mode === 'leasing' ? t('autobahn:finance.leasingDetails') : t('autobahn:finance.financingDetails')}
|
|
462
|
+
onClick={onShowDetails}
|
|
463
|
+
/>
|
|
464
|
+
)}
|
|
465
|
+
</div>
|
|
466
|
+
)}
|
|
447
467
|
</div>
|
|
448
468
|
|
|
449
|
-
{/*
|
|
450
|
-
|
|
469
|
+
{/* ============================== MOBILE (≤600px) — new layout ============================== */}
|
|
470
|
+
<div className="show-mobile" style={{ flexDirection: 'column' }}>
|
|
471
|
+
{/* side-by-side CTAs */}
|
|
472
|
+
<div style={{ display: 'flex', gap: 10, marginTop: 14 }}>
|
|
473
|
+
<Button variant="filled" size="lg" icon="shield-check" style={{ flex: 1 }} onClick={onSecure}>
|
|
474
|
+
{t('autobahn:finance.secureOffer')}
|
|
475
|
+
</Button>
|
|
476
|
+
<Button variant="outlined" size="lg" icon="phone" style={{ flex: 1 }} onClick={onCall}>
|
|
477
|
+
{t('autobahn:finance.callCta')}
|
|
478
|
+
</Button>
|
|
479
|
+
</div>
|
|
480
|
+
|
|
481
|
+
{/* Persönliche Beratung (left) · Finanzierungs-/Leasingdetails or hint (right) */}
|
|
451
482
|
<div className="fin-info">
|
|
452
|
-
{
|
|
453
|
-
|
|
454
|
-
<
|
|
455
|
-
label={
|
|
456
|
-
|
|
457
|
-
|
|
483
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>
|
|
484
|
+
<Icon name="user" size={18} color="var(--md-on-surface-variant)" style={{ flex: 'none' }} />
|
|
485
|
+
<div style={{ minWidth: 0 }}>
|
|
486
|
+
<div className="md-label-sm" style={{ fontWeight: 700, color: 'var(--md-on-surface)' }}>
|
|
487
|
+
{t('autobahn:finance.personalConsultation')}
|
|
488
|
+
</div>
|
|
489
|
+
<div className="md-label-sm">{t('autobahn:finance.personalConsultationSub')}</div>
|
|
490
|
+
</div>
|
|
491
|
+
</div>
|
|
492
|
+
{isCash ? (
|
|
493
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 6, color: 'var(--md-on-surface-variant)' }}>
|
|
494
|
+
<Icon name="info" size={15} />
|
|
495
|
+
<span className="md-label-sm">{t('autobahn:finance.financingLeasingPossible')}</span>
|
|
496
|
+
</div>
|
|
497
|
+
) : (
|
|
498
|
+
onShowDetails && (
|
|
499
|
+
<DetailsLink
|
|
500
|
+
label={mode === 'leasing' ? t('autobahn:finance.leasingDetails') : t('autobahn:finance.financingDetails')}
|
|
501
|
+
onClick={onShowDetails}
|
|
502
|
+
/>
|
|
503
|
+
)
|
|
458
504
|
)}
|
|
459
505
|
</div>
|
|
460
|
-
|
|
506
|
+
|
|
507
|
+
{/* "Rate anpassen" / "Details anzeigen" — opens the full bottom sheet (not an inline expander) */}
|
|
508
|
+
{onOpenAdjust && (
|
|
509
|
+
<button
|
|
510
|
+
type="button"
|
|
511
|
+
onClick={onOpenAdjust}
|
|
512
|
+
style={{ ...expanderBtnStyle, borderTop: '1px solid var(--md-outline-variant)', marginTop: 4, padding: '14px 0 2px' }}
|
|
513
|
+
>
|
|
514
|
+
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 7 }}>
|
|
515
|
+
<Icon name={isCash ? 'list' : 'sliders-horizontal'} size={16} color="var(--md-primary)" />
|
|
516
|
+
{isCash ? t('autobahn:finance.detailsShow') : t('autobahn:finance.adjustRate')}
|
|
517
|
+
</span>
|
|
518
|
+
<Icon name="chevron-right" size={18} color="var(--md-primary)" />
|
|
519
|
+
</button>
|
|
520
|
+
)}
|
|
521
|
+
</div>
|
|
461
522
|
</div>
|
|
462
523
|
);
|
|
463
524
|
};
|
package/vdp/VdpModules.tsx
CHANGED
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
import React, { useState } from 'react';
|
|
25
25
|
import { TranslationFunction } from '../types';
|
|
26
26
|
|
|
27
|
-
import { Button, Icon, Spec, StatChip, Stars } from '../ui';
|
|
28
|
-
import { CO2_CLASSES, CO2_COLORS } from '../constants';
|
|
27
|
+
import { Button, Icon, Spec, StatChip, Stars, Switch } from '../ui';
|
|
28
|
+
import { CO2_CLASSES, CO2_COLORS, TIER_LABELS, TIER_KEYS, TIER_COLORS } from '../constants';
|
|
29
29
|
import { defaultVdpSpecs, EquipmentGroups, VehicleDetail, VdpSpec, VdpReview } from './types';
|
|
30
|
-
import { Slider, TermChips } from './FinanceCalculator';
|
|
30
|
+
import { CalcRow, expanderBtnStyle, FinanceMode, Slider, TermChips } from './FinanceCalculator';
|
|
31
31
|
|
|
32
32
|
const eur = (n: number): string => n.toLocaleString('de-DE');
|
|
33
33
|
|
|
@@ -934,6 +934,29 @@ export const InspectionBlock: React.FunctionComponent<InspectionBlockProps> = ({
|
|
|
934
934
|
// Finanzierungs-/Leasingdetails popup (desktop modal / mobile bottom-sheet)
|
|
935
935
|
// ---------------------------------------------------------------------------
|
|
936
936
|
|
|
937
|
+
/** full-width label(+optional trailing price) / Switch row — Switch itself is inline-flex
|
|
938
|
+
* (sized to its own content), so the space-between has to live on this wrapper, not on
|
|
939
|
+
* Switch's `label` prop (which renders track-then-label, not a full-width justified row). */
|
|
940
|
+
const ToggleRow: React.FunctionComponent<{
|
|
941
|
+
label: string;
|
|
942
|
+
price?: string;
|
|
943
|
+
checked: boolean;
|
|
944
|
+
disabled?: boolean;
|
|
945
|
+
onChange: (checked: boolean) => void;
|
|
946
|
+
}> = ({ label, price, checked, disabled, onChange }) => (
|
|
947
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10 }}>
|
|
948
|
+
<span className="md-title-sm">{label}</span>
|
|
949
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
|
950
|
+
{price && (
|
|
951
|
+
<span className="num" style={{ fontWeight: 700, fontSize: 13.5 }}>
|
|
952
|
+
{price}
|
|
953
|
+
</span>
|
|
954
|
+
)}
|
|
955
|
+
<Switch checked={checked} disabled={disabled} onChange={(e) => onChange(e.target.checked)} ariaLabel={label} />
|
|
956
|
+
</div>
|
|
957
|
+
</div>
|
|
958
|
+
);
|
|
959
|
+
|
|
937
960
|
export interface FinanceDetailsModalProps {
|
|
938
961
|
/** open flag (controlled by the page). */
|
|
939
962
|
open: boolean;
|
|
@@ -971,6 +994,25 @@ export interface FinanceDetailsModalProps {
|
|
|
971
994
|
onTermChange?: (term: number) => void;
|
|
972
995
|
onAnnualMileageChange?: (km: number) => void;
|
|
973
996
|
onClose: () => void;
|
|
997
|
+
/** mobile only — start with the Finanzierungs-/Leasingdetails breakdown already expanded
|
|
998
|
+
* (opened directly via that link, skipping the "Rate anpassen" collapsed default view). */
|
|
999
|
+
initialShowBreakdown?: boolean;
|
|
1000
|
+
/** mobile only — price-rating label, drives the eval pill/bar (same as the collapsed card). */
|
|
1001
|
+
ratingLabel?: string;
|
|
1002
|
+
/** mobile only — mode tabs inside the sheet (mirrors the collapsed card's tabs). */
|
|
1003
|
+
onModeChange?: (mode: FinanceMode) => void;
|
|
1004
|
+
/** mobile only — primary "Rate anfragen"/"Angebot anfragen" CTA. */
|
|
1005
|
+
onSecure?: () => void;
|
|
1006
|
+
/** mobile only — secondary call CTA. */
|
|
1007
|
+
onCall?: () => void;
|
|
1008
|
+
/** mobile only — "Mit Schlussrate" toggle (financing only; leasing has no balloon concept). */
|
|
1009
|
+
withFinalInstallment?: boolean;
|
|
1010
|
+
onToggleFinalInstallment?: (checked: boolean) => void;
|
|
1011
|
+
/** mobile only — "Rundum-Sorglos-Paket" toggle (all modes; forced-on + disabled for cash). */
|
|
1012
|
+
withAddServices?: boolean;
|
|
1013
|
+
addServicesCost?: number;
|
|
1014
|
+
addServicesDisabled?: boolean;
|
|
1015
|
+
onToggleAddServices?: (checked: boolean) => void;
|
|
974
1016
|
/** i18n function (drilled from the page). */
|
|
975
1017
|
t: TranslationFunction;
|
|
976
1018
|
}
|
|
@@ -1008,11 +1050,41 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
|
|
|
1008
1050
|
onTermChange,
|
|
1009
1051
|
onAnnualMileageChange,
|
|
1010
1052
|
onClose,
|
|
1053
|
+
initialShowBreakdown,
|
|
1054
|
+
ratingLabel,
|
|
1055
|
+
onModeChange,
|
|
1056
|
+
onSecure,
|
|
1057
|
+
onCall,
|
|
1058
|
+
withFinalInstallment,
|
|
1059
|
+
onToggleFinalInstallment,
|
|
1060
|
+
withAddServices,
|
|
1061
|
+
addServicesCost,
|
|
1062
|
+
addServicesDisabled,
|
|
1063
|
+
onToggleAddServices,
|
|
1011
1064
|
t,
|
|
1012
1065
|
}) => {
|
|
1066
|
+
// mobile-only: the Finanzierungs-/Leasingdetails breakdown starts collapsed (opened via "Rate
|
|
1067
|
+
// anpassen") or expanded (opened directly via the details link) — reset on every open, since
|
|
1068
|
+
// this component stays mounted (the page always renders it, gated by `open`, not unmounted).
|
|
1069
|
+
const [showBreakdown, setShowBreakdown] = React.useState(!!initialShowBreakdown);
|
|
1070
|
+
React.useEffect(() => {
|
|
1071
|
+
if (open) setShowBreakdown(!!initialShowBreakdown);
|
|
1072
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1073
|
+
}, [open, initialShowBreakdown]);
|
|
1074
|
+
|
|
1013
1075
|
if (!open) return null;
|
|
1014
1076
|
const isLease = mode === 'leasing';
|
|
1077
|
+
const isCash = mode === 'cash';
|
|
1015
1078
|
const title = isLease ? t('autobahn:modal.leasingDetails') : t('autobahn:modal.financingDetails');
|
|
1079
|
+
const mobileTitle = isCash
|
|
1080
|
+
? t('autobahn:modal.sheetTitleCash')
|
|
1081
|
+
: isLease
|
|
1082
|
+
? t('autobahn:modal.sheetTitleLeasing')
|
|
1083
|
+
: t('autobahn:modal.sheetTitleFinancing');
|
|
1084
|
+
// price-evaluation tier — identical derivation to the collapsed card (FinanceCalculator).
|
|
1085
|
+
const tierIdx = TIER_LABELS.indexOf(ratingLabel as (typeof TIER_LABELS)[number]);
|
|
1086
|
+
const evalTier = tierIdx >= 0 ? tierIdx : 1;
|
|
1087
|
+
const evalTierLabel = t('autobahn:tier.' + TIER_KEYS[evalTier]);
|
|
1016
1088
|
// adjustable only for fin/leasing, and only when the page supplies the change handlers + bounds.
|
|
1017
1089
|
const adjustable = mode !== 'cash' && !!onDownChange && downMin != null && downMax != null && termMin != null && termMax != null;
|
|
1018
1090
|
// discrete term chip values (supplied periods, else derived from the bounds).
|
|
@@ -1041,89 +1113,274 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
|
|
|
1041
1113
|
[isLease ? t('autobahn:modal.leasingRate') : t('autobahn:modal.monthlyRate'), t('autobahn:modal.ratePerMonth', { amount: eur(monthly) })],
|
|
1042
1114
|
...(grossAmount != null && grossAmount > 0 ? ([[isLease ? t('autobahn:modal.totalAmount') : t('autobahn:modal.totalRepayment'), `${eur(grossAmount)} €`]] as Array<[string, string]>) : []),
|
|
1043
1115
|
];
|
|
1116
|
+
const breakdownRows = rows.map(([l, v], i) => (
|
|
1117
|
+
<div
|
|
1118
|
+
key={l}
|
|
1119
|
+
style={{
|
|
1120
|
+
display: 'flex',
|
|
1121
|
+
justifyContent: 'space-between',
|
|
1122
|
+
gap: 12,
|
|
1123
|
+
padding: '9px 0',
|
|
1124
|
+
borderBottom: i < rows.length - 1 ? '1px solid var(--md-outline-variant)' : 0,
|
|
1125
|
+
}}
|
|
1126
|
+
>
|
|
1127
|
+
<span className="md-label-sm">{l}</span>
|
|
1128
|
+
<span className="num" style={{ fontSize: 13.5, fontWeight: 600 }}>
|
|
1129
|
+
{v}
|
|
1130
|
+
</span>
|
|
1131
|
+
</div>
|
|
1132
|
+
));
|
|
1133
|
+
|
|
1044
1134
|
return (
|
|
1045
1135
|
<div className="fin-modal-scrim" onClick={onClose}>
|
|
1046
1136
|
<div className="fin-modal" role="dialog" aria-modal="true" aria-label={title} onClick={(e) => e.stopPropagation()}>
|
|
1047
1137
|
<div className="fin-modal-head">
|
|
1048
|
-
<h2 className="md-headline-sm" style={{ margin: 0 }}>
|
|
1138
|
+
<h2 className="md-headline-sm hide-mobile" style={{ margin: 0 }}>
|
|
1049
1139
|
{title}
|
|
1050
1140
|
</h2>
|
|
1141
|
+
<h2 className="md-headline-sm show-mobile" style={{ margin: 0 }}>
|
|
1142
|
+
{mobileTitle}
|
|
1143
|
+
</h2>
|
|
1051
1144
|
<button className="icon-btn" onClick={onClose} aria-label={t('autobahn:modal.close')}>
|
|
1052
1145
|
<Icon name="x" size={20} />
|
|
1053
1146
|
</button>
|
|
1054
1147
|
</div>
|
|
1055
1148
|
<div className="fin-modal-body">
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
<span className="md-title-sm">{t('autobahn:finance.downPaymentSliderLabel')}</span>
|
|
1068
|
-
<span className="num" style={{ fontWeight: 700 }}>
|
|
1069
|
-
{eur(down)} €
|
|
1070
|
-
</span>
|
|
1071
|
-
</div>
|
|
1072
|
-
<Slider
|
|
1073
|
-
min={downMin as number}
|
|
1074
|
-
max={downMax as number}
|
|
1075
|
-
step={downStep}
|
|
1076
|
-
value={down}
|
|
1077
|
-
onChange={onDownChange as (n: number) => void}
|
|
1078
|
-
fmt={(v) => `${eur(Number(v))} €`}
|
|
1079
|
-
/>
|
|
1080
|
-
</div>
|
|
1081
|
-
{isLease && annualMileage != null && onAnnualMileageChange && (
|
|
1149
|
+
{/* ============================== DESKTOP (≥601px) — unchanged ============================== */}
|
|
1150
|
+
<div className="hide-mobile">
|
|
1151
|
+
<div style={{ display: 'flex', alignItems: 'baseline', gap: 6, marginBottom: 14 }}>
|
|
1152
|
+
<span className="num" style={{ fontSize: 34, fontWeight: 800, color: 'var(--md-primary)', letterSpacing: '-1px' }}>
|
|
1153
|
+
{eur(monthly)} €
|
|
1154
|
+
</span>
|
|
1155
|
+
<span className="md-label-sm">{t('autobahn:finance.perMonth')}</span>
|
|
1156
|
+
</div>
|
|
1157
|
+
{/* adjustment sliders — change a value and every figure below recomputes live (via the page) */}
|
|
1158
|
+
{adjustable && (
|
|
1159
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, marginBottom: 18 }}>
|
|
1082
1160
|
<div>
|
|
1083
1161
|
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
|
|
1084
|
-
<span className="md-title-sm">{t('autobahn:finance.
|
|
1162
|
+
<span className="md-title-sm">{t('autobahn:finance.downPaymentSliderLabel')}</span>
|
|
1085
1163
|
<span className="num" style={{ fontWeight: 700 }}>
|
|
1086
|
-
{eur(
|
|
1164
|
+
{eur(down)} €
|
|
1087
1165
|
</span>
|
|
1088
1166
|
</div>
|
|
1089
1167
|
<Slider
|
|
1090
|
-
min={
|
|
1091
|
-
max={
|
|
1092
|
-
step={
|
|
1093
|
-
value={
|
|
1094
|
-
onChange={
|
|
1095
|
-
fmt={(v) => `${eur(Number(v))}
|
|
1168
|
+
min={downMin as number}
|
|
1169
|
+
max={downMax as number}
|
|
1170
|
+
step={downStep}
|
|
1171
|
+
value={down}
|
|
1172
|
+
onChange={onDownChange as (n: number) => void}
|
|
1173
|
+
fmt={(v) => `${eur(Number(v))} €`}
|
|
1096
1174
|
/>
|
|
1097
1175
|
</div>
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1176
|
+
{isLease && annualMileage != null && onAnnualMileageChange && (
|
|
1177
|
+
<div>
|
|
1178
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
|
|
1179
|
+
<span className="md-title-sm">{t('autobahn:finance.annualMileageLabel')}</span>
|
|
1180
|
+
<span className="num" style={{ fontWeight: 700 }}>
|
|
1181
|
+
{eur(annualMileage)} km
|
|
1182
|
+
</span>
|
|
1183
|
+
</div>
|
|
1184
|
+
<Slider
|
|
1185
|
+
min={annualMileageMin}
|
|
1186
|
+
max={annualMileageMax}
|
|
1187
|
+
step={annualMileageStep}
|
|
1188
|
+
value={annualMileage}
|
|
1189
|
+
onChange={onAnnualMileageChange}
|
|
1190
|
+
fmt={(v) => `${eur(Number(v))} km`}
|
|
1191
|
+
/>
|
|
1192
|
+
</div>
|
|
1193
|
+
)}
|
|
1194
|
+
<div>
|
|
1195
|
+
<div className="md-title-sm" style={{ marginBottom: 8 }}>
|
|
1196
|
+
{t('autobahn:finance.termSelection')}
|
|
1197
|
+
</div>
|
|
1198
|
+
<TermChips options={termVals} value={term} onChange={onTermChange as (n: number) => void} t={t} />
|
|
1102
1199
|
</div>
|
|
1103
|
-
|
|
1200
|
+
</div>
|
|
1201
|
+
)}
|
|
1202
|
+
{breakdownRows}
|
|
1203
|
+
<p className="md-label-sm" style={{ marginTop: 16, lineHeight: 1.55 }}>
|
|
1204
|
+
{isLease ? t('autobahn:modal.legalLeasing') : t('autobahn:modal.legalFinancing')}
|
|
1205
|
+
</p>
|
|
1206
|
+
</div>
|
|
1207
|
+
|
|
1208
|
+
{/* ============================== MOBILE (≤600px) — full "Rate anpassen" sheet ============================== */}
|
|
1209
|
+
<div className="show-mobile" style={{ flexDirection: 'column', gap: 18 }}>
|
|
1210
|
+
{onModeChange && (
|
|
1211
|
+
<div className="fin-tabs" role="tablist">
|
|
1212
|
+
{(
|
|
1213
|
+
[
|
|
1214
|
+
['fin', 'autobahn:finance.mode.financing'],
|
|
1215
|
+
['leasing', 'autobahn:finance.mode.leasing'],
|
|
1216
|
+
['cash', 'autobahn:finance.mode.cash'],
|
|
1217
|
+
] as Array<[FinanceMode, string]>
|
|
1218
|
+
).map(([k, labelKey]) => (
|
|
1219
|
+
<button
|
|
1220
|
+
key={k}
|
|
1221
|
+
type="button"
|
|
1222
|
+
role="tab"
|
|
1223
|
+
aria-selected={mode === k}
|
|
1224
|
+
className={'fin-tab' + (mode === k ? ' on' : '')}
|
|
1225
|
+
onClick={() => onModeChange(k)}
|
|
1226
|
+
>
|
|
1227
|
+
{t(labelKey)}
|
|
1228
|
+
</button>
|
|
1229
|
+
))}
|
|
1230
|
+
</div>
|
|
1231
|
+
)}
|
|
1232
|
+
|
|
1233
|
+
<div className="fin-price-grid">
|
|
1234
|
+
<div className="fin-rate-main">
|
|
1235
|
+
<div style={{ display: 'flex', alignItems: 'baseline', gap: 5 }}>
|
|
1236
|
+
<span className="num" style={{ fontSize: 34, fontWeight: 800, color: 'var(--md-primary)', letterSpacing: '-1px' }}>
|
|
1237
|
+
{isCash ? eur(price) : eur(Math.round(monthly))}
|
|
1238
|
+
</span>
|
|
1239
|
+
<span className="md-body-sm" style={{ fontWeight: 600 }}>
|
|
1240
|
+
{isCash ? '€' : `€ ${t('autobahn:finance.perMonth')}`}
|
|
1241
|
+
</span>
|
|
1242
|
+
</div>
|
|
1243
|
+
</div>
|
|
1244
|
+
<div>
|
|
1245
|
+
{ratingLabel && (
|
|
1246
|
+
<span
|
|
1247
|
+
className="fin-eval-pill"
|
|
1248
|
+
style={{ color: TIER_COLORS[evalTier], background: `color-mix(in srgb, ${TIER_COLORS[evalTier]} 16%, transparent)` }}
|
|
1249
|
+
>
|
|
1250
|
+
<Icon
|
|
1251
|
+
name={evalTier <= 1 ? 'check' : evalTier === 2 ? 'minus' : 'trending-up'}
|
|
1252
|
+
size={14}
|
|
1253
|
+
stroke={2.6}
|
|
1254
|
+
color={TIER_COLORS[evalTier]}
|
|
1255
|
+
/>
|
|
1256
|
+
{evalTierLabel}
|
|
1257
|
+
</span>
|
|
1258
|
+
)}
|
|
1259
|
+
</div>
|
|
1260
|
+
<div />
|
|
1261
|
+
<div>
|
|
1262
|
+
{ratingLabel && (
|
|
1263
|
+
<div className="fin-eval-bar">
|
|
1264
|
+
<div className="fin-eval-thumb" style={{ left: `${(evalTier + 0.5) * 20}%`, borderColor: TIER_COLORS[evalTier] }} />
|
|
1265
|
+
</div>
|
|
1266
|
+
)}
|
|
1104
1267
|
</div>
|
|
1105
1268
|
</div>
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1269
|
+
|
|
1270
|
+
{isCash ? (
|
|
1271
|
+
<>
|
|
1272
|
+
<div>
|
|
1273
|
+
<CalcRow label={t('autobahn:finance.paymentLabel')}>{t('autobahn:finance.paymentValue')}</CalcRow>
|
|
1274
|
+
<CalcRow label={t('autobahn:finance.warrantyLabel')}>{t('autobahn:finance.warrantyValue')}</CalcRow>
|
|
1275
|
+
<CalcRow label={t('autobahn:finance.deliveryLabel')} last>
|
|
1276
|
+
{t('autobahn:finance.deliveryValue')}
|
|
1277
|
+
</CalcRow>
|
|
1278
|
+
</div>
|
|
1279
|
+
{addServicesCost != null && (
|
|
1280
|
+
<ToggleRow
|
|
1281
|
+
label={t('autobahn:finance.addServicesToggle')}
|
|
1282
|
+
price={t('autobahn:finance.addServicesOnce', { value: eur(addServicesCost) })}
|
|
1283
|
+
checked={!!withAddServices}
|
|
1284
|
+
disabled={addServicesDisabled}
|
|
1285
|
+
onChange={(checked) => onToggleAddServices && onToggleAddServices(checked)}
|
|
1286
|
+
/>
|
|
1287
|
+
)}
|
|
1288
|
+
<div style={{ display: 'flex', gap: 10 }}>
|
|
1289
|
+
<Button variant="filled" size="lg" icon="shield-check" style={{ flex: 1 }} onClick={onSecure}>
|
|
1290
|
+
{t('autobahn:finance.secureOffer')}
|
|
1291
|
+
</Button>
|
|
1292
|
+
<Button variant="outlined" size="lg" icon="phone" style={{ width: 52, flex: 'none' }} onClick={onCall} />
|
|
1293
|
+
</div>
|
|
1294
|
+
</>
|
|
1295
|
+
) : (
|
|
1296
|
+
<>
|
|
1297
|
+
{adjustable && (
|
|
1298
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
|
1299
|
+
<div>
|
|
1300
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
|
|
1301
|
+
<span className="md-title-sm">{t('autobahn:finance.downPaymentSliderLabel')}</span>
|
|
1302
|
+
<span className="num" style={{ fontWeight: 700 }}>
|
|
1303
|
+
{eur(down)} €
|
|
1304
|
+
</span>
|
|
1305
|
+
</div>
|
|
1306
|
+
<Slider
|
|
1307
|
+
min={downMin as number}
|
|
1308
|
+
max={downMax as number}
|
|
1309
|
+
step={downStep}
|
|
1310
|
+
value={down}
|
|
1311
|
+
onChange={onDownChange as (n: number) => void}
|
|
1312
|
+
fmt={(v) => `${eur(Number(v))} €`}
|
|
1313
|
+
/>
|
|
1314
|
+
</div>
|
|
1315
|
+
{isLease && annualMileage != null && onAnnualMileageChange && (
|
|
1316
|
+
<div>
|
|
1317
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
|
|
1318
|
+
<span className="md-title-sm">{t('autobahn:finance.annualMileageLabel')}</span>
|
|
1319
|
+
<span className="num" style={{ fontWeight: 700 }}>
|
|
1320
|
+
{eur(annualMileage)} km
|
|
1321
|
+
</span>
|
|
1322
|
+
</div>
|
|
1323
|
+
<Slider
|
|
1324
|
+
min={annualMileageMin}
|
|
1325
|
+
max={annualMileageMax}
|
|
1326
|
+
step={annualMileageStep}
|
|
1327
|
+
value={annualMileage}
|
|
1328
|
+
onChange={onAnnualMileageChange}
|
|
1329
|
+
fmt={(v) => `${eur(Number(v))} km`}
|
|
1330
|
+
/>
|
|
1331
|
+
</div>
|
|
1332
|
+
)}
|
|
1333
|
+
<div>
|
|
1334
|
+
<div className="md-title-sm" style={{ marginBottom: 8 }}>
|
|
1335
|
+
{t('autobahn:finance.termSelection')}
|
|
1336
|
+
</div>
|
|
1337
|
+
<TermChips options={termVals} value={term} onChange={onTermChange as (n: number) => void} t={t} />
|
|
1338
|
+
</div>
|
|
1339
|
+
</div>
|
|
1340
|
+
)}
|
|
1341
|
+
|
|
1342
|
+
{!isLease && onToggleFinalInstallment && (
|
|
1343
|
+
<ToggleRow
|
|
1344
|
+
label={t('autobahn:finance.finalInstallmentToggle')}
|
|
1345
|
+
checked={!!withFinalInstallment}
|
|
1346
|
+
onChange={onToggleFinalInstallment}
|
|
1347
|
+
/>
|
|
1348
|
+
)}
|
|
1349
|
+
{addServicesCost != null && onToggleAddServices && (
|
|
1350
|
+
<ToggleRow
|
|
1351
|
+
label={t('autobahn:finance.addServicesToggle')}
|
|
1352
|
+
price={t('autobahn:finance.addServicesPerMonth', { value: eur(addServicesCost) })}
|
|
1353
|
+
checked={!!withAddServices}
|
|
1354
|
+
disabled={addServicesDisabled}
|
|
1355
|
+
onChange={onToggleAddServices}
|
|
1356
|
+
/>
|
|
1357
|
+
)}
|
|
1358
|
+
|
|
1359
|
+
<div style={{ display: 'flex', gap: 10 }}>
|
|
1360
|
+
<Button variant="filled" size="lg" icon="shield-check" style={{ flex: 1 }} onClick={onSecure}>
|
|
1361
|
+
{t('autobahn:finance.requestRate')}
|
|
1362
|
+
</Button>
|
|
1363
|
+
<Button variant="outlined" size="lg" icon="phone" style={{ width: 52, flex: 'none' }} onClick={onCall} />
|
|
1364
|
+
</div>
|
|
1365
|
+
|
|
1366
|
+
<button type="button" onClick={() => setShowBreakdown((v) => !v)} style={expanderBtnStyle}>
|
|
1367
|
+
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 7 }}>
|
|
1368
|
+
<Icon name="info" size={16} color="var(--md-primary)" />
|
|
1369
|
+
{isLease ? t('autobahn:modal.leasingDetails') : t('autobahn:modal.financingDetails')}
|
|
1370
|
+
</span>
|
|
1371
|
+
<Icon name={showBreakdown ? 'chevron-up' : 'chevron-down'} size={18} color="var(--md-primary)" />
|
|
1372
|
+
</button>
|
|
1373
|
+
{showBreakdown && (
|
|
1374
|
+
<div>
|
|
1375
|
+
{breakdownRows}
|
|
1376
|
+
<p className="md-label-sm" style={{ marginTop: 16, lineHeight: 1.55 }}>
|
|
1377
|
+
{isLease ? t('autobahn:modal.legalLeasing') : t('autobahn:modal.legalFinancing')}
|
|
1378
|
+
</p>
|
|
1379
|
+
</div>
|
|
1380
|
+
)}
|
|
1381
|
+
</>
|
|
1382
|
+
)}
|
|
1383
|
+
</div>
|
|
1127
1384
|
</div>
|
|
1128
1385
|
</div>
|
|
1129
1386
|
</div>
|