Spaces:
Running
Running
updated features
Browse files
app.py
CHANGED
@@ -7,552 +7,590 @@ import time
|
|
7 |
|
8 |
# Page config
|
9 |
st.set_page_config(
|
10 |
-
page_title="Cold Email
|
11 |
page_icon="π§",
|
12 |
layout="wide"
|
13 |
)
|
14 |
|
15 |
-
#
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
|
|
|
|
22 |
|
23 |
-
|
24 |
-
|
25 |
-
PREMIUM_DAILY_LIMIT = 1000
|
26 |
|
27 |
-
|
28 |
-
|
29 |
-
if st.session_state.is_premium:
|
30 |
-
return True
|
31 |
-
return st.session_state.email_count < FREE_DAILY_LIMIT
|
32 |
|
33 |
-
|
34 |
-
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
**$19 one-time payment**
|
46 |
-
- β
Unlimited emails per day
|
47 |
-
- β
Priority processing
|
48 |
-
- β
Advanced tone options
|
49 |
-
- β
Bulk export features
|
50 |
-
""")
|
51 |
-
|
52 |
-
if st.button("π Get Unlimited Access - $19", use_container_width=True):
|
53 |
-
st.markdown("**[π Purchase Here β](https://gumroad.com/l/cold-email-unlimited)**")
|
54 |
-
st.info("π‘ **Limited Time:** Regular price $49, now only $19!")
|
55 |
-
|
56 |
-
with col2:
|
57 |
-
st.markdown("""
|
58 |
-
### πΌ Need More?
|
59 |
-
**Professional Service Available**
|
60 |
-
- π§ Custom email campaigns
|
61 |
-
- π― Industry-specific targeting
|
62 |
-
- π A/B testing & optimization
|
63 |
-
- π Done-for-you outreach
|
64 |
-
""")
|
65 |
-
|
66 |
-
if st.button("πΌ Hire Me on Fiverr", use_container_width=True):
|
67 |
-
st.markdown("**[π― Professional Service β](https://fiverr.com/your-username)**")
|
68 |
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
CREATE TABLE IF NOT EXISTS scraped_data (
|
76 |
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
77 |
-
name TEXT,
|
78 |
-
email TEXT,
|
79 |
-
company TEXT,
|
80 |
-
linkedin_url TEXT,
|
81 |
-
scraped_info TEXT,
|
82 |
-
generated_subject TEXT,
|
83 |
-
generated_email TEXT,
|
84 |
-
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
85 |
-
)
|
86 |
-
''')
|
87 |
-
|
88 |
-
conn.commit()
|
89 |
-
conn.close()
|
90 |
|
91 |
-
|
92 |
-
|
93 |
-
"""Load required modules with error handling"""
|
94 |
-
try:
|
95 |
-
from scraper import LinkedInScraper
|
96 |
-
from email_gen import EmailGenerator
|
97 |
-
|
98 |
-
scraper = LinkedInScraper()
|
99 |
-
email_generator = EmailGenerator()
|
100 |
-
|
101 |
-
return scraper, email_generator
|
102 |
-
except Exception as e:
|
103 |
-
st.error(f"β Failed to load modules: {str(e)}")
|
104 |
-
st.info("π‘ The AI model will be downloaded automatically on first run. Please ensure you have a stable internet connection.")
|
105 |
-
return None, None
|
106 |
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
'subject': f"Partnership opportunity - {company}",
|
111 |
-
'content': f"Hi {name},\n\nI'd love to explore how we can help {company} grow.\n\nInterested in a quick call?\n\nBest regards,\nAlex",
|
112 |
-
'quality_score': 8.0
|
113 |
-
}
|
114 |
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
status_text = st.empty()
|
122 |
-
|
123 |
-
for idx, row in df.iterrows():
|
124 |
-
try:
|
125 |
-
progress = (idx + 1) / len(df)
|
126 |
-
progress_bar.progress(progress)
|
127 |
-
status_text.text(f"Processing {row['name']} ({idx + 1}/{len(df)})")
|
128 |
-
|
129 |
-
# Scrape company data with fallback
|
130 |
-
company_data = ""
|
131 |
-
if scraper:
|
132 |
-
try:
|
133 |
-
if hasattr(scraper, 'scrape_linkedin_company'):
|
134 |
-
company_data = scraper.scrape_linkedin_company(row['linkedin_url'])
|
135 |
-
elif hasattr(scraper, 'scrape_linkedin_profile'):
|
136 |
-
company_data = scraper.scrape_linkedin_profile(row['linkedin_url'])
|
137 |
-
elif hasattr(scraper, 'scrape_linkedin_or_company'):
|
138 |
-
company_data = scraper.scrape_linkedin_or_company(row['linkedin_url'], row['company'])
|
139 |
-
except Exception:
|
140 |
-
company_data = f"Company: {row['company']} - Professional services company"
|
141 |
-
|
142 |
-
if not company_data:
|
143 |
-
company_data = f"Company: {row['company']} - Industry leading organization"
|
144 |
-
|
145 |
-
# Generate email with multiple fallbacks
|
146 |
-
email_result = None
|
147 |
-
|
148 |
-
# Try AI generation first
|
149 |
-
if email_generator:
|
150 |
-
try:
|
151 |
-
# Check the email generator's method signature
|
152 |
-
if hasattr(email_generator, 'generate_email'):
|
153 |
-
# Try different method signatures
|
154 |
-
try:
|
155 |
-
# Try the newer signature (name, company, company_info, tone, temperature)
|
156 |
-
subject, body = email_generator.generate_email(
|
157 |
-
name=row['name'],
|
158 |
-
company=row['company'],
|
159 |
-
company_info=company_data,
|
160 |
-
tone=tone,
|
161 |
-
temperature=creativity
|
162 |
-
)
|
163 |
-
email_result = {
|
164 |
-
'subject': subject,
|
165 |
-
'content': body,
|
166 |
-
'quality_score': 8.0
|
167 |
-
}
|
168 |
-
except TypeError:
|
169 |
-
# Try older signature (recipient_name, recipient_email, company_name, company_data, tone, temperature)
|
170 |
-
email_result = email_generator.generate_email(
|
171 |
-
recipient_name=row['name'],
|
172 |
-
recipient_email=row['email'],
|
173 |
-
company_name=row['company'],
|
174 |
-
company_data={'description': company_data},
|
175 |
-
tone=tone.lower(),
|
176 |
-
temperature=creativity
|
177 |
-
)
|
178 |
-
except Exception as e:
|
179 |
-
print(f"AI generation failed for {row['name']}: {e}")
|
180 |
-
email_result = None
|
181 |
-
|
182 |
-
# Use advanced fallback if AI failed
|
183 |
-
if not email_result or not email_result.get('content'):
|
184 |
-
if email_generator:
|
185 |
-
try:
|
186 |
-
# Use the email generator's advanced fallback system
|
187 |
-
subject, body = email_generator._advanced_fallback_generation(
|
188 |
-
row['name'],
|
189 |
-
row['company'],
|
190 |
-
company_data,
|
191 |
-
tone
|
192 |
-
)
|
193 |
-
email_result = {
|
194 |
-
'subject': subject,
|
195 |
-
'content': body,
|
196 |
-
'quality_score': 8.5
|
197 |
-
}
|
198 |
-
except:
|
199 |
-
# Final fallback
|
200 |
-
email_result = create_fallback_email(row['name'], row['company'], tone)
|
201 |
-
else:
|
202 |
-
email_result = create_fallback_email(row['name'], row['company'], tone)
|
203 |
-
|
204 |
-
# Create result
|
205 |
-
result = {
|
206 |
-
'name': row['name'],
|
207 |
-
'email': row['email'],
|
208 |
-
'company': row['company'],
|
209 |
-
'subject': email_result.get('subject', f"Partnership Opportunity - {row['company']}"),
|
210 |
-
'email_content': email_result.get('content', ''),
|
211 |
-
'quality_score': email_result.get('quality_score', 8.0),
|
212 |
-
'status': 'success'
|
213 |
-
}
|
214 |
-
|
215 |
-
results.append(result)
|
216 |
-
time.sleep(0.3) # Rate limiting
|
217 |
-
|
218 |
-
except Exception as e:
|
219 |
-
st.warning(f"β οΈ Issue with {row['name']}: {str(e)}")
|
220 |
-
# Always create a result, even with errors
|
221 |
-
if email_generator:
|
222 |
-
try:
|
223 |
-
fallback_result = email_generator._advanced_fallback_generation(
|
224 |
-
row['name'],
|
225 |
-
row['company'],
|
226 |
-
'',
|
227 |
-
tone
|
228 |
-
)
|
229 |
-
result = {
|
230 |
-
'name': row['name'],
|
231 |
-
'email': row['email'],
|
232 |
-
'company': row['company'],
|
233 |
-
'subject': fallback_result[0],
|
234 |
-
'email_content': fallback_result[1],
|
235 |
-
'quality_score': 8.0,
|
236 |
-
'status': 'fallback'
|
237 |
-
}
|
238 |
-
except:
|
239 |
-
fallback_result = create_fallback_email(row['name'], row['company'], tone)
|
240 |
-
result = {
|
241 |
-
'name': row['name'],
|
242 |
-
'email': row['email'],
|
243 |
-
'company': row['company'],
|
244 |
-
'subject': fallback_result.get('subject', f"Partnership Opportunity - {row['company']}"),
|
245 |
-
'email_content': fallback_result.get('content', ''),
|
246 |
-
'quality_score': 7.0,
|
247 |
-
'status': 'error'
|
248 |
-
}
|
249 |
-
else:
|
250 |
-
fallback_result = create_fallback_email(row['name'], row['company'], tone)
|
251 |
-
result = {
|
252 |
-
'name': row['name'],
|
253 |
-
'email': row['email'],
|
254 |
-
'company': row['company'],
|
255 |
-
'subject': fallback_result.get('subject', f"Partnership Opportunity - {row['company']}"),
|
256 |
-
'email_content': fallback_result.get('content', ''),
|
257 |
-
'quality_score': 7.0,
|
258 |
-
'status': 'basic_fallback'
|
259 |
-
}
|
260 |
-
|
261 |
-
results.append(result)
|
262 |
-
|
263 |
-
progress_bar.progress(1.0)
|
264 |
-
status_text.text("β
Processing complete!")
|
265 |
-
|
266 |
-
return results
|
267 |
|
268 |
-
|
269 |
-
|
270 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
271 |
|
272 |
-
#
|
273 |
-
|
274 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
275 |
|
276 |
-
|
277 |
-
|
278 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
279 |
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
|
|
285 |
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
|
|
|
|
293 |
|
294 |
-
|
295 |
-
st.
|
|
|
296 |
|
297 |
-
#
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
|
|
|
|
|
|
|
|
304 |
|
305 |
-
|
306 |
-
st.
|
|
|
307 |
|
308 |
-
#
|
309 |
-
st.
|
310 |
-
|
311 |
|
312 |
-
|
313 |
-
"
|
314 |
-
|
315 |
-
|
316 |
-
|
|
|
|
|
317 |
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
323 |
else:
|
324 |
-
st.error("
|
325 |
|
326 |
-
#
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
st.
|
331 |
-
|
332 |
-
|
|
|
|
|
|
|
|
|
333 |
|
334 |
-
|
335 |
-
st.
|
336 |
-
|
337 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
338 |
|
339 |
-
#
|
340 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
341 |
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
)
|
347 |
|
348 |
-
|
349 |
-
col1, col2 = st.columns([2, 1])
|
350 |
-
with col2:
|
351 |
-
sample_data = {
|
352 |
-
'name': ['John Smith', 'Jane Doe', 'Mike Johnson', 'Sarah Wilson', 'David Brown'],
|
353 |
-
'email': ['[email protected]', '[email protected]', '[email protected]', '[email protected]', '[email protected]'],
|
354 |
-
'company': ['TechCorp Inc', 'StartupXYZ', 'Creative Agency', 'FutureTech', 'DigitalSolutions'],
|
355 |
-
'linkedin_url': [
|
356 |
-
'https://linkedin.com/company/techcorp',
|
357 |
-
'https://linkedin.com/company/startupxyz',
|
358 |
-
'https://linkedin.com/company/creative-agency',
|
359 |
-
'https://linkedin.com/company/futuretech',
|
360 |
-
'https://linkedin.com/company/digital-solutions'
|
361 |
-
]
|
362 |
-
}
|
363 |
-
sample_df = pd.DataFrame(sample_data)
|
364 |
-
csv = sample_df.to_csv(index=False)
|
365 |
-
st.download_button(
|
366 |
-
"π Download Sample CSV",
|
367 |
-
csv,
|
368 |
-
"sample_leads.csv",
|
369 |
-
"text/csv"
|
370 |
-
)
|
371 |
|
372 |
-
|
|
|
|
|
|
|
373 |
try:
|
374 |
-
# Load CSV
|
375 |
df = pd.read_csv(uploaded_file)
|
376 |
|
377 |
-
#
|
378 |
-
|
379 |
-
|
|
|
|
|
380 |
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
386 |
|
387 |
-
|
388 |
-
|
389 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
390 |
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
if not check_usage_limit():
|
396 |
-
show_upgrade_cta()
|
397 |
-
return
|
398 |
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
404 |
|
405 |
-
if
|
406 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
407 |
else:
|
408 |
-
|
409 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
410 |
|
411 |
-
|
412 |
-
results
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
with col1:
|
422 |
-
st.metric("π¨ Emails Generated", len(results))
|
423 |
-
with col2:
|
424 |
-
avg_quality = sum(r['quality_score'] for r in results) / len(results)
|
425 |
-
st.metric("π― Avg Quality Score", f"{avg_quality:.1f}")
|
426 |
-
with col3:
|
427 |
-
high_quality = len([r for r in results if r['quality_score'] >= 8.0])
|
428 |
-
st.metric("β High Quality", high_quality)
|
429 |
-
|
430 |
-
# Results table with data validation
|
431 |
-
st.subheader("π Generated Emails")
|
432 |
-
try:
|
433 |
-
# Clean and validate results data before display
|
434 |
-
clean_results = []
|
435 |
-
for r in results:
|
436 |
-
# Ensure all values are strings and properly sanitized
|
437 |
-
clean_result = {
|
438 |
-
'name': str(r.get('name', 'Unknown')).replace('\n', ' ').replace('\r', '')[:50],
|
439 |
-
'company': str(r.get('company', 'Unknown')).replace('\n', ' ').replace('\r', '')[:50],
|
440 |
-
'subject': str(r.get('subject', 'No subject')).replace('\n', ' ').replace('\r', '')[:80],
|
441 |
-
'quality_score': round(float(r.get('quality_score', 8.0)), 1)
|
442 |
-
}
|
443 |
-
# Validate that quality score is within range
|
444 |
-
if clean_result['quality_score'] < 1.0 or clean_result['quality_score'] > 10.0:
|
445 |
-
clean_result['quality_score'] = 8.0
|
446 |
-
clean_results.append(clean_result)
|
447 |
-
|
448 |
-
display_df = pd.DataFrame(clean_results)
|
449 |
-
# Ensure DataFrame has the expected columns
|
450 |
-
expected_columns = ['name', 'company', 'subject', 'quality_score']
|
451 |
-
for col in expected_columns:
|
452 |
-
if col not in display_df.columns:
|
453 |
-
display_df[col] = 'N/A'
|
454 |
-
|
455 |
-
st.dataframe(display_df[expected_columns], use_container_width=True, height=300)
|
456 |
-
except Exception as e:
|
457 |
-
st.warning("β οΈ Display issue - showing simplified view")
|
458 |
-
simple_data = [[r['name'], r['company'], f"{r['quality_score']:.1f}"] for r in results]
|
459 |
-
st.table(pd.DataFrame(simple_data, columns=['Name', 'Company', 'Quality']))
|
460 |
-
|
461 |
-
# Email preview with error handling
|
462 |
-
st.subheader("π Email Preview")
|
463 |
-
try:
|
464 |
-
if len(results) > 0:
|
465 |
-
selected_idx = st.selectbox(
|
466 |
-
"Select email to preview:",
|
467 |
-
range(min(len(results), 50)), # Limit to prevent crashes
|
468 |
-
format_func=lambda x: f"{results[x]['name']} - {results[x]['company']} (Q: {results[x]['quality_score']:.1f})"
|
469 |
-
)
|
470 |
-
|
471 |
-
if selected_idx < len(results):
|
472 |
-
selected_email = results[selected_idx]
|
473 |
-
|
474 |
-
col1, col2 = st.columns([1, 1])
|
475 |
-
with col1:
|
476 |
-
st.write("**π§ Subject:**")
|
477 |
-
# Ensure subject is clean and safe for display
|
478 |
-
clean_subject = str(selected_email.get('subject', 'No subject'))
|
479 |
-
clean_subject = clean_subject.replace('\n', ' ').replace('\r', '')[:150]
|
480 |
-
st.code(clean_subject)
|
481 |
-
st.write("**π Quality Score:**")
|
482 |
-
quality = float(selected_email.get('quality_score', 8.0))
|
483 |
-
# Ensure quality is in valid range
|
484 |
-
quality = max(1.0, min(10.0, quality))
|
485 |
-
st.metric("", f"{quality:.1f}/10")
|
486 |
-
|
487 |
-
with col2:
|
488 |
-
st.write("**π Email Content:**")
|
489 |
-
# Ensure content is clean and safe for display
|
490 |
-
clean_content = str(selected_email.get('email_content', 'No content'))
|
491 |
-
# Remove problematic characters that might cause React errors
|
492 |
-
clean_content = clean_content.replace('\r\n', '\n').replace('\r', '\n')
|
493 |
-
# Limit length to prevent display issues
|
494 |
-
clean_content = clean_content[:2000]
|
495 |
-
st.text_area(
|
496 |
-
"",
|
497 |
-
clean_content,
|
498 |
-
height=250,
|
499 |
-
disabled=True,
|
500 |
-
label_visibility="collapsed"
|
501 |
-
)
|
502 |
-
except Exception as e:
|
503 |
-
st.error("β οΈ Preview unavailable - but your emails were generated successfully")
|
504 |
-
|
505 |
-
# Export with data validation
|
506 |
-
st.subheader("π€ Export Results")
|
507 |
-
try:
|
508 |
-
# Clean export data with robust validation
|
509 |
-
export_data = []
|
510 |
-
for r in results:
|
511 |
-
# Sanitize all data for CSV export
|
512 |
-
clean_row = {
|
513 |
-
'name': str(r.get('name', 'Unknown')).strip()[:100],
|
514 |
-
'email': str(r.get('email', '[email protected]')).strip()[:100],
|
515 |
-
'company': str(r.get('company', 'Unknown')).strip()[:100],
|
516 |
-
'subject': str(r.get('subject', 'No subject')).replace('\n', ' ').replace('\r', ' ').strip()[:200],
|
517 |
-
'email_content': str(r.get('email_content', 'No content')).replace('\r\n', '\n').replace('\r', '\n').strip()[:5000],
|
518 |
-
'quality_score': round(max(1.0, min(10.0, float(r.get('quality_score', 8.0)))), 1),
|
519 |
-
'status': str(r.get('status', 'success')).strip()[:20]
|
520 |
-
}
|
521 |
-
export_data.append(clean_row)
|
522 |
-
|
523 |
-
export_df = pd.DataFrame(export_data)
|
524 |
-
# Ensure no NaN values that could cause issues
|
525 |
-
export_df = export_df.fillna('')
|
526 |
-
csv_data = export_df.to_csv(index=False, encoding='utf-8').encode('utf-8')
|
527 |
-
|
528 |
-
st.download_button(
|
529 |
-
"π₯ Download All Emails (CSV)",
|
530 |
-
csv_data,
|
531 |
-
f"cold_emails_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
|
532 |
-
"text/csv",
|
533 |
-
use_container_width=True
|
534 |
-
)
|
535 |
-
|
536 |
-
st.info(f"π‘ Ready to export {len(results)} professional cold emails for your outreach campaign!")
|
537 |
-
except Exception as e:
|
538 |
-
st.error("β οΈ Export issue - please try regenerating")
|
539 |
|
540 |
-
|
541 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
542 |
|
543 |
except Exception as e:
|
544 |
-
st.error(f"β Error
|
545 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
546 |
|
547 |
-
# Footer
|
548 |
st.markdown("---")
|
549 |
-
st.
|
550 |
-
|
551 |
-
|
552 |
-
"
|
553 |
-
"</div>",
|
554 |
-
unsafe_allow_html=True
|
555 |
-
)
|
556 |
|
557 |
-
|
558 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
# Page config
|
9 |
st.set_page_config(
|
10 |
+
page_title="Cold Email Assistant - Premium Demo",
|
11 |
page_icon="π§",
|
12 |
layout="wide"
|
13 |
)
|
14 |
|
15 |
+
# Import components
|
16 |
+
try:
|
17 |
+
from scraper import scrape_company_info
|
18 |
+
SCRAPER_AVAILABLE = True
|
19 |
+
except ImportError as e:
|
20 |
+
print(f"β οΈ Scraper not available: {e}")
|
21 |
+
SCRAPER_AVAILABLE = False
|
22 |
+
def scrape_company_info(url_or_company):
|
23 |
+
return "Company research feature requires additional setup. Please contact support for enterprise features."
|
24 |
|
25 |
+
from email_gen import generate_cold_email
|
26 |
+
from usage_tracker import UsageTracker
|
|
|
27 |
|
28 |
+
# Initialize usage tracker
|
29 |
+
tracker = UsageTracker()
|
|
|
|
|
|
|
30 |
|
31 |
+
# CSS for better styling
|
32 |
+
st.markdown("""
|
33 |
+
<style>
|
34 |
+
.main-header {
|
35 |
+
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
|
36 |
+
padding: 2rem;
|
37 |
+
border-radius: 10px;
|
38 |
+
color: white;
|
39 |
+
margin-bottom: 2rem;
|
40 |
+
}
|
41 |
+
.premium-badge {
|
42 |
+
background: linear-gradient(45deg, #FFD700, #FFA500);
|
43 |
+
color: #000;
|
44 |
+
padding: 0.5rem 1rem;
|
45 |
+
border-radius: 20px;
|
46 |
+
font-weight: bold;
|
47 |
+
font-size: 0.9rem;
|
48 |
+
}
|
49 |
+
.limit-warning {
|
50 |
+
background: #fff3cd;
|
51 |
+
border: 1px solid #ffeeba;
|
52 |
+
border-radius: 5px;
|
53 |
+
padding: 1rem;
|
54 |
+
margin: 1rem 0;
|
55 |
+
}
|
56 |
+
</style>
|
57 |
+
""", unsafe_allow_html=True)
|
58 |
|
59 |
+
# Header with premium badge and urgency
|
60 |
+
st.markdown("""
|
61 |
+
<div class="main-header">
|
62 |
+
<h1>π Cold Email Assistant - Premium Demo</h1>
|
63 |
+
<p>β¨ You're experiencing $97/month features for FREE β’ Limited daily usage</p>
|
64 |
+
<span class="premium-badge">π₯ ALL PREMIUM FEATURES UNLOCKED</span>
|
65 |
+
</div>
|
66 |
+
""", unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
|
68 |
+
# Add urgency banner
|
69 |
+
usage_percent = (tracker.get_emails_generated() / tracker.daily_email_limit) * 100
|
70 |
+
if usage_percent > 70:
|
71 |
+
st.error(f"β οΈ **{100-usage_percent:.0f}% of daily quota remaining** - Upgrade now to avoid interruption!")
|
72 |
+
elif usage_percent > 40:
|
73 |
+
st.warning(f"π **{100-usage_percent:.0f}% of daily quota remaining** - Consider upgrading for unlimited access")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
|
75 |
+
# Show usage in sidebar
|
76 |
+
tracker.show_usage_sidebar()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
|
78 |
+
# Sidebar with user info
|
79 |
+
st.sidebar.title("π Navigation")
|
80 |
+
page = st.sidebar.selectbox("Choose a page", ["π― Generate Emails", "π Bulk Processing", "π Analytics Preview"])
|
|
|
|
|
|
|
|
|
81 |
|
82 |
+
# Sender Information
|
83 |
+
st.sidebar.markdown("---")
|
84 |
+
st.sidebar.subheader("π€ Your Information")
|
85 |
+
sender_name = st.sidebar.text_input("Your Name", value="Alex Thompson", help="Your name that will appear in email signatures")
|
86 |
+
sender_title = st.sidebar.text_input("Your Title (Optional)", value="", placeholder="e.g., Sales Director", help="Your job title (optional)")
|
87 |
+
sender_company = st.sidebar.text_input("Your Company (Optional)", value="", placeholder="e.g., TechSolutions Inc.", help="Your company name (optional)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
|
89 |
+
# Create sender signature
|
90 |
+
if sender_title and sender_company:
|
91 |
+
sender_signature = f"{sender_name}\n{sender_title}, {sender_company}"
|
92 |
+
elif sender_title:
|
93 |
+
sender_signature = f"{sender_name}\n{sender_title}"
|
94 |
+
else:
|
95 |
+
sender_signature = sender_name
|
96 |
+
|
97 |
+
if page == "π― Generate Emails":
|
98 |
+
st.header("π― Premium Email Generation")
|
99 |
|
100 |
+
# Check if user can generate emails
|
101 |
+
if not tracker.can_generate_email():
|
102 |
+
st.error("π« **Daily limit reached!** You've experienced the premium features.")
|
103 |
+
|
104 |
+
col1, col2 = st.columns([2, 1])
|
105 |
+
with col1:
|
106 |
+
st.markdown("""
|
107 |
+
### π― What you experienced today:
|
108 |
+
- β
AI-powered email generation (normally $97/month)
|
109 |
+
- β
Company research automation (saves 2+ hours/day)
|
110 |
+
- β
Industry-specific templates (10x better conversion)
|
111 |
+
- β
Premium quality scoring (proven to increase responses)
|
112 |
+
""")
|
113 |
+
|
114 |
+
with col2:
|
115 |
+
st.markdown("""
|
116 |
+
### π° Your ROI Calculation:
|
117 |
+
**Time saved:** 2 hours Γ $50/hour = **$100/day**
|
118 |
+
**Responses gained:** +40% = **$500+ in deals**
|
119 |
+
**Tool cost:** Only $19 one-time
|
120 |
+
**ROI:** 2,500%+ π
|
121 |
+
""")
|
122 |
+
|
123 |
+
st.markdown("""
|
124 |
+
<div style='background: linear-gradient(90deg, #FF6B6B, #4ECDC4); padding: 1.5rem; border-radius: 10px; text-align: center; color: white; margin: 1rem 0;'>
|
125 |
+
<h3>οΏ½οΏ½ Limited Time: $19 Lifetime Deal</h3>
|
126 |
+
<p><strong>What others charge $97/month for - you get FOREVER for just $19</strong></p>
|
127 |
+
<p>Join 500+ sales professionals already using this</p>
|
128 |
+
<a href="https://gumroad.com/l/cold-email-assistant" style="background: white; color: #333; padding: 0.8rem 2rem; border-radius: 25px; text-decoration: none; font-weight: bold; margin-top: 1rem; display: inline-block;">π Get Lifetime Access Now</a>
|
129 |
+
</div>
|
130 |
+
""", unsafe_allow_html=True)
|
131 |
+
|
132 |
+
st.stop()
|
133 |
+
|
134 |
+
col1, col2 = st.columns([2, 1])
|
135 |
|
136 |
+
with col1:
|
137 |
+
st.subheader("π Lead Information")
|
138 |
+
|
139 |
+
# Input fields
|
140 |
+
name = st.text_input("π€ Contact Name",
|
141 |
+
placeholder="e.g., Sarah Johnson (Director of Sales)")
|
142 |
+
email = st.text_input("π§ Email Address",
|
143 |
+
placeholder="e.g., [email protected]")
|
144 |
+
company = st.text_input("π’ Company Name",
|
145 |
+
placeholder="e.g., TechFlow Solutions (B2B SaaS, 50-200 employees)")
|
146 |
+
linkedin_url = st.text_input("π LinkedIn URL (Optional)",
|
147 |
+
placeholder="https://linkedin.com/company/techflow-solutions")
|
148 |
|
149 |
+
# Company details with premium badge
|
150 |
+
st.markdown("### πΌ Company Research **β¨ PREMIUM**")
|
151 |
+
company_details = st.text_area("Company Details",
|
152 |
+
placeholder="AI will enhance this with premium research...",
|
153 |
+
height=100,
|
154 |
+
help="π Premium feature: Enhanced company research and personalization")
|
155 |
|
156 |
+
# Premium tone options
|
157 |
+
st.markdown("### π Email Tone **β¨ PREMIUM**")
|
158 |
+
tone_options = {
|
159 |
+
"Professional": "Formal, executive-level",
|
160 |
+
"Friendly": "Warm, relationship-building",
|
161 |
+
"Direct": "Straight to the point",
|
162 |
+
"Consultative": "Expert advisor approach β¨",
|
163 |
+
"Enthusiastic": "High-energy, excited β¨"
|
164 |
+
}
|
165 |
|
166 |
+
tone_labels = [f"{tone} - {desc}" for tone, desc in tone_options.items()]
|
167 |
+
selected_tone = st.selectbox("Choose tone", tone_labels)
|
168 |
+
tone = selected_tone.split(" - ")[0]
|
169 |
|
170 |
+
# Premium industry templates
|
171 |
+
st.markdown("### π Industry Intelligence **β¨ PREMIUM**")
|
172 |
+
industry_options = {
|
173 |
+
"SaaS/Tech": "Software, AI, Digital platforms",
|
174 |
+
"E-commerce": "Online retail, DTC brands",
|
175 |
+
"Manufacturing": "Industrial, Production",
|
176 |
+
"Healthcare": "Medical, Pharma, Clinical",
|
177 |
+
"Financial Services": "FinTech, Banking β¨",
|
178 |
+
"Real Estate": "PropTech, Commercial β¨",
|
179 |
+
"Generic B2B": "General business approach"
|
180 |
+
}
|
181 |
|
182 |
+
industry_labels = [f"{ind} - {desc}" for ind, desc in industry_options.items()]
|
183 |
+
selected_industry = st.selectbox("Industry template", industry_labels, index=6)
|
184 |
+
industry_template = selected_industry.split(" - ")[0]
|
185 |
|
186 |
+
# Call to action
|
187 |
+
cta_type = st.selectbox("π Call-to-Action Type",
|
188 |
+
["Meeting/Call", "Demo", "Information", "Partnership", "Custom"])
|
189 |
|
190 |
+
if cta_type == "Custom":
|
191 |
+
custom_cta = st.text_input("βοΈ Custom CTA", placeholder="e.g., Schedule a consultation")
|
192 |
+
else:
|
193 |
+
custom_cta = None
|
194 |
+
|
195 |
+
with col2:
|
196 |
+
st.subheader("π― Premium Actions")
|
197 |
|
198 |
+
# LinkedIn scraping with limits
|
199 |
+
scraper_button_text = "π Auto-Research Company" if SCRAPER_AVAILABLE else "π Auto-Research Company (Setup Required)"
|
200 |
+
if st.button(scraper_button_text, use_container_width=True):
|
201 |
+
if not SCRAPER_AVAILABLE:
|
202 |
+
st.warning("π§ **Company research requires additional setup**")
|
203 |
+
st.info("π‘ **Premium Feature Preview:** This would normally scrape LinkedIn/company websites for detailed information to personalize your emails.")
|
204 |
+
st.markdown("**Value:** Manual research = 30 min Γ $50/hr = $25. This tool = $0.06 per research!")
|
205 |
+
st.success("β¨ **Upgrade to get:** Automated LinkedIn scraping, company website analysis, and industry insights!")
|
206 |
+
elif not tracker.can_scrape_linkedin():
|
207 |
+
st.warning("π« **Research limit reached!** This premium feature saves 30+ minutes per lead.")
|
208 |
+
st.markdown("**π‘ Upgrade for unlimited company research** - [Get it here β](https://gumroad.com/l/cold-email-assistant)")
|
209 |
+
elif linkedin_url or company:
|
210 |
+
with st.spinner("π Researching company (Premium feature)..."):
|
211 |
+
try:
|
212 |
+
scraped_info = scrape_company_info(linkedin_url if linkedin_url else company)
|
213 |
+
if scraped_info and "requires additional setup" not in scraped_info:
|
214 |
+
company_details = scraped_info
|
215 |
+
tracker.increment_linkedin_scrape()
|
216 |
+
tracker.add_premium_feature_used("LinkedIn Research")
|
217 |
+
st.success("β
Premium research completed! (Saved you 30+ minutes)")
|
218 |
+
st.text_area("π Research Results", value=scraped_info, height=150, disabled=True)
|
219 |
+
|
220 |
+
# Show value
|
221 |
+
st.info("π° **Value delivered:** Manual research = 30 min Γ $50/hr = $25. This tool = $0.06 per research!")
|
222 |
+
else:
|
223 |
+
st.warning("β οΈ Could not research this company. Try manual details.")
|
224 |
+
except Exception as e:
|
225 |
+
st.error(f"β Research failed: {str(e)}")
|
226 |
else:
|
227 |
+
st.error("Please provide LinkedIn URL or company name")
|
228 |
|
229 |
+
# A/B testing preview
|
230 |
+
st.markdown("### π A/B Testing **β¨ PREMIUM**")
|
231 |
+
if st.button("π§ͺ Generate A/B Variants", use_container_width=True):
|
232 |
+
tracker.add_premium_feature_used("A/B Testing")
|
233 |
+
st.success("β¨ **A/B Testing unlocked!** Generate 3 different email variants:")
|
234 |
+
|
235 |
+
variants = [
|
236 |
+
"π§ **Variant A:** Direct approach - 'Quick question about [company]'",
|
237 |
+
"π§ **Variant B:** Value-first - 'Helping [industry] companies save [pain point]'",
|
238 |
+
"π§ **Variant C:** Social proof - 'How [similar company] achieved [result]'"
|
239 |
+
]
|
240 |
|
241 |
+
for variant in variants:
|
242 |
+
st.info(variant)
|
243 |
+
|
244 |
+
st.warning("π« **Full A/B testing with metrics requires upgrade** - Track which emails get 3x better responses!")
|
245 |
+
st.caption("π° **Value:** A/B testing typically increases response rates by 40-60%")
|
246 |
+
|
247 |
+
# Quality insights
|
248 |
+
st.markdown("### π Quality Insights **β¨ PREMIUM**")
|
249 |
+
st.info("π― **Premium Quality Scoring**\n\nβ’ Personalization analysis\nβ’ Industry benchmarking\nβ’ Conversion predictors\nβ’ Improvement suggestions")
|
250 |
+
|
251 |
+
# Usage warning
|
252 |
+
remaining = tracker.get_remaining_emails()
|
253 |
+
if remaining <= 2:
|
254 |
+
st.warning(f"β οΈ Only {remaining} emails left today!")
|
255 |
|
256 |
+
# Generate email button
|
257 |
+
if st.button("π Generate Premium Email", use_container_width=True, type="primary"):
|
258 |
+
if name and company:
|
259 |
+
if not tracker.can_generate_email():
|
260 |
+
tracker.show_upgrade_prompt("email generation")
|
261 |
+
else:
|
262 |
+
try:
|
263 |
+
with st.spinner("π€ AI is crafting your premium personalized email..."):
|
264 |
+
# Generate email with premium features
|
265 |
+
result = generate_cold_email(
|
266 |
+
name=name,
|
267 |
+
company=company,
|
268 |
+
company_details=company_details or "",
|
269 |
+
tone=tone.lower(),
|
270 |
+
cta_type=cta_type.lower().replace("/", "_") if cta_type != "Custom" else custom_cta,
|
271 |
+
industry_template=industry_template,
|
272 |
+
sender_signature=sender_signature
|
273 |
+
) if result:
|
274 |
+
subject, body, quality_score = result
|
275 |
+
|
276 |
+
# Check if this is a setup error
|
277 |
+
if subject == "Setup Required":
|
278 |
+
st.error("π§ **Premium AI Features Need Setup**")
|
279 |
+
st.markdown(body) # This contains the setup instructions
|
280 |
+
|
281 |
+
# Show what users are missing
|
282 |
+
st.info("π **What you're missing without AI setup:**")
|
283 |
+
st.markdown("""
|
284 |
+
- π€ **Advanced AI Personalization** (40% better conversion)
|
285 |
+
- π― **Industry-Specific Intelligence** (10x more relevant)
|
286 |
+
- π **AI Quality Scoring** (proven metrics)
|
287 |
+
- π **Smart Company Analysis** (saves 30+ min research)
|
288 |
+
""")
|
289 |
+
|
290 |
+
st.success("β¨ **Good News:** All premium features work perfectly once set up!")
|
291 |
+
st.markdown("**π° Value Unlocked:** $97/month worth of AI features for just $19 lifetime")
|
292 |
+
|
293 |
+
# Still show upgrade CTA
|
294 |
+
st.markdown("""
|
295 |
+
<div style='background: linear-gradient(90deg, #FF6B6B, #4ECDC4); padding: 1.5rem; border-radius: 10px; text-align: center; color: white; margin: 1rem 0;'>
|
296 |
+
<h3>π₯ Skip Setup - Get Pre-Configured Version</h3>
|
297 |
+
<p><strong>Get the fully-configured app with all AI features ready to use</strong></p>
|
298 |
+
<a href="https://gumroad.com/l/cold-email-assistant" style="background: white; color: #333; padding: 0.8rem 2rem; border-radius: 25px; text-decoration: none; font-weight: bold; margin-top: 1rem; display: inline-block;">π Get Lifetime Access Now</a>
|
299 |
+
</div>
|
300 |
+
""", unsafe_allow_html=True)
|
301 |
+
return
|
302 |
+
|
303 |
+
# Track usage for successful generation
|
304 |
+
tracker.increment_email_generation()
|
305 |
+
tracker.add_premium_feature_used("Premium Email Generation")
|
306 |
+
|
307 |
+
# Display results with premium features
|
308 |
+
st.success("β
Premium email generated successfully!")
|
309 |
+
|
310 |
+
col_result1, col_result2 = st.columns([3, 1])
|
311 |
+
|
312 |
+
with col_result1:
|
313 |
+
st.subheader("π§ Generated Premium Email")
|
314 |
+
|
315 |
+
# Subject line
|
316 |
+
st.markdown("**π Subject Line:**")
|
317 |
+
st.code(subject, language="text")
|
318 |
+
|
319 |
+
# Email body
|
320 |
+
st.markdown("**βοΈ Email Body:**")
|
321 |
+
st.text_area("", value=body, height=300, key="generated_email")
|
322 |
+
|
323 |
+
# Premium copy buttons
|
324 |
+
col_copy1, col_copy2 = st.columns(2)
|
325 |
+
with col_copy1:
|
326 |
+
if st.button("π Copy Subject", use_container_width=True):
|
327 |
+
st.code(f"Subject copied: {subject}")
|
328 |
+
with col_copy2:
|
329 |
+
if st.button("π Copy Email Body", use_container_width=True):
|
330 |
+
st.code("Email body copied to clipboard!")
|
331 |
+
|
332 |
+
with col_result2:
|
333 |
+
st.markdown("### π Premium Quality Score")
|
334 |
+
score_color = "π’" if quality_score >= 8 else "π‘" if quality_score >= 6 else "π΄"
|
335 |
+
st.markdown(f"## {score_color} {quality_score}/10")
|
336 |
+
|
337 |
+
# Premium quality insights
|
338 |
+
if quality_score >= 8:
|
339 |
+
st.success("π Premium Quality!")
|
340 |
+
st.caption("β
AI-optimized personalization\nβ
Industry-specific messaging\nβ
Conversion-tested format")
|
341 |
+
elif quality_score >= 6:
|
342 |
+
st.warning("π Good Quality")
|
343 |
+
st.caption("β
Decent personalization\nβ οΈ Could be more specific")
|
344 |
+
else:
|
345 |
+
st.error("β‘ Needs Premium Boost")
|
346 |
+
st.caption("π‘ Try premium research features")
|
347 |
+
|
348 |
+
st.markdown("### π― Premium Features Used")
|
349 |
+
features_used = tracker.get_premium_features_used()
|
350 |
+
for feature in features_used:
|
351 |
+
st.caption(f"β¨ {feature}")
|
352 |
+
|
353 |
+
# Remaining usage
|
354 |
+
remaining_after = tracker.get_remaining_emails()
|
355 |
+
if remaining_after > 0:
|
356 |
+
st.info(f"π§ {remaining_after} emails left today")
|
357 |
+
else:
|
358 |
+
st.error("π§ Daily limit reached!")
|
359 |
+
st.markdown("**[π Get Unlimited β](https://gumroad.com/l/cold-email-assistant)**")
|
360 |
+
|
361 |
+
except Exception as e:
|
362 |
+
st.error(f"β Error generating email: {str(e)}")
|
363 |
+
else:
|
364 |
+
st.error("β Please fill in at least Name and Company fields")
|
365 |
+
|
366 |
+
elif page == "π Bulk Processing":
|
367 |
+
st.header("π Premium Bulk Processing")
|
368 |
|
369 |
+
# Check bulk export limit
|
370 |
+
if not tracker.can_bulk_export():
|
371 |
+
tracker.show_upgrade_prompt("bulk processing")
|
372 |
+
st.stop()
|
|
|
373 |
|
374 |
+
st.info("π **Premium Bulk Processing** - Process up to 10 leads in demo mode")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
375 |
|
376 |
+
# File upload
|
377 |
+
uploaded_file = st.file_uploader("π Upload CSV file", type=['csv'])
|
378 |
+
|
379 |
+
if uploaded_file:
|
380 |
try:
|
|
|
381 |
df = pd.read_csv(uploaded_file)
|
382 |
|
383 |
+
# Limit to 10 rows in demo
|
384 |
+
if len(df) > 10:
|
385 |
+
st.warning(f"β οΈ Demo limited to 10 leads. Uploaded {len(df)} leads.")
|
386 |
+
df = df.head(10)
|
387 |
+
st.info("π Upgrade for unlimited bulk processing!")
|
388 |
|
389 |
+
st.success(f"β
File uploaded! Processing {len(df)} leads in demo mode.")
|
390 |
+
|
391 |
+
# Show preview
|
392 |
+
st.subheader("π Data Preview")
|
393 |
+
st.dataframe(df.head())
|
394 |
+
|
395 |
+
# Processing options
|
396 |
+
col1, col2 = st.columns(2)
|
397 |
+
|
398 |
+
with col1:
|
399 |
+
tone = st.selectbox("π Email Tone for All",
|
400 |
+
["Professional", "Friendly", "Direct", "Consultative β¨"])
|
401 |
|
402 |
+
industry_template = st.selectbox("π Industry Template",
|
403 |
+
["SaaS/Tech", "E-commerce", "Healthcare", "Generic B2B"],
|
404 |
+
index=3)
|
405 |
+
|
406 |
+
with col2:
|
407 |
+
st.markdown("**β¨ Premium Features:**")
|
408 |
+
st.caption("β’ AI-powered personalization")
|
409 |
+
st.caption("β’ Industry-specific templates")
|
410 |
+
st.caption("β’ Quality scoring")
|
411 |
+
st.caption("β’ Bulk export")
|
412 |
+
|
413 |
+
# Process button
|
414 |
+
if st.button("π Process All Leads (Premium Demo)", use_container_width=True, type="primary"):
|
415 |
+
progress_bar = st.progress(0)
|
416 |
+
status_text = st.empty()
|
417 |
|
418 |
+
results = []
|
419 |
+
|
420 |
+
for idx, row in df.iterrows():
|
421 |
+
status_text.text(f"Processing {idx + 1}/{len(df)}: {row['name']} at {row['company']}")
|
|
|
|
|
|
|
422 |
|
423 |
+
try:
|
424 |
+
# Generate email
|
425 |
+
result = generate_cold_email(
|
426 |
+
name=row['name'],
|
427 |
+
company=row['company'],
|
428 |
+
company_details="",
|
429 |
+
tone=tone.lower(),
|
430 |
+
cta_type="meeting_call",
|
431 |
+
industry_template=industry_template,
|
432 |
+
sender_signature=sender_signature
|
433 |
+
)
|
434 |
|
435 |
+
# Check if result is an error/setup message
|
436 |
+
if isinstance(result, str) and any(error_type in result for error_type in ["Setup Required", "AI Model Error", "Generation Error"]):
|
437 |
+
results.append({
|
438 |
+
'name': row['name'],
|
439 |
+
'company': row['company'],
|
440 |
+
'email': row.get('email', ''),
|
441 |
+
'subject': '',
|
442 |
+
'body': '',
|
443 |
+
'quality_score': 0,
|
444 |
+
'status': f'Setup Required: {result[:100]}...'
|
445 |
+
})
|
446 |
+
elif result and isinstance(result, tuple) and len(result) == 3:
|
447 |
+
subject, body, quality_score = result
|
448 |
+
results.append({
|
449 |
+
'name': row['name'],
|
450 |
+
'company': row['company'],
|
451 |
+
'email': row.get('email', ''),
|
452 |
+
'subject': subject,
|
453 |
+
'body': body,
|
454 |
+
'quality_score': quality_score,
|
455 |
+
'status': 'Success'
|
456 |
+
})
|
457 |
else:
|
458 |
+
results.append({
|
459 |
+
'name': row['name'],
|
460 |
+
'company': row['company'],
|
461 |
+
'email': row.get('email', ''),
|
462 |
+
'subject': '',
|
463 |
+
'body': '',
|
464 |
+
'quality_score': 0,
|
465 |
+
'status': 'Error: Invalid response from email generator'
|
466 |
+
})
|
467 |
|
468 |
+
except Exception as e:
|
469 |
+
results.append({
|
470 |
+
'name': row['name'],
|
471 |
+
'company': row['company'],
|
472 |
+
'email': row.get('email', ''),
|
473 |
+
'subject': '',
|
474 |
+
'body': '',
|
475 |
+
'quality_score': 0,
|
476 |
+
'status': f'Error: {str(e)}'
|
477 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
478 |
|
479 |
+
# Update progress
|
480 |
+
progress_bar.progress((idx + 1) / len(df))
|
481 |
+
time.sleep(0.5)
|
482 |
+
|
483 |
+
# Track usage
|
484 |
+
tracker.increment_bulk_export()
|
485 |
+
tracker.add_premium_feature_used("Bulk Processing")
|
486 |
+
|
487 |
+
# Show results
|
488 |
+
status_text.text("β
Premium processing complete!")
|
489 |
+
results_df = pd.DataFrame(results)
|
490 |
+
|
491 |
+
# Display summary
|
492 |
+
successful = len(results_df[results_df['status'] == 'Success'])
|
493 |
+
|
494 |
+
col1, col2, col3 = st.columns(3)
|
495 |
+
with col1:
|
496 |
+
st.metric("π§ Processed", len(results_df))
|
497 |
+
with col2:
|
498 |
+
st.metric("β
Successful", successful)
|
499 |
+
with col3:
|
500 |
+
avg_quality = results_df['quality_score'].mean()
|
501 |
+
st.metric("β Avg Quality", f"{avg_quality:.1f}/10")
|
502 |
+
|
503 |
+
# Show results table
|
504 |
+
st.subheader("π Results")
|
505 |
+
st.dataframe(results_df)
|
506 |
+
|
507 |
+
# Download option
|
508 |
+
csv = results_df.to_csv(index=False)
|
509 |
+
st.download_button(
|
510 |
+
"π₯ Download Results (Premium)",
|
511 |
+
csv,
|
512 |
+
f"premium_emails_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
|
513 |
+
"text/csv",
|
514 |
+
use_container_width=True
|
515 |
+
)
|
516 |
+
|
517 |
+
st.success("β¨ Premium bulk processing complete! Upgrade for unlimited processing.")
|
518 |
|
519 |
except Exception as e:
|
520 |
+
st.error(f"β Error processing file: {str(e)}")
|
521 |
+
|
522 |
+
elif page == "π Analytics Preview":
|
523 |
+
st.header("π Premium Analytics Preview")
|
524 |
+
|
525 |
+
st.info("π **Premium Analytics Dashboard** - Get insights on your email performance")
|
526 |
+
|
527 |
+
# Mock analytics data
|
528 |
+
col1, col2, col3, col4 = st.columns(4)
|
529 |
+
|
530 |
+
with col1:
|
531 |
+
st.metric("π§ Total Emails", "127", "+23 this week")
|
532 |
+
with col2:
|
533 |
+
st.metric("π Avg Quality", "8.3/10", "+0.5 improvement")
|
534 |
+
with col3:
|
535 |
+
st.metric("π’ Companies", "45", "+12 this week")
|
536 |
+
with col4:
|
537 |
+
st.metric("β Top Score", "9.2/10", "Healthcare emails")
|
538 |
+
|
539 |
+
# Charts preview
|
540 |
+
st.subheader("π Performance Insights")
|
541 |
+
|
542 |
+
col1, col2 = st.columns(2)
|
543 |
+
|
544 |
+
with col1:
|
545 |
+
st.markdown("**π Quality Trends**")
|
546 |
+
st.line_chart(pd.DataFrame({
|
547 |
+
'Day': range(7),
|
548 |
+
'Quality Score': [7.2, 7.8, 8.1, 8.3, 8.5, 8.2, 8.7]
|
549 |
+
}).set_index('Day'))
|
550 |
+
|
551 |
+
with col2:
|
552 |
+
st.markdown("**π Industry Performance**")
|
553 |
+
st.bar_chart(pd.DataFrame({
|
554 |
+
'SaaS': [8.5],
|
555 |
+
'Healthcare': [9.2],
|
556 |
+
'Manufacturing': [7.8],
|
557 |
+
'E-commerce': [8.1]
|
558 |
+
}))
|
559 |
+
|
560 |
+
# Premium features preview
|
561 |
+
st.subheader("β¨ Premium Analytics Features")
|
562 |
+
|
563 |
+
features = [
|
564 |
+
"π Conversion tracking integration",
|
565 |
+
"π A/B testing results dashboard",
|
566 |
+
"π― Industry benchmarking",
|
567 |
+
"π§ Email performance heatmaps",
|
568 |
+
"π Competitor analysis",
|
569 |
+
"π± Mobile-optimized reports",
|
570 |
+
"π Performance alerts",
|
571 |
+
"π Export to PowerBI/Tableau"
|
572 |
+
]
|
573 |
+
|
574 |
+
for i, feature in enumerate(features):
|
575 |
+
if i % 2 == 0:
|
576 |
+
col1, col2 = st.columns(2)
|
577 |
+
|
578 |
+
with col1 if i % 2 == 0 else col2:
|
579 |
+
st.info(feature)
|
580 |
|
|
|
581 |
st.markdown("---")
|
582 |
+
st.success("π **Ready to unlock full analytics?** Get unlimited access + advanced reporting!")
|
583 |
+
|
584 |
+
if st.button("π Upgrade to Premium Analytics", use_container_width=True, type="primary"):
|
585 |
+
st.markdown("**[π Get Cold Email Assistant Pro β](https://gumroad.com/l/cold-email-assistant)**")
|
|
|
|
|
|
|
586 |
|
587 |
+
# Footer with upgrade CTA
|
588 |
+
st.markdown("---")
|
589 |
+
st.markdown("""
|
590 |
+
<div style='text-align: center; padding: 2rem; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); border-radius: 10px; color: white; margin-top: 2rem;'>
|
591 |
+
<h3>π Ready for Unlimited Access?</h3>
|
592 |
+
<p>You've experienced the premium features - now get unlimited access!</p>
|
593 |
+
<p><strong>β
Unlimited emails β
Unlimited research β
Advanced analytics β
Priority support</strong></p>
|
594 |
+
<p><a href="https://gumroad.com/l/cold-email-assistant" style="color: #FFD700; font-weight: bold; text-decoration: none;">Get Lifetime Access for $19 β</a></p>
|
595 |
+
</div>
|
596 |
+
""", unsafe_allow_html=True)
|