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 .forms import TruckForm, ShipmentForm, BidForm, UserRegistrationForm, OTPVerifyForm 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 from .whatsapp import send_whatsapp_message from django.contrib.auth.forms import AuthenticationForm def home(request): """Render the landing screen for MASAR CARGO.""" context = { "deployment_timestamp": timezone.now().timestamp(), } return render(request, "core/index.html", context) def register(request): if request.method == 'POST': form = UserRegistrationForm(request.POST) if form.is_valid(): # Store data in session to be used after OTP verification registration_data = { 'username': form.cleaned_data['username'], 'email': form.cleaned_data['email'], 'password': form.data['password1'], # We need raw password to create user later 'role': form.cleaned_data['role'], 'phone_number': form.cleaned_data['phone_number'], 'country_code': form.cleaned_data['country_code'], } request.session['registration_data'] = registration_data # Send OTP full_phone = f"{registration_data['country_code']}{registration_data['phone_number']}" otp = OTPCode.generate_code(full_phone) msg = f"Your verification code for MASAR CARGO is: {otp.code}" if send_whatsapp_message(full_phone, msg): messages.info(request, _("A verification code has been sent to your WhatsApp.")) return redirect('verify_otp_registration') else: messages.error(request, _("Failed to send verification code. Please check your phone number.")) else: messages.error(request, _("Please correct the errors below.")) else: form = UserRegistrationForm() return render(request, 'registration/register.html', {'form': form}) def verify_otp_registration(request): registration_data = request.session.get('registration_data') if not registration_data: return redirect('register') if request.method == 'POST': form = OTPVerifyForm(request.POST) if form.is_valid(): code = form.cleaned_data['otp_code'] full_phone = f"{registration_data['country_code']}{registration_data['phone_number']}" otp_record = OTPCode.objects.filter(phone_number=full_phone, code=code, is_used=False).last() if otp_record and otp_record.is_valid(): otp_record.is_used = True otp_record.save() # Create user user = User.objects.create_user( username=registration_data['username'], email=registration_data['email'], password=registration_data['password'] ) profile = user.profile profile.role = registration_data['role'] profile.phone_number = registration_data['phone_number'] profile.country_code = registration_data['country_code'] profile.save() login(request, user) del request.session['registration_data'] messages.success(request, _("Registration successful. Welcome!")) return redirect('dashboard') else: messages.error(request, _("Invalid or expired verification code.")) else: form = OTPVerifyForm() return render(request, 'registration/verify_otp.html', {'form': form, 'purpose': 'registration'}) def custom_login(request): if request.method == 'POST': form = AuthenticationForm(request, data=request.POST) if form.is_valid(): user = form.get_user() profile = user.profile if not profile.phone_number: messages.error(request, _("Your account does not have a phone number. Please contact admin.")) return redirect('login') # Store user ID in session temporarily request.session['pre_otp_user_id'] = user.id # Send OTP full_phone = profile.full_phone_number otp = OTPCode.generate_code(full_phone) msg = f"Your login verification code for MASAR CARGO is: {otp.code}" if send_whatsapp_message(full_phone, msg): messages.info(request, _("A verification code has been sent to your WhatsApp.")) return redirect('verify_otp_login') else: # If WhatsApp fails, maybe allow login but warn? Or strictly enforce? # For now, strictly enforce messages.error(request, _("Failed to send verification code. Please check your connection.")) else: messages.error(request, _("Invalid username or password.")) else: form = AuthenticationForm() return render(request, 'registration/login.html', {'form': form}) def verify_otp_login(request): user_id = request.session.get('pre_otp_user_id') if not user_id: return redirect('login') user = get_object_or_404(User, id=user_id) profile = user.profile if request.method == 'POST': form = OTPVerifyForm(request.POST) if form.is_valid(): code = form.cleaned_data['otp_code'] full_phone = profile.full_phone_number otp_record = OTPCode.objects.filter(phone_number=full_phone, code=code, is_used=False).last() if otp_record and otp_record.is_valid(): otp_record.is_used = True otp_record.save() login(request, user) del request.session['pre_otp_user_id'] messages.success(request, _("Logged in successfully!")) return redirect('dashboard') else: messages.error(request, _("Invalid or expired verification code.")) else: form = OTPVerifyForm() return render(request, 'registration/verify_otp.html', {'form': form, 'purpose': 'login'}) @login_required 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}) 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') return render(request, 'core/truck_owner_dashboard.html', { 'trucks': approved_trucks, 'pending_trucks': pending_trucks, 'bids': my_bids }) elif profile.role == 'ADMIN' or request.user.is_superuser: pending_trucks = Truck.objects.filter(is_approved=False).order_by('-created_at') approved_trucks = Truck.objects.filter(is_approved=True).order_by('-created_at') context = { 'total_users': User.objects.count(), 'total_trucks': Truck.objects.count(), 'total_shipments': Shipment.objects.count(), 'total_bids': Bid.objects.count(), 'pending_trucks': pending_trucks, 'approved_trucks': approved_trucks, } return render(request, 'core/admin_dashboard.html', context) else: # Fallback for undefined roles return redirect('/') @login_required def truck_register(request): if request.user.profile.role != 'TRUCK_OWNER': return redirect('dashboard') if request.method == 'POST': form = TruckForm(request.POST, request.FILES) 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.save() messages.success(request, _("Truck registered successfully! It will be visible after admin approval.")) 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', {'form': form}) @login_required def edit_truck(request, truck_id): truck = get_object_or_404(Truck, id=truck_id, owner=request.user) if request.method == 'POST': 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.save() messages.success(request, _("Truck data updated successfully! It will be reviewed by admin again.")) return redirect('dashboard') else: messages.error(request, _("There was an error updating your truck. Please check the form.")) else: form = TruckForm(instance=truck) return render(request, 'core/truck_register.html', {'form': form, 'edit_mode': True, 'truck': truck}) @login_required def approve_truck(request, truck_id): if not (request.user.profile.role == 'ADMIN' or request.user.is_superuser): return redirect('dashboard') truck = get_object_or_404(Truck, id=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." send_whatsapp_message(owner_phone, msg) messages.success(request, _("Truck approved successfully!")) return redirect('dashboard') @login_required def suspend_truck(request, truck_id): if not (request.user.profile.role == 'ADMIN' or request.user.is_superuser): return redirect('dashboard') truck = get_object_or_404(Truck, id=truck_id) truck.is_approved = False truck.save() messages.warning(request, _("Truck has been suspended.")) return redirect('dashboard') @login_required def post_shipment(request): if request.user.profile.role != 'SHIPPER': return redirect('dashboard') if request.method == 'POST': form = ShipmentForm(request.POST) if form.is_valid(): shipment = form.save(commit=False) shipment.shipper = request.user shipment.save() messages.success(request, _("Shipment posted successfully!")) return redirect('dashboard') else: messages.error(request, _("Please correct the errors in the form.")) else: form = ShipmentForm() return render(request, 'core/post_shipment.html', {'form': form}) @login_required def marketplace(request): if request.user.profile.role != 'TRUCK_OWNER': return redirect('dashboard') shipments = Shipment.objects.filter(status='OPEN').order_by('-created_at') return render(request, 'core/marketplace.html', {'shipments': shipments}) @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.")) return redirect('dashboard') if request.method == 'POST': 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() # 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) 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', {'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' and not 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): bid = get_object_or_404(Bid, id=bid_id) if bid.shipment.shipper != request.user: 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) messages.success(request, _("Bid accepted! Shipment is now in progress.")) return redirect('shipment_detail', shipment_id=bid.shipment.id)