cx_ai_agent_v1 / ui /theme.py
muzakkirhussain011's picture
Add application files (text files only)
8bab08d
"""
Enterprise UI Theme for CX AI Agent
Professional styling and custom Gradio theme
"""
import gradio as gr
def get_enterprise_theme():
"""
Return enterprise-grade Gradio theme with professional styling
"""
return gr.themes.Soft(
primary_hue="blue",
secondary_hue="slate",
neutral_hue="slate",
font=("Inter", gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"),
font_mono=("'IBM Plex Mono'", gr.themes.GoogleFont("IBM Plex Mono"), "monospace"),
).set(
# Button styling
button_primary_background_fill="*primary_600",
button_primary_background_fill_hover="*primary_700",
button_primary_text_color="white",
button_secondary_background_fill="*neutral_100",
button_secondary_background_fill_hover="*neutral_200",
button_secondary_text_color="*neutral_800",
# Input styling
input_background_fill="white",
input_border_color="*neutral_300",
input_shadow="0 1px 2px 0 rgba(0, 0, 0, 0.05)",
# Container styling
block_background_fill="white",
block_border_width="1px",
block_border_color="*neutral_200",
block_shadow="0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1)",
block_radius="0.75rem",
block_padding="1.5rem",
# Panel styling
panel_background_fill="*neutral_50",
panel_border_width="1px",
panel_border_color="*neutral_200",
)
def get_custom_css():
"""
Return custom CSS for enterprise styling
"""
return """
/* Enterprise theme customizations */
.gradio-container {
max-width: 1600px !important;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
}
/* Header styling */
.app-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 2rem;
color: white;
border-radius: 0.75rem;
margin-bottom: 2rem;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
.app-header h1 {
margin: 0;
font-size: 2rem;
font-weight: 700;
}
.app-header p {
margin: 0.5rem 0 0 0;
opacity: 0.9;
font-size: 1rem;
}
/* Navigation tabs */
.nav-tabs {
display: flex;
gap: 0.5rem;
margin-bottom: 2rem;
border-bottom: 2px solid #e5e7eb;
padding-bottom: 0;
}
.nav-tab {
padding: 0.75rem 1.5rem !important;
border: none !important;
border-bottom: 3px solid transparent !important;
background: transparent !important;
font-weight: 500 !important;
color: #6b7280 !important;
cursor: pointer;
transition: all 0.2s;
}
.nav-tab:hover {
color: #374151 !important;
background: #f3f4f6 !important;
border-radius: 0.5rem 0.5rem 0 0 !important;
}
.nav-tab.active {
color: #3b82f6 !important;
border-bottom-color: #3b82f6 !important;
background: #eff6ff !important;
}
/* Metric cards */
.metric-card {
background: white;
border: 1px solid #e5e7eb;
border-radius: 0.75rem;
padding: 1.5rem;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
transition: transform 0.2s, box-shadow 0.2s;
}
.metric-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.metric-value {
font-size: 2.25rem;
font-weight: 700;
color: #111827;
margin: 0.5rem 0;
}
.metric-label {
font-size: 0.875rem;
font-weight: 500;
color: #6b7280;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.metric-change {
font-size: 0.875rem;
font-weight: 500;
margin-top: 0.5rem;
}
.metric-change.positive {
color: #10b981;
}
.metric-change.negative {
color: #ef4444;
}
/* Status badges */
.status-badge {
display: inline-block;
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.status-active {
background: #d1fae5;
color: #065f46;
}
.status-draft {
background: #e5e7eb;
color: #374151;
}
.status-paused {
background: #fef3c7;
color: #92400e;
}
.status-completed {
background: #dbeafe;
color: #1e40af;
}
/* Data tables */
.data-table {
width: 100%;
border-collapse: collapse;
background: white;
border-radius: 0.75rem;
overflow: hidden;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
}
.data-table thead {
background: #f9fafb;
border-bottom: 2px solid #e5e7eb;
}
.data-table th {
padding: 0.75rem 1rem;
text-align: left;
font-size: 0.75rem;
font-weight: 600;
color: #6b7280;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.data-table td {
padding: 1rem;
border-bottom: 1px solid #f3f4f6;
color: #374151;
}
.data-table tr:hover {
background: #f9fafb;
}
.data-table tr:last-child td {
border-bottom: none;
}
/* Progress bars */
.progress-bar {
width: 100%;
height: 0.5rem;
background: #e5e7eb;
border-radius: 9999px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #3b82f6 0%, #8b5cf6 100%);
border-radius: 9999px;
transition: width 0.3s ease;
}
/* Activity feed */
.activity-feed {
background: white;
border: 1px solid #e5e7eb;
border-radius: 0.75rem;
padding: 1.5rem;
}
.activity-item {
display: flex;
gap: 1rem;
padding: 1rem 0;
border-bottom: 1px solid #f3f4f6;
}
.activity-item:last-child {
border-bottom: none;
}
.activity-icon {
width: 2.5rem;
height: 2.5rem;
border-radius: 9999px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.25rem;
flex-shrink: 0;
}
.activity-content {
flex: 1;
}
.activity-title {
font-weight: 500;
color: #111827;
margin-bottom: 0.25rem;
}
.activity-meta {
font-size: 0.875rem;
color: #6b7280;
}
/* Charts */
.chart-container {
background: white;
border: 1px solid #e5e7eb;
border-radius: 0.75rem;
padding: 1.5rem;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
}
.chart-title {
font-size: 1.125rem;
font-weight: 600;
color: #111827;
margin-bottom: 1rem;
}
/* Forms */
.form-group {
margin-bottom: 1.5rem;
}
.form-label {
display: block;
font-size: 0.875rem;
font-weight: 500;
color: #374151;
margin-bottom: 0.5rem;
}
.form-help {
font-size: 0.75rem;
color: #6b7280;
margin-top: 0.25rem;
}
/* Empty states */
.empty-state {
text-align: center;
padding: 4rem 2rem;
color: #6b7280;
}
.empty-state-icon {
font-size: 3rem;
margin-bottom: 1rem;
opacity: 0.5;
}
.empty-state-title {
font-size: 1.25rem;
font-weight: 600;
color: #374151;
margin-bottom: 0.5rem;
}
.empty-state-description {
font-size: 0.875rem;
max-width: 28rem;
margin: 0 auto 1.5rem;
}
/* Loading states */
.loading-spinner {
display: inline-block;
width: 1.5rem;
height: 1.5rem;
border: 3px solid #e5e7eb;
border-top-color: #3b82f6;
border-radius: 50%;
animation: spin 0.6s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Utility classes */
.text-center { text-align: center; }
.text-right { text-align: right; }
.text-sm { font-size: 0.875rem; }
.text-xs { font-size: 0.75rem; }
.font-semibold { font-weight: 600; }
.font-bold { font-weight: 700; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-4 { margin-bottom: 1rem; }
.mt-2 { margin-top: 0.5rem; }
.mt-4 { margin-top: 1rem; }
.p-4 { padding: 1rem; }
.flex { display: flex; }
.gap-2 { gap: 0.5rem; }
.gap-4 { gap: 1rem; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
"""
def create_header():
"""Create enterprise header component"""
return gr.HTML("""
<div class="app-header">
<h1>🤖 CX AI Agent - Enterprise Edition</h1>
<p>Autonomous Multi-Agent Customer Experience Platform powered by MCP</p>
</div>
""")
def create_metric_card(label: str, value: str, change: str = None, change_positive: bool = True):
"""Create a metric card component"""
change_class = "positive" if change_positive else "negative"
change_arrow = "↑" if change_positive else "↓"
change_html = f'<div class="metric-change {change_class}">{change_arrow} {change}</div>' if change else ""
return f"""
<div class="metric-card">
<div class="metric-label">{label}</div>
<div class="metric-value">{value}</div>
{change_html}
</div>
"""
def create_status_badge(status: str):
"""Create a status badge"""
status_lower = status.lower()
return f'<span class="status-badge status-{status_lower}">{status}</span>'
def create_progress_bar(percentage: float):
"""Create a progress bar"""
return f"""
<div class="progress-bar">
<div class="progress-fill" style="width: {percentage}%"></div>
</div>
"""
def create_empty_state(icon: str, title: str, description: str, action_text: str = None):
"""Create an empty state component"""
action_html = f'<button class="btn btn-primary">{action_text}</button>' if action_text else ""
return f"""
<div class="empty-state">
<div class="empty-state-icon">{icon}</div>
<div class="empty-state-title">{title}</div>
<div class="empty-state-description">{description}</div>
{action_html}
</div>
"""