update to python fastpi
This commit is contained in:
86
Frontend/src/services/api/apiClient.ts
Normal file
86
Frontend/src/services/api/apiClient.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import axios from 'axios';
|
||||
|
||||
// Base URL from environment or default. Ensure it points to the
|
||||
// server API root (append '/api' if not provided) so frontend calls
|
||||
// like '/bookings/me' resolve to e.g. 'http://localhost:8000/api/bookings/me'.
|
||||
const rawBase = import.meta.env.VITE_API_URL || 'http://localhost:8000';
|
||||
// Normalize base and ensure a single /api suffix. If the provided
|
||||
// VITE_API_URL already points to the API root (contains '/api'),
|
||||
// don't append another '/api'.
|
||||
const normalized = String(rawBase).replace(/\/$/, '');
|
||||
const API_BASE_URL = /\/api(\/?$)/i.test(normalized)
|
||||
? normalized
|
||||
: normalized + '/api';
|
||||
|
||||
// Create axios instance
|
||||
const apiClient = axios.create({
|
||||
baseURL: API_BASE_URL,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
timeout: 10000,
|
||||
withCredentials: true, // Enable sending cookies
|
||||
});
|
||||
|
||||
// Request interceptor - Add token to header
|
||||
apiClient.interceptors.request.use(
|
||||
(config) => {
|
||||
// Normalize request URL: if a request path accidentally begins
|
||||
// with '/api', strip that prefix so it will be appended to
|
||||
// our baseURL exactly once. This prevents double '/api/api'
|
||||
// when code uses absolute '/api/...' paths.
|
||||
if (config.url && typeof config.url === 'string') {
|
||||
if (config.url.startsWith('/api/')) {
|
||||
config.url = config.url.replace(/^\/api/, '');
|
||||
}
|
||||
// Also avoid accidental double slashes after concatenation
|
||||
config.url = config.url.replace(/\/\/+/, '/');
|
||||
}
|
||||
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// Response interceptor - Handle common errors
|
||||
apiClient.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
// Handle network errors
|
||||
if (!error.response) {
|
||||
console.error('Network error:', error);
|
||||
// You can show a toast notification here
|
||||
return Promise.reject({
|
||||
...error,
|
||||
message: 'Network error. Please check ' +
|
||||
'your internet connection.',
|
||||
});
|
||||
}
|
||||
|
||||
if (error.response?.status === 401) {
|
||||
// Token expired or invalid
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('userInfo');
|
||||
window.location.href = '/login';
|
||||
}
|
||||
|
||||
// Handle other HTTP errors
|
||||
if (error.response?.status >= 500) {
|
||||
console.error('Server error:', error);
|
||||
return Promise.reject({
|
||||
...error,
|
||||
message: 'Server error. Please try again later.',
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default apiClient;
|
||||
137
Frontend/src/services/api/authService.ts
Normal file
137
Frontend/src/services/api/authService.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import apiClient from './apiClient';
|
||||
|
||||
// Types
|
||||
export interface LoginCredentials {
|
||||
email: string;
|
||||
password: string;
|
||||
rememberMe?: boolean;
|
||||
}
|
||||
|
||||
export interface RegisterData {
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
phone?: string;
|
||||
}
|
||||
|
||||
export interface AuthResponse {
|
||||
// Server may use `status: 'success'` or boolean `success`
|
||||
status?: string;
|
||||
success?: boolean;
|
||||
message?: string;
|
||||
data?: {
|
||||
token?: string;
|
||||
refreshToken?: string;
|
||||
user?: {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
phone?: string;
|
||||
avatar?: string;
|
||||
role: string;
|
||||
createdAt?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface ForgotPasswordData {
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface ResetPasswordData {
|
||||
token: string;
|
||||
password: string;
|
||||
confirmPassword: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auth Service - Handles API calls related
|
||||
* to authentication
|
||||
*/
|
||||
const authService = {
|
||||
/**
|
||||
* Login
|
||||
*/
|
||||
login: async (
|
||||
credentials: LoginCredentials
|
||||
): Promise<AuthResponse> => {
|
||||
const response = await apiClient.post<AuthResponse>(
|
||||
'/api/auth/login',
|
||||
credentials
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Register new account
|
||||
*/
|
||||
register: async (
|
||||
data: RegisterData
|
||||
): Promise<AuthResponse> => {
|
||||
const response = await apiClient.post<AuthResponse>(
|
||||
'/api/auth/register',
|
||||
data
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Logout
|
||||
*/
|
||||
logout: async (): Promise<void> => {
|
||||
try {
|
||||
await apiClient.post('/api/auth/logout');
|
||||
} catch (error) {
|
||||
console.error('Logout error:', error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get current user information
|
||||
*/
|
||||
getProfile: async (): Promise<AuthResponse> => {
|
||||
const response = await apiClient.get<AuthResponse>(
|
||||
'/api/auth/profile'
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh token
|
||||
*/
|
||||
refreshToken: async (): Promise<AuthResponse> => {
|
||||
// No need to send refreshToken in body - it's in cookie
|
||||
const response = await apiClient.post<AuthResponse>(
|
||||
'/api/auth/refresh-token'
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Forgot password - Send reset email
|
||||
*/
|
||||
forgotPassword: async (
|
||||
data: ForgotPasswordData
|
||||
): Promise<{ status?: string; success?: boolean; message?: string }> => {
|
||||
const response = await apiClient.post(
|
||||
'/api/auth/forgot-password',
|
||||
data
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset password
|
||||
*/
|
||||
resetPassword: async (
|
||||
data: ResetPasswordData
|
||||
): Promise<{ status?: string; success?: boolean; message?: string }> => {
|
||||
const response = await apiClient.post(
|
||||
'/api/auth/reset-password',
|
||||
data
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
};
|
||||
|
||||
export default authService;
|
||||
54
Frontend/src/services/api/bannerService.ts
Normal file
54
Frontend/src/services/api/bannerService.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import apiClient from './apiClient';
|
||||
|
||||
/**
|
||||
* Banner API Service
|
||||
*/
|
||||
|
||||
export interface Banner {
|
||||
id: number;
|
||||
title: string;
|
||||
image_url: string;
|
||||
link?: string;
|
||||
position: string;
|
||||
display_order: number;
|
||||
is_active: boolean;
|
||||
start_date?: string;
|
||||
end_date?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface BannerListResponse {
|
||||
success: boolean;
|
||||
status?: string;
|
||||
data: {
|
||||
banners: Banner[];
|
||||
};
|
||||
message?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get banners by position
|
||||
*/
|
||||
export const getBannersByPosition = async (
|
||||
position: string = 'home'
|
||||
): Promise<BannerListResponse> => {
|
||||
const response = await apiClient.get('/banners', {
|
||||
params: { position },
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all active banners
|
||||
*/
|
||||
export const getActiveBanners = async ():
|
||||
Promise<BannerListResponse> => {
|
||||
const response = await apiClient.get('/banners');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default {
|
||||
getBannersByPosition,
|
||||
getActiveBanners,
|
||||
};
|
||||
314
Frontend/src/services/api/bookingService.ts
Normal file
314
Frontend/src/services/api/bookingService.ts
Normal file
@@ -0,0 +1,314 @@
|
||||
import apiClient from './apiClient';
|
||||
|
||||
// Types
|
||||
export interface BookingData {
|
||||
room_id: number;
|
||||
check_in_date: string; // YYYY-MM-DD
|
||||
check_out_date: string; // YYYY-MM-DD
|
||||
guest_count: number;
|
||||
notes?: string;
|
||||
payment_method: 'cash' | 'bank_transfer';
|
||||
total_price: number;
|
||||
guest_info: {
|
||||
full_name: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Booking {
|
||||
id: number;
|
||||
booking_number: string;
|
||||
user_id: number;
|
||||
room_id: number;
|
||||
check_in_date: string;
|
||||
check_out_date: string;
|
||||
guest_count: number;
|
||||
total_price: number;
|
||||
status:
|
||||
| 'pending'
|
||||
| 'confirmed'
|
||||
| 'cancelled'
|
||||
| 'checked_in'
|
||||
| 'checked_out';
|
||||
payment_method: 'cash' | 'bank_transfer';
|
||||
payment_status:
|
||||
| 'unpaid'
|
||||
| 'paid'
|
||||
| 'refunded';
|
||||
deposit_paid?: boolean;
|
||||
requires_deposit?: boolean;
|
||||
notes?: string;
|
||||
guest_info?: {
|
||||
full_name: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
};
|
||||
room?: {
|
||||
id: number;
|
||||
room_number: string;
|
||||
floor: number;
|
||||
status: string;
|
||||
room_type: {
|
||||
id: number;
|
||||
name: string;
|
||||
base_price: number;
|
||||
capacity: number;
|
||||
images?: string[];
|
||||
};
|
||||
};
|
||||
user?: {
|
||||
id: number;
|
||||
name: string;
|
||||
full_name: string;
|
||||
email: string;
|
||||
phone?: string;
|
||||
phone_number?: string;
|
||||
};
|
||||
payments?: Payment[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface Payment {
|
||||
id: number;
|
||||
booking_id: number;
|
||||
amount: number;
|
||||
payment_method: string;
|
||||
payment_type: 'full' | 'deposit' | 'remaining';
|
||||
deposit_percentage?: number;
|
||||
payment_status: 'pending' | 'completed' | 'failed' | 'refunded';
|
||||
transaction_id?: string;
|
||||
payment_date?: string;
|
||||
notes?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface BookingResponse {
|
||||
success: boolean;
|
||||
data: {
|
||||
booking: Booking;
|
||||
};
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface BookingsResponse {
|
||||
success: boolean;
|
||||
data: {
|
||||
bookings: Booking[];
|
||||
pagination?: {
|
||||
page: number;
|
||||
limit: number;
|
||||
total: number;
|
||||
totalPages: number;
|
||||
};
|
||||
};
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface CheckBookingResponse {
|
||||
success: boolean;
|
||||
data: {
|
||||
booking: Booking;
|
||||
};
|
||||
message?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new booking
|
||||
* POST /api/bookings
|
||||
*/
|
||||
export const createBooking = async (
|
||||
bookingData: BookingData
|
||||
): Promise<BookingResponse> => {
|
||||
const response = await apiClient.post<BookingResponse>(
|
||||
'/bookings',
|
||||
bookingData
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all bookings of the current user
|
||||
* GET /api/bookings/me
|
||||
*/
|
||||
export const getMyBookings = async ():
|
||||
Promise<BookingsResponse> => {
|
||||
const response = await apiClient.get<BookingsResponse>(
|
||||
'/bookings/me'
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get booking by ID
|
||||
* GET /api/bookings/:id
|
||||
*/
|
||||
export const getBookingById = async (
|
||||
id: number
|
||||
): Promise<BookingResponse> => {
|
||||
const response = await apiClient.get<BookingResponse>(
|
||||
`/bookings/${id}`
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel a booking
|
||||
* PATCH /api/bookings/:id/cancel
|
||||
*/
|
||||
export const cancelBooking = async (
|
||||
id: number
|
||||
): Promise<BookingResponse> => {
|
||||
const response = await apiClient.patch<BookingResponse>(
|
||||
`/bookings/${id}/cancel`
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check booking by booking number
|
||||
* GET /api/bookings/check/:bookingNumber
|
||||
*/
|
||||
export const checkBookingByNumber = async (
|
||||
bookingNumber: string
|
||||
): Promise<CheckBookingResponse> => {
|
||||
const response =
|
||||
await apiClient.get<CheckBookingResponse>(
|
||||
`/bookings/check/${bookingNumber}`
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all bookings (admin)
|
||||
* GET /api/bookings
|
||||
*/
|
||||
export const getAllBookings = async (
|
||||
params?: {
|
||||
status?: string;
|
||||
search?: string;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}
|
||||
): Promise<BookingsResponse> => {
|
||||
const response = await apiClient.get<BookingsResponse>('/bookings', { params });
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update booking status (admin)
|
||||
* PUT /api/bookings/:id
|
||||
*/
|
||||
export const updateBooking = async (
|
||||
id: number,
|
||||
data: Partial<Booking>
|
||||
): Promise<BookingResponse> => {
|
||||
const response = await apiClient.put<BookingResponse>(`/bookings/${id}`, data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check room availability (helper function)
|
||||
* GET /api/rooms/available?roomId=...&from=...&to=...
|
||||
*/
|
||||
export const checkRoomAvailability = async (
|
||||
roomId: number,
|
||||
checkInDate: string,
|
||||
checkOutDate: string
|
||||
): Promise<{ available: boolean; message?: string }> => {
|
||||
try {
|
||||
const response = await apiClient.get(
|
||||
'/rooms/available',
|
||||
{
|
||||
params: {
|
||||
roomId,
|
||||
from: checkInDate,
|
||||
to: checkOutDate,
|
||||
},
|
||||
}
|
||||
);
|
||||
return {
|
||||
available: true,
|
||||
message: response.data.message,
|
||||
};
|
||||
} catch (error: any) {
|
||||
if (error.response?.status === 409) {
|
||||
return {
|
||||
available: false,
|
||||
message:
|
||||
error.response.data.message ||
|
||||
'Room already booked during this time',
|
||||
};
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify payment (upload payment receipt)
|
||||
* POST /api/notify/payment
|
||||
*/
|
||||
export const notifyPayment = async (
|
||||
bookingId: number,
|
||||
file?: File
|
||||
): Promise<{ success: boolean; message?: string }> => {
|
||||
const formData = new FormData();
|
||||
formData.append('bookingId', bookingId.toString());
|
||||
|
||||
if (file) {
|
||||
formData.append('receipt', file);
|
||||
}
|
||||
|
||||
const response = await apiClient.post(
|
||||
'/notify/payment',
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate QR code URL for bank transfer
|
||||
*/
|
||||
export const generateQRCode = (
|
||||
bookingNumber: string,
|
||||
amount: number
|
||||
): string => {
|
||||
// Using VietQR API format
|
||||
// Bank: Vietcombank (VCB)
|
||||
// Account: 0123456789
|
||||
const bankCode = 'VCB';
|
||||
const accountNumber = '0123456789';
|
||||
const accountName = 'KHACH SAN ABC';
|
||||
const transferContent = bookingNumber;
|
||||
|
||||
// VietQR format
|
||||
const qrUrl =
|
||||
`https://img.vietqr.io/image/${bankCode}-` +
|
||||
`${accountNumber}-compact2.jpg?` +
|
||||
`amount=${amount}&` +
|
||||
`addInfo=${encodeURIComponent(transferContent)}&` +
|
||||
`accountName=${encodeURIComponent(accountName)}`;
|
||||
|
||||
return qrUrl;
|
||||
};
|
||||
|
||||
export default {
|
||||
createBooking,
|
||||
getMyBookings,
|
||||
getBookingById,
|
||||
cancelBooking,
|
||||
checkBookingByNumber,
|
||||
checkRoomAvailability,
|
||||
notifyPayment,
|
||||
generateQRCode,
|
||||
getAllBookings,
|
||||
updateBooking,
|
||||
};
|
||||
95
Frontend/src/services/api/favoriteService.ts
Normal file
95
Frontend/src/services/api/favoriteService.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import apiClient from './apiClient';
|
||||
import type { Room } from './roomService';
|
||||
|
||||
/**
|
||||
* Favorite API Service
|
||||
*/
|
||||
|
||||
export interface Favorite {
|
||||
id: number;
|
||||
user_id: number;
|
||||
room_id: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
room?: Room;
|
||||
}
|
||||
|
||||
export interface FavoriteResponse {
|
||||
success?: boolean;
|
||||
status: string;
|
||||
message?: string;
|
||||
data?: {
|
||||
favorites: Favorite[];
|
||||
total: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface FavoriteActionResponse {
|
||||
success?: boolean;
|
||||
status: string;
|
||||
message: string;
|
||||
data?: {
|
||||
favorite: Favorite;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CheckFavoriteResponse {
|
||||
success?: boolean;
|
||||
status: string;
|
||||
data: {
|
||||
isFavorited: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user's favorite rooms
|
||||
*/
|
||||
export const getFavorites = async (): Promise<
|
||||
FavoriteResponse
|
||||
> => {
|
||||
const response = await apiClient.get('/api/favorites');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add room to favorites
|
||||
*/
|
||||
export const addFavorite = async (
|
||||
roomId: number
|
||||
): Promise<FavoriteActionResponse> => {
|
||||
const response = await apiClient.post(
|
||||
`/api/favorites/${roomId}`
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove room from favorites
|
||||
*/
|
||||
export const removeFavorite = async (
|
||||
roomId: number
|
||||
): Promise<FavoriteActionResponse> => {
|
||||
const response = await apiClient.delete(
|
||||
`/api/favorites/${roomId}`
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if room is favorited
|
||||
*/
|
||||
export const checkFavorite = async (
|
||||
roomId: number
|
||||
): Promise<CheckFavoriteResponse> => {
|
||||
const response = await apiClient.get(
|
||||
`/api/favorites/check/${roomId}`
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default {
|
||||
getFavorites,
|
||||
addFavorite,
|
||||
removeFavorite,
|
||||
checkFavorite,
|
||||
};
|
||||
33
Frontend/src/services/api/index.ts
Normal file
33
Frontend/src/services/api/index.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
export { default as apiClient } from './apiClient';
|
||||
export { default as authService } from './authService';
|
||||
export type * from './authService';
|
||||
|
||||
export { default as roomService } from './roomService';
|
||||
export type * from './roomService';
|
||||
|
||||
export { default as bannerService } from './bannerService';
|
||||
export type * from './bannerService';
|
||||
|
||||
export { default as reviewService } from './reviewService';
|
||||
export type * from './reviewService';
|
||||
|
||||
export { default as favoriteService } from './favoriteService';
|
||||
export type * from './favoriteService';
|
||||
|
||||
export { default as bookingService } from './bookingService';
|
||||
export type * from './bookingService';
|
||||
|
||||
export { default as paymentService } from './paymentService';
|
||||
export type * from './paymentService';
|
||||
|
||||
export { default as userService } from './userService';
|
||||
export type * from './userService';
|
||||
|
||||
export { default as serviceService } from './serviceService';
|
||||
export type * from './serviceService';
|
||||
|
||||
export { default as promotionService } from './promotionService';
|
||||
export type * from './promotionService';
|
||||
|
||||
export { default as reportService } from './reportService';
|
||||
export type * from './reportService';
|
||||
188
Frontend/src/services/api/paymentService.ts
Normal file
188
Frontend/src/services/api/paymentService.ts
Normal file
@@ -0,0 +1,188 @@
|
||||
import apiClient from './apiClient';
|
||||
|
||||
// Types
|
||||
export interface PaymentData {
|
||||
booking_id: number;
|
||||
amount: number;
|
||||
payment_method: 'cash' | 'bank_transfer';
|
||||
transaction_id?: string;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface Payment {
|
||||
id: number;
|
||||
booking_id: number;
|
||||
amount: number;
|
||||
payment_method: 'cash' | 'bank_transfer' | 'credit_card' | 'debit_card' | 'e_wallet';
|
||||
payment_type: 'full' | 'deposit' | 'remaining';
|
||||
deposit_percentage?: number;
|
||||
payment_status: 'pending' | 'completed' | 'failed' | 'refunded';
|
||||
transaction_id?: string;
|
||||
payment_date?: string;
|
||||
notes?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface BankInfo {
|
||||
bank_name: string;
|
||||
bank_code: string;
|
||||
account_number: string;
|
||||
account_name: string;
|
||||
amount: number;
|
||||
content: string;
|
||||
qr_url: string;
|
||||
}
|
||||
|
||||
export interface PaymentResponse {
|
||||
success: boolean;
|
||||
data: {
|
||||
payment: Payment;
|
||||
};
|
||||
message?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new payment record
|
||||
* POST /api/payments
|
||||
*/
|
||||
export const createPayment = async (
|
||||
paymentData: PaymentData
|
||||
): Promise<PaymentResponse> => {
|
||||
const response = await apiClient.post<PaymentResponse>(
|
||||
'/payments',
|
||||
paymentData
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get payment details by booking ID
|
||||
* GET /api/payments/:bookingId
|
||||
*/
|
||||
export const getPaymentByBookingId = async (
|
||||
bookingId: number
|
||||
): Promise<PaymentResponse> => {
|
||||
const response = await apiClient.get<PaymentResponse>(
|
||||
`/payments/${bookingId}`
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Confirm bank transfer payment (with receipt)
|
||||
* POST /api/payments/confirm
|
||||
*/
|
||||
export const confirmBankTransfer = async (
|
||||
bookingId: number,
|
||||
transactionId?: string,
|
||||
receipt?: File
|
||||
): Promise<{ success: boolean; message?: string }> => {
|
||||
const formData = new FormData();
|
||||
formData.append('booking_id', bookingId.toString());
|
||||
|
||||
if (transactionId) {
|
||||
formData.append('transaction_id', transactionId);
|
||||
}
|
||||
|
||||
if (receipt) {
|
||||
formData.append('receipt', receipt);
|
||||
}
|
||||
|
||||
const response = await apiClient.post(
|
||||
'/payments/confirm',
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get bank transfer info with QR code for deposit
|
||||
* GET /api/payments/:paymentId/bank-info
|
||||
*/
|
||||
export const getBankTransferInfo = async (
|
||||
paymentId: number
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data: { payment: Payment; bank_info: BankInfo };
|
||||
message?: string;
|
||||
}> => {
|
||||
const response = await apiClient.get(
|
||||
`/payments/${paymentId}/bank-info`
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Confirm deposit payment
|
||||
* POST /api/payments/confirm-deposit
|
||||
*/
|
||||
export const confirmDepositPayment = async (
|
||||
paymentId: number,
|
||||
transactionId?: string
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data: { payment: Payment; booking: any };
|
||||
message?: string;
|
||||
}> => {
|
||||
const response = await apiClient.post(
|
||||
'/payments/confirm-deposit',
|
||||
{
|
||||
payment_id: paymentId,
|
||||
transaction_id: transactionId,
|
||||
}
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify payment completion (for admin verification)
|
||||
* POST /api/payments/notify
|
||||
*/
|
||||
export const notifyPaymentCompletion = async (
|
||||
paymentId: number,
|
||||
notes?: string
|
||||
): Promise<{ success: boolean; message?: string }> => {
|
||||
const response = await apiClient.post(
|
||||
'/payments/notify',
|
||||
{
|
||||
payment_id: paymentId,
|
||||
notes,
|
||||
}
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get payments for a booking
|
||||
* GET /api/payments/booking/:bookingId
|
||||
*/
|
||||
export const getPaymentsByBookingId = async (
|
||||
bookingId: number
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
data: { payments: Payment[] };
|
||||
message?: string;
|
||||
}> => {
|
||||
const response = await apiClient.get(
|
||||
`/payments/booking/${bookingId}`
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
|
||||
export default {
|
||||
createPayment,
|
||||
getPaymentByBookingId,
|
||||
confirmBankTransfer,
|
||||
getBankTransferInfo,
|
||||
confirmDepositPayment,
|
||||
notifyPaymentCompletion,
|
||||
getPaymentsByBookingId,
|
||||
};
|
||||
147
Frontend/src/services/api/promotionService.ts
Normal file
147
Frontend/src/services/api/promotionService.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
import apiClient from './apiClient';
|
||||
|
||||
/**
|
||||
* Promotion API Service
|
||||
*/
|
||||
|
||||
export interface Promotion {
|
||||
id: number;
|
||||
code: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
discount_type: 'percentage' | 'fixed';
|
||||
discount_value: number;
|
||||
min_booking_amount?: number;
|
||||
max_discount_amount?: number;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
usage_limit?: number;
|
||||
used_count?: number;
|
||||
status: 'active' | 'inactive' | 'expired';
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface PromotionListResponse {
|
||||
success: boolean;
|
||||
status?: string;
|
||||
data: {
|
||||
promotions: Promotion[];
|
||||
pagination?: {
|
||||
total: number;
|
||||
page: number;
|
||||
limit: number;
|
||||
totalPages: number;
|
||||
};
|
||||
};
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface CreatePromotionData {
|
||||
code: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
discount_type: 'percentage' | 'fixed';
|
||||
discount_value: number;
|
||||
min_booking_amount?: number;
|
||||
max_discount_amount?: number;
|
||||
start_date: string;
|
||||
end_date: string;
|
||||
usage_limit?: number;
|
||||
status?: 'active' | 'inactive' | 'expired';
|
||||
}
|
||||
|
||||
export interface UpdatePromotionData {
|
||||
code?: string;
|
||||
name?: string;
|
||||
description?: string;
|
||||
discount_type?: 'percentage' | 'fixed';
|
||||
discount_value?: number;
|
||||
min_booking_amount?: number;
|
||||
max_discount_amount?: number;
|
||||
start_date?: string;
|
||||
end_date?: string;
|
||||
usage_limit?: number;
|
||||
status?: 'active' | 'inactive' | 'expired';
|
||||
}
|
||||
|
||||
export interface PromotionSearchParams {
|
||||
status?: string;
|
||||
search?: string;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all promotions
|
||||
*/
|
||||
export const getPromotions = async (
|
||||
params: PromotionSearchParams = {}
|
||||
): Promise<PromotionListResponse> => {
|
||||
const response = await apiClient.get('/promotions', { params });
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get promotion by ID
|
||||
*/
|
||||
export const getPromotionById = async (
|
||||
id: number
|
||||
): Promise<{ success: boolean; data: { promotion: Promotion } }> => {
|
||||
const response = await apiClient.get(`/promotions/${id}`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create new promotion
|
||||
*/
|
||||
export const createPromotion = async (
|
||||
data: CreatePromotionData
|
||||
): Promise<{ success: boolean; data: { promotion: Promotion }; message: string }> => {
|
||||
const response = await apiClient.post('/promotions', data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update promotion
|
||||
*/
|
||||
export const updatePromotion = async (
|
||||
id: number,
|
||||
data: UpdatePromotionData
|
||||
): Promise<{ success: boolean; data: { promotion: Promotion }; message: string }> => {
|
||||
const response = await apiClient.put(`/promotions/${id}`, data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete promotion
|
||||
*/
|
||||
export const deletePromotion = async (
|
||||
id: number
|
||||
): Promise<{ success: boolean; message: string }> => {
|
||||
const response = await apiClient.delete(`/promotions/${id}`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate promotion code
|
||||
*/
|
||||
export const validatePromotion = async (
|
||||
code: string,
|
||||
bookingValue: number
|
||||
): Promise<{ success: boolean; data: { promotion: Promotion; discount: number }; message: string }> => {
|
||||
const response = await apiClient.post('/promotions/validate', {
|
||||
code,
|
||||
booking_value: bookingValue,
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default {
|
||||
getPromotions,
|
||||
getPromotionById,
|
||||
createPromotion,
|
||||
updatePromotion,
|
||||
deletePromotion,
|
||||
validatePromotion,
|
||||
};
|
||||
87
Frontend/src/services/api/reportService.ts
Normal file
87
Frontend/src/services/api/reportService.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import apiClient from './apiClient';
|
||||
|
||||
/**
|
||||
* Report API Service
|
||||
*/
|
||||
|
||||
export interface ReportData {
|
||||
total_bookings: number;
|
||||
total_revenue: number;
|
||||
total_customers: number;
|
||||
available_rooms: number;
|
||||
occupied_rooms: number;
|
||||
revenue_by_date?: Array<{
|
||||
date: string;
|
||||
revenue: number;
|
||||
bookings: number;
|
||||
}>;
|
||||
bookings_by_status?: {
|
||||
pending: number;
|
||||
confirmed: number;
|
||||
checked_in: number;
|
||||
checked_out: number;
|
||||
cancelled: number;
|
||||
};
|
||||
top_rooms?: Array<{
|
||||
room_id: number;
|
||||
room_number: string;
|
||||
bookings: number;
|
||||
revenue: number;
|
||||
}>;
|
||||
service_usage?: Array<{
|
||||
service_id: number;
|
||||
service_name: string;
|
||||
usage_count: number;
|
||||
total_revenue: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface ReportResponse {
|
||||
success: boolean;
|
||||
status?: string;
|
||||
data: ReportData;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface ReportParams {
|
||||
from?: string;
|
||||
to?: string;
|
||||
type?: 'daily' | 'weekly' | 'monthly' | 'yearly';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reports
|
||||
*/
|
||||
export const getReports = async (
|
||||
params: ReportParams = {}
|
||||
): Promise<ReportResponse> => {
|
||||
const response = await apiClient.get('/reports', { params });
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get dashboard statistics
|
||||
*/
|
||||
export const getDashboardStats = async (): Promise<ReportResponse> => {
|
||||
const response = await apiClient.get('/reports/dashboard');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Export report to CSV
|
||||
*/
|
||||
export const exportReport = async (
|
||||
params: ReportParams = {}
|
||||
): Promise<Blob> => {
|
||||
const response = await apiClient.get('/reports/export', {
|
||||
params,
|
||||
responseType: 'blob',
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default {
|
||||
getReports,
|
||||
getDashboardStats,
|
||||
exportReport,
|
||||
};
|
||||
117
Frontend/src/services/api/reviewService.ts
Normal file
117
Frontend/src/services/api/reviewService.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import apiClient from './apiClient';
|
||||
|
||||
/**
|
||||
* Review API Service
|
||||
*/
|
||||
|
||||
export interface Review {
|
||||
id: number;
|
||||
user_id: number;
|
||||
room_id: number;
|
||||
rating: number;
|
||||
comment: string;
|
||||
status: 'pending' | 'approved' | 'rejected';
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
user?: {
|
||||
id: number;
|
||||
name: string;
|
||||
full_name: string;
|
||||
email: string;
|
||||
};
|
||||
room?: {
|
||||
id: number;
|
||||
room_number: string;
|
||||
room_type?: {
|
||||
name: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface ReviewListResponse {
|
||||
success: boolean;
|
||||
status?: string;
|
||||
data: {
|
||||
reviews: Review[];
|
||||
average_rating?: number;
|
||||
total_reviews?: number;
|
||||
pagination?: {
|
||||
total: number;
|
||||
page: number;
|
||||
limit: number;
|
||||
totalPages: number;
|
||||
};
|
||||
};
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface CreateReviewData {
|
||||
room_id: number;
|
||||
rating: number;
|
||||
comment: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reviews for a specific room
|
||||
*/
|
||||
export const getRoomReviews = async (
|
||||
roomId: number
|
||||
): Promise<ReviewListResponse> => {
|
||||
const response = await apiClient.get(
|
||||
`/api/rooms/${roomId}/reviews`
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new review
|
||||
*/
|
||||
export const createReview = async (
|
||||
data: CreateReviewData
|
||||
): Promise<{ success: boolean; message: string; data?: Review }> => {
|
||||
const response = await apiClient.post('/api/reviews', data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all reviews (admin)
|
||||
*/
|
||||
export const getReviews = async (
|
||||
params?: {
|
||||
status?: string;
|
||||
roomId?: number;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}
|
||||
): Promise<ReviewListResponse> => {
|
||||
const response = await apiClient.get('/reviews', { params });
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Approve review (admin)
|
||||
*/
|
||||
export const approveReview = async (
|
||||
id: number
|
||||
): Promise<{ success: boolean; message: string }> => {
|
||||
const response = await apiClient.patch(`/reviews/${id}/approve`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reject review (admin)
|
||||
*/
|
||||
export const rejectReview = async (
|
||||
id: number
|
||||
): Promise<{ success: boolean; message: string }> => {
|
||||
const response = await apiClient.patch(`/reviews/${id}/reject`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default {
|
||||
getRoomReviews,
|
||||
createReview,
|
||||
getReviews,
|
||||
approveReview,
|
||||
rejectReview,
|
||||
};
|
||||
179
Frontend/src/services/api/roomService.ts
Normal file
179
Frontend/src/services/api/roomService.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import apiClient from './apiClient';
|
||||
|
||||
/**
|
||||
* Room API Service
|
||||
*/
|
||||
|
||||
export interface Room {
|
||||
id: number;
|
||||
room_type_id: number;
|
||||
room_number: string;
|
||||
floor: number;
|
||||
status: 'available' | 'occupied' | 'maintenance';
|
||||
featured: boolean;
|
||||
images?: string[];
|
||||
amenities?: string[];
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
room_type?: {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
base_price: number;
|
||||
capacity: number;
|
||||
amenities: string[];
|
||||
images: string[];
|
||||
};
|
||||
average_rating?: number | string | null;
|
||||
total_reviews?: number | string | null;
|
||||
}
|
||||
|
||||
export interface RoomListResponse {
|
||||
success: boolean;
|
||||
status?: string;
|
||||
data: {
|
||||
rooms: Room[];
|
||||
pagination?: {
|
||||
total: number;
|
||||
page: number;
|
||||
limit: number;
|
||||
totalPages: number;
|
||||
};
|
||||
};
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface FeaturedRoomsParams {
|
||||
featured?: boolean;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export interface RoomSearchParams {
|
||||
type?: string;
|
||||
minPrice?: number;
|
||||
maxPrice?: number;
|
||||
capacity?: number;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
sort?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get featured rooms for homepage
|
||||
*/
|
||||
export const getFeaturedRooms = async (
|
||||
params: FeaturedRoomsParams = {}
|
||||
): Promise<RoomListResponse> => {
|
||||
const response = await apiClient.get('/rooms', {
|
||||
params: {
|
||||
featured: params.featured ?? true,
|
||||
limit: params.limit ?? 6,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all rooms with filters
|
||||
*/
|
||||
export const getRooms = async (
|
||||
params: RoomSearchParams = {}
|
||||
): Promise<RoomListResponse> => {
|
||||
const response = await apiClient.get('/rooms', {
|
||||
params,
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get room by ID
|
||||
*/
|
||||
export const getRoomById = async (
|
||||
id: number
|
||||
): Promise<{ success: boolean; data: { room: Room } }> => {
|
||||
const response = await apiClient.get(`/rooms/${id}`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Search available rooms
|
||||
*/
|
||||
export interface AvailableSearchParams {
|
||||
from: string;
|
||||
to: string;
|
||||
type?: string;
|
||||
capacity?: number;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export const searchAvailableRooms = async (
|
||||
params: AvailableSearchParams
|
||||
): Promise<RoomListResponse> => {
|
||||
const response = await apiClient.get('/rooms/available', {
|
||||
params,
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get available amenities list (unique)
|
||||
*/
|
||||
export const getAmenities = async (): Promise<{
|
||||
success?: boolean;
|
||||
status?: string;
|
||||
data: { amenities: string[] };
|
||||
}> => {
|
||||
const response = await apiClient.get('/rooms/amenities');
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create new room
|
||||
*/
|
||||
export interface CreateRoomData {
|
||||
room_number: string;
|
||||
floor: number;
|
||||
room_type_id: number;
|
||||
status: 'available' | 'occupied' | 'maintenance';
|
||||
featured?: boolean;
|
||||
}
|
||||
|
||||
export const createRoom = async (
|
||||
data: CreateRoomData
|
||||
): Promise<{ success: boolean; data: { room: Room }; message: string }> => {
|
||||
const response = await apiClient.post('/rooms', data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update room
|
||||
*/
|
||||
export const updateRoom = async (
|
||||
id: number,
|
||||
data: Partial<CreateRoomData>
|
||||
): Promise<{ success: boolean; data: { room: Room }; message: string }> => {
|
||||
const response = await apiClient.put(`/rooms/${id}`, data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete room
|
||||
*/
|
||||
export const deleteRoom = async (
|
||||
id: number
|
||||
): Promise<{ success: boolean; message: string }> => {
|
||||
const response = await apiClient.delete(`/rooms/${id}`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default {
|
||||
getFeaturedRooms,
|
||||
getRooms,
|
||||
getRoomById,
|
||||
searchAvailableRooms,
|
||||
getAmenities,
|
||||
createRoom,
|
||||
updateRoom,
|
||||
deleteRoom,
|
||||
};
|
||||
126
Frontend/src/services/api/serviceService.ts
Normal file
126
Frontend/src/services/api/serviceService.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import apiClient from './apiClient';
|
||||
|
||||
/**
|
||||
* Service API Service
|
||||
*/
|
||||
|
||||
export interface Service {
|
||||
id: number;
|
||||
name: string;
|
||||
description?: string;
|
||||
price: number;
|
||||
unit?: string;
|
||||
status: 'active' | 'inactive';
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface ServiceListResponse {
|
||||
success: boolean;
|
||||
status?: string;
|
||||
data: {
|
||||
services: Service[];
|
||||
pagination?: {
|
||||
total: number;
|
||||
page: number;
|
||||
limit: number;
|
||||
totalPages: number;
|
||||
};
|
||||
};
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface CreateServiceData {
|
||||
name: string;
|
||||
description?: string;
|
||||
price: number;
|
||||
unit?: string;
|
||||
status?: 'active' | 'inactive';
|
||||
}
|
||||
|
||||
export interface UpdateServiceData {
|
||||
name?: string;
|
||||
description?: string;
|
||||
price?: number;
|
||||
unit?: string;
|
||||
status?: 'active' | 'inactive';
|
||||
}
|
||||
|
||||
export interface ServiceSearchParams {
|
||||
status?: string;
|
||||
search?: string;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all services
|
||||
*/
|
||||
export const getServices = async (
|
||||
params: ServiceSearchParams = {}
|
||||
): Promise<ServiceListResponse> => {
|
||||
const response = await apiClient.get('/services', { params });
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get service by ID
|
||||
*/
|
||||
export const getServiceById = async (
|
||||
id: number
|
||||
): Promise<{ success: boolean; data: { service: Service } }> => {
|
||||
const response = await apiClient.get(`/services/${id}`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create new service
|
||||
*/
|
||||
export const createService = async (
|
||||
data: CreateServiceData
|
||||
): Promise<{ success: boolean; data: { service: Service }; message: string }> => {
|
||||
const response = await apiClient.post('/services', data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update service
|
||||
*/
|
||||
export const updateService = async (
|
||||
id: number,
|
||||
data: UpdateServiceData
|
||||
): Promise<{ success: boolean; data: { service: Service }; message: string }> => {
|
||||
const response = await apiClient.put(`/services/${id}`, data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete service
|
||||
*/
|
||||
export const deleteService = async (
|
||||
id: number
|
||||
): Promise<{ success: boolean; message: string }> => {
|
||||
const response = await apiClient.delete(`/services/${id}`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Use service
|
||||
*/
|
||||
export const useService = async (data: {
|
||||
booking_id: number;
|
||||
service_id: number;
|
||||
quantity: number;
|
||||
}): Promise<{ success: boolean; message: string }> => {
|
||||
const response = await apiClient.post('/services/use', data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default {
|
||||
getServices,
|
||||
getServiceById,
|
||||
createService,
|
||||
updateService,
|
||||
deleteService,
|
||||
useService,
|
||||
};
|
||||
117
Frontend/src/services/api/userService.ts
Normal file
117
Frontend/src/services/api/userService.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import apiClient from './apiClient';
|
||||
|
||||
/**
|
||||
* User API Service
|
||||
*/
|
||||
|
||||
export interface User {
|
||||
id: number;
|
||||
full_name: string;
|
||||
email: string;
|
||||
phone_number?: string;
|
||||
avatar?: string;
|
||||
role: string;
|
||||
status?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface UserListResponse {
|
||||
success: boolean;
|
||||
status?: string;
|
||||
data: {
|
||||
users: User[];
|
||||
pagination?: {
|
||||
total: number;
|
||||
page: number;
|
||||
limit: number;
|
||||
totalPages: number;
|
||||
};
|
||||
};
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface CreateUserData {
|
||||
full_name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
phone_number?: string;
|
||||
role: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface UpdateUserData {
|
||||
full_name?: string;
|
||||
email?: string;
|
||||
phone_number?: string;
|
||||
role?: string;
|
||||
password?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface UserSearchParams {
|
||||
role?: string;
|
||||
status?: string;
|
||||
search?: string;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all users
|
||||
*/
|
||||
export const getUsers = async (
|
||||
params: UserSearchParams = {}
|
||||
): Promise<UserListResponse> => {
|
||||
const response = await apiClient.get('/users', { params });
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get user by ID
|
||||
*/
|
||||
export const getUserById = async (
|
||||
id: number
|
||||
): Promise<{ success: boolean; data: { user: User } }> => {
|
||||
const response = await apiClient.get(`/users/${id}`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create new user
|
||||
*/
|
||||
export const createUser = async (
|
||||
data: CreateUserData
|
||||
): Promise<{ success: boolean; data: { user: User }; message: string }> => {
|
||||
const response = await apiClient.post('/users', data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update user
|
||||
*/
|
||||
export const updateUser = async (
|
||||
id: number,
|
||||
data: UpdateUserData
|
||||
): Promise<{ success: boolean; data: { user: User }; message: string }> => {
|
||||
const response = await apiClient.put(`/users/${id}`, data);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete user
|
||||
*/
|
||||
export const deleteUser = async (
|
||||
id: number
|
||||
): Promise<{ success: boolean; message: string }> => {
|
||||
const response = await apiClient.delete(`/users/${id}`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export default {
|
||||
getUsers,
|
||||
getUserById,
|
||||
createUser,
|
||||
updateUser,
|
||||
deleteUser,
|
||||
};
|
||||
Reference in New Issue
Block a user