@bytebrand/fe-ui-core-autobahn 1.0.0 → 1.0.1
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/assets/autode-logo.svg +6 -6
- package/cards/CarCard.tsx +174 -174
- package/cards/CarRow.tsx +182 -182
- package/cards/cardParts.tsx +489 -489
- package/cards/index.ts +35 -35
- package/cards/types.ts +114 -114
- package/chrome/Footer.tsx +189 -189
- package/chrome/TopBar.tsx +405 -405
- package/chrome/index.ts +20 -20
- package/chrome/svg.d.ts +11 -11
- package/compat/AccardionSection/AccardionSection.styl +45 -45
- package/compat/AccardionSection/AccardionSection.tsx +56 -56
- package/compat/AccordionWidget/AccordionWidget.styl +220 -220
- package/compat/AccordionWidget/AccordionWidget.tsx +257 -257
- package/compat/AccordionWrapper/AccordionItem/AccordionItem.styl +34 -34
- package/compat/AccordionWrapper/AccordionItem/AccordionItem.tsx +64 -64
- package/compat/AccordionWrapper/AccordionWrapper.tsx +34 -34
- package/compat/AdvantageItem/AdvantageItem.styl +47 -47
- package/compat/AdvantageItem/AdvantageItem.tsx +26 -26
- package/compat/Alternative/DealerInfo.styl +116 -116
- package/compat/Alternative/DealerInfo.tsx +171 -171
- package/compat/Badge/Badge.styl +44 -44
- package/compat/Badge/Badge.tsx +25 -25
- package/compat/BodyTypeIcons/Audi.tsx +9 -9
- package/compat/BodyTypeIcons/Bmw.tsx +860 -860
- package/compat/BodyTypeIcons/Ford.tsx +31 -31
- package/compat/BodyTypeIcons/IconsConfig.tsx +44 -44
- package/compat/BodyTypeIcons/IconsConfigCompact.tsx +30 -30
- package/compat/BodyTypeIcons/Mazda.tsx +44 -44
- package/compat/BodyTypeIcons/MercedesBenz.tsx +38 -38
- package/compat/BodyTypeIcons/Opel.tsx +19 -19
- package/compat/BodyTypeIcons/Search.tsx +19 -19
- package/compat/BodyTypeIcons/SelectorCategoryCombi.tsx +68 -68
- package/compat/BodyTypeIcons/SelectorCategoryCompact.tsx +20 -20
- package/compat/BodyTypeIcons/SelectorCategoryConvertible.tsx +68 -68
- package/compat/BodyTypeIcons/SelectorCategoryCoupe.tsx +60 -60
- package/compat/BodyTypeIcons/SelectorCategoryMinibus.tsx +64 -64
- package/compat/BodyTypeIcons/SelectorCategorySedan.tsx +69 -69
- package/compat/BodyTypeIcons/SelectorCategorySuv.tsx +77 -77
- package/compat/BodyTypeIcons/SelectorCategoryTransporter.tsx +68 -68
- package/compat/BodyTypeIcons/SelectorCategoryVan.tsx +72 -72
- package/compat/BodyTypeIcons/Volkswagen.tsx +330 -330
- package/compat/BodyTypeIcons/index.ts +17 -17
- package/compat/Button/Button.tsx +65 -65
- package/compat/ButtonOld/Button.styl +65 -65
- package/compat/ButtonOld/Button.tsx +84 -84
- package/compat/Checkbox/Checkbox.styl +119 -119
- package/compat/Checkbox/Checkbox.tsx +151 -151
- package/compat/Checkbox/FormCheckbox.styl +22 -22
- package/compat/Checkbox/FormCheckbox.tsx +86 -86
- package/compat/Co2Widget/CO2Efficiency.styl +219 -219
- package/compat/Co2Widget/CO2Efficiency.tsx +93 -93
- package/compat/ContactInfo/ContactInfo.styl +22 -22
- package/compat/ContactInfo/ContactInfo.tsx +161 -161
- package/compat/ContactInfo/TemporaryTranslation.ts +11 -11
- package/compat/CookieModal/CookieModal.tsx +49 -49
- package/compat/CookieModal/Modal.tsx +43 -43
- package/compat/CookieModal/ModalsConfig.tsx +23 -23
- package/compat/CookieModal/modals/ManageCookieModal/ManageCookieModal.styl +86 -86
- package/compat/CookieModal/modals/ManageCookieModal/ManageCookieModal.tsx +184 -184
- package/compat/CookieModal/modals/PreviewCookieModal/PreviewCookieModal.styl +66 -66
- package/compat/CookieModal/modals/PreviewCookieModal/PreviewCookieModal.tsx +51 -51
- package/compat/CookieModal/modals/TestModal.tsx +7 -7
- package/compat/CookieModal/useModal.ts +13 -13
- package/compat/CustomerQuote/CustomerQuote.styl +79 -79
- package/compat/CustomerQuote/CustomerQuote.tsx +68 -68
- package/compat/CustomerQuotesSlider/CustomerQuotesSlider.styl +46 -46
- package/compat/CustomerQuotesSlider/CustomerQuotesSlider.tsx +79 -79
- package/compat/CustomerQuotesSlider/SliderArrow.tsx +20 -20
- package/compat/CustomerQuotesSlider/slider.global.styl +2 -2
- package/compat/ExpansionPanel/ExpansionPanel.tsx +68 -68
- package/compat/FirstInfoBlock/FirstInfoBlock.styl +33 -33
- package/compat/FirstInfoBlock/FirstInfoBlock.tsx +49 -49
- package/compat/FirstInfoBlock/FirstInfoBlockItem/FirstInfoBlockItem.styl +66 -66
- package/compat/FirstInfoBlock/FirstInfoBlockItem/FirstInfoBlockItem.tsx +21 -21
- package/compat/FormattedNumber/FormattedNumber.styl +1 -1
- package/compat/FormattedNumber/FormattedNumber.tsx +90 -90
- package/compat/IconContainer/IconContainer.styl +23 -23
- package/compat/IconContainer/IconContainer.tsx +34 -34
- package/compat/Image/Image.styl +109 -109
- package/compat/Image/Image.tsx +302 -302
- package/compat/ImageSettings/EditableOptionsTable/CustomCheckbox/CustomCheckbox.styl +7 -7
- package/compat/ImageSettings/EditableOptionsTable/CustomCheckbox/CustomCheckbox.tsx +30 -30
- package/compat/ImageSettings/EditableOptionsTable/EditableOptionsTable.styl +122 -122
- package/compat/ImageSettings/EditableOptionsTable/EditableOptionsTable.tsx +199 -199
- package/compat/ImageSettings/EditableOptionsTable/ImageIcon/ImageIcon.styl +3 -3
- package/compat/ImageSettings/EditableOptionsTable/ImageIcon/ImageIcon.tsx +15 -15
- package/compat/ImageSettings/OptionsTable/OptionsTable.styl +47 -47
- package/compat/ImageSettings/OptionsTable/OptionsTable.tsx +122 -122
- package/compat/ImageSettings/PricePlan/PricePlan.styl +63 -63
- package/compat/ImageSettings/PricePlan/PricePlan.tsx +32 -32
- package/compat/ImageSettings/index.ts +42 -42
- package/compat/ImagesSet/ImagesSet.styl +27 -27
- package/compat/ImagesSet/ImagesSet.tsx +76 -76
- package/compat/InfoBlockWrapper/InfoBlockWrapper.styl +41 -41
- package/compat/InfoBlockWrapper/InfoBlockWrapper.tsx +46 -46
- package/compat/Loader/Loader.styl +39 -39
- package/compat/Loader/Loader.tsx +32 -32
- package/compat/MagnifyGlass/MagnifyGlass.styl +33 -33
- package/compat/MagnifyGlass/MagnifyGlass.tsx +213 -213
- package/compat/MdAccordionGroup/MdAccordionGroup.tsx +53 -53
- package/compat/MdCheckbox/MdCheckbox.tsx +69 -69
- package/compat/MdField/MdField.tsx +100 -100
- package/compat/MdSelect/MdSelect.tsx +103 -103
- package/compat/MdSwitch/MdSwitch.tsx +29 -29
- package/compat/MdTabs/MdTabs.tsx +47 -47
- package/compat/MdTooltip/MdTooltip.tsx +31 -31
- package/compat/OfferDetailedSection/OfferDetailedSection.styl +31 -31
- package/compat/OfferDetailedSection/OfferDetailedSection.tsx +100 -100
- package/compat/OfferDetailedSection/partials/PanelConfig.styl +131 -131
- package/compat/OfferDetailedSection/partials/PanelConfig.tsx +121 -121
- package/compat/OfferPanel/AboAccordion/AboAccordion.styl +24 -24
- package/compat/OfferPanel/AboAccordion/AboAccordion.tsx +128 -128
- package/compat/OfferPanel/AboAccordion/AboColorOptions/AboColorOptions.styl +11 -11
- package/compat/OfferPanel/AboAccordion/AboColorOptions/AboColorOptions.tsx +35 -35
- package/compat/OfferPanel/AboAccordion/AboFaq/AboFaq.styl +20 -20
- package/compat/OfferPanel/AboAccordion/AboFaq/AboFaq.tsx +25 -25
- package/compat/OfferPanel/AboAccordion/AboIncludedServices/AboIncludedServices.styl +13 -13
- package/compat/OfferPanel/AboAccordion/AboIncludedServices/AboIncludedServices.tsx +39 -39
- package/compat/OfferPanel/MuiOfferPeriod/MuiOfferPeriod.tsx +42 -42
- package/compat/OfferPanel/OfferCheckboxGroup/CheckboxContainer.tsx +140 -140
- package/compat/OfferPanel/OfferCheckboxGroup/OfferCheckboxGroup.styl +144 -144
- package/compat/OfferPanel/OfferCheckboxGroup/OfferCheckboxGroup.tsx +23 -23
- package/compat/OfferPanel/OfferPanel.styl +193 -193
- package/compat/OfferPanel/OfferPanel.tsx +81 -81
- package/compat/OfferPanel/OfferPeriod/OfferPeriod.styl +24 -24
- package/compat/OfferPanel/OfferPeriod/OfferPeriod.tsx +45 -45
- package/compat/OfferPanel/RangeGroup/RangeGroup.tsx +39 -39
- package/compat/OfferRequestButtonWrapper/OfferRequestButtonWrapper.styl +119 -119
- package/compat/OfferRequestButtonWrapper/OfferRequestButtonWrapper.styled.tsx +33 -33
- package/compat/OfferRequestButtonWrapper/OfferRequestButtonWrapper.tsx +122 -122
- package/compat/PriceRating/PriceRating.styl +47 -47
- package/compat/PriceRating/PriceRating.tsx +64 -64
- package/compat/PriceRatingDetailed/PriceRatingDetailed.styl +120 -120
- package/compat/PriceRatingDetailed/PriceRatingDetailed.tsx +191 -191
- package/compat/PricingToolBlock/PricingTool.styl +6 -6
- package/compat/PricingToolBlock/PricingTool.tsx +29 -29
- package/compat/Radio/Radio.styl +115 -115
- package/compat/Radio/Radio.tsx +141 -141
- package/compat/Radio/RadioGroup.styl +5 -5
- package/compat/Radio/RadioGroup.tsx +65 -65
- package/compat/Range/Range.tsx +79 -79
- package/compat/RateSearchValue/RateSearchValue.styl +22 -22
- package/compat/RateSearchValue/RateSearchValue.tsx +55 -55
- package/compat/RateSearchWidget/RateSearchWidget.styl +106 -106
- package/compat/RateSearchWidget/RateSearchWidget.tsx +163 -163
- package/compat/ServiceBlockItem/ServiceBlockItem.styl +47 -47
- package/compat/ServiceBlockItem/ServiceBlockItem.tsx +25 -25
- package/compat/SkeletonItem/SkeletonItem.styl +80 -80
- package/compat/SkeletonItem/SkeletonItem.tsx +80 -80
- package/compat/StarButton/StarButton.styl +39 -39
- package/compat/StarButton/StarButton.tsx +31 -31
- package/compat/TabPanel/TabPanel.tsx +28 -28
- package/compat/TimePicker/TimePicker.styl +22 -22
- package/compat/TimePicker/TimePicker.tsx +156 -156
- package/compat/Tooltip/Tooltip.styl +117 -117
- package/compat/Tooltip/Tooltip.tsx +63 -63
- package/compat/Usp/Usp.styl +78 -78
- package/compat/Usp/Usp.tsx +30 -30
- package/compat/Usp/uspTranslate.ts +12 -12
- package/compat/VehicleDetailedSidebar/VehicleDetailedSidebar.styl +36 -36
- package/compat/VehicleDetailedSidebar/VehicleDetailedSidebar.tsx +178 -178
- package/compat/VehicleDetailedSidebar/partials/Price.styl +27 -27
- package/compat/VehicleDetailedSidebar/partials/Price.tsx +41 -41
- package/compat/VehicleDetailedSidebar/partials/PriceContent.styl +123 -123
- package/compat/VehicleDetailedSidebar/partials/PriceContent.tsx +128 -128
- package/compat/VehicleDetailedSidebar/partials/Properties.styl +58 -58
- package/compat/VehicleDetailedSidebar/partials/Properties.tsx +155 -155
- package/compat/VehicleDetailedSidebar/partials/Title.styl +35 -35
- package/compat/VehicleDetailedSidebar/partials/Title.tsx +33 -33
- package/compat/VehicleDetailedSlider/VehicleDetailedSlider.styl +146 -146
- package/compat/VehicleDetailedSlider/VehicleDetailedSlider.tsx +382 -382
- package/compat/VehicleDetailedSlider/partials/MobileStast.tsx +61 -61
- package/compat/VehicleDetailedSlider/partials/MobileStats.styl +71 -71
- package/compat/VehicleDetailedSlider/partials/PriceData.styl +50 -50
- package/compat/VehicleDetailedSlider/partials/PriceData.tsx +67 -67
- package/compat/VehicleDetailedSlider/partials/Stats.styl +80 -80
- package/compat/VehicleDetailedSlider/partials/Stats.tsx +62 -62
- package/compat/VehicleDetailedSlider/partials/Title.styl +44 -44
- package/compat/VehicleDetailedSlider/partials/Title.tsx +36 -36
- package/compat/VehicleFormattedPrice/VehicleFormattedPrice.styl +148 -148
- package/compat/VehicleFormattedPrice/VehicleFormattedPrice.tsx +163 -163
- package/compat/VehicleSlider/VehicleSlider.styl +62 -62
- package/compat/VehicleSlider/VehicleSlider.tsx +125 -125
- package/compat/VehicleSlider/VehicleSliderForSRL.tsx +175 -175
- package/compat/VehicleSmallCard/SeoText/SeoText.styl +8 -8
- package/compat/VehicleSmallCard/SeoText/SeoText.tsx +16 -16
- package/compat/VehicleSmallCard/VehicleData/VechiclePriceItem/VechiclePriceItem.styl +204 -204
- package/compat/VehicleSmallCard/VehicleData/VechiclePriceItem/VechiclePriceItem.tsx +199 -199
- package/compat/VehicleSmallCard/VehicleData/VehicleInfo/VehicleInfo.styl +273 -273
- package/compat/VehicleSmallCard/VehicleData/VehicleInfo/VehicleInfo.tsx +179 -179
- package/compat/VehicleSmallCard/VehicleData/VehiclePrice/VehiclePrice.styl +165 -165
- package/compat/VehicleSmallCard/VehicleData/VehiclePrice/VehiclePrice.tsx +299 -299
- package/compat/VehicleSmallCard/VehicleData/VehicleProperty/VehicleProperty.styl +32 -32
- package/compat/VehicleSmallCard/VehicleData/VehicleProperty/VehicleProperty.tsx +35 -35
- package/compat/VehicleSmallCard/VehicleData/VehicleTitle/VehicleTitle.styl +135 -135
- package/compat/VehicleSmallCard/VehicleData/VehicleTitle/VehicleTitle.tsx +91 -91
- package/compat/VehicleSmallCard/VehicleSmallCard.styl +309 -309
- package/compat/VehicleSmallCard/VehicleSmallCard.tsx +448 -448
- package/compat/index.ts +77 -77
- package/compat/legacy-theme/mixins.styl +93 -93
- package/compat/legacy-theme/priceReviewColors.styl +12 -12
- package/compat/legacy-theme/slider.global.styl +5 -5
- package/compat/legacy-theme/theme.styl +136 -136
- package/compat/withLabel/withLabel.styl +33 -33
- package/compat/withLabel/withLabel.tsx +44 -44
- package/compat/withStats/withStats.styl +174 -174
- package/compat/withStats/withStats.tsx +238 -238
- package/constants/RateSearchWidget.ts +15 -15
- package/constants/Search.ts +290 -290
- package/constants/SearchWidget.ts +108 -108
- package/constants/app.ts +223 -223
- package/constants/common.ts +127 -127
- package/constants/core.ts +131 -131
- package/constants/highlights.ts +165 -165
- package/constants/price.ts +9 -9
- package/constants/selectors.ts +38 -38
- package/constants.ts +91 -91
- package/data/DataTransformers.ts +88 -88
- package/data/fieldMapping.ts +342 -342
- package/data/toAutobahnCar.ts +442 -442
- package/iconsvg/IconSVG.tsx +30 -30
- package/iconsvg/IconSVGConfig.tsx +768 -768
- package/iconsvg/SVG/AboPartner.tsx +16 -16
- package/iconsvg/SVG/ArrowQuotes.tsx +22 -22
- package/iconsvg/SVG/CloseIcon.tsx +17 -17
- package/iconsvg/SVG/CloseIconSelect.tsx +18 -18
- package/iconsvg/SVG/ClosedLockIcon.tsx +18 -18
- package/iconsvg/SVG/DealerCheckIcon.tsx +18 -18
- package/iconsvg/SVG/DealerPopoverMail.tsx +21 -21
- package/iconsvg/SVG/DealerPopoverPhone.tsx +21 -21
- package/iconsvg/SVG/GrayPhoneIcon.tsx +22 -22
- package/iconsvg/SVG/InternetIcon.tsx +18 -18
- package/iconsvg/SVG/WhiteEnvelopeIcon.tsx +21 -21
- package/iconsvg/SVG/bodyType/Cabrio.tsx +23 -23
- package/iconsvg/SVG/bodyType/Coupe.tsx +22 -22
- package/iconsvg/SVG/bodyType/Minibus.tsx +22 -22
- package/iconsvg/SVG/bodyType/Other.tsx +25 -25
- package/iconsvg/SVG/bodyType/Sedan.tsx +22 -22
- package/iconsvg/SVG/bodyType/SelectorCategoryCombi.tsx +68 -68
- package/iconsvg/SVG/bodyType/SelectorCategoryCompact.tsx +20 -20
- package/iconsvg/SVG/bodyType/SelectorCategoryConvertible.tsx +68 -68
- package/iconsvg/SVG/bodyType/SelectorCategoryCoupe.tsx +60 -60
- package/iconsvg/SVG/bodyType/SelectorCategoryMinibus.tsx +64 -64
- package/iconsvg/SVG/bodyType/SelectorCategoryOther.tsx +60 -60
- package/iconsvg/SVG/bodyType/SelectorCategorySedan.tsx +69 -69
- package/iconsvg/SVG/bodyType/SelectorCategorySuv.tsx +77 -77
- package/iconsvg/SVG/bodyType/SelectorCategoryTransporter.tsx +68 -68
- package/iconsvg/SVG/bodyType/SelectorCategoryVan.tsx +72 -72
- package/iconsvg/SVG/bodyType/Small.tsx +23 -23
- package/iconsvg/SVG/bodyType/Suv.tsx +23 -23
- package/iconsvg/SVG/bodyType/Van.tsx +23 -23
- package/iconsvg/SVG/bodyType/Wagon.tsx +23 -23
- package/iconsvg/SVG/common/AddIcon.tsx +16 -16
- package/iconsvg/SVG/common/AddPlus.tsx +16 -16
- package/iconsvg/SVG/common/AddressDashboard.tsx +29 -29
- package/iconsvg/SVG/common/AddressIcon.tsx +20 -20
- package/iconsvg/SVG/common/ArrowDetailedAccordionBlue.tsx +26 -26
- package/iconsvg/SVG/common/ArrowDownBlue.tsx +21 -21
- package/iconsvg/SVG/common/ArrowSelect.tsx +23 -23
- package/iconsvg/SVG/common/ArrowStepDown.tsx +18 -18
- package/iconsvg/SVG/common/Autocheck.tsx +19 -19
- package/iconsvg/SVG/common/AutodeLogo.tsx +36 -36
- package/iconsvg/SVG/common/Avatar.tsx +17 -17
- package/iconsvg/SVG/common/BellGray.tsx +19 -19
- package/iconsvg/SVG/common/BigInfoTransparent.tsx +13 -13
- package/iconsvg/SVG/common/Browse.tsx +20 -20
- package/iconsvg/SVG/common/Calendar.tsx +24 -24
- package/iconsvg/SVG/common/CallbackDashboard.tsx +30 -30
- package/iconsvg/SVG/common/Camera.tsx +17 -17
- package/iconsvg/SVG/common/Car.tsx +16 -16
- package/iconsvg/SVG/common/CarBody.tsx +19 -19
- package/iconsvg/SVG/common/CarFront.tsx +43 -43
- package/iconsvg/SVG/common/CarInspectionCheck.tsx +21 -21
- package/iconsvg/SVG/common/CarInspectionScheme0.tsx +32 -32
- package/iconsvg/SVG/common/CarInspectionScheme1.tsx +53 -53
- package/iconsvg/SVG/common/CarInspectionScheme2.tsx +52 -52
- package/iconsvg/SVG/common/CarInspectionScheme3.tsx +32 -32
- package/iconsvg/SVG/common/CarInspectionScheme4.tsx +32 -32
- package/iconsvg/SVG/common/CarInspectionScheme5.tsx +32 -32
- package/iconsvg/SVG/common/CarInspectionScheme6.tsx +32 -32
- package/iconsvg/SVG/common/CarInspectionSchemeGeneral copy.tsx +24 -24
- package/iconsvg/SVG/common/CarInspectionSchemeGeneral.tsx +24 -24
- package/iconsvg/SVG/common/CheckBlue.tsx +18 -18
- package/iconsvg/SVG/common/CheckGreen.tsx +22 -22
- package/iconsvg/SVG/common/CheckMark.tsx +16 -16
- package/iconsvg/SVG/common/CheckWhite.tsx +19 -19
- package/iconsvg/SVG/common/Chevron.tsx +17 -17
- package/iconsvg/SVG/common/CircledCheckmark.tsx +25 -25
- package/iconsvg/SVG/common/Clock.tsx +18 -18
- package/iconsvg/SVG/common/Close.tsx +19 -19
- package/iconsvg/SVG/common/CloseGrey.tsx +22 -22
- package/iconsvg/SVG/common/ContactEmail.tsx +21 -21
- package/iconsvg/SVG/common/ContactPhone.tsx +28 -28
- package/iconsvg/SVG/common/CopyIcon copy.tsx +22 -22
- package/iconsvg/SVG/common/CopyIcon.tsx +22 -22
- package/iconsvg/SVG/common/CopyIconForDealer.tsx +21 -21
- package/iconsvg/SVG/common/DashboardIcon.tsx +20 -20
- package/iconsvg/SVG/common/DashboardQuestionMark.tsx +20 -20
- package/iconsvg/SVG/common/DealersIcon.tsx +20 -20
- package/iconsvg/SVG/common/DeleteCircle.tsx +31 -31
- package/iconsvg/SVG/common/DeleteV1Icon.tsx +18 -18
- package/iconsvg/SVG/common/DeliveryDashboard.tsx +25 -25
- package/iconsvg/SVG/common/DeliveryMail.tsx +24 -24
- package/iconsvg/SVG/common/Discount100De.tsx +18 -18
- package/iconsvg/SVG/common/Discount100En.tsx +19 -19
- package/iconsvg/SVG/common/Discount66De.tsx +28 -28
- package/iconsvg/SVG/common/Discount66En.tsx +20 -20
- package/iconsvg/SVG/common/DoneIcon.tsx +18 -18
- package/iconsvg/SVG/common/EditCircle.tsx +32 -32
- package/iconsvg/SVG/common/EditPriceIcon.tsx +20 -20
- package/iconsvg/SVG/common/EuroCurrency.tsx +18 -18
- package/iconsvg/SVG/common/Export.tsx +20 -20
- package/iconsvg/SVG/common/Eye.tsx +17 -17
- package/iconsvg/SVG/common/EyeCatch.tsx +19 -19
- package/iconsvg/SVG/common/FavoritesIcon.tsx +20 -20
- package/iconsvg/SVG/common/FilterRange.tsx +16 -16
- package/iconsvg/SVG/common/FilterToolBlack.tsx +23 -23
- package/iconsvg/SVG/common/FlagDe.tsx +21 -21
- package/iconsvg/SVG/common/FlagEn.tsx +43 -43
- package/iconsvg/SVG/common/GrayEnvelopeIcon.tsx +19 -19
- package/iconsvg/SVG/common/HamburgerMenuIcon.tsx +18 -18
- package/iconsvg/SVG/common/HandingOverDashboard.tsx +32 -32
- package/iconsvg/SVG/common/ImageSettingsIcon.tsx +20 -20
- package/iconsvg/SVG/common/Info.tsx +16 -16
- package/iconsvg/SVG/common/InfoIcon.tsx +17 -17
- package/iconsvg/SVG/common/InfoTransparent.tsx +18 -18
- package/iconsvg/SVG/common/LogoutIcon.tsx +20 -20
- package/iconsvg/SVG/common/MagnifyingGlass.tsx +31 -31
- package/iconsvg/SVG/common/MenuArrow.tsx +16 -16
- package/iconsvg/SVG/common/MenuGrey.tsx +22 -22
- package/iconsvg/SVG/common/MobileDeLogo.tsx +108 -108
- package/iconsvg/SVG/common/MyOrderIcon.tsx +20 -20
- package/iconsvg/SVG/common/MyVehiclesIcon.tsx +20 -20
- package/iconsvg/SVG/common/NewFlagDe.tsx +25 -25
- package/iconsvg/SVG/common/NewFlagEn.tsx +34 -34
- package/iconsvg/SVG/common/OrderReceivedDashboard.tsx +27 -27
- package/iconsvg/SVG/common/Package.tsx +30 -30
- package/iconsvg/SVG/common/Phone.tsx +18 -18
- package/iconsvg/SVG/common/PhoneGrey.tsx +22 -22
- package/iconsvg/SVG/common/PickupDashboard.tsx +30 -30
- package/iconsvg/SVG/common/Picture.tsx +24 -24
- package/iconsvg/SVG/common/Pictures.tsx +32 -32
- package/iconsvg/SVG/common/Plus.tsx +16 -16
- package/iconsvg/SVG/common/PlusNew.tsx +16 -16
- package/iconsvg/SVG/common/PreparationDashboard.tsx +29 -29
- package/iconsvg/SVG/common/ProfileDashboard.tsx +24 -24
- package/iconsvg/SVG/common/ProfileIcon.tsx +17 -17
- package/iconsvg/SVG/common/Question.tsx +19 -19
- package/iconsvg/SVG/common/QuestionBold.tsx +25 -25
- package/iconsvg/SVG/common/RegistrationDashboard.tsx +31 -31
- package/iconsvg/SVG/common/SavedSearchsIcon.tsx +20 -20
- package/iconsvg/SVG/common/Search.tsx +19 -19
- package/iconsvg/SVG/common/SearchGrey.tsx +22 -22
- package/iconsvg/SVG/common/SettingPenIcon.tsx +20 -20
- package/iconsvg/SVG/common/Share.tsx +16 -16
- package/iconsvg/SVG/common/ShoppingCart.tsx +18 -18
- package/iconsvg/SVG/common/SliderArrow.tsx +21 -21
- package/iconsvg/SVG/common/SliderEye.tsx +17 -17
- package/iconsvg/SVG/common/SortUpDownArrows.tsx +18 -18
- package/iconsvg/SVG/common/Speedometer.tsx +23 -23
- package/iconsvg/SVG/common/Star.tsx +20 -20
- package/iconsvg/SVG/common/StarNew.tsx +16 -16
- package/iconsvg/SVG/common/StarOutline.tsx +20 -20
- package/iconsvg/SVG/common/StarRoundedCorners.tsx +18 -18
- package/iconsvg/SVG/common/Stats.tsx +17 -17
- package/iconsvg/SVG/common/Step1.tsx +21 -21
- package/iconsvg/SVG/common/Step2.tsx +20 -20
- package/iconsvg/SVG/common/Step3.tsx +21 -21
- package/iconsvg/SVG/common/SupportCallbackIcon.tsx +20 -20
- package/iconsvg/SVG/common/Telephone.tsx +22 -22
- package/iconsvg/SVG/common/Time.tsx +17 -17
- package/iconsvg/SVG/common/Timetable.tsx +18 -18
- package/iconsvg/SVG/common/Trash.tsx +22 -22
- package/iconsvg/SVG/common/TrashRed.tsx +23 -23
- package/iconsvg/SVG/common/Upload.tsx +17 -17
- package/iconsvg/SVG/common/UserGrey.tsx +22 -22
- package/iconsvg/SVG/common/UserProfileIcon.tsx +20 -20
- package/iconsvg/SVG/common/ValidateFailure.tsx +21 -21
- package/iconsvg/SVG/common/ValidateSuccess.tsx +21 -21
- package/iconsvg/SVG/common/WarningTriangle.tsx +20 -20
- package/iconsvg/SVG/common/WhiteArrowUp.tsx +21 -21
- package/iconsvg/SVG/common/WhiteKey.tsx +21 -21
- package/iconsvg/SVG/common/WhitePeople.tsx +23 -23
- package/iconsvg/SVG/common/WhiteTick.tsx +19 -19
- package/iconsvg/SVG/flags/AT.tsx +19 -19
- package/iconsvg/SVG/flags/BE.tsx +20 -20
- package/iconsvg/SVG/flags/BG.tsx +20 -20
- package/iconsvg/SVG/flags/CH.tsx +22 -22
- package/iconsvg/SVG/flags/CZ.tsx +18 -18
- package/iconsvg/SVG/flags/DE.tsx +18 -18
- package/iconsvg/SVG/flags/DK.tsx +18 -18
- package/iconsvg/SVG/flags/ES.tsx +2585 -2585
- package/iconsvg/SVG/flags/FI.tsx +18 -18
- package/iconsvg/SVG/flags/FR.tsx +20 -20
- package/iconsvg/SVG/flags/GB.tsx +26 -26
- package/iconsvg/SVG/flags/HR.tsx +135 -135
- package/iconsvg/SVG/flags/HU.tsx +20 -20
- package/iconsvg/SVG/flags/IT.tsx +20 -20
- package/iconsvg/SVG/flags/LI.tsx +106 -106
- package/iconsvg/SVG/flags/LU.tsx +18 -18
- package/iconsvg/SVG/flags/NL.tsx +18 -18
- package/iconsvg/SVG/flags/NO.tsx +20 -20
- package/iconsvg/SVG/flags/PL.tsx +19 -19
- package/iconsvg/SVG/flags/RO.tsx +20 -20
- package/iconsvg/SVG/flags/RU.tsx +20 -20
- package/iconsvg/SVG/flags/SE.tsx +20 -20
- package/iconsvg/SVG/flags/SI.tsx +54 -54
- package/iconsvg/SVG/flags/SK.tsx +34 -34
- package/iconsvg/SVG/flags/UA.tsx +19 -19
- package/iconsvg/SVG/infoBlocks/14DaysReturnPolicy.tsx +18 -18
- package/iconsvg/SVG/infoBlocks/AlertInfo.tsx +19 -19
- package/iconsvg/SVG/infoBlocks/Autocheck.tsx +19 -19
- package/iconsvg/SVG/infoBlocks/CheckTheStory.tsx +22 -22
- package/iconsvg/SVG/infoBlocks/CustomerServiceWhite.tsx +16 -16
- package/iconsvg/SVG/infoBlocks/Guarantee.tsx +16 -16
- package/iconsvg/SVG/infoBlocks/OrderWhite.tsx +13 -13
- package/iconsvg/SVG/infoBlocks/PriceGuarantee.tsx +19 -19
- package/iconsvg/SVG/infoBlocks/PriceGuaranteeWhite.tsx +17 -17
- package/iconsvg/SVG/infoBlocks/TransportAndDelivery.tsx +21 -21
- package/iconsvg/SVG/infoBlocks/Warranty.tsx +23 -23
- package/iconsvg/SVG/popower/Mail.tsx +18 -18
- package/iconsvg/SVG/popower/Phone.tsx +19 -19
- package/iconsvg/SVG/popower/Schedule.tsx +18 -18
- package/iconsvg/SVG/searchWidgets/drive/AAllWheelDrive.tsx +26 -26
- package/iconsvg/SVG/searchWidgets/drive/AllWheelDrive.tsx +26 -26
- package/iconsvg/SVG/searchWidgets/drive/FrontWheelDrive.tsx +25 -25
- package/iconsvg/SVG/searchWidgets/drive/OtherWheelDrive.tsx +26 -26
- package/iconsvg/SVG/searchWidgets/drive/RearWheelDrive.tsx +25 -25
- package/iconsvg/SVG/searchWidgets/fuel/FuelDiesel.tsx +18 -18
- package/iconsvg/SVG/searchWidgets/fuel/FuelElectrical.tsx +19 -19
- package/iconsvg/SVG/searchWidgets/fuel/FuelElectricalDiesel.tsx +34 -34
- package/iconsvg/SVG/searchWidgets/fuel/FuelElectricalPetrol.tsx +20 -20
- package/iconsvg/SVG/searchWidgets/fuel/FuelEthanol.tsx +53 -53
- package/iconsvg/SVG/searchWidgets/fuel/FuelHydrogen.tsx +24 -24
- package/iconsvg/SVG/searchWidgets/fuel/FuelLpg.tsx +24 -24
- package/iconsvg/SVG/searchWidgets/fuel/FuelNaturalGas.tsx +23 -23
- package/iconsvg/SVG/searchWidgets/fuel/FuelOther.tsx +28 -28
- package/iconsvg/SVG/searchWidgets/fuel/FuelPetrol.tsx +18 -18
- package/iconsvg/SVG/settings/Account.tsx +17 -17
- package/iconsvg/SVG/settings/DataUsage.tsx +17 -17
- package/iconsvg/SVG/settings/MarketResearch.tsx +17 -17
- package/iconsvg/SVG/settings/Newsletter.tsx +17 -17
- package/iconsvg/SVG/settings/RemoveEye.tsx +17 -17
- package/iconsvg/SVG/settings/RemoveEyeOff.tsx +17 -17
- package/iconsvg/SVG/settings/Security.tsx +17 -17
- package/iconsvg/SVG/settings/Warning.tsx +17 -17
- package/iconsvg/SVG/slider/360.tsx +23 -23
- package/iconsvg/SVG/slider/360New.tsx +20 -20
- package/iconsvg/SVG/slider/Checked.tsx +19 -19
- package/iconsvg/SVG/slider/DiscountRibbon.tsx +51 -51
- package/iconsvg/SVG/slider/DiscountRibbonMobile.tsx +47 -47
- package/iconsvg/SVG/slider/DiscountRibbonNew.tsx +29 -29
- package/iconsvg/SVG/slider/Easy.tsx +19 -19
- package/iconsvg/SVG/slider/Fullscreen.tsx +16 -16
- package/iconsvg/SVG/slider/FullscreenWindow.tsx +23 -23
- package/iconsvg/SVG/slider/Safe.tsx +19 -19
- package/iconsvg/SVG/slider/ToBuy.tsx +19 -19
- package/iconsvg/SVG/slider/YoutubeButton.tsx +26 -26
- package/iconsvg/SVG/slider/ZoomIn.tsx +21 -21
- package/iconsvg/SVG/slider/ZoomOut.tsx +21 -21
- package/iconsvg/SVG/social/AndroidLogo.tsx +18 -18
- package/iconsvg/SVG/social/Apple.tsx +19 -19
- package/iconsvg/SVG/social/FacebookF.tsx +19 -19
- package/iconsvg/SVG/social/GooglePlusG.tsx +19 -19
- package/iconsvg/SVG/social/Instagram.tsx +17 -17
- package/iconsvg/SVG/social/Pinterest.tsx +19 -19
- package/iconsvg/SVG/social/SharingInterface.tsx +21 -21
- package/iconsvg/SVG/social/Twitter.tsx +19 -19
- package/iconsvg/SVG/social/Youtube.tsx +19 -19
- package/iconsvg/SVG/vehicle/AboIcon.tsx +11 -11
- package/iconsvg/SVG/vehicle/Abs.tsx +24 -24
- package/iconsvg/SVG/vehicle/AirCond.tsx +34 -34
- package/iconsvg/SVG/vehicle/Airbag.tsx +21 -21
- package/iconsvg/SVG/vehicle/AuxiliaryHeating.tsx +23 -23
- package/iconsvg/SVG/vehicle/Bluetooth.tsx +22 -22
- package/iconsvg/SVG/vehicle/BuyIcon.tsx +15 -15
- package/iconsvg/SVG/vehicle/CarAvailability.tsx +22 -22
- package/iconsvg/SVG/vehicle/CarInspectionCheck.tsx +21 -21
- package/iconsvg/SVG/vehicle/CarInspectionCross.tsx +21 -21
- package/iconsvg/SVG/vehicle/CheckoutChecked.tsx +15 -15
- package/iconsvg/SVG/vehicle/Co2.tsx +24 -24
- package/iconsvg/SVG/vehicle/CruiseControl.tsx +25 -25
- package/iconsvg/SVG/vehicle/Date.tsx +21 -21
- package/iconsvg/SVG/vehicle/Delivery.tsx +12 -12
- package/iconsvg/SVG/vehicle/Diesel.tsx +28 -28
- package/iconsvg/SVG/vehicle/Display.tsx +25 -25
- package/iconsvg/SVG/vehicle/Done.tsx +11 -11
- package/iconsvg/SVG/vehicle/Door.tsx +19 -19
- package/iconsvg/SVG/vehicle/DoubleDone.tsx +18 -18
- package/iconsvg/SVG/vehicle/EditIcon.tsx +11 -11
- package/iconsvg/SVG/vehicle/EmergencyBrakeAssistant.tsx +27 -27
- package/iconsvg/SVG/vehicle/Eps.tsx +24 -24
- package/iconsvg/SVG/vehicle/Esp.tsx +24 -24
- package/iconsvg/SVG/vehicle/FinancingIcon.tsx +20 -20
- package/iconsvg/SVG/vehicle/FuelConsumption.tsx +20 -20
- package/iconsvg/SVG/vehicle/Fueling.tsx +22 -22
- package/iconsvg/SVG/vehicle/FullDrive.tsx +24 -24
- package/iconsvg/SVG/vehicle/GasElectric.tsx +28 -28
- package/iconsvg/SVG/vehicle/HandsFreePhoneSystem.tsx +24 -24
- package/iconsvg/SVG/vehicle/Kw.tsx +23 -23
- package/iconsvg/SVG/vehicle/LastCrawledAt.tsx +22 -22
- package/iconsvg/SVG/vehicle/LeasingIcon.tsx +14 -14
- package/iconsvg/SVG/vehicle/Led.tsx +24 -24
- package/iconsvg/SVG/vehicle/LightSensor.tsx +22 -22
- package/iconsvg/SVG/vehicle/LinkAlternative.tsx +15 -15
- package/iconsvg/SVG/vehicle/Location.tsx +17 -17
- package/iconsvg/SVG/vehicle/MinusIcon.tsx +11 -11
- package/iconsvg/SVG/vehicle/Mirror.tsx +19 -19
- package/iconsvg/SVG/vehicle/Navigation.tsx +27 -27
- package/iconsvg/SVG/vehicle/NewCar.tsx +21 -21
- package/iconsvg/SVG/vehicle/NoImage.tsx +17 -17
- package/iconsvg/SVG/vehicle/Owner.tsx +18 -18
- package/iconsvg/SVG/vehicle/PanoramicRoof.tsx +23 -23
- package/iconsvg/SVG/vehicle/ParkingAssistants.tsx +24 -24
- package/iconsvg/SVG/vehicle/PickYourself.tsx +18 -18
- package/iconsvg/SVG/vehicle/PlusIcon.tsx +11 -11
- package/iconsvg/SVG/vehicle/Power.tsx +21 -21
- package/iconsvg/SVG/vehicle/Ps.tsx +19 -19
- package/iconsvg/SVG/vehicle/Question.tsx +24 -24
- package/iconsvg/SVG/vehicle/Seats.tsx +21 -21
- package/iconsvg/SVG/vehicle/SeatsHeating.tsx +26 -26
- package/iconsvg/SVG/vehicle/SelectorGearboxAutomatic.tsx +24 -24
- package/iconsvg/SVG/vehicle/SelectorGearboxManualShift.tsx +19 -19
- package/iconsvg/SVG/vehicle/SelectorGearboxOther.tsx +24 -24
- package/iconsvg/SVG/vehicle/SelectorGearboxSemiAutomatic.tsx +30 -30
- package/iconsvg/SVG/vehicle/SelectorInteriorTypeAlcantara.tsx +225 -225
- package/iconsvg/SVG/vehicle/SelectorInteriorTypeFabric.tsx +29 -29
- package/iconsvg/SVG/vehicle/SelectorInteriorTypeFullLeather.tsx +29 -29
- package/iconsvg/SVG/vehicle/SelectorInteriorTypeOther.tsx +26 -26
- package/iconsvg/SVG/vehicle/SelectorInteriorTypePartLeather.tsx +142 -142
- package/iconsvg/SVG/vehicle/SelectorInteriorTypeSyntheticLeather.tsx +27 -27
- package/iconsvg/SVG/vehicle/SelectorInteriorTypeVelours.tsx +39 -39
- package/iconsvg/SVG/vehicle/Shield.tsx +39 -39
- package/iconsvg/SVG/vehicle/Speedometer.tsx +17 -17
- package/iconsvg/SVG/vehicle/StartStopSystem.tsx +30 -30
- package/iconsvg/SVG/vehicle/Stop.tsx +25 -25
- package/iconsvg/SVG/vehicle/TickMark.tsx +21 -21
- package/iconsvg/SVG/vehicle/TrafficSignRecognition.tsx +29 -29
- package/iconsvg/SVG/vehicle/TrailerCoupling.tsx +24 -24
- package/iconsvg/SVG/vehicle/Transmission.tsx +27 -27
- package/iconsvg/SVG/vehicle/Wheel.tsx +43 -43
- package/iconsvg/SVG/vehicle/Wipers.tsx +22 -22
- package/iconsvg/SVG/vehicle/Wrench.tsx +17 -17
- package/iconsvg/index.ts +7 -7
- package/index.ts +18 -18
- package/locales/de/autobahn.json +671 -671
- package/locales/en/autobahn.json +671 -671
- package/locales/legacy/SearchDetailedPage.ts +205 -205
- package/locales/legacy/cbd.ts +719 -719
- package/locales/legacy/common.ts +109 -109
- package/locales/legacy/data.ts +1118 -1118
- package/locales/legacy/priceRating.ts +10 -10
- package/locales/legacy/sortingOptions.ts +21 -21
- package/media/images/raiting-stars-min.svg +7 -7
- package/media/images/refresh-button.svg +49 -49
- package/media/images/user-icon.svg +16 -16
- package/media/svg/infoBlocks/whyAutodeIconSet-F.svg +9 -9
- package/media/svg/infoBlocks/whyAutodeIconSet-G.svg +8 -8
- package/media/svg/infoBlocks/whyAutodeIconSet-Q.svg +6 -6
- package/media/svg/infoBlocks/whyAutodeIconSet-R.svg +9 -9
- package/media/svg/usp/icon1.svg +16 -16
- package/media/svg/usp/icon2.svg +13 -13
- package/media/svg/usp/icon3.svg +10 -10
- package/media/svg/usp/icon4.svg +14 -14
- package/package.json +30 -30
- package/schemes/AggregationScheme.ts +73 -73
- package/searchFilters/filterOptions.ts +119 -119
- package/searchFilters/filterProps.ts +35 -35
- package/searchFilters/index.ts +15 -15
- package/theme/autobahn.css +25 -25
- package/theme/colors_and_type.css +113 -113
- package/theme/md3.css +2034 -2034
- package/types/types.ts +373 -373
- package/types.ts +8 -8
- package/ui/Accordion.tsx +134 -134
- package/ui/Button.tsx +62 -62
- package/ui/CardCarousel.tsx +133 -133
- package/ui/Chip.tsx +29 -29
- package/ui/DatePicker.tsx +145 -145
- package/ui/Dropdown.tsx +205 -205
- package/ui/FileUpload.tsx +154 -154
- package/ui/FormCheckbox.tsx +79 -79
- package/ui/FormDropdown.tsx +327 -327
- package/ui/FormField.tsx +88 -88
- package/ui/FormRadio.tsx +93 -93
- package/ui/FormRadioGroup.tsx +106 -106
- package/ui/Icon.tsx +391 -391
- package/ui/IconButton.tsx +44 -44
- package/ui/Modal.tsx +187 -187
- package/ui/Pagination.tsx +140 -140
- package/ui/Price.tsx +25 -25
- package/ui/PriceEvalBar.tsx +48 -48
- package/ui/SaveChip.tsx +34 -34
- package/ui/Segmented.tsx +48 -48
- package/ui/Skeleton.tsx +75 -75
- package/ui/Spec.tsx +24 -24
- package/ui/Stars.tsx +29 -29
- package/ui/StatChip.tsx +21 -21
- package/ui/Stepper.tsx +106 -106
- package/ui/Switch.tsx +65 -65
- package/ui/Table.tsx +156 -156
- package/ui/Textarea.tsx +58 -58
- package/ui/index.ts +88 -88
- package/utils/CommonUtils.ts +648 -648
- package/utils/DateUtils.ts +101 -101
- package/utils/index.ts +74 -74
- package/utils/power.ts +11 -11
- package/utils/query.ts +21 -21
- package/vdp/FinanceCalculator.tsx +465 -465
- package/vdp/VdpModules.tsx +1490 -1490
- package/vdp/VdpViewer.tsx +209 -139
- package/vdp/index.ts +75 -75
- package/vdp/types.ts +197 -197
- package/vehiclesProps/cbdSelectors.ts +117 -117
- package/vehiclesProps/decoratedLightProps.tsx +572 -572
- package/vehiclesProps/decoratedProps.tsx +1746 -1746
- package/vehiclesProps/equipment.ts +129 -129
- package/vehiclesProps/vehicleDetails.ts +428 -428
- package/widgets/SearchBox.tsx +256 -256
- package/widgets/index.ts +5 -5
package/cards/cardParts.tsx
CHANGED
|
@@ -1,489 +1,489 @@
|
|
|
1
|
-
// Autobahn (auto.de redesign) — car-card helper parts.
|
|
2
|
-
//
|
|
3
|
-
// Typed React 18 + TS port of the helper components from the design kit
|
|
4
|
-
// (docs/redesign/design-source/ui_kits/website/CarCard.jsx). Pure /
|
|
5
|
-
// presentational: every value comes from the `AutobahnCar` prop, there is no
|
|
6
|
-
// store / API / `window` access. The design kit's `window.CO2_CLASSES` /
|
|
7
|
-
// `window.CO2_COLORS` globals are replaced by the shared autobahn constants.
|
|
8
|
-
//
|
|
9
|
-
// These render correctly only inside a `<div className="md3">` subtree (the
|
|
10
|
-
// `.pricepaths`, `.pp-*`, `.specstrip`, `.imglabel` … classes are scoped there).
|
|
11
|
-
//
|
|
12
|
-
// NEEDS-DATA discipline (the point of the ticket): wherever the design showed a
|
|
13
|
-
// figure we have no real source for, these helpers render the NEEDS_DATA_LABEL
|
|
14
|
-
// ('Daten folgt') marker instead of inventing a number — see PricePaths'
|
|
15
|
-
// market band and PpActivity. DropChip renders nothing when there is no drop.
|
|
16
|
-
|
|
17
|
-
import React from 'react';
|
|
18
|
-
import { TranslationFunction } from '../types';
|
|
19
|
-
import { defaultConfig } from '@bytebrand/fe-financing';
|
|
20
|
-
|
|
21
|
-
import { Icon } from '../ui';
|
|
22
|
-
import { CO2_CLASSES, CO2_COLORS, TIER_COLORS, TIER_KEYS } from '../constants';
|
|
23
|
-
import { AutobahnCar } from './types';
|
|
24
|
-
|
|
25
|
-
// Default financing/leasing terms shown under the rate (the SRP card has no per-car selector, so
|
|
26
|
-
// these are the same default config the displayed rate is computed with — consistent, not invented).
|
|
27
|
-
const _finCfg = (defaultConfig as any).financing || {};
|
|
28
|
-
const _leasCfg = (defaultConfig as any).leasing || {};
|
|
29
|
-
/** financing terms inputs (rendered via t in PricePaths), e.g. "0 € Anzahlung · 48 Monate Laufzeit". */
|
|
30
|
-
const _finFirstInstallment = (_finCfg.firstInstallment || 0).toLocaleString('de-DE');
|
|
31
|
-
const _finMonths = _finCfg.rawPaybackPeriod || _finCfg.paybackPeriod || 48;
|
|
32
|
-
/** leasing terms inputs (only when the config carries mileage + period), e.g. "10.000 km/Jahr · 48 Monate". */
|
|
33
|
-
const _leasPeriod = _leasCfg.rawPaybackPeriod || _leasCfg.paybackPeriod;
|
|
34
|
-
const _leasMileage = _leasCfg.annualMileage || _leasCfg.mileage;
|
|
35
|
-
const _hasLeasTerms = Boolean(_leasMileage && _leasPeriod);
|
|
36
|
-
const _leasMileageStr = _leasMileage ? Number(_leasMileage).toLocaleString('de-DE') : '';
|
|
37
|
-
|
|
38
|
-
/** mileage → 'X.XXX km' (de-DE grouping). */
|
|
39
|
-
export function fmtKm(n: number): string {
|
|
40
|
-
return n.toLocaleString('de-DE') + ' km';
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Monthly rate → de-DE integer euro string (e.g. 402.12 → "402"). Rates are shown as whole
|
|
45
|
-
* euros like the legacy card ("271 €"); a raw float printed directly rendered as "402.12 €",
|
|
46
|
-
* which a German reader parses as 402.120 — fixed here with rounding + de-DE grouping.
|
|
47
|
-
*/
|
|
48
|
-
export function fmtRate(n: number): string {
|
|
49
|
-
return Math.round(n).toLocaleString('de-DE');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** strip a trailing parenthetical, e.g. '110 kW (150 PS)' → '110 kW'. */
|
|
53
|
-
function stripParen(s: string): string {
|
|
54
|
-
return s.replace(/\s*\(.*\)/, '');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Compact spec for cards: short fuel/gear labels so all five values fit ONE line
|
|
58
|
-
// without clipping.
|
|
59
|
-
const FUEL_SHORT: Record<string, string> = {
|
|
60
|
-
'Plug-in-Hybrid': 'Hybrid',
|
|
61
|
-
Benzin: 'Benzin',
|
|
62
|
-
Diesel: 'Diesel',
|
|
63
|
-
Elektro: 'Elektro',
|
|
64
|
-
};
|
|
65
|
-
const GEAR_SHORT: Record<string, string> = {
|
|
66
|
-
Automatik: 'Automatik',
|
|
67
|
-
Schaltgetriebe: 'Manuell',
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
/** five compact spec values for the grid card's one-line spec strip. */
|
|
71
|
-
export function cardSpec(car: AutobahnCar): string[] {
|
|
72
|
-
const fuel = stripParen(car.fuel);
|
|
73
|
-
return [
|
|
74
|
-
fmtKm(car.km),
|
|
75
|
-
car.reg,
|
|
76
|
-
stripParen(car.power),
|
|
77
|
-
FUEL_SHORT[fuel] || fuel,
|
|
78
|
-
GEAR_SHORT[car.gear] || car.gear,
|
|
79
|
-
];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export interface SpecLineProps {
|
|
83
|
-
items: string[];
|
|
84
|
-
size?: number;
|
|
85
|
-
/** never wrap — clip overflow with ellipsis (single line). */
|
|
86
|
-
oneLine?: boolean;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** Compact pipe-separated spec line (AutoScout pattern). */
|
|
90
|
-
export const SpecLine: React.FunctionComponent<SpecLineProps> = ({ items, size = 12.5, oneLine = false }) => (
|
|
91
|
-
<div
|
|
92
|
-
style={{
|
|
93
|
-
display: 'flex',
|
|
94
|
-
flexWrap: oneLine ? 'nowrap' : 'wrap',
|
|
95
|
-
alignItems: 'center',
|
|
96
|
-
gap: '2px 8px',
|
|
97
|
-
fontSize: size,
|
|
98
|
-
color: 'var(--md-on-surface-variant)',
|
|
99
|
-
...(oneLine ? { overflow: 'hidden', whiteSpace: 'nowrap' } : {}),
|
|
100
|
-
}}
|
|
101
|
-
>
|
|
102
|
-
{items.map((v, i) => (
|
|
103
|
-
<React.Fragment key={i}>
|
|
104
|
-
{i > 0 && <span style={{ color: 'var(--md-outline-variant)', flex: 'none' }}>·</span>}
|
|
105
|
-
<span style={oneLine ? { whiteSpace: 'nowrap' } : undefined}>{v}</span>
|
|
106
|
-
</React.Fragment>
|
|
107
|
-
))}
|
|
108
|
-
</div>
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
export interface PriceBlockProps {
|
|
112
|
-
car: AutobahnCar;
|
|
113
|
-
align?: 'left' | 'right';
|
|
114
|
-
t: TranslationFunction;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** Price block — rate-first (monthly is the lead), purchase price secondary. */
|
|
118
|
-
export const PriceBlock: React.FunctionComponent<PriceBlockProps> = ({ car, align = 'left', t }) => {
|
|
119
|
-
const showStrike = car.savings != null && car.savings > 600 && car.listPrice != null;
|
|
120
|
-
return (
|
|
121
|
-
<div style={{ textAlign: align }}>
|
|
122
|
-
<div
|
|
123
|
-
style={{
|
|
124
|
-
display: 'flex',
|
|
125
|
-
alignItems: 'baseline',
|
|
126
|
-
gap: 5,
|
|
127
|
-
justifyContent: align === 'right' ? 'flex-end' : 'flex-start',
|
|
128
|
-
}}
|
|
129
|
-
>
|
|
130
|
-
<span
|
|
131
|
-
className="num"
|
|
132
|
-
style={{
|
|
133
|
-
fontWeight: 700,
|
|
134
|
-
fontSize: 26,
|
|
135
|
-
color: 'var(--md-primary)',
|
|
136
|
-
letterSpacing: '-.6px',
|
|
137
|
-
whiteSpace: 'nowrap',
|
|
138
|
-
}}
|
|
139
|
-
>
|
|
140
|
-
{fmtRate(car.monthly)} €
|
|
141
|
-
</span>
|
|
142
|
-
<span className="md-label-sm">{t('autobahn:card.price.perMonth')}</span>
|
|
143
|
-
</div>
|
|
144
|
-
<div
|
|
145
|
-
className="md-label-sm"
|
|
146
|
-
style={{
|
|
147
|
-
marginTop: 1,
|
|
148
|
-
display: 'flex',
|
|
149
|
-
flexWrap: 'wrap',
|
|
150
|
-
gap: '0 6px',
|
|
151
|
-
justifyContent: align === 'right' ? 'flex-end' : 'flex-start',
|
|
152
|
-
}}
|
|
153
|
-
>
|
|
154
|
-
<span style={{ whiteSpace: 'nowrap' }}>
|
|
155
|
-
{t('autobahn:card.price.purchasePrice')}{' '}
|
|
156
|
-
<b style={{ color: 'var(--md-on-surface)' }}>{car.price.toLocaleString('de-DE')} €</b>
|
|
157
|
-
</span>
|
|
158
|
-
{showStrike && (
|
|
159
|
-
<span
|
|
160
|
-
style={{
|
|
161
|
-
color: 'var(--md-on-surface-variant)',
|
|
162
|
-
textDecoration: 'line-through',
|
|
163
|
-
whiteSpace: 'nowrap',
|
|
164
|
-
}}
|
|
165
|
-
>
|
|
166
|
-
{car.listPrice!.toLocaleString('de-DE')} €
|
|
167
|
-
</span>
|
|
168
|
-
)}
|
|
169
|
-
</div>
|
|
170
|
-
</div>
|
|
171
|
-
);
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
export interface DropChipProps {
|
|
175
|
-
/** real price reduction in EUR; when undefined the chip renders nothing. */
|
|
176
|
-
amount?: number;
|
|
177
|
-
t: TranslationFunction;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Price-drop chip — a REAL reduction on this car. White pill so it doesn't
|
|
182
|
-
* compete with the solid green rating chip. Renders nothing when there is no
|
|
183
|
-
* drop (we never fabricate a reduction).
|
|
184
|
-
*/
|
|
185
|
-
export const DropChip: React.FunctionComponent<DropChipProps> = ({ amount, t }) => {
|
|
186
|
-
if (amount == null) {
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
189
|
-
return (
|
|
190
|
-
<span
|
|
191
|
-
style={{
|
|
192
|
-
display: 'inline-flex',
|
|
193
|
-
alignItems: 'center',
|
|
194
|
-
gap: 3,
|
|
195
|
-
height: 22,
|
|
196
|
-
padding: '0 8px',
|
|
197
|
-
borderRadius: 'var(--md-r-full)',
|
|
198
|
-
background: 'rgba(255,255,255,.94)',
|
|
199
|
-
color: 'var(--md-good)',
|
|
200
|
-
fontSize: 10.5,
|
|
201
|
-
fontWeight: 700,
|
|
202
|
-
boxShadow: 'var(--md-e1)',
|
|
203
|
-
whiteSpace: 'nowrap',
|
|
204
|
-
}}
|
|
205
|
-
>
|
|
206
|
-
<Icon name="trending-down" size={12} stroke={2.4} color="var(--md-good)" />
|
|
207
|
-
{t('autobahn:card.price.priceDropAmount', { amount: amount.toLocaleString('de-DE') })}
|
|
208
|
-
</span>
|
|
209
|
-
);
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
/** shared deal-badge pill style (NEU/drop/percent/zero-down all share the same compact shape). */
|
|
213
|
-
const dealPill: React.CSSProperties = {
|
|
214
|
-
display: 'inline-flex',
|
|
215
|
-
alignItems: 'center',
|
|
216
|
-
height: 22,
|
|
217
|
-
padding: '0 8px',
|
|
218
|
-
borderRadius: 'var(--md-r-full)',
|
|
219
|
-
fontSize: 10.5,
|
|
220
|
-
fontWeight: 700,
|
|
221
|
-
boxShadow: 'var(--md-e1)',
|
|
222
|
-
whiteSpace: 'nowrap',
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
/** Percent price-drop chip (−X%). Red pill — pairs with DropChip. Renders nothing without a real pct. */
|
|
226
|
-
export const PercentChip: React.FunctionComponent<{ pct?: number }> = ({ pct }) => {
|
|
227
|
-
if (pct == null || pct <= 0) return null;
|
|
228
|
-
return <span style={{ ...dealPill, background: 'var(--md-error)', color: '#fff' }}>−{pct}%</span>;
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Zero-down-payment chip (offer.showDownPayment). Cyan pill. Spells out "0 € Anzahlung" where
|
|
233
|
-
* there is room (grid cards / wide image) and uses the compact "0 € Anz." on the narrow list-card
|
|
234
|
-
* image, so the badge row always stays on ONE line on both desktop and mobile.
|
|
235
|
-
*/
|
|
236
|
-
export const ZeroDownChip: React.FunctionComponent<{ show?: boolean; compact?: boolean; t: TranslationFunction }> = ({
|
|
237
|
-
show,
|
|
238
|
-
compact,
|
|
239
|
-
t,
|
|
240
|
-
}) => {
|
|
241
|
-
if (!show) return null;
|
|
242
|
-
return (
|
|
243
|
-
<span style={{ ...dealPill, background: 'var(--md-accent-container)', color: 'var(--md-on-accent-container)' }}>
|
|
244
|
-
{compact ? t('autobahn:card.price.zeroDownCompact') : t('autobahn:card.price.zeroDown')}
|
|
245
|
-
</span>
|
|
246
|
-
);
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
interface InlineClsProps {
|
|
250
|
-
/** CO₂ class letter, e.g. 'B'. */
|
|
251
|
-
c: string;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/** Small inline colored CO₂-class letter (keeps EnVKV line a fixed height). */
|
|
255
|
-
const InlineCls: React.FunctionComponent<InlineClsProps> = ({ c }) => {
|
|
256
|
-
const i = Math.max(0, CO2_CLASSES.indexOf(c as (typeof CO2_CLASSES)[number]));
|
|
257
|
-
const color = CO2_COLORS[i] || 'var(--md-outline)';
|
|
258
|
-
return (
|
|
259
|
-
<span
|
|
260
|
-
style={{
|
|
261
|
-
display: 'inline-flex',
|
|
262
|
-
width: 14,
|
|
263
|
-
height: 14,
|
|
264
|
-
borderRadius: 3,
|
|
265
|
-
background: color,
|
|
266
|
-
color: '#fff',
|
|
267
|
-
fontSize: 9.5,
|
|
268
|
-
fontWeight: 800,
|
|
269
|
-
alignItems: 'center',
|
|
270
|
-
justifyContent: 'center',
|
|
271
|
-
verticalAlign: '-2px',
|
|
272
|
-
margin: '0 1px',
|
|
273
|
-
}}
|
|
274
|
-
>
|
|
275
|
-
{c}
|
|
276
|
-
</span>
|
|
277
|
-
);
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
export interface EcoLineProps {
|
|
281
|
-
car: AutobahnCar;
|
|
282
|
-
style?: React.CSSProperties;
|
|
283
|
-
t: TranslationFunction;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Pkw-EnVKV legal line — full consumption + CO₂ + class. REQUIRED on every
|
|
288
|
-
* German car ad and must stay visible. Quiet fine-print footnote with a fixed
|
|
289
|
-
* min-height so all cards stay the same height. Handles the PHEV dual-figure
|
|
290
|
-
* variant when a `phev` block is present.
|
|
291
|
-
*/
|
|
292
|
-
export const EcoLine: React.FunctionComponent<EcoLineProps> = ({ car, style, t }) => {
|
|
293
|
-
const wrap: React.CSSProperties = {
|
|
294
|
-
borderTop: '1px solid var(--md-outline-variant)',
|
|
295
|
-
paddingTop: 8,
|
|
296
|
-
minHeight: 41,
|
|
297
|
-
fontSize: 11,
|
|
298
|
-
lineHeight: 1.5,
|
|
299
|
-
color: 'var(--md-on-surface-variant)',
|
|
300
|
-
...style,
|
|
301
|
-
};
|
|
302
|
-
if (car.phev) {
|
|
303
|
-
const p = car.phev;
|
|
304
|
-
return (
|
|
305
|
-
<div style={wrap}>
|
|
306
|
-
{p.consEl}{' '}
|
|
307
|
-
{t('autobahn:card.eco.phevConsumptionLine', {
|
|
308
|
-
consFuel: p.consFuel,
|
|
309
|
-
consDis: p.consDis,
|
|
310
|
-
co2w: p.co2w,
|
|
311
|
-
co2classW: p.co2classW,
|
|
312
|
-
co2classD: p.co2classD,
|
|
313
|
-
})}
|
|
314
|
-
</div>
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
return (
|
|
318
|
-
<div style={wrap}>
|
|
319
|
-
{t('autobahn:card.eco.consumptionLine', { cons: car.cons, co2: car.co2, co2class: car.co2class })}
|
|
320
|
-
</div>
|
|
321
|
-
);
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
export interface PricePathsProps {
|
|
325
|
-
car: AutobahnCar;
|
|
326
|
-
t: TranslationFunction;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/** Map the AutobahnCar rating tone → meter segment index (0 best … 4 worst). */
|
|
330
|
-
const TONE_TO_TIER: Record<string, number> = { top: 0, good: 1, fair: 2, raised: 3, high: 4 };
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Segmented Preisbewertung meter + label. Extracted from PricePaths so the SRP price
|
|
334
|
-
* column (Option A layout) can pin it to the BOTTOM of the column, separate from the
|
|
335
|
-
* grouped price figures at the top. Renders nothing when there is no rating/band
|
|
336
|
-
* (needs-data: we never draw a fake market position).
|
|
337
|
-
*/
|
|
338
|
-
export const PriceMeter: React.FunctionComponent<{ car: AutobahnCar; t: TranslationFunction }> = ({ car, t }) => {
|
|
339
|
-
const hasMarket =
|
|
340
|
-
car.marketLow != null && car.marketAvg != null && car.marketHigh != null && car.marketHigh > car.marketLow;
|
|
341
|
-
const seg = (tier: number) => (
|
|
342
|
-
<div className="pp-meter">
|
|
343
|
-
{[0, 1, 2, 3, 4].map((s) => (
|
|
344
|
-
<span key={s} className="pp-seg" style={{ background: TIER_COLORS[s], opacity: s === tier ? 1 : 0.26 }} />
|
|
345
|
-
))}
|
|
346
|
-
</div>
|
|
347
|
-
);
|
|
348
|
-
if (hasMarket) {
|
|
349
|
-
const delta = car.marketAvg! - car.price;
|
|
350
|
-
const good = delta > 150;
|
|
351
|
-
const mpos = Math.max(0, Math.min(1, (car.price - car.marketLow!) / (car.marketHigh! - car.marketLow!)));
|
|
352
|
-
return (
|
|
353
|
-
<div className="pp-meter-block">
|
|
354
|
-
{seg(Math.round(mpos * 4))}
|
|
355
|
-
<span className={'pp-mlabel' + (good ? ' pp-mlabel--good' : '')}>
|
|
356
|
-
{good
|
|
357
|
-
? t('autobahn:card.pricing.belowMarket', { delta: delta.toLocaleString('de-DE') })
|
|
358
|
-
: t('autobahn:card.pricing.marketPrice')}
|
|
359
|
-
</span>
|
|
360
|
-
</div>
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
if (car.rating && TONE_TO_TIER[car.rating] != null) {
|
|
364
|
-
// No market BAND yet, but the real Preisbewertung tier (same signal as the rating chip)
|
|
365
|
-
// drives the segmented position meter. Real data, not faked.
|
|
366
|
-
const tier = TONE_TO_TIER[car.rating];
|
|
367
|
-
return (
|
|
368
|
-
<div className="pp-meter-block">
|
|
369
|
-
{seg(tier)}
|
|
370
|
-
<span className={'pp-mlabel' + (tier <= 1 ? ' pp-mlabel--good' : '')}>
|
|
371
|
-
{TIER_KEYS[tier] ? t('autobahn:tier.' + TIER_KEYS[tier]) : t('autobahn:card.pricing.marketPrice')}
|
|
372
|
-
</span>
|
|
373
|
-
</div>
|
|
374
|
-
);
|
|
375
|
-
}
|
|
376
|
-
return null;
|
|
377
|
-
};
|
|
378
|
-
|
|
379
|
-
interface PpRowProps {
|
|
380
|
-
/** payment-path label (Finanzierung / Leasing / Kaufen). */
|
|
381
|
-
label: string;
|
|
382
|
-
/** the figure to show — a monthly rate (money=false) or a full price (money=true). */
|
|
383
|
-
value: number;
|
|
384
|
-
/** small unit before the figure, e.g. 'mtl.'. */
|
|
385
|
-
unit?: string;
|
|
386
|
-
/** real previous price to strike through (only ever the genuine list price). */
|
|
387
|
-
old?: number | null;
|
|
388
|
-
/** the financing row — kept visually dominant (larger). */
|
|
389
|
-
focus?: boolean;
|
|
390
|
-
/** format as a full euro price (Kaufen) instead of a rounded rate. */
|
|
391
|
-
money?: boolean;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* One payment-path row: label (+ a quiet info marker) on the left, the price right-aligned
|
|
396
|
-
* with an optional struck-through previous price above it. Even row spacing reproduces the
|
|
397
|
-
* production "Finanzierung / Leasing / Kaufen" price table the design references.
|
|
398
|
-
*/
|
|
399
|
-
const PpRow: React.FunctionComponent<PpRowProps> = ({ label, value, unit, old, focus, money }) => (
|
|
400
|
-
<div className={'pp-row' + (focus ? ' pp-row--focus' : '')}>
|
|
401
|
-
<span className="pp-row-label">
|
|
402
|
-
{label}
|
|
403
|
-
<Icon name="info" size={13} stroke={1.9} cls="pp-row-i" />
|
|
404
|
-
</span>
|
|
405
|
-
<span className="pp-row-price">
|
|
406
|
-
{old != null && <span className="pp-old">{old.toLocaleString('de-DE')} €</span>}
|
|
407
|
-
<span className="pp-now">
|
|
408
|
-
{unit && <span className="pp-unit">{unit}</span>}
|
|
409
|
-
<b className="num pp-val">{money ? value.toLocaleString('de-DE') : fmtRate(value)} €</b>
|
|
410
|
-
</span>
|
|
411
|
-
</span>
|
|
412
|
-
</div>
|
|
413
|
-
);
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Monthly-rate payment paths (SRP price column, TOP group) — modelled on the legacy design:
|
|
417
|
-
* Finanzierung is the hero (eyebrow + big blue rate + its terms line), which both keeps the
|
|
418
|
-
* financing focus and FILLS the upper gap with real default-config info (Anzahlung · Laufzeit).
|
|
419
|
-
* Leasing follows as a quiet neutral row (with its own terms when the config carries them) and
|
|
420
|
-
* is omitted entirely when leasing isn't active. The Kaufen row is rendered separately
|
|
421
|
-
* (PriceKauf) below a divider so only the financing rate stands out — no wall of blue prices.
|
|
422
|
-
*/
|
|
423
|
-
export const PricePaths: React.FunctionComponent<PricePathsProps> = ({ car, t }) => {
|
|
424
|
-
const hasLease = car.lease != null;
|
|
425
|
-
return (
|
|
426
|
-
<div className="pricepaths">
|
|
427
|
-
<div className="pp-hero">
|
|
428
|
-
<div className="pp-eyebrow">{t('autobahn:card.price.financingFrom')}</div>
|
|
429
|
-
<div className="pp-hero-rate">
|
|
430
|
-
<b className="num pp-hero-val">{fmtRate(car.monthly)} €</b>
|
|
431
|
-
<span className="pp-hero-unit">{t('autobahn:card.price.perMonth')}</span>
|
|
432
|
-
</div>
|
|
433
|
-
<div className="pp-hero-terms">
|
|
434
|
-
{t('autobahn:card.price.financeTerms', {
|
|
435
|
-
firstInstallment: _finFirstInstallment,
|
|
436
|
-
months: _finMonths,
|
|
437
|
-
count: _finMonths,
|
|
438
|
-
})}
|
|
439
|
-
</div>
|
|
440
|
-
</div>
|
|
441
|
-
{hasLease && (
|
|
442
|
-
<div className="pp-lease-row">
|
|
443
|
-
<PpRow label={t('autobahn:card.price.leasing')} value={car.lease as number} unit="mtl." />
|
|
444
|
-
{_hasLeasTerms && (
|
|
445
|
-
<div className="pp-sub-terms">
|
|
446
|
-
{t('autobahn:card.price.leaseTerms', { mileage: _leasMileageStr, months: _leasPeriod })}
|
|
447
|
-
</div>
|
|
448
|
-
)}
|
|
449
|
-
</div>
|
|
450
|
-
)}
|
|
451
|
-
</div>
|
|
452
|
-
);
|
|
453
|
-
};
|
|
454
|
-
|
|
455
|
-
/**
|
|
456
|
-
* Kaufen (outright purchase) row — the BOTTOM group, separated from the monthly-rate paths by a
|
|
457
|
-
* divider. Quiet/neutral (not blue) and strikes the previous price only on a real list-price
|
|
458
|
-
* saving (never a fabricated "was" price).
|
|
459
|
-
*/
|
|
460
|
-
export const PriceKauf: React.FunctionComponent<PricePathsProps> = ({ car, t }) => {
|
|
461
|
-
const showStrike = car.savings != null && car.savings > 600 && car.listPrice != null;
|
|
462
|
-
return (
|
|
463
|
-
<PpRow label={t('autobahn:card.price.buy')} value={car.price} old={showStrike ? (car.listPrice as number) : null} money />
|
|
464
|
-
);
|
|
465
|
-
};
|
|
466
|
-
|
|
467
|
-
export interface PpActivityProps {
|
|
468
|
-
car: AutobahnCar;
|
|
469
|
-
t: TranslationFunction;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/**
|
|
473
|
-
* Buyer-activity cue rendered below the CTA in the listing card.
|
|
474
|
-
*
|
|
475
|
-
* NEEDS-DATA: needs watchers + views. When either is missing we show the
|
|
476
|
-
* 'Daten folgt' marker instead of inventing interest figures.
|
|
477
|
-
*/
|
|
478
|
-
export const PpActivity: React.FunctionComponent<PpActivityProps> = ({ car, t }) => {
|
|
479
|
-
// No real watcher/view data (no backend yet) → render nothing rather than a "Daten folgt"
|
|
480
|
-
// placeholder that reads as perpetual loading. Wire when UserActionsAPI stats are available.
|
|
481
|
-
if (car.watchers == null || car.views == null) return null;
|
|
482
|
-
const hrs = 1 + (car.views % 6);
|
|
483
|
-
return (
|
|
484
|
-
<div className="pp-activity">
|
|
485
|
-
<Icon name="eye" size={12} stroke={1.9} />
|
|
486
|
-
{t('autobahn:card.activity.watchers', { watchers: car.watchers, hrs, count: car.watchers })}
|
|
487
|
-
</div>
|
|
488
|
-
);
|
|
489
|
-
};
|
|
1
|
+
// Autobahn (auto.de redesign) — car-card helper parts.
|
|
2
|
+
//
|
|
3
|
+
// Typed React 18 + TS port of the helper components from the design kit
|
|
4
|
+
// (docs/redesign/design-source/ui_kits/website/CarCard.jsx). Pure /
|
|
5
|
+
// presentational: every value comes from the `AutobahnCar` prop, there is no
|
|
6
|
+
// store / API / `window` access. The design kit's `window.CO2_CLASSES` /
|
|
7
|
+
// `window.CO2_COLORS` globals are replaced by the shared autobahn constants.
|
|
8
|
+
//
|
|
9
|
+
// These render correctly only inside a `<div className="md3">` subtree (the
|
|
10
|
+
// `.pricepaths`, `.pp-*`, `.specstrip`, `.imglabel` … classes are scoped there).
|
|
11
|
+
//
|
|
12
|
+
// NEEDS-DATA discipline (the point of the ticket): wherever the design showed a
|
|
13
|
+
// figure we have no real source for, these helpers render the NEEDS_DATA_LABEL
|
|
14
|
+
// ('Daten folgt') marker instead of inventing a number — see PricePaths'
|
|
15
|
+
// market band and PpActivity. DropChip renders nothing when there is no drop.
|
|
16
|
+
|
|
17
|
+
import React from 'react';
|
|
18
|
+
import { TranslationFunction } from '../types';
|
|
19
|
+
import { defaultConfig } from '@bytebrand/fe-financing';
|
|
20
|
+
|
|
21
|
+
import { Icon } from '../ui';
|
|
22
|
+
import { CO2_CLASSES, CO2_COLORS, TIER_COLORS, TIER_KEYS } from '../constants';
|
|
23
|
+
import { AutobahnCar } from './types';
|
|
24
|
+
|
|
25
|
+
// Default financing/leasing terms shown under the rate (the SRP card has no per-car selector, so
|
|
26
|
+
// these are the same default config the displayed rate is computed with — consistent, not invented).
|
|
27
|
+
const _finCfg = (defaultConfig as any).financing || {};
|
|
28
|
+
const _leasCfg = (defaultConfig as any).leasing || {};
|
|
29
|
+
/** financing terms inputs (rendered via t in PricePaths), e.g. "0 € Anzahlung · 48 Monate Laufzeit". */
|
|
30
|
+
const _finFirstInstallment = (_finCfg.firstInstallment || 0).toLocaleString('de-DE');
|
|
31
|
+
const _finMonths = _finCfg.rawPaybackPeriod || _finCfg.paybackPeriod || 48;
|
|
32
|
+
/** leasing terms inputs (only when the config carries mileage + period), e.g. "10.000 km/Jahr · 48 Monate". */
|
|
33
|
+
const _leasPeriod = _leasCfg.rawPaybackPeriod || _leasCfg.paybackPeriod;
|
|
34
|
+
const _leasMileage = _leasCfg.annualMileage || _leasCfg.mileage;
|
|
35
|
+
const _hasLeasTerms = Boolean(_leasMileage && _leasPeriod);
|
|
36
|
+
const _leasMileageStr = _leasMileage ? Number(_leasMileage).toLocaleString('de-DE') : '';
|
|
37
|
+
|
|
38
|
+
/** mileage → 'X.XXX km' (de-DE grouping). */
|
|
39
|
+
export function fmtKm(n: number): string {
|
|
40
|
+
return n.toLocaleString('de-DE') + ' km';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Monthly rate → de-DE integer euro string (e.g. 402.12 → "402"). Rates are shown as whole
|
|
45
|
+
* euros like the legacy card ("271 €"); a raw float printed directly rendered as "402.12 €",
|
|
46
|
+
* which a German reader parses as 402.120 — fixed here with rounding + de-DE grouping.
|
|
47
|
+
*/
|
|
48
|
+
export function fmtRate(n: number): string {
|
|
49
|
+
return Math.round(n).toLocaleString('de-DE');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** strip a trailing parenthetical, e.g. '110 kW (150 PS)' → '110 kW'. */
|
|
53
|
+
function stripParen(s: string): string {
|
|
54
|
+
return s.replace(/\s*\(.*\)/, '');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Compact spec for cards: short fuel/gear labels so all five values fit ONE line
|
|
58
|
+
// without clipping.
|
|
59
|
+
const FUEL_SHORT: Record<string, string> = {
|
|
60
|
+
'Plug-in-Hybrid': 'Hybrid',
|
|
61
|
+
Benzin: 'Benzin',
|
|
62
|
+
Diesel: 'Diesel',
|
|
63
|
+
Elektro: 'Elektro',
|
|
64
|
+
};
|
|
65
|
+
const GEAR_SHORT: Record<string, string> = {
|
|
66
|
+
Automatik: 'Automatik',
|
|
67
|
+
Schaltgetriebe: 'Manuell',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/** five compact spec values for the grid card's one-line spec strip. */
|
|
71
|
+
export function cardSpec(car: AutobahnCar): string[] {
|
|
72
|
+
const fuel = stripParen(car.fuel);
|
|
73
|
+
return [
|
|
74
|
+
fmtKm(car.km),
|
|
75
|
+
car.reg,
|
|
76
|
+
stripParen(car.power),
|
|
77
|
+
FUEL_SHORT[fuel] || fuel,
|
|
78
|
+
GEAR_SHORT[car.gear] || car.gear,
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface SpecLineProps {
|
|
83
|
+
items: string[];
|
|
84
|
+
size?: number;
|
|
85
|
+
/** never wrap — clip overflow with ellipsis (single line). */
|
|
86
|
+
oneLine?: boolean;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Compact pipe-separated spec line (AutoScout pattern). */
|
|
90
|
+
export const SpecLine: React.FunctionComponent<SpecLineProps> = ({ items, size = 12.5, oneLine = false }) => (
|
|
91
|
+
<div
|
|
92
|
+
style={{
|
|
93
|
+
display: 'flex',
|
|
94
|
+
flexWrap: oneLine ? 'nowrap' : 'wrap',
|
|
95
|
+
alignItems: 'center',
|
|
96
|
+
gap: '2px 8px',
|
|
97
|
+
fontSize: size,
|
|
98
|
+
color: 'var(--md-on-surface-variant)',
|
|
99
|
+
...(oneLine ? { overflow: 'hidden', whiteSpace: 'nowrap' } : {}),
|
|
100
|
+
}}
|
|
101
|
+
>
|
|
102
|
+
{items.map((v, i) => (
|
|
103
|
+
<React.Fragment key={i}>
|
|
104
|
+
{i > 0 && <span style={{ color: 'var(--md-outline-variant)', flex: 'none' }}>·</span>}
|
|
105
|
+
<span style={oneLine ? { whiteSpace: 'nowrap' } : undefined}>{v}</span>
|
|
106
|
+
</React.Fragment>
|
|
107
|
+
))}
|
|
108
|
+
</div>
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
export interface PriceBlockProps {
|
|
112
|
+
car: AutobahnCar;
|
|
113
|
+
align?: 'left' | 'right';
|
|
114
|
+
t: TranslationFunction;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Price block — rate-first (monthly is the lead), purchase price secondary. */
|
|
118
|
+
export const PriceBlock: React.FunctionComponent<PriceBlockProps> = ({ car, align = 'left', t }) => {
|
|
119
|
+
const showStrike = car.savings != null && car.savings > 600 && car.listPrice != null;
|
|
120
|
+
return (
|
|
121
|
+
<div style={{ textAlign: align }}>
|
|
122
|
+
<div
|
|
123
|
+
style={{
|
|
124
|
+
display: 'flex',
|
|
125
|
+
alignItems: 'baseline',
|
|
126
|
+
gap: 5,
|
|
127
|
+
justifyContent: align === 'right' ? 'flex-end' : 'flex-start',
|
|
128
|
+
}}
|
|
129
|
+
>
|
|
130
|
+
<span
|
|
131
|
+
className="num"
|
|
132
|
+
style={{
|
|
133
|
+
fontWeight: 700,
|
|
134
|
+
fontSize: 26,
|
|
135
|
+
color: 'var(--md-primary)',
|
|
136
|
+
letterSpacing: '-.6px',
|
|
137
|
+
whiteSpace: 'nowrap',
|
|
138
|
+
}}
|
|
139
|
+
>
|
|
140
|
+
{fmtRate(car.monthly)} €
|
|
141
|
+
</span>
|
|
142
|
+
<span className="md-label-sm">{t('autobahn:card.price.perMonth')}</span>
|
|
143
|
+
</div>
|
|
144
|
+
<div
|
|
145
|
+
className="md-label-sm"
|
|
146
|
+
style={{
|
|
147
|
+
marginTop: 1,
|
|
148
|
+
display: 'flex',
|
|
149
|
+
flexWrap: 'wrap',
|
|
150
|
+
gap: '0 6px',
|
|
151
|
+
justifyContent: align === 'right' ? 'flex-end' : 'flex-start',
|
|
152
|
+
}}
|
|
153
|
+
>
|
|
154
|
+
<span style={{ whiteSpace: 'nowrap' }}>
|
|
155
|
+
{t('autobahn:card.price.purchasePrice')}{' '}
|
|
156
|
+
<b style={{ color: 'var(--md-on-surface)' }}>{car.price.toLocaleString('de-DE')} €</b>
|
|
157
|
+
</span>
|
|
158
|
+
{showStrike && (
|
|
159
|
+
<span
|
|
160
|
+
style={{
|
|
161
|
+
color: 'var(--md-on-surface-variant)',
|
|
162
|
+
textDecoration: 'line-through',
|
|
163
|
+
whiteSpace: 'nowrap',
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
{car.listPrice!.toLocaleString('de-DE')} €
|
|
167
|
+
</span>
|
|
168
|
+
)}
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export interface DropChipProps {
|
|
175
|
+
/** real price reduction in EUR; when undefined the chip renders nothing. */
|
|
176
|
+
amount?: number;
|
|
177
|
+
t: TranslationFunction;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Price-drop chip — a REAL reduction on this car. White pill so it doesn't
|
|
182
|
+
* compete with the solid green rating chip. Renders nothing when there is no
|
|
183
|
+
* drop (we never fabricate a reduction).
|
|
184
|
+
*/
|
|
185
|
+
export const DropChip: React.FunctionComponent<DropChipProps> = ({ amount, t }) => {
|
|
186
|
+
if (amount == null) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
return (
|
|
190
|
+
<span
|
|
191
|
+
style={{
|
|
192
|
+
display: 'inline-flex',
|
|
193
|
+
alignItems: 'center',
|
|
194
|
+
gap: 3,
|
|
195
|
+
height: 22,
|
|
196
|
+
padding: '0 8px',
|
|
197
|
+
borderRadius: 'var(--md-r-full)',
|
|
198
|
+
background: 'rgba(255,255,255,.94)',
|
|
199
|
+
color: 'var(--md-good)',
|
|
200
|
+
fontSize: 10.5,
|
|
201
|
+
fontWeight: 700,
|
|
202
|
+
boxShadow: 'var(--md-e1)',
|
|
203
|
+
whiteSpace: 'nowrap',
|
|
204
|
+
}}
|
|
205
|
+
>
|
|
206
|
+
<Icon name="trending-down" size={12} stroke={2.4} color="var(--md-good)" />
|
|
207
|
+
{t('autobahn:card.price.priceDropAmount', { amount: amount.toLocaleString('de-DE') })}
|
|
208
|
+
</span>
|
|
209
|
+
);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
/** shared deal-badge pill style (NEU/drop/percent/zero-down all share the same compact shape). */
|
|
213
|
+
const dealPill: React.CSSProperties = {
|
|
214
|
+
display: 'inline-flex',
|
|
215
|
+
alignItems: 'center',
|
|
216
|
+
height: 22,
|
|
217
|
+
padding: '0 8px',
|
|
218
|
+
borderRadius: 'var(--md-r-full)',
|
|
219
|
+
fontSize: 10.5,
|
|
220
|
+
fontWeight: 700,
|
|
221
|
+
boxShadow: 'var(--md-e1)',
|
|
222
|
+
whiteSpace: 'nowrap',
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
/** Percent price-drop chip (−X%). Red pill — pairs with DropChip. Renders nothing without a real pct. */
|
|
226
|
+
export const PercentChip: React.FunctionComponent<{ pct?: number }> = ({ pct }) => {
|
|
227
|
+
if (pct == null || pct <= 0) return null;
|
|
228
|
+
return <span style={{ ...dealPill, background: 'var(--md-error)', color: '#fff' }}>−{pct}%</span>;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Zero-down-payment chip (offer.showDownPayment). Cyan pill. Spells out "0 € Anzahlung" where
|
|
233
|
+
* there is room (grid cards / wide image) and uses the compact "0 € Anz." on the narrow list-card
|
|
234
|
+
* image, so the badge row always stays on ONE line on both desktop and mobile.
|
|
235
|
+
*/
|
|
236
|
+
export const ZeroDownChip: React.FunctionComponent<{ show?: boolean; compact?: boolean; t: TranslationFunction }> = ({
|
|
237
|
+
show,
|
|
238
|
+
compact,
|
|
239
|
+
t,
|
|
240
|
+
}) => {
|
|
241
|
+
if (!show) return null;
|
|
242
|
+
return (
|
|
243
|
+
<span style={{ ...dealPill, background: 'var(--md-accent-container)', color: 'var(--md-on-accent-container)' }}>
|
|
244
|
+
{compact ? t('autobahn:card.price.zeroDownCompact') : t('autobahn:card.price.zeroDown')}
|
|
245
|
+
</span>
|
|
246
|
+
);
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
interface InlineClsProps {
|
|
250
|
+
/** CO₂ class letter, e.g. 'B'. */
|
|
251
|
+
c: string;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Small inline colored CO₂-class letter (keeps EnVKV line a fixed height). */
|
|
255
|
+
const InlineCls: React.FunctionComponent<InlineClsProps> = ({ c }) => {
|
|
256
|
+
const i = Math.max(0, CO2_CLASSES.indexOf(c as (typeof CO2_CLASSES)[number]));
|
|
257
|
+
const color = CO2_COLORS[i] || 'var(--md-outline)';
|
|
258
|
+
return (
|
|
259
|
+
<span
|
|
260
|
+
style={{
|
|
261
|
+
display: 'inline-flex',
|
|
262
|
+
width: 14,
|
|
263
|
+
height: 14,
|
|
264
|
+
borderRadius: 3,
|
|
265
|
+
background: color,
|
|
266
|
+
color: '#fff',
|
|
267
|
+
fontSize: 9.5,
|
|
268
|
+
fontWeight: 800,
|
|
269
|
+
alignItems: 'center',
|
|
270
|
+
justifyContent: 'center',
|
|
271
|
+
verticalAlign: '-2px',
|
|
272
|
+
margin: '0 1px',
|
|
273
|
+
}}
|
|
274
|
+
>
|
|
275
|
+
{c}
|
|
276
|
+
</span>
|
|
277
|
+
);
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
export interface EcoLineProps {
|
|
281
|
+
car: AutobahnCar;
|
|
282
|
+
style?: React.CSSProperties;
|
|
283
|
+
t: TranslationFunction;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Pkw-EnVKV legal line — full consumption + CO₂ + class. REQUIRED on every
|
|
288
|
+
* German car ad and must stay visible. Quiet fine-print footnote with a fixed
|
|
289
|
+
* min-height so all cards stay the same height. Handles the PHEV dual-figure
|
|
290
|
+
* variant when a `phev` block is present.
|
|
291
|
+
*/
|
|
292
|
+
export const EcoLine: React.FunctionComponent<EcoLineProps> = ({ car, style, t }) => {
|
|
293
|
+
const wrap: React.CSSProperties = {
|
|
294
|
+
borderTop: '1px solid var(--md-outline-variant)',
|
|
295
|
+
paddingTop: 8,
|
|
296
|
+
minHeight: 41,
|
|
297
|
+
fontSize: 11,
|
|
298
|
+
lineHeight: 1.5,
|
|
299
|
+
color: 'var(--md-on-surface-variant)',
|
|
300
|
+
...style,
|
|
301
|
+
};
|
|
302
|
+
if (car.phev) {
|
|
303
|
+
const p = car.phev;
|
|
304
|
+
return (
|
|
305
|
+
<div style={wrap}>
|
|
306
|
+
{p.consEl}{' '}
|
|
307
|
+
{t('autobahn:card.eco.phevConsumptionLine', {
|
|
308
|
+
consFuel: p.consFuel,
|
|
309
|
+
consDis: p.consDis,
|
|
310
|
+
co2w: p.co2w,
|
|
311
|
+
co2classW: p.co2classW,
|
|
312
|
+
co2classD: p.co2classD,
|
|
313
|
+
})}
|
|
314
|
+
</div>
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
return (
|
|
318
|
+
<div style={wrap}>
|
|
319
|
+
{t('autobahn:card.eco.consumptionLine', { cons: car.cons, co2: car.co2, co2class: car.co2class })}
|
|
320
|
+
</div>
|
|
321
|
+
);
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
export interface PricePathsProps {
|
|
325
|
+
car: AutobahnCar;
|
|
326
|
+
t: TranslationFunction;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** Map the AutobahnCar rating tone → meter segment index (0 best … 4 worst). */
|
|
330
|
+
const TONE_TO_TIER: Record<string, number> = { top: 0, good: 1, fair: 2, raised: 3, high: 4 };
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Segmented Preisbewertung meter + label. Extracted from PricePaths so the SRP price
|
|
334
|
+
* column (Option A layout) can pin it to the BOTTOM of the column, separate from the
|
|
335
|
+
* grouped price figures at the top. Renders nothing when there is no rating/band
|
|
336
|
+
* (needs-data: we never draw a fake market position).
|
|
337
|
+
*/
|
|
338
|
+
export const PriceMeter: React.FunctionComponent<{ car: AutobahnCar; t: TranslationFunction }> = ({ car, t }) => {
|
|
339
|
+
const hasMarket =
|
|
340
|
+
car.marketLow != null && car.marketAvg != null && car.marketHigh != null && car.marketHigh > car.marketLow;
|
|
341
|
+
const seg = (tier: number) => (
|
|
342
|
+
<div className="pp-meter">
|
|
343
|
+
{[0, 1, 2, 3, 4].map((s) => (
|
|
344
|
+
<span key={s} className="pp-seg" style={{ background: TIER_COLORS[s], opacity: s === tier ? 1 : 0.26 }} />
|
|
345
|
+
))}
|
|
346
|
+
</div>
|
|
347
|
+
);
|
|
348
|
+
if (hasMarket) {
|
|
349
|
+
const delta = car.marketAvg! - car.price;
|
|
350
|
+
const good = delta > 150;
|
|
351
|
+
const mpos = Math.max(0, Math.min(1, (car.price - car.marketLow!) / (car.marketHigh! - car.marketLow!)));
|
|
352
|
+
return (
|
|
353
|
+
<div className="pp-meter-block">
|
|
354
|
+
{seg(Math.round(mpos * 4))}
|
|
355
|
+
<span className={'pp-mlabel' + (good ? ' pp-mlabel--good' : '')}>
|
|
356
|
+
{good
|
|
357
|
+
? t('autobahn:card.pricing.belowMarket', { delta: delta.toLocaleString('de-DE') })
|
|
358
|
+
: t('autobahn:card.pricing.marketPrice')}
|
|
359
|
+
</span>
|
|
360
|
+
</div>
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
if (car.rating && TONE_TO_TIER[car.rating] != null) {
|
|
364
|
+
// No market BAND yet, but the real Preisbewertung tier (same signal as the rating chip)
|
|
365
|
+
// drives the segmented position meter. Real data, not faked.
|
|
366
|
+
const tier = TONE_TO_TIER[car.rating];
|
|
367
|
+
return (
|
|
368
|
+
<div className="pp-meter-block">
|
|
369
|
+
{seg(tier)}
|
|
370
|
+
<span className={'pp-mlabel' + (tier <= 1 ? ' pp-mlabel--good' : '')}>
|
|
371
|
+
{TIER_KEYS[tier] ? t('autobahn:tier.' + TIER_KEYS[tier]) : t('autobahn:card.pricing.marketPrice')}
|
|
372
|
+
</span>
|
|
373
|
+
</div>
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
return null;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
interface PpRowProps {
|
|
380
|
+
/** payment-path label (Finanzierung / Leasing / Kaufen). */
|
|
381
|
+
label: string;
|
|
382
|
+
/** the figure to show — a monthly rate (money=false) or a full price (money=true). */
|
|
383
|
+
value: number;
|
|
384
|
+
/** small unit before the figure, e.g. 'mtl.'. */
|
|
385
|
+
unit?: string;
|
|
386
|
+
/** real previous price to strike through (only ever the genuine list price). */
|
|
387
|
+
old?: number | null;
|
|
388
|
+
/** the financing row — kept visually dominant (larger). */
|
|
389
|
+
focus?: boolean;
|
|
390
|
+
/** format as a full euro price (Kaufen) instead of a rounded rate. */
|
|
391
|
+
money?: boolean;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* One payment-path row: label (+ a quiet info marker) on the left, the price right-aligned
|
|
396
|
+
* with an optional struck-through previous price above it. Even row spacing reproduces the
|
|
397
|
+
* production "Finanzierung / Leasing / Kaufen" price table the design references.
|
|
398
|
+
*/
|
|
399
|
+
const PpRow: React.FunctionComponent<PpRowProps> = ({ label, value, unit, old, focus, money }) => (
|
|
400
|
+
<div className={'pp-row' + (focus ? ' pp-row--focus' : '')}>
|
|
401
|
+
<span className="pp-row-label">
|
|
402
|
+
{label}
|
|
403
|
+
<Icon name="info" size={13} stroke={1.9} cls="pp-row-i" />
|
|
404
|
+
</span>
|
|
405
|
+
<span className="pp-row-price">
|
|
406
|
+
{old != null && <span className="pp-old">{old.toLocaleString('de-DE')} €</span>}
|
|
407
|
+
<span className="pp-now">
|
|
408
|
+
{unit && <span className="pp-unit">{unit}</span>}
|
|
409
|
+
<b className="num pp-val">{money ? value.toLocaleString('de-DE') : fmtRate(value)} €</b>
|
|
410
|
+
</span>
|
|
411
|
+
</span>
|
|
412
|
+
</div>
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Monthly-rate payment paths (SRP price column, TOP group) — modelled on the legacy design:
|
|
417
|
+
* Finanzierung is the hero (eyebrow + big blue rate + its terms line), which both keeps the
|
|
418
|
+
* financing focus and FILLS the upper gap with real default-config info (Anzahlung · Laufzeit).
|
|
419
|
+
* Leasing follows as a quiet neutral row (with its own terms when the config carries them) and
|
|
420
|
+
* is omitted entirely when leasing isn't active. The Kaufen row is rendered separately
|
|
421
|
+
* (PriceKauf) below a divider so only the financing rate stands out — no wall of blue prices.
|
|
422
|
+
*/
|
|
423
|
+
export const PricePaths: React.FunctionComponent<PricePathsProps> = ({ car, t }) => {
|
|
424
|
+
const hasLease = car.lease != null;
|
|
425
|
+
return (
|
|
426
|
+
<div className="pricepaths">
|
|
427
|
+
<div className="pp-hero">
|
|
428
|
+
<div className="pp-eyebrow">{t('autobahn:card.price.financingFrom')}</div>
|
|
429
|
+
<div className="pp-hero-rate">
|
|
430
|
+
<b className="num pp-hero-val">{fmtRate(car.monthly)} €</b>
|
|
431
|
+
<span className="pp-hero-unit">{t('autobahn:card.price.perMonth')}</span>
|
|
432
|
+
</div>
|
|
433
|
+
<div className="pp-hero-terms">
|
|
434
|
+
{t('autobahn:card.price.financeTerms', {
|
|
435
|
+
firstInstallment: _finFirstInstallment,
|
|
436
|
+
months: _finMonths,
|
|
437
|
+
count: _finMonths,
|
|
438
|
+
})}
|
|
439
|
+
</div>
|
|
440
|
+
</div>
|
|
441
|
+
{hasLease && (
|
|
442
|
+
<div className="pp-lease-row">
|
|
443
|
+
<PpRow label={t('autobahn:card.price.leasing')} value={car.lease as number} unit="mtl." />
|
|
444
|
+
{_hasLeasTerms && (
|
|
445
|
+
<div className="pp-sub-terms">
|
|
446
|
+
{t('autobahn:card.price.leaseTerms', { mileage: _leasMileageStr, months: _leasPeriod })}
|
|
447
|
+
</div>
|
|
448
|
+
)}
|
|
449
|
+
</div>
|
|
450
|
+
)}
|
|
451
|
+
</div>
|
|
452
|
+
);
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Kaufen (outright purchase) row — the BOTTOM group, separated from the monthly-rate paths by a
|
|
457
|
+
* divider. Quiet/neutral (not blue) and strikes the previous price only on a real list-price
|
|
458
|
+
* saving (never a fabricated "was" price).
|
|
459
|
+
*/
|
|
460
|
+
export const PriceKauf: React.FunctionComponent<PricePathsProps> = ({ car, t }) => {
|
|
461
|
+
const showStrike = car.savings != null && car.savings > 600 && car.listPrice != null;
|
|
462
|
+
return (
|
|
463
|
+
<PpRow label={t('autobahn:card.price.buy')} value={car.price} old={showStrike ? (car.listPrice as number) : null} money />
|
|
464
|
+
);
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
export interface PpActivityProps {
|
|
468
|
+
car: AutobahnCar;
|
|
469
|
+
t: TranslationFunction;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Buyer-activity cue rendered below the CTA in the listing card.
|
|
474
|
+
*
|
|
475
|
+
* NEEDS-DATA: needs watchers + views. When either is missing we show the
|
|
476
|
+
* 'Daten folgt' marker instead of inventing interest figures.
|
|
477
|
+
*/
|
|
478
|
+
export const PpActivity: React.FunctionComponent<PpActivityProps> = ({ car, t }) => {
|
|
479
|
+
// No real watcher/view data (no backend yet) → render nothing rather than a "Daten folgt"
|
|
480
|
+
// placeholder that reads as perpetual loading. Wire when UserActionsAPI stats are available.
|
|
481
|
+
if (car.watchers == null || car.views == null) return null;
|
|
482
|
+
const hrs = 1 + (car.views % 6);
|
|
483
|
+
return (
|
|
484
|
+
<div className="pp-activity">
|
|
485
|
+
<Icon name="eye" size={12} stroke={1.9} />
|
|
486
|
+
{t('autobahn:card.activity.watchers', { watchers: car.watchers, hrs, count: car.watchers })}
|
|
487
|
+
</div>
|
|
488
|
+
);
|
|
489
|
+
};
|