You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

160 lines
4.7 KiB
TypeScript

import { BookingError } from '@/src/lib/errors';
import { BookingResponseOk } from '@/src/lib/types';
type ApiClient = (url: string, options?: RequestInit) => Promise<Response>;
/**
* Service layer for booking-related API operations
* Centralizes all API calls and error handling
*/
export class BookingService {
constructor(private readonly apiClient: ApiClient) {}
/**
* Get booking details by slot ID
*/
async getBooking(slotId: string): Promise<BookingResponseOk> {
try {
const response = await this.apiClient(`/booking/${encodeURIComponent(slotId)}`);
if (!response.ok) {
if (response.status === 404) {
throw BookingError.notFound(slotId);
}
const data = await response.json().catch(() => ({ message: 'Failed to load booking' }));
throw BookingError.serverError(data.message || 'Failed to load booking');
}
return response.json();
} catch (error) {
if (error instanceof BookingError) throw error;
throw BookingError.networkError('Network error while loading booking');
}
}
/**
* Approve a swap approval request
*/
async approveSwap(slotId: string, approvalId: number): Promise<void> {
try {
const response = await this.apiClient(
`/booking/${slotId}/swap-approval/${approvalId}`,
{ method: 'PATCH' }
);
if (!response.ok) {
const data = await response.json().catch(() => ({ message: 'Failed to approve swap' }));
if (response.status === 400) {
throw BookingError.badRequest(data.message || 'Invalid approval request');
}
throw BookingError.serverError(data.message || 'Failed to approve swap');
}
} catch (error) {
if (error instanceof BookingError) throw error;
throw BookingError.networkError('Network error while approving swap');
}
}
/**
* Cancel a swap group
*/
async cancelSwap(slotId: string, swapGroupId: string): Promise<void> {
try {
const response = await this.apiClient(
`/booking/${slotId}/swap-group/${swapGroupId}`,
{ method: 'DELETE' }
);
if (!response.ok) {
const data = await response.json().catch(() => ({ message: 'Failed to cancel swap' }));
if (response.status === 403) {
throw BookingError.forbidden(data.message || 'Only the initiator can cancel a swap');
}
if (response.status === 400) {
throw BookingError.badRequest(data.message || 'Invalid cancellation request');
}
throw BookingError.serverError(data.message || 'Failed to cancel swap');
}
} catch (error) {
if (error instanceof BookingError) throw error;
throw BookingError.networkError('Network error while cancelling swap');
}
}
/**
* Submit or update match score
*/
async submitMatchScore(
slotId: string,
sets: number[][],
method: 'POST' | 'PATCH' = 'POST'
): Promise<void> {
try {
const response = await this.apiClient(
`/booking/${slotId}/match/result`,
{
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sets })
}
);
if (!response.ok) {
const data = await response.json().catch(() => ({ message: 'Failed to submit score' }));
if (response.status === 400) {
throw BookingError.badRequest(data.message || 'Invalid score data');
}
throw BookingError.serverError(data.message || 'Failed to submit score');
}
} catch (error) {
if (error instanceof BookingError) throw error;
throw BookingError.networkError('Network error while submitting score');
}
}
/**
* Change match type
*/
async changeMatchType(
slotId: string,
matchTypeId: number,
forced: boolean = false
): Promise<void> {
try {
const response = await this.apiClient(
`/booking/${slotId}/match-type`,
{
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ match_type_id: matchTypeId, forced })
}
);
if (!response.ok) {
const data = await response.json().catch(() => ({ message: 'Failed to change match type' }));
if (response.status === 400) {
throw BookingError.badRequest(data.message || 'Invalid match type change request');
}
if (response.status === 403) {
throw BookingError.forbidden(data.message || 'Not authorized to change match type');
}
throw BookingError.serverError(data.message || 'Failed to change match type');
}
} catch (error) {
if (error instanceof BookingError) throw error;
throw BookingError.networkError('Network error while changing match type');
}
}
}