adding banner

This commit is contained in:
Flatlogic Bot 2026-01-23 17:23:29 +00:00
parent 9964306747
commit 08da7e84a0
11 changed files with 128 additions and 6 deletions

View File

@ -4,7 +4,7 @@ from django.shortcuts import render
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.contrib import messages from django.contrib import messages
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from .models import Profile, Truck, Shipment, Bid, Message, WhatsAppConfig, Country, City, TruckType, AppSetting from .models import Profile, Truck, Shipment, Bid, Message, WhatsAppConfig, Country, City, TruckType, AppSetting, Banner
from .whatsapp import send_whatsapp_message from .whatsapp import send_whatsapp_message
@admin.register(Country) @admin.register(Country)
@ -97,3 +97,9 @@ class AppSettingAdmin(admin.ModelAdmin):
if self.model.objects.exists(): if self.model.objects.exists():
return False return False
return super().has_add_permission(request) return super().has_add_permission(request)
@admin.register(Banner)
class BannerAdmin(admin.ModelAdmin):
list_display = ('title', 'order', 'is_active', 'created_at')
list_editable = ('order', 'is_active')
search_fields = ('title', 'title_ar', 'subtitle', 'subtitle_ar')

View File

@ -0,0 +1,33 @@
# Generated by Django 5.2.7 on 2026-01-23 17:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0014_appsetting'),
]
operations = [
migrations.CreateModel(
name='Banner',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200, verbose_name='Title (EN)')),
('title_ar', models.CharField(blank=True, max_length=200, verbose_name='Title (AR)')),
('subtitle', models.CharField(blank=True, max_length=255, verbose_name='Subtitle (EN)')),
('subtitle_ar', models.CharField(blank=True, max_length=255, verbose_name='Subtitle (AR)')),
('image', models.ImageField(upload_to='banners/', verbose_name='Banner Image')),
('link', models.URLField(blank=True, help_text='Internal or external URL', null=True, verbose_name='Link URL')),
('is_active', models.BooleanField(default=True, verbose_name='Is Active')),
('order', models.PositiveIntegerField(default=0, verbose_name='Order')),
('created_at', models.DateTimeField(auto_now_add=True)),
],
options={
'verbose_name': 'Banner',
'verbose_name_plural': 'Banners',
'ordering': ['order', '-created_at'],
},
),
]

View File

@ -248,6 +248,37 @@ class AppSetting(models.Model):
def __str__(self): def __str__(self):
return self.app_name return self.app_name
class Banner(models.Model):
title = models.CharField(_('Title (EN)'), max_length=200)
title_ar = models.CharField(_('Title (AR)'), max_length=200, blank=True)
subtitle = models.CharField(_('Subtitle (EN)'), max_length=255, blank=True)
subtitle_ar = models.CharField(_('Subtitle (AR)'), max_length=255, blank=True)
image = models.ImageField(_('Banner Image'), upload_to='banners/')
link = models.URLField(_('Link URL'), blank=True, null=True, help_text=_("Internal or external URL"))
is_active = models.BooleanField(_('Is Active'), default=True)
order = models.PositiveIntegerField(_('Order'), default=0)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = _('Banner')
verbose_name_plural = _('Banners')
ordering = ['order', '-created_at']
def __str__(self):
return self.title
@property
def display_title(self):
if get_language() == 'ar' and self.title_ar:
return self.title_ar
return self.title
@property
def display_subtitle(self):
if get_language() == 'ar' and self.subtitle_ar:
return self.subtitle_ar
return self.subtitle
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs): def create_user_profile(sender, instance, created, **kwargs):
if created: if created:

View File

@ -2,7 +2,56 @@
{% load i18n static %} {% load i18n static %}
{% block content %} {% block content %}
<!-- Hero Section -->
{% if banners %}
<!-- Carousel Section -->
<section class="banner-carousel mb-4 mt-n1">
<div id="mainBannerCarousel" class="carousel slide" data-bs-ride="carousel">
<div class="carousel-indicators">
{% for banner in banners %}
<button type="button" data-bs-target="#mainBannerCarousel" data-bs-slide-to="{{ forloop.counter0 }}" {% if forloop.first %}class="active" aria-current="true"{% endif %} aria-label="Slide {{ forloop.counter }}"></button>
{% endfor %}
</div>
<div class="carousel-inner">
{% for banner in banners %}
<div class="carousel-item {% if forloop.first %}active{% endif %}" data-bs-interval="5000">
<div class="banner-wrapper position-relative" style="height: 450px; overflow: hidden;">
<img src="{{ banner.image.url }}" class="d-block w-100 h-100" style="object-fit: cover;" alt="{{ banner.display_title }}">
<div class="carousel-caption d-none d-md-block text-start start-0 bottom-0 mb-5 ms-5 p-4 rounded" style="background: rgba(0,0,0,0.5); backdrop-filter: blur(5px); max-width: 600px;">
<h2 class="fw-bold display-6">{{ banner.display_title }}</h2>
<p class="lead">{{ banner.display_subtitle }}</p>
{% if banner.link %}
<a href="{{ banner.link }}" class="btn btn-primary btn-lg px-4">{% trans "Learn More" %}</a>
{% endif %}
</div>
</div>
<!-- Mobile caption visible only on small screens below the image -->
<div class="d-md-none bg-dark text-white p-4 text-center">
<h3 class="fw-bold">{{ banner.display_title }}</h3>
<p>{{ banner.display_subtitle }}</p>
{% if banner.link %}
<a href="{{ banner.link }}" class="btn btn-primary">{% trans "Learn More" %}</a>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% if banners.count > 1 %}
<button class="carousel-control-prev" type="button" data-bs-target="#mainBannerCarousel" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#mainBannerCarousel" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>
{% endif %}
</div>
</section>
{% endif %}
<!-- Hero Section (Only show if no banners, or maybe as a secondary section?) -->
{% if not banners %}
<section class="hero-section py-5"> <section class="hero-section py-5">
<div class="container"> <div class="container">
<div class="row align-items-center"> <div class="row align-items-center">
@ -25,6 +74,7 @@
</div> </div>
</div> </div>
</section> </section>
{% endif %}
<!-- Role Selection Section --> <!-- Role Selection Section -->
<section class="py-5 bg-light" id="how-it-works"> <section class="py-5 bg-light" id="how-it-works">

View File

@ -2,7 +2,7 @@ from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth import login, authenticate, logout from django.contrib.auth import login, authenticate, logout
from django.utils import timezone from django.utils import timezone
from .models import Profile, Truck, Shipment, Bid, Message, OTPCode, Country, City, AppSetting from .models import Profile, Truck, Shipment, Bid, Message, OTPCode, Country, City, AppSetting, Banner
from .forms import TruckForm, ShipmentForm, BidForm, UserRegistrationForm, OTPVerifyForm, ShipperOfferForm from .forms import TruckForm, ShipmentForm, BidForm, UserRegistrationForm, OTPVerifyForm, ShipperOfferForm
from django.contrib import messages from django.contrib import messages
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
@ -13,8 +13,10 @@ from django.contrib.auth.forms import AuthenticationForm
def home(request): def home(request):
"""Render the landing screen for MASAR CARGO.""" """Render the landing screen for MASAR CARGO."""
banners = Banner.objects.filter(is_active=True)
context = { context = {
"deployment_timestamp": timezone.now().timestamp(), "deployment_timestamp": timezone.now().timestamp(),
"banners": banners,
} }
return render(request, "core/index.html", context) return render(request, "core/index.html", context)

BIN
media/banners/banner_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

BIN
media/banners/banner_2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB