diff --git a/frontend/src/components/BidFeed.jsx b/frontend/src/components/BidFeed.jsx new file mode 100644 index 0000000..0347554 --- /dev/null +++ b/frontend/src/components/BidFeed.jsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { useQuery } from '@tanstack/react-query'; +import { supabase } from '../supabaseClient'; + +function BidFeed({ loadId }) { + const { data: bids = [], isLoading, isError, refetch } = useQuery({ + queryKey: ['bids', loadId], + queryFn: async () => { + const { data, error } = await supabase + .from('bids') + .select('*, driver:portal_users(username)') + .eq('load_id', loadId) + .order('created_at', { ascending: false }); + if (error) throw error; + return data; + }, + refetchInterval: 10000, // Poll every 10 seconds for updates + }); + + if (isLoading) return
Loading bids...
; + if (isError) return
Error loading bids
; + + return ( +
+

Active Bids

+ {bids.length === 0 ? ( +

No bids yet. Be the first to offer!

+ ) : ( + + + + + + + + + + + + {bids.map((bid) => ( + + + + + + + + ))} + +
DriverBid AmountStatusTimeActions
{bid.driver?.username || 'Unknown'}₹{parseFloat(bid.bid_amount).toLocaleString('en-IN')} + + {bid.status} + + {new Date(bid.created_at).toLocaleString()} + {/* Action buttons will be added for shipper to accept/reject */} + +
+ )} +
+ ); +} + +export default BidFeed; \ No newline at end of file diff --git a/frontend/src/components/BidSubmissionModal.jsx b/frontend/src/components/BidSubmissionModal.jsx new file mode 100644 index 0000000..9b69b27 --- /dev/null +++ b/frontend/src/components/BidSubmissionModal.jsx @@ -0,0 +1,91 @@ +import React, { useState } from 'react'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { supabase } from '../supabaseClient'; + +function BidSubmissionModal({ loadId, onClose, onSuccess }) { + const [bidAmount, setBidAmount] = useState(''); + const [notes, setNotes] = useState(''); + const [isSubmitting, setIsSubmitting] = useState(false); + const queryClient = useQueryClient(); + + const mutation = useMutation({ + mutationFn: async (bidData) => { + const { data, error } = await supabase + .from('bids') + .insert({ + load_id: loadId, + driver_id: (await supabase.auth.getUser()).data.user?.id, + bid_amount: parseFloat(bidAmount), + notes: notes, + status: 'pending', + }) + .select(); + if (error) throw error; + return data[0]; + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['bids', loadId] }); + onSuccess?.(); + onClose(); + }, + }); + + const handleSubmit = async (e) => { + e.preventDefault(); + setIsSubmitting(true); + try { + await mutation.mutateAsync(); + } catch (err) { + console.error('Bid submission failed:', err.message); + } finally { + setIsSubmitting(false); + } + }; + + return ( +
+
+
+
+
+
Submit Bid for Load
+ +
+
+
+ + setBidAmount(e.target.value)} + required + /> +
+
+ +