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_URL = 'media/'
MEDIA_ROOT = BASE_DIR / '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 }} <strong>{% trans "Goods:" %}</strong> {{ shipment.description }}
</div> </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"> <form method="post">
{% csrf_token %} {% csrf_token %}
<div class="mb-3"> <div class="mb-3">
<label class="form-label">{% trans "Select Truck" %}</label> <label class="form-label">{% trans "Select Truck" %}</label>
<select name="truck" class="form-select" required> {{ form.truck }}
{% for truck in trucks %} {{ form.truck.errors }}
<option value="{{ truck.id }}">{{ truck.truck_type }} - {{ truck.plate_no }} ({{ truck.load_capacity }})</option>
{% endfor %}
</select>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">{% trans "Your Offer Amount" %}</label> <label class="form-label">{% trans "Your Offer Amount" %}</label>
<div class="input-group"> <div class="input-group">
<span class="input-group-text">$</span> <span class="input-group-text">$</span>
<input type="number" name="amount" class="form-control" step="0.01" required> {{ form.amount }}
</div> </div>
{{ form.amount.errors }}
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">{% trans "Comments/Conditions" %}</label> <label class="form-label">{% trans "Comments/Conditions" %}</label>
<textarea name="comments" class="form-control" rows="3"></textarea> {{ form.comments }}
{{ form.comments.errors }}
</div> </div>
<button type="submit" class="btn btn-primary w-100 py-3">{% trans "Submit Offer" %}</button> <button type="submit" class="btn btn-primary w-100 py-3">{% trans "Submit Offer" %}</button>
</form> </form>

View File

@ -8,29 +8,42 @@
<div class="card shadow"> <div class="card shadow">
<div class="card-body p-5"> <div class="card-body p-5">
<h2 class="mb-4">{% trans "Post a New Shipment" %}</h2> <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"> <form method="post">
{% csrf_token %} {% csrf_token %}
<div class="mb-3"> <div class="mb-3">
<label class="form-label">{% trans "Goods Description" %}</label> <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>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">{% trans "Weight/Volume" %}</label> <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>
<div class="row"> <div class="row">
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">{% trans "Origin" %}</label> <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>
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">{% trans "Destination" %}</label> <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> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">{% trans "Requested Delivery Date" %}</label> <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> </div>
<button type="submit" class="btn btn-primary w-100 py-3 mt-4">{% trans "Post Shipment" %}</button> <button type="submit" class="btn btn-primary w-100 py-3 mt-4">{% trans "Post Shipment" %}</button>
</form> </form>

View File

@ -8,35 +8,49 @@
<div class="card shadow"> <div class="card shadow">
<div class="card-body p-5"> <div class="card-body p-5">
<h2 class="mb-4">{% trans "Register a Truck" %}</h2> <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"> <form method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<div class="row"> <div class="row">
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">{% trans "Truck Type" %}</label> <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>
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">{% trans "Model" %}</label> <label class="form-label">{% trans "Model" %}</label>
<input type="text" name="model" class="form-control" required> {{ form.model }}
{{ form.model.errors }}
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-4 mb-3"> <div class="col-md-4 mb-3">
<label class="form-label">{% trans "Year" %}</label> <label class="form-label">{% trans "Year" %}</label>
<input type="number" name="year" class="form-control" required> {{ form.year }}
{{ form.year.errors }}
</div> </div>
<div class="col-md-4 mb-3"> <div class="col-md-4 mb-3">
<label class="form-label">{% trans "Plate No" %}</label> <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>
<div class="col-md-4 mb-3"> <div class="col-md-4 mb-3">
<label class="form-label">{% trans "Color" %}</label> <label class="form-label">{% trans "Color" %}</label>
<input type="text" name="color" class="form-control" required> {{ form.color }}
{{ form.color.errors }}
</div> </div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">{% trans "Load Capacity (e.g. 20 Tons)" %}</label> <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> </div>
<hr class="my-4"> <hr class="my-4">
@ -45,21 +59,25 @@
<div class="row"> <div class="row">
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">{% trans "Truck Picture" %}</label> <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>
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">{% trans "Driver License" %}</label> <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> </div>
<div class="row"> <div class="row">
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">{% trans "Registration (Front Face)" %}</label> <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>
<div class="col-md-6 mb-3"> <div class="col-md-6 mb-3">
<label class="form-label">{% trans "Registration (Back Face)" %}</label> <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>
</div> </div>

View File

@ -4,14 +4,14 @@ from django.contrib.auth import login, authenticate
from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.forms import UserCreationForm
from django.utils import timezone from django.utils import timezone
from .models import Profile, Truck, Shipment, Bid, Message from .models import Profile, Truck, Shipment, Bid, Message
from .forms import TruckForm, ShipmentForm, BidForm
from django.contrib import messages from django.contrib import messages
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.db.models import Q from django.db.models import Q
from django.contrib.auth.models import User
def home(request): def home(request):
"""Render the landing screen for MASAR CARGO.""" """Render the landing screen for MASAR CARGO."""
if request.user.is_authenticated:
return redirect('dashboard')
context = { context = {
"deployment_timestamp": timezone.now().timestamp(), "deployment_timestamp": timezone.now().timestamp(),
} }
@ -47,8 +47,17 @@ def dashboard(request):
'trucks': my_trucks, 'trucks': my_trucks,
'bids': my_bids '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: else:
return redirect('/admin/') # Fallback for undefined roles
return redirect('/')
@login_required @login_required
def truck_register(request): def truck_register(request):
@ -56,30 +65,19 @@ def truck_register(request):
return redirect('dashboard') return redirect('dashboard')
if request.method == 'POST': if request.method == 'POST':
truck_type = request.POST.get('truck_type') form = TruckForm(request.POST, request.FILES)
model = request.POST.get('model') if form.is_valid():
year = request.POST.get('year') truck = form.save(commit=False)
plate_no = request.POST.get('plate_no') truck.owner = request.user
load_capacity = request.POST.get('load_capacity') truck.save()
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!")) messages.success(request, _("Truck registered successfully!"))
return redirect('dashboard') 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 @login_required
def post_shipment(request): def post_shipment(request):
@ -120,31 +118,28 @@ def place_bid(request, shipment_id):
if request.user.profile.role != 'TRUCK_OWNER': if request.user.profile.role != 'TRUCK_OWNER':
return redirect('dashboard') return redirect('dashboard')
my_trucks = Truck.objects.filter(owner=request.user)
if request.method == 'POST': if request.method == 'POST':
truck_id = request.POST.get('truck') form = BidForm(request.POST, user=request.user)
amount = request.POST.get('amount') if form.is_valid():
comments = request.POST.get('comments') bid = form.save(commit=False)
bid.truck_owner = request.user
truck = get_object_or_404(Truck, id=truck_id, owner=request.user) bid.shipment = shipment
Bid.objects.create( bid.save()
shipment=shipment,
truck_owner=request.user,
truck=truck,
amount=amount,
comments=comments
)
messages.success(request, _("Bid placed successfully!")) messages.success(request, _("Bid placed successfully!"))
return redirect('marketplace') 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 @login_required
def shipment_detail(request, shipment_id): def shipment_detail(request, shipment_id):
shipment = get_object_or_404(Shipment, id=shipment_id) shipment = get_object_or_404(Shipment, id=shipment_id)
# Security: check if user is shipper or a truck owner who bid # 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 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') return redirect('dashboard')
bids = shipment.bids.all() bids = shipment.bids.all()