[31388f42] Restructure Lead Engine UI: vertical layout with 2-column top section for source data
This commit is contained in:
@@ -129,120 +129,105 @@ if not df.empty:
|
||||
except:
|
||||
pass
|
||||
|
||||
with st.expander(f"{date_str} | {row['company_name']} ({row['status']})"):
|
||||
# Quality Warning
|
||||
with st.expander(f"{date_str} | {row['company_name']}"):
|
||||
# Metadata Parsing
|
||||
meta = {}
|
||||
if row.get('lead_metadata'):
|
||||
try: meta = json.loads(row['lead_metadata'])
|
||||
except: pass
|
||||
|
||||
# --- TOP SECTION: QUALITY WARNING ---
|
||||
if meta.get('is_low_quality'):
|
||||
st.warning("⚠️ Low Quality Lead detected (Free-mail or missing company). Use for reclamation if applicable.")
|
||||
st.warning("⚠️ **Low Quality Lead detected** (Free-mail or missing company).")
|
||||
|
||||
# --- SECTION 1: LEAD INFO (2 Columns) ---
|
||||
st.markdown("### 📋 Lead Data")
|
||||
c1, c2 = st.columns(2)
|
||||
|
||||
# --- Left Column: Lead Data ---
|
||||
c1.write(f"**Contact:** {row['contact_name']}")
|
||||
c1.write(f"**Email:** {row['email']}")
|
||||
|
||||
# Metadata Display
|
||||
meta = {}
|
||||
if row.get('lead_metadata'):
|
||||
try:
|
||||
meta = json.loads(row['lead_metadata'])
|
||||
except:
|
||||
pass
|
||||
|
||||
role = meta.get('role')
|
||||
if role:
|
||||
c1.info(f"**Role:** {role}")
|
||||
else:
|
||||
if c1.button("🔍 Find Role (SerpAPI)", key=f"role_{row['id']}"):
|
||||
from enrich import enrich_contact_role
|
||||
with st.spinner("Searching LinkedIn via Google..."):
|
||||
found_role = enrich_contact_role(row)
|
||||
if found_role:
|
||||
st.success(f"Found: {found_role}")
|
||||
st.rerun()
|
||||
else:
|
||||
st.error("No role found.")
|
||||
|
||||
if meta:
|
||||
c1.write("---")
|
||||
c1.write(f"**Area:** {meta.get('area', '-')}")
|
||||
c1.write(f"**Purpose:** {meta.get('purpose', '-')}")
|
||||
c1.write(f"**Location:** {meta.get('zip', '')} {meta.get('city', '')}")
|
||||
|
||||
# Manual Sync Button for individual lead
|
||||
if row['status'] == 'new':
|
||||
if c1.button("🚀 Sync to Company Explorer", key=f"sync_single_{row['id']}"):
|
||||
with st.spinner("Processing lead (Role + CE Sync)..."):
|
||||
res = sync_single_lead(row['id'])
|
||||
if res.get('status') != 'error':
|
||||
st.success("Lead synced successfully!")
|
||||
st.rerun()
|
||||
else:
|
||||
st.error(f"Sync failed: {res.get('message')}")
|
||||
else:
|
||||
if c1.button("🔄 Reset Status to New", key=f"reset_{row['id']}"):
|
||||
reset_lead(row['id'])
|
||||
st.toast("Lead status reset.")
|
||||
st.rerun()
|
||||
|
||||
with c1.expander("Show Original Email Content"):
|
||||
st.text(clean_html_to_text(row['raw_body']))
|
||||
if st.checkbox("Show Raw HTML", key=f"raw_{row['id']}"):
|
||||
st.code(row['raw_body'], language="html")
|
||||
|
||||
# --- Right Column: Enrichment & Response ---
|
||||
enrichment = json.loads(row['enrichment_data']) if row['enrichment_data'] else {}
|
||||
|
||||
if enrichment:
|
||||
c2.write("--- Integration Status ---")
|
||||
ce_id = enrichment.get('ce_id')
|
||||
with c1:
|
||||
st.write(f"**Contact:** {row['contact_name']}")
|
||||
st.write(f"**Email:** {row['email']}")
|
||||
|
||||
if ce_id:
|
||||
c2.success(f"✅ Linked to Company Explorer (ID: {ce_id})")
|
||||
|
||||
# CE Data Display
|
||||
ce_data = enrichment.get('ce_data', {})
|
||||
# Support both names (CE uses industry_ai internally)
|
||||
vertical = ce_data.get('industry_ai') or ce_data.get('vertical')
|
||||
summary = ce_data.get('research_dossier') or ce_data.get('summary')
|
||||
|
||||
if vertical and vertical != 'None':
|
||||
c2.info(f"**Industry:** {vertical}")
|
||||
else:
|
||||
c2.warning("Industry Analysis pending...")
|
||||
|
||||
if summary:
|
||||
with c2.expander("Show Analysis Summary"):
|
||||
st.write(summary)
|
||||
|
||||
# Refresh Button
|
||||
if c2.button("🔄 Refresh Analysis Data", key=f"refresh_{row['id']}"):
|
||||
with st.spinner("Fetching latest data from Company Explorer..."):
|
||||
new_data = refresh_ce_data(row['id'], ce_id)
|
||||
st.toast("Data refreshed!")
|
||||
st.rerun()
|
||||
|
||||
# Generate Reply Button (only if we have data)
|
||||
c2.write("--- Response Generation ---")
|
||||
if c2.button("✨ Generate Expert Reply", key=f"gen_{row['id']}"):
|
||||
with st.spinner("Generating draft with Gemini..."):
|
||||
# Prepare lead data dict from row
|
||||
lead_dict = row.to_dict()
|
||||
draft = generate_email_draft(lead_dict, ce_data)
|
||||
# Store draft in session state to persist
|
||||
st.session_state[f"draft_{row['id']}"] = draft
|
||||
|
||||
# Display Draft
|
||||
if f"draft_{row['id']}" in st.session_state:
|
||||
c2.text_area("Draft Email", value=st.session_state[f"draft_{row['id']}"], height=300)
|
||||
|
||||
role = meta.get('role')
|
||||
if role:
|
||||
st.info(f"**Role:** {role}")
|
||||
else:
|
||||
c2.warning("⚠️ Not yet synced or failed")
|
||||
c2.info(f"Log: {enrichment.get('message')}")
|
||||
if st.button("🔍 Find Role", key=f"role_{row['id']}"):
|
||||
from enrich import enrich_contact_role
|
||||
with st.spinner("Searching..."):
|
||||
found_role = enrich_contact_role(row)
|
||||
if found_role: st.success(f"Found: {found_role}"); st.rerun()
|
||||
else: st.error("No role found.")
|
||||
|
||||
with c2:
|
||||
st.write(f"**Area:** {meta.get('area', '-')}")
|
||||
st.write(f"**Purpose:** {meta.get('purpose', '-')}")
|
||||
st.write(f"**Location:** {meta.get('zip', '')} {meta.get('city', '')}")
|
||||
|
||||
with st.expander("Original Body Preview"):
|
||||
st.text(clean_html_to_text(row['raw_body']))
|
||||
if st.checkbox("Show HTML", key=f"raw_{row['id']}"):
|
||||
st.code(row['raw_body'], language="html")
|
||||
|
||||
st.divider()
|
||||
|
||||
# --- SECTION 2: INTELLIGENCE (CE) ---
|
||||
st.markdown("### 🔍 Intelligence (CE)")
|
||||
enrichment = json.loads(row['enrichment_data']) if row['enrichment_data'] else {}
|
||||
ce_id = enrichment.get('ce_id')
|
||||
|
||||
if ce_id:
|
||||
st.success(f"✅ Linked to Company Explorer (ID: {ce_id})")
|
||||
ce_data = enrichment.get('ce_data', {})
|
||||
|
||||
vertical = ce_data.get('industry_ai') or ce_data.get('vertical')
|
||||
summary = ce_data.get('research_dossier') or ce_data.get('summary')
|
||||
|
||||
intel_col1, intel_col2 = st.columns([1, 2])
|
||||
with intel_col1:
|
||||
if vertical and vertical != 'None':
|
||||
st.info(f"**Industry:** {vertical}")
|
||||
else:
|
||||
st.warning("Industry Analysis pending...")
|
||||
|
||||
if st.button("🔄 Refresh CE Data", key=f"refresh_{row['id']}"):
|
||||
with st.spinner("Fetching..."):
|
||||
refresh_ce_data(row['id'], ce_id)
|
||||
st.rerun()
|
||||
|
||||
with intel_col2:
|
||||
if summary:
|
||||
with st.expander("Show AI Research Dossier", expanded=True):
|
||||
st.write(summary)
|
||||
else:
|
||||
st.warning("⚠️ Not synced with Company Explorer yet")
|
||||
if st.button("🚀 Sync to Company Explorer", key=f"sync_single_{row['id']}"):
|
||||
with st.spinner("Syncing..."):
|
||||
sync_single_lead(row['id'])
|
||||
st.rerun()
|
||||
|
||||
st.divider()
|
||||
|
||||
# --- SECTION 3: RESPONSE DRAFT ---
|
||||
st.markdown("### ✉️ Response Draft")
|
||||
if row['status'] != 'new' and ce_id:
|
||||
if st.button("✨ Generate Expert Reply", key=f"gen_{row['id']}", type="primary"):
|
||||
with st.spinner("Writing email..."):
|
||||
ce_data = enrichment.get('ce_data', {})
|
||||
draft = generate_email_draft(row.to_dict(), ce_data)
|
||||
st.session_state[f"draft_{row['id']}"] = draft
|
||||
|
||||
if f"draft_{row['id']}" in st.session_state:
|
||||
st.text_area("Email Entwurf", value=st.session_state[f"draft_{row['id']}"], height=400)
|
||||
st.button("📋 Copy to Clipboard", key=f"copy_{row['id']}", on_click=lambda: st.write("Copy functionality simulated"))
|
||||
else:
|
||||
st.info("Sync with Company Explorer first to generate a response.")
|
||||
|
||||
if row['status'] != 'new':
|
||||
st.markdown("---")
|
||||
if st.button("🔄 Reset Lead Status", key=f"reset_{row['id']}", help="Back to 'new' status"):
|
||||
reset_lead(row['id'])
|
||||
st.rerun()
|
||||
|
||||
else:
|
||||
st.info("No leads found. Click 'Ingest Emails' in the sidebar.")
|
||||
|
||||
Reference in New Issue
Block a user