Testimonials

The reviews below are illustrative sample testimonials shown to demonstrate the page design; they will be replaced with real, attributed client testimonials before launch.

Client Testimonials

Leave a Review
(async function(){ // config const rootList = document.getElementById('tc_list'); const pagEl = document.getElementById('tc_pagination'); const perPage = 12; const searchInput = document.getElementById('tc_search'); const ratingInput = document.getElementById('tc_rating'); const showInput = document.getElementById('tc_show'); const filterBtn = document.getElementById('tc_filter_btn'); const resetBtn = document.getElementById('tc_reset_btn'); const topLeaveBtn = document.getElementById('mc_leave_review_top'); const reviewModal = document.getElementById('mcReviewModal'); // helper utilities function escapeAttr(s){ return String(s||'').replace(/["']/g, m => m === '"' ? '"' : '''); } function stripHtml(html){ const tmp = document.createElement('div'); tmp.innerHTML = html || ''; return (tmp.textContent || tmp.innerText || '').trim(); } function excerptFromPost(p, max = 220){ if (p.excerpt && p.excerpt.rendered && stripHtml(p.excerpt.rendered).length) return stripHtml(p.excerpt.rendered); const txt = stripHtml((p.content && p.content.rendered) ? p.content.rendered : ''); return txt.length > max ? txt.slice(0, max).trim() + '…' : txt; } function renderStars(r){ r = Number(r) || 5; r = Math.max(0, Math.min(5, r)); let s = ''; for (let i=0;i<r;i++) s += ''; return s; } // discover social review links client-side (tries globals then ACF options) async function getSocialLinksClient() { if (window.MC_Social && (window.MC_Social.facebook || window.MC_Social.google)) { return { facebook: window.MC_Social.facebook || '', google: window.MC_Social.google || '' }; } if (window.mcSocial && (window.mcSocial.facebook || window.mcSocial.google)) { return { facebook: window.mcSocial.facebook || '', google: window.mcSocial.google || '' }; } // try ACF options endpoint try { const r = await fetch('/wp-json/acf/v3/options', { credentials: 'same-origin' }); if (r.ok) { const j = await r.json(); const fields = j.acf || j.fields || j; const fb = fields && (fields.facebook_review_url || fields.fb_review_url || fields.facebook_url || fields.facebook); const gg = fields && (fields.google_review_url || fields.google_url || fields.google); if (fb || gg) return { facebook: fb || '', google: gg || '' }; } } catch(e){} return { facebook: '', google: '' }; } // render a card (no per-card Leave button) function renderCard(p, social) { const img = (p.meta && p.meta.testimonial_image) || (p._embedded && p._embedded['wp:featuredmedia'] && p._embedded['wp:featuredmedia'][0] && p._embedded['wp:featuredmedia'][0].source_url) || ('https://picsum.photos/seed/'+encodeURIComponent((p.slug|| (p.title && p.title.rendered) || 'anon')).slice(0,20) +'/200/200'); const title = (p.title && p.title.rendered) ? stripHtml(p.title.rendered) : 'Anonymous'; const excerpt = excerptFromPost(p, 220); const ratingMeta = Number((p.meta && p.meta.rating) || 0) || 5; const permalink = p.link || ('/testimonials/' + encodeURIComponent(p.slug || '')); // build actions: conditional social links + one Read Full let actions = ''; if (social && social.facebook) actions += 'Facebook Review '; if (social && social.google) actions += 'Google Review '; actions += 'Read full'; return `
${escapeAttr(title)} portrait
${escapeAttr(title)}
${renderStars(ratingMeta)}
${escapeAttr(excerpt)}
`; } // fetch candidates and apply client filters (keeps your existing approach) async function loadAndRender(page=1) { rootList.innerHTML = '
Loading...
'; try { const q = searchInput.value.trim(); const rating = ratingInput.value; const show = showInput.value; // fetch a candidate set (heuristic) const candidatePerPage = perPage * 3; const endpoint = '/wp-json/wp/v2/testimonial?per_page=' + candidatePerPage + '&page=' + page + '&_embed'; const res = await fetch(endpoint, { credentials: 'same-origin' }); if (!res.ok) throw new Error('Fetch failed: '+res.status); const total = parseInt(res.headers.get('X-WP-Total') || '0', 10); const totalPages = parseInt(res.headers.get('X-WP-TotalPages') || '1', 10); let posts = await res.json(); // apply client filters if (rating) posts = posts.filter(p => Number((p.meta && p.meta.rating) || 0) >= Number(rating)); if (show === 'featured') posts = posts.filter(p => !!(p.meta && (p.meta.featured === true || p.meta.featured === '1' || p.meta.featured === 1))); if (q) { const lower = q.toLowerCase(); posts = posts.filter(p => { const t = (p.title && p.title.rendered || '').toLowerCase(); const c = stripHtml(p.content && p.content.rendered || '').toLowerCase(); return t.includes(lower) || c.includes(lower); }); } // paginate client-side list const pageItems = posts.slice(0, perPage); const social = await getSocialLinksClient(); if (pageItems.length === 0) { rootList.innerHTML = '
No testimonials match.
'; } else { rootList.innerHTML = pageItems.map(p => renderCard(p, social)).join(''); } // Render pagination (best-effort using server pages) renderPagination(totalPages || 1, page); } catch (err) { console.error(err); rootList.innerHTML = '
Error loading testimonials.
'; pagEl.innerHTML = ''; } } function renderPagination(totalPages, currentPage) { pagEl.innerHTML = ''; currentPage = Number(currentPage) || 1; totalPages = Math.max(1, Number(totalPages)); function makeItem(label, page, disabled=false, active=false){ return `
  • ${label}
  • `; } pagEl.innerHTML += makeItem('«', Math.max(1, currentPage-1), currentPage<=1); const start = Math.max(1, currentPage-2); const end = Math.min(totalPages, currentPage+2); for (let i = start; i =totalPages); } // filter events filterBtn.addEventListener('click', ()=> loadAndRender(1)); resetBtn.addEventListener('click', ()=> { searchInput.value=''; ratingInput.value=''; showInput.value='all'; loadAndRender(1); }); pagEl.addEventListener('click', (e) => { const a = e.target.closest('a.page-link'); if (!a) return; e.preventDefault(); const page = Number(a.getAttribute('data-page')) || 1; loadAndRender(page); window.scrollTo({top:0,behavior:'smooth'}); }); // open modal (top button) if (topLeaveBtn && reviewModal) { topLeaveBtn.addEventListener('click', function(e){ e.preventDefault(); reviewModal.style.display = 'block'; setTimeout(function(){ reviewModal.classList.add('visible'); }, 10); }); } // close modal (background or X button) document.addEventListener('click', function(e){ if (!reviewModal) return; if (e.target.classList && e.target.classList.contains('mc-modal-bg')) { reviewModal.classList.remove('visible'); setTimeout(function(){ reviewModal.style.display = 'none'; }, 220); } if (e.target.closest && e.target.closest('.mc-modal-close')) { reviewModal.classList.remove('visible'); setTimeout(function(){ reviewModal.style.display = 'none'; }, 220); } }); // hook Forminator success for form ID 284: close modal + refresh list if (window.jQuery) { (function($){ $(document).on('forminator:form:submit:success', '#forminator-module-284', function(e, formData){ if (reviewModal) { reviewModal.classList.remove('visible'); setTimeout(function(){ reviewModal.style.display = 'none'; }, 220); } // reload first page so newly created testimonial can appear once approved/published loadAndRender(1); }); })(jQuery); } // initial load loadAndRender(1); })();