feat: 5 fixes — dead site scoring, Kit Digital precision, social icons, GMB detection, social/GMB weighting

1. scorer: dead sites capped at 5 (was scoring HOT from SSL/CMS signals)
2. Kit Digital: require explicit kit-digital/agente-digitalizador signals;
   generic EU logo patterns (fondos-europeos, logo-ue, cofinanciado) removed.
   Gemini kit_digital_confirmed now overwrites heuristic in DB.
3. Browse table: social links replaced with compact coloured icon badges
   (fb/ig/in/x/tt/yt) linked to the profile URLs
4. site_analyzer: added has_gmb / gmb_url detection (Maps embed, Place links,
   LocalBusiness schema); fed to Gemini prompt
5. scorer: +5 no-social, +3 reachable contact; Gemini prompt includes GMB and
   social media management as sellable services; modal shows GMB/social status

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-14 07:21:02 +02:00
parent 793aea8a5f
commit dad910b6b0
6 changed files with 159 additions and 63 deletions

View File

@@ -91,12 +91,21 @@ tr:hover td{background:rgba(255,255,255,.025)}
.score{display:inline-block;padding:1px 6px;border-radius:5px;font-weight:800;font-size:11px;min-width:28px;text-align:center}
/* Contact chips */
.contact-chips{display:flex;flex-wrap:wrap;gap:3px}
.contact-chips{display:flex;flex-wrap:wrap;gap:3px;align-items:center}
.chip{display:inline-flex;align-items:center;gap:3px;padding:1px 6px;border-radius:4px;font-size:10px;background:var(--surface2);border:1px solid var(--border);color:var(--muted);max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.chip.email{border-color:#00d4aa33;color:var(--accent2)}
.chip.phone{border-color:#6c63ff33;color:var(--accent)}
.chip.wa{border-color:#22c55e33;color:#4ade80}
.chip.social{border-color:#f59e0b33;color:var(--kd)}
/* Social platform icon badges */
.sicon{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border-radius:4px;font-size:9px;font-weight:900;text-decoration:none;flex-shrink:0;line-height:1}
.sicon.fb{background:#1877f2;color:#fff}
.sicon.ig{background:linear-gradient(135deg,#f09433 0%,#e6683c 25%,#dc2743 50%,#cc2366 75%,#bc1888 100%);color:#fff}
.sicon.li{background:#0a66c2;color:#fff}
.sicon.tw{background:#000;color:#fff}
.sicon.tt{background:#010101;color:#fff}
.sicon.yt{background:#ff0000;color:#fff}
.sicon.gmb{background:#4285f4;color:#fff}
.sicon.other{background:var(--surface2);border:1px solid var(--border);color:var(--muted)}
/* Tooltip */
[title]{cursor:help}
@@ -182,6 +191,8 @@ tr:hover td{background:rgba(255,255,255,.025)}
<div class="mrow"><span class="mlabel">SEO</span><span x-text="modal.ai.seo_status||'—'"></span></div>
<div class="mrow"><span class="mlabel">Hosting</span><span x-text="(modal.sa?.org||'?') + ' / ' + (modal.sa?.ip_country||'?') + (modal.sa?.eu_hosted===false?' ❌ Non-EU':modal.sa?.eu_hosted?' ✅ EU':'')"></span></div>
<div class="mrow"><span class="mlabel">GDPR</span><span :style="(!modal.sa?.has_cookie_notice)?'color:var(--danger)':''" x-text="modal.ai.gdpr_compliance||'—'"></span></div>
<div class="mrow"><span class="mlabel">GMB</span><span :style="!modal.ai.has_gmb?'color:var(--warn)':'color:var(--accent2)'" x-text="modal.ai.has_gmb ? '✅ Found' : '❌ Not detected — opportunity'"></span></div>
<div class="mrow"><span class="mlabel">Social</span><span :style="!modal.ai.has_social_media?'color:var(--warn)':''" x-text="modal.ai.has_social_media ? '✅ Present' : '❌ No social media found — opportunity'"></span></div>
<!-- Content issues -->
<div x-show="(modal.ai.content_issues||[]).length>0" style="margin:8px 0">
@@ -215,8 +226,11 @@ tr:hover td{background:rgba(255,255,255,.025)}
<template x-for="wa in (modal.sa?.whatsapp||[])">
<a :href="wa" target="_blank" class="chip wa">💬 WhatsApp</a>
</template>
<template x-for="s in (modal.sa?.social_links||[]).slice(0,3)">
<a :href="s" target="_blank" class="chip social" x-text="s.replace('https://','').split('/')[0]"></a>
<template x-for="s in (modal.sa?.social_links||[])">
<a :href="s" target="_blank" :class="'sicon '+socialIconClass(s)" :title="s" x-text="socialIconLabel(s)"></a>
</template>
<template x-if="modal.ai?.has_gmb">
<a :href="modal.sa?.gmb_url||'#'" target="_blank" class="sicon gmb" title="Google My Business">G</a>
</template>
</div>
</div>
@@ -371,16 +385,16 @@ tr:hover td{background:rgba(255,255,255,.025)}
<td>
<div class="contact-chips" x-data="{c: parseContacts(row.contact_info)}">
<template x-for="em in (c.emails||[]).slice(0,1)" :key="em">
<span class="chip email" :title="em"><span x-text="em"></span></span>
<a :href="'mailto:'+em" class="chip email" :title="em"><span x-text="em"></span></a>
</template>
<template x-for="ph in (c.phones||[]).slice(0,1)" :key="ph">
<span class="chip phone" :title="ph">📞 <span x-text="ph"></span></span>
<a :href="'tel:'+ph" class="chip phone" :title="ph">📞 <span x-text="ph"></span></a>
</template>
<template x-for="wa in (c.whatsapp||[]).slice(0,1)" :key="wa">
<span class="chip wa" title="WhatsApp">💬 WA</span>
<a :href="wa" target="_blank" class="chip wa" title="WhatsApp">💬</a>
</template>
<template x-if="(c.social||[]).length>0">
<span class="chip social" :title="(c.social||[]).join(', ')">📲 <span x-text="(c.social||[]).length"></span></span>
<template x-for="url in (c.social||[]).slice(0,4)" :key="url">
<a :href="url" target="_blank" :class="'sicon '+socialIconClass(url)" :title="url" x-text="socialIconLabel(url)"></a>
</template>
</div>
</td>
@@ -698,6 +712,30 @@ function app() {
try { return JSON.parse(raw); } catch(e) { return {}; }
},
socialIconClass(url) {
if(!url) return 'other';
const u = url.toLowerCase();
if(u.includes('facebook.com') || u.includes('fb.com')) return 'fb';
if(u.includes('instagram.com')) return 'ig';
if(u.includes('linkedin.com')) return 'li';
if(u.includes('twitter.com') || u.includes('x.com')) return 'tw';
if(u.includes('tiktok.com')) return 'tt';
if(u.includes('youtube.com') || u.includes('youtu.be')) return 'yt';
return 'other';
},
socialIconLabel(url) {
if(!url) return '?';
const u = url.toLowerCase();
if(u.includes('facebook.com') || u.includes('fb.com')) return 'f';
if(u.includes('instagram.com')) return 'ig';
if(u.includes('linkedin.com')) return 'in';
if(u.includes('twitter.com') || u.includes('x.com')) return '𝕏';
if(u.includes('tiktok.com')) return 'tt';
if(u.includes('youtube.com') || u.includes('youtu.be')) return '▶';
return '↗';
},
parseSignals(raw) {
if(!raw) return 'No signals';
try { return JSON.parse(raw).join('\n'); } catch(e) { return raw; }