Commit f2706159 authored by Jan Köster's avatar Jan Köster
Browse files

test

parent 0381eb9a
Loading
Loading
Loading
Loading
+145 −48
Original line number Diff line number Diff line
@@ -64,58 +64,155 @@ namespace blogi {
            void Settings(libhttppp::HttpRequest &curreq, libhtmlpp::HtmlElement &settings) override {}

            void Render(libhtmlpp::HtmlElement &el) const override {
                // Wrapper div for reviews
                std::string uid = "gr_" + std::string(Id);

                libhtmlpp::HtmlElement *container = new libhtmlpp::HtmlElement("div");
                container->setAttribute("id", "googlereviews-container");
                container->setAttribute("class", "googlereviews-grid");
                container->setAttribute("id", uid.c_str());
                el.appendChild(container);

                // Add script to dynamically load reviews
                libhtmlpp::HtmlElement *script = new libhtmlpp::HtmlElement("script");
                libhtmlpp::ScriptElement js;
                js.setScript(R"(
                    document.addEventListener('DOMContentLoaded', function() {
                        fetch('/google/reviews/list')
                            .then(response => response.json())
                            .then(data => {
                                const container = document.getElementById('googlereviews-container');
                                if (!data || data.length === 0) {
                                    container.innerHTML = '<p>Noch keine Bewertungen vorhanden.</p>';
                                    return;
                js.setScript(R"JS(
(function(){
  var cs = document.currentScript;
  var root = cs ? cs.previousElementSibling : null;
  if(!root) return;
  var uid = root.id;

  if(!document.getElementById(uid+'_css')){
    var st = document.createElement('style');
    st.id = uid+'_css';
    st.textContent =
      '#'+uid+'{background:#fff;border-radius:16px;box-shadow:0 18px 45px rgba(15,23,42,.12),0 0 0 1px rgba(15,23,42,.03);padding:32px 24px;box-sizing:border-box;width:100%;font-family:inherit;}'+
      '#'+uid+' .gr-row{display:flex;align-items:flex-start;gap:24px;}'+
      '#'+uid+' .gr-hdr{width:220px;flex-shrink:0;display:flex;flex-direction:column;align-items:center;text-align:center;padding-top:12px;}'+
      '#'+uid+' .gr-logo{height:44px;margin-bottom:8px;}'+
      '#'+uid+' .gr-avg{font-size:2.2em;font-weight:700;color:#222;line-height:1;}'+
      '#'+uid+' .gr-avgstars{color:#ffb400;font-size:1.15em;margin:4px 0 2px;}'+
      '#'+uid+' .gr-cnt{font-size:.82em;color:#888;}'+
      '#'+uid+' .gr-wrap{position:relative;flex:1;overflow:hidden;}'+
      '#'+uid+' .gr-vp{overflow:hidden;}'+
      '#'+uid+' .gr-track{display:flex;transition:transform .4s ease;gap:16px;}'+
      '#'+uid+' .gr-card{flex:0 0 calc(33.333% - 11px);max-width:calc(33.333% - 11px);background:#f9fafb;border-radius:14px;padding:16px;display:flex;flex-direction:row;gap:12px;border:1px solid rgba(148,163,184,.3);box-sizing:border-box;}'+
      '#'+uid+' .gr-av{width:40px;height:40px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:1.05em;color:#fff;}'+
      '#'+uid+' .gr-bd{flex:1;font-size:.95em;color:#111827;}'+
      '#'+uid+' .gr-name{font-weight:600;margin-bottom:3px;}'+
      '#'+uid+' .gr-rstars{color:#fbbf24;font-size:.9em;margin-bottom:4px;}'+
      '#'+uid+' .gr-txt{font-size:.92em;line-height:1.45;}'+
      '#'+uid+' .gr-dt{font-size:.79em;color:#6b7280;margin-top:6px;}'+
      '#'+uid+' .gr-btn{position:absolute;top:50%;transform:translateY(-50%);width:32px;height:32px;border-radius:50%;border:1px solid #d1d5db;background:#fff;cursor:pointer;box-shadow:0 2px 6px rgba(15,23,42,.15);z-index:10;font-size:1em;display:flex;align-items:center;justify-content:center;padding:0;}'+
      '#'+uid+' .gr-btn:disabled{opacity:.35;cursor:default;}'+
      '#'+uid+' .gr-prev{left:-6px;}'+
      '#'+uid+' .gr-next{right:-6px;}'+
      '#'+uid+' .gr-more{border:0;background:none;padding:0;color:#2563eb;cursor:pointer;font-size:.9em;}'+
      '@media(max-width:768px){'+
        '#'+uid+'{padding:24px 16px;}'+
        '#'+uid+' .gr-row{flex-direction:column;}'+
        '#'+uid+' .gr-hdr{width:100%;}'+
        '#'+uid+' .gr-card{flex:0 0 100%;max-width:100%;}'+
      '}';
    document.head.appendChild(st);
  }

  var AVCOLORS = ['#4f46e5','#0891b2','#059669','#d97706','#dc2626','#7c3aed','#db2777','#0284c7'];
  function avColor(name){
    var h=0;
    for(var i=0;i<name.length;i++) h=(h*31+name.charCodeAt(i))&0xffff;
    return AVCOLORS[h%AVCOLORS.length];
  }
  function starsHtml(n){
    var s='';
    for(var i=0;i<5;i++) s+=(i<n)?'&#9733;':'&#9734;';
    return s;
  }
                                container.innerHTML = '';
                                data.forEach(rev => {
                                    let stars = '';
                                    for(let i=0; i<5; i++) {
                                        if (i < rev.rating) stars += '⭐';
                                        else stars += '★';
  function formatDate(ts){
    if(!ts) return '';
    var n=Number(ts);
    var d=n>1e9 ? new Date(n*1000) : new Date(ts);
    return isNaN(d.getTime()) ? '' : d.toLocaleDateString('de-DE');
  }
  function perPage(){ return window.innerWidth<=768?1:3; }

                                    const box = document.createElement('div');
                                    box.style = 'background-color:#ffffff; padding:20px; border-radius:10px; border:1px solid #d4e8a8; margin-bottom:20px; flex:1; min-width:250px;';
  function build(reviews){
    if(!reviews||!reviews.length){
      root.textContent='Noch keine Bewertungen vorhanden.';
      return;
    }
    var sum=0;
    for(var i=0;i<reviews.length;i++) sum+=reviews[i].rating||0;
    var avg=Math.round((sum/reviews.length)*10)/10;

    root.innerHTML=
      '<div class="gr-row">'+
        '<div class="gr-hdr">'+
          '<img class="gr-logo" src="https://www.gstatic.com/images/branding/googlelogo/svg/googlelogo_clr_74x24px.svg" alt="Google">'+
          '<div class="gr-avg">'+avg.toFixed(1)+'</div>'+
          '<div class="gr-avgstars">'+starsHtml(Math.round(avg))+'</div>'+
          '<div class="gr-cnt">'+reviews.length+' Bewertungen</div>'+
        '</div>'+
        '<div class="gr-wrap">'+
          '<button type="button" class="gr-btn gr-prev" aria-label="Zurück">&#10094;</button>'+
          '<div class="gr-vp"><div class="gr-track"></div></div>'+
          '<button type="button" class="gr-btn gr-next" aria-label="Weiter">&#10095;</button>'+
        '</div>'+
      '</div>';

    var track=root.querySelector('.gr-track');
    reviews.forEach(function(r){
      var name=r.author_name||'Anonym';
      var init=name.charAt(0).toUpperCase();
      var full=String(r.text||'');
      var isLong=full.length>100;
      var short=isLong?full.slice(0,100)+'…':full;
      var card=document.createElement('div');
      card.className='gr-card';
      card.innerHTML=
        '<div class="gr-av" style="background:'+avColor(name)+'">'+init+'</div>'+
        '<div class="gr-bd" data-full="'+encodeURIComponent(full)+'">'+
          '<div class="gr-name">'+name+'</div>'+
          '<div class="gr-rstars">'+starsHtml(r.rating||5)+'</div>'+
          '<div class="gr-txt">'+short+(isLong?' <button type="button" class="gr-more">mehr lesen</button>':'')+'</div>'+
          '<div class="gr-dt">'+formatDate(r.time)+'</div>'+
        '</div>';
      track.appendChild(card);
    });

                                    const head = document.createElement('div');
                                    head.style = 'font-size:14px; color:#07071a; font-weight:bold; padding-bottom:10px;';
                                    head.innerText = rev.author_name + ' ' + stars;
    var page=0;
    var pp=perPage();
    function total(){ return Math.max(1,Math.ceil(reviews.length/pp)); }
    function slide(idx){
      idx=Math.max(0,Math.min(idx,total()-1));
      var first=track.children[0];
      var w=first?(first.offsetWidth+16):0;
      track.style.transform='translateX(-'+(idx*pp*w)+'px)';
      page=idx;
    }

                                    const body = document.createElement('div');
                                    body.style = 'font-size:14px; color:#3d4a30;';
                                    body.innerText = '"' + rev.text + '"';
    var prev=root.querySelector('.gr-prev');
    var next=root.querySelector('.gr-next');
    if(prev) prev.addEventListener('click',function(){ slide(page<=0?total()-1:page-1); });
    if(next) next.addEventListener('click',function(){ slide(page>=total()-1?0:page+1); });

                                    box.appendChild(head);
                                    box.appendChild(body);
                                    container.appendChild(box);
                                });
                            })
                            .catch(err => console.error('Error fetching reviews:', err));
    track.addEventListener('click',function(ev){
      if(!ev.target.classList.contains('gr-more')) return;
      var bd=ev.target.closest('.gr-bd');
      if(bd) bd.querySelector('.gr-txt').textContent=decodeURIComponent(bd.getAttribute('data-full')||'');
    });
                )");

                script->appendChild(js);
                
                // Add inline style for a responsive flex grid wrapper if needed
                container->setAttribute("style", "display: flex; gap: 30px; flex-wrap: wrap; justify-content: center;");
    window.addEventListener('resize',function(){ pp=perPage(); slide(0); });
    slide(0);
  }

                el.appendChild(container);
  fetch('/google/reviews/list')
    .then(function(r){ if(!r.ok) throw new Error(r.status); return r.json(); })
    .then(build)
    .catch(function(e){
      console.error('GoogleReviews widget:', e);
      root.textContent='Bewertungen konnten nicht geladen werden.';
    });
})();
)JS");
                script->appendChild(js);
                el.appendChild(script);
            }