Tire Implementation: Critical Review & Improvement Plan
Overall Assessment: The tire implementation is functionally complete but has significant gaps in: - Performance optimization (no React.memo, unoptimized...
Tire Implementation: Critical Review & Improvement Plan
Executive Summary
Overall Assessment: The tire implementation is functionally complete but has significant gaps in:
- Performance optimization (no React.memo, unoptimized images)
- Test coverage (0% in frontend)
- SEO metadata (missing OpenGraph, structured data)
- Accessibility (missing ARIA labels)
- Backend autocomplete (ignores tire index completely)
Critical Issues Found
Frontend (CROP-front)
| # | Issue | Severity | File | Line |
|---|---|---|---|---|
| 1 | Missing React.memo on TireCard/TiresGrid | Critical | tires-catalog-client.tsx | 123, 440 |
| 2 | Zero test coverage | Critical | app/tires/* | - |
| 3 | Missing SEO metadata (OpenGraph, Twitter) | Critical | [slug]/page.tsx | 112-136 |
| 4 | Images use unoptimized flag | High | tires-catalog-client.tsx | 110 |
| 5 | Missing ARIA labels/live regions | High | tires-catalog-client.tsx | 454, 456 |
| 6 | console.log in production code | Medium | [slug]/page.tsx | 99 |
| 7 | Inconsistent error handling | Medium | (catalog)/page.tsx | 163 |
| 8 | Missing error retry button | Medium | real-time-inventory.tsx | 154 |
Backend (CROP-parts-services)
| # | Issue | Severity | File | Line |
|---|---|---|---|---|
| 9 | Autocomplete ignores tire index | Critical | autocomplete.ts | 366, 460, 488 |
| 10 | Multi-index search no type validation | High | parts.ts | 41-64 |
| 11 | Index resolver edge case bugs | High | index-resolver.ts | 37-40 |
| 12 | KMT slug generation no validation | High | transformers.ts | 23-26 |
| 13 | ProductType not in autocomplete payload | Medium | autocomplete-sections-service.ts | 826 |
| 14 | No tire-specific sort options | Low | search.ts | 200-208 |
Detailed Analysis
1. Performance: Missing React.memo
Problem: TireCard and TiresGrid re-render on every parent state change (filters, pagination, imageVariant).
Impact: With 24+ tires per page, every filter change causes 24+ unnecessary re-renders.
Current Code (tires-catalog-client.tsx:123):
function TireCard({ tire, imageVariant }: { tire: Tire; imageVariant?: TireImageVariant }) {
// Complex rendering - re-renders on ANY parent state change
}Fix:
const TireCard = memo(function TireCard({ tire, imageVariant }: Props) {
// Same code, but only re-renders when props change
});2. Test Coverage: Zero Tests
Problem: No test files exist for:
- Slug transformation (
lib/constants/manufacturers.ts) - Data transformation (
app/tires/_lib/transform.ts) - Real-time inventory (
real-time-inventory.tsx) - API routes (
app/api/tires/inventory/route.ts)
Required Tests:
app/tires/_lib/transform.test.ts
app/tires/_components/real-time-inventory.test.tsx
lib/constants/manufacturers.test.ts
app/api/tires/__tests__/inventory.test.ts3. SEO: Missing Metadata
Problem: Tire pages lack OpenGraph, Twitter cards, and structured data.
Current ([slug]/page.tsx:112):
return {
title: `${tire.title} · ${brand} · CROP Prime Grid`,
description: tire.description,
// Missing: openGraph, twitter, structured data
};Required:
return {
title: `${tire.title} · ${brand} · CROP Prime Grid`,
description: tire.description,
openGraph: {
title: tire.title,
description: tire.description,
images: imageUrl ? [{ url: imageUrl, alt: tire.title }] : [],
type: 'product',
},
twitter: {
card: 'summary_large_image',
title: tire.title,
images: imageUrl ? [imageUrl] : [],
},
};4. Autocomplete Ignores Tires (CRITICAL)
Problem: All autocomplete queries use env.SEARCH_INDEX_NAME (parts only).
Current (autocomplete.ts:366):
const sectionsResult = await fetchAutocompleteSections(es, {
indexName: env.SEARCH_INDEX_NAME, // Only parts!
});Impact: Users cannot find tires via autocomplete at all.
Fix: Use getAllSearchIndices() or add tire-specific detection:
const targetIndex = shouldSearchTires(query, p.manufacturer)
? env.TIRES_INDEX_NAME
: env.SEARCH_INDEX_NAME;5. Images: Unoptimized
Problem: All tire images use unoptimized flag, bypassing Next.js optimization.
Current (tires-catalog-client.tsx:110):
<Image
src={imageUrl}
unoptimized // Disables WebP/AVIF, responsive srcsets
/>Fix: Configure remote patterns in next.config.ts:
images: {
remotePatterns: [
{ protocol: 'https', hostname: 'nyc3.digitaloceanspaces.com' }, // K&M CDN
],
}Then remove unoptimized prop.
6. Slug Generation Edge Cases
Problem: generateKmtSlug doesn't validate empty results.
Current (transformers.ts:23):
function generateKmtSlug(partNumber: string): string {
const compactPn = partNumber.toLowerCase().replace(/[^a-z0-9]/g, '');
return `ct-kmt-${compactPn}`; // Could be 'ct-kmt-' if empty
}Fix:
function generateKmtSlug(partNumber: string): string {
const compactPn = partNumber.toLowerCase().replace(/[^a-z0-9]/g, '');
if (!compactPn) {
throw new Error(`Invalid part number for slug: "${partNumber}"`);
}
return `ct-kmt-${compactPn.slice(0, 50)}`; // Limit length
}Improvement Plan
Phase 1: Critical Fixes (P0)
| Task | Files | Effort |
|---|---|---|
| Add React.memo to TireCard/TiresGrid | tires-catalog-client.tsx | 30min |
| Fix autocomplete to include tires | autocomplete.ts | 2h |
| Add SEO metadata | [slug]/page.tsx, (catalog)/page.tsx | 1h |
| Remove console.log | [slug]/page.tsx | 5min |
| Validate slug generation | transformers.ts | 30min |
Phase 2: Quality Improvements (P1)
| Task | Files | Effort |
|---|---|---|
| Add test coverage for transforms | transform.test.ts | 2h |
| Add test coverage for manufacturers | manufacturers.test.ts | 1h |
| Configure image optimization | next.config.ts | 30min |
| Fix accessibility (ARIA) | tires-catalog-client.tsx | 1h |
| Add productType to autocomplete | autocomplete-sections-service.ts | 1h |
Phase 3: Enhancements (P2)
| Task | Files | Effort |
|---|---|---|
| Add error retry button | real-time-inventory.tsx | 30min |
| Add structured data (JSON-LD) | [slug]/page.tsx | 1h |
| Migrate to React Query | real-time-inventory.tsx | 2h |
| Add tire-specific sort options | search.ts | 1h |
| Optimize multi-index queries | parts.ts | 2h |
Code Examples for Immediate Fixes
Fix 1: React.memo for TireCard
// tires-catalog-client.tsx
import { memo } from "react";
const TireCard = memo(function TireCard({
tire,
imageVariant = "brand-logo"
}: {
tire: Tire;
imageVariant?: TireImageVariant
}) {
// existing implementation
});
const TiresGrid = memo(function TiresGrid({
tires,
imageVariant
}: {
tires: Tire[];
imageVariant: TireImageVariant
}) {
return (
<div className="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{tires.map((tire) => (
<TireCard key={tire.id} tire={tire} imageVariant={imageVariant} />
))}
</div>
);
});Fix 2: Remove console.log
// [slug]/page.tsx - Remove line 99
// console.log(`[TireDetail] Trying fallback slug: ${oldSlug}`);
// Replace with nothing (or use proper logging if needed)Fix 3: SEO Metadata
// [slug]/page.tsx
export async function generateMetadata({ params }: TireDetailPageProps): Promise<Metadata> {
const { slug } = await params;
if (isOldTireSlugFormat(slug)) {
return { title: "Redirecting..." };
}
const tire = await fetchTireBySlug(slug);
if (!tire) {
return {
title: "Tire not found",
description: "The requested tire could not be found.",
};
}
const brand = tire.specifications?.Brand ?? "Unknown";
const imageUrl = tire.media?.images?.[0]?.url;
const price = tire.pricing?.list?.value;
return {
title: `${tire.title} · ${brand} · CROP Prime Grid`,
description: tire.description ?? `${tire.title} by ${brand}. View pricing and availability.`,
openGraph: {
title: tire.title,
description: tire.description,
type: "website",
images: imageUrl ? [{ url: imageUrl, alt: tire.title, width: 300, height: 300 }] : [],
},
twitter: {
card: "summary_large_image",
title: tire.title,
description: tire.description,
images: imageUrl ? [imageUrl] : [],
},
};
}Verification Checklist
After implementing fixes:
# 1. Run type check
bun run type-check
# 2. Run lint
bun run lint
# 3. Run tests (after adding them)
bun test
# 4. Check performance (React DevTools Profiler)
# - TireCard should NOT re-render when filters change
# - Only re-render when tire data changes
# 5. Check SEO (view-source or Lighthouse)
# - OpenGraph tags present
# - Twitter cards present
# 6. Check accessibility (axe DevTools)
# - No ARIA violations
# - All interactive elements accessible
# 7. Check autocomplete
curl 'https://api.crop-dev.app/api/autocomplete?q=tire' | jq '.sections'
# Should return tire suggestionsSummary
| Category | Issues Found | Critical | Fixed | TODO |
|---|---|---|---|---|
| Performance | 3 | 1 | 0 | 3 |
| Testing | 1 | 1 | 0 | 1 |
| SEO | 1 | 1 | 0 | 1 |
| Accessibility | 1 | 0 | 0 | 1 |
| Backend | 6 | 1 | 0 | 6 |
| Type Safety | 2 | 0 | 0 | 2 |
| Total | 14 | 4 | 0 | 14 |
Priority Order:
- Fix autocomplete to include tires (users can't find tires!)
- Add React.memo (performance issue visible to users)
- Add SEO metadata (affects discoverability)
- Add test coverage (prevents regressions)
- Fix remaining issues