feat: richer B2B assessment — legal page scraping, full contacts, summary
beauty_ai.py: - Add _scrape_legal_pages(): fetches /aviso-legal, /politica-de-privacidad, /privacidad, /quienes-somos, /legal in parallel — Spanish aviso legal pages legally contain razón social, CIF/NIF, address and a contact email; legal snippet passed to AI so it can identify the registered company name - Rewrite _build_beauty_prompt(): full technical profile (SSL, analytics, CMS, load time, word count, GDPR, mobile), all contact channels merged from both site_analyzer and legal pages, updated assessment rules with clearer HOT/WARM criteria, 700-char search results, richer portfolio portfolio context - New JSON schema fields: summary (executive description), pitch_angle (one Spanish hook sentence), all_contacts dict (emails/phones/whatsapp/social full lists), best_contact_channel, best_contact_value, partnership_signals, revenue_estimate; outreach_email is now a complete ready-to-send email - max_output_tokens raised from 2000 → 4000 - Contact merge: all_contacts populated from both site_analyzer and legal pages; top-level contact_* fields filled from merged data as fallback - Run DDG search and legal page scraping in parallel (no extra wall-clock cost) index.html (Pipeline): - Business Summary panel with pitch_angle as accent subtitle - Full all_contacts display: all emails (mailto links), all phones, all WhatsApp (green links), all social profiles (shortened display) - partnership_signals chips alongside brand detection - outreach_notes shown in amber at bottom of contact panel - best_contact_channel chip in contact header - Table contact column now shows best_contact_value if available Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -340,7 +340,7 @@ textarea{width:100%;resize:vertical;font-family:monospace;font-size:12px}
|
||||
<span x-show="!((row._beauty||{}).dist_matches||[]).length" style="color:var(--muted)">—</span>
|
||||
</td>
|
||||
<td style="font-size:11px;max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"
|
||||
x-text="(row._beauty||{}).contact_email||row.emails||'—'"></td>
|
||||
x-text="(row._beauty||{}).best_contact_value||(row._beauty||{}).contact_email||row.emails||'—'"></td>
|
||||
<td @click.stop style="white-space:nowrap;display:flex;gap:4px">
|
||||
<button class="btn-secondary btn-sm" @click="copyOutreach(row)">Copy email</button>
|
||||
<button class="btn-secondary btn-sm" @click="reassessOne(row.domain)" title="Re-run B2B assessment">↺</button>
|
||||
@@ -350,14 +350,18 @@ textarea{width:100%;resize:vertical;font-family:monospace;font-size:12px}
|
||||
<tr class="detail-row" x-show="expandedLead===row.domain" @click="expandedLead=null" style="cursor:pointer">
|
||||
<td colspan="8">
|
||||
<div class="detail-grid" @click.stop>
|
||||
<div class="detail-box">
|
||||
<h4>B2B Proposal</h4>
|
||||
<p x-text="(row._beauty||{}).b2b_proposal||'—'"></p>
|
||||
</div>
|
||||
<div class="detail-box">
|
||||
<h4>Lead Reasoning</h4>
|
||||
<p x-text="(row._beauty||{}).lead_reasoning||'—'"></p>
|
||||
|
||||
<!-- Summary + pitch -->
|
||||
<div class="detail-box" style="grid-column:1/-1;background:rgba(232,121,160,.06);border-color:rgba(232,121,160,.2)">
|
||||
<h4 style="display:flex;align-items:center;gap:8px">
|
||||
Business Summary
|
||||
<span x-show="(row._beauty||{}).pitch_angle" style="color:var(--accent);font-size:11px;font-weight:400;font-style:italic" x-text="'→ '+((row._beauty||{}).pitch_angle||'')"></span>
|
||||
</h4>
|
||||
<p x-text="(row._beauty||{}).summary||(row._beauty||{}).b2b_proposal||'—'"></p>
|
||||
<p x-show="(row._beauty||{}).lead_reasoning" style="margin-top:6px;font-size:11px;color:var(--muted)" x-text="(row._beauty||{}).lead_reasoning"></p>
|
||||
</div>
|
||||
|
||||
<!-- Outreach email -->
|
||||
<div class="detail-box" style="grid-column:1/-1">
|
||||
<h4 style="display:flex;align-items:center;gap:8px">
|
||||
Outreach Email
|
||||
@@ -366,35 +370,81 @@ textarea{width:100%;resize:vertical;font-family:monospace;font-size:12px}
|
||||
</h4>
|
||||
<p style="white-space:pre-wrap;font-size:11px;color:var(--text);margin-top:6px;line-height:1.6" x-text="(row._beauty||{}).outreach_email||'—'"></p>
|
||||
</div>
|
||||
|
||||
<!-- Brands detected -->
|
||||
<div class="detail-box">
|
||||
<h4>Brands Detected on Site</h4>
|
||||
<p style="font-size:11px">
|
||||
<template x-for="b in ((row._beauty||{}).detected_brands||[]).slice(0,30)" :key="b">
|
||||
<template x-for="b in ((row._beauty||{}).detected_brands||[]).slice(0,40)" :key="b">
|
||||
<span class="chip" x-text="b"></span>
|
||||
</template>
|
||||
<span x-show="!((row._beauty||{}).detected_brands||[]).length" style="color:var(--muted)">None detected in scraped text</span>
|
||||
<span x-show="!((row._beauty||{}).detected_brands||[]).length" style="color:var(--muted)">None detected</span>
|
||||
</p>
|
||||
<template x-if="((row._beauty||{}).partnership_signals||[]).length>0">
|
||||
<p style="margin-top:8px;font-size:11px">
|
||||
<span style="color:var(--muted)">Signals: </span>
|
||||
<template x-for="s in ((row._beauty||{}).partnership_signals||[])" :key="s">
|
||||
<span class="chip chip-match" x-text="s"></span>
|
||||
</template>
|
||||
</p>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Full contact details -->
|
||||
<div class="detail-box">
|
||||
<h4>Contact Details</h4>
|
||||
<p style="font-size:12px;line-height:1.8">
|
||||
<template x-if="(row._beauty||{}).contact_email">
|
||||
<span>Email: <a :href="'mailto:'+(row._beauty||{}).contact_email" x-text="(row._beauty||{}).contact_email"></a><br></span>
|
||||
<h4 style="display:flex;align-items:center;gap:8px">
|
||||
Contact Details
|
||||
<template x-if="(row._beauty||{}).best_contact_channel">
|
||||
<span class="chip chip-match" x-text="'best: '+((row._beauty||{}).best_contact_channel||'')"></span>
|
||||
</template>
|
||||
<template x-if="(row._beauty||{}).contact_phone">
|
||||
<span>Phone: <span x-text="(row._beauty||{}).contact_phone"></span><br></span>
|
||||
</h4>
|
||||
<div style="font-size:12px;line-height:1.9">
|
||||
<!-- All emails -->
|
||||
<template x-if="((row._beauty||{}).all_contacts||{}).emails?.length">
|
||||
<div>
|
||||
<span style="color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.04em">Emails</span><br>
|
||||
<template x-for="em in ((row._beauty||{}).all_contacts||{}).emails||[]" :key="em">
|
||||
<span><a :href="'mailto:'+em" x-text="em" style="display:inline-block;margin-right:8px"></a></span>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="(row._beauty||{}).contact_whatsapp">
|
||||
<span>WhatsApp: <a :href="(row._beauty||{}).contact_whatsapp" target="_blank" x-text="(row._beauty||{}).contact_whatsapp"></a><br></span>
|
||||
<!-- All phones -->
|
||||
<template x-if="((row._beauty||{}).all_contacts||{}).phones?.length">
|
||||
<div style="margin-top:4px">
|
||||
<span style="color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.04em">Phones</span><br>
|
||||
<template x-for="ph in ((row._beauty||{}).all_contacts||{}).phones||[]" :key="ph">
|
||||
<span x-text="ph" style="display:inline-block;margin-right:8px"></span>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="(row._beauty||{}).contact_social">
|
||||
<span style="color:var(--muted)">Social: <span x-text="(row._beauty||{}).contact_social"></span><br></span>
|
||||
<!-- WhatsApp -->
|
||||
<template x-if="((row._beauty||{}).all_contacts||{}).whatsapp?.length">
|
||||
<div style="margin-top:4px">
|
||||
<span style="color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.04em">WhatsApp</span><br>
|
||||
<template x-for="wa in ((row._beauty||{}).all_contacts||{}).whatsapp||[]" :key="wa">
|
||||
<a :href="wa" target="_blank" x-text="wa" style="display:inline-block;margin-right:8px;color:var(--success)"></a>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="row.emails && !(row._beauty||{}).contact_email">
|
||||
<span style="color:var(--muted);font-size:11px">On-site: <span x-text="row.emails"></span></span>
|
||||
<!-- Social -->
|
||||
<template x-if="((row._beauty||{}).all_contacts||{}).social?.length">
|
||||
<div style="margin-top:4px">
|
||||
<span style="color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.04em">Social</span><br>
|
||||
<template x-for="soc in ((row._beauty||{}).all_contacts||{}).social||[]" :key="soc">
|
||||
<a :href="soc" target="_blank" x-text="soc.replace('https://','').replace('www.','').split('/').slice(0,2).join('/')" style="display:inline-block;margin-right:8px;color:var(--info)"></a>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</p>
|
||||
<!-- Fallback if all_contacts not set (older assessments) -->
|
||||
<template x-if="!(row._beauty||{}).all_contacts && (row._beauty||{}).contact_email">
|
||||
<div>Email: <a :href="'mailto:'+(row._beauty||{}).contact_email" x-text="(row._beauty||{}).contact_email"></a></div>
|
||||
</template>
|
||||
</div>
|
||||
<template x-if="(row._beauty||{}).outreach_notes">
|
||||
<p style="margin-top:8px;font-size:11px;color:var(--warn);border-top:1px solid var(--border);padding-top:6px" x-text="(row._beauty||{}).outreach_notes"></p>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user