This commit is contained in:
Flatlogic Bot 2026-01-23 17:14:56 +00:00
parent 346ce7c5aa
commit 9964306747
13 changed files with 129 additions and 18 deletions

View File

@ -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
from .models import Profile, Truck, Shipment, Bid, Message, WhatsAppConfig, Country, City, TruckType, AppSetting
from .whatsapp import send_whatsapp_message
@admin.register(Country)
@ -86,4 +86,14 @@ class WhatsAppConfigAdmin(admin.ModelAdmin):
title=_("Send Test WhatsApp Message"),
opts=self.model._meta,
)
return render(request, "admin/core/whatsapp_test.html", context)
return render(request, "admin/core/whatsapp_test.html", context)
@admin.register(AppSetting)
class AppSettingAdmin(admin.ModelAdmin):
list_display = ('app_name', 'contact_phone', 'contact_email')
def has_add_permission(self, request):
# Only allow one configuration record
if self.model.objects.exists():
return False
return super().has_add_permission(request)

View File

@ -1,13 +1,16 @@
import os
import time
from .models import AppSetting
def project_context(request):
"""
Adds project-specific environment variables to the template context globally.
Adds project-specific environment variables and app settings to the template context globally.
"""
app_settings = AppSetting.objects.first()
return {
"project_description": os.getenv("PROJECT_DESCRIPTION", ""),
"project_image_url": os.getenv("PROJECT_IMAGE_URL", ""),
# Used for cache-busting static assets
"deployment_timestamp": int(time.time()),
}
"app_settings": app_settings,
}

View File

@ -0,0 +1,33 @@
# Generated by Django 5.2.7 on 2026-01-23 17:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0013_alter_truck_registration_back_and_more'),
]
operations = [
migrations.CreateModel(
name='AppSetting',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('app_name', models.CharField(max_length=100, verbose_name='App Name')),
('logo', models.ImageField(blank=True, null=True, upload_to='app/', verbose_name='Logo')),
('slogan', models.CharField(blank=True, max_length=255, verbose_name='Slogan')),
('registration_number', models.CharField(blank=True, max_length=100, verbose_name='Registration Number')),
('tax_number', models.CharField(blank=True, max_length=100, verbose_name='Tax Number')),
('contact_phone', models.CharField(blank=True, max_length=20, verbose_name='Contact Phone')),
('contact_email', models.EmailField(blank=True, max_length=254, verbose_name='Contact Email')),
('contact_address', models.TextField(blank=True, verbose_name='Contact Address')),
('terms_of_service', models.TextField(blank=True, verbose_name='Terms of Service')),
('privacy_policy', models.TextField(blank=True, verbose_name='Privacy Policy')),
],
options={
'verbose_name': 'App Setting',
'verbose_name_plural': 'App Settings',
},
),
]

View File

@ -229,6 +229,25 @@ class WhatsAppConfig(models.Model):
def __str__(self):
return str(_("WhatsApp Configuration"))
class AppSetting(models.Model):
app_name = models.CharField(_('App Name'), max_length=100)
logo = models.ImageField(_('Logo'), upload_to='app/', blank=True, null=True)
slogan = models.CharField(_('Slogan'), max_length=255, blank=True)
registration_number = models.CharField(_('Registration Number'), max_length=100, blank=True)
tax_number = models.CharField(_('Tax Number'), max_length=100, blank=True)
contact_phone = models.CharField(_('Contact Phone'), max_length=20, blank=True)
contact_email = models.EmailField(_('Contact Email'), blank=True)
contact_address = models.TextField(_('Contact Address'), blank=True)
terms_of_service = models.TextField(_('Terms of Service'), blank=True)
privacy_policy = models.TextField(_('Privacy Policy'), blank=True)
class Meta:
verbose_name = _('App Setting')
verbose_name_plural = _('App Settings')
def __str__(self):
return self.app_name
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
@ -257,4 +276,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)

View File

