'use client'; import { useEffect, useRef } from 'react'; import { useCart } from '@/core/providers/store-provider'; /** * Mounts the Brainerce AI chat widget (the store's shopping assistant). * * Everything about the bot — name, avatar, colors, greeting, starter * questions, capabilities, guardrails — is configured by the merchant in the * Brainerce dashboard (Customers → Storefront Bot). This component renders * nothing until the bot is switched Live there, so it is always safe to keep * mounted. * * The widget's product cards call `onAddToCart`, which goes through the same * smartAddToCart + refreshCart path as the storefront's own product cards, so * the header cart count stays in sync. */ export function BrainerceBotWidget() { const { refreshCart } = useCart(); const refreshCartRef = useRef(refreshCart); refreshCartRef.current = refreshCart; useEffect(() => { const connectionId = process.env.NEXT_PUBLIC_BRAINERCE_SALES_CHANNEL_ID || process.env.NEXT_PUBLIC_BRAINERCE_CONNECTION_ID; if (!connectionId) return; let bot: { destroy(): void } | null = null; let cancelled = false; void import('brainerce/bot').then(({ BrainerceBot }) => BrainerceBot.mount({ connectionId, baseUrl: process.env.NEXT_PUBLIC_BRAINERCE_API_URL || undefined, // Order-lookup login gating: this template's own same-origin BFF proxy // (see src/app/api/store/[...path]/route.ts) already forwards the // shopper's httpOnly session cookie as a Bearer header — the same // mechanism @/core/lib/brainerce's own client uses. Opting the bot in // here lets it detect login state without the widget ever touching the // cookie itself. customerSessionProxyPath: '/api/store', onAddToCart: async ({ productId, variantId, quantity }) => { try { const { getClient } = await import('@/core/lib/brainerce'); await getClient().smartAddToCart({ productId, variantId: variantId ?? undefined, quantity, }); await refreshCartRef.current(); return true; } catch { return false; // widget falls back to the product page } }, }).then((mounted) => { if (cancelled) mounted?.destroy(); else bot = mounted; }), ); return () => { cancelled = true; bot?.destroy(); }; }, []); return null; }