@bytebrand/fe-ui-core-autobahn 1.0.102 → 1.0.104
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/package.json +1 -1
- package/utils/CommonUtils.ts +17 -0
- package/utils/index.ts +1 -0
- package/vdp/VdpModules.tsx +17 -13
- package/vehiclesProps/decoratedProps.tsx +5 -2
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.104",
|
|
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/utils/CommonUtils.ts
CHANGED
|
@@ -25,6 +25,23 @@ export const formatMileage = (millage: number | string) => {
|
|
|
25
25
|
return millage.toLocaleString('en-US').replace(/,/g, '.');
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
// Same thresholds as the "NEU" badge (ADV-166, see fe-main/fe-landing/fe-wp-widgets
|
|
29
|
+
// DataTransformers.ts) minus its extra "recently listed" (modificationDate) check —
|
|
30
|
+
// this is about the car itself being practically brand-new, not about listing recency.
|
|
31
|
+
// Missing firstRegistration passes the age check (can't disqualify what we don't know).
|
|
32
|
+
const EFFECTIVELY_NEW_MAX_MILEAGE_KM = 1000;
|
|
33
|
+
const EFFECTIVELY_NEW_MAX_AGE_MONTHS = 12;
|
|
34
|
+
const AVG_MS_PER_MONTH = 1000 * 60 * 60 * 24 * 30.44;
|
|
35
|
+
|
|
36
|
+
export const isEffectivelyNewCar = (mileageKm?: number | string | null, firstRegistration?: number | Date | string | null): boolean => {
|
|
37
|
+
if (!(Number(mileageKm) < EFFECTIVELY_NEW_MAX_MILEAGE_KM)) return false;
|
|
38
|
+
if (!firstRegistration) return true;
|
|
39
|
+
const registeredAtMs = firstRegistration instanceof Date ? firstRegistration.getTime() : Number(firstRegistration) * 1000;
|
|
40
|
+
if (!Number.isFinite(registeredAtMs)) return true;
|
|
41
|
+
const ageMonths = (Date.now() - registeredAtMs) / AVG_MS_PER_MONTH;
|
|
42
|
+
return ageMonths < EFFECTIVELY_NEW_MAX_AGE_MONTHS;
|
|
43
|
+
};
|
|
44
|
+
|
|
28
45
|
export const getFormattedNumber = (num: number) => Number.isFinite(num) ? num.toLocaleString('de-DE') : null;
|
|
29
46
|
|
|
30
47
|
export const numberWithDot = (value: number | string) => {
|
package/utils/index.ts
CHANGED
package/vdp/VdpModules.tsx
CHANGED
|
@@ -2074,17 +2074,20 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
|
|
|
2074
2074
|
// already carried inside `monthly` instead), the real flat price when switched off.
|
|
2075
2075
|
const addServicesRowValue = withAddServices ? 0 : addServicesCostRaw ?? 0;
|
|
2076
2076
|
const hasAddServicesRow = addServicesCostRaw != null && addServicesCostRaw > 0;
|
|
2077
|
-
// Grand total (Figma "Gesamtbelastung inkl. Anzahlung & Paket") — down payment + the
|
|
2078
|
-
//
|
|
2079
|
-
// the
|
|
2080
|
-
//
|
|
2081
|
-
//
|
|
2082
|
-
//
|
|
2083
|
-
//
|
|
2084
|
-
//
|
|
2077
|
+
// Grand total (Figma "Gesamtbelastung inkl. Anzahlung & Paket") — down payment + the "Gesamtbetrag
|
|
2078
|
+
// Finanzierung" figure directly above it (totalFinancingAmount; falls back to grossAmount when
|
|
2079
|
+
// the page hasn't been updated to pass it yet — financing-only, mirrors totalRepaymentValue
|
|
2080
|
+
// below) + the package fee. The package is mandatory (see packageMandatoryNotice) — the toggle
|
|
2081
|
+
// only decides HOW it's paid (bundled into the rate vs. a separate one-time charge), never
|
|
2082
|
+
// WHETHER it's owed, so the total must include it either way: `addServicesCost` when bundled
|
|
2083
|
+
// (toggle on), the raw one-time fee (`addServicesRowValue`, same as the breakdown row above it)
|
|
2084
|
+
// when paid separately (toggle off) — previously dropped to 0 in the off case, so the grand
|
|
2085
|
+
// total silently excluded a cost the breakdown row directly above it was still showing.
|
|
2086
|
+
// Only shown when the total is known (never fabricated).
|
|
2087
|
+
const totalRepaymentBase = totalFinancingAmount ?? grossAmount;
|
|
2085
2088
|
const totalCost =
|
|
2086
|
-
|
|
2087
|
-
? down +
|
|
2089
|
+
totalRepaymentBase != null && totalRepaymentBase > 0
|
|
2090
|
+
? down + totalRepaymentBase + (hasAddServices ? (addServicesCost as number) : hasAddServicesRow ? addServicesRowValue : 0)
|
|
2088
2091
|
: undefined;
|
|
2089
2092
|
|
|
2090
2093
|
let rows: Array<[string, string, boolean?]>;
|
|
@@ -2135,9 +2138,10 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
|
|
|
2135
2138
|
// `term` payments are regular — no adjustment.
|
|
2136
2139
|
const hasBalloon = finalInstallment != null && finalInstallment > 0;
|
|
2137
2140
|
const regularRateCount = hasBalloon ? term - 1 : term;
|
|
2138
|
-
// Repräsentatives-Beispiel total for THIS line
|
|
2139
|
-
// page hasn't been updated to
|
|
2140
|
-
|
|
2141
|
+
// Repräsentatives-Beispiel total for THIS line (and the grand total below it, via
|
|
2142
|
+
// totalRepaymentBase) — falls back to grossAmount when the page hasn't been updated to
|
|
2143
|
+
// pass totalFinancingAmount yet.
|
|
2144
|
+
const totalRepaymentValue = totalRepaymentBase;
|
|
2141
2145
|
rows = [
|
|
2142
2146
|
[t('autobahn:modal.vehiclePrice'), `${eurCents(basePrice ?? price)} €`],
|
|
2143
2147
|
[t('autobahn:modal.term'), t('autobahn:modal.months', { months: term, count: term })],
|
|
@@ -4,7 +4,7 @@ import moment from 'moment';
|
|
|
4
4
|
import Icon from '../compat/IconContainer/IconContainer';
|
|
5
5
|
import CO2Efficiency from '../compat/Co2Widget/CO2Efficiency';
|
|
6
6
|
import { formatTimestamp } from '../utils/DateUtils';
|
|
7
|
-
import { formatMileage } from '../utils/CommonUtils';
|
|
7
|
+
import { formatMileage, isEffectivelyNewCar } from '../utils/CommonUtils';
|
|
8
8
|
import { EMISSION_STICKERS_ICONS, WP_EMISSION_STICKERS_ICONS } from '../constants/Search';
|
|
9
9
|
import { DELIVERY_PERIODS_EXTRA } from '../constants';
|
|
10
10
|
import { IDecoratedProp, ICar, IFinancingUtils } from '../types/types';
|
|
@@ -1120,7 +1120,10 @@ const getDecoratedProps = (
|
|
|
1120
1120
|
maintenanceGuide: {
|
|
1121
1121
|
title: t('vehicleProps:title.maintenanceGuide'),
|
|
1122
1122
|
get value() {
|
|
1123
|
-
|
|
1123
|
+
// A practically brand-new car (near-zero mileage, recent/no first registration)
|
|
1124
|
+
// is always treated as full-service-history, regardless of the raw dealer flag —
|
|
1125
|
+
// there's simply been no time yet for a service history to matter.
|
|
1126
|
+
const fullserviceHistory = isEffectivelyNewCar(mileage, firstRegistration) ? true : car.careService.fullServiceHistory;
|
|
1124
1127
|
return isPropDefined(fullserviceHistory)
|
|
1125
1128
|
? (fullserviceHistory ? t('vehicleProps:value.yes') : t('vehicleProps:value.no'))
|
|
1126
1129
|
: t('vehicleProps:value.na');
|