@bytebrand/fe-ui-core-autobahn 1.0.163 → 1.0.165
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/locales/de/autobahn.json +2 -1
- package/locales/en/autobahn.json +2 -1
- package/package.json +30 -30
- package/ui/Dropdown.tsx +16 -8
- package/ui/index.ts +2 -0
- package/ui/scrollFieldIntoView.ts +41 -0
- package/ui/usePortalMenuPosition.ts +14 -12
- package/utils/index.ts +2 -0
- package/vdp/VdpModules.tsx +3 -3
- package/vdp/types.ts +5 -5
- package/vehiclesProps/accidentDisclosure.ts +25 -0
- package/vehiclesProps/decoratedLightProps.tsx +4 -0
- package/vehiclesProps/decoratedProps.tsx +4 -0
package/locales/de/autobahn.json
CHANGED
package/locales/en/autobahn.json
CHANGED
package/package.json
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@bytebrand/fe-ui-core-autobahn",
|
|
3
|
-
"version": "1.0.
|
|
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
|
-
"main": "index.ts",
|
|
6
|
-
"license": "UNLICENSED",
|
|
7
|
-
"peerDependencies": {
|
|
8
|
-
"react": "^18.0.0",
|
|
9
|
-
"react-dom": "^18.0.0"
|
|
10
|
-
},
|
|
11
|
-
"dependencies": {
|
|
12
|
-
"@bytebrand/car-schema-selectors": "^2.0.7",
|
|
13
|
-
"@bytebrand/fe-financing": "^5.14.20",
|
|
14
|
-
"classnames": "2.2.6",
|
|
15
|
-
"clsx": "^2.1.1",
|
|
16
|
-
"d3-format": "1.4.5",
|
|
17
|
-
"js-cookie": "^2.2.0",
|
|
18
|
-
"lodash": "^4.17.21",
|
|
19
|
-
"lucide-react": "^0.288.0",
|
|
20
|
-
"mobx-react": "^7.6.0",
|
|
21
|
-
"moment": "^2.29.1",
|
|
22
|
-
"qs": "^6.6.0",
|
|
23
|
-
"react-device-detect": "^2.1.2",
|
|
24
|
-
"react-grid-system": "^5.0.2",
|
|
25
|
-
"react-lazyload": "^3.2.0",
|
|
26
|
-
"react-scroll": "^1.9.3",
|
|
27
|
-
"react-slick": "^0.29.0",
|
|
28
|
-
"slick-carousel": "1.8.1"
|
|
29
|
-
}
|
|
30
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@bytebrand/fe-ui-core-autobahn",
|
|
3
|
+
"version": "1.0.165",
|
|
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
|
+
"main": "index.ts",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"peerDependencies": {
|
|
8
|
+
"react": "^18.0.0",
|
|
9
|
+
"react-dom": "^18.0.0"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@bytebrand/car-schema-selectors": "^2.0.7",
|
|
13
|
+
"@bytebrand/fe-financing": "^5.14.20",
|
|
14
|
+
"classnames": "2.2.6",
|
|
15
|
+
"clsx": "^2.1.1",
|
|
16
|
+
"d3-format": "1.4.5",
|
|
17
|
+
"js-cookie": "^2.2.0",
|
|
18
|
+
"lodash": "^4.17.21",
|
|
19
|
+
"lucide-react": "^0.288.0",
|
|
20
|
+
"mobx-react": "^7.6.0",
|
|
21
|
+
"moment": "^2.29.1",
|
|
22
|
+
"qs": "^6.6.0",
|
|
23
|
+
"react-device-detect": "^2.1.2",
|
|
24
|
+
"react-grid-system": "^5.0.2",
|
|
25
|
+
"react-lazyload": "^3.2.0",
|
|
26
|
+
"react-scroll": "^1.9.3",
|
|
27
|
+
"react-slick": "^0.29.0",
|
|
28
|
+
"slick-carousel": "1.8.1"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/ui/Dropdown.tsx
CHANGED
|
@@ -22,6 +22,7 @@ import * as ReactDOM from 'react-dom';
|
|
|
22
22
|
|
|
23
23
|
import Icon from './Icon';
|
|
24
24
|
import { usePortalMenuPosition } from './usePortalMenuPosition';
|
|
25
|
+
import { scrollFieldIntoView } from './scrollFieldIntoView';
|
|
25
26
|
|
|
26
27
|
export interface DropdownOption {
|
|
27
28
|
value: string;
|
|
@@ -189,15 +190,22 @@ const Dropdown: React.FunctionComponent<DropdownProps> = ({
|
|
|
189
190
|
if (allowCustomValue) setQuery(value || '');
|
|
190
191
|
}, [disabled, selectedIndex, allowCustomValue, value]);
|
|
191
192
|
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
//
|
|
193
|
+
// Scroll the trigger (searchable or not) to the top of the page on open, THEN — searchable
|
|
194
|
+
// only — focus the search input instead of leaving focus on the trigger. Every Dropdown shares
|
|
195
|
+
// this, not just searchable ones: on mobile a trigger sitting mid-page can open a menu that
|
|
196
|
+
// runs off the bottom of the viewport with no way to see the rest of it, same underlying
|
|
197
|
+
// problem a searchable trigger has with the keyboard eating the space below it. A plain
|
|
198
|
+
// `scrollIntoView({block:'center'})` used to run here (searchable-only) — "center" only moves
|
|
199
|
+
// the trigger when it's already near a viewport edge, so on a trigger that's roughly mid-page
|
|
200
|
+
// already (the common case) it was a near no-op, leaving a few hundred px of dead space above
|
|
201
|
+
// the trigger that the keyboard (searchable) or the menu's own bottom edge (any dropdown) then
|
|
202
|
+
// ran into.
|
|
196
203
|
React.useEffect(() => {
|
|
197
|
-
if (!open
|
|
198
|
-
if (rootRef.current
|
|
199
|
-
|
|
200
|
-
|
|
204
|
+
if (!open) return;
|
|
205
|
+
if (rootRef.current) scrollFieldIntoView(rootRef.current);
|
|
206
|
+
// searchRef is only ever attached in the searchable+open (editing) render branch, so this
|
|
207
|
+
// naturally no-ops for a non-searchable dropdown — nothing else to focus there, the trigger
|
|
208
|
+
// <button> already has focus from the click/keypress that opened it.
|
|
201
209
|
if (searchRef.current) {
|
|
202
210
|
searchRef.current.focus();
|
|
203
211
|
// pre-filled custom value (see openMenu) starts selected, so typing immediately
|
package/ui/index.ts
CHANGED
|
@@ -39,6 +39,8 @@ export type { FormFieldProps } from './FormField';
|
|
|
39
39
|
export { default as Dropdown } from './Dropdown';
|
|
40
40
|
export type { DropdownProps, DropdownOption } from './Dropdown';
|
|
41
41
|
|
|
42
|
+
export { scrollFieldIntoView } from './scrollFieldIntoView';
|
|
43
|
+
|
|
42
44
|
export { default as Spec } from './Spec';
|
|
43
45
|
export type { SpecProps } from './Spec';
|
|
44
46
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Autobahn (auto.de redesign) — shared "scroll a just-opened/just-focused field to the top of the
|
|
2
|
+
// page" behaviour, used by Dropdown.tsx and by fe-main's DualRange/RateSlider (FilterContent.tsx)
|
|
3
|
+
// text fields on /search/detailed. On mobile, opening a dropdown's menu or focusing a text field
|
|
4
|
+
// brings the on-screen keyboard up and/or needs room for a popover below the field — if the field
|
|
5
|
+
// is sitting mid-page, there may not be enough space left below it once the keyboard/menu takes
|
|
6
|
+
// its share. Scrolling the field to the very top of the page first maximizes what's available.
|
|
7
|
+
//
|
|
8
|
+
// Extracted out of Dropdown.tsx so it can be shared verbatim with fe-main's non-Dropdown form
|
|
9
|
+
// fields (DualRange/RateSlider's text inputs) — same problem, same fix, one place to keep them
|
|
10
|
+
// from drifting apart. See Dropdown.tsx's own call site for the original reasoning this was
|
|
11
|
+
// pulled from.
|
|
12
|
+
export function scrollFieldIntoView(field: HTMLElement): void {
|
|
13
|
+
if (typeof window === 'undefined') return;
|
|
14
|
+
// Mobile-only: the on-screen-keyboard/popover space problem this fixes doesn't exist on
|
|
15
|
+
// desktop, where there's no keyboard pushing the viewport around. Same breakpoint + matchMedia
|
|
16
|
+
// pattern as Modal.tsx/CardCarousel.tsx elsewhere in this lib.
|
|
17
|
+
if (window.matchMedia('(min-width: 601px)').matches) return;
|
|
18
|
+
// Anchor on whichever is higher up: the field itself, or a short field label/caption sitting
|
|
19
|
+
// directly above it (own previous sibling, or — the common "label above a row of controls"
|
|
20
|
+
// shape, e.g. "Marke & Modell" above the Make/Model row on /search/detailed — the wrapping
|
|
21
|
+
// row's previous sibling). Checked by height, not by class name, so this works for ANY
|
|
22
|
+
// caller's label without this needing to know that call site's specific markup.
|
|
23
|
+
const MAX_LABEL_HEIGHT = 30;
|
|
24
|
+
const asLabel = (el: Element | null): HTMLElement | null => {
|
|
25
|
+
if (!(el instanceof HTMLElement)) return null;
|
|
26
|
+
const h = el.getBoundingClientRect().height;
|
|
27
|
+
return h > 0 && h <= MAX_LABEL_HEIGHT ? el : null;
|
|
28
|
+
};
|
|
29
|
+
const anchor =
|
|
30
|
+
asLabel(field.previousElementSibling) ||
|
|
31
|
+
asLabel(field.parentElement && field.parentElement.previousElementSibling) ||
|
|
32
|
+
field;
|
|
33
|
+
// No sticky-header reservation here: every current caller (NewMainPage '/', FilterContent on
|
|
34
|
+
// '/search*') sits on a page where TopBar's own autoHideOnScroll is on (App.tsx: isHomePage ||
|
|
35
|
+
// isSearchPage) — the header slides itself out of the way the instant this scroll starts,
|
|
36
|
+
// independently of this call, so reserving space for it here would double-count.
|
|
37
|
+
const GAP = 8;
|
|
38
|
+
const rect = anchor.getBoundingClientRect();
|
|
39
|
+
const targetY = window.scrollY + rect.top - GAP;
|
|
40
|
+
window.scrollTo({ top: Math.max(0, targetY), behavior: 'smooth' });
|
|
41
|
+
}
|
|
@@ -79,6 +79,20 @@ const ESTIMATED_MENU_HEIGHT = 300;
|
|
|
79
79
|
// a scroll within this window of opening REPOSITIONS instead of closing — see the effect below.
|
|
80
80
|
const SELF_SCROLL_GRACE_MS = 500;
|
|
81
81
|
|
|
82
|
+
// Module-level (not hook-local) so Dropdown.tsx's own searchable-open scroll effect can reuse the
|
|
83
|
+
// exact same measurement — the sticky app header (.topbar, position:sticky/top:0) sits over the
|
|
84
|
+
// top of the viewport on every page that uses it (TopBar's autoHideOnScroll is opt-in, off by
|
|
85
|
+
// default — most pages keep it permanently docked), so anything that scrolls/positions content
|
|
86
|
+
// near the top of the viewport needs to stop at its live bottom edge, not y=0. Reading the
|
|
87
|
+
// header's live bottom edge (0 when absent, e.g. inside a modal, or when autoHideOnScroll has slid
|
|
88
|
+
// it away) gives a correct boundary without hardcoding a pixel height that could drift from the
|
|
89
|
+
// header's own CSS.
|
|
90
|
+
export const getSafeTop = (): number => {
|
|
91
|
+
if (typeof document === 'undefined') return 0;
|
|
92
|
+
const header = document.querySelector('.topbar');
|
|
93
|
+
return header ? Math.max(0, header.getBoundingClientRect().bottom) : 0;
|
|
94
|
+
};
|
|
95
|
+
|
|
82
96
|
export function usePortalMenuPosition<TMenuEl extends HTMLElement = HTMLDivElement>({
|
|
83
97
|
enabled,
|
|
84
98
|
open,
|
|
@@ -121,18 +135,6 @@ export function usePortalMenuPosition<TMenuEl extends HTMLElement = HTMLDivEleme
|
|
|
121
135
|
};
|
|
122
136
|
}, [enabled, open, portalRoot]);
|
|
123
137
|
|
|
124
|
-
// the portal menu's z-index (1400, .mselect-menu--portal) is intentionally higher than every
|
|
125
|
-
// other fixed overlay it must paint over (checkout sheets/modals) — which also means it would
|
|
126
|
-
// paint over the sticky app header (.topbar, position:sticky/top:0/z-index:50) if left
|
|
127
|
-
// unconstrained. Reading the header's live bottom edge (0 when absent, e.g. inside a modal, or
|
|
128
|
-
// when TopBar's autoHideOnScroll has slid it away) gives a correct top boundary without
|
|
129
|
-
// hardcoding a pixel height that could drift from the header's own CSS.
|
|
130
|
-
const getSafeTop = (): number => {
|
|
131
|
-
if (typeof document === 'undefined') return 0;
|
|
132
|
-
const header = document.querySelector('.topbar');
|
|
133
|
-
return header ? Math.max(0, header.getBoundingClientRect().bottom) : 0;
|
|
134
|
-
};
|
|
135
|
-
|
|
136
138
|
const [menuPos, setMenuPos] = React.useState<PortalMenuPos | null>(null);
|
|
137
139
|
|
|
138
140
|
const positionMenu = React.useCallback(() => {
|
package/utils/index.ts
CHANGED
|
@@ -83,3 +83,5 @@ export type { IValues, IFilters, IQuery } from './query';
|
|
|
83
83
|
// Decorated car props (renders React UI; lives in vehiclesProps/ with its
|
|
84
84
|
// IconContainer + Co2Widget dependencies under compat/).
|
|
85
85
|
export { default as getDecoratedProps } from '../vehiclesProps/decoratedProps';
|
|
86
|
+
export { setStrictConditionOwners, isStrictConditionOwner, strictAccidentStatus } from '../vehiclesProps/accidentDisclosure';
|
|
87
|
+
export type { AccidentStatus } from '../vehiclesProps/accidentDisclosure';
|
package/vdp/VdpModules.tsx
CHANGED
|
@@ -92,7 +92,7 @@ export const VdpHeaderSummary: React.FunctionComponent<VdpHeaderCardProps> = ({
|
|
|
92
92
|
['zap', extractPS(vehicle.power)],
|
|
93
93
|
['fuel', stripParen(vehicle.fuel)],
|
|
94
94
|
['cog', vehicle.gear],
|
|
95
|
-
['user', t('autobahn:header.owners', { owners: vehicle.owners, count: vehicle.owners })],
|
|
95
|
+
...(vehicle.owners == null ? [] : ([['user', t('autobahn:header.owners', { owners: vehicle.owners, count: vehicle.owners })]] as const)),
|
|
96
96
|
];
|
|
97
97
|
return (
|
|
98
98
|
<>
|
|
@@ -142,7 +142,7 @@ export const VdpFactsGrid: React.FunctionComponent<VdpHeaderCardProps> = ({ vehi
|
|
|
142
142
|
['calendar', vehicle.reg],
|
|
143
143
|
['cog', vehicle.gear],
|
|
144
144
|
['fuel', stripParen(vehicle.fuel)],
|
|
145
|
-
['user', t('autobahn:header.owners', { owners: vehicle.owners, count: vehicle.owners })],
|
|
145
|
+
...(vehicle.owners == null ? [] : ([['user', t('autobahn:header.owners', { owners: vehicle.owners, count: vehicle.owners })]] as const)),
|
|
146
146
|
...(vehicle.body ? ([['car-front', vehicle.body]] as const) : []),
|
|
147
147
|
];
|
|
148
148
|
return (
|
|
@@ -216,7 +216,7 @@ export const VdpHeaderCondition: React.FunctionComponent<VdpHeaderCardProps> = (
|
|
|
216
216
|
// only the Garantie tile has one, linking out to auto.de's warranty policy page.
|
|
217
217
|
const tiles: ReadonlyArray<readonly [string, string, string, string?]> = [
|
|
218
218
|
['wrench', t('autobahn:header.huLabel'), condition.hu === 'Neu' ? t('autobahn:header.huNew') : t('autobahn:header.huUntil', { date: condition.hu })],
|
|
219
|
-
['shield-check', t('autobahn:header.accidentFree'), condition.unfallfrei ? t('autobahn:header.yes') : t('autobahn:header.no')],
|
|
219
|
+
['shield-check', t('autobahn:header.accidentFree'), condition.unfallfrei == null ? t('autobahn:header.notSpecified') : condition.unfallfrei ? t('autobahn:header.yes') : t('autobahn:header.no')],
|
|
220
220
|
['clipboard-check', t('autobahn:header.serviceHistory'), condition.scheckheft ? t('autobahn:header.yes') : t('autobahn:header.no')],
|
|
221
221
|
['award', t('autobahn:header.warranty'), condition.garantie, 'https://www.auto.de/garantie/'],
|
|
222
222
|
];
|
package/vdp/types.ts
CHANGED
|
@@ -111,8 +111,8 @@ export interface Co2CostsSummary {
|
|
|
111
111
|
export interface VehicleCondition {
|
|
112
112
|
/** TÜV/HU validity, e.g. '03/2027' — or 'Neu' for a new HU. */
|
|
113
113
|
hu: string;
|
|
114
|
-
/** accident-free flag (Unfallfrei). */
|
|
115
|
-
unfallfrei: boolean;
|
|
114
|
+
/** accident-free flag (Unfallfrei); null = not specified by the source ("keine Angabe"). */
|
|
115
|
+
unfallfrei: boolean | null;
|
|
116
116
|
/** full service history (Scheckheftgepflegt). */
|
|
117
117
|
scheckheft: boolean;
|
|
118
118
|
/** warranty label, e.g. '12 Monate inkl.'. */
|
|
@@ -191,8 +191,8 @@ export interface VehicleDetail {
|
|
|
191
191
|
fuel: string;
|
|
192
192
|
/** gearbox label, e.g. 'Automatik' / 'Schaltgetriebe'. */
|
|
193
193
|
gear: string;
|
|
194
|
-
/** number of previous owners (rendered as 'N. Hand'). */
|
|
195
|
-
owners: number;
|
|
194
|
+
/** number of previous owners (rendered as 'N. Hand'); null = not known, the fact is not shown. */
|
|
195
|
+
owners: number | null;
|
|
196
196
|
/** number of seats. */
|
|
197
197
|
seats: number;
|
|
198
198
|
/** number of doors. */
|
|
@@ -302,7 +302,7 @@ export function defaultVdpSpecs(v: VehicleDetail): VdpSpec[] {
|
|
|
302
302
|
[['cog', 'Getriebe', v.gear], true],
|
|
303
303
|
[['users', 'Sitze', `${v.seats} Sitze`], false],
|
|
304
304
|
[['door-open', 'Türen', `${v.doors} Türen`], false],
|
|
305
|
-
[['user', 'Halter', `${v.owners}. Hand`], true], // 0 Vorbesitzer is valid
|
|
305
|
+
...(v.owners == null ? [] : [[['user', 'Halter', `${v.owners}. Hand`], true] as const]), // 0 Vorbesitzer is valid
|
|
306
306
|
[['palette', 'Außenfarbe', v.color], true],
|
|
307
307
|
[['car-front', 'Karosserie', v.body], true],
|
|
308
308
|
[['droplet', 'Verbrauch komb.', v.cons], true], // Pkw-EnVKV — exempt from this rule
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// FINN handover v0.6 (Daniel, 15.10.2026): for the owners set here, accident and owner facts follow
|
|
2
|
+
// only what the source states — mainData.accidentDamaged false → accident-free, true → accident,
|
|
3
|
+
// empty → not specified; an empty owner count is not shown as "0. Hand". Every other dealer keeps
|
|
4
|
+
// the old display (accident-free unless `damaged`). The app sets the owners once at start.
|
|
5
|
+
let strictOwners: ReadonlyArray<string> = [];
|
|
6
|
+
|
|
7
|
+
export function setStrictConditionOwners(ownerIds: ReadonlyArray<string>): void {
|
|
8
|
+
strictOwners = (ownerIds || []).filter(Boolean);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function isStrictConditionOwner(car: any): boolean {
|
|
12
|
+
const ownerId = car?.metaData?.ownerId;
|
|
13
|
+
return !!ownerId && strictOwners.includes(ownerId);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type AccidentStatus = 'accidentFree' | 'accident' | 'notSpecified';
|
|
17
|
+
|
|
18
|
+
// The strict status, or undefined when the car's owner keeps the old display.
|
|
19
|
+
export function strictAccidentStatus(car: any): AccidentStatus | undefined {
|
|
20
|
+
if (!isStrictConditionOwner(car)) return undefined;
|
|
21
|
+
const value = car?.mainData?.accidentDamaged;
|
|
22
|
+
if (value === true) return 'accident';
|
|
23
|
+
if (value === false) return 'accidentFree';
|
|
24
|
+
return 'notSpecified';
|
|
25
|
+
}
|
|
@@ -6,6 +6,7 @@ import { formatTimestamp } from '../utils/DateUtils';
|
|
|
6
6
|
import { DELIVERY_PERIODS_EXTRA } from '../constants/core';
|
|
7
7
|
import moment from 'moment';
|
|
8
8
|
import { IDecoratedProp } from '../types/types';
|
|
9
|
+
import { strictAccidentStatus } from './accidentDisclosure';
|
|
9
10
|
|
|
10
11
|
interface IDecoratedProps {
|
|
11
12
|
[key: string]: IDecoratedProp;
|
|
@@ -519,6 +520,9 @@ const getDecoratedLightProps = (
|
|
|
519
520
|
title: t('cbd:mainData_accidentDamaged'),
|
|
520
521
|
icon: 'wrench',
|
|
521
522
|
get value() {
|
|
523
|
+
const strict = strictAccidentStatus(car);
|
|
524
|
+
if (strict === 'notSpecified') return t('vehicleProps:value.na');
|
|
525
|
+
if (strict) return strict === 'accident' ? t('vehicleProps:value.damaged') : t('vehicleProps:value.accidentFree');
|
|
522
526
|
return damaged ? t('vehicleProps:value.damaged') : t('vehicleProps:value.accidentFree');
|
|
523
527
|
}
|
|
524
528
|
},
|
|
@@ -9,6 +9,7 @@ import { EMISSION_STICKERS_ICONS, WP_EMISSION_STICKERS_ICONS } from '../constant
|
|
|
9
9
|
import { DELIVERY_PERIODS_EXTRA } from '../constants';
|
|
10
10
|
import { IDecoratedProp, ICar, IFinancingUtils } from '../types/types';
|
|
11
11
|
import { getFormattedPrice } from '../utils/CommonUtils';
|
|
12
|
+
import { strictAccidentStatus } from './accidentDisclosure';
|
|
12
13
|
|
|
13
14
|
const formatDeliveryPeriod = (t: (key: string, options?: object) => string, deliveryPeriod: string) => {
|
|
14
15
|
const count: number = +DELIVERY_PERIODS_EXTRA.find((period: any) => period.value === deliveryPeriod).label;
|
|
@@ -195,6 +196,9 @@ const getDecoratedProps = (
|
|
|
195
196
|
title: t('cbd:mainData_accidentDamaged'),
|
|
196
197
|
icon: 'wrench',
|
|
197
198
|
get value() {
|
|
199
|
+
const strict = strictAccidentStatus(car);
|
|
200
|
+
if (strict === 'notSpecified') return t('vehicleProps:value.na');
|
|
201
|
+
if (strict) return strict === 'accident' ? t('vehicleProps:value.damaged') : t('vehicleProps:value.accidentFree');
|
|
198
202
|
return damaged ? t('vehicleProps:value.damaged') : t('vehicleProps:value.accidentFree');
|
|
199
203
|
}
|
|
200
204
|
},
|