Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
@@ -603,147 +603,250 @@ def home_page():
|
|
603 |
legend_title_font_size=16
|
604 |
)
|
605 |
st.plotly_chart(fig, use_container_width=True)
|
606 |
-
|
607 |
-
def settings_page():
|
608 |
-
"""صفحة الإعدادات"""
|
609 |
-
st.markdown("# ⚙️ الإعدادات")
|
610 |
-
|
611 |
-
st.markdown("""
|
612 |
-
## إعدادات النظام
|
613 |
-
|
614 |
-
يمكنك من خلال هذه الصفحة تخصيص إعدادات النظام بما يتناسب مع احتياجاتك.
|
615 |
-
""")
|
616 |
-
|
617 |
-
tab1, tab2, tab3 = st.tabs(["إعدادات عامة", "إعدادات الحساب", "الدعم والمساعدة"])
|
618 |
-
|
619 |
-
with tab1:
|
620 |
-
st.markdown("### الإعدادات العامة")
|
621 |
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
|
|
642 |
|
643 |
with tab2:
|
644 |
-
st.markdown("### إعدادات الحساب")
|
645 |
-
|
646 |
-
st.markdown("#### البيانات الشخصية")
|
647 |
-
|
648 |
col1, col2 = st.columns(2)
|
649 |
|
650 |
with col1:
|
651 |
-
|
652 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
653 |
|
654 |
with col2:
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
668 |
|
669 |
with tab3:
|
670 |
-
st.
|
671 |
-
|
672 |
-
st.markdown("""
|
673 |
-
#### كيفية استخدام النظام
|
674 |
-
|
675 |
-
يوفر النظام مجموعة من الأدوات والتحليلات التي تساعدك في إدارة المناقصات والعقود وتحليل المخاطر.
|
676 |
-
يمكنك الاطلاع على دليل الاستخدام للحصول على شرح تفصيلي لكافة الوظائف.
|
677 |
-
""")
|
678 |
-
|
679 |
-
st.download_button(
|
680 |
-
"تحميل دليل الاستخدام",
|
681 |
-
data="محتوى دليل الاستخدام",
|
682 |
-
file_name="user_guide.pdf",
|
683 |
-
mime="application/pdf"
|
684 |
-
)
|
685 |
-
|
686 |
-
st.markdown("#### التواصل مع الدعم الفني")
|
687 |
|
688 |
-
with
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
)
|
695 |
-
st.file_uploader("إرفاق ملف (اختياري):")
|
696 |
|
697 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
698 |
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
* إدارة وتحليل العقود
|
721 |
-
* تخطيط سلسلة التوريد
|
722 |
-
* تحليل المتطلبات
|
723 |
-
|
724 |
-
### فريق التطوير
|
725 |
-
|
726 |
-
تم تطوير هذا النظام بواسطة فريق من الخبراء المتخصصين في مجالات البرمجة والذكاء الاصطناعي وإدارة المشاريع والعقود.
|
727 |
|
728 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
729 |
|
730 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
731 |
|
732 |
-
|
733 |
-
|
734 |
-
""")
|
735 |
|
736 |
-
col1, col2
|
737 |
|
738 |
with col1:
|
739 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
740 |
|
741 |
with col2:
|
742 |
-
st.
|
743 |
-
|
744 |
-
|
745 |
-
|
|
|
746 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
747 |
def dashboard_page():
|
748 |
"""صفحة لوحة المؤشرات"""
|
749 |
st.markdown("# 📊 لوحة المؤشرات")
|
@@ -900,86 +1003,6 @@ def dashboard_page():
|
|
900 |
st.dataframe(upcoming_display, hide_index=True, use_container_width=True)
|
901 |
else:
|
902 |
st.info("لا توجد مناقصات قادمة خلال الثلاثين يوماً القادمة.")
|
903 |
-
|
904 |
-
def procurement_page():
|
905 |
-
"""صفحة المناقصات والعقود"""
|
906 |
-
st.markdown("# 📑 المناقصات والعقود")
|
907 |
-
|
908 |
-
tab1, tab2 = st.tabs(["المناقصات", "العقود"])
|
909 |
-
|
910 |
-
with tab1:
|
911 |
-
st.markdown("## إدارة المناقصات")
|
912 |
-
|
913 |
-
# أزرار الإجراءات
|
914 |
-
col1, col2, col3, col4 = st.columns(4)
|
915 |
-
with col1:
|
916 |
-
if st.button("🆕 إضافة مناقصة جديدة"):
|
917 |
-
st.session_state.show_add_tender = True
|
918 |
-
|
919 |
-
with col2:
|
920 |
-
st.button("🔍 بحث متقدم")
|
921 |
-
|
922 |
-
with col3:
|
923 |
-
st.button("📊 تصدير التقرير")
|
924 |
-
|
925 |
-
with col4:
|
926 |
-
st.button("🔄 تحديث البيانات")
|
927 |
-
|
928 |
-
# عرض جدول المناقصات
|
929 |
-
st.markdown("### قائمة المناقصات")
|
930 |
-
|
931 |
-
# فلاتر
|
932 |
-
col1, col2, col3 = st.columns(3)
|
933 |
-
with col1:
|
934 |
-
status_filter = st.multiselect(
|
935 |
-
"الحالة:",
|
936 |
-
["الكل"] + list(st.session_state.tenders_df['الحالة'].unique()),
|
937 |
-
default=["الكل"]
|
938 |
-
)
|
939 |
-
|
940 |
-
with col2:
|
941 |
-
date_range = st.date_input(
|
942 |
-
"تاريخ الإعلان:",
|
943 |
-
value=(
|
944 |
-
(datetime.now() - timedelta(days=60)).date(),
|
945 |
-
datetime.now().date()
|
946 |
-
)
|
947 |
-
)
|
948 |
-
|
949 |
-
with col3:
|
950 |
-
min_value, max_value = st.slider(
|
951 |
-
"القيمة التقديرية (ريال):",
|
952 |
-
0,
|
953 |
-
int(st.session_state.tenders_df['القيمة التقديرية'].max()),
|
954 |
-
(0, int(st.session_state.tenders_df['القيمة التقديرية'].max())),
|
955 |
-
step=100000
|
956 |
-
)
|
957 |
-
|
958 |
-
# تطبيق الفلاتر
|
959 |
-
filtered_df = st.session_state.tenders_df.copy()
|
960 |
-
|
961 |
-
# فلتر الحالة
|
962 |
-
if "الكل" not in status_filter:
|
963 |
-
filtered_df = filtered_df[filtered_df['الحالة'].isin(status_filter)]
|
964 |
-
|
965 |
-
# فلتر التاريخ
|
966 |
-
filtered_df = filtered_df[
|
967 |
-
(pd.to_datetime(filtered_df['تاريخ الإعلان']) >= pd.Timestamp(date_range[0])) &
|
968 |
-
(pd.to_datetime(filtered_df['تاريخ الإعلان']) <= pd.Timestamp(date_range[1]))
|
969 |
-
]
|
970 |
-
|
971 |
-
# فلتر القيمة
|
972 |
-
filtered_df = filtered_df[
|
973 |
-
(filtered_df['القيمة التقد��رية'] >= min_value) &
|
974 |
-
(filtered_df['القيمة التقديرية'] <= max_value)
|
975 |
-
]
|
976 |
-
|
977 |
-
# عرض الجدول
|
978 |
-
st.dataframe(
|
979 |
-
filtered_df,
|
980 |
-
use_container_width=True,
|
981 |
-
hide_index=True
|
982 |
-
)
|
983 |
|
984 |
def procurement_page():
|
985 |
"""صفحة المناقصات والعقود"""
|
@@ -1194,7 +1217,7 @@ def requirements_analysis_page():
|
|
1194 |
st.metric("التكلفة التقديرية", "435,000 ريال")
|
1195 |
st.metric("درجة التعقيد", "متوسطة")
|
1196 |
|
1197 |
-
|
1198 |
requirements_demo = pd.DataFrame({
|
1199 |
"المعرف": [f"REQ-{i:03d}" for i in range(1, 11)],
|
1200 |
"النوع": ["وظيفي", "وظيفي", "غير وظيفي", "وظيفي", "وظيفي", "غير وظيفي", "وظيفي", "وظيفي", "غير وظيفي", "وظيفي"],
|
|
|
603 |
legend_title_font_size=16
|
604 |
)
|
605 |
st.plotly_chart(fig, use_container_width=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
606 |
|
607 |
+
with col2:
|
608 |
+
# نسب النجاح المتوقعة للمناقصات النشطة
|
609 |
+
active_tenders = st.session_state.tenders_df[st.session_state.tenders_df['الحالة'].isin(['جديدة', 'مفتوحة', 'قيد التقييم'])]
|
610 |
+
active_tenders = active_tenders.sort_values('نسبة النجاح المتوقعة', ascending=False).head(8)
|
611 |
+
|
612 |
+
fig = px.bar(
|
613 |
+
active_tenders,
|
614 |
+
x='نسبة النجاح المتوقعة',
|
615 |
+
y='رقم المناقصة',
|
616 |
+
title='نسب النجاح المتوقعة للمناقصات النشطة',
|
617 |
+
color='نسبة النجاح المتوقعة',
|
618 |
+
color_continuous_scale='Viridis',
|
619 |
+
orientation='h'
|
620 |
+
)
|
621 |
+
fig.update_layout(
|
622 |
+
title_font_size=20,
|
623 |
+
title_font_family="Tajawal",
|
624 |
+
title_x=0.5,
|
625 |
+
yaxis={'categoryorder':'total ascending'}
|
626 |
+
)
|
627 |
+
st.plotly_chart(fig, use_container_width=True)
|
628 |
|
629 |
with tab2:
|
|
|
|
|
|
|
|
|
630 |
col1, col2 = st.columns(2)
|
631 |
|
632 |
with col1:
|
633 |
+
# توزيع قيم العقود
|
634 |
+
fig = px.histogram(
|
635 |
+
st.session_state.contracts_df,
|
636 |
+
x='القيمة',
|
637 |
+
nbins=10,
|
638 |
+
title='توزيع قيم العقود',
|
639 |
+
color_discrete_sequence=['#3498db']
|
640 |
+
)
|
641 |
+
fig.update_layout(
|
642 |
+
title_font_size=20,
|
643 |
+
title_font_family="Tajawal",
|
644 |
+
title_x=0.5,
|
645 |
+
xaxis_title="القيمة (ريال)",
|
646 |
+
yaxis_title="عدد العقود"
|
647 |
+
)
|
648 |
+
st.plotly_chart(fig, use_container_width=True)
|
649 |
|
650 |
with col2:
|
651 |
+
# نسب المحتوى المحلي
|
652 |
+
fig = px.scatter(
|
653 |
+
st.session_state.contracts_df,
|
654 |
+
x='القيمة',
|
655 |
+
y='المحتوى المحلي',
|
656 |
+
size='نسبة الإنجاز',
|
657 |
+
color='مستوى المخاطر',
|
658 |
+
hover_name='رقم العقد',
|
659 |
+
title='نسب المحتوى المحلي للعقود',
|
660 |
+
color_discrete_map={
|
661 |
+
'منخفض': '#2ecc71',
|
662 |
+
'متوسط': '#f39c12',
|
663 |
+
'عالي': '#e74c3c'
|
664 |
+
}
|
665 |
+
)
|
666 |
+
fig.update_layout(
|
667 |
+
title_font_size=20,
|
668 |
+
title_font_family="Tajawal",
|
669 |
+
title_x=0.5,
|
670 |
+
xaxis_title="القيمة (ريال)",
|
671 |
+
yaxis_title="نسبة المحتوى المحلي (%)"
|
672 |
+
)
|
673 |
+
st.plotly_chart(fig, use_container_width=True)
|
674 |
|
675 |
with tab3:
|
676 |
+
col1, col2 = st.columns(2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
677 |
|
678 |
+
with col1:
|
679 |
+
# مصفوفة المخاطر
|
680 |
+
risk_matrix = pd.crosstab(
|
681 |
+
st.session_state.risks_df['التأثير'],
|
682 |
+
st.session_state.risks_df['الاحتمالية'],
|
683 |
+
margins=False
|
684 |
)
|
|
|
685 |
|
686 |
+
# تحويل القيم إلى حرارية
|
687 |
+
risk_severity = {
|
688 |
+
('منخفض', 'منخفضة'): 1,
|
689 |
+
('منخفض', 'متوسطة'): 2,
|
690 |
+
('منخفض', 'عالية'): 3,
|
691 |
+
('متوسط', 'منخفضة'): 2,
|
692 |
+
('متوسط', 'متوسطة'): 4,
|
693 |
+
('متوسط', 'عالية'): 6,
|
694 |
+
('عالي', 'منخفضة'): 3,
|
695 |
+
('عالي', 'متوسطة'): 6,
|
696 |
+
('عالي', 'عالية'): 9,
|
697 |
+
('حرج', 'منخفضة'): 4,
|
698 |
+
('حرج', 'متوسطة'): 8,
|
699 |
+
('حرج', 'عالية'): 12
|
700 |
+
}
|
701 |
+
|
702 |
+
# تحضير بيانات المصفوفة
|
703 |
+
impact_order = ['منخفض', 'متوسط', 'عالي', 'حرج']
|
704 |
+
probability_order = ['منخفضة', 'متوسطة', 'عالية']
|
705 |
+
|
706 |
+
# إنشاء مصفوفة حرارية
|
707 |
+
heat_values = []
|
708 |
+
annotations = []
|
709 |
+
|
710 |
+
for impact in impact_order:
|
711 |
+
heat_row = []
|
712 |
+
for prob in probability_order:
|
713 |
+
try:
|
714 |
+
value = risk_matrix.loc[impact, prob]
|
715 |
+
except:
|
716 |
+
value = 0
|
717 |
+
heat_row.append(risk_severity.get((impact, prob), 0))
|
718 |
+
annotations.append(dict(
|
719 |
+
text=str(value),
|
720 |
+
x=prob,
|
721 |
+
y=impact,
|
722 |
+
showarrow=False
|
723 |
+
))
|
724 |
+
heat_values.append(heat_row)
|
725 |
+
|
726 |
+
# إنشاء الرسم البياني
|
727 |
+
fig = go.Figure(data=go.Heatmap(
|
728 |
+
z=heat_values,
|
729 |
+
x=probability_order,
|
730 |
+
y=impact_order,
|
731 |
+
colorscale=[
|
732 |
+
[0.0, "#2ecc71"], # أخضر للمخاطر المنخفضة
|
733 |
+
[0.33, "#f1c40f"], # أصفر للمخاطر المتوسطة
|
734 |
+
[0.66, "#e67e22"], # برتقالي للمخاطر العالية
|
735 |
+
[1.0, "#e74c3c"] # أحمر للمخاطر الحرجة
|
736 |
+
],
|
737 |
+
showscale=False
|
738 |
+
))
|
739 |
+
|
740 |
+
# إضافة النصوص
|
741 |
+
for annotation in annotations:
|
742 |
+
fig.add_annotation(annotation)
|
743 |
+
|
744 |
+
fig.update_layout(
|
745 |
+
title='مصفوفة المخاطر',
|
746 |
+
title_font_size=20,
|
747 |
+
title_font_family="Tajawal",
|
748 |
+
title_x=0.5,
|
749 |
+
xaxis_title="الاحتمالية",
|
750 |
+
yaxis_title="التأثير"
|
751 |
+
)
|
752 |
+
|
753 |
+
st.plotly_chart(fig, use_container_width=True)
|
754 |
|
755 |
+
with col2:
|
756 |
+
# توزيع المخاطر حسب استراتيجية المعالجة
|
757 |
+
treatment_counts = st.session_state.risks_df['المعالجة'].value_counts().reset_index()
|
758 |
+
treatment_counts.columns = ['المعالجة', 'العدد']
|
759 |
+
|
760 |
+
fig = px.bar(
|
761 |
+
treatment_counts,
|
762 |
+
x='المعالجة',
|
763 |
+
y='العدد',
|
764 |
+
title='استراتيجيات معالجة المخاطر',
|
765 |
+
color='المعالجة',
|
766 |
+
color_discrete_sequence=px.colors.qualitative.Pastel
|
767 |
+
)
|
768 |
+
fig.update_layout(
|
769 |
+
title_font_size=20,
|
770 |
+
title_font_family="Tajawal",
|
771 |
+
title_x=0.5,
|
772 |
+
xaxis_title="استراتيجية المعالجة",
|
773 |
+
yaxis_title="عدد المخاطر"
|
774 |
+
)
|
775 |
+
st.plotly_chart(fig, use_container_width=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
776 |
|
777 |
+
# قسم المناقصات القادمة
|
778 |
+
st.markdown("## 📅 المناقصات القادمة للإغلاق")
|
779 |
+
upcoming_tenders = st.session_state.tenders_df[
|
780 |
+
(pd.to_datetime(st.session_state.tenders_df['تاريخ الإغلاق']) > datetime.now()) &
|
781 |
+
(pd.to_datetime(st.session_state.tenders_df['تاريخ الإغلاق']) <= datetime.now() + timedelta(days=14))
|
782 |
+
]
|
783 |
+
upcoming_tenders = upcoming_tenders.sort_values('تاريخ الإغلاق')
|
784 |
|
785 |
+
if not upcoming_tenders.empty:
|
786 |
+
# عرض المناقصات في جدول
|
787 |
+
upcoming_display = upcoming_tenders[['رقم المناقصة', 'العنوان', 'تاريخ الإغلاق', 'القيمة التقديرية', 'نسبة النجاح المتوقعة']]
|
788 |
+
upcoming_display['أيام متبقية'] = (pd.to_datetime(upcoming_display['تاريخ الإغلاق']) - datetime.now()).dt.days
|
789 |
+
upcoming_display['القيمة التقديرية'] = upcoming_display['القيمة التقديرية'].apply(lambda x: f"{x:,.0f} ريال")
|
790 |
+
upcoming_display['نسبة النجاح المتوقعة'] = upcoming_display['نسبة النجاح المتوقعة'].apply(lambda x: f"{x}%")
|
791 |
+
|
792 |
+
st.dataframe(
|
793 |
+
upcoming_display,
|
794 |
+
hide_index=True,
|
795 |
+
use_container_width=True
|
796 |
+
)
|
797 |
+
else:
|
798 |
+
st.info("لا توجد مناقصات ستغلق خلال الأسبوعين القادمين")
|
799 |
|
800 |
+
# قسم تحليل الذكاء الاصطناعي
|
801 |
+
st.markdown("## 🤖 تحليلات الذكاء الاصطناعي")
|
|
|
802 |
|
803 |
+
col1, col2 = st.columns(2)
|
804 |
|
805 |
with col1:
|
806 |
+
st.markdown("### التنبؤ بنجاح المناقصات")
|
807 |
+
st.markdown("""
|
808 |
+
تستخدم خوارزميات الذكاء الاصطناعي لتحليل البيانات التاريخية وتحديد العوامل المؤثرة في نجاح المناقصات.
|
809 |
+
يمكن للنظام التنبؤ بنسب النجاح المتوقعة للمناقصات الجديدة بناءً على خصائصها ومتطلباتها.
|
810 |
+
""")
|
811 |
+
|
812 |
+
if st.button("تحليل مناقصة جديدة", key="analyze_new_tender"):
|
813 |
+
with st.spinner("جاري تحليل المناقصة..."):
|
814 |
+
time.sleep(2) # محاكاة وقت المعالجة
|
815 |
+
st.success("تم التحليل بنجاح!")
|
816 |
+
st.info("نسبة النجاح المتوقعة للمناق��ة: 78.5%")
|
817 |
+
st.markdown("""
|
818 |
+
**العوامل المؤثرة في النتيجة:**
|
819 |
+
- قدرة عالية على تلبية المتطلبات الفنية (+15%)
|
820 |
+
- توافق المنتج مع المعايير المطلوبة (+12%)
|
821 |
+
- خبرة سابقة مع نفس الجهة (+10%)
|
822 |
+
- المنافسة المتوقعة قوية (-7%)
|
823 |
+
""")
|
824 |
|
825 |
with col2:
|
826 |
+
st.markdown("### تحليل المتطلبات")
|
827 |
+
st.markdown("""
|
828 |
+
يقوم النظام بتحليل وثائق المتطلبات باستخدام تقنيات معالجة اللغة الطبيعية لاستخراج البنود المهمة والمخاطر المحتملة.
|
829 |
+
يساعد هذا التحليل في تقييم مدى تعقيد المتطلبات وتحديد النقاط التي تحتاج إلى اهتمام خاص.
|
830 |
+
""")
|
831 |
|
832 |
+
uploaded_file = st.file_uploader("رفع ملف متطلبات للتحليل", type=['pdf', 'docx', 'txt'])
|
833 |
+
if uploaded_file is not None:
|
834 |
+
with st.spinner("جاري تحليل المتطلبات..."):
|
835 |
+
time.sleep(3) # محاكاة وقت المعالجة
|
836 |
+
st.success("تم تحليل المتطلبات بنجاح!")
|
837 |
+
|
838 |
+
# عرض نتائج التحليل
|
839 |
+
col1, col2 = st.columns(2)
|
840 |
+
with col1:
|
841 |
+
st.metric("عدد المتطلبات", "27")
|
842 |
+
st.metric("المتطلبات الوظيفية", "19")
|
843 |
+
st.metric("المتطلبات غير الوظيفية", "8")
|
844 |
+
|
845 |
+
with col2:
|
846 |
+
st.metric("درجة التعقيد", "متوسطة")
|
847 |
+
st.metric("المخاطر المحتملة", "4")
|
848 |
+
st.metric("البنود الغامضة", "3")
|
849 |
+
|
850 |
def dashboard_page():
|
851 |
"""صفحة لوحة المؤشرات"""
|
852 |
st.markdown("# 📊 لوحة المؤشرات")
|
|
|
1003 |
st.dataframe(upcoming_display, hide_index=True, use_container_width=True)
|
1004 |
else:
|
1005 |
st.info("لا توجد مناقصات قادمة خلال الثلاثين يوماً القادمة.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1006 |
|
1007 |
def procurement_page():
|
1008 |
"""صفحة المناقصات والعقود"""
|
|
|
1217 |
st.metric("التكلفة التقديرية", "435,000 ريال")
|
1218 |
st.metric("درجة التعقيد", "متوسطة")
|
1219 |
|
1220 |
+
with tab2:
|
1221 |
requirements_demo = pd.DataFrame({
|
1222 |
"المعرف": [f"REQ-{i:03d}" for i in range(1, 11)],
|
1223 |
"النوع": ["وظيفي", "وظيفي", "غير وظيفي", "وظيفي", "وظيفي", "غير وظيفي", "وظيفي", "وظيفي", "غير وظيفي", "وظيفي"],
|