Public-facing vehicle detail page for “Vehicles Stripping For Spares” listings. Shows vehicle information, images, supplier details, and enquiry form. Handles contact tracking, view analytics, and stripping alert subscriptions.
Route: /vehicles/[id].astro
URL Parameter: Accepts both slug or vehicle ID
Cache Control: No-store (dynamic content, prevents stale data)
Listing Expiration: 4 months from date_listed
slug (URL-friendly)id (UUID)active statustouchRatio, longSwipes, followFinger, grabCursor?t=${Date.now()})/api/send-vehicle-enquiry with supplier email + BCC to info@enginefinder.co.zaTwo separate tracking systems:
/api/track-vehicle-view (vehicle_views table)/api/supplier/track-view (supplier_analytics table)Both track only once per session per vehicle to avoid inflation.
marketing_emails tablenormalizeMake() function/{normalized-make}-stripping-for-spares/Home > Vehicles Stripping For Spares > {Make} Spares > {Year} {Make} {Model}
normalizeMake()| Component | Type | Location | Purpose |
|---|---|---|---|
| VehicleEnquiryForm | React (.tsx) | src/components/VehicleEnquiryForm.tsx | Submit enquiry to supplier |
| VehicleSubscriptionWidget | React (.tsx) | src/components/VehicleSubscriptionWidget.tsx | Email alert subscription |
| VehicleViewTracker | Astro (.astro) | src/components/VehicleViewTracker.astro | Track page views with UTM params |
| Breadcrumb | Astro (.astro) | src/components/Breadcrumb.astro | Navigation breadcrumb |
| ReportListingButton | React (.tsx) | src/components/ReportListingButton.tsx | Opens report modal |
| ReportListingModal | React (.tsx) | src/components/ReportListingModal.tsx | Report submission form |
vehiclesVehicle stripping listings.
| Column | Type | Description |
|---|---|---|
id | uuid | Primary key |
slug | text | URL-friendly identifier (unique) |
supplier_id | uuid | FK to suppliers |
year | integer | Model year |
make | text | Vehicle make |
model | text | Vehicle model |
engine_code | text | Engine identifier |
engine_size | text | Displacement (e.g., “2.0L”) |
transmission | text | Manual/Automatic/CVT |
mileage | integer | Odometer reading |
color | text | Vehicle color |
status | text | available/sold/expired |
featured_image | text | Main image URL (Cloudinary) |
gallery_images | text[] | Array of image URLs |
reference_number | text | Supplier’s stock code |
description | text | Long-form vehicle notes |
date_listed | timestamp | When listing created |
vehicle_viewsView analytics per vehicle (one record per view).
| Column | Type | Description |
|---|---|---|
id | uuid | Primary key |
vehicle_id | uuid | FK to vehicles |
supplier_id | uuid | FK to suppliers |
source | text | direct/search/social/newsletter/supplier_page/other |
ip_hash | text | SHA256 hash of visitor IP |
user_agent | text | Browser/device info |
created_at | timestamp | View timestamp |
Tracking Behavior:
tracked_vehicle_view_{vehicleId})supplier_analyticsDaily aggregated metrics per supplier.
| Column | Type | Description |
|---|---|---|
id | uuid | Primary key |
supplier_id | uuid | FK to suppliers |
date | date | Analytics date (daily rollup) |
profile_views | integer | Supplier profile page views |
listing_views | integer | Vehicle detail page views (this page) |
inquiries | integer | Enquiry form submissions |
phone_clicks | integer | Phone button clicks |
whatsapp_clicks | integer | WhatsApp link clicks |
Note: Records created/updated once per view type per day per supplier
suppliersSupplier master data.
| Column | Type | Description |
|---|---|---|
id | uuid | Primary key |
name | text | Business name |
status | text | active/suspended/inactive |
makes | text[] | Array of vehicle makes |
featured_image | text | Supplier logo/image |
phone | text | Primary contact number |
whats_app | text | WhatsApp link (https://wa.me/…) |
address | text | Physical location |
emails | text[] | Array of supplier emails |
coordinates | jsonb | {lat, lng} for map |
website | text | Website URL |
description | text | Business description |
marketing_emailsEmail subscription records (for stripping alerts).
| Column | Type | Description |
|---|---|---|
id | uuid | Primary key |
email | text | Subscriber email (unique with make) |
vehicle_make | text | Subscribed make (e.g., BMW) |
vehicle_models | text[] | Subscribed models |
is_active | boolean | Subscription status |
consent_source | text | widget/manual/import |
consent_ip | inet | IP address when subscribing |
segment_tags | text[] | For email segmentation |
consent_timestamp | timestamp | When consent given |
created_at | timestamp | Record creation |
updated_at | timestamp | Last update |
/api/send-vehicle-enquiry (POST)Purpose: Send vehicle enquiry to supplier
Request:
{
"to": "supplier@email.com",
"bcc": "info@enginefinder.co.za",
"vehicleDetails": {
"make": "BMW",
"model": "E46",
"year": "2005",
"engineCode": "M54B30",
"reference": "STOCK-001"
},
"customerDetails": {
"name": "John Doe",
"phone": "0712345678",
"email": "john@example.com",
"city": "Cape Town",
"message": "Looking for this specific model"
}
}
Response:
{
"success": true,
"data": { "id": "resend-message-id" }
}
Email Template:
Engine Finder <info@enginefinder.co.za>Vehicle Enquiry: 2005 BMW E46Error Handling:
/api/track-vehicle-view (POST)Purpose: Record individual vehicle view
Request:
{
"vehicle_id": "uuid",
"source": "direct|search|social|newsletter|supplier_page|other"
}
Response:
{ "success": true }
Behavior:
vehicle_views table/api/supplier/track-view (POST)Purpose: Record supplier-level engagement (listing view, phone click, etc.)
Request:
{
"supplier_id": "uuid",
"view_type": "listing|phone_click|whatsapp_click"
}
Response:
{ "success": true }
Behavior:
supplier_analytics/api/reports/listing (POST)Purpose: Submit a report about a vehicle listing
Request:
{
"vehicleId": "uuid",
"vehicleSlug": "slug-string",
"reason": "Vehicle no longer available|Incorrect information|Suspicious listing|Duplicate listing|Other",
"details": "Optional additional details",
"reporterEmail": "optional@email.com"
}
Response (Success):
{
"success": true,
"message": "Report submitted successfully",
"data": { "id": "resend-email-id" }
}
Response (Rate Limited):
{
"success": false,
"message": "Rate limit exceeded. Maximum 3 reports per hour per IP address."
}
Behavior:
Error Handling:
/vehicles/2005-bmw-e46-m54b30-stripping-for-spares
{year}-{make}-{model}-{engineCode}-stripping-for-spares/vehicles/550e8400-e29b-41d4-a716-446655440000
1. User visits /vehicles/[slug-or-id]
↓
2. Page looks up by slug, falls back to ID
↓
3. Validates supplier is active, listing not expired
↓
4. Fetches related supplier data
↓
5. Renders vehicle details + gallery + forms
↓
6. JavaScript runs:
- VehicleViewTracker sends view to /api/track-vehicle-view
- Supplier tracking sends to /api/supplier/track-view
- VehicleEnquiryForm ready for submission
- VehicleSubscriptionWidget ready for subscription
The page includes custom formatting for vehicle specifications:
// Camel case splitting
E82 → E82 (letter+number kept together)
BMW → BMW (kept together)
EngineCode → Engine Code (space inserted)
// Known patterns handled specially
"E82" → "E82" (not split)
"not provided" → "" (filtered out)
"not available" → "" (filtered out)
Each page generates contextual breadcrumbs:
{Year} {Make} {Model} {EngineCode} Stripping For Spareshttps://www.enginefinder.co.za/vehicles/{slug}HTTP Headers:
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Expires: 0
Surrogate-Control: no-store
Image Optimization:
?t={Date.now()})Client-side Tracking:
supplier_analytics.whatsapp_clicksUpdated WhatsApp Message Format:
Hi, I found this {year} {make} {model} on Engine Finder (enginefinder.co.za). I'm looking for a {part}. Is it still available?
/suppliers/ (was incorrectly /scrap-yards/)| Component | Location | Purpose |
|---|---|---|
PartsAvailability.astro | src/components/PartsAvailability.astro | Static parts checklist with WhatsApp links |
SimilarVehicles.astro | src/components/SimilarVehicles.astro | Related vehicles grid/scroll |
whatsappNormalizer.ts | src/lib/normalizers/whatsappNormalizer.ts | Normalize SA phone numbers for wa.me links |
/suppliers/{supplier-slug}/ (not /scrap-yards/)Listing Expiration:
# Create vehicle with date_listed > 4 months ago
# Navigate to /vehicles/{id}
# Should redirect to home
View Tracking:
# Visit /vehicles/{id}
# Check vehicle_views table - should have 1 record
# Visit again same session - no new records added
# New session - creates new record
Enquiry Form:
# Fill enquiry form with valid data
# Check supplier inbox for email
# BCC should arrive at info@enginefinder.co.za
# Resend dashboard should show delivered event
Subscription Widget:
# Select make, model(s), enter email
# Check marketing_emails table for new subscription
# Verify is_active=true, consent_source='widget'
Page Load:
Image Gallery (Swiper.js):
| Issue | Cause | Solution |
|---|---|---|
| Page redirects to home | Supplier inactive or listing expired | Check supplier.status and date_listed |
| Images not loading | Cloudinary URL invalid or domain blocked | Verify image URLs in database, check Cloudinary account |
| Enquiry not received | Email domain not verified | Verify info@enginefinder.co.za in Resend dashboard |
| View not tracked | Session already tracked or API failed | Check sessionStorage, browser console for errors |
| Subscription fails | Invalid email or privacy consent unchecked | Check validation errors in widget UI |
| Make link broken | normalizeMake() output incorrect | Verify make name in database matches normalization rules |
src/pages/api/supplier/CLAUDE.md - Vehicle stats, track-view endpointssrc/pages/api/leads/CLAUDE.md - Vehicle enquiry handlingsrc/pages/api/webhooks/CLAUDE.md - Web scraper importssrc/pages/api/subscription/CLAUDE.md - Stripping alerts for vehiclessupabase/CLAUDE.md - RLS policies for vehicles table| Package | Purpose |
|---|---|
swiper | Touch-enabled image slider (mobile-first) |
src/pages/vehicles/[id].astrosrc/components/VehicleEnquiryForm.tsxsrc/components/PartsAvailability.astrosrc/components/SimilarVehicles.astrosrc/components/VehicleViewTracker.astrosrc/components/Breadcrumb.astrosrc/lib/normalizers/whatsappNormalizer.tspublic/images/parts/*.webp (AI-generated)src/pages/api/send-vehicle-enquiry.tssrc/pages/api/track-vehicle-view.tssrc/pages/api/supplier/track-view.tsBefore modifying any files in this feature:
subagent_type: "general-purpose"