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.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 => (
| {name} |
))}
{sortedData.map((report) => (
| {report.date} |
{report.program} |
{report.targetAudience} |
{report.beneficiariesCount || 0} |
{allActivityNames.map(activityName => {
const act = report.activities?.find(a => a.name === activityName);
return (
{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 (
);
}
// --- 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) => (
))}
);
}
function InputGroup({ label, ...props }) {
return (
props.onChange(e.target.value)} />
);
}
function SelectOrNew({ label, options, value, newValue, onSelect, onNew }) {
return (
onNew(e.target.value)} />
);
}
function LoginModal({ onClose, onLogin }) {
const [u, setU] = useState('');
const [p, setP] = useState('');
return (
);
}
function EmptyState({ icon, text }) {
return (
);
}