use crate::shared::state::AppState; use axum::{ extract::{Query, State}, response::{Html, IntoResponse}, routing::get, Router, }; use serde::{Deserialize, Serialize}; use std::sync::Arc; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SearchQuery { pub q: Option, pub category: Option, } pub fn configure_sources_routes() -> Router> { Router::new() // Tab endpoints - match frontend hx-get endpoints .route("/api/sources/prompts", get(handle_prompts)) .route("/api/sources/templates", get(handle_templates)) .route("/api/sources/news", get(handle_news)) .route("/api/sources/mcp-servers", get(handle_mcp_servers)) .route("/api/sources/llm-tools", get(handle_llm_tools)) .route("/api/sources/models", get(handle_models)) // Search .route("/api/sources/search", get(handle_search)) } /// GET /api/sources/prompts - Prompts tab content pub async fn handle_prompts( State(_state): State>, Query(params): Query, ) -> impl IntoResponse { let category = params.category.unwrap_or_else(|| "all".to_string()); let prompts = get_prompts_data(&category); let mut html = String::new(); html.push_str("
"); // Categories sidebar html.push_str(""); // Prompts grid html.push_str("
"); html.push_str("
"); for prompt in &prompts { html.push_str("
"); html.push_str("
"); html.push_str(""); html.push_str(&prompt.icon); html.push_str(""); html.push_str("

"); html.push_str(&html_escape(&prompt.title)); html.push_str("

"); html.push_str("
"); html.push_str("

"); html.push_str(&html_escape(&prompt.description)); html.push_str("

"); html.push_str("
"); html.push_str(""); html.push_str(&html_escape(&prompt.category)); html.push_str(""); html.push_str(""); html.push_str("
"); html.push_str("
"); } if prompts.is_empty() { html.push_str("
"); html.push_str("

No prompts found in this category

"); html.push_str("
"); } html.push_str("
"); html.push_str("
"); html.push_str("
"); Html(html) } /// GET /api/sources/templates - Templates tab content pub async fn handle_templates(State(_state): State>) -> impl IntoResponse { let templates = get_templates_data(); let mut html = String::new(); html.push_str("
"); html.push_str("
"); html.push_str("

Bot Templates

"); html.push_str("

Pre-built bot configurations ready to deploy

"); html.push_str("
"); html.push_str("
"); for template in &templates { html.push_str("
"); html.push_str("
"); html.push_str(&template.icon); html.push_str("
"); html.push_str("
"); html.push_str("

"); html.push_str(&html_escape(&template.name)); html.push_str("

"); html.push_str("

"); html.push_str(&html_escape(&template.description)); html.push_str("

"); html.push_str("
"); html.push_str(""); html.push_str(&html_escape(&template.category)); html.push_str(""); html.push_str("
"); html.push_str("
"); html.push_str("
"); html.push_str(""); html.push_str(""); html.push_str("
"); html.push_str("
"); } html.push_str("
"); html.push_str("
"); Html(html) } /// GET /api/sources/news - News tab content pub async fn handle_news(State(_state): State>) -> impl IntoResponse { let news_items = vec![ ("", "General Bots 6.0 Released", "Major update with improved performance and new features", "2 hours ago"), ("", "New MCP Server Integration", "Connect to external tools more easily with our new MCP support", "1 day ago"), ("", "Analytics Dashboard Update", "Real-time metrics and improved visualizations", "3 days ago"), ("", "Security Enhancement", "Enhanced encryption and authentication options", "1 week ago"), ("", "Multi-language Support", "Now supporting 15+ languages for bot conversations", "2 weeks ago"), ]; let mut html = String::new(); html.push_str("
"); html.push_str("
"); html.push_str("

Latest News

"); html.push_str("

Updates and announcements from the General Bots team

"); html.push_str("
"); html.push_str("
"); for (icon, title, description, time) in &news_items { html.push_str("
"); html.push_str("
"); html.push_str(icon); html.push_str("
"); html.push_str("
"); html.push_str("

"); html.push_str(&html_escape(title)); html.push_str("

"); html.push_str("

"); html.push_str(&html_escape(description)); html.push_str("

"); html.push_str(""); html.push_str(time); html.push_str(""); html.push_str("
"); html.push_str("
"); } html.push_str("
"); html.push_str("
"); Html(html) } /// GET /api/sources/mcp-servers - MCP Servers tab content pub async fn handle_mcp_servers(State(_state): State>) -> impl IntoResponse { let servers = vec![ ("", "Database Server", "PostgreSQL, MySQL, SQLite connections", "Active", true), ("", "Filesystem Server", "Local and cloud file access", "Active", true), ("", "Web Server", "HTTP/REST API integrations", "Active", true), ("", "Email Server", "SMTP/IMAP email handling", "Inactive", false), ("", "Slack Server", "Slack workspace integration", "Active", true), ("", "Analytics Server", "Data processing and reporting", "Active", true), ]; let mut html = String::new(); html.push_str("
"); html.push_str("
"); html.push_str("

MCP Servers

"); html.push_str("

Model Context Protocol servers for extended capabilities

"); html.push_str(""); html.push_str("
"); html.push_str("
"); for (icon, name, description, status, is_active) in &servers { let status_class = if *is_active { "status-active" } else { "status-inactive" }; html.push_str("
"); html.push_str("
"); html.push_str(icon); html.push_str("
"); html.push_str("
"); html.push_str("

"); html.push_str(&html_escape(name)); html.push_str("

"); html.push_str("

"); html.push_str(&html_escape(description)); html.push_str("

"); html.push_str("
"); html.push_str("
"); html.push_str(status); html.push_str("
"); html.push_str("
"); html.push_str(""); if *is_active { html.push_str(""); } else { html.push_str(""); } html.push_str("
"); html.push_str("
"); } html.push_str("
"); html.push_str("
"); Html(html) } /// GET /api/sources/llm-tools - LLM Tools tab content pub async fn handle_llm_tools(State(_state): State>) -> impl IntoResponse { let tools = vec![ ("", "Web Search", "Search the web for real-time information", true), ("", "Calculator", "Perform mathematical calculations", true), ("", "Calendar", "Manage calendar events and schedules", true), ("", "Note Taking", "Create and manage notes", true), ("", "Weather", "Get weather forecasts and conditions", false), ("", "News Reader", "Fetch and summarize news articles", false), ("", "URL Fetcher", "Retrieve and parse web content", true), ("", "Code Executor", "Run code snippets safely", false), ]; let mut html = String::new(); html.push_str("
"); html.push_str("
"); html.push_str("

LLM Tools

"); html.push_str("

Extend your bot's capabilities with these tools

"); html.push_str("
"); html.push_str("
"); for (icon, name, description, enabled) in &tools { let enabled_class = if *enabled { "enabled" } else { "disabled" }; html.push_str("
"); html.push_str("
"); html.push_str(icon); html.push_str("
"); html.push_str("
"); html.push_str("

"); html.push_str(&html_escape(name)); html.push_str("

"); html.push_str("

"); html.push_str(&html_escape(description)); html.push_str("

"); html.push_str("
"); html.push_str(""); html.push_str("
"); } html.push_str("
"); html.push_str("
"); Html(html) } /// GET /api/sources/models - Models tab content pub async fn handle_models(State(_state): State>) -> impl IntoResponse { let models = vec![ ("🧠", "GPT-4o", "OpenAI", "Latest multimodal model with vision capabilities", "Active"), ("🧠", "GPT-4o-mini", "OpenAI", "Fast and efficient for most tasks", "Active"), ("🦙", "Llama 3.1 70B", "Meta", "Open source large language model", "Available"), ("🔷", "Claude 3.5 Sonnet", "Anthropic", "Advanced reasoning and analysis", "Available"), ("💎", "Gemini Pro", "Google", "Multimodal AI with long context", "Available"), ("", "Mistral Large", "Mistral AI", "European AI model with strong performance", "Available"), ]; let mut html = String::new(); html.push_str("
"); html.push_str("
"); html.push_str("

AI Models

"); html.push_str("

Available language models for your bots

"); html.push_str("
"); html.push_str("
"); for (icon, name, provider, description, status) in &models { let status_class = if *status == "Active" { "model-active" } else { "model-available" }; html.push_str("
"); html.push_str("
"); html.push_str(icon); html.push_str("
"); html.push_str("
"); html.push_str("
"); html.push_str("

"); html.push_str(&html_escape(name)); html.push_str("

"); html.push_str(""); html.push_str(&html_escape(provider)); html.push_str(""); html.push_str("
"); html.push_str("

"); html.push_str(&html_escape(description)); html.push_str("

"); html.push_str("
"); html.push_str(""); html.push_str(status); html.push_str(""); if *status == "Active" { html.push_str(""); } else { html.push_str(""); } html.push_str("
"); html.push_str("
"); html.push_str("
"); } html.push_str("
"); html.push_str("
"); Html(html) } /// GET /api/sources/search - Search across all sources pub async fn handle_search( State(_state): State>, Query(params): Query, ) -> impl IntoResponse { let query = params.q.unwrap_or_default(); if query.is_empty() { return Html("

Enter a search term

".to_string()); } let query_lower = query.to_lowercase(); // Search across prompts let prompts = get_prompts_data("all"); let matching_prompts: Vec<_> = prompts .iter() .filter(|p| { p.title.to_lowercase().contains(&query_lower) || p.description.to_lowercase().contains(&query_lower) }) .collect(); // Search across templates let templates = get_templates_data(); let matching_templates: Vec<_> = templates .iter() .filter(|t| { t.name.to_lowercase().contains(&query_lower) || t.description.to_lowercase().contains(&query_lower) }) .collect(); let mut html = String::new(); html.push_str("
"); html.push_str("
"); html.push_str("

Search Results for \""); html.push_str(&html_escape(&query)); html.push_str("\"

"); html.push_str("
"); if matching_prompts.is_empty() && matching_templates.is_empty() { html.push_str("
"); html.push_str("

No results found

"); html.push_str("

Try different keywords

"); html.push_str("
"); } else { if !matching_prompts.is_empty() { html.push_str("
"); html.push_str("

Prompts ("); html.push_str(&matching_prompts.len().to_string()); html.push_str(")

"); html.push_str("
"); for prompt in matching_prompts { html.push_str("
"); html.push_str(""); html.push_str(&prompt.icon); html.push_str(""); html.push_str("
"); html.push_str(""); html.push_str(&html_escape(&prompt.title)); html.push_str(""); html.push_str("

"); html.push_str(&html_escape(&prompt.description)); html.push_str("

"); html.push_str("
"); html.push_str("
"); } html.push_str("
"); html.push_str("
"); } if !matching_templates.is_empty() { html.push_str("
"); html.push_str("

Templates ("); html.push_str(&matching_templates.len().to_string()); html.push_str(")

"); html.push_str("
"); for template in matching_templates { html.push_str("
"); html.push_str(""); html.push_str(&template.icon); html.push_str(""); html.push_str("
"); html.push_str(""); html.push_str(&html_escape(&template.name)); html.push_str(""); html.push_str("

"); html.push_str(&html_escape(&template.description)); html.push_str("

"); html.push_str("
"); html.push_str("
"); } html.push_str("
"); html.push_str("
"); } } html.push_str("
"); Html(html) } // Data structures struct PromptData { id: String, title: String, description: String, category: String, icon: String, } struct TemplateData { name: String, description: String, category: String, icon: String, } fn get_prompts_data(category: &str) -> Vec { let all_prompts = vec![ PromptData { id: "summarize".to_string(), title: "Summarize Text".to_string(), description: "Create concise summaries of long documents or articles".to_string(), category: "writing".to_string(), icon: "".to_string(), }, PromptData { id: "code-review".to_string(), title: "Code Review".to_string(), description: "Analyze code for bugs, improvements, and best practices".to_string(), category: "coding".to_string(), icon: "".to_string(), }, PromptData { id: "data-analysis".to_string(), title: "Data Analysis".to_string(), description: "Extract insights and patterns from data sets".to_string(), category: "analysis".to_string(), icon: "".to_string(), }, PromptData { id: "creative-writing".to_string(), title: "Creative Writing".to_string(), description: "Generate stories, poems, and creative content".to_string(), category: "creative".to_string(), icon: "".to_string(), }, PromptData { id: "email-draft".to_string(), title: "Email Draft".to_string(), description: "Compose professional emails quickly".to_string(), category: "business".to_string(), icon: "".to_string(), }, PromptData { id: "explain-concept".to_string(), title: "Explain Concept".to_string(), description: "Break down complex topics into simple explanations".to_string(), category: "education".to_string(), icon: "".to_string(), }, PromptData { id: "debug-code".to_string(), title: "Debug Code".to_string(), description: "Find and fix issues in your code".to_string(), category: "coding".to_string(), icon: "🐛".to_string(), }, PromptData { id: "meeting-notes".to_string(), title: "Meeting Notes".to_string(), description: "Organize and format meeting discussions".to_string(), category: "business".to_string(), icon: "".to_string(), }, ]; if category == "all" { all_prompts } else { all_prompts .into_iter() .filter(|p| p.category == category) .collect() } } fn get_templates_data() -> Vec { vec![ TemplateData { name: "Customer Support Bot".to_string(), description: "Handle customer inquiries and support tickets automatically".to_string(), category: "Support".to_string(), icon: "🎧".to_string(), }, TemplateData { name: "FAQ Bot".to_string(), description: "Answer frequently asked questions from your knowledge base".to_string(), category: "Support".to_string(), icon: "".to_string(), }, TemplateData { name: "Lead Generation Bot".to_string(), description: "Qualify leads and collect prospect information".to_string(), category: "Sales".to_string(), icon: "".to_string(), }, TemplateData { name: "Onboarding Bot".to_string(), description: "Guide new users through your product or service".to_string(), category: "HR".to_string(), icon: "👋".to_string(), }, TemplateData { name: "Survey Bot".to_string(), description: "Collect feedback through conversational surveys".to_string(), category: "Research".to_string(), icon: "".to_string(), }, TemplateData { name: "Appointment Scheduler".to_string(), description: "Book and manage appointments automatically".to_string(), category: "Productivity".to_string(), icon: "".to_string(), }, ] } fn html_escape(s: &str) -> String { s.replace('&', "&") .replace('<', "<") .replace('>', ">") .replace('"', """) .replace('\'', "'") }