diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index eae1cef..e67bd62 100644 Binary files a/config/__pycache__/settings.cpython-311.pyc and b/config/__pycache__/settings.cpython-311.pyc differ diff --git a/config/__pycache__/urls.cpython-311.pyc b/config/__pycache__/urls.cpython-311.pyc index 9449abe..f58ff9d 100644 Binary files a/config/__pycache__/urls.cpython-311.pyc and b/config/__pycache__/urls.cpython-311.pyc differ diff --git a/config/settings.py b/config/settings.py index 6d4fdfd..028cede 100644 --- a/config/settings.py +++ b/config/settings.py @@ -43,6 +43,8 @@ SESSION_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True SESSION_COOKIE_SAMESITE = "None" CSRF_COOKIE_SAMESITE = "None" +LANGUAGE_COOKIE_SAMESITE = "None" +LANGUAGE_COOKIE_SECURE = True # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/ diff --git a/config/urls.py b/config/urls.py index 9cb6c91..c0f2fb9 100644 --- a/config/urls.py +++ b/config/urls.py @@ -5,9 +5,10 @@ from django.conf.urls.static import static urlpatterns = [ path('admin/', admin.site.urls), + path('i18n/', include('django.conf.urls.i18n')), path('', include('core.urls')), ] if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) - urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) \ No newline at end of file + urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index 76092ac..3c262bf 100644 Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ diff --git a/core/admin.py b/core/admin.py index 1db7bb5..6643cc2 100644 --- a/core/admin.py +++ b/core/admin.py @@ -101,3 +101,12 @@ class PackageAdmin(ActionsModelAdmin): list_editable = ('is_active',) filter_horizontal = ('subjects',) search_fields = ('name_en', 'name_ar') + + class Media: + js = ('js/admin_package.js',) + + def get_form(self, request, obj=None, **kwargs): + form = super().get_form(request, obj, **kwargs) + if obj and obj.classroom: + form.base_fields['subjects'].queryset = Subject.objects.filter(classroom=obj.classroom) + return form \ No newline at end of file diff --git a/core/templates/base.html b/core/templates/base.html index 8130aff..e1a70db 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -1,14 +1,18 @@ {% load i18n static %} +{% get_current_language as LANGUAGE_CODE %} - + - {% block title %}منصة التعليم الإلكتروني{% endblock %} + {% block title %}{% if LANGUAGE_CODE == 'ar' %}منصة التعليم الإلكتروني{% else %}EduPlatform{% endif %}{% endblock %} - - + + + + + @@ -91,6 +95,11 @@ background: radial-gradient(circle, rgba(45, 49, 250, 0.1) 0%, transparent 70%); z-index: 0; } + + html[dir="ltr"] .hero-section::before { + right: auto; + left: 0; + } .footer { padding: 3rem 0; @@ -122,35 +131,62 @@ @@ -161,7 +197,7 @@ diff --git a/core/templates/core/index.html b/core/templates/core/index.html index a9620cc..59522f3 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,7 +1,9 @@ {% extends "base.html" %} {% load i18n static %} -{% block title %}الرئيسية | EduPlatform{% endblock %} +{% block title %} + {% if LANGUAGE_CODE == 'ar' %}الرئيسية | منصة التعليم الإلكتروني{% else %}Home | EduPlatform{% endif %} +{% endblock %} {% block content %}
@@ -9,14 +11,26 @@

- تعلم من أفضل المعلمين في أي وقت وأي مكان + {% if LANGUAGE_CODE == 'ar' %} + تعلم من أفضل المعلمين في أي وقت وأي مكان + {% else %} + Learn from the best teachers anytime, anywhere + {% endif %}

- منصة تعليمية متكاملة تدعم الطلاب والمعلمين مع دروس حية ومصادر تعليمية متميزة. + {% if LANGUAGE_CODE == 'ar' %} + منصة تعليمية متكاملة تدعم الطلاب والمعلمين مع دروس حية ومصادر تعليمية متميزة. + {% else %} + A comprehensive educational platform supporting students and teachers with live lessons and premium resources. + {% endif %}

@@ -24,8 +38,12 @@
-

مستعد للبدء؟

-

انضم لآلاف الطلاب اليوم.

+

