2026-03-30 19:26:03 +03:00
|
|
|
'use client';
|
|
|
|
|
|
2026-03-30 17:54:42 +00:00
|
|
|
import { createContext, useContext, useState, useEffect, useCallback } from 'react';
|
|
|
|
|
import { getUserFavoriteProperties, addFavoriteProperty, removeFavoriteProperty } from '../utils/api';
|
|
|
|
|
import AuthService from '../services/AuthService';
|
2026-03-30 19:26:03 +03:00
|
|
|
|
|
|
|
|
const FavoritesContext = createContext();
|
|
|
|
|
|
|
|
|
|
export const useFavorites = () => {
|
|
|
|
|
const context = useContext(FavoritesContext);
|
|
|
|
|
if (!context) {
|
|
|
|
|
throw new Error('useFavorites must be used within FavoritesProvider');
|
|
|
|
|
}
|
|
|
|
|
return context;
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-30 17:54:42 +00:00
|
|
|
function mapApiFavorite(item) {
|
|
|
|
|
const info = item.propertyInformation || {};
|
|
|
|
|
let details = {};
|
|
|
|
|
try { details = JSON.parse(info.detailsJSON || '{}'); } catch {}
|
|
|
|
|
|
|
|
|
|
const price = item.monthlyRent || item.dailyRent || 0;
|
|
|
|
|
const priceUnit = item.monthlyRent ? 'monthly' : 'daily';
|
|
|
|
|
const buildingType = info.buildingType ?? 0;
|
|
|
|
|
const type = { 0: 'apartment', 1: 'villa', 2: 'house' }[buildingType] || 'apartment';
|
|
|
|
|
const typeLabel = { 0: 'شقة', 1: 'فيلا', 2: 'بيت' }[buildingType] || 'عقار';
|
|
|
|
|
const address = info.address || '';
|
|
|
|
|
const addressParts = address.split(',').map(s => s.trim()).filter(Boolean);
|
|
|
|
|
const images = info.images || [];
|
|
|
|
|
const resolvedImages = images.map(img => {
|
|
|
|
|
if (!img) return '';
|
|
|
|
|
if (img.startsWith('http')) return img;
|
|
|
|
|
return `http://45.93.137.91${img.startsWith('/') ? '' : '/'}${img}`;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
id: info.id || item.propertyInformationId,
|
|
|
|
|
faveId: item.id, // needed to remove from favorites
|
|
|
|
|
title: `${typeLabel} في ${addressParts[0] || address}`,
|
|
|
|
|
type,
|
|
|
|
|
typeLabel,
|
|
|
|
|
price,
|
|
|
|
|
priceUnit,
|
|
|
|
|
bedrooms: info.numberOfBedRooms || 0,
|
|
|
|
|
bathrooms: info.numberOfBathRooms || 0,
|
|
|
|
|
area: info.space || 0,
|
|
|
|
|
location: {
|
|
|
|
|
city: addressParts[addressParts.length - 1] || '',
|
|
|
|
|
district: addressParts[0] || '',
|
|
|
|
|
},
|
|
|
|
|
images: resolvedImages,
|
|
|
|
|
rating: item.rating || 0,
|
|
|
|
|
deposit: item.deposit || 0,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-30 19:26:03 +03:00
|
|
|
export const FavoritesProvider = ({ children }) => {
|
|
|
|
|
const [favorites, setFavorites] = useState([]);
|
2026-03-30 17:54:42 +00:00
|
|
|
const [isLoading, setIsLoading] = useState(false);
|
2026-03-30 19:26:03 +03:00
|
|
|
|
2026-03-30 17:54:42 +00:00
|
|
|
const fetchFavorites = useCallback(async () => {
|
|
|
|
|
if (!AuthService.isAuthenticated()) {
|
|
|
|
|
setFavorites([]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
try {
|
|
|
|
|
const data = await getUserFavoriteProperties();
|
|
|
|
|
const items = Array.isArray(data) ? data : [];
|
|
|
|
|
setFavorites(items.map(mapApiFavorite));
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('[Favorites] Failed to fetch:', err);
|
|
|
|
|
setFavorites([]);
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
2026-03-30 19:26:03 +03:00
|
|
|
}
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
2026-03-30 17:54:42 +00:00
|
|
|
fetchFavorites();
|
|
|
|
|
}, [fetchFavorites]);
|
|
|
|
|
|
|
|
|
|
const addFavorite = async (propId) => {
|
|
|
|
|
if (!AuthService.isAuthenticated()) return false;
|
|
|
|
|
try {
|
|
|
|
|
await addFavoriteProperty(propId);
|
2026-03-30 18:18:09 +00:00
|
|
|
// Refresh to get the full object with faveId
|
|
|
|
|
await fetchFavorites();
|
2026-03-30 17:54:42 +00:00
|
|
|
return true;
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('[Favorites] Add failed:', err);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2026-03-30 19:26:03 +03:00
|
|
|
};
|
|
|
|
|
|
2026-03-30 17:54:42 +00:00
|
|
|
const removeFavorite = async (propId) => {
|
|
|
|
|
if (!AuthService.isAuthenticated()) return false;
|
|
|
|
|
const fav = favorites.find(f => f.id === propId);
|
|
|
|
|
if (!fav) return false;
|
2026-03-30 18:18:09 +00:00
|
|
|
// Optimistic update — remove immediately from UI
|
|
|
|
|
const previous = [...favorites];
|
|
|
|
|
setFavorites(prev => prev.filter(f => f.id !== propId));
|
2026-03-30 17:54:42 +00:00
|
|
|
try {
|
|
|
|
|
await removeFavoriteProperty(fav.faveId);
|
|
|
|
|
return true;
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('[Favorites] Remove failed:', err);
|
2026-03-30 18:18:09 +00:00
|
|
|
// Rollback on failure
|
|
|
|
|
setFavorites(previous);
|
2026-03-30 17:54:42 +00:00
|
|
|
return false;
|
|
|
|
|
}
|
2026-03-30 19:26:03 +03:00
|
|
|
};
|
|
|
|
|
|
2026-03-30 17:54:42 +00:00
|
|
|
const isFavorite = (propId) => {
|
|
|
|
|
return favorites.some(f => f.id === propId);
|
2026-03-30 19:26:03 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
2026-03-30 17:54:42 +00:00
|
|
|
<FavoritesContext.Provider value={{ favorites, isLoading, addFavorite, removeFavorite, isFavorite, refreshFavorites: fetchFavorites }}>
|
2026-03-30 19:26:03 +03:00
|
|
|
{children}
|
|
|
|
|
</FavoritesContext.Provider>
|
|
|
|
|
);
|
2026-03-30 17:54:42 +00:00
|
|
|
};
|