Some checks failed
CI/CD Pipeline - Xpeditis PreProd / Backend - Build & Test (push) Failing after 5m51s
CI/CD Pipeline - Xpeditis PreProd / Backend - Docker Build & Push (push) Has been skipped
CI/CD Pipeline - Xpeditis PreProd / Frontend - Build & Test (push) Successful in 10m57s
CI/CD Pipeline - Xpeditis PreProd / Frontend - Docker Build & Push (push) Failing after 12m28s
CI/CD Pipeline - Xpeditis PreProd / Deploy to PreProd Server (push) Has been skipped
CI/CD Pipeline - Xpeditis PreProd / Run Smoke Tests (push) Has been skipped
290 lines
7.8 KiB
TypeScript
290 lines
7.8 KiB
TypeScript
/**
|
|
* Bookings API
|
|
*
|
|
* Endpoints for managing container bookings
|
|
*/
|
|
|
|
import { get, post, patch, upload } from './client';
|
|
import type {
|
|
CreateBookingRequest,
|
|
BookingResponse,
|
|
BookingListResponse,
|
|
SuccessResponse,
|
|
} from '@/types/api';
|
|
|
|
// TODO: These types should be moved to @/types/api.ts
|
|
export interface BookingSearchRequest {
|
|
query?: string;
|
|
status?: string;
|
|
originPort?: string;
|
|
destinationPort?: string;
|
|
startDate?: string;
|
|
endDate?: string;
|
|
organizationId?: string;
|
|
}
|
|
|
|
export interface BookingSearchResponse {
|
|
bookings: BookingResponse[];
|
|
total: number;
|
|
}
|
|
|
|
export interface UpdateBookingStatusRequest {
|
|
status: string;
|
|
notes?: string;
|
|
}
|
|
|
|
/**
|
|
* CSV Booking types
|
|
*/
|
|
export interface CsvBookingResponse {
|
|
id: string;
|
|
bookingId: string;
|
|
carrierName: string;
|
|
carrierEmail: string;
|
|
origin: string;
|
|
destination: string;
|
|
volumeCBM: number;
|
|
weightKG: number;
|
|
palletCount: number;
|
|
priceUSD: number;
|
|
priceEUR: number;
|
|
primaryCurrency: string;
|
|
transitDays: number;
|
|
containerType: string;
|
|
status: 'PENDING' | 'ACCEPTED' | 'REJECTED';
|
|
documents: Array<{
|
|
type: string;
|
|
fileName: string;
|
|
url: string;
|
|
}>;
|
|
notes?: string;
|
|
confirmationToken: string;
|
|
emailSentAt?: string;
|
|
acceptedAt?: string;
|
|
rejectedAt?: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CsvBookingListResponse {
|
|
bookings: CsvBookingResponse[]; // Changed from 'items' to match backend response
|
|
total: number;
|
|
page: number;
|
|
limit: number;
|
|
totalPages: number;
|
|
}
|
|
|
|
export interface CsvBookingStatsResponse {
|
|
total: number;
|
|
pending: number;
|
|
accepted: number;
|
|
rejected: number;
|
|
}
|
|
|
|
/**
|
|
* Create a new booking
|
|
* POST /api/v1/bookings
|
|
*/
|
|
export async function createBooking(data: CreateBookingRequest): Promise<BookingResponse> {
|
|
return post<BookingResponse>('/api/v1/bookings', data);
|
|
}
|
|
|
|
/**
|
|
* Get booking by ID
|
|
* GET /api/v1/bookings/:id
|
|
*/
|
|
export async function getBooking(id: string): Promise<BookingResponse> {
|
|
return get<BookingResponse>(`/api/v1/bookings/${id}`);
|
|
}
|
|
|
|
/**
|
|
* Get booking by booking number
|
|
* GET /api/v1/bookings/number/:bookingNumber
|
|
*/
|
|
export async function getBookingByNumber(bookingNumber: string): Promise<BookingResponse> {
|
|
return get<BookingResponse>(`/api/v1/bookings/number/${bookingNumber}`);
|
|
}
|
|
|
|
/**
|
|
* List bookings with pagination
|
|
* GET /api/v1/bookings?page=1&limit=20&status=CONFIRMED&organizationId=xxx
|
|
*/
|
|
export async function listBookings(params?: {
|
|
page?: number;
|
|
limit?: number;
|
|
status?: string;
|
|
organizationId?: string;
|
|
}): Promise<BookingListResponse> {
|
|
const queryParams = new URLSearchParams();
|
|
if (params?.page) queryParams.append('page', params.page.toString());
|
|
if (params?.limit) queryParams.append('limit', params.limit.toString());
|
|
if (params?.status) queryParams.append('status', params.status);
|
|
if (params?.organizationId) queryParams.append('organizationId', params.organizationId);
|
|
|
|
const queryString = queryParams.toString();
|
|
return get<BookingListResponse>(`/api/v1/bookings${queryString ? `?${queryString}` : ''}`);
|
|
}
|
|
|
|
/**
|
|
* Fuzzy search bookings
|
|
* GET /api/v1/bookings/search?q=WCM-2024&limit=10
|
|
*/
|
|
export async function fuzzySearchBookings(params: {
|
|
q: string;
|
|
limit?: number;
|
|
}): Promise<BookingSearchResponse> {
|
|
const queryParams = new URLSearchParams();
|
|
queryParams.append('q', params.q);
|
|
if (params.limit) queryParams.append('limit', params.limit.toString());
|
|
|
|
return get<BookingSearchResponse>(`/api/v1/bookings/search?${queryParams.toString()}`);
|
|
}
|
|
|
|
/**
|
|
* Advanced search bookings
|
|
* POST /api/v1/bookings/search/advanced
|
|
*/
|
|
export async function advancedSearchBookings(
|
|
data: BookingSearchRequest
|
|
): Promise<BookingSearchResponse> {
|
|
return post<BookingSearchResponse>('/api/v1/bookings/search/advanced', data);
|
|
}
|
|
|
|
/**
|
|
* Export bookings (CSV/PDF)
|
|
* GET /api/v1/bookings/export?format=csv&status=CONFIRMED
|
|
* Returns blob for download
|
|
*/
|
|
export async function exportBookings(params: {
|
|
format: 'csv' | 'pdf';
|
|
status?: string;
|
|
organizationId?: string;
|
|
startDate?: string;
|
|
endDate?: string;
|
|
}): Promise<Blob> {
|
|
const queryParams = new URLSearchParams();
|
|
queryParams.append('format', params.format);
|
|
if (params.status) queryParams.append('status', params.status);
|
|
if (params.organizationId) queryParams.append('organizationId', params.organizationId);
|
|
if (params.startDate) queryParams.append('startDate', params.startDate);
|
|
if (params.endDate) queryParams.append('endDate', params.endDate);
|
|
|
|
const response = await fetch(
|
|
`${process.env.NEXT_PUBLIC_API_URL}/api/v1/bookings/export?${queryParams.toString()}`,
|
|
{
|
|
method: 'GET',
|
|
headers: {
|
|
Authorization: `Bearer ${
|
|
typeof window !== 'undefined' ? localStorage.getItem('access_token') : ''
|
|
}`,
|
|
},
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Export failed: ${response.statusText}`);
|
|
}
|
|
|
|
return response.blob();
|
|
}
|
|
|
|
/**
|
|
* Update booking status
|
|
* PATCH /api/v1/bookings/:id/status
|
|
*/
|
|
export async function updateBookingStatus(
|
|
id: string,
|
|
data: UpdateBookingStatusRequest
|
|
): Promise<SuccessResponse> {
|
|
return patch<SuccessResponse>(`/api/v1/bookings/${id}/status`, data);
|
|
}
|
|
|
|
// ============================================================================
|
|
// CSV BOOKINGS API
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Create a new CSV booking with document uploads
|
|
* POST /api/v1/csv-bookings
|
|
*
|
|
* Uses multipart/form-data for file uploads
|
|
*/
|
|
export async function createCsvBooking(formData: FormData): Promise<CsvBookingResponse> {
|
|
return upload<CsvBookingResponse>('/api/v1/csv-bookings', formData);
|
|
}
|
|
|
|
/**
|
|
* Get CSV booking by ID
|
|
* GET /api/v1/csv-bookings/:id
|
|
*/
|
|
export async function getCsvBooking(id: string): Promise<CsvBookingResponse> {
|
|
return get<CsvBookingResponse>(`/api/v1/csv-bookings/${id}`);
|
|
}
|
|
|
|
/**
|
|
* List CSV bookings with pagination and filters
|
|
* GET /api/v1/csv-bookings?page=1&limit=20&status=PENDING
|
|
*/
|
|
export async function listCsvBookings(params?: {
|
|
page?: number;
|
|
limit?: number;
|
|
status?: 'PENDING' | 'ACCEPTED' | 'REJECTED';
|
|
startDate?: string;
|
|
endDate?: string;
|
|
}): Promise<CsvBookingListResponse> {
|
|
const queryParams = new URLSearchParams();
|
|
if (params?.page) queryParams.append('page', params.page.toString());
|
|
if (params?.limit) queryParams.append('limit', params.limit.toString());
|
|
if (params?.status) queryParams.append('status', params.status);
|
|
if (params?.startDate) queryParams.append('startDate', params.startDate);
|
|
if (params?.endDate) queryParams.append('endDate', params.endDate);
|
|
|
|
const queryString = queryParams.toString();
|
|
return get<CsvBookingListResponse>(
|
|
`/api/v1/csv-bookings${queryString ? `?${queryString}` : ''}`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get CSV booking statistics for current user
|
|
* GET /api/v1/csv-bookings/stats
|
|
*/
|
|
export async function getCsvBookingStats(): Promise<CsvBookingStatsResponse> {
|
|
return get<CsvBookingStatsResponse>('/api/v1/csv-bookings/stats');
|
|
}
|
|
|
|
/**
|
|
* Cancel a pending CSV booking
|
|
* PATCH /api/v1/csv-bookings/:id/cancel
|
|
*/
|
|
export async function cancelCsvBooking(id: string): Promise<SuccessResponse> {
|
|
return patch<SuccessResponse>(`/api/v1/csv-bookings/${id}/cancel`, {});
|
|
}
|
|
|
|
/**
|
|
* Accept a CSV booking (public endpoint, no auth required)
|
|
* POST /api/v1/csv-bookings/:token/accept
|
|
*/
|
|
export async function acceptCsvBooking(token: string): Promise<CsvBookingResponse> {
|
|
return post<CsvBookingResponse>(
|
|
`/api/v1/csv-bookings/${token}/accept`,
|
|
{},
|
|
false // includeAuth = false
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Reject a CSV booking with reason (public endpoint, no auth required)
|
|
* POST /api/v1/csv-bookings/:token/reject
|
|
*/
|
|
export async function rejectCsvBooking(
|
|
token: string,
|
|
reason?: string
|
|
): Promise<CsvBookingResponse> {
|
|
return post<CsvBookingResponse>(
|
|
`/api/v1/csv-bookings/${token}/reject`,
|
|
{ reason },
|
|
false // includeAuth = false
|
|
);
|
|
}
|