Autosave: 20260204-144027

This commit is contained in:
Flatlogic Bot 2026-02-04 14:40:28 +00:00
parent 05987f69ac
commit c673d9b2b1
35 changed files with 1123 additions and 369 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

View File

@ -138,7 +138,7 @@ AUTH_PASSWORD_VALIDATORS = [
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/5.2/topics/i18n/ # https://docs.djangoproject.com/en/5.2/topics/i18n/
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'ar'
TIME_ZONE = 'UTC' TIME_ZONE = 'UTC'

View File

@ -17,13 +17,13 @@ class ActionsModelAdmin(admin.ModelAdmin):
return format_html( return format_html(
'<div style="white-space: nowrap;">' '<div style="white-space: nowrap;">'
'<a class="btn btn-sm btn-primary" href="{}" title="Edit" style="margin-right:5px;"><i class="fas fa-edit"></i></a>' '<a class="btn btn-sm btn-primary" href="{}" title="تعديل" style="margin-right:5px;"><i class="fas fa-edit"></i></a>'
'<a class="btn btn-sm btn-danger" href="{}" title="Delete"><i class="fas fa-trash-alt"></i></a>' '<a class="btn btn-sm btn-danger" href="{}" title="حذف"><i class="fas fa-trash-alt"></i></a>'
'</div>', '</div>',
edit_url, edit_url,
delete_url delete_url
) )
actions_column.short_description = 'Actions' actions_column.short_description = 'إجراءات'
@admin.register(Classroom) @admin.register(Classroom)
class ClassroomAdmin(ActionsModelAdmin): class ClassroomAdmin(ActionsModelAdmin):
@ -42,7 +42,7 @@ class SubjectAdmin(ActionsModelAdmin):
def get_teachers(self, obj): def get_teachers(self, obj):
return ", ".join([str(t) for t in obj.teachers.all()]) return ", ".join([str(t) for t in obj.teachers.all()])
get_teachers.short_description = 'Teachers' get_teachers.short_description = 'المعلمون'
@admin.register(City) @admin.register(City)
class CityAdmin(ActionsModelAdmin): class CityAdmin(ActionsModelAdmin):
@ -57,7 +57,7 @@ class ResourceAdminForm(forms.ModelForm):
classroom = forms.ModelChoiceField( classroom = forms.ModelChoiceField(
queryset=Classroom.objects.all(), queryset=Classroom.objects.all(),
required=True, required=True,
label="Filter by Classroom" label="تصفية حسب الصف"
) )
class Meta: class Meta:

View File

@ -1,25 +1,31 @@
from django import forms from django import forms
from django.contrib.auth.models import User from django.contrib.auth.models import User
from .models import Student, Subject, Classroom, City, Governorate from .models import Student, Subject, Classroom, City, Governorate, Teacher, Resource
class StudentRegistrationForm(forms.ModelForm): class StudentRegistrationForm(forms.ModelForm):
full_name = forms.CharField(max_length=150, required=True, label="Full Name") full_name = forms.CharField(max_length=150, required=True, label="الاسم الكامل")
username = forms.CharField(max_length=150, required=True) username = forms.CharField(max_length=150, required=True, label="اسم المستخدم")
email = forms.EmailField(required=True) email = forms.EmailField(required=True, label="البريد الإلكتروني")
password = forms.CharField(widget=forms.PasswordInput, required=True) password = forms.CharField(widget=forms.PasswordInput, required=True, label="كلمة المرور")
password_confirm = forms.CharField(widget=forms.PasswordInput, required=True, label="Confirm Password") password_confirm = forms.CharField(widget=forms.PasswordInput, required=True, label="تأكيد كلمة المرور")
classroom = forms.ModelChoiceField(queryset=Classroom.objects.all(), required=True, empty_label="Select Classroom") classroom = forms.ModelChoiceField(queryset=Classroom.objects.all(), required=True, empty_label="اختر الصف", label="الصف الدراسي")
subjects = forms.ModelMultipleChoiceField( subjects = forms.ModelMultipleChoiceField(
queryset=Subject.objects.all(), queryset=Subject.objects.all(),
required=False, required=False,
widget=forms.CheckboxSelectMultiple, widget=forms.CheckboxSelectMultiple,
label="Select Subjects" label="اختر المواد"
) )
class Meta: class Meta:
model = Student model = Student
fields = ['mobile_number', 'governorate', 'city', 'avatar'] fields = ['mobile_number', 'governorate', 'city', 'avatar']
labels = {
'mobile_number': 'رقم الهاتف',
'governorate': 'المحافظة',
'city': 'المدينة',
'avatar': 'الصورة الشخصية'
}
widgets = { widgets = {
'avatar': forms.FileInput(attrs={'accept': 'image/*', 'capture': 'camera'}), 'avatar': forms.FileInput(attrs={'accept': 'image/*', 'capture': 'camera'}),
} }
@ -65,7 +71,7 @@ class StudentRegistrationForm(forms.ModelForm):
password_confirm = cleaned_data.get("password_confirm") password_confirm = cleaned_data.get("password_confirm")
if password and password_confirm and password != password_confirm: if password and password_confirm and password != password_confirm:
self.add_error('password_confirm', "Passwords do not match") self.add_error('password_confirm', "كلمات المرور غير متطابقة")
return cleaned_data return cleaned_data
@ -92,3 +98,58 @@ class StudentRegistrationForm(forms.ModelForm):
student.save() student.save()
student.subscribed_subjects.set(self.cleaned_data['subjects']) student.subscribed_subjects.set(self.cleaned_data['subjects'])
return student return student
class TeacherProfileForm(forms.ModelForm):
first_name = forms.CharField(max_length=30, required=True, label="الاسم الأول")
last_name = forms.CharField(max_length=150, required=True, label="الاسم الأخير")
email = forms.EmailField(required=True, label="البريد الإلكتروني")
class Meta:
model = Teacher
fields = ['bio', 'specialization', 'avatar']
labels = {
'bio': 'نبذة عني',
'specialization': 'التخصص',
'avatar': 'الصورة الشخصية'
}
widgets = {
'bio': forms.Textarea(attrs={'rows': 4}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance and self.instance.user:
self.fields['first_name'].initial = self.instance.user.first_name
self.fields['last_name'].initial = self.instance.user.last_name
self.fields['email'].initial = self.instance.user.email
for field_name, field in self.fields.items():
field.widget.attrs['class'] = 'form-control'
def save(self, commit=True):
teacher = super().save(commit=False)
if commit:
user = teacher.user
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.email = self.cleaned_data['email']
user.save()
teacher.save()
return teacher
class ResourceForm(forms.ModelForm):
class Meta:
model = Resource
fields = ['title_ar', 'title_en', 'resource_type', 'file', 'link']
labels = {
'title_ar': 'العنوان (عربي)',
'title_en': 'العنوان (إنجليزي)',
'resource_type': 'نوع المصدر',
'file': 'الملف',
'link': 'الرابط'
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field_name, field in self.fields.items():
field.widget.attrs['class'] = 'form-control'

View File

@ -0,0 +1,314 @@
# Generated by Django 5.2.7 on 2026-02-04 10:25
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0013_resource_resource_type_alter_resource_link'),
]
operations = [
migrations.AlterModelOptions(
name='city',
options={'verbose_name': 'مدينة', 'verbose_name_plural': 'المدن'},
),
migrations.AlterModelOptions(
name='classroom',
options={'verbose_name': 'صف دراسي', 'verbose_name_plural': 'الصفوف الدراسية'},
),
migrations.AlterModelOptions(
name='governorate',
options={'verbose_name': 'محافظة', 'verbose_name_plural': 'المحافظات'},
),
migrations.AlterModelOptions(
name='platformsettings',
options={'verbose_name': 'إعدادات المنصة', 'verbose_name_plural': 'إعدادات المنصة'},
),
migrations.AlterModelOptions(
name='resource',
options={'verbose_name': 'مصدر تعليمي', 'verbose_name_plural': 'المصادر التعليمية'},
),
migrations.AlterModelOptions(
name='student',
options={'verbose_name': 'طالب', 'verbose_name_plural': 'الطلاب'},
),
migrations.AlterModelOptions(
name='subject',
options={'verbose_name': 'مادة دراسية', 'verbose_name_plural': 'المواد الدراسية'},
),
migrations.AlterModelOptions(
name='teacher',
options={'verbose_name': 'معلم', 'verbose_name_plural': 'المعلمون'},
),
migrations.AlterModelOptions(
name='thawaniconfiguration',
options={'verbose_name': 'إعدادات ثواني', 'verbose_name_plural': 'إعدادات ثواني'},
),
migrations.AlterModelOptions(
name='wablasconfiguration',
options={'verbose_name': 'إعدادات Wablas', 'verbose_name_plural': 'إعدادات Wablas'},
),
migrations.AlterField(
model_name='city',
name='governorate',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cities', to='core.governorate', verbose_name='المحافظة'),
),
migrations.AlterField(
model_name='city',
name='name_ar',
field=models.CharField(default='', max_length=100, verbose_name='الاسم (عربي)'),
),
migrations.AlterField(
model_name='city',
name='name_en',
field=models.CharField(default='', max_length=100, verbose_name='الاسم (إنجليزي)'),
),
migrations.AlterField(
model_name='classroom',
name='description',
field=models.TextField(blank=True, verbose_name='الوصف'),
),
migrations.AlterField(
model_name='classroom',
name='name_ar',
field=models.CharField(max_length=100, verbose_name='الاسم (عربي)'),
),
migrations.AlterField(
model_name='classroom',
name='name_en',
field=models.CharField(max_length=100, verbose_name='الاسم (إنجليزي)'),
),
migrations.AlterField(
model_name='governorate',
name='name_ar',
field=models.CharField(default='', max_length=100, verbose_name='الاسم (عربي)'),
),
migrations.AlterField(
model_name='governorate',
name='name_en',
field=models.CharField(default='', max_length=100, verbose_name='الاسم (إنجليزي)'),
),
migrations.AlterField(
model_name='platformsettings',
name='address',
field=models.TextField(blank=True, null=True, verbose_name='العنوان'),
),
migrations.AlterField(
model_name='platformsettings',
name='contact_email',
field=models.EmailField(blank=True, max_length=254, null=True, verbose_name='بريد التواصل'),
),
migrations.AlterField(
model_name='platformsettings',
name='contact_phone',
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='هاتف التواصل'),
),
migrations.AlterField(
model_name='platformsettings',
name='description',
field=models.TextField(blank=True, null=True, verbose_name='وصف المنصة'),
),
migrations.AlterField(
model_name='platformsettings',
name='facebook_link',
field=models.URLField(blank=True, null=True, verbose_name='رابط فيسبوك'),
),
migrations.AlterField(
model_name='platformsettings',
name='instagram_link',
field=models.URLField(blank=True, null=True, verbose_name='رابط انستغرام'),
),
migrations.AlterField(
model_name='platformsettings',
name='logo',
field=models.ImageField(blank=True, null=True, upload_to='platform/', verbose_name='الشعار'),
),
migrations.AlterField(
model_name='platformsettings',
name='name',
field=models.CharField(default='منصتي التعليمية', max_length=100, verbose_name='اسم المنصة'),
),
migrations.AlterField(
model_name='platformsettings',
name='twitter_link',
field=models.URLField(blank=True, null=True, verbose_name='رابط تويتر'),
),
migrations.AlterField(
model_name='resource',
name='file',
field=models.FileField(blank=True, null=True, upload_to='resources/', verbose_name='الملف'),
),
migrations.AlterField(
model_name='resource',
name='link',
field=models.URLField(blank=True, help_text='رابط YouTube للفيديو، أو رابط خارجي.', verbose_name='الرابط'),
),
migrations.AlterField(
model_name='resource',
name='resource_type',
field=models.CharField(choices=[('FILE', 'ملف (PDF, Doc, إلخ)'), ('VIDEO', 'فيديو (YouTube)'), ('LINK', 'رابط خارجي')], default='FILE', max_length=10, verbose_name='نوع المصدر'),
),
migrations.AlterField(
model_name='resource',
name='subject',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='resources', to='core.subject', verbose_name='المادة'),
),
migrations.AlterField(
model_name='resource',
name='title_ar',
field=models.CharField(max_length=200, verbose_name='العنوان (عربي)'),
),
migrations.AlterField(
model_name='resource',
name='title_en',
field=models.CharField(max_length=200, verbose_name='العنوان (إنجليزي)'),
),
migrations.AlterField(
model_name='student',
name='avatar',
field=models.ImageField(blank=True, null=True, upload_to='students/', verbose_name='الصورة الشخصية'),
),
migrations.AlterField(
model_name='student',
name='city',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.city', verbose_name='المدينة'),
),
migrations.AlterField(
model_name='student',
name='classroom',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='students', to='core.classroom', verbose_name='الصف'),
),
migrations.AlterField(
model_name='student',
name='email_otp_code',
field=models.CharField(blank=True, max_length=6, null=True, verbose_name='رمز تفعيل البريد'),
),
migrations.AlterField(
model_name='student',
name='governorate',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.governorate', verbose_name='المحافظة'),
),
migrations.AlterField(
model_name='student',
name='is_email_verified',
field=models.BooleanField(default=False, verbose_name='هل البريد مفعل؟'),
),
migrations.AlterField(
model_name='student',
name='mobile_number',
field=models.CharField(blank=True, max_length=20, verbose_name='رقم الجوال'),
),
migrations.AlterField(
model_name='student',
name='phone_number',
field=models.CharField(blank=True, max_length=20, verbose_name='رقم الهاتف الأرضي'),
),
migrations.AlterField(
model_name='student',
name='subscribed_subjects',
field=models.ManyToManyField(blank=True, related_name='subscribers', to='core.subject', verbose_name='المواد المسجلة'),
),
migrations.AlterField(
model_name='subject',
name='classroom',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subjects', to='core.classroom', verbose_name='الصف'),
),
migrations.AlterField(
model_name='subject',
name='description_ar',
field=models.TextField(blank=True, verbose_name='الوصف (عربي)'),
),
migrations.AlterField(
model_name='subject',
name='description_en',
field=models.TextField(blank=True, verbose_name='الوصف (إنجليزي)'),
),
migrations.AlterField(
model_name='subject',
name='google_drive_link',
field=models.URLField(blank=True, verbose_name='رابط Google Drive'),
),
migrations.AlterField(
model_name='subject',
name='google_meet_link',
field=models.URLField(blank=True, verbose_name='رابط Google Meet'),
),
migrations.AlterField(
model_name='subject',
name='image',
field=models.ImageField(blank=True, null=True, upload_to='subjects/', verbose_name='الصورة'),
),
migrations.AlterField(
model_name='subject',
name='name_ar',
field=models.CharField(max_length=200, verbose_name='الاسم (عربي)'),
),
migrations.AlterField(
model_name='subject',
name='name_en',
field=models.CharField(max_length=200, verbose_name='الاسم (إنجليزي)'),
),
migrations.AlterField(
model_name='subject',
name='price',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=10, verbose_name='السعر'),
),
migrations.AlterField(
model_name='subject',
name='teachers',
field=models.ManyToManyField(blank=True, related_name='subjects', to='core.teacher', verbose_name='المعلمون'),
),
migrations.AlterField(
model_name='subject',
name='youtube_live_url',
field=models.URLField(blank=True, help_text='ضع رابط بث YouTube هنا للبثوث الكبيرة (500+ طالب).', verbose_name='رابط بث YouTube'),
),
migrations.AlterField(
model_name='teacher',
name='avatar',
field=models.ImageField(blank=True, null=True, upload_to='teachers/', verbose_name='الصورة الشخصية'),
),
migrations.AlterField(
model_name='teacher',
name='bio',
field=models.TextField(blank=True, verbose_name='نبذة'),
),
migrations.AlterField(
model_name='teacher',
name='specialization',
field=models.CharField(blank=True, max_length=255, verbose_name='التخصص'),
),
migrations.AlterField(
model_name='thawaniconfiguration',
name='api_key',
field=models.CharField(help_text='Thawani Secret Key', max_length=255, verbose_name='API Key'),
),
migrations.AlterField(
model_name='thawaniconfiguration',
name='is_sandbox',
field=models.BooleanField(default=True, help_text='اختر هذا الخيار لاستخدام بيئة التجربة', verbose_name='وضع التجربة (Sandbox)'),
),
migrations.AlterField(
model_name='thawaniconfiguration',
name='publishable_key',
field=models.CharField(help_text='Thawani Publishable Key', max_length=255, verbose_name='Publishable Key'),
),
migrations.AlterField(
model_name='wablasconfiguration',
name='api_token',
field=models.CharField(max_length=255, verbose_name='API Token'),
),
migrations.AlterField(
model_name='wablasconfiguration',
name='api_url',
field=models.URLField(default='https://texas.wablas.com', verbose_name='رابط API'),
),
migrations.AlterField(
model_name='wablasconfiguration',
name='secret_key',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Secret Key'),
),
]

