This commit is contained in:
Flatlogic Bot 2026-01-23 09:55:56 +00:00
parent de92a4ccc0
commit ba8173f9d8
10 changed files with 267 additions and 69 deletions

View File

@ -196,3 +196,13 @@ LOCALE_PATHS = [
MEDIA_URL = 'media/'
MEDIA_ROOT = BASE_DIR / 'media'
from django.contrib.messages import constants as messages
MESSAGE_TAGS = {
messages.DEBUG: 'secondary',
messages.INFO: 'info',
messages.SUCCESS: 'success',
messages.WARNING: 'warning',
messages.ERROR: 'danger',
}

Binary file not shown.

52
core/forms.py Normal file
View File

@ -0,0 +1,52 @@
from django import forms
from .models import Truck, Shipment, Bid
from django.utils.translation import gettext_lazy as _
class TruckForm(forms.ModelForm):
class Meta:
model = Truck
fields = [
'truck_type', 'model', 'year', 'plate_no',
'load_capacity', 'color', 'truck_picture',
'registration_front', 'registration_back', 'driver_license'
]
widgets = {
'truck_type': forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('e.g. Flatbed, Trailer')}),
'model': forms.TextInput(attrs={'class': 'form-control'}),
'year': forms.NumberInput(attrs={'class': 'form-control'}),
'plate_no': forms.TextInput(attrs={'class': 'form-control'}),
'load_capacity': forms.TextInput(attrs={'class': 'form-control'}),
'color': forms.TextInput(attrs={'class': 'form-control'}),
'truck_picture': forms.FileInput(attrs={'class': 'form-control'}),
'registration_front': forms.FileInput(attrs={'class': 'form-control'}),
'registration_back': forms.FileInput(attrs={'class': 'form-control'}),
'driver_license': forms.FileInput(attrs={'class': 'form-control'}),
}
class ShipmentForm(forms.ModelForm):
class Meta:
model = Shipment
fields = ['description', 'weight', 'origin', 'destination', 'delivery_date']
widgets = {
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'weight': forms.TextInput(attrs={'class': 'form-control'}),
'origin': forms.TextInput(attrs={'class': 'form-control'}),
'destination': forms.TextInput(attrs={'class': 'form-control'}),
'delivery_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
}
class BidForm(forms.ModelForm):
class Meta:
model = Bid
fields = ['truck', 'amount', 'comments']
widgets = {
'truck': forms.Select(attrs={'class': 'form-select'}),
'amount': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'}),
'comments': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
if user:
self.fields['truck'].queryset = Truck.objects.filter(owner=user)

View File

@ -0,0 +1,104 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Admin Dashboard" %} - MASAR CARGO{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row mb-4">
<div class="col">
<h2 class="fw-bold">{% trans "Administrator Dashboard" %}</h2>
<p class="text-muted">{% trans "System overview and management" %}</p>
</div>
<div class="col-auto">
<a href="/admin/" class="btn btn-outline-primary">
<i class="fa-solid fa-gears me-2"></i>{% trans "Go to Django Admin" %}
</a>
</div>
</div>
<div class="row g-4">
<div class="col-md-3">
<div class="card border-0 shadow-sm text-center p-4">
<div class="display-5 text-primary mb-2"><i class="fa-solid fa-users"></i></div>
<h3 class="fw-bold">{{ total_users }}</h3>
<p class="text-muted mb-0">{% trans "Total Users" %}</p>
</div>
</div>
<div class="col-md-3">
<div class="card border-0 shadow-sm text-center p-4">
<div class="display-5 text-success mb-2"><i class="fa-solid fa-truck"></i></div>
<h3 class="fw-bold">{{ total_trucks }}</h3>
<p class="text-muted mb-0">{% trans "Registered Trucks" %}</p>
</div>
</div>
<div class="col-md-3">
<div class="card border-0 shadow-sm text-center p-4">
<div class="display-5 text-warning mb-2"><i class="fa-solid fa-box"></i></div>
<h3 class="fw-bold">{{ total_shipments }}</h3>
<p class="text-muted mb-0">{% trans "Total Shipments" %}</p>
</div>
</div>
<div class="col-md-3">
<div class="card border-0 shadow-sm text-center p-4">
<div class="display-5 text-info mb-2"><i class="fa-solid fa-hand-holding-dollar"></i></div>
<h3 class="fw-bold">{{ total_bids }}</h3>
<p class="text-muted mb-0">{% trans "Active Bids" %}</p>
</div>
</div>
</div>
<div class="mt-5">
<h4 class="fw-bold mb-4">{% trans "Quick Actions" %}</h4>
<div class="row g-3">
<div class="col-md-4">
<a href="/" class="card border-0 shadow-sm p-3 text-decoration-none text-dark h-100 hover-card">
<div class="d-flex align-items-center">
<div class="rounded-circle bg-light p-3 me-3 text-primary">
<i class="fa-solid fa-house"></i>
</div>
<div>
<h6 class="mb-0 fw-bold">{% trans "View Landing Page" %}</h6>
<small class="text-muted">{% trans "See how the site looks to visitors" %}</small>
</div>
</div>
</a>
</div>
<div class="col-md-4">
<a href="/admin/core/shipment/" class="card border-0 shadow-sm p-3 text-decoration-none text-dark h-100 hover-card">
<div class="d-flex align-items-center">
<div class="rounded-circle bg-light p-3 me-3 text-warning">
<i class="fa-solid fa-clipboard-list"></i>
</div>
<div>
<h6 class="mb-0 fw-bold">{% trans "Manage Shipments" %}</h6>
<small class="text-muted">{% trans "Approve or moderate shipments" %}</small>
</div>
</div>
</a>
</div>
<div class="col-md-4">
<a href="/admin/auth/user/" class="card border-0 shadow-sm p-3 text-decoration-none text-dark h-100 hover-card">
<div class="d-flex align-items-center">
<div class="rounded-circle bg-light p-3 me-3 text-success">
<i class="fa-solid fa-user-gear"></i>
</div>
<div>
<h6 class="mb-0 fw-bold">{% trans "Manage Users" %}</h6>
<small class="text-muted">{% trans "Review registered profiles" %}</small>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
<style>
.hover-card:hover {
background-color: #f1f8ff !important;
transform: translateY(-5px);
transition: all 0.3s ease;
}
</style>
{% endblock %}

View File

@ -13,27 +13,33 @@
<strong>{% trans "Goods:" %}</strong> {{ shipment.description }}
</div>
{% if trucks %}
{% if form.errors %}
<div class="alert alert-danger">
{% trans "Please correct the errors below." %}
{{ form.non_field_errors }}
</div>
{% endif %}
{% if form.fields.truck.queryset.exists %}
<form method="post">
{% csrf_token %}
<div class="mb-3">
<label class="form-label">{% trans "Select Truck" %}</label>
<select name="truck" class="form-select" required>
{% for truck in trucks %}
<option value="{{ truck.id }}">{{ truck.truck_type }} - {{ truck.plate_no }} ({{ truck.load_capacity }})</option>
{% endfor %}
</select>
{{ form.truck }}
{{ form.truck.errors }}
</div>
<div class="mb-3">
<label class="form-label">{% trans "Your Offer Amount" %}</label>
<div class="input-group">
<span class="input-group-text">$</span>
<input type="number" name="amount" class="form-control" step="0.01" required>
{{ form.amount }}
</div>
{{ form.amount.errors }}
</div>
<div class="mb-3">
<label class="form-label">{% trans "Comments/Conditions" %}</label>
<textarea name="comments" class="form-control" rows="3"></textarea>
{{ form.comments }}
{{ form.comments.errors }}
</div>
<button type="submit" class="btn btn-primary w-100 py-3">{% trans "Submit Offer" %}</button>
</form>
@ -48,4 +54,4 @@
</div>
</div>
</div>
{% endblock %}
{% endblock %}

View File

@ -8,29 +8,42 @@
<div class="card shadow">
<div class="card-body p-5">
<h2 class="mb-4">{% trans "Post a New Shipment" %}</h2>
{% if form.errors %}
<div class="alert alert-danger">
{% trans "Please correct the errors below." %}
{{ form.non_field_errors }}
</div>
{% endif %}
<form method="post">
{% csrf_token %}
<div class="mb-3">
<label class="form-label">{% trans "Goods Description" %}</label>
<textarea name="description" class="form-control" rows="4" placeholder="{% trans 'What are you moving? (e.g. 500 boxes of food)' %}" required></textarea>
{{ form.description }}
{{ form.description.errors }}
</div>
<div class="mb-3">
<label class="form-label">{% trans "Weight/Volume" %}</label>
<input type="text" name="weight" class="form-control" placeholder="e.g. 5 Tons" required>
{{ form.weight }}
{{ form.weight.errors }}
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">{% trans "Origin" %}</label>
<input type="text" name="origin" class="form-control" placeholder="{% trans 'City, Country' %}" required>
{{ form.origin }}
{{ form.origin.errors }}
</div>
<div class="col-md-6 mb-3">
<label class="form-label">{% trans "Destination" %}</label>
<input type="text" name="destination" class="form-control" placeholder="{% trans 'City, Country' %}" required>
{{ form.destination }}
{{ form.destination.errors }}
</div>
</div>
<div class="mb-3">
<label class="form-label">{% trans "Requested Delivery Date" %}</label>
<input type="date" name="delivery_date" class="form-control" required>
{{ form.delivery_date }}
{{ form.delivery_date.errors }}
</div>
<button type="submit" class="btn btn-primary w-100 py-3 mt-4">{% trans "Post Shipment" %}</button>
</form>
@ -39,4 +52,4 @@
</div>
</div>
</div>
{% endblock %}
{% endblock %}

View File

@ -8,35 +8,49 @@
<div class="card shadow">
<div class="card-body p-5">
<h2 class="mb-4">{% trans "Register a Truck" %}</h2>
{% if form.errors %}
<div class="alert alert-danger">
{% trans "Please correct the errors below." %}
{{ form.non_field_errors }}
</div>
{% endif %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">{% trans "Truck Type" %}</label>
<input type="text" name="truck_type" class="form-control" placeholder="e.g. Flatbed, Trailer" required>
{{ form.truck_type }}
{{ form.truck_type.errors }}
</div>
<div class="col-md-6 mb-3">
<label class="form-label">{% trans "Model" %}</label>
<input type="text" name="model" class="form-control" required>
{{ form.model }}
{{ form.model.errors }}
</div>
</div>
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label">{% trans "Year" %}</label>
<input type="number" name="year" class="form-control" required>
{{ form.year }}
{{ form.year.errors }}
</div>
<div class="col-md-4 mb-3">
<label class="form-label">{% trans "Plate No" %}</label>
<input type="text" name="plate_no" class="form-control" required>
{{ form.plate_no }}
{{ form.plate_no.errors }}
</div>
<div class="col-md-4 mb-3">
<label class="form-label">{% trans "Color" %}</label>
<input type="text" name="color" class="form-control" required>
{{ form.color }}
{{ form.color.errors }}
</div>
</div>
<div class="mb-3">
<label class="form-label">{% trans "Load Capacity (e.g. 20 Tons)" %}</label>
<input type="text" name="load_capacity" class="form-control" required>
{{ form.load_capacity }}
{{ form.load_capacity.errors }}
</div>
<hr class="my-4">
@ -45,21 +59,25 @@
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">{% trans "Truck Picture" %}</label>
<input type="file" name="truck_picture" class="form-control" accept="image/*">
{{ form.truck_picture }}
{{ form.truck_picture.errors }}
</div>
<div class="col-md-6 mb-3">
<label class="form-label">{% trans "Driver License" %}</label>
<input type="file" name="driver_license" class="form-control" accept="image/*">
{{ form.driver_license }}
{{ form.driver_license.errors }}
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label">{% trans "Registration (Front Face)" %}</label>
<input type="file" name="registration_front" class="form-control" accept="image/*">
{{ form.registration_front }}
{{ form.registration_front.errors }}
</div>
<div class="col-md-6 mb-3">
<label class="form-label">{% trans "Registration (Back Face)" %}</label>
<input type="file" name="registration_back" class="form-control" accept="image/*">
{{ form.registration_back }}
{{ form.registration_back.errors }}
</div>
</div>
@ -70,4 +88,4 @@
</div>
</div>
</div>
{% endblock %}
{% endblock %}

View File

@ -4,14 +4,14 @@ from django.contrib.auth import login, authenticate
from django.contrib.auth.forms import UserCreationForm
from django.utils import timezone
from .models import Profile, Truck, Shipment, Bid, Message
from .forms import TruckForm, ShipmentForm, BidForm
from django.contrib import messages
from django.utils.translation import gettext as _
from django.db.models import Q
from django.contrib.auth.models import User
def home(request):
"""Render the landing screen for MASAR CARGO."""
if request.user.is_authenticated:
return redirect('dashboard')
context = {
"deployment_timestamp": timezone.now().timestamp(),
}
@ -47,8 +47,17 @@ def dashboard(request):
'trucks': my_trucks,
'bids': my_bids
})
elif profile.role == 'ADMIN' or request.user.is_superuser:
context = {
'total_users': User.objects.count(),
'total_trucks': Truck.objects.count(),
'total_shipments': Shipment.objects.count(),
'total_bids': Bid.objects.count(),
}
return render(request, 'core/admin_dashboard.html', context)
else:
return redirect('/admin/')
# Fallback for undefined roles
return redirect('/')
@login_required
def truck_register(request):
@ -56,30 +65,19 @@ def truck_register(request):
return redirect('dashboard')
if request.method == 'POST':
truck_type = request.POST.get('truck_type')
model = request.POST.get('model')
year = request.POST.get('year')
plate_no = request.POST.get('plate_no')
load_capacity = request.POST.get('load_capacity')
color = request.POST.get('color')
truck = Truck.objects.create(
owner=request.user,
truck_type=truck_type,
model=model,
year=year,
plate_no=plate_no,
load_capacity=load_capacity,
color=color,
truck_picture=request.FILES.get('truck_picture'),
registration_front=request.FILES.get('registration_front'),
registration_back=request.FILES.get('registration_back'),
driver_license=request.FILES.get('driver_license')
)
messages.success(request, _("Truck registered successfully!"))
return redirect('dashboard')
form = TruckForm(request.POST, request.FILES)
if form.is_valid():
truck = form.save(commit=False)
truck.owner = request.user
truck.save()
messages.success(request, _("Truck registered successfully!"))
return redirect('dashboard')
else:
messages.error(request, _("There was an error in your registration. Please check the form."))
else:
form = TruckForm()
return render(request, 'core/truck_register.html')
return render(request, 'core/truck_register.html', {'form': form})
@login_required
def post_shipment(request):
@ -120,31 +118,28 @@ def place_bid(request, shipment_id):
if request.user.profile.role != 'TRUCK_OWNER':
return redirect('dashboard')
my_trucks = Truck.objects.filter(owner=request.user)
if request.method == 'POST':
truck_id = request.POST.get('truck')
amount = request.POST.get('amount')
comments = request.POST.get('comments')
truck = get_object_or_404(Truck, id=truck_id, owner=request.user)
Bid.objects.create(
shipment=shipment,
truck_owner=request.user,
truck=truck,
amount=amount,
comments=comments
)
messages.success(request, _("Bid placed successfully!"))
return redirect('marketplace')
form = BidForm(request.POST, user=request.user)
if form.is_valid():
bid = form.save(commit=False)
bid.truck_owner = request.user
bid.shipment = shipment
bid.save()
messages.success(request, _("Bid placed successfully!"))
return redirect('marketplace')
else:
messages.error(request, _("Error placing bid. Please check the form."))
else:
form = BidForm(user=request.user)
return render(request, 'core/place_bid.html', {'shipment': shipment, 'trucks': my_trucks})
return render(request, 'core/place_bid.html', {'form': form, 'shipment': shipment})
@login_required
def shipment_detail(request, shipment_id):
shipment = get_object_or_404(Shipment, id=shipment_id)
# Security: check if user is shipper or a truck owner who bid
if shipment.shipper != request.user and not Bid.objects.filter(shipment=shipment, truck_owner=request.user).exists():
if request.user.profile.role != 'ADMIN':
if request.user.profile.role != 'ADMIN' and not request.user.is_superuser:
return redirect('dashboard')
bids = shipment.bids.all()