feat(demo): VIN-input CTA card replaces plain "Hesap Aç" footer
Demo footer was a single-button conversion ("Hesap Aç" → /register with no
VIN). For a parts-shop owner who just browsed the example VW Golf, the
next natural action is "let me try MY customer's VIN" — not "let me click
register and re-type". Adds an inline VIN input to the footer CTA on both
/demo and /demo/categories/:id; submitting routes to /register?vin=… so
the register teaser (see ed74e2f) lifts off where this leaves off.
* New DemoVinCta component owns the form + analytics. Empty submit still
works as plain "Hesap Aç" (no VIN); typing a VIN flips the button label
to "Bu VIN için Hesap Aç".
* B2B copy retained from prior commits: "Sınırsız şase sorgulamak için
ücretsiz hesap aç" headline, "Kart bilgisi gerekmez · 30 gün ücretsiz ·
istediğin an iptal" trust strip — see [[sase-b2b-copy-not-consumer]].
* Input placeholder reads "Müşterinizin şase numarası (17 karakter)" — B2B
framing ("müşterinizin") not "kendi aracınız", per the same memory.
* Mobile-friendly: input + button stack vertically <sm, side-by-side ≥sm;
full-width button on mobile clears the Chatwoot widget.
* PostHog: emits demo_to_register_click with {source, has_vin, vin_length}
so we can split conversion by "browsed → typed VIN → registered" vs
"browsed → bare register".
DemoBanner stays a single-CTA sticky anchor (the "Yeni VIN sorgula" gate
lives in the footer card instead — banner real estate is too narrow on
mobile for a useful input).
This commit is contained in:
77
apps/web/src/components/demo/demo-vin-cta.tsx
Normal file
77
apps/web/src/components/demo/demo-vin-cta.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import { capture } from "@/lib/posthog";
|
||||
import { Button, Input } from "@sase/ui";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import { type FormEvent, useState } from "react";
|
||||
|
||||
interface DemoVinCtaProps {
|
||||
/** Analytics surface tag — e.g. "footer", "category_footer". */
|
||||
source: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Conversion card shown at the bottom of /demo pages: B2B trust strip + a
|
||||
* VIN input the visitor can paste their OWN VIN into. Submitting the form
|
||||
* (or clicking the empty-input button) routes to /register with the VIN
|
||||
* carried via `?vin=` so the register teaser can lift off where this leaves
|
||||
* off. The "Yeni VIN sorgula" gate from the 1C spec lives here rather than
|
||||
* in the sticky DemoBanner so the banner stays a single-CTA conversion
|
||||
* anchor without crowding mobile widths.
|
||||
*/
|
||||
export function DemoVinCta({ source }: DemoVinCtaProps) {
|
||||
const navigate = useNavigate();
|
||||
const [vin, setVin] = useState("");
|
||||
|
||||
const trimmed = vin.trim().toUpperCase();
|
||||
const hasVin = trimmed.length > 0;
|
||||
const submitLabel = hasVin ? "Bu VIN için Hesap Aç" : "Hesap Aç";
|
||||
|
||||
const handleSubmit = (e?: FormEvent) => {
|
||||
e?.preventDefault();
|
||||
capture("demo_to_register_click", {
|
||||
source,
|
||||
has_vin: hasVin,
|
||||
vin_length: trimmed.length,
|
||||
});
|
||||
navigate({
|
||||
to: "/register",
|
||||
search: hasVin ? { vin: trimmed } : {},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-20 mt-8 rounded-lg border border-border bg-muted/30 p-5 sm:mb-0">
|
||||
<div className="mb-3 flex flex-col gap-1">
|
||||
<p className="text-sm font-semibold">Sınırsız şase sorgulamak için ücretsiz hesap aç</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Kart bilgisi gerekmez · 30 gün ücretsiz · istediğin an iptal
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="flex flex-col gap-2 sm:flex-row">
|
||||
<Input
|
||||
value={vin}
|
||||
onChange={(e) => setVin(e.target.value.toUpperCase())}
|
||||
placeholder="Müşterinizin şase numarası (17 karakter)"
|
||||
maxLength={17}
|
||||
inputMode="text"
|
||||
autoCapitalize="characters"
|
||||
autoCorrect="off"
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
enterKeyHint="go"
|
||||
aria-label="Müşterinizin şase numarası"
|
||||
className="font-mono"
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full shrink-0 sm:w-auto"
|
||||
data-faro-user-action-name={`demo-vin-cta-${source}`}
|
||||
>
|
||||
{submitLabel}
|
||||
<ArrowRight className="ml-1 h-4 w-4" />
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
import { DemoBanner } from "@/components/demo/demo-banner";
|
||||
import { DemoVinCta } from "@/components/demo/demo-vin-cta";
|
||||
import { usePageMeta } from "@/hooks/use-page-meta";
|
||||
import { ApiError, api } from "@/lib/api-client";
|
||||
import { KEYS_8 } from "@/lib/keys";
|
||||
import { capture } from "@/lib/posthog";
|
||||
import { cleanModelName } from "@/lib/vehicle";
|
||||
import type { CategoryNode, Vehicle } from "@sase/shared";
|
||||
import { Button, Card, CardContent, CardHeader, CardTitle, Skeleton } from "@sase/ui";
|
||||
import { Card, CardContent, CardHeader, CardTitle, Skeleton } from "@sase/ui";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Link, createFileRoute } from "@tanstack/react-router";
|
||||
import { ArrowRight, FolderOpen } from "lucide-react";
|
||||
@@ -133,31 +134,11 @@ function DemoVehiclePage() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Footer CTA — second conversion surface after browsing. Mobile layout
|
||||
stacks (col) with full-width button; Chatwoot widget sits in the
|
||||
bottom-right so we leave breathing room and keep the CTA above it
|
||||
with `mb-20` on mobile. */}
|
||||
<div className="mb-20 mt-8 flex flex-col items-start gap-3 rounded-lg border border-border bg-muted/30 p-5 sm:mb-0 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-semibold">Sınırsız şase sorgulamak için ücretsiz hesap aç</p>
|
||||
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||
Kart bilgisi gerekmez · 30 gün ücretsiz · istediğin an iptal
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
asChild
|
||||
className="w-full shrink-0 sm:w-auto"
|
||||
data-faro-user-action-name="demo-footer-signup"
|
||||
>
|
||||
<Link
|
||||
to="/register"
|
||||
onClick={() => capture("demo_to_register_click", { source: "footer" })}
|
||||
>
|
||||
Hesap Aç
|
||||
<ArrowRight className="ml-1 h-4 w-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
{/* Footer conversion card — VIN input lets the visitor jump straight
|
||||
from the demo into a signup pre-filled with THEIR vehicle, instead
|
||||
of having to bounce back to the hero. Empty submit = same as
|
||||
plain "Hesap Aç". */}
|
||||
<DemoVinCta source="footer" />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { DemoBanner } from "@/components/demo/demo-banner";
|
||||
import { DemoVinCta } from "@/components/demo/demo-vin-cta";
|
||||
import { SchemaViewer } from "@/components/schema/schema-viewer";
|
||||
import { usePageMeta } from "@/hooks/use-page-meta";
|
||||
import type { CategorySchema } from "@/hooks/use-parts";
|
||||
@@ -189,24 +190,10 @@ function DemoCategoryPage() {
|
||||
</Suspense>
|
||||
))}
|
||||
|
||||
{/* Footer CTA — present on every leaf so the conversion path is always
|
||||
one click away after the user has just had an "aha" moment. Mobile
|
||||
layout stacks with a full-width button and clears the Chatwoot
|
||||
widget that sits in the bottom-right (`mb-20` on mobile). */}
|
||||
{data && !data.loadError && (
|
||||
<div className="mb-20 mt-6 flex flex-col items-start gap-3 rounded-lg border border-border bg-muted/30 p-5 sm:mb-0 sm:flex-row sm:items-center sm:justify-between">
|
||||
<p className="text-sm font-semibold">Sınırsız şase sorgulamak için ücretsiz hesap aç</p>
|
||||
<Button asChild className="w-full shrink-0 sm:w-auto">
|
||||
<Link
|
||||
to="/register"
|
||||
onClick={() => capture("demo_to_register_click", { source: "category_footer" })}
|
||||
>
|
||||
Hesap Aç
|
||||
<ArrowRight className="ml-1 h-4 w-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{/* Footer conversion card — VIN input at the "aha moment" lets the
|
||||
visitor pivot straight from browsing the demo to signing up with
|
||||
their own vehicle pre-filled. */}
|
||||
{data && !data.loadError && <DemoVinCta source="category_footer" />}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user