AI Image and Audio Generation Improvements.

AI Video Generation Pre-Flight Checklist. Cost Estimate Improvements.
This commit is contained in:
ajaysi
2025-12-25 16:26:08 +05:30
parent 59913bffa9
commit 7512933c65
163 changed files with 8938 additions and 37401 deletions

View File

@@ -1,634 +0,0 @@
"""
Enhanced UI Cards with modern styling and improved functionality.
"""
import streamlit as st
from typing import Dict, List, Optional, Callable
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime
def apply_cards_styling():
"""Apply modern CSS styling for cards."""
st.markdown("""
<style>
/* Modern Card Styles */
.modern-card {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20px);
border-radius: 16px;
padding: 1.5rem;
margin: 1rem 0;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.modern-card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
}
.modern-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(135deg, #1DA1F2, #0C85D0);
}
.feature-card {
background: white;
border-radius: 12px;
padding: 1.5rem;
margin: 0.75rem 0;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
border: 1px solid #E1E8ED;
transition: all 0.3s ease;
cursor: pointer;
}
.feature-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 30px rgba(29, 161, 242, 0.15);
border-color: #1DA1F2;
}
.feature-card-header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1rem;
}
.feature-icon {
font-size: 2rem;
width: 60px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #E6F7FF, #F0F9FF);
border-radius: 12px;
border: 2px solid #91D5FF;
}
.feature-title {
font-size: 1.25rem;
font-weight: 600;
color: #2D3748;
margin: 0;
}
.feature-description {
color: #657786;
font-size: 0.95rem;
line-height: 1.5;
margin-bottom: 1rem;
}
.feature-stats {
display: flex;
gap: 1rem;
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid #E1E8ED;
}
.stat-item {
text-align: center;
flex: 1;
}
.stat-value {
font-size: 1.5rem;
font-weight: 700;
color: #1DA1F2;
display: block;
}
.stat-label {
font-size: 0.8rem;
color: #657786;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.tweet-card {
background: white;
border: 1px solid #E1E8ED;
border-radius: 16px;
padding: 1.5rem;
margin: 1rem 0;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
position: relative;
}
.tweet-card::before {
content: "🐦";
position: absolute;
top: -10px;
left: 20px;
background: white;
padding: 0 10px;
font-size: 1.2rem;
}
.tweet-content {
font-size: 1.1rem;
line-height: 1.5;
color: #14171A;
margin-bottom: 1rem;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.tweet-metadata {
display: flex;
justify-content: space-between;
align-items: center;
color: #657786;
font-size: 0.9rem;
border-top: 1px solid #E1E8ED;
padding-top: 1rem;
}
.engagement-badge {
background: linear-gradient(135deg, #52C41A, #73D13D);
color: white;
padding: 0.5rem 1rem;
border-radius: 20px;
font-weight: 600;
font-size: 0.9rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.character-badge {
padding: 0.25rem 0.75rem;
border-radius: 20px;
font-weight: 600;
font-size: 0.8rem;
}
.char-good { background: #E6F7FF; color: #1890FF; }
.char-warning { background: #FFF7E6; color: #FA8C16; }
.char-danger { background: #FFF1F0; color: #F5222D; }
.card-actions {
display: flex;
gap: 0.5rem;
margin-top: 1rem;
flex-wrap: wrap;
}
.action-button {
background: #F7F9FA;
border: 1px solid #E1E8ED;
border-radius: 8px;
padding: 0.5rem 1rem;
color: #657786;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.action-button:hover {
background: #1DA1F2;
color: white;
border-color: #1DA1F2;
transform: translateY(-1px);
}
.action-button.primary {
background: #1DA1F2;
color: white;
border-color: #1DA1F2;
}
.action-button.primary:hover {
background: #0C85D0;
border-color: #0C85D0;
}
.metrics-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 1rem;
margin: 1rem 0;
}
.metric-card {
background: white;
border-radius: 8px;
padding: 1rem;
text-align: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
border: 1px solid #E1E8ED;
}
.metric-value {
font-size: 1.5rem;
font-weight: 700;
color: #1DA1F2;
display: block;
margin-bottom: 0.25rem;
}
.metric-label {
font-size: 0.8rem;
color: #657786;
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* Responsive Design */
@media (max-width: 768px) {
.modern-card, .feature-card, .tweet-card {
margin: 0.5rem;
padding: 1rem;
}
.feature-card-header {
flex-direction: column;
text-align: center;
}
.feature-stats {
flex-direction: column;
gap: 0.5rem;
}
.card-actions {
justify-content: center;
}
.metrics-grid {
grid-template-columns: repeat(2, 1fr);
}
}
</style>
""", unsafe_allow_html=True)
class FeatureCard:
"""Modern feature card component."""
def __init__(
self,
title: str,
description: str,
icon: str = "🔧",
stats: Optional[Dict[str, any]] = None,
actions: Optional[List[Dict]] = None,
on_click: Optional[Callable] = None
):
self.title = title
self.description = description
self.icon = icon
self.stats = stats or {}
self.actions = actions or []
self.on_click = on_click
def render(self):
"""Render the feature card."""
apply_cards_styling()
# Create stats HTML
stats_html = ""
if self.stats:
stats_items = []
for label, value in self.stats.items():
stats_items.append(f"""
<div class="stat-item">
<span class="stat-value">{value}</span>
<span class="stat-label">{label}</span>
</div>
""")
stats_html = f"""
<div class="feature-stats">
{''.join(stats_items)}
</div>
"""
# Create actions HTML
actions_html = ""
if self.actions:
action_buttons = []
for action in self.actions:
button_class = "action-button"
if action.get("primary", False):
button_class += " primary"
action_buttons.append(f"""
<button class="{button_class}" onclick="{action.get('onclick', '')}">
{action.get('icon', '')} {action.get('label', 'Action')}
</button>
""")
actions_html = f"""
<div class="card-actions">
{''.join(action_buttons)}
</div>
"""
# Render the card
card_html = f"""
<div class="feature-card" onclick="{self.on_click or ''}">
<div class="feature-card-header">
<div class="feature-icon">{self.icon}</div>
<div>
<h3 class="feature-title">{self.title}</h3>
</div>
</div>
<p class="feature-description">{self.description}</p>
{stats_html}
{actions_html}
</div>
"""
st.markdown(card_html, unsafe_allow_html=True)
class TweetCard:
"""Modern tweet card component."""
def __init__(
self,
content: str,
engagement_score: int = 0,
hashtags: List[str] = None,
emojis: List[str] = None,
metrics: Optional[Dict] = None,
timestamp: Optional[str] = None,
on_copy: Optional[Callable] = None,
on_save: Optional[Callable] = None,
on_edit: Optional[Callable] = None,
on_post: Optional[Callable] = None
):
self.content = content
self.engagement_score = engagement_score
self.hashtags = hashtags or []
self.emojis = emojis or []
self.metrics = metrics or {}
self.timestamp = timestamp or datetime.now().strftime("%Y-%m-%d %H:%M")
self.on_copy = on_copy
self.on_save = on_save
self.on_edit = on_edit
self.on_post = on_post
def _get_character_info(self):
"""Get character count information."""
full_text = f"{self.content} {' '.join(self.hashtags)}"
count = len(full_text)
remaining = 280 - count
if count <= 240:
status_class = "char-good"
elif count <= 270:
status_class = "char-warning"
else:
status_class = "char-danger"
return {
"count": count,
"remaining": remaining,
"status_class": status_class
}
def render(self):
"""Render the tweet card."""
apply_cards_styling()
char_info = self._get_character_info()
full_content = f"{self.content} {' '.join(self.hashtags)}"
# Create metrics HTML
metrics_html = ""
if self.metrics:
metric_items = []
for label, value in self.metrics.items():
metric_items.append(f"""
<div class="metric-card">
<span class="metric-value">{value}</span>
<span class="metric-label">{label}</span>
</div>
""")
metrics_html = f"""
<div class="metrics-grid">
{''.join(metric_items)}
</div>
"""
# Create actions
actions = []
if self.on_copy:
actions.append('<button class="action-button" onclick="copyTweet()">📋 Copy</button>')
if self.on_save:
actions.append('<button class="action-button" onclick="saveTweet()">💾 Save</button>')
if self.on_edit:
actions.append('<button class="action-button" onclick="editTweet()">✏️ Edit</button>')
if self.on_post:
actions.append('<button class="action-button primary" onclick="postTweet()">🐦 Post</button>')
actions_html = f'<div class="card-actions">{"".join(actions)}</div>' if actions else ""
# Render the card
card_html = f"""
<div class="tweet-card">
<div class="tweet-content">{full_content}</div>
{metrics_html}
<div class="tweet-metadata">
<div class="engagement-badge">
📊 {self.engagement_score}% Engagement
</div>
<div class="character-badge {char_info['status_class']}">
{char_info['count']}/280
</div>
</div>
{actions_html}
</div>
"""
st.markdown(card_html, unsafe_allow_html=True)
class MetricsCard:
"""Modern metrics display card."""
def __init__(
self,
title: str,
metrics: Dict[str, any],
chart_data: Optional[Dict] = None,
trend: Optional[str] = None
):
self.title = title
self.metrics = metrics
self.chart_data = chart_data
self.trend = trend
def render(self):
"""Render the metrics card."""
apply_cards_styling()
# Create metrics grid
metric_items = []
for label, value in self.metrics.items():
metric_items.append(f"""
<div class="metric-card">
<span class="metric-value">{value}</span>
<span class="metric-label">{label}</span>
</div>
""")
metrics_grid = f"""
<div class="metrics-grid">
{''.join(metric_items)}
</div>
"""
# Add trend indicator
trend_html = ""
if self.trend:
trend_color = "#52C41A" if "up" in self.trend.lower() else "#F5222D"
trend_icon = "📈" if "up" in self.trend.lower() else "📉"
trend_html = f"""
<div style="text-align: center; margin-top: 1rem; color: {trend_color};">
{trend_icon} {self.trend}
</div>
"""
# Render the card
card_html = f"""
<div class="modern-card">
<h3 style="margin-bottom: 1rem; color: #2D3748;">{self.title}</h3>
{metrics_grid}
{trend_html}
</div>
"""
st.markdown(card_html, unsafe_allow_html=True)
# Add chart if provided
if self.chart_data:
self._render_chart()
def _render_chart(self):
"""Render chart for metrics."""
if self.chart_data.get("type") == "line":
fig = px.line(
x=self.chart_data.get("x", []),
y=self.chart_data.get("y", []),
title=self.chart_data.get("title", ""),
labels=self.chart_data.get("labels", {})
)
elif self.chart_data.get("type") == "bar":
fig = px.bar(
x=self.chart_data.get("x", []),
y=self.chart_data.get("y", []),
title=self.chart_data.get("title", ""),
labels=self.chart_data.get("labels", {})
)
else:
return
fig.update_layout(
plot_bgcolor='rgba(0,0,0,0)',
paper_bgcolor='rgba(0,0,0,0)',
showlegend=False,
height=300
)
st.plotly_chart(fig, use_container_width=True)
class StatusCard:
"""Status indicator card."""
def __init__(
self,
title: str,
status: str,
message: str,
icon: str = "",
actions: Optional[List[Dict]] = None
):
self.title = title
self.status = status # success, warning, error, info
self.message = message
self.icon = icon
self.actions = actions or []
def render(self):
"""Render the status card."""
apply_cards_styling()
# Status colors
status_colors = {
"success": "#52C41A",
"warning": "#FA8C16",
"error": "#F5222D",
"info": "#1890FF"
}
color = status_colors.get(self.status, "#1890FF")
# Create actions
actions_html = ""
if self.actions:
action_buttons = []
for action in self.actions:
action_buttons.append(f"""
<button class="action-button" onclick="{action.get('onclick', '')}">
{action.get('icon', '')} {action.get('label', 'Action')}
</button>
""")
actions_html = f"""
<div class="card-actions">
{''.join(action_buttons)}
</div>
"""
# Render the card
card_html = f"""
<div class="modern-card" style="border-left: 4px solid {color};">
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1rem;">
<span style="font-size: 2rem;">{self.icon}</span>
<div>
<h3 style="margin: 0; color: #2D3748;">{self.title}</h3>
<span style="color: {color}; font-weight: 600; text-transform: uppercase; font-size: 0.8rem;">
{self.status}
</span>
</div>
</div>
<p style="color: #657786; margin-bottom: 1rem;">{self.message}</p>
{actions_html}
</div>
"""
st.markdown(card_html, unsafe_allow_html=True)
# Utility functions for creating common cards
def create_feature_card(title: str, description: str, icon: str = "🔧", **kwargs):
"""Create and render a feature card."""
card = FeatureCard(title, description, icon, **kwargs)
card.render()
def create_tweet_card(content: str, **kwargs):
"""Create and render a tweet card."""
card = TweetCard(content, **kwargs)
card.render()
def create_metrics_card(title: str, metrics: Dict, **kwargs):
"""Create and render a metrics card."""
card = MetricsCard(title, metrics, **kwargs)
card.render()
def create_status_card(title: str, status: str, message: str, **kwargs):
"""Create and render a status card."""
card = StatusCard(title, status, message, **kwargs)
card.render()