+ {% if LANGUAGE_CODE == 'ar' %}مستعد للبدء؟{% else %}Ready to Start?{% endif %} +

+

+ {% if LANGUAGE_CODE == 'ar' %}انضم لآلاف الطلاب اليوم.{% else %}Join thousands of students today.{% endif %} +

@@ -41,8 +59,12 @@
-

برامجنا التعليمية

-

استكشف المواد المخصصة لكل مرحلة دراسية.

+

+ {% if LANGUAGE_CODE == 'ar' %}برامجنا التعليمية{% else %}Our Educational Programs{% endif %} +

+

+ {% if LANGUAGE_CODE == 'ar' %}استكشف المواد المخصصة لكل مرحلة دراسية.{% else %}Explore subjects tailored for each educational level.{% endif %} +

{% for level in levels %} @@ -52,7 +74,7 @@ {{ forloop.counter }}

- {{ level.name_ar }} + {% if LANGUAGE_CODE == 'ar' %}{{ level.name_ar }}{% else %}{{ level.name_en|default:level.name_ar }}{% endif %}


@@ -63,21 +85,27 @@
{% if subject.image %} - {{ subject.name_en }} + {% if LANGUAGE_CODE == 'ar' %}{{ subject.name_ar }}{% else %}{{ subject.name_en }}{% endif %} {% else %} 📚 {% endif %}

- {{ subject.name_ar }} + {% if LANGUAGE_CODE == 'ar' %}{{ subject.name_ar }}{% else %}{{ subject.name_en|default:subject.name_ar }}{% endif %}

- {{ subject.description_ar|truncatewords:15 }} + {% if LANGUAGE_CODE == 'ar' %} + {{ subject.description_ar|truncatewords:15 }} + {% else %} + {{ subject.description_en|default:subject.description_ar|truncatewords:15 }} + {% endif %}

@@ -85,7 +113,7 @@ {% empty %}
- لا توجد مواد متاحة لهذا المستوى بعد. + {% if LANGUAGE_CODE == 'ar' %}لا توجد مواد متاحة لهذا المستوى بعد.{% else %}No subjects available for this level yet.{% endif %}
{% endfor %} @@ -99,8 +127,12 @@
-

تعرف على معلمينا

-

تعلم من نخبة من المعلمين.

+

+ {% if LANGUAGE_CODE == 'ar' %}تعرف على معلمينا{% else %}Meet Our Teachers{% endif %} +

+

+ {% if LANGUAGE_CODE == 'ar' %}تعلم من نخبة من المعلمين.{% else %}Learn from elite educators.{% endif %} +

@@ -122,13 +154,15 @@

{{ teacher.specialization|default:"معلم" }}

- {{ teacher.bio|default:"لا توجد نبذة."|truncatewords:20 }} + {{ teacher.bio|default:"No bio available."|truncatewords:20 }}

{% empty %}
-

لا يوجد معلمين حالياً.

+

+ {% if LANGUAGE_CODE == 'ar' %}لا يوجد معلمين حالياً.{% else %}No teachers available at the moment.{% endif %} +

{% endfor %}
@@ -141,22 +175,34 @@
🎥
-
دروس مباشرة
-

انضم لجلسات تفاعلية عبر Google Meet.

+
+ {% if LANGUAGE_CODE == 'ar' %}دروس مباشرة{% else %}Live Lessons{% endif %} +
+

+ {% if LANGUAGE_CODE == 'ar' %}انضم لجلسات تفاعلية عبر Google Meet.{% else %}Join interactive sessions via Google Meet.{% endif %} +

📁
-
المصادر
-

الوصول لمواد الدورة عبر Google Drive.

+
+ {% if LANGUAGE_CODE == 'ar' %}المصادر{% else %}Resources{% endif %} +
+

+ {% if LANGUAGE_CODE == 'ar' %}الوصول لمواد الدورة عبر Google Drive.{% else %}Access course materials via Google Drive.{% endif %} +

💳
-
دفع سهل
-

دفع آمن عبر بوابة ثواني.

+
+ {% if LANGUAGE_CODE == 'ar' %}دفع سهل{% else %}Easy Payment{% endif %} +
+

