@bytebrand/fe-ui-core-autobahn 1.0.73 → 1.0.75

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/theme/md3.css CHANGED
@@ -4365,7 +4365,6 @@ line-height: 31px;
4365
4365
  justify-content: center;
4366
4366
  position: absolute;
4367
4367
  top: -5px;
4368
- right: -5px;
4369
4368
  border-radius: 50%;
4370
4369
  width: 12px;
4371
4370
  height: 11px;
@@ -4627,17 +4626,20 @@ line-height: 31px;
4627
4626
  border: 1px solid transparent;
4628
4627
  }
4629
4628
  .md3 .cc-card .deal-chip--drop {
4630
- background: rgba(17, 110, 57, 0.2) !important;
4629
+ /* solid equivalent of rgba(17, 110, 57, 0.2) over white — the badge now sits over an
4630
+ edge-to-edge photo (CardCarousel padding removed), so a translucent fill let the image
4631
+ show through instead of the intended pastel tint. */
4632
+ background: rgb(207, 226, 215) !important;
4631
4633
  border-color: rgba(17, 110, 57, 0.05);
4632
4634
  color: #116e39 !important;
4633
4635
  }
4634
4636
  .md3 .cc-card .deal-chip--percent {
4635
- background: rgba(217, 45, 32, 0.2) !important;
4637
+ background: rgb(247, 213, 210) !important;
4636
4638
  border-color: rgba(217, 45, 32, 0.05);
4637
4639
  color: #d92d20 !important;
4638
4640
  }
4639
4641
  .md3 .cc-card .deal-chip--zerodown {
4640
- background: rgba(0, 92, 255, 0.2) !important;
4642
+ background: rgb(204, 222, 255) !important;
4641
4643
  border-color: rgba(0, 92, 255, 0.05);
4642
4644
  color: #005cff !important;
4643
4645
  }
@@ -4645,7 +4647,7 @@ line-height: 31px;
4645
4647
  .md3 .cc-card .neu-badge {
4646
4648
  height: 30px;
4647
4649
  padding: 0 10px;
4648
- background: rgba(255, 184, 0, 0.2);
4650
+ background: rgb(255, 241, 204);
4649
4651
  border: 1px solid rgba(255, 184, 0, 0.05);
4650
4652
  color: #ffb800;
4651
4653
  font-size: 13px;
@@ -4656,7 +4658,7 @@ line-height: 31px;
4656
4658
  height: 30px;
4657
4659
  padding: 0 10px;
4658
4660
  border-radius: 10px;
4659
- background: rgba(0, 0, 0, 0.2);
4661
+ background: rgb(204, 204, 204);
4660
4662
  border: 1px solid rgba(0, 0, 0, 0.05);
4661
4663
  color: #000;
4662
4664
  font-size: 14px;
@@ -4668,12 +4670,34 @@ line-height: 31px;
4668
4670
  }
4669
4671
  .md3 .cc-price .fin-tabs { margin-bottom: 12px; }
4670
4672
  .md3 .cc-price .fin-tab { font-size: 17px; font-weight: 700; padding: 10px 5px; }
4673
+ @media (max-width: 1024px) {
4674
+ /* mobile CarCard tabs: only the ACTIVE tab bold + blue, matching the min-width:1025px desktop
4675
+ override further below — :not(.on) matches its specificity so a later, equal-specificity
4676
+ rule (.md3 .fin-tab:not(.on), bold+#111820 for both states) can't win the cascade on
4677
+ source-order alone. */
4678
+ .md3 .cc-price .fin-tab:not(.on) {
4679
+ font-family: 'Arial';
4680
+ font-weight: 400;
4681
+ font-size: 14px;
4682
+ line-height: 16px;
4683
+ color: #0E2238;
4684
+ }
4685
+ .md3 .cc-price .fin-tab.on {
4686
+ font-family: 'Arial';
4687
+ font-weight: 700;
4688
+ font-size: 14px;
4689
+ line-height: 16px;
4690
+ color: #005CFF;
4691
+ }
4692
+ }
4671
4693
  /* Finanzierung tab price row (Figma "similar cars" card) — matches the VDP finance-calculator's
4672
4694
  MOBILE .fin-price-grid treatment one-to-one (same rate/old-price/pill typography and colors),
4673
4695
  by explicit spec. Previously gated to min-width:601px (left the pre-existing mobile .cc-card
4674
4696
  price row alone); now applied at every width on request, so mobile CarCard matches too. */
4675
4697
  .md3 .cc-price .pp-eyebrow,
4676
- .md3 .fin-calc-card .pp-eyebrow {
4698
+ .md3 .fin-calc-card .pp-eyebrow,
4699
+ .md3 .vdp-bottom-bar .pp-eyebrow,
4700
+ .md3 .fin-modal .pp-eyebrow {
4677
4701
  position: relative;
4678
4702
  font-family: Arial, Helvetica, sans-serif;
4679
4703
  font-style: normal;
@@ -4689,7 +4713,9 @@ line-height: 31px;
4689
4713
  corner — absolute against .pp-eyebrow's own position:relative above. Shared by the CarCard
4690
4714
  hero (.cc-price) and the VDP FinanceCalculator's own rate example (.fin-calc-card). */
4691
4715
  .md3 .cc-price .pp-eyebrow-badge,
4692
- .md3 .fin-calc-card .pp-eyebrow-badge {
4716
+ .md3 .fin-calc-card .pp-eyebrow-badge,
4717
+ .md3 .vdp-bottom-bar .pp-eyebrow-badge,
4718
+ .md3 .fin-modal .pp-eyebrow-badge {
4693
4719
  display: inline-flex;
4694
4720
  align-items: center;
4695
4721
  justify-content: center;
@@ -4712,7 +4738,9 @@ line-height: 31px;
4712
4738
  }
4713
4739
  /* mobile-only nudge: same badge, tucked slightly closer to the eyebrow text on narrow cards. */
4714
4740
  @media (max-width: 1024px) {
4715
- .md3 .cc-price .pp-eyebrow-badge {
4741
+ .md3 .cc-price .pp-eyebrow-badge,
4742
+ .md3 .vdp-bottom-bar .pp-eyebrow-badge,
4743
+ .md3 .fin-modal .pp-eyebrow-badge {
4716
4744
  top: -5px;
4717
4745
  right: -5px;
4718
4746
  }
@@ -4720,13 +4748,17 @@ line-height: 31px;
4720
4748
  /* "2" badge (Kaufen, nested in .pp-hero-old instead of .pp-eyebrow) sits closer to the price
4721
4749
  figure than the "3" badge's own offsets above. */
4722
4750
  .md3 .cc-price .pp-hero-old .pp-eyebrow-badge,
4723
- .md3 .fin-calc-card .pp-hero-old .pp-eyebrow-badge {
4751
+ .md3 .fin-calc-card .pp-hero-old .pp-eyebrow-badge,
4752
+ .md3 .vdp-bottom-bar .fin-rate-old-bb .pp-eyebrow-badge,
4753
+ .md3 .fin-modal .fin-rate-old-list .pp-eyebrow-badge {
4724
4754
  top: -7px;
4725
4755
  right: -12px;
4726
4756
  }
4727
4757
  /* "3" footnote popover — click the badge above to open, the × or an outside click to close. */
4728
4758
  .md3 .cc-price .pp-uvp-tooltip,
4729
- .md3 .fin-calc-card .pp-uvp-tooltip {
4759
+ .md3 .fin-calc-card .pp-uvp-tooltip,
4760
+ .md3 .vdp-bottom-bar .pp-uvp-tooltip,
4761
+ .md3 .fin-modal .pp-uvp-tooltip {
4730
4762
  position: absolute;
4731
4763
  top: 6px;
4732
4764
  right: -84px;
@@ -4739,10 +4771,18 @@ line-height: 31px;
4739
4771
  white-space: normal;
4740
4772
  cursor: default;
4741
4773
  }
4774
+ /* the bottom bar spans nearly the full phone width, so the shared -84px offset (tuned for
4775
+ the narrower .cc-price card / .fin-calc-card sheet) leaves the popover sitting too far left. */
4776
+ .md3 .vdp-bottom-bar .pp-uvp-tooltip {
4777
+ top: -20px;
4778
+ right: -110px;
4779
+ }
4742
4780
  /* floating circular close button — overlaps the tooltip's top-right corner from outside,
4743
4781
  same "glass circle" convention as VdpViewer's close/back/next overlay buttons. */
4744
4782
  .md3 .cc-price .pp-uvp-tooltip-close,
4745
- .md3 .fin-calc-card .pp-uvp-tooltip-close {
4783
+ .md3 .fin-calc-card .pp-uvp-tooltip-close,
4784
+ .md3 .vdp-bottom-bar .pp-uvp-tooltip-close,
4785
+ .md3 .fin-modal .pp-uvp-tooltip-close {
4746
4786
  position: absolute;
4747
4787
  top: -38px;
4748
4788
  right: -35px;
@@ -4758,7 +4798,9 @@ line-height: 31px;
4758
4798
  cursor: pointer;
4759
4799
  }
4760
4800
  .md3 .cc-price .pp-uvp-tooltip-body,
4761
- .md3 .fin-calc-card .pp-uvp-tooltip-body {
4801
+ .md3 .fin-calc-card .pp-uvp-tooltip-body,
4802
+ .md3 .vdp-bottom-bar .pp-uvp-tooltip-body,
4803
+ .md3 .fin-modal .pp-uvp-tooltip-body {
4762
4804
  font-family: Arial, Helvetica, sans-serif;
4763
4805
  font-weight: 400;
4764
4806
  font-size: 11px;
@@ -4846,6 +4888,25 @@ line-height: 31px;
4846
4888
  order: -1;
4847
4889
  align-self: flex-end;
4848
4890
  }
4891
+ /* Kaufen hero price on mobile — was falling through to the generic (rate-sized) .pp-hero-val
4892
+ above (65px, shrinking only via the rate-digit len4/5/6 classes cardParts.tsx never applies
4893
+ in Kaufen mode), so real purchase prices had no digit-based shrink at all here and could
4894
+ overflow. Mirrors the existing min-width:1025px desktop rule exactly. */
4895
+ .md3 .cc-price .pp-hero--buy .pp-hero-val {
4896
+ font-family: 'Arial';
4897
+ font-weight: 700;
4898
+ font-size: 52px;
4899
+ line-height: 58px;
4900
+ color: #005CFF;
4901
+ }
4902
+ .md3 .cc-price .pp-hero--buy .pp-hero-val.pp-hero-val--buy-len6 {
4903
+ font-size: 48px;
4904
+ line-height: 54px;
4905
+ }
4906
+ .md3 .cc-price .pp-hero--buy .pp-hero-val.pp-hero-val--buy-len7 {
4907
+ font-size: 43px;
4908
+ line-height: 47px;
4909
+ }
4849
4910
  /* Preisbewertung pill + segmented tier bar, grouped in one column exactly like the VDP
4850
4911
  finance-calculator's MOBILE .fin-eval-col (FinanceCalculator.tsx) — replaces the old
4851
4912
  StatChip pill + full-width gradient .pp-bar (see PriceEvalCol in cardParts.tsx). No
@@ -4877,9 +4938,11 @@ line-height: 31px;
4877
4938
  /* footnote 4 ("Top-Preis" claim) — .fin-eval-pill is a flex container, so vertical-align has
4878
4939
  no effect on the sup (it's a flex item, not inline content); position:relative/top raises
4879
4940
  it instead, same technique as the hero price's own 1/2/3 footnote markers. Shared by the
4880
- CarCard hero (.cc-price) and the VDP FinanceCalculator's own pill (.fin-calc-card). */
4941
+ CarCard hero (.cc-price), the VDP FinanceCalculator's own pill (.fin-calc-card), and the
4942
+ FinanceDetailsModal's embedded mobile rate sheet (.fin-modal). */
4881
4943
  .md3 .cc-price .fin-eval-pill sup,
4882
- .md3 .fin-calc-card .fin-eval-pill sup {
4944
+ .md3 .fin-calc-card .fin-eval-pill sup,
4945
+ .md3 .fin-modal .fin-eval-pill sup {
4883
4946
  position: relative;
4884
4947
  top: 0px;
4885
4948
  left: -4px;
@@ -4909,11 +4972,12 @@ line-height: 31px;
4909
4972
  (PricePaths); the link resets button chrome and reuses the primary color so it reads as tappable. */
4910
4973
  .md3 .cc-terms {
4911
4974
  margin-top: 9px;
4975
+ margin-bottom: 10px;
4912
4976
  font-family: Arial, Helvetica, sans-serif;
4913
4977
  font-style: normal;
4914
4978
  font-weight: 400;
4915
- font-size: 13px;
4916
- line-height: 15px;
4979
+ font-size: 12px;
4980
+ line-height: 14px;
4917
4981
  color: #0e2238;
4918
4982
  }
4919
4983
  .md3 .cc-terms-dot {
@@ -4936,6 +5000,7 @@ line-height: 31px;
4936
5000
  line-height: 15px;
4937
5001
  letter-spacing: -0.03em;
4938
5002
  color: #0e2238;
5003
+ text-decoration: underline;
4939
5004
  cursor: pointer;
4940
5005
  }
4941
5006
  /* card CTA row (Angebot anfragen / phone / Rate anpassen) — the shared .mbtn padding (0 24px)
@@ -4995,6 +5060,12 @@ line-height: 31px;
4995
5060
  border-bottom: 1px solid var(--md-outline-variant);
4996
5061
  margin-bottom: 18px;
4997
5062
  }
5063
+ /* VDP FinanceCalculator card only — scoped so it doesn't touch .vdp-bottom-bar / .fin-modal-body /
5064
+ .vdp-aside's own .fin-tabs margins (desktop base + mobile override above are both 1-class, so
5065
+ this 2-class selector wins purely on specificity regardless of viewport or source order). */
5066
+ .md3 .fin-calc-card .fin-tabs {
5067
+ margin-bottom: 10px;
5068
+ }
4998
5069
  .md3 .fin-tab {
4999
5070
  flex: 1;
5000
5071
  background: none;
@@ -5035,7 +5106,7 @@ line-height: 31px;
5035
5106
  grid-template-areas: "rate pill" "cond bar";
5036
5107
  gap: 12px 16px;
5037
5108
  align-items: center;
5038
- margin-top: 8px;
5109
+ margin-top: 0;
5039
5110
  }
5040
5111
  .md3 .fin-rate-main {
5041
5112
  grid-area: rate;
@@ -5488,7 +5559,24 @@ line-height: 31px;
5488
5559
  }
5489
5560
  .md3 .bb-tabs .fin-tab {
5490
5561
  padding: 15px 14px 10px 14px;
5491
- font-size: 13.5px;
5562
+ }
5563
+ /* :not(.on) — a later, equal-specificity mobile rule (.md3 .fin-tab:not(.on), Figma "dark +
5564
+ bold" spec for the desktop-style tabs) would otherwise win on source order alone; matching
5565
+ its :not(.on) here brings this to 4 classes vs its 3 so bb-tabs always wins regardless of
5566
+ where either rule sits in the file. */
5567
+ .md3 .bb-tabs .fin-tab:not(.on) {
5568
+ font-family: 'Arial';
5569
+ font-weight: 400;
5570
+ font-size: 14px;
5571
+ line-height: 16px;
5572
+ color: #0E2238;
5573
+ }
5574
+ .md3 .bb-tabs .fin-tab.on {
5575
+ font-family: 'Arial';
5576
+ font-weight: 700;
5577
+ font-size: 14px;
5578
+ line-height: 16px;
5579
+ color: #005CFF;
5492
5580
  }
5493
5581
  .md3 .bb-price-row .num.num--len5 {
5494
5582
  font-size: 42px;
@@ -7994,15 +8082,6 @@ color: #005CFF;
7994
8082
  line-height: 9px;
7995
8083
  color: #111820;
7996
8084
  }
7997
- /* "z.B. 499 €² / mtl.³" representative-example line under the hero rate */
7998
- .md3 .fin-rate-main .fin-rate-example-label {
7999
- font-family: Arial, Helvetica, sans-serif;
8000
- font-style: normal;
8001
- font-weight: 700;
8002
- font-size: 10px;
8003
- line-height: 11px;
8004
- color: #5b6b7f;
8005
- }
8006
8085
  .md3 .fin-rate-main .fin-rate-example-price {
8007
8086
  position: relative;
8008
8087
  display: inline-block;
@@ -1,62 +1,62 @@
1
- // Autobahn (auto.de redesign) — shared financing-number formatting.
2
- //
3
- // Extracted from vdp/VdpModules.tsx's FinanceDetailsModal so non-React consumers (e.g. the
4
- // Meta AIA marketing-image renderer) format the SAME real numbers the same way, instead of a
5
- // second copy drifting from the VDP's. Pure TS, no React/JSX — safe to import from a plain
6
- // Node service.
7
- //
8
- // The "only real figures, never a fabricated total" rule is load-bearing (legal: balloon
9
- // financing means monthly×term is NOT the total) and lives here once, not per-caller.
10
-
11
- /** Monetary figure → de-DE grouped string (e.g. 47400 → "47.400"). Feed rounded integers —
12
- * toLocaleString on a fractional value prints a bogus 3-decimal grouping. */
13
- export function formatEur(n: number): string {
14
- return n.toLocaleString('de-DE');
15
- }
16
-
17
- /** Rate → de-DE percent string (e.g. 6.49 → "6,49 %"). */
18
- export function formatPct(n: number): string {
19
- return `${n.toFixed(2).replace('.', ',')} %`;
20
- }
21
-
22
- export interface FinanceExampleFields {
23
- price: number;
24
- apr: number;
25
- down: number;
26
- /** Real config figure — falls back to price-down ONLY when genuinely absent. */
27
- netLoanAmount?: number;
28
- nominalRate?: number;
29
- finalInstallment?: number;
30
- grossAmount?: number;
31
- }
32
-
33
- export interface FinanceExampleLabels {
34
- vehiclePrice: string;
35
- priceSuffix: string;
36
- apr: string;
37
- nominal: string;
38
- net: string;
39
- down: string;
40
- final: string;
41
- gross: string;
42
- }
43
-
44
- /**
45
- * The "Repräsentatives Beispiel" disclaimer line (PAngV). Only fields with a genuine value are
46
- * included — nominalRate/finalInstallment/grossAmount are optional real config figures, never
47
- * derived (see fields doc). Matches production wording/format exactly (VdpModules.tsx's
48
- * `exampleLine`, byte-for-byte).
49
- */
50
- export function buildFinanceExampleLine(fields: FinanceExampleFields, labels: FinanceExampleLabels, footnoteMarker = '³'): string {
51
- const netto = fields.netLoanAmount != null ? fields.netLoanAmount : Math.max(0, fields.price - fields.down);
52
- const parts: string[] = [
53
- `${labels.vehiclePrice}: ${formatEur(fields.price)} € ${labels.priceSuffix}`,
54
- ...(fields.apr > 0 ? [`${labels.apr}: ${formatPct(fields.apr)}`] : []),
55
- ...(fields.nominalRate != null && fields.nominalRate > 0 ? [`${labels.nominal}: ${formatPct(fields.nominalRate)}`] : []),
56
- `${labels.net}: ${formatEur(netto)} €`,
57
- `${labels.down}: ${formatEur(fields.down)} €`,
58
- ...(fields.finalInstallment != null && fields.finalInstallment > 0 ? [`${labels.final}: ${formatEur(fields.finalInstallment)} €`] : []),
59
- ...(fields.grossAmount != null && fields.grossAmount > 0 ? [`${labels.gross}: ${formatEur(fields.grossAmount)} €`] : []),
60
- ];
61
- return `${footnoteMarker} ${parts.join('; ')};`;
62
- }
1
+ // Autobahn (auto.de redesign) — shared financing-number formatting.
2
+ //
3
+ // Extracted from vdp/VdpModules.tsx's FinanceDetailsModal so non-React consumers (e.g. the
4
+ // Meta AIA marketing-image renderer) format the SAME real numbers the same way, instead of a
5
+ // second copy drifting from the VDP's. Pure TS, no React/JSX — safe to import from a plain
6
+ // Node service.
7
+ //
8
+ // The "only real figures, never a fabricated total" rule is load-bearing (legal: balloon
9
+ // financing means monthly×term is NOT the total) and lives here once, not per-caller.
10
+
11
+ /** Monetary figure → de-DE grouped string (e.g. 47400 → "47.400"). Feed rounded integers —
12
+ * toLocaleString on a fractional value prints a bogus 3-decimal grouping. */
13
+ export function formatEur(n: number): string {
14
+ return n.toLocaleString('de-DE');
15
+ }
16
+
17
+ /** Rate → de-DE percent string (e.g. 6.49 → "6,49 %"). */
18
+ export function formatPct(n: number): string {
19
+ return `${n.toFixed(2).replace('.', ',')} %`;
20
+ }
21
+
22
+ export interface FinanceExampleFields {
23
+ price: number;
24
+ apr: number;
25
+ down: number;
26
+ /** Real config figure — falls back to price-down ONLY when genuinely absent. */
27
+ netLoanAmount?: number;
28
+ nominalRate?: number;
29
+ finalInstallment?: number;
30
+ grossAmount?: number;
31
+ }
32
+
33
+ export interface FinanceExampleLabels {
34
+ vehiclePrice: string;
35
+ priceSuffix: string;
36
+ apr: string;
37
+ nominal: string;
38
+ net: string;
39
+ down: string;
40
+ final: string;
41
+ gross: string;
42
+ }
43
+
44
+ /**
45
+ * The "Repräsentatives Beispiel" disclaimer line (PAngV). Only fields with a genuine value are
46
+ * included — nominalRate/finalInstallment/grossAmount are optional real config figures, never
47
+ * derived (see fields doc). Matches production wording/format exactly (VdpModules.tsx's
48
+ * `exampleLine`, byte-for-byte).
49
+ */
50
+ export function buildFinanceExampleLine(fields: FinanceExampleFields, labels: FinanceExampleLabels, footnoteMarker = '³'): string {
51
+ const netto = fields.netLoanAmount != null ? fields.netLoanAmount : Math.max(0, fields.price - fields.down);
52
+ const parts: string[] = [
53
+ `${labels.vehiclePrice}: ${formatEur(fields.price)} € ${labels.priceSuffix}`,
54
+ ...(fields.apr > 0 ? [`${labels.apr}: ${formatPct(fields.apr)}`] : []),
55
+ ...(fields.nominalRate != null && fields.nominalRate > 0 ? [`${labels.nominal}: ${formatPct(fields.nominalRate)}`] : []),
56
+ `${labels.net}: ${formatEur(netto)} €`,
57
+ `${labels.down}: ${formatEur(fields.down)} €`,
58
+ ...(fields.finalInstallment != null && fields.finalInstallment > 0 ? [`${labels.final}: ${formatEur(fields.finalInstallment)} €`] : []),
59
+ ...(fields.grossAmount != null && fields.grossAmount > 0 ? [`${labels.gross}: ${formatEur(fields.grossAmount)} €`] : []),
60
+ ];
61
+ return `${footnoteMarker} ${parts.join('; ')};`;
62
+ }
@@ -1,58 +1,58 @@
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) — a pure, testable leaf
6
- // module. CommonUtils.ts re-exports these for existing consumers — one implementation, not a fork.
7
- // (image-generator's Meta AIA renderer vendors a copy of this math directly instead of depending
8
- // on this package — see services/image-generator/src/services/metaAia/vendoredAutobahnMath.ts —
9
- // so a plain-Node backend doesn't need to npm-install this whole design system's dependency tree.)
10
- import { priceRatings, priceRatingConfig } from '../constants/price';
11
- import { PREDICTABLE_PRICE_INDEX } from '../constants/core';
12
-
13
- export const getPriceRatingConfig = (currentPrice: number, pricePredicted: number) => { // tslint:disable-line
14
- const round = (value: number) => {
15
- return Number.isFinite(pricePredicted)
16
- ? Math.round((pricePredicted * (1 + (value / 100))) / 100) * 100 // tslint:disable-line
17
- : '???';
18
- };
19
-
20
- if (currentPrice === 0) {
21
- console.log('Empty current price');
22
- }
23
-
24
- return Object.keys(priceRatingConfig).reduce((acc: any, key: any) => {
25
- const { min, max } = priceRatingConfig[key];
26
- return { ...acc, [key]: { min: round(min), max: round(max) } };
27
- }, {}); // tslint:disable-line
28
- };
29
-
30
- export const getPredictablePrice = (currentSalesPrice: number, predictablePrice: number) => {
31
- if (Number.isFinite(Number.parseFloat(`${predictablePrice}`))) {
32
- return predictablePrice;
33
- }
34
-
35
- return currentSalesPrice * PREDICTABLE_PRICE_INDEX;
36
- }
37
-
38
- export const getPriceRating = (currentSalesPrice: number, predictablePrice: number) => {
39
- const isCorrectCurrentSalesPrice = Number.isFinite(Number.parseFloat(`${currentSalesPrice}`));
40
- // const isCorrectPredictablePrice = Number.isFinite(Number.parseFloat(`${predictablePrice}`));
41
-
42
- if (!isCorrectCurrentSalesPrice) return 0;
43
-
44
-
45
- const validatedPredictablePrice = getPredictablePrice(currentSalesPrice, predictablePrice);
46
-
47
- const ratingConfig = getPriceRatingConfig(currentSalesPrice, validatedPredictablePrice);
48
-
49
- for (const key of Object.keys(ratingConfig)) {
50
- const { min, max } = ratingConfig[key];
51
- const TOP_PRICE = 5;
52
- if (key === 'highPrice' && currentSalesPrice === max) return 1;
53
- if (key === 'topPrice' && currentSalesPrice <= min) return TOP_PRICE;
54
- if (currentSalesPrice >= min && currentSalesPrice < max) return priceRatings.indexOf(key);
55
- }
56
-
57
- return 0;
58
- };
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) — a pure, testable leaf
6
+ // module. CommonUtils.ts re-exports these for existing consumers — one implementation, not a fork.
7
+ // (image-generator's Meta AIA renderer vendors a copy of this math directly instead of depending
8
+ // on this package — see services/image-generator/src/services/metaAia/vendoredAutobahnMath.ts —
9
+ // so a plain-Node backend doesn't need to npm-install this whole design system's dependency tree.)
10
+ import { priceRatings, priceRatingConfig } from '../constants/price';
11
+ import { PREDICTABLE_PRICE_INDEX } from '../constants/core';
12
+
13
+ export const getPriceRatingConfig = (currentPrice: number, pricePredicted: number) => { // tslint:disable-line
14
+ const round = (value: number) => {
15
+ return Number.isFinite(pricePredicted)
16
+ ? Math.round((pricePredicted * (1 + (value / 100))) / 100) * 100 // tslint:disable-line
17
+ : '???';
18
+ };
19
+
20
+ if (currentPrice === 0) {
21
+ console.log('Empty current price');
22
+ }
23
+
24
+ return Object.keys(priceRatingConfig).reduce((acc: any, key: any) => {
25
+ const { min, max } = priceRatingConfig[key];
26
+ return { ...acc, [key]: { min: round(min), max: round(max) } };
27
+ }, {}); // tslint:disable-line
28
+ };
29
+
30
+ export const getPredictablePrice = (currentSalesPrice: number, predictablePrice: number) => {
31
+ if (Number.isFinite(Number.parseFloat(`${predictablePrice}`))) {
32
+ return predictablePrice;
33
+ }
34
+
35
+ return currentSalesPrice * PREDICTABLE_PRICE_INDEX;
36
+ }
37
+
38
+ export const getPriceRating = (currentSalesPrice: number, predictablePrice: number) => {
39
+ const isCorrectCurrentSalesPrice = Number.isFinite(Number.parseFloat(`${currentSalesPrice}`));
40
+ // const isCorrectPredictablePrice = Number.isFinite(Number.parseFloat(`${predictablePrice}`));
41
+
42
+ if (!isCorrectCurrentSalesPrice) return 0;
43
+
44
+
45
+ const validatedPredictablePrice = getPredictablePrice(currentSalesPrice, predictablePrice);
46
+
47
+ const ratingConfig = getPriceRatingConfig(currentSalesPrice, validatedPredictablePrice);
48
+
49
+ for (const key of Object.keys(ratingConfig)) {
50
+ const { min, max } = ratingConfig[key];
51
+ const TOP_PRICE = 5;
52
+ if (key === 'highPrice' && currentSalesPrice === max) return 1;
53
+ if (key === 'topPrice' && currentSalesPrice <= min) return TOP_PRICE;
54
+ if (currentSalesPrice >= min && currentSalesPrice < max) return priceRatings.indexOf(key);
55
+ }
56
+
57
+ return 0;
58
+ };