Spaces:
Runtime error
Runtime error
Commit
Β·
83136bf
1
Parent(s):
d1220fd
Add application files
Browse files
app.py
CHANGED
|
@@ -380,14 +380,29 @@ footer { display: none !important; }
|
|
| 380 |
|
| 381 |
/* ============== MAIN CONTENT AREA ============== */
|
| 382 |
.main-wrapper {
|
| 383 |
-
margin-left: var(--sidebar-width);
|
|
|
|
|
|
|
| 384 |
min-height: 100vh;
|
| 385 |
padding: 20px;
|
| 386 |
-
transition: margin-left 0.3s ease;
|
| 387 |
background: var(--bg-secondary);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 388 |
}
|
| 389 |
|
| 390 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 391 |
|
| 392 |
.content-area {
|
| 393 |
max-width: 1200px;
|
|
@@ -458,13 +473,19 @@ footer { display: none !important; }
|
|
| 458 |
|
| 459 |
.main-wrapper {
|
| 460 |
margin-left: 0 !important;
|
|
|
|
|
|
|
| 461 |
padding: 16px;
|
| 462 |
padding-top: calc(var(--header-height) + 16px);
|
| 463 |
}
|
| 464 |
}
|
| 465 |
|
| 466 |
@media (max-width: 480px) {
|
| 467 |
-
.main-wrapper {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 468 |
.page-header { padding: 16px; }
|
| 469 |
.page-title { font-size: 20px; }
|
| 470 |
}
|
|
@@ -566,6 +587,94 @@ footer { display: none !important; }
|
|
| 566 |
.action-card h3 { margin: 0 0 12px 0; color: var(--text-primary); font-size: 18px; font-weight: 600; }
|
| 567 |
.action-card p { margin: 0 0 16px 0; color: var(--text-secondary); font-size: 14px; line-height: 1.6; }
|
| 568 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 569 |
button.primary {
|
| 570 |
background: linear-gradient(135deg, var(--primary-blue) 0%, var(--primary-dark) 100%) !important;
|
| 571 |
color: white !important;
|
|
@@ -2679,6 +2788,21 @@ def create_app():
|
|
| 2679 |
<p class="page-subtitle">Configure your company and API credentials</p>
|
| 2680 |
</div>
|
| 2681 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2682 |
""")
|
| 2683 |
|
| 2684 |
with gr.Row():
|
|
@@ -2724,7 +2848,18 @@ def create_app():
|
|
| 2724 |
gr.HTML("""<div class="page-header"><div>
|
| 2725 |
<h1 class="page-title">π Dashboard</h1>
|
| 2726 |
<p class="page-subtitle">Overview of your sales pipeline</p>
|
| 2727 |
-
</div></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2728 |
|
| 2729 |
client_status = gr.HTML(get_client_status_html())
|
| 2730 |
|
|
@@ -2742,7 +2877,23 @@ def create_app():
|
|
| 2742 |
gr.HTML("""<div class="page-header"><div>
|
| 2743 |
<h1 class="page-title">π Discovery</h1>
|
| 2744 |
<p class="page-subtitle">AI-powered prospect discovery</p>
|
| 2745 |
-
</div></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2746 |
|
| 2747 |
client_status_2 = gr.HTML(get_client_status_html())
|
| 2748 |
|
|
@@ -2763,7 +2914,18 @@ def create_app():
|
|
| 2763 |
gr.HTML("""<div class="page-header"><div>
|
| 2764 |
<h1 class="page-title">π― Prospects</h1>
|
| 2765 |
<p class="page-subtitle">Companies discovered by AI</p>
|
| 2766 |
-
</div></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2767 |
refresh_prospects_btn = gr.Button("π Refresh", variant="secondary", size="sm")
|
| 2768 |
prospects_list = gr.HTML(get_prospects_html())
|
| 2769 |
|
|
@@ -2772,7 +2934,18 @@ def create_app():
|
|
| 2772 |
gr.HTML("""<div class="page-header"><div>
|
| 2773 |
<h1 class="page-title">π₯ Contacts</h1>
|
| 2774 |
<p class="page-subtitle">Decision makers found by AI</p>
|
| 2775 |
-
</div></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2776 |
refresh_contacts_btn = gr.Button("π Refresh", variant="secondary", size="sm")
|
| 2777 |
contacts_list = gr.HTML(get_contacts_html())
|
| 2778 |
|
|
@@ -2781,7 +2954,18 @@ def create_app():
|
|
| 2781 |
gr.HTML("""<div class="page-header"><div>
|
| 2782 |
<h1 class="page-title">βοΈ Emails</h1>
|
| 2783 |
<p class="page-subtitle">AI-drafted outreach emails</p>
|
| 2784 |
-
</div></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2785 |
refresh_emails_btn = gr.Button("π Refresh", variant="secondary", size="sm")
|
| 2786 |
emails_list = gr.HTML(get_emails_html())
|
| 2787 |
|
|
@@ -2790,7 +2974,18 @@ def create_app():
|
|
| 2790 |
gr.HTML("""<div class="page-header"><div>
|
| 2791 |
<h1 class="page-title">π¬ AI Chat</h1>
|
| 2792 |
<p class="page-subtitle">Your AI sales assistant</p>
|
| 2793 |
-
</div></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2794 |
|
| 2795 |
chatbot = gr.Chatbot(value=[], height=400, label="Chat")
|
| 2796 |
|
|
@@ -2804,7 +2999,7 @@ def create_app():
|
|
| 2804 |
send_btn = gr.Button("Send", variant="primary", scale=1)
|
| 2805 |
|
| 2806 |
gr.HTML("""<div class="action-card" style="margin-top: 16px;">
|
| 2807 |
-
<h4>π‘
|
| 2808 |
<ul style="font-size: 13px; line-height: 1.8; margin: 8px 0 0 0; padding-left: 20px;">
|
| 2809 |
<li>"Search for DTC fashion brands that raised Series A"</li>
|
| 2810 |
<li>"Draft an email to the CEO of Warby Parker"</li>
|
|
@@ -2851,77 +3046,62 @@ def create_app():
|
|
| 2851 |
btn_emails.click(fn=lambda: show_page("emails"), outputs=all_pages)
|
| 2852 |
btn_chat.click(fn=lambda: show_page("chat"), outputs=all_pages)
|
| 2853 |
|
| 2854 |
-
# JavaScript to connect sidebar nav items to Gradio buttons
|
| 2855 |
gr.HTML("""
|
| 2856 |
<script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2857 |
window.selectPage = function(pageName) {
|
| 2858 |
-
// Close mobile sidebar
|
| 2859 |
if (window.closeMobileSidebar) {
|
| 2860 |
window.closeMobileSidebar();
|
| 2861 |
}
|
| 2862 |
|
| 2863 |
-
// Update active nav item
|
| 2864 |
document.querySelectorAll('.nav-item').forEach(item => {
|
| 2865 |
-
item.classList.
|
| 2866 |
-
if (item.dataset.page === pageName) {
|
| 2867 |
-
item.classList.add('active');
|
| 2868 |
-
}
|
| 2869 |
});
|
| 2870 |
|
| 2871 |
-
//
|
| 2872 |
-
const
|
| 2873 |
-
|
| 2874 |
-
|
| 2875 |
-
'discovery': 'btn-discovery',
|
| 2876 |
-
'prospects': 'btn-prospects',
|
| 2877 |
-
'contacts': 'btn-contacts',
|
| 2878 |
-
'emails': 'btn-emails',
|
| 2879 |
-
'chat': 'btn-chat'
|
| 2880 |
-
};
|
| 2881 |
-
|
| 2882 |
-
// Find button by elem_id in Gradio's structure
|
| 2883 |
-
const btnId = buttonMap[pageName];
|
| 2884 |
-
if (btnId) {
|
| 2885 |
-
// Gradio wraps buttons in a div with id containing the elem_id
|
| 2886 |
-
const btn = document.querySelector(`[id*="${btnId}"] button`) ||
|
| 2887 |
-
document.querySelector(`button[id*="${btnId}"]`) ||
|
| 2888 |
-
document.getElementById(btnId);
|
| 2889 |
-
if (btn) {
|
| 2890 |
-
btn.click();
|
| 2891 |
-
return;
|
| 2892 |
-
}
|
| 2893 |
|
| 2894 |
-
//
|
| 2895 |
-
|
| 2896 |
-
|
| 2897 |
-
|
| 2898 |
-
|
| 2899 |
-
|
| 2900 |
-
|
| 2901 |
-
|
| 2902 |
-
|
| 2903 |
-
|
| 2904 |
-
|
| 2905 |
-
buttons.forEach(b => {
|
| 2906 |
-
if (b.textContent.includes(labels[pageName])) {
|
| 2907 |
-
b.click();
|
| 2908 |
-
}
|
| 2909 |
-
});
|
| 2910 |
}
|
| 2911 |
};
|
| 2912 |
|
| 2913 |
-
//
|
| 2914 |
-
document.
|
| 2915 |
-
|
| 2916 |
-
|
| 2917 |
-
|
| 2918 |
-
|
| 2919 |
-
navButtons.forEach(b => b.classList.remove('active-nav-btn'));
|
| 2920 |
-
this.classList.add('active-nav-btn');
|
| 2921 |
-
});
|
| 2922 |
-
});
|
| 2923 |
-
}, 500);
|
| 2924 |
-
});
|
| 2925 |
</script>
|
| 2926 |
""")
|
| 2927 |
|
|
|
|
| 380 |
|
| 381 |
/* ============== MAIN CONTENT AREA ============== */
|
| 382 |
.main-wrapper {
|
| 383 |
+
margin-left: var(--sidebar-width) !important;
|
| 384 |
+
width: calc(100% - var(--sidebar-width)) !important;
|
| 385 |
+
max-width: calc(100vw - var(--sidebar-width)) !important;
|
| 386 |
min-height: 100vh;
|
| 387 |
padding: 20px;
|
| 388 |
+
transition: margin-left 0.3s ease, width 0.3s ease;
|
| 389 |
background: var(--bg-secondary);
|
| 390 |
+
overflow-x: hidden;
|
| 391 |
+
box-sizing: border-box !important;
|
| 392 |
+
}
|
| 393 |
+
|
| 394 |
+
.main-wrapper.expanded {
|
| 395 |
+
margin-left: var(--sidebar-collapsed) !important;
|
| 396 |
+
width: calc(100% - var(--sidebar-collapsed)) !important;
|
| 397 |
+
max-width: calc(100vw - var(--sidebar-collapsed)) !important;
|
| 398 |
}
|
| 399 |
|
| 400 |
+
/* Ensure Gradio's inner containers don't overflow */
|
| 401 |
+
.main-wrapper > div,
|
| 402 |
+
.main-wrapper > div > div {
|
| 403 |
+
max-width: 100% !important;
|
| 404 |
+
overflow-x: hidden;
|
| 405 |
+
}
|
| 406 |
|
| 407 |
.content-area {
|
| 408 |
max-width: 1200px;
|
|
|
|
| 473 |
|
| 474 |
.main-wrapper {
|
| 475 |
margin-left: 0 !important;
|
| 476 |
+
width: 100% !important;
|
| 477 |
+
max-width: 100vw !important;
|
| 478 |
padding: 16px;
|
| 479 |
padding-top: calc(var(--header-height) + 16px);
|
| 480 |
}
|
| 481 |
}
|
| 482 |
|
| 483 |
@media (max-width: 480px) {
|
| 484 |
+
.main-wrapper {
|
| 485 |
+
padding: 12px;
|
| 486 |
+
padding-top: calc(var(--header-height) + 12px);
|
| 487 |
+
width: 100% !important;
|
| 488 |
+
}
|
| 489 |
.page-header { padding: 16px; }
|
| 490 |
.page-title { font-size: 20px; }
|
| 491 |
}
|
|
|
|
| 587 |
.action-card h3 { margin: 0 0 12px 0; color: var(--text-primary); font-size: 18px; font-weight: 600; }
|
| 588 |
.action-card p { margin: 0 0 16px 0; color: var(--text-secondary); font-size: 14px; line-height: 1.6; }
|
| 589 |
|
| 590 |
+
/* ============== INFO BOX / HELP TIPS ============== */
|
| 591 |
+
.info-box {
|
| 592 |
+
background: linear-gradient(135deg, var(--primary-light) 0%, #E8F4FD 100%);
|
| 593 |
+
border: 1px solid var(--primary-blue);
|
| 594 |
+
border-left: 4px solid var(--primary-blue);
|
| 595 |
+
border-radius: 8px;
|
| 596 |
+
padding: 16px 20px;
|
| 597 |
+
margin-bottom: 20px;
|
| 598 |
+
display: flex;
|
| 599 |
+
gap: 12px;
|
| 600 |
+
align-items: flex-start;
|
| 601 |
+
}
|
| 602 |
+
|
| 603 |
+
.info-box.tip {
|
| 604 |
+
background: linear-gradient(135deg, #FEF3C7 0%, #FEF9E7 100%);
|
| 605 |
+
border-color: var(--warning-orange);
|
| 606 |
+
border-left-color: var(--warning-orange);
|
| 607 |
+
}
|
| 608 |
+
|
| 609 |
+
.info-box.success {
|
| 610 |
+
background: linear-gradient(135deg, var(--success-light) 0%, #E8F8ED 100%);
|
| 611 |
+
border-color: var(--success-green);
|
| 612 |
+
border-left-color: var(--success-green);
|
| 613 |
+
}
|
| 614 |
+
|
| 615 |
+
.info-box-icon {
|
| 616 |
+
font-size: 20px;
|
| 617 |
+
flex-shrink: 0;
|
| 618 |
+
margin-top: 2px;
|
| 619 |
+
}
|
| 620 |
+
|
| 621 |
+
.info-box-content {
|
| 622 |
+
flex: 1;
|
| 623 |
+
}
|
| 624 |
+
|
| 625 |
+
.info-box-title {
|
| 626 |
+
font-weight: 600;
|
| 627 |
+
color: var(--text-primary);
|
| 628 |
+
margin-bottom: 4px;
|
| 629 |
+
font-size: 14px;
|
| 630 |
+
}
|
| 631 |
+
|
| 632 |
+
.info-box-text {
|
| 633 |
+
color: var(--text-secondary);
|
| 634 |
+
font-size: 13px;
|
| 635 |
+
line-height: 1.5;
|
| 636 |
+
margin: 0;
|
| 637 |
+
}
|
| 638 |
+
|
| 639 |
+
.info-box-text ul {
|
| 640 |
+
margin: 8px 0 0 0;
|
| 641 |
+
padding-left: 18px;
|
| 642 |
+
}
|
| 643 |
+
|
| 644 |
+
.info-box-text li {
|
| 645 |
+
margin-bottom: 4px;
|
| 646 |
+
}
|
| 647 |
+
|
| 648 |
+
.dark .info-box {
|
| 649 |
+
background: linear-gradient(135deg, rgba(1, 118, 211, 0.15) 0%, rgba(1, 118, 211, 0.08) 100%);
|
| 650 |
+
}
|
| 651 |
+
|
| 652 |
+
.dark .info-box.tip {
|
| 653 |
+
background: linear-gradient(135deg, rgba(251, 191, 36, 0.15) 0%, rgba(251, 191, 36, 0.08) 100%);
|
| 654 |
+
}
|
| 655 |
+
|
| 656 |
+
.dark .info-box.success {
|
| 657 |
+
background: linear-gradient(135deg, rgba(46, 132, 74, 0.15) 0%, rgba(46, 132, 74, 0.08) 100%);
|
| 658 |
+
}
|
| 659 |
+
|
| 660 |
+
/* Collapsible help section */
|
| 661 |
+
.help-toggle {
|
| 662 |
+
background: none;
|
| 663 |
+
border: none;
|
| 664 |
+
color: var(--primary-blue);
|
| 665 |
+
cursor: pointer;
|
| 666 |
+
font-size: 13px;
|
| 667 |
+
padding: 4px 8px;
|
| 668 |
+
display: inline-flex;
|
| 669 |
+
align-items: center;
|
| 670 |
+
gap: 4px;
|
| 671 |
+
margin-bottom: 8px;
|
| 672 |
+
}
|
| 673 |
+
|
| 674 |
+
.help-toggle:hover {
|
| 675 |
+
text-decoration: underline;
|
| 676 |
+
}
|
| 677 |
+
|
| 678 |
button.primary {
|
| 679 |
background: linear-gradient(135deg, var(--primary-blue) 0%, var(--primary-dark) 100%) !important;
|
| 680 |
color: white !important;
|
|
|
|
| 2788 |
<p class="page-subtitle">Configure your company and API credentials</p>
|
| 2789 |
</div>
|
| 2790 |
</div>
|
| 2791 |
+
|
| 2792 |
+
<div class="info-box">
|
| 2793 |
+
<span class="info-box-icon">π</span>
|
| 2794 |
+
<div class="info-box-content">
|
| 2795 |
+
<div class="info-box-title">Getting Started</div>
|
| 2796 |
+
<div class="info-box-text">
|
| 2797 |
+
Complete these steps to start finding prospects:
|
| 2798 |
+
<ul>
|
| 2799 |
+
<li><strong>HuggingFace Token</strong> - Required for AI-powered research and email drafting</li>
|
| 2800 |
+
<li><strong>Serper API Key</strong> - Optional, enables real-time web search for company info</li>
|
| 2801 |
+
<li><strong>Company Name</strong> - Your company name helps AI find relevant prospects</li>
|
| 2802 |
+
</ul>
|
| 2803 |
+
</div>
|
| 2804 |
+
</div>
|
| 2805 |
+
</div>
|
| 2806 |
""")
|
| 2807 |
|
| 2808 |
with gr.Row():
|
|
|
|
| 2848 |
gr.HTML("""<div class="page-header"><div>
|
| 2849 |
<h1 class="page-title">π Dashboard</h1>
|
| 2850 |
<p class="page-subtitle">Overview of your sales pipeline</p>
|
| 2851 |
+
</div></div>
|
| 2852 |
+
|
| 2853 |
+
<div class="info-box success">
|
| 2854 |
+
<span class="info-box-icon">π</span>
|
| 2855 |
+
<div class="info-box-content">
|
| 2856 |
+
<div class="info-box-title">Pipeline Overview</div>
|
| 2857 |
+
<div class="info-box-text">
|
| 2858 |
+
Track your progress at a glance. The dashboard shows real-time counts of prospects discovered, contacts found, and emails drafted. Click "Refresh" to update the stats after running Discovery.
|
| 2859 |
+
</div>
|
| 2860 |
+
</div>
|
| 2861 |
+
</div>
|
| 2862 |
+
""")
|
| 2863 |
|
| 2864 |
client_status = gr.HTML(get_client_status_html())
|
| 2865 |
|
|
|
|
| 2877 |
gr.HTML("""<div class="page-header"><div>
|
| 2878 |
<h1 class="page-title">π Discovery</h1>
|
| 2879 |
<p class="page-subtitle">AI-powered prospect discovery</p>
|
| 2880 |
+
</div></div>
|
| 2881 |
+
|
| 2882 |
+
<div class="info-box tip">
|
| 2883 |
+
<span class="info-box-icon">π‘</span>
|
| 2884 |
+
<div class="info-box-content">
|
| 2885 |
+
<div class="info-box-title">How Discovery Works</div>
|
| 2886 |
+
<div class="info-box-text">
|
| 2887 |
+
<ul>
|
| 2888 |
+
<li><strong>Step 1:</strong> AI searches the web for companies matching your profile</li>
|
| 2889 |
+
<li><strong>Step 2:</strong> Finds decision-makers (CEOs, VPs, Founders) with verified emails</li>
|
| 2890 |
+
<li><strong>Step 3:</strong> Drafts personalized outreach emails for each contact</li>
|
| 2891 |
+
</ul>
|
| 2892 |
+
<em>Tip: Start with 2-3 prospects to test, then increase the number.</em>
|
| 2893 |
+
</div>
|
| 2894 |
+
</div>
|
| 2895 |
+
</div>
|
| 2896 |
+
""")
|
| 2897 |
|
| 2898 |
client_status_2 = gr.HTML(get_client_status_html())
|
| 2899 |
|
|
|
|
| 2914 |
gr.HTML("""<div class="page-header"><div>
|
| 2915 |
<h1 class="page-title">π― Prospects</h1>
|
| 2916 |
<p class="page-subtitle">Companies discovered by AI</p>
|
| 2917 |
+
</div></div>
|
| 2918 |
+
|
| 2919 |
+
<div class="info-box">
|
| 2920 |
+
<span class="info-box-icon">π’</span>
|
| 2921 |
+
<div class="info-box-content">
|
| 2922 |
+
<div class="info-box-title">Your Prospect Companies</div>
|
| 2923 |
+
<div class="info-box-text">
|
| 2924 |
+
This list shows all companies found by the AI. Each prospect includes company details, industry, and a fit score (0-100) indicating how well they match your ideal customer profile. Higher scores = better fit!
|
| 2925 |
+
</div>
|
| 2926 |
+
</div>
|
| 2927 |
+
</div>
|
| 2928 |
+
""")
|
| 2929 |
refresh_prospects_btn = gr.Button("π Refresh", variant="secondary", size="sm")
|
| 2930 |
prospects_list = gr.HTML(get_prospects_html())
|
| 2931 |
|
|
|
|
| 2934 |
gr.HTML("""<div class="page-header"><div>
|
| 2935 |
<h1 class="page-title">π₯ Contacts</h1>
|
| 2936 |
<p class="page-subtitle">Decision makers found by AI</p>
|
| 2937 |
+
</div></div>
|
| 2938 |
+
|
| 2939 |
+
<div class="info-box">
|
| 2940 |
+
<span class="info-box-icon">π€</span>
|
| 2941 |
+
<div class="info-box-content">
|
| 2942 |
+
<div class="info-box-title">Decision Maker Contacts</div>
|
| 2943 |
+
<div class="info-box-text">
|
| 2944 |
+
AI finds key decision-makers (CEOs, VPs, Founders, Directors) at each prospect company. Contact info includes name, title, email, and company. Only verified contacts with real email addresses are shown.
|
| 2945 |
+
</div>
|
| 2946 |
+
</div>
|
| 2947 |
+
</div>
|
| 2948 |
+
""")
|
| 2949 |
refresh_contacts_btn = gr.Button("π Refresh", variant="secondary", size="sm")
|
| 2950 |
contacts_list = gr.HTML(get_contacts_html())
|
| 2951 |
|
|
|
|
| 2954 |
gr.HTML("""<div class="page-header"><div>
|
| 2955 |
<h1 class="page-title">βοΈ Emails</h1>
|
| 2956 |
<p class="page-subtitle">AI-drafted outreach emails</p>
|
| 2957 |
+
</div></div>
|
| 2958 |
+
|
| 2959 |
+
<div class="info-box tip">
|
| 2960 |
+
<span class="info-box-icon">βοΈ</span>
|
| 2961 |
+
<div class="info-box-content">
|
| 2962 |
+
<div class="info-box-title">AI-Written Outreach Emails</div>
|
| 2963 |
+
<div class="info-box-text">
|
| 2964 |
+
Each email is personalized based on the prospect's company, industry, and any pain points discovered during research. Review and customize before sending. Emails are designed to start conversations, not close deals.
|
| 2965 |
+
</div>
|
| 2966 |
+
</div>
|
| 2967 |
+
</div>
|
| 2968 |
+
""")
|
| 2969 |
refresh_emails_btn = gr.Button("π Refresh", variant="secondary", size="sm")
|
| 2970 |
emails_list = gr.HTML(get_emails_html())
|
| 2971 |
|
|
|
|
| 2974 |
gr.HTML("""<div class="page-header"><div>
|
| 2975 |
<h1 class="page-title">π¬ AI Chat</h1>
|
| 2976 |
<p class="page-subtitle">Your AI sales assistant</p>
|
| 2977 |
+
</div></div>
|
| 2978 |
+
|
| 2979 |
+
<div class="info-box success">
|
| 2980 |
+
<span class="info-box-icon">π€</span>
|
| 2981 |
+
<div class="info-box-content">
|
| 2982 |
+
<div class="info-box-title">Your AI Sales Assistant</div>
|
| 2983 |
+
<div class="info-box-text">
|
| 2984 |
+
Chat with AI to research companies, draft emails, get talking points, or manage your pipeline. The AI has access to all your prospect data and can perform web searches for real-time info.
|
| 2985 |
+
</div>
|
| 2986 |
+
</div>
|
| 2987 |
+
</div>
|
| 2988 |
+
""")
|
| 2989 |
|
| 2990 |
chatbot = gr.Chatbot(value=[], height=400, label="Chat")
|
| 2991 |
|
|
|
|
| 2999 |
send_btn = gr.Button("Send", variant="primary", scale=1)
|
| 3000 |
|
| 3001 |
gr.HTML("""<div class="action-card" style="margin-top: 16px;">
|
| 3002 |
+
<h4>π‘ Try These Prompts</h4>
|
| 3003 |
<ul style="font-size: 13px; line-height: 1.8; margin: 8px 0 0 0; padding-left: 20px;">
|
| 3004 |
<li>"Search for DTC fashion brands that raised Series A"</li>
|
| 3005 |
<li>"Draft an email to the CEO of Warby Parker"</li>
|
|
|
|
| 3046 |
btn_emails.click(fn=lambda: show_page("emails"), outputs=all_pages)
|
| 3047 |
btn_chat.click(fn=lambda: show_page("chat"), outputs=all_pages)
|
| 3048 |
|
| 3049 |
+
# JavaScript to connect sidebar nav items to Gradio buttons (optimized)
|
| 3050 |
gr.HTML("""
|
| 3051 |
<script>
|
| 3052 |
+
// Cache for navigation buttons - populated once on load
|
| 3053 |
+
window._navButtonCache = null;
|
| 3054 |
+
window._pageOrder = ['setup', 'dashboard', 'discovery', 'prospects', 'contacts', 'emails', 'chat'];
|
| 3055 |
+
|
| 3056 |
+
// Initialize button cache
|
| 3057 |
+
function initNavCache() {
|
| 3058 |
+
if (window._navButtonCache) return window._navButtonCache;
|
| 3059 |
+
const buttons = document.querySelectorAll('.nav-buttons-row button');
|
| 3060 |
+
if (buttons.length >= 7) {
|
| 3061 |
+
window._navButtonCache = {};
|
| 3062 |
+
window._pageOrder.forEach((page, idx) => {
|
| 3063 |
+
window._navButtonCache[page] = buttons[idx];
|
| 3064 |
+
});
|
| 3065 |
+
}
|
| 3066 |
+
return window._navButtonCache;
|
| 3067 |
+
}
|
| 3068 |
+
|
| 3069 |
window.selectPage = function(pageName) {
|
| 3070 |
+
// Close mobile sidebar immediately
|
| 3071 |
if (window.closeMobileSidebar) {
|
| 3072 |
window.closeMobileSidebar();
|
| 3073 |
}
|
| 3074 |
|
| 3075 |
+
// Update active nav item in sidebar (instant visual feedback)
|
| 3076 |
document.querySelectorAll('.nav-item').forEach(item => {
|
| 3077 |
+
item.classList.toggle('active', item.dataset.page === pageName);
|
|
|
|
|
|
|
|
|
|
| 3078 |
});
|
| 3079 |
|
| 3080 |
+
// Use cached button reference for instant navigation
|
| 3081 |
+
const cache = initNavCache();
|
| 3082 |
+
if (cache && cache[pageName]) {
|
| 3083 |
+
cache[pageName].click();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3084 |
|
| 3085 |
+
// Update button row active state
|
| 3086 |
+
Object.values(cache).forEach(b => b.classList.remove('active-nav-btn'));
|
| 3087 |
+
cache[pageName].classList.add('active-nav-btn');
|
| 3088 |
+
return;
|
| 3089 |
+
}
|
| 3090 |
+
|
| 3091 |
+
// Fallback: direct index access (no searching)
|
| 3092 |
+
const buttons = document.querySelectorAll('.nav-buttons-row button');
|
| 3093 |
+
const idx = window._pageOrder.indexOf(pageName);
|
| 3094 |
+
if (idx >= 0 && buttons[idx]) {
|
| 3095 |
+
buttons[idx].click();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3096 |
}
|
| 3097 |
};
|
| 3098 |
|
| 3099 |
+
// Initialize cache after Gradio loads
|
| 3100 |
+
if (document.readyState === 'loading') {
|
| 3101 |
+
document.addEventListener('DOMContentLoaded', () => setTimeout(initNavCache, 100));
|
| 3102 |
+
} else {
|
| 3103 |
+
setTimeout(initNavCache, 100);
|
| 3104 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3105 |
</script>
|
| 3106 |
""")
|
| 3107 |
|