@ -6,7 +6,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}MASAR CARGO{% endblock %}</title>
<title>{% block title %}{{ app_settings.app_name|default:"MASAR CARGO" }}{% endblock %}</title>
{% if project_description %}
<meta name="description" content="{{ project_description }}">
{% endif %}
@ -37,8 +37,13 @@
<body>
<nav class="navbar navbar-expand-lg navbar-dark sticky-top">
<div class="container">
<a class="navbar-brand" href="/">
<i class="fa-solid fa-truck-fast me-2 text-info"></i> MASAR CARGO
<a class="navbar-brand d-flex align-items-center" href="/">
{% if app_settings.logo %}
<img src="{{ app_settings.logo.url }}" alt="{{ app_settings.app_name }}" height="40" class="me-2">
{% else %}
<i class="fa-solid fa-truck-fast me-2 text-info"></i>
{% endif %}
<span>{{ app_settings.app_name|default:"MASAR CARGO" }}</span>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
@ -86,6 +91,11 @@
<i class="fa-solid fa-user-circle me-1"></i> {{ user.username }}
</a>
<ul class="dropdown-menu">
{% if user.is_staff %}
<li>
<a class="dropdown-item" href="{% url 'admin:index' %}">{% trans "Admin Panel" %}</a>
</li>
{% endif %}
<li>
<form action="{% url 'logout' %}" method="post">
{% csrf_token %}
@ -126,27 +136,41 @@
<div class="container">
<div class="row">
<div class="col-md-4 mb-4">
<h4 class="fw-bold">MASAR CARGO</h4>
<p class="text-white-50">{% trans "Empowering logistics with smart technology. Locally and abroad." %}</p>
<h4 class="fw-bold">{{ app_settings.app_name|default:"MASAR CARGO" }}</h4>
{% if app_settings.slogan %}
<p class="text-white-50">{{ app_settings.slogan }}</p>
{% else %}
<p class="text-white-50">{% trans "Empowering logistics with smart technology. Locally and abroad." %}</p>
{% endif %}
</div>
<div class="col-md-4 mb-4">
<h5 class="fw-bold">{% trans "Quick Links" %}</h5>
<ul class="list-unstyled">
<li><a href="#" class="text-white-50 text-decoration-none">{% trans "Privacy Policy" %}</a></li>
<li><a href="#" class="text-white-50 text-decoration-none">{% trans "Terms of Service" %}</a></li>
<li><a href="{% url 'privacy_policy' %}" class="text-white-50 text-decoration-none">{% trans "Privacy Policy" %}</a></li>
<li><a href="{% url 'terms_of_service' %}" class="text-white-50 text-decoration-none">{% trans "Terms of Service" %}</a></li>
<li><a href="#" class="text-white-50 text-decoration-none">{% trans "Contact Us" %}</a></li>
</ul>
</div>
<div class="col-md-4 mb-4">
<h5 class="fw-bold">{% trans "Contact" %}</h5>
<p class="text-white-50">
<i class="fa-brands fa-whatsapp me-2"></i> +123 456 7890<br>
<i class="fa-solid fa-envelope me-2"></i> info@masarcargo.com
{% if app_settings.contact_phone %}
<i class="fa-solid fa-phone me-2"></i> {{ app_settings.contact_phone }}<br>
{% endif %}
{% if app_settings.contact_email %}
<i class="fa-solid fa-envelope me-2"></i> {{ app_settings.contact_email }}
{% endif %}
</p>
{% if app_settings.registration_number %}
<p class="text-white-50 small mb-1">{% trans "CR No:" %} {{ app_settings.registration_number }}</p>
{% endif %}
{% if app_settings.tax_number %}
<p class="text-white-50 small mb-0">{% trans "VAT No:" %} {{ app_settings.tax_number }}</p>
{% endif %}
</div>
</div>
<hr class="bg-white-50">
<p class="text-center text-white-50 mb-0">&copy; 2026 MASAR CARGO. All rights reserved.</p>
<p class="text-center text-white-50 mb-0">&copy; 2026 {{ app_settings.app_name|default:"MASAR CARGO" }}. All rights reserved.</p>
</div>
</footer>
<div class="container text-center text-muted small py-2">Debug: Lang={{ CURRENT_LANG }}, Cookie={{ request.COOKIES.django_language }}</div>

View File

@ -20,4 +20,6 @@ urlpatterns = [
path("truck/<int:truck_id>/offer/", views.place_bid, name="place_bid"),
path("bid/<int:bid_id>/accept/", views.accept_bid, name="accept_bid"),
path("bid/<int:bid_id>/reject/", views.reject_bid, name="reject_bid"),
]
path("privacy-policy/", views.privacy_policy, name="privacy_policy"),
path("terms-of-service/", views.terms_of_service, name="terms_of_service"),
]

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 import login, authenticate, logout
from django.utils import timezone
from .models import Profile, Truck, Shipment, Bid, Message, OTPCode, Country, City
from .models import Profile, Truck, Shipment, Bid, Message, OTPCode, Country, City, AppSetting
from .forms import TruckForm, ShipmentForm, BidForm, UserRegistrationForm, OTPVerifyForm, ShipperOfferForm
from django.contrib import messages
from django.utils.translation import gettext as _
@ -377,4 +377,24 @@ def reject_bid(request, bid_id):
bid.status = 'REJECTED'
bid.save()
messages.info(request, _("Offer rejected."))
return redirect('dashboard')
return redirect('dashboard')
def privacy_policy(request):
app_settings = AppSetting.objects.first()
context = {
'article': {
'title': _('Privacy Policy'),
'content': app_settings.privacy_policy if app_settings else _("Privacy policy is coming soon.")
}
}
return render(request, 'core/article_detail.html', context)
def terms_of_service(request):
app_settings = AppSetting.objects.first()
context = {
'article': {
'title': _('Terms of Service'),
'content': app_settings.terms_of_service if app_settings else _("Terms of service are coming soon.")
}
}
return render(request, 'core/article_detail.html', context)