CROP
ProjectsCROP Frontend

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)

#IssueSeverityFileLine
1Missing React.memo on TireCard/TiresGridCriticaltires-catalog-client.tsx123, 440
2Zero test coverageCriticalapp/tires/*-
3Missing SEO metadata (OpenGraph, Twitter)Critical[slug]/page.tsx112-136
4Images use unoptimized flagHightires-catalog-client.tsx110
5Missing ARIA labels/live regionsHightires-catalog-client.tsx454, 456
6console.log in production codeMedium[slug]/page.tsx99
7Inconsistent error handlingMedium(catalog)/page.tsx163
8Missing error retry buttonMediumreal-time-inventory.tsx154

Backend (CROP-parts-services)

#IssueSeverityFileLine
9Autocomplete ignores tire indexCriticalautocomplete.ts366, 460, 488
10Multi-index search no type validationHighparts.ts41-64
11Index resolver edge case bugsHighindex-resolver.ts37-40
12KMT slug generation no validationHightransformers.ts23-26
13ProductType not in autocomplete payloadMediumautocomplete-sections-service.ts826
14No tire-specific sort optionsLowsearch.ts200-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.ts

3. 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)

TaskFilesEffort
Add React.memo to TireCard/TiresGridtires-catalog-client.tsx30min
Fix autocomplete to include tiresautocomplete.ts2h
Add SEO metadata[slug]/page.tsx, (catalog)/page.tsx1h
Remove console.log[slug]/page.tsx5min
Validate slug generationtransformers.ts30min

Phase 2: Quality Improvements (P1)

TaskFilesEffort
Add test coverage for transformstransform.test.ts2h
Add test coverage for manufacturersmanufacturers.test.ts1h
Configure image optimizationnext.config.ts30min
Fix accessibility (ARIA)tires-catalog-client.tsx1h
Add productType to autocompleteautocomplete-sections-service.ts1h

Phase 3: Enhancements (P2)

TaskFilesEffort
Add error retry buttonreal-time-inventory.tsx30min
Add structured data (JSON-LD)[slug]/page.tsx1h
Migrate to React Queryreal-time-inventory.tsx2h
Add tire-specific sort optionssearch.ts1h
Optimize multi-index queriesparts.ts2h

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 suggestions

Summary

CategoryIssues FoundCriticalFixedTODO
Performance3103
Testing1101
SEO1101
Accessibility1001
Backend6106
Type Safety2002
Total144014

Priority Order:

  1. Fix autocomplete to include tires (users can't find tires!)
  2. Add React.memo (performance issue visible to users)
  3. Add SEO metadata (affects discoverability)
  4. Add test coverage (prevents regressions)
  5. Fix remaining issues

On this page