153 lines
4.9 KiB
TypeScript
153 lines
4.9 KiB
TypeScript
/**
|
|
* Custom hook for managing bookings
|
|
*/
|
|
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import { Booking, BookingFilters, BookingListResponse, ExportOptions } from '@/types/booking';
|
|
|
|
export function useBookings(initialFilters?: BookingFilters) {
|
|
const [bookings, setBookings] = useState<Booking[]>([]);
|
|
const [total, setTotal] = useState(0);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [filters, setFilters] = useState<BookingFilters>(initialFilters || {});
|
|
const [selectedBookings, setSelectedBookings] = useState<Set<string>>(new Set());
|
|
|
|
const fetchBookings = useCallback(async () => {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
// Build query parameters
|
|
const queryParams = new URLSearchParams();
|
|
|
|
if (filters.status && filters.status.length > 0) {
|
|
queryParams.append('status', filters.status.join(','));
|
|
}
|
|
if (filters.search) queryParams.append('search', filters.search);
|
|
if (filters.carrier) queryParams.append('carrier', filters.carrier);
|
|
if (filters.originPort) queryParams.append('originPort', filters.originPort);
|
|
if (filters.destinationPort) queryParams.append('destinationPort', filters.destinationPort);
|
|
if (filters.shipper) queryParams.append('shipper', filters.shipper);
|
|
if (filters.consignee) queryParams.append('consignee', filters.consignee);
|
|
if (filters.createdFrom) queryParams.append('createdFrom', filters.createdFrom);
|
|
if (filters.createdTo) queryParams.append('createdTo', filters.createdTo);
|
|
if (filters.etdFrom) queryParams.append('etdFrom', filters.etdFrom);
|
|
if (filters.etdTo) queryParams.append('etdTo', filters.etdTo);
|
|
if (filters.sortBy) queryParams.append('sortBy', filters.sortBy);
|
|
if (filters.sortOrder) queryParams.append('sortOrder', filters.sortOrder);
|
|
|
|
queryParams.append('page', String(filters.page || 1));
|
|
queryParams.append('pageSize', String(filters.pageSize || 20));
|
|
|
|
const response = await fetch(`/api/v1/bookings/advanced/search?${queryParams.toString()}`, {
|
|
headers: {
|
|
Authorization: `Bearer ${localStorage.getItem('accessToken')}`,
|
|
},
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Failed to fetch bookings');
|
|
}
|
|
|
|
const data: BookingListResponse = await response.json();
|
|
setBookings(data.bookings);
|
|
setTotal(data.total);
|
|
} catch (err: any) {
|
|
setError(err.message || 'An error occurred');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [filters]);
|
|
|
|
useEffect(() => {
|
|
fetchBookings();
|
|
}, [fetchBookings]);
|
|
|
|
const updateFilters = useCallback((newFilters: Partial<BookingFilters>) => {
|
|
setFilters(prev => ({ ...prev, ...newFilters }));
|
|
}, []);
|
|
|
|
const resetFilters = useCallback(() => {
|
|
setFilters({});
|
|
setSelectedBookings(new Set());
|
|
}, []);
|
|
|
|
const toggleBookingSelection = useCallback((bookingId: string) => {
|
|
setSelectedBookings((prev: Set<string>) => {
|
|
const newSet = new Set(prev);
|
|
if (newSet.has(bookingId)) {
|
|
newSet.delete(bookingId);
|
|
} else {
|
|
newSet.add(bookingId);
|
|
}
|
|
return newSet;
|
|
});
|
|
}, []);
|
|
|
|
const toggleAllBookings = useCallback(() => {
|
|
if (selectedBookings.size === bookings.length) {
|
|
setSelectedBookings(new Set());
|
|
} else {
|
|
setSelectedBookings(new Set(bookings.map(b => b.id)));
|
|
}
|
|
}, [bookings, selectedBookings]);
|
|
|
|
const clearSelection = useCallback(() => {
|
|
setSelectedBookings(new Set());
|
|
}, []);
|
|
|
|
const exportBookings = useCallback(
|
|
async (options: ExportOptions) => {
|
|
try {
|
|
const response = await fetch('/api/v1/bookings/export', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
Authorization: `Bearer ${localStorage.getItem('accessToken')}`,
|
|
},
|
|
body: JSON.stringify({
|
|
format: options.format,
|
|
fields: options.fields,
|
|
bookingIds: options.bookingIds || Array.from(selectedBookings),
|
|
}),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Export failed');
|
|
}
|
|
|
|
// Download file
|
|
const blob = await response.blob();
|
|
const url = window.URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = `bookings-export.${options.format}`;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
window.URL.revokeObjectURL(url);
|
|
document.body.removeChild(a);
|
|
} catch (err: any) {
|
|
throw new Error(err.message || 'Export failed');
|
|
}
|
|
},
|
|
[selectedBookings]
|
|
);
|
|
|
|
return {
|
|
bookings,
|
|
total,
|
|
loading,
|
|
error,
|
|
filters,
|
|
selectedBookings,
|
|
updateFilters,
|
|
resetFilters,
|
|
toggleBookingSelection,
|
|
toggleAllBookings,
|
|
clearSelection,
|
|
exportBookings,
|
|
refetch: fetchBookings,
|
|
};
|
|
}
|