diff --git a/app/property/[id]/PropertyDetail.js b/app/property/[id]/PropertyDetail.js
new file mode 100644
index 0000000..2366bec
--- /dev/null
+++ b/app/property/[id]/PropertyDetail.js
@@ -0,0 +1,908 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import { motion } from 'framer-motion';
+import toast, { Toaster } from 'react-hot-toast';
+import Image from 'next/image';
+import Link from 'next/link';
+import { useParams } from 'next/navigation';
+import {
+ MapPin,
+ Bed,
+ Bath,
+ Square,
+ DollarSign,
+ Heart,
+ Share2,
+ Phone,
+ Mail,
+ MessageCircle,
+ Calendar,
+ Shield,
+ Star,
+ ChevronLeft,
+ ChevronRight,
+ Check,
+ X,
+ Wifi,
+ Car,
+ Coffee,
+ Wind,
+ Thermometer,
+ Lock,
+ Camera,
+ Home,
+ Building2,
+ Users,
+ Ruler,
+ CalendarDays,
+ Clock,
+ Award,
+ FileText,
+ Printer,
+ Download,
+ ArrowLeft,
+ LogIn
+} from 'lucide-react';
+import { getRentProperty, getSaleProperty, bookReservation, checkAvailability, getAvailableDateRanges } from '../../utils/api';
+import AuthService from '../../services/AuthService';
+import { BuildingTypeKeys, PropertyStatusKeys, extractCity } from '../../enums';
+
+// Map API response to the UI format
+function mapApiDetail(item) {
+ if (!item) return null;
+
+ const info = item.propertyInformation || {};
+
+ const dailyPrice = item.dailyRent ?? item.monthlyRent ?? item.price ?? 0;
+ const monthlyPrice = item.monthlyRent ?? 0;
+
+ const propType = BuildingTypeKeys[info.buildingType] ?? BuildingTypeKeys[item.type] ?? 'apartment';
+ const status = PropertyStatusKeys[info.status] ?? PropertyStatusKeys[item.status] ?? 'available';
+
+ const features = [];
+ if (item.isSmokeAllow) features.push({ name: 'يسمح بالتدخين', available: true, description: '' });
+ if (item.isVisitorAllow) features.push({ name: 'يسمح بالزوار', available: true, description: '' });
+ if (item.specializedFor) features.push({ name: 'متخصص', available: true, description: '' });
+ if (info.numberOfBedRooms) features.push({ name: 'غرف النوم', available: true, description: `${info.numberOfBedRooms} غرف` });
+ if (info.numberOfBathRooms) features.push({ name: 'الحمامات', available: true, description: `${info.numberOfBathRooms} حمامات` });
+ if (info.space) features.push({ name: 'المساحة', available: true, description: `${info.space} م²` });
+
+ const typeLabels = { 0: 'شقة', 1: 'فيلا', 2: 'بيت' };
+
+ // Extract images from API and build full URLs
+ const apiBase = typeof window !== 'undefined' ? (process.env.NEXT_PUBLIC_API_URL || 'http://45.93.137.91/api') : '';
+ const rawImages = Array.isArray(info.images) ? info.images : [];
+ const images = rawImages.length > 0
+ ? rawImages.map(img => img.startsWith('http') ? img : `${apiBase}${img.startsWith('/') ? '' : '/'}${img}`)
+ : ['/property-placeholder.jpg', '/villa1.jpg', '/villa2.jpg'];
+
+ return {
+ id: item.id,
+ title: info.address || `عقار #${item.id}`,
+ description: info.description || 'عقار سكني مميز في موقع استراتيجي.',
+ type: propType,
+ price: dailyPrice,
+ priceUnit: 'daily',
+ location: {
+ city: extractCity(info.address) || 'دمشق',
+ district: info.address || '',
+ address: info.address || '',
+ lat: parseFloat(info.cordsX) || 0,
+ lng: parseFloat(info.cordsY) || 0,
+ },
+ bedrooms: info.numberOfBedRooms || 0,
+ bathrooms: info.numberOfBathRooms || 0,
+ area: info.space || 0,
+ features: features.length > 0 ? features : [
+ { name: 'متاح للإيجار', available: true, description: '' },
+ ],
+ images,
+ status,
+ rating: item.rating || 4.5,
+ reviews: 0,
+ reviewList: [],
+ owner: {
+ name: 'المالك',
+ phone: '—',
+ email: '—',
+ rating: 4.8,
+ properties: 1,
+ memberSince: '2024',
+ responseRate: '95%',
+ responseTime: 'خلال ساعات',
+ },
+ nearby: [],
+ specifications: {
+ constructionYear: null,
+ floor: '-',
+ parking: 0,
+ gardenArea: 0,
+ poolArea: 0,
+ furnished: false,
+ airConditioning: '-',
+ heating: '-',
+ electricity: '220V',
+ water: 'شبكة عامة',
+ },
+ rules: [],
+ _raw: item,
+ };
+}
+
+// extractCity is now imported from @/app/enums
+
+// API-only — no fallback data
+
+export default function PropertyDetailsPage() {
+ const params = useParams();
+ const [currentImage, setCurrentImage] = useState(0);
+ const [showContact, setShowContact] = useState(false);
+ const [bookingDates, setBookingDates] = useState({ start: '', end: '' });
+ const [selectedDuration, setSelectedDuration] = useState(1);
+ const [property, setProperty] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [bookingError, setBookingError] = useState(null);
+ const [bookingSuccess, setBookingSuccess] = useState(false);
+ const [availableRanges, setAvailableRanges] = useState([]);
+ const [calendarMonth, setCalendarMonth] = useState(new Date());
+ const [selectingEnd, setSelectingEnd] = useState(false);
+ const [showLoginDialog, setShowLoginDialog] = useState(false);
+
+ useEffect(() => {
+ const id = params.id;
+ setLoading(true);
+ setBookingError(null);
+ setBookingSuccess(false);
+
+ async function fetchProperty() {
+ try {
+ // Try RentProperties first, then SaleProperties
+ let data = null;
+ try {
+ data = await getRentProperty(id);
+ } catch {
+ try {
+ data = await getSaleProperty(id);
+ } catch {
+ // neither worked
+ }
+ }
+
+ if (data) {
+ const mapped = mapApiDetail(data);
+ if (mapped) {
+ setProperty(mapped);
+ setLoading(false);
+ return;
+ }
+ }
+ setProperty(null);
+ } catch (err) {
+ console.error('[Property] Failed to fetch property:', err);
+ setProperty(null);
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ fetchProperty();
+ }, [params.id]);
+
+ // Fetch available date ranges
+ useEffect(() => {
+ if (!property) return;
+ const propId = property._raw?.id || params.id;
+ console.log('[Property] Fetching available dates for:', propId);
+ getAvailableDateRanges(propId)
+ .then((data) => {
+ const ranges = Array.isArray(data) ? data : [];
+ console.log('[Property] Available date ranges:', ranges);
+ setAvailableRanges(ranges);
+ })
+ .catch((err) => {
+ console.warn('[Property] Failed to fetch available dates:', err);
+ });
+ }, [property, params.id]);
+
+ // Set Open Graph meta tags dynamically for Facebook/Twitter sharing
+ useEffect(() => {
+ if (!property) return;
+
+ const typeLabel = property.type === 'villa' ? 'فيلا' : property.type === 'apartment' ? 'شقة' : 'بيت';
+ const priceLabel = `${formatCurrency(property.price)} / ${property.priceUnit === 'daily' ? 'يوم' : 'شهر'}`;
+ const desc = `${typeLabel} في ${property.location?.address || ''} · ${property.bedrooms} غرف نوم · ${property.bathrooms} حمامات · ${property.area} م²`;
+ const imageUrl = property.images?.[0]
+ ? (property.images[0].startsWith('http') ? property.images[0] : `http://45.93.137.91${property.images[0]}`)
+ : '';
+
+ const setMeta = (prop, content) => {
+ let tag = document.querySelector(`meta[property="${prop}"]`);
+ if (!tag) {
+ tag = document.createElement('meta');
+ tag.setAttribute('property', prop);
+ document.head.appendChild(tag);
+ }
+ tag.setAttribute('content', content);
+ };
+
+ const setMetaName = (name, content) => {
+ let tag = document.querySelector(`meta[name="${name}"]`);
+ if (!tag) {
+ tag = document.createElement('meta');
+ tag.setAttribute('name', name);
+ document.head.appendChild(tag);
+ }
+ tag.setAttribute('content', content);
+ };
+
+ setMeta('og:title', `${property.title} - ${priceLabel}`);
+ setMeta('og:description', desc);
+ if (imageUrl) setMeta('og:image', imageUrl);
+ setMeta('og:url', window.location.href);
+ setMeta('og:type', 'website');
+ setMeta('og:site_name', 'SweetHome');
+
+ // Twitter cards
+ setMetaName('twitter:card', 'summary_large_image');
+ setMetaName('twitter:title', `${property.title} - ${priceLabel}`);
+ setMetaName('twitter:description', desc);
+ if (imageUrl) setMetaName('twitter:image', imageUrl);
+ }, [property]);
+
+ const formatCurrency = (amount) => {
+ return amount?.toLocaleString() + ' ل.س';
+ };
+
+ const calculateTotalPrice = () => {
+ if (!property) return 0;
+ const days = bookingDates.start && bookingDates.end
+ ? Math.ceil((new Date(bookingDates.end) - new Date(bookingDates.start)) / (1000 * 60 * 60 * 24))
+ : selectedDuration;
+ return property.price * (days > 0 ? days : 1);
+ };
+
+ // Calendar helpers
+ const isDateAvailable = (dateStr) => {
+ const d = new Date(dateStr + 'T00:00:00');
+ return availableRanges.some((range) => {
+ const start = new Date(range.startDate);
+ const end = new Date(range.endDate);
+ return d >= start && d <= end;
+ });
+ };
+
+ const isInRange = (dateStr) => {
+ if (!bookingDates.start) return false;
+ const d = new Date(dateStr + 'T00:00:00');
+ const start = new Date(bookingDates.start + 'T00:00:00');
+ const end = bookingDates.end ? new Date(bookingDates.end + 'T00:00:00') : start;
+ return d >= start && d <= end;
+ };
+
+ const isRangeFullyAvailable = (startStr, endStr) => {
+ const start = new Date(startStr + 'T00:00:00');
+ const end = new Date(endStr + 'T00:00:00');
+ for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
+ if (!isDateAvailable(d.toISOString().split('T')[0])) return false;
+ }
+ return true;
+ };
+
+ const handleCalendarClick = (dateStr) => {
+ if (!isDateAvailable(dateStr)) return;
+
+ if (!bookingDates.start || selectingEnd) {
+ if (!bookingDates.start) {
+ setBookingDates({ start: dateStr, end: '' });
+ setSelectingEnd(true);
+ } else {
+ const start = bookingDates.start;
+ const end = dateStr;
+ const [s, e] = end > start ? [start, end] : [end, start];
+ if (isRangeFullyAvailable(s, e)) {
+ setBookingDates({ start: s, end: e });
+ setSelectingEnd(false);
+ } else {
+ toast.error('بعض التواريخ في هذه الفترة غير متاحة');
+ }
+ }
+ } else {
+ setBookingDates({ start: dateStr, end: '' });
+ setSelectingEnd(true);
+ }
+ };
+
+ const getDaysInMonth = (year, month) => new Date(year, month + 1, 0).getDate();
+ const getFirstDayOfMonth = (year, month) => new Date(year, month, 1).getDay();
+
+ const formatDateStr = (year, month, day) => {
+ return `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
+ };
+
+ const monthNames = ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'];
+ const dayNames = ['أح', 'إث', 'ثل', 'أر', 'خم', 'جم', 'سب'];
+
+ const handleBooking = async () => {
+ if (!AuthService.isAuthenticated()) {
+ setShowLoginDialog(true);
+ return;
+ }
+
+ setBookingError(null);
+ setBookingSuccess(false);
+
+ if (!bookingDates.start || !bookingDates.end) {
+ setBookingError('يرجى اختيار تاريخ البداية والنهاية');
+ return;
+ }
+
+ const propId = property?._raw?.id || parseInt(params.id);
+ const startDate = new Date(bookingDates.start).toISOString();
+ const endDate = new Date(bookingDates.end).toISOString();
+
+ console.log('[Booking] Reserving:', { propertyId: propId, startDate, endDate });
+
+ try {
+ const res = await bookReservation(propId, startDate, endDate);
+ console.log('[Booking] Success:', res);
+ setBookingSuccess(true);
+ toast.success('تم إرسال طلب الحجز بنجاح!');
+ } catch (err) {
+ console.error('[Booking] Failed:', err);
+ setBookingError(err.message || 'فشل في إرسال طلب الحجز');
+ }
+ };
+
+ if (loading) {
+ return (
+
+
+
+
جاري تحميل تفاصيل العقار...
+
+
+ );
+ }
+
+ if (!property) {
+ return (
+
+
+
+
العقار غير موجود
+
لم نتمكن من العثور على العقار المطلوب
+
+ العودة إلى العقارات
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
العودة إلى العقارات
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {property.images.length > 1 && (
+ <>
+
+
+ >
+ )}
+
+
+ {property.images.map((_, idx) => (
+
+
+
+
+ {currentImage + 1} / {property.images.length}
+
+
+
+
+ {property.images.slice(1, 5).map((img, idx) => (
+
setCurrentImage(idx + 1)}
+ className="relative h-[240px] rounded-2xl overflow-hidden cursor-pointer hover:opacity-90 transition-opacity bg-gray-100"
+ >
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
{property.title}
+
+
+ {property.location.address}
+
+
+
+
{formatCurrency(property.price)}
+
/{property.priceUnit === 'daily' ? 'يوم' : 'شهر'}
+
+
+
+
+
+
+ {property.rating}
+ {property.reviews > 0 && ({property.reviews} تقييم)}
+
+
+
+ {property.status === 'available' ? 'متاح للإيجار' : 'محجوز حالياً'}
+
+
+
+
+
+ المواصفات الرئيسية
+
+
+
+
{property.bedrooms}
+
غرف نوم
+
+
+
+
{property.bathrooms}
+
حمامات
+
+
+
+
{property.area}
+
م²
+
+
+
+
+ {property.type === 'villa' ? 'فيلا' :
+ property.type === 'apartment' ? 'شقة' : 'بيت'}
+
+
نوع العقار
+
+
+
+
+
+ وصف العقار
+ {property.description || 'لا يوجد وصف متاح.'}
+
+
+
+ المميزات والخدمات
+
+ {property.features.map((feature, idx) => (
+
+
+ {feature.available ? (
+
+ ) : (
+
+ )}
+
+
+
+ {feature.name}
+
+ {feature.description && (
+
+ {feature.description}
+
+ )}
+
+
+ ))}
+
+
+
+ {property.reviewList && property.reviewList.length > 0 && (
+
+ تقييمات المستأجرين
+
+ {property.reviewList.map((review, idx) => (
+
+
+
+
{review.user}
+
+ {[...Array(5)].map((_, i) => (
+
+ ))}
+
+
+
{review.date}
+
+
{review.comment}
+
+ ))}
+
+
+ )}
+
+ {property.rules && property.rules.length > 0 && (
+
+ قوانين المنزل
+
+ {property.rules.map((rule, idx) => (
+ -
+
+ {rule}
+
+ ))}
+
+
+ )}
+
+
+
+
+
+ احجز هذا العقار
+
+ {/* Selected dates display */}
+
+
+ من
+ {bookingDates.start || '—'}
+
+
+ إلى
+ {bookingDates.end || '—'}
+
+
+
+ {bookingDates.start && bookingDates.end && (() => {
+ const days = Math.ceil((new Date(bookingDates.end) - new Date(bookingDates.start)) / (1000 * 60 * 60 * 24));
+ return days > 0 ? (
+
+ {days} يوم{days > 1 ? 'اً' : 'اً'} {selectingEnd ? '— اضغط على تاريخ النهاية' : '✓'}
+
+ ) : null;
+ })()}
+
+ {/* Calendar */}
+
+
+
+ {monthNames[calendarMonth.getMonth()]} {calendarMonth.getFullYear()}
+
+
+
+
+ {dayNames.map((d) => (
+
{d}
+ ))}
+
+
+
+ {(() => {
+ const year = calendarMonth.getFullYear();
+ const month = calendarMonth.getMonth();
+ const daysInMonth = getDaysInMonth(year, month);
+ const firstDay = getFirstDayOfMonth(year, month);
+ const today = new Date().toISOString().split('T')[0];
+ const cells = [];
+
+ // Empty cells before first day
+ for (let i = 0; i < firstDay; i++) {
+ cells.push(
);
+ }
+
+ for (let day = 1; day <= daysInMonth; day++) {
+ const dateStr = formatDateStr(year, month, day);
+ const available = isDateAvailable(dateStr);
+ const isStart = bookingDates.start === dateStr;
+ const isEnd = bookingDates.end === dateStr;
+ const inRange = isInRange(dateStr);
+ const isPast = dateStr < today;
+
+ cells.push(
+
+ );
+ }
+ return cells;
+ })()}
+
+
+
+
+
+
+ {(() => {
+ const days = bookingDates.start && bookingDates.end
+ ? Math.ceil((new Date(bookingDates.end) - new Date(bookingDates.start)) / (1000 * 60 * 60 * 24))
+ : 0;
+ const effectiveDays = days > 0 ? days : 1;
+ return (
+ <>
+
+ السعر لـ {effectiveDays} يوم{effectiveDays > 1 ? 'اً' : 'اً'}
+ {formatCurrency(property.price * effectiveDays)}
+
+
+ سلفة ضمان
+ {formatCurrency(property._raw?.deposit || 0)}
+
+
+ الإجمالي
+ {formatCurrency(property.price * effectiveDays + (property._raw?.deposit || 0))}
+
+ >
+ );
+ })()}
+
+
+ {bookingError && (
+
+ {bookingError}
+
+ )}
+
+ {bookingSuccess && (
+
+ تم إرسال طلب الحجز بنجاح. سيتم التواصل معك قريباً.
+
+ )}
+
+
+
+
+
+ الدفع آمن ومضمون. سلفة الضمان قابلة للاسترداد.
+
+
+
+
+ معلومات المالك
+
+
+
+ {property.owner.name.charAt(0)}
+
+
+
+
{property.owner.name}
+
+
+ {property.owner.rating}
+ · {property.owner.properties} عقارات
+
+ {property.owner.responseRate && (
+
+
+ استجابة: {property.owner.responseRate}
+
+ )}
+
+
+
+ {showContact ? (
+
+
+
+
{property.owner.phone}
+
+
+
+ {property.owner.email}
+
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+ {/* Login/Register Dialog */}
+ {showLoginDialog && (
+
setShowLoginDialog(false)}>
+
e.stopPropagation()}
+ className="bg-white rounded-2xl p-8 max-w-md w-full mx-4 shadow-2xl text-center"
+ >
+
+
+
+ سجّل الدخول للمتابعة
+ يجب عليك إنشاء حساب أو تسجيل الدخول لحجز هذا العقار
+
+
+ إنشاء حساب جديد
+
+
+ تسجيل الدخول
+
+
+
+
+
+ )}
+
+ );
+}
diff --git a/app/property/[id]/page.js b/app/property/[id]/page.js
index 2366bec..993d40b 100644
--- a/app/property/[id]/page.js
+++ b/app/property/[id]/page.js
@@ -1,908 +1,90 @@
-'use client';
+import PropertyDetail from './PropertyDetail';
-import { useState, useEffect } from 'react';
-import { motion } from 'framer-motion';
-import toast, { Toaster } from 'react-hot-toast';
-import Image from 'next/image';
-import Link from 'next/link';
-import { useParams } from 'next/navigation';
-import {
- MapPin,
- Bed,
- Bath,
- Square,
- DollarSign,
- Heart,
- Share2,
- Phone,
- Mail,
- MessageCircle,
- Calendar,
- Shield,
- Star,
- ChevronLeft,
- ChevronRight,
- Check,
- X,
- Wifi,
- Car,
- Coffee,
- Wind,
- Thermometer,
- Lock,
- Camera,
- Home,
- Building2,
- Users,
- Ruler,
- CalendarDays,
- Clock,
- Award,
- FileText,
- Printer,
- Download,
- ArrowLeft,
- LogIn
-} from 'lucide-react';
-import { getRentProperty, getSaleProperty, bookReservation, checkAvailability, getAvailableDateRanges } from '../../utils/api';
-import AuthService from '../../services/AuthService';
-import { BuildingTypeKeys, PropertyStatusKeys, extractCity } from '../../enums';
+// Server-side API fetch for metadata (runs at request time on server)
+async function fetchPropertyForMeta(id) {
+ try {
+ const res = await fetch(`http://45.93.137.91/api/RentProperties/GetRentProperties`, {
+ next: { revalidate: 60 },
+ });
+ if (!res.ok) return null;
+ const text = await res.text();
+ const json = JSON.parse(text);
+ const items = Array.isArray(json?.data) ? json.data : Array.isArray(json) ? json : [];
+ return items.find(p => p.id == id) || items[0] || null;
+ } catch {
+ return null;
+ }
+}
-// Map API response to the UI format
-function mapApiDetail(item) {
- if (!item) return null;
+function mapProperty(item) {
+ const info = item.propertyInformation || item.PropertyInformation || {};
+ let details = {};
+ try { details = JSON.parse(info.detailsJSON || info.DetailsJSON || '{}'); } catch {}
- const info = item.propertyInformation || {};
-
- const dailyPrice = item.dailyRent ?? item.monthlyRent ?? item.price ?? 0;
- const monthlyPrice = item.monthlyRent ?? 0;
-
- const propType = BuildingTypeKeys[info.buildingType] ?? BuildingTypeKeys[item.type] ?? 'apartment';
- const status = PropertyStatusKeys[info.status] ?? PropertyStatusKeys[item.status] ?? 'available';
-
- const features = [];
- if (item.isSmokeAllow) features.push({ name: 'يسمح بالتدخين', available: true, description: '' });
- if (item.isVisitorAllow) features.push({ name: 'يسمح بالزوار', available: true, description: '' });
- if (item.specializedFor) features.push({ name: 'متخصص', available: true, description: '' });
- if (info.numberOfBedRooms) features.push({ name: 'غرف النوم', available: true, description: `${info.numberOfBedRooms} غرف` });
- if (info.numberOfBathRooms) features.push({ name: 'الحمامات', available: true, description: `${info.numberOfBathRooms} حمامات` });
- if (info.space) features.push({ name: 'المساحة', available: true, description: `${info.space} م²` });
-
- const typeLabels = { 0: 'شقة', 1: 'فيلا', 2: 'بيت' };
-
- // Extract images from API and build full URLs
- const apiBase = typeof window !== 'undefined' ? (process.env.NEXT_PUBLIC_API_URL || 'http://45.93.137.91/api') : '';
- const rawImages = Array.isArray(info.images) ? info.images : [];
- const images = rawImages.length > 0
- ? rawImages.map(img => img.startsWith('http') ? img : `${apiBase}${img.startsWith('/') ? '' : '/'}${img}`)
- : ['/property-placeholder.jpg', '/villa1.jpg', '/villa2.jpg'];
+ const price = item.monthlyRent || item.MonthlyRent || item.dailyRent || item.DailyRent || 0;
+ const priceUnit = item.monthlyRent || item.MonthlyRent ? 'monthly' : 'daily';
+ const buildingType = info.buildingType ?? info.BuildingType ?? 0;
+ const type = { 0: 'apartment', 1: 'villa', 2: 'house' }[buildingType] || 'apartment';
+ const typeLabel = { 0: 'شقة', 1: 'فيلا', 2: 'بيت' }[buildingType] || 'عقار';
+ const address = info.address || info.Address || '';
+ const bedrooms = info.numberOfBedRooms || info.NumberOfBedRooms || 0;
+ const bathrooms = info.numberOfBathRooms || info.NumberOfBathRooms || 0;
+ const area = info.space || info.Space || 0;
+ const desc = info.description || info.Description || '';
+ const images = info.images || info.Images || [];
+ const firstImage = Array.isArray(images) && images[0] ? images[0] : '';
return {
- id: item.id,
- title: info.address || `عقار #${item.id}`,
- description: info.description || 'عقار سكني مميز في موقع استراتيجي.',
- type: propType,
- price: dailyPrice,
- priceUnit: 'daily',
- location: {
- city: extractCity(info.address) || 'دمشق',
- district: info.address || '',
- address: info.address || '',
- lat: parseFloat(info.cordsX) || 0,
- lng: parseFloat(info.cordsY) || 0,
- },
- bedrooms: info.numberOfBedRooms || 0,
- bathrooms: info.numberOfBathRooms || 0,
- area: info.space || 0,
- features: features.length > 0 ? features : [
- { name: 'متاح للإيجار', available: true, description: '' },
- ],
- images,
- status,
- rating: item.rating || 4.5,
- reviews: 0,
- reviewList: [],
- owner: {
- name: 'المالك',
- phone: '—',
- email: '—',
- rating: 4.8,
- properties: 1,
- memberSince: '2024',
- responseRate: '95%',
- responseTime: 'خلال ساعات',
- },
- nearby: [],
- specifications: {
- constructionYear: null,
- floor: '-',
- parking: 0,
- gardenArea: 0,
- poolArea: 0,
- furnished: false,
- airConditioning: '-',
- heating: '-',
- electricity: '220V',
- water: 'شبكة عامة',
- },
- rules: [],
- _raw: item,
+ title: `${typeLabel} في ${address}`,
+ description: desc || `${typeLabel} في ${address} · ${bedrooms} غرف نوم · ${bathrooms} حمامات · ${area} م²`,
+ price,
+ priceUnit,
+ typeLabel,
+ address,
+ bedrooms,
+ bathrooms,
+ area,
+ image: firstImage,
};
}
-// extractCity is now imported from @/app/enums
+export async function generateMetadata({ params }) {
+ const { id } = await params;
+ const raw = await fetchPropertyForMeta(id);
-// API-only — no fallback data
-
-export default function PropertyDetailsPage() {
- const params = useParams();
- const [currentImage, setCurrentImage] = useState(0);
- const [showContact, setShowContact] = useState(false);
- const [bookingDates, setBookingDates] = useState({ start: '', end: '' });
- const [selectedDuration, setSelectedDuration] = useState(1);
- const [property, setProperty] = useState(null);
- const [loading, setLoading] = useState(true);
- const [bookingError, setBookingError] = useState(null);
- const [bookingSuccess, setBookingSuccess] = useState(false);
- const [availableRanges, setAvailableRanges] = useState([]);
- const [calendarMonth, setCalendarMonth] = useState(new Date());
- const [selectingEnd, setSelectingEnd] = useState(false);
- const [showLoginDialog, setShowLoginDialog] = useState(false);
-
- useEffect(() => {
- const id = params.id;
- setLoading(true);
- setBookingError(null);
- setBookingSuccess(false);
-
- async function fetchProperty() {
- try {
- // Try RentProperties first, then SaleProperties
- let data = null;
- try {
- data = await getRentProperty(id);
- } catch {
- try {
- data = await getSaleProperty(id);
- } catch {
- // neither worked
- }
- }
-
- if (data) {
- const mapped = mapApiDetail(data);
- if (mapped) {
- setProperty(mapped);
- setLoading(false);
- return;
- }
- }
- setProperty(null);
- } catch (err) {
- console.error('[Property] Failed to fetch property:', err);
- setProperty(null);
- } finally {
- setLoading(false);
- }
- }
-
- fetchProperty();
- }, [params.id]);
-
- // Fetch available date ranges
- useEffect(() => {
- if (!property) return;
- const propId = property._raw?.id || params.id;
- console.log('[Property] Fetching available dates for:', propId);
- getAvailableDateRanges(propId)
- .then((data) => {
- const ranges = Array.isArray(data) ? data : [];
- console.log('[Property] Available date ranges:', ranges);
- setAvailableRanges(ranges);
- })
- .catch((err) => {
- console.warn('[Property] Failed to fetch available dates:', err);
- });
- }, [property, params.id]);
-
- // Set Open Graph meta tags dynamically for Facebook/Twitter sharing
- useEffect(() => {
- if (!property) return;
-
- const typeLabel = property.type === 'villa' ? 'فيلا' : property.type === 'apartment' ? 'شقة' : 'بيت';
- const priceLabel = `${formatCurrency(property.price)} / ${property.priceUnit === 'daily' ? 'يوم' : 'شهر'}`;
- const desc = `${typeLabel} في ${property.location?.address || ''} · ${property.bedrooms} غرف نوم · ${property.bathrooms} حمامات · ${property.area} م²`;
- const imageUrl = property.images?.[0]
- ? (property.images[0].startsWith('http') ? property.images[0] : `http://45.93.137.91${property.images[0]}`)
- : '';
-
- const setMeta = (prop, content) => {
- let tag = document.querySelector(`meta[property="${prop}"]`);
- if (!tag) {
- tag = document.createElement('meta');
- tag.setAttribute('property', prop);
- document.head.appendChild(tag);
- }
- tag.setAttribute('content', content);
+ if (!raw) {
+ return {
+ title: 'SweetHome - عقار',
+ description: 'اكتشف أفضل العقارات للإيجار',
};
-
- const setMetaName = (name, content) => {
- let tag = document.querySelector(`meta[name="${name}"]`);
- if (!tag) {
- tag = document.createElement('meta');
- tag.setAttribute('name', name);
- document.head.appendChild(tag);
- }
- tag.setAttribute('content', content);
- };
-
- setMeta('og:title', `${property.title} - ${priceLabel}`);
- setMeta('og:description', desc);
- if (imageUrl) setMeta('og:image', imageUrl);
- setMeta('og:url', window.location.href);
- setMeta('og:type', 'website');
- setMeta('og:site_name', 'SweetHome');
-
- // Twitter cards
- setMetaName('twitter:card', 'summary_large_image');
- setMetaName('twitter:title', `${property.title} - ${priceLabel}`);
- setMetaName('twitter:description', desc);
- if (imageUrl) setMetaName('twitter:image', imageUrl);
- }, [property]);
-
- const formatCurrency = (amount) => {
- return amount?.toLocaleString() + ' ل.س';
- };
-
- const calculateTotalPrice = () => {
- if (!property) return 0;
- const days = bookingDates.start && bookingDates.end
- ? Math.ceil((new Date(bookingDates.end) - new Date(bookingDates.start)) / (1000 * 60 * 60 * 24))
- : selectedDuration;
- return property.price * (days > 0 ? days : 1);
- };
-
- // Calendar helpers
- const isDateAvailable = (dateStr) => {
- const d = new Date(dateStr + 'T00:00:00');
- return availableRanges.some((range) => {
- const start = new Date(range.startDate);
- const end = new Date(range.endDate);
- return d >= start && d <= end;
- });
- };
-
- const isInRange = (dateStr) => {
- if (!bookingDates.start) return false;
- const d = new Date(dateStr + 'T00:00:00');
- const start = new Date(bookingDates.start + 'T00:00:00');
- const end = bookingDates.end ? new Date(bookingDates.end + 'T00:00:00') : start;
- return d >= start && d <= end;
- };
-
- const isRangeFullyAvailable = (startStr, endStr) => {
- const start = new Date(startStr + 'T00:00:00');
- const end = new Date(endStr + 'T00:00:00');
- for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
- if (!isDateAvailable(d.toISOString().split('T')[0])) return false;
- }
- return true;
- };
-
- const handleCalendarClick = (dateStr) => {
- if (!isDateAvailable(dateStr)) return;
-
- if (!bookingDates.start || selectingEnd) {
- if (!bookingDates.start) {
- setBookingDates({ start: dateStr, end: '' });
- setSelectingEnd(true);
- } else {
- const start = bookingDates.start;
- const end = dateStr;
- const [s, e] = end > start ? [start, end] : [end, start];
- if (isRangeFullyAvailable(s, e)) {
- setBookingDates({ start: s, end: e });
- setSelectingEnd(false);
- } else {
- toast.error('بعض التواريخ في هذه الفترة غير متاحة');
- }
- }
- } else {
- setBookingDates({ start: dateStr, end: '' });
- setSelectingEnd(true);
- }
- };
-
- const getDaysInMonth = (year, month) => new Date(year, month + 1, 0).getDate();
- const getFirstDayOfMonth = (year, month) => new Date(year, month, 1).getDay();
-
- const formatDateStr = (year, month, day) => {
- return `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
- };
-
- const monthNames = ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'أكتوبر', 'نوفمبر', 'ديسمبر'];
- const dayNames = ['أح', 'إث', 'ثل', 'أر', 'خم', 'جم', 'سب'];
-
- const handleBooking = async () => {
- if (!AuthService.isAuthenticated()) {
- setShowLoginDialog(true);
- return;
- }
-
- setBookingError(null);
- setBookingSuccess(false);
-
- if (!bookingDates.start || !bookingDates.end) {
- setBookingError('يرجى اختيار تاريخ البداية والنهاية');
- return;
- }
-
- const propId = property?._raw?.id || parseInt(params.id);
- const startDate = new Date(bookingDates.start).toISOString();
- const endDate = new Date(bookingDates.end).toISOString();
-
- console.log('[Booking] Reserving:', { propertyId: propId, startDate, endDate });
-
- try {
- const res = await bookReservation(propId, startDate, endDate);
- console.log('[Booking] Success:', res);
- setBookingSuccess(true);
- toast.success('تم إرسال طلب الحجز بنجاح!');
- } catch (err) {
- console.error('[Booking] Failed:', err);
- setBookingError(err.message || 'فشل في إرسال طلب الحجز');
- }
- };
-
- if (loading) {
- return (
-
-
-
-
جاري تحميل تفاصيل العقار...
-
-
- );
}
- if (!property) {
- return (
-
-
-
-
العقار غير موجود
-
لم نتمكن من العثور على العقار المطلوب
-
- العودة إلى العقارات
-
-
-
- );
- }
+ const p = mapProperty(raw);
+ const priceStr = `${p.price.toLocaleString()} ل.س / ${p.priceUnit === 'daily' ? 'يوم' : 'شهر'}`;
+ const imageUrl = p.image
+ ? (p.image.startsWith('http') ? p.image : `http://45.93.137.91${p.image}`)
+ : '';
- return (
-
-
-
-
-
-
-
-
العودة إلى العقارات
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {property.images.length > 1 && (
- <>
-
-
- >
- )}
-
-
- {property.images.map((_, idx) => (
-
-
-
-
- {currentImage + 1} / {property.images.length}
-
-
-
-
- {property.images.slice(1, 5).map((img, idx) => (
-
setCurrentImage(idx + 1)}
- className="relative h-[240px] rounded-2xl overflow-hidden cursor-pointer hover:opacity-90 transition-opacity bg-gray-100"
- >
-
-
- ))}
-
-
-
-
-
-
-
-
-
-
{property.title}
-
-
- {property.location.address}
-
-
-
-
{formatCurrency(property.price)}
-
/{property.priceUnit === 'daily' ? 'يوم' : 'شهر'}
-
-
-
-
-
-
- {property.rating}
- {property.reviews > 0 && ({property.reviews} تقييم)}
-
-
-
- {property.status === 'available' ? 'متاح للإيجار' : 'محجوز حالياً'}
-
-
-
-
-
- المواصفات الرئيسية
-
-
-
-
{property.bedrooms}
-
غرف نوم
-
-
-
-
{property.bathrooms}
-
حمامات
-
-
-
-
{property.area}
-
م²
-
-
-
-
- {property.type === 'villa' ? 'فيلا' :
- property.type === 'apartment' ? 'شقة' : 'بيت'}
-
-
نوع العقار
-
-
-
-
-
- وصف العقار
- {property.description || 'لا يوجد وصف متاح.'}
-
-
-
- المميزات والخدمات
-
- {property.features.map((feature, idx) => (
-
-
- {feature.available ? (
-
- ) : (
-
- )}
-
-
-
- {feature.name}
-
- {feature.description && (
-
- {feature.description}
-
- )}
-
-
- ))}
-
-
-
- {property.reviewList && property.reviewList.length > 0 && (
-
- تقييمات المستأجرين
-
- {property.reviewList.map((review, idx) => (
-
-
-
-
{review.user}
-
- {[...Array(5)].map((_, i) => (
-
- ))}
-
-
-
{review.date}
-
-
{review.comment}
-
- ))}
-
-
- )}
-
- {property.rules && property.rules.length > 0 && (
-
- قوانين المنزل
-
- {property.rules.map((rule, idx) => (
- -
-
- {rule}
-
- ))}
-
-
- )}
-
-
-
-
-
- احجز هذا العقار
-
- {/* Selected dates display */}
-
-
- من
- {bookingDates.start || '—'}
-
-
- إلى
- {bookingDates.end || '—'}
-
-
-
- {bookingDates.start && bookingDates.end && (() => {
- const days = Math.ceil((new Date(bookingDates.end) - new Date(bookingDates.start)) / (1000 * 60 * 60 * 24));
- return days > 0 ? (
-
- {days} يوم{days > 1 ? 'اً' : 'اً'} {selectingEnd ? '— اضغط على تاريخ النهاية' : '✓'}
-
- ) : null;
- })()}
-
- {/* Calendar */}
-
-
-
- {monthNames[calendarMonth.getMonth()]} {calendarMonth.getFullYear()}
-
-
-
-
- {dayNames.map((d) => (
-
{d}
- ))}
-
-
-
- {(() => {
- const year = calendarMonth.getFullYear();
- const month = calendarMonth.getMonth();
- const daysInMonth = getDaysInMonth(year, month);
- const firstDay = getFirstDayOfMonth(year, month);
- const today = new Date().toISOString().split('T')[0];
- const cells = [];
-
- // Empty cells before first day
- for (let i = 0; i < firstDay; i++) {
- cells.push(
);
- }
-
- for (let day = 1; day <= daysInMonth; day++) {
- const dateStr = formatDateStr(year, month, day);
- const available = isDateAvailable(dateStr);
- const isStart = bookingDates.start === dateStr;
- const isEnd = bookingDates.end === dateStr;
- const inRange = isInRange(dateStr);
- const isPast = dateStr < today;
-
- cells.push(
-
- );
- }
- return cells;
- })()}
-
-
-
-
-
-
- {(() => {
- const days = bookingDates.start && bookingDates.end
- ? Math.ceil((new Date(bookingDates.end) - new Date(bookingDates.start)) / (1000 * 60 * 60 * 24))
- : 0;
- const effectiveDays = days > 0 ? days : 1;
- return (
- <>
-
- السعر لـ {effectiveDays} يوم{effectiveDays > 1 ? 'اً' : 'اً'}
- {formatCurrency(property.price * effectiveDays)}
-
-
- سلفة ضمان
- {formatCurrency(property._raw?.deposit || 0)}
-
-
- الإجمالي
- {formatCurrency(property.price * effectiveDays + (property._raw?.deposit || 0))}
-
- >
- );
- })()}
-
-
- {bookingError && (
-
- {bookingError}
-
- )}
-
- {bookingSuccess && (
-
- تم إرسال طلب الحجز بنجاح. سيتم التواصل معك قريباً.
-
- )}
-
-
-
-
-
- الدفع آمن ومضمون. سلفة الضمان قابلة للاسترداد.
-
-
-
-
- معلومات المالك
-
-
-
- {property.owner.name.charAt(0)}
-
-
-
-
{property.owner.name}
-
-
- {property.owner.rating}
- · {property.owner.properties} عقارات
-
- {property.owner.responseRate && (
-
-
- استجابة: {property.owner.responseRate}
-
- )}
-
-
-
- {showContact ? (
-
-
-
-
{property.owner.phone}
-
-
-
- {property.owner.email}
-
-
- ) : (
-
- )}
-
-
-
-
-
-
-
-
- {/* Login/Register Dialog */}
- {showLoginDialog && (
-
setShowLoginDialog(false)}>
-
e.stopPropagation()}
- className="bg-white rounded-2xl p-8 max-w-md w-full mx-4 shadow-2xl text-center"
- >
-
-
-
- سجّل الدخول للمتابعة
- يجب عليك إنشاء حساب أو تسجيل الدخول لحجز هذا العقار
-
-
- إنشاء حساب جديد
-
-
- تسجيل الدخول
-
-
-
-
-
- )}
-
- );
+ return {
+ title: `${p.title} - ${priceStr}`,
+ description: p.description,
+ openGraph: {
+ title: `${p.title} - ${priceStr}`,
+ description: p.description,
+ images: imageUrl ? [imageUrl] : [],
+ url: `https://sweethome.example/property/${id}`,
+ type: 'website',
+ siteName: 'SweetHome',
+ },
+ twitter: {
+ card: 'summary_large_image',
+ title: `${p.title} - ${priceStr}`,
+ description: p.description,
+ images: imageUrl ? [imageUrl] : [],
+ },
+ };
+}
+
+export default function PropertyPage({ params }) {
+ return ;
}