CROP
ProjectsCROP Frontend

Tire Implementation Audit & Improvement Plan

This audit reviews the K&M Tire integration across the CROP ecosystem. The implementation is functional but has several issues that need addressing for...

Tire Implementation Audit & Improvement Plan

Executive Summary

This audit reviews the K&M Tire integration across the CROP ecosystem. The implementation is functional but has several issues that need addressing for production readiness.


Critical Issues

Issue #1: Old Slug URLs Return 404 (CRITICAL)

Problem: URLs with old slug format (/tires/kmt-04493560000) return 404. The slug format was changed from kmt-{partNumber} to ct-kmt-{partNumber} but:

  1. Old bookmarked URLs break
  2. Search engines have indexed old URLs
  3. No redirect mechanism exists

Solution: Add redirect logic in tire detail page to handle old format.

// In app/tires/[slug]/page.tsx
if (slug.startsWith('kmt-') && !slug.startsWith('ct-kmt-')) {
  const newSlug = `ct-${slug}`;
  redirect(`/tires/${newSlug}`);
}

Status: To be implemented


Issue #2: Elasticsearch Index Not Rebuilt

Problem: MongoDB has updated slugs (ct-kmt-*) but Elasticsearch tires_current index may still have old slugs or no data on dev environment.

Solution:

  1. Deploy new search service version
  2. Run GCP rebuild script: bash services/search/scripts/gcp-rebuild-tires-index.sh

Status: Pending deployment


Code Quality Issues

Issue #3: Real-Time Inventory Uses Raw fetch Instead of React Query

Current Implementation (real-time-inventory.tsx):

const [data, setData] = useState<RealTimeInventoryData | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);

const fetchInventory = useCallback(async () => {
  setIsLoading(true);
  // manual fetch...
}, [partNumber]);

Best Practice (React Query):

const { data, isLoading, error, refetch } = useQuery({
  queryKey: ['tire-inventory', partNumber],
  queryFn: () => fetchTireInventory(partNumber),
  staleTime: 30 * 1000, // 30 seconds
  retry: 2,
});

Benefits:

  • Automatic caching and deduplication
  • Built-in loading/error states
  • Automatic background refetching
  • Better DevTools integration

Status: Recommended improvement


Issue #4: Hardcoded Manufacturer Code

Location: app/tires/(catalog)/page.tsx:53

manufacturer: "KMT",

Risk: If manufacturer code changes, catalog breaks.

Solution: Extract to constant or config.

// lib/constants/manufacturers.ts
export const TIRE_MANUFACTURER = {
  code: 'KMT',
  name: 'K&M Tire',
} as const;

Status: Recommended improvement


Current: All tire detail pages are dynamically rendered.

Best Practice (Next.js): Pre-generate popular tire pages for better performance.

export async function generateStaticParams() {
  // Fetch top 100 popular tires
  const tires = await getPopularTires(100);
  return tires.map((tire) => ({ slug: tire.slug }));
}

export const dynamicParams = true; // Allow dynamic for non-pregenerated

Status: Optional improvement


Issue #6: Missing Error Boundary with Retry

Current: Error shows generic message without retry option.

Best Practice: Add retry button in error state.

Status: Recommended improvement


Backend Issues (CROP-parts-services)

Issue #7: Search Zero-Results Fallback Ignores Target Index

Location: services/search/src/routes/search.ts:569, 591

// Uses env.SEARCH_INDEX_NAME instead of targetIndex
const spellingSuggestion = await getSpellingSuggestions(client, {
  indexName: env.SEARCH_INDEX_NAME, // Should be targetIndex
});

Impact: Tire searches with zero results get suggestions from parts index.

Status: Identified, needs fix


Issue #8: Equipment Route Hardcoded to Parts Index

Location: services/search/src/routes/equipment.ts:174, 376, 652

Impact: Equipment searches cannot find tires if they have fitment data.

Status: Identified, needs fix


Issue #9: Unused isTiresRequest Function

Location: services/search/src/utils/index-resolver.ts

export function isTiresRequest(params: IndexResolverParams): boolean {
  return resolveSearchIndex(params) === env.TIRES_INDEX_NAME;
}

Status: Remove or mark as @internal


Best Practices Comparison

Next.js Data Fetching

PatternCurrentBest Practice
Server ComponentsYesYes
Revalidate60sOK
Error HandlingBasicShould add retry
generateStaticParamsNoShould add for top tires
Redirect for old URLsNoMUST add

React Query (TanStack Query v5)

PatternCurrentBest Practice
Data fetchingRaw useStateuseQuery
CachingManualAutomatic staleTime
Error handlingManual try/catchBuilt-in error state
RefetchingManual buttonAutomatic + manual
Loading stateManual useStateBuilt-in isLoading

Caching Strategy

EndpointCurrent CacheRecommended
Tire catalog60s60s (OK)
Tire detail60s60s (OK)
Real-time inventory0s (no-store)30s staleTime in React Query
K&M API (backend)5min5min (OK)

Implementation Priority

P0 - Critical (Must Fix Now)

  1. Add redirect for old slug format (kmt-*ct-kmt-*) - DONE
  2. Add fallback for old slugs during ES migration - DONE
  3. Deploy search service (has multi-index fix but not deployed)
  4. Rebuild tires ES index (has old slugs)

Deployment Commands

# Step 1: Deploy search service
cd /Users/vova/Code/CROP/CROP-parts-services
gcloud run deploy search-service --source . --region us-east1 --project noted-bliss-466410-q6

# Step 2: Rebuild tires index
bash services/search/scripts/gcp-rebuild-tires-index.sh

# Step 3: Verify
curl 'https://api.crop-dev.app/api/parts/ct-kmt-04493560000' | jq '.part.slug'

P1 - High (Should Fix Soon)

  1. Fix search zero-results fallback to use targetIndex - ALREADY DONE
  2. Extract manufacturer code to constant - DONE
  3. Add error retry button to tire pages

P2 - Medium (Nice to Have)

  1. Migrate real-time inventory to React Query
  2. Add generateStaticParams for popular tires
  3. Fix equipment route multi-index support - ALREADY DONE

P3 - Low (Future)

  1. Remove unused isTiresRequest function
  2. Add admin route index parameter

Files to Modify

Frontend (CROP-front)

FileChangePriority
app/tires/[slug]/page.tsxAdd redirect for old slugsP0
lib/constants/manufacturers.tsCreate fileP1
app/tires/(catalog)/page.tsxUse constantP1
app/tires/_components/real-time-inventory.tsxMigrate to useQueryP2

Backend (CROP-parts-services)

FileChangePriority
services/search/src/routes/search.tsUse targetIndexP1
services/search/src/routes/equipment.tsUse getAllSearchIndicesP2
services/search/src/utils/index-resolver.tsRemove isTiresRequestP3

Verification Checklist

After implementing fixes:

# 1. Test old slug redirect
curl -I http://localhost:3000/tires/kmt-04493560000
# Expected: 308 redirect to /tires/ct-kmt-04493560000

# 2. Test new slug works
curl http://localhost:3000/tires/ct-kmt-04493560000
# Expected: 200 OK with tire page

# 3. Test catalog page
curl http://localhost:3000/tires/
# Expected: 200 OK with tire list

# 4. Test real-time inventory
# Open tire detail page, check "Live" badge appears

Summary

CategoryStatus
Slug format changeDone (code)
MongoDB migrationDone (7091 docs)
ES index rebuildPending deployment
Old URL redirectTo implement
React Query migrationRecommended
Backend index fixesIdentified

On this page