perf(p): instant OEM nav + trim 30x-bloated payload
OEM detail "slow load" was two things, not the query (DB 135ms / API 13-120ms): 1. The parts-panel link opened a NEW TAB → full SPA cold boot every click. Switch to in-app client navigation on plain click (real href kept, so ctrl/cmd/middle-click still opens a new tab). 2. The /p/oem response shipped each article's oeNumbers + compatible lists (up to 200 each × 60 articles) that the UI never renders — 96% of a 370 KB payload. Ship lean articles; aggregates already carry the cross-refs. 60-article code: 370 KB → ~12 KB. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -240,7 +240,18 @@ export class PSourceDbService implements OnModuleInit, OnModuleDestroy {
|
||||
query,
|
||||
queryNorm,
|
||||
matched: true,
|
||||
articles,
|
||||
// Ship lean articles — the per-article oeNumbers/compatible lists were
|
||||
// only needed above to build the aggregates; sending them too bloated
|
||||
// heavy responses ~30x (370 KB → ~12 KB for a 60-article code).
|
||||
articles: articles.map((a) => ({
|
||||
id: a.id,
|
||||
brand: a.brand,
|
||||
articleNumber: a.articleNumber,
|
||||
name: a.name,
|
||||
spareInfo: a.spareInfo,
|
||||
images: a.images,
|
||||
eans: a.eans,
|
||||
})),
|
||||
aftermarketParts,
|
||||
oeCrossReferences,
|
||||
truncated,
|
||||
|
||||
@@ -17,7 +17,9 @@ export interface PImage {
|
||||
thumb: string | null;
|
||||
}
|
||||
|
||||
/** One P article whose OE number list contains the queried OEM code. */
|
||||
/** One P article whose OE number list contains the queried OEM code. Internal
|
||||
* shape — `oeNumbers`/`compatible` feed the aggregates and are NOT shipped to
|
||||
* the client (they're large and unused there; see PArticleSummary). */
|
||||
export interface PArticle {
|
||||
id: string;
|
||||
brand: string;
|
||||
@@ -30,6 +32,11 @@ export interface PArticle {
|
||||
compatible: PCompatible[];
|
||||
}
|
||||
|
||||
/** What the OEM detail page actually renders per matched article — without the
|
||||
* heavy per-article OE/compat lists (those are deduped into the top-level
|
||||
* aggregates). Trimming them cut a 60-article payload from ~370 KB to ~12 KB. */
|
||||
export type PArticleSummary = Omit<PArticle, "oeNumbers" | "compatible">;
|
||||
|
||||
/** Response of the OEM detail lookup. `matched: false` covers every miss —
|
||||
* feature disabled, code too short, or no P article carries that OE
|
||||
* number — so the UI has a single empty-state path. */
|
||||
@@ -37,8 +44,9 @@ export interface POemResult {
|
||||
query: string;
|
||||
queryNorm: string;
|
||||
matched: boolean;
|
||||
/** Distinct articles whose OE list contains the queried code. */
|
||||
articles: PArticle[];
|
||||
/** Distinct articles whose OE list contains the queried code (lean — per-article
|
||||
* OE/compat lists are aggregated below, not duplicated here). */
|
||||
articles: PArticleSummary[];
|
||||
/** Deduped buyable aftermarket part numbers across all matched articles
|
||||
* (the matched articles themselves + their compatibility entries). */
|
||||
aftermarketParts: Array<{ brand: string; articleNumber: string; thumb: string | null }>;
|
||||
|
||||
@@ -396,14 +396,13 @@ export function PartsPanel({
|
||||
{part.oemCode &&
|
||||
part.oemCode !== "N/A" &&
|
||||
matchedOemCodes.has(part.oemCode) ? (
|
||||
// Link ONLY codes with a real cross-reference (new tab,
|
||||
// keeps catalog/schema context). Plain anchor, not router
|
||||
// Link — fresh load resolves the route, no router context
|
||||
// needed here. Unmatched codes fall through to plain text.
|
||||
// Link ONLY codes with a real cross-reference. Plain
|
||||
// left-click → in-app client navigation (no full SPA
|
||||
// reload — the new-tab boot was the slow part). Real href
|
||||
// kept so ctrl/cmd/middle-click still opens a new tab.
|
||||
// Unmatched codes fall through to plain text.
|
||||
<a
|
||||
href={`/dashboard/oem/${encodeURIComponent(part.oemCode)}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title="Uyumlu parça kodlarını gör"
|
||||
className="underline decoration-dotted underline-offset-2 transition-colors hover:text-foreground hover:decoration-solid"
|
||||
onClick={(e) => {
|
||||
@@ -414,6 +413,12 @@ export function PartsPanel({
|
||||
vehicle_id: vehicleId,
|
||||
category_id: categoryId,
|
||||
});
|
||||
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0) return;
|
||||
e.preventDefault();
|
||||
navigate({
|
||||
to: "/dashboard/oem/$code",
|
||||
params: { code: part.oemCode },
|
||||
});
|
||||
}}
|
||||
>
|
||||
{part.oemCode}
|
||||
|
||||
@@ -29,8 +29,6 @@ interface PArticle {
|
||||
spareInfo: string | null;
|
||||
images: Array<{ url: string; thumb: string | null }>;
|
||||
eans: string[];
|
||||
oeNumbers: OeNumber[];
|
||||
compatible: Array<{ brand: string; article: string }>;
|
||||
}
|
||||
interface POemResult {
|
||||
query: string;
|
||||
|
||||
Reference in New Issue
Block a user