update ai commerce section

This commit is contained in:
2026-07-23 10:26:49 +05:30
parent 4e0facc9af
commit a4328a791a
135 changed files with 4081 additions and 391 deletions

View File

@@ -0,0 +1,189 @@
"use client";
import { useCallback, useEffect, useRef, useState } from "react";
import { useReducedMotion } from "framer-motion";
// The eight beats of a merchant's life on Nearle. The visitor watches the whole
// arc — join → digitize → sell → grow — play out once, then can replay/drive it.
export type MerchantStageId =
| "join"
| "digitize"
| "products"
| "browse"
| "orders"
| "packed"
| "rider"
| "grows";
export type MerchantStep = {
id: MerchantStageId;
num: string;
label: string; // short label for the progress rail
icon: string; // Material Symbol for the rail node
title: string;
desc: string;
};
export const MERCHANT_STEPS: MerchantStep[] = [
{
id: "join",
num: "01",
label: "Join",
icon: "handshake",
title: "Merchant Joins Nearle",
desc: "Your neighbourhood shop signs up in minutes — no tech skills needed.",
},
{
id: "digitize",
num: "02",
label: "Digitize",
icon: "qr_code_2",
title: "Store Digitized",
desc: "A quick QR scan turns your shelves into a searchable digital storefront.",
},
{
id: "products",
num: "03",
label: "Products",
icon: "inventory_2",
title: "Products Added",
desc: "AI reads a shelf photo and builds your live, shoppable catalogue.",
},
{
id: "browse",
num: "04",
label: "Browse",
icon: "search",
title: "Customers Browse",
desc: "Nearby shoppers discover and search your store in natural language.",
},
{
id: "orders",
num: "05",
label: "Orders",
icon: "receipt_long",
title: "Orders Received",
desc: "Orders land directly with you — an instant alert for every sale.",
},
{
id: "packed",
num: "06",
label: "Packed",
icon: "package_2",
title: "Products Packed",
desc: "You pack the order yourself, staying at the centre of every sale.",
},
{
id: "rider",
num: "07",
label: "Delivery",
icon: "two_wheeler",
title: "Nearle Rider Delivers",
desc: "A Nearle rider picks up and delivers to your customers door.",
},
{
id: "grows",
num: "08",
label: "Growth",
icon: "trending_up",
title: "Business Grows",
desc: "Demand insights and repeat orders compound into steady local growth.",
},
];
const AUTOPLAY_MS = 3200; // each stage stays readable for ~3.2s
export type MerchantJourney = {
steps: MerchantStep[];
index: number;
step: MerchantStep;
started: boolean;
autoplaying: boolean;
atEnd: boolean;
reduced: boolean;
start: () => void;
goTo: (i: number) => void;
next: () => void;
prev: () => void;
replay: () => void;
};
// Autoplays through the stages exactly once on scroll-into-view, then rests on
// the final stage. Any manual input (rail node, arrow, swipe) cancels autoplay
// and hands control to the visitor. Reduced motion → manual from the start.
export function useMerchantJourney(): MerchantJourney {
const reduced = useReducedMotion() ?? false;
const [started, setStarted] = useState(false);
const [index, setIndex] = useState(0);
const [autoplaying, setAutoplaying] = useState(false);
const last = MERCHANT_STEPS.length - 1;
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
const clear = useCallback(() => {
if (timer.current) clearTimeout(timer.current);
timer.current = null;
}, []);
useEffect(() => clear, [clear]);
// Auto-advance one stage at a time; simply stop scheduling (don't loop) at the
// end so the journey rests on the final stage. No synchronous setState here.
useEffect(() => {
if (!started || reduced || !autoplaying || index >= last) return;
timer.current = setTimeout(() => setIndex((i) => Math.min(i + 1, last)), AUTOPLAY_MS);
return clear;
}, [started, reduced, autoplaying, index, last, clear]);
const start = useCallback(() => {
setStarted((was) => {
if (!was && !reduced) setAutoplaying(true);
return true;
});
}, [reduced]);
const goTo = useCallback(
(i: number) => {
clear();
setAutoplaying(false);
setStarted(true);
setIndex(Math.max(0, Math.min(i, last)));
},
[clear, last]
);
const next = useCallback(() => {
clear();
setAutoplaying(false);
setStarted(true);
setIndex((i) => Math.min(i + 1, last));
}, [clear, last]);
const prev = useCallback(() => {
clear();
setAutoplaying(false);
setStarted(true);
setIndex((i) => Math.max(i - 1, 0));
}, [clear]);
const replay = useCallback(() => {
clear();
setIndex(0);
setStarted(true);
setAutoplaying(!reduced);
}, [clear, reduced]);
return {
steps: MERCHANT_STEPS,
index,
step: MERCHANT_STEPS[index],
started,
autoplaying,
atEnd: index >= last,
reduced,
start,
goTo,
next,
prev,
replay,
};
}