feat(web): source-aware vehicle attributes — surface pcat parameters

Vehicle info only ever read PL24's vinfoBasic segment, so emex/pcat/vin-api
vehicles fell back to 3-4 sparse DB columns. Introduce a getDisplayAttributes
dispatcher keyed on the decode source. First source added: parts-catalogs,
which ships a structured pcatCar.parameters list (Year, Region, Steering,
Transmission type/code, Engine, Type, Configuration…) — sorted by sortOrder
with English names mapped to Turkish. A shared header-dedup drops the
brand/model/year rows that already live in the h2 and the collapsed summary.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-25 03:47:01 +03:00
parent dd95641497
commit 1549c14a09

View File

@@ -266,30 +266,91 @@ function getVehicleAttributes(vehicle: any): Array<{ label: string; value: strin
}));
}
function VehicleAttributes({ vehicle }: { vehicle: any }) {
const richAttrs = getVehicleAttributes(vehicle);
type Attr = { label: string; value: string };
// Human-readable fields the VIN-code records don't carry (a decoded engine
// description, body type). Prepended so the legible info leads and the raw
// decoded codes follow — instead of the codes hiding the readable data.
const readableExtras = [
{ label: "Motor", value: vehicle?.engine },
{ label: "Çekiş", value: vehicle?.rawData?.driveType },
{ label: "Kasa", value: vehicle?.bodyType },
].filter((a): a is { label: string; value: string } => Boolean(a.value));
const isAttr = (a: { label: string; value: unknown }): a is Attr =>
a.value != null && String(a.value).trim() !== "";
const fallbackAttrs = [
/** Labels already conveyed by the page header (brand/model/year) or the
* collapsed summary — dropped from the detail grid to avoid repetition. */
const HEADER_DUPE = new Set([
"marka",
"model",
"yıl",
"yil",
"year",
"model yılı",
"model yili",
"car name",
]);
const dropHeaderDupes = (attrs: Attr[]): Attr[] =>
attrs.filter((a) => !HEADER_DUPE.has(a.label.toLowerCase()));
/** parts-catalogs (pcat) ships a structured parameter list with English names. */
const PCAT_LABELS: Record<string, string> = {
Year: "Yıl",
Region: "Bölge",
Steering: "Direksiyon",
"Transmission type": "Şanzıman tipi",
"Transmission code": "Şanzıman kodu",
Engine: "Motor kodu",
"Engine index": "Motor indeksi",
Type: "Tip",
Modification: "Tip",
Configuration: "Donanım",
"Car name": "Model",
};
function fromPcat(vehicle: any): Attr[] {
const params = vehicle?.rawData?.pcatCar?.parameters;
if (!Array.isArray(params)) return [];
return [...params]
.sort((a: any, b: any) => (a?.sortOrder ?? 999) - (b?.sortOrder ?? 999))
.map((p: any) => ({
label: PCAT_LABELS[p?.name] ?? String(p?.name ?? "").trim(),
value: String(p?.value ?? "").trim(),
}))
.filter((a: Attr) => Boolean(a.label && a.value));
}
/** Plain DB columns — last resort when no source payload is recognised. */
function getFallbackAttributes(vehicle: any): Attr[] {
return [
{ label: "Marka", value: vehicle?.brandName },
{ label: "Model", value: vehicle?.model },
{ label: "Yıl", value: vehicle?.year },
{ label: "Motor", value: vehicle?.engine },
{ label: "Vites", value: vehicle?.transmission },
{ label: "Kasa", value: vehicle?.bodyType },
].filter((a): a is { label: string; value: string } => Boolean(a.value));
].filter(isAttr);
}
// With VIN-decoded detail: lead with readable fields, then the decoded codes.
// Without it: plain DB fields.
const attrs = richAttrs.length > 0 ? [...readableExtras, ...richAttrs] : fallbackAttrs;
/** Resolve the attribute list for the vehicle's decode source. Each catalog
* ships its detail in a different shape; surface the richest one available. */
function getDisplayAttributes(vehicle: any): Attr[] {
// PL24 — readable extras lead, then the decoded VIN codes.
const vinfo = getVehicleAttributes(vehicle);
if (vinfo.length > 0) {
const extras = [
{ label: "Motor", value: vehicle?.engine },
{ label: "Çekiş", value: vehicle?.rawData?.driveType },
{ label: "Kasa", value: vehicle?.bodyType },
].filter(isAttr);
return [...extras, ...vinfo];
}
// parts-catalogs — structured parameter list.
if (vehicle?.source === "parts-catalogs") {
const a = dropHeaderDupes(fromPcat(vehicle));
if (a.length > 0) return a;
}
return dropHeaderDupes(getFallbackAttributes(vehicle));
}
function VehicleAttributes({ vehicle }: { vehicle: any }) {
const attrs = getDisplayAttributes(vehicle);
if (attrs.length === 0) {
return (