View File

@ -18,40 +18,48 @@ class SingletonModel(models.Model):
class Teacher(models.Model): class Teacher(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='teacher_profile') user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='teacher_profile')
bio = models.TextField(_("Bio"), blank=True) bio = models.TextField("نبذة", blank=True)
avatar = models.ImageField(_("Avatar"), upload_to='teachers/', blank=True, null=True) avatar = models.ImageField("الصورة الشخصية", upload_to='teachers/', blank=True, null=True)
specialization = models.CharField(_("Specialization"), max_length=255, blank=True) specialization = models.CharField("التخصص", max_length=255, blank=True)
class Meta:
verbose_name = "معلم"
verbose_name_plural = "المعلمون"
def __str__(self): def __str__(self):
return self.user.get_full_name() or self.user.username return self.user.get_full_name() or self.user.username
class Classroom(models.Model): class Classroom(models.Model):
name_en = models.CharField(_("Name (English)"), max_length=100) name_en = models.CharField("الاسم (إنجليزي)", max_length=100)
name_ar = models.CharField(_("Name (Arabic)"), max_length=100) name_ar = models.CharField("الاسم (عربي)", max_length=100)
description = models.TextField(_("Description"), blank=True) description = models.TextField("الوصف", blank=True)
class Meta: class Meta:
verbose_name = _("Classroom") verbose_name = "صف دراسي"
verbose_name_plural = _("Classrooms") verbose_name_plural = "الصفوف الدراسية"
def __str__(self): def __str__(self):
return self.name_en return self.name_ar or self.name_en
class Subject(models.Model): class Subject(models.Model):
classroom = models.ForeignKey(Classroom, on_delete=models.CASCADE, related_name='subjects') classroom = models.ForeignKey(Classroom, on_delete=models.CASCADE, related_name='subjects', verbose_name="الصف")
teachers = models.ManyToManyField(Teacher, blank=True, related_name='subjects') teachers = models.ManyToManyField(Teacher, blank=True, related_name='subjects', verbose_name="المعلمون")
name_en = models.CharField(_("Name (English)"), max_length=200) name_en = models.CharField("الاسم (إنجليزي)", max_length=200)
name_ar = models.CharField(_("Name (Arabic)"), max_length=200) name_ar = models.CharField("الاسم (عربي)", max_length=200)
description_en = models.TextField(_("Description (English)"), blank=True) description_en = models.TextField("الوصف (إنجليزي)", blank=True)
description_ar = models.TextField(_("Description (Arabic)"), blank=True) description_ar = models.TextField("الوصف (عربي)", blank=True)
price = models.DecimalField(_("Price"), max_digits=10, decimal_places=2, default=0.00) price = models.DecimalField("السعر", max_digits=10, decimal_places=2, default=0.00)
image = models.ImageField(_("Image"), upload_to='subjects/', blank=True, null=True) image = models.ImageField("الصورة", upload_to='subjects/', blank=True, null=True)
google_drive_link = models.URLField(_("Google Drive Link"), blank=True) google_drive_link = models.URLField("رابط Google Drive", blank=True)
google_meet_link = models.URLField(_("Google Meet Link"), blank=True) google_meet_link = models.URLField("رابط Google Meet", blank=True)
youtube_live_url = models.URLField(_("YouTube Live URL"), blank=True, help_text=_("Paste the YouTube Live link here for large broadcasts (500+ students).")) youtube_live_url = models.URLField("رابط بث YouTube", blank=True, help_text="ضع رابط بث YouTube هنا للبثوث الكبيرة (500+ طالب).")
class Meta:
verbose_name = "مادة دراسية"
verbose_name_plural = "المواد الدراسية"
def __str__(self): def __str__(self):
return self.name_en return self.name_ar or self.name_en
def get_youtube_id(self): def get_youtube_id(self):
"""Extracts the video ID from a YouTube URL.""" """Extracts the video ID from a YouTube URL."""
@ -75,21 +83,25 @@ class Subject(models.Model):
class Resource(models.Model): class Resource(models.Model):
RESOURCE_TYPES = ( RESOURCE_TYPES = (
('FILE', _('File (PDF, Doc, etc.)')), ('FILE', 'ملف (PDF, Doc, إلخ)'),
('VIDEO', _('Video Link (YouTube)')), ('VIDEO', 'فيديو (YouTube)'),
('LINK', _('External Link')), ('LINK', 'رابط خارجي'),
) )
subject = models.ForeignKey(Subject, on_delete=models.CASCADE, related_name='resources') subject = models.ForeignKey(Subject, on_delete=models.CASCADE, related_name='resources', verbose_name="المادة")
title_en = models.CharField(_("Title (English)"), max_length=200) title_en = models.CharField("العنوان (إنجليزي)", max_length=200)
title_ar = models.CharField(_("Title (Arabic)"), max_length=200) title_ar = models.CharField("العنوان (عربي)", max_length=200)
resource_type = models.CharField(_("Resource Type"), max_length=10, choices=RESOURCE_TYPES, default='FILE') resource_type = models.CharField("نوع المصدر", max_length=10, choices=RESOURCE_TYPES, default='FILE')
file = models.FileField(_("File"), upload_to='resources/', blank=True, null=True) file = models.FileField("الملف", upload_to='resources/', blank=True, null=True)
link = models.URLField(_("Link"), blank=True, help_text=_("YouTube URL for Video type, or external URL for Link type.")) link = models.URLField("الرابط", blank=True, help_text="رابط YouTube للفيديو، أو رابط خارجي.")
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = "مصدر تعليمي"
verbose_name_plural = "المصادر التعليمية"
def __str__(self): def __str__(self):
return self.title_en return self.title_ar or self.title_en
def get_youtube_id(self): def get_youtube_id(self):
"""Extracts the video ID from the link if it is a YouTube URL.""" """Extracts the video ID from the link if it is a YouTube URL."""
@ -118,42 +130,46 @@ class Resource(models.Model):
return False return False
class Governorate(models.Model): class Governorate(models.Model):
name_en = models.CharField(_("Name (English)"), max_length=100, default="") name_en = models.CharField("الاسم (إنجليزي)", max_length=100, default="")
name_ar = models.CharField(_("Name (Arabic)"), max_length=100, default="") name_ar = models.CharField("الاسم (عربي)", max_length=100, default="")
class Meta: class Meta:
verbose_name = _("Governorate") verbose_name = "محافظة"
verbose_name_plural = _("Governorates") verbose_name_plural = "المحافظات"
def __str__(self): def __str__(self):
return f"{self.name_en}" return self.name_ar or f"{self.name_en}"
class City(models.Model): class City(models.Model):
governorate = models.ForeignKey(Governorate, on_delete=models.CASCADE, related_name='cities', verbose_name=_("Governorate"), null=True, blank=True) governorate = models.ForeignKey(Governorate, on_delete=models.CASCADE, related_name='cities', verbose_name="المحافظة", null=True, blank=True)
name_en = models.CharField(_("Name (English)"), max_length=100, default="") name_en = models.CharField("الاسم (إنجليزي)", max_length=100, default="")
name_ar = models.CharField(_("Name (Arabic)"), max_length=100, default="") name_ar = models.CharField("الاسم (عربي)", max_length=100, default="")
class Meta: class Meta:
verbose_name = _("City") verbose_name = "مدينة"
verbose_name_plural = _("Cities") verbose_name_plural = "المدن"
def __str__(self): def __str__(self):
return f"{self.name_en}" return self.name_ar or f"{self.name_en}"
class Student(models.Model): class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='student_profile') user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='student_profile')
classroom = models.ForeignKey(Classroom, on_delete=models.SET_NULL, null=True, blank=True, related_name='students') classroom = models.ForeignKey(Classroom, on_delete=models.SET_NULL, null=True, blank=True, related_name='students', verbose_name="الصف")
phone_number = models.CharField(_("Phone Number"), max_length=20, blank=True) phone_number = models.CharField("رقم الهاتف الأرضي", max_length=20, blank=True)
mobile_number = models.CharField(_("Mobile Number"), max_length=20, blank=True) mobile_number = models.CharField("رقم الجوال", max_length=20, blank=True)
subscribed_subjects = models.ManyToManyField(Subject, blank=True, related_name='subscribers') subscribed_subjects = models.ManyToManyField(Subject, blank=True, related_name='subscribers', verbose_name="المواد المسجلة")
governorate = models.ForeignKey(Governorate, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_("Governorate")) governorate = models.ForeignKey(Governorate, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="المحافظة")
city = models.ForeignKey(City, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_("City")) city = models.ForeignKey(City, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="المدينة")
avatar = models.ImageField(_("Picture"), upload_to='students/', blank=True, null=True) avatar = models.ImageField("الصورة الشخصية", upload_to='students/', blank=True, null=True)
# Email Verification # Email Verification
email_otp_code = models.CharField(max_length=6, blank=True, null=True) email_otp_code = models.CharField("رمز تفعيل البريد", max_length=6, blank=True, null=True)
is_email_verified = models.BooleanField(default=False) is_email_verified = models.BooleanField("هل البريد مفعل؟", default=False)
class Meta:
verbose_name = "طالب"
verbose_name_plural = "الطلاب"
def __str__(self): def __str__(self):
return self.user.get_full_name() or self.user.username return self.user.get_full_name() or self.user.username
@ -161,45 +177,45 @@ class Student(models.Model):
# --- Configuration Models --- # --- Configuration Models ---
class WablasConfiguration(SingletonModel): class WablasConfiguration(SingletonModel):
api_url = models.URLField(default="https://texas.wablas.com") api_url = models.URLField("رابط API", default="https://texas.wablas.com")
api_token = models.CharField(max_length=255) api_token = models.CharField("API Token", max_length=255)
secret_key = models.CharField(max_length=255, blank=True, null=True) secret_key = models.CharField("Secret Key", max_length=255, blank=True, null=True)
class Meta: class Meta:
verbose_name = "Wablas Configuration" verbose_name = "إعدادات Wablas"
verbose_name_plural = "Wablas Configuration" verbose_name_plural = "إعدادات Wablas"
def __str__(self): def __str__(self):
return "Wablas Configuration" return "إعدادات Wablas"
class ThawaniConfiguration(SingletonModel): class ThawaniConfiguration(SingletonModel):
api_key = models.CharField(max_length=255, help_text="Thawani Secret Key") api_key = models.CharField("API Key", max_length=255, help_text="Thawani Secret Key")
publishable_key = models.CharField(max_length=255, help_text="Thawani Publishable Key") publishable_key = models.CharField("Publishable Key", max_length=255, help_text="Thawani Publishable Key")
is_sandbox = models.BooleanField(default=True, help_text="Check to use Sandbox environment") is_sandbox = models.BooleanField("وضع التجربة (Sandbox)", default=True, help_text="اختر هذا الخيار لاستخدام بيئة التجربة")
class Meta: class Meta:
verbose_name = "Thawani Configuration" verbose_name = "إعدادات ثواني"
verbose_name_plural = "Thawani Configuration" verbose_name_plural = "إعدادات ثواني"
def __str__(self): def __str__(self):
return "Thawani Configuration" return "إعدادات ثواني"
class PlatformSettings(SingletonModel): class PlatformSettings(SingletonModel):
name = models.CharField(max_length=100, default="My School Platform") name = models.CharField("اسم المنصة", max_length=100, default="منصتي التعليمية")
logo = models.ImageField(upload_to='platform/', blank=True, null=True) logo = models.ImageField("الشعار", upload_to='platform/', blank=True, null=True)
description = models.TextField(blank=True, null=True) description = models.TextField("وصف المنصة", blank=True, null=True)
contact_email = models.EmailField(blank=True, null=True) contact_email = models.EmailField("بريد التواصل", blank=True, null=True)
contact_phone = models.CharField(max_length=20, blank=True, null=True) contact_phone = models.CharField("هاتف التواصل", max_length=20, blank=True, null=True)
address = models.TextField(blank=True, null=True) address = models.TextField("العنوان", blank=True, null=True)
# Social Media # Social Media
facebook_link = models.URLField(blank=True, null=True) facebook_link = models.URLField("رابط فيسبوك", blank=True, null=True)
twitter_link = models.URLField(blank=True, null=True) twitter_link = models.URLField("رابط تويتر", blank=True, null=True)
instagram_link = models.URLField(blank=True, null=True) instagram_link = models.URLField("رابط انستغرام", blank=True, null=True)
class Meta: class Meta:
verbose_name = "Platform Profile" verbose_name = "إعدادات المنصة"
verbose_name_plural = "Platform Profile" verbose_name_plural = "إعدادات المنصة"
def __str__(self): def __str__(self):
return "Platform Profile" return "إعدادات المنصة"

View File

