demo2
This commit is contained in:
parent
de92a4ccc0
commit
ba8173f9d8
Binary file not shown.
@ -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',
|
||||
}
|
||||
|
||||
|
||||
BIN
core/__pycache__/forms.cpython-311.pyc
Normal file
BIN
core/__pycache__/forms.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
52
core/forms.py
Normal file
52
core/forms.py
Normal 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)
|
||||
104
core/templates/core/admin_dashboard.html
Normal file
104
core/templates/core/admin_dashboard.html
Normal 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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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 %}
|
||||
@ -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()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user