@bytebrand/fe-ui-core-autobahn 1.0.152 → 1.0.153
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/chrome/Footer.tsx +16 -3
- package/compat/MdField/MdField.tsx +9 -3
- package/package.json +1 -1
- package/theme/md3.css +541 -60
- package/ui/FormCheckbox.tsx +15 -2
- package/ui/Icon.tsx +4 -0
- package/vdp/VdpModules.tsx +15 -3
package/chrome/Footer.tsx
CHANGED
|
@@ -86,6 +86,14 @@ export interface FooterProps {
|
|
|
86
86
|
* (VehicleDetailedNextPage), never site-wide, since every other page shows only footnotes 1-4.
|
|
87
87
|
*/
|
|
88
88
|
showCostFootnotes?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Hides the brand blurb + "stay in contact"/social + 3 link columns block (the
|
|
91
|
+
* `.footer-grid`), leaving just the legal disclaimer and copyright/"Nach Oben" bar. Opt-in,
|
|
92
|
+
* off by default — every other caller keeps the full footer. Only CheckoutThankYouPage.tsx
|
|
93
|
+
* (checkout step 6 "Danke") uses this today: that page's own Figma reference (64637:37033)
|
|
94
|
+
* has just the compact legal/copyright strip, not the full site footer.
|
|
95
|
+
*/
|
|
96
|
+
hideBrandColumns?: boolean;
|
|
89
97
|
}
|
|
90
98
|
|
|
91
99
|
/**
|
|
@@ -93,7 +101,7 @@ export interface FooterProps {
|
|
|
93
101
|
* disclaimer block, and a copyright + "Nach Oben" bar. Pure/presentational MD3 chrome — renders
|
|
94
102
|
* correctly only inside a `<div className="md3">` subtree (scoped theme at source/theme/autobahn).
|
|
95
103
|
*/
|
|
96
|
-
const Footer: React.FunctionComponent<FooterProps> = ({ t, showCostFootnotes }) => {
|
|
104
|
+
const Footer: React.FunctionComponent<FooterProps> = ({ t, showCostFootnotes, hideBrandColumns }) => {
|
|
97
105
|
const scrollTop = (): void => window.scroll({ top: 0, left: 0, behavior: 'smooth' });
|
|
98
106
|
const now = new Date();
|
|
99
107
|
const year = now.getFullYear();
|
|
@@ -104,6 +112,7 @@ const Footer: React.FunctionComponent<FooterProps> = ({ t, showCostFootnotes })
|
|
|
104
112
|
|
|
105
113
|
return (
|
|
106
114
|
<footer style={{ background: 'var(--md-sc-low)', borderTop: '1px solid var(--md-outline-variant)' }}>
|
|
115
|
+
{!hideBrandColumns ? (
|
|
107
116
|
<div className="container footer-grid" style={{ padding: '40px 24px 24px' }}>
|
|
108
117
|
<div>
|
|
109
118
|
<img src={logoUrl} height="30" alt="auto.de" />
|
|
@@ -169,10 +178,14 @@ const Footer: React.FunctionComponent<FooterProps> = ({ t, showCostFootnotes })
|
|
|
169
178
|
</div>
|
|
170
179
|
))}
|
|
171
180
|
</div>
|
|
181
|
+
) : null}
|
|
172
182
|
|
|
173
183
|
{/* Legally required disclaimers (PAngV §17a financing example w/ live APR/nominal rate +
|
|
174
|
-
term, DAT CO₂-Leitfaden, MwSt, Streichpreis, image notice). Same text as the legacy footer.
|
|
175
|
-
|
|
184
|
+
term, DAT CO₂-Leitfaden, MwSt, Streichpreis, image notice). Same text as the legacy footer.
|
|
185
|
+
The top border separates this from .footer-grid above — with hideBrandColumns that
|
|
186
|
+
block is gone, so the border would just double up against whatever's already directly
|
|
187
|
+
above the footer on the page (CheckoutThankYouPage.tsx's own header border). */}
|
|
188
|
+
<div style={hideBrandColumns ? undefined : { borderTop: '1px solid var(--md-outline-variant)' }}>
|
|
176
189
|
<div className="container" style={{ padding: '18px 24px' }}>
|
|
177
190
|
<p
|
|
178
191
|
className="md-label-sm"
|
|
@@ -37,11 +37,17 @@ export interface MdFieldProps {
|
|
|
37
37
|
* Same contract/markup as FormField's own showValid (.mfield--valid/.mfield-ok, md3.css). */
|
|
38
38
|
showValid?: boolean;
|
|
39
39
|
onChange: (value: string, name?: string, event?: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
|
|
40
|
+
/** fires after the field's own internal blur handling (floating-label state). Callers use
|
|
41
|
+
* this to trigger `field.validate()` (mobx-react-form) on blur — this component only owns
|
|
42
|
+
* the `error` prop's display, not when validation actually runs, so without a caller wiring
|
|
43
|
+
* this in, `field.error` never populates from blur and a required-but-empty field never
|
|
44
|
+
* shows its error text/red border until some other validate() call happens to run. */
|
|
45
|
+
onBlur?: () => void;
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
const MdField: React.FunctionComponent<MdFieldProps> = ({
|
|
43
49
|
className, error, sx, required, disabled, multiline, autoComplete,
|
|
44
|
-
value, type, name, rows, label, placeholder, floatingLabel, maxLength, showValid, onChange
|
|
50
|
+
value, type, name, rows, label, placeholder, floatingLabel, maxLength, showValid, onChange, onBlur
|
|
45
51
|
}) => {
|
|
46
52
|
const [reveal, setReveal] = React.useState(false);
|
|
47
53
|
const [focused, setFocused] = React.useState(false);
|
|
@@ -113,7 +119,7 @@ const MdField: React.FunctionComponent<MdFieldProps> = ({
|
|
|
113
119
|
value={value == null ? '' : value}
|
|
114
120
|
onChange={handleChange}
|
|
115
121
|
onFocus={() => setFocused(true)}
|
|
116
|
-
onBlur={() => setFocused(false)}
|
|
122
|
+
onBlur={() => { setFocused(false); onBlur?.(); }}
|
|
117
123
|
disabled={disabled}
|
|
118
124
|
rows={rows || 3}
|
|
119
125
|
maxLength={maxLength}
|
|
@@ -132,7 +138,7 @@ const MdField: React.FunctionComponent<MdFieldProps> = ({
|
|
|
132
138
|
value={value == null ? '' : value}
|
|
133
139
|
onChange={handleChange}
|
|
134
140
|
onFocus={() => setFocused(true)}
|
|
135
|
-
onBlur={() => setFocused(false)}
|
|
141
|
+
onBlur={() => { setFocused(false); onBlur?.(); }}
|
|
136
142
|
disabled={disabled}
|
|
137
143
|
maxLength={maxLength}
|
|
138
144
|
placeholder={effectivePlaceholder}
|
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.153",
|
|
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",
|
package/theme/md3.css
CHANGED
|
@@ -2290,6 +2290,14 @@ line-height: 31px;
|
|
|
2290
2290
|
font-size: 13px;
|
|
2291
2291
|
line-height: 15px;
|
|
2292
2292
|
color: #44464F;
|
|
2293
|
+
/* a long .co-error message (e.g. "Bitte wählen Sie eine der Optionen für Garantiepaket und
|
|
2294
|
+
Fahrzeugzulassung.") sharing this row (≥601px: same row via margin-right:auto/flex-end,
|
|
2295
|
+
see .co-next's own media queries) squeezed this plain text span for space same as any
|
|
2296
|
+
other flex child — with nothing stopping it from wrapping, "Schritt 4 von 5" split onto 2
|
|
2297
|
+
lines instead of the long error text (the one thing actually meant to wrap) absorbing the
|
|
2298
|
+
squeeze. nowrap + flex-shrink:0 keeps this at its natural one-line width unconditionally. */
|
|
2299
|
+
white-space: nowrap;
|
|
2300
|
+
flex-shrink: 0;
|
|
2293
2301
|
}
|
|
2294
2302
|
/* "Weiter" — its own fixed-size look, not the shared .mbtn--lg sizing (icon already dropped
|
|
2295
2303
|
in the JSX, not just hidden here). Text styling lands on the button itself: Button.tsx
|
|
@@ -2356,6 +2364,22 @@ line-height: 31px;
|
|
|
2356
2364
|
background: #F4F5FA;
|
|
2357
2365
|
border-top: 0;
|
|
2358
2366
|
}
|
|
2367
|
+
/* DOM order is .co-error (only rendered when a step's validation fails), .co-back-btn,
|
|
2368
|
+
.co-step-count, Weiter. `order` visually moves Zurück before the error text (its DOM
|
|
2369
|
+
position doesn't change, so the back button's click handler/focus order are unaffected).
|
|
2370
|
+
Zurück's base margin-right:auto (line ~2265) is what pushes Schritt/Weiter to the row's
|
|
2371
|
+
right edge in the common case (no error, .co-back-btn is the only thing before the gap) —
|
|
2372
|
+
kept as-is here. Only when .co-error IS present does that job need to hand off to it
|
|
2373
|
+
instead (so the error text sits right after Zurück rather than the auto-margin gap opening
|
|
2374
|
+
up BEFORE the error, between it and Zurück): the `.co-error + .co-back-btn` sibling
|
|
2375
|
+
selector matches only that case (error is Zurück's immediately preceding DOM sibling) and
|
|
2376
|
+
zeroes Zurück's margin, leaving .co-error's own margin-right:auto below as the sole push. */
|
|
2377
|
+
.md3 .co-next .co-back-btn {
|
|
2378
|
+
order: -1;
|
|
2379
|
+
}
|
|
2380
|
+
.md3 .co-next .co-error + .co-back-btn {
|
|
2381
|
+
margin-right: 0;
|
|
2382
|
+
}
|
|
2359
2383
|
.md3 .co-next .co-error {
|
|
2360
2384
|
margin-right: auto;
|
|
2361
2385
|
}
|
|
@@ -2394,11 +2418,26 @@ line-height: 31px;
|
|
|
2394
2418
|
.md3 .co-form.co-form--payment-details {
|
|
2395
2419
|
border-bottom: 0;
|
|
2396
2420
|
}
|
|
2421
|
+
/* order-overview step only — same reasoning, see OrderOverviewNext.tsx's "co-form oo-page". */
|
|
2422
|
+
.md3 .co-form.oo-page {
|
|
2423
|
+
border-bottom: 0;
|
|
2424
|
+
}
|
|
2397
2425
|
.md3 .co-step-title {
|
|
2398
2426
|
font: 700 22px var(--md-font-display, var(--md-font));
|
|
2399
2427
|
margin: 0;
|
|
2400
2428
|
color: var(--md-on-surface);
|
|
2401
2429
|
}
|
|
2430
|
+
/* order-overview only ("Bestellübersicht") — desktop already shows this same title in
|
|
2431
|
+
CheckoutTopBarNext's own .co-topbar-heading (CheckoutChromeNext.tsx, same steps[activeStep].
|
|
2432
|
+
title source), so this becomes a duplicate there; kept for mobile, where that top bar is a
|
|
2433
|
+
compact single row with no room for it (CheckoutMobilePageTitle covers OTHER steps' mobile
|
|
2434
|
+
copy, but order-overview renders its own here instead). Same 600px mobile/desktop cutoff as
|
|
2435
|
+
.co-back-btn etc. elsewhere on this page. */
|
|
2436
|
+
@media (min-width: 601px) {
|
|
2437
|
+
.md3 .oo-page > .co-step-title {
|
|
2438
|
+
display: none;
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2402
2441
|
.md3 .co-step-sub {
|
|
2403
2442
|
color: var(--md-on-surface-variant);
|
|
2404
2443
|
font-size: 14px;
|
|
@@ -3249,8 +3288,12 @@ line-height: 31px;
|
|
|
3249
3288
|
.md3 .co-pay-bene b {
|
|
3250
3289
|
display: block;
|
|
3251
3290
|
margin-bottom: 2px;
|
|
3252
|
-
font-
|
|
3253
|
-
|
|
3291
|
+
font-family: 'Arimo';
|
|
3292
|
+
font-style: normal;
|
|
3293
|
+
font-weight: 700;
|
|
3294
|
+
font-size: 15px;
|
|
3295
|
+
line-height: 140%;
|
|
3296
|
+
color: #1A1B22;
|
|
3254
3297
|
}
|
|
3255
3298
|
.md3 .co-pay-bene-desc {
|
|
3256
3299
|
font-family: 'Arimo';
|
|
@@ -4474,10 +4517,17 @@ line-height: 31px;
|
|
|
4474
4517
|
}
|
|
4475
4518
|
/* "4. Lieferung & Services" (Figma node 63570:28320) — up to 3 cells in a row, divided by a 1px
|
|
4476
4519
|
rule; the rule is a border on the cell itself (:not(:first-child)) rather than a separate DOM
|
|
4477
|
-
node, so it stays correct no matter which 1-3 of the 3 cells are conditionally rendered.
|
|
4520
|
+
node, so it stays correct no matter which 1-3 of the 3 cells are conditionally rendered.
|
|
4521
|
+
align-items: stretch (not flex-start) — a border-left only ever reaches as far down as its own
|
|
4522
|
+
box, so with flex-start each divider stopped at its OWN cell's content height instead of the
|
|
4523
|
+
row's tallest cell (the 3-line-wrapping "9. Gewährleistung & Herstellergarantie" title makes
|
|
4524
|
+
that cell the tallest whenever it's shown), leaving the shorter cells' dividers visibly cut off
|
|
4525
|
+
partway down. Cells stretching to the row's full height doesn't shift their own icon+text
|
|
4526
|
+
(each cell keeps its own align-items: flex-start below), it only extends the empty space (and
|
|
4527
|
+
therefore the border) to match. */
|
|
4478
4528
|
.md3 .oo-ls-row {
|
|
4479
4529
|
display: flex;
|
|
4480
|
-
align-items:
|
|
4530
|
+
align-items: stretch;
|
|
4481
4531
|
}
|
|
4482
4532
|
.md3 .oo-ls-cell {
|
|
4483
4533
|
display: flex;
|
|
@@ -4801,11 +4851,6 @@ line-height: 31px;
|
|
|
4801
4851
|
color: var(--md-on-surface-variant);
|
|
4802
4852
|
cursor: pointer;
|
|
4803
4853
|
}
|
|
4804
|
-
.md3 .oo-underline {
|
|
4805
|
-
color: var(--md-primary);
|
|
4806
|
-
text-decoration: underline;
|
|
4807
|
-
cursor: pointer;
|
|
4808
|
-
}
|
|
4809
4854
|
/* "1. Fahrzeug & Verkäufer" card (Figma node 63570:28262) — own full-width .co-card, sitting
|
|
4810
4855
|
above .oo-grid (not a grid column), so no bottom divider/margin like the legacy .oo-car row
|
|
4811
4856
|
(that one used to sit above other rows sharing the same card). */
|
|
@@ -4948,16 +4993,94 @@ line-height: 31px;
|
|
|
4948
4993
|
font-size: 13px;
|
|
4949
4994
|
cursor: pointer;
|
|
4950
4995
|
}
|
|
4951
|
-
|
|
4952
|
-
|
|
4996
|
+
/* Figma 63570:28475 ("CK / Content — Rechtliche Hinweise") — text/underline color is the SAME
|
|
4997
|
+
muted gray as the body copy (var(--md-on-surface-variant) === Figma's var(--text/muted,
|
|
4998
|
+
#44464f) fallback exactly), not the design system's usual blue link color (.oo-underline,
|
|
4999
|
+
used elsewhere), so links here are styled with underline only. No border per explicit
|
|
5000
|
+
request. Sits below .oo-next (order-overview's own bottom bar) — a spot this exact node
|
|
5001
|
+
isn't shown at in Figma (its own component description has it above the buy button instead),
|
|
5002
|
+
so this margin-top is this page's own established gap rhythm, not a value read off the design. */
|
|
5003
|
+
.md3 .oo-legal-notice {
|
|
5004
|
+
margin-top: 16px;
|
|
5005
|
+
font-family: Arial;
|
|
4953
5006
|
font-size: 13px;
|
|
5007
|
+
line-height: 1.5;
|
|
4954
5008
|
color: var(--md-on-surface-variant);
|
|
4955
|
-
line-height: 1.6;
|
|
4956
5009
|
}
|
|
4957
|
-
.md3 .oo-
|
|
5010
|
+
.md3 .oo-legal-link {
|
|
5011
|
+
text-decoration: underline;
|
|
5012
|
+
color: inherit;
|
|
5013
|
+
cursor: pointer;
|
|
5014
|
+
}
|
|
5015
|
+
/* Order overview's own "Zurück / Schritt X von Y / Jetzt kaufen" action bar (Figma 63502:5801) —
|
|
5016
|
+
distinct classes from .co-next/.co-back-btn/.co-step-count (CheckoutChromeNext.tsx) rather than
|
|
5017
|
+
reusing them: that bar's CTA is "advance to next step" and returns null on the last step, this
|
|
5018
|
+
one's CTA is "buy now" (OfferRequestButtonWrapper) with its own, larger Figma-specified sizing. */
|
|
5019
|
+
.md3 .oo-next {
|
|
4958
5020
|
display: flex;
|
|
4959
|
-
|
|
4960
|
-
|
|
5021
|
+
align-items: center;
|
|
5022
|
+
gap: 16px;
|
|
5023
|
+
margin-top: 24px;
|
|
5024
|
+
padding-top: 18px;
|
|
5025
|
+
border-top: 1px solid #C5C6D2;
|
|
5026
|
+
}
|
|
5027
|
+
.md3 .oo-next-back {
|
|
5028
|
+
display: flex;
|
|
5029
|
+
align-items: center;
|
|
5030
|
+
justify-content: center;
|
|
5031
|
+
height: 48px;
|
|
5032
|
+
padding: 0 16px;
|
|
5033
|
+
margin-right: auto;
|
|
5034
|
+
border: 0;
|
|
5035
|
+
background: none;
|
|
5036
|
+
border-radius: 16px;
|
|
5037
|
+
font-family: 'Arial';
|
|
5038
|
+
font-style: normal;
|
|
5039
|
+
font-weight: 700;
|
|
5040
|
+
font-size: 15px;
|
|
5041
|
+
line-height: normal;
|
|
5042
|
+
color: #005CFF;
|
|
5043
|
+
cursor: pointer;
|
|
5044
|
+
}
|
|
5045
|
+
.md3 .oo-next-count {
|
|
5046
|
+
font-family: 'Arial';
|
|
5047
|
+
font-style: normal;
|
|
5048
|
+
font-weight: 400;
|
|
5049
|
+
font-size: 13px;
|
|
5050
|
+
line-height: normal;
|
|
5051
|
+
color: #44464F;
|
|
5052
|
+
white-space: nowrap;
|
|
5053
|
+
}
|
|
5054
|
+
.md3 .oo-buy-btn {
|
|
5055
|
+
display: flex;
|
|
5056
|
+
flex-direction: row;
|
|
5057
|
+
justify-content: center;
|
|
5058
|
+
align-items: center;
|
|
5059
|
+
padding: 0 16px;
|
|
5060
|
+
gap: 8px;
|
|
5061
|
+
width: 340px;
|
|
5062
|
+
height: 60px;
|
|
5063
|
+
background: #005CFF;
|
|
5064
|
+
border-radius: 16px;
|
|
5065
|
+
font-family: 'Arial';
|
|
5066
|
+
font-style: normal;
|
|
5067
|
+
font-weight: 700;
|
|
5068
|
+
font-size: 17px;
|
|
5069
|
+
line-height: normal;
|
|
5070
|
+
color: #FFFFFF;
|
|
5071
|
+
}
|
|
5072
|
+
@media (max-width: 700px) {
|
|
5073
|
+
.md3 .oo-next-count {
|
|
5074
|
+
display: none;
|
|
5075
|
+
}
|
|
5076
|
+
}
|
|
5077
|
+
@media (max-width: 600px) {
|
|
5078
|
+
.md3 .oo-next-back {
|
|
5079
|
+
display: none;
|
|
5080
|
+
}
|
|
5081
|
+
.md3 .oo-buy-btn {
|
|
5082
|
+
width: 100%;
|
|
5083
|
+
}
|
|
4961
5084
|
}
|
|
4962
5085
|
@media (max-width: 800px) {
|
|
4963
5086
|
.md3 .oo-grid {
|
|
@@ -5124,12 +5247,33 @@ line-height: 31px;
|
|
|
5124
5247
|
line instead of sitting visibly lower (more obvious once the label wraps to 2 lines). */
|
|
5125
5248
|
margin-top: 7px;
|
|
5126
5249
|
}
|
|
5127
|
-
/*
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
.md3 .oo-terms-block .mcheck-error-
|
|
5132
|
-
|
|
5250
|
+
/* Error state (Figma 64670:106293, "Jetzt kaufen" clicked without confirming both agreements) —
|
|
5251
|
+
unconfirmed checkbox gets a thicker red box, plus an icon+message row underneath, indented
|
|
5252
|
+
32px (20px box + 12px gap) to sit under the label rather than the box. errorIcon (FormCheckbox
|
|
5253
|
+
prop, opt-in) renders that icon; every other FormCheckbox caller sitewide is untouched. */
|
|
5254
|
+
.md3 .oo-terms-block .mcheck-group--error .mcheck-input:not(:checked) + .mcheck-box {
|
|
5255
|
+
border-width: 2px;
|
|
5256
|
+
}
|
|
5257
|
+
.md3 .oo-terms-block .mcheck-error-text--icon {
|
|
5258
|
+
display: flex;
|
|
5259
|
+
align-items: flex-start;
|
|
5260
|
+
gap: 6px;
|
|
5261
|
+
padding-left: 32px;
|
|
5262
|
+
margin-top: -2px;
|
|
5263
|
+
}
|
|
5264
|
+
.md3 .oo-terms-block .mcheck-error-icon {
|
|
5265
|
+
flex: none;
|
|
5266
|
+
margin-top: 1px;
|
|
5267
|
+
}
|
|
5268
|
+
.md3 .oo-terms-block .mcheck-error-msg {
|
|
5269
|
+
flex: 1 0 0;
|
|
5270
|
+
font-size: 13px;
|
|
5271
|
+
line-height: 1.35;
|
|
5272
|
+
}
|
|
5273
|
+
/* Card itself gets a red border once either checkbox is showing its error (OrderOverviewNext's
|
|
5274
|
+
termsHasError — same hasError+error double-check as FormCheckbox uses internally). */
|
|
5275
|
+
.md3 .oo-terms-card--error {
|
|
5276
|
+
border: 2px solid var(--md-error);
|
|
5133
5277
|
}
|
|
5134
5278
|
/* "7. Ergänzende Regelungen" dialogs (Verkürzung der Verjährungsfrist / Ausschluss der
|
|
5135
5279
|
Aktualisierung, Figma 64596:36289 desktop / 64596:36264 mobile) — rendered via the shared
|
|
@@ -5186,6 +5330,60 @@ line-height: 31px;
|
|
|
5186
5330
|
line-height: 1.5;
|
|
5187
5331
|
color: #0E2238;
|
|
5188
5332
|
}
|
|
5333
|
+
/* Legal-notice row's 3 dialogs (AGB von auto.de / AGB des Verkäufers / Widerrufsbelehrung —
|
|
5334
|
+
Figma 64604:48257 / 63583:6830 / 63583:6850) reuse the SAME oo-terms-modal chrome above, but
|
|
5335
|
+
these bodies are long reference documents rather than a single clause, so Figma specs them
|
|
5336
|
+
smaller/muted (text/muted #44464F) with bold text/strong (#1A1B22) section headings instead of
|
|
5337
|
+
.oo-terms-modal-text's bigger, darker single-paragraph style. oo-terms-modal-legal-text also
|
|
5338
|
+
carries the .oo-terms-modal-legal-html "AGB von auto.de"/"AGB des Verkäufers" bodies' shared
|
|
5339
|
+
base (dangerouslySetInnerHTML'd CMS content / seller.termCond plain text — both re-used
|
|
5340
|
+
verbatim from elsewhere in the app, not authored here, see OrderOverviewNext.tsx). */
|
|
5341
|
+
.md3 .oo-terms-modal-legal-text,
|
|
5342
|
+
.md3 .oo-terms-modal-legal-html {
|
|
5343
|
+
font-family: Arial;
|
|
5344
|
+
font-weight: 400;
|
|
5345
|
+
font-size: 13px;
|
|
5346
|
+
line-height: 1.48;
|
|
5347
|
+
color: #44464F;
|
|
5348
|
+
}
|
|
5349
|
+
.md3 .oo-terms-modal-legal-text {
|
|
5350
|
+
margin: 0;
|
|
5351
|
+
/* seller.termCond is plain text (Imprint.tsx renders it the same way) — preserves its literal
|
|
5352
|
+
line breaks instead of collapsing them. No-op for the revocation dialog's own JSON strings,
|
|
5353
|
+
which don't contain any. */
|
|
5354
|
+
white-space: pre-line;
|
|
5355
|
+
}
|
|
5356
|
+
.md3 .oo-terms-modal-legal-heading {
|
|
5357
|
+
margin: 0;
|
|
5358
|
+
font-family: Arial;
|
|
5359
|
+
font-weight: 700;
|
|
5360
|
+
font-size: 15px;
|
|
5361
|
+
line-height: 1.48;
|
|
5362
|
+
color: #1A1B22;
|
|
5363
|
+
}
|
|
5364
|
+
.md3 .oo-terms-modal-legal-body {
|
|
5365
|
+
display: flex;
|
|
5366
|
+
flex-direction: column;
|
|
5367
|
+
gap: 12px;
|
|
5368
|
+
}
|
|
5369
|
+
/* ourTermsAndConditions is a WordPress `content.rendered` HTML blob (CheckoutStore.ts) — its own
|
|
5370
|
+
<p> tags need their own spacing/heading treatment since it isn't discrete React children like
|
|
5371
|
+
oo-terms-modal-legal-body's blocks above. */
|
|
5372
|
+
.md3 .oo-terms-modal-legal-html p {
|
|
5373
|
+
margin: 0 0 12px;
|
|
5374
|
+
}
|
|
5375
|
+
.md3 .oo-terms-modal-legal-html p:last-child {
|
|
5376
|
+
margin-bottom: 0;
|
|
5377
|
+
}
|
|
5378
|
+
.md3 .oo-terms-modal-legal-html strong,
|
|
5379
|
+
.md3 .oo-terms-modal-legal-html b,
|
|
5380
|
+
.md3 .oo-terms-modal-legal-html h1,
|
|
5381
|
+
.md3 .oo-terms-modal-legal-html h2,
|
|
5382
|
+
.md3 .oo-terms-modal-legal-html h3 {
|
|
5383
|
+
font-weight: 700;
|
|
5384
|
+
font-size: 15px;
|
|
5385
|
+
color: #1A1B22;
|
|
5386
|
+
}
|
|
5189
5387
|
/* mobile bottom sheet (.mmodal's own @media max-width:560px already makes it full-width/
|
|
5190
5388
|
bottom-pinned/top-rounded) — Figma's mobile header additionally wants a 24px title (vs 22px
|
|
5191
5389
|
on desktop) and a decorative grip bar (Figma 64596:36264's handleRow), which this Modal
|
|
@@ -5256,15 +5454,16 @@ line-height: 31px;
|
|
|
5256
5454
|
.md3 .oo-promo-body .mfield:focus-within {
|
|
5257
5455
|
box-shadow: inset 0 0 0 1px var(--md-outline-variant);
|
|
5258
5456
|
}
|
|
5259
|
-
/*
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5457
|
+
/* Cards 9 "Gewährleistung" / 10 "Herstellergarantie" (ADV-295; Figma 64669:106647 /
|
|
5458
|
+
64669:108591) — share one .oo-warranty-card/.oo-warranty-body treatment (same card chrome,
|
|
5459
|
+
flat single-level collapse, same link/image styling); which fields each renders is decided in
|
|
5460
|
+
the JSX, not here. Both graphic blocks (the EU statutory-warranty notice, the GARAN
|
|
5461
|
+
certificate) are static images, not hand-coded — see the long comment above WARRANTY_NOTICE_IMG
|
|
5462
|
+
in OrderOverviewNext.tsx for why (Figma ships them fully vector-flattened). */
|
|
5265
5463
|
/* same fix as .oo-promo-card above — .co-card-title's shared margin-bottom:16px leaves a
|
|
5266
|
-
stray gap under the title when the card is collapsed
|
|
5267
|
-
|
|
5464
|
+
stray gap under the title when the card is collapsed; .oo-warranty-body's own margin-top
|
|
5465
|
+
restores that 16px only once actually expanded (both cards default expanded, but the user can
|
|
5466
|
+
still collapse them). */
|
|
5268
5467
|
.md3 .oo-warranty-card .co-card-title {
|
|
5269
5468
|
margin-bottom: 0;
|
|
5270
5469
|
}
|
|
@@ -5272,6 +5471,7 @@ line-height: 31px;
|
|
|
5272
5471
|
margin-top: 16px;
|
|
5273
5472
|
display: flex;
|
|
5274
5473
|
flex-direction: column;
|
|
5474
|
+
gap: 12px;
|
|
5275
5475
|
}
|
|
5276
5476
|
.md3 .oo-warranty-notice-img {
|
|
5277
5477
|
display: block;
|
|
@@ -5279,36 +5479,6 @@ line-height: 31px;
|
|
|
5279
5479
|
max-width: 400px;
|
|
5280
5480
|
height: auto;
|
|
5281
5481
|
}
|
|
5282
|
-
.md3 .oo-warranty-divider {
|
|
5283
|
-
margin-top: 16px;
|
|
5284
|
-
height: 1px;
|
|
5285
|
-
background: var(--md-outline-variant);
|
|
5286
|
-
}
|
|
5287
|
-
.md3 .oo-warranty-subrow {
|
|
5288
|
-
margin-top: 14px;
|
|
5289
|
-
padding-bottom: 6px;
|
|
5290
|
-
display: flex;
|
|
5291
|
-
align-items: center;
|
|
5292
|
-
justify-content: space-between;
|
|
5293
|
-
gap: 10px;
|
|
5294
|
-
width: 100%;
|
|
5295
|
-
background: none;
|
|
5296
|
-
border: 0;
|
|
5297
|
-
cursor: pointer;
|
|
5298
|
-
text-align: left;
|
|
5299
|
-
}
|
|
5300
|
-
.md3 .oo-warranty-subrow-label {
|
|
5301
|
-
font-family: Arial, Helvetica, Arimo, sans-serif;
|
|
5302
|
-
font-weight: 700;
|
|
5303
|
-
font-size: 15px;
|
|
5304
|
-
color: #0E2238;
|
|
5305
|
-
}
|
|
5306
|
-
.md3 .oo-warranty-manufacturer-body {
|
|
5307
|
-
margin-top: 10px;
|
|
5308
|
-
display: flex;
|
|
5309
|
-
flex-direction: column;
|
|
5310
|
-
gap: 12px;
|
|
5311
|
-
}
|
|
5312
5482
|
.md3 .oo-warranty-garan-img {
|
|
5313
5483
|
display: block;
|
|
5314
5484
|
width: 100%;
|
|
@@ -5783,11 +5953,20 @@ a.oo-warranty-link--icon:hover {
|
|
|
5783
5953
|
@media (min-width: 601px) and (max-width: 993px) {
|
|
5784
5954
|
.md3 .co-next {
|
|
5785
5955
|
flex-direction: row;
|
|
5786
|
-
flex-wrap:
|
|
5956
|
+
flex-wrap: wrap;
|
|
5787
5957
|
align-items: center;
|
|
5788
5958
|
justify-content: flex-end;
|
|
5789
5959
|
gap: 16px;
|
|
5790
5960
|
}
|
|
5961
|
+
/* same reasoning as the ≤700px block above: .co-error (a step-validation message, can run
|
|
5962
|
+
long — e.g. "Bitte wählen Sie eine der Optionen für Garantiepaket und Fahrzeugzulassung.")
|
|
5963
|
+
must not crowd onto the Zurück/Schritt/Weiter row. Without this, flex-wrap: nowrap forced
|
|
5964
|
+
all 4 children onto one line with no room, so the error visually overlapped Zurück and
|
|
5965
|
+
squeezed "Schritt X von Y" into wrapping onto 2 lines. flex-basis:100% forces the error onto
|
|
5966
|
+
its own first line; flex-wrap: wrap (above) then lays the 3 buttons/label out on the next. */
|
|
5967
|
+
.md3 .co-next .co-error {
|
|
5968
|
+
flex: 1 0 100%;
|
|
5969
|
+
}
|
|
5791
5970
|
}
|
|
5792
5971
|
/* below the 992px desktop breakpoint the content no longer reserves the 424px left
|
|
5793
5972
|
gap, so the fixed rail would overlap the form — hide it (mobile uses the header stepper). */
|
|
@@ -9299,6 +9478,64 @@ a.oo-warranty-link--icon:hover {
|
|
|
9299
9478
|
padding: 18px 20px 22px;
|
|
9300
9479
|
overflow-y: auto;
|
|
9301
9480
|
}
|
|
9481
|
+
/* Checkout's Finanzierungsdetails/Leasingdetails modal (CheckoutFinancePanelNext's cf-details
|
|
9482
|
+
button) reuses this SAME FinanceDetailsModal as the VDP page, but the rate hero + price-rating
|
|
9483
|
+
pill/bar (.fin-price-grid) would just repeat the SAME figures the cf-panel already shows above
|
|
9484
|
+
this button — unlike the Anzahlung/Laufzeit sliders and Mit-Schlussrate/Rundum-Sorglos-Paket
|
|
9485
|
+
toggles (disabled by simply not passing their change handlers), nothing gates this block by
|
|
9486
|
+
prop, so it's hidden here instead. Scoped to the checkout portal wrapper only — VDP/SearchPage
|
|
9487
|
+
keep it. */
|
|
9488
|
+
.md3.cf-fin-details-modal .fin-price-grid {
|
|
9489
|
+
display: none;
|
|
9490
|
+
}
|
|
9491
|
+
/* Leasing's breakdown table runs longer than financing's (Leasinggeber+Vertragsart split, the
|
|
9492
|
+
mandatory Rundum-Sorglos-Paket notice box, an extra disclaimer line) — 88vh left it a little
|
|
9493
|
+
short of fitting whole, needing a few pixels of real scroll even after the hero/sliders/
|
|
9494
|
+
toggles/CTA above are stripped. A taller cap gives it room to fit outright on realistic
|
|
9495
|
+
viewports; overflow-y:auto above still catches genuinely short windows as a fallback. */
|
|
9496
|
+
.md3.cf-fin-details-modal .fin-modal {
|
|
9497
|
+
max-height: 95vh;
|
|
9498
|
+
}
|
|
9499
|
+
/* overflow-y:visible here (tried first for the short financing case) turned out to break the
|
|
9500
|
+
LONGER leasing case: without a real scroll container, flexbox's automatic min-height:auto
|
|
9501
|
+
let .fin-modal-body grow to its full content height (839px+ — leasing adds the Leasinggeber/
|
|
9502
|
+
Vertragsart split + the mandatory Rundum-Sorglos-Paket notice box) past .fin-modal's own
|
|
9503
|
+
max-height:88vh, and since .fin-modal-scrim centers it, the overflow pushed the header off the
|
|
9504
|
+
TOP of the viewport instead of clipping the bottom. Keep the real overflow-y:auto (correct
|
|
9505
|
+
height-constraining behaviour for any content length) and just hide the bar. */
|
|
9506
|
+
.md3.cf-fin-details-modal .fin-modal-body {
|
|
9507
|
+
scrollbar-width: none;
|
|
9508
|
+
-ms-overflow-style: none;
|
|
9509
|
+
}
|
|
9510
|
+
.md3.cf-fin-details-modal .fin-modal-body::-webkit-scrollbar {
|
|
9511
|
+
display: none;
|
|
9512
|
+
}
|
|
9513
|
+
/* "Rate anfragen"/call CTA row — checkout has no separate request flow to trigger from inside an
|
|
9514
|
+
already-running checkout (reviewing the breakdown IS the action), so this instance drops the
|
|
9515
|
+
row entirely rather than wiring it to a stand-in action. Unlike the sliders/toggles, it isn't
|
|
9516
|
+
gated by a handler prop, so it's hidden here. Shared class, one rule covers both breakpoints.
|
|
9517
|
+
`!important` — the element carries an inline style="display:flex" (VdpModules.tsx), which
|
|
9518
|
+
otherwise wins over any external rule regardless of selector specificity. */
|
|
9519
|
+
.md3.cf-fin-details-modal .fin-sheet-cta-row {
|
|
9520
|
+
display: none !important;
|
|
9521
|
+
}
|
|
9522
|
+
/* the (kept) legal breakdown's wrapper carries an inline marginTop:18 meant to separate it from
|
|
9523
|
+
the now-hidden hero/sliders/toggles above — stacked on .fin-modal-body's own 18px top padding
|
|
9524
|
+
that reads as unexplained empty space once those sections are gone. `!important` because the
|
|
9525
|
+
marginTop is inline (VdpModules.tsx), and only the DESKTOP wrapper needs it: it's the immediate
|
|
9526
|
+
next sibling of the (also hidden) .fin-price-grid there; the mobile breakdown wrapper is
|
|
9527
|
+
separated from .fin-price-grid by the still-visible data-sheet-fold button, so this selector
|
|
9528
|
+
doesn't reach it and mobile's spacing (already just marginBottom, no marginTop) is untouched. */
|
|
9529
|
+
.md3.cf-fin-details-modal .fin-price-grid + div {
|
|
9530
|
+
margin-top: 0 !important;
|
|
9531
|
+
}
|
|
9532
|
+
/* mobile breakpoint below sets align-items:flex-start on .fin-modal-head, top-aligning the close
|
|
9533
|
+
button against the 2-line "Kredit-Rate anpassen" + vehicle-name title — checkout's stripped
|
|
9534
|
+
instance has nothing that needs that extra top room, so it stays vertically centered instead
|
|
9535
|
+
(3-class selector already outweighs the 2-class mobile rule, no !important needed). */
|
|
9536
|
+
.md3.cf-fin-details-modal .fin-modal-head {
|
|
9537
|
+
align-items: center;
|
|
9538
|
+
}
|
|
9302
9539
|
@media (max-width: 600px) {
|
|
9303
9540
|
.md3 .fin-modal-scrim {
|
|
9304
9541
|
align-items: flex-end;
|
|
@@ -14177,3 +14414,247 @@ color: #005CFF;
|
|
|
14177
14414
|
border-bottom: 1px solid var(--md-outline-variant);
|
|
14178
14415
|
}
|
|
14179
14416
|
}
|
|
14417
|
+
|
|
14418
|
+
/* Post-purchase confirmation modal (CONGRATULATIONS_CHECKOUT_MODAL, Congratulations.tsx) —
|
|
14419
|
+
Figma 64637:37064 (desktop) / 63536:6276 (mobile). BaseModal's own `.modalCheckout` chrome
|
|
14420
|
+
(BaseModal.styl: fixed 375px width, square corners, its own white bg + shadow) is neutralized
|
|
14421
|
+
here via the `cgz-modal` className OfferRequestButtonWrapper passes — Congratulations.tsx's
|
|
14422
|
+
own `.cgz-card` supplies the actual Figma card (border/radius/bg/padding) instead, so there's
|
|
14423
|
+
no double box. `.md3` ancestor: App.tsx's root `<div className="${styles.app} md3">` wraps
|
|
14424
|
+
the whole SPA unconditionally (confirmed — this is NOT the same as the per-page opt-in `.md3`
|
|
14425
|
+
noted elsewhere for legacy MainContent pages), and BaseModal renders inline (no portal), so a
|
|
14426
|
+
single `.md3`-prefixed selector is enough, same as every other CheckoutPage-tree rule already
|
|
14427
|
+
in this file. */
|
|
14428
|
+
.md3 .cgz-modal {
|
|
14429
|
+
width: 800px;
|
|
14430
|
+
max-width: calc(100vw - 48px);
|
|
14431
|
+
background: transparent;
|
|
14432
|
+
border: 0;
|
|
14433
|
+
border-radius: 0;
|
|
14434
|
+
box-shadow: none;
|
|
14435
|
+
padding: 0;
|
|
14436
|
+
}
|
|
14437
|
+
.md3 .cgz-card {
|
|
14438
|
+
display: flex;
|
|
14439
|
+
flex-direction: column;
|
|
14440
|
+
align-items: center;
|
|
14441
|
+
gap: 32px;
|
|
14442
|
+
/* Figma: fixed 800px, not fluid — was width:100%, which (now that this also renders as a
|
|
14443
|
+
real page, not just inside the fixed-800px .cgz-modal) stretched edge-to-edge on any
|
|
14444
|
+
viewport wider than 800px+padding instead of staying a centered 800px card. max-width is
|
|
14445
|
+
just the narrow-viewport safety clamp (matches .cgz-modal's own calc(100vw - 48px)) —
|
|
14446
|
+
the max-width:600px block below still overrides both to 100% for the mobile layout. */
|
|
14447
|
+
width: 800px;
|
|
14448
|
+
max-width: calc(100vw - 48px);
|
|
14449
|
+
background: #FFFFFF;
|
|
14450
|
+
border: 1px solid var(--md-outline-variant);
|
|
14451
|
+
border-radius: 16px;
|
|
14452
|
+
padding: 48px 56px;
|
|
14453
|
+
box-sizing: border-box;
|
|
14454
|
+
}
|
|
14455
|
+
.md3 .cgz-intro {
|
|
14456
|
+
display: flex;
|
|
14457
|
+
flex-direction: column;
|
|
14458
|
+
align-items: center;
|
|
14459
|
+
gap: 12px;
|
|
14460
|
+
width: 100%;
|
|
14461
|
+
}
|
|
14462
|
+
.md3 .cgz-logo {
|
|
14463
|
+
width: 138.6px;
|
|
14464
|
+
height: 44px;
|
|
14465
|
+
}
|
|
14466
|
+
.md3 .cgz-badge {
|
|
14467
|
+
display: flex;
|
|
14468
|
+
align-items: center;
|
|
14469
|
+
gap: 8px;
|
|
14470
|
+
padding: 6px 13px;
|
|
14471
|
+
background: #DFF3E5;
|
|
14472
|
+
border-radius: 999px;
|
|
14473
|
+
font-family: Arimo, Arial, sans-serif;
|
|
14474
|
+
font-weight: 700;
|
|
14475
|
+
font-size: 13.5px;
|
|
14476
|
+
line-height: 1.35;
|
|
14477
|
+
color: #0F6B36;
|
|
14478
|
+
white-space: nowrap;
|
|
14479
|
+
}
|
|
14480
|
+
/* Figma specifies "42dot Sans" for this headline — not a typeface loaded anywhere in this
|
|
14481
|
+
project (confirmed: no other component/font-face references it). Mapped to the design
|
|
14482
|
+
system's own existing "big display heading" token instead (same one .co-step-title/hero
|
|
14483
|
+
numbers use) rather than adding a whole new webfont for one screen. 500 is the lightest
|
|
14484
|
+
weight actually loaded for it (Schibsted+Grotesk:wght@500;600;700;800, see :root) — closest
|
|
14485
|
+
available match to Figma's "Regular". */
|
|
14486
|
+
.md3 .cgz-title {
|
|
14487
|
+
margin: 0;
|
|
14488
|
+
font-family: var(--md-font-display, var(--md-font));
|
|
14489
|
+
font-weight: 500;
|
|
14490
|
+
font-size: 32px;
|
|
14491
|
+
line-height: 1.35;
|
|
14492
|
+
color: var(--md-on-surface);
|
|
14493
|
+
text-align: center;
|
|
14494
|
+
}
|
|
14495
|
+
.md3 .cgz-desc {
|
|
14496
|
+
margin: 0;
|
|
14497
|
+
max-width: 560px;
|
|
14498
|
+
font-family: Arimo, Arial, sans-serif;
|
|
14499
|
+
font-weight: 400;
|
|
14500
|
+
font-size: 16px;
|
|
14501
|
+
line-height: 1.35;
|
|
14502
|
+
color: var(--md-on-surface-variant);
|
|
14503
|
+
text-align: center;
|
|
14504
|
+
}
|
|
14505
|
+
.md3 .cgz-car {
|
|
14506
|
+
width: 420px;
|
|
14507
|
+
height: 275px;
|
|
14508
|
+
object-fit: contain;
|
|
14509
|
+
}
|
|
14510
|
+
.md3 .cgz-steps {
|
|
14511
|
+
display: flex;
|
|
14512
|
+
flex-direction: row;
|
|
14513
|
+
gap: 24px;
|
|
14514
|
+
width: 100%;
|
|
14515
|
+
padding: 20px 0;
|
|
14516
|
+
border-top: 1px solid var(--md-outline-variant);
|
|
14517
|
+
border-bottom: 1px solid var(--md-outline-variant);
|
|
14518
|
+
box-sizing: border-box;
|
|
14519
|
+
}
|
|
14520
|
+
.md3 .cgz-step {
|
|
14521
|
+
display: flex;
|
|
14522
|
+
flex: 1 0 0;
|
|
14523
|
+
min-width: 0;
|
|
14524
|
+
align-items: flex-start;
|
|
14525
|
+
gap: 10px;
|
|
14526
|
+
}
|
|
14527
|
+
.md3 .cgz-step-text {
|
|
14528
|
+
display: flex;
|
|
14529
|
+
flex-direction: column;
|
|
14530
|
+
gap: 1px;
|
|
14531
|
+
min-width: 0;
|
|
14532
|
+
}
|
|
14533
|
+
.md3 .cgz-step-title {
|
|
14534
|
+
margin: 0;
|
|
14535
|
+
font-family: Arimo, Arial, sans-serif;
|
|
14536
|
+
font-weight: 700;
|
|
14537
|
+
font-size: 14px;
|
|
14538
|
+
line-height: 1.35;
|
|
14539
|
+
color: var(--md-on-surface);
|
|
14540
|
+
}
|
|
14541
|
+
.md3 .cgz-step-sub {
|
|
14542
|
+
margin: 0;
|
|
14543
|
+
font-family: Arimo, Arial, sans-serif;
|
|
14544
|
+
font-weight: 400;
|
|
14545
|
+
font-size: 13px;
|
|
14546
|
+
line-height: 1.35;
|
|
14547
|
+
color: var(--md-on-surface-variant);
|
|
14548
|
+
}
|
|
14549
|
+
.md3 .cgz-btn {
|
|
14550
|
+
width: 320px;
|
|
14551
|
+
height: 48px;
|
|
14552
|
+
padding: 0 16px;
|
|
14553
|
+
gap: 8px;
|
|
14554
|
+
background: #005cff;
|
|
14555
|
+
border-radius: 16px;
|
|
14556
|
+
font-family: Arial;
|
|
14557
|
+
font-weight: 700;
|
|
14558
|
+
font-size: 15px;
|
|
14559
|
+
}
|
|
14560
|
+
.md3 .cgz-btn:hover {
|
|
14561
|
+
box-shadow: var(--md-e2);
|
|
14562
|
+
background: var(--autode-blau-strong);
|
|
14563
|
+
}
|
|
14564
|
+
@media (hover: none) {
|
|
14565
|
+
.md3 .cgz-btn:hover {
|
|
14566
|
+
box-shadow: none;
|
|
14567
|
+
background: #005cff;
|
|
14568
|
+
}
|
|
14569
|
+
}
|
|
14570
|
+
@media (max-width: 600px) {
|
|
14571
|
+
.md3 .cgz-modal {
|
|
14572
|
+
width: 100%;
|
|
14573
|
+
max-width: 100%;
|
|
14574
|
+
}
|
|
14575
|
+
.md3 .cgz-card {
|
|
14576
|
+
width: 100%;
|
|
14577
|
+
max-width: 100%;
|
|
14578
|
+
gap: 16px;
|
|
14579
|
+
padding: 24px 20px 26px;
|
|
14580
|
+
}
|
|
14581
|
+
.md3 .cgz-title {
|
|
14582
|
+
font-size: 26px;
|
|
14583
|
+
}
|
|
14584
|
+
.md3 .cgz-desc {
|
|
14585
|
+
max-width: none;
|
|
14586
|
+
font-family: Arial;
|
|
14587
|
+
font-size: 15px;
|
|
14588
|
+
}
|
|
14589
|
+
.md3 .cgz-car {
|
|
14590
|
+
width: 260px;
|
|
14591
|
+
height: 170px;
|
|
14592
|
+
}
|
|
14593
|
+
.md3 .cgz-steps {
|
|
14594
|
+
flex-direction: column;
|
|
14595
|
+
gap: 12px;
|
|
14596
|
+
padding: 16px 0;
|
|
14597
|
+
}
|
|
14598
|
+
.md3 .cgz-step-title,
|
|
14599
|
+
.md3 .cgz-step-sub {
|
|
14600
|
+
font-family: Arial;
|
|
14601
|
+
}
|
|
14602
|
+
.md3 .cgz-btn {
|
|
14603
|
+
width: 100%;
|
|
14604
|
+
}
|
|
14605
|
+
}
|
|
14606
|
+
|
|
14607
|
+
/* Escapes MainContent.styl's `container()` mixin (max-width: 1100px, centered) — App.tsx wraps
|
|
14608
|
+
every non-SPA-layout route (checkout included) in <MainContent>, which defaults to that
|
|
14609
|
+
1100px cap regardless of what the routed page itself renders. CheckoutPage.tsx already
|
|
14610
|
+
escapes it the CSS-module way (its own mainContentWrap, max-width:100%); this is the same
|
|
14611
|
+
fix as a plain literal class for CheckoutThankYouPage.tsx's <Page mainContentClass="..."> —
|
|
14612
|
+
consistent with cgz-modal above (a plain class, not a CSS-module one, so it's reliably
|
|
14613
|
+
targetable here in md3.css, a completely different bundle/file). */
|
|
14614
|
+
.md3 .cgz-main-content {
|
|
14615
|
+
max-width: 100%;
|
|
14616
|
+
min-height: 100%;
|
|
14617
|
+
height: auto;
|
|
14618
|
+
}
|
|
14619
|
+
|
|
14620
|
+
/* Checkout step 6 "Danke" page chrome (CheckoutThankYouPage.tsx) — Figma 64637:37033's own
|
|
14621
|
+
"Checkout Header/Footer Desktop", deliberately simpler than CheckoutTopBarNext/
|
|
14622
|
+
CheckoutHeaderNext (no car price/back/stepper — the order's already placed by the time this
|
|
14623
|
+
renders). `.cgz-card` etc. above are shared with the CONGRATULATIONS_CHECKOUT_MODAL path and
|
|
14624
|
+
need no changes for this context. */
|
|
14625
|
+
.md3 .cgz-page {
|
|
14626
|
+
display: flex;
|
|
14627
|
+
flex-direction: column;
|
|
14628
|
+
min-height: 100vh;
|
|
14629
|
+
}
|
|
14630
|
+
.md3 .cgz-page-header {
|
|
14631
|
+
display: flex;
|
|
14632
|
+
align-items: center;
|
|
14633
|
+
flex: none;
|
|
14634
|
+
height: 64px;
|
|
14635
|
+
padding: 0 120px;
|
|
14636
|
+
background: #FFFFFF;
|
|
14637
|
+
border-bottom: 1px solid var(--md-outline-variant);
|
|
14638
|
+
}
|
|
14639
|
+
.md3 .cgz-page-logo {
|
|
14640
|
+
display: block;
|
|
14641
|
+
width: 81.9px;
|
|
14642
|
+
height: 26px;
|
|
14643
|
+
}
|
|
14644
|
+
.md3 .cgz-page-main {
|
|
14645
|
+
display: flex;
|
|
14646
|
+
flex-direction: column;
|
|
14647
|
+
align-items: center;
|
|
14648
|
+
flex: 1 0 auto;
|
|
14649
|
+
padding: 56px 24px 80px;
|
|
14650
|
+
background: #F4F5FA;
|
|
14651
|
+
}
|
|
14652
|
+
@media (max-width: 600px) {
|
|
14653
|
+
.md3 .cgz-page-header {
|
|
14654
|
+
height: 56px;
|
|
14655
|
+
padding: 0 23px;
|
|
14656
|
+
}
|
|
14657
|
+
.md3 .cgz-page-main {
|
|
14658
|
+
padding: 24px 23px 40px;
|
|
14659
|
+
}
|
|
14660
|
+
}
|
package/ui/FormCheckbox.tsx
CHANGED
|
@@ -36,10 +36,14 @@ export interface FormCheckboxProps {
|
|
|
36
36
|
/** override the field's own disabled flag. */
|
|
37
37
|
disabled?: boolean;
|
|
38
38
|
id?: string;
|
|
39
|
+
/** show a small alert-circle glyph before the error text (Figma 64670:106293's "Fehlermeldung"
|
|
40
|
+
* row) — opt-in, defaults off so every other FormCheckbox caller keeps its plain text-only
|
|
41
|
+
* error message unchanged. */
|
|
42
|
+
errorIcon?: boolean;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
const FormCheckbox: React.FunctionComponent<FormCheckboxProps> = observer(
|
|
42
|
-
({ field, t, label, children, disabled, id }) => {
|
|
46
|
+
({ field, t, label, children, disabled, id, errorIcon }) => {
|
|
43
47
|
const isChecked = !!field.checked;
|
|
44
48
|
const isDisabled = disabled != null ? disabled : !!field.disabled;
|
|
45
49
|
// field.hasError alone is a LIVE check (mobx-react-form's Field#hasError getter re-evaluates
|
|
@@ -77,7 +81,16 @@ const FormCheckbox: React.FunctionComponent<FormCheckboxProps> = observer(
|
|
|
77
81
|
</span>
|
|
78
82
|
{content != null ? <span className="mcheck-label">{content}</span> : null}
|
|
79
83
|
</label>
|
|
80
|
-
{hasError ?
|
|
84
|
+
{hasError ? (
|
|
85
|
+
errorIcon ? (
|
|
86
|
+
<span className="mcheck-error-text mcheck-error-text--icon">
|
|
87
|
+
<Icon name="alert-circle" size={16} stroke={2} cls="mcheck-error-icon" />
|
|
88
|
+
<span className="mcheck-error-msg">{field.error}</span>
|
|
89
|
+
</span>
|
|
90
|
+
) : (
|
|
91
|
+
<span className="mcheck-error-text">{field.error}</span>
|
|
92
|
+
)
|
|
93
|
+
) : null}
|
|
81
94
|
</div>
|
|
82
95
|
);
|
|
83
96
|
},
|
package/ui/Icon.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Activity,
|
|
4
|
+
AlertCircle,
|
|
4
5
|
Apple,
|
|
5
6
|
Armchair,
|
|
6
7
|
ArrowLeft,
|
|
@@ -57,6 +58,7 @@ import {
|
|
|
57
58
|
Facebook,
|
|
58
59
|
File,
|
|
59
60
|
FileCheck,
|
|
61
|
+
FileSignature,
|
|
60
62
|
FileText,
|
|
61
63
|
Flame,
|
|
62
64
|
Fuel,
|
|
@@ -199,6 +201,7 @@ function toPascalCase(name: string): string {
|
|
|
199
201
|
*/
|
|
200
202
|
const REGISTRY: Record<string, React.ComponentType<LucideProps>> = {
|
|
201
203
|
Activity,
|
|
204
|
+
AlertCircle,
|
|
202
205
|
Apple,
|
|
203
206
|
Armchair,
|
|
204
207
|
ArrowLeft,
|
|
@@ -256,6 +259,7 @@ const REGISTRY: Record<string, React.ComponentType<LucideProps>> = {
|
|
|
256
259
|
Facebook,
|
|
257
260
|
File,
|
|
258
261
|
FileCheck,
|
|
262
|
+
FileSignature,
|
|
259
263
|
FileText,
|
|
260
264
|
Flame,
|
|
261
265
|
Fuel,
|
package/vdp/VdpModules.tsx
CHANGED
|
@@ -2126,6 +2126,12 @@ export interface FinanceDetailsModalProps {
|
|
|
2126
2126
|
/** opened directly via the page's Finanzierungs-/Leasingdetails link — the sheet opens
|
|
2127
2127
|
* scrolled to the breakdown instead of the "Rate anpassen" controls at its top. */
|
|
2128
2128
|
initialShowBreakdown?: boolean;
|
|
2129
|
+
/** skips the smooth scrollIntoView/scrollSheetToBreakdown animation initialShowBreakdown
|
|
2130
|
+
* otherwise triggers on open (still sets showBreakdown itself) — for a consumer whose sheet
|
|
2131
|
+
* has nothing ABOVE the breakdown to scroll past (e.g. checkout's stripped-down instance,
|
|
2132
|
+
* with the rate hero/sliders/toggles above it hidden), that animation has nowhere real to go
|
|
2133
|
+
* and only reads as a stray jump. */
|
|
2134
|
+
skipInitialScroll?: boolean;
|
|
2129
2135
|
/** mobile only — price-rating label, drives the eval pill/bar (same as the collapsed card). */
|
|
2130
2136
|
ratingLabel?: string;
|
|
2131
2137
|
/** Kaufen tab only — real carStore.isAvailable (same source as VdpTrustRow's "Sofort
|
|
@@ -2154,6 +2160,10 @@ export interface FinanceDetailsModalProps {
|
|
|
2154
2160
|
leasingAvailable?: boolean;
|
|
2155
2161
|
/** i18n function (drilled from the page). */
|
|
2156
2162
|
t: TranslationFunction;
|
|
2163
|
+
/** overrides the desktop dialog heading (default: the mode's own, shorter "Details" text) —
|
|
2164
|
+
* a consumer whose own trigger already carries a specific label (e.g. checkout's cf-details
|
|
2165
|
+
* button, "Finanzierungsdetails"/"Leasingdetails") can pass that same text through here. */
|
|
2166
|
+
titleOverride?: string;
|
|
2157
2167
|
}
|
|
2158
2168
|
|
|
2159
2169
|
/**
|
|
@@ -2197,6 +2207,7 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
|
|
|
2197
2207
|
onAnnualMileageChange,
|
|
2198
2208
|
onClose,
|
|
2199
2209
|
initialShowBreakdown,
|
|
2210
|
+
skipInitialScroll,
|
|
2200
2211
|
ratingLabel,
|
|
2201
2212
|
available,
|
|
2202
2213
|
onModeChange,
|
|
@@ -2211,6 +2222,7 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
|
|
|
2211
2222
|
onToggleAddServices,
|
|
2212
2223
|
leasingAvailable = true,
|
|
2213
2224
|
t,
|
|
2225
|
+
titleOverride,
|
|
2214
2226
|
}) => {
|
|
2215
2227
|
// same "UVP-Rate"/list-price footnote badge + popover as FinanceCalculator's own hero — this
|
|
2216
2228
|
// modal's mobile "Rate anpassen" sheet renders its own separate copy of that hero row, so it
|
|
@@ -2314,14 +2326,14 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
|
|
|
2314
2326
|
React.useEffect(() => {
|
|
2315
2327
|
if (!open) return;
|
|
2316
2328
|
setShowBreakdown(!!initialShowBreakdown);
|
|
2317
|
-
if (!initialShowBreakdown) return;
|
|
2329
|
+
if (!initialShowBreakdown || skipInitialScroll) return;
|
|
2318
2330
|
const timer = window.setTimeout(() => {
|
|
2319
2331
|
if (mobileBreakdownRef.current?.offsetParent) scrollSheetToBreakdown();
|
|
2320
2332
|
else desktopBreakdownRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
2321
2333
|
}, 50);
|
|
2322
2334
|
return () => window.clearTimeout(timer);
|
|
2323
2335
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2324
|
-
}, [open, initialShowBreakdown]);
|
|
2336
|
+
}, [open, initialShowBreakdown, skipInitialScroll]);
|
|
2325
2337
|
// in-sheet details link: expanding scrolls down to the breakdown, collapsing back to the top.
|
|
2326
2338
|
const revealBreakdownRef = React.useRef(false);
|
|
2327
2339
|
React.useEffect(() => {
|
|
@@ -2389,7 +2401,7 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
|
|
|
2389
2401
|
if (!open) return null;
|
|
2390
2402
|
const isLease = mode === 'leasing';
|
|
2391
2403
|
const isCash = mode === 'cash';
|
|
2392
|
-
const title = isLease ? t('autobahn:modal.leasingDetails') : t('autobahn:modal.financingDetails');
|
|
2404
|
+
const title = titleOverride || (isLease ? t('autobahn:modal.leasingDetails') : t('autobahn:modal.financingDetails'));
|
|
2393
2405
|
const mobileTitle = isCash
|
|
2394
2406
|
? t('autobahn:modal.sheetTitleCash')
|
|
2395
2407
|
: isLease
|