diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index 3418d8b..b932693 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 d5259c3..6d264ac 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 e809853..cb8f7bd 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 29a95fc..2ba60ea 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 +from .models import Profile, Parcel, Country, Governate, City, PlatformProfile, Testimonial from django.utils.translation import gettext_lazy as _ from django.urls import path, reverse from django.shortcuts import render @@ -124,6 +124,12 @@ class PlatformProfileAdmin(admin.ModelAdmin): fieldsets += ((_('Tools'), {'fields': ('test_connection_link',)}),) return fieldsets +class TestimonialAdmin(admin.ModelAdmin): + list_display = ('name_en', 'role_en', 'is_active', 'created_at') + list_filter = ('is_active', 'created_at') + search_fields = ('name_en', 'name_ar', 'content_en', 'content_ar') + list_editable = ('is_active',) + admin.site.unregister(User) admin.site.register(User, CustomUserAdmin) admin.site.register(Parcel, ParcelAdmin) @@ -131,3 +137,4 @@ admin.site.register(Country) admin.site.register(Governate) admin.site.register(City) admin.site.register(PlatformProfile, PlatformProfileAdmin) +admin.site.register(Testimonial, TestimonialAdmin) \ No newline at end of file diff --git a/core/migrations/0015_testimonial.py b/core/migrations/0015_testimonial.py new file mode 100644 index 0000000..5fe46cf --- /dev/null +++ b/core/migrations/0015_testimonial.py @@ -0,0 +1,34 @@ +# Generated by Django 5.2.7 on 2026-01-25 16:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0014_alter_otpverification_purpose'), + ] + + operations = [ + migrations.CreateModel( + name='Testimonial', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name_en', models.CharField(max_length=100, verbose_name='Name (English)')), + ('name_ar', models.CharField(max_length=100, verbose_name='Name (Arabic)')), + ('role_en', models.CharField(max_length=100, verbose_name='Role (English)')), + ('role_ar', models.CharField(max_length=100, verbose_name='Role (Arabic)')), + ('content_en', models.TextField(verbose_name='Testimony (English)')), + ('content_ar', models.TextField(verbose_name='Testimony (Arabic)')), + ('image', models.ImageField(blank=True, null=True, upload_to='testimonials/', verbose_name='Image')), + ('is_active', models.BooleanField(default=True, verbose_name='Active')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': 'Testimonial', + 'verbose_name_plural': 'Testimonials', + 'ordering': ['-created_at'], + }, + ), + ] diff --git a/core/migrations/__pycache__/0015_testimonial.cpython-311.pyc b/core/migrations/__pycache__/0015_testimonial.cpython-311.pyc new file mode 100644 index 0000000..8a3b7fc Binary files /dev/null and b/core/migrations/__pycache__/0015_testimonial.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 35f9189..fb8a8a6 100644 --- a/core/models.py +++ b/core/models.py @@ -208,4 +208,36 @@ class OTPVerification(models.Model): def is_valid(self): # OTP valid for 10 minutes - return self.created_at >= timezone.now() - timezone.timedelta(minutes=10) \ No newline at end of file + return self.created_at >= timezone.now() - timezone.timedelta(minutes=10) + +class Testimonial(models.Model): + name_en = models.CharField(_('Name (English)'), max_length=100) + name_ar = models.CharField(_('Name (Arabic)'), max_length=100) + role_en = models.CharField(_('Role (English)'), max_length=100) + role_ar = models.CharField(_('Role (Arabic)'), max_length=100) + content_en = models.TextField(_('Testimony (English)')) + content_ar = models.TextField(_('Testimony (Arabic)')) + image = models.ImageField(_('Image'), upload_to='testimonials/', blank=True, null=True) + is_active = models.BooleanField(_('Active'), default=True) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + @property + def name(self): + return self.name_ar if get_language() == 'ar' else self.name_en + + @property + def role(self): + return self.role_ar if get_language() == 'ar' else self.role_en + + @property + def content(self): + return self.content_ar if get_language() == 'ar' else self.content_en + + def __str__(self): + return self.name + + class Meta: + verbose_name = _('Testimonial') + verbose_name_plural = _('Testimonials') + ordering = ['-created_at'] diff --git a/core/templates/core/index.html b/core/templates/core/index.html index a82bcf4..cb52783 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -86,6 +86,38 @@ + +{% if testimonials %} +
+
+
+

{% trans "What Our Users Say" %}

+

{% trans "Real stories from our community" %}

+
+
+ {% for testimonial in testimonials %} +
+
+
+ {% if testimonial.image %} + {{ testimonial.name }} + {% else %} +
+ {{ testimonial.name|first|upper }} +
+ {% endif %} +
{{ testimonial.name }}
+

{{ testimonial.role }}

+

"{{ testimonial.content }}"

+
+
+
+ {% endfor %} +
+
+
+{% endif %} +
diff --git a/core/views.py b/core/views.py index cdb8c84..58d1cb2 100644 --- a/core/views.py +++ b/core/views.py @@ -3,7 +3,7 @@ from django.contrib.auth import login, authenticate, logout from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User -from .models import Parcel, Profile, Country, Governate, City, OTPVerification, PlatformProfile +from .models import Parcel, Profile, Country, Governate, City, OTPVerification, PlatformProfile, Testimonial from .forms import UserRegistrationForm, ParcelForm, ContactForm, UserProfileForm from django.utils.translation import gettext_lazy as _ from django.utils.translation import get_language @@ -34,10 +34,13 @@ def index(request): except Parcel.DoesNotExist: error = _("Parcel not found.") + testimonials = Testimonial.objects.filter(is_active=True) + return render(request, 'core/index.html', { 'parcel': parcel, 'error': error, - 'tracking_id': tracking_id + 'tracking_id': tracking_id, + 'testimonials': testimonials }) def register(request): @@ -391,4 +394,4 @@ def verify_otp_view(request): except OTPVerification.DoesNotExist: messages.error(request, _("Invalid code.")) - return render(request, 'core/verify_otp.html') + return render(request, 'core/verify_otp.html') \ No newline at end of file