diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc
index 76d369c..6364bac 100644
Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ
diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc
index c579a65..a682bb2 100644
Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ
diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc
index ad9397a..9911f8c 100644
Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ
diff --git a/core/admin.py b/core/admin.py
index 63cc71d..54f6b0f 100644
--- a/core/admin.py
+++ b/core/admin.py
@@ -1,7 +1,7 @@
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
-from .models import Profile, Parcel, Country, Governate, City, PlatformProfile, Testimonial
+from .models import Profile, Parcel, Country, Governate, City, PlatformProfile, Testimonial, DriverRating
from django.utils.translation import gettext_lazy as _
from django.urls import path, reverse
from django.shortcuts import render
@@ -18,9 +18,25 @@ class ProfileInline(admin.StackedInline):
model = Profile
can_delete = False
verbose_name_plural = _('Profiles')
+ fieldsets = (
+ (None, {'fields': ('role', 'is_approved', 'phone_number', 'profile_picture', 'address')}),
+ (_('Driver Info'), {'fields': ('license_front_image', 'license_back_image', 'car_plate_number'), 'classes': ('collapse',)}),
+ (_('Location'), {'fields': ('country', 'governate', 'city'), 'classes': ('collapse',)}),
+ )
class CustomUserAdmin(UserAdmin):
inlines = (ProfileInline,)
+ list_display = ('username', 'email', 'get_role', 'get_approval_status', 'is_active', 'is_staff')
+ list_filter = ('is_active', 'is_staff', 'profile__role', 'profile__is_approved')
+
+ def get_role(self, obj):
+ return obj.profile.get_role_display()
+ get_role.short_description = _('Role')
+
+ def get_approval_status(self, obj):
+ return obj.profile.is_approved
+ get_approval_status.short_description = _('Approved')
+ get_approval_status.boolean = True
def get_inline_instances(self, request, obj=None):
if not obj:
@@ -171,6 +187,4 @@ admin.site.register(Governate)
admin.site.register(City)
admin.site.register(PlatformProfile, PlatformProfileAdmin)
admin.site.register(Testimonial, TestimonialAdmin)
-
-# Set custom admin index template - using default 'admin/index.html' which we have overridden
-# admin.site.index_template = 'admin/dashboard.html'
+admin.site.register(DriverRating)
\ No newline at end of file
diff --git a/core/migrations/0020_profile_is_approved.py b/core/migrations/0020_profile_is_approved.py
new file mode 100644
index 0000000..7c1f894
--- /dev/null
+++ b/core/migrations/0020_profile_is_approved.py
@@ -0,0 +1,18 @@
+# Generated by Django 5.2.7 on 2026-01-28 00:05
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('core', '0019_profile_car_plate_number_profile_license_back_image_and_more'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='profile',
+ name='is_approved',
+ field=models.BooleanField(default=False, help_text='Designates whether this user is approved to use the platform (mainly for drivers).', verbose_name='Approved'),
+ ),
+ ]
diff --git a/core/migrations/__pycache__/0020_profile_is_approved.cpython-311.pyc b/core/migrations/__pycache__/0020_profile_is_approved.cpython-311.pyc
new file mode 100644
index 0000000..bbfac88
Binary files /dev/null and b/core/migrations/__pycache__/0020_profile_is_approved.cpython-311.pyc differ
diff --git a/core/models.py b/core/models.py
index db45946..5f44e60 100644
--- a/core/models.py
+++ b/core/models.py
@@ -82,6 +82,9 @@ class Profile(models.Model):
governate = models.ForeignKey(Governate, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_('Governate'))
city = models.ForeignKey(City, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_('City'))
+ # Approval Status
+ is_approved = models.BooleanField(_('Approved'), default=False, help_text=_("Designates whether this user is approved to use the platform (mainly for drivers)."))
+
def __str__(self):
return f"{self.user.username} - {self.get_role_display()}"
@@ -275,4 +278,4 @@ class DriverRating(models.Model):
class Meta:
verbose_name = _('Driver Rating')
- verbose_name_plural = _('Driver Ratings')
+ verbose_name_plural = _('Driver Ratings')
\ No newline at end of file
diff --git a/core/templates/core/driver_dashboard.html b/core/templates/core/driver_dashboard.html
index 641c04f..e732c42 100644
--- a/core/templates/core/driver_dashboard.html
+++ b/core/templates/core/driver_dashboard.html
@@ -10,6 +10,16 @@
+ {% if not is_approved %}
+
+
+
+
{% trans "Account Pending Approval" %}
+
{% trans "Your driver account is currently under review by our administrators. You will be notified once your documents are verified and your account is approved to accept shipments." %}
diff --git a/core/views.py b/core/views.py
index ce6cfdb..a5d3156 100644
--- a/core/views.py
+++ b/core/views.py
@@ -88,6 +88,11 @@ def register_shipper(request):
user.is_active = False
user.save()
+ # Auto-approve Shippers
+ if hasattr(user, 'profile'):
+ user.profile.is_approved = True
+ user.profile.save()
+
# Generate OTP
code = ''.join(random.choices(string.digits, k=6))
OTPVerification.objects.create(user=user, code=code, purpose='registration')
@@ -124,6 +129,12 @@ def register_driver(request):
user = form.save(commit=True)
user.is_active = False
user.save()
+
+ # Drivers are NOT approved by default (model default is False)
+ # We can rely on the default, but explicit is fine too if we want to be sure
+ if hasattr(user, 'profile'):
+ user.profile.is_approved = False
+ user.profile.save()
# Generate OTP
code = ''.join(random.choices(string.digits, k=6))
@@ -236,6 +247,12 @@ def dashboard(request):
else:
available_parcels_list = Parcel.objects.filter(status='pending').order_by('-created_at')
+ # Check Approval Status
+ if not profile.is_approved:
+ messages.warning(request, _("Your account is pending approval. You cannot accept shipments yet."))
+ # Empty list if not approved
+ available_parcels_list = Parcel.objects.none()
+
# Pagination for Available Shipments
page = request.GET.get('page', 1)
paginator = Paginator(available_parcels_list, 9) # Show 9 parcels per page
@@ -260,7 +277,8 @@ def dashboard(request):
'available_parcels': available_parcels,
'my_parcels': my_parcels,
'completed_parcels': completed_parcels,
- 'cancelled_parcels': cancelled_parcels
+ 'cancelled_parcels': cancelled_parcels,
+ 'is_approved': profile.is_approved # Pass to template
})
@login_required
@@ -292,6 +310,11 @@ def accept_parcel(request, parcel_id):
if profile.role != 'car_owner':
messages.error(request, _("Only car owners can accept shipments."))
return redirect('dashboard')
+
+ # Check Approval
+ if not profile.is_approved:
+ messages.error(request, _("Your account is pending approval. You cannot accept shipments yet."))
+ return redirect('dashboard')
platform_profile = PlatformProfile.objects.first()
payments_enabled = platform_profile.enable_payment if platform_profile else True
@@ -877,6 +900,10 @@ def update_parcel_status_ajax(request):
if payments_enabled and parcel.payment_status != 'paid':
return JsonResponse({'success': False, 'error': _('Payment pending')})
+ # Check Approval for Driver via AJAX
+ if not request.user.profile.is_approved:
+ return JsonResponse({'success': False, 'error': _('Account pending approval')})
+
parcel.carrier = request.user
parcel.status = 'picked_up' # Or 'assigned'? Logic says 'picked_up' in accept_parcel
parcel.save()
@@ -928,4 +955,4 @@ def cancel_parcel(request, parcel_id):
parcel.save()
messages.success(request, _("Shipment cancelled successfully."))
- return redirect('dashboard')
+ return redirect('dashboard')
\ No newline at end of file
diff --git a/locale/ar/LC_MESSAGES/django.mo b/locale/ar/LC_MESSAGES/django.mo
index d2acdf1..7f90fed 100644
Binary files a/locale/ar/LC_MESSAGES/django.mo and b/locale/ar/LC_MESSAGES/django.mo differ
diff --git a/locale/ar/LC_MESSAGES/django.po b/locale/ar/LC_MESSAGES/django.po
index 6fc17ff..0d64c13 100644
--- a/locale/ar/LC_MESSAGES/django.po
+++ b/locale/ar/LC_MESSAGES/django.po
@@ -1393,3 +1393,92 @@ msgstr "رقم التتبع"
msgid "Cancelled Shipments"
msgstr "الشحنات الملغاة"
+
+msgid "Grid"
+msgstr "شبكة"
+
+msgid "List"
+msgstr "قائمة"
+
+msgid "Scan Parcel"
+msgstr "مسح الطرد"
+
+msgid "Account Pending Approval"
+msgstr "الحساب قيد الانتظار"
+
+msgid ""
+"Your driver account is currently under review by our administrators. You "
+"will be notified once your documents are verified and your account is "
+"approved to accept shipments."
+msgstr ""
+"حساب السائق الخاص بك قيد المراجعة حالياً من قبل المشرفين. سيتم إشعارك "
+"بمجرد التحقق من مستنداتك والموافقة على حسابك لقبول الشحنات."
+
+msgid "Welcome to masarX"
+msgstr "مرحباً بك في مسارX"
+
+msgid "Choose how you want to join us today."
+msgstr "اختر كيف تريد الانضمام إلينا اليوم."
+
+msgid "I am a Shipper"
+msgstr "أنا شاحن"
+
+msgid "I want to send parcels and track my shipments easily."
+msgstr "أريد إرسال الطرود وتتبع شحناتي بسهولة."
+
+msgid "Register as Shipper"
+msgstr "التسجيل كشاحن"
+
+msgid "I am a Car Owner"
+msgstr "أنا صاحب سيارة"
+
+msgid "I want to deliver parcels and earn money on my own schedule."
+msgstr "أريد توصيل الطرود وكسب المال وفق جدولي الخاص."
+
+msgid "Register as Car Owner"
+msgstr "التسجيل كصاحب سيارة"
+
+msgid "Car Owner Registration"
+msgstr "تسجيل صاحب سيارة"
+
+msgid "Take photo with Webcam"
+msgstr "التقاط صورة بالكاميرا"
+
+msgid ""
+"Please provide clear photos of your license (front and back) and car plate "
+"number for verification."
+msgstr ""
+"يرجى تقديم صور واضحة لرخصتك (الأمام والخلف) ورقم لوحة السيارة للتحقق."
+
+msgid "Submit Application"
+msgstr "تقديم الطلب"
+
+msgid "Take Photo"
+msgstr "التقاط صورة"
+
+msgid "Could not access webcam. Please check permissions."
+msgstr "تعذر الوصول إلى الكاميرا. يرجى التحقق من الأذونات."
+
+msgid "Photo captured!"
+msgstr "تم التقاط الصورة!"
+
+msgid "Back"
+msgstr "عودة"
+
+msgid "License Front Image"
+msgstr "صورة الرخصة (الأمام)"
+
+msgid "License Back Image"
+msgstr "صورة الرخصة (الخلف)"
+
+msgid "Car Plate Number"
+msgstr "رقم لوحة السيارة"
+
+msgid "Approved"
+msgstr "موافق عليه"
+
+msgid ""
+"Designates whether this user is approved to use the platform (mainly for "
+"drivers)."
+
+msgstr "يحدد ما إذا كان هذا المستخدم معتمداً لاستخدام المنصة (بشكل رئيسي للسائقين)."