+ {% if LANGUAGE_CODE == 'ar' %}دفع آمن عبر بوابة ثواني.{% else %}Secure payment via Thawani Gateway.{% endif %} +

diff --git a/core/templates/core/login.html b/core/templates/core/login.html index f35bd6c..4104ce2 100644 --- a/core/templates/core/login.html +++ b/core/templates/core/login.html @@ -1,7 +1,9 @@ {% extends 'base.html' %} {% load i18n static %} -{% block title %}تسجيل الدخول{% endblock %} +{% block title %} + {% if LANGUAGE_CODE == 'ar' %}تسجيل الدخول{% else %}Login{% endif %} +{% endblock %} {% block content %}
@@ -9,8 +11,12 @@
-

مرحباً بعودتك

-

سجل الدخول لمتابعة رحلتك التعليمية

+

+ {% if LANGUAGE_CODE == 'ar' %}مرحباً بعودتك{% else %}Welcome Back{% endif %} +

+

+ {% if LANGUAGE_CODE == 'ar' %}سجل الدخول لمتابعة رحلتك التعليمية{% else %}Login to continue your educational journey{% endif %} +

@@ -18,29 +24,32 @@ {% if form.errors %} {% endif %}
- - + +
- - + +

- ليس لديك حساب؟ - سجل هنا + {% if LANGUAGE_CODE == 'ar' %}ليس لديك حساب؟{% else %}Don't have an account?{% endif %} + + {% if LANGUAGE_CODE == 'ar' %}سجل هنا{% else %}Register here{% endif %} +

diff --git a/static/js/admin_package.js b/static/js/admin_package.js new file mode 100644 index 0000000..da40161 --- /dev/null +++ b/static/js/admin_package.js @@ -0,0 +1,95 @@ +(function($) { + 'use strict'; + $(function() { + console.log("Admin Package JS loaded"); + + var $levelSelect = $('#id_classroom'); + var $subjectSelect = $('#id_subjects'); + + // Debug selector finding + if (!$levelSelect.length) console.error("Classroom select #id_classroom not found!"); + if (!$subjectSelect.length) console.error("Subjects select #id_subjects not found!"); + + // Check if we are in the admin change form + if (!$levelSelect.length || !$subjectSelect.length) return; + + var isArabic = $('html').attr('lang') === 'ar' || $('body').hasClass('rtl'); + + function updateSubjects(levelId, selectedValues) { + console.log("Updating subjects for level:", levelId); + + if (!levelId) { + $subjectSelect.html(''); + refreshSelectFilter(); + return; + } + + $.getJSON('/ajax/get-subjects-by-level/', {level_id: levelId}, function(data) { + console.log("Received subjects:", data); + var options = ''; + $.each(data, function(index, subject) { + var selected = ''; + if (selectedValues && selectedValues.indexOf(subject.id.toString()) !== -1) { + selected = ' selected'; + } + var name = isArabic ? subject.name_ar : subject.name_en; + options += ''; + }); + $subjectSelect.html(options); + refreshSelectFilter(); + }) + .fail(function(jqxhr, textStatus, error) { + console.error("AJAX Error:", textStatus, error); + }); + } + + function refreshSelectFilter() { + var fieldName = 'subjects'; + // Django's filter_horizontal uses SelectFilter2 + if (typeof SelectFilter2 !== 'undefined') { + console.log("Refreshing SelectFilter2"); + // Remove the existing widget elements + $('.selector').has('#id_' + fieldName + '_from').remove(); + + // Re-initialize it + SelectFilter2.init('id_' + fieldName, fieldName, false); + } else if (typeof SelectFilter !== 'undefined') { + console.log("Refreshing SelectFilter (Legacy)"); + $('.selector').has('#id_' + fieldName + '_from').remove(); + SelectFilter.init('id_' + fieldName, fieldName, false); + } else { + console.warn("SelectFilter not found!"); + } + } + + $levelSelect.on('change', function() { + console.log("Classroom changed to:", $(this).val()); + var selectedValues = []; + // Reset subjects when classroom changes + updateSubjects($(this).val(), []); + }); + + // If we are editing an existing package + if ($levelSelect.val()) { + console.log("Initial load with classroom:", $levelSelect.val()); + // Get currently selected values + var selectedValues = []; + + // Try to get from the "chosen" side of filter_horizontal if it exists + // Since this runs on page load, standard Django might have already rendered the widget + $('#id_subjects_to option').each(function() { + selectedValues.push($(this).val()); + }); + + // Fallback if widget not yet initialized or for simple select + if (selectedValues.length === 0) { + $subjectSelect.find('option:selected').each(function() { + selectedValues.push($(this).val()); + }); + } + + console.log("Initial selected values:", selectedValues); + updateSubjects($levelSelect.val(), selectedValues); + } + }); +})(django.jQuery); \ No newline at end of file diff --git a/staticfiles/js/admin_package.js b/staticfiles/js/admin_package.js new file mode 100644 index 0000000..da40161 --- /dev/null +++ b/staticfiles/js/admin_package.js @@ -0,0 +1,95 @@ +(function($) { + 'use strict'; + $(function() { + console.log("Admin Package JS loaded"); + + var $levelSelect = $('#id_classroom'); + var $subjectSelect = $('#id_subjects'); + + // Debug selector finding + if (!$levelSelect.length) console.error("Classroom select #id_classroom not found!"); + if (!$subjectSelect.length) console.error("Subjects select #id_subjects not found!"); + + // Check if we are in the admin change form + if (!$levelSelect.length || !$subjectSelect.length) return; + + var isArabic = $('html').attr('lang') === 'ar' || $('body').hasClass('rtl'); + + function updateSubjects(levelId, selectedValues) { + console.log("Updating subjects for level:", levelId); + + if (!levelId) { + $subjectSelect.html(''); + refreshSelectFilter(); + return; + } + + $.getJSON('/ajax/get-subjects-by-level/', {level_id: levelId}, function(data) { + console.log("Received subjects:", data); + var options = ''; + $.each(data, function(index, subject) { + var selected = ''; + if (selectedValues && selectedValues.indexOf(subject.id.toString()) !== -1) { + selected = ' selected'; + } + var name = isArabic ? subject.name_ar : subject.name_en; + options += ''; + }); + $subjectSelect.html(options); + refreshSelectFilter(); + }) + .fail(function(jqxhr, textStatus, error) { + console.error("AJAX Error:", textStatus, error); + }); + } + + function refreshSelectFilter() { + var fieldName = 'subjects'; + // Django's filter_horizontal uses SelectFilter2 + if (typeof SelectFilter2 !== 'undefined') { + console.log("Refreshing SelectFilter2"); + // Remove the existing widget elements + $('.selector').has('#id_' + fieldName + '_from').remove(); + + // Re-initialize it + SelectFilter2.init('id_' + fieldName, fieldName, false); + } else if (typeof SelectFilter !== 'undefined') { + console.log("Refreshing SelectFilter (Legacy)"); + $('.selector').has('#id_' + fieldName + '_from').remove(); + SelectFilter.init('id_' + fieldName, fieldName, false); + } else { + console.warn("SelectFilter not found!"); + } + } + + $levelSelect.on('change', function() { + console.log("Classroom changed to:", $(this).val()); + var selectedValues = []; + // Reset subjects when classroom changes + updateSubjects($(this).val(), []); + }); + + // If we are editing an existing package + if ($levelSelect.val()) { + console.log("Initial load with classroom:", $levelSelect.val()); + // Get currently selected values + var selectedValues = []; + + // Try to get from the "chosen" side of filter_horizontal if it exists + // Since this runs on page load, standard Django might have already rendered the widget + $('#id_subjects_to option').each(function() { + selectedValues.push($(this).val()); + }); + + // Fallback if widget not yet initialized or for simple select + if (selectedValues.length === 0) { + $subjectSelect.find('option:selected').each(function() { + selectedValues.push($(this).val()); + }); + } + + console.log("Initial selected values:", selectedValues); + updateSubjects($levelSelect.val(), selectedValues); + } + }); +})(django.jQuery); \ No newline at end of file diff --git a/staticfiles/pasted-20260204-135454-c72e44de.png b/staticfiles/pasted-20260204-135454-c72e44de.png new file mode 100644 index 0000000..1aa3dfd Binary files /dev/null and b/staticfiles/pasted-20260204-135454-c72e44de.png differ