From 459f38ada60fa49252ed5e1ec6b4225aecb87a11 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 23 Jan 2026 15:01:49 +0000 Subject: [PATCH] change18 --- core/__pycache__/forms.cpython-311.pyc | Bin 12434 -> 13305 bytes core/__pycache__/models.cpython-311.pyc | Bin 15820 -> 18005 bytes core/__pycache__/views.cpython-311.pyc | Bin 22043 -> 22392 bytes core/forms.py | 25 +++- ...ountry_shipment_origin_country_and_more.py | 57 ++++++++ ...nt_origin_country_and_more.cpython-311.pyc | Bin 0 -> 2751 bytes core/models.py | 39 +++++- core/templates/core/place_bid.html | 100 +++++++++++--- core/templates/core/post_shipment.html | 122 +++++++++++++----- core/templates/core/shipment_detail.html | 92 +++++++------ core/templates/core/shipper_dashboard.html | 21 +-- .../templates/core/truck_owner_dashboard.html | 4 +- core/views.py | 24 ++-- 13 files changed, 364 insertions(+), 120 deletions(-) create mode 100644 core/migrations/0010_shipment_destination_country_shipment_origin_country_and_more.py create mode 100644 core/migrations/__pycache__/0010_shipment_destination_country_shipment_origin_country_and_more.cpython-311.pyc diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc index 82e7cc0f947e9b3dbb67aff82a84edb54ad1a527..f7f591a17adb0efffa2210bef1ea14bc55d90fc4 100644 GIT binary patch delta 3993 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 N?+e+3pV1KO`+wt!#<~Ci diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 1c5c1d2bf99902d45b26e1c2a2fc5e5f8ffa94f7..c9053e46a468856bdbea168ac0956cf7ac522c37 100644 GIT binary patch delta 4038 zcmbVPdr(x@8Q-%n7M5Lbf!$?!>>`3J5MF{BwMG!6R0R`2h1Be9xK~&nyLj#u3)(E1 z*bMEE`Z&&nRGrYKHQ3NrvrcN8WF|HJ!#1XKnO-~eCV$vR`k4MvojRSVolg3F=OQ3v zCY_$W_s91-=Y01&kMHBw@!ONc`MSfAVt{sHQi{A^bjIl=U%a~hRbp#42-znMf{&$> z9J|kG5N)DWG|lF)Vq-xlXNkXLG#K81S8qbzNy8-|-*_l6bmuPEX3plUu+3j(n|?wJrOw~N|%jV zax5AW#6eLOwNxq&hGkI*MZ-hlSTY(0T7jevaFaUO)v9c=gMCm{!fMPJ@m|tx70fWE zkDadeu-xQi zmR@RQ`z*N?{Vh|BCGD(l-&x<-L^lFQ zOAvS@FiNb->SLp+&iISKEc;5Qf8wy}^iL;U*?4WcvhSec?3#0Ssm`wP=6QS0#75Pg zGgS}2fmd2Dv|f=E`_?)8R@J_Byz#EtWXZj2SPl13c7I8cLM*j+4GZ8}B5U=PC}(HZ zXD3RMP7w>O1m_YrZ1)MNNc=b@gOE+^1AAX7-HhL|AAx_>L8SH~>;}+m!!$N5QhAg< z#lorHlr5-1C}c0B{+(>WeZ?jnH^`1eo6}9;&*3yHakZe?GXO!Nk%7xbx|=l+7>^#!P zUd{dvd5$&s+DMe0^-Tl4Aty#;_FB$sKsV>MS;ugg7ukhee-qt=v>5@{LbJqVgh`f^7a%8CN#0BKM^L~cvmU9Z5so8#4dELI-$Ymu6&Ym+ zEs1~TnN8sBzXRFEwI(*ZzJv7_{3LD#9dDvjwDe&^np7EAx59o_#!M>n4kuTb9jgdV0;=f=CeT(vl);1$gRnay4?b2hhH3Z!pEAkzdN z_h^@p|SBz9;ulSG)6sO_0R8^F_a~Iza_M>R*+%3WNY$w*{=R@Cd13 zZxvP!c4+BRUu1YljLK3dM&U3=gC>5>P<~$U&eyV*OaxJqBT+b;vFKP@8@CUX>=Apz zJ)^@-MM_#HJTtXw*-o9jX})FGDaF>w zZ`hv2eFOb)zu@~1yo_EpoG>iqE4miAMO&J)T-R>|bGUY%^k&2tXkgD9E}GhLjKy8p zZn$iOY*!u#nl-x~?+{c1qnh&}qvt(63tDv{ErPKmoH?N9kjE;F*!t$_BNvS?840mie@d$>WO6 z_kV@Q4KqVq86URC>;0V2&{^mQfk|Js%k=1EJR(^hBKtDUsyJPS@y2qO;v_C?on?Jq z0}}~T+Cs1b7@-a_-JuqJK__{z%2aoilF<&&l}5F8k6Qbr&MB$w5G*y#uh{$x1;<)e zT2yLZmJe8{s3wkggl21R>Ik)V?%6|s2rMtv=ns&36X7btj}Q_FX8|+^RECiek&cE0 zOsL;S1>WzPbMf@Z^k~lj{j7w6+k|2aXdbWWmI}M`C`1^CS86dbQv7E8PFC^h{c2Y6 z^s(#3?^G)LI+UKHO4i6+)`*%lGTu7xDm-1Rx(cTouk56J^p#Z;3z9u&)?~3n`LEpQp5~C?W)BUCk`(S0rH%#9t~}O84xFv1B|l?z6^%7~ zuYQ(0w{d*#2-M?I8HUpL*>CvlbErjln|)Yumds%Z#zrguL%zq3RjqqiWPQ8ps^udv z`UN{sectvlP*CIVVGpY7$cL<|W+LUcI1Iwi*^QbTHl%`t-e!{8t?8b!Mp z3e2E&*EU%G1dNh6RBIxeevfjimed!P!m>>1Ux3%lQW%a7uXdL@n<)RPKY#)KguPt1 z&hj@PZztZX8!(cCtbB7l`GiF`Z?D5Hnxj{gA=88w3R+%R^8ve1Ydstpgj1;{iC^g% zJPQ9q?PtH+oa0$G0a{slY3%4Q{UghGyvgzaBp8KnaS!G9my}Ntc4eD8 J@!qyn@?RE=@m&A_ delta 2202 zcmZ`)ZA_C_6z-)TltK%X@=>4^kf~KB<4dukf)teDUXO&YQCDUuVNDd?M24qI4?8rK|qB_=#&I7(vWm{8Co&<-lm z@Ic%YA-UnbrX)fZ^TBxk5;?l6vZ<=lRn2N)4{RYq9YO`dDgasE#X?<_NqwxEPdn3i zxxvJ16CRR$p0M1?>k~&waX8VOLP$w?VUmuR%W>ug00)st`^Qmi6|b;ZQ%Z3yohY$D zuuawpfwq7ovPK@VB$F-tprw?Q@h>caR&>eo(AMbkO0+r9NoxYMLtvY5z=6<$;6{@; zQSwRwUniCPA%O{~w;^O9*by3MVF}-xe2FyjM+MgK>XcedGHk2}2IV+LJHV0P33@vz zKa&1Nj3bK0}|A-s*ygRm1}FT#ER+46cw z3`l`qS{syT8)ZsZWlbOTGS-a-F&OZOHjPn!KYMp2#o0>a7a(guz(cZK2>l4}AUqRw zKk5)xhR@|_)R{Z~;Bi+-Ai}H!IU5m}8lhgk<=ojHC{0 zVGi_2b0;fvQf6f#c_i)W4Y^Z=SnC0mJ55N3a?Icl7Ic#xymetgzpQIukodu0r(w1n z>!P9*2to#jg1b^T(C!|PsgUiW9dJvmFB|WJ6@w`&0ce)ZUQvX6fLs;*fv!#(lthO{ z$zm4CQZ#KxXaexfMG^$uzM5}x)Fi9}=1|MJkg^jwmfv(ZmmUJ8_!7WA)ucJ)_~Nnn zYWNS8Ow22e(CeBTu@N?=uU6`--eKjZ@)yM(fLSx?$?xVDtviGAS%d)q*$7XMavG0- zry0TlOb1!tRNd^U-?)CgY??ckQY(z2%_e+n5(beXNYKSYgr9l&v&u=MZJ=esXglFO z;T_VSY(L%psc+2a95*^AjLv;Eljhv_=TDe(hYI1hWqe*S9O5hWnAtUMc1@UF`|2jS zyl=)<;UIbgVj zs~~*Db5@M%kvRw(}sr^g>lUGg-})Lyds}`;k9hnWvoudM#{U)gDFa_nlZ|au z@`RYj$0#PF@}W}}T#uu=?ST%^%F%SEucJqxtd>_)W~4sh0IjrbEY#D*#yP93);<8u zm;6TM2(j_NnoBMZ$ZV@(_F4!spT(gs#ECiGF`8d#fc+c<3*wm92`QhT~T=FmX{XHN6 diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 0d02fbb5857a57865929d9b9eb03e6205d552eab..9951dda00e153d5d3e5fda5b4457bcc3e06a7b57 100644 GIT binary patch delta 5027 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 H2D|d( zkxIoCaVb@iD#aafE1rl)@kYF$Hwg~8TJc4Ee4H=)m6}L^j|=2lB^U|vaiM&lQWvR% zu~jIN?^o(0^<3_h8>U@*akY+23<2qt2RhmKG9~pNPx7 z8+0`rbbDBlIZbTroVj)J9rKrjG%UWAm$KA`z!ka~KzF2KsydgX0^N$TZ3xYaLyr52 z&4uG8HkaSm>c^o6K+lsB(@C8~X&$~=MM{&DT3{Tq(K?*96Jb01M}9+TJx*~@aYj-# znAo*gSD+En$X@Vz$@}b$LP{>O`l2I5WV1y#$j2<`+(h)n1I}*p;+0}@d^_&ch0w$| zN@}TSJSm8@8D(f`{|$>_@m`!@m(*xl6@@TeagVluB4k-7pj+9$N*;e0E!FL*G)*N{ zF;q<-z<0}xsBz!uE_kI?2>5xr8(|MZ8Gvq2PMs3tnyTBTB^kWa^W>PS(LU7Wsj*p+ z`q+o1)dzB-ifio%Jdk!Z9gmBuO50KN5CRXQ!w4gHp2re>qaCcXtg3MohN=S~n0+XQ znC@5xoUeYs=F5(gYIdhA;&M5|;WD z%)Fgls%{_xQ+-EY{J=+Qj=@5nwHt&+aLirrfl)v0WncIM3;ie^KsbPK5Fq5?zS2V| zK=-K?!H2+&|5|Px$0hjBE7FpPl2q9X_^6ryAJI(bk2=yuFW6oS4Qu^cwx!Z5%+ z$TWdEgfpmD*rzorDP}W)epmiI$du3C32e=hY4=j)9%iitGA+_tyOoTyN9zArG7a14 z)~?>egF_HPiA^Q3ZOl1AfHjKMMD*77q`3|czG{?o9B^1<4NUNEXVms3fB1zq9AEe1&ir9Tvcii)Cpt5)3vVAZ=r%$ zKGQN!$~K>KI-W3TM`E#yVnJwOU` ztY&ZRsk^7W;T3zcJz*|J6H;P^_XNlluX`(It!0K&O{*y}A!JQ>8b1Fi{Hc$!8@qbh zm9~y7sXuFTnx!K{Nln}v44;(LApA=t=Yn#K&WQ5aAQgplLWm`_;8a=*o)%MDa5_nY z@uZT9Y0{J=OWN5W2m}d^mKKCwsey&fGzK5bo)Z`W*8m@>VGr-EVZYnkZ5*4DuRk^w zxEvXqL#ru#U7MSn#+Z9l9O_9arHBb_T};rrDC$a3Nf~VT75u3mv5t-=_N#rd@};JM z_Zkmm8V|fzG?*zGTq+u5iB>N=(Y^~3cd0!<%2pf;(*zn6XKz2`n>-Ces5|Re$4Spm z&0(C%%k?ad@DPX0MgnoEY6e(lPoWZ{r$r4o85imIKvBUS?2vtpFpA^6v8DJ#XT^{Q0JB-bQx_mGkCPSjzZw*(A8;^G;!omfx%3y)x(B z&8~JGBOX@fuVtR@ZsLVefSv8$=Bu8!%$o(@74zEfjG01y_UrD}1zxZ?@mC^k)Xzde zRO3`iX;Lzw+vY@R=A@?El2n2QuA>r&2b2V*QxIh+^7L#}fMc!a4UhB?LufG#IyT^h&ot z|!K$OQ!pW95%z;oht$1P#0h+Xd+BLVhh z-qIrUvzpgA&>1&3 z+(oy@s($}~OzSuE+ri~`>3xqT?pKAbCB z8lT9F$1>weu8@z}rV;FDcqv#I*-UJhecI1nJF?~2YE0Gzfrn@xPQ(x`_tV!=fZna` z9R*axA9UQ0Uw}5jT1U^?8(}mQYGZGVzF4PQp)o2dEyoRb|H6VuT?qVlqMJQ7R@shU z)jh(gSYjsGoYo{+ZPsWkp~4LWY8Un5bhJk=&CU^~n(-zktF)RejRi~jb=NU>GN!3` z&G0`Hu3f0XRSgJT2s;o?AWR@2d+2$DClH=Ocp3p4An!2zs8b{$#UxSm@P>(^d%P)v|v*x4kdN-3%5F>tKF_}v*1n!!-Xo6K8o6JKO{tK)6LGu6r diff --git a/core/forms.py b/core/forms.py index d48204e..c9de2b1 100644 --- a/core/forms.py +++ b/core/forms.py @@ -1,5 +1,5 @@ from django import forms -from .models import Truck, Shipment, Bid, Profile, Country, OTPCode +from .models import Truck, Shipment, Bid, Profile, Country, OTPCode, City from django.utils.translation import gettext_lazy as _ from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.models import User @@ -89,12 +89,19 @@ class TruckForm(forms.ModelForm): class ShipmentForm(forms.ModelForm): class Meta: model = Shipment - fields = ['description', 'weight', 'origin', 'destination', 'delivery_date'] + fields = [ + 'description', 'weight', + 'origin_country', 'origin_city', + 'destination_country', 'destination_city', + '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'}), + 'origin_country': forms.Select(attrs={'class': 'form-select location-selector', 'data-type': 'origin'}), + 'origin_city': forms.Select(attrs={'class': 'form-select'}), + 'destination_country': forms.Select(attrs={'class': 'form-select location-selector', 'data-type': 'destination'}), + 'destination_city': forms.Select(attrs={'class': 'form-select'}), 'delivery_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), } @@ -120,8 +127,12 @@ class BidForm(forms.ModelForm): 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'})) + + origin_country = forms.ModelChoiceField(queryset=Country.objects.all(), widget=forms.Select(attrs={'class': 'form-select location-selector', 'data-type': 'origin'})) + origin_city = forms.ModelChoiceField(queryset=City.objects.all(), widget=forms.Select(attrs={'class': 'form-select'})) + destination_country = forms.ModelChoiceField(queryset=Country.objects.all(), widget=forms.Select(attrs={'class': 'form-select location-selector', 'data-type': 'destination'})) + destination_city = forms.ModelChoiceField(queryset=City.objects.all(), widget=forms.Select(attrs={'class': 'form-select'})) + 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})) + comments = forms.CharField(label=_('Comments'), required=False, widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 2})) \ No newline at end of file diff --git a/core/migrations/0010_shipment_destination_country_shipment_origin_country_and_more.py b/core/migrations/0010_shipment_destination_country_shipment_origin_country_and_more.py new file mode 100644 index 0000000..eede752 --- /dev/null +++ b/core/migrations/0010_shipment_destination_country_shipment_origin_country_and_more.py @@ -0,0 +1,57 @@ +# Generated by Django 5.2.7 on 2026-01-23 14:58 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0009_otpcode_alter_profile_phone_number'), + ] + + operations = [ + migrations.AddField( + model_name='shipment', + name='destination_country', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='shipments_destination', to='core.country'), + ), + migrations.AddField( + model_name='shipment', + name='origin_country', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='shipments_origin', to='core.country'), + ), + migrations.AlterField( + model_name='shipment', + name='destination', + field=models.CharField(blank=True, max_length=255, verbose_name='Destination (Legacy)'), + ), + migrations.AlterField( + model_name='shipment', + name='origin', + field=models.CharField(blank=True, max_length=255, verbose_name='Origin (Legacy)'), + ), + migrations.CreateModel( + name='City', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, verbose_name='City Name')), + ('country', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='cities', to='core.country')), + ], + options={ + 'verbose_name': 'City', + 'verbose_name_plural': 'Cities', + 'ordering': ['name'], + }, + ), + migrations.AddField( + model_name='shipment', + name='destination_city', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='shipments_destination', to='core.city'), + ), + migrations.AddField( + model_name='shipment', + name='origin_city', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='shipments_origin', to='core.city'), + ), + ] diff --git a/core/migrations/__pycache__/0010_shipment_destination_country_shipment_origin_country_and_more.cpython-311.pyc b/core/migrations/__pycache__/0010_shipment_destination_country_shipment_origin_country_and_more.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b086e3eed231ddb8f22e588b3f2d8b0dd409f1a0 GIT binary patch literal 2751 zcmbtWJ#Z686y7_X&ypNQf zf}IpDQl!Xa=%7eVl8FmS3W}5|&oftOw#rN@Ix)DSN#5@1WD7)q;iP+a`}ckCef!=@ zzxMY>5nS6pImV#~LjUkaaP_oao-e`6eS{H~Oq7@4DVuU7ln+&uydohPT}C+c1HuXk zm4h|tVF;l=;P$V450-K$-Sd<;vuT+{D@KXxHN&WamgX0%fq>qhJLO z zJo!s#=V<3RaCK(RR44m8X3q3GXU?(r;EUon@|Ak?aXbT+cAQ171r-M6rW9>T*vYxM zxp~d5Rf|w?nr_wz)vDB9GfbjY*KLbvR=rXnl!cvjqgo+WjSXYs)C@~hs#dh?R*i1n zWTU~jqa7IEigA|N)~vL`6svBUEMi+Q%Oo|zVw9MAjbP2vD}>24I~{_PfLFiYqlo^V zHG`O#4cOEu8CJl`hW1JQ5!QEr{OP}tQClNR`|eJiS|KI9xS0l$o`R`c8^ERC)l6cQ zYU?a^htPuUK+N11)rZ^~ye>%FYnifW)Ha!H;AJLXU4o!udc9_AMM`)etglLqicUAR z4YJ814xxr_8e4>>z#AF|^AvNxA{=EZ3ruO59^zws0_$yQ#nhrvGl-K8QC{Sf*Bj*} z##C774~PZB2c6ccW}WIL3)>U}u~mZQoRXE*5WLQY989RuP<|{FrCq1&o*Z#!-Hbvgk78 zf>E+Q16@>H)~%AwWL#ijkqU6`vvBUxO)YzCWral-cq)p;EV8(+w_f`eg~-=BW}C>X{so4 zp&61#)a{jK6h+2=jC$cCjpTGAexi|>xp%@#%=|X;cdGb7l;YXUJV z5VHa?`wHT9>4Sv2{VD&@3qgXWRhIHAauye6{Xbu`L0QtXFpWjp
-
+

{% trans "Send Shipping Offer" %}

@@ -34,41 +34,56 @@
{% trans "Shipment Details" %}
- + {{ 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 "Route" %}
+
+
+ +
+ {{ form.origin_country }} + {{ form.origin_country.errors }} +
+
+ {{ form.origin_city }} + {{ form.origin_city.errors }} +
+
+
+ +
+ {{ form.destination_country }} + {{ form.destination_country.errors }} +
+
+ {{ form.destination_city }} + {{ form.destination_city.errors }} +
+
+
+
{% trans "Pricing & Terms" %}
- +
$ {{ form.amount }} @@ -77,7 +92,7 @@
- + {{ form.comments }} {{ form.comments.errors }}
@@ -94,4 +109,47 @@
-{% endblock %} + + +{% endblock %} \ No newline at end of file diff --git a/core/templates/core/post_shipment.html b/core/templates/core/post_shipment.html index 4e89f1e..2a11611 100644 --- a/core/templates/core/post_shipment.html +++ b/core/templates/core/post_shipment.html @@ -5,51 +5,111 @@
-
+

{% trans "Post a New Shipment" %}

+

{% trans "Enter shipment details to receive bids or send as an offer." %}

- {% if form.errors %} -
- {% trans "Please correct the errors below." %} - {{ form.non_field_errors }} -
- {% endif %} - -
+ {% csrf_token %} -
- + +
+ {{ form.description }} - {{ form.description.errors }} + {% if form.description.errors %}
{{ form.description.errors }}
{% endif %}
-
- - {{ form.weight }} - {{ form.weight.errors }} + +
+
+ + {{ form.weight }} + {% if form.weight.errors %}
{{ form.weight.errors }}
{% endif %} +
+
+ + {{ form.delivery_date }} + {% if form.delivery_date.errors %}
{{ form.delivery_date.errors }}
{% endif %} +
+ +
+ +
{% trans "Route Details" %}
+
-
- - {{ form.origin }} - {{ form.origin.errors }} +
+
+ + {{ form.origin_country }} + + + {{ form.origin_city }} +
-
- - {{ form.destination }} - {{ form.destination.errors }} +
+
+ + {{ form.destination_country }} + + + {{ form.destination_city }} +
-
- - {{ form.delivery_date }} - {{ form.delivery_date.errors }} -
- + +
-{% 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 df5aab2..c14a45d 100644 --- a/core/templates/core/shipment_detail.html +++ b/core/templates/core/shipment_detail.html @@ -5,39 +5,39 @@
-
+
-

{{ shipment.origin }} {{ shipment.destination }}

+

{{ shipment.display_origin }} {{ shipment.display_destination }}

{{ shipment.get_status_display }}

-
{% trans "Shipment Details" %}
-

{{ shipment.description }}

-
+
{% trans "Shipment Details" %}
+

{{ shipment.description }}

+
- {% trans "Weight" %} - {{ shipment.weight }} + {% trans "Weight / Volume" %} + {{ shipment.weight }}
- {% trans "Requested Delivery Date" %} - {{ shipment.delivery_date }} + {% trans "Requested Delivery Date" %} + {{ shipment.delivery_date }}
-
-
-
{% trans "Offer Status" %}
+
+
+
{% trans "Offer Details" %}
- - +
+ @@ -48,8 +48,11 @@ {% for bid in bids %} - - + + {% empty %} - + {% endfor %} @@ -78,42 +84,56 @@ {% if shipment.status == 'IN_PROGRESS' %} -
- -
-
{% trans "Shipment is 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 "Stakeholders" %}
-
- {% trans "Shipper" %} - {{ shipment.shipper.username }} +
{% trans "Contact Information" %}
+
+ {% trans "Shipper" %} +
+
+ +
+ {{ shipment.shipper.username }} +
{% if shipment.assigned_truck %} -
- {% trans "Truck Owner" %} - {{ shipment.assigned_truck.owner.username }} +
+ {% trans "Truck Owner" %} +
+
+ +
+ {{ shipment.assigned_truck.owner.username }} +
{% endif %}
{% if shipment.status == 'IN_PROGRESS' %} -
+
{% if user == shipment.shipper %} - - {% trans "Message Driver" %} + + {% trans "WhatsApp Driver" %} {% elif user == shipment.assigned_truck.owner %} - - {% trans "Message Shipper" %} + + {% trans "WhatsApp Shipper" %} {% endif %}
@@ -121,4 +141,4 @@
-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/core/templates/core/shipper_dashboard.html b/core/templates/core/shipper_dashboard.html index cf8ba59..5048969 100644 --- a/core/templates/core/shipper_dashboard.html +++ b/core/templates/core/shipper_dashboard.html @@ -8,9 +8,14 @@

{% trans "Shipper Dashboard" %}

{% trans "Manage your shipping offers and active shipments." %}

- - {% trans "Browse Trucks" %} - +
@@ -39,9 +44,9 @@ {{ bid.truck.plate_no }}
@@ -93,10 +98,9 @@ {% for shipment in shipments %} - {% if shipment.status != 'OPEN' or not shipment.bids.exists %} - + - {% endif %} {% empty %} @@ -126,4 +129,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/core/templates/core/truck_owner_dashboard.html b/core/templates/core/truck_owner_dashboard.html index 8c1013d..850f182 100644 --- a/core/templates/core/truck_owner_dashboard.html +++ b/core/templates/core/truck_owner_dashboard.html @@ -38,7 +38,7 @@ {% for bid in bids %} @@ -122,4 +122,4 @@ {% endfor %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/core/views.py b/core/views.py index ee86512..435c180 100644 --- a/core/views.py +++ b/core/views.py @@ -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 +from .models import Profile, Truck, Shipment, Bid, Message, OTPCode, Country, City from .forms import TruckForm, ShipmentForm, BidForm, UserRegistrationForm, OTPVerifyForm, ShipperOfferForm from django.contrib import messages from django.utils.translation import gettext as _ @@ -77,7 +77,8 @@ def verify_otp_registration(request): profile.save() login(request, user) - del request.session['registration_data'] + if 'registration_data' in request.session: + del request.session['registration_data'] messages.success(request, _("Registration successful. Welcome!")) return redirect('dashboard') else: @@ -137,7 +138,8 @@ def verify_otp_login(request): otp_record.save() login(request, user) - del request.session['pre_otp_user_id'] + if 'pre_otp_user_id' in request.session: + del request.session['pre_otp_user_id'] messages.success(request, _("Logged in successfully!")) return redirect('dashboard') else: @@ -253,7 +255,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.""" + """Note: This is used as the 'Add A Bid' / 'Add Post' action for Shippers.""" if request.user.profile.role != 'SHIPPER': return redirect('dashboard') @@ -263,7 +265,7 @@ def post_shipment(request): shipment = form.save(commit=False) shipment.shipper = request.user shipment.save() - messages.success(request, _("Shipment posted successfully!")) + messages.success(request, _("Shipment posted successfully! It is now open for bids or you can browse trucks to send it as an offer.")) return redirect('dashboard') else: messages.error(request, _("Please correct the errors in the form.")) @@ -296,8 +298,10 @@ def place_bid(request, truck_id): shipper=request.user, description=form.cleaned_data['description'], weight=form.cleaned_data['weight'], - origin=form.cleaned_data['origin'], - destination=form.cleaned_data['destination'], + origin_country=form.cleaned_data['origin_country'], + origin_city=form.cleaned_data['origin_city'], + destination_country=form.cleaned_data['destination_country'], + destination_city=form.cleaned_data['destination_city'], delivery_date=form.cleaned_data['delivery_date'], status='OPEN' ) @@ -314,7 +318,7 @@ def place_bid(request, truck_id): # 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}" + msg = f"New offer received for your truck ({truck.plate_no})! Route: {shipment.display_origin} to {shipment.display_destination}. Amount: {bid.amount}" send_whatsapp_message(owner_phone, msg) messages.success(request, _("Offer sent successfully!")) @@ -357,7 +361,7 @@ def accept_bid(request, bid_id): # 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!" + msg = f"Your offer for truck {bid.truck.plate_no} ({bid.shipment.display_origin} to {bid.shipment.display_destination}) has been accepted!" send_whatsapp_message(shipper_phone, msg) messages.success(request, _("Offer accepted! Shipment is now in progress.")) @@ -373,4 +377,4 @@ def reject_bid(request, bid_id): bid.status = 'REJECTED' bid.save() messages.info(request, _("Offer rejected.")) - return redirect('dashboard') + return redirect('dashboard') \ No newline at end of file
{% trans "Truck" %} {% trans "Amount" %}
{{ bid.truck.display_truck_type }} ({{ bid.truck.plate_no }})${{ bid.amount }} +
{{ bid.truck.display_truck_type }}
+ {{ bid.truck.plate_no }} +
${{ bid.amount }} {{ bid.get_status_display }} @@ -68,7 +71,10 @@
{% trans "No offers for this shipment." %} + +

{% trans "No offers for this shipment yet." %}

+
- {{ bid.shipment.origin }} + {{ bid.shipment.display_origin }} - {{ bid.shipment.destination }} + {{ bid.shipment.display_destination }} ${{ bid.amount }} {{ bid.created_at|date:"d M Y" }}
{{ shipment.description|truncatechars:30 }}{{ shipment.origin }} {{ shipment.destination }}{{ shipment.display_origin }} {{ shipment.display_destination }} {{ shipment.delivery_date }} @@ -114,7 +118,6 @@ {% trans "Track" %}
{% trans "No active shipments." %}
-
{{ bid.shipment.origin }} {{ bid.shipment.destination }}
+
{{ bid.shipment.display_origin }} {{ bid.shipment.display_destination }}
{{ bid.shipment.delivery_date }}
{{ bid.truck.plate_no }}