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:
@@ -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; }
|
||||
|
||||
Reference in New Issue
Block a user