2026-03-30 13:44:52 +00:00
'use client' ;
2026-05-25 21:53:57 +03:00
import { useState , useEffect , useMemo } from 'react' ;
import { motion , AnimatePresence } from 'framer-motion' ;
2026-03-30 13:44:52 +00:00
import toast , { Toaster } from 'react-hot-toast' ;
import Link from 'next/link' ;
2026-05-25 21:27:39 +03:00
import { useParams , useRouter } from 'next/navigation' ;
2026-03-30 13:44:52 +00:00
import {
2026-05-25 21:27:39 +03:00
MapPin , Bed , Bath , Square , DollarSign , Heart , Share2 ,
Phone , Mail , MessageCircle , Calendar , Shield , Star ,
ChevronLeft , ChevronRight , Check , X , Wifi , Car , Wind ,
Camera , Home , Building2 , Users , Clock , FileText ,
2026-05-25 21:53:57 +03:00
LogIn , Loader2 , ArrowLeft , ImageIcon , ChevronDown ,
Layers , Sofa , DoorOpen , School , Hospital , Store ,
TreePine , Building , GraduationCap , ExternalLink ,
Smile , Ban , Wine , Dog , CassetteTape , Info
2026-03-30 13:44:52 +00:00
} from 'lucide-react' ;
2026-05-26 19:04:57 +03:00
import { getRentProperty , getSaleProperty , getSalePropertyById , bookReservation , getAvailableDateRanges , getOwnerContactInformation , getMyRentListings , getMySaleListings , getOwnerByUserId } from '../../utils/api' ;
2026-03-30 13:44:52 +00:00
import AuthService from '../../services/AuthService' ;
2026-03-30 17:54:42 +00:00
import { useFavorites } from '@/app/contexts/FavoritesContext' ;
2026-03-30 13:44:52 +00:00
import { BuildingTypeKeys , PropertyStatusKeys , extractCity } from '../../enums' ;
2026-04-26 13:46:30 +03:00
import PropertyRatingList from '@/app/components/ratings/PropertyRatingList' ;
import PropertyRatingForm from '@/app/components/ratings/PropertyRatingForm' ;
2026-05-25 21:27:39 +03:00
import StarRating from '@/app/components/ratings/StarRating' ;
import { getPropertyAverageRating } from '../../utils/ratings' ;
import dynamic from 'next/dynamic' ;
import 'leaflet/dist/leaflet.css' ;
const MapContainer = dynamic ( ( ) => import ( 'react-leaflet' ) . then ( m => m . MapContainer ) , { ssr : false } ) ;
const TileLayer = dynamic ( ( ) => import ( 'react-leaflet' ) . then ( m => m . TileLayer ) , { ssr : false } ) ;
const Marker = dynamic ( ( ) => import ( 'react-leaflet' ) . then ( m => m . Marker ) , { ssr : false } ) ;
const Popup = dynamic ( ( ) => import ( 'react-leaflet' ) . then ( m => m . Popup ) , { ssr : false } ) ;
2026-04-26 13:46:30 +03:00
function formatCurrency ( amount ) {
2026-05-25 21:27:39 +03:00
if ( ! amount || isNaN ( amount ) ) return '0' ;
return Number ( amount ) . toLocaleString ( 'ar-SA' ) ;
2026-03-30 16:28:09 +00:00
}
2026-03-30 13:44:52 +00:00
2026-05-25 21:27:39 +03:00
function buildImageUrl ( img ) {
if ( ! img ) return '' ;
const apiBase = typeof window !== 'undefined'
? ( process . env . NEXT _PUBLIC _API _URL || 'https://45.93.137.91.nip.io/api' )
: '' ;
if ( img . startsWith ( 'http' ) ) return img ;
return ` ${ apiBase } ${ img . startsWith ( '/' ) ? '' : '/Pictures/' } ${ img } ` ;
2026-03-30 16:28:09 +00:00
}
2026-05-25 21:53:57 +03:00
const serviceLabels = {
Electricity : 'كهرباء' , Internet : 'إنترنت' , Heating : 'تدفئة' , Water : 'ماء' ,
Pool : 'مسبح' , PrivateGarden : 'حديقة خاصة' , Parking : 'موقف سيارات' ,
Security247 : 'حراسة 24 س' , CentralHeating : 'تدفئة مركزية' ,
CentralAirConditioning : 'تكييف مركزي' , EquippedKitchen : 'مطبخ مجهز' ,
MaidsRoom : 'غرفة خادمة' , Elevator : 'مصعد' , Gym : 'نادي رياضي' ,
Sauna : 'ساونا' , Jacuzzi : 'جاكوزي' , Balcony : 'بلكونة' ,
Rooftop : 'سطح' , Furnished : 'مفروش' , AirConditioning : 'تكييف' ,
SatelliteTV : 'تلفاز' , Fireplace : 'مدفأة' , StudyRoom : 'غرفة دراسة' ,
Storage : 'مستودع' , Laundry : 'غرفة غسيل' , SmartHome : 'منزل ذكي' ,
} ;
const termLabels = {
NoSmoking : 'ممنوع التدخين' , NoAnimals : 'ممنوع الحيوانات الأليفة' ,
NoParties : 'ممنوع الحفلات' , NoAlcohol : 'ممنوع الكحول' ,
SuitableForChildren : 'مناسب للأطفال' , SuitableForFamilies : 'مناسب للعائلات' ,
SuitableForStudents : 'مناسب للطلاب' , SuitableForElderly : 'مناسب لكبار السن' ,
OnlyFemales : 'إناث فقط' , OnlyMales : 'ذكور فقط' ,
} ;
const proximityLabels = {
School : 'مدرسة' , Hospital : 'مستشفى' , Restaurant : 'مطعم' ,
University : 'جامعة' , Park : 'حديقة' , Mall : 'مركز تسوق' ,
Supermarket : 'سوبر ماركت' , Pharmacy : 'صيدلية' , Mosque : 'مسجد' ,
Bank : 'بنك' , Airport : 'مطار' , BusStation : 'موقف باص' ,
} ;
2026-03-30 13:44:52 +00:00
function mapApiDetail ( item ) {
if ( ! item ) return null ;
const info = item . propertyInformation || { } ;
2026-05-26 00:20:20 +03:00
const details = typeof info . detailsJSON === 'object' && info . detailsJSON ? info . detailsJSON : ( ( ) => { try { return JSON . parse ( info . detailsJSON || '{}' ) ; } catch { return { } ; } } ) ( ) ;
2026-04-28 13:17:57 -07:00
2026-05-25 21:27:39 +03:00
const isRent = item . dailyRent != null || item . monthlyRent != null ;
const dailyPrice = item . dailyRent || 0 ;
const monthlyPrice = item . monthlyRent || 0 ;
const salePrice = item . price || 0 ;
const price = isRent ? ( monthlyPrice || dailyPrice ) : salePrice ;
const priceUnit = isRent ? ( monthlyPrice ? 'monthly' : 'daily' ) : 'sale' ;
2026-03-30 13:44:52 +00:00
2026-05-25 21:27:39 +03:00
const propType = BuildingTypeKeys [ info . buildingType ] ? ? BuildingTypeKeys [ item . type ] ? ? 'apartment' ;
2026-05-25 21:53:57 +03:00
const typeLabel = { apartment : 'شقة' , villa : 'فيلا' , house : 'بيت' , sweet : 'سويت' , studio : 'استوديو' , office : 'مكتب' , shop : 'متجر' , warehouse : 'مستودع' , farms : 'مزرعة' , room : 'غرفة ضمن شقة' } [ propType ] || 'عقار' ;
2026-05-25 21:27:39 +03:00
const status = PropertyStatusKeys [ info . status ] ? ? PropertyStatusKeys [ item . status ] ? ? 'available' ;
2026-05-25 21:53:57 +03:00
const statusLabel = { available : 'متاح' , booked : 'محجوز' , notAvailable : 'غير متاح' , maintenance : 'صيانة' } [ status ] || 'متاح' ;
2026-04-28 13:17:57 -07:00
2026-05-25 21:27:39 +03:00
const rawImages = Array . isArray ( info . images ) ? info . images : [ ] ;
2026-05-25 21:53:57 +03:00
const photosFallback = Array . isArray ( details . photos ) ? details . photos : [ ] ;
const allRaw = rawImages . length > 0 ? rawImages : photosFallback ;
const images = allRaw . length > 0 ? allRaw . map ( buildImageUrl ) : [ ] ;
2026-04-28 13:17:57 -07:00
2026-05-25 22:14:37 +03:00
// Normalize services: Flutter sends list of strings or object with boolean values
const rawServices = details . services || { } ;
const serviceList = Array . isArray ( rawServices ) ? rawServices : Object . keys ( rawServices ) . filter ( k => rawServices [ k ] ) ;
const serviceDetails = details . serviceDetails || { } ;
const services = { } ;
serviceList . forEach ( s => { services [ s ] = serviceDetails [ s ] || true ; } ) ;
2026-05-25 23:55:02 +03:00
const rawTerms = details . terms || { } ;
const terms = Array . isArray ( rawTerms ) ? rawTerms . reduce ( ( acc , t ) => ( { ... acc , [ t ] : true } ) , { } ) : rawTerms ;
2026-05-25 22:14:37 +03:00
// Try multiple key aliases like Flutter does
const floor = details . floorNumber ? ? details . floor ? ? 0 ;
const salons = details . numberOfSalons ? ? details . salonsCount ? ? details . salons ? ? 0 ;
const balconies = details . numberOfBalconies ? ? details . balconiesCount ? ? details . balconies ? ? 0 ;
2026-05-25 23:55:02 +03:00
const rawProximity = details . nearbyDistances || details . proximity || { } ;
const proximity = { } ;
Object . entries ( rawProximity ) . forEach ( ( [ k , v ] ) => {
if ( ! v ) return ;
const normalizedKey = k . charAt ( 0 ) . toUpperCase ( ) + k . slice ( 1 ) ;
proximity [ normalizedKey ] = v ;
} ) ;
2026-05-25 22:14:37 +03:00
const roomDetails = details . room || details . roomDetails || { } ;
2026-03-30 13:44:52 +00:00
2026-05-26 00:20:20 +03:00
const displayType = details . displayType || ( isRent ? ( monthlyPrice && dailyPrice ? 'Both' : monthlyPrice ? 'Monthly' : 'Daily' ) : 'Sale' ) ;
const propertyCondition = details . propertyCondition || '' ;
const furnished = propertyCondition . toLowerCase ( ) . includes ( 'furniture' ) ? propertyCondition . toLowerCase ( ) === 'withfurniture' : ! ! item . isFurnished ;
2026-03-30 13:44:52 +00:00
return {
id : item . id ,
2026-05-25 22:14:37 +03:00
propertyInformationId : info . id ,
2026-05-25 21:53:57 +03:00
title : details . description || info . address || ` عقار # ${ item . id } ` ,
description : details . description || info . description || '' ,
2026-03-30 13:44:52 +00:00
type : propType ,
2026-05-25 21:27:39 +03:00
typeLabel ,
price ,
2026-04-26 13:46:30 +03:00
priceUnit ,
2026-05-25 21:27:39 +03:00
priceDisplay : { daily : dailyPrice , monthly : monthlyPrice , sale : salePrice } ,
isRent ,
2026-05-26 00:20:20 +03:00
displayType ,
propertyCondition ,
furnished ,
2026-03-30 13:44:52 +00:00
location : {
city : extractCity ( info . address ) || 'دمشق' ,
address : info . address || '' ,
2026-05-25 21:27:39 +03:00
lat : parseFloat ( info . cordsX ) || 0 ,
lng : parseFloat ( info . cordsY ) || 0 ,
2026-03-30 13:44:52 +00:00
} ,
bedrooms : info . numberOfBedRooms || 0 ,
bathrooms : info . numberOfBathRooms || 0 ,
area : info . space || 0 ,
2026-05-25 22:14:37 +03:00
floor ,
salons ,
balconies ,
2026-03-30 13:44:52 +00:00
images ,
status ,
2026-05-25 21:27:39 +03:00
statusLabel ,
services ,
terms ,
2026-05-25 21:53:57 +03:00
proximity ,
roomDetails ,
2026-05-25 21:27:39 +03:00
details ,
2026-05-25 23:07:29 +03:00
bookedCount : details . bookedCount || 0 ,
2026-04-26 13:46:30 +03:00
deposit : item . deposit || 0 ,
2026-05-25 21:27:39 +03:00
currencyId : item . currencyId ,
isSmokeAllow : item . isSmokeAllow ,
isVisitorAllow : item . isVisitorAllow ,
specializedFor : item . specializedFor ,
2026-05-26 18:43:47 +03:00
ownerId : info . ownerId ? ? info . userId ? ? item . ownerId ? ? item . userId ? ? null ,
2026-03-30 13:44:52 +00:00
_raw : item ,
} ;
}
export default function PropertyDetailsPage ( ) {
const params = useParams ( ) ;
2026-05-25 21:27:39 +03:00
const router = useRouter ( ) ;
2026-03-30 17:54:42 +00:00
const { isFavorite , addFavorite , removeFavorite } = useFavorites ( ) ;
2026-04-26 13:46:30 +03:00
const [ property , setProperty ] = useState ( null ) ;
const [ loading , setLoading ] = useState ( true ) ;
2026-03-30 13:44:52 +00:00
const [ currentImage , setCurrentImage ] = useState ( 0 ) ;
2026-04-26 13:46:30 +03:00
const [ showLoginDialog , setShowLoginDialog ] = useState ( false ) ;
2026-03-30 13:44:52 +00:00
const [ bookingDates , setBookingDates ] = useState ( { start : '' , end : '' } ) ;
2026-05-25 21:27:39 +03:00
const [ bookingLoading , setBookingLoading ] = useState ( false ) ;
2026-03-30 13:44:52 +00:00
const [ bookingError , setBookingError ] = useState ( null ) ;
const [ bookingSuccess , setBookingSuccess ] = useState ( false ) ;
const [ availableRanges , setAvailableRanges ] = useState ( [ ] ) ;
2026-05-26 16:38:16 +03:00
const [ bookingStep , setBookingStep ] = useState ( 'entry' ) ;
const [ selectedStart , setSelectedStart ] = useState ( null ) ;
const [ selectedEnd , setSelectedEnd ] = useState ( null ) ;
const [ calendarMonth , setCalendarMonth ] = useState ( ( ) => new Date ( ) . getMonth ( ) ) ;
const [ calendarYear , setCalendarYear ] = useState ( ( ) => new Date ( ) . getFullYear ( ) ) ;
const [ pricingMode , setPricingMode ] = useState ( 'daily' ) ;
2026-05-26 17:31:01 +03:00
const [ isOwnProperty , setIsOwnProperty ] = useState ( false ) ;
2026-05-25 21:27:39 +03:00
const [ favLoading , setFavLoading ] = useState ( false ) ;
const [ avgRating , setAvgRating ] = useState ( null ) ;
2026-04-12 20:44:04 +00:00
const [ showRatingForm , setShowRatingForm ] = useState ( false ) ;
2026-05-25 21:27:39 +03:00
const [ showContact , setShowContact ] = useState ( false ) ;
const [ contactInfo , setContactInfo ] = useState ( null ) ;
const [ ownerData , setOwnerData ] = useState ( null ) ;
2026-03-30 13:44:52 +00:00
useEffect ( ( ) => {
2026-05-25 21:27:39 +03:00
const id = params . id ;
if ( ! id ) return ;
setLoading ( true ) ;
2026-04-28 13:17:57 -07:00
2026-05-25 21:27:39 +03:00
async function fetchProperty ( ) {
2026-03-30 13:44:52 +00:00
try {
let data = null ;
2026-05-25 21:27:39 +03:00
try { data = await getRentProperty ( id ) ; } catch { }
if ( ! data ) { try { data = await getSalePropertyById ( id ) || await getSaleProperty ( id ) ; } catch { } }
if ( ! data ) { try { data = await getSaleProperty ( id ) ; } catch { } }
2026-03-30 13:44:52 +00:00
if ( data ) {
2026-05-25 21:27:39 +03:00
const mapped = mapApiDetail ( data ) ;
setProperty ( mapped ) ;
if ( mapped ) fetchAvgRating ( mapped . id ) ;
2026-05-26 16:38:16 +03:00
if ( mapped && mapped . isRent ) {
try {
const propInfoId = mapped . _raw ? . propertyInformationId || mapped . id ;
const ranges = await getAvailableDateRanges ( propInfoId ) ;
if ( ranges && Array . isArray ( ranges ) ) {
setAvailableRanges ( ranges ) ;
}
} catch ( e ) {
console . warn ( 'Failed to fetch date ranges' , e ) ;
}
}
2026-05-26 19:04:57 +03:00
// Check if current user owns this property
if ( AuthService . isAuthenticated ( ) && AuthService . isOwner ( ) ) {
try {
const authUser = AuthService . getUser ( ) ;
const rawInfo = ( mapped . _raw || { } ) . propertyInformation || { } ;
let owned = false ;
// Check 1: Fetch owner profile to get DB id, then compare with propertyInformation.ownerId
if ( authUser ? . id ) {
try {
const ownerProfile = await getOwnerByUserId ( authUser . id ) ;
if ( ownerProfile ? . id && mapped . ownerId && Number ( ownerProfile . id ) === Number ( mapped . ownerId ) ) {
owned = true ;
}
} catch ( e ) {
console . warn ( '[OwnerCheck] getOwnerByUserId failed:' , e ) ;
}
}
// Check 2: Fallback via managed property IDs (like Flutter ManagedPropertiesService)
if ( ! owned ) {
2026-05-26 18:22:21 +03:00
const [ myRent , mySale ] = await Promise . allSettled ( [
getMyRentListings ( ) ,
getMySaleListings ( ) ,
] ) ;
const myPropIds = new Set ( ) ;
const collectIds = ( result ) => {
if ( result . status !== 'fulfilled' || ! result . value ) return ;
const list = Array . isArray ( result . value ) ? result . value : [ result . value ] ;
list . forEach ( p => {
const info = p . propertyInformation || { } ;
if ( info . id ) myPropIds . add ( Number ( info . id ) ) ;
if ( p . id ) myPropIds . add ( Number ( p . id ) ) ;
} ) ;
} ;
collectIds ( myRent ) ;
collectIds ( mySale ) ;
const checkId = mapped . id ? Number ( mapped . id ) : null ;
const checkInfoId = rawInfo . id ? Number ( rawInfo . id ) : null ;
if ( ( checkId && myPropIds . has ( checkId ) ) || ( checkInfoId && myPropIds . has ( checkInfoId ) ) ) {
2026-05-26 19:04:57 +03:00
owned = true ;
2026-05-26 18:22:21 +03:00
}
2026-05-26 19:04:57 +03:00
}
setIsOwnProperty ( owned ) ;
} catch ( e ) {
console . error ( '[OwnerCheck] error:' , e ) ;
2026-05-26 18:22:21 +03:00
}
2026-05-26 17:31:01 +03:00
}
2026-03-30 13:44:52 +00:00
}
} catch ( err ) {
2026-05-25 21:27:39 +03:00
console . error ( '[PropertyDetail] Failed:' , err ) ;
2026-03-30 13:44:52 +00:00
} finally {
setLoading ( false ) ;
}
}
2026-05-25 21:27:39 +03:00
fetchProperty ( ) ;
} , [ params . id ] ) ;
2026-03-30 13:44:52 +00:00
2026-05-25 21:27:39 +03:00
const fetchAvgRating = async ( propId ) => {
try {
const avg = await getPropertyAverageRating ( propId ) ;
setAvgRating ( avg ) ;
} catch { }
2026-04-28 13:17:57 -07:00
} ;
2026-05-25 21:27:39 +03:00
const fetchContactInfo = async ( ) => {
if ( ! property ) return ;
try {
const info = await getOwnerContactInformation ( property . _raw ? . propertyInformationId || property . id ) ;
setContactInfo ( info ) ;
setShowContact ( true ) ;
} catch ( err ) {
toast . error ( 'فشل تحميل معلومات الاتصال' ) ;
2026-03-30 13:44:52 +00:00
}
} ;
2026-05-25 21:27:39 +03:00
const handleFavorite = async ( ) => {
if ( ! AuthService . isAuthenticated ( ) ) { setShowLoginDialog ( true ) ; return ; }
if ( ! property ) return ;
setFavLoading ( true ) ;
try {
if ( isFavorite ( property . id ) ) {
await removeFavorite ( property . id ) ;
2026-03-30 13:44:52 +00:00
} else {
2026-05-25 21:27:39 +03:00
await addFavorite ( property . id ) ;
2026-03-30 13:44:52 +00:00
}
2026-05-25 21:27:39 +03:00
} catch ( err ) {
toast . error ( 'حدث خطأ' ) ;
} finally {
setFavLoading ( false ) ;
2026-03-30 13:44:52 +00:00
}
} ;
2026-05-25 21:27:39 +03:00
const handleBookNow = async ( ) => {
if ( ! AuthService . isAuthenticated ( ) ) { setShowLoginDialog ( true ) ; return ; }
2026-03-30 13:44:52 +00:00
if ( ! bookingDates . start || ! bookingDates . end ) {
2026-05-25 21:27:39 +03:00
setBookingError ( 'يرجى تحديد تاريخ البداية والنهاية' ) ;
2026-04-28 12:57:06 -07:00
return ;
}
2026-05-25 21:27:39 +03:00
setBookingLoading ( true ) ;
setBookingError ( null ) ;
2026-03-30 13:44:52 +00:00
try {
2026-05-25 21:27:39 +03:00
await bookReservation ( property . _raw ? . propertyInformationId || property . id , bookingDates . start , bookingDates . end ) ;
2026-03-30 13:44:52 +00:00
setBookingSuccess ( true ) ;
2026-04-26 13:46:30 +03:00
toast . success ( 'تم إرسال طلب الحجز بنجاح' ) ;
2026-03-30 13:44:52 +00:00
} catch ( err ) {
2026-04-26 13:46:30 +03:00
setBookingError ( err . message || 'فشل الحجز' ) ;
2026-04-28 13:17:57 -07:00
} finally {
setBookingLoading ( false ) ;
2026-03-30 13:44:52 +00:00
}
} ;
2026-05-26 16:38:16 +03:00
const MONTHS _AR = [ 'يناير' , 'فبراير' , 'مارس' , 'أبريل' , 'مايو' , 'يونيو' , 'يوليو' , 'أغسطس' , 'سبتمبر' , 'أكتوبر' , 'نوفمبر' , 'ديسمبر' ] ;
const DAYS _AR = [ 'ح' , 'ن' , 'ث' , 'ر' , 'خ' , 'ج' , 'س' ] ;
const availableDatesSet = useMemo ( ( ) => {
const dates = new Set ( ) ;
if ( ! Array . isArray ( availableRanges ) ) return dates ;
availableRanges . forEach ( r => {
const start = new Date ( r . startDate || r . start ) ;
const end = new Date ( r . endDate || r . end ) ;
for ( let d = new Date ( start ) ; d <= end ; d . setDate ( d . getDate ( ) + 1 ) ) {
dates . add ( d . toISOString ( ) . split ( 'T' ) [ 0 ] ) ;
}
} ) ;
return dates ;
} , [ availableRanges ] ) ;
const isDateAvailable = ( dateStr ) => availableDatesSet . has ( dateStr ) ;
const isPastDate = ( dateStr ) => {
const today = new Date ( ) ;
today . setHours ( 0 , 0 , 0 , 0 ) ;
return new Date ( dateStr ) < today ;
} ;
const handleDayClick = ( dateStr ) => {
if ( bookingStep === 'entry' ) {
setSelectedStart ( dateStr ) ;
setSelectedEnd ( null ) ;
setBookingStep ( 'exit' ) ;
} else {
if ( new Date ( dateStr ) <= new Date ( selectedStart ) ) {
setSelectedStart ( dateStr ) ;
setSelectedEnd ( null ) ;
setBookingStep ( 'exit' ) ;
} else {
setSelectedEnd ( dateStr ) ;
setBookingStep ( 'entry' ) ;
}
}
} ;
const handleBookingConfirm = async ( ) => {
if ( ! AuthService . isAuthenticated ( ) ) { setShowLoginDialog ( true ) ; return ; }
if ( ! selectedStart || ! selectedEnd ) {
setBookingError ( 'يرجى تحديد تاريخ البداية والنهاية' ) ;
return ;
}
setBookingLoading ( true ) ;
setBookingError ( null ) ;
try {
const propInfoId = property . _raw ? . propertyInformationId || property . id ;
const startDate = new Date ( selectedStart + 'T00:00:00.000' ) . toISOString ( ) ;
const endDate = new Date ( selectedEnd + 'T00:00:00.000' ) . toISOString ( ) ;
await bookReservation ( propInfoId , startDate , endDate ) ;
setBookingSuccess ( true ) ;
toast . success ( 'تم إرسال طلب الحجز بنجاح' ) ;
} catch ( err ) {
setBookingError ( err . message || 'فشل الحجز' ) ;
} finally {
setBookingLoading ( false ) ;
}
} ;
const navigateMonth = ( delta ) => {
let month = calendarMonth + delta ;
let year = calendarYear ;
if ( month < 0 ) { month = 11 ; year -- ; }
if ( month > 11 ) { month = 0 ; year ++ ; }
setCalendarMonth ( month ) ;
setCalendarYear ( year ) ;
} ;
2026-05-25 21:27:39 +03:00
const handleRatingSuccess = ( ) => {
setShowRatingForm ( false ) ;
if ( property ) fetchAvgRating ( property . id ) ;
} ;
2026-03-30 13:44:52 +00:00
if ( loading ) {
return (
2026-05-25 21:27:39 +03:00
< div className = "min-h-screen bg-gray-50 flex items-center justify-center" >
< div className = "text-center" >
< Loader2 className = "w-12 h-12 text-amber-500 animate-spin mx-auto mb-4" / >
< p className = "text-gray-600" > جاري تحميل العقار ... < / p >
< / d i v >
2026-03-30 13:44:52 +00:00
< / d i v >
) ;
}
if ( ! property ) {
return (
2026-05-25 21:27:39 +03:00
< div className = "min-h-screen bg-gray-50 flex items-center justify-center p-4" >
< div className = "text-center max-w-md" >
< div className = "w-24 h-24 bg-amber-100 rounded-full flex items-center justify-center mx-auto mb-4" >
< Home className = "w-12 h-12 text-amber-600" / >
< / d i v >
< h2 className = "text-2xl font-bold text-gray-900 mb-2" > العقار غير موجود < / h 2 >
< p className = "text-gray-600 mb-6" > لم يتم العثور على العقار المطلوب < / p >
< Link href = "/properties" className = "inline-flex items-center gap-2 bg-amber-500 text-white px-6 py-3 rounded-xl font-medium hover:bg-amber-600" >
< ArrowLeft className = "w-5 h-5" / >
العودة إلى العقارات
< / L i n k >
< / d i v >
2026-03-30 13:44:52 +00:00
< / d i v >
) ;
}
2026-05-25 21:27:39 +03:00
const isFav = isFavorite ( property . id ) ;
2026-05-25 21:53:57 +03:00
const isRoomType = property . type === 'room' ;
const isMostRequested = avgRating !== null && avgRating >= 4.5 ;
2026-05-26 16:38:16 +03:00
const showPricingToggle = property . isRent && property . priceDisplay ? . daily > 0 && property . priceDisplay ? . monthly > 0 ;
const effectivePricingMode = showPricingToggle ? pricingMode : ( property . isRent && property . priceDisplay ? . monthly > 0 ? 'monthly' : 'daily' ) ;
2026-04-28 13:17:57 -07:00
2026-03-30 13:44:52 +00:00
return (
2026-05-25 21:27:39 +03:00
< div className = "min-h-screen bg-gray-50" dir = "rtl" >
< Toaster position = "top-center" reverseOrder = { false } / >
< div className = "container mx-auto px-4 py-6" >
< motion . div initial = { { opacity : 0 , y : - 10 } } animate = { { opacity : 1 , y : 0 } } className = "mb-6" >
< Link href = "/properties" className = "inline-flex items-center gap-2 text-gray-600 hover:text-amber-600 transition-colors" >
< ArrowLeft className = "w-5 h-5" / >
العودة إلى العقارات
< / L i n k >
< / m o t i o n . d i v >
2026-05-25 22:42:53 +03:00
< div className = "grid grid-cols-1 lg:grid-cols-3 gap-6" >
< div className = "lg:col-span-2 space-y-5" >
2026-05-25 21:27:39 +03:00
{ /* Image Gallery */ }
< motion . div initial = { { opacity : 0 , y : 20 } } animate = { { opacity : 1 , y : 0 } } className = "bg-white rounded-2xl overflow-hidden shadow-sm border border-gray-200" >
2026-05-25 22:42:53 +03:00
< div className = "relative bg-gray-900" style = { { minHeight : '380px' , maxHeight : '460px' } } >
2026-05-25 21:27:39 +03:00
{ property . images . length > 0 ? (
2026-05-25 21:53:57 +03:00
< img src = { property . images [ currentImage ] } alt = { property . title }
className = "w-full h-full object-contain mx-auto"
2026-05-25 22:42:53 +03:00
style = { { minHeight : '380px' , maxHeight : '460px' } } / >
2026-05-25 21:27:39 +03:00
) : (
2026-05-25 21:53:57 +03:00
< div className = "w-full h-full flex items-center justify-center" style = { { minHeight : '420px' } } >
< div className = "text-center" >
< ImageIcon className = "w-20 h-20 text-gray-500 mx-auto mb-2" / >
< p className = "text-gray-400 text-sm" > لا توجد صور < / p >
< / d i v >
2026-05-25 21:27:39 +03:00
< / d i v >
) }
2026-05-25 21:53:57 +03:00
{ isMostRequested && (
< div className = "absolute top-4 right-4 z-10" >
< span className = "bg-gradient-to-l from-amber-500 to-amber-600 text-white px-3 py-1.5 rounded-full text-xs font-bold shadow-lg flex items-center gap-1" >
< Star className = "w-3 h-3 fill-white" / > الأكثر طلبا ً
< / s p a n >
< / d i v >
) }
2026-05-25 21:27:39 +03:00
{ property . images . length > 1 && (
2026-03-30 16:08:35 +00:00
< >
2026-05-25 21:27:39 +03:00
< button onClick = { ( ) => setCurrentImage ( prev => ( prev - 1 + property . images . length ) % property . images . length ) }
2026-05-25 21:53:57 +03:00
className = "absolute right-4 top-1/2 -translate-y-1/2 bg-black/60 text-white p-3 rounded-full hover:bg-black/80 transition-all shadow-lg" >
2026-05-25 21:27:39 +03:00
< ChevronRight className = "w-5 h-5" / >
< / b u t t o n >
< button onClick = { ( ) => setCurrentImage ( prev => ( prev + 1 ) % property . images . length ) }
2026-05-25 21:53:57 +03:00
className = "absolute left-4 top-1/2 -translate-y-1/2 bg-black/60 text-white p-3 rounded-full hover:bg-black/80 transition-all shadow-lg" >
2026-05-25 21:27:39 +03:00
< ChevronLeft className = "w-5 h-5" / >
< / b u t t o n >
2026-03-30 16:08:35 +00:00
< / >
) }
2026-05-25 21:53:57 +03:00
< div className = "absolute bottom-4 right-4 flex gap-2 z-10" >
< span className = "bg-black/70 text-white px-3 py-1 rounded-full text-sm font-medium backdrop-blur-sm" > { property . statusLabel } < / s p a n >
< span className = "bg-black/70 text-white px-3 py-1 rounded-full text-sm font-medium backdrop-blur-sm" > { property . typeLabel } < / s p a n >
< / d i v >
< div className = "absolute bottom-4 left-4 bg-black/70 text-white px-3 py-1 rounded-full text-xs backdrop-blur-sm z-10" >
{ currentImage + 1 } / { property . images . length || 1 }
2026-03-30 13:44:52 +00:00
< / d i v >
< / d i v >
2026-05-25 21:53:57 +03:00
2026-05-25 21:27:39 +03:00
{ property . images . length > 1 && (
2026-05-25 21:53:57 +03:00
< div className = "flex gap-2 p-3 bg-gray-50 overflow-x-auto" style = { { scrollBehavior : 'smooth' } } >
2026-05-25 21:27:39 +03:00
{ property . images . map ( ( img , idx ) => (
< button key = { idx } onClick = { ( ) => setCurrentImage ( idx ) }
2026-05-25 21:53:57 +03:00
className = { ` flex-shrink-0 w-24 h-20 rounded-xl overflow-hidden border-2 transition-all duration-200 ${ idx === currentImage ? 'border-amber-500 ring-2 ring-amber-200 shadow-md' : 'border-gray-200 opacity-60 hover:opacity-100' } ` } >
2026-05-25 21:27:39 +03:00
< img src = { img } alt = "" className = "w-full h-full object-cover" / >
< / b u t t o n >
) ) }
2026-03-30 13:44:52 +00:00
< / d i v >
2026-05-25 21:27:39 +03:00
) }
< / m o t i o n . d i v >
2026-04-28 13:17:57 -07:00
2026-05-25 21:27:39 +03:00
{ /* Property Info */ }
2026-05-25 22:42:53 +03:00
< motion . div initial = { { opacity : 0 , y : 20 } } animate = { { opacity : 1 , y : 0 } } className = "bg-white rounded-2xl p-5 shadow-sm border border-gray-200" >
< div className = "flex justify-between items-start mb-3" >
2026-04-28 13:17:57 -07:00
< div >
2026-05-26 00:20:20 +03:00
< div className = "flex items-center gap-1.5 mb-1 flex-wrap" >
2026-05-25 22:42:53 +03:00
< span className = "px-2 py-0.5 bg-amber-100 text-amber-800 rounded-full text-xs" > { property . typeLabel } < / s p a n >
< span className = { ` px-2 py-0.5 rounded-full text-xs ${ property . status === 'available' ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800' } ` } > { property . statusLabel } < / s p a n >
2026-05-26 00:20:20 +03:00
{ property . isRent && property . displayType && (
< span className = "px-2 py-0.5 bg-blue-100 text-blue-800 rounded-full text-xs" >
{ ( ( ) => {
const dt = property . displayType . toLowerCase ( ) ;
if ( dt === 'both' || dt . includes ( 'both' ) ) return 'يومي وشهري' ;
if ( dt . includes ( 'daily' ) ) return 'يومي' ;
if ( dt . includes ( 'monthly' ) ) return 'شهري' ;
return dt ;
} ) ( ) }
< / s p a n >
) }
< span className = { ` px-2 py-0.5 rounded-full text-xs ${ property . furnished ? 'bg-purple-100 text-purple-800' : 'bg-gray-100 text-gray-600' } ` } >
{ property . furnished ? 'مفروش' : 'غير مفروش' }
< / s p a n >
2026-05-25 21:27:39 +03:00
< / d i v >
2026-05-25 22:42:53 +03:00
< h1 className = "text-xl font-bold text-gray-900" > { property . title } < / h 1 >
< div className = "flex items-center gap-1 text-gray-500 text-xs mt-0.5" >
< MapPin className = "w-3 h-3" / >
2026-05-25 21:27:39 +03:00
< span > { property . location . address || property . location . city } < / s p a n >
< / d i v >
2026-03-30 13:44:52 +00:00
< / d i v >
2026-05-25 22:42:53 +03:00
< button onClick = { handleFavorite } disabled = { favLoading }
className = "p-1.5 rounded-full hover:bg-gray-100 transition-colors" >
< Heart className = { ` w-5 h-5 ${ isFav ? 'fill-red-500 text-red-500' : 'text-gray-400' } ` } / >
< / b u t t o n >
2026-05-25 21:27:39 +03:00
< / d i v >
2026-04-28 13:17:57 -07:00
2026-05-25 21:27:39 +03:00
{ /* Price */ }
2026-05-25 22:42:53 +03:00
< div className = "bg-amber-50 rounded-xl p-3 mb-4" >
2026-05-25 21:27:39 +03:00
{ property . isRent ? (
2026-05-25 21:53:57 +03:00
< div className = "flex flex-wrap gap-6 items-end" >
2026-05-25 21:27:39 +03:00
{ property . priceDisplay . monthly > 0 && (
< div >
< span className = "text-2xl font-bold text-amber-600" > { formatCurrency ( property . priceDisplay . monthly ) } < / s p a n >
< span className = "text-gray-500 mr-1" > ل . س / شهريا ً < / s p a n >
< / d i v >
) }
{ property . priceDisplay . daily > 0 && (
< div >
< span className = "text-2xl font-bold text-amber-600" > { formatCurrency ( property . priceDisplay . daily ) } < / s p a n >
< span className = "text-gray-500 mr-1" > ل . س / يوميا ً < / s p a n >
< / d i v >
) }
{ property . deposit > 0 && (
2026-05-25 21:53:57 +03:00
< div className = "text-sm text-gray-500" >
< span className = "font-medium" > تأمين : < / s p a n > { f o r m a t C u r r e n c y ( p r o p e r t y . d e p o s i t ) } ل . س
< / d i v >
2026-05-25 21:27:39 +03:00
) }
< / d i v >
) : (
2026-04-28 13:17:57 -07:00
< div >
2026-05-25 21:27:39 +03:00
< span className = "text-2xl font-bold text-blue-600" > { formatCurrency ( property . price ) } < / s p a n >
< span className = "text-gray-500 mr-1" > ل . س < / s p a n >
< span className = "text-sm text-gray-400 mr-2" > للبيع < / s p a n >
2026-03-30 13:44:52 +00:00
< / d i v >
2026-05-25 21:27:39 +03:00
) }
2026-03-30 13:44:52 +00:00
< / d i v >
2026-04-28 13:17:57 -07:00
2026-05-25 21:53:57 +03:00
{ /* Specs Tiles */ }
2026-05-25 22:42:53 +03:00
< div className = "grid grid-cols-3 md:grid-cols-6 gap-1.5 mb-4" >
2026-05-25 21:27:39 +03:00
{ property . bedrooms > 0 && (
2026-05-25 22:42:53 +03:00
< div className = "bg-gray-50 rounded-lg p-2 text-center" >
< Bed className = "w-4 h-4 text-amber-500 mx-auto mb-0.5" / >
< div className = "font-bold text-gray-900 text-sm" > { property . bedrooms } < / d i v >
< div className = "text-[10px] text-gray-500" > غرف نوم < / d i v >
2026-05-25 21:27:39 +03:00
< / d i v >
) }
{ property . bathrooms > 0 && (
2026-05-25 22:42:53 +03:00
< div className = "bg-gray-50 rounded-lg p-2 text-center" >
< Bath className = "w-4 h-4 text-amber-500 mx-auto mb-0.5" / >
< div className = "font-bold text-gray-900 text-sm" > { property . bathrooms } < / d i v >
< div className = "text-[10px] text-gray-500" > حمامات < / d i v >
2026-03-30 13:44:52 +00:00
< / d i v >
2026-05-25 21:27:39 +03:00
) }
{ property . area > 0 && (
2026-05-25 22:42:53 +03:00
< div className = "bg-gray-50 rounded-lg p-2 text-center" >
< Square className = "w-4 h-4 text-amber-500 mx-auto mb-0.5" / >
< div className = "font-bold text-gray-900 text-sm" > { property . area } < / d i v >
< div className = "text-[10px] text-gray-500" > م² < / d i v >
2026-05-25 21:27:39 +03:00
< / d i v >
) }
2026-05-25 21:53:57 +03:00
{ property . floor > 0 && (
2026-05-25 22:42:53 +03:00
< div className = "bg-gray-50 rounded-lg p-2 text-center" >
< Layers className = "w-4 h-4 text-amber-500 mx-auto mb-0.5" / >
< div className = "font-bold text-gray-900 text-sm" > { property . floor } < / d i v >
< div className = "text-[10px] text-gray-500" > طابق < / d i v >
2026-05-25 21:53:57 +03:00
< / d i v >
) }
{ property . salons > 0 && (
2026-05-25 22:42:53 +03:00
< div className = "bg-gray-50 rounded-lg p-2 text-center" >
< Sofa className = "w-4 h-4 text-amber-500 mx-auto mb-0.5" / >
< div className = "font-bold text-gray-900 text-sm" > { property . salons } < / d i v >
< div className = "text-[10px] text-gray-500" > صالونات < / d i v >
2026-05-25 21:53:57 +03:00
< / d i v >
) }
{ property . balconies > 0 && (
2026-05-25 22:42:53 +03:00
< div className = "bg-gray-50 rounded-lg p-2 text-center" >
< DoorOpen className = "w-4 h-4 text-amber-500 mx-auto mb-0.5" / >
< div className = "font-bold text-gray-900 text-sm" > { property . balconies } < / d i v >
< div className = "text-[10px] text-gray-500" > بلكونات < / d i v >
2026-05-25 21:53:57 +03:00
< / d i v >
) }
2026-05-25 21:27:39 +03:00
{ avgRating !== null && avgRating > 0 && (
2026-05-25 22:42:53 +03:00
< div className = "bg-gray-50 rounded-lg p-2 text-center" >
< Star className = "w-4 h-4 text-amber-500 mx-auto mb-0.5 fill-amber-500" / >
< div className = "font-bold text-gray-900 text-sm" > { avgRating . toFixed ( 1 ) } < / d i v >
< div className = "text-[10px] text-gray-500" > التقييم < / d i v >
2026-05-25 21:27:39 +03:00
< / d i v >
) }
2026-05-25 23:07:29 +03:00
{ property . bookedCount > 0 && (
< div className = "bg-gray-50 rounded-lg p-2 text-center" >
< Calendar className = "w-4 h-4 text-amber-500 mx-auto mb-0.5" / >
< div className = "font-bold text-gray-900 text-sm" > { property . bookedCount } < / d i v >
< div className = "text-[10px] text-gray-500" > حجوزات < / d i v >
< / d i v >
) }
2026-03-30 13:44:52 +00:00
< / d i v >
2026-05-25 21:27:39 +03:00
{ /* Description */ }
{ property . description && (
2026-05-25 22:42:53 +03:00
< div className = "mb-4" >
< h3 className = "font-bold text-gray-900 mb-1 text-sm" > الوصف < / h 3 >
< p className = "text-gray-600 text-sm leading-relaxed" > { property . description } < / p >
2026-05-25 21:27:39 +03:00
< / d i v >
) }
2026-03-30 13:44:52 +00:00
2026-05-25 21:27:39 +03:00
{ /* Features */ }
2026-05-25 22:42:53 +03:00
< div className = "flex flex-wrap gap-1.5 mb-4" >
{ property . isSmokeAllow && < span className = "px-2 py-0.5 bg-gray-100 text-gray-700 rounded-full text-xs border flex items-center gap-1" > < Wind className = "w-3 h-3" / > يسمح بالتدخين < / s p a n > }
{ ! property . isSmokeAllow && < span className = "px-2 py-0.5 bg-gray-100 text-gray-700 rounded-full text-xs border flex items-center gap-1" > < Ban className = "w-3 h-3" / > ممنوع التدخين < / s p a n > }
{ property . isVisitorAllow && < span className = "px-2 py-0.5 bg-gray-100 text-gray-700 rounded-full text-xs border flex items-center gap-1" > < Users className = "w-3 h-3" / > يسمح بالزوار < / s p a n > }
{ property . specializedFor && < span className = "px-2 py-0.5 bg-amber-50 text-amber-700 rounded-full text-xs border border-amber-200 flex items-center gap-1" > < Users className = "w-3 h-3" / > { property . specializedFor } < / s p a n > }
2026-04-26 13:46:30 +03:00
< / d i v >
2026-05-25 21:27:39 +03:00
2026-05-25 21:53:57 +03:00
{ /* Services with detail text */ }
2026-05-25 22:54:38 +03:00
{ property . services && ( Array . isArray ( property . services ) ? property . services . length > 0 : Object . keys ( property . services ) . length > 0 ) && (
2026-05-25 22:42:53 +03:00
< div className = "mb-4" >
< h3 className = "font-bold text-gray-900 mb-2 text-sm" > الخدمات < / h 3 >
< div className = "flex flex-wrap gap-1.5" >
2026-05-25 22:54:38 +03:00
{ Array . isArray ( property . services ) ? (
property . services . map ( ( svc , i ) => (
< span key = { i } className = "px-3 py-1 bg-green-50 text-green-700 rounded-full text-sm border border-green-200 flex items-center gap-1" >
{ serviceLabels [ svc ] || svc }
2026-05-25 21:53:57 +03:00
< / s p a n >
2026-05-25 22:54:38 +03:00
) )
) : (
Object . entries ( property . services ) . map ( ( [ key , val ] ) => {
if ( ! val ) return null ;
2026-05-25 23:07:29 +03:00
const detail = typeof val === 'object' && val . detail ? val . detail : ( typeof val === 'string' ? val : null ) ;
2026-05-25 22:54:38 +03:00
return (
< span key = { key } className = "px-3 py-1 bg-green-50 text-green-700 rounded-full text-sm border border-green-200 flex items-center gap-1" >
{ serviceLabels [ key ] || key }
{ detail && < span className = "text-green-400" > · { detail } < / s p a n > }
< / s p a n >
) ;
} )
) }
2026-05-25 21:53:57 +03:00
< / d i v >
< / d i v >
) }
{ /* Room Details (only for room type) */ }
{ isRoomType && Object . keys ( property . roomDetails ) . length > 0 && (
2026-05-25 22:42:53 +03:00
< div className = "mb-4 bg-blue-50 rounded-xl p-3" >
< h3 className = "font-bold text-gray-900 mb-2 text-sm flex items-center gap-2" >
< Info className = "w-4 h-4 text-blue-500" / >
2026-05-25 21:53:57 +03:00
تفاصيل الغرفة
< / h 3 >
2026-05-25 22:42:53 +03:00
< div className = "grid grid-cols-2 md:grid-cols-3 gap-2" >
2026-05-25 22:14:37 +03:00
{ ( ( ) => {
const rd = property . roomDetails ;
const items = [ ] ;
if ( rd . areaType ) items . push ( { label : 'نوع المساحة' , value : rd . areaType === 'private room' ? 'غرفة خاصة' : rd . areaType === 'shared room' ? 'غرفة مشتركة' : rd . areaType } ) ;
if ( rd . peopleAllowed ) items . push ( { label : 'عدد الأشخاص' , value : rd . peopleAllowed } ) ;
if ( rd . furnitureDetails || rd . furniture ) items . push ( { label : 'الأثاث' , value : rd . furnitureDetails || rd . furniture } ) ;
if ( rd . entranceType ) items . push ( { label : 'نوع المدخل' , value : rd . entranceType === 'shared entrance' ? 'مدخل مشترك' : rd . entranceType === 'independent entrance' ? 'مدخل مستقل' : rd . entranceType } ) ;
if ( rd . bathroomType ) items . push ( { label : 'الحمام' , value : rd . bathroomType === 'room specific' ? 'خاص بالغرفة' : rd . bathroomType === 'shared' ? 'مشترك' : rd . bathroomType } ) ;
if ( rd . kitchenType ) items . push ( { label : 'المطبخ' , value : rd . kitchenType === 'shared' ? 'مشترك' : rd . kitchenType === 'not available' ? 'غير متوفر' : rd . kitchenType } ) ;
if ( rd . homeResidentsCount ? ? rd . residents ) items . push ( { label : 'عدد السكان' , value : rd . homeResidentsCount ? ? rd . residents } ) ;
if ( rd . currentPopulationGender ) items . push ( { label : 'جنس السكان' , value : rd . currentPopulationGender === 'men' ? 'رجال' : rd . currentPopulationGender === 'women' ? 'نساء' : rd . currentPopulationGender === 'family' ? 'عائلة' : rd . currentPopulationGender } ) ;
if ( rd . dedicatedTo ) items . push ( { label : 'مخصص لـ' , value : rd . dedicatedTo === 'men only' ? 'رجال فقط' : rd . dedicatedTo === 'women only' ? 'نساء فقط' : rd . dedicatedTo === 'families only' ? 'عائلات فقط' : rd . dedicatedTo === 'everyone' ? 'الجميع' : rd . dedicatedTo } ) ;
if ( rd . hasRestrictedOwnerAreas !== undefined ) items . push ( { label : 'مناطق ممنوعة' , value : rd . hasRestrictedOwnerAreas ? 'نعم' : 'لا' } ) ;
if ( rd . hasChildren !== undefined ) items . push ( { label : 'أطفال' , value : rd . hasChildren ? 'مسموح' : 'غير مسموح' } ) ;
if ( rd . hasPets !== undefined ) items . push ( { label : 'حيوانات أليفة' , value : rd . hasPets ? 'مسموح' : 'غير مسموح' } ) ;
if ( rd . languageDialect ) items . push ( { label : 'اللغة' , value : rd . languageDialect } ) ;
if ( rd . visitorsAllowed !== undefined ) items . push ( { label : 'الزوار' , value : rd . visitorsAllowed ? 'مسموح' : 'ممنوع' } ) ;
if ( rd . quietTimesEnabled ? ? rd . quietTimes ) items . push ( { label : 'أوقات الهدوء' , value : rd . quietTimesDetails || rd . quietTimes || ( rd . quietTimesEnabled ? 'مفعلة' : '' ) } ) ;
return items . map ( ( item , i ) => (
< div key = { i } className = "bg-white rounded-lg p-2 text-center" >
< div className = "text-xs text-gray-500" > { item . label } < / d i v >
< div className = "font-medium text-sm" > { item . value } < / d i v >
< / d i v >
) ) ;
} ) ( ) }
2026-05-25 21:53:57 +03:00
< / d i v >
< / d i v >
) }
{ /* Proximity */ }
{ Object . keys ( property . proximity ) . length > 0 && (
2026-05-25 22:42:53 +03:00
< div className = "mb-4" >
< h3 className = "font-bold text-gray-900 mb-2 text-sm" > القرب من الخدمات < / h 3 >
< div className = "grid grid-cols-2 md:grid-cols-3 gap-1.5" >
2026-05-25 21:53:57 +03:00
{ Object . entries ( property . proximity ) . map ( ( [ key , val ] ) => {
if ( ! val ) return null ;
const dist = typeof val === 'object' ? val . distance : val ;
return (
2026-05-25 22:42:53 +03:00
< div key = { key } className = "bg-gray-50 rounded-lg p-2 flex items-center gap-1.5" >
{ key === 'School' && < School className = "w-3.5 h-3.5 text-amber-500 flex-shrink-0" / > }
{ key === 'Hospital' && < Hospital className = "w-3.5 h-3.5 text-amber-500 flex-shrink-0" / > }
{ key === 'Restaurant' && < Store className = "w-3.5 h-3.5 text-amber-500 flex-shrink-0" / > }
{ key === 'University' && < GraduationCap className = "w-3.5 h-3.5 text-amber-500 flex-shrink-0" / > }
{ key === 'Park' && < TreePine className = "w-3.5 h-3.5 text-amber-500 flex-shrink-0" / > }
{ key === 'Mall' && < Building className = "w-3.5 h-3.5 text-amber-500 flex-shrink-0" / > }
{ ! [ 'School' , 'Hospital' , 'Restaurant' , 'University' , 'Park' , 'Mall' ] . includes ( key ) && < MapPin className = "w-3.5 h-3.5 text-amber-500 flex-shrink-0" / > }
2026-05-25 21:53:57 +03:00
< div >
2026-05-25 22:42:53 +03:00
< div className = "text-xs font-medium text-gray-900" > { proximityLabels [ key ] || key } < / d i v >
< div className = "text-[10px] text-gray-500" > { dist } { typeof dist === 'number' ? 'كم' : '' } < / d i v >
2026-05-25 21:53:57 +03:00
< / d i v >
< / d i v >
) ;
2026-05-25 21:27:39 +03:00
} ) }
< / d i v >
< / d i v >
) }
2026-05-25 21:53:57 +03:00
{ /* Terms as checklist */ }
2026-05-25 21:27:39 +03:00
{ Object . keys ( property . terms ) . length > 0 && (
2026-05-25 22:42:53 +03:00
< div className = "mb-4" >
< h3 className = "font-bold text-gray-900 mb-2 text-sm" > الشروط < / h 3 >
< div className = "grid grid-cols-1 md:grid-cols-2 gap-1.5" >
2026-05-25 21:27:39 +03:00
{ Object . entries ( property . terms ) . map ( ( [ key , val ] ) => {
if ( ! val ) return null ;
2026-05-25 21:53:57 +03:00
return (
2026-05-25 22:42:53 +03:00
< div key = { key } className = "flex items-center gap-1.5 p-1.5 bg-gray-50 rounded-lg" >
2026-05-25 21:53:57 +03:00
{ key . startsWith ( 'No' ) || key . startsWith ( 'Only' ) ? (
2026-05-25 22:42:53 +03:00
< Ban className = "w-3.5 h-3.5 text-red-500 flex-shrink-0" / >
2026-05-25 21:53:57 +03:00
) : (
2026-05-25 22:42:53 +03:00
< Check className = "w-3.5 h-3.5 text-green-500 flex-shrink-0" / >
2026-05-25 21:53:57 +03:00
) }
2026-05-25 22:42:53 +03:00
< span className = "text-xs text-gray-700" > { termLabels [ key ] || key } < / s p a n >
2026-05-25 21:53:57 +03:00
< / d i v >
) ;
2026-05-25 21:27:39 +03:00
} ) }
< / d i v >
< / d i v >
) }
< / m o t i o n . d i v >
{ /* Map */ }
{ property . location . lat && property . location . lng && (
2026-05-25 21:53:57 +03:00
< motion . div initial = { { opacity : 0 , y : 20 } } animate = { { opacity : 1 , y : 0 } } className = "bg-white rounded-2xl overflow-hidden shadow-sm border border-gray-200" >
< div className = "h-64" >
< MapContainer center = { [ property . location . lat , property . location . lng ] } zoom = { 14 } className = "h-full w-full" scrollWheelZoom = { false } >
< TileLayer url = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" / >
< Marker position = { [ property . location . lat , property . location . lng ] } >
< Popup > { property . title } < / P o p u p >
< / M a r k e r >
< / M a p C o n t a i n e r >
< / d i v >
< div className = "p-3 bg-amber-50 text-center text-sm text-amber-700 flex items-center justify-center gap-2" >
< Info className = "w-4 h-4" / >
< span > موقع تقريبي — يظهر الموقع الدقيق بعد تأكيد الحجز < / s p a n >
< / d i v >
< div className = "p-3 border-t border-gray-100" >
< a href = { ` https://www.google.com/maps?q= ${ property . location . lat } , ${ property . location . lng } ` } target = "_blank" rel = "noopener noreferrer"
className = "flex items-center justify-center gap-2 text-blue-600 hover:text-blue-700 font-medium text-sm" >
< ExternalLink className = "w-4 h-4" / >
فتح في Google Maps
< / a >
< / d i v >
2026-05-25 21:27:39 +03:00
< / m o t i o n . d i v >
) }
{ /* Ratings Section */ }
< motion . div initial = { { opacity : 0 , y : 20 } } animate = { { opacity : 1 , y : 0 } } >
< PropertyRatingList propertyId = { property . _raw ? . propertyInformationId || property . id } / >
< / m o t i o n . d i v >
{ AuthService . isAuthenticated ( ) && ! showRatingForm && (
< motion . div initial = { { opacity : 0 , y : 20 } } animate = { { opacity : 1 , y : 0 } }
className = "bg-amber-50 border-2 border-dashed border-amber-200 rounded-2xl p-6 text-center cursor-pointer hover:bg-amber-100 transition-colors"
onClick = { ( ) => setShowRatingForm ( true ) } >
< Star className = "w-10 h-10 text-amber-500 mx-auto mb-2" / >
< h3 className = "font-bold text-amber-800 text-lg mb-1" > قي ّ م هذا العقار < / h 3 >
< p className = "text-amber-600 text-sm" > شارك تجربتك مع المستأجرين الآخرين < / p >
< / m o t i o n . d i v >
2026-03-30 13:44:52 +00:00
) }
2026-04-12 20:44:04 +00:00
2026-05-25 21:27:39 +03:00
{ showRatingForm && (
2026-04-26 13:46:30 +03:00
< PropertyRatingForm
2026-05-25 21:27:39 +03:00
reservationId = { property . _raw ? . propertyInformationId || property . id }
onSuccess = { handleRatingSuccess }
2026-04-26 13:46:30 +03:00
onCancel = { ( ) => setShowRatingForm ( false ) }
2026-04-12 20:44:04 +00:00
/ >
2026-04-26 13:46:30 +03:00
) }
2026-03-30 13:44:52 +00:00
< / d i v >
2026-05-25 21:27:39 +03:00
{ /* Sidebar */ }
2026-05-25 22:42:53 +03:00
< div className = "space-y-4" >
2026-05-26 18:43:47 +03:00
{ /* Debug: Owner Check Info */ }
{ typeof window !== 'undefined' && (
< details className = "bg-red-50 p-2 rounded-xl text-[10px] text-red-700 border border-red-200" >
< summary className = "cursor-pointer font-bold" > 🔍 Owner Debug < / s u m m a r y >
< div className = "mt-1 space-y-0.5" >
< p > isOwnProperty : < b > { String ( isOwnProperty ) } < / b > < / p >
2026-05-26 19:04:57 +03:00
< p > prop ownerId : < b > { String ( property . ownerId ? ? 'null' ) } < / b > < / p >
< p > user id ( JWT ) : < b > { String ( AuthService . getUser ( ) ? . id ? ? 'null' ) } < / b > < / p >
< p > user name : < b > { String ( AuthService . getUser ( ) ? . name ? ? 'null' ) } < / b > < / p >
< p > authenticated : < b > { String ( AuthService . isAuthenticated ( ) ) } < / b > | i s O w n e r : < b > { S t r i n g ( A u t h S e r v i c e . i s O w n e r ( ) ) } < / b > < / p >
2026-05-26 18:43:47 +03:00
< / d i v >
< / d e t a i l s >
) }
2026-05-25 21:27:39 +03:00
{ /* Booking Card */ }
{ property . isRent && (
2026-05-25 22:42:53 +03:00
< motion . div initial = { { opacity : 0 , x : 20 } } animate = { { opacity : 1 , x : 0 } } className = "bg-white rounded-2xl p-5 shadow-sm border border-gray-200 sticky top-6" >
2026-05-26 17:31:01 +03:00
{ isOwnProperty ? (
< div className = "text-center py-3" >
< div className = "w-14 h-14 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-2" >
< Home className = "w-7 h-7 text-gray-400" / >
< / d i v >
< h4 className = "font-bold text-gray-700 text-sm mb-1" > هذا عقارك < / h 4 >
< p className = "text-xs text-gray-500" > لا يمكنك حجز عقارك الخاص < / p >
< / d i v >
) : bookingSuccess ? (
2026-05-25 22:42:53 +03:00
< div className = "text-center py-3" >
< div className = "w-14 h-14 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-2" >
< Check className = "w-7 h-7 text-green-600" / >
2026-05-25 21:27:39 +03:00
< / d i v >
2026-05-25 22:42:53 +03:00
< h4 className = "font-bold text-green-700 text-sm mb-1" > تم إرسال طلب الحجز < / h 4 >
< p className = "text-xs text-gray-500" > سيتم مراجعة طلبك من قبل المالك < / p >
2026-03-30 13:44:52 +00:00
< / d i v >
2026-05-25 21:27:39 +03:00
) : (
< >
2026-05-26 16:38:16 +03:00
{ /* Pricing Mode Toggle */ }
{ showPricingToggle && (
< div className = "grid grid-cols-2 gap-2 mb-3" >
< button onClick = { ( ) => setPricingMode ( 'daily' ) }
className = { ` p-2.5 rounded-xl text-center border-2 transition-all ${ effectivePricingMode === 'daily' ? 'border-amber-500 bg-amber-50' : 'border-gray-200 hover:border-gray-300' } ` } >
< div className = "text-xs font-bold text-gray-900" > إيجار يومي < / d i v >
< div className = "text-sm font-bold text-amber-600" > { formatCurrency ( property . priceDisplay . daily ) } ل . س < / d i v >
< / b u t t o n >
< button onClick = { ( ) => setPricingMode ( 'monthly' ) }
className = { ` p-2.5 rounded-xl text-center border-2 transition-all ${ effectivePricingMode === 'monthly' ? 'border-amber-500 bg-amber-50' : 'border-gray-200 hover:border-gray-300' } ` } >
< div className = "text-xs font-bold text-gray-900" > إيجار شهري < / d i v >
< div className = "text-sm font-bold text-amber-600" > { formatCurrency ( property . priceDisplay . monthly ) } ل . س < / d i v >
< / b u t t o n >
2026-05-25 21:27:39 +03:00
< / d i v >
2026-05-26 16:38:16 +03:00
) }
{ /* Step Indicator */ }
< div className = "flex items-center justify-center gap-2 mb-3" >
< div className = { ` flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${ bookingStep === 'entry' ? 'bg-amber-100 text-amber-800' : 'bg-gray-100 text-gray-500' } ` } >
< div className = { ` w-1.5 h-1.5 rounded-full ${ bookingStep === 'entry' ? 'bg-amber-500' : 'bg-gray-400' } ` } / >
تحديد تاريخ البداية
< / d i v >
< div className = "text-gray-300 text-xs" > ← < / d i v >
< div className = { ` flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium ${ bookingStep === 'exit' ? 'bg-amber-100 text-amber-800' : 'bg-gray-100 text-gray-500' } ` } >
< div className = { ` w-1.5 h-1.5 rounded-full ${ bookingStep === 'exit' ? 'bg-amber-500' : 'bg-gray-400' } ` } / >
تحديد تاريخ النهاية
2026-04-28 13:17:57 -07:00
< / d i v >
2026-03-30 13:44:52 +00:00
< / d i v >
2026-04-28 13:17:57 -07:00
2026-05-26 16:38:16 +03:00
{ /* Calendar */ }
{ effectivePricingMode === 'daily' ? (
< div className = "mb-3" >
< div className = "flex items-center justify-between mb-2" >
< button onClick = { ( ) => navigateMonth ( - 1 ) } className = "p-1 rounded-lg hover:bg-gray-100 transition-colors" >
< ChevronRight className = "w-4 h-4 text-gray-600" / >
< / b u t t o n >
< span className = "text-sm font-bold text-gray-900" > { MONTHS _AR [ calendarMonth ] } { calendarYear } < / s p a n >
< button onClick = { ( ) => navigateMonth ( 1 ) } className = "p-1 rounded-lg hover:bg-gray-100 transition-colors" >
< ChevronLeft className = "w-4 h-4 text-gray-600" / >
< / b u t t o n >
< / d i v >
< div className = "grid grid-cols-7 mb-1" >
{ DAYS _AR . map ( ( d , i ) => (
< div key = { i } className = "text-center text-[10px] text-gray-400 font-medium py-1" > { d } < / d i v >
) ) }
< / d i v >
{ ( ( ) => {
const firstDay = new Date ( calendarYear , calendarMonth , 1 ) . getDay ( ) ;
const daysInMonth = new Date ( calendarYear , calendarMonth + 1 , 0 ) . getDate ( ) ;
const adjustedFirstDay = ( firstDay + 1 ) % 7 ;
const cells = [ ] ;
for ( let i = 0 ; i < adjustedFirstDay ; i ++ ) {
cells . push ( < div key = { ` e- ${ i } ` } / > ) ;
}
for ( let day = 1 ; day <= daysInMonth ; day ++ ) {
const dateStr = ` ${ calendarYear } - ${ String ( calendarMonth + 1 ) . padStart ( 2 , '0' ) } - ${ String ( day ) . padStart ( 2 , '0' ) } ` ;
const past = isPastDate ( dateStr ) ;
const available = isDateAvailable ( dateStr ) ;
const isSelStart = dateStr === selectedStart ;
const isSelEnd = dateStr === selectedEnd ;
const inRange = selectedStart && selectedEnd && new Date ( dateStr ) > new Date ( selectedStart ) && new Date ( dateStr ) < new Date ( selectedEnd ) ;
const disabled = past || ! available ;
cells . push (
< button key = { dateStr } onClick = { ( ) => ! disabled && handleDayClick ( dateStr ) } disabled = { disabled }
className = { ` text-center py-1.5 text-xs rounded-lg transition-all ${ disabled ? 'text-gray-300 cursor-not-allowed' : '' } ${ isSelStart || isSelEnd ? 'bg-amber-500 text-white font-bold shadow-sm' : '' } ${ inRange ? 'bg-amber-100 text-amber-800' : '' } ${ ! disabled && ! isSelStart && ! isSelEnd && ! inRange && available ? 'hover:bg-amber-50 text-gray-700' : '' } ${ ! disabled && ! isSelStart && ! isSelEnd && ! inRange && ! available ? 'text-red-300' : '' } ` } >
{ day }
< / b u t t o n >
) ;
}
return < div className = "grid grid-cols-7 gap-0.5" > { cells } < / d i v > ;
} ) ( ) }
< / d i v >
) : (
< div className = "mb-3" >
< div className = "flex items-center justify-between mb-2" >
< button onClick = { ( ) => setCalendarYear ( prev => prev - 1 ) } className = "p-1 rounded-lg hover:bg-gray-100 transition-colors" >
< ChevronRight className = "w-4 h-4 text-gray-600" / >
< / b u t t o n >
< span className = "text-sm font-bold text-gray-900" > { calendarYear } < / s p a n >
< button onClick = { ( ) => setCalendarYear ( prev => prev + 1 ) } className = "p-1 rounded-lg hover:bg-gray-100 transition-colors" >
< ChevronLeft className = "w-4 h-4 text-gray-600" / >
< / b u t t o n >
< / d i v >
< div className = "grid grid-cols-3 gap-2" >
{ MONTHS _AR . map ( ( name , idx ) => {
const monthStr = ` ${ calendarYear } - ${ String ( idx + 1 ) . padStart ( 2 , '0' ) } ` ;
const isSelStart = selectedStart && selectedStart . startsWith ( monthStr ) ;
const isSelEnd = selectedEnd && selectedEnd . startsWith ( monthStr ) ;
const inRange = selectedStart && selectedEnd && monthStr > selectedStart . substring ( 0 , 7 ) && monthStr < selectedEnd . substring ( 0 , 7 ) ;
return (
< button key = { idx } onClick = { ( ) => {
const firstDay = ` ${ monthStr } -01 ` ;
if ( bookingStep === 'entry' || ( selectedStart && monthStr <= selectedStart . substring ( 0 , 7 ) ) ) {
setSelectedStart ( firstDay ) ;
setSelectedEnd ( null ) ;
setBookingStep ( 'exit' ) ;
} else {
const lastDay = new Date ( calendarYear , idx + 1 , 0 ) . getDate ( ) ;
setSelectedEnd ( ` ${ monthStr } - ${ String ( lastDay ) . padStart ( 2 , '0' ) } ` ) ;
setBookingStep ( 'entry' ) ;
}
} }
className = { ` p-2.5 rounded-xl text-center text-xs font-medium transition-all border ${ isSelStart || isSelEnd ? 'bg-amber-500 text-white border-amber-500 shadow-sm' : inRange ? 'bg-amber-100 text-amber-800 border-amber-200' : 'bg-white text-gray-700 border-gray-200 hover:border-amber-300' } ` } >
{ name }
< / b u t t o n >
) ;
} ) }
< / d i v >
< / d i v >
) }
{ /* Summary */ }
{ selectedStart && (
< div className = "bg-gray-50 rounded-xl p-3 mb-3 space-y-1.5" >
< div className = "flex justify-between text-xs" >
< span className = "text-gray-500" > تاريخ البداية < / s p a n >
< span className = "font-medium text-gray-900" > { selectedStart } < / s p a n >
< / d i v >
{ selectedEnd && (
< >
< div className = "flex justify-between text-xs" >
< span className = "text-gray-500" > تاريخ النهاية < / s p a n >
< span className = "font-medium text-gray-900" > { selectedEnd } < / s p a n >
< / d i v >
< div className = "border-t border-gray-200 my-1" / >
< div className = "flex justify-between text-xs" >
< span className = "text-gray-500" > { effectivePricingMode === 'daily' ? 'عدد الأيام' : 'عدد الأشهر' } < / s p a n >
< span className = "font-medium text-gray-900" >
{ effectivePricingMode === 'daily'
? Math . max ( 1 , Math . round ( ( new Date ( selectedEnd ) - new Date ( selectedStart ) ) / ( 1000 * 60 * 60 * 24 ) ) + 1 )
: ( new Date ( selectedEnd ) . getMonth ( ) - new Date ( selectedStart ) . getMonth ( ) + ( new Date ( selectedEnd ) . getFullYear ( ) - new Date ( selectedStart ) . getFullYear ( ) ) * 12 ) + 1 }
< / s p a n >
< / d i v >
< div className = "flex justify-between text-xs font-bold" >
< span className = "text-gray-700" > المجموع < / s p a n >
< span className = "text-amber-600" >
{ formatCurrency ( effectivePricingMode === 'daily'
? Math . max ( 1 , Math . round ( ( new Date ( selectedEnd ) - new Date ( selectedStart ) ) / ( 1000 * 60 * 60 * 24 ) ) + 1 ) * property . priceDisplay . daily
: ( ( new Date ( selectedEnd ) . getMonth ( ) - new Date ( selectedStart ) . getMonth ( ) + ( new Date ( selectedEnd ) . getFullYear ( ) - new Date ( selectedStart ) . getFullYear ( ) ) * 12 ) + 1 ) * property . priceDisplay . monthly ) } ل . س
< / s p a n >
< / d i v >
{ property . deposit > 0 && (
< div className = "flex justify-between text-xs" >
< span className = "text-gray-500" > تأمين < / s p a n >
< span className = "font-medium text-gray-900" > { formatCurrency ( property . deposit ) } ل . س < / s p a n >
< / d i v >
) }
< / >
) }
< / d i v >
) }
2026-05-25 21:27:39 +03:00
{ bookingError && (
2026-05-25 22:42:53 +03:00
< div className = "bg-red-50 text-red-600 p-2.5 rounded-xl text-xs mb-3" > { bookingError } < / d i v >
2026-05-25 21:27:39 +03:00
) }
2026-03-30 13:44:52 +00:00
2026-05-26 16:38:16 +03:00
< button onClick = { handleBookingConfirm } disabled = { bookingLoading || ! selectedStart || ! selectedEnd }
2026-05-25 22:42:53 +03:00
className = "w-full bg-amber-500 hover:bg-amber-600 text-white py-2.5 rounded-xl font-bold text-sm transition-all disabled:opacity-50 flex items-center justify-center gap-2" >
{ bookingLoading ? < Loader2 className = "w-4 h-4 animate-spin" / > : < Calendar className = "w-4 h-4" / > }
2026-05-26 16:38:16 +03:00
{ bookingLoading ? 'جاري الحجز...' : 'تأكيد الحجز' }
2026-05-25 21:27:39 +03:00
< / b u t t o n >
< / >
2026-03-30 13:44:52 +00:00
) }
< / m o t i o n . d i v >
2026-05-25 21:27:39 +03:00
) }
2026-03-30 13:44:52 +00:00
2026-05-25 21:27:39 +03:00
{ /* Contact Card */ }
2026-05-26 18:01:18 +03:00
{ ! isOwnProperty && (
2026-05-25 22:42:53 +03:00
< motion . div initial = { { opacity : 0 , x : 20 } } animate = { { opacity : 1 , x : 0 } } className = "bg-white rounded-2xl p-5 shadow-sm border border-gray-200" >
< div className = "flex items-center gap-2 mb-3" >
< Phone className = "w-4 h-4 text-amber-500" / >
< h3 className = "font-bold text-gray-900" > معلومات المالك < / h 3 >
2026-05-25 21:53:57 +03:00
< / d i v >
2026-04-28 13:17:57 -07:00
2026-05-25 21:27:39 +03:00
{ showContact && contactInfo ? (
2026-05-25 22:42:53 +03:00
< div className = "space-y-2" >
< div className = "flex items-center gap-2.5 p-2.5 bg-gray-50 rounded-xl" >
< Phone className = "w-4 h-4 text-gray-600 flex-shrink-0" / >
< span className = "font-medium text-gray-900 text-sm" dir = "ltr" > { contactInfo . phone || contactInfo . phoneNumber || '—' } < / s p a n >
2026-03-30 13:44:52 +00:00
< / d i v >
2026-05-25 21:27:39 +03:00
{ contactInfo . whatsAppNumber && (
2026-05-25 21:53:57 +03:00
< a href = { ` https://wa.me/ ${ contactInfo . whatsAppNumber . replace ( /[^0-9]/g , '' ) } ` } target = "_blank" rel = "noopener noreferrer"
2026-05-25 22:42:53 +03:00
className = "flex items-center gap-2.5 p-2.5 bg-green-50 rounded-xl hover:bg-green-100 transition-colors" >
< MessageCircle className = "w-4 h-4 text-green-600 flex-shrink-0" / >
< span className = "font-medium text-gray-900 text-sm" dir = "ltr" > { contactInfo . whatsAppNumber } < / s p a n >
2026-05-25 21:53:57 +03:00
< / a >
2026-05-25 21:27:39 +03:00
) }
2026-03-30 13:44:52 +00:00
< / d i v >
2026-05-25 21:27:39 +03:00
) : (
< button onClick = { fetchContactInfo }
2026-05-25 22:42:53 +03:00
className = "w-full bg-gray-800 hover:bg-gray-900 text-white py-2.5 rounded-xl font-medium text-sm transition-colors flex items-center justify-center gap-2" >
< Phone className = "w-4 h-4" / >
2026-05-25 21:27:39 +03:00
عرض معلومات الاتصال
2026-03-30 13:44:52 +00:00
< / b u t t o n >
2026-05-25 21:27:39 +03:00
) }
< / m o t i o n . d i v >
2026-05-26 18:01:18 +03:00
) }
2026-03-30 13:44:52 +00:00
< / d i v >
< / d i v >
< / d i v >
2026-05-25 21:27:39 +03:00
{ /* Login Dialog */ }
2026-03-30 13:44:52 +00:00
{ showLoginDialog && (
2026-05-25 21:27:39 +03:00
< div className = "fixed inset-0 bg-black/50 flex items-center justify-center z-50" onClick = { ( ) => setShowLoginDialog ( false ) } >
< div className = "bg-white rounded-2xl p-6 max-w-sm text-center mx-4" onClick = { e => e . stopPropagation ( ) } >
2026-03-30 13:44:52 +00:00
< div className = "w-16 h-16 bg-amber-100 rounded-full flex items-center justify-center mx-auto mb-4" >
< LogIn className = "w-8 h-8 text-amber-600" / >
< / d i v >
2026-05-25 21:27:39 +03:00
< h3 className = "text-xl font-bold mb-2" > تسجيل الدخول مطلوب < / h 3 >
< p className = "text-gray-500 mb-4" > للحجز أو إضافة المفضلة ، سجل دخولك . < / p >
< Link href = "/login" className = "block w-full bg-amber-500 text-white py-3 rounded-xl font-medium mb-2 hover:bg-amber-600" > تسجيل الدخول < / L i n k >
< Link href = "/auth/choose-role" className = "block w-full bg-gray-100 py-3 rounded-xl font-medium hover:bg-gray-200" > إنشاء حساب < / L i n k >
< button onClick = { ( ) => setShowLoginDialog ( false ) } className = "mt-3 text-gray-400 hover:text-gray-600" > إلغاء < / b u t t o n >
2026-04-26 13:46:30 +03:00
< / d i v >
2026-03-30 13:44:52 +00:00
< / d i v >
) }
< / d i v >
) ;
2026-05-25 21:27:39 +03:00
}