Wahbi-AI / styling /enhanced_ui.py
EGYADMIN's picture
Upload 34 files
ae93751 verified
raw
history blame
18.4 kB
"""
تحسينات التصميم المرئي وواجهة المستخدم لنظام تحليل المناقصات
"""
import streamlit as st
import streamlit_option_menu as option_menu
from streamlit_extras.colored_header import colored_header
from streamlit_extras.switch_page_button import switch_page
import os
import sys
from pathlib import Path
# إضافة مسار المشروع للنظام
sys.path.append(str(Path(__file__).parent.parent))
class UIEnhancer:
"""فئة لتحسين واجهة المستخدم وتوحيد التصميم المرئي عبر النظام"""
# ألوان النظام
COLORS = {
'primary': '#1E5F74', # أزرق داكن للعناصر الرئيسية
'secondary': '#4BA3C3', # أزرق فاتح للعناصر الثانوية
'accent': '#F39237', # برتقالي للتأكيد والأزرار المهمة
'success': '#4CAF50', # أخضر للنجاح
'warning': '#FFC107', # أصفر للتحذيرات
'danger': '#E63946', # أحمر للأخطاء
'light': '#F5F5F5', # فاتح للخلفيات في الوضع الفاتح
'dark': '#1A1A1A', # داكن للخلفيات في الوضع الداكن
'text_light': '#FFFFFF', # نص أبيض للوضع الداكن
'text_dark': '#333333', # نص داكن للوضع الفاتح
'border': '#DDDDDD', # لون الحدود
'hover': '#2A7F9E', # لون التحويم
}
# أنماط CSS المخصصة
CUSTOM_CSS = """
<style>
/* تخصيص الشعار والعنوان */
.logo-title {
display: flex;
align-items: center;
margin-bottom: 1rem;
}
.logo-img {
height: 60px;
margin-right: 10px;
}
/* تخصيص البطاقات */
.card {
border-radius: 10px;
padding: 1.5rem;
margin-bottom: 1rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
}
/* تخصيص الأزرار */
.custom-button {
border-radius: 8px;
padding: 0.5rem 1rem;
font-weight: 500;
transition: all 0.3s ease;
}
.custom-button:hover {
opacity: 0.9;
transform: translateY(-2px);
}
/* تخصيص القوائم */
.nav-link {
border-radius: 8px;
margin: 0.2rem 0;
transition: all 0.3s ease;
}
.nav-link:hover {
transform: translateX(5px);
}
/* تخصيص الجداول */
.styled-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
border-radius: 10px;
overflow: hidden;
}
.styled-table th {
background-color: var(--primary-color);
color: white;
padding: 12px 15px;
text-align: right;
}
.styled-table td {
padding: 12px 15px;
border-bottom: 1px solid var(--border-color);
}
.styled-table tr:last-child td {
border-bottom: none;
}
.styled-table tr:nth-child(even) {
background-color: rgba(0, 0, 0, 0.05);
}
/* تخصيص لوحات المعلومات */
.dashboard-metric {
text-align: center;
padding: 1rem;
border-radius: 10px;
background-color: var(--secondary-color);
color: white;
}
.dashboard-metric h3 {
font-size: 2rem;
margin: 0;
}
.dashboard-metric p {
margin: 0;
opacity: 0.8;
}
/* تخصيص الرسوم البيانية */
.chart-container {
border-radius: 10px;
padding: 1rem;
background-color: white;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
/* تخصيص الإشعارات */
.notification {
padding: 1rem;
border-radius: 8px;
margin-bottom: 0.5rem;
display: flex;
align-items: center;
}
.notification-icon {
margin-left: 1rem;
font-size: 1.5rem;
}
/* تخصيص الشريط الجانبي */
.sidebar .sidebar-content {
background-color: var(--primary-color);
color: white;
}
/* تحسين اتجاه النص للغة العربية */
body {
direction: rtl;
text-align: right;
}
/* تخصيص الخط */
@font-face {
font-family: 'Tajawal';
src: url('https://fonts.googleapis.com/css2?family=Tajawal:wght@400;500;700&display=swap');
}
* {
font-family: 'Tajawal', sans-serif;
}
</style>
"""
def __init__(self, page_title="نظام تحليل المناقصات", page_icon="📊", layout="wide"):
"""تهيئة محسن واجهة المستخدم"""
self.page_title = page_title
self.page_icon = page_icon
self.layout = layout
self.setup_page()
def setup_page(self):
"""إعداد الصفحة وتطبيق الإعدادات العامة"""
st.set_page_config(
page_title=self.page_title,
page_icon=self.page_icon,
layout=self.layout,
initial_sidebar_state="expanded"
)
# تطبيق CSS المخصص
st.markdown(self.CUSTOM_CSS, unsafe_allow_html=True)
# إعداد متغيرات الجلسة إذا لم تكن موجودة
if 'theme' not in st.session_state:
st.session_state.theme = 'light'
if 'language' not in st.session_state:
st.session_state.language = 'ar'
def create_sidebar(self, menu_items):
"""إنشاء شريط جانبي محسن مع قائمة"""
with st.sidebar:
# عرض الشعار والعنوان
st.markdown(
f"""
<div class="logo-title">
<img src="./assets/images/logo.png" class="logo-img">
<h2>{self.page_title}</h2>
</div>
""",
unsafe_allow_html=True
)
# إنشاء قائمة الخيارات
selected = option_menu.option_menu(
menu_title=None,
options=[item["name"] for item in menu_items],
icons=[item["icon"] for item in menu_items],
menu_icon="cast",
default_index=0,
styles={
"container": {"padding": "0!important", "background-color": f"{self.COLORS['primary']}"},
"icon": {"color": "white", "font-size": "18px"},
"nav-link": {"color": "white", "font-size": "16px", "text-align": "right", "margin":"0px"},
"nav-link-selected": {"background-color": f"{self.COLORS['accent']}"},
}
)
# إضافة مفتاح تبديل السمة
st.markdown("<hr>", unsafe_allow_html=True)
col1, col2 = st.columns([1, 3])
with col1:
st.write("السمة:")
with col2:
if st.toggle("الوضع الداكن", st.session_state.theme == 'dark'):
st.session_state.theme = 'dark'
else:
st.session_state.theme = 'light'
# إضافة معلومات المستخدم
st.markdown("<hr>", unsafe_allow_html=True)
st.markdown(
f"""
<div style="text-align: center;">
<p>مرحباً، المستخدم</p>
<button class="custom-button" style="background-color: {self.COLORS['danger']}; color: white; border: none;">تسجيل الخروج</button>
</div>
""",
unsafe_allow_html=True
)
return selected
def create_header(self, title, description=None):
"""إنشاء ترويسة محسنة للصفحة"""
colored_header(
label=title,
description=description,
color_name=self.COLORS['primary']
)
def create_card(self, title, content, color=None):
"""إنشاء بطاقة محسنة"""
bg_color = color if color else self.COLORS['light'] if st.session_state.theme == 'light' else self.COLORS['dark']
text_color = self.COLORS['text_dark'] if st.session_state.theme == 'light' else self.COLORS['text_light']
st.markdown(
f"""
<div class="card" style="background-color: {bg_color}; color: {text_color};">
<h3>{title}</h3>
<p>{content}</p>
</div>
""",
unsafe_allow_html=True
)
def create_metric_card(self, title, value, delta=None, color=None):
"""إنشاء بطاقة مقاييس محسنة"""
bg_color = color if color else self.COLORS['primary']
delta_html = f"<span style='color: {'green' if delta > 0 else 'red'};'>{delta}%</span>" if delta is not None else ""
st.markdown(
f"""
<div class="dashboard-metric" style="background-color: {bg_color};">
<p>{title}</p>
<h3>{value}</h3>
{delta_html}
</div>
""",
unsafe_allow_html=True
)
def create_notification(self, message, type_="info"):
"""إنشاء إشعار محسن"""
colors = {
"info": self.COLORS['secondary'],
"success": self.COLORS['success'],
"warning": self.COLORS['warning'],
"error": self.COLORS['danger']
}
icons = {
"info": "ℹ️",
"success": "✅",
"warning": "⚠️",
"error": "❌"
}
bg_color = colors.get(type_, colors["info"])
icon = icons.get(type_, icons["info"])
st.markdown(
f"""
<div class="notification" style="background-color: {bg_color};">
<div class="notification-icon">{icon}</div>
<div>{message}</div>
</div>
""",
unsafe_allow_html=True
)
def create_button(self, label, type_="primary", on_click=None):
"""إنشاء زر محسن"""
colors = {
"primary": self.COLORS['primary'],
"secondary": self.COLORS['secondary'],
"accent": self.COLORS['accent'],
"success": self.COLORS['success'],
"warning": self.COLORS['warning'],
"danger": self.COLORS['danger']
}
bg_color = colors.get(type_, colors["primary"])
text_color = self.COLORS['text_light']
return st.button(
label,
key=f"btn_{label}_{type_}",
on_click=on_click,
use_container_width=True
)
def create_table(self, data, columns):
"""إنشاء جدول محسن"""
# تحويل البيانات إلى HTML
table_html = f"""
<table class="styled-table">
<thead>
<tr>
{"".join([f"<th>{col}</th>" for col in columns])}
</tr>
</thead>
<tbody>
"""
for row in data:
table_html += "<tr>"
for col in columns:
table_html += f"<td>{row.get(col, '')}</td>"
table_html += "</tr>"
table_html += """
</tbody>
</table>
"""
st.markdown(table_html, unsafe_allow_html=True)
def apply_theme_colors(self):
"""تطبيق ألوان السمة الحالية"""
theme_colors = {
'light': {
'background': self.COLORS['light'],
'text': self.COLORS['text_dark'],
'border': self.COLORS['border']
},
'dark': {
'background': self.COLORS['dark'],
'text': self.COLORS['text_light'],
'border': '#444444'
}
}
current_theme = theme_colors[st.session_state.theme]
st.markdown(
f"""
<style>
:root {{
--background-color: {current_theme['background']};
--text-color: {current_theme['text']};
--border-color: {current_theme['border']};
--primary-color: {self.COLORS['primary']};
--secondary-color: {self.COLORS['secondary']};
--accent-color: {self.COLORS['accent']};
}}
.stApp {{
background-color: var(--background-color);
color: var(--text-color);
}}
.card {{
background-color: var(--background-color);
color: var(--text-color);
border: 1px solid var(--border-color);
}}
</style>
""",
unsafe_allow_html=True
)
# استخدام الفئة
if __name__ == "__main__":
ui = UIEnhancer()
ui.apply_theme_colors()
# إنشاء قائمة العناصر
menu_items = [
{"name": "لوحة المعلومات", "icon": "house"},
{"name": "المناقصات والعقود", "icon": "file-text"},
{"name": "تحليل المستندات", "icon": "file-earmark-text"},
{"name": "نظام التسعير", "icon": "calculator"},
{"name": "حاسبة تكاليف البناء", "icon": "building"},
{"name": "الموارد والتكاليف", "icon": "people"},
{"name": "تحليل المخاطر", "icon": "exclamation-triangle"},
{"name": "إدارة المشاريع", "icon": "kanban"},
{"name": "الخرائط والمواقع", "icon": "geo-alt"},
{"name": "الجدول الزمني", "icon": "calendar3"},
{"name": "الإشعارات", "icon": "bell"},
{"name": "مقارنة المستندات", "icon": "files"},
{"name": "المساعد الذكي", "icon": "robot"},
{"name": "التقارير", "icon": "bar-chart"},
{"name": "الإعدادات", "icon": "gear"}
]
# إنشاء الشريط الجانبي
selected = ui.create_sidebar(menu_items)
# إنشاء ترويسة الصفحة
ui.create_header("لوحة المعلومات", "نظرة عامة على المناقصات والمشاريع")
# إنشاء صفوف للمقاييس
col1, col2, col3, col4 = st.columns(4)
with col1:
ui.create_metric_card("المناقصات النشطة", "12", delta=5)
with col2:
ui.create_metric_card("المشاريع الجارية", "8", delta=-2)
with col3:
ui.create_metric_card("المناقصات المربوحة", "65%", delta=10)
with col4:
ui.create_metric_card("قيمة المشاريع", "25M ريال", delta=15)
# إنشاء صفين للبطاقات والإشعارات
col1, col2 = st.columns([2, 1])
with col1:
ui.create_header("المناقصات الحالية", "آخر تحديث: اليوم")
# بيانات المناقصات
tenders_data = [
{"رقم المناقصة": "T-2025-001", "العنوان": "إنشاء مبنى إداري", "الحالة": "قيد الدراسة", "تاريخ التقديم": "2025-04-15"},
{"رقم المناقصة": "T-2025-002", "العنوان": "صيانة طرق", "الحالة": "تم التقديم", "تاريخ التقديم": "2025-03-20"},
{"رقم المناقصة": "T-2025-003", "العنوان": "توريد معدات", "الحالة": "فائز", "تاريخ التقديم": "2025-02-10"}
]
ui.create_table(tenders_data, ["رقم المناقصة", "العنوان", "الحالة", "تاريخ التقديم"])
with col2:
ui.create_header("الإشعارات", "آخر التنبيهات")
ui.create_notification("موعد تسليم مناقصة T-2025-001 بعد 5 أيام", "warning")
ui.create_notification("تم ترسية مناقصة T-2025-003", "success")
ui.create_notification("تم تحديث مستندات مناقصة T-2025-002", "info")
st.markdown("<br>", unsafe_allow_html=True)
ui.create_button("عرض جميع الإشعارات", "secondary")
# إنشاء صف للبطاقات
st.markdown("<br>", unsafe_allow_html=True)
ui.create_header("الإجراءات السريعة", "اختر إجراءً للبدء")
col1, col2, col3 = st.columns(3)
with col1:
ui.create_card(
"إضافة مناقصة جديدة",
"إنشاء مناقصة جديدة وإدخال البيانات الأساسية"
)
with col2:
ui.create_card(
"تحليل مستند",
"تحميل مستند مناقصة جديد للتحليل التلقائي"
)
with col3:
ui.create_card(
"إنشاء تقرير",
"إنشاء تقارير مخصصة للمناقصات والمشاريع"
)