Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 6 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,9 @@
<p align="center">
<img src="https://copilot.microsoft.com/th/id/BCO.e7449f1e-e1d0-4557-85d9-832895266a75.png" alt="School Library Logo" width="150"/>
</p># 📚 المكتبة المدرسية - نسخة تعليمية
# بوابة مدرسة النهضه الجديده

واجهة ويب ثابتة لعرض كتب مدرسية التعلم فقط.
هذا المستودع محدث ليصبح مخصصًا لمدرسة النهضه الجديده. يحتوي على واجهة ويب بسيطة مترجمة للعربية وتدعم الربط مع Firebase (Firestore, Storage, Auth).

## ✨ الميزات
- بحث فوري بالعنوان أو المؤلف
- فلاتر حسب المرحلة، التخصص، والصيغة
- تصميم خفيف ومتجاوب يعمل على أي متصفح
ملاحظات سريعة:
- أضفت لوحة إدارة لخلق الإعلانات والصفوف.
- يمكنك تسجيل دخول المسؤول عبر Email/Password (أنشئ المستخدم في Firebase auth ثم ضع في مجموعة users/{uid} الحقل role = 'admin').
- لتشغيل: ضع إعدادات firebaseConfig في `index.html` ثم قم برفع المستودع إلى GitHub Pages أو Firebase Hosting.

## 🚀 التشغيل
افتح ملف `index.html` في المتصفح مباشرة
أو فعّل **GitHub Pages** من إعدادات المستودع لعرض الموقع أونلاين.

##

## 📜 الترخيص
MIT licnese ©2025
moheynasir-ctrl
172 changes: 172 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// app.js — تكامل مع Firestore و Storage ومزايا الإدارة والرفع
const db = firebase.firestore();
const auth = firebase.auth();
const storage = firebase.storage();

// DOM helpers
const $ = id => document.getElementById(id);

// Tab navigation
['ann','classes','teachers','attend','homework','admin'].forEach(k=>{
$('tab-'+k).addEventListener('click', ()=> showTab(k === 'ann' ? 'announcements' : k === 'attend' ? 'attendance' : k === 'homework' ? 'homework' : k === 'admin' ? 'admin' : k+'s'));
});
function showTab(id){
document.querySelectorAll('.tab').forEach(s=>s.hidden=true);
document.getElementById(id).hidden=false;
}

// Escape
function esc(s){ return String(s||'').replace(/[&<>\"']/g,m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m])); }

// Renderers
function renderAnnouncements(docs){
const ul = $('ann-list'); ul.innerHTML = '';
docs.forEach(doc=>{
const d = doc.data();
const li = document.createElement('li');
li.innerHTML = `<strong>${esc(d.title)}</strong><div>${esc(d.body)}</div><small class="muted">${esc(d.date||'')}</small>`;
ul.appendChild(li);
});
}
function renderClasses(docs){
const ul = $('class-list'); ul.innerHTML = '';
docs.forEach(doc=>{
const c = doc.data();
const li = document.createElement('li');
li.innerHTML = `<strong>${esc(c.name)}</strong> — <small>${esc(c.teacher)}</small>`;
ul.appendChild(li);
});
}
function renderTeachers(docs){
const ul = $('teacher-list'); ul.innerHTML = '';
docs.forEach(doc=>{
const t = doc.data();
const li = document.createElement('li');
li.innerHTML = `<strong>${esc(t.name)}</strong> — <small>${esc(t.email||'')}</small>`;
ul.appendChild(li);
});
}
function renderAttendance(docs){
const ul = $('att-log'); ul.innerHTML = '';
docs.forEach(doc=>{
const a = doc.data();
const ts = a.createdAt && a.createdAt.toDate ? a.createdAt.toDate().toLocaleString() : '';
const li = document.createElement('li'); li.textContent = `${ts} — ${a.name} — ${a.status}`;
ul.appendChild(li);
});
}
function renderHomework(docs){
const ul = $('hw-list'); ul.innerHTML = '';
docs.forEach(doc=>{
const h = doc.data();
const li = document.createElement('li');
const link = h.fileUrl ? ` <a href="${h.fileUrl}" target="_blank">تحميل الملف</a>` : '';
li.innerHTML = `<strong>${esc(h.title)}</strong> — ${esc(h.student)}${link} <small>${h.createdAt && h.createdAt.toDate ? h.createdAt.toDate().toLocaleString() : ''}</small>`;
ul.appendChild(li);
});
}

// Auth: admin login (email/password) or anonymous for normal users
auth.onAuthStateChanged(user=>{
if(user){
// show admin if custom claim or if email verified — for simplicity, we check a field in users collection
db.collection('users').doc(user.uid).get().then(snap=>{
const isAdmin = snap.exists && snap.data().role === 'admin';
$('admin-logout').hidden = false;
if(isAdmin){
$('admin-actions').hidden = false;
} else {
$('admin-actions').hidden = true;
}
}).catch(()=>{ $('admin-actions').hidden = true; });
setupRealtime();
}else{
// sign in anonymously for regular usage
auth.signInAnonymously().catch(()=>{ /* ignore */ });
}
});

// Admin login buttons
$('admin-login').addEventListener('click', async ()=>{
const email = $('admin-email').value.trim();
const pass = $('admin-pass').value;
if(!email || !pass) return alert('أدخل بيانات الدخول');
try{
await auth.signInWithEmailAndPassword(email, pass);
// after login, back-end (you) can set users/{uid}.role = 'admin' in Firestore
$('admin-actions').hidden = false;
alert('تم تسجيل الدخول كمسؤول');
}catch(e){ alert('فشل تسجيل الدخول: '+e.message); }
});
$('admin-logout').addEventListener('click', ()=>auth.signOut());

// Add announcement (admin)
$('add-ann-form').addEventListener('submit', async e=>{
e.preventDefault();
const title = $('ann-title').value.trim();
const body = $('ann-body').value.trim();
if(!title || !body) return;
await db.collection('announcements').add({title, body, date: new Date().toISOString()});
$('ann-title').value=''; $('ann-body').value='';
});

// Add class (admin)
$('add-class-form').addEventListener('submit', async e=>{
e.preventDefault();
const name = $('class-name').value.trim();
const teacher = $('class-teacher').value.trim();
if(!name) return;
await db.collection('classes').add({name, teacher});
$('class-name').value=''; $('class-teacher').value='';
});

// Attendance form
$('att-form').addEventListener('submit', async e=>{
e.preventDefault();
const name = $('att-name').value.trim();
const status = $('att-status').value;
if(!name) return;
await db.collection('attendance').add({name, status, createdAt: firebase.firestore.FieldValue.serverTimestamp()});
$('att-name').value='';
});

// Homework upload
$('hw-form').addEventListener('submit', async e=>{
e.preventDefault();
const title = $('hw-title').value.trim();
const student = $('hw-student').value.trim();
const file = $('hw-file').files[0];
if(!title||!student||!file) return alert('املأ الحقول واختر ملف');
const path = `homework/${Date.now()}_${file.name}`;
const ref = storage.ref(path);
const snap = await ref.put(file);
const url = await snap.ref.getDownloadURL();
await db.collection('homework').add({title, student, fileUrl: url, createdAt: firebase.firestore.FieldValue.serverTimestamp()});
$('hw-title').value=''; $('hw-student').value=''; $('hw-file').value='';
alert('تم إرسال الواجب');
});

// Real-time listeners
function setupRealtime(){
db.collection('announcements').orderBy('date','desc').onSnapshot(snap => renderAnnouncements(snap.docs));
db.collection('classes').orderBy('name').onSnapshot(snap => renderClasses(snap.docs));
db.collection('teachers').orderBy('name').onSnapshot(snap => renderTeachers(snap.docs));
db.collection('attendance').orderBy('createdAt','desc').limit(50).onSnapshot(snap => renderAttendance(snap.docs));
db.collection('homework').orderBy('createdAt','desc').onSnapshot(snap => renderHomework(snap.docs));
}

// Search (simple client-side)
$('search').addEventListener('input', e=>{
const q = e.target.value.trim().toLowerCase();
// filter announcements client-side (simple)
db.collection('announcements').orderBy('date','desc').get().then(snap=>{
const docs = snap.docs.filter(d=>{
const t = (d.data().title||'') + ' ' + (d.data().body||'');
return t.toLowerCase().includes(q);
});
renderAnnouncements(docs);
});
});

// initial tab
showTab('announcements');
11 changes: 11 additions & 0 deletions data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"announcements": [
{"id":1,"title":"مرحباً بكم في مدرسة النهضه الجديده","body":"نرحب بجميع الطلبة وأولياء الأمور في العام الدراسي الجديد.","date":"2026-01-02"}
],
"classes": [
{"id":1,"name":"الصف الأول","teacher":"أ. فاطمة"}
],
"teachers": [
{"id":1,"name":"أ. فاطمة","email":"[email protected]"}
]
}
167 changes: 118 additions & 49 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,58 +1,127 @@
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<!doctype html>
<html lang="ar">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>المكتبة المدرسية - نسخة تعليمية</title>
<link rel="stylesheet" href="style.css" />
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>بوابة مدرسة النهضه الجديده</title>
<link rel="manifest" href="manifest.json">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header class="header">
<h1>المكتبة المدرسية</h1>
<p class="subtitle">نسخة تعليمية تجريبية ببيانات وهمية لأغراض التعلم فقط</p>
<body dir="rtl" class="rtl">
<header>
<div class="brand">
<img id="school-logo" src="logo-192.svg" alt="شعار المدرسة" width="48" height="48">
<h1>مدرسة النهضه الجديده</h1>
</div>
<nav>
<button id="tab-ann">الإعلانات</button>
<button id="tab-classes">الصفوف</button>
<button id="tab-teachers">المعلمين</button>
<button id="tab-attend">الحضور</button>
<button id="tab-homework">الواجبات</button>
<button id="tab-admin">لوحة الإدارة</button>
</nav>
</header>

<section class="controls">
<div class="control">
<label for="q">بحث</label>
<input id="q" type="search" placeholder="ابحث بعنوان الكتاب أو اسم المؤلف..." />
</div>
<div class="control">
<label for="level">المرحلة</label>
<select id="level">
<option value="">الكل</option>
<option value="أساسي">أساسي</option>
<option value="ثانوي">ثانوي</option>
</select>
</div>
<div class="control">
<label for="subject">التخصص</label>
<select id="subject">
<option value="">الكل</option>
<option value="رياضيات">رياضيات</option>
<option value="علوم">علوم</option>
<option value="لغة عربية">لغة عربية</option>
<option value="لغة إنجليزية">لغة إنجليزية</option>
<option value="تاريخ">تاريخ</option>
</select>
</div>
<div class="control">
<label for="format">صيغة</label>
<select id="format">
<option value="">الكل</option>
<option value="PDF">PDF</option>
<option value="EPUB">EPUB</option>
</select>
</div>
<button id="clear">مسح الفلاتر</button>
</section>
<main id="app">
<section id="announcements" class="tab">
<div class="toolbar">
<input id="search" placeholder="بحث..." />
<button id="refresh">تحديث</button>
</div>
<h2>الإعلانات</h2>
<ul id="ann-list"></ul>
</section>

<section id="classes" class="tab" hidden>
<h2>الصفوف</h2>
<ul id="class-list"></ul>
</section>

<section id="teachers" class="tab" hidden>
<h2>المعلمين</h2>
<ul id="teacher-list"></ul>
</section>

<section id="attendance" class="tab" hidden>
<h2>تسجيل الحضور</h2>
<form id="att-form">
<label>اسم الطالب<input id="att-name" required></label>
<label>الحالة
<select id="att-status">
<option>حاضر</option>
<option>غائب</option>
<option>متأخر</option>
</select>
</label>
<button type="submit">سجل</button>
</form>
<ul id="att-log"></ul>
</section>

<section id="homework" class="tab" hidden>
<h2>تسليم الواجبات</h2>
<form id="hw-form">
<label>عنوان الواجب<input id="hw-title" required></label>
<label>اسم الطالب<input id="hw-student" required></label>
<label>الملف <input id="hw-file" type="file" required></label>
<button type="submit">أرسل الواجب</button>
</form>
<ul id="hw-list"></ul>
</section>

<section id="admin" class="tab" hidden>
<h2>لوحة الإدارة</h2>
<div id="admin-auth">
<label>البريد الإلكتروني<input id="admin-email" type="email" value="[email protected]"></label>
<label>كلمة المرور<input id="admin-pass" type="password"></label>
<button id="admin-login">تسجيل دخول</button>
<button id="admin-logout" hidden>تسجيل خروج</button>
</div>

<div id="admin-actions" hidden>
<h3>إضافة إعلان</h3>
<form id="add-ann-form">
<input id="ann-title" placeholder="العنوان" required>
<textarea id="ann-body" placeholder="النص" required></textarea>
<button type="submit">أضف إعلان</button>
</form>

<h3>إضافة صف</h3>
<form id="add-class-form">
<input id="class-name" placeholder="اسم الصف" required>
<input id="class-teacher" placeholder="اسم المدرس" required>
<button type="submit">أضف صف</button>
</form>
</div>
</section>
</main>

<main id="results" class="grid"></main>
<!-- Firebase compat SDKs -->
<script src="https://www.gstatic.com/firebasejs/9.22.1/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/9.22.1/firebase-firestore-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/9.22.1/firebase-auth-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/9.22.1/firebase-storage-compat.js"></script>

<footer class="footer">
<p>© نسخة تعليمية تجريبية. هذه البيانات وهمية وغير تجارية.</p>
</footer>
<script>
// استبدل القيم هنا بقيم مشروع Firebase لديك
const firebaseConfig = {
apiKey: "REPLACE_WITH_YOUR_API_KEY",
authDomain: "REPLACE_WITH_YOUR_AUTH_DOMAIN",
projectId: "REPLACE_WITH_YOUR_PROJECT_ID",
storageBucket: "REPLACE_WITH_YOUR_STORAGE_BUCKET",
messagingSenderId: "REPLACE_WITH_YOUR_MESSAGING_SENDER_ID",
appId: "REPLACE_WITH_YOUR_APP_ID"
};
firebase.initializeApp(firebaseConfig);
</script>

<script src="script.js"></script>
<script src="app.js"></script>
<script>
// تسجيل service worker بسيط (يحسن تجربة PWA)
if('serviceWorker' in navigator){
navigator.serviceWorker.register('sw.js').catch(()=>{/* ignore */});
}
</script>
</body>
</html>
6 changes: 6 additions & 0 deletions logo-192.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading