UNPKG

@konvo-ai/sdk-web

Version:

KonvoAI Conversational Ad SDK for Web Applications - Intelligent contextual advertising with AI-powered decision engine

126 lines (125 loc) 4.33 kB
import React, { useEffect, useState } from 'react'; import { KonvoAIAds } from './core'; export const KonvoAdView = ({ intent, placement = 'chat', style, onFill, onNoFill }) => { const [ad, setAd] = useState(null); const [isLoading, setIsLoading] = useState(true); useEffect(() => { let mounted = true; const fetchAd = async () => { try { // Call the decide API with the intent const decision = await KonvoAIAds.decide({ user: { anonId: KonvoAIAds.generateAnonId(), country: navigator.language.split('-')[1] || 'US', language: navigator.language.split('-')[0] || 'en' }, chat: { lastUserMsg: intent, context: placement }, surface: 'inline-chip' }); if (!mounted) return; if (decision.fill && decision.render) { setAd(decision); onFill?.(decision); // Track impression automatically await KonvoAIAds.handleImpression(decision); } else { onNoFill?.(); } } catch (err) { console.error('KonvoAdView: Failed to fetch ad', err); onNoFill?.(); } finally { if (mounted) { setIsLoading(false); } } }; fetchAd(); return () => { mounted = false; }; }, [intent, placement]); // No ad available - render nothing if (!ad || !ad.fill || !ad.render) { return null; } const { title, desc, imageUrl, cta } = ad.render; // Default styles for the ad container const containerStyles = { borderWidth: 1, borderStyle: 'solid', borderColor: '#e5e7eb', borderRadius: 8, padding: 12, backgroundColor: '#ffffff', boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1)', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', ...style }; const imageStyles = { width: '100%', maxWidth: 300, height: 150, objectFit: 'cover', borderRadius: 4, marginBottom: 8 }; const titleStyles = { fontWeight: 600, fontSize: 16, color: '#111827', marginBottom: 4, lineHeight: 1.4 }; const descStyles = { fontSize: 14, color: '#6b7280', marginBottom: 12, lineHeight: 1.5 }; const buttonStyles = { padding: '8px 16px', backgroundColor: '#3b82f6', color: '#ffffff', border: 'none', borderRadius: 6, fontSize: 14, fontWeight: 500, cursor: 'pointer', transition: 'background-color 0.2s', width: '100%' }; const adBadgeStyles = { position: 'absolute', top: 4, right: 4, fontSize: 10, color: '#9ca3af', backgroundColor: 'rgba(255, 255, 255, 0.9)', padding: '2px 6px', borderRadius: 4, fontWeight: 500 }; return (React.createElement("div", { style: containerStyles }, React.createElement("div", { style: { position: 'relative' } }, React.createElement("span", { style: adBadgeStyles }, "Ad"), imageUrl && (React.createElement("img", { src: imageUrl, alt: title, style: imageStyles, onError: (e) => { // Hide broken images e.target.style.display = 'none'; } })), title && React.createElement("h4", { style: titleStyles }, title), desc && React.createElement("p", { style: descStyles }, desc), cta && (React.createElement("button", { style: buttonStyles, onClick: cta.handler, onMouseEnter: (e) => { e.currentTarget.style.backgroundColor = '#2563eb'; }, onMouseLeave: (e) => { e.currentTarget.style.backgroundColor = '#3b82f6'; } }, cta.label))))); };