@bytebrand/fe-ui-core-autobahn 1.0.50 → 1.0.53

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.
@@ -0,0 +1,57 @@
1
+ // Autobahn (auto.de redesign) — Preisbewertung rating math, extracted from CommonUtils.ts.
2
+ //
3
+ // Pulled out on purpose: CommonUtils.ts transitively imports `../constants/Search`, which
4
+ // `require()`s image assets (webpack-only — breaks any plain Node `require`/`tsc` consumer).
5
+ // This file only imports `../constants/price` (zero further imports), so the Meta AIA
6
+ // marketing-image renderer (a plain Node backend, see ../data/toAutobahnCar.ts's `mapRating`)
7
+ // can import real Preisbewertung math without dragging in frontend-only asset requires.
8
+ // CommonUtils.ts re-exports these for existing consumers — one implementation, not a fork.
9
+ import { priceRatings, priceRatingConfig } from '../constants/price';
10
+ import { PREDICTABLE_PRICE_INDEX } from '../constants/core';
11
+
12
+ export const getPriceRatingConfig = (currentPrice: number, pricePredicted: number) => { // tslint:disable-line
13
+ const round = (value: number) => {
14
+ return Number.isFinite(pricePredicted)
15
+ ? Math.round((pricePredicted * (1 + (value / 100))) / 100) * 100 // tslint:disable-line
16
+ : '???';
17
+ };
18
+
19
+ if (currentPrice === 0) {
20
+ console.log('Empty current price');
21
+ }
22
+
23
+ return Object.keys(priceRatingConfig).reduce((acc: any, key: any) => {
24
+ const { min, max } = priceRatingConfig[key];
25
+ return { ...acc, [key]: { min: round(min), max: round(max) } };
26
+ }, {}); // tslint:disable-line
27
+ };
28
+
29
+ export const getPredictablePrice = (currentSalesPrice: number, predictablePrice: number) => {
30
+ if (Number.isFinite(Number.parseFloat(`${predictablePrice}`))) {
31
+ return predictablePrice;
32
+ }
33
+
34
+ return currentSalesPrice * PREDICTABLE_PRICE_INDEX;
35
+ }
36
+
37
+ export const getPriceRating = (currentSalesPrice: number, predictablePrice: number) => {
38
+ const isCorrectCurrentSalesPrice = Number.isFinite(Number.parseFloat(`${currentSalesPrice}`));
39
+ // const isCorrectPredictablePrice = Number.isFinite(Number.parseFloat(`${predictablePrice}`));
40
+
41
+ if (!isCorrectCurrentSalesPrice) return 0;
42
+
43
+
44
+ const validatedPredictablePrice = getPredictablePrice(currentSalesPrice, predictablePrice);
45
+
46
+ const ratingConfig = getPriceRatingConfig(currentSalesPrice, validatedPredictablePrice);
47
+
48
+ for (const key of Object.keys(ratingConfig)) {
49
+ const { min, max } = ratingConfig[key];
50
+ const TOP_PRICE = 5;
51
+ if (key === 'highPrice' && currentSalesPrice === max) return 1;
52
+ if (key === 'topPrice' && currentSalesPrice <= min) return TOP_PRICE;
53
+ if (currentSalesPrice >= min && currentSalesPrice < max) return priceRatings.indexOf(key);
54
+ }
55
+
56
+ return 0;
57
+ };
@@ -28,8 +28,7 @@ import { Button, Icon, Spec, StatChip, Stars, Switch } from '../ui';
28
28
  import { CO2_CLASSES, CO2_COLORS, TIER_LABELS, TIER_KEYS, TIER_COLORS } from '../constants';
29
29
  import { defaultVdpSpecs, EquipmentGroups, PriceRatingBands, VehicleDetail, VdpSpec, VdpReview } from './types';
30
30
  import { CalcRow, DocCheckIcon, EvalBar, expanderBtnStyle, FinanceMode, Slider, TermChips } from './FinanceCalculator';
31
-
32
- const eur = (n: number): string => n.toLocaleString('de-DE');
31
+ import { formatEur as eur, formatPct as pct, buildFinanceExampleLine } from '../utils/financeFormat';
33
32
 
34
33
  /** Figma "check with open arc" glyph (modern lucide circle-check-big; the bundled lucide
35
34
  * 0.288 only ships the closed-circle variant, so the two paths are inlined). */
@@ -1539,7 +1538,6 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
1539
1538
  for (let v = termMin as number; v <= (termMax as number); v += termStep || 12) out.push(v);
1540
1539
  return out.length ? out : [term];
1541
1540
  })();
1542
- const pct = (n: number) => `${n.toFixed(2).replace('.', ',')} %`;
1543
1541
  // Only REAL figures (from the financing config). Balloon financing means monthly·term is NOT the
1544
1542
  // total, so Schlussrate + Gesamtbetrag come from the config and are omitted when unavailable —
1545
1543
  // never a fabricated/misleading total.
@@ -1576,17 +1574,21 @@ export const FinanceDetailsModal: React.FunctionComponent<FinanceDetailsModalPro
1576
1574
 
1577
1575
  // mobile sheet footnote — the Figma representative example: real figures inline
1578
1576
  // ("³ Fahrzeugpreis: … €; Effektiver Jahreszins: … %; …") followed by the PangV sentence.
1579
- // Only REAL config figures are included; absent ones are simply omitted.
1580
- const exampleParts: string[] = [
1581
- `${t('autobahn:modal.exampleVehiclePrice')}: ${eur(price)} € ${t('autobahn:modal.examplePriceSuffix')}`,
1582
- ...(apr > 0 ? [`${t('autobahn:modal.exampleApr')}: ${pct(apr)}`] : []),
1583
- ...(nominalRate != null && nominalRate > 0 ? [`${t('autobahn:modal.exampleNominal')}: ${pct(nominalRate)}`] : []),
1584
- `${t('autobahn:modal.exampleNet')}: ${eur(netto)} €`,
1585
- `${t('autobahn:modal.exampleDown')}: ${eur(down)} €`,
1586
- ...(finalInstallment != null && finalInstallment > 0 ? [`${t('autobahn:modal.exampleFinal')}: ${eur(finalInstallment)} €`] : []),
1587
- ...(grossAmount != null && grossAmount > 0 ? [`${t('autobahn:modal.exampleGross')}: ${eur(grossAmount)} €`] : []),
1588
- ];
1589
- const exampleLine = `³ ${exampleParts.join('; ')};`;
1577
+ // Only REAL config figures are included; absent ones are simply omitted — shared logic with
1578
+ // the Meta AIA marketing-image renderer, see ../utils/financeFormat.ts.
1579
+ const exampleLine = buildFinanceExampleLine(
1580
+ { price, apr, down, netLoanAmount, nominalRate, finalInstallment, grossAmount },
1581
+ {
1582
+ vehiclePrice: t('autobahn:modal.exampleVehiclePrice'),
1583
+ priceSuffix: t('autobahn:modal.examplePriceSuffix'),
1584
+ apr: t('autobahn:modal.exampleApr'),
1585
+ nominal: t('autobahn:modal.exampleNominal'),
1586
+ net: t('autobahn:modal.exampleNet'),
1587
+ down: t('autobahn:modal.exampleDown'),
1588
+ final: t('autobahn:modal.exampleFinal'),
1589
+ gross: t('autobahn:modal.exampleGross'),
1590
+ },
1591
+ );
1590
1592
 
1591
1593
  return (
1592
1594
  <div className="fin-modal-scrim" onClick={onClose}>