feat(landing): hero Ara → /demo for empty input or example VIN match
Real visitors were repeatedly clicking the hero "Ara" button while only the
placeholder VIN (WVWZZZ1JZ3W597935) was showing — they mistook the example
for typed text. The click hit handleVinSearch's `if (!trimmed) return`
early-out and did nothing, leaving them stuck.
Now: empty input OR a value that exactly matches the example VIN routes to
/demo (the pre-warmed VW Golf 2003 catalog) instead of /register. The button
label also updates to "Örnek aracı dene" in that state so the outcome is
predictable before the click. Any other typed VIN still goes through the
existing /register?vin= flow unchanged.
PostHog: emits hero_ara_demo_redirect with {had_value, was_example_match}
so we can split the funnel by what triggered the demo entry.
This commit is contained in:
@@ -31,6 +31,13 @@ import {
|
||||
} from "lucide-react";
|
||||
import { Suspense, lazy, useEffect, useRef, useState } from "react";
|
||||
|
||||
// The example VIN shown as the hero input placeholder. Clicking "Ara" with
|
||||
// the input empty (or already containing this exact VIN — common when users
|
||||
// mistake the placeholder for typed text) opens /demo with the pre-warmed
|
||||
// VW Golf 2003 catalog instead of stalling at the signup wall. Any other
|
||||
// VIN takes the normal /register?vin= path.
|
||||
const EXAMPLE_VIN = "WVWZZZ1JZ3W597935";
|
||||
|
||||
// Remotion — lazy loaded to keep initial bundle lean
|
||||
const RemotionSchemaPlayer = lazy(() =>
|
||||
Promise.all([import("@remotion/player"), import("@/remotion/SchemaDemo")]).then(
|
||||
@@ -366,8 +373,25 @@ export function HomePage() {
|
||||
}, [vin]);
|
||||
|
||||
const handleVinSearch = async () => {
|
||||
const trimmed = vin.trim();
|
||||
if (!trimmed) return;
|
||||
const trimmed = vin.trim().toUpperCase();
|
||||
|
||||
// Empty input OR the example VIN itself → open the public demo instead
|
||||
// of the signup wall. Real visitors were repeatedly clicking "Ara" with
|
||||
// the placeholder showing because they mistook it for typed text; this
|
||||
// turns that dead-end click into an "aha" catalog browse.
|
||||
if (!trimmed || trimmed === EXAMPLE_VIN) {
|
||||
capture("hero_ara_demo_redirect", {
|
||||
had_value: !!trimmed,
|
||||
was_example_match: trimmed === EXAMPLE_VIN,
|
||||
});
|
||||
navigate({
|
||||
to: "/demo",
|
||||
search: {
|
||||
source: trimmed === EXAMPLE_VIN ? "hero_example_match" : "hero_empty",
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Read latest auth state directly from store (avoids stale closure)
|
||||
const { user, isLoading } = useAuthStore.getState();
|
||||
@@ -430,7 +454,7 @@ export function HomePage() {
|
||||
};
|
||||
|
||||
const fillExampleVin = () => {
|
||||
setVin("WVWZZZ1JZ3W597935");
|
||||
setVin(EXAMPLE_VIN);
|
||||
setIsExampleVin(true);
|
||||
};
|
||||
|
||||
@@ -933,7 +957,13 @@ export function HomePage() {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{t("landing.hero.searchCta")}
|
||||
{/* When the input is empty or already holds the example
|
||||
VIN, the button takes the visitor to /demo instead of
|
||||
the signup wall — surface that intent in the label so
|
||||
the click outcome is predictable. */}
|
||||
{vin.trim() === "" || vin.trim().toUpperCase() === EXAMPLE_VIN
|
||||
? "Örnek aracı dene"
|
||||
: t("landing.hero.searchCta")}
|
||||
<ArrowRight className="ml-2 size-4" />
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user