@ -1,17 +1,14 @@
{% load i18n static %} {% load i18n static %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{{ LANGUAGE_CODE }}" dir="{% if LANGUAGE_BIDI %}rtl{% else %}ltr{% endif %}"> <html lang="ar" dir="rtl">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{% trans "E-Learning Platform" %}{% endblock %}</title> <title>{% block title %}منصة التعليم الإلكتروني{% endblock %}</title>
<!-- Bootstrap 5 CDN --> <!-- Bootstrap 5 CDN (RTL) -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
{% if LANGUAGE_BIDI %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.rtl.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
{% endif %}
<!-- Google Fonts --> <!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
@ -30,7 +27,7 @@
} }
body { body {
font-family: 'Outfit', 'Noto Kufi Arabic', sans-serif; font-family: 'Noto Kufi Arabic', 'Outfit', sans-serif;
background-color: var(--bg-light); background-color: var(--bg-light);
color: #333; color: #333;
overflow-x: hidden; overflow-x: hidden;
@ -88,7 +85,7 @@
content: ''; content: '';
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0; /* RTL adjusted */
width: 300px; width: 300px;
height: 300px; height: 300px;
background: radial-gradient(circle, rgba(45, 49, 250, 0.1) 0%, transparent 70%); background: radial-gradient(circle, rgba(45, 49, 250, 0.1) 0%, transparent 70%);
@ -125,44 +122,35 @@
<div class="collapse navbar-collapse" id="navbarNav"> <div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto"> <ul class="navbar-nav me-auto">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="#">{% trans "Courses" %}</a> <a class="nav-link" href="#">الدورات</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="#">{% trans "Teachers" %}</a> <a class="nav-link" href="#">المعلمون</a>
</li> </li>
{% if user.is_staff %} {% if user.is_staff %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/admin/">{% trans "Admin" %}</a> <a class="nav-link" href="/admin/">المسؤول</a>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<div class="dropdown me-3">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown">
{% if LANGUAGE_CODE == 'ar' %}العربية{% else %}English{% endif %}
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-menu-item dropdown-item" href="{% url 'set_language' 'en' %}?next={{ request.path }}">English</a></li>
<li><a class="dropdown-menu-item dropdown-item" href="{% url 'set_language' 'ar' %}?next={{ request.path }}">العربية</a></li>
</ul>
</div>
{% if user.is_authenticated %} {% if user.is_authenticated %}
<div class="dropdown ms-2"> <div class="dropdown ms-2">
<button class="btn btn-link text-decoration-none dropdown-toggle text-dark d-flex align-items-center" type="button" id="userDropdown" data-bs-toggle="dropdown" aria-expanded="false"> <button class="btn btn-link text-decoration-none dropdown-toggle text-dark d-flex align-items-center" type="button" id="userDropdown" data-bs-toggle="dropdown" aria-expanded="false">
<span class="fw-semibold">{{ user.username }}</span> <span class="fw-semibold">{{ user.username }}</span>
</button> </button>
<ul class="dropdown-menu dropdown-menu-end shadow-sm border-0" aria-labelledby="userDropdown" style="border-radius: 12px;"> <ul class="dropdown-menu dropdown-menu-end shadow-sm border-0" aria-labelledby="userDropdown" style="border-radius: 12px;">
<li><a class="dropdown-item py-2" href="{% url 'profile' %}">{% trans "Profile" %}</a></li> <li><a class="dropdown-item py-2" href="{% url 'profile' %}">الملف الشخصي</a></li>
{% if user.is_staff %} {% if user.is_staff %}
<li><a class="dropdown-item py-2" href="/admin/">{% trans "Admin Panel" %}</a></li> <li><a class="dropdown-item py-2" href="/admin/">لوحة تحكم المسؤول</a></li>
{% endif %} {% endif %}
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item py-2 text-danger" href="{% url 'logout' %}">{% trans "Logout" %}</a></li> <li><a class="dropdown-item py-2 text-danger" href="{% url 'logout' %}">تسجيل الخروج</a></li>
</ul> </ul>
</div> </div>
{% else %} {% else %}
<a href="{% url 'register_student' %}" class="btn btn-outline-primary me-2">{% trans "Register" %}</a> <a href="{% url 'register_student' %}" class="btn btn-outline-primary me-2">تسجيل</a>
<a href="{% url 'login' %}" class="btn btn-primary">{% trans "Login" %}</a> <a href="{% url 'login' %}" class="btn btn-primary">دخول</a>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -173,7 +161,7 @@
<footer class="footer mt-5"> <footer class="footer mt-5">
<div class="container text-center"> <div class="container text-center">
<p>&copy; 2026 EduPlatform. {% trans "All rights reserved." %}</p> <p>&copy; 2026 EduPlatform. جميع الحقوق محفوظة.</p>
</div> </div>
</footer> </footer>

View File

@ -0,0 +1,41 @@
{% extends 'base.html' %}
{% load i18n static %}
{% block title %}إضافة مصدر جديد - {{ subject.name_ar }}{% endblock %}
{% block content %}
<div class="container" style="margin-top: 100px; margin-bottom: 50px;">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6">
<div class="glass-card p-4 p-md-5">
<div class="text-center mb-4">
<h3 class="fw-bold text-primary">إضافة مصدر جديد</h3>
<p class="text-muted">للمادة: {{ subject.name_ar }}</p>
</div>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="mb-3">
<label for="{{ field.id_for_label }}" class="form-label fw-bold">{{ field.label }}</label>
{{ field }}
{% if field.help_text %}
<div class="form-text">{{ field.help_text }}</div>
{% endif %}
{% for error in field.errors %}
<div class="text-danger small">{{ error }}</div>
{% endfor %}
</div>
{% endfor %}
<div class="d-grid gap-2 mt-4">
<button type="submit" class="btn btn-primary rounded-pill btn-lg">حفظ المصدر</button>
<a href="{% url 'subject_detail' subject.id %}" class="btn btn-outline-secondary rounded-pill">إلغاء</a>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,34 @@
{% extends 'base.html' %}
{% load i18n static %}
{% block title %}حذف المصدر - {{ resource.title_ar }}{% endblock %}
{% block content %}
<div class="container" style="margin-top: 100px; margin-bottom: 50px;">
<div class="row justify-content-center">
<div class="col-md-6 col-lg-5">
<div class="glass-card p-4 p-md-5 text-center">
<div class="mb-4 text-danger">
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="currentColor" class="bi bi-exclamation-circle" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
<path d="M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 4.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995z"/>
</svg>
</div>
<h3 class="fw-bold mb-3">هل أنت متأكد؟</h3>
<p class="text-muted mb-4">
أنت على وشك حذف المصدر <strong>"{{ resource.title_ar }}"</strong>. لا يمكن التراجع عن هذا الإجراء.
</p>
<form method="post">
{% csrf_token %}
<div class="d-grid gap-2">
<button type="submit" class="btn btn-danger rounded-pill btn-lg">نعم، احذف المصدر</button>
<a href="{% url 'subject_detail' resource.subject.id %}" class="btn btn-outline-secondary rounded-pill">إلغاء</a>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,41 @@
{% extends 'base.html' %}
{% load i18n static %}
{% block title %}تعديل المصدر - {{ resource.title_ar }}{% endblock %}
{% block content %}
<div class="container" style="margin-top: 100px; margin-bottom: 50px;">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6">
<div class="glass-card p-4 p-md-5">
<div class="text-center mb-4">
<h3 class="fw-bold text-primary">تعديل المصدر</h3>
<p class="text-muted">المادة: {{ subject.name_ar }}</p>
</div>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="mb-3">
<label for="{{ field.id_for_label }}" class="form-label fw-bold">{{ field.label }}</label>
{{ field }}
{% if field.help_text %}
<div class="form-text">{{ field.help_text }}</div>
{% endif %}
{% for error in field.errors %}
<div class="text-danger small">{{ error }}</div>
{% endfor %}
</div>
{% endfor %}
<div class="d-grid gap-2 mt-4">
<button type="submit" class="btn btn-primary rounded-pill btn-lg">حفظ التغييرات</button>
<a href="{% url 'subject_detail' subject.id %}" class="btn btn-outline-secondary rounded-pill">إلغاء</a>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,59 @@
{% extends 'base.html' %}
{% load i18n static %}
{% block title %}تعديل ملف المعلم - EduPlatform{% endblock %}
{% block content %}
<div class="container" style="margin-top: 100px; margin-bottom: 50px;">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6">
<div class="card shadow border-0 rounded-4">
<div class="card-body p-4 p-md-5">
<h3 class="fw-bold text-center mb-4">تعديل الملف الشخصي</h3>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<!-- Avatar Preview -->
<div class="text-center mb-4">
<div class="position-relative d-inline-block">
{% if form.instance.avatar %}
<img src="{{ form.instance.avatar.url }}" alt="Avatar" class="rounded-circle img-thumbnail" style="width: 120px; height: 120px; object-fit: cover;">
{% else %}
<div class="rounded-circle bg-light d-flex align-items-center justify-content-center mx-auto" style="width: 120px; height: 120px;">
<span class="text-muted display-4">{{ user.first_name|first|upper }}</span>
</div>
{% endif %}
</div>
</div>
{% for field in form %}
<div class="mb-3">
<label for="{{ field.id_for_label }}" class="form-label fw-semibold">{{ field.label }}</label>
{{ field }}
{% if field.help_text %}
<div class="form-text text-muted">{{ field.help_text }}</div>
{% endif %}
{% for error in field.errors %}
<div class="invalid-feedback d-block">
{{ error }}
</div>
{% endfor %}
</div>
{% endfor %}
<div class="d-grid gap-2 mt-4">
<button type="submit" class="btn btn-primary btn-lg rounded-pill fw-bold">
حفظ التغييرات
</button>
<a href="{% url 'profile' %}" class="btn btn-outline-secondary rounded-pill">
إلغاء
</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,11 +1,12 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load i18n static %}
{% block content %} {% block content %}
<div class="container mt-5"> <div class="container mt-5">
<div class="alert alert-danger"> <div class="alert alert-danger">
<h4>Error</h4> <h4>خطأ</h4>
<p>{{ message }}</p> <p>{{ message }}</p>
<a href="{% url 'profile' %}" class="btn btn-secondary">Go Back to Profile</a> <a href="{% url 'profile' %}" class="btn btn-secondary">العودة للملف الشخصي</a>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load i18n static %} {% load i18n static %}
{% block title %}{% trans "Home" %} | EduPlatform{% endblock %} {% block title %}الرئيسية | EduPlatform{% endblock %}
{% block content %} {% block content %}
<section class="hero-section"> <section class="hero-section">
@ -9,22 +9,14 @@
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-lg-6"> <div class="col-lg-6">
<h1 class="display-4 fw-bold mb-4"> <h1 class="display-4 fw-bold mb-4">
{% if LANGUAGE_CODE == 'ar' %}
تعلم من أفضل المعلمين في أي وقت وأي مكان تعلم من أفضل المعلمين في أي وقت وأي مكان
{% else %}
Learn from the best teachers anytime, anywhere
{% endif %}
</h1> </h1>
<p class="lead mb-5 text-muted"> <p class="lead mb-5 text-muted">
{% if LANGUAGE_CODE == 'ar' %}
منصة تعليمية متكاملة تدعم الطلاب والمعلمين مع دروس حية ومصادر تعليمية متميزة. منصة تعليمية متكاملة تدعم الطلاب والمعلمين مع دروس حية ومصادر تعليمية متميزة.
{% else %}
A comprehensive educational platform supporting students and teachers with live classes and premium educational resources.
{% endif %}
</p> </p>
<div class="d-flex gap-3"> <div class="d-flex gap-3">
<a href="#levels-section" class="btn btn-primary btn-lg px-5">{% trans "Get Started" %}</a> <a href="#levels-section" class="btn btn-primary btn-lg px-5">ابدأ الآن</a>
<a href="#" class="btn btn-outline-secondary btn-lg px-5">{% trans "Learn More" %}</a> <a href="#" class="btn btn-outline-secondary btn-lg px-5">اعرف المزيد</a>
</div> </div>
</div> </div>
<div class="col-lg-6 d-none d-lg-block"> <div class="col-lg-6 d-none d-lg-block">
@ -32,8 +24,8 @@
<div style="width: 400px; height: 400px; background: var(--primary-color); border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%; opacity: 0.1; position: absolute; top: -50px; left: 50px;"></div> <div style="width: 400px; height: 400px; background: var(--primary-color); border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%; opacity: 0.1; position: absolute; top: -50px; left: 50px;"></div>
<div style="width: 300px; height: 300px; background: var(--secondary-color); border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%; opacity: 0.1; position: absolute; bottom: -50px; right: 50px;"></div> <div style="width: 300px; height: 300px; background: var(--secondary-color); border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%; opacity: 0.1; position: absolute; bottom: -50px; right: 50px;"></div>
<div class="glass-card text-center position-relative z-1"> <div class="glass-card text-center position-relative z-1">
<h3 class="fw-bold mb-3">{% trans "Ready to start?" %}</h3> <h3 class="fw-bold mb-3">مستعد للبدء؟</h3>
<p>{% trans "Join thousands of students learning today." %}</p> <p>انضم لآلاف الطلاب اليوم.</p>
<div class="avatar-group d-flex justify-content-center mt-4"> <div class="avatar-group d-flex justify-content-center mt-4">
<div style="width: 40px; height: 40px; border-radius: 50%; background: #ccc; border: 2px solid #fff; margin-left: -10px;"></div> <div style="width: 40px; height: 40px; border-radius: 50%; background: #ccc; border: 2px solid #fff; margin-left: -10px;"></div>
<div style="width: 40px; height: 40px; border-radius: 50%; background: #ddd; border: 2px solid #fff; margin-left: -10px;"></div> <div style="width: 40px; height: 40px; border-radius: 50%; background: #ddd; border: 2px solid #fff; margin-left: -10px;"></div>
@ -49,8 +41,8 @@
<section id="levels-section" class="subjects-section py-5"> <section id="levels-section" class="subjects-section py-5">
<div class="container"> <div class="container">
<div class="text-center mb-5"> <div class="text-center mb-5">
<h2 class="fw-bold">{% trans "Our Educational Programs" %}</h2> <h2 class="fw-bold">برامجنا التعليمية</h2>
<p class="text-muted">{% trans "Explore subjects tailored for each educational level." %}</p> <p class="text-muted">استكشف المواد المخصصة لكل مرحلة دراسية.</p>
</div> </div>
{% for level in levels %} {% for level in levels %}
@ -60,7 +52,7 @@
{{ forloop.counter }} {{ forloop.counter }}
</div> </div>
<h3 class="fw-bold mb-0"> <h3 class="fw-bold mb-0">
{% if LANGUAGE_CODE == 'ar' %}{{ level.name_ar }}{% else %}{{ level.name_en }}{% endif %} {{ level.name_ar }}
</h3> </h3>
<hr class="flex-grow-1 ms-4 d-none d-md-block opacity-10"> <hr class="flex-grow-1 ms-4 d-none d-md-block opacity-10">
</div> </div>
@ -78,14 +70,14 @@
</div> </div>
<div class="p-4"> <div class="p-4">
<h4 class="fw-bold mb-3"> <h4 class="fw-bold mb-3">
{% if LANGUAGE_CODE == 'ar' %}{{ subject.name_ar }}{% else %}{{ subject.name_en }}{% endif %} {{ subject.name_ar }}
</h4> </h4>
<p class="text-muted small mb-4"> <p class="text-muted small mb-4">
{% if LANGUAGE_CODE == 'ar' %}{{ subject.description_ar|truncatewords:15 }}{% else %}{{ subject.description_en|truncatewords:15 }}{% endif %} {{ subject.description_ar|truncatewords:15 }}
</p> </p>
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<span class="h5 fw-bold mb-0 text-primary">${{ subject.price }}</span> <span class="h5 fw-bold mb-0 text-primary">${{ subject.price }}</span>
<a href="{% url 'subject_detail' subject.pk %}" class="btn btn-primary btn-sm">{% trans "Subscribe" %}</a> <a href="{% url 'subject_detail' subject.pk %}" class="btn btn-primary btn-sm">اشتراك</a>
</div> </div>
</div> </div>
</div> </div>
@ -93,7 +85,7 @@
{% empty %} {% empty %}
<div class="col-12"> <div class="col-12">
<div class="alert alert-light border-0 text-center py-4"> <div class="alert alert-light border-0 text-center py-4">
{% trans "No subjects available for this level yet." %} لا توجد مواد متاحة لهذا المستوى بعد.
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
@ -107,8 +99,8 @@
<section id="teachers-section" class="py-5 bg-light"> <section id="teachers-section" class="py-5 bg-light">
<div class="container"> <div class="container">
<div class="text-center mb-5"> <div class="text-center mb-5">
<h2 class="fw-bold">{% trans "Meet Our Teachers" %}</h2> <h2 class="fw-bold">تعرف على معلمينا</h2>
<p class="text-muted">{% trans "Learn from our expert instructors." %}</p> <p class="text-muted">تعلم من نخبة من المعلمين.</p>
</div> </div>
<div class="row g-4 justify-content-center"> <div class="row g-4 justify-content-center">
@ -127,20 +119,16 @@
</div> </div>
<h5 class="fw-bold mb-1">{{ teacher.user.get_full_name|default:teacher.user.username }}</h5> <h5 class="fw-bold mb-1">{{ teacher.user.get_full_name|default:teacher.user.username }}</h5>
<p class="text-primary small fw-bold mb-3">{{ teacher.specialization|default:"Teacher" }}</p> <p class="text-primary small fw-bold mb-3">{{ teacher.specialization|default:"معلم" }}</p>
<p class="text-muted small mb-4"> <p class="text-muted small mb-4">
{{ teacher.bio|default:"No bio available."|truncatewords:20 }} {{ teacher.bio|default:"لا توجد نبذة."|truncatewords:20 }}
</p> </p>
<div class="mt-auto">
<!-- Optional: Add social links or 'View Profile' button here if needed -->
</div>
</div> </div>
</div> </div>
{% empty %} {% empty %}
<div class="col-12 text-center"> <div class="col-12 text-center">
<p class="text-muted">{% trans "No teachers found at the moment." %}</p> <p class="text-muted">لا يوجد معلمين حالياً.</p>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
@ -153,22 +141,22 @@
<div class="col-md-4"> <div class="col-md-4">
<div class="p-4"> <div class="p-4">
<div class="mb-3 text-primary h1">🎥</div> <div class="mb-3 text-primary h1">🎥</div>
<h5 class="fw-bold">{% trans "Live Classes" %}</h5> <h5 class="fw-bold">دروس مباشرة</h5>
<p class="text-muted">{% trans "Join interactive sessions via Google Meet." %}</p> <p class="text-muted">انضم لجلسات تفاعلية عبر Google Meet.</p>
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<div class="p-4"> <div class="p-4">
<div class="mb-3 text-primary h1">📁</div> <div class="mb-3 text-primary h1">📁</div>
<h5 class="fw-bold">{% trans "Resources" %}</h5> <h5 class="fw-bold">المصادر</h5>
<p class="text-muted">{% trans "Access course materials on Google Drive." %}</p> <p class="text-muted">الوصول لمواد الدورة عبر Google Drive.</p>
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<div class="p-4"> <div class="p-4">
<div class="mb-3 text-primary h1">💳</div> <div class="mb-3 text-primary h1">💳</div>
<h5 class="fw-bold">{% trans "Easy Payments" %}</h5> <h5 class="fw-bold">دفع سهل</h5>
<p class="text-muted">{% trans "Secure payments with Thawani gateway." %}</p> <p class="text-muted">دفع آمن عبر بوابة ثواني.</p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load i18n static %} {% load i18n static %}
{% block title %}{% trans "Live Classroom" %} - {{ subject.name_en }}{% endblock %} {% block title %}الفصل المباشر - {{ subject.name_ar }}{% endblock %}
{% block content %} {% block content %}
<div class="container-fluid p-0" style="height: calc(100vh - 76px); margin-top: 76px;"> <div class="container-fluid p-0" style="height: calc(100vh - 76px); margin-top: 76px;">
@ -12,10 +12,10 @@
<!-- Google Meet Mode (Highest Priority) --> <!-- Google Meet Mode (Highest Priority) -->
<div class="d-flex flex-column align-items-center justify-content-center h-100 bg-light"> <div class="d-flex flex-column align-items-center justify-content-center h-100 bg-light">
<div class="text-center p-5 bg-white shadow rounded"> <div class="text-center p-5 bg-white shadow rounded">
<h2 class="mb-4">{% trans "Live Class via Google Meet" %}</h2> <h2 class="mb-4">درس مباشر عبر Google Meet</h2>
<p class="mb-4 text-muted">{% trans "This class is being held on Google Meet. Click the button below to join." %}</p> <p class="mb-4 text-muted">يقام هذا الدرس عبر Google Meet. انقر الزر أدناه للانضمام.</p>
<a href="{{ subject.google_meet_link }}" target="_blank" class="btn btn-primary btn-lg"> <a href="{{ subject.google_meet_link }}" target="_blank" class="btn btn-primary btn-lg">
<i class="fas fa-video me-2"></i> {% trans "Join Google Meet" %} <i class="fas fa-video me-2"></i> انضم إلى Google Meet
</a> </a>
</div> </div>
</div> </div>

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load i18n static %} {% load i18n static %}
{% block title %}{% trans "Login" %}{% endblock %} {% block title %}تسجيل الدخول{% endblock %}
{% block content %} {% block content %}
<div class="container hero-section"> <div class="container hero-section">
@ -9,8 +9,8 @@
<div class="col-md-6 col-lg-5"> <div class="col-md-6 col-lg-5">
<div class="glass-card p-5"> <div class="glass-card p-5">
<div class="text-center mb-4"> <div class="text-center mb-4">
<h2 class="text-primary fw-bold">{% trans "Welcome Back" %}</h2> <h2 class="text-primary fw-bold">مرحباً بعودتك</h2>
<p class="text-muted">{% trans "Sign in to continue your learning journey" %}</p> <p class="text-muted">سجل الدخول لمتابعة رحلتك التعليمية</p>
</div> </div>
<form method="post"> <form method="post">
@ -18,29 +18,29 @@
{% if form.errors %} {% if form.errors %}
<div class="alert alert-danger alert-dismissible fade show" role="alert"> <div class="alert alert-danger alert-dismissible fade show" role="alert">
{% trans "Invalid username or password." %} اسم المستخدم أو كلمة المرور غير صحيحة.
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div> </div>
{% endif %} {% endif %}
<div class="form-floating mb-3"> <div class="form-floating mb-3">
<input type="text" name="username" autofocus autocapitalize="none" autocomplete="username" maxlength="150" required id="id_username" class="form-control" placeholder="{% trans 'Username' %}"> <input type="text" name="username" autofocus autocapitalize="none" autocomplete="username" maxlength="150" required id="id_username" class="form-control" placeholder="اسم المستخدم">
<label for="id_username"><i class="fas fa-user me-2"></i>{% trans "Username" %}</label> <label for="id_username"><i class="fas fa-user me-2"></i>اسم المستخدم</label>
</div> </div>
<div class="form-floating mb-4"> <div class="form-floating mb-4">
<input type="password" name="password" autocomplete="current-password" required id="id_password" class="form-control" placeholder="{% trans 'Password' %}"> <input type="password" name="password" autocomplete="current-password" required id="id_password" class="form-control" placeholder="كلمة المرور">
<label for="id_password"><i class="fas fa-lock me-2"></i>{% trans "Password" %}</label> <label for="id_password"><i class="fas fa-lock me-2"></i>كلمة المرور</label>
</div> </div>
<button type="submit" class="btn btn-primary w-100 py-3 fs-5 fw-bold shadow-sm"> <button type="submit" class="btn btn-primary w-100 py-3 fs-5 fw-bold shadow-sm">
{% trans "Login" %} <i class="fas fa-arrow-right ms-2"></i> دخول <i class="fas fa-arrow-left ms-2"></i>
</button> </button>
<div class="text-center mt-4"> <div class="text-center mt-4">
<p class="text-muted mb-0"> <p class="text-muted mb-0">
{% trans "Don't have an account?" %} ليس لديك حساب؟
<a href="{% url 'register_student' %}" class="text-primary text-decoration-none fw-bold">{% trans "Register Here" %}</a> <a href="{% url 'register_student' %}" class="text-primary text-decoration-none fw-bold">سجل هنا</a>
</p> </p>
</div> </div>
</form> </form>

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load i18n static %} {% load i18n static %}
{% block title %}{% trans "My Profile" %} | EduPlatform{% endblock %} {% block title %}ملفي الشخصي | EduPlatform{% endblock %}
{% block content %} {% block content %}
<section class="hero-section text-center pt-5 pb-5"> <section class="hero-section text-center pt-5 pb-5">
@ -21,9 +21,9 @@
<h2 class="mb-1">{{ user.get_full_name|default:user.username }}</h2> <h2 class="mb-1">{{ user.get_full_name|default:user.username }}</h2>
<p class="text-muted mb-0">{{ user.email }}</p> <p class="text-muted mb-0">{{ user.email }}</p>
{% if student_profile and student_profile.classroom %} {% if student_profile and student_profile.classroom %}
<span class="badge bg-primary">{{ student_profile.classroom.name_en }}</span> <span class="badge bg-primary">{{ student_profile.classroom.name_ar }}</span>
{% elif user.is_staff %} {% elif user.is_staff %}
<span class="badge bg-success">{% trans "Staff / Admin" %}</span> <span class="badge bg-success">طاقم / مسؤول</span>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -31,42 +31,42 @@
<hr class="my-4"> <hr class="my-4">
{% if student_profile %} {% if student_profile %}
<h4 class="mb-3">{% trans "Student Details" %}</h4> <h4 class="mb-3">تفاصيل الطالب</h4>
<div class="row"> <div class="row">
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label text-muted">{% trans "Username" %}</label> <label class="form-label text-muted">اسم المستخدم</label>
<p class="fw-bold">{{ user.username }}</p> <p class="fw-bold">{{ user.username }}</p>
</div> </div>
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label text-muted">{% trans "City" %}</label> <label class="form-label text-muted">المدينة</label>
<p class="fw-bold">{{ student_profile.city|default:"-" }}</p> <p class="fw-bold">{{ student_profile.city.name_ar|default:"-" }}</p>
</div> </div>
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label text-muted">{% trans "Governorate" %}</label> <label class="form-label text-muted">المحافظة</label>
<p class="fw-bold">{{ student_profile.governorate|default:"-" }}</p> <p class="fw-bold">{{ student_profile.governorate.name_ar|default:"-" }}</p>
</div> </div>
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label text-muted">{% trans "Mobile Number" %}</label> <label class="form-label text-muted">رقم الهاتف</label>
<p class="fw-bold">{{ student_profile.mobile_number|default:"-" }}</p> <p class="fw-bold">{{ student_profile.mobile_number|default:"-" }}</p>
</div> </div>
</div> </div>
<h4 class="mb-3 mt-4">{% trans "My Subjects" %}</h4> <h4 class="mb-3 mt-4">موادي</h4>
{% if student_profile.subscribed_subjects.all %} {% if student_profile.subscribed_subjects.all %}
<div class="list-group"> <div class="list-group">
{% for subject in student_profile.subscribed_subjects.all %} {% for subject in student_profile.subscribed_subjects.all %}
<a href="{% url 'subject_detail' subject.pk %}" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center"> <a href="{% url 'subject_detail' subject.pk %}" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
{{ subject.name_en }} {{ subject.name_ar }}
<span class="badge bg-secondary rounded-pill">{% trans "View" %}</span> <span class="badge bg-secondary rounded-pill">عرض</span>
</a> </a>
{% endfor %} {% endfor %}
</div> </div>
{% else %} {% else %}
<p class="text-muted">{% trans "No subjects subscribed yet." %}</p> <p class="text-muted">لم يتم الاشتراك في أي مواد بعد.</p>
{% endif %} {% endif %}
{% else %} {% else %}
<div class="alert alert-info"> <div class="alert alert-info">
{% trans "You are logged in as a staff member or user without a student profile." %} أنت مسجل دخول كعضو طاقم أو مستخدم بدون ملف طالب.
</div> </div>
{% endif %} {% endif %}
</div> </div>

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load i18n static %} {% load i18n static %}
{% block title %}{% trans "Student Registration" %}{% endblock %} {% block title %}تسجيل الطالب{% endblock %}
{% block content %} {% block content %}
<div class="container hero-section"> <div class="container hero-section">
@ -9,8 +9,8 @@
<div class="col-lg-8"> <div class="col-lg-8">
<div class="glass-card p-4 p-md-5"> <div class="glass-card p-4 p-md-5">
<div class="text-center mb-5"> <div class="text-center mb-5">
<h2 class="text-primary fw-bold">{% trans "Create Your Account" %}</h2> <h2 class="text-primary fw-bold">أنشئ حسابك</h2>
<p class="text-muted">{% trans "Join our learning platform today" %}</p> <p class="text-muted">انضم إلى منصتنا التعليمية اليوم</p>
</div> </div>
<form method="post" enctype="multipart/form-data" id="registrationForm" class="needs-validation"> <form method="post" enctype="multipart/form-data" id="registrationForm" class="needs-validation">
@ -34,34 +34,34 @@
<!-- Section: Personal Info --> <!-- Section: Personal Info -->
<div class="card border-0 shadow-sm mb-4"> <div class="card border-0 shadow-sm mb-4">
<div class="card-body p-4"> <div class="card-body p-4">
<h5 class="card-title text-primary mb-3"><i class="fas fa-user-circle me-2"></i>{% trans "Personal Information" %}</h5> <h5 class="card-title text-primary mb-3"><i class="fas fa-user-circle me-2"></i>المعلومات الشخصية</h5>
<div class="form-floating mb-3"> <div class="form-floating mb-3">
{{ form.full_name }} {{ form.full_name }}
<label for="{{ form.full_name.id_for_label }}">{% trans "Full Name" %}</label> <label for="{{ form.full_name.id_for_label }}">الاسم الكامل</label>
</div> </div>
<div class="form-floating mb-3"> <div class="form-floating mb-3">
{{ form.username }} {{ form.username }}
<label for="{{ form.username.id_for_label }}">{% trans "Username" %}</label> <label for="{{ form.username.id_for_label }}">اسم المستخدم</label>
</div> </div>
<div class="form-floating mb-3"> <div class="form-floating mb-3">
{{ form.email }} {{ form.email }}
<label for="{{ form.email.id_for_label }}">{% trans "Email Address" %}</label> <label for="{{ form.email.id_for_label }}">البريد الإلكتروني</label>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<div class="form-floating"> <div class="form-floating">
{{ form.password }} {{ form.password }}
<label for="{{ form.password.id_for_label }}">{% trans "Password" %}</label> <label for="{{ form.password.id_for_label }}">كلمة المرور</label>
</div> </div>
</div> </div>
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<div class="form-floating"> <div class="form-floating">
{{ form.password_confirm }} {{ form.password_confirm }}
<label for="{{ form.password_confirm.id_for_label }}">{% trans "Confirm Password" %}</label> <label for="{{ form.password_confirm.id_for_label }}">تأكيد كلمة المرور</label>
</div> </div>
</div> </div>
</div> </div>
@ -71,24 +71,24 @@
<!-- Section: Student Details --> <!-- Section: Student Details -->
<div class="card border-0 shadow-sm mb-4"> <div class="card border-0 shadow-sm mb-4">
<div class="card-body p-4"> <div class="card-body p-4">
<h5 class="card-title text-primary mb-3"><i class="fas fa-id-card me-2"></i>{% trans "Student Profile" %}</h5> <h5 class="card-title text-primary mb-3"><i class="fas fa-id-card me-2"></i>ملف الطالب</h5>
<div class="row"> <div class="row">
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<div class="form-floating"> <div class="form-floating">
{{ form.mobile_number }} {{ form.mobile_number }}
<label for="{{ form.mobile_number.id_for_label }}">{% trans "Mobile Number" %}</label> <label for="{{ form.mobile_number.id_for_label }}">رقم الهاتف</label>
</div> </div>
</div> </div>
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<div class="form-floating"> <div class="form-floating">
{{ form.governorate }} {{ form.governorate }}
<label for="{{ form.governorate.id_for_label }}">{% trans "Governorate" %}</label> <label for="{{ form.governorate.id_for_label }}">المحافظة</label>
</div> </div>
</div> </div>
<div class="col-md-12 mb-3"> <div class="col-md-12 mb-3">
<div class="form-floating"> <div class="form-floating">
{{ form.city }} {{ form.city }}
<label for="{{ form.city.id_for_label }}">{% trans "City" %}</label> <label for="{{ form.city.id_for_label }}">المدينة</label>
</div> </div>
</div> </div>
</div> </div>
@ -98,21 +98,21 @@
<!-- Section: Education --> <!-- Section: Education -->
<div class="card border-0 shadow-sm mb-4"> <div class="card border-0 shadow-sm mb-4">
<div class="card-body p-4"> <div class="card-body p-4">
<h5 class="card-title text-primary mb-3"><i class="fas fa-graduation-cap me-2"></i>{% trans "Education" %}</h5> <h5 class="card-title text-primary mb-3"><i class="fas fa-graduation-cap me-2"></i>التعليم</h5>
<div class="mb-3"> <div class="mb-3">
<label class="form-label text-muted small">{% trans "Select Classroom" %}</label> <label class="form-label text-muted small">اختر الصف الدراسي</label>
{{ form.classroom }} {{ form.classroom }}
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label text-muted small">{% trans "Select Subjects" %}</label> <label class="form-label text-muted small">اختر المواد</label>
<div id="subjects-container" class="border p-3 rounded bg-light" style="min-height: 100px;"> <div id="subjects-container" class="border p-3 rounded bg-light" style="min-height: 100px;">
<p class="text-muted text-center mt-3 small"><i class="fas fa-chalkboard me-1"></i> {% trans "Select a classroom first." %}</p> <p class="text-muted text-center mt-3 small"><i class="fas fa-chalkboard me-1"></i> اختر الصف أولاً.</p>
</div> </div>
</div> </div>
<div class="d-flex justify-content-between align-items-center p-3 bg-primary bg-opacity-10 text-primary rounded"> <div class="d-flex justify-content-between align-items-center p-3 bg-primary bg-opacity-10 text-primary rounded">
<h5 class="m-0">{% trans "Total Amount:" %}</h5> <h5 class="m-0">المبلغ الإجمالي:</h5>
<h4 class="m-0 fw-bold"><span id="total-amount">0.00</span></h4> <h4 class="m-0 fw-bold"><span id="total-amount">0.00</span></h4>
</div> </div>
</div> </div>
@ -121,35 +121,35 @@
<!-- Section: Photo --> <!-- Section: Photo -->
<div class="card border-0 shadow-sm mb-4"> <div class="card border-0 shadow-sm mb-4">
<div class="card-body p-4"> <div class="card-body p-4">
<h5 class="card-title text-primary mb-3"><i class="fas fa-camera me-2"></i>{% trans "Profile Picture" %}</h5> <h5 class="card-title text-primary mb-3"><i class="fas fa-camera me-2"></i>الصورة الشخصية</h5>
<div class="row"> <div class="row">
<div class="col-md-6 d-flex flex-column align-items-center mb-3 mb-md-0"> <div class="col-md-6 d-flex flex-column align-items-center mb-3 mb-md-0">
<label class="mb-2 text-muted small">{% trans "Webcam Capture" %}</label> <label class="mb-2 text-muted small">التقاط بالكاميرا</label>
<div id="video-container" style="position: relative;" class="shadow-sm rounded overflow-hidden"> <div id="video-container" style="position: relative;" class="shadow-sm rounded overflow-hidden">
<video id="video" width="240" height="180" autoplay style="background: #000;"></video> <video id="video" width="240" height="180" autoplay style="background: #000;"></video>
<button type="button" id="snap" class="btn btn-sm btn-light position-absolute bottom-0 start-50 translate-middle-x mb-2 rounded-pill px-3"> <button type="button" id="snap" class="btn btn-sm btn-light position-absolute bottom-0 start-50 translate-middle-x mb-2 rounded-pill px-3">
<i class="fas fa-camera"></i> {% trans "Snap" %} <i class="fas fa-camera"></i> التقاط
</button> </button>
</div> </div>
</div> </div>
<div class="col-md-6 d-flex flex-column align-items-center"> <div class="col-md-6 d-flex flex-column align-items-center">
<label class="mb-2 text-muted small">{% trans "Preview" %}</label> <label class="mb-2 text-muted small">معاينة</label>
<canvas id="canvas" width="240" height="180" style="display:none;"></canvas> <canvas id="canvas" width="240" height="180" style="display:none;"></canvas>
<div id="preview-placeholder" class="bg-light rounded d-flex align-items-center justify-content-center text-muted small" style="width: 240px; height: 180px; border: 2px dashed #ddd;"> <div id="preview-placeholder" class="bg-light rounded d-flex align-items-center justify-content-center text-muted small" style="width: 240px; height: 180px; border: 2px dashed #ddd;">
{% trans "No photo" %} لا توجد صورة
</div> </div>
<img id="photo-preview" width="240" height="180" class="rounded shadow-sm" style="display:none; border: 2px solid #4ECDC4;"> <img id="photo-preview" width="240" height="180" class="rounded shadow-sm" style="display:none; border: 2px solid #4ECDC4;">
</div> </div>
</div> </div>
<div class="mt-4"> <div class="mt-4">
<label class="form-label text-muted small">{% trans "Or upload file:" %}</label> <label class="form-label text-muted small">أو رفع ملف:</label>
{{ form.avatar }} {{ form.avatar }}
</div> </div>
</div> </div>
</div> </div>
<button type="submit" class="btn btn-primary w-100 py-3 fs-5 fw-bold shadow-sm rounded-pill"> <button type="submit" class="btn btn-primary w-100 py-3 fs-5 fw-bold shadow-sm rounded-pill">
{% trans "Complete Registration" %} <i class="fas fa-check-circle ms-2"></i> إتمام التسجيل <i class="fas fa-check-circle ms-2"></i>
</button> </button>
</form> </form>
</div> </div>
@ -169,7 +169,7 @@ document.addEventListener('DOMContentLoaded', function() {
classroomSelect.addEventListener('change', function() { classroomSelect.addEventListener('change', function() {
const classroomId = this.value; const classroomId = this.value;
if (!classroomId) { if (!classroomId) {
subjectsContainer.innerHTML = '<p class="text-muted text-center mt-3 small"><i class="fas fa-chalkboard me-1"></i> {% trans "Select a classroom first." %}</p>'; subjectsContainer.innerHTML = '<p class="text-muted text-center mt-3 small"><i class="fas fa-chalkboard me-1"></i> اختر الصف أولاً.</p>';
updateTotal(); updateTotal();
return; return;
} }
@ -179,7 +179,7 @@ document.addEventListener('DOMContentLoaded', function() {
.then(data => { .then(data => {
subjectsContainer.innerHTML = ''; subjectsContainer.innerHTML = '';
if (data.length === 0) { if (data.length === 0) {
subjectsContainer.innerHTML = '<p class="text-warning text-center small">{% trans "No subjects found for this classroom." %}</p>'; subjectsContainer.innerHTML = '<p class="text-warning text-center small">لا توجد مواد لهذا الصف.</p>';
} else { } else {
data.forEach(subject => { data.forEach(subject => {
const price = parseFloat(subject.price) || 0; const price = parseFloat(subject.price) || 0;
@ -188,7 +188,7 @@ document.addEventListener('DOMContentLoaded', function() {
div.innerHTML = ` div.innerHTML = `
<input class="form-check-input subject-checkbox" type="checkbox" name="subjects" value="${subject.id}" id="subject_${subject.id}" data-price="${price}" checked style="transform: scale(1.2); margin-top: 0.3rem;"> <input class="form-check-input subject-checkbox" type="checkbox" name="subjects" value="${subject.id}" id="subject_${subject.id}" data-price="${price}" checked style="transform: scale(1.2); margin-top: 0.3rem;">
<label class="form-check-label d-flex justify-content-between w-100 ms-2" for="subject_${subject.id}"> <label class="form-check-label d-flex justify-content-between w-100 ms-2" for="subject_${subject.id}">
<span class="fw-medium">${subject.name_en}</span> <span class="fw-medium">${subject.name_ar || subject.name_en}</span>
<span class="badge bg-primary rounded-pill align-self-center">${price.toFixed(2)}</span> <span class="badge bg-primary rounded-pill align-self-center">${price.toFixed(2)}</span>
</label> </label>
`; `;
@ -199,7 +199,7 @@ document.addEventListener('DOMContentLoaded', function() {
}) })
.catch(err => { .catch(err => {
console.error('Error fetching subjects:', err); console.error('Error fetching subjects:', err);
subjectsContainer.innerHTML = '<p class="text-danger small">{% trans "Error loading subjects." %}</p>'; subjectsContainer.innerHTML = '<p class="text-danger small">خطأ في تحميل المواد.</p>';
}); });
}); });
@ -224,7 +224,7 @@ document.addEventListener('DOMContentLoaded', function() {
if (governorateSelect && citySelect) { if (governorateSelect && citySelect) {
governorateSelect.addEventListener('change', function() { governorateSelect.addEventListener('change', function() {
const governorateId = this.value; const governorateId = this.value;
citySelect.innerHTML = '<option value="">{% trans "Loading..." %}</option>'; citySelect.innerHTML = '<option value="">جاري التحميل...</option>';
if (!governorateId) { if (!governorateId) {
citySelect.innerHTML = '<option value="">---------</option>'; citySelect.innerHTML = '<option value="">---------</option>';
@ -235,17 +235,16 @@ document.addEventListener('DOMContentLoaded', function() {
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
citySelect.innerHTML = '<option value="">---------</option>'; citySelect.innerHTML = '<option value="">---------</option>';
const currentLang = "{{ LANGUAGE_CODE }}";
data.forEach(city => { data.forEach(city => {
const option = document.createElement('option'); const option = document.createElement('option');
option.value = city.id; option.value = city.id;
option.textContent = currentLang === 'ar' ? city.name_ar : city.name_en; option.textContent = city.name_ar || city.name_en; // Prefer Arabic
citySelect.appendChild(option); citySelect.appendChild(option);
}); });
}) })
.catch(err => { .catch(err => {
console.error('Error fetching cities:', err); console.error('Error fetching cities:', err);
citySelect.innerHTML = '<option value="">{% trans "Error loading cities" %}</option>'; citySelect.innerHTML = '<option value="">خطأ في تحميل المدن</option>';
}); });
}); });
} }
@ -275,10 +274,10 @@ document.addEventListener('DOMContentLoaded', function() {
video.play(); video.play();
}) })
.catch(function(err) { .catch(function(err) {
showCameraError("{% trans 'Camera access denied or unavailable.' %}"); showCameraError("تم رفض الوصول للكاميرا أو غير متاحة.");
}); });
} else { } else {
showCameraError("{% trans 'Browser does not support camera.' %}"); showCameraError("المتصفح لا يدعم الكاميرا.");
} }
if (snap) { if (snap) {

View File

@ -1,15 +1,15 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load i18n static %} {% load i18n static %}
{% block title %}{% trans "Student Dashboard" %} - EduPlatform{% endblock %} {% block title %}لوحة تحكم الطالب - EduPlatform{% endblock %}
{% block content %} {% block content %}
<div class="container" style="margin-top: 100px; margin-bottom: 50px;"> <div class="container" style="margin-top: 100px; margin-bottom: 50px;">
<!-- Welcome Header --> <!-- Welcome Header -->
<div class="row mb-4"> <div class="row mb-4">
<div class="col-12"> <div class="col-12">
<h2 class="fw-bold">{% trans "Welcome back," %} <span style="color: var(--primary-color);">{{ user.first_name|default:user.username }}</span>! 👋</h2> <h2 class="fw-bold">مرحباً بعودتك، <span style="color: var(--primary-color);">{{ user.first_name|default:user.username }}</span>! 👋</h2>
<p class="text-muted">{% trans "Here is an overview of your learning journey." %}</p> <p class="text-muted">إليك نظرة عامة على رحلتك التعليمية.</p>
</div> </div>
</div> </div>
@ -28,43 +28,43 @@
{% if student_profile.is_mobile_verified and student_profile.is_email_verified %} {% if student_profile.is_mobile_verified and student_profile.is_email_verified %}
<span class="position-absolute bottom-0 end-0 bg-success border border-white rounded-circle p-2" title="Verified Account"> <span class="position-absolute bottom-0 end-0 bg-success border border-white rounded-circle p-2" title="Verified Account">
<span class="visually-hidden">Verified</span> <span class="visually-hidden">مؤكد</span>
</span> </span>
{% endif %} {% endif %}
</div> </div>
<h4 class="fw-bold mb-1">{{ user.get_full_name|default:user.username }}</h4> <h4 class="fw-bold mb-1">{{ user.get_full_name|default:user.username }}</h4>
<p class="text-muted mb-3">{% trans "Student" %} {% if student_profile.classroom %} • {{ student_profile.classroom.name_en }}{% endif %}</p> <p class="text-muted mb-3">طالب {% if student_profile.classroom %} • {{ student_profile.classroom.name_ar }}{% endif %}</p>
<hr class="my-4"> <hr class="my-4">
<div class="text-start"> <div class="text-start">
<div class="mb-3"> <div class="mb-3">
<small class="text-muted d-block">{% trans "Email" %}</small> <small class="text-muted d-block">البريد الإلكتروني</small>
<span>{{ user.email }}</span> <span>{{ user.email }}</span>
{% if student_profile.is_email_verified %} {% if student_profile.is_email_verified %}
<span class="badge bg-success ms-1">{% trans "Verified" %}</span> <span class="badge bg-success ms-1">مؤكد</span>
{% else %} {% else %}
<span class="badge bg-warning text-dark ms-1">{% trans "Unverified" %}</span> <span class="badge bg-warning text-dark ms-1">غير مؤكد</span>
{% endif %} {% endif %}
</div> </div>
<div class="mb-3"> <div class="mb-3">
<small class="text-muted d-block">{% trans "Phone" %}</small> <small class="text-muted d-block">رقم الهاتف</small>
<span>{{ student_profile.mobile_number|default:"-" }}</span> <span>{{ student_profile.mobile_number|default:"-" }}</span>
{% if student_profile.is_mobile_verified %} {% if student_profile.is_mobile_verified %}
<span class="badge bg-success ms-1">{% trans "Verified" %}</span> <span class="badge bg-success ms-1">مؤكد</span>
{% else %} {% else %}
<span class="badge bg-warning text-dark ms-1">{% trans "Unverified" %}</span> <span class="badge bg-warning text-dark ms-1">غير مؤكد</span>
{% endif %} {% endif %}
</div> </div>
<div class="mb-3"> <div class="mb-3">
<small class="text-muted d-block">{% trans "City" %}</small> <small class="text-muted d-block">المدينة</small>
<span>{{ student_profile.city.name|default:"-" }}</span> <span>{{ student_profile.city.name_ar|default:"-" }}</span>
</div> </div>
</div> </div>
<div class="mt-4"> <div class="mt-4">
<a href="#" class="btn btn-outline-primary w-100 rounded-pill">{% trans "Edit Profile" %}</a> <a href="#" class="btn btn-outline-primary w-100 rounded-pill">تعديل الملف الشخصي</a>
</div> </div>
</div> </div>
</div> </div>
@ -76,7 +76,7 @@
<div class="col-md-6 mb-3 mb-md-0"> <div class="col-md-6 mb-3 mb-md-0">
<div class="glass-card py-3 px-4 d-flex align-items-center justify-content-between"> <div class="glass-card py-3 px-4 d-flex align-items-center justify-content-between">
<div> <div>
<h6 class="text-muted mb-0">{% trans "Enrolled Subjects" %}</h6> <h6 class="text-muted mb-0">المواد المسجلة</h6>
<h2 class="fw-bold mb-0 text-primary">{{ subscribed_subjects.count }}</h2> <h2 class="fw-bold mb-0 text-primary">{{ subscribed_subjects.count }}</h2>
</div> </div>
<div class="bg-primary bg-opacity-10 p-3 rounded-circle text-primary"> <div class="bg-primary bg-opacity-10 p-3 rounded-circle text-primary">
@ -89,8 +89,8 @@
<div class="col-md-6"> <div class="col-md-6">
<div class="glass-card py-3 px-4 d-flex align-items-center justify-content-between"> <div class="glass-card py-3 px-4 d-flex align-items-center justify-content-between">
<div> <div>
<h6 class="text-muted mb-0">{% trans "Classroom" %}</h6> <h6 class="text-muted mb-0">الصف الدراسي</h6>
<h5 class="fw-bold mb-0 text-success">{{ student_profile.classroom.name_en|default:"Not assigned" }}</h5> <h5 class="fw-bold mb-0 text-success">{{ student_profile.classroom.name_ar|default:"غير معين" }}</h5>
</div> </div>
<div class="bg-success bg-opacity-10 p-3 rounded-circle text-success"> <div class="bg-success bg-opacity-10 p-3 rounded-circle text-success">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-mortarboard" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-mortarboard" viewBox="0 0 16 16">
@ -104,7 +104,7 @@
<!-- My Subjects Section --> <!-- My Subjects Section -->
<div class="mb-4"> <div class="mb-4">
<h4 class="fw-bold mb-3">{% trans "My Subjects" %}</h4> <h4 class="fw-bold mb-3">موادي</h4>
{% if subscribed_subjects %} {% if subscribed_subjects %}
<div class="row g-4"> <div class="row g-4">
{% for subject in subscribed_subjects %} {% for subject in subscribed_subjects %}
@ -116,27 +116,27 @@
</div> </div>
{% else %} {% else %}
<div class="bg-light d-flex align-items-center justify-content-center" style="height: 150px;"> <div class="bg-light d-flex align-items-center justify-content-center" style="height: 150px;">
<span class="text-muted">{% trans "No Image" %}</span> <span class="text-muted">لا توجد صورة</span>
</div> </div>
{% endif %} {% endif %}
<div class="p-3"> <div class="p-3">
<h5 class="fw-bold mb-2">{{ subject.name_en }}</h5> <h5 class="fw-bold mb-2">{{ subject.name_ar }}</h5>
<p class="text-muted small mb-3 text-truncate">{{ subject.description_en }}</p> <p class="text-muted small mb-3 text-truncate">{{ subject.description_ar }}</p>
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<span class="badge bg-primary bg-opacity-10 text-primary"> <span class="badge bg-primary bg-opacity-10 text-primary">
{% for teacher in subject.teachers.all %} {% for teacher in subject.teachers.all %}
{{ teacher.user.get_full_name|default:teacher.user.username }}{% if not forloop.last %}, {% endif %} {{ teacher.user.get_full_name|default:teacher.user.username }}{% if not forloop.last %}, {% endif %}
{% empty %} {% empty %}
{% trans "No Teacher" %} بدون معلم
{% endfor %} {% endfor %}
</span> </span>
<div> <div>
<a href="{% url 'live_classroom' subject.id %}" class="btn btn-sm btn-danger rounded-pill me-1" title="{% trans 'Join Live Class' %}"> <a href="{% url 'live_classroom' subject.id %}" class="btn btn-sm btn-danger rounded-pill me-1" title="انضم للدرس المباشر">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-camera-video-fill" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-camera-video-fill" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M0 5a2 2 0 0 1 2-2h7.5a2 2 0 0 1 1.983 1.738l3.11-1.382A1 1 0 0 1 16 4.269v7.461a1 1 0 0 1-1.406.913l-3.111-1.382A2 2 0 0 1 9.5 13H2a2 2 0 0 1-2-2V5z"/> <path fill-rule="evenodd" d="M0 5a2 2 0 0 1 2-2h7.5a2 2 0 0 1 1.983 1.738l3.11-1.382A1 1 0 0 1 16 4.269v7.461a1 1 0 0 1-1.406.913l-3.111-1.382A2 2 0 0 1 9.5 13H2a2 2 0 0 1-2-2V5z"/>
</svg> </svg>
</a> </a>
<a href="{% url 'subject_detail' subject.id %}" class="btn btn-sm btn-primary rounded-pill">{% trans "Go to Class" %}</a> <a href="{% url 'subject_detail' subject.id %}" class="btn btn-sm btn-primary rounded-pill">الذهاب للصف</a>
</div> </div>
</div> </div>
</div> </div>
@ -146,14 +146,14 @@
</div> </div>
{% else %} {% else %}
<div class="glass-card text-center py-5"> <div class="glass-card text-center py-5">
<h5 class="text-muted">{% trans "You haven't enrolled in any subjects yet." %}</h5> <h5 class="text-muted">لم تسجل في أي مادة بعد.</h5>
</div> </div>
{% endif %} {% endif %}
</div> </div>
<!-- Available Subjects Section --> <!-- Available Subjects Section -->
<div class="mb-4"> <div class="mb-4">
<h4 class="fw-bold mb-3">{% trans "Available Subjects" %}</h4> <h4 class="fw-bold mb-3">المواد المتاحة</h4>
{% if available_subjects %} {% if available_subjects %}
<div class="row g-4"> <div class="row g-4">
{% for subject in available_subjects %} {% for subject in available_subjects %}
@ -165,15 +165,15 @@
</div> </div>
{% else %} {% else %}
<div class="bg-light d-flex align-items-center justify-content-center" style="height: 150px;"> <div class="bg-light d-flex align-items-center justify-content-center" style="height: 150px;">
<span class="text-muted">{% trans "No Image" %}</span> <span class="text-muted">لا توجد صورة</span>
</div> </div>
{% endif %} {% endif %}
<div class="p-3"> <div class="p-3">
<h5 class="fw-bold mb-2">{{ subject.name_en }}</h5> <h5 class="fw-bold mb-2">{{ subject.name_ar }}</h5>
<p class="text-muted small mb-3 text-truncate">{{ subject.description_en }}</p> <p class="text-muted small mb-3 text-truncate">{{ subject.description_ar }}</p>
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<span class="fw-bold text-success">{{ subject.price }} OMR</span> <span class="fw-bold text-success">{{ subject.price }} OMR</span>
<a href="{% url 'subscribe_subject' subject.id %}" class="btn btn-sm btn-success rounded-pill">{% trans "Subscribe" %}</a> <a href="{% url 'subscribe_subject' subject.id %}" class="btn btn-sm btn-success rounded-pill">اشتراك</a>
</div> </div>
</div> </div>
</div> </div>
@ -182,7 +182,7 @@
</div> </div>
{% else %} {% else %}
<div class="glass-card text-center py-5"> <div class="glass-card text-center py-5">
<h5 class="text-muted">{% trans "No new subjects available." %}</h5> <h5 class="text-muted">لا توجد مواد جديدة متاحة.</h5>
</div> </div>
{% endif %} {% endif %}
</div> </div>

View File

@ -2,19 +2,29 @@
{% load i18n static %} {% load i18n static %}
{% block title %} {% block title %}
{% if LANGUAGE_CODE == 'ar' %}{{ subject.name_ar }}{% else %}{{ subject.name_en }}{% endif %} | EduPlatform {{ subject.name_ar }} | EduPlatform
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="container py-5"> <div class="container py-5">
<nav aria-label="breadcrumb" class="mb-4"> <div class="d-flex justify-content-between align-items-center mb-4">
<ol class="breadcrumb"> <nav aria-label="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'index' %}">{% trans "Home" %}</a></li> <ol class="breadcrumb mb-0">
<li class="breadcrumb-item active" aria-current="page"> <li class="breadcrumb-item"><a href="{% url 'index' %}">الرئيسية</a></li>
{% if LANGUAGE_CODE == 'ar' %}{{ subject.classroom.name_ar }}{% else %}{{ subject.classroom.name_en }}{% endif %} <li class="breadcrumb-item active" aria-current="page">
</li> {{ subject.classroom.name_ar }}
</ol> </li>
</nav> </ol>
</nav>
{% if is_teacher %}
<a href="{% url 'profile' %}" class="btn btn-outline-secondary rounded-pill">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-right me-2" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8z"/>
</svg>
العودة للوحة التحكم
</a>
{% endif %}
</div>
<div class="row g-5"> <div class="row g-5">
<div class="col-lg-8"> <div class="col-lg-8">
@ -27,13 +37,13 @@
{% endif %} {% endif %}
<div class="position-absolute top-0 end-0 m-4"> <div class="position-absolute top-0 end-0 m-4">
<span class="badge bg-primary fs-6 px-3 py-2 shadow-sm"> <span class="badge bg-primary fs-6 px-3 py-2 shadow-sm">
{% if LANGUAGE_CODE == 'ar' %}{{ subject.classroom.name_ar }}{% else %}{{ subject.classroom.name_en }}{% endif %} {{ subject.classroom.name_ar }}
</span> </span>
</div> </div>
</div> </div>
<div class="p-4 p-md-5"> <div class="p-4 p-md-5">
<h1 class="fw-bold mb-4"> <h1 class="fw-bold mb-4">
{% if LANGUAGE_CODE == 'ar' %}{{ subject.name_ar }}{% else %}{{ subject.name_en }}{% endif %} {{ subject.name_ar }}
</h1> </h1>
{% for teacher in subject.teachers.all %} {% for teacher in subject.teachers.all %}
@ -42,33 +52,71 @@
{{ teacher.user.username|first|upper }} {{ teacher.user.username|first|upper }}
</div> </div>
<div> <div>
<p class="mb-0 text-muted small">{% trans "Teacher" %}</p> <p class="mb-0 text-muted small">المعلم</p>
<h6 class="fw-bold mb-0">{{ teacher }}</h6> <h6 class="fw-bold mb-0">{{ teacher }}</h6>
</div> </div>
</div> </div>
{% empty %} {% empty %}
<div class="d-flex align-items-center mb-3 p-3 rounded-3 bg-light"> <div class="d-flex align-items-center mb-3 p-3 rounded-3 bg-light">
<div> <div>
<p class="mb-0 text-muted small">{% trans "Teacher" %}</p> <p class="mb-0 text-muted small">المعلم</p>
<h6 class="fw-bold mb-0">{% trans "Not Assigned" %}</h6> <h6 class="fw-bold mb-0">غير معين</h6>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
<h4 class="fw-bold mb-3 mt-4">{% trans "About this Subject" %}</h4> {% if subject.youtube_live_url %}
<div class="lead text-muted mb-5"> <div class="mt-5 mb-5">
{% if LANGUAGE_CODE == 'ar' %} <div class="ratio ratio-16x9 rounded-4 overflow-hidden shadow-sm">
{{ subject.description_ar|linebreaks }} <iframe src="https://www.youtube.com/embed/{{ subject.get_youtube_id }}"
{% else %} title="YouTube video player"
{{ subject.description_en|linebreaks }} frameborder="0"
{% endif %} allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen>
</iframe>
</div>
</div> </div>
{% endif %}
<h4 class="fw-bold mb-3 mt-4">عن هذه المادة</h4>
<div class="lead text-muted mb-5">
{{ subject.description_ar|linebreaks }}
</div>
<!-- Google Drive Link -->
{% if subject.google_drive_link %}
<div class="mb-5">
<a href="{{ subject.google_drive_link }}" target="_blank" class="btn btn-outline-success btn-lg w-100 p-4 rounded-4 d-flex align-items-center justify-content-between hover-shadow transition-all">
<div class="d-flex align-items-center">
<div class="me-3 display-6">📂</div>
<div class="text-start">
<h5 class="fw-bold mb-1">مجلد المادة على Drive</h5>
<p class="mb-0 small text-muted">الوصول للملفات والمستندات المشتركة</p>
</div>
</div>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-box-arrow-up-right" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8.636 3.5a.5.5 0 0 0-.5-.5H1.5A1.5 1.5 0 0 0 0 4.5v10A1.5 1.5 0 0 0 1.5 16h10a1.5 1.5 0 0 0 1.5-1.5V7.864a.5.5 0 0 0-1 0V14.5a.5.5 0 0 1-.5.5h-10a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5h6.636a.5.5 0 0 0 .5-.5z"/>
<path fill-rule="evenodd" d="M16 .5a.5.5 0 0 0-.5-.5h-5a.5.5 0 0 0 0 1h3.793L6.146 9.146a.5.5 0 1 0 .708.708L15 1.707V5.5a.5.5 0 0 0 1 0v-5z"/>
</svg>
</a>
</div>
{% endif %}
<!-- Resources Section --> <!-- Resources Section -->
<div class="mb-5"> <div class="mb-5">
<div class="d-flex align-items-center mb-4"> <div class="d-flex align-items-center justify-content-between mb-4">
<h4 class="fw-bold mb-0">{% trans "Course Resources" %}</h4> <div class="d-flex align-items-center">
<span class="badge bg-light text-primary ms-3 border rounded-pill px-3">{{ subject.resources.count }}</span> <h4 class="fw-bold mb-0">مصادر الدورة</h4>
<span class="badge bg-light text-primary ms-3 border rounded-pill px-3">{{ subject.resources.count }}</span>
</div>
{% if is_teacher %}
<a href="{% url 'add_resource' subject.id %}" class="btn btn-success rounded-pill">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-lg me-2" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2z"/>
</svg>
إضافة مصدر
</a>
{% endif %}
</div> </div>
{% if subject.resources.all %} {% if subject.resources.all %}
@ -97,28 +145,40 @@
</div> </div>
<div> <div>
<h6 class="mb-0 fw-semibold"> <h6 class="mb-0 fw-semibold">
{% if LANGUAGE_CODE == 'ar' %}{{ resource.title_ar }}{% else %}{{ resource.title_en }}{% endif %} {{ resource.title_ar }}
</h6> </h6>
<small class="text-muted"> <small class="text-muted">
{{ resource.get_resource_type_display }} {{ resource.get_resource_type_display }}
</small> </small>
</div> </div>
</div> </div>
<button class="btn btn-sm btn-outline-primary rounded-pill px-3 view-resource-btn" <div class="d-flex align-items-center gap-2">
data-type="{{ resource.resource_type }}" {% if is_teacher %}
data-title="{% if LANGUAGE_CODE == 'ar' %}{{ resource.title_ar }}{% else %}{{ resource.title_en }}{% endif %}" <a href="{% url 'edit_resource' resource.id %}" class="btn btn-sm btn-outline-secondary rounded-pill">تعديل</a>
data-file-url="{% if resource.file %}{{ resource.file.url }}{% endif %}" <a href="{% url 'delete_resource' resource.id %}" class="btn btn-sm btn-outline-danger rounded-pill">حذف</a>
data-link="{% if resource.link %}{{ resource.link }}{% endif %}" {% endif %}
data-youtube-id="{{ resource.get_youtube_id|default:'' }}"> <button class="btn btn-sm btn-outline-primary rounded-pill px-3 view-resource-btn"
{% trans "View" %} data-type="{{ resource.resource_type }}"
</button> data-title="{{ resource.title_ar }}"
data-file-url="{% if resource.file %}{{ resource.file.url }}{% endif %}"
data-view-url="{% url 'view_resource' resource.id %}"
data-link="{% if resource.link %}{{ resource.link }}{% endif %}"
data-youtube-id="{{ resource.get_youtube_id|default:'' }}">
عرض
</button>
</div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% else %} {% else %}
<div class="text-center p-5 bg-light rounded-4"> <div class="text-center p-5 bg-light rounded-4">
<div class="display-6 mb-3">📂</div> <div class="display-6 mb-3">📂</div>
<h6 class="text-muted">{% trans "No resources available yet." %}</h6> <h6 class="text-muted">لا توجد مصادر متاحة بعد.</h6>
{% if is_teacher %}
<a href="{% url 'add_resource' subject.id %}" class="btn btn-primary rounded-pill mt-3">
إضافة أول مصدر
</a>
{% endif %}
</div> </div>
{% endif %} {% endif %}
</div> </div>
@ -135,21 +195,21 @@
<ul class="list-unstyled mb-4"> <ul class="list-unstyled mb-4">
<li class="mb-3 d-flex align-items-center"> <li class="mb-3 d-flex align-items-center">
<span class="text-success me-2"></span> {% trans "Full lifetime access" %} <span class="text-success me-2"></span> وصول كامل مدى الحياة
</li> </li>
<li class="mb-3 d-flex align-items-center"> <li class="mb-3 d-flex align-items-center">
<span class="text-success me-2"></span> {% trans "Access on mobile and desktop" %} <span class="text-success me-2"></span> دخول من الهاتف والكمبيوتر
</li> </li>
<li class="mb-3 d-flex align-items-center"> <li class="mb-3 d-flex align-items-center">
<span class="text-success me-2"></span> {% trans "Certificate of completion" %} <span class="text-success me-2"></span> شهادة إتمام
</li> </li>
</ul> </ul>
<button class="btn btn-primary btn-lg w-100 py-3 fw-bold mb-3 shadow-sm"> <button class="btn btn-primary btn-lg w-100 py-3 fw-bold mb-3 shadow-sm">
{% trans "Enroll Now" %} اشترك الآن
</button> </button>
<p class="text-center text-muted small mb-0"> <p class="text-center text-muted small mb-0">
{% trans "30-Day Money-Back Guarantee" %} ضمان استرداد الأموال خلال 30 يوم
</p> </p>
</div> </div>
</div> </div>
@ -160,20 +220,15 @@
<!-- Resource Modal --> <!-- Resource Modal -->
<div class="modal fade" id="resourceModal" tabindex="-1" aria-hidden="true"> <div class="modal fade" id="resourceModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered"> <div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content rounded-4 border-0 shadow-lg"> <div class="modal-content rounded-4 border-0 shadow-lg" style="height: 90vh;">
<div class="modal-header border-0 pb-0"> <div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold" id="resourceModalTitle">{% trans "Resource" %}</h5> <h5 class="modal-title fw-bold" id="resourceModalTitle">المصدر</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body p-4" id="resourceModalBody"> <div class="modal-body p-0" id="resourceModalBody" style="background-color: #f8f9fa;">
<!-- Content injected via JS --> <!-- Content injected via JS -->
</div> </div>
<div class="modal-footer border-0 pt-0">
<a href="#" target="_blank" id="openNewTabBtn" class="btn btn-primary rounded-pill">
{% trans "Open in New Tab" %} <i class="bi bi-box-arrow-up-right ms-2"></i>
</a>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -186,59 +241,72 @@ document.addEventListener('DOMContentLoaded', function() {
const resourceModal = new bootstrap.Modal(document.getElementById('resourceModal')); const resourceModal = new bootstrap.Modal(document.getElementById('resourceModal'));
const modalTitle = document.getElementById('resourceModalTitle'); const modalTitle = document.getElementById('resourceModalTitle');
const modalBody = document.getElementById('resourceModalBody'); const modalBody = document.getElementById('resourceModalBody');
const openNewTabBtn = document.getElementById('openNewTabBtn');
document.querySelectorAll('.view-resource-btn').forEach(btn => { document.querySelectorAll('.view-resource-btn').forEach(btn => {
btn.addEventListener('click', function() { btn.addEventListener('click', function() {
const type = this.dataset.type; const type = this.dataset.type;
const title = this.dataset.title; const title = this.dataset.title;
const fileUrl = this.dataset.fileUrl; const fileUrl = this.dataset.fileUrl;
const viewUrl = this.dataset.viewUrl;
const link = this.dataset.link; const link = this.dataset.link;
const youtubeId = this.dataset.youtubeId; const youtubeId = this.dataset.youtubeId;
modalTitle.textContent = title; modalTitle.textContent = title;
openNewTabBtn.style.display = 'inline-block'; // Default to showing it modalBody.innerHTML = ''; // Clear previous content
if (type === 'VIDEO' && youtubeId) { if (type === 'VIDEO' && youtubeId) {
modalBody.innerHTML = ` modalBody.innerHTML = `
<div class="ratio ratio-16x9"> <div class="ratio ratio-16x9 h-100">
<iframe src="https://www.youtube.com/embed/${youtubeId}?autoplay=1" title="${title}" allowfullscreen></iframe> <iframe src="https://www.youtube.com/embed/${youtubeId}?autoplay=1" title="${title}" allowfullscreen></iframe>
</div> </div>
`; `;
openNewTabBtn.href = link;
} else if (type === 'FILE' && fileUrl) { } else if (type === 'FILE' && fileUrl) {
// Check if it's an image or PDF for preview // Check if it's an image or PDF for preview
const ext = fileUrl.split('.').pop().toLowerCase(); const ext = fileUrl.split('.').pop().toLowerCase();
// Use viewUrl for fetching content (permission checked)
const secureUrl = viewUrl;
if (['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext)) { if (['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(ext)) {
modalBody.innerHTML = `<img src="${fileUrl}" class="img-fluid rounded" alt="${title}">`;
} else if (ext === 'pdf') {
modalBody.innerHTML = ` modalBody.innerHTML = `
<div class="ratio ratio-4x3"> <div class="d-flex align-items-center justify-content-center h-100 p-4">
<iframe src="${fileUrl}" title="${title}"></iframe> <img src="${secureUrl}" class="img-fluid rounded shadow" alt="${title}" style="max-height: 100%;">
</div>
`;
} else if (ext === 'pdf') {
// Use iframe for better PDF embedding
modalBody.innerHTML = `
<div class="w-100 h-100" style="min-height: 80vh;">
<iframe src="${secureUrl}" style="width: 100%; height: 100%; border: none;">
<div class="d-flex flex-column align-items-center justify-content-center h-100 p-5 text-center">
<p class="text-muted mb-4">متصفحك لا يدعم عرض ملفات PDF داخل الصفحة.</p>
<a href="${secureUrl}" class="btn btn-primary rounded-pill px-5 py-3" download>
<i class="bi bi-download me-2"></i> تحميل الملف
</a>
</div>
</iframe>
</div> </div>
`; `;
} else { } else {
// Other files: no preview, just download button // Other files: no preview, just download button
modalBody.innerHTML = ` modalBody.innerHTML = `
<div class="text-center py-5"> <div class="d-flex flex-column align-items-center justify-content-center h-100">
<div class="mb-3 display-4">📄</div> <div class="mb-3 display-4">📄</div>
<p class="mb-3">{% trans "This file type cannot be previewed directly." %}</p> <p class="mb-3">لا يمكن معاينة هذا النوع من الملفات مباشرة.</p>
<a href="${fileUrl}" class="btn btn-primary rounded-pill" download>{% trans "Download File" %}</a> <a href="${secureUrl}" class="btn btn-primary rounded-pill px-4" download>
<i class="bi bi-download me-2"></i> تحميل الملف
</a>
</div> </div>
`; `;
openNewTabBtn.style.display = 'none'; // Use the internal download button instead
} }
openNewTabBtn.href = fileUrl;
} else if (type === 'LINK' && link) { } else if (type === 'LINK' && link) {
// Embed link in iframe
modalBody.innerHTML = ` modalBody.innerHTML = `
<div class="text-center py-5"> <div class="w-100 h-100">
<div class="mb-3 display-4">🔗</div> <iframe src="${link}" title="${title}" style="width: 100%; height: 100%; border: none;" allowfullscreen sandbox="allow-scripts allow-same-origin allow-forms allow-popups"></iframe>
<p class="mb-3">{% trans "You are about to visit an external link." %}</p>
<p class="text-primary fw-bold text-break">${link}</p>
</div> </div>
`; `;
openNewTabBtn.href = link;
} }
resourceModal.show(); resourceModal.show();

View File

@ -1,15 +1,15 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load i18n static %} {% load i18n static %}
{% block title %}{% trans "Teacher Dashboard" %} - EduPlatform{% endblock %} {% block title %}لوحة تحكم المعلم - EduPlatform{% endblock %}
{% block content %} {% block content %}
<div class="container" style="margin-top: 100px; margin-bottom: 50px;"> <div class="container" style="margin-top: 100px; margin-bottom: 50px;">
<!-- Welcome Header --> <!-- Welcome Header -->
<div class="row mb-4"> <div class="row mb-4">
<div class="col-12"> <div class="col-12">
<h2 class="fw-bold">{% trans "Welcome, Teacher" %} <span style="color: var(--primary-color);">{{ user.last_name|default:user.username }}</span>! 👨‍🏫</h2> <h2 class="fw-bold">مرحباً، أستاذ <span style="color: var(--primary-color);">{{ user.last_name|default:user.username }}</span>! 👨‍🏫</h2>
<p class="text-muted">{% trans "Manage your classes and students here." %}</p> <p class="text-muted">أدر صفوفك وطلابك هنا.</p>
</div> </div>
</div> </div>
@ -28,19 +28,26 @@
</div> </div>
<h4 class="fw-bold mb-1">{{ user.get_full_name|default:user.username }}</h4> <h4 class="fw-bold mb-1">{{ user.get_full_name|default:user.username }}</h4>
<p class="text-muted mb-2">{{ teacher_profile.specialization|default:"Teacher" }}</p> <p class="text-muted mb-2">{{ teacher_profile.specialization|default:"معلم" }}</p>
<hr class="my-4"> <hr class="my-4">
<div class="text-start"> <div class="text-start">
<h6 class="fw-bold mb-2">{% trans "Bio" %}</h6> <h6 class="fw-bold mb-2">نبذة</h6>
<p class="text-muted small"> <p class="text-muted small">
{{ teacher_profile.bio|default:"No bio provided yet." }} {{ teacher_profile.bio|default:"لم يتم تقديم نبذة بعد." }}
</p> </p>
</div> </div>
<div class="mt-4"> <div class="mt-4">
<a href="#" class="btn btn-outline-primary w-100 rounded-pill">{% trans "Edit Profile" %}</a> <a href="{% url 'edit_teacher_profile' %}" class="btn btn-outline-primary w-100 rounded-pill">تعديل الملف الشخصي</a>
<a href="https://drive.google.com" target="_blank" class="btn btn-outline-success w-100 rounded-pill mt-2">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-hdd-network me-2" viewBox="0 0 16 16">
<path d="M4.5 5a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1zM3 4.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0z"/>
<path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v1a2 2 0 0 1-2 2H8.5v3a1.5 1.5 0 0 1 1.5 1.5h5.5a.5.5 0 0 1 0 1H10A1.5 1.5 0 0 1 8.5 14h-1A1.5 1.5 0 0 1 6 12.5H.5a.5.5 0 0 1 0-1H6A1.5 1.5 0 0 1 7.5 10V7H2a2 2 0 0 1-2-2V4zm1 0v1a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1zm6 7.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5z"/>
</svg>
ملفاتي في Google Drive
</a>
</div> </div>
</div> </div>
</div> </div>
@ -52,7 +59,7 @@
<div class="col-md-6 mb-3 mb-md-0"> <div class="col-md-6 mb-3 mb-md-0">
<div class="glass-card py-3 px-4 d-flex align-items-center justify-content-between"> <div class="glass-card py-3 px-4 d-flex align-items-center justify-content-between">
<div> <div>
<h6 class="text-muted mb-0">{% trans "Subjects Taught" %}</h6> <h6 class="text-muted mb-0">المواد التي تدرسها</h6>
<h2 class="fw-bold mb-0 text-primary">{{ subjects.count }}</h2> <h2 class="fw-bold mb-0 text-primary">{{ subjects.count }}</h2>
</div> </div>
<div class="bg-primary bg-opacity-10 p-3 rounded-circle text-primary"> <div class="bg-primary bg-opacity-10 p-3 rounded-circle text-primary">
@ -66,8 +73,8 @@
<div class="col-md-6"> <div class="col-md-6">
<div class="glass-card py-3 px-4 d-flex align-items-center justify-content-between"> <div class="glass-card py-3 px-4 d-flex align-items-center justify-content-between">
<div> <div>
<h6 class="text-muted mb-0">{% trans "Status" %}</h6> <h6 class="text-muted mb-0">الحالة</h6>
<h5 class="fw-bold mb-0 text-success">Active</h5> <h5 class="fw-bold mb-0 text-success">نشط</h5>
</div> </div>
<div class="bg-success bg-opacity-10 p-3 rounded-circle text-success"> <div class="bg-success bg-opacity-10 p-3 rounded-circle text-success">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-person-badge" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-person-badge" viewBox="0 0 16 16">
@ -81,7 +88,7 @@
<!-- My Subjects Section --> <!-- My Subjects Section -->
<div class="mb-4"> <div class="mb-4">
<h4 class="fw-bold mb-3">{% trans "My Classes" %}</h4> <h4 class="fw-bold mb-3">صفوفي</h4>
{% if subjects %} {% if subjects %}
<div class="row g-4"> <div class="row g-4">
{% for subject in subjects %} {% for subject in subjects %}
@ -93,18 +100,18 @@
</div> </div>
{% else %} {% else %}
<div class="bg-light d-flex align-items-center justify-content-center" style="height: 150px;"> <div class="bg-light d-flex align-items-center justify-content-center" style="height: 150px;">
<span class="text-muted">{% trans "No Image" %}</span> <span class="text-muted">لا توجد صورة</span>
</div> </div>
{% endif %} {% endif %}
<div class="p-3"> <div class="p-3">
<h5 class="fw-bold mb-2">{{ subject.name_en }}</h5> <h5 class="fw-bold mb-2">{{ subject.name_ar }}</h5>
<p class="text-muted small mb-2 text-truncate">{{ subject.description_en }}</p> <p class="text-muted small mb-2 text-truncate">{{ subject.description_ar }}</p>
<div class="d-flex justify-content-between align-items-center mb-2"> <div class="d-flex justify-content-between align-items-center mb-2">
<span class="badge bg-info bg-opacity-10 text-info"> <span class="badge bg-info bg-opacity-10 text-info">
{{ subject.classroom.name_en }} {{ subject.classroom.name_ar }}
</span> </span>
<span class="text-muted small"> <span class="text-muted small">
{{ subject.subscribers.count }} {% trans "Students" %} {{ subject.subscribers.count }} طلاب
</span> </span>
</div> </div>
<div class="mt-3 d-grid gap-2"> <div class="mt-3 d-grid gap-2">
@ -112,9 +119,16 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-camera-video-fill me-2" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-camera-video-fill me-2" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M0 5a2 2 0 0 1 2-2h7.5a2 2 0 0 1 1.983 1.738l3.11-1.382A1 1 0 0 1 16 4.269v7.461a1 1 0 0 1-1.406.913l-3.111-1.382A2 2 0 0 1 9.5 13H2a2 2 0 0 1-2-2V5z"/> <path fill-rule="evenodd" d="M0 5a2 2 0 0 1 2-2h7.5a2 2 0 0 1 1.983 1.738l3.11-1.382A1 1 0 0 1 16 4.269v7.461a1 1 0 0 1-1.406.913l-3.111-1.382A2 2 0 0 1 9.5 13H2a2 2 0 0 1-2-2V5z"/>
</svg> </svg>
{% trans "Start Live Class" %} ابدأ الدرس المباشر
</a> </a>
<a href="{% url 'subject_detail' subject.id %}" class="btn btn-sm btn-primary rounded-pill">{% trans "Manage Class" %}</a> <a href="{% url 'add_resource' subject.id %}" class="btn btn-sm btn-success rounded-pill">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-circle me-2" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"/>
</svg>
إضافة مصدر
</a>
<a href="{% url 'subject_detail' subject.id %}" class="btn btn-sm btn-primary rounded-pill">إدارة الصف</a>
</div> </div>
</div> </div>
</div> </div>
@ -123,8 +137,8 @@
</div> </div>
{% else %} {% else %}
<div class="glass-card text-center py-5"> <div class="glass-card text-center py-5">
<h5 class="text-muted">{% trans "You are not assigned to any subjects yet." %}</h5> <h5 class="text-muted">لم يتم تعيين أي مواد لك بعد.</h5>
<p class="text-muted small">{% trans "Contact the administrator to assign classes." %}</p> <p class="text-muted small">تواصل مع المسؤول لتعيين الصفوف.</p>
</div> </div>
{% endif %} {% endif %}
</div> </div>

View File

@ -1,14 +1,14 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load i18n static %} {% load i18n static %}
{% block title %}{% trans "Verify Account" %}{% endblock %} {% block title %}تفعيل الحساب{% endblock %}
{% block content %} {% block content %}
<div class="container hero-section"> <div class="container hero-section">
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-lg-6"> <div class="col-lg-6">
<div class="glass-card"> <div class="glass-card">
<h2 class="text-center mb-4 text-primary">{% trans "Account Verification" %}</h2> <h2 class="text-center mb-4 text-primary">تفعيل الحساب</h2>
{% if error %} {% if error %}
<div class="alert alert-danger"> <div class="alert alert-danger">
@ -17,33 +17,33 @@
{% endif %} {% endif %}
<p class="text-muted text-center mb-4"> <p class="text-muted text-center mb-4">
{% trans "We have sent verification codes to your mobile number and email address. Please enter them below." %} لقد أرسلنا رموز التحقق إلى رقم هاتفك وبريدك الإلكتروني. الرجاء إدخالها أدناه.
</p> </p>
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
<div class="mb-3"> <div class="mb-3">
<label class="form-label">{% trans "Mobile Verification Code" %}</label> <label class="form-label">رمز تحقق الهاتف</label>
<input type="text" name="mobile_otp" class="form-control" placeholder="6-digit code" {% if student.is_mobile_verified %}value="Verified" disabled{% endif %} required> <input type="text" name="mobile_otp" class="form-control" placeholder="رمز مكون من 6 أرقام" {% if student.is_mobile_verified %}value="تم التفعيل" disabled{% endif %} required>
{% if student.is_mobile_verified %} {% if student.is_mobile_verified %}
<small class="text-success"><i class="fas fa-check-circle"></i> {% trans "Mobile Verified" %}</small> <small class="text-success"><i class="fas fa-check-circle"></i> تم تفعيل الهاتف</small>
{% else %} {% else %}
<small class="text-muted">{% trans "Sent to" %} {{ student.mobile_number }}</small> <small class="text-muted">أرسل إلى {{ student.mobile_number }}</small>
{% endif %} {% endif %}
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">{% trans "Email Verification Code" %}</label> <label class="form-label">رمز تحقق البريد</label>
<input type="text" name="email_otp" class="form-control" placeholder="6-digit code" {% if student.is_email_verified %}value="Verified" disabled{% endif %} required> <input type="text" name="email_otp" class="form-control" placeholder="رمز مكون من 6 أرقام" {% if student.is_email_verified %}value="تم التفعيل" disabled{% endif %} required>
{% if student.is_email_verified %} {% if student.is_email_verified %}
<small class="text-success"><i class="fas fa-check-circle"></i> {% trans "Email Verified" %}</small> <small class="text-success"><i class="fas fa-check-circle"></i> تم تفعيل البريد</small>
{% else %} {% else %}
<small class="text-muted">{% trans "Sent to" %} {{ student.user.email }}</small> <small class="text-muted">أرسل إلى {{ student.user.email }}</small>
{% endif %} {% endif %}
</div> </div>
<button type="submit" class="btn btn-success w-100 py-2 fs-5">{% trans "Verify" %}</button> <button type="submit" class="btn btn-success w-100 py-2 fs-5">تفعيل</button>
</form> </form>
</div> </div>
</div> </div>

View File

@ -13,9 +13,14 @@ urlpatterns = [
path('verify-otp/', views.verify_otp, name='verify_otp'), path('verify-otp/', views.verify_otp, name='verify_otp'),
path('ajax/get-classroom-subjects/', views.get_classroom_subjects, name='get_classroom_subjects'), path('ajax/get-classroom-subjects/', views.get_classroom_subjects, name='get_classroom_subjects'),
path('profile/', views.profile, name='profile'), path('profile/', views.profile, name='profile'),
path('profile/edit/teacher/', views.edit_teacher_profile, name='edit_teacher_profile'),
path('logout/', views.custom_logout, name='logout'), path('logout/', views.custom_logout, name='logout'),
path('subscribe/<int:subject_id>/', views.subscribe_subject, name='subscribe_subject'), path('subscribe/<int:subject_id>/', views.subscribe_subject, name='subscribe_subject'),
path('payment/success/', views.payment_success, name='payment_success'), path('payment/success/', views.payment_success, name='payment_success'),
path('payment/cancel/', views.payment_cancel, name='payment_cancel'), path('payment/cancel/', views.payment_cancel, name='payment_cancel'),
path('classroom/<int:subject_id>/', views.live_classroom, name='live_classroom'), path('classroom/<int:subject_id>/', views.live_classroom, name='live_classroom'),
path('subject/<int:subject_id>/add-resource/', views.add_resource, name='add_resource'),
path('resource/<int:resource_id>/edit/', views.edit_resource, name='edit_resource'),
path('resource/<int:resource_id>/delete/', views.delete_resource, name='delete_resource'),
path('resource/<int:resource_id>/view/', views.view_resource, name='view_resource'),
] ]

View File

@ -2,17 +2,19 @@ from django.core.exceptions import PermissionDenied
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
from django.utils import translation from django.utils import translation
from django.conf import settings from django.conf import settings
from django.http import JsonResponse from django.http import JsonResponse, FileResponse, Http404
from django.contrib.admin.views.decorators import staff_member_required from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth import logout, login from django.contrib.auth import logout, login
from django.urls import reverse from django.urls import reverse
from .models import Classroom, Subject, Teacher, Student, City from .models import Classroom, Subject, Teacher, Student, City, Resource
from .forms import StudentRegistrationForm from .forms import StudentRegistrationForm, TeacherProfileForm, ResourceForm
from .wablas import send_whatsapp_message from .wablas import send_whatsapp_message
from .thawani import ThawaniClient from .thawani import ThawaniClient
import random import random
import string import string
import mimetypes
import os
def index(request): def index(request):
levels = Classroom.objects.prefetch_related('subjects').all() levels = Classroom.objects.prefetch_related('subjects').all()
@ -32,7 +34,11 @@ def set_language(request, lang_code):
def subject_detail(request, pk): def subject_detail(request, pk):
subject = get_object_or_404(Subject, pk=pk) subject = get_object_or_404(Subject, pk=pk)
return render(request, 'core/subject_detail.html', {'subject': subject}) is_teacher = False
if request.user.is_authenticated and hasattr(request.user, 'teacher_profile'):
if request.user.teacher_profile in subject.teachers.all():
is_teacher = True
return render(request, 'core/subject_detail.html', {'subject': subject, 'is_teacher': is_teacher})
@staff_member_required @staff_member_required
def get_subjects_by_level(request): def get_subjects_by_level(request):
@ -46,7 +52,7 @@ def get_classroom_subjects(request):
classroom_id = request.GET.get('classroom_id') classroom_id = request.GET.get('classroom_id')
if not classroom_id: if not classroom_id:
return JsonResponse([], safe=False) return JsonResponse([], safe=False)
subjects = Subject.objects.filter(classroom_id=classroom_id).values('id', 'name_en', 'price') subjects = Subject.objects.filter(classroom_id=classroom_id).values('id', 'name_en', 'name_ar', 'price')
return JsonResponse(list(subjects), safe=False) return JsonResponse(list(subjects), safe=False)
def get_cities_by_governorate(request): def get_cities_by_governorate(request):
@ -79,7 +85,7 @@ def register_student(request):
# Note: This will only work if Wablas is configured in Admin # Note: This will only work if Wablas is configured in Admin
send_whatsapp_message( send_whatsapp_message(
student.mobile_number, student.mobile_number,
f"Your Verification Code is: {mobile_otp}" f"رمز التحقق الخاص بك هو: {mobile_otp}"
) )
# Simulate sending OTPs (Log to console) # Simulate sending OTPs (Log to console)
@ -123,14 +129,14 @@ def verify_otp(request):
student.is_mobile_verified = True student.is_mobile_verified = True
mobile_ok = True mobile_ok = True
else: else:
error = "Invalid Mobile OTP" error = "رمز الهاتف غير صحيح"
if not email_ok and (error is None or "Mobile" not in error): if not email_ok and (error is None or "الهاتف" not in error):
if entered_email_otp == student.email_otp_code: if entered_email_otp == student.email_otp_code:
student.is_email_verified = True student.is_email_verified = True
email_ok = True email_ok = True
else: else:
error = "Invalid Email OTP" error = "رمز البريد غير صحيح"
if mobile_ok and email_ok: if mobile_ok and email_ok:
student.mobile_otp_code = "" # Clear codes student.mobile_otp_code = "" # Clear codes
@ -180,6 +186,23 @@ def profile(request):
student_profile = None student_profile = None
return render(request, 'core/profile.html', {'student_profile': student_profile}) return render(request, 'core/profile.html', {'student_profile': student_profile})
@login_required
def edit_teacher_profile(request):
try:
teacher = request.user.teacher_profile
except Teacher.DoesNotExist:
return redirect('profile')
if request.method == 'POST':
form = TeacherProfileForm(request.POST, request.FILES, instance=teacher)
if form.is_valid():
form.save()
return redirect('profile')
else:
form = TeacherProfileForm(instance=teacher)
return render(request, 'core/edit_teacher_profile.html', {'form': form})
def custom_logout(request): def custom_logout(request):
logout(request) logout(request)
return redirect('index') return redirect('index')
@ -206,13 +229,13 @@ def subscribe_subject(request, subject_id):
session_id = session.get('data', {}).get('session_id') session_id = session.get('data', {}).get('session_id')
if not session_id: if not session_id:
return render(request, 'core/error.html', {'message': 'Could not create payment session.'}) return render(request, 'core/error.html', {'message': 'تعذر إنشاء جلسة الدفع.'})
return redirect(f"{thawani.checkout_base_url}/{session_id}") return redirect(f"{thawani.checkout_base_url}/{session_id}")
except Exception as e: except Exception as e:
print(f"Payment Error: {e}") print(f"Payment Error: {e}")
return render(request, 'core/error.html', {'message': f'Payment initialization failed: {str(e)}'}) return render(request, 'core/error.html', {'message': f'فشل بدء عملية الدفع: {str(e)}'})
@login_required @login_required
def payment_success(request): def payment_success(request):
@ -237,11 +260,11 @@ def payment_success(request):
pass # Already handled or user mismatch? pass # Already handled or user mismatch?
return redirect('profile') return redirect('profile')
else: else:
return render(request, 'core/error.html', {'message': f'Payment was not successful. Status: {payment_status}'}) return render(request, 'core/error.html', {'message': f'لم تتم عملية الدفع بنجاح. الحالة: {payment_status}'})
except Exception as e: except Exception as e:
print(f"Payment Verification Error: {e}") print(f"Payment Verification Error: {e}")
return render(request, 'core/error.html', {'message': f'Payment verification failed: {str(e)}'}) return render(request, 'core/error.html', {'message': f'فشل التحقق من الدفع: {str(e)}'})
@login_required @login_required
def payment_cancel(request): def payment_cancel(request):
@ -261,7 +284,7 @@ def live_classroom(request, subject_id):
is_student = True is_student = True
if not (is_teacher or is_student): if not (is_teacher or is_student):
raise PermissionDenied("You are not authorized to join this class.") raise PermissionDenied("ليس لديك صلاحية الانضمام لهذا الفصل.")
# Generate Room Name # Generate Room Name
# We use a consistent room name based on Subject ID # We use a consistent room name based on Subject ID
@ -273,3 +296,105 @@ def live_classroom(request, subject_id):
'user_display_name': request.user.get_full_name() or request.user.username, 'user_display_name': request.user.get_full_name() or request.user.username,
'is_teacher': is_teacher 'is_teacher': is_teacher
}) })
@login_required
def add_resource(request, subject_id):
try:
teacher = request.user.teacher_profile
except Teacher.DoesNotExist:
raise PermissionDenied("يجب أن تكون معلماً للوصول إلى هذه الصفحة.")
subject = get_object_or_404(Subject, pk=subject_id)
if teacher not in subject.teachers.all():
raise PermissionDenied("ليس لديك صلاحية لإضافة مصادر لهذه المادة.")
if request.method == 'POST':
form = ResourceForm(request.POST, request.FILES)
if form.is_valid():
resource = form.save(commit=False)
resource.subject = subject
resource.save()
return redirect('subject_detail', pk=subject.id)
else:
form = ResourceForm()
return render(request, 'core/add_resource.html', {'form': form, 'subject': subject})
@login_required
def edit_resource(request, resource_id):
resource = get_object_or_404(Resource, pk=resource_id)
try:
teacher = request.user.teacher_profile
except Teacher.DoesNotExist:
raise PermissionDenied("يجب أن تكون معلماً للوصول إلى هذه الصفحة.")
if teacher not in resource.subject.teachers.all():
raise PermissionDenied("ليس لديك صلاحية لتعديل هذا المصدر.")
if request.method == 'POST':
form = ResourceForm(request.POST, request.FILES, instance=resource)
if form.is_valid():
form.save()
return redirect('subject_detail', pk=resource.subject.id)
else:
form = ResourceForm(instance=resource)
return render(request, 'core/edit_resource.html', {'form': form, 'resource': resource, 'subject': resource.subject})
@login_required
def delete_resource(request, resource_id):
resource = get_object_or_404(Resource, pk=resource_id)
try:
teacher = request.user.teacher_profile
except Teacher.DoesNotExist:
raise PermissionDenied("يجب أن تكون معلماً للوصول إلى هذه الصفحة.")
if teacher not in resource.subject.teachers.all():
raise PermissionDenied("ليس لديك صلاحية لحذف هذا المصدر.")
subject_id = resource.subject.id
if request.method == 'POST':
resource.delete()
return redirect('subject_detail', pk=subject_id)
return render(request, 'core/delete_resource.html', {'resource': resource})
@login_required
def view_resource(request, resource_id):
resource = get_object_or_404(Resource, pk=resource_id)
has_access = False
# Check Teacher Access
if hasattr(request.user, 'teacher_profile') and request.user.teacher_profile in resource.subject.teachers.all():
has_access = True
# Check Student Access
elif hasattr(request.user, 'student_profile') and resource.subject in request.user.student_profile.subscribed_subjects.all():
has_access = True
if not has_access:
raise PermissionDenied("ليس لديك صلاحية لعرض هذا المصدر.")
if resource.resource_type == 'FILE' and resource.file:
try:
# Open file
file_handle = resource.file.open('rb')
response = FileResponse(file_handle)
# Check for PDF to force inline display
filename = resource.file.name
if filename.lower().endswith('.pdf'):
response['Content-Type'] = 'application/pdf'
response['Content-Disposition'] = f'inline; filename="{os.path.basename(filename)}"'
return response
except FileNotFoundError:
raise Http404("الملف غير موجود على الخادم.")
elif resource.resource_type in ['LINK', 'VIDEO']:
return redirect(resource.link)
return redirect('subject_detail', pk=resource.subject.id)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
media/teachers/men.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB