diff --git a/frontend/src/components/ShipperDashboard.jsx b/frontend/src/components/ShipperDashboard.jsx new file mode 100644 index 0000000..f0d7741 --- /dev/null +++ b/frontend/src/components/ShipperDashboard.jsx @@ -0,0 +1,164 @@ +import React, { useState, useEffect } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import { supabase } from '../supabaseClient'; +import { formatINR } from '../lib/india'; +import { getStatusColor } from '../lib/india'; + +function ShipperDashboard() { + const [selectedLoadId, setSelectedLoadId] = useState(null); + const [bidModalOpen, setBidModalOpen] = useState(false); + + const { data: loads, isLoading, isError } = useQuery({ + queryKey: ['loads'], + queryFn: async () => { + const { data, error } = await supabase + .from('loads') + .select('*, shipper:shippers(name), vehicle:vehicles(number)') + .order('date', { ascending: false }) + .limit(100); + if (error) throw error; + return data; + }, + }); + + const { data: bids } = useQuery({ + queryKey: ['bids', selectedLoadId], + queryFn: async () => { + const { data, error } = await supabase + .from('bids') + .select('*, driver:portal_users(username)') + .eq('load_id', selectedLoadId) + .order('created_at', { ascending: false }); + if (error) throw error; + return data; + }, + }); + + const handleAccept = async (bidId) => { + await fetch('/api/update-bid-status', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ bidId, newStatus: 'accepted' }), + }); + // Refresh data after update + await refetch(); + }; + + const { refetch } = useQuery({ + queryKey: ['loads'], + queryFn: async () => { + const { data, error } = await supabase + .from('loads') + .select('*, shipper:shippers(name), vehicle:vehicles(number)') + .order('date', { ascending: false }) + .limit(100); + if (error) throw error; + return data; + }, + ); + + return ( +
+

Shipper Dashboard

+ {isLoading ? ( +
Loading loads...
+ ) : isError ? ( +
{isError}
+ ) : ( + <> +
+ + {selectedLoadId && ( + + +
+ + + + + + + + + + + + + + + + {loads.map((load) => ( + + + + + + + + + + ))} + +
DateRouteShipperFreightStatusBidsActions
{new Date(load.date).toLocaleDateString('en-IN')}{load.from_city} → {load.to_city}{load.shipper?.name || ' — '}{formatINR(load.freight_charged)} + + {load.status} + + + {bids ? ( +
+ {bids.map((b) => ( +
+ {bid.driver?.username || ' — '}: {formatINR(bid.bid_amount)} +
+ ))} +
+ ) : ' — '} + {selectedLoadId === load.id && ( +
+ {bids.length > 0 && ( + <> + + + } +
+ } +
+ + {/* Modal for creating a new bid */} + {bidModalOpen && ( + { + setBidModalOpen(false); + setSelectedLoadId(null); + }} + /> + )} +
+ ); +} + +export default ShipperDashboard; \ No newline at end of file