Used Cars 2025 Handoff

Handoff Doc:

IDE Agent Handoff Document

Project: Automotive Research Hub

Status: Development Complete → Production Prep Required
Framework: React + TypeScript → Cloudflare Pages


Outstanding Implementation Tasks

Priority 1: Pre-Production Fixes

Task 1.1: Reactive Scroll Indicator

typescript

// Current: Static SVG animation
// Required: React to scroll position, hide after scroll threshold

const ScrollIndicator = () => {
  const [isVisible, setIsVisible] = useState(true);
  
  useEffect(() => {
    const handleScroll = () => {
      setIsVisible(window.scrollY < 100);
    };
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);
  
  // Add opacity transition based on scroll velocity
};

Task 1.2: Search Bar Reveal

typescript

// Required: CTA button transforms to search input
// Implementation Pattern:
// - Click "Explore Sources" → morphs into search bar
// - Search should filter sources array by title + publisher
// - Hybrid search: exact match priority, then fuzzy match

interface SearchState {
  isActive: boolean;
  query: string;
  results: Source[];
}

// Consider: Fuse.js for fuzzy search (2.4kb gzipped)

Framer Motion Migration Guide

Current State: CSS Transitions

css

/* Existing */
transition: all 0.3s ease-in-out;
transform: translateY(-8px);

Target State: Framer Motion

typescript

import { motion, useInView, AnimatePresence } from 'framer-motion';

// Card Animation
<motion.div
  initial={{ opacity: 0, y: 30 }}
  animate={{ opacity: 1, y: 0 }}
  transition={{ 
    duration: 0.5, 
    delay: index * 0.05,
    ease: [0.22, 1, 0.36, 1] // Custom easing
  }}
  whileHover={{ 
    y: -8, 
    boxShadow: '0 20px 25px -5px rgba(139, 92, 246, 0.2)' 
  }}
>

Migration Steps:

  1. Install: npm install framer-motion

  2. Replace <div> with <motion.div> for animated elements

  3. Replace useState visibility with useInView hook

  4. Replace CSS transitions with whileHover, whileTap props

  5. Use AnimatePresence for accordion content expand/collapse

Benefits:

  • Spring physics for natural motion

  • Layout animations (accordion height)

  • Gesture detection (swipe on mobile)

  • Better performance (GPU acceleration)


Code Weak Spots & Potential Issues

1. State Management Constraint

Issue: Single expandedCard state at App level
Limitation: Can't expand multiple cards simultaneously
Fix Priority: Low (matches design intent)
Future Enhancement: Add multi-select mode for comparison

2. Scroll Observer Memory Leak Risk

typescript

// Current: Observer disconnects after first intersection
// Potential Issue: Rapid unmounting could leak observers

// Hardening Required:
useEffect(() => {
  const observer = new IntersectionObserver(/*...*/);
  const currentRef = cardRef.current;
  
  if (currentRef) observer.observe(currentRef);
  
  return () => {
    if (currentRef) observer.unobserve(currentRef);
    observer.disconnect(); // Ensure cleanup
  };
}, []);
```

### 3. Data Structure Scalability
**Current:** 95-item array in component file  
**Constraint:** No server-side rendering, all data client-side  
**Recommendation:** Move to JSON file or CMS for >200 sources  
**Production Path:**
```
/src
  /data
    sources.json       ← Extract here
    sources.schema.ts  ← Type definitions

4. Search Performance

Concern: Filtering 95 items on every keystroke
Solution: Debounce search input (300ms)

typescript

const debouncedSearch = useMemo(
  () => debounce((query) => filterSources(query), 300),
  []
);

5. Mobile Accordion Height Jump

Issue: max-height transitions can feel janky
Framer Motion Fix:

typescript

<motion.div
  animate={{ height: isExpanded ? 'auto' : 0 }}
  transition={{ type: 'spring', stiffness: 300, damping: 30 }}
>

6. No Error Boundaries

Missing: Graceful failure handling
Add:

typescript

class ErrorBoundary extends React.Component {
  // Wrap App component
  // Log errors to monitoring service
}
```

---

## Cloudflare Pages Deployment Configuration

### Required Files & Structure
```
project-root/
├── public/
│   ├── _headers              ← Security headers
│   ├── _redirects            ← URL redirects
│   └── robots.txt
│
├── src/
│   ├── components/
│   │   ├── Hero.tsx
│   │   ├── SourceCard.tsx
│   │   ├── LeverageFooter.tsx
│   │   └── SearchBar.tsx
│   ├── data/
│   │   └── sources.json
│   ├── App.tsx
│   └── main.tsx
│
├── .node-version             ← Node 18.x
├── wrangler.toml             ← Cloudflare config
├── package.json
├── vite.config.ts
├── tsconfig.json
├── tailwind.config.js
└── README.md

Configuration Files

1. wrangler.toml

toml

name = "automotive-research-hub"
compatibility_date = "2025-01-01"

[site]
bucket = "./dist"

[[pages_build_output_dir]]
value = "dist"

[build]
command = "npm run build"

[build.environment]
NODE_VERSION = "18"
```

**2. _headers**
```
/*
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()
  Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';

3. package.json (build scripts)

json

{
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview",
    "deploy": "npm run build && wrangler pages deploy dist"
  }
}

4. vite.config.ts

typescript

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  build: {
    outDir: 'dist',
    sourcemap: false,
    minify: 'terser',
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          icons: ['lucide-react']
        }
      }
    }
  }
});
```

---

## Blog Integration: Yoopta-Editor

### Extended File Tree
```
src/
├── components/
│   ├── blog/
│   │   ├── BlogEditor.tsx         ← Yoopta wrapper
│   │   ├── BlogPostCard.tsx       ← List view
│   │   ├── BlogPostView.tsx       ← Single post
│   │   └── AdminPanel.tsx         ← Auth gated
│   │
│   ├── seo/
│   │   ├── MetaTags.tsx           ← Dynamic meta tags
│   │   ├── StructuredData.tsx     ← JSON-LD schemas
│   │   └── OpenGraph.tsx          ← OG tags
│   │
├── data/
│   └── blog-posts.json            ← D1 database alternative
│
├── lib/
│   ├── yoopta-config.ts           ← Editor configuration
│   └── seo-utils.ts               ← Meta generation
│
└── pages/
    ├── BlogIndex.tsx              ← /blog
    ├── BlogPost.tsx               ← /blog/:slug
    └── AdminDashboard.tsx         ← /admin (auth required)

Yoopta-Editor Setup

Installation:

bash

npm install @yoopta/editor @yoopta/exports

Basic Configuration:

typescript

// lib/yoopta-config.ts
import { createYooptaEditor } from '@yoopta/editor';

export const editorConfig = {
  plugins: [
    // Rich text
    { type: 'paragraph' },
    { type: 'heading', levels: [1, 2, 3] },
    { type: 'blockquote' },
    
    // Media
    { type: 'image', upload: cloudflareR2Upload },
    { type: 'video', embed: true },
    
    // SEO
    { type: 'metadata', fields: ['title', 'description', 'keywords'] }
  ],
  toolbar: {
    position: 'sticky',
    formatting: ['bold', 'italic', 'link', 'code']
  }
};

SEO Integration:

typescript

// components/seo/MetaTags.tsx
interface BlogPostSEO {
  title: string;
  description: string;
  keywords: string[];
  author: string;
  publishDate: string;
  slug: string;
}

export const MetaTags = ({ post }: { post: BlogPostSEO }) => (
  <Helmet>
    <title>{post.title} | Automotive Research Hub</title>
    <meta name="description" content={post.description} />
    <meta name="keywords" content={post.keywords.join(', ')} />
    <meta name="author" content={post.author} />
    
    {/* Open Graph */}
    <meta property="og:type" content="article" />
    <meta property="og:title" content={post.title} />
    <meta property="article:published_time" content={post.publishDate} />
    
    {/* Schema.org */}
    <script type="application/ld+json">
      {JSON.stringify({
        "@context": "https://schema.org",
        "@type": "BlogPosting",
        "headline": post.title,
        "datePublished": post.publishDate,
        "author": { "@type": "Organization", "name": "LEVERAGEAI LLC" }
      })}
    </script>
  </Helmet>
);

Admin Authentication Strategy

Options:

  1. Cloudflare Access (Recommended)

    • Zero trust security

    • No custom auth code

    • /admin/* protected at edge

  2. Custom Auth via D1

    • User table in D1 database

    • JWT tokens in cookies

    • Session management

Deployment Note: Yoopta content stored in Cloudflare D1 or R2 for persistence.


Pre-Deployment Checklist

  • Framer Motion migration complete

  • Search functionality implemented

  • Scroll indicator reactivity fixed

  • Error boundaries added

  • Blog editor integrated

  • SEO meta tags configured

  • Admin authentication enabled

  • Performance audit (<3s FCP)

  • Accessibility audit (WCAG 2.1 AA)

  • Mobile testing (iOS/Android)

  • Cloudflare D1 database schema created

  • Environment variables configured

  • Analytics integrated (Cloudflare Web Analytics)


Deployment Commands

bash

# Local Development
npm install
npm run dev

# Build & Test
npm run build
npm run preview

# Deploy to Cloudflare Pages
wrangler pages deploy dist --project-name=automotive-research-hub

# Environment Variables (via Dashboard or CLI)
wrangler pages secret put ADMIN_PASSWORD
wrangler pages secret put BLOG_API_KEY

Handoff Complete. All architectural decisions logged. Constraints identified. Migration path clear.