132 lines
No EOL
3.9 KiB
JavaScript
132 lines
No EOL
3.9 KiB
JavaScript
import React, { useState } from 'react';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import { supabase } from '../supabaseClient';
|
|
|
|
// Utility functions (mirroring india.js logic for now)
|
|
const formatINR = (n) => {
|
|
if (n === null || n === undefined || isNaN(n)) return '—';
|
|
return '₹' + parseFloat(n).toLocaleString('en-IN');
|
|
};
|
|
|
|
const getStatusColor = (status) => {
|
|
const colors = {
|
|
'settled': 'success',
|
|
'completed': 'success',
|
|
'commission received': 'success',
|
|
'reconciled': 'success',
|
|
'loaded / in transit': 'primary',
|
|
'assigned': 'primary',
|
|
'assigned vehicle': 'primary',
|
|
'pending collection': 'warning',
|
|
'partially pending': 'warning',
|
|
'fully pending from shipper': 'warning',
|
|
'commission due': 'warning',
|
|
'cancelled': 'danger',
|
|
'partial': 'secondary',
|
|
'available vehicle': 'secondary',
|
|
};
|
|
return colors[status] || 'secondary';
|
|
};
|
|
|
|
function LoadsList() {
|
|
const [filterStatus, setFilterStatus] = useState('');
|
|
const [searchTerm, setSearchTerm] = useState('');
|
|
|
|
const { data: loads = [], isLoading, isError, refetch } = useQuery({
|
|
queryKey: ['loads', filterStatus, searchTerm],
|
|
queryFn: async () => {
|
|
let query = supabase
|
|
.from('loads')
|
|
.select(`
|
|
id,
|
|
date,
|
|
from_city,
|
|
to_city,
|
|
freight_charged,
|
|
commission,
|
|
status,
|
|
shipper:shippers(name),
|
|
vehicle:vehicles(number)
|
|
`)
|
|
.order('date', { ascending: false })
|
|
.limit(100);
|
|
|
|
if (filterStatus) query = query.eq('status', filterStatus);
|
|
if (searchTerm) {
|
|
query = query.or(`from_city.ilike.%${searchTerm}%,to_city.ilike.%${searchTerm}%`);
|
|
}
|
|
|
|
const { data, error } = await query;
|
|
if (error) throw error;
|
|
return data;
|
|
},
|
|
staleTime: 5 * 60 * 1000, // 5 minutes
|
|
});
|
|
|
|
if (isLoading) return <div className="text-center py-5">Loading loads...</div>;
|
|
if (isError) return <div className="text-center py-5 text-danger">Error loading loads</div>;
|
|
|
|
return (
|
|
<div className="container mt-4">
|
|
<h2>Loads Management</h2>
|
|
|
|
{/* Filters */}
|
|
<div className="row mb-3 g-2">
|
|
<div className="col-md-4">
|
|
<input
|
|
type="text"
|
|
className="form-control"
|
|
placeholder="Search cities..."
|
|
value={searchTerm}
|
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
/>
|
|
</div>
|
|
<div className="col-md-4">
|
|
<select
|
|
className="form-select"
|
|
value={filterStatus}
|
|
onChange={(e) => setFilterStatus(e.target.value)}
|
|
>
|
|
<option value="">All Statuses</option>
|
|
<option value="settled">Settled</option>
|
|
<option value="loaded / in transit">In Transit</option>
|
|
<option value="pending collection">Pending Collection</option>
|
|
<option value="cancelled">Cancelled</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Loads Table */}
|
|
<table className="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th>Date</th>
|
|
<th>Route</th>
|
|
<th>Shipper</th>
|
|
<th>Freight</th>
|
|
<th>Commission</th>
|
|
<th>Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{loads.map((load) => (
|
|
<tr key={load.id}>
|
|
<td>{new Date(load.date).toLocaleDateString('en-IN')}</td>
|
|
<td>{load.from_city} → {load.to_city}</td>
|
|
<td>{load.shipper?.name || '—'}</td>
|
|
<td>{formatINR(load.freight_charged)}</td>
|
|
<td>{formatINR(load.commission)}</td>
|
|
<td>
|
|
<span className={`badge bg-${getStatusColor(load.status)}`}>
|
|
{load.status}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default LoadsList; |