View File

@@ -1,554 +0,0 @@
"""
Enhanced Navigation Component for Twitter UI with modern styling and improved functionality.
"""
import streamlit as st
from typing import Dict, List, Optional, Callable, Any
from ..styles.theme import Theme
import os
def apply_navigation_styling():
"""Apply modern CSS styling for navigation components."""
st.markdown("""
<style>
/* Navigation Styles */
.nav-container {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(20px);
border-radius: 16px;
padding: 1rem;
margin-bottom: 2rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.nav-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 2px solid #E2E8F0;
}
.nav-title {
font-size: 1.5rem;
font-weight: 700;
color: #1DA1F2;
display: flex;
align-items: center;
gap: 0.5rem;
}
.nav-status {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
border-radius: 20px;
font-size: 0.9rem;
font-weight: 600;
}
.status-connected {
background: linear-gradient(135deg, #52C41A, #73D13D);
color: white;
}
.status-disconnected {
background: linear-gradient(135deg, #FA8C16, #FFA940);
color: white;
}
.nav-menu {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.nav-item {
background: #F7F9FA;
border: 2px solid transparent;
border-radius: 12px;
padding: 0.75rem 1.5rem;
color: #657786;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-decoration: none;
display: flex;
align-items: center;
gap: 0.5rem;
}
.nav-item:hover {
background: #E1F5FE;
border-color: #1DA1F2;
color: #1DA1F2;
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(29, 161, 242, 0.2);
}
.nav-item.active {
background: linear-gradient(135deg, #1DA1F2, #0C85D0);
color: white;
border-color: #1DA1F2;
box-shadow: 0 4px 15px rgba(29, 161, 242, 0.3);
}
.nav-item.active:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(29, 161, 242, 0.4);
}
.nav-breadcrumb {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
font-size: 0.9rem;
color: #657786;
}
.breadcrumb-item {
display: flex;
align-items: center;
gap: 0.25rem;
}
.breadcrumb-separator {
color: #CBD5E0;
margin: 0 0.5rem;
}
.nav-actions {
display: flex;
gap: 0.5rem;
align-items: center;
}
.action-button {
background: linear-gradient(135deg, #52C41A, #73D13D);
color: white;
border: none;
border-radius: 8px;
padding: 0.5rem 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
}
.action-button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(82, 196, 26, 0.3);
}
.action-button.secondary {
background: #F7F9FA;
color: #657786;
border: 1px solid #E1E8ED;
}
.action-button.secondary:hover {
background: #E1F5FE;
color: #1DA1F2;
border-color: #1DA1F2;
}
/* Mobile Responsive */
@media (max-width: 768px) {
.nav-header {
flex-direction: column;
gap: 1rem;
align-items: flex-start;
}
.nav-menu {
flex-direction: column;
width: 100%;
}
.nav-item {
width: 100%;
justify-content: center;
}
.nav-actions {
width: 100%;
justify-content: center;
}
}
</style>
""", unsafe_allow_html=True)
class TwitterNavigation:
"""Enhanced navigation component for Twitter dashboard."""
def __init__(self, theme: Optional[Theme] = None):
self.theme = theme or Theme()
self.current_page = st.session_state.get('current_page', 'dashboard')
def render_header(self, title: str = "Twitter AI Assistant", show_status: bool = True):
"""Render the navigation header with title and status."""
apply_navigation_styling()
st.markdown('<div class="nav-container">', unsafe_allow_html=True)
st.markdown('<div class="nav-header">', unsafe_allow_html=True)
# Title
st.markdown(f'<div class="nav-title">🐦 {title}</div>', unsafe_allow_html=True)
# Status indicator
if show_status:
twitter_connected = self._check_twitter_connection()
status_class = "status-connected" if twitter_connected else "status-disconnected"
status_text = "Connected" if twitter_connected else "Not Connected"
status_icon = "" if twitter_connected else "⚠️"
st.markdown(f'''
<div class="nav-status {status_class}">
{status_icon} Twitter {status_text}
</div>
''', unsafe_allow_html=True)
st.markdown('</div>', unsafe_allow_html=True)
def render_menu(self, menu_items: List[Dict], current_page: Optional[str] = None):
"""Render navigation menu with items."""
if current_page:
self.current_page = current_page
st.session_state.current_page = current_page
st.markdown('<div class="nav-menu">', unsafe_allow_html=True)
cols = st.columns(len(menu_items))
for i, item in enumerate(menu_items):
with cols[i]:
active_class = "active" if item.get('key') == self.current_page else ""
if st.button(
f"{item.get('icon', '')} {item.get('label', '')}",
key=f"nav_{item.get('key', i)}",
use_container_width=True,
type="primary" if active_class else "secondary"
):
st.session_state.current_page = item.get('key')
if item.get('callback'):
item['callback']()
st.rerun()
st.markdown('</div>', unsafe_allow_html=True)
st.markdown('</div>', unsafe_allow_html=True)
return st.session_state.get('current_page', menu_items[0].get('key'))
def render_breadcrumb(self, items: List[Dict]):
"""Render breadcrumb navigation."""
st.markdown('<div class="nav-breadcrumb">', unsafe_allow_html=True)
for i, item in enumerate(items):
if i > 0:
st.markdown('<span class="breadcrumb-separator"></span>', unsafe_allow_html=True)
icon = item.get('icon', '')
label = item.get('label', '')
if item.get('active', False):
st.markdown(f'<span class="breadcrumb-item"><strong>{icon} {label}</strong></span>', unsafe_allow_html=True)
else:
st.markdown(f'<span class="breadcrumb-item">{icon} {label}</span>', unsafe_allow_html=True)
st.markdown('</div>', unsafe_allow_html=True)
def render_actions(self, actions: List[Dict]):
"""Render action buttons in navigation."""
st.markdown('<div class="nav-actions">', unsafe_allow_html=True)
cols = st.columns(len(actions))
for i, action in enumerate(actions):
with cols[i]:
button_type = action.get('type', 'primary')
if st.button(
f"{action.get('icon', '')} {action.get('label', '')}",
key=f"action_{action.get('key', i)}",
type=button_type,
use_container_width=True,
help=action.get('help', '')
):
if action.get('callback'):
action['callback']()
st.markdown('</div>', unsafe_allow_html=True)
def render_sidebar_menu(self, menu_items: List[Dict]):
"""Render sidebar navigation menu."""
with st.sidebar:
st.markdown("### 🐦 Twitter Tools")
for item in menu_items:
icon = item.get('icon', '')
label = item.get('label', '')
key = item.get('key', '')
if st.button(f"{icon} {label}", key=f"sidebar_{key}", use_container_width=True):
st.session_state.current_page = key
if item.get('callback'):
item['callback']()
st.rerun()
# Twitter connection status in sidebar
st.markdown("---")
twitter_connected = self._check_twitter_connection()
if twitter_connected:
st.success("🐦 Twitter Connected")
else:
st.warning("⚠️ Twitter Not Connected")
if st.button("🔧 Configure Twitter", use_container_width=True):
st.session_state.show_twitter_config = True
st.rerun()
def _check_twitter_connection(self) -> bool:
"""Check if Twitter is connected."""
twitter_config = st.session_state.get('twitter_config', {})
return bool(twitter_config and all([
twitter_config.get('api_key'),
twitter_config.get('api_secret'),
twitter_config.get('access_token'),
twitter_config.get('access_token_secret')
]))
class Sidebar:
"""Sidebar navigation component."""
def __init__(self, title: str = "Navigation", logo: Optional[str] = None):
"""Initialize the sidebar."""
self.title = title
self.logo = logo
self.menu_items = []
def add_menu_item(self, label: str, icon: str, key: str, callback: Optional[Callable] = None):
"""Add a menu item to the sidebar."""
self.menu_items.append({
'label': label,
'icon': icon,
'key': key,
'callback': callback
})
def render(self) -> str:
"""Render the sidebar and return the selected page."""
with st.sidebar:
# Logo and title
if self.logo and os.path.exists(self.logo):
st.image(self.logo, width=100)
st.title(self.title)
st.markdown("---")
# Menu items
selected_page = None
for item in self.menu_items:
if st.button(
f"{item['icon']} {item['label']}",
key=f"sidebar_{item['key']}",
use_container_width=True
):
selected_page = item['key']
if item.get('callback'):
item['callback']()
return selected_page or st.session_state.get('current_page', 'dashboard')
class Header:
"""Header component with title and actions."""
def __init__(self, title: str = "Dashboard", subtitle: str = ""):
"""Initialize the header."""
self.title = title
self.subtitle = subtitle
self.actions = []
def add_action(self, label: str, icon: str, callback: Callable, help_text: str = ""):
"""Add an action button to the header."""
self.actions.append({
'label': label,
'icon': icon,
'callback': callback,
'help': help_text
})
def render(self):
"""Render the header."""
col1, col2 = st.columns([3, 1])
with col1:
st.title(f"{self.title}")
if self.subtitle:
st.markdown(f"*{self.subtitle}*")
with col2:
if self.actions:
for i, action in enumerate(self.actions):
if st.button(
f"{action['icon']} {action['label']}",
key=f"header_action_{i}",
help=action.get('help', ''),
use_container_width=True
):
action['callback']()
class Tabs:
"""Tab navigation component."""
def __init__(self):
"""Initialize the tabs."""
self.tabs = []
def add_tab(self, label: str, icon: str, content_func: Callable):
"""Add a tab."""
self.tabs.append({
'label': label,
'icon': icon,
'content_func': content_func
})
def render(self):
"""Render the tabs."""
if not self.tabs:
return
tab_labels = [f"{tab['icon']} {tab['label']}" for tab in self.tabs]
selected_tabs = st.tabs(tab_labels)
for i, tab in enumerate(self.tabs):
with selected_tabs[i]:
tab['content_func']()
class Breadcrumbs:
"""Breadcrumb navigation component."""
def __init__(self):
"""Initialize breadcrumbs."""
self.items = []
def add_item(self, label: str, key: str = None, callback: Callable = None):
"""Add a breadcrumb item."""
self.items.append({
'label': label,
'key': key,
'callback': callback
})
def render(self):
"""Render the breadcrumbs."""
if not self.items:
return
breadcrumb_html = '<div class="nav-breadcrumb">'
for i, item in enumerate(self.items):
if i > 0:
breadcrumb_html += '<span class="breadcrumb-separator"></span>'
if item.get('callback'):
breadcrumb_html += f'<span class="breadcrumb-item clickable" onclick="handleBreadcrumbClick(\'{item["key"]}\')">{item["label"]}</span>'
else:
breadcrumb_html += f'<span class="breadcrumb-item">{item["label"]}</span>'
breadcrumb_html += '</div>'
st.markdown(breadcrumb_html, unsafe_allow_html=True)
def create_main_navigation() -> TwitterNavigation:
"""Create and return the main navigation instance."""
return TwitterNavigation()
def render_page_header(title: str, subtitle: str = "", icon: str = ""):
"""Render a consistent page header."""
st.markdown(f"""
<div style="text-align: center; margin-bottom: 2rem; padding: 2rem; background: linear-gradient(135deg, #E6F7FF, #F0F9FF); border-radius: 16px;">
<h1 style="color: #1DA1F2; margin-bottom: 0.5rem;">{icon} {title}</h1>
{f'<p style="color: #657786; font-size: 1.1rem;">{subtitle}</p>' if subtitle else ''}
</div>
""", unsafe_allow_html=True)
def render_quick_actions(actions: List[Dict]):
"""Render quick action buttons."""
st.markdown("### ⚡ Quick Actions")
cols = st.columns(len(actions))
for i, action in enumerate(actions):
with cols[i]:
if st.button(
f"{action.get('icon', '')} {action.get('label', '')}",
key=f"quick_action_{i}",
use_container_width=True,
help=action.get('help', '')
):
if action.get('callback'):
action['callback']()
# Default menu items for Twitter dashboard
DEFAULT_MENU_ITEMS = [
{
'key': 'dashboard',
'label': 'Dashboard',
'icon': '🏠',
'help': 'Main dashboard overview'
},
{
'key': 'generator',
'label': 'Tweet Generator',
'icon': '',
'help': 'AI-powered tweet generation'
},
{
'key': 'analytics',
'label': 'Analytics',
'icon': '📊',
'help': 'Tweet performance analytics'
},
{
'key': 'scheduler',
'label': 'Scheduler',
'icon': '📅',
'help': 'Schedule tweets for later'
},
{
'key': 'settings',
'label': 'Settings',
'icon': '⚙️',
'help': 'Twitter account and API settings'
}
]
DEFAULT_QUICK_ACTIONS = [
{
'key': 'new_tweet',
'label': 'New Tweet',
'icon': '✍️',
'help': 'Create a new tweet'
},
{
'key': 'ai_generate',
'label': 'AI Generate',
'icon': '🤖',
'help': 'Generate tweets with AI'
},
{
'key': 'view_analytics',
'label': 'View Analytics',
'icon': '📈',
'help': 'Check tweet performance'
}
]