adding editing main page from admin panel
This commit is contained in:
parent
f1cec56a71
commit
081f578fb5
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.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, Banner
|
from .models import Profile, Truck, Shipment, Bid, Message, WhatsAppConfig, Country, City, TruckType, AppSetting, Banner, HomeSection
|
||||||
from .whatsapp import send_whatsapp_message
|
from .whatsapp import send_whatsapp_message
|
||||||
|
|
||||||
@admin.register(Country)
|
@admin.register(Country)
|
||||||
@ -102,4 +102,11 @@ class AppSettingAdmin(admin.ModelAdmin):
|
|||||||
class BannerAdmin(admin.ModelAdmin):
|
class BannerAdmin(admin.ModelAdmin):
|
||||||
list_display = ('title', 'order', 'is_active', 'created_at')
|
list_display = ('title', 'order', 'is_active', 'created_at')
|
||||||
list_editable = ('order', 'is_active')
|
list_editable = ('order', 'is_active')
|
||||||
search_fields = ('title', 'title_ar', 'subtitle', 'subtitle_ar')
|
search_fields = ('title', 'title_ar', 'subtitle', 'subtitle_ar')
|
||||||
|
|
||||||
|
@admin.register(HomeSection)
|
||||||
|
class HomeSectionAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('title', 'section_type', 'order', 'is_active')
|
||||||
|
list_editable = ('order', 'is_active')
|
||||||
|
list_filter = ('section_type', 'is_active', 'background_color')
|
||||||
|
search_fields = ('title', 'title_ar', 'subtitle', 'subtitle_ar', 'content', 'content_ar')
|
||||||
|
|||||||
35
core/migrations/0016_homesection.py
Normal file
35
core/migrations/0016_homesection.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2026-01-24 03:16
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('core', '0015_banner'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='HomeSection',
|
||||||
|
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)')),
|
||||||
|
('content', models.TextField(blank=True, verbose_name='Content (EN)')),
|
||||||
|
('content_ar', models.TextField(blank=True, verbose_name='Content (AR)')),
|
||||||
|
('image', models.ImageField(blank=True, null=True, upload_to='home_sections/', verbose_name='Image')),
|
||||||
|
('order', models.PositiveIntegerField(default=0, verbose_name='Order')),
|
||||||
|
('is_active', models.BooleanField(default=True, verbose_name='Is Active')),
|
||||||
|
('section_type', models.CharField(choices=[('SIMPLE', 'Simple Text & Image'), ('FEATURES', 'Features List'), ('CTA', 'Call to Action')], default='SIMPLE', max_length=20)),
|
||||||
|
('background_color', models.CharField(default='white', help_text='e.g. white, light, primary', max_length=50)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Home Section',
|
||||||
|
'verbose_name_plural': 'Home Sections',
|
||||||
|
'ordering': ['order'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
BIN
core/migrations/__pycache__/0016_homesection.cpython-311.pyc
Normal file
BIN
core/migrations/__pycache__/0016_homesection.cpython-311.pyc
Normal file
Binary file not shown.
@ -279,6 +279,50 @@ class Banner(models.Model):
|
|||||||
return self.subtitle_ar
|
return self.subtitle_ar
|
||||||
return self.subtitle
|
return self.subtitle
|
||||||
|
|
||||||
|
class HomeSection(models.Model):
|
||||||
|
SECTION_TYPES = (
|
||||||
|
('SIMPLE', _('Simple Text & Image')),
|
||||||
|
('FEATURES', _('Features List')),
|
||||||
|
('CTA', _('Call to Action')),
|
||||||
|
)
|
||||||
|
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)
|
||||||
|
content = models.TextField(_('Content (EN)'), blank=True)
|
||||||
|
content_ar = models.TextField(_('Content (AR)'), blank=True)
|
||||||
|
image = models.ImageField(_('Image'), upload_to='home_sections/', blank=True, null=True)
|
||||||
|
order = models.PositiveIntegerField(_('Order'), default=0)
|
||||||
|
is_active = models.BooleanField(_('Is Active'), default=True)
|
||||||
|
section_type = models.CharField(max_length=20, choices=SECTION_TYPES, default='SIMPLE')
|
||||||
|
background_color = models.CharField(max_length=50, default='white', help_text="e.g. white, light, primary")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Home Section')
|
||||||
|
verbose_name_plural = _('Home Sections')
|
||||||
|
ordering = ['order']
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display_content(self):
|
||||||
|
if get_language() == 'ar' and self.content_ar:
|
||||||
|
return self.content_ar
|
||||||
|
return self.content
|
||||||
|
|
||||||
@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:
|
||||||
@ -307,4 +351,4 @@ def sync_user_groups(sender, instance, **kwargs):
|
|||||||
instance.user.groups.remove(*other_groups)
|
instance.user.groups.remove(*other_groups)
|
||||||
|
|
||||||
# Add user to the correct group
|
# Add user to the correct group
|
||||||
instance.user.groups.add(group)
|
instance.user.groups.add(group)
|
||||||
@ -50,7 +50,7 @@
|
|||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- Hero Section (Only show if no banners, or maybe as a secondary section?) -->
|
<!-- Hero Section (Only show if no banners) -->
|
||||||
{% if not banners %}
|
{% if not banners %}
|
||||||
<section class="hero-section py-5">
|
<section class="hero-section py-5">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -76,6 +76,51 @@
|
|||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Dynamic Home Sections -->
|
||||||
|
{% for section in home_sections %}
|
||||||
|
<section class="py-5 {% if section.background_color == 'light' %}bg-light{% elif section.background_color == 'primary' %}bg-primary text-white{% else %}bg-white{% endif %}">
|
||||||
|
<div class="container py-5">
|
||||||
|
{% if section.section_type == 'SIMPLE' %}
|
||||||
|
<div class="row align-items-center {% if forloop.counter|divisibleby:2 %}flex-row-reverse{% endif %}">
|
||||||
|
<div class="col-lg-6 mb-4 mb-lg-0">
|
||||||
|
<h2 class="fw-bold mb-3">{{ section.display_title }}</h2>
|
||||||
|
{% if section.display_subtitle %}
|
||||||
|
<h5 class="{% if section.background_color == 'primary' %}text-white-50{% else %}text-muted{% endif %} mb-4">{{ section.display_subtitle }}</h5>
|
||||||
|
{% endif %}
|
||||||
|
<div class="content lead">
|
||||||
|
{{ section.display_content|linebreaks }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if section.image %}
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<img src="{{ section.image.url }}" alt="{{ section.display_title }}" class="img-fluid rounded-4 shadow">
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% elif section.section_type == 'CTA' %}
|
||||||
|
<div class="text-center">
|
||||||
|
<h2 class="fw-bold mb-3">{{ section.display_title }}</h2>
|
||||||
|
{% if section.display_subtitle %}
|
||||||
|
<p class="lead mb-4 opacity-75">{{ section.display_subtitle }}</p>
|
||||||
|
{% endif %}
|
||||||
|
<div class="mb-4">
|
||||||
|
{{ section.display_content|linebreaks }}
|
||||||
|
</div>
|
||||||
|
<a href="{% url 'register' %}" class="btn {% if section.background_color == 'primary' %}btn-light text-primary{% else %}btn-primary{% endif %} btn-lg px-5 rounded-pill fw-bold">{% trans "Get Started" %}</a>
|
||||||
|
</div>
|
||||||
|
{% elif section.section_type == 'FEATURES' %}
|
||||||
|
<div class="text-center mb-5">
|
||||||
|
<h2 class="fw-bold mb-3">{{ section.display_title }}</h2>
|
||||||
|
<p class="lead text-muted">{{ section.display_subtitle }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="row g-4">
|
||||||
|
{{ section.display_content|safe }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
<!-- 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">
|
||||||
<div class="container py-5">
|
<div class="container py-5">
|
||||||
@ -189,4 +234,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</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.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, Banner
|
from .models import Profile, Truck, Shipment, Bid, Message, OTPCode, Country, City, AppSetting, Banner, HomeSection
|
||||||
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 _
|
||||||
@ -14,9 +14,11 @@ 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)
|
banners = Banner.objects.filter(is_active=True)
|
||||||
|
home_sections = HomeSection.objects.filter(is_active=True).order_by('order')
|
||||||
context = {
|
context = {
|
||||||
"deployment_timestamp": timezone.now().timestamp(),
|
"deployment_timestamp": timezone.now().timestamp(),
|
||||||
"banners": banners,
|
"banners": banners,
|
||||||
|
"home_sections": home_sections,
|
||||||
}
|
}
|
||||||
return render(request, "core/index.html", context)
|
return render(request, "core/index.html", context)
|
||||||
|
|
||||||
@ -399,4 +401,4 @@ def terms_of_service(request):
|
|||||||
'content': app_settings.terms_of_service if app_settings else _("Terms of service are coming soon.")
|
'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/app/ahlalalkhair_logo.jpg
Normal file
BIN
media/app/ahlalalkhair_logo.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
BIN
media/app/masarlogo.jpg
Normal file
BIN
media/app/masarlogo.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
Loading…
x
Reference in New Issue
Block a user