refactor: modularize API client by splitting resources into individual files and standardizing HTTP request handling.
This commit is contained in:
@@ -1,41 +1,7 @@
|
|||||||
import type {
|
|
||||||
AdminBooking,
|
|
||||||
Complex,
|
|
||||||
ComplexWithRole,
|
|
||||||
Court,
|
|
||||||
CreateAdminBookingInput,
|
|
||||||
CreateComplexPayload,
|
|
||||||
CreateComplexResponse,
|
|
||||||
CreateCourtInput,
|
|
||||||
CreatePublicBookingInput,
|
|
||||||
CreateSportInput,
|
|
||||||
OnboardingCompleteInput,
|
|
||||||
OnboardingCompleteResponse,
|
|
||||||
OnboardingResendOtpInput,
|
|
||||||
OnboardingResendOtpResponse,
|
|
||||||
OnboardingStartInput,
|
|
||||||
OnboardingStartResponse,
|
|
||||||
OnboardingVerifyOtpInput,
|
|
||||||
OnboardingVerifyOtpResponse,
|
|
||||||
PlanSummary,
|
|
||||||
PublicAvailabilityResponse,
|
|
||||||
PublicBooking,
|
|
||||||
PublicBookingConfirmation,
|
|
||||||
SelectComplexInput,
|
|
||||||
Sport,
|
|
||||||
UpdateAdminBookingStatusInput,
|
|
||||||
UpdateComplexPayload,
|
|
||||||
UpdateComplexResponse,
|
|
||||||
UpdateCourtInput,
|
|
||||||
UpdateSportInput,
|
|
||||||
UserProfileResponse,
|
|
||||||
} from '@repo/api-contract';
|
|
||||||
import axios, { AxiosError } from 'axios';
|
|
||||||
import { createAuthClient } from 'better-auth/react';
|
import { createAuthClient } from 'better-auth/react';
|
||||||
|
import * as api from './api';
|
||||||
|
|
||||||
const apiBaseUrl =
|
const apiBaseUrl = api.apiBaseUrl;
|
||||||
import.meta.env.VITE_API_BASE_URL?.trim() ||
|
|
||||||
(import.meta.env.DEV ? 'http://localhost:3000' : window.location.origin);
|
|
||||||
|
|
||||||
export const authClient = createAuthClient({
|
export const authClient = createAuthClient({
|
||||||
baseURL: apiBaseUrl,
|
baseURL: apiBaseUrl,
|
||||||
@@ -61,245 +27,30 @@ export class ApiClientError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ErrorHandlers = {
|
|
||||||
onForbidden?: (error: ApiClientError) => void | Promise<void>;
|
|
||||||
onError?: (error: ApiClientError) => void | Promise<void>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlers: ErrorHandlers = {};
|
|
||||||
|
|
||||||
const http = axios.create({
|
|
||||||
baseURL: apiBaseUrl,
|
|
||||||
withCredentials: true,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
function extractMessage(details: unknown, fallback: string) {
|
|
||||||
if (
|
|
||||||
typeof details === 'object' &&
|
|
||||||
details &&
|
|
||||||
'message' in details &&
|
|
||||||
typeof details.message === 'string'
|
|
||||||
) {
|
|
||||||
return details.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeError(error: unknown): ApiClientError {
|
|
||||||
if (error instanceof ApiClientError) return error;
|
|
||||||
|
|
||||||
if (axios.isAxiosError(error)) {
|
|
||||||
const status = error.response?.status;
|
|
||||||
const details = error.response?.data;
|
|
||||||
|
|
||||||
return new ApiClientError(extractMessage(details, error.message || 'Request failed'), {
|
|
||||||
status,
|
|
||||||
details,
|
|
||||||
causeError: error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error instanceof Error) {
|
|
||||||
return new ApiClientError(error.message, {
|
|
||||||
causeError: error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ApiClientError('Error inesperado.', {
|
|
||||||
causeError: error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
http.interceptors.response.use(
|
|
||||||
(response) => response,
|
|
||||||
async (error: AxiosError) => {
|
|
||||||
const normalized = normalizeError(error);
|
|
||||||
|
|
||||||
if (normalized.status === 403 && handlers.onForbidden) {
|
|
||||||
await handlers.onForbidden(normalized);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handlers.onError) {
|
|
||||||
await handlers.onError(normalized);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.reject(normalized);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const apiClient = {
|
export const apiClient = {
|
||||||
onboarding: {
|
onboarding: api.onboarding,
|
||||||
start: async (payload: OnboardingStartInput) => {
|
plans: api.plans,
|
||||||
const response = await http.post<OnboardingStartResponse>('/api/onboarding/start', payload);
|
user: api.user,
|
||||||
return response.data;
|
complexes: api.complexes,
|
||||||
},
|
sports: api.sports,
|
||||||
verifyOtp: async (payload: OnboardingVerifyOtpInput) => {
|
courts: api.courts,
|
||||||
const response = await http.post<OnboardingVerifyOtpResponse>(
|
|
||||||
'/api/onboarding/verify-otp',
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
resendOtp: async (payload: OnboardingResendOtpInput) => {
|
|
||||||
const response = await http.post<OnboardingResendOtpResponse>(
|
|
||||||
'/api/onboarding/resend-otp',
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
complete: async (payload: OnboardingCompleteInput) => {
|
|
||||||
const response = await http.post<OnboardingCompleteResponse>(
|
|
||||||
'/api/onboarding/complete',
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plans: {
|
|
||||||
list: async () => {
|
|
||||||
const response = await http.get<PlanSummary[]>('/api/plans');
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
getProfile: async () => {
|
|
||||||
const response = await http.get<UserProfileResponse>('/api/user/profile');
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
complexes: {
|
|
||||||
create: async (payload: CreateComplexPayload) => {
|
|
||||||
const response = await http.post<CreateComplexResponse>('/api/complexes', payload);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
getById: async (id: string) => {
|
|
||||||
const response = await http.get<Complex>(`/api/complexes/${id}`);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
getBySlug: async (slug: string) => {
|
|
||||||
const response = await http.get<Complex>(`/api/complexes/slug/${slug}`);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
update: async (id: string, payload: UpdateComplexPayload) => {
|
|
||||||
const response = await http.patch<UpdateComplexResponse>(`/api/complexes/${id}`, payload);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
listMine: async () => {
|
|
||||||
const response = await http.get<ComplexWithRole[]>('/api/complexes/mine');
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
getCurrent: async () => {
|
|
||||||
const response = await http.get<ComplexWithRole | null>('/api/complexes/me');
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
select: async (payload: SelectComplexInput) => {
|
|
||||||
const response = await http.post<ComplexWithRole>('/api/complexes/select', JSON.stringify(payload), {
|
|
||||||
headers: { 'Content-Type': 'application/json' }
|
|
||||||
});
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
sports: {
|
|
||||||
list: async () => {
|
|
||||||
const response = await http.get<Sport[]>('/api/sports');
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
create: async (payload: CreateSportInput) => {
|
|
||||||
const response = await http.post<Sport>('/api/sports', payload);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
update: async (id: string, payload: UpdateSportInput) => {
|
|
||||||
const response = await http.patch<Sport>(`/api/sports/${id}`, payload);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
courts: {
|
|
||||||
listByComplex: async (complexId: string) => {
|
|
||||||
const response = await http.get<Court[]>(`/api/courts/complex/${complexId}`);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
create: async (complexId: string, payload: CreateCourtInput) => {
|
|
||||||
const response = await http.post<Court>(`/api/courts/complex/${complexId}`, payload);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
update: async (id: string, payload: UpdateCourtInput) => {
|
|
||||||
const response = await http.patch<Court>(`/api/courts/${id}`, payload);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
publicBookings: {
|
publicBookings: {
|
||||||
getAvailability: async (
|
getAvailability: api.getAvailability,
|
||||||
complexSlug: string,
|
create: api.createPublic,
|
||||||
params: {
|
getConfirmation: api.getConfirmation,
|
||||||
date: string;
|
|
||||||
sportId?: string;
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
const response = await http.get<PublicAvailabilityResponse>(
|
|
||||||
`/api/public-bookings/complex/${complexSlug}/availability`,
|
|
||||||
{
|
|
||||||
params,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
create: async (complexSlug: string, payload: CreatePublicBookingInput) => {
|
|
||||||
const response = await http.post<PublicBooking>(
|
|
||||||
`/api/public-bookings/complex/${complexSlug}`,
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
getConfirmation: async (complexSlug: string, bookingCode: string) => {
|
|
||||||
const response = await http.get<PublicBookingConfirmation>(
|
|
||||||
`/api/public-bookings/complex/${complexSlug}/confirmation/${bookingCode}`
|
|
||||||
);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
adminBookings: {
|
adminBookings: {
|
||||||
listByComplex: async (
|
listByComplex: api.listByComplex,
|
||||||
complexId: string,
|
create: api.createAdmin,
|
||||||
params: {
|
updateStatus: api.updateStatus,
|
||||||
fromDate: string;
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
const response = await http.get<{ bookings: AdminBooking[] }>(
|
|
||||||
`/api/admin-bookings/complex/${complexId}`,
|
|
||||||
{
|
|
||||||
params,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
create: async (complexId: string, payload: CreateAdminBookingInput) => {
|
|
||||||
const response = await http.post<AdminBooking>(
|
|
||||||
`/api/admin-bookings/complex/${complexId}`,
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
updateStatus: async (bookingId: string, payload: UpdateAdminBookingStatusInput) => {
|
|
||||||
const response = await http.patch<AdminBooking>(
|
|
||||||
`/api/admin-bookings/${bookingId}/status`,
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
return response.data;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ApiClient = typeof apiClient;
|
export type { ErrorHandlers } from './api';
|
||||||
|
export { configureApiClient } from './api';
|
||||||
export function configureApiClient(nextHandlers: ErrorHandlers) {
|
|
||||||
handlers.onForbidden = nextHandlers.onForbidden;
|
|
||||||
handlers.onError = nextHandlers.onError;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setApiAccessToken(_token: string | null) {
|
export function setApiAccessToken(_token: string | null) {
|
||||||
// accessToken = token;
|
// accessToken = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ApiClient = typeof apiClient;
|
||||||
34
apps/frontend/src/lib/api/base.ts
Normal file
34
apps/frontend/src/lib/api/base.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
export class ApiClientError extends Error {
|
||||||
|
status?: number;
|
||||||
|
details?: unknown;
|
||||||
|
causeError?: unknown;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
message: string,
|
||||||
|
options?: { status?: number; details?: unknown; causeError?: unknown }
|
||||||
|
) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'ApiClientError';
|
||||||
|
this.status = options?.status;
|
||||||
|
this.details = options?.details;
|
||||||
|
this.causeError = options?.causeError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ErrorHandlers = {
|
||||||
|
onForbidden?: (error: ApiClientError) => void | Promise<void>;
|
||||||
|
onError?: (error: ApiClientError) => void | Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlers: ErrorHandlers = {};
|
||||||
|
|
||||||
|
export const apiBaseUrl =
|
||||||
|
import.meta.env.VITE_API_BASE_URL?.trim() ||
|
||||||
|
(import.meta.env.DEV ? 'http://localhost:3000' : window.location.origin);
|
||||||
|
|
||||||
|
export function configureApiClient(nextHandlers: ErrorHandlers) {
|
||||||
|
handlers.onForbidden = nextHandlers.onForbidden;
|
||||||
|
handlers.onError = nextHandlers.onError;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { handlers };
|
||||||
60
apps/frontend/src/lib/api/http.ts
Normal file
60
apps/frontend/src/lib/api/http.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import axios, { AxiosError, type AxiosInstance } from 'axios';
|
||||||
|
import { ApiClientError, apiBaseUrl, handlers } from './base';
|
||||||
|
|
||||||
|
export const http: AxiosInstance = axios.create({
|
||||||
|
baseURL: apiBaseUrl,
|
||||||
|
withCredentials: true,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function extractMessage(details: unknown, fallback: string): string {
|
||||||
|
if (
|
||||||
|
typeof details === 'object' &&
|
||||||
|
details &&
|
||||||
|
'message' in details &&
|
||||||
|
typeof details.message === 'string'
|
||||||
|
) {
|
||||||
|
return details.message;
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeError(error: unknown): ApiClientError {
|
||||||
|
if (error instanceof ApiClientError) return error;
|
||||||
|
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
const status = error.response?.status;
|
||||||
|
const details = error.response?.data;
|
||||||
|
|
||||||
|
return new ApiClientError(extractMessage(details, error.message || 'Request failed'), {
|
||||||
|
status,
|
||||||
|
details,
|
||||||
|
causeError: error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error instanceof Error) {
|
||||||
|
return new ApiClientError(error.message, { causeError: error });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ApiClientError('Error inesperado.', { causeError: error });
|
||||||
|
}
|
||||||
|
|
||||||
|
http.interceptors.response.use(
|
||||||
|
(response) => response,
|
||||||
|
async (error: AxiosError) => {
|
||||||
|
const normalized = normalizeError(error);
|
||||||
|
|
||||||
|
if (normalized.status === 403 && handlers.onForbidden) {
|
||||||
|
await handlers.onForbidden(normalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handlers.onError) {
|
||||||
|
await handlers.onError(normalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(normalized);
|
||||||
|
}
|
||||||
|
);
|
||||||
18
apps/frontend/src/lib/api/index.ts
Normal file
18
apps/frontend/src/lib/api/index.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export { ApiClientError, configureApiClient, apiBaseUrl, type ErrorHandlers } from './base';
|
||||||
|
export { http } from './http';
|
||||||
|
|
||||||
|
export * as complexes from './resources/complexes';
|
||||||
|
export * as courts from './resources/courts';
|
||||||
|
export * as user from './resources/user';
|
||||||
|
export * as sports from './resources/sports';
|
||||||
|
export * as plans from './resources/plans';
|
||||||
|
export * as onboarding from './resources/onboarding';
|
||||||
|
|
||||||
|
export {
|
||||||
|
getAvailability,
|
||||||
|
createPublic,
|
||||||
|
getConfirmation,
|
||||||
|
listByComplex,
|
||||||
|
createAdmin,
|
||||||
|
updateStatus,
|
||||||
|
} from './resources/bookings';
|
||||||
60
apps/frontend/src/lib/api/resources/bookings.ts
Normal file
60
apps/frontend/src/lib/api/resources/bookings.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import type {
|
||||||
|
AdminBooking,
|
||||||
|
CreateAdminBookingInput,
|
||||||
|
CreatePublicBookingInput,
|
||||||
|
PublicAvailabilityResponse,
|
||||||
|
PublicBooking,
|
||||||
|
PublicBookingConfirmation,
|
||||||
|
UpdateAdminBookingStatusInput,
|
||||||
|
} from '@repo/api-contract';
|
||||||
|
import { http } from '../http';
|
||||||
|
|
||||||
|
export async function getAvailability(
|
||||||
|
complexSlug: string,
|
||||||
|
params: { date: string; sportId?: string }
|
||||||
|
) {
|
||||||
|
const response = await http.get<PublicAvailabilityResponse>(
|
||||||
|
`/api/public-bookings/complex/${complexSlug}/availability`,
|
||||||
|
{ params }
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createPublic(complexSlug: string, payload: CreatePublicBookingInput) {
|
||||||
|
const response = await http.post<PublicBooking>(
|
||||||
|
`/api/public-bookings/complex/${complexSlug}`,
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getConfirmation(complexSlug: string, bookingCode: string) {
|
||||||
|
const response = await http.get<PublicBookingConfirmation>(
|
||||||
|
`/api/public-bookings/complex/${complexSlug}/confirmation/${bookingCode}`
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listByComplex(complexId: string, params: { fromDate: string }) {
|
||||||
|
const response = await http.get<{ bookings: AdminBooking[] }>(
|
||||||
|
`/api/admin-bookings/complex/${complexId}`,
|
||||||
|
{ params }
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createAdmin(complexId: string, payload: CreateAdminBookingInput) {
|
||||||
|
const response = await http.post<AdminBooking>(
|
||||||
|
`/api/admin-bookings/complex/${complexId}`,
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateStatus(bookingId: string, payload: UpdateAdminBookingStatusInput) {
|
||||||
|
const response = await http.patch<AdminBooking>(
|
||||||
|
`/api/admin-bookings/${bookingId}/status`,
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
49
apps/frontend/src/lib/api/resources/complexes.ts
Normal file
49
apps/frontend/src/lib/api/resources/complexes.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import type {
|
||||||
|
Complex,
|
||||||
|
ComplexWithRole,
|
||||||
|
CreateComplexPayload,
|
||||||
|
CreateComplexResponse,
|
||||||
|
SelectComplexInput,
|
||||||
|
UpdateComplexPayload,
|
||||||
|
UpdateComplexResponse,
|
||||||
|
} from '@repo/api-contract';
|
||||||
|
import { http } from '../http';
|
||||||
|
|
||||||
|
export async function create(payload: CreateComplexPayload) {
|
||||||
|
const response = await http.post<CreateComplexResponse>('/api/complexes', payload);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getById(id: string) {
|
||||||
|
const response = await http.get<Complex>(`/api/complexes/${id}`);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getBySlug(slug: string) {
|
||||||
|
const response = await http.get<Complex>(`/api/complexes/slug/${slug}`);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function update(id: string, payload: UpdateComplexPayload) {
|
||||||
|
const response = await http.patch<UpdateComplexResponse>(`/api/complexes/${id}`, payload);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listMine() {
|
||||||
|
const response = await http.get<ComplexWithRole[]>('/api/complexes/mine');
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCurrent() {
|
||||||
|
const response = await http.get<ComplexWithRole | null>('/api/complexes/me');
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function select(payload: SelectComplexInput) {
|
||||||
|
const response = await http.post<ComplexWithRole>(
|
||||||
|
'/api/complexes/select',
|
||||||
|
JSON.stringify(payload),
|
||||||
|
{ headers: { 'Content-Type': 'application/json' } }
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
17
apps/frontend/src/lib/api/resources/courts.ts
Normal file
17
apps/frontend/src/lib/api/resources/courts.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import type { Court, CreateCourtInput, UpdateCourtInput } from '@repo/api-contract';
|
||||||
|
import { http } from '../http';
|
||||||
|
|
||||||
|
export async function listByComplex(complexId: string) {
|
||||||
|
const response = await http.get<Court[]>(`/api/courts/complex/${complexId}`);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function create(complexId: string, payload: CreateCourtInput) {
|
||||||
|
const response = await http.post<Court>(`/api/courts/complex/${complexId}`, payload);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function update(id: string, payload: UpdateCourtInput) {
|
||||||
|
const response = await http.patch<Court>(`/api/courts/${id}`, payload);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
40
apps/frontend/src/lib/api/resources/onboarding.ts
Normal file
40
apps/frontend/src/lib/api/resources/onboarding.ts
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import type {
|
||||||
|
OnboardingCompleteInput,
|
||||||
|
OnboardingCompleteResponse,
|
||||||
|
OnboardingResendOtpInput,
|
||||||
|
OnboardingResendOtpResponse,
|
||||||
|
OnboardingStartInput,
|
||||||
|
OnboardingStartResponse,
|
||||||
|
OnboardingVerifyOtpInput,
|
||||||
|
OnboardingVerifyOtpResponse,
|
||||||
|
} from '@repo/api-contract';
|
||||||
|
import { http } from '../http';
|
||||||
|
|
||||||
|
export async function start(payload: OnboardingStartInput) {
|
||||||
|
const response = await http.post<OnboardingStartResponse>('/api/onboarding/start', payload);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function verifyOtp(payload: OnboardingVerifyOtpInput) {
|
||||||
|
const response = await http.post<OnboardingVerifyOtpResponse>(
|
||||||
|
'/api/onboarding/verify-otp',
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resendOtp(payload: OnboardingResendOtpInput) {
|
||||||
|
const response = await http.post<OnboardingResendOtpResponse>(
|
||||||
|
'/api/onboarding/resend-otp',
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function complete(payload: OnboardingCompleteInput) {
|
||||||
|
const response = await http.post<OnboardingCompleteResponse>(
|
||||||
|
'/api/onboarding/complete',
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
7
apps/frontend/src/lib/api/resources/plans.ts
Normal file
7
apps/frontend/src/lib/api/resources/plans.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import type { PlanSummary } from '@repo/api-contract';
|
||||||
|
import { http } from '../http';
|
||||||
|
|
||||||
|
export async function list() {
|
||||||
|
const response = await http.get<PlanSummary[]>('/api/plans');
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
17
apps/frontend/src/lib/api/resources/sports.ts
Normal file
17
apps/frontend/src/lib/api/resources/sports.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import type { CreateSportInput, Sport, UpdateSportInput } from '@repo/api-contract';
|
||||||
|
import { http } from '../http';
|
||||||
|
|
||||||
|
export async function list() {
|
||||||
|
const response = await http.get<Sport[]>('/api/sports');
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function create(payload: CreateSportInput) {
|
||||||
|
const response = await http.post<Sport>('/api/sports', payload);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function update(id: string, payload: UpdateSportInput) {
|
||||||
|
const response = await http.patch<Sport>(`/api/sports/${id}`, payload);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
7
apps/frontend/src/lib/api/resources/user.ts
Normal file
7
apps/frontend/src/lib/api/resources/user.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import type { UserProfileResponse } from '@repo/api-contract';
|
||||||
|
import { http } from '../http';
|
||||||
|
|
||||||
|
export async function getProfile() {
|
||||||
|
const response = await http.get<UserProfileResponse>('/api/user/profile');
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user