// import AuthService from '../services/AuthService'; // const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'https://45.93.137.91.nip.io/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/GetAllReservations'); // } // 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() { // console.log('[API] Fetching my rent listings'); // return apiFetch(`/RentProperties/GetMyRentListings`); // } // 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' }); // } // export async function getUserNotifications() { // return apiFetch('/Notifications/GetUserNotifications'); // } // // ─── Booking/Reservation Management ─── // export async function confirmDepositPayment(bookingId) { // return apiFetch('/Reservations/ConfirmDepositPayment', { // method: 'POST', // body: JSON.stringify({ bookingId }), // }); // } // export async function adminConfirmDeposit(reservationId, adminId, comment = null) { // const token = AuthService.getToken(); // const endpoint = `${API_BASE}/Reservations/AdminConfirmDeposit/admin-confirm-deposit`; // const normalizedComment = // typeof comment === 'string' && comment.trim() // ? comment.trim() // : null; // const payload = { // reservationId, // adminId, // comment: normalizedComment, // }; // console.log('[API] AdminConfirmDeposit request', { // method: 'PUT', // endpoint, // payload, // adminIdSource: 'jwt-user-id', // hasToken: Boolean(token), // tokenPreview: token ? `${token.slice(0, 18)}...${token.slice(-8)}` : null, // }); // const res = await fetch(endpoint, { // method: 'PUT', // headers: { // 'Content-Type': 'application/json', // ...(token && { Authorization: `Bearer ${token}` }), // }, // body: JSON.stringify(payload), // }); // const text = await res.text(); // let data = null; // console.log('[API] AdminConfirmDeposit raw response', { // status: res.status, // ok: res.ok, // endpoint, // rawText: text, // }); // try { // data = text ? JSON.parse(text) : null; // if (data && typeof data === 'object' && 'data' in data) { // data = data.data; // } // } catch { // data = text; // } // const message = typeof data === 'object' && data?.message ? data.message : null; // console.log('[API] AdminConfirmDeposit parsed response', { // status: res.status, // ok: res.ok, // message, // data, // }); // return { status: res.status, data, ok: res.ok, message }; // } // export async function updateBookingStatus(bookingId, status) { // return apiFetch('/Reservations/UpdateStatus', { // method: 'PUT', // body: JSON.stringify({ bookingId, status }), // }); // } import AuthService from '../services/AuthService'; const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'https://45.93.137.91.nip.io/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; } 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, fromDate = null, toDate = null) { console.log('[API] Fetching available dates for property:', { propertyId, fromDate, toDate, }); const qs = new URLSearchParams(); if (fromDate) qs.set('fromDate', fromDate); if (toDate) qs.set('toDate', toDate); const query = qs.toString(); return apiFetch( `/Reservations/GetAvailableDates/available/${propertyId}${query ? `?${query}` : ''}` ); } export async function getReservations() { return apiFetch('/Reservations/GetAllReservations'); } 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(propertyInfoId, startDate, endDate) { const payload = { propertyInfoId, startDate, endDate, }; console.log('[API] Booking reservation FINAL:', payload); return apiFetch('/Reservations/BookReservation/book', { method: 'POST', body: JSON.stringify(payload), }); } // ─── 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() { console.log('[API] Fetching my rent listings'); return apiFetch(`/RentProperties/GetMyRentListings`); } 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}`); try { const json = JSON.parse(text); return json?.data || json; } catch { return text; } } // ─── Auth: Registration ─── async function multipartAuthFetch(endpoint, formData) { console.log('[Auth] Multipart request:', `${API_BASE}${endpoint}`); const res = await fetch(`${API_BASE}${endpoint}`, { method: 'POST', 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' }); } export async function getUserNotifications() { return apiFetch('/Notifications/GetUserNotifications'); } // ─── Booking/Reservation Management ─── export async function confirmDepositPayment(bookingId) { return apiFetch('/Reservations/ConfirmDepositPayment', { method: 'POST', body: JSON.stringify({ bookingId }), }); } export async function adminConfirmDeposit(reservationId, adminId, comment = null) { const token = AuthService.getToken(); const endpoint = `${API_BASE}/Reservations/AdminConfirmDeposit/admin-confirm-deposit`; const normalizedComment = typeof comment === 'string' && comment.trim() ? comment.trim() : null; const payload = { reservationId, adminId, comment: normalizedComment, }; console.log('[API] AdminConfirmDeposit request', { method: 'PUT', endpoint, payload, adminIdSource: 'jwt-user-id', hasToken: Boolean(token), tokenPreview: token ? `${token.slice(0, 18)}...${token.slice(-8)}` : null, }); const res = await fetch(endpoint, { method: 'PUT', headers: { 'Content-Type': 'application/json', ...(token && { Authorization: `Bearer ${token}` }), }, body: JSON.stringify(payload), }); const text = await res.text(); let data = null; console.log('[API] AdminConfirmDeposit raw response', { status: res.status, ok: res.ok, endpoint, rawText: text, }); try { data = text ? JSON.parse(text) : null; if (data && typeof data === 'object' && 'data' in data) { data = data.data; } } catch { data = text; } const message = typeof data === 'object' && data?.message ? data.message : null; console.log('[API] AdminConfirmDeposit parsed response', { status: res.status, ok: res.ok, message, data, }); return { status: res.status, data, ok: res.ok, message }; } export async function updateBookingStatus(bookingId, status) { return apiFetch('/Reservations/UpdateStatus', { method: 'PUT', body: JSON.stringify({ bookingId, status }), }); }