""" Jay's Mobile Wash AI Business System Current Date and Time (UTC): 2025-06-14 02:50:32 Current User's Login: jjmandog Fully interactive UI with custom button navigation matching the existing design """ import gradio as gr import datetime import time import json import random import numpy as np import pandas as pd import matplotlib.pyplot as plt from datetime import datetime, timedelta import re import uuid # [Previous mock data and functions from earlier message remain the same] # ====================================================================== # CSS FOR UI STYLING (Updated for button navigation) # ====================================================================== css = """ /* Jay's Mobile Wash Branding Colors */ :root { --primary: #003366; --secondary: #CC0000; --accent: #0080ff; --light-bg: #f0f6ff; --dark-text: #111827; --light-text: #ffffff; --success: #10b981; --warning: #f59e0b; --error: #ef4444; } /* Overall layout */ body, .gradio-container { font-family: 'Segoe UI', Arial, sans-serif; color: var(--dark-text); background-color: var(--light-bg); } /* Headings style */ h1, h2, h3 { color: var(--primary); font-weight: 600; } /* Statistics cards with blue left border */ .stat-card { background: white; border-radius: 10px; padding: 15px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); margin-bottom: 15px; border-left: 4px solid var(--accent); } /* System status text colors */ .status-connected { color: var(--success); font-weight: 600; } .status-offline { color: var(--error); font-weight: 600; } /* Custom navigation bar to exactly match screenshot */ .nav-container { display: flex; flex-wrap: wrap; background-color: var(--primary); border-radius: 8px; overflow: hidden; margin-bottom: 20px; } .nav-button { border: none; padding: 12px 16px; background: none; color: white; cursor: pointer; font-size: 16px; font-weight: 500; transition: background-color 0.3s; display: flex; align-items: center; gap: 8px; } .nav-button.active { background-color: var(--accent); } .nav-button:hover:not(.active) { background-color: rgba(255, 255, 255, 0.1); } /* Content sections */ .content-section { display: none; } .content-section.active { display: block; } /* Switch component for system on/off */ .switch-container { display: flex; align-items: center; justify-content: flex-end; } .switch { position: relative; display: inline-block; width: 60px; height: 30px; } .switch input { opacity: 0; width: 0; height: 0; } .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 30px; } .slider:before { position: absolute; content: ""; height: 22px; width: 22px; left: 4px; bottom: 4px; background-color: white; transition: .4s; border-radius: 50%; } input:checked + .slider { background-color: var(--success); } input:disabled + .slider { background-color: var(--error); } input:checked + .slider:before { transform: translateX(30px); } /* Header with user info */ .header-container { display: flex; justify-content: space-between; align-items: center; padding: 10px 15px; background-color: white; border-radius: 10px; margin-bottom: 15px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); } .header-user { display: flex; align-items: center; gap: 10px; } .user-avatar { width: 40px; height: 40px; border-radius: 50%; background-color: var(--accent); color: white; display: flex; align-items: center; justify-content: center; font-weight: bold; } /* Running status dot */ .status-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; background-color: var(--success); margin-right: 5px; } /* Button styling */ button.primary { background-color: var(--accent); color: var(--light-text); border: none; border-radius: 5px; padding: 10px 15px; font-weight: 500; } /* Tables matching screenshot */ table { width: 100%; border-collapse: collapse; } th { background-color: var(--primary); color: var(--light-text); padding: 10px; text-align: left; } td { padding: 8px 10px; border-bottom: 1px solid #e5e7eb; } /* Footer styling */ .footer { text-align: center; padding: 15px; border-top: 1px solid #e5e7eb; margin-top: 20px; font-size: 14px; color: #666; } /* Nav icon custom styling */ .nav-icon { font-size: 18px; margin-right: 4px; } /* Running pill with dot */ .running-pill { display: inline-flex; align-items: center; background-color: rgba(16, 185, 129, 0.1); color: var(--success); padding: 6px 12px; border-radius: 20px; font-weight: 500; margin-right: 10px; } """ # JavaScript for custom navigation (matching the screenshot) js = """ function setupNavigation() { // Get all the nav buttons and content sections const navButtons = document.querySelectorAll('.nav-button'); const contentSections = document.querySelectorAll('.content-section'); // Set first button as active by default navButtons[0].classList.add('active'); contentSections[0].classList.add('active'); // Add click event to each button navButtons.forEach((button, index) => { button.addEventListener('click', () => { // Remove active class from all buttons and sections navButtons.forEach(btn => btn.classList.remove('active')); contentSections.forEach(section => section.classList.remove('active')); // Add active class to clicked button and corresponding section button.classList.add('active'); contentSections[index].classList.add('active'); }); }); } // Run setup when the page loads document.addEventListener('DOMContentLoaded', setupNavigation); // This ensures the script runs after dynamic updates too function refreshNavigation() { setTimeout(setupNavigation, 100); } """ # ====================================================================== # MAIN UI IMPLEMENTATION WITH CUSTOM BUTTON NAVIGATION # ====================================================================== with gr.Blocks(css=css, js=js) as demo: # System state for tracking (will be updated by UI components) system_enabled = gr.State(True) # Header section with logo, title and on/off switch with gr.Row(equal_height=True) as header: with gr.Column(scale=1): gr.Image(value="https://jaysmobilewash.com/wp-content/uploads/2023/06/cropped-JaysLogo.png", height=60, container=False, show_label=False) with gr.Column(scale=2): gr.HTML(""" <div style="height: 60px; display: flex; align-items: center;"> <h1 style="margin: 0;">Jay's Mobile Wash AI Business System</h1> </div> """) with gr.Column(scale=1): with gr.Row(): gr.HTML(""" <div class="running-pill"> <span class="status-dot"></span> Running </div> """) system_switch = gr.Checkbox( value=True, label="System Status", interactive=True, elem_classes=["system-switch"] ) # User info bar with gr.Row(elem_classes=["header-container"]): with gr.Column(scale=3): gr.HTML(""" <div style="display: flex; align-items: center; gap: 10px;"> <div style="width: 48px; height: 48px; overflow: hidden; border-radius: 5px; border: 1px solid #eee;"> <img src="https://cdn-icons-png.flaticon.com/512/8324/8324775.png" width="48" height="48" alt="System Logo"> </div> <div> <h2 style="margin: 0; font-size: 24px;">Jay's Mobile Wash AI Business System</h2> </div> </div> """) with gr.Column(scale=1): gr.HTML(f""" <div style="display: flex; align-items: center; justify-content: flex-end; height: 100%;"> <div style="text-align: right;"> <div> <div class="user-avatar">π€</div> <div style="font-weight: bold;">jjmandog</div> <div>{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} UTC</div> </div> </div> </div> """) # Custom navigation bar (exactly matching the screenshot) gr.HTML(""" <div class="nav-container"> <button class="nav-button" onclick="refreshNavigation()"> <span class="nav-icon">π</span> Dashboard </button> <button class="nav-button" onclick="refreshNavigation()"> <span class="nav-icon">π</span> Call Management </button> <button class="nav-button" onclick="refreshNavigation()"> <span class="nav-icon">π±</span> SMS Management </button> <button class="nav-button" onclick="refreshNavigation()"> <span class="nav-icon">π
</span> Booking Management </button> <button class="nav-button" onclick="refreshNavigation()"> <span class="nav-icon">π§ </span> AI Training Center </button> <button class="nav-button" onclick="refreshNavigation()"> <span class="nav-icon">βοΈ</span> Settings </button> </div> """) # CONTENT SECTIONS (each shown/hidden based on navigation) # 1. DASHBOARD SECTION with gr.Box(elem_classes=["content-section"]): # Mock data stats = { "total_calls": 127, "calls_jay_answered": 85, "calls_ai_handled": 34, "spam_blocked": 8, "active_calls": 0, "sms_received": 312, "bookings_confirmed": 96, "revenue_month": "$12,480" } system_status = { "twilio": "Connected", "database": "Online", "ai_models": "Operational", "last_update": datetime.now().strftime('%Y-%m-%d %H:%M:%S'), "server_load": "23%", "space_used": "1.2 GB / 5 GB" } with gr.Row(): gr.Markdown("## Business Overview") with gr.Row(): # Stats cards - first column with gr.Column(): with gr.Box(elem_classes=["stat-card"]): gr.Markdown(f""" ### π Total Calls # {stats['total_calls']} Jay Answered: {stats['calls_jay_answered']} | AI Handled: {stats['calls_ai_handled']} """) with gr.Box(elem_classes=["stat-card"]): gr.Markdown(f""" ### π± SMS Interactions # {stats['sms_received']} AI Response Rate: 90% """) with gr.Box(elem_classes=["stat-card"]): gr.Markdown(f""" ### π° Monthly Revenue # {stats['revenue_month']} Confirmed Bookings: {stats['bookings_confirmed']} """) # System status section with gr.Row(): gr.Markdown("## System Status") with gr.Row(): with gr.Box(): with gr.Row(): with gr.Column(): gr.Markdown(f"**Twilio:** <span class='status-connected'>{system_status['twilio']}</span>") with gr.Column(): gr.Markdown(f"**Database:** <span class='status-connected'>{system_status['database']}</span>") with gr.Row(): with gr.Column(): gr.Markdown(f"**AI Models:**<br><span class='status-connected'>{system_status['ai_models']}</span>") with gr.Column(): gr.Markdown(f"**Last Update:**<br>{system_status['last_update']}") with gr.Row(): with gr.Column(): gr.Markdown(f"**Server Load:** {system_status['server_load']}") with gr.Column(): gr.Markdown(f"**Storage:** {system_status['space_used']}") # Recent call activity with gr.Row(): gr.Markdown("## Recent Call Activity") with gr.Row(): mock_calls = [ {"timestamp": "2025-06-13 21:34:12", "caller": "+15622289429", "duration": "3m 24s", "status": "Jay Answered", "notes": "Booking confirmed"}, {"timestamp": "2025-06-13 20:15:35", "caller": "+13108675309", "duration": "2m 10s", "status": "AI Handled", "notes": "Pricing inquiry"}, {"timestamp": "2025-06-13 19:42:03", "caller": "+15625551234", "duration": "4m 02s", "status": "Jay Answered", "notes": "Scheduled luxury wash"}, ] call_table = gr.DataFrame( mock_calls, headers=["Timestamp", "Caller", "Duration", "Status", "Notes"], datatype=["str", "str", "str", "str", "str"], row_count=3, col_count=5, ) # Upcoming bookings with gr.Row(): gr.Markdown("## Upcoming Bookings") with gr.Row(): mock_bookings = [ {"ref": "JMW2365", "customer": "John D.", "service": "Luxury Wash", "date": "2025-06-15", "time": "10:00 AM", "price": "$130", "status": "Confirmed"}, {"ref": "JMW2366", "customer": "Sarah M.", "service": "Basic Wash", "date": "2025-06-14", "time": "2:30 PM", "price": "$60", "status": "Pending"}, {"ref": "JMW2367", "customer": "Robert K.", "service": "Max Detail", "date": "2025-06-16", "time": "9:00 AM", "price": "$210", "status": "Confirmed"} ] bookings_table = gr.DataFrame( mock_bookings, headers=["Ref", "Customer", "Service", "Date", "Time", "Price", "Status"], datatype=["str", "str", "str", "str", "str", "str", "str"], row_count=3, col_count=7, ) # Refresh button with gr.Row(): dashboard_status = gr.Textbox(visible=False) refresh_button = gr.Button("π Refresh Dashboard", variant="primary") # 2. CALL MANAGEMENT SECTION with gr.Box(elem_classes=["content-section"]): with gr.Row(): gr.Markdown("## Call Management Center") with gr.Row(): with gr.Column(): active_calls_box = gr.Textbox( label="Active Calls", value="No active calls at the moment.", interactive=False ) call_history_table = gr.DataFrame( mock_calls, headers=["Timestamp", "Caller", "Duration", "Status", "Notes"], datatype=["str", "str", "str", "str", "str"], row_count=10, interactive=False ) with gr.Row(): gr.Markdown("## Call Settings") with gr.Row(): with gr.Column(): ring_duration = gr.Slider( minimum=10, maximum=60, value=20, step=5, label="Ring Duration Before AI Pickup (seconds)" ) with gr.Column(): voice_timeout = gr.Slider( minimum=3, maximum=15, value=5, step=1, label="Voice Input Timeout (seconds)" ) with gr.Row(): ai_fallback_delay = gr.Slider( minimum=0, maximum=30, value=5, step=5, label="AI Fallback Delay After Failed Forward (seconds)" ) update_call_settings = gr.Button("πΎ Update Call Settings", variant="primary") call_settings_status = gr.Textbox( label="Settings Status", interactive=False, lines=4 ) # Call simulation section with gr.Row(): with gr.Box(): gr.Markdown("## Call Simulation") with gr.Row(): test_caller = gr.Textbox( label="Test Caller Number", placeholder="+15551234567", value="+15622289429" ) test_scenario = gr.Dropdown( choices=["Normal Call", "Spam Call", "After Hours", "Angry Customer"], label="Test Scenario", value="Normal Call" ) simulate_call_btn = gr.Button("π± Simulate Incoming Call", variant="primary") simulation_result = gr.Textbox( label="Simulation Result", interactive=False, lines=15 ) # 3. SMS MANAGEMENT SECTION with gr.Box(elem_classes=["content-section"]): with gr.Row(): with gr.Column(scale=1): gr.Markdown("## Contacts") contact_search = gr.Textbox( label="Search Contacts", placeholder="Name or phone number..." ) mock_customers = [ {"name": "John Deere", "phone": "+15622289429"}, {"name": "Sarah Miller", "phone": "+15625551234"}, {"name": "Robert King", "phone": "+13108675309"}, {"name": "Maria Lopez", "phone": "+12135557890"}, {"name": "David Chen", "phone": "+13235554321"} ] contact_list = gr.DataFrame( [[c["name"], c["phone"]] for c in mock_customers], headers=["Name", "Phone"], datatype=["str", "str"], row_count=5, interactive=False ) with gr.Column(scale=2): gr.Markdown("## SMS Conversation") conversation_header = gr.Markdown("#### Conversation with: John Deere (+15622289429)") mock_sms = [ {"timestamp": "2025-06-13 22:15:22", "contact": "+15622289429", "direction": "incoming", "content": "What time can you come tomorrow for the wash?"}, {"timestamp": "2025-06-13 22:15:45", "contact": "+15622289429", "direction": "outgoing", "content": "Hi John! I have an opening for your Tesla's luxury wash at 10:00 AM tomorrow. Does that work for you?"}, {"timestamp": "2025-06-13 22:16:30", "contact": "+15622289429", "direction": "incoming", "content": "Perfect, see you then"}, {"timestamp": "2025-06-13 22:16:45", "contact": "+15622289429", "direction": "outgoing", "content": "Great! I've confirmed your luxury wash for tomorrow at 10:00 AM at 2521 E. 4th St. See you then! -Jay"} ] sms_thread = gr.Chatbot( [(msg["content"], None) if msg["direction"] == "incoming" else (None, msg["content"]) for msg in mock_sms], height=400, show_label=False ) with gr.Row(): sms_input = gr.Textbox( placeholder="Type your message here...", lines=2, label="" ) send_sms_btn = gr.Button("π€ Send", variant="primary") sms_status = gr.Textbox( label="Status", interactive=False, value="Ready to send messages", lines=1 ) # SMS Settings with gr.Row(): with gr.Box(): gr.Markdown("## SMS Settings") with gr.Row(): with gr.Column(): response_time = gr.Slider( minimum=1, maximum=30, value=5, step=1, label="Automatic Response Time (seconds)" ) with gr.Column(): template_selection = gr.Dropdown( choices=["Standard", "Friendly", "Professional", "Concise"], value="Professional", label="Response Style Template" ) update_sms_settings = gr.Button("πΎ Update SMS Settings", variant="primary") sms_settings_status = gr.Textbox( label="Settings Status", interactive=False, lines=1 ) # 4. BOOKING MANAGEMENT SECTION with gr.Box(elem_classes=["content-section"]): with gr.Row(): gr.Markdown("## Booking Management") with gr.Row(): with gr.Column(): with gr.Box(): gr.Markdown("## Create New Booking") customer_name = gr.Textbox(label="Customer Name", value="James Wilson") phone_number = gr.Textbox(label="Phone Number", value="+15551234567") with gr.Row(): service_type = gr.Dropdown( choices=["Basic Wash", "Luxury Wash", "Max Detail"], label="Service Type", value="Luxury Wash" ) vehicle_type = gr.Textbox(label="Vehicle Type", value="BMW X5") with gr.Row(): service_date = gr.Textbox( label="Date", placeholder="YYYY-MM-DD", value=(datetime.now() + timedelta(days=2)).strftime("%Y-%m-%d") ) service_time = gr.Textbox( label="Time", placeholder="HH:MM AM/PM", value="10:30 AM" ) service_address = gr.Textbox( label="Service Address", lines=2, value="123 Ocean Ave, Long Beach, CA 90802" ) create_booking_btn = gr.Button("π Create Booking", variant="primary") booking_status = gr.Textbox( label="Booking Status", interactive=False, lines=8 ) with gr.Column(): with gr.Box(): gr.Markdown("## Upcoming Bookings") upcoming_bookings = gr.DataFrame( mock_bookings, headers=["Ref", "Customer", "Service", "Date", "Time", "Price", "Status"], datatype=["str", "str", "str", "str", "str", "str", "str"], row_count=5, interactive=False ) with gr.Row(): booking_ref = gr.Textbox( label="Booking Reference", placeholder="Enter booking reference...", value="JMW2365" ) action = gr.Dropdown( choices=["Confirm", "Cancel", "Reschedule", "Complete", "Send Reminder"], label="Action", value="Confirm" ) booking_action_btn = gr.Button("β
Apply Action", variant="primary") booking_action_status = gr.Textbox( label="Action Status", interactive=False, lines=3 ) # 5. AI TRAINING CENTER SECTION with gr.Box(elem_classes=["content-section"]): # AI Training sub-tabs with gr.Tabs() as training_tabs: # SMS Training with gr.Tab("π± SMS Training"): gr.Markdown("## Train SMS Responses") with gr.Row(): sms_customer_message = gr.Textbox( label="Customer SMS", lines=3, placeholder="Type a message as if from a customer...", value="Do you have any availability this weekend for a wash?" ) sms_expected_response = gr.Textbox( label="Expected Response (Optional)", lines=3, placeholder="Provide the correct response if needed" ) use_correction = gr.Checkbox(label="Use correction instead of AI response", value=False) train_sms_button = gr.Button("π€ Train SMS Response", variant="primary") with gr.Row(): sms_ai_response = gr.Textbox(label="AI Generated Response", lines=3) sms_final_response = gr.Textbox(label="Final Training Response", lines=3) sms_training_status = gr.Textbox(label="Training Status") # Example messages for quick testing with gr.Accordion("Example Messages", open=True): example_buttons = [ gr.Button("Price Question"), gr.Button("Booking Request"), gr.Button("Vehicle Question"), gr.Button("Availability Question"), gr.Button("Complaint") ] # Voice Training with gr.Tab("π€ Voice Training"): gr.Markdown("## Advanced Voice Training") with gr.Row(): # Left column - Recording & Inputs with gr.Column(): audio_input = gr.Audio( source="microphone", type="filepath", label="Record customer voice" ) scenario_type = gr.Dropdown( choices=["General Question", "Pricing Inquiry", "Booking Request", "Service Complaint", "Schedule Change", "Location Question"], label="Scenario Type", value="General Question" ) emotion_type = gr.Dropdown( choices=["Let AI Detect", "Neutral", "Happy", "Angry", "Confused", "Frustrated", "Satisfied"], label="Customer Emotion", value="Let AI Detect" ) notes = gr.Textbox( label="Training Notes (Optional)", placeholder="Add any special notes for this training sample..." ) train_voice_button = gr.Button("π Process Voice Training", variant="primary") # Right column - Results & Analysis with gr.Column(): with gr.Box(): training_status = gr.Textbox(label="Status") device_detected = gr.Textbox(label="Recording Device") emotion_detected = gr.Textbox(label="Detected Emotion") gr.Markdown("### Transcription & Response") transcription_output = gr.Textbox(label="Customer Speech Transcription", lines=3) response_output = gr.Textbox(label="AI Response", lines=3) # Call Simulation Training with gr.Tab("π Call Simulation Training"): gr.Markdown("## Call Simulation Training") with gr.Row(): with gr.Column(): sim_caller_number = gr.Textbox( label="Simulated Caller Number", placeholder="+15551234567", value="+15622289429" ) sim_scenario = gr.Dropdown( choices=["New Customer", "Repeat Customer", "Pricing Question", "Booking Request", "Complaint", "Emergency Request"], label="Call Scenario", value="New Customer" ) sim_caller_emotion = gr.Dropdown( choices=["Neutral", "Happy", "Angry", "Confused", "Impatient", "Demanding", "Anxious"], label="Caller Emotion", value="Neutral" ) jay_answers = gr.Radio( choices=["Jay Answers", "Jay Doesn't Answer"], label="Call Routing", value="Jay Answers" ) run_simulation_btn = gr.Button("βΆοΈ Run Call Simulation", variant="primary") with gr.Column(): sim_result = gr.Textbox( label="Simulation Result", lines=20, interactive=False ) # Response Feedback with gr.Tab("β Response Rating & Feedback"): gr.Markdown("## Rate and Improve AI Responses") with gr.Row(): with gr.Column(): feedback_type = gr.Radio( choices=["SMS Response", "Voice Response", "Call Handling"], label="Feedback Type", value="SMS Response" ) mock_training_samples = [ {"type": "sms", "input": "Do you offer ceramic coating?", "ai_response": "We don't currently offer ceramic coating, but our Max Detail includes a premium wax that offers excellent protection."}, {"type": "voice", "input": "I need an emergency wash before a meeting", "ai_response": "I understand this is urgent. I can offer you a priority slot today at 3pm, would that work for your meeting?"}, {"type": "sms", "input": "How much for a normal wash?", "ai_response": "Our Basic Wash is $60 for cars and $70 for SUVs/trucks. It includes exterior wash, soap, scrub and shine. Would you like to schedule one?"} ] sample_responses = gr.DataFrame( [[sample["type"], sample["input"][:30] + "...", sample["ai_response"][:30] + "..."] for sample in mock_training_samples], headers=["Type", "Customer Input", "AI Response"], datatype=["str", "str", "str"], row_count=3 ) rating = gr.Radio( choices=["1 - Poor", "2 - Below Average", "3 - Average", "4 - Good", "5 - Excellent"], label="Response Rating", value="4 - Good" ) improvement_suggestion = gr.Textbox( label="Improvement Suggestion (Optional)", lines=3, placeholder="Suggest how the response could be improved..." ) submit_feedback_btn = gr.Button("π Submit Feedback", variant="primary") with gr.Column(): selected_response = gr.Textbox( label="Selected Response", lines=10, placeholder="Select a response from the list to provide feedback..." ) feedback_status = gr.Textbox( label="Feedback Status", lines=2 ) # 6. SETTINGS SECTION with gr.Box(elem_classes=["content-section"]): # Settings sub-tabs with gr.Tabs() as settings_tabs: # General Settings with gr.Tab("General"): with gr.Row(): with gr.Column(): business_name = gr.Textbox( label="Business Name", value="Jay's Mobile Wash" ) with gr.Row(): business_phone = gr.Textbox( label="Business Phone", value="+18557059842" ) personal_phone = gr.Textbox( label="Jay's Phone", value="+15622289429" ) business_hours = gr.Textbox( label="Business Hours", value="6:00 AM - 6:00 PM" ) business_timezone = gr.Dropdown( choices=["America/Los_Angeles", "America/New_York", "America/Chicago"], label="Timezone", value="America/Los_Angeles" ) save_general_btn = gr.Button("πΎ Save General Settings", variant="primary") general_status = gr.Textbox( label="Status", interactive=False, lines=1 ) # API Settings with gr.Tab("API Settings"): with gr.Box(): gr.Markdown("## API Keys & Integrations") with gr.Row(): twilio_sid = gr.Textbox( label="Twilio Account SID", value="AC48c581ecc0e0f57fa4757052a2a1a1ba", type="password" ) twilio_token = gr.Textbox( label="Twilio Auth Token", value="ββββββββββββββββββββββββββββ", type="password" ) with gr.Row(): openai_key = gr.Textbox( label="OpenAI/DeepSeek API Key", value="ββββββββββββββββββββββββββββ", type="password" ) google_key = gr.Textbox( label="Google/Gemini API Key", value="ββββββββββββββββββββββββββββ", type="password" ) test_connection_btn = gr.Button("π Test Connections", variant="primary") api_status = gr.Textbox( label="API Status", interactive=False, lines=6 ) # Pricing Settings with gr.Tab("Pricing"): with gr.Box(): gr.Markdown("## Service Pricing") with gr.Row(): gr.Markdown("### Basic Wash") basic_car_price = gr.Number( label="Car Price ($)", value=60 ) basic_suv_price = gr.Number( label="SUV/Truck Price ($)", value=70 ) with gr.Row(): gr.Markdown("### Luxury Wash") luxury_car_price = gr.Number( label="Car Price ($)", value=130 ) luxury_suv_price = gr.Number( label="SUV/Truck Price ($)", value=140 ) with gr.Row(): gr.Markdown("### Max Detail") max_car_price = gr.Number( label="Car Price ($)", value=200 ) max_suv_price = gr.Number( label="SUV/Truck Price ($)", value=210 ) save_pricing_btn = gr.Button("π° Save Pricing", variant="primary") pricing_status = gr.Textbox( label="Status", interactive=False, lines=1 ) # Footer with gr.Row(elem_classes=["footer"]): gr.Markdown(f""" Jay's Mobile Wash AI Business System | Current Version: 1.0.0 | Last Updated: 2025-06-14 <br>HuggingFace Space: <a href="https://huggingface.co/spaces/jjmandog/Jayscallcenter">jjmandog/Jayscallcenter</a> | Β© 2025 Jay's Mobile Wash <br>Current Date and Time (UTC): {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} | User: jjmandog """) # System switch logic def update_system_state(enabled): """Update system state when toggle switch is changed""" system_state["system_enabled"] = enabled system_state["system_status"] = "Online" if enabled else "Offline" status_text = "System is now ENABLED and fully operational." if enabled else "System is now DISABLED. All operations paused." return status_text system_switch.change( update_system_state, inputs=[system_switch], outputs=[gr.Textbox(visible=False)] ) # Launch the app demo.launch() - Initial Deployment
44bea55
verified
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Jay's Mobile Wash AI Business System</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<script> | |
tailwind.config = { | |
theme: { | |
extend: { | |
colors: { | |
primary: '#003366', | |
secondary: '#CC0000', | |
accent: '#0080ff', | |
'light-bg': '#f0f6ff', | |
'dark-text': '#111827', | |
'light-text': '#ffffff', | |
success: '#10b981', | |
warning: '#f59e0b', | |
error: '#ef4444', | |
} | |
} | |
} | |
} | |
</script> | |
<style> | |
body, html { | |
font-family: 'Segoe UI', Arial, sans-serif; | |
background-color: #f0f6ff; | |
color: #111827; | |
height: 100%; | |
} | |
.stat-card { | |
border-left: 4px solid #0080ff; | |
} | |
.running-pill { | |
background-color: rgba(16, 185, 129, 0.1); | |
color: #10b981; | |
} | |
.status-dot { | |
background-color: #10b981; | |
} | |
.nav-button:hover:not(.active) { | |
background-color: rgba(255, 255, 255, 0.1); | |
} | |
/* Custom switch toggle */ | |
.switch { | |
position: relative; | |
display: inline-block; | |
width: 60px; | |
height: 30px; | |
} | |
.switch input { | |
opacity: 0; | |
width: 0; | |
height: 0; | |
} | |
.slider { | |
position: absolute; | |
cursor: pointer; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
background-color: #ccc; | |
transition: .4s; | |
border-radius: 30px; | |
} | |
.slider:before { | |
position: absolute; | |
content: ""; | |
height: 22px; | |
width: 22px; | |
left: 4px; | |
bottom: 4px; | |
background-color: white; | |
transition: .4s; | |
border-radius: 50%; | |
} | |
input:checked + .slider { | |
background-color: #10b981; | |
} | |
input:checked + .slider:before { | |
transform: translateX(30px); | |
} | |
.tab-content { | |
display: none; | |
} | |
.tab-content.active { | |
display: block; | |
} | |
.chat-bubble-received { | |
border-radius: 1rem 1rem 1rem 0; | |
background: #e5e7eb; | |
color: #111827; | |
} | |
.chat-bubble-sent { | |
border-radius: 1rem 1rem 0 1rem; | |
background: #0080ff; | |
color: #ffffff; | |
} | |
/* Responsive table */ | |
@media (max-width: 640px) { | |
.responsive-table th { | |
display: none; | |
} | |
.responsive-table tr { | |
display: block; | |
margin-bottom: 1rem; | |
border: 1px solid #e5e7eb; | |
border-radius: 0.5rem; | |
} | |
.responsive-table td { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
padding: 0.5rem 1rem; | |
border-bottom: none; | |
} | |
.responsive-table td::before { | |
content: attr(data-label); | |
font-weight: bold; | |
margin-right: 1rem; | |
color: #003366; | |
} | |
} | |
</style> | |
</head> | |
<body class="min-h-screen flex flex-col bg-light-bg"> | |
<!-- Header --> | |
<header class="bg-white shadow-sm"> | |
<div class="mx-auto px-4 py-3"> | |
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4"> | |
<div class="flex items-center space-x-4"> | |
<img src="https://jaysmobilewash.com/wp-content/uploads/2023/06/cropped-JaysLogo.png" | |
alt="Jay's Mobile Wash Logo" | |
class="h-14 w-auto"> | |
<h1 class="text-xl md:text-2xl font-bold text-primary">Jay's Mobile Wash AI Business System</h1> | |
</div> | |
<div class="flex items-center justify-end gap-4"> | |
<div class="flex items-center bg-light-bg py-1 px-3 rounded-full"> | |
<span class="status-dot inline-block rounded-full w-3 h-3 mr-2"></span> | |
<span class="font-semibold text-success">Running</span> | |
</div> | |
<label class="flex items-center cursor-pointer"> | |
<div class="relative"> | |
<input type="checkbox" class="sr-only" checked> | |
<div class="slider block w-16 h-8 rounded-full bg-gray-300"></div> | |
<div class="dot absolute left-1 top-1 bg-white w-6 h-6 rounded-full transition"></div> | |
</div> | |
<span class="ml-2 text-sm font-medium text-gray-700">System Status</span> | |
</label> | |
</div> | |
</div> | |
</div> | |
</header> | |
<!-- User Info Bar --> | |
<div class="bg-white shadow-sm rounded-lg mx-4 my-2"> | |
<div class="px-6 py-4"> | |
<div class="flex flex-col md:flex-row justify-between items-center"> | |
<div class="flex items-center"> | |
<div class="bg-gray-200 border-2 border-dashed rounded-xl w-16 h-16"></div> | |
<div class="ml-4"> | |
<h2 class="text-2xl font-bold text-dark-text">Jay's Mobile Wash AI Business System</h2> | |
</div> | |
</div> | |
<div class="mt-4 md:mt-0"> | |
<div class="text-right"> | |
<div class="flex items-center justify-end"> | |
<div class="bg-gray-200 border-2 border-dashed rounded-xl w-10 h-10"></div> | |
<span class="ml-2 font-bold">jjmandog</span> | |
</div> | |
<p class="text-gray-600">2025-06-14 02:50:32 UTC</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Navigation --> | |
<nav class="bg-primary rounded-lg mx-4 my-4"> | |
<div class="flex flex-wrap"> | |
<button class="nav-button active px-4 py-3 text-white font-medium text-sm md:text-base flex items-center"> | |
<i class="fas fa-chart-bar mr-2"></i> Dashboard | |
</button> | |
<button class="nav-button px-4 py-3 text-white font-medium text-sm md:text-base flex items-center"> | |
<i class="fas fa-phone-alt mr-2"></i> Call Management | |
</button> | |
<button class="nav-button px-4 py-3 text-white font-medium text-sm md:text-base flex items-center"> | |
<i class="fas fa-sms mr-2"></i> SMS Management | |
</button> | |
<button class="nav-button px-4 py-3 text-white font-medium text-sm md:text-base flex items-center"> | |
<i class="fas fa-calendar-alt mr-2"></i> Booking Management | |
</button> | |
<button class="nav-button px-4 py-3 text-white font-medium text-sm md:text-base flex items-center"> | |
<i class="fas fa-brain mr-2"></i> AI Training Center | |
</button> | |
<button class="nav-button px-4 py-3 text-white font-medium text-sm md:text-base flex items-center"> | |
<i class="fas fa-cog mr-2"></i> Settings | |
</button> | |
</div> | |
</nav> | |
<!-- Main Content --> | |
<main class="flex-1 mx-4 mb-8"> | |
<!-- Dashboard Tab --> | |
<div id="dashboard-tab" class="tab-content active"> | |
<!-- Business Overview --> | |
<div class="mb-8"> | |
<h2 class="text-2xl font-bold text-dark-text mb-4">Business Overview</h2> | |
<div class="grid grid-cols-1 md:grid-cols-3 gap-4"> | |
<!-- Stats Cards --> | |
<div class="stat-card bg-white p-5 rounded-lg shadow"> | |
<h3 class="text-lg font-semibold text-gray-600 flex items-center"> | |
<i class="fas fa-phone-alt text-accent mr-2"></i> Total Calls | |
</h3> | |
<p class="text-3xl font-bold mt-1">127</p> | |
<p class="text-sm text-gray-600 mt-2"> | |
Jay Answered: 85 | AI Handled: 34 | |
</p> | |
</div> | |
<div class="stat-card bg-white p-5 rounded-lg shadow"> | |
<h3 class="text-lg font-semibold text-gray-600 flex items-center"> | |
<i class="fas fa-sms text-accent mr-2"></i> SMS Interactions | |
</h3> | |
<p class="text-3xl font-bold mt-1">312</p> | |
<p class="text-sm text-gray-600 mt-2"> | |
AI Response Rate: 90% | |
</p> | |
</div> | |
<div class="stat-card bg-white p-5 rounded-lg shadow"> | |
<h3 class="text-lg font-semibold text-gray-600 flex items-center"> | |
<i class="fas fa-dollar-sign text-accent mr-2"></i> Monthly Revenue | |
</h3> | |
<p class="text-3xl font-bold mt-1">$12,480</p> | |
<p class="text-sm text-gray-600 mt-2"> | |
Confirmed Bookings: 96 | |
</p> | |
</div> | |
</div> | |
</div> | |
<!-- System Status --> | |
<div class="mb-8"> | |
<h2 class="text-2xl font-bold text-dark-text mb-4">System Status</h2> | |
<div class="bg-white rounded-lg shadow p-6"> | |
<div class="grid grid-cols-1 md:grid-cols-3 gap-4"> | |
<div> | |
<p class="text-gray-600"><strong>Twilio:</strong> <span class="text-success font-semibold">Connected</span></p> | |
</div> | |
<div> | |
<p class="text-gray-600"><strong>Database:</strong> <span class="text-success font-semibold">Online</span></p> | |
</div> | |
<div> | |
<p class="text-gray-600"><strong>AI Models:</strong> <span class="text-success font-semibold">Operational</span></p> | |
</div> | |
<div> | |
<p class="text-gray-600"><strong>Last Update:</strong> 2025-06-14 02:50:32</p> | |
</div> | |
<div> | |
<p class="text-gray-600"><strong>Server Load:</strong> 23%</p> | |
</div> | |
<div> | |
<p class="text-gray-600"><strong>Storage:</strong> 1.2 GB / 5 GB</p> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Recent Activity --> | |
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> | |
<!-- Recent Calls --> | |
<div> | |
<h2 class="text-2xl font-bold text-dark-text mb-4">Recent Call Activity</h2> | |
<div class="bg-white rounded-lg shadow overflow-hidden"> | |
<div class="overflow-x-auto"> | |
<table class="min-w-full responsive-table"> | |
<thead class="bg-primary text-light-text"> | |
<tr> | |
<th class="py-3 px-4 text-left">Timestamp</th> | |
<th class="py-3 px-4 text-left">Caller</th> | |
<th class="py-3 px-4 text-left">Duration</th> | |
<th class="py-3 px-4 text-left">Status</th> | |
<th class="py-3 px-4 text-left">Notes</th> | |
</tr> | |
</thead> | |
<tbody class="divide-y divide-gray-200"> | |
<tr> | |
<td class="py-3 px-4" data-label="Timestamp">2025-06-13 21:34:12</td> | |
<td class="py-3 px-4" data-label="Caller">+15622289429</td> | |
<td class="py-3 px-4" data-label="Duration">3m 24s</td> | |
<td class="py-3 px-4" data-label="Status">Jay Answered</td> | |
<td class="py-3 px-4" data-label="Notes">Booking confirmed</td> | |
</tr> | |
<tr> | |
<td class="py-3 px-4" data-label="Timestamp">2025-06-13 20:15:35</td> | |
<td class="py-3 px-4" data-label="Caller">+13108675309</td> | |
<td class="py-3 px-4" data-label="Duration">2m 10s</td> | |
<td class="py-3 px-4" data-label="Status">AI Handled</td> | |
<td class="py-3 px-4" data-label="Notes">Pricing inquiry</td> | |
</tr> | |
<tr> | |
<td class="py-3 px-4" data-label="Timestamp">2025-06-13 19:42:03</td> | |
<td class="py-3 px-4" data-label="Caller">+15625551234</td> | |
<td class="py-3 px-4" data-label="Duration">4m 02s</td> | |
<td class="py-3 px-4" data-label="Status">Jay Answered</td> | |
<td class="py-3 px-4" data-label="Notes">Scheduled luxury wash</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
</div> | |
<!-- Upcoming Bookings --> | |
<div> | |
<h2 class="text-2xl font-bold text-dark-text mb-4">Upcoming Bookings</h2> | |
<div class="bg-white rounded-lg shadow overflow-hidden"> | |
<div class="overflow-x-auto"> | |
<table class="min-w-full responsive-table"> | |
<thead class="bg-primary text-light-text"> | |
<tr> | |
<th class="py-3 px-4 text-left">Ref</th> | |
<th class="py-3 px-4 text-left">Customer</th> | |
<th class="py-3 px-4 text-left">Service</th> | |
<th class="py-3 px-4 text-left">Date</th> | |
<th class="py-3 px-4 text-left">Time</th> | |
<th class="py-3 px-4 text-left">Price</th> | |
<th class="py-3 px-4 text-left">Status</th> | |
</tr> | |
</thead> | |
<tbody class="divide-y divide-gray-200"> | |
<tr> | |
<td class="py-3 px-4" data-label="Ref">JMW2365</td> | |
<td class="py-3 px-4" data-label="Customer">John D.</td> | |
<td class="py-3 px-4" data-label="Service">Luxury Wash</td> | |
<td class="py-3 px-4" data-label="Date">2025-06-15</td> | |
<td class="py-3 px-4" data-label="Time">10:00 AM</td> | |
<td class="py-3 px-4" data-label="Price">$130</td> | |
<td class="py-3 px-4" data-label="Status"><span class="px-2 py-1 bg-green-100 text-green-800 rounded-full text-xs">Confirmed</span></td> | |
</tr> | |
<tr> | |
<td class="py-3 px-4" data-label="Ref">JMW2366</td> | |
<td class="py-3 px-4" data-label="Customer">Sarah M.</td> | |
<td class="py-3 px-4" data-label="Service">Basic Wash</td> | |
<td class="py-3 px-4" data-label="Date">2025-06-14</td> | |
<td class="py-3 px-4" data-label="Time">2:30 PM</td> | |
<td class="py-3 px-4" data-label="Price">$60</td> | |
<td class="py-3 px-4" data-label="Status"><span class="px-2 py-1 bg-yellow-100 text-yellow-800 rounded-full text-xs">Pending</span></td> | |
</tr> | |
<tr> | |
<td class="py-3 px-4" data-label="Ref">JMW2367</td> | |
<td class="py-3 px-4" data-label="Customer">Robert K.</td> | |
<td class="py-3 px-4" data-label="Service">Max Detail</td> | |
<td class="py-3 px-4" data-label="Date">2025-06-16</td> | |
<td class="py-3 px-4" data-label="Time">9:00 AM</td> | |
<td class="py-3 px-4" data-label="Price">$210</td> | |
<td class="py-3 px-4" data-label="Status"><span class="px-2 py-1 bg-green-100 text-green-800 rounded-full text-xs">Confirmed</span></td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Refresh Button --> | |
<div class="mt-6 flex justify-center"> | |
<button class="bg-accent hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-lg transition duration-300 flex items-center"> | |
<i class="fas fa-sync-alt mr-2"></i> Refresh Dashboard | |
</button> | |
</div> | |
</div> | |
<!-- Other Tabs (Placeholder Content) --> | |
<div id="call-management-tab" class="tab-content hidden"> | |
<div class="bg-white rounded-lg shadow p-6"> | |
<h2 class="text-2xl font-bold text-dark-text mb-6">Call Management</h2> | |
<p class="text-gray-600">This section would contain:</p> | |
<ul class="list-disc pl-6 text-gray-600 mt-3"> | |
<li>Active call monitoring</li> | |
<li>Call history records</li> | |
<li>Call routing settings</li> | |
<li>Call simulation tools</li> | |
</ul> | |
</div> | |
</div> | |
<div id="sms-management-tab" class="tab-content hidden"> | |
<div class="bg-white rounded-lg shadow p-6"> | |
<h2 class="text-2xl font-bold text-dark-text mb-6">SMS Management</h2> | |
<p class="text-gray-600">This section would contain:</p> | |
<ul class="list-disc pl-6 text-gray-600 mt-3"> | |
<li>SMS conversation interface</li> | |
<li>Contact management</li> | |
<li>SMS templates</li> | |
<li>Automation settings</li> | |
</ul> | |
</div> | |
</div> | |
<div id="booking-management-tab" class="tab-content hidden"> | |
<div class="bg-white rounded-lg shadow p-6"> | |
<h2 class="text-2xl font-bold text-dark-text mb-6">Booking Management</h2> | |
<p class="text-gray-600">This section would contain:</p> | |
<ul class="list-disc pl-6 text-gray-600 mt-3"> | |
<li>Calendar view of appointments</li> | |
<li>Booking creation form</li> | |
<li>Manage existing bookings</li> | |
<li>Service pricing configuration</li> | |
</ul> | |
</div> | |
</div> | |
<div id="ai-training-tab" class="tab-content hidden"> | |
<div class="bg-white rounded-lg shadow p-6"> | |
<h2 class="text-2xl font-bold text-dark-text mb-6">AI Training Center</h2> | |
<p class="text-gray-600">This section would contain:</p> | |
<ul class="list-disc pl-6 text-gray-600 mt-3"> | |
<li>SMS training interface</li> | |
<li>Voice response training</li> | |
<li>Call simulation scenarios</li> | |
<li>Response feedback system</li> | |
</ul> | |
</div> | |
</div> | |
<div id="settings-tab" class="tab-content hidden"> | |
<div class="bg-white rounded-lg shadow p-6"> | |
<h2 class="text-2xl font-bold text-dark-text mb-6">System Settings</h2> | |
<p class="text-gray-600">This section would contain:</p> | |
<ul class="list-disc pl-6 text-gray-600 mt-3"> | |
<li>Business profile settings</li> | |
<li>API key management</li> | |
<li>Service pricing configuration</li> | |
<li>User management</li> | |
<li>System preferences</li> | |
</ul> | |
</div> | |
</div> | |
</main> | |
<!-- Footer --> | |
<footer class="bg-white mt-auto border-t border-gray-200"> | |
<div class="mx-auto px-4 py-4 text-center text-gray-600 text-sm"> | |
<p>Jay's Mobile Wash AI Business System | Current Version: 1.0.0 | Last Updated: 2025-06-14</p> | |
<p>Β© 2025 Jay's Mobile Wash</p> | |
<p class="mt-2">Current Date and Time (UTC): 2025-06-14 02:50:32 | User: jjmandog</p> | |
</div> | |
</footer> | |
<script> | |
// Tab Navigation | |
const navButtons = document.querySelectorAll('.nav-button'); | |
const tabContents = document.querySelectorAll('.tab-content'); | |
// Tab mapping | |
const tabMap = { | |
0: 'dashboard-tab', | |
1: 'call-management-tab', | |
2: 'sms-management-tab', | |
3: 'booking-management-tab', | |
4: 'ai-training-tab', | |
5: 'settings-tab' | |
}; | |
navButtons.forEach((button, index) => { | |
button.addEventListener('click', () => { | |
// Remove active class from all buttons and tabs | |
navButtons.forEach(btn => btn.classList.remove('active')); | |
tabContents.forEach(tab => tab.classList.add('hidden')); | |
// Add active class to current button | |
button.classList.add('active'); | |
// Show the corresponding tab | |
const tabId = tabMap[index]; | |
document.getElementById(tabId).classList.remove('hidden'); | |
document.getElementById(tabId).classList.add('active'); | |
}); | |
}); | |
// Toggle Switch | |
const toggleSwitch = document.querySelector('input[type="checkbox"]'); | |
toggleSwitch.addEventListener('change', function() { | |
const statusText = document.querySelector('.running-pill'); | |
const statusDot = document.querySelector('.status-dot'); | |
if (this.checked) { | |
statusText.textContent = 'Running'; | |
statusText.classList.add('text-success'); | |
statusText.classList.remove('text-error'); | |
statusDot.classList.add('bg-success'); | |
statusDot.classList.remove('bg-error'); | |
} else { | |
statusText.textContent = 'Offline'; | |
statusText.classList.add('text-error'); | |
statusText.classList.remove('text-success'); | |
statusDot.classList.add('bg-error'); | |
statusDot.classList.remove('bg-success'); | |
} | |
}); | |
// Responsive table labels for mobile | |
window.addEventListener('resize', setupResponsiveTables); | |
function setupResponsiveTables() { | |
const tables = document.querySelectorAll('.responsive-table'); | |
tables.forEach(table => { | |
if (window.innerWidth < 640) { | |
// Add data-label attributes to each cell | |
const headers = Array.from(table.querySelectorAll('th')).map(th => th.textContent); | |
const rows = table.querySelectorAll('tbody tr'); | |
rows.forEach(row => { | |
const cells = row.querySelectorAll('td'); | |
cells.forEach((cell, index) => { | |
cell.setAttribute('data-label', headers[index]); | |
}); | |
}); | |
} | |
}); | |
} | |
// Initialize on load | |
setupResponsiveTables(); | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://deepsite.hf.co/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://deepsite.hf.co" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 𧬠<a href="https://deepsite.hf.co?remix=jjmandog/vcdxxx" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |