adding testimonial

This commit is contained in:
Flatlogic Bot 2026-01-25 16:46:17 +00:00
parent d8387d341e
commit 08e8aa82d4
9 changed files with 113 additions and 5 deletions

View File

@ -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)

View File

@ -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'],
},
),
]

View File

@ -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)
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']

View File

@ -86,6 +86,38 @@
</div>
</section>
<!-- Testimonials Section -->
{% if testimonials %}
<section class="py-5 bg-light">
<div class="container py-5">
<div class="text-center mb-5">
<h2 class="display-5">{% trans "What Our Users Say" %}</h2>
<p class="text-muted">{% trans "Real stories from our community" %}</p>
</div>
<div class="row g-4 justify-content-center">
{% for testimonial in testimonials %}
<div class="col-md-6 col-lg-4">
<div class="card h-100 border-0 shadow-sm">
<div class="card-body p-4 text-center">
{% if testimonial.image %}
<img src="{{ testimonial.image.url }}" alt="{{ testimonial.name }}" class="rounded-circle mb-3" width="80" height="80" style="object-fit: cover;">
{% else %}
<div class="rounded-circle bg-secondary text-white d-flex align-items-center justify-content-center mx-auto mb-3" style="width: 80px; height: 80px; font-size: 2rem;">
{{ testimonial.name|first|upper }}
</div>
{% endif %}
<h5 class="card-title">{{ testimonial.name }}</h5>
<p class="text-muted small mb-3">{{ testimonial.role }}</p>
<p class="card-text fst-italic">"{{ testimonial.content }}"</p>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</section>
{% endif %}
<!-- CTA Section -->
<section class="py-5" style="background-color: var(--primary-dark); color: white;">
<div class="container py-4 text-center">

View File

@ -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')