import React, { useState, useEffect, useMemo } from 'react'; import { initializeApp } from 'firebase/app'; import { getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken } from 'firebase/auth'; import { getFirestore, collection, onSnapshot, addDoc, doc, setDoc, getDoc, query, orderBy } from 'firebase/firestore'; import { Upload, Lock, Unlock, Plus, X, Calendar, Users, FileText, CheckSquare, Image as ImageIcon, LogOut, LayoutDashboard, Table as TableIcon, CalendarDays, List, Download, ChevronUp, ChevronDown, UserPlus, Printer } from 'lucide-react'; // --- Firebase Initialization --- const myCustomConfig = { apiKey: "AIzaSyDeOqnl-ad-pfnrA9iEZmvi9y7BmvhTkao", authDomain: "salam-report.firebaseapp.com", projectId: "salam-report", storageBucket: "salam-report.firebasestorage.app", messagingSenderId: "549503940854", appId: "1:549503940854:web:76b159b1c9094633db54d7", measurementId: "G-DBGFT4316J" }; const firebaseConfig = myCustomConfig.apiKey !== "YOUR_API_KEY" ? myCustomConfig : (typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : {}); const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getFirestore(app); const appId = "salam-report-v1"; export default function App() { const [user, setUser] = useState(null); const [isAdmin, setIsAdmin] = useState(false); const [activeTab, setActiveTab] = useState('reports'); const [showLoginModal, setShowLoginModal] = useState(false); const [showUploadModal, setShowUploadModal] = useState(false); const [reports, setReports] = useState([]); const [metadata, setMetadata] = useState({ targetAudiences: [], programs: [], activityNames: [] }); useEffect(() => { const initAuth = async () => { try { if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) { await signInWithCustomToken(auth, __initial_auth_token); } else { await signInAnonymously(auth); } } catch (error) { console.error("Auth Error:", error); } }; initAuth(); const unsubscribe = onAuthStateChanged(auth, (currentUser) => { setUser(currentUser); if (sessionStorage.getItem('isAdminLogged') === 'true') setIsAdmin(true); }); return () => unsubscribe(); }, []); useEffect(() => { if (!user) return; // استخدام مسار موحد للتقارير لضمان التوافق مع القواعد المفتوحة const reportsRef = collection(db, 'reports'); const unsubscribeReports = onSnapshot(reportsRef, (snapshot) => { const reportsData = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); setReports(reportsData); }, (error) => { console.error("Firestore Reports Error:", error); }); const metadataRef = doc(db, 'settings', 'metadata'); const unsubscribeMetadata = onSnapshot(metadataRef, (docSnap) => { if (docSnap.exists()) setMetadata(docSnap.data()); }, (error) => { console.error("Firestore Metadata Error:", error); }); return () => { unsubscribeReports(); unsubscribeMetadata(); }; }, [user]); const handleLogin = (username, password) => { if (username === 'admin' && password === '123456') { setIsAdmin(true); sessionStorage.setItem('isAdminLogged', 'true'); setShowLoginModal(false); } else { alert('اسم المستخدم أو رمز الدخول غير صحيح'); } }; const handleLogout = () => { setIsAdmin(false); sessionStorage.removeItem('isAdminLogged'); }; return (

منصة إدارة الأنشطة

{isAdmin && ( )} {isAdmin ? ( ) : ( )}
{activeTab === 'reports' && } {activeTab === 'detailedTable' && } {activeTab === 'monthlyTable' && } {activeTab === 'stats' && }
{showLoginModal && setShowLoginModal(false)} onLogin={handleLogin} />} {showUploadModal && isAdmin && setShowUploadModal(false)} metadata={metadata} user={user} />}
); } // --- Shared Components --- function TabButton({ active, icon, text, onClick }) { return ( ); } const handlePrintTable = () => { window.print(); }; // --- Page: Reports --- function ReportsPage({ reports }) { const sortedReports = useMemo(() => [...reports].sort((a, b) => new Date(b.date) - new Date(a.date)), [reports]); if (reports.length === 0) return } text="لا توجد تقارير حالياً" />; return (
{sortedReports.map((report) => (
{report.imageUrl ? ( {report.program} ) : (
)}
{report.beneficiariesCount || 0} مستفيد

{report.program}

{report.date}

الفئة: {report.targetAudience}

{report.activities?.map((act, idx) => (
{act.name}
{act.description &&

{act.description}

}
))}
))}
); } // --- Page: Detailed Table --- function DetailedTablePage({ reports }) { const [sortConfig, setSortConfig] = useState({ key: 'date', direction: 'desc' }); const allActivityNames = useMemo(() => { const names = new Set(); reports.forEach(r => r.activities?.forEach(a => names.add(a.name))); return Array.from(names); }, [reports]); const sortedData = useMemo(() => { let items = [...reports]; items.sort((a, b) => { if (a[sortConfig.key] < b[sortConfig.key]) return sortConfig.direction === 'asc' ? -1 : 1; if (a[sortConfig.key] > b[sortConfig.key]) return sortConfig.direction === 'asc' ? 1 : -1; return 0; }); return items; }, [reports, sortConfig]); const requestSort = (key) => { let direction = 'asc'; if (sortConfig.key === key && sortConfig.direction === 'asc') direction = 'desc'; setSortConfig({ key, direction }); }; if (reports.length === 0) return } text="لا توجد بيانات للجدول" />; return (
{allActivityNames.map(name => ( ))} {sortedData.map((report) => ( {allActivityNames.map(activityName => { const act = report.activities?.find(a => a.name === activityName); return ( ); })} ))}
{name}
{report.date} {report.program} {report.targetAudience} {report.beneficiariesCount || 0} {act ? (
{act.description}
) : -}
); } function SortableHeader({ label, field, config, onSort }) { const isActive = config.key === field; return ( onSort(field)} className="p-4 cursor-pointer hover:bg-slate-700 transition-colors whitespace-nowrap print:cursor-default">
{label} {isActive ? (config.direction === 'asc' ? : ) :
}
); } // --- Page: Monthly Table --- function MonthlyTablePage({ reports }) { const [sortConfig, setSortConfig] = useState({ key: 'month', direction: 'desc' }); const monthlyData = useMemo(() => { const monthMap = {}; reports.forEach(r => { const month = r.date?.substring(0, 7) || 'غير محدد'; r.activities?.forEach(act => { const key = `${month}-${act.name}`; if (!monthMap[key]) monthMap[key] = { month, activity: act.name, count: 0 }; monthMap[key].count++; }); }); return Object.values(monthMap); }, [reports]); const sortedData = useMemo(() => { let items = [...monthlyData]; items.sort((a, b) => { if (a[sortConfig.key] < b[sortConfig.key]) return sortConfig.direction === 'asc' ? -1 : 1; if (a[sortConfig.key] > b[sortConfig.key]) return sortConfig.direction === 'asc' ? 1 : -1; return 0; }); return items; }, [monthlyData, sortConfig]); const requestSort = (key) => { let direction = 'asc'; if (sortConfig.key === key && sortConfig.direction === 'asc') direction = 'desc'; setSortConfig({ key, direction }); }; if (monthlyData.length === 0) return } text="لا توجد بيانات شهرية" />; return (
{sortedData.map((row, i) => ( ))}
#
{i + 1} {row.month} {row.activity} {row.count}
); } // --- Page: Stats --- function StatsPage({ reports }) { const stats = useMemo(() => { let totalBeneficiaries = 0; let totalActs = 0; reports.forEach(r => { totalBeneficiaries += (parseInt(r.beneficiariesCount) || 0); totalActs += (r.activities?.length || 0); }); return { reportsCount: reports.length, actsCount: totalActs, beneficiaries: totalBeneficiaries, programs: new Set(reports.map(r => r.program)).size }; }, [reports]); return (
} color="text-blue-600 bg-blue-50" /> } color="text-green-600 bg-green-50" /> } color="text-purple-600 bg-purple-50" /> } color="text-orange-600 bg-orange-50" />
); } function StatCard({ title, value, icon, color }) { return (
{icon}

{title}

{value}

); } // --- Modals --- function UploadModal({ onClose, metadata, user }) { const [formData, setFormData] = useState({ date: new Date().toISOString().split('T')[0], targetAudience: '', newTarget: '', program: '', newProgram: '', beneficiariesCount: '', imageUrl: '' }); const [activitiesList, setActivitiesList] = useState([]); const [newActivityName, setNewActivityName] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); useEffect(() => { setActivitiesList((metadata.activityNames || []).map(name => ({ name, description: '', checked: false }))); }, [metadata]); const handleSubmit = async (e) => { e.preventDefault(); const finalTarget = formData.newTarget.trim() || formData.targetAudience; const finalProgram = formData.newProgram.trim() || formData.program; const selectedActs = activitiesList.filter(a => a.checked).map(a => ({ name: a.name, description: a.description })); if (!finalTarget || !finalProgram || selectedActs.length === 0) return alert('أكمل البيانات المطلوبة'); setIsSubmitting(true); try { const metadataRef = doc(db, 'settings', 'metadata'); const newMeta = { targetAudiences: metadata.targetAudiences || [], programs: metadata.programs || [], activityNames: metadata.activityNames || [] }; let changed = false; if (finalTarget && !newMeta.targetAudiences.includes(finalTarget)) { newMeta.targetAudiences.push(finalTarget); changed = true; } if (finalProgram && !newMeta.programs.includes(finalProgram)) { newMeta.programs.push(finalProgram); changed = true; } selectedActs.forEach(a => { if (!newMeta.activityNames.includes(a.name)) { newMeta.activityNames.push(a.name); changed = true; } }); if (changed) await setDoc(metadataRef, newMeta, { merge: true }); await addDoc(collection(db, 'reports'), { ...formData, targetAudience: finalTarget, program: finalProgram, activities: selectedActs, createdAt: new Date().toISOString() }); onClose(); } catch (err) { console.error("Submit Error:", err); alert('خطأ في الاتصال بقاعدة البيانات. تأكد من أن قواعد الحماية (Rules) محدثة في Firebase Console.'); } finally { setIsSubmitting(false); } }; return (

رفع تقرير نشاط

setFormData({...formData, date: v})} /> setFormData({...formData, beneficiariesCount: v})} placeholder="0" /> setFormData({...formData, imageUrl: v})} placeholder="https://..." />
setFormData({...formData, targetAudience: v, newTarget: ''})} onNew={v => setFormData({...formData, newTarget: v, targetAudience: ''})} /> setFormData({...formData, program: v, newProgram: ''})} onNew={v => setFormData({...formData, newProgram: v, program: ''})} />

الأنشطة والوصف

{activitiesList.map((act, i) => (
{ const newList = [...activitiesList]; newList[i].checked = !newList[i].checked; setActivitiesList(newList); }} /> {act.name}
{act.checked && (