Spaces:
Running
Running
| import streamlit as st | |
| import openai | |
| import json | |
| import re | |
| from datetime import datetime | |
| import pandas as pd | |
| from io import BytesIO | |
| from reportlab.lib.pagesizes import letter | |
| from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle | |
| from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle | |
| from reportlab.lib.units import inch | |
| from reportlab.lib import colors | |
| from sendgrid import SendGridAPIClient | |
| from sendgrid.helpers.mail import Mail | |
| # Page config | |
| st.set_page_config( | |
| page_title="AI ROI Calculator - Eau Claire AI", | |
| page_icon="📊", | |
| layout="wide", | |
| initial_sidebar_state="collapsed" | |
| ) | |
| # Professional Eau Claire AI Brand CSS Design | |
| st.markdown(""" | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); | |
| /* Eau Claire AI Brand Colors */ | |
| :root { | |
| --primary-blue: #1e3a8a; | |
| --secondary-blue: #3b82f6; | |
| --teal: #0d9488; | |
| --light-teal: #14b8a6; | |
| --grey-dark: #374151; | |
| --grey-medium: #6b7280; | |
| --grey-light: #f3f4f6; | |
| --white: #ffffff; | |
| } | |
| /* Clean background */ | |
| .stApp { | |
| background-color: #f8fafc !important; | |
| font-family: 'Inter', sans-serif !important; | |
| } | |
| /* Professional main container */ | |
| .main .block-container { | |
| background: var(--white) !important; | |
| border-radius: 12px !important; | |
| box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08) !important; | |
| border: 1px solid #e2e8f0 !important; | |
| margin: 1.5rem auto !important; | |
| padding: 2.5rem !important; | |
| max-width: 1000px !important; | |
| } | |
| /* Brand-consistent headers */ | |
| h1 { | |
| color: var(--primary-blue) !important; | |
| font-size: 2.5rem !important; | |
| font-weight: 700 !important; | |
| text-align: center !important; | |
| margin-bottom: 0.5rem !important; | |
| font-family: 'Inter', sans-serif !important; | |
| } | |
| h2 { | |
| color: var(--grey-dark) !important; | |
| font-weight: 600 !important; | |
| margin-top: 2rem !important; | |
| margin-bottom: 1rem !important; | |
| font-size: 1.5rem !important; | |
| } | |
| h3 { | |
| color: var(--grey-dark) !important; | |
| font-weight: 600 !important; | |
| margin-bottom: 1rem !important; | |
| font-size: 1.25rem !important; | |
| } | |
| /* Professional form inputs with perfect contrast */ | |
| .stTextInput input, .stTextArea textarea { | |
| background: var(--white) !important; | |
| border: 2px solid #d1d5db !important; | |
| border-radius: 8px !important; | |
| padding: 12px 16px !important; | |
| color: var(--grey-dark) !important; | |
| font-size: 16px !important; | |
| font-weight: 500 !important; | |
| } | |
| .stTextInput input:focus, .stTextArea textarea:focus { | |
| border-color: var(--secondary-blue) !important; | |
| box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important; | |
| outline: none !important; | |
| } | |
| /* Number inputs with styled buttons */ | |
| .stNumberInput input { | |
| background: var(--white) !important; | |
| border: 2px solid #d1d5db !important; | |
| border-radius: 8px !important; | |
| padding: 12px 16px !important; | |
| color: var(--grey-dark) !important; | |
| font-size: 16px !important; | |
| font-weight: 500 !important; | |
| } | |
| .stNumberInput input:focus { | |
| border-color: var(--secondary-blue) !important; | |
| box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important; | |
| outline: none !important; | |
| } | |
| /* Fix number input buttons - properly centered */ | |
| .stNumberInput > div > div > button { | |
| background: var(--secondary-blue) !important; | |
| color: var(--white) !important; | |
| border: none !important; | |
| width: 32px !important; | |
| height: 32px !important; | |
| border-radius: 6px !important; | |
| font-weight: bold !important; | |
| display: flex !important; | |
| align-items: center !important; | |
| justify-content: center !important; | |
| font-size: 14px !important; | |
| margin: 2px !important; | |
| } | |
| .stNumberInput > div > div > button:hover { | |
| background: var(--primary-blue) !important; | |
| } | |
| .stNumberInput > div { | |
| position: relative !important; | |
| } | |
| .stNumberInput > div > div { | |
| display: flex !important; | |
| align-items: center !important; | |
| } | |
| /* Selectbox styling with visible dropdown arrow */ | |
| .stSelectbox > div > div { | |
| background: var(--white) !important; | |
| border: 2px solid #d1d5db !important; | |
| border-radius: 8px !important; | |
| color: var(--grey-dark) !important; | |
| font-weight: 500 !important; | |
| position: relative !important; | |
| } | |
| .stSelectbox > div > div:focus-within { | |
| border-color: var(--secondary-blue) !important; | |
| box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important; | |
| } | |
| /* Fix dropdown arrow - make it visible */ | |
| .stSelectbox > div > div::after { | |
| content: "▼" !important; | |
| position: absolute !important; | |
| right: 12px !important; | |
| top: 50% !important; | |
| transform: translateY(-50%) !important; | |
| color: var(--grey-medium) !important; | |
| font-size: 12px !important; | |
| pointer-events: none !important; | |
| } | |
| /* Dropdown options */ | |
| .stSelectbox > div > div > div { | |
| color: var(--grey-dark) !important; | |
| background: var(--white) !important; | |
| } | |
| /* Hide default dropdown arrow */ | |
| .stSelectbox select { | |
| appearance: none !important; | |
| -webkit-appearance: none !important; | |
| -moz-appearance: none !important; | |
| background-image: none !important; | |
| } | |
| /* Labels */ | |
| .stTextInput label, .stNumberInput label, .stSelectbox label, .stTextArea label { | |
| color: var(--grey-dark) !important; | |
| font-weight: 600 !important; | |
| font-size: 14px !important; | |
| margin-bottom: 8px !important; | |
| } | |
| /* Professional buttons */ | |
| .stButton > button { | |
| background: linear-gradient(135deg, var(--secondary-blue) 0%, var(--teal) 100%) !important; | |
| color: var(--white) !important; | |
| border: none !important; | |
| border-radius: 8px !important; | |
| padding: 12px 24px !important; | |
| font-weight: 600 !important; | |
| font-size: 16px !important; | |
| transition: all 0.2s ease !important; | |
| box-shadow: 0 2px 8px rgba(59, 130, 246, 0.2) !important; | |
| } | |
| .stButton > button:hover { | |
| transform: translateY(-1px) !important; | |
| box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3) !important; | |
| background: linear-gradient(135deg, var(--primary-blue) 0%, var(--teal) 100%) !important; | |
| } | |
| /* Professional metric cards */ | |
| .metric-card { | |
| background: var(--white) !important; | |
| border: 2px solid #e5e7eb !important; | |
| border-radius: 12px !important; | |
| padding: 1.5rem !important; | |
| margin: 1rem 0 !important; | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05) !important; | |
| transition: all 0.2s ease !important; | |
| } | |
| .metric-card:hover { | |
| border-color: var(--secondary-blue) !important; | |
| box-shadow: 0 4px 16px rgba(59, 130, 246, 0.1) !important; | |
| transform: translateY(-2px) !important; | |
| } | |
| .metric-value { | |
| font-size: 2.5rem !important; | |
| font-weight: 700 !important; | |
| color: var(--teal) !important; | |
| margin-bottom: 0.5rem !important; | |
| } | |
| .metric-label { | |
| color: var(--grey-medium) !important; | |
| font-size: 1rem !important; | |
| font-weight: 500 !important; | |
| } | |
| /* Clean recommendation boxes */ | |
| .recommendation-box { | |
| background: var(--white) !important; | |
| border: 2px solid var(--light-teal) !important; | |
| border-radius: 12px !important; | |
| padding: 1.5rem !important; | |
| margin: 1rem 0 !important; | |
| position: relative !important; | |
| } | |
| .recommendation-box::before { | |
| content: '' !important; | |
| position: absolute !important; | |
| top: 0 !important; | |
| left: 0 !important; | |
| right: 0 !important; | |
| height: 4px !important; | |
| background: var(--light-teal) !important; | |
| border-radius: 12px 12px 0 0 !important; | |
| } | |
| .recommendation-box h4 { | |
| color: var(--teal) !important; | |
| font-weight: 600 !important; | |
| margin-bottom: 0.5rem !important; | |
| } | |
| .recommendation-box p { | |
| color: var(--grey-dark) !important; | |
| line-height: 1.5 !important; | |
| } | |
| /* Progress bars */ | |
| .stProgress > div > div { | |
| background: linear-gradient(90deg, var(--secondary-blue), var(--teal)) !important; | |
| border-radius: 6px !important; | |
| } | |
| /* Clean footer */ | |
| .footer { | |
| background: var(--grey-light) !important; | |
| border-radius: 12px !important; | |
| padding: 1.5rem !important; | |
| margin-top: 2rem !important; | |
| text-align: center !important; | |
| color: var(--grey-medium) !important; | |
| border: 1px solid #e5e7eb !important; | |
| } | |
| /* Messages */ | |
| .stSuccess { | |
| background: #f0fdf4 !important; | |
| border: 2px solid #22c55e !important; | |
| color: #166534 !important; | |
| border-radius: 8px !important; | |
| padding: 1rem !important; | |
| } | |
| .stError { | |
| background: #fef2f2 !important; | |
| border: 2px solid #ef4444 !important; | |
| color: #991b1b !important; | |
| border-radius: 8px !important; | |
| padding: 1rem !important; | |
| } | |
| /* Fix checkbox styling */ | |
| .stCheckbox > label { | |
| color: var(--grey-dark) !important; | |
| font-weight: 500 !important; | |
| display: flex !important; | |
| align-items: center !important; | |
| gap: 8px !important; | |
| } | |
| .stCheckbox > label > div { | |
| display: flex !important; | |
| align-items: center !important; | |
| } | |
| .stCheckbox input[type="checkbox"] { | |
| appearance: none !important; | |
| width: 20px !important; | |
| height: 20px !important; | |
| border: 2px solid #d1d5db !important; | |
| border-radius: 4px !important; | |
| background: var(--white) !important; | |
| position: relative !important; | |
| cursor: pointer !important; | |
| margin: 0 !important; | |
| } | |
| .stCheckbox input[type="checkbox"]:checked { | |
| background: var(--secondary-blue) !important; | |
| border-color: var(--secondary-blue) !important; | |
| } | |
| .stCheckbox input[type="checkbox"]:checked::after { | |
| content: "✓" !important; | |
| position: absolute !important; | |
| top: 50% !important; | |
| left: 50% !important; | |
| transform: translate(-50%, -50%) !important; | |
| color: var(--white) !important; | |
| font-size: 12px !important; | |
| font-weight: bold !important; | |
| } | |
| .stCheckbox input[type="checkbox"]:focus { | |
| outline: none !important; | |
| box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important; | |
| } | |
| /* All text should be readable */ | |
| .stMarkdown p, .stText, div[data-testid="stMarkdownContainer"] p { | |
| color: var(--grey-dark) !important; | |
| line-height: 1.6 !important; | |
| } | |
| /* Remove Streamlit elements */ | |
| .stApp > header, .stDeployButton, #MainMenu, footer { | |
| display: none !important; | |
| } | |
| /* Mobile responsive */ | |
| @media (max-width: 768px) { | |
| .main .block-container { | |
| margin: 1rem !important; | |
| padding: 1.5rem !important; | |
| } | |
| h1 { | |
| font-size: 2rem !important; | |
| } | |
| .metric-value { | |
| font-size: 2rem !important; | |
| } | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # Access control | |
| def check_access(): | |
| """Check if user has valid access key""" | |
| access_key = st.query_params.get('key', None) | |
| valid_key = st.secrets.get("ACCESS_KEY", "a5a474d55b7c79e3") | |
| if access_key != valid_key: | |
| st.error("🚫 Access Restricted") | |
| st.markdown("This tool is exclusively available through **www.eauclaireai.com**") | |
| st.markdown("Please visit our website to access the ROI Calculator.") | |
| st.stop() | |
| # Check access before showing app | |
| check_access() | |
| # Initialize session state | |
| if 'calculations_done' not in st.session_state: | |
| st.session_state.calculations_done = False | |
| if 'ai_recommendations' not in st.session_state: | |
| st.session_state.ai_recommendations = None | |
| def validate_email(email): | |
| """Validate email format""" | |
| pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' | |
| return re.match(pattern, email) is not None | |
| def send_lead_notification(business_info, calculations): | |
| """Send email notification when new lead completes ROI calculator""" | |
| try: | |
| # Get SendGrid API key from secrets | |
| sg = SendGridAPIClient(api_key=st.secrets["SENDGRID_API_KEY"]) | |
| # Prepare email content | |
| subject = f"New ROI Calculator Lead: {business_info['company']}" | |
| # Create formatted task list | |
| tasks_list = "\n".join([f"• {task}" for task in business_info['tasks']]) | |
| html_content = f""" | |
| <div style="font-family: Arial, sans-serif; max-width: 600px;"> | |
| <h2 style="color: #1f4e79;">New ROI Calculator Submission</h2> | |
| <div style="background: #f8f9fa; padding: 20px; border-radius: 8px; margin: 20px 0;"> | |
| <h3 style="color: #2c5aa0; margin-top: 0;">Business Information</h3> | |
| <p><strong>Company:</strong> {business_info['company']}</p> | |
| <p><strong>Industry:</strong> {business_info['industry']}</p> | |
| <p><strong>Employees:</strong> {business_info['employees']}</p> | |
| <p><strong>Email:</strong> {business_info['email']}</p> | |
| <p><strong>Phone:</strong> {business_info.get('phone', 'Not provided')}</p> | |
| <p><strong>Avg Hourly Wage:</strong> ${business_info['avg_wage']}/hour</p> | |
| </div> | |
| <div style="background: #e8f4fd; padding: 20px; border-radius: 8px; margin: 20px 0;"> | |
| <h3 style="color: #2c5aa0; margin-top: 0;">ROI Analysis Results</h3> | |
| <p><strong>Annual Labor Savings:</strong> ${calculations['annual_labor_savings']:,.0f}</p> | |
| <p><strong>Year 1 Investment:</strong> ${calculations['ai_costs']['total_year_one']:,.0f}</p> | |
| <p><strong>Year 1 Net Savings:</strong> ${calculations['year_one_savings']:,.0f}</p> | |
| <p><strong>Ongoing Annual Savings:</strong> ${calculations['annual_ongoing_savings']:,.0f}</p> | |
| <p><strong>Monthly Savings (Year 2+):</strong> ${calculations['monthly_savings']:,.0f}</p> | |
| <p><strong>Hours Saved Per Week:</strong> {calculations['hours_saved']:.1f}</p> | |
| <p><strong>3-Year ROI:</strong> {calculations['roi_three_year']:.0f}%</p> | |
| <p><strong>Payback Period:</strong> {calculations['payback_months']:.1f} months</p> | |
| </div> | |
| <div style="background: #fff3cd; padding: 20px; border-radius: 8px; margin: 20px 0;"> | |
| <h3 style="color: #856404; margin-top: 0;">Time-Consuming Tasks</h3> | |
| <div style="white-space: pre-line;">{tasks_list}</div> | |
| </div> | |
| <div style="background: #d1ecf1; padding: 20px; border-radius: 8px; margin: 20px 0;"> | |
| <h3 style="color: #0c5460; margin-top: 0;">Next Steps</h3> | |
| <p>This lead has shown significant interest in AI implementation with potential ongoing savings of <strong>${calculations['annual_ongoing_savings']:,.0f} annually</strong>.</p> | |
| <p>Consider reaching out within 24 hours while their interest is high.</p> | |
| </div> | |
| <hr style="margin: 30px 0;"> | |
| <p style="color: #666; font-size: 14px;">Generated by AI ROI Calculator - Eau Claire AI</p> | |
| </div> | |
| """ | |
| # Create and send email | |
| message = Mail( | |
| from_email='noreply@eauclaireai.com', | |
| to_emails='info@eauclaireai.com', | |
| subject=subject, | |
| html_content=html_content | |
| ) | |
| response = sg.send(message) | |
| return True | |
| except Exception as e: | |
| st.error(f"Email notification failed: {str(e)}") | |
| return False | |
| def get_ai_recommendations(industry, company_size, main_tasks, annual_savings): | |
| """Get AI-powered recommendations from OpenAI""" | |
| try: | |
| client = openai.OpenAI(api_key=st.secrets["OPENAI_API_KEY"]) | |
| prompt = f"""You are an AI consultant for Eau Claire AI. Provide personalized recommendations for a business with these details: | |
| - Industry: {industry} | |
| - Company size: {company_size} employees | |
| - Main time-consuming tasks: {main_tasks} | |
| - Potential annual savings: ${annual_savings:,.0f} | |
| Provide 3 specific, actionable AI implementation recommendations that would be most impactful for this business. | |
| Focus on practical, cost-effective solutions that align with Eau Claire AI's approach of "problem-led, not tool-led" implementation. | |
| IMPORTANT: Respond with ONLY valid JSON, no other text. Use this exact structure: | |
| {{ | |
| "recommendations": [ | |
| {{ | |
| "title": "Clear, specific recommendation title", | |
| "description": "2-3 sentence description of the solution", | |
| "impact": "Expected time savings or business impact", | |
| "implementation": "Brief overview of how this would be implemented", | |
| "priority": "High" | |
| }}, | |
| {{ | |
| "title": "Clear, specific recommendation title", | |
| "description": "2-3 sentence description of the solution", | |
| "impact": "Expected time savings or business impact", | |
| "implementation": "Brief overview of how this would be implemented", | |
| "priority": "Medium" | |
| }}, | |
| {{ | |
| "title": "Clear, specific recommendation title", | |
| "description": "2-3 sentence description of the solution", | |
| "impact": "Expected time savings or business impact", | |
| "implementation": "Brief overview of how this would be implemented", | |
| "priority": "Low" | |
| }} | |
| ], | |
| "next_steps": "2-3 specific next steps they should take", | |
| "timeline": "Realistic implementation timeline" | |
| }}""" | |
| response = client.chat.completions.create( | |
| model="gpt-4o-mini", | |
| messages=[{"role": "user", "content": prompt}], | |
| temperature=0.7, | |
| max_tokens=1500 | |
| ) | |
| content = response.choices[0].message.content.strip() | |
| # Clean up common JSON formatting issues | |
| if content.startswith('```json'): | |
| content = content[7:] | |
| if content.endswith('```'): | |
| content = content[:-3] | |
| content = content.strip() | |
| return json.loads(content) | |
| except json.JSONDecodeError as e: | |
| st.error(f"AI response format error. Please try again.") | |
| return None | |
| except Exception as e: | |
| st.error(f"Error generating recommendations: {str(e)}") | |
| return None | |
| def generate_pdf_report(business_info, calculations, recommendations): | |
| """Generate PDF report""" | |
| buffer = BytesIO() | |
| doc = SimpleDocTemplate(buffer, pagesize=letter) | |
| styles = getSampleStyleSheet() | |
| story = [] | |
| # Header | |
| title_style = ParagraphStyle( | |
| 'CustomTitle', | |
| parent=styles['Heading1'], | |
| fontSize=24, | |
| spaceAfter=30, | |
| textColor=colors.HexColor('#1f4e79'), | |
| alignment=1 | |
| ) | |
| story.append(Paragraph("AI ROI Analysis Report", title_style)) | |
| story.append(Paragraph("Eau Claire AI - Your path to AI, made clear.", styles['Normal'])) | |
| story.append(Spacer(1, 20)) | |
| # Business Information | |
| story.append(Paragraph("Business Information", styles['Heading2'])) | |
| business_data = [ | |
| ['Company', business_info['company']], | |
| ['Industry', business_info['industry']], | |
| ['Employees', str(business_info['employees'])], | |
| ['Contact', business_info['email']] | |
| ] | |
| business_table = Table(business_data, colWidths=[2*inch, 3*inch]) | |
| business_table.setStyle(TableStyle([ | |
| ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#f0f0f0')), | |
| ('TEXTCOLOR', (0, 0), (-1, 0), colors.black), | |
| ('ALIGN', (0, 0), (-1, -1), 'LEFT'), | |
| ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), | |
| ('FONTSIZE', (0, 0), (-1, 0), 12), | |
| ('BOTTOMPADDING', (0, 0), (-1, 0), 12), | |
| ('BACKGROUND', (0, 1), (-1, -1), colors.white), | |
| ('GRID', (0, 0), (-1, -1), 1, colors.black) | |
| ])) | |
| story.append(business_table) | |
| story.append(Spacer(1, 20)) | |
| # How AI Saves Money - Explanation Section | |
| story.append(Paragraph("How AI Will Save Your Business Money", styles['Heading2'])) | |
| # Calculate key metrics for explanation | |
| weekly_hours = calculations['annual_cost'] / 52 / business_info.get('avg_wage', 25) | |
| hours_saved = calculations['hours_saved'] | |
| explanation_text = f""" | |
| Based on your current operations, your team spends approximately {weekly_hours:.1f} hours per week on repetitive tasks | |
| that can be significantly automated with AI. Here's how AI will generate savings: | |
| <b>Current Situation:</b><br/> | |
| • Your team dedicates {weekly_hours:.1f} hours weekly to manual, repetitive work<br/> | |
| • This costs your business ${calculations['annual_cost']:,.0f} annually in labor<br/> | |
| • These tasks include: {', '.join(business_info.get('tasks', ['data entry', 'manual processes']))}<br/> | |
| <b>AI Transformation:</b><br/> | |
| • AI can automate approximately 60% of these repetitive tasks<br/> | |
| • This frees up {hours_saved:.1f} hours per week for your team<br/> | |
| • Your staff can focus on higher-value activities like customer service, strategy, and growth<br/> | |
| <b>Investment Requirements:</b><br/> | |
| • Setup & Implementation: ${calculations['ai_costs']['setup']:,.0f}<br/> | |
| • Annual Software Costs: ${calculations['ai_costs']['annual_software']:,.0f}<br/> | |
| • Annual Maintenance: ${calculations['ai_costs']['annual_maintenance']:,.0f}<br/> | |
| • Total Year 1 Investment: ${calculations['ai_costs']['total_year_one']:,.0f}<br/> | |
| <b>Financial Impact:</b><br/> | |
| • Annual labor savings: ${calculations['annual_labor_savings']:,.0f}<br/> | |
| • Year 1 net savings: ${calculations['year_one_savings']:,.0f}<br/> | |
| • Ongoing annual net savings: ${calculations['annual_ongoing_savings']:,.0f}<br/> | |
| • 3-Year Total ROI: {calculations['roi_three_year']:.0f}%<br/> | |
| <b>Conservative Approach:</b><br/> | |
| This analysis uses conservative estimates for both automation rates (60%) and implementation costs | |
| to ensure realistic expectations and successful outcomes. | |
| """ | |
| story.append(Paragraph(explanation_text, styles['Normal'])) | |
| story.append(Spacer(1, 20)) | |
| # ROI Calculations | |
| story.append(Paragraph("ROI Analysis Summary", styles['Heading2'])) | |
| roi_data = [ | |
| ['Metric', 'Value'], | |
| ['Annual Labor Cost for Repetitive Tasks', f"${calculations['annual_cost']:,.0f}"], | |
| ['Annual Labor Savings from AI', f"${calculations['annual_labor_savings']:,.0f}"], | |
| ['Total Year 1 AI Investment', f"${calculations['ai_costs']['total_year_one']:,.0f}"], | |
| ['Year 1 Net Savings', f"${calculations['year_one_savings']:,.0f}"], | |
| ['Ongoing Annual Net Savings', f"${calculations['annual_ongoing_savings']:,.0f}"], | |
| ['Monthly Savings (Year 2+)', f"${calculations['monthly_savings']:,.0f}"], | |
| ['Hours Saved Per Week', f"{calculations['hours_saved']:.1f}"], | |
| ['3-Year Total ROI', f"{calculations['roi_three_year']:.0f}%"], | |
| ['Payback Period', f"{calculations['payback_months']:.1f} months"] | |
| ] | |
| roi_table = Table(roi_data, colWidths=[3*inch, 2*inch]) | |
| roi_table.setStyle(TableStyle([ | |
| ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#2c5aa0')), | |
| ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke), | |
| ('ALIGN', (0, 0), (-1, -1), 'LEFT'), | |
| ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'), | |
| ('FONTSIZE', (0, 0), (-1, 0), 12), | |
| ('BOTTOMPADDING', (0, 0), (-1, 0), 12), | |
| ('BACKGROUND', (0, 1), (-1, -1), colors.white), | |
| ('GRID', (0, 0), (-1, -1), 1, colors.black) | |
| ])) | |
| story.append(roi_table) | |
| if recommendations: | |
| story.append(Spacer(1, 20)) | |
| story.append(Paragraph("AI Implementation Recommendations", styles['Heading2'])) | |
| for i, rec in enumerate(recommendations['recommendations'], 1): | |
| story.append(Paragraph(f"{i}. {rec['title']}", styles['Heading3'])) | |
| story.append(Paragraph(rec['description'], styles['Normal'])) | |
| story.append(Paragraph(f"<b>Impact:</b> {rec['impact']}", styles['Normal'])) | |
| story.append(Paragraph(f"<b>Priority:</b> {rec['priority']}", styles['Normal'])) | |
| story.append(Spacer(1, 10)) | |
| story.append(Paragraph("Next Steps", styles['Heading3'])) | |
| story.append(Paragraph(recommendations['next_steps'], styles['Normal'])) | |
| story.append(Paragraph(f"<b>Timeline:</b> {recommendations['timeline']}", styles['Normal'])) | |
| # Footer | |
| story.append(Spacer(1, 30)) | |
| story.append(Paragraph("Contact Eau Claire AI", styles['Heading3'])) | |
| story.append(Paragraph("Ready to implement AI in your business?", styles['Normal'])) | |
| story.append(Paragraph("Email: hello@eauclaireai.com", styles['Normal'])) | |
| story.append(Paragraph("Website: www.eauclaireai.com", styles['Normal'])) | |
| story.append(Paragraph("Your path to AI, made clear.", styles['Italic'])) | |
| doc.build(story) | |
| buffer.seek(0) | |
| return buffer | |
| # Header | |
| st.markdown(""" | |
| <div class="main-header"> | |
| <h1>AI ROI Calculator</h1> | |
| <p>Discover how much time and money AI can save your business</p> | |
| <p><em>Your path to AI, made clear.</em></p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Methodology explanation - clean and professional | |
| st.subheader("How We Calculate Your ROI") | |
| st.markdown("Our calculations are based on conservative, real-world data to give you accurate expectations:") | |
| # Use Streamlit columns for clean layout | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.markdown(""" | |
| **📊 Your Current Costs** | |
| We calculate your annual labor cost for repetitive tasks using your team size, hourly wages, and time spent on manual work. | |
| **💰 Implementation Costs** | |
| We estimate AI costs based on your business size: software subscriptions, setup, training, and ongoing support. | |
| """) | |
| with col2: | |
| st.markdown(""" | |
| **🤖 AI Automation Potential** | |
| Research shows AI can automate 40-70% of repetitive tasks. We use a conservative 60% estimate for reliable projections. | |
| **📈 ROI Calculation** | |
| Annual savings minus total AI costs, divided by investment cost. We include both one-time and recurring expenses. | |
| """) | |
| st.info("**Conservative Approach:** Our estimates err on the side of caution to ensure realistic expectations and successful implementations.") | |
| st.markdown("---") | |
| # Main form | |
| with st.container(): | |
| col1, col2 = st.columns([2, 1]) | |
| with col1: | |
| st.subheader("Tell us about your business") | |
| # Business information | |
| company_name = st.text_input("Company Name*", placeholder="Your Company Name") | |
| col_a, col_b = st.columns(2) | |
| with col_a: | |
| industry = st.selectbox( | |
| "Industry*", | |
| ["", "Real Estate", "Construction", "Healthcare", "Professional Services", | |
| "Retail", "Food & Beverage", "Fitness", "Manufacturing", "Other"] | |
| ) | |
| with col_b: | |
| num_employees = st.number_input("Number of Employees*", min_value=1, max_value=10000, value=10) | |
| # Task analysis | |
| st.subheader("Current Operations") | |
| col_c, col_d = st.columns(2) | |
| with col_c: | |
| avg_hourly_wage = st.number_input("Average Hourly Wage ($)*", min_value=10.0, max_value=200.0, value=25.0, step=1.0) | |
| with col_d: | |
| hours_on_repetitive = st.number_input("Hours/Week on Repetitive Tasks*", min_value=0.5, max_value=80.0, value=10.0, step=0.5) | |
| # Task breakdown | |
| st.subheader("What takes up most of your team's time?") | |
| task_options = { | |
| "Data entry and manual record keeping": False, | |
| "Email management and responses": False, | |
| "Scheduling and calendar management": False, | |
| "Report generation and analysis": False, | |
| "Customer service inquiries": False, | |
| "Invoice processing and accounting": False, | |
| "Content creation and writing": False, | |
| "Research and information gathering": False, | |
| "File organization and management": False, | |
| "Quality control and reviewing": False | |
| } | |
| selected_tasks = [] | |
| for task, _ in task_options.items(): | |
| if st.checkbox(task): | |
| selected_tasks.append(task) | |
| other_tasks = st.text_area("Other time-consuming tasks", placeholder="Describe any other repetitive tasks...") | |
| if other_tasks: | |
| selected_tasks.append(other_tasks) | |
| # Contact information | |
| st.subheader("Get Your Personalized Results") | |
| email = st.text_input("Email Address*", placeholder="your.email@company.com") | |
| phone = st.text_input("Phone Number (Optional)", placeholder="(555) 123-4567") | |
| # Calculate button | |
| calculate_button = st.button("Calculate My AI ROI", use_container_width=True) | |
| with col2: | |
| st.info(""" | |
| ### What you'll get: | |
| ✅ **Personalized ROI Analysis** | |
| See exactly how much time and money AI could save your business | |
| ✅ **Custom Recommendations** | |
| AI-powered suggestions specific to your industry and needs | |
| ✅ **Implementation Roadmap** | |
| Clear next steps to get started | |
| ✅ **Detailed PDF Report** | |
| Professional analysis you can share with your team | |
| --- | |
| **Powered by Eau Claire AI** | |
| *Your path to AI, made clear.* | |
| """) | |
| # Validation and calculations | |
| if calculate_button: | |
| # Validate required fields | |
| errors = [] | |
| if not company_name.strip(): | |
| errors.append("Company name is required") | |
| if not industry: | |
| errors.append("Please select your industry") | |
| if not email.strip(): | |
| errors.append("Email address is required") | |
| elif not validate_email(email): | |
| errors.append("Please enter a valid email address") | |
| if not selected_tasks: | |
| errors.append("Please select at least one time-consuming task") | |
| if errors: | |
| for error in errors: | |
| st.error(error) | |
| else: | |
| with st.spinner("Calculating your AI ROI and generating personalized recommendations..."): | |
| # Calculate current costs | |
| annual_hours_repetitive = hours_on_repetitive * 52 | |
| annual_cost_repetitive = annual_hours_repetitive * avg_hourly_wage | |
| # Conservative automation estimates | |
| automation_percentage = 0.6 # 60% automation potential | |
| annual_labor_savings = annual_cost_repetitive * automation_percentage | |
| hours_saved_weekly = hours_on_repetitive * automation_percentage | |
| # Realistic AI Implementation Costs based on business size | |
| def calculate_ai_costs(num_employees, selected_tasks): | |
| # Base costs by company size - realistic market rates | |
| if num_employees <= 10: | |
| setup_cost = 3000 # Small business setup - basic automation | |
| monthly_software = 200 # Basic AI tools (ChatGPT, Zapier, etc.) | |
| elif num_employees <= 50: | |
| setup_cost = 6000 # Medium business setup | |
| monthly_software = 500 # More comprehensive tools | |
| elif num_employees <= 200: | |
| setup_cost = 12000 # Large business setup | |
| monthly_software = 1200 # Enterprise tools | |
| else: | |
| setup_cost = 20000 # Enterprise setup | |
| monthly_software = 2500 # Full enterprise suite | |
| # Adjust based on complexity (number of task types) | |
| complexity_multiplier = min(1.5, 1 + (len(selected_tasks) * 0.1)) | |
| setup_cost *= complexity_multiplier | |
| monthly_software *= complexity_multiplier | |
| # Annual ongoing costs | |
| annual_software = monthly_software * 12 | |
| annual_maintenance = setup_cost * 0.15 # 15% of setup for maintenance | |
| return { | |
| 'setup': setup_cost, | |
| 'annual_software': annual_software, | |
| 'annual_maintenance': annual_maintenance, | |
| 'total_year_one': setup_cost + annual_software + annual_maintenance, | |
| 'annual_ongoing': annual_software + annual_maintenance | |
| } | |
| ai_costs = calculate_ai_costs(num_employees, selected_tasks) | |
| # Calculate net benefits | |
| year_one_savings = annual_labor_savings - ai_costs['total_year_one'] | |
| annual_ongoing_savings = annual_labor_savings - ai_costs['annual_ongoing'] | |
| monthly_savings = annual_ongoing_savings / 12 | |
| # ROI calculations - fixed logic | |
| monthly_net_savings = annual_ongoing_savings / 12 | |
| payback_months = ai_costs['setup'] / monthly_net_savings if monthly_net_savings > 0 else 0 | |
| # More intuitive ROI: Total profit over 3 years divided by initial investment | |
| three_year_total_profit = (annual_ongoing_savings * 2) + year_one_savings # Year 1 (may be negative) + Years 2&3 | |
| roi_three_year = (three_year_total_profit / ai_costs['setup']) * 100 if ai_costs['setup'] > 0 else 0 | |
| # Annual ROI based on ongoing savings vs ongoing costs | |
| roi_ongoing = (annual_ongoing_savings / ai_costs['annual_ongoing']) * 100 if ai_costs['annual_ongoing'] > 0 else 0 | |
| calculations = { | |
| 'annual_cost': annual_cost_repetitive, | |
| 'annual_labor_savings': annual_labor_savings, | |
| 'annual_ongoing_savings': annual_ongoing_savings, | |
| 'monthly_savings': monthly_savings, | |
| 'hours_saved': hours_saved_weekly, | |
| 'ai_costs': ai_costs, | |
| 'payback_months': payback_months, | |
| 'roi_three_year': roi_three_year, | |
| 'roi_ongoing': roi_ongoing, | |
| 'year_one_savings': year_one_savings, | |
| 'three_year_total_profit': three_year_total_profit | |
| } | |
| # Get AI recommendations | |
| main_tasks_text = ", ".join(selected_tasks[:3]) # Top 3 tasks | |
| recommendations = get_ai_recommendations(industry, num_employees, main_tasks_text, annual_labor_savings) | |
| st.session_state.calculations_done = True | |
| st.session_state.calculations = calculations | |
| st.session_state.ai_recommendations = recommendations | |
| st.session_state.business_info = { | |
| 'company': company_name, | |
| 'industry': industry, | |
| 'employees': num_employees, | |
| 'email': email, | |
| 'phone': phone, | |
| 'tasks': selected_tasks, | |
| 'avg_wage': avg_hourly_wage | |
| } | |
| # Send email notification | |
| send_lead_notification(st.session_state.business_info, st.session_state.calculations) | |
| # Display results | |
| if st.session_state.calculations_done: | |
| st.success("Your AI ROI Analysis is Complete!") | |
| calc = st.session_state.calculations | |
| # Key Metrics | |
| col1, col2, col3, col4 = st.columns(4) | |
| with col1: | |
| st.markdown(f""" | |
| <div class="metric-card"> | |
| <div class="metric-value">${calc['annual_ongoing_savings']:,.0f}</div> | |
| <div class="metric-label">Annual Net Savings</div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col2: | |
| st.markdown(f""" | |
| <div class="metric-card"> | |
| <div class="metric-value">{calc['hours_saved']:.1f}</div> | |
| <div class="metric-label">Hours Saved Per Week</div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col3: | |
| st.markdown(f""" | |
| <div class="metric-card"> | |
| <div class="metric-value">{calc['roi_three_year']:.0f}%</div> | |
| <div class="metric-label">3-Year ROI</div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col4: | |
| st.markdown(f""" | |
| <div class="metric-card"> | |
| <div class="metric-value">{calc['payback_months']:.1f}</div> | |
| <div class="metric-label">Payback (Months)</div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Cost Breakdown Section | |
| st.subheader("Investment Breakdown") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.markdown("**Annual Labor Savings from AI:**") | |
| st.success(f"${calc['annual_labor_savings']:,.0f}") | |
| st.markdown("**AI Implementation Costs:**") | |
| st.info(f""" | |
| - **Setup & Training:** ${calc['ai_costs']['setup']:,.0f} | |
| - **Annual Software:** ${calc['ai_costs']['annual_software']:,.0f} | |
| - **Annual Maintenance:** ${calc['ai_costs']['annual_maintenance']:,.0f} | |
| - **Total Year 1:** ${calc['ai_costs']['total_year_one']:,.0f} | |
| """) | |
| with col2: | |
| st.markdown("**Net Financial Impact:**") | |
| year_one_color = "success" if calc['year_one_savings'] > 0 else "warning" | |
| getattr(st, year_one_color)(f"**Year 1 Net Savings:** ${calc['year_one_savings']:,.0f}") | |
| st.success(f"**Ongoing Annual Savings:** ${calc['annual_ongoing_savings']:,.0f}") | |
| st.info(f"**Monthly Savings (Year 2+):** ${calc['monthly_savings']:,.0f}") | |
| # AI Recommendations | |
| if st.session_state.ai_recommendations: | |
| st.subheader("Your Personalized AI Recommendations") | |
| for i, rec in enumerate(st.session_state.ai_recommendations['recommendations'], 1): | |
| priority_color = {"High": "#dc3545", "Medium": "#ffc107", "Low": "#28a745"} | |
| color = priority_color.get(rec['priority'], "#6c757d") | |
| st.markdown(f""" | |
| <div class="recommendation-box"> | |
| <h4 style="margin-top: 0; color: #1f4e79;">{i}. {rec['title']} | |
| <span style="background: {color}; color: white; padding: 2px 8px; border-radius: 12px; font-size: 0.8em; margin-left: 10px;">{rec['priority']}</span></h4> | |
| <p><strong>Description:</strong> {rec['description']}</p> | |
| <p><strong>Expected Impact:</strong> {rec['impact']}</p> | |
| <p><strong>Implementation:</strong> {rec['implementation']}</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Next steps | |
| st.subheader("Recommended Next Steps") | |
| st.info(st.session_state.ai_recommendations['next_steps']) | |
| st.info(f"**Timeline:** {st.session_state.ai_recommendations['timeline']}") | |
| # Download report | |
| st.subheader("Download Your Report") | |
| if st.button("Generate PDF Report", use_container_width=True): | |
| with st.spinner("Generating your personalized PDF report..."): | |
| pdf_buffer = generate_pdf_report( | |
| st.session_state.business_info, | |
| st.session_state.calculations, | |
| st.session_state.ai_recommendations | |
| ) | |
| st.download_button( | |
| label="Download AI ROI Report", | |
| data=pdf_buffer, | |
| file_name=f"AI_ROI_Report_{st.session_state.business_info['company'].replace(' ', '_')}_{datetime.now().strftime('%Y%m%d')}.pdf", | |
| mime="application/pdf", | |
| use_container_width=True | |
| ) | |
| # Call to action | |
| st.markdown(""" | |
| <div class="footer"> | |
| <h3>Ready to Transform Your Business with AI?</h3> | |
| <p>Let's discuss how Eau Claire AI can help you implement these recommendations and start saving time and money.</p> | |
| <p><strong>Schedule a free consultation:</strong> hello@eauclaireai.com</p> | |
| <p><strong>Website:</strong> www.eauclaireai.com</p> | |
| <p><em>Your path to AI, made clear.</em></p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Reset button | |
| if st.session_state.calculations_done: | |
| if st.button("Calculate for Another Business", use_container_width=True): | |
| st.session_state.calculations_done = False | |
| st.session_state.ai_recommendations = None | |
| st.rerun() |