update to python fastpi

This commit is contained in:
Iliyan Angelov
2025-11-16 15:59:05 +02:00
parent 93d4c1df80
commit 98ccd5b6ff
4464 changed files with 773233 additions and 13740 deletions

View 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;

View 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;

View 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,
};

View 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,
};

View 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,
};

View 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';

View 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,
};

View 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,
};

View 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,
};

View 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,
};

View 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,
};

View 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,
};

View 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,
};