adding banner
This commit is contained in:
parent
9964306747
commit
08da7e84a0
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -4,7 +4,7 @@ from django.shortcuts import render
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.contrib import messages
|
||||
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
|
||||
|
||||
@admin.register(Country)
|
||||
@ -97,3 +97,9 @@ class AppSettingAdmin(admin.ModelAdmin):
|
||||
if self.model.objects.exists():
|
||||
return False
|
||||
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')
|
||||
33
core/migrations/0015_banner.py
Normal file
33
core/migrations/0015_banner.py
Normal 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'],
|
||||
},
|
||||
),
|
||||
]
|
||||
BIN
core/migrations/__pycache__/0015_banner.cpython-311.pyc
Normal file
BIN
core/migrations/__pycache__/0015_banner.cpython-311.pyc
Normal file
Binary file not shown.
@ -248,6 +248,37 @@ class AppSetting(models.Model):
|
||||
def __str__(self):
|
||||
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)
|
||||
def create_user_profile(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
@ -276,4 +307,4 @@ def sync_user_groups(sender, instance, **kwargs):
|
||||
instance.user.groups.remove(*other_groups)
|
||||
|
||||
# Add user to the correct group
|
||||
instance.user.groups.add(group)
|
||||
instance.user.groups.add(group)
|
||||
|
||||
@ -2,7 +2,56 @@
|
||||
{% load i18n static %}
|
||||
|
||||
{% 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">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
@ -25,6 +74,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<!-- Role Selection Section -->
|
||||
<section class="py-5 bg-light" id="how-it-works">
|
||||
@ -139,4 +189,4 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@ -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 import login, authenticate, logout
|
||||
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 django.contrib import messages
|
||||
from django.utils.translation import gettext as _
|
||||
@ -13,8 +13,10 @@ from django.contrib.auth.forms import AuthenticationForm
|
||||
|
||||
def home(request):
|
||||
"""Render the landing screen for MASAR CARGO."""
|
||||
banners = Banner.objects.filter(is_active=True)
|
||||
context = {
|
||||
"deployment_timestamp": timezone.now().timestamp(),
|
||||
"banners": banners,
|
||||
}
|
||||
return render(request, "core/index.html", context)
|
||||
|
||||
@ -397,4 +399,4 @@ def terms_of_service(request):
|
||||
'content': app_settings.terms_of_service if app_settings else _("Terms of service are coming soon.")
|
||||
}
|
||||
}
|
||||
return render(request, 'core/article_detail.html', context)
|
||||
return render(request, 'core/article_detail.html', context)
|
||||
BIN
media/banners/banner_1.jpg
Normal file
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
BIN
media/banners/banner_2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 92 KiB |
Loading…
x
Reference in New Issue
Block a user