""" محسن واجهة المستخدم - نظام تحليل المناقصات """ import streamlit as st import pandas as pd import numpy as np import base64 from pathlib import Path import os class UIEnhancer: """فئة لتحسين واجهة المستخدم وتوحيد التصميم عبر النظام""" # ألوان النظام COLORS = { 'primary': '#1E88E5', # أزرق 'secondary': '#5E35B1', # بنفسجي 'success': '#43A047', # أخضر 'warning': '#FB8C00', # برتقالي 'danger': '#E53935', # أحمر 'info': '#00ACC1', # سماوي 'light': '#F5F5F5', # رمادي فاتح 'dark': '#212121', # رمادي داكن 'accent': '#FF4081', # وردي 'background': '#FFFFFF', # أبيض 'text': '#212121', # أسود 'border': '#E0E0E0' # رمادي حدود } # أحجام الخطوط FONT_SIZES = { 'xs': '0.75rem', 'sm': '0.875rem', 'md': '1rem', 'lg': '1.125rem', 'xl': '1.25rem', '2xl': '1.5rem', '3xl': '1.875rem', '4xl': '2.25rem', '5xl': '3rem' } def __init__(self, page_title="نظام تحليل المناقصات", page_icon="📊"): """تهيئة محسن واجهة المستخدم""" self.page_title = page_title self.page_icon = page_icon self.theme_mode = "light" # الوضع الافتراضي هو الوضع الفاتح # تهيئة متغير السمة في حالة الجلسة إذا لم يكن موجوداً if 'theme' not in st.session_state: st.session_state.theme = 'light' def apply_global_styles(self): """تطبيق التنسيقات العامة على الصفحة""" # تعريف CSS العام css = f""" @import url('https://fonts.googleapis.com/css2?family=Tajawal:wght@300;400;500;700&display=swap'); * {{ font-family: 'Tajawal', sans-serif; direction: rtl; }} h1, h2, h3, h4, h5, h6 {{ font-family: 'Tajawal', sans-serif; font-weight: 700; color: {self.COLORS['dark']}; }} .module-title {{ color: {self.COLORS['primary']}; font-size: {self.FONT_SIZES['3xl']}; margin-bottom: 1rem; border-bottom: 2px solid {self.COLORS['primary']}; padding-bottom: 0.5rem; }} .stTabs [data-baseweb="tab-list"] {{ gap: 2px; }} .stTabs [data-baseweb="tab"] {{ height: 50px; white-space: pre-wrap; background-color: {self.COLORS['light']}; border-radius: 4px 4px 0 0; gap: 1px; padding-top: 10px; padding-bottom: 10px; }} .stTabs [aria-selected="true"] {{ background-color: {self.COLORS['primary']}; color: white; }} div[data-testid="stSidebarNav"] li div a span {{ direction: rtl; text-align: right; font-family: 'Tajawal', sans-serif; }} div[data-testid="stSidebarNav"] {{ background-color: {self.COLORS['light']}; }} div[data-testid="stSidebarNav"] li div {{ margin-right: 0; margin-left: auto; }} div[data-testid="stSidebarNav"] li div a {{ padding-right: 10px; padding-left: 0; }} div[data-testid="stSidebarNav"] li div a:hover {{ background-color: {self.COLORS['primary'] + '20'}; }} div[data-testid="stSidebarNav"] li div[aria-selected="true"] {{ background-color: {self.COLORS['primary'] + '40'}; }} div[data-testid="stSidebarNav"] li div[aria-selected="true"] a span {{ color: {self.COLORS['primary']}; font-weight: 500; }} .metric-card {{ background-color: white; border-radius: 10px; padding: 20px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); text-align: center; transition: transform 0.3s ease; }} .metric-card:hover {{ transform: translateY(-5px); }} .metric-value {{ font-size: 2.5rem; font-weight: 700; margin: 10px 0; }} .metric-label {{ font-size: 1rem; color: #666; }} .metric-change {{ font-size: 0.9rem; margin-top: 5px; }} .metric-change-positive {{ color: {self.COLORS['success']}; }} .metric-change-negative {{ color: {self.COLORS['danger']}; }} .custom-button {{ background-color: {self.COLORS['primary']}; color: white; border: none; border-radius: 5px; padding: 10px 20px; font-size: 1rem; cursor: pointer; transition: background-color 0.3s ease; }} .custom-button:hover {{ background-color: {self.COLORS['secondary']}; }} .custom-card {{ background-color: white; border-radius: 10px; padding: 20px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); margin-bottom: 20px; }} .header-container {{ display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem; padding-bottom: 1rem; border-bottom: 1px solid {self.COLORS['border']}; }} .header-title {{ color: {self.COLORS['primary']}; font-size: {self.FONT_SIZES['3xl']}; margin: 0; }} .header-subtitle {{ color: {self.COLORS['dark']}; font-size: {self.FONT_SIZES['lg']}; margin: 0; }} .header-actions {{ display: flex; gap: 10px; }} /* تنسيق الجداول */ div[data-testid="stTable"] table {{ width: 100%; border-collapse: collapse; }} div[data-testid="stTable"] thead tr th {{ background-color: {self.COLORS['primary']}; color: white; text-align: right; padding: 12px; }} div[data-testid="stTable"] tbody tr:nth-child(even) {{ background-color: {self.COLORS['light']}; }} div[data-testid="stTable"] tbody tr:hover {{ background-color: {self.COLORS['primary'] + '10'}; }} div[data-testid="stTable"] tbody tr td {{ padding: 10px; text-align: right; }} /* تنسيق النماذج */ div[data-testid="stForm"] {{ background-color: white; border-radius: 10px; padding: 20px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); }} button[kind="primaryFormSubmit"] {{ background-color: {self.COLORS['primary']}; color: white; }} button[kind="secondaryFormSubmit"] {{ background-color: {self.COLORS['light']}; color: {self.COLORS['dark']}; border: 1px solid {self.COLORS['border']}; }} /* تنسيق الرسوم البيانية */ div[data-testid="stVegaLiteChart"] {{ background-color: white; border-radius: 10px; padding: 20px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); }} """ # تطبيق CSS st.markdown(f'', unsafe_allow_html=True) def apply_theme_colors(self): """تطبيق ألوان السمة الحالية""" # تحديد ألوان السمة بناءً على الوضع if self.theme_mode == "dark": self.COLORS['background'] = '#121212' self.COLORS['text'] = '#FFFFFF' self.COLORS['border'] = '#333333' else: self.COLORS['background'] = '#FFFFFF' self.COLORS['text'] = '#212121' self.COLORS['border'] = '#E0E0E0' # تطبيق CSS للسمة theme_css = f""" body {{ background-color: {self.COLORS['background']}; color: {self.COLORS['text']}; }} """ st.markdown(f'', unsafe_allow_html=True) def toggle_theme(self): """تبديل وضع السمة بين الفاتح والداكن""" if self.theme_mode == "light": self.theme_mode = "dark" else: self.theme_mode = "light" self.apply_theme_colors() def create_sidebar(self, menu_items): """إنشاء الشريط الجانبي مع قائمة العناصر""" with st.sidebar: # إضافة الشعار st.markdown( f"""
مهندس تامر الجوهري
مدير المشاريع
نظام تحليل المناقصات | الإصدار 2.0.0
© 2025 جميع الحقوق محفوظة
{subtitle}
', unsafe_allow_html=True) if show_actions: with col2: col2_1, col2_2 = st.columns(2) with col2_1: st.button("إضافة جديد", key=add_button_key) with col2_2: st.button("تحديث", key=update_button_key) st.divider() def create_metric_card(self, label, value, change=None, color=None): """إنشاء بطاقة مقياس""" if color is None: color = self.COLORS['primary'] change_html = "" if change is not None: if change.startswith("+"): change_class = "metric-change-positive" change_icon = "↑" elif change.startswith("-"): change_class = "metric-change-negative" change_icon = "↓" else: change_class = "" change_icon = "" change_html = f'