# utils/ui_components.py import streamlit as st import plotly.graph_objects as go from config.settings import BADGES from config.themes import THEMES def apply_custom_theme(theme_name: str): """Applies a custom theme using CSS variables.""" theme = THEMES.get(theme_name, THEMES["default"]) css_vars = { "--primary-color": theme["primary_color"], "--background-color": theme["background_color"], "--secondary-background-color": theme["secondary_background_color"], "--text-color": theme["text_color"], "--font": theme["font"], "--card-background": theme.get("card_background", "#FFFFFF"), "--shadow-light": theme.get("shadow_light", "0 4px 15px rgba(0, 0, 0, 0.05)"), "--shadow-medium": theme.get("shadow_medium", "0 8px 30px rgba(0, 0, 0, 0.1)"), "--highlight-color": theme.get("highlight_color", theme["primary_color"]), } css_vars_str = "\n".join([f"{key}: {value};" for key, value in css_vars.items()]) css = f""" """ st.markdown(css, unsafe_allow_html=True) def display_user_stats(stats: dict, ui: dict): """Displays user statistics in a dashboard format.""" cols = st.columns(4) with cols[0]: st.metric(ui.get("total_score", "Total Score"), f"{stats.get('total_score', 0):,}") with cols[1]: st.metric(ui.get("quizzes", "Quizzes Played"), stats.get("quizzes_completed", 0)) with cols[2]: st.metric(ui.get("current_streak", "Current Streak"), f"🔥 {stats.get('current_streak', 0)}") with cols[3]: st.metric(ui.get("best_streak", "Best Streak"), f"⭐ {stats.get('best_streak', 0)}") def display_badges(earned_badges: list, ui: dict): """Displays earned badges.""" if not earned_badges: return st.markdown(f"### 🏆 {ui.get('your_badges', 'Your Badges')}") cols = st.columns(len(earned_badges)) for i, badge_id in enumerate(earned_badges): badge = BADGES.get(badge_id) if badge: with cols[i]: st.markdown(f"
{badge['icon']}
{ui.get(badge['name'].lower(), badge['name'])}
", unsafe_allow_html=True) def create_progress_chart(stats: dict, ui: dict) -> go.Figure: """Creates a progress chart for the user.""" history = stats.get("quiz_history", []) if not history: return go.Figure() dates = [item["timestamp"] for item in history] scores = [item["percentage"] for item in history] fig = go.Figure() fig.add_trace(go.Scatter(x=dates, y=scores, mode="lines+markers", name=ui.get("score", "Score") + " %")) fig.update_layout( title=ui.get("quiz_performance_over_time", "Quiz Performance Over Time"), xaxis_title=ui.get("date", "Date"), yaxis_title=f"{ui.get('score', 'Score')} (%)", yaxis_range=[0, 100], ) return fig def animated_success(message: str): """Displays an animated success message.""" st.markdown(f"
{st.success(message)}
", unsafe_allow_html=True) def create_quiz_card(question: str, options: list, key: str): """Creates a card for a multiple-choice question.""" st.markdown(f"
", unsafe_allow_html=True) st.subheader(question) st.session_state[key] = st.radio( "Select your answer:", options, index=None, key=f"{key}_radio", label_visibility="collapsed", ) st.markdown("
", unsafe_allow_html=True) def render_flashcard(front_content: str, back_content: str, is_flipped: bool): """Renders a flippable flashcard.""" card_style = "width: 100%; height: 300px; perspective: 1000px;" flipper_style = f'position: relative; width: 100%; height: 100%; transition: transform 0.6s; transform-style: preserve-3d; transform: {"rotateY(180deg)" if is_flipped else "none"};' face_style = "position: absolute; width: 100%; height: 100%; -webkit-backface-visibility: hidden; backface-visibility: hidden; display: flex; justify-content: center; align-items: center; padding: 20px; border-radius: 10px; box-shadow: var(--shadow-light);" front_style = f"{face_style} background-color: var(--primary-color); color: white;" back_style = f"{face_style} background-color: var(--card-background); color: var(--text-color); transform: rotateY(180deg);" st.markdown( f"
" f"
{front_content}
" f"
{back_content}
" f"
", unsafe_allow_html=True, )