|
|
<!DOCTYPE html> |
|
|
<html lang="ar" dir="rtl"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>نظام إدارة الفصل - مدرسة الشهيد حموم سعيد</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css"> |
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> |
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script> |
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> |
|
|
<style> |
|
|
@import url('https://fonts.googleapis.com/css2?family=Tajawal:wght@400;500;700&display=swap'); |
|
|
|
|
|
body { |
|
|
font-family: 'Tajawal', sans-serif; |
|
|
transition: background-color 0.3s, color 0.3s; |
|
|
} |
|
|
|
|
|
.rtl { |
|
|
direction: rtl; |
|
|
} |
|
|
|
|
|
.subject-icon { |
|
|
transition: transform 0.3s; |
|
|
} |
|
|
|
|
|
.subject-icon:hover { |
|
|
transform: scale(1.1); |
|
|
} |
|
|
|
|
|
.dark-mode { |
|
|
background-color: #1a202c; |
|
|
color: #f7fafc; |
|
|
} |
|
|
|
|
|
.dark-mode .box { |
|
|
background-color: #2d3748; |
|
|
color: #f7fafc; |
|
|
} |
|
|
|
|
|
.dark-mode .table { |
|
|
background-color: #2d3748; |
|
|
color: #f7fafc; |
|
|
} |
|
|
|
|
|
.dark-mode .table th, .dark-mode .table td { |
|
|
color: #f7fafc; |
|
|
border-color: #4a5568; |
|
|
} |
|
|
|
|
|
.dark-mode input, .dark-mode select { |
|
|
background-color: #4a5568; |
|
|
color: #f7fafc; |
|
|
border-color: #4a5568; |
|
|
} |
|
|
|
|
|
.dark-mode .button.is-primary { |
|
|
background-color: #4299e1; |
|
|
} |
|
|
|
|
|
.dark-mode .button.is-danger { |
|
|
background-color: #f56565; |
|
|
} |
|
|
|
|
|
.dark-mode .notification { |
|
|
background-color: #2d3748; |
|
|
color: #f7fafc; |
|
|
} |
|
|
|
|
|
.gallery-item { |
|
|
transition: transform 0.3s; |
|
|
} |
|
|
|
|
|
.gallery-item:hover { |
|
|
transform: scale(1.05); |
|
|
} |
|
|
|
|
|
.highlight { |
|
|
animation: highlight 2s; |
|
|
} |
|
|
|
|
|
@keyframes highlight { |
|
|
0% { background-color: yellow; } |
|
|
100% { background-color: transparent; } |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="rtl"> |
|
|
|
|
|
<section id="login-section" class="hero is-fullheight"> |
|
|
<div class="hero-body"> |
|
|
<div class="container"> |
|
|
<div class="columns is-centered"> |
|
|
<div class="column is-5-tablet is-4-desktop is-3-widescreen"> |
|
|
<div class="box p-6 has-text-centered"> |
|
|
<img src="https://img.icons8.com/color/96/000000/school.png" alt="School Logo" class="mb-4"> |
|
|
<h1 class="title is-3 mb-6">نظام إدارة الفصل الدراسي</h1> |
|
|
<h2 class="subtitle is-5 mb-6">مدرسة الشهيد حموم سعيد - بجاية</h2> |
|
|
|
|
|
<div class="field"> |
|
|
<label class="label">اسم المستخدم</label> |
|
|
<div class="control has-icons-left"> |
|
|
<input id="username" class="input" type="text" placeholder="اسم المستخدم" required> |
|
|
<span class="icon is-small is-left"> |
|
|
<i class="fas fa-user"></i> |
|
|
</span> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="field"> |
|
|
<label class="label">كلمة المرور</label> |
|
|
<div class="control has-icons-left"> |
|
|
<input id="password" class="input" type="password" placeholder="كلمة المرور" required> |
|
|
<span class="icon is-small is-left"> |
|
|
<i class="fas fa-lock"></i> |
|
|
</span> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="field mt-5"> |
|
|
<button id="login-btn" class="button is-primary is-fullwidth"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-sign-in-alt"></i> |
|
|
</span> |
|
|
<span>تسجيل الدخول</span> |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div id="login-error" class="notification is-danger is-light is-hidden"> |
|
|
اسم المستخدم أو كلمة المرور غير صحيحة! |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</section> |
|
|
|
|
|
|
|
|
<div id="app" class="is-hidden"> |
|
|
|
|
|
<nav class="navbar is-primary" role="navigation" aria-label="main navigation"> |
|
|
<div class="navbar-brand"> |
|
|
<a class="navbar-item" href="#"> |
|
|
<i class="fas fa-school mr-2"></i> |
|
|
<span>فصلي الأول</span> |
|
|
</a> |
|
|
|
|
|
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasic"> |
|
|
<span aria-hidden="true"></span> |
|
|
<span aria-hidden="true"></span> |
|
|
<span aria-hidden="true"></span> |
|
|
</a> |
|
|
</div> |
|
|
|
|
|
<div id="navbarBasic" class="navbar-menu"> |
|
|
<div class="navbar-start"> |
|
|
<a id="home-link" class="navbar-item"> |
|
|
<i class="fas fa-home mr-2"></i> |
|
|
الصفحة الرئيسية |
|
|
</a> |
|
|
|
|
|
<a id="grades-link" class="navbar-item"> |
|
|
<i class="fas fa-table mr-2"></i> |
|
|
إدارة الدرجات |
|
|
</a> |
|
|
|
|
|
<a id="gallery-link" class="navbar-item"> |
|
|
<i class="fas fa-images mr-2"></i> |
|
|
معرض الأعمال |
|
|
</a> |
|
|
</div> |
|
|
|
|
|
<div class="navbar-end"> |
|
|
<div class="navbar-item"> |
|
|
<div class="buttons"> |
|
|
<button id="dark-mode-toggle" class="button is-light"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-moon"></i> |
|
|
</span> |
|
|
<span>الوضع الليلي</span> |
|
|
</button> |
|
|
|
|
|
<button id="logout-btn" class="button is-light"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-sign-out-alt"></i> |
|
|
</span> |
|
|
<span>تسجيل الخروج</span> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</nav> |
|
|
|
|
|
|
|
|
<div class="notification-bell fixed top-4 right-4 z-50"> |
|
|
<div class="dropdown is-right is-hoverable"> |
|
|
<div class="dropdown-trigger"> |
|
|
<button id="notification-btn" class="button is-primary is-rounded" aria-haspopup="true" aria-controls="notification-menu"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-bell"></i> |
|
|
</span> |
|
|
<span id="notification-count" class="tag is-danger is-small is-rounded">0</span> |
|
|
</button> |
|
|
</div> |
|
|
<div class="dropdown-menu" id="notification-menu" role="menu"> |
|
|
<div class="dropdown-content"> |
|
|
<div class="dropdown-item"> |
|
|
<p>الإشعارات</p> |
|
|
</div> |
|
|
<hr class="dropdown-divider"> |
|
|
<div id="notification-items" class="dropdown-item"> |
|
|
<p class="has-text-grey">لا توجد إشعارات جديدة</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="container mt-5"> |
|
|
|
|
|
<section id="home-page" class="section"> |
|
|
<h1 class="title has-text-centered mb-6">مرحباً بك في نظام إدارة الفصل</h1> |
|
|
<h2 class="subtitle has-text-centered mb-6">اختر المادة التي تريد إدارتها</h2> |
|
|
|
|
|
<div class="columns is-multiline is-centered"> |
|
|
|
|
|
<div class="column is-2-desktop is-4-tablet"> |
|
|
<div class="box has-text-centered subject-icon" data-subject="arabic"> |
|
|
<span class="icon is-large mb-3"> |
|
|
<i class="fas fa-language fa-3x"></i> |
|
|
</span> |
|
|
<h3 class="title is-5">اللغة العربية</h3> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="column is-2-desktop is-4-tablet"> |
|
|
<div class="box has-text-centered subject-icon" data-subject="math"> |
|
|
<span class="icon is-large mb-3"> |
|
|
<i class="fas fa-square-root-alt fa-3x"></i> |
|
|
</span> |
|
|
<h3 class="title is-5">الرياضيات</h3> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="column is-2-desktop is-4-tablet"> |
|
|
<div class="box has-text-centered subject-icon" data-subject="islamic"> |
|
|
<span class="icon is-large mb-3"> |
|
|
<i class="fas fa-mosque fa-3x"></i> |
|
|
</span> |
|
|
<h3 class="title is-5">التربية الإسلامية</h3> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="column is-2-desktop is-4-tablet"> |
|
|
<div class="box has-text-centered subject-icon" data-subject="art"> |
|
|
<span class="icon is-large mb-3"> |
|
|
<i class="fas fa-palette fa-3x"></i> |
|
|
</span> |
|
|
<h3 class="title is-5">التربية الفنية</h3> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="column is-2-desktop is-4-tablet"> |
|
|
<div class="box has-text-centered subject-icon" data-subject="music"> |
|
|
<span class="icon is-large mb-3"> |
|
|
<i class="fas fa-music fa-3x"></i> |
|
|
</span> |
|
|
<h3 class="title is-5">التربية الموسيقية</h3> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="has-text-centered mt-6"> |
|
|
<button id="view-grades-btn" class="button is-primary is-medium"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-table"></i> |
|
|
</span> |
|
|
<span>عرض كافة الدرجات</span> |
|
|
</button> |
|
|
</div> |
|
|
</section> |
|
|
|
|
|
|
|
|
<section id="subject-pages" class="section is-hidden"> |
|
|
<div class="level"> |
|
|
<div class="level-left"> |
|
|
<button id="back-to-home" class="button is-light"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-arrow-right"></i> |
|
|
</span> |
|
|
<span>العودة إلى الرئيسية</span> |
|
|
</button> |
|
|
</div> |
|
|
<div class="level-right"> |
|
|
<h1 id="subject-title" class="title"></h1> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="tabs is-centered"> |
|
|
<ul> |
|
|
<li class="is-active" data-tab="grades"><a>الدرجات</a></li> |
|
|
<li data-tab="stats"><a>الإحصائيات</a></li> |
|
|
<li data-tab="activities"><a>الأنشطة</a></li> |
|
|
</ul> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="grades-tab" class="tab-content"> |
|
|
<div class="box"> |
|
|
<div class="table-container"> |
|
|
<table class="table is-fullwidth is-striped"> |
|
|
<thead> |
|
|
<tr> |
|
|
<th>الرقم</th> |
|
|
<th>اسم التلميذ</th> |
|
|
<th>الاختبار 1</th> |
|
|
<th>الاختبار 2</th> |
|
|
<th>الاختبار 3</th> |
|
|
<th>المعدل</th> |
|
|
<th>الملاحظات</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody id="subject-grades-table"> |
|
|
|
|
|
</tbody> |
|
|
</table> |
|
|
</div> |
|
|
|
|
|
<div class="buttons is-centered mt-5"> |
|
|
<button class="button is-primary save-grades"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-save"></i> |
|
|
</span> |
|
|
<span>حفظ التغييرات</span> |
|
|
</button> |
|
|
<button class="button is-info export-pdf"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-file-pdf"></i> |
|
|
</span> |
|
|
<span>تصدير PDF</span> |
|
|
</button> |
|
|
<button class="button is-success export-excel"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-file-excel"></i> |
|
|
</span> |
|
|
<span>تصدير Excel</span> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="stats-tab" class="tab-content is-hidden"> |
|
|
<div class="columns"> |
|
|
<div class="column"> |
|
|
<div class="box"> |
|
|
<h2 class="title is-5 has-text-centered">توزيع الدرجات</h2> |
|
|
<canvas id="grades-chart"></canvas> |
|
|
</div> |
|
|
</div> |
|
|
<div class="column"> |
|
|
<div class="box"> |
|
|
<h2 class="title is-5 has-text-centered">المعدلات حسب الجنس</h2> |
|
|
<canvas id="gender-chart"></canvas> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="box mt-4"> |
|
|
<h2 class="title is-5 has-text-centered">أعلى 5 وأقل 5 درجات</h2> |
|
|
<div class="columns"> |
|
|
<div class="column"> |
|
|
<div class="notification is-info"> |
|
|
<h3 class="title is-6">أعلى 5 درجات</h3> |
|
|
<ol id="top-students" class="ml-4"> |
|
|
|
|
|
</ol> |
|
|
</div> |
|
|
</div> |
|
|
<div class="column"> |
|
|
<div class="notification is-warning"> |
|
|
<h3 class="title is-6">أقل 5 درجات</h3> |
|
|
<ol id="bottom-students" class="ml-4"> |
|
|
|
|
|
</ol> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="activities-tab" class="tab-content is-hidden"> |
|
|
<div class="box"> |
|
|
<h2 class="title is-5 has-text-centered">أنشطة المادة</h2> |
|
|
<div class="columns is-multiline" id="subject-activities"> |
|
|
|
|
|
</div> |
|
|
|
|
|
<div class="field mt-5"> |
|
|
<label class="label">إضافة نشاط جديد</label> |
|
|
<div class="control"> |
|
|
<input id="new-activity-title" class="input" type="text" placeholder="عنوان النشاط"> |
|
|
</div> |
|
|
<div class="control mt-3"> |
|
|
<textarea id="new-activity-desc" class="textarea" placeholder="وصف النشاط"></textarea> |
|
|
</div> |
|
|
<div class="control mt-3"> |
|
|
<input id="new-activity-image" class="input" type="file" accept="image/*"> |
|
|
</div> |
|
|
<button id="add-activity-btn" class="button is-primary mt-3"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-plus"></i> |
|
|
</span> |
|
|
<span>إضافة النشاط</span> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</section> |
|
|
|
|
|
|
|
|
<section id="grades-page" class="section is-hidden"> |
|
|
<div class="level"> |
|
|
<div class="level-left"> |
|
|
<button id="back-to-home-from-grades" class="button is-light"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-arrow-right"></i> |
|
|
</span> |
|
|
<span>العودة إلى الرئيسية</span> |
|
|
</button> |
|
|
</div> |
|
|
<div class="level-right"> |
|
|
<h1 class="title">كافة الدرجات</h1> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="tabs is-centered"> |
|
|
<ul> |
|
|
<li class="is-active" data-tab="all-grades"><a>كافة الدرجات</a></li> |
|
|
<li data-tab="overall-stats"><a>إحصائيات عامة</a></li> |
|
|
<li data-tab="missing-grades"><a>الدرجات الناقصة</a></li> |
|
|
</ul> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="all-grades-tab" class="tab-content"> |
|
|
<div class="box"> |
|
|
<div class="table-container"> |
|
|
<table class="table is-fullwidth is-striped"> |
|
|
<thead> |
|
|
<tr> |
|
|
<th>الرقم</th> |
|
|
<th>اسم التلميذ</th> |
|
|
<th>العربية</th> |
|
|
<th>الرياضيات</th> |
|
|
<th>الإسلامية</th> |
|
|
<th>الفنية</th> |
|
|
<th>الموسيقية</th> |
|
|
<th>المعدل العام</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody id="all-grades-table"> |
|
|
|
|
|
</tbody> |
|
|
</table> |
|
|
</div> |
|
|
|
|
|
<div class="buttons is-centered mt-5"> |
|
|
<button class="button is-primary save-all-grades"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-save"></i> |
|
|
</span> |
|
|
<span>حفظ التغييرات</span> |
|
|
</button> |
|
|
<button class="button is-info export-all-pdf"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-file-pdf"></i> |
|
|
</span> |
|
|
<span>تصدير PDF</span> |
|
|
</button> |
|
|
<button class="button is-success export-all-excel"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-file-excel"></i> |
|
|
</span> |
|
|
<span>تصدير Excel</span> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="overall-stats-tab" class="tab-content is-hidden"> |
|
|
<div class="columns"> |
|
|
<div class="column"> |
|
|
<div class="box"> |
|
|
<h2 class="title is-5 has-text-centered">المعدلات حسب المواد</h2> |
|
|
<canvas id="subjects-chart"></canvas> |
|
|
</div> |
|
|
</div> |
|
|
<div class="column"> |
|
|
<div class="box"> |
|
|
<h2 class="title is-5 has-text-centered">أفضل 5 تلاميذ</h2> |
|
|
<canvas id="top-students-chart"></canvas> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="box mt-4"> |
|
|
<h2 class="title is-5 has-text-centered">التصنيف العام</h2> |
|
|
<div class="table-container"> |
|
|
<table class="table is-fullwidth is-striped"> |
|
|
<thead> |
|
|
<tr> |
|
|
<th>الترتيب</th> |
|
|
<th>اسم التلميذ</th> |
|
|
<th>المعدل العام</th> |
|
|
<th>التقدير</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody id="ranking-table"> |
|
|
|
|
|
</tbody> |
|
|
</table> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="missing-grades-tab" class="tab-content is-hidden"> |
|
|
<div class="box"> |
|
|
<h2 class="title is-5 has-text-centered">الدرجات الناقصة</h2> |
|
|
<div class="table-container"> |
|
|
<table class="table is-fullwidth is-striped"> |
|
|
<thead> |
|
|
<tr> |
|
|
<th>اسم التلميذ</th> |
|
|
<th>المادة</th> |
|
|
<th>الاختبار الناقص</th> |
|
|
<th>حالة التنبيه</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody id="missing-grades-table"> |
|
|
|
|
|
</tbody> |
|
|
</table> |
|
|
</div> |
|
|
|
|
|
<div class="buttons is-centered mt-5"> |
|
|
<button id="send-reminders" class="button is-warning"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-bell"></i> |
|
|
</span> |
|
|
<span>إرسال تنبيهات</span> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</section> |
|
|
|
|
|
|
|
|
<section id="gallery-page" class="section is-hidden"> |
|
|
<div class="level"> |
|
|
<div class="level-left"> |
|
|
<button id="back-to-home-from-gallery" class="button is-light"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-arrow-right"></i> |
|
|
</span> |
|
|
<span>العودة إلى الرئيسية</span> |
|
|
</button> |
|
|
</div> |
|
|
<div class="level-right"> |
|
|
<h1 class="title">معرض الأعمال الفنية</h1> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="tabs is-centered"> |
|
|
<ul> |
|
|
<li class="is-active" data-tab="all-activities"><a>جميع الأنشطة</a></li> |
|
|
<li data-tab="arabic-activities"><a>اللغة العربية</a></li> |
|
|
<li data-tab="math-activities"><a>الرياضيات</a></li> |
|
|
<li data-tab="islamic-activities"><a>الإسلامية</a></li> |
|
|
<li data-tab="art-activities"><a>الفنية</a></li> |
|
|
<li data-tab="music-activities"><a>الموسيقية</a></li> |
|
|
</ul> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="all-activities-tab" class="tab-content"> |
|
|
<div class="box"> |
|
|
<div class="columns is-multiline" id="all-activities-gallery"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="arabic-activities-tab" class="tab-content is-hidden"> |
|
|
<div class="box"> |
|
|
<div class="columns is-multiline" id="arabic-activities-gallery"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div id="math-activities-tab" class="tab-content is-hidden"> |
|
|
<div class="box"> |
|
|
<div class="columns is-multiline" id="math-activities-gallery"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div id="islamic-activities-tab" class="tab-content is-hidden"> |
|
|
<div class="box"> |
|
|
<div class="columns is-multiline" id="islamic-activities-gallery"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div id="art-activities-tab" class="tab-content is-hidden"> |
|
|
<div class="box"> |
|
|
<div class="columns is-multiline" id="art-activities-gallery"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div id="music-activities-tab" class="tab-content is-hidden"> |
|
|
<div class="box"> |
|
|
<div class="columns is-multiline" id="music-activities-gallery"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="box mt-4"> |
|
|
<h2 class="title is-5">إضافة عمل فني جديد</h2> |
|
|
<div class="field"> |
|
|
<label class="label">العنوان</label> |
|
|
<div class="control"> |
|
|
<input id="new-artwork-title" class="input" type="text" placeholder="عنوان العمل"> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="field"> |
|
|
<label class="label">المادة</label> |
|
|
<div class="control"> |
|
|
<div class="select"> |
|
|
<select id="new-artwork-subject"> |
|
|
<option value="arabic">اللغة العربية</option> |
|
|
<option value="math">الرياضيات</option> |
|
|
<option value="islamic">التربية الإسلامية</option> |
|
|
<option value="art">التربية الفنية</option> |
|
|
<option value="music">التربية الموسيقية</option> |
|
|
</select> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="field"> |
|
|
<label class="label">الصورة</label> |
|
|
<div class="control"> |
|
|
<input id="new-artwork-image" class="input" type="file" accept="image/*"> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="field"> |
|
|
<label class="label">الوصف</label> |
|
|
<div class="control"> |
|
|
<textarea id="new-artwork-desc" class="textarea" placeholder="وصف العمل"></textarea> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="field"> |
|
|
<div class="control"> |
|
|
<button id="add-artwork-btn" class="button is-primary"> |
|
|
<span class="icon"> |
|
|
<i class="fas fa-plus"></i> |
|
|
</span> |
|
|
<span>إضافة العمل</span> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</section> |
|
|
</div> |
|
|
|
|
|
|
|
|
<footer class="footer mt-6"> |
|
|
<div class="content has-text-centered"> |
|
|
<p> |
|
|
<strong>نظام إدارة الفصل الدراسي</strong> - مدرسة الشهيد حموم سعيد - بجاية |
|
|
</p> |
|
|
<p> |
|
|
السنة الدراسية 2023/2024 - الصف الأول ابتدائي |
|
|
</p> |
|
|
</div> |
|
|
</footer> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
|
|
|
if (localStorage.getItem('darkMode') === 'enabled') { |
|
|
document.body.classList.add('dark-mode'); |
|
|
document.getElementById('dark-mode-toggle').innerHTML = ` |
|
|
<span class="icon"> |
|
|
<i class="fas fa-sun"></i> |
|
|
</span> |
|
|
<span>الوضع النهاري</span> |
|
|
`; |
|
|
} |
|
|
|
|
|
|
|
|
if (!localStorage.getItem('classData')) { |
|
|
initializeSampleData(); |
|
|
} |
|
|
|
|
|
|
|
|
updateNotificationCount(); |
|
|
|
|
|
|
|
|
document.getElementById('login-btn').addEventListener('click', function() { |
|
|
const username = document.getElementById('username').value; |
|
|
const password = document.getElementById('password').value; |
|
|
|
|
|
if (username === "ma classe" && password === "1412") { |
|
|
document.getElementById('login-section').classList.add('is-hidden'); |
|
|
document.getElementById('app').classList.remove('is-hidden'); |
|
|
} else { |
|
|
document.getElementById('login-error').classList.remove('is-hidden'); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('dark-mode-toggle').addEventListener('click', function() { |
|
|
document.body.classList.toggle('dark-mode'); |
|
|
|
|
|
if (document.body.classList.contains('dark-mode')) { |
|
|
localStorage.setItem('darkMode', 'enabled'); |
|
|
this.innerHTML = ` |
|
|
<span class="icon"> |
|
|
<i class="fas fa-sun"></i> |
|
|
</span> |
|
|
<span>الوضع النهاري</span> |
|
|
`; |
|
|
} else { |
|
|
localStorage.setItem('darkMode', 'disabled'); |
|
|
this.innerHTML = ` |
|
|
<span class="icon"> |
|
|
<i class="fas fa-moon"></i> |
|
|
</span> |
|
|
<span>الوضع الليلي</span> |
|
|
`; |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('logout-btn').addEventListener('click', function() { |
|
|
document.getElementById('app').classList.add('is-hidden'); |
|
|
document.getElementById('login-section').classList.remove('is-hidden'); |
|
|
document.getElementById('username').value = ''; |
|
|
document.getElementById('password').value = ''; |
|
|
document.getElementById('login-error').classList.add('is-hidden'); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('home-link').addEventListener('click', function() { |
|
|
showPage('home-page'); |
|
|
}); |
|
|
|
|
|
document.getElementById('grades-link').addEventListener('click', function() { |
|
|
showPage('grades-page'); |
|
|
renderAllGradesTable(); |
|
|
renderOverallStats(); |
|
|
renderMissingGrades(); |
|
|
}); |
|
|
|
|
|
document.getElementById('gallery-link').addEventListener('click', function() { |
|
|
showPage('gallery-page'); |
|
|
renderGallery('all'); |
|
|
}); |
|
|
|
|
|
document.getElementById('view-grades-btn').addEventListener('click', function() { |
|
|
showPage('grades-page'); |
|
|
renderAllGradesTable(); |
|
|
renderOverallStats(); |
|
|
renderMissingGrades(); |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.subject-icon').forEach(icon => { |
|
|
icon.addEventListener('click', function() { |
|
|
const subject = this.getAttribute('data-subject'); |
|
|
showSubjectPage(subject); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('back-to-home').addEventListener('click', function() { |
|
|
showPage('home-page'); |
|
|
}); |
|
|
|
|
|
document.getElementById('back-to-home-from-grades').addEventListener('click', function() { |
|
|
showPage('home-page'); |
|
|
}); |
|
|
|
|
|
document.getElementById('back-to-home-from-gallery').addEventListener('click', function() { |
|
|
showPage('home-page'); |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('#subject-pages .tabs li').forEach(tab => { |
|
|
tab.addEventListener('click', function() { |
|
|
const tabName = this.getAttribute('data-tab'); |
|
|
switchSubjectTab(tabName); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('#grades-page .tabs li').forEach(tab => { |
|
|
tab.addEventListener('click', function() { |
|
|
const tabName = this.getAttribute('data-tab'); |
|
|
switchGradesTab(tabName); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('#gallery-page .tabs li').forEach(tab => { |
|
|
tab.addEventListener('click', function() { |
|
|
const tabName = this.getAttribute('data-tab').replace('-activities', ''); |
|
|
renderGallery(tabName); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.save-grades').forEach(btn => { |
|
|
btn.addEventListener('click', function() { |
|
|
saveSubjectGrades(); |
|
|
}); |
|
|
}); |
|
|
|
|
|
document.querySelector('.save-all-grades').addEventListener('click', function() { |
|
|
saveAllGrades(); |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.export-pdf').forEach(btn => { |
|
|
btn.addEventListener('click', function() { |
|
|
exportToPDF('subject'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
document.querySelector('.export-all-pdf').addEventListener('click', function() { |
|
|
exportToPDF('all'); |
|
|
}); |
|
|
|
|
|
document.querySelectorAll('.export-excel').forEach(btn => { |
|
|
btn.addEventListener('click', function() { |
|
|
exportToExcel('subject'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
document.querySelector('.export-all-excel').addEventListener('click', function() { |
|
|
exportToExcel('all'); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('add-activity-btn').addEventListener('click', function() { |
|
|
addActivity(); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('add-artwork-btn').addEventListener('click', function() { |
|
|
addArtwork(); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('send-reminders').addEventListener('click', function() { |
|
|
sendReminders(); |
|
|
}); |
|
|
|
|
|
|
|
|
const navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0); |
|
|
if (navbarBurgers.length > 0) { |
|
|
navbarBurgers.forEach(el => { |
|
|
el.addEventListener('click', () => { |
|
|
const target = el.dataset.target; |
|
|
const $target = document.getElementById(target); |
|
|
el.classList.toggle('is-active'); |
|
|
$target.classList.toggle('is-active'); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
function showPage(pageId) { |
|
|
|
|
|
document.getElementById('home-page').classList.add('is-hidden'); |
|
|
document.getElementById('subject-pages').classList.add('is-hidden'); |
|
|
document.getElementById('grades-page').classList.add('is-hidden'); |
|
|
document.getElementById('gallery-page').classList.add('is-hidden'); |
|
|
|
|
|
|
|
|
document.getElementById(pageId).classList.remove('is-hidden'); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.navbar-item').forEach(item => { |
|
|
item.classList.remove('is-active'); |
|
|
}); |
|
|
|
|
|
if (pageId === 'home-page') { |
|
|
document.getElementById('home-link').classList.add('is-active'); |
|
|
} else if (pageId === 'grades-page') { |
|
|
document.getElementById('grades-link').classList.add('is-active'); |
|
|
} else if (pageId === 'gallery-page') { |
|
|
document.getElementById('gallery-link').classList.add('is-active'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function showSubjectPage(subject) { |
|
|
showPage('subject-pages'); |
|
|
|
|
|
|
|
|
const subjectTitles = { |
|
|
'arabic': 'اللغة العربية', |
|
|
'math': 'الرياضيات', |
|
|
'islamic': 'التربية الإسلامية', |
|
|
'art': 'التربية الفنية', |
|
|
'music': 'التربية الموسيقية' |
|
|
}; |
|
|
|
|
|
document.getElementById('subject-title').textContent = subjectTitles[subject]; |
|
|
|
|
|
|
|
|
document.getElementById('subject-pages').setAttribute('data-current-subject', subject); |
|
|
|
|
|
|
|
|
renderSubjectGradesTable(subject); |
|
|
renderSubjectStats(subject); |
|
|
renderSubjectActivities(subject); |
|
|
|
|
|
|
|
|
switchSubjectTab('grades'); |
|
|
} |
|
|
|
|
|
|
|
|
function switchSubjectTab(tabName) { |
|
|
|
|
|
document.querySelectorAll('#subject-pages .tabs li').forEach(tab => { |
|
|
tab.classList.remove('is-active'); |
|
|
if (tab.getAttribute('data-tab') === tabName) { |
|
|
tab.classList.add('is-active'); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('grades-tab').classList.add('is-hidden'); |
|
|
document.getElementById('stats-tab').classList.add('is-hidden'); |
|
|
document.getElementById('activities-tab').classList.add('is-hidden'); |
|
|
|
|
|
document.getElementById(`${tabName}-tab`).classList.remove('is-hidden'); |
|
|
} |
|
|
|
|
|
|
|
|
function switchGradesTab(tabName) { |
|
|
|
|
|
document.querySelectorAll('#grades-page .tabs li').forEach(tab => { |
|
|
tab.classList.remove('is-active'); |
|
|
if (tab.getAttribute('data-tab') === tabName) { |
|
|
tab.classList.add('is-active'); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('all-grades-tab').classList.add('is-hidden'); |
|
|
document.getElementById('overall-stats-tab').classList.add('is-hidden'); |
|
|
document.getElementById('missing-grades-tab').classList.add('is-hidden'); |
|
|
|
|
|
document.getElementById(`${tabName}-tab`).classList.remove('is-hidden'); |
|
|
} |
|
|
|
|
|
|
|
|
function initializeSampleData() { |
|
|
const classData = { |
|
|
students: [], |
|
|
grades: { |
|
|
arabic: [], |
|
|
math: [], |
|
|
islamic: [], |
|
|
art: [], |
|
|
music: [] |
|
|
}, |
|
|
activities: { |
|
|
arabic: [], |
|
|
math: [], |
|
|
islamic: [], |
|
|
art: [], |
|
|
music: [] |
|
|
}, |
|
|
artworks: [] |
|
|
}; |
|
|
|
|
|
|
|
|
const firstNames = ['محمد', 'أحمد', 'علي', 'يوسف', 'إبراهيم', 'مريم', 'فاطمة', 'زينب', 'آمنة', 'خديجة']; |
|
|
const lastNames = ['بن علي', 'بن عمر', 'بن أحمد', 'بن إبراهيم', 'بنت محمد', 'بنت علي', 'بنت أحمد', 'بنت يوسف']; |
|
|
|
|
|
for (let i = 1; i <= 36; i++) { |
|
|
const firstName = firstNames[Math.floor(Math.random() * firstNames.length)]; |
|
|
const lastName = lastNames[Math.floor(Math.random() * lastNames.length)]; |
|
|
const gender = Math.random() > 0.5 ? 'male' : 'female'; |
|
|
|
|
|
classData.students.push({ |
|
|
id: i, |
|
|
name: `${firstName} ${lastName}`, |
|
|
gender: gender |
|
|
}); |
|
|
|
|
|
|
|
|
classData.grades.arabic.push({ |
|
|
studentId: i, |
|
|
test1: Math.floor(Math.random() * 16) + 5, |
|
|
test2: Math.floor(Math.random() * 16) + 5, |
|
|
test3: null, |
|
|
notes: '' |
|
|
}); |
|
|
|
|
|
classData.grades.math.push({ |
|
|
studentId: i, |
|
|
test1: Math.floor(Math.random() * 16) + 5, |
|
|
test2: Math.floor(Math.random() * 16) + 5, |
|
|
test3: Math.floor(Math.random() * 16) + 5, |
|
|
notes: '' |
|
|
}); |
|
|
|
|
|
classData.grades.islamic.push({ |
|
|
studentId: i, |
|
|
test1: Math.floor(Math.random() * 16) + 5, |
|
|
test2: null, |
|
|
test3: Math.floor(Math.random() * 16) + 5, |
|
|
notes: '' |
|
|
}); |
|
|
|
|
|
classData.grades.art.push({ |
|
|
studentId: i, |
|
|
test1: Math.floor(Math.random() * 16) + 5, |
|
|
test2: Math.floor(Math.random() * 16) + 5, |
|
|
test3: Math.floor(Math.random() * 16) + 5, |
|
|
notes: '' |
|
|
}); |
|
|
|
|
|
classData.grades.music.push({ |
|
|
studentId: i, |
|
|
test1: null, |
|
|
test2: Math.floor(Math.random() * 16) + 5, |
|
|
test3: null, |
|
|
notes: '' |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
classData.activities.arabic.push({ |
|
|
id: 1, |
|
|
title: 'قراءة قصة', |
|
|
description: 'قراءة قصة قصيرة ومناقشتها في الصف', |
|
|
date: '2023-10-15' |
|
|
}); |
|
|
|
|
|
classData.activities.math.push({ |
|
|
id: 1, |
|
|
title: 'تمارين الجمع', |
|
|
description: 'حل تمارين الجمع باستخدام العدادات', |
|
|
date: '2023-10-20' |
|
|
}); |
|
|
|
|
|
classData.activities.islamic.push({ |
|
|
id: 1, |
|
|
title: 'حفظ سورة الفاتحة', |
|
|
description: 'حفظ سورة الفاتحة وتلاوتها', |
|
|
date: '2023-10-10' |
|
|
}); |
|
|
|
|
|
classData.activities.art.push({ |
|
|
id: 1, |
|
|
title: 'رسم لوحة الخريف', |
|
|
description: 'رسم لوحة تعبر عن فصل الخريف', |
|
|
date: '2023-11-05' |
|
|
}); |
|
|
|
|
|
classData.activities.music.push({ |
|
|
id: 1, |
|
|
title: 'أنشودة الألوان', |
|
|
description: 'تعلم أنشودة الألوان مع الحركات', |
|
|
date: '2023-11-10' |
|
|
}); |
|
|
|
|
|
|
|
|
classData.artworks.push({ |
|
|
id: 1, |
|
|
title: 'لوحة الخريف', |
|
|
subject: 'art', |
|
|
description: 'رسمها التلميذ محمد بن علي', |
|
|
image: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDAgMjAwIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgZmlsbD0iI2ZmYzI3MyIvPjxjaXJjbGUgY3g9IjUwIiBjeT0iNTAiIHI9IjMwIiBmaWxsPSIjZmY5ZTBlIi8+PGNpcmNsZSBjeD0iMTUwIiBjeT0iNTAiIHI9IjMwIiBmaWxsPSIjZmY3ZjBlIi8+PHBhdGggZD0iTTEwMCAxNTAgTDE1MCAxMDAgTDIwMCAxNTAiIHN0cm9rZT0iIzc5NTU0OCIgc3Ryb2tlLXdpZHRoPSIyIiBmaWxsPSJub25lIi8+PC9zdmc+', |
|
|
date: '2023-11-05' |
|
|
}); |
|
|
|
|
|
classData.artworks.push({ |
|
|
id: 2, |
|
|
title: 'قصيدة الحروف', |
|
|
subject: 'arabic', |
|
|
description: 'كتابة قصيدة الحروف بخط جميل', |
|
|
image: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMDAgMjAwIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgZmlsbD0iI2ZmZTBiYiIvPjx0ZXh0IHg9IjEwMCIgeT0iMTAwIiBmb250LWZhbWlseT0iQXJpYWwiIGZvbnQtc2l6ZT0iMjQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiM3OTU1NDgiPuS4reS7luS4i+S5jOS4reS7luS4i+S5jDwvdGV4dD48L3N2Zz4=', |
|
|
date: '2023-10-15' |
|
|
}); |
|
|
|
|
|
|
|
|
localStorage.setItem('classData', JSON.stringify(classData)); |
|
|
} |
|
|
|
|
|
|
|
|
function getClassData() { |
|
|
return JSON.parse(localStorage.getItem('classData')); |
|
|
} |
|
|
|
|
|
|
|
|
function saveClassData(classData) { |
|
|
localStorage.setItem('classData', JSON.stringify(classData)); |
|
|
} |
|
|
|
|
|
|
|
|
function renderSubjectGradesTable(subject) { |
|
|
const classData = getClassData(); |
|
|
const tableBody = document.getElementById('subject-grades-table'); |
|
|
tableBody.innerHTML = ''; |
|
|
|
|
|
classData.students.forEach(student => { |
|
|
const studentGrades = classData.grades[subject].find(g => g.studentId === student.id); |
|
|
|
|
|
const row = document.createElement('tr'); |
|
|
row.innerHTML = ` |
|
|
<td>${student.id}</td> |
|
|
<td>${student.name}</td> |
|
|
<td><input class="input grade-input" type="number" min="0" max="20" step="0.5" value="${studentGrades.test1 !== null ? studentGrades.test1 : ''}" data-test="test1" data-student-id="${student.id}"></td> |
|
|
<td><input class="input grade-input" type="number" min="0" max="20" step="0.5" value="${studentGrades.test2 !== null ? studentGrades.test2 : ''}" data-test="test2" data-student-id="${student.id}"></td> |
|
|
<td><input class="input grade-input" type="number" min="0" max="20" step="0.5" value="${studentGrades.test3 !== null ? studentGrades.test3 : ''}" data-test="test3" data-student-id="${student.id}"></td> |
|
|
<td>${calculateAverage(studentGrades)}</td> |
|
|
<td><input class="input" type="text" value="${studentGrades.notes}" data-test="notes" data-student-id="${student.id}"></td> |
|
|
`; |
|
|
|
|
|
tableBody.appendChild(row); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function calculateAverage(grades) { |
|
|
const validGrades = [grades.test1, grades.test2, grades.test3].filter(g => g !== null); |
|
|
if (validGrades.length === 0) return 'N/A'; |
|
|
|
|
|
const sum = validGrades.reduce((a, b) => a + b, 0); |
|
|
const avg = sum / validGrades.length; |
|
|
return avg.toFixed(2); |
|
|
} |
|
|
|
|
|
|
|
|
function saveSubjectGrades() { |
|
|
const classData = getClassData(); |
|
|
const subject = document.getElementById('subject-pages').getAttribute('data-current-subject'); |
|
|
|
|
|
document.querySelectorAll(`#subject-grades-table .grade-input`).forEach(input => { |
|
|
const studentId = parseInt(input.getAttribute('data-student-id')); |
|
|
const test = input.getAttribute('data-test'); |
|
|
const value = input.value === '' ? null : parseFloat(input.value); |
|
|
|
|
|
const studentGrades = classData.grades[subject].find(g => g.studentId === studentId); |
|
|
studentGrades[test] = value; |
|
|
}); |
|
|
|
|
|
|
|
|
document.querySelectorAll(`#subject-grades-table input[data-test="notes"]`).forEach(input => { |
|
|
const studentId = parseInt(input.getAttribute('data-student-id')); |
|
|
const value = input.value; |
|
|
|
|
|
const studentGrades = classData.grades[subject].find(g => g.studentId === studentId); |
|
|
studentGrades.notes = value; |
|
|
}); |
|
|
|
|
|
saveClassData(classData); |
|
|
|
|
|
|
|
|
showNotification('تم حفظ الدرجات بنجاح', 'success'); |
|
|
|
|
|
|
|
|
renderSubjectGradesTable(subject); |
|
|
} |
|
|
|
|
|
|
|
|
function renderAllGradesTable() { |
|
|
const classData = getClassData(); |
|
|
const tableBody = document.getElementById('all-grades-table'); |
|
|
tableBody.innerHTML = ''; |
|
|
|
|
|
classData.students.forEach(student => { |
|
|
const arabicGrades = classData.grades.arabic.find(g => g.studentId === student.id); |
|
|
const mathGrades = classData.grades.math.find(g => g.studentId === student.id); |
|
|
const islamicGrades = classData.grades.islamic.find(g => g.studentId === student.id); |
|
|
const artGrades = classData.grades.art.find(g => g.studentId === student.id); |
|
|
const musicGrades = classData.grades.music.find(g => g.studentId === student.id); |
|
|
|
|
|
const arabicAvg = calculateAverage(arabicGrades); |
|
|
const mathAvg = calculateAverage(mathGrades); |
|
|
const islamicAvg = calculateAverage(islamicGrades); |
|
|
const artAvg = calculateAverage(artGrades); |
|
|
const musicAvg = calculateAverage(musicGrades); |
|
|
|
|
|
|
|
|
const validAverages = [arabicAvg, mathAvg, islamicAvg, artAvg, musicAvg] |
|
|
.filter(avg => avg !== 'N/A') |
|
|
.map(avg => parseFloat(avg)); |
|
|
|
|
|
const overallAvg = validAverages.length > 0 |
|
|
? (validAverages.reduce((a, b) => a + b, 0) / validAverages.length).toFixed(2) |
|
|
: 'N/A'; |
|
|
|
|
|
const row = document.createElement('tr'); |
|
|
row.innerHTML = ` |
|
|
<td>${student.id}</td> |
|
|
<td>${student.name}</td> |
|
|
<td>${arabicAvg}</td> |
|
|
<td>${mathAvg}</td> |
|
|
<td>${islamicAvg}</td> |
|
|
<td>${artAvg}</td> |
|
|
<td>${musicAvg}</td> |
|
|
<td>${overallAvg}</td> |
|
|
`; |
|
|
|
|
|
tableBody.appendChild(row); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function saveAllGrades() { |
|
|
|
|
|
|
|
|
showNotification('يجب تعديل الدرجات من صفحة كل مادة على حدة', 'info'); |
|
|
} |
|
|
|
|
|
|
|
|
function renderSubjectStats(subject) { |
|
|
const classData = getClassData(); |
|
|
const grades = classData.grades[subject]; |
|
|
|
|
|
|
|
|
const averages = grades.map(g => { |
|
|
const validGrades = [g.test1, g.test2, g.test3].filter(grade => grade !== null); |
|
|
if (validGrades.length === 0) return null; |
|
|
return validGrades.reduce((a, b) => a + b, 0) / validGrades.length; |
|
|
}).filter(avg => avg !== null); |
|
|
|
|
|
|
|
|
const histogramData = { |
|
|
'0-5': 0, |
|
|
'5-10': 0, |
|
|
'10-15': 0, |
|
|
'15-20': 0 |
|
|
}; |
|
|
|
|
|
averages.forEach(avg => { |
|
|
if (avg < 5) histogramData['0-5']++; |
|
|
else if (avg < 10) histogramData['5-10']++; |
|
|
else if (avg < 15) histogramData['10-15']++; |
|
|
else histogramData['15-20']++; |
|
|
}); |
|
|
|
|
|
|
|
|
const gradesCtx = document.getElementById('grades-chart').getContext('2d'); |
|
|
if (window.gradesChart) window.gradesChart.destroy(); |
|
|
|
|
|
window.gradesChart = new Chart(gradesCtx, { |
|
|
type: 'bar', |
|
|
data: { |
|
|
labels: ['0-5', '5-10', '10-15', '15-20'], |
|
|
datasets: [{ |
|
|
label: 'عدد التلاميذ', |
|
|
data: Object.values(histogramData), |
|
|
backgroundColor: 'rgba(54, 162, 235, 0.7)', |
|
|
borderColor: 'rgba(54, 162, 235, 1)', |
|
|
borderWidth: 1 |
|
|
}] |
|
|
}, |
|
|
options: { |
|
|
scales: { |
|
|
y: { |
|
|
beginAtZero: true, |
|
|
ticks: { |
|
|
stepSize: 1 |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
const maleAverages = []; |
|
|
const femaleAverages = []; |
|
|
|
|
|
grades.forEach(g => { |
|
|
const student = classData.students.find(s => s.id === g.studentId); |
|
|
const validGrades = [g.test1, g.test2, g.test3].filter(grade => grade !== null); |
|
|
if (validGrades.length === 0) return; |
|
|
|
|
|
const avg = validGrades.reduce((a, b) => a + b, 0) / validGrades.length; |
|
|
|
|
|
if (student.gender === 'male') maleAverages.push(avg); |
|
|
else femaleAverages.push(avg); |
|
|
}); |
|
|
|
|
|
const maleAvg = maleAverages.length > 0 ? maleAverages.reduce((a, b) => a + b, 0) / maleAverages.length : 0; |
|
|
const femaleAvg = femaleAverages.length > 0 ? femaleAverages.reduce((a, b) => a + b, 0) / femaleAverages.length : 0; |
|
|
|
|
|
|
|
|
const genderCtx = document.getElementById('gender-chart').getContext('2d'); |
|
|
if (window.genderChart) window.genderChart.destroy(); |
|
|
|
|
|
window.genderChart = new Chart(genderCtx, { |
|
|
type: 'pie', |
|
|
data: { |
|
|
labels: ['ذكور', 'إناث'], |
|
|
datasets: [{ |
|
|
data: [maleAvg, femaleAvg], |
|
|
backgroundColor: ['rgba(54, 162, 235, 0.7)', 'rgba(255, 99, 132, 0.7)'], |
|
|
borderColor: ['rgba(54, 162, 235, 1)', 'rgba(255, 99, 132, 1)'], |
|
|
borderWidth: 1 |
|
|
}] |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
const studentsWithAverages = classData.students.map(student => { |
|
|
const grade = grades.find(g => g.studentId === student.id); |
|
|
const validGrades = [grade.test1, grade.test2, grade.test3].filter(g => g !== null); |
|
|
const avg = validGrades.length > 0 ? validGrades.reduce((a, b) => a + b, 0) / validGrades.length : null; |
|
|
|
|
|
return { |
|
|
id: student.id, |
|
|
name: student.name, |
|
|
average: avg |
|
|
}; |
|
|
}).filter(s => s.average !== null); |
|
|
|
|
|
|
|
|
studentsWithAverages.sort((a, b) => b.average - a.average); |
|
|
|
|
|
|
|
|
const topStudentsList = document.getElementById('top-students'); |
|
|
topStudentsList.innerHTML = ''; |
|
|
|
|
|
studentsWithAverages.slice(0, 5).forEach((student, index) => { |
|
|
const li = document.createElement('li'); |
|
|
li.textContent = `${index + 1}. ${student.name} - ${student.average.toFixed(2)}`; |
|
|
topStudentsList.appendChild(li); |
|
|
}); |
|
|
|
|
|
|
|
|
const bottomStudentsList = document.getElementById('bottom-students'); |
|
|
bottomStudentsList.innerHTML = ''; |
|
|
|
|
|
studentsWithAverages.slice(-5).reverse().forEach((student, index) => { |
|
|
const li = document.createElement('li'); |
|
|
li.textContent = `${studentsWithAverages.length - index}. ${student.name} - ${student.average.toFixed(2)}`; |
|
|
bottomStudentsList.appendChild(li); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function renderOverallStats() { |
|
|
const classData = getClassData(); |
|
|
|
|
|
|
|
|
const subjectAverages = {}; |
|
|
const subjects = ['arabic', 'math', 'islamic', 'art', 'music']; |
|
|
const subjectNames = { |
|
|
'arabic': 'العربية', |
|
|
'math': 'الرياضيات', |
|
|
'islamic': 'الإسلامية', |
|
|
'art': 'الفنية', |
|
|
'music': 'الموسيقية' |
|
|
}; |
|
|
|
|
|
subjects.forEach(subject => { |
|
|
const grades = classData.grades[subject]; |
|
|
const averages = grades.map(g => { |
|
|
const validGrades = [g.test1, g.test2, g.test3].filter(grade => grade !== null); |
|
|
if (validGrades.length === 0) return null; |
|
|
return validGrades.reduce((a, b) => a + b, 0) / validGrades.length; |
|
|
}).filter(avg => avg !== null); |
|
|
|
|
|
subjectAverages[subject] = averages.length > 0 |
|
|
? averages.reduce((a, b) => a + b, 0) / averages.length |
|
|
: 0; |
|
|
}); |
|
|
|
|
|
|
|
|
const subjectsCtx = document.getElementById('subjects-chart').getContext('2d'); |
|
|
if (window.subjectsChart) window.subjectsChart.destroy(); |
|
|
|
|
|
window.subjectsChart = new Chart(subjectsCtx, { |
|
|
type: 'bar', |
|
|
data: { |
|
|
labels: Object.values(subjectNames), |
|
|
datasets: [{ |
|
|
label: 'المعدل', |
|
|
data: Object.values(subjectAverages), |
|
|
backgroundColor: [ |
|
|
'rgba(255, 99, 132, 0.7)', |
|
|
'rgba(54, 162, 235, 0.7)', |
|
|
'rgba(255, 206, 86, 0.7)', |
|
|
'rgba(75, 192, 192, 0.7)', |
|
|
'rgba(153, 102, 255, 0.7)' |
|
|
], |
|
|
borderColor: [ |
|
|
'rgba(255, 99, 132, 1)', |
|
|
'rgba(54, 162, 235, 1)', |
|
|
'rgba(255, 206, 86, 1)', |
|
|
'rgba(75, 192, 192, 1)', |
|
|
'rgba(153, 102, 255, 1)' |
|
|
], |
|
|
borderWidth: 1 |
|
|
}] |
|
|
}, |
|
|
options: { |
|
|
scales: { |
|
|
y: { |
|
|
beginAtZero: true, |
|
|
max: 20 |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
const studentsWithAverages = classData.students.map(student => { |
|
|
let sum = 0; |
|
|
let count = 0; |
|
|
|
|
|
subjects.forEach(subject => { |
|
|
const grade = classData.grades[subject].find(g => g.studentId === student.id); |
|
|
const validGrades = [grade.test1, grade.test2, grade.test3].filter(g => g !== null); |
|
|
|
|
|
if (validGrades.length > 0) { |
|
|
sum += validGrades.reduce((a, b) => a + b, 0) / validGrades.length; |
|
|
count++; |
|
|
} |
|
|
}); |
|
|
|
|
|
return { |
|
|
id: student.id, |
|
|
name: student.name, |
|
|
average: count > 0 ? sum / count : null |
|
|
}; |
|
|
}).filter(s => s.average !== null); |
|
|
|
|
|
|
|
|
studentsWithAverages.sort((a, b) => b.average - a.average); |
|
|
|
|
|
|
|
|
const topStudentsCtx = document.getElementById('top-students-chart').getContext('2d'); |
|
|
if (window.topStudentsChart) window.topStudentsChart.destroy(); |
|
|
|
|
|
window.topStudentsChart = new Chart(topStudentsCtx, { |
|
|
type: 'bar', |
|
|
data: { |
|
|
labels: studentsWithAverages.slice(0, 5).map(s => s.name), |
|
|
datasets: [{ |
|
|
label: 'المعدل العام', |
|
|
data: studentsWithAverages.slice(0, 5).map(s => s.average), |
|
|
backgroundColor: 'rgba(75, 192, 192, 0.7)', |
|
|
borderColor: 'rgba(75, 192, 192, 1)', |
|
|
borderWidth: 1 |
|
|
}] |
|
|
}, |
|
|
options: { |
|
|
scales: { |
|
|
y: { |
|
|
beginAtZero: true, |
|
|
max: 20 |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
const rankingTable = document.getElementById('ranking-table'); |
|
|
rankingTable.innerHTML = ''; |
|
|
|
|
|
studentsWithAverages.forEach((student, index) => { |
|
|
const row = document.createElement('tr'); |
|
|
|
|
|
|
|
|
let grade; |
|
|
if (student.average >= 16) grade = 'ممتاز'; |
|
|
else if (student.average >= 14) grade = 'جيد جداً'; |
|
|
else if (student.average >= 12) grade = 'جيد'; |
|
|
else if (student.average >= 10) grade = 'مقبول'; |
|
|
else grade = 'ضعيف'; |
|
|
|
|
|
row.innerHTML = ` |
|
|
<td>${index + 1}</td> |
|
|
<td>${student.name}</td> |
|
|
<td>${student.average.toFixed(2)}</td> |
|
|
<td>${grade}</td> |
|
|
`; |
|
|
|
|
|
rankingTable.appendChild(row); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function renderMissingGrades() { |
|
|
const classData = getClassData(); |
|
|
const tableBody = document.getElementById('missing-grades-table'); |
|
|
tableBody.innerHTML = ''; |
|
|
|
|
|
const subjectNames = { |
|
|
'arabic': 'العربية', |
|
|
'math': 'الرياضيات', |
|
|
'islamic': 'الإسلامية', |
|
|
'art': 'الفنية', |
|
|
'music': 'الموسيقية' |
|
|
}; |
|
|
|
|
|
const testNames = { |
|
|
'test1': 'الاختبار 1', |
|
|
'test2': 'الاختبار 2', |
|
|
'test3': 'الاختبار 3' |
|
|
}; |
|
|
|
|
|
let missingCount = 0; |
|
|
|
|
|
classData.students.forEach(student => { |
|
|
['arabic', 'math', 'islamic', 'art', 'music'].forEach(subject => { |
|
|
const grade = classData.grades[subject].find(g => g.studentId === student.id); |
|
|
|
|
|
['test1', 'test2', 'test3'].forEach(test => { |
|
|
if (grade[test] === null) { |
|
|
missingCount++; |
|
|
const row = document.createElement('tr'); |
|
|
row.innerHTML = ` |
|
|
<td>${student.name}</td> |
|
|
<td>${subjectNames[subject]}</td> |
|
|
<td>${testNames[test]}</td> |
|
|
<td><span class="tag is-warning">غير مدخل</span></td> |
|
|
`; |
|
|
tableBody.appendChild(row); |
|
|
} |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
updateNotificationCount(); |
|
|
|
|
|
if (missingCount === 0) { |
|
|
const row = document.createElement('tr'); |
|
|
row.innerHTML = ` |
|
|
<td colspan="4" class="has-text-centered">لا توجد درجات ناقصة</td> |
|
|
`; |
|
|
tableBody.appendChild(row); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function updateNotificationCount() { |
|
|
const classData = getClassData(); |
|
|
let missingCount = 0; |
|
|
|
|
|
classData.students.forEach(student => { |
|
|
['arabic', 'math', 'islamic', 'art', 'music'].forEach(subject => { |
|
|
const grade = classData.grades[subject].find(g => g.studentId === student.id); |
|
|
|
|
|
['test1', 'test2', 'test3'].forEach(test => { |
|
|
if (grade[test] === null) { |
|
|
missingCount++; |
|
|
} |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
const notificationCount = document.getElementById('notification-count'); |
|
|
notificationCount.textContent = missingCount; |
|
|
|
|
|
if (missingCount > 0) { |
|
|
notificationCount.classList.remove('is-hidden'); |
|
|
|
|
|
|
|
|
const notificationItems = document.getElementById('notification-items'); |
|
|
notificationItems.innerHTML = ''; |
|
|
|
|
|
const notification = document.createElement('div'); |
|
|
notification.className = 'notification is-warning is-light'; |
|
|
notification.innerHTML = ` |
|
|
<p>يوجد ${missingCount} درجة ناقصة تحتاج إلى إدخال</p> |
|
|
<button class="button is-small mt-2 view-missing-grades">عرض الدرجات الناقصة</button> |
|
|
`; |
|
|
|
|
|
notification.querySelector('.view-missing-grades').addEventListener('click', function() { |
|
|
showPage('grades-page'); |
|
|
switchGradesTab('missing-grades'); |
|
|
renderMissingGrades(); |
|
|
}); |
|
|
|
|
|
notificationItems.appendChild(notification); |
|
|
} else { |
|
|
notificationCount.classList.add('is-hidden'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function sendReminders() { |
|
|
showNotification('تم إرسال التنبيهات بنجاح', 'success'); |
|
|
} |
|
|
|
|
|
|
|
|
function renderSubjectActivities(subject) { |
|
|
const classData = getClassData(); |
|
|
const activitiesContainer = document.getElementById('subject-activities'); |
|
|
activitiesContainer.innerHTML = ''; |
|
|
|
|
|
classData.activities[subject].forEach(activity => { |
|
|
const col = document.createElement('div'); |
|
|
col.className = 'column is-4'; |
|
|
|
|
|
col.innerHTML = ` |
|
|
<div class="card"> |
|
|
<div class="card-content"> |
|
|
<p class="title is-5">${activity.title}</p> |
|
|
<p class="subtitle is-6">${activity.description}</p> |
|
|
<p class="has-text-grey">${activity.date}</p> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
activitiesContainer.appendChild(col); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function addActivity() { |
|
|
const subject = document.getElementById('subject-pages').getAttribute('data-current-subject'); |
|
|
const title = document.getElementById('new-activity-title').value; |
|
|
const description = document.getElementById('new-activity-desc').value; |
|
|
const imageInput = document.getElementById('new-activity-image'); |
|
|
|
|
|
if (!title || !description) { |
|
|
showNotification('الرجاء إدخال عنوان ووصف للنشاط', 'danger'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const classData = getClassData(); |
|
|
const newId = classData.activities[subject].length > 0 |
|
|
? Math.max(...classData.activities[subject].map(a => a.id)) + 1 |
|
|
: 1; |
|
|
|
|
|
const newActivity = { |
|
|
id: newId, |
|
|
title: title, |
|
|
description: description, |
|
|
date: new Date().toISOString().split('T')[0] |
|
|
}; |
|
|
|
|
|
classData.activities[subject].push(newActivity); |
|
|
saveClassData(classData); |
|
|
|
|
|
|
|
|
document.getElementById('new-activity-title').value = ''; |
|
|
document.getElementById('new-activity-desc').value = ''; |
|
|
imageInput.value = ''; |
|
|
|
|
|
|
|
|
showNotification('تم إضافة النشاط بنجاح', 'success'); |
|
|
|
|
|
|
|
|
renderSubjectActivities(subject); |
|
|
} |
|
|
|
|
|
|
|
|
function renderGallery(subject) { |
|
|
const classData = getClassData(); |
|
|
let galleryContainer; |
|
|
|
|
|
if (subject === 'all') { |
|
|
galleryContainer = document.getElementById('all-activities-gallery'); |
|
|
} else { |
|
|
galleryContainer = document.getElementById(`${subject}-activities-gallery`); |
|
|
} |
|
|
|
|
|
galleryContainer.innerHTML = ''; |
|
|
|
|
|
let artworksToShow = []; |
|
|
|
|
|
if (subject === 'all') { |
|
|
artworksToShow = classData.artworks; |
|
|
} else { |
|
|
artworksToShow = classData.artworks.filter(art => art.subject === subject); |
|
|
} |
|
|
|
|
|
if (artworksToShow.length === 0) { |
|
|
const col = document.createElement('div'); |
|
|
col.className = 'column is-12'; |
|
|
col.innerHTML = ` |
|
|
<div class="notification is-light has-text-centered"> |
|
|
لا توجد أعمال فنية متاحة |
|
|
</div> |
|
|
`; |
|
|
galleryContainer.appendChild(col); |
|
|
return; |
|
|
} |
|
|
|
|
|
artworksToShow.forEach(artwork => { |
|
|
const col = document.createElement('div'); |
|
|
col.className = 'column is-4'; |
|
|
|
|
|
col.innerHTML = ` |
|
|
<div class="card gallery-item"> |
|
|
<div class="card-image"> |
|
|
<figure class="image is-4by3"> |
|
|
<img src="${artwork.image}" alt="${artwork.title}"> |
|
|
</figure> |
|
|
</div> |
|
|
<div class="card-content"> |
|
|
<p class="title is-5">${artwork.title}</p> |
|
|
<p class="subtitle is-6">${artwork.description}</p> |
|
|
<p class="has-text-grey">${artwork.date}</p> |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
galleryContainer.appendChild(col); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function addArtwork() { |
|
|
const title = document.getElementById('new-artwork-title').value; |
|
|
const subject = document.getElementById('new-artwork-subject').value; |
|
|
const description = document.getElementById('new-artwork-desc').value; |
|
|
const imageInput = document.getElementById('new-artwork-image'); |
|
|
|
|
|
if (!title || !description || !imageInput.files[0]) { |
|
|
showNotification('الرجاء إدخال جميع البيانات المطلوبة', 'danger'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const file = imageInput.files[0]; |
|
|
const reader = new FileReader(); |
|
|
|
|
|
reader.onload = function(e) { |
|
|
const classData = getClassData(); |
|
|
const newId = classData.artworks.length > 0 |
|
|
? Math.max(...classData.artworks.map(a => a.id)) + 1 |
|
|
: 1; |
|
|
|
|
|
const newArtwork = { |
|
|
id: newId, |
|
|
title: title, |
|
|
subject: subject, |
|
|
description: description, |
|
|
image: e.target.result, |
|
|
date: new Date().toISOString().split('T')[0] |
|
|
}; |
|
|
|
|
|
classData.artworks.push(newArtwork); |
|
|
saveClassData(classData); |
|
|
|
|
|
|
|
|
document.getElementById('new-artwork-title').value = ''; |
|
|
document.getElementById('new-artwork-desc').value = ''; |
|
|
imageInput.value = ''; |
|
|
|
|
|
|
|
|
showNotification('تم إضافة العمل الفني بنجاح', 'success'); |
|
|
|
|
|
|
|
|
renderGallery('all'); |
|
|
}; |
|
|
|
|
|
reader.readAsDataURL(file); |
|
|
} |
|
|
|
|
|
|
|
|
function exportToPDF(type) { |
|
|
const { jsPDF } = window.jspdf; |
|
|
const doc = new jsPDF(); |
|
|
|
|
|
if (type === 'subject') { |
|
|
const subject = document.getElementById('subject-pages').getAttribute('data-current-subject'); |
|
|
const subjectNames = { |
|
|
'arabic': 'اللغة العربية', |
|
|
'math': 'الرياضيات', |
|
|
'islamic': 'التربية الإسلامية', |
|
|
'art': 'التربية الفنية', |
|
|
'music': 'التربية الموسيقية' |
|
|
}; |
|
|
|
|
|
doc.text(`كشف درجات مادة ${subjectNames[subject]}`, 10, 10); |
|
|
|
|
|
const classData = getClassData(); |
|
|
let y = 20; |
|
|
|
|
|
doc.text('الرقم', 10, y); |
|
|
doc.text('اسم التلميذ', 30, y); |
|
|
doc.text('الاختبار 1', 70, y); |
|
|
doc.text('الاختبار 2', 90, y); |
|
|
doc.text('الاختبار 3', 110, y); |
|
|
doc.text('المعدل', 130, y); |
|
|
|
|
|
y += 10; |
|
|
|
|
|
classData.students.forEach(student => { |
|
|
const grades = classData.grades[subject].find(g => g.studentId === student.id); |
|
|
const avg = calculateAverage(grades); |
|
|
|
|
|
doc.text(student.id.toString(), 10, y |
|
|
</html> |