@konvo-ai/sdk-web
Version:
KonvoAI Conversational Ad SDK for Web Applications - Intelligent contextual advertising with AI-powered decision engine
127 lines (126 loc) • 4.58 kB
JavaScript
import React, { useEffect, useState } from 'react';
import { KonvoAIAds } from '../core';
export const KonvoAIAdsView = ({ surface, user, chat, className = '', onLoad, onError }) => {
const [ad, setAd] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const loadAd = async () => {
try {
const response = await KonvoAIAds.decide({
user,
chat,
surface
});
if (response.fill) {
setAd(response);
await KonvoAIAds.handleImpression(response);
onLoad?.();
}
else {
setAd(null);
onLoad?.();
}
}
catch (error) {
console.error('Failed to load ad:', error);
onError?.(error);
setAd(null);
}
finally {
setLoading(false);
}
};
loadAd();
}, [surface, user.anonId, chat.lastUserMsg]);
if (loading) {
return React.createElement("div", { className: `konvo-ai-ads-loading ${className}` }, "Loading...");
}
if (!ad || !ad.fill || !ad.render) {
return null;
}
const baseStyles = {
padding: '12px',
borderRadius: '8px',
backgroundColor: '#f8f9fa',
border: '1px solid #e0e0e0',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
};
const surfaceStyles = {
'inline-chip': {
...baseStyles,
display: 'inline-flex',
alignItems: 'center',
gap: '12px',
maxWidth: '400px'
},
'banner': {
...baseStyles,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%'
},
'interstitial': {
...baseStyles,
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
zIndex: 1000,
minWidth: '300px',
maxWidth: '500px',
boxShadow: '0 4px 12px rgba(0,0,0,0.15)'
},
'native': {
...baseStyles,
display: 'flex',
flexDirection: 'column',
gap: '8px'
}
};
return (React.createElement("div", { className: `konvo-ai-ads-view konvo-ai-ads-${surface} ${className}`, style: surfaceStyles[surface] },
ad.render.imageUrl && (React.createElement("img", { src: ad.render.imageUrl, alt: "", style: {
width: surface === 'inline-chip' ? '40px' : '80px',
height: surface === 'inline-chip' ? '40px' : '80px',
borderRadius: '4px',
objectFit: 'cover'
} })),
React.createElement("div", { style: { flex: 1 } },
React.createElement("h4", { style: {
margin: '0 0 4px 0',
fontSize: '14px',
fontWeight: '600',
color: '#333'
} }, ad.render.title),
React.createElement("p", { style: {
margin: '0 0 8px 0',
fontSize: '13px',
color: '#666',
lineHeight: '1.4'
} }, ad.render.desc),
React.createElement("button", { onClick: ad.render.cta.handler, style: {
padding: '6px 12px',
backgroundColor: '#0066cc',
color: 'white',
border: 'none',
borderRadius: '4px',
fontSize: '13px',
fontWeight: '500',
cursor: 'pointer',
transition: 'background-color 0.2s'
}, onMouseEnter: (e) => {
e.currentTarget.style.backgroundColor = '#0052a3';
}, onMouseLeave: (e) => {
e.currentTarget.style.backgroundColor = '#0066cc';
} }, ad.render.cta.label)),
React.createElement("span", { style: {
position: 'absolute',
top: '4px',
right: '4px',
fontSize: '10px',
color: '#999',
backgroundColor: 'rgba(255,255,255,0.9)',
padding: '2px 4px',
borderRadius: '2px'
} }, "Ad")));
};