diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc index b9f3e54..82e7cc0 100644 Binary files a/core/__pycache__/forms.cpython-311.pyc and b/core/__pycache__/forms.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index fddd747..aadbeb1 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index be364c3..0d02fbb 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/forms.py b/core/forms.py index b575ed7..d48204e 100644 --- a/core/forms.py +++ b/core/forms.py @@ -115,4 +115,13 @@ class BidForm(forms.ModelForm): # Only allow bidding with approved trucks self.fields['truck'].queryset = Truck.objects.filter(owner=user, is_approved=True) if not self.fields['truck'].queryset.exists(): - self.fields['truck'].help_text = _("You must have an approved truck to place a bid.") \ No newline at end of file + self.fields['truck'].help_text = _("You must have an approved truck to place a bid.") + +class ShipperOfferForm(forms.Form): + description = forms.CharField(label=_('Goods Description'), widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 3})) + weight = forms.CharField(label=_('Weight/Volume'), widget=forms.TextInput(attrs={'class': 'form-control'})) + origin = forms.CharField(label=_('Origin'), widget=forms.TextInput(attrs={'class': 'form-control'})) + destination = forms.CharField(label=_('Destination'), widget=forms.TextInput(attrs={'class': 'form-control'})) + delivery_date = forms.DateField(label=_('Requested Delivery Date'), widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'})) + amount = forms.DecimalField(label=_('Offer Amount'), max_digits=10, decimal_places=2, widget=forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'})) + comments = forms.CharField(label=_('Comments'), required=False, widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 2})) diff --git a/core/templates/core/marketplace.html b/core/templates/core/marketplace.html index d3c8358..359a453 100644 --- a/core/templates/core/marketplace.html +++ b/core/templates/core/marketplace.html @@ -1,39 +1,50 @@ {% extends "base.html" %} {% load i18n %} +{% load static %} {% block content %}
-

{% trans "Shipment Marketplace" %}

+

{% trans "Truck Marketplace" %}

+

{% trans "Browse available trucks and send your shipping offers directly to truck owners." %}

+
- {% for shipment in shipments %} -
-
+ {% for truck in trucks %} +
+
+ {% if truck.truck_picture %} + {{ truck.display_truck_type }} + {% else %} +
+ +
+ {% endif %}
- {% trans "Open for Bids" %} - {{ shipment.created_at|timesince }} {% trans "ago" %} + {% trans "Available" %} + {% trans "Plate" %}: {{ truck.plate_no }}
-
{{ shipment.origin }} {{ shipment.destination }}
-

{{ shipment.description|truncatechars:100 }}

-
-
- {% trans "Weight" %} - {{ shipment.weight }} -
-
- {% trans "Delivery Date" %} - {{ shipment.delivery_date }} -
+
{{ truck.display_truck_type }} - {{ truck.display_model }}
+
+ {% trans "Year" %}: {{ truck.year }} + {% trans "Capacity" %}: {{ truck.display_load_capacity }}
- {% trans "Place an Offer" %} +

+ {% trans "Owner" %}: {{ truck.owner.username }} +

+ + {% trans "Send Shipping Offer" %} +
{% empty %}
-

{% trans "No shipments available at the moment." %}

+
+ +

{% trans "No approved trucks are currently available." %}

+
{% endfor %}
-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/core/templates/core/place_bid.html b/core/templates/core/place_bid.html index ea68a77..6abaa7d 100644 --- a/core/templates/core/place_bid.html +++ b/core/templates/core/place_bid.html @@ -4,54 +4,94 @@ {% block content %}
-
-
+
+
-

{% trans "Place an Offer" %}

-
- {% trans "Shipment:" %} {{ shipment.origin }} to {{ shipment.destination }}
- {% trans "Goods:" %} {{ shipment.description }} +

{% trans "Send Shipping Offer" %}

+ +
+
+
{% trans "Target Truck" %}
+
+ {% if truck.truck_picture %} + + {% else %} +
+ +
+ {% endif %} +
+
{{ truck.display_truck_type }} - {{ truck.display_model }}
+ {% trans "Owner" %}: {{ truck.owner.username }} | {% trans "Plate" %}: {{ truck.plate_no }} +
+
+
- {% if form.errors %} -
- {% trans "Please correct the errors below." %} - {{ form.non_field_errors }} -
- {% endif %} - - {% if form.fields.truck.queryset.exists %} -
+ {% csrf_token %} + +
{% trans "Shipment Details" %}
+
- - {{ form.truck }} - {{ form.truck.errors }} + + {{ form.description }} + {{ form.description.errors }}
+ +
+
+ + {{ form.origin }} + {{ form.origin.errors }} +
+
+ + {{ form.destination }} + {{ form.destination.errors }} +
+
+ +
+
+ + {{ form.weight }} + {{ form.weight.errors }} +
+
+ + {{ form.delivery_date }} + {{ form.delivery_date.errors }} +
+
+ +
{% trans "Pricing & Terms" %}
+
- +
$ {{ form.amount }}
{{ form.amount.errors }}
-
- + +
+ {{ form.comments }} {{ form.comments.errors }}
- + +
+ + {% trans "Cancel" %} +
- {% else %} -
-

{% trans "You must register a truck before placing a bid." %}

- {% trans "Register Truck Now" %} -
- {% endif %}
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/core/templates/core/shipment_detail.html b/core/templates/core/shipment_detail.html index 7238546..df5aab2 100644 --- a/core/templates/core/shipment_detail.html +++ b/core/templates/core/shipment_detail.html @@ -14,50 +14,61 @@

-
{% trans "Details" %}
-

{{ shipment.description }}

+
{% trans "Shipment Details" %}
+

{{ shipment.description }}

{% trans "Weight" %} {{ shipment.weight }}
- {% trans "Delivery Date" %} + {% trans "Requested Delivery Date" %} {{ shipment.delivery_date }}
- {% if user == shipment.shipper and shipment.status == 'OPEN' %} -
+ +
-
{% trans "Received Bids" %}
+
{% trans "Offer Status" %}
- + {% for bid in bids %} - - + + {% empty %} - + {% endfor %} @@ -65,37 +76,49 @@ - {% endif %} {% if shipment.status == 'IN_PROGRESS' %} -
- +
+
- {% trans "Shipment in progress!" %}
- {% trans "Assigned Truck:" %} {{ shipment.assigned_truck.display_truck_type }} ({{ shipment.assigned_truck.plate_no }}) +
{% trans "Shipment is IN PROGRESS" %}
+

{% trans "Assigned Truck:" %} {{ shipment.assigned_truck.display_truck_type }} ({{ shipment.assigned_truck.plate_no }})

- {% endif %}
-
+
-
{% trans "Contact Information" %}
-

- {% trans "Shipper:" %} {{ shipment.shipper.username }}
- {% if shipment.status == 'IN_PROGRESS' %} - {% trans "Phone:" %} {{ shipment.shipper.profile.phone_number }} - {% endif %} -

+
{% trans "Stakeholders" %}
+
+ {% trans "Shipper" %} + {{ shipment.shipper.username }} +
+ {% if shipment.assigned_truck %} +
+ {% trans "Truck Owner" %} + {{ shipment.assigned_truck.owner.username }} +
+ {% endif %}
+ + {% if shipment.status == 'IN_PROGRESS' %} +
+ {% if user == shipment.shipper %} + + {% trans "Message Driver" %} + + {% elif user == shipment.assigned_truck.owner %} + + {% trans "Message Shipper" %} + + {% endif %} +
+ {% endif %}
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/core/templates/core/shipper_dashboard.html b/core/templates/core/shipper_dashboard.html index 9672079..cf8ba59 100644 --- a/core/templates/core/shipper_dashboard.html +++ b/core/templates/core/shipper_dashboard.html @@ -4,58 +4,126 @@ {% block content %}
{% trans "Truck Owner" %} {% trans "Truck" %} {% trans "Amount" %}{% trans "Status" %} {% trans "Action" %}
{{ bid.truck_owner.username }}{{ bid.truck.display_truck_type }}{{ bid.truck.display_truck_type }} ({{ bid.truck.plate_no }}) ${{ bid.amount }} - {% trans "Accept" %} + + {{ bid.get_status_display }} + + + {% if bid.status == 'PENDING' and user == bid.truck_owner %} + + {% else %} + {% trans "No action available" %} + {% endif %}
{% trans "No bids received yet." %}{% trans "No offers for this shipment." %}
- - - - - - - - - - - - {% for shipment in shipments %} - - - - - - - - - {% empty %} - - - - {% endfor %} - -
{% trans "Description" %}{% trans "Route" %}{% trans "Delivery Date" %}{% trans "Status" %}{% trans "Bids" %}{% trans "Action" %}
{{ shipment.description|truncatechars:30 }}{{ shipment.origin }} {{ shipment.destination }}{{ shipment.delivery_date }} - - {{ shipment.get_status_display }} - - {{ shipment.bids.count }} - {% trans "View Details" %} -
{% trans "No shipments posted yet." %}
-
-
+ +
+
+
{% trans "My Shipping Offers" %}
+
+
+
+ + + + + + + + + + + + + {% for bid in bids %} + + + + + + + + + {% empty %} + + + + {% endfor %} + +
{% trans "Truck" %}{% trans "Route" %}{% trans "Amount" %}{% trans "Date Sent" %}{% trans "Status" %}{% trans "Action" %}
+
{{ bid.truck.display_truck_type }}
+ {{ bid.truck.plate_no }} +
+ {{ bid.shipment.origin }} + + {{ bid.shipment.destination }} + ${{ bid.amount }}{{ bid.created_at|date:"d M Y" }} + {% if bid.status == 'PENDING' %} + {% trans "Pending" %} + {% elif bid.status == 'ACCEPTED' %} + {% trans "Accepted" %} + {% else %} + {% trans "Rejected" %} + {% endif %} + + {% trans "Details" %} +
+ +

{% trans "You haven't sent any offers yet." %}

+ {% trans "Find a Truck" %} +
+
+
+
+ + +
+
+
{% trans "Active Shipments" %}
+
+
+
+ + + + + + + + + + + + + {% for shipment in shipments %} + {% if shipment.status != 'OPEN' or not shipment.bids.exists %} + + + + + + + + + {% endif %} + {% empty %} + + + + {% endfor %} + +
{% trans "Description" %}{% trans "Route" %}{% trans "Delivery Date" %}{% trans "Status" %}{% trans "Assigned Truck" %}{% trans "Action" %}
{{ shipment.description|truncatechars:30 }}{{ shipment.origin }} {{ shipment.destination }}{{ shipment.delivery_date }} + + {{ shipment.get_status_display }} + + + {% if shipment.assigned_truck %} + {{ shipment.assigned_truck.plate_no }} + {% else %} + {% trans "None" %} + {% endif %} + + {% trans "Track" %} +
{% trans "No active shipments." %}
-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/core/templates/core/truck_owner_dashboard.html b/core/templates/core/truck_owner_dashboard.html index f51caf0..8c1013d 100644 --- a/core/templates/core/truck_owner_dashboard.html +++ b/core/templates/core/truck_owner_dashboard.html @@ -7,121 +7,119 @@

{% trans "Truck Owner Dashboard" %}

-

{% trans "Manage your fleet and active bids." %}

+

{% trans "Manage your fleet and incoming shipping offers." %}

- -

{% trans "My Approved Trucks" %}

-
- {% if trucks %} - {% for truck in trucks %} -
-
- {% if truck.truck_picture %} - {{ truck.display_truck_type }} - {% else %} -
- -
- {% endif %} -
-
{{ truck.display_truck_type }}
-

{% trans "Plate No:" %} {{ truck.plate_no }}

-

{% trans "Model:" %} {{ truck.display_model }} ({{ truck.year }})

-

{% trans "Capacity:" %} {{ truck.display_load_capacity }}

- {% if truck.registration_expiry_date %} -

{% trans "Expiry Date:" %} {{ truck.registration_expiry_date }}

- {% endif %} - - {% trans "Edit Details" %} - -
- -
+ +
+
+
{% trans "Received Shipping Offers" %}
+
+
+
+ + + + + + + + + + + + + {% for bid in bids %} + + + + + + + + + {% empty %} + + + + {% endfor %} + +
{% trans "Route" %}{% trans "Truck" %}{% trans "Shipper" %}{% trans "Offer Amount" %}{% trans "Status" %}{% trans "Action" %}
+
{{ bid.shipment.origin }} {{ bid.shipment.destination }}
+ {{ bid.shipment.delivery_date }} +
{{ bid.truck.plate_no }}{{ bid.shipment.shipper.username }}${{ bid.amount }} + {% if bid.status == 'PENDING' %} + {% trans "Pending" %} + {% elif bid.status == 'ACCEPTED' %} + {% trans "Accepted" %} + {% else %} + {% trans "Rejected" %} + {% endif %} + + {% if bid.status == 'PENDING' %} + + {% else %} + {% trans "View Details" %} + {% endif %} +
+ +

{% trans "No offers received yet." %}

+
- {% endfor %} - {% else %} -
-

{% trans "No approved trucks yet." %}

-
- {% endif %} +
- - {% if pending_trucks %} -

{% trans "Pending Approval" %}

-
- {% for truck in pending_trucks %} + +

{% trans "My Fleet" %}

+
+ {% for truck in trucks %}
-
+
+ {% if truck.truck_picture %} + {{ truck.display_truck_type }} + {% else %} +
+ +
+ {% endif %} - +
+ {% endfor %} + + {% for truck in pending_trucks %} +
+
+
+
+
{{ truck.display_truck_type }}
+ {% trans "Pending" %} +
+

{% trans "Plate No:" %} {{ truck.plate_no }}

+

{% trans "Waiting for admin approval..." %}

{% endfor %}
- {% endif %} - -
-
-
-
-
{% trans "My Active Bids" %}
-
-
-
- - - - - - - - - - - {% for bid in bids %} - - - - - - - {% empty %} - - - - {% endfor %} - -
{% trans "Shipment" %}{% trans "Amount" %}{% trans "Status" %}{% trans "Date" %}
{{ bid.shipment.origin }} - {{ bid.shipment.destination }}${{ bid.amount }} - - {{ bid.get_status_display }} - - {{ bid.created_at|date }}
{% trans "No bids placed yet." %}
-
-
-
-
-
-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/core/urls.py b/core/urls.py index 597f03a..1283e6c 100644 --- a/core/urls.py +++ b/core/urls.py @@ -17,6 +17,7 @@ urlpatterns = [ path("shipment/post/", views.post_shipment, name="post_shipment"), path("marketplace/", views.marketplace, name="marketplace"), path("shipment//", views.shipment_detail, name="shipment_detail"), - path("shipment//bid/", views.place_bid, name="place_bid"), + path("truck//offer/", views.place_bid, name="place_bid"), path("bid//accept/", views.accept_bid, name="accept_bid"), -] + path("bid//reject/", views.reject_bid, name="reject_bid"), +] \ No newline at end of file diff --git a/core/views.py b/core/views.py index 1bda90a..ee86512 100644 --- a/core/views.py +++ b/core/views.py @@ -3,7 +3,7 @@ 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 -from .forms import TruckForm, ShipmentForm, BidForm, UserRegistrationForm, OTPVerifyForm +from .forms import TruckForm, ShipmentForm, BidForm, UserRegistrationForm, OTPVerifyForm, ShipperOfferForm from django.contrib import messages from django.utils.translation import gettext as _ from django.db.models import Q @@ -152,15 +152,21 @@ def dashboard(request): profile, created = Profile.objects.get_or_create(user=request.user) if profile.role == 'SHIPPER': my_shipments = Shipment.objects.filter(shipper=request.user).order_by('-created_at') - return render(request, 'core/shipper_dashboard.html', {'shipments': my_shipments}) + # In the new flow, Shippers place bids. + my_bids = Bid.objects.filter(shipment__shipper=request.user).order_by('-created_at') + return render(request, 'core/shipper_dashboard.html', { + 'shipments': my_shipments, + 'bids': my_bids + }) elif profile.role == 'TRUCK_OWNER': approved_trucks = Truck.objects.filter(owner=request.user, is_approved=True) pending_trucks = Truck.objects.filter(owner=request.user, is_approved=False) - my_bids = Bid.objects.filter(truck_owner=request.user).order_by('-created_at') + # Truck owners receive bids in the new flow + my_received_bids = Bid.objects.filter(truck_owner=request.user).order_by('-created_at') return render(request, 'core/truck_owner_dashboard.html', { 'trucks': approved_trucks, 'pending_trucks': pending_trucks, - 'bids': my_bids + 'bids': my_received_bids }) elif profile.role == 'ADMIN' or request.user.is_superuser: pending_trucks = Truck.objects.filter(is_approved=False).order_by('-created_at') @@ -175,7 +181,6 @@ def dashboard(request): } return render(request, 'core/admin_dashboard.html', context) else: - # Fallback for undefined roles return redirect('/') @login_required @@ -188,7 +193,7 @@ def truck_register(request): if form.is_valid(): truck = form.save(commit=False) truck.owner = request.user - truck.is_approved = False # Ensure it stays false on new reg + truck.is_approved = False truck.save() messages.success(request, _("Truck registered successfully! It will be visible after admin approval.")) return redirect('dashboard') @@ -207,7 +212,7 @@ def edit_truck(request, truck_id): form = TruckForm(request.POST, request.FILES, instance=truck) if form.is_valid(): truck = form.save(commit=False) - truck.is_approved = False # Reset approval status on update + truck.is_approved = False truck.save() messages.success(request, _("Truck data updated successfully! It will be reviewed by admin again.")) return redirect('dashboard') @@ -227,10 +232,9 @@ def approve_truck(request, truck_id): truck.is_approved = True truck.save() - # Notify Truck Owner via WhatsApp owner_phone = getattr(truck.owner.profile, 'full_phone_number', None) if owner_phone: - msg = f"Your truck ({truck.plate_no}) has been approved! You can now place bids on shipments." + msg = f"Your truck ({truck.plate_no}) has been approved! You can now receive offers for shipments." send_whatsapp_message(owner_phone, msg) messages.success(request, _("Truck approved successfully!")) @@ -249,6 +253,7 @@ def suspend_truck(request, truck_id): @login_required def post_shipment(request): + """Note: This is now largely redundant but kept for compatibility or direct posting.""" if request.user.profile.role != 'SHIPPER': return redirect('dashboard') @@ -269,80 +274,103 @@ def post_shipment(request): @login_required def marketplace(request): - if request.user.profile.role != 'TRUCK_OWNER': + """Shippers browse available trucks here.""" + if request.user.profile.role != 'SHIPPER': return redirect('dashboard') - shipments = Shipment.objects.filter(status='OPEN').order_by('-created_at') - return render(request, 'core/marketplace.html', {'shipments': shipments}) + trucks = Truck.objects.filter(is_approved=True).order_by('-created_at') + return render(request, 'core/marketplace.html', {'trucks': trucks}) @login_required -def place_bid(request, shipment_id): - shipment = get_object_or_404(Shipment, id=shipment_id) - if request.user.profile.role != 'TRUCK_OWNER': - return redirect('dashboard') - - # Optional: Only allow bidding if user has at least one approved truck - if not Truck.objects.filter(owner=request.user, is_approved=True).exists(): - messages.warning(request, _("You must have at least one approved truck to place a bid.")) +def place_bid(request, truck_id): + """Shipper makes an offer to a specific truck.""" + truck = get_object_or_404(Truck, id=truck_id, is_approved=True) + if request.user.profile.role != 'SHIPPER': return redirect('dashboard') if request.method == 'POST': - form = BidForm(request.POST, user=request.user) + form = ShipperOfferForm(request.POST) if form.is_valid(): - bid = form.save(commit=False) - bid.truck_owner = request.user - bid.shipment = shipment - bid.save() + # Create Shipment + shipment = Shipment.objects.create( + shipper=request.user, + description=form.cleaned_data['description'], + weight=form.cleaned_data['weight'], + origin=form.cleaned_data['origin'], + destination=form.cleaned_data['destination'], + delivery_date=form.cleaned_data['delivery_date'], + status='OPEN' + ) + # Create Bid (Offer) + bid = Bid.objects.create( + shipment=shipment, + truck_owner=truck.owner, + truck=truck, + amount=form.cleaned_data['amount'], + comments=form.cleaned_data.get('comments', ''), + status='PENDING' + ) - # Notify Shipper via WhatsApp - shipper_phone = getattr(shipment.shipper.profile, 'full_phone_number', None) - if shipper_phone: - msg = f"New bid placed on your shipment from {shipment.origin} to {shipment.destination}! Amount: {bid.amount}" - send_whatsapp_message(shipper_phone, msg) + # Notify Truck Owner via WhatsApp + owner_phone = getattr(truck.owner.profile, 'full_phone_number', None) + if owner_phone: + msg = f"New offer received for your truck ({truck.plate_no})! Route: {shipment.origin} to {shipment.destination}. Amount: {bid.amount}" + send_whatsapp_message(owner_phone, msg) - messages.success(request, _("Bid placed successfully!")) - return redirect('marketplace') + messages.success(request, _("Offer sent successfully!")) + return redirect('dashboard') else: - messages.error(request, _("Error placing bid. Please check the form.")) + messages.error(request, _("Error sending offer. Please check the form.")) else: - form = BidForm(user=request.user) + form = ShipperOfferForm() - return render(request, 'core/place_bid.html', {'form': form, 'shipment': shipment}) + return render(request, 'core/place_bid.html', {'form': form, 'truck': truck}) @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' and not request.user.is_superuser: - return redirect('dashboard') + # Security: check if user is shipper, truck owner of a bid, or admin + is_involved = Bid.objects.filter(shipment=shipment, truck_owner=request.user).exists() or shipment.shipper == request.user + if not is_involved and not (request.user.profile.role == 'ADMIN' or request.user.is_superuser): + return redirect('dashboard') bids = shipment.bids.all() return render(request, 'core/shipment_detail.html', {'shipment': shipment, 'bids': bids}) @login_required def accept_bid(request, bid_id): + """Truck owner accepts an offer from a shipper.""" bid = get_object_or_404(Bid, id=bid_id) - if bid.shipment.shipper != request.user: + if bid.truck_owner != request.user: + messages.error(request, _("You are not authorized to accept this offer.")) return redirect('dashboard') # Accept this bid bid.status = 'ACCEPTED' bid.save() - # Reject others - bid.shipment.bids.exclude(id=bid_id).update(status='REJECTED') - # Update shipment bid.shipment.status = 'IN_PROGRESS' bid.shipment.assigned_truck = bid.truck bid.shipment.save() - # Notify Truck Owner via WhatsApp - owner_phone = getattr(bid.truck_owner.profile, 'full_phone_number', None) - if owner_phone: - msg = f"Congratulations! Your bid for the shipment from {bid.shipment.origin} to {bid.shipment.destination} has been accepted." - send_whatsapp_message(owner_phone, msg) + # Notify Shipper via WhatsApp + shipper_phone = getattr(bid.shipment.shipper.profile, 'full_phone_number', None) + if shipper_phone: + msg = f"Your offer for truck {bid.truck.plate_no} ({bid.shipment.origin} to {bid.shipment.destination}) has been accepted!" + send_whatsapp_message(shipper_phone, msg) - messages.success(request, _("Bid accepted! Shipment is now in progress.")) - return redirect('shipment_detail', shipment_id=bid.shipment.id) \ No newline at end of file + messages.success(request, _("Offer accepted! Shipment is now in progress.")) + return redirect('dashboard') + +@login_required +def reject_bid(request, bid_id): + """Truck owner rejects an offer.""" + bid = get_object_or_404(Bid, id=bid_id) + if bid.truck_owner != request.user: + return redirect('dashboard') + + bid.status = 'REJECTED' + bid.save() + messages.info(request, _("Offer rejected.")) + return redirect('dashboard')