Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import sqlite3 | |
| from datetime import datetime | |
| from passlib.hash import bcrypt | |
| import os | |
| import shutil | |
| from dotenv import load_dotenv | |
| # تهيئة الصفحة | |
| st.set_page_config(page_title="نظام إدارة الشكاوى", layout="centered") | |
| # تحميل متغيرات البيئة | |
| load_dotenv() | |
| ADMIN_DB_PASSWORD = os.getenv("ADMIN_DB_PASS", "Admin@1234") # كلمة مرور افتراضية إذا لم يتم تعيينها | |
| # إدارة اتصال قاعدة البيانات | |
| def get_db_connection(): | |
| conn = sqlite3.connect("complaintsBD.db", check_same_thread=False) | |
| conn.row_factory = sqlite3.Row | |
| return conn | |
| # إنشاء الجداول والتأكد من وجود جميع الأعمدة | |
| def init_db(): | |
| conn = get_db_connection() | |
| # إنشاء جدول المستخدمين إذا لم يكن موجوداً | |
| conn.execute(""" | |
| CREATE TABLE IF NOT EXISTS users ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| username TEXT UNIQUE, | |
| password TEXT, | |
| role TEXT | |
| ) | |
| """) | |
| # إنشاء جدول الشكاوى (لو مش موجود) | |
| conn.execute(""" | |
| CREATE TABLE IF NOT EXISTS complaints ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| complaint_id TEXT UNIQUE, | |
| username TEXT, | |
| complaint_text TEXT, | |
| status TEXT, | |
| response TEXT, | |
| timestamp TEXT, | |
| sender_comment TEXT DEFAULT '' | |
| ) | |
| """) | |
| # ✅ التأكد من وجود عمود sender_comment (وإضافته لو ناقص) | |
| try: | |
| columns = [col[1] for col in conn.execute("PRAGMA table_info(complaints)").fetchall()] | |
| if "sender_comment" not in columns: | |
| conn.execute("ALTER TABLE complaints ADD COLUMN sender_comment TEXT DEFAULT ''") | |
| conn.commit() | |
| except Exception as e: | |
| st.error(f"⚠️ خطأ أثناء التأكد من الأعمدة: {e}") | |
| # إنشاء جدول معلومات قاعدة البيانات | |
| conn.execute(""" | |
| CREATE TABLE IF NOT EXISTS db_info ( | |
| version INTEGER PRIMARY KEY | |
| ) | |
| """) | |
| # إدراج الإصدار الأول إذا لم يكن موجوداً | |
| conn.execute(""" | |
| INSERT OR IGNORE INTO db_info (version) VALUES (1) | |
| """) | |
| conn.commit() | |
| conn.close() | |
| # إدارة حالة الجلسة | |
| def init_session(): | |
| if "auth" not in st.session_state: | |
| st.session_state.auth = { | |
| "is_logged_in": False, | |
| "username": "", | |
| "role": "" | |
| } | |
| init_session() | |
| # واجهة تسجيل الدخول | |
| def login_page(): | |
| st.title("🔐 تسجيل الدخول") | |
| username = st.text_input("اسم المستخدم") | |
| password = st.text_input("كلمة المرور", type="password") | |
| if st.button("تسجيل الدخول"): | |
| conn = get_db_connection() | |
| user = conn.execute("SELECT * FROM users WHERE username = ?", (username,)).fetchone() | |
| conn.close() | |
| if user and bcrypt.verify(password, user["password"]): | |
| st.session_state.auth = { | |
| "is_logged_in": True, | |
| "username": user["username"], | |
| "role": user["role"] | |
| } | |
| st.rerun() | |
| elif user: | |
| st.error("❌ كلمة المرور غير صحيحة") | |
| else: | |
| st.warning("❌ اسم المستخدم غير موجود") | |
| # صفحة مقدم الشكاوى | |
| def sender_page(): | |
| st.title("✉️ تقديم شكوى جديدة") | |
| conn = get_db_connection() | |
| username = st.session_state.auth["username"] | |
| try: | |
| user_complaint_count = conn.execute( | |
| "SELECT COUNT(*) FROM complaints WHERE username = ?", | |
| (username,) | |
| ).fetchone()[0] | |
| complaint_id = f"{username}_{user_complaint_count + 1:06d}" | |
| complaint_text = st.text_area("📝 اكتب شكواك هنا", key="complaint_input") | |
| if st.button("📨 إرسال الشكوى"): | |
| if complaint_text.strip(): | |
| timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| conn.execute( | |
| "INSERT INTO complaints (complaint_id, username, complaint_text, status, response, timestamp, sender_comment) VALUES (?, ?, ?, ?, ?, ?, ?)", | |
| (complaint_id, username, complaint_text.strip(), "Pending", "", timestamp, "") | |
| ) | |
| conn.commit() | |
| st.success(f"✅ تم إرسال الشكوى بنجاح. معرف الشكوى هو: {complaint_id}") | |
| st.rerun() | |
| else: | |
| st.warning("⚠️ لا يمكن إرسال شكوى فارغة") | |
| st.subheader("📋 الشكاوى السابقة") | |
| complaints = conn.execute( | |
| "SELECT * FROM complaints WHERE username = ? ORDER BY id DESC", | |
| (username,) | |
| ).fetchall() | |
| for complaint in complaints: | |
| with st.expander(f"📨 {complaint['complaint_id']} | الحالة: {complaint['status']}"): | |
| st.write(f"📅 تم الإرسال في: {complaint['timestamp']}") | |
| st.write(f"📄 الشكوى: {complaint['complaint_text']}") | |
| if complaint['response']: | |
| st.success(f"💬 الرد من المسؤول: {complaint['response']}") | |
| st.toast(f"📬 تم الرد على الشكوى رقم {complaint['complaint_id']}") | |
| comment_key = f"comment_{complaint['complaint_id']}" | |
| sender_comment = st.text_area("🗨️ تعليقك على الرد", | |
| value=complaint['sender_comment'] or "", | |
| key=comment_key) | |
| if st.button("💬 حفظ التعليق", key=f"save_comment_{complaint['complaint_id']}"): | |
| if sender_comment.strip(): | |
| conn.execute( | |
| "UPDATE complaints SET sender_comment = ? WHERE complaint_id = ?", | |
| (sender_comment.strip(), complaint['complaint_id']) | |
| ) | |
| conn.commit() | |
| st.success("✅ تم حفظ تعليقك على الرد") | |
| st.rerun() | |
| finally: | |
| conn.close() | |
| # صفحة مستقبل الشكاوى مع لوحة التحكم الإدارية المحمية | |
| def receiver_page(): | |
| st.title("📬 إدارة الشكاوى") | |
| tab1, tab2 = st.tabs(["إدارة الشكاوى", "لوحة التحكم الإدارية"]) | |
| with tab1: | |
| conn = get_db_connection() | |
| try: | |
| pending_count = conn.execute( | |
| "SELECT COUNT(*) FROM complaints WHERE status = 'Pending'" | |
| ).fetchone()[0] | |
| if pending_count > 0: | |
| st.warning(f"🚨 يوجد {pending_count} شكوى جديدة لم يتم التعامل معها!") | |
| complaints = conn.execute( | |
| "SELECT * FROM complaints ORDER BY id DESC" | |
| ).fetchall() | |
| if not complaints: | |
| st.info("لا توجد شكاوى حالياً.") | |
| else: | |
| st.subheader("📋 جميع الشكاوى") | |
| selected_complaint = st.selectbox( | |
| "اختر شكوى للتعامل معها", | |
| complaints, | |
| format_func=lambda x: f"{x['complaint_id']} - {x['username']}" | |
| ) | |
| st.subheader(f"📨 معرف الشكوى: {selected_complaint['complaint_id']}") | |
| st.write(f"👤 مقدم الشكوى: {selected_complaint['username']}") | |
| st.write(f"📝 نص الشكوى: {selected_complaint['complaint_text']}") | |
| st.write(f"📅 وقت الإرسال: {selected_complaint['timestamp']}") | |
| new_status = st.selectbox( | |
| "🔄 تحديث الحالة", | |
| ["Pending", "In Progress", "Resolved"], | |
| index=["Pending", "In Progress", "Resolved"].index(selected_complaint["status"]) | |
| ) | |
| new_response = st.text_area( | |
| "💬 الرد على الشكوى", | |
| value=selected_complaint["response"] or "" | |
| ) | |
| if st.button("💾 حفظ التعديلات"): | |
| conn.execute( | |
| "UPDATE complaints SET status = ?, response = ? WHERE complaint_id = ?", | |
| (new_status, new_response.strip(), selected_complaint["complaint_id"]) | |
| ) | |
| conn.commit() | |
| st.success("✅ تم حفظ التعديلات بنجاح") | |
| st.rerun() | |
| finally: | |
| conn.close() | |
| with tab2: | |
| st.title("⚙️ لوحة التحكم الإدارية") | |
| admin_pass = st.text_input("أدخل كلمة المرور الإدارية:", type="password", key="admin_pass") | |
| if admin_pass == ADMIN_DB_PASSWORD: | |
| st.success("✅ تم المصادقة بنجاح") | |
| if os.path.exists("complaintsBD.db"): | |
| with open("complaintsBD.db", "rb") as db_file: | |
| st.download_button( | |
| label="📥 تحميل نسخة احتياطية", | |
| data=db_file, | |
| file_name=f"complaints_backup_{datetime.now().strftime('%Y%m%d')}.db", | |
| mime="application/octet-stream" | |
| ) | |
| if st.button("🔄 إنشاء نسخة احتياطية جديدة"): | |
| backup_name = f"backup_{datetime.now().strftime('%Y%m%d_%H%M')}.db" | |
| shutil.copy2("complaintsBD.db", backup_name) | |
| st.success(f"تم إنشاء النسخة الاحتياطية: {backup_name}") | |
| elif admin_pass: | |
| st.error("❌ كلمة المرور الإدارية غير صحيحة") | |
| # الواجهة الرئيسية | |
| def main(): | |
| init_db() # تهيئة قاعدة البيانات | |
| # ✅ طباعة مسار قاعدة البيانات للتأكد | |
| st.write("📂 Database path:", os.path.abspath("complaintsBD.db")) | |
| if not st.session_state.auth["is_logged_in"]: | |
| login_page() | |
| else: | |
| if st.session_state.auth["role"] == "sender": | |
| sender_page() | |
| else: | |
| receiver_page() | |
| st.markdown("---") | |
| if st.button("🚪 تسجيل الخروج"): | |
| st.session_state.auth = { | |
| "is_logged_in": False, | |
| "username": "", | |
| "role": "" | |
| } | |
| st.rerun() | |
| if __name__ == "__main__": | |
| main() | |