body: JSON.stringify(payload) }); if (!res.ok) throw new Error('Submission failed'); statusEl.textContent = "Review submitted successfully!"; statusEl.style.color = "#22c55e"; document.getElementById('reviewForm').reset(); loadReviews(id); // Reload list } catch (err) { statusEl.textContent = "Failed to submit review. Please try again."; statusEl.style.color = "#ff2a5f"; } }; // Fetch and render reviews const loadReviews = async (id) => { const listEl = document.getElementById('reviewsList'); try { const res = await fetch('/api/reviews?id=' + id); const data = await res.json(); if (data.reviews && data.reviews.length > 0) { listEl.innerHTML = data.reviews.map(r => `
${window.escapeHTML(r.user_name || 'Anonymous')} ${'★'.repeat(r.rating)}${'☆'.repeat(5-r.rating)}

${window.escapeHTML(r.comment)}

${new Date(r.created_at).toLocaleDateString()}
`).join(''); } else { listEl.innerHTML = '
No reviews yet. Be the first to review!
'; } } catch (e) { listEl.innerHTML = '
Failed to load reviews.
'; } }; loadReviews(site.id); // Inject Related Sites let related = []; try { const relatedRes = await fetch(`/api/sites?category=${encodeURIComponent(site.category)}&sort=random&limit=4`); const relatedData = await relatedRes.json(); if (relatedData && relatedData.sites) { related = relatedData.sites.filter(s => s.id !== site.id).slice(0, 3); } } catch (e) { console.error("Failed to load related sites"); } // Inject mid-review ad safely const adContainer = document.getElementById('mid-review-ad-container'); if (adContainer) { const confScript = document.createElement('script'); confScript.innerHTML = "atOptions = {'key':'384264be4aaafb8eb28962829e409253','format':'iframe','height':250,'width':300,'params':{}};"; const invokeScript = document.createElement('script'); invokeScript.src = "https://revolthem.com/384264be4aaafb8eb28962829e409253/invoke.js"; adContainer.appendChild(confScript); adContainer.appendChild(invokeScript); } const relatedGrid = document.getElementById('relatedGrid'); related.forEach(s => { const sUrl = new URL(s.url); const sFavicon = `https://www.google.com/s2/favicons?domain=${sUrl.hostname}&sz=64`; const sLocalName = escapeHTML(s[`name_${currentLang}`] || s.name); const sLocalCat = (t.categories && t.categories[s.category]) ? t.categories[s.category] : s.category; const card = document.createElement('div'); card.className = 'card'; card.innerHTML = `
${sLocalCat}
${escapeHTML(s[`description_${currentLang}`] || s.description)}
`; relatedGrid.appendChild(card); }); // === Inject WebPage + AggregateRating Schema (fixes GSC schema errors) === const schemaEl = document.getElementById('review-schema'); if (schemaEl && site) { const pageSchema = { "@context": "https://schema.org", "@type": "WebPage", "name": site.name + " Review | HentaiVault", "url": `https://hentaivault.me/site?id=${site.id}`, "description": `Expert review of ${site.name} — ratings, pros, cons, and alternatives.`, "about": { "@type": "WebSite", "name": site.name, "url": site.url }, "aggregateRating": { "@type": "AggregateRating", "ratingValue": String(site.rating), "bestRating": "5", "worstRating": "1", "ratingCount": "1" }, "author": { "@type": "Organization", "name": "HentaiVault", "url": "https://hentaivault.me" } }; schemaEl.textContent = JSON.stringify(pageSchema); } }); QR Code — HentaiVault.me

hentaivault.me

Share this link

📱 Scan Me