diff --git a/app/property/[id]/PropertyDetail.js b/app/property/[id]/PropertyDetail.js index b402d77..7d9f950 100644 --- a/app/property/[id]/PropertyDetail.js +++ b/app/property/[id]/PropertyDetail.js @@ -179,6 +179,12 @@ export default function PropertyDetailsPage() { const [bookingError, setBookingError] = useState(null); const [bookingSuccess, setBookingSuccess] = useState(false); const [availableRanges, setAvailableRanges] = useState([]); + 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'); const [favLoading, setFavLoading] = useState(false); const [avgRating, setAvgRating] = useState(null); const [showRatingForm, setShowRatingForm] = useState(false); @@ -202,6 +208,17 @@ export default function PropertyDetailsPage() { const mapped = mapApiDetail(data); setProperty(mapped); if (mapped) fetchAvgRating(mapped.id); + 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); + } + } } } catch (err) { console.error('[PropertyDetail] Failed:', err); @@ -267,6 +284,77 @@ export default function PropertyDetailsPage() { } }; + 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); + }; + const handleRatingSuccess = () => { setShowRatingForm(false); if (property) fetchAvgRating(property.id); @@ -304,6 +392,8 @@ export default function PropertyDetailsPage() { const isFav = isFavorite(property.id); const isRoomType = property.type === 'room'; const isMostRequested = avgRating !== null && avgRating >= 4.5; + const showPricingToggle = property.isRent && property.priceDisplay?.daily > 0 && property.priceDisplay?.monthly > 0; + const effectivePricingMode = showPricingToggle ? pricingMode : (property.isRent && property.priceDisplay?.monthly > 0 ? 'monthly' : 'daily'); return (
@@ -702,27 +792,168 @@ export default function PropertyDetailsPage() {
) : ( <> -
-
- - setBookingDates(prev => ({ ...prev, start: e.target.value }))} - className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-xl focus:ring-2 focus:ring-amber-500" /> + {/* Pricing Mode Toggle */} + {showPricingToggle && ( +
+ +
-
- - setBookingDates(prev => ({ ...prev, end: e.target.value }))} - className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-xl focus:ring-2 focus:ring-amber-500" /> + )} + + {/* Step Indicator */} +
+
+
+ تحديد تاريخ البداية +
+
+
+
+ تحديد تاريخ النهاية
+ {/* Calendar */} + {effectivePricingMode === 'daily' ? ( +
+
+ + {MONTHS_AR[calendarMonth]} {calendarYear} + +
+ +
+ {DAYS_AR.map((d, i) => ( +
{d}
+ ))} +
+ + {(() => { + 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(
); + } + 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( + + ); + } + return
{cells}
; + })()} +
+ ) : ( +
+
+ + {calendarYear} + +
+
+ {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 ( + + ); + })} +
+
+ )} + + {/* Summary */} + {selectedStart && ( +
+
+ تاريخ البداية + {selectedStart} +
+ {selectedEnd && ( + <> +
+ تاريخ النهاية + {selectedEnd} +
+
+
+ {effectivePricingMode === 'daily' ? 'عدد الأيام' : 'عدد الأشهر'} + + {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} + +
+
+ المجموع + + {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)} ل.س + +
+ {property.deposit > 0 && ( +
+ تأمين + {formatCurrency(property.deposit)} ل.س +
+ )} + + )} +
+ )} + {bookingError && (
{bookingError}
)} - )}