Review Widget
Customize and embed your reviews widget on your website
Only show reviews that mention these keywords (great for topic-specific pages). Leave empty to show all.
This is how your widget will look on your website
Copy and paste this snippet into your website's HTML. Make sure your app is published first.
Endpoint: https://app.base44.app/api/apps/6a184a632e40e6e6306fd741/functions/getWidgetData
⚠️ Publish your app first — the widget only works on the published domain.
<!-- Reviews Widget -->
<div id="reviews-widget-undefined"></div>
<script>
(function() {
fetch('https://app.base44.app/api/apps/6a184a632e40e6e6306fd741/functions/getWidgetData', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ client_id: 'undefined' })
})
.then(function(r) { return r.json(); })
.then(function(data) {
var cfg = data.config || {};
var reviews = data.reviews || [];
var appOrigin = 'https://app.base44.app/api/apps/6a184a632e40e6e6306fd741/functions/getWidgetData'.split('/api/')[0];
var reviewUrl = data.review_url || (data.funnel_slug ? (appOrigin + '/review/' + data.funnel_slug) : '#');
var bg = cfg.background_color || '#ffffff';
var txt = cfg.text_color || '#111827';
var star = cfg.star_color || '#F59E0B';
var radius = (cfg.border_radius || 12) + 'px';
var layout = cfg.layout || 'grid';
function stars(n) {
var s = '';
for (var i = 1; i <= 5; i++) {
s += '<span style="color:' + (i <= n ? star : '#d1d5db') + ';font-size:16px;">★</span>';
}
return s;
}
function altText(r) {
var nm = r.reviewer_name || 'Anonymous';
var platMap = { google: 'Google', yelp: 'Yelp', facebook: 'Facebook', internal: 'Direct' };
var plat = platMap[r.platform] || (r.platform ? r.platform.charAt(0).toUpperCase() + r.platform.slice(1) : '');
var rt = r.rating ? r.rating + '-star' : '';
var snip = (r.review_text || '').replace(/s+/g, ' ').trim().slice(0, 120).replace(/"/g, '"');
var parts = [nm, rt, plat ? plat + ' review' : 'review'];
if (snip) parts.push('"' + snip + '"');
return parts.join(' - ');
}
function avatar(r) {
var nm = r.reviewer_name || 'Anonymous';
var sp = nm.indexOf(' ');
var ini = (nm[0] + (sp > 0 ? nm[sp + 1] : '')).toUpperCase();
var palette = ['#4285F4','#34A853','#EA4335','#FBBC05','#8B5CF6','#2563EB'];
var h = 0; for (var i = 0; i < nm.length; i++) h = (h * 31 + nm.charCodeAt(i)) >>> 0;
var bgc = palette[h % palette.length];
return r.reviewer_photo_url
? '<img src="' + r.reviewer_photo_url + '" alt="' + altText(r) + '" style="width:40px;height:40px;border-radius:50%;object-fit:cover;flex-shrink:0;">'
: '<div style="width:40px;height:40px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:600;font-size:14px;background:' + bgc + ';flex-shrink:0;">' + ini + '</div>';
}
function card(r) {
var date = r.review_date ? new Date(r.review_date).toLocaleDateString('en-US', { month: 'short', year: 'numeric' }) : '';
var platformColors = { google: '#4285F4', yelp: '#D32323', facebook: '#1877F2', internal: '#8B5CF6' };
var pColor = platformColors[r.platform] || '#888';
return '<div style="background:#fff;border:1px solid #e5e7eb;border-radius:' + radius + ';padding:20px;box-sizing:border-box;">' +
'<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:10px;">' +
'<div style="display:flex;gap:10px;align-items:center;">' + avatar(r) + '<div>' + stars(r.rating) + (cfg.show_reviewer_name !== false && r.reviewer_name ? '<p style="margin:4px 0 0;font-size:13px;font-weight:600;color:' + txt + ';">' + r.reviewer_name + '</p>' : '') + '</div></div>' +
(cfg.show_platform_badge !== false && r.platform ? '<span style="font-size:11px;padding:2px 8px;border-radius:20px;background:' + pColor + '20;color:' + pColor + ';">' + r.platform.charAt(0).toUpperCase() + r.platform.slice(1) + '</span>' : '') +
'</div>' +
(r.review_text ? '<p style="margin:0 0 8px;font-size:13px;line-height:1.5;color:' + txt + 'cc;">' + r.review_text.substring(0, 200) + (r.review_text.length > 200 ? '...' : '') + '</p>' : '') +
(cfg.show_review_date !== false && date ? '<p style="margin:0;font-size:11px;color:' + txt + '55;">' + date + '</p>' : '') +
'</div>';
}
var gSvg = '<svg width="18" height="18" viewBox="0 0 48 48"><path fill="#4285F4" d="M45 24c0-1.6-.1-3.1-.4-4.5H24v9h11.8c-.5 2.8-2.1 5.1-4.4 6.6v5.5h7.1c4.2-3.8 6.5-9.5 6.5-16.1z"/><path fill="#34A853" d="M24 46c5.9 0 10.9-2 14.5-5.3l-7.1-5.5c-2 1.3-4.5 2.1-7.4 2.1-5.7 0-10.5-3.9-12.2-9H4.3v5.7C7.9 41 15.4 46 24 46z"/><path fill="#FBBC05" d="M11.8 28.3c-.4-1.3-.7-2.7-.7-4.3s.3-3 .7-4.3v-5.7H4.3C2.8 17.5 2 20.6 2 24s.8 6.5 2.3 9.7l7.5-5.7z"/><path fill="#EA4335" d="M24 10.7c3.2 0 6.1 1.1 8.4 3.3l6.3-6.3C34.9 4.2 29.9 2 24 2 15.4 2 7.9 6.9 4.3 14l7.5 5.7c1.7-5.1 6.5-9 12.2-9z"/></svg>';
var vCheck = '<svg width="16" height="16" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" fill="#0d6efd"/><path d="M7 12.5l3 3 6-6" stroke="#fff" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>';
function scCard(r) {
var date = r.review_date ? new Date(r.review_date) : null;
var ago = '';
if (date) {
var d = Math.floor((Date.now() - date.getTime()) / 86400000);
if (d < 1) ago = 'recently';
else if (d < 30) ago = d + ' day' + (d > 1 ? 's' : '') + ' ago';
else if (d < 365) { var m = Math.floor(d / 30); ago = m + ' month' + (m > 1 ? 's' : '') + ' ago'; }
else { var y = Math.floor(d / 365); ago = y + ' year' + (y > 1 ? 's' : '') + ' ago'; }
}
var nm = r.reviewer_name || 'Anonymous';
var sp = nm.indexOf(' ');
var ini = (nm[0] + (sp > 0 ? nm[sp + 1] : '')).toUpperCase();
var palette = ['#4285F4','#34A853','#EA4335','#FBBC05','#8B5CF6','#2563EB'];
var h = 0; for (var i = 0; i < nm.length; i++) h = (h * 31 + nm.charCodeAt(i)) >>> 0;
var bg = palette[h % palette.length];
var photo = r.reviewer_photo_url
? '<img src="' + r.reviewer_photo_url + '" alt="' + altText(r) + '" style="width:44px;height:44px;border-radius:50%;object-fit:cover;">'
: '<div style="width:44px;height:44px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:600;background:' + bg + ';">' + ini + '</div>';
var text = r.review_text || '';
return '<div style="background:#fff;border:1px solid #e5e7eb;border-radius:' + radius + ';padding:20px;box-sizing:border-box;display:flex;flex-direction:column;height:100%;">'
+ '<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:12px;">'
+ '<div style="display:flex;gap:12px;align-items:center;">' + photo + '<div><div style="font-size:14px;font-weight:600;color:#212529;line-height:1.2;">' + nm + '</div><div style="font-size:12px;color:#6c757d;margin-top:2px;">' + ago + '</div></div></div>'
+ (r.platform === 'google' ? gSvg : '')
+ '</div>'
+ '<div style="display:flex;align-items:center;gap:6px;margin-bottom:8px;">' + stars(r.rating) + vCheck + '</div>'
+ '<div class="rw-clamp" style="font-size:14px;line-height:1.5;color:#212529;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;">' + text + '</div>'
+ (text.length > 180 ? '<button type="button" class="rw-more" style="align-self:flex-start;margin-top:4px;background:none;border:none;color:#0d6efd;font-size:14px;cursor:pointer;padding:0;font-family:inherit;">Show more</button>' : '')
+ '</div>';
}
var accent = cfg.primary_color || '#2563eb';
var html = '<div style="font-family:system-ui,sans-serif;background:' + bg + ';padding:24px;box-sizing:border-box;">';
if (cfg.show_title !== false && cfg.title && layout !== 'carousel_auto') {
html += '<h2 style="text-align:center;font-size:22px;font-weight:700;margin:0 0 20px;color:' + txt + ';">' + cfg.title + '</h2>';
}
if (layout === 'list') {
html += '<div style="display:flex;flex-direction:column;gap:16px;">' + reviews.map(card).join('') + '</div>';
} else if (layout === 'badge') {
html += '<div style="display:inline-flex;align-items:center;gap:12px;border:1px solid #e5e7eb;border-radius:' + radius + ';padding:12px 20px;background:#fff;">' +
'<span style="font-size:28px;font-weight:700;color:' + txt + ';">' + (data.avg_rating || '—') + '</span>' +
'<div>' + stars(Math.round(data.avg_rating || 0)) + '<div style="font-size:12px;color:#6b7280;">' + (data.total_reviews || 0) + ' reviews</div></div>' +
'</div>';
} else if (layout === 'carousel') {
html += '<div style="display:flex;gap:16px;overflow-x:auto;padding-bottom:8px;scroll-snap-type:x mandatory;">' +
reviews.map(function(r){ return '<div style="min-width:280px;scroll-snap-align:start;">' + card(r) + '</div>'; }).join('') +
'</div>';
} else if (layout === 'carousel_auto') {
html += '<div style="position:relative;overflow:hidden;">' +
'<div id="rw-track-undefined" style="display:flex;transition:transform 0.5s ease;">' +
reviews.map(function(r){ return '<div style="min-width:100%;box-sizing:border-box;padding:0 4px;">' + card(r) + '</div>'; }).join('') +
'</div>' +
'</div>';
} else if (layout === 'summary_carousel') {
var rated = reviews.filter(function(r){ return r.rating; });
var avg = rated.length ? (rated.reduce(function(a,b){return a+b.rating;},0)/rated.length).toFixed(1) : '—';
var logoHtml = data.logo_url
? '<img src="' + data.logo_url + '" style="width:64px;height:64px;border-radius:50%;object-fit:cover;margin-bottom:12px;">'
: '<div style="width:64px;height:64px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff;font-size:18px;font-weight:700;background:' + (cfg.primary_color||'#4285F4') + ';margin-bottom:12px;">' + (data.business_name||'Y').charAt(0).toUpperCase() + '</div>';
var summaryCard = '<div style="background:#fff;border:1px solid #e5e7eb;border-radius:' + radius + ';padding:24px;box-sizing:border-box;display:flex;flex-direction:column;align-items:center;text-align:center;min-width:220px;">'
+ logoHtml
+ '<div style="font-size:16px;font-weight:700;color:#212529;margin-bottom:8px;">' + (data.business_name||'') + '</div>'
+ '<div style="display:flex;align-items:center;gap:6px;margin-bottom:4px;"><span style="font-size:24px;font-weight:700;color:' + star + ';">' + avg + '</span>' + stars(Math.round(avg==='—'?0:avg)) + '</div>'
+ '<div style="display:flex;align-items:center;gap:6px;font-size:14px;color:#212529;margin-bottom:16px;"><span>' + (data.total_reviews||rated.length) + ' reviews on</span>' + gSvg + '</div>'
+ '<a href="' + reviewUrl + '" target="_blank" rel="noopener noreferrer" style="display:block;width:100%;padding:10px;text-align:center;font-size:12px;font-weight:600;letter-spacing:0.5px;color:#212529;border:1px solid #dee2e6;border-radius:6px;background:#fff;text-decoration:none;">LEAVE A REVIEW</a>'
+ '</div>';
var perPage = 3;
var pages = [];
for (var pi = 0; pi < reviews.length; pi += perPage) pages.push(reviews.slice(pi, pi + perPage));
var pagesHtml = pages.map(function(pg, idx) {
return '<div class="rw-page" data-idx="' + idx + '" style="display:' + (idx === 0 ? 'grid' : 'none') + ';grid-template-columns:repeat(3,1fr);grid-auto-rows:1fr;gap:16px;height:100%;">'
+ pg.map(scCard).join('')
+ '</div>';
}).join('');
html += '<div style="display:flex;gap:20px;align-items:stretch;">'
+ '<div style="flex:0 0 auto;">' + summaryCard + '</div>'
+ '<div id="rw-pages-undefined" style="position:relative;flex:1;min-width:0;">'
+ pagesHtml
+ (pages.length > 1
? '<button id="rw-prev-undefined" type="button" style="position:absolute;left:0;top:50%;transform:translate(-8px,-50%);width:36px;height:36px;border-radius:50%;background:#fff;border:1px solid #dee2e6;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 1px 3px rgba(0,0,0,0.1);font-size:18px;color:#6c757d;">‹</button>'
+ '<button id="rw-next-undefined" type="button" style="position:absolute;right:0;top:50%;transform:translate(8px,-50%);width:36px;height:36px;border-radius:50%;background:#fff;border:1px solid #dee2e6;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 1px 3px rgba(0,0,0,0.1);font-size:18px;color:#6c757d;">›</button>'
: '')
+ '</div>'
+ '</div>';
} else {
var locs = data.locations || [];
var pubOrigin = data.public_app_url || appOrigin;
function locUrl(id) {
var slug;
if (id === 'all') slug = data.funnel_slug;
else { var l = locs.find(function(x){return x.id===id;}); slug = l ? l.funnel_slug : data.funnel_slug; }
if (slug) return (pubOrigin || appOrigin) + '/review/' + slug;
return '#';
}
function locStats(id) {
if (id === 'all') return { avg: data.avg_rating, count: data.total_reviews };
var l = locs.find(function(x){return x.id===id;});
return l ? { avg: l.avg_rating, count: l.review_count } : { avg: null, count: 0 };
}
var gridSummary = '<div style="background:#fff;border:1px solid #e5e7eb;border-radius:' + radius + ';padding:20px;box-sizing:border-box;display:flex;flex-direction:column;align-items:center;text-align:center;justify-content:center;">'
+ (data.logo_url ? '<img src="' + data.logo_url + '" style="width:48px;height:48px;border-radius:50%;object-fit:cover;margin-bottom:8px;">' : '<div style="width:48px;height:48px;border-radius:50%;display:flex;align-items:center;justify-content:center;color:#fff;font-size:18px;font-weight:700;background:' + (cfg.primary_color||'#4285F4') + ';margin-bottom:8px;">' + (data.business_name||'Y').charAt(0).toUpperCase() + '</div>')
+ '<div style="font-size:14px;font-weight:700;color:#212529;margin-bottom:6px;">' + (data.business_name||'') + '</div>'
+ '<div id="rw-gavg-undefined" style="display:flex;align-items:center;gap:6px;margin-bottom:4px;"><span style="font-size:20px;font-weight:700;color:' + star + ';">' + (data.avg_rating||'—') + '</span>' + stars(Math.round(data.avg_rating||0)) + '</div>'
+ '<div id="rw-gcount-undefined" style="font-size:12px;color:#6c757d;margin-bottom:12px;">' + (data.total_reviews||0) + ' reviews</div>'
+ '<a id="rw-glink-undefined" href="' + locUrl('all') + '" target="_blank" rel="noopener noreferrer" style="display:block;width:100%;padding:8px;text-align:center;font-size:11px;font-weight:600;letter-spacing:0.5px;color:#212529;border:1px solid #dee2e6;border-radius:6px;background:#fff;text-decoration:none;">LEAVE A REVIEW</a>'
+ '</div>';
var locFilter = locs.length > 1
? '<div style="margin-bottom:16px;"><select id="rw-gloc-undefined" style="width:100%;padding:8px 10px;font-size:13px;border:1px solid #dee2e6;border-radius:6px;background:#fff;color:#212529;"><option value="all">All Locations</option>' + locs.map(function(l){ return '<option value="' + l.id + '">' + l.name + '</option>'; }).join('') + '</select></div>'
: '';
function buildCards(arr) {
var g = (arr.length > 0 && arr.length % 2 === 0) ? arr.slice(0, -1) : arr;
if (!g.length) return '<div style="grid-column:2;display:flex;align-items:center;justify-content:center;color:#6c757d;font-size:13px;padding:20px;">No reviews match your filters yet.</div>';
return g.map(function(r){ return '<div class="rw-gcard" data-loc="' + (r.location_id||'') + '">' + card(r) + '</div>'; }).join('');
}
var cardsHtml = buildCards(reviews);
html += locFilter + '<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:16px;">' + gridSummary + '<div id="rw-gcards-undefined" style="display:contents;">' + cardsHtml + '</div>' + '</div>';
}
html += '<div style="text-align:center;padding:16px 0 4px;"><a href="https://yeloreputation.com" target="_blank" rel="noopener noreferrer" style="font-size:12px;color:' + txt + '44;text-decoration:none;">Powered by Yelo</a></div>';
html += '</div>';
document.getElementById('reviews-widget-undefined').innerHTML = html;
if (layout === 'carousel_auto' && reviews.length > 1) {
var idx = 0;
var track = document.getElementById('rw-track-undefined');
setInterval(function() {
idx = (idx + 1) % reviews.length;
track.style.transform = 'translateX(-' + (idx * 100) + '%)';
}, 4000);
}
if (layout === 'summary_carousel') {
var pagesWrap = document.getElementById('rw-pages-undefined');
var prevBtn = document.getElementById('rw-prev-undefined');
var nextBtn = document.getElementById('rw-next-undefined');
if (pagesWrap) {
var pageEls = pagesWrap.querySelectorAll('.rw-page');
var curPage = 0;
function showPage(n) {
pageEls.forEach(function(el, i) { el.style.display = (i === n) ? 'grid' : 'none'; });
}
if (prevBtn) prevBtn.addEventListener('click', function(){ curPage = (curPage - 1 + pageEls.length) % pageEls.length; showPage(curPage); });
if (nextBtn) nextBtn.addEventListener('click', function(){ curPage = (curPage + 1) % pageEls.length; showPage(curPage); });
pagesWrap.querySelectorAll('.rw-more').forEach(function(btn){
btn.addEventListener('click', function(){
var body = btn.previousElementSibling;
var open = body.style.webkitLineClamp === 'none';
body.style.webkitLineClamp = open ? '3' : 'none';
btn.textContent = open ? 'Show more' : 'Show less';
});
});
}
}
var glocSel = document.getElementById('rw-gloc-undefined');
if (glocSel) {
glocSel.addEventListener('change', function() {
var id = glocSel.value;
var set = (id === 'all') ? reviews : ((data.reviews_by_location && data.reviews_by_location[id]) || []);
var gcardsEl = document.getElementById('rw-gcards-undefined');
if (gcardsEl) gcardsEl.innerHTML = buildCards(set);
var stats = locStats(id);
var avgEl = document.getElementById('rw-gavg-undefined');
var cntEl = document.getElementById('rw-gcount-undefined');
var lnkEl = document.getElementById('rw-glink-undefined');
if (avgEl) avgEl.innerHTML = '<span style="font-size:20px;font-weight:700;color:' + star + ';">' + (stats.avg||'—') + '</span>' + stars(Math.round(stats.avg||0));
if (cntEl) cntEl.textContent = (stats.count||0) + ' reviews';
if (lnkEl) lnkEl.href = locUrl(id);
});
}
})
.catch(function(e) { console.error('Widget error:', e); });
})();
</script>