import AuthService from '../services/AuthService'; const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://45.93.137.91/api'; /** * Generic API fetch — attaches auth token, unwraps { data } envelope */ async function apiFetch(endpoint, options = {}) { const token = AuthService.getToken(); const headers = { 'Content-Type': 'application/json', ...(token && { Authorization: `Bearer ${token}` }), ...options.headers, }; console.log('[API] Request:', options.method || 'GET', `${API_BASE}${endpoint}`); const res = await fetch(`${API_BASE}${endpoint}`, { ...options, headers, }); console.log('[API] Response:', res.status, endpoint); if (!res.ok && res.status !== 206) { const text = await res.text().catch(() => ''); console.error('[API] Error:', res.status, text); throw new Error(`API ${res.status}: ${text || res.statusText}`); } const text = await res.text(); if (!text) return null; try { const json = JSON.parse(text); if (json && typeof json === 'object' && 'data' in json) { return json.data; } return json; } catch { return text; } } /** * Auth fetch — returns full { status, data, ok } for status-code handling */ async function authFetch(endpoint, body, token = null) { console.log('[Auth] Request:', `${API_BASE}${endpoint}`); const headers = { 'Content-Type': 'application/json' }; if (token) { headers['Authorization'] = `Bearer ${token}`; console.log('[Auth] Sending with Bearer token'); } const res = await fetch(`${API_BASE}${endpoint}`, { method: 'POST', headers, body: JSON.stringify(body), }); console.log('[Auth] Response status:', res.status, endpoint); const text = await res.text(); let data = null; try { data = text ? JSON.parse(text) : null; if (data && typeof data === 'object' && 'data' in data) { data = data.data; } } catch { data = text; } // Build message from response for toast display const message = (typeof data === 'object' && data?.message) ? data.message : null; return { status: res.status, data, ok: res.ok || res.status === 206, message }; } // ─── Rent Properties ─── export async function getRentProperties() { return apiFetch('/RentProperties/GetRentProperties'); } export async function getRentProperty(id) { return apiFetch(`/RentProperties/GetRentPropertyById/${id}`); } export async function getRentPropertyLocations(params = {}) { const qs = new URLSearchParams(); if (params.maxOffset != null) qs.set('maxOffset', params.maxOffset); if (params.minOffset != null) qs.set('minOffset', params.minOffset); const query = qs.toString(); return apiFetch(`/RentProperties/GetRentPropertiesLocations${query ? `?${query}` : ''}`); } // ─── Sale Properties ─── export async function getSaleProperties() { return apiFetch('/SaleProperties/GetSaleProperties'); } export async function getSaleProperty(id) { const items = await apiFetch('/SaleProperties/GetSaleProperties'); if (!Array.isArray(items)) return items; return items.find(p => p.id == id) || items[0]; } // ─── Properties (generic) ─── export async function getProperty(id) { return apiFetch(`/Properties/Get/${id}`); } // ─── Recommendations ─── export async function getRecommendations() { return apiFetch('/Recommendations/GetRecommendations'); } export async function getTopRecommendations(count = 10) { return apiFetch(`/Recommendations/GetTopRecommendations?count=${count}`); } // ─── Reservations ─── export async function getAvailableDateRanges(propertyId) { console.log('[API] Fetching available dates for property:', propertyId); return apiFetch(`/Reservations/GetAvailableDates/available/${propertyId}`); } export async function getReservations() { return apiFetch('/Reservations/GetReservations'); } export async function getReservation(id) { return apiFetch(`/Reservations/GetReservation?id=${id}`); } export async function checkAvailability(propertyId, fromDate = null, toDate = null) { const qs = new URLSearchParams(); if (fromDate) qs.set('fromDate', fromDate); if (toDate) qs.set('toDate', toDate); const query = qs.toString(); return apiFetch(`/Reservations/GetAvailable/${propertyId}${query ? `?${query}` : ''}`); } export async function bookReservation(propertyId, startDate, endDate) { console.log('[API] Booking reservation:', { propertyId, startDate, endDate }); return apiFetch('/Reservations/BookReservation/book', { method: 'POST', body: JSON.stringify({ propertyId, startDate, endDate }), }); } // ─── Terms ─── export async function getTerms() { return apiFetch('/Terms/GetTerms'); } // ─── Profile ─── export async function getCustomerByUserId(userId) { console.log('[API] Fetching customer by user ID:', userId); return apiFetch(`/Customer/GetByUserId/${userId}`); } export async function getOwnerByUserId(userId) { console.log('[API] Fetching owner by user ID:', userId); return apiFetch(`/Owner/GetByUserId/${userId}`); } // ─── Properties ─── export async function getMyRentListings(userId) { console.log('[API] Fetching my rent listings for user:', userId); return apiFetch(`/RentProperties/GetMyRentListings/${userId}`); } export async function addRentProperty(data) { console.log('[API] Adding rent property:', data.PropertyInformation?.Address); return apiFetch('/RentProperties/AddRentProperty', { method: 'POST', body: JSON.stringify(data), }); } // ─── Currencies ─── export async function getCurrencies() { return apiFetch('/Currency/GetAll'); } // ─── Files ─── export async function uploadPicture(file) { console.log('[API] Uploading picture:', file.name); const formData = new FormData(); formData.append('image', file); const token = AuthService.getToken(); const res = await fetch(`${API_BASE}/Files/UploadPicture`, { method: 'POST', headers: { ...(token && { Authorization: `Bearer ${token}` }), }, body: formData, }); const text = await res.text(); console.log('[API] Upload response:', res.status, text?.substring(0, 100)); if (!res.ok) throw new Error(`Upload failed: ${res.status} ${text}`); // Response is the relative path string (e.g. /Pictures/abc123.jpg) try { const json = JSON.parse(text); return json?.data || json; } catch { return text; } } // ─── Auth: Registration ─── /** * Register a new owner * @param {Object} data — { name, email, phoneNumber, whatsAppNumber, password, ownerType } * @returns {Promise<{status, data, ok, message}>} */ // Multipart form-data fetch for file uploads async function multipartAuthFetch(endpoint, formData) { console.log('[Auth] Multipart request:', `${API_BASE}${endpoint}`); const res = await fetch(`${API_BASE}${endpoint}`, { method: 'POST', // Don't set Content-Type — browser sets it with boundary body: formData, }); console.log('[Auth] Response status:', res.status, endpoint); const text = await res.text(); let data = null; try { data = text ? JSON.parse(text) : null; if (data && typeof data === 'object' && 'data' in data) { data = data.data; } } catch { data = text; } return { status: res.status, data, ok: res.ok || res.status === 206, message: data?.message }; } export async function addOwner(data, frontImage = null, backImage = null) { console.log('[Auth] Registering owner (multipart):', data.email); const formData = new FormData(); formData.append('FirstName', data.firstName || data.FirstName || ''); formData.append('LastName', data.lastName || data.LastName || ''); formData.append('Email', data.email || ''); formData.append('PhoneNumber', data.phoneNumber || ''); formData.append('WhatsAppNumber', data.whatsAppNumber || ''); formData.append('Phone', data.phone || ''); formData.append('NationalNumber', data.nationalNumber || ''); formData.append('Password', data.password || ''); formData.append('Type', String(data.ownerType ?? data.Type ?? 0)); formData.append('Language', '0'); if (frontImage) formData.append('FrontIdCarImagePath', frontImage); if (backImage) formData.append('RearIdCarImagePath', backImage); return multipartAuthFetch('/Owner/Add', formData); } export async function addCustomer(data, frontImage = null, backImage = null) { console.log('[Auth] Registering customer (multipart):', data.email); const formData = new FormData(); formData.append('FirstName', data.firstName || data.FirstName || ''); formData.append('LastName', data.lastName || data.LastName || ''); formData.append('Email', data.email || ''); formData.append('PhoneNumber', data.phoneNumber || ''); formData.append('WhatsAppNumber', data.whatsAppNumber || ''); formData.append('Phone', data.phone || ''); formData.append('NationalNumber', data.nationalNumber || ''); formData.append('Password', data.password || ''); formData.append('Type', String(data.customerType ?? data.Type ?? 0)); formData.append('Language', '0'); if (frontImage) formData.append('FrontIdCarImagePath', frontImage); if (backImage) formData.append('RearIdCarImagePath', backImage); return multipartAuthFetch('/Customer/Add', formData); } // ─── Auth: Login ─── export async function loginWithEmail(credential, password) { console.log('[Auth] Login with email:', credential); return authFetch('/Auth/LogInWithEmail', { credential, password, device: 0, appVersion: '', }); } export async function loginWithPhone(credential, password) { console.log('[Auth] Login with phone:', credential); return authFetch('/Auth/LogInWithPhoneNumber', { credential, password, device: 0, appVersion: '', }); } // ─── Auth: OTP ─── export async function sendEmailOTP() { console.log('[Auth] Sending email OTP...'); return apiFetch('/Auth/SendEmailOTP', { method: 'POST' }); } export async function sendPhoneOTP() { console.log('[Auth] Sending phone OTP...'); return apiFetch('/Auth/SendPhoneNumberOTP', { method: 'POST' }); } export async function verifyEmail(code) { console.log('[Auth] Verifying email with code:', code); const token = AuthService.getToken(); return authFetch(`/Auth/VerifyEmail?code=${encodeURIComponent(code)}`, {}, token); } export async function verifyPhone(code) { console.log('[Auth] Verifying phone with code:', code); const token = AuthService.getToken(); return authFetch(`/Auth/VerifyPhoneNumber?code=${encodeURIComponent(code)}`, {}, token); } // ─── Helpers ─── export function isEmail(value) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value); } export function isPhoneNumber(value) { return /^\+?\d{7,15}$/.test(value.replace(/[\s\-()]/g, '')); } // ─── Favorites ─── export async function getUserFavoriteProperties() { return apiFetch('/FavoriteProperty/GetUserFavoriteProperties'); } export async function addFavoriteProperty(propId) { return apiFetch(`/FavoriteProperty/Add?propId=${propId}`, { method: 'POST' }); } export async function removeFavoriteProperty(favePropId) { return apiFetch(`/FavoriteProperty/Remove?favePropId=${favePropId}`, { method: 'DELETE' }); }