diff --git a/app/property/[id]/page.js b/app/property/[id]/page.js index 0ac809f..596b1be 100644 --- a/app/property/[id]/page.js +++ b/app/property/[id]/page.js @@ -2,6 +2,7 @@ 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'; @@ -42,7 +43,7 @@ import { Download, ArrowLeft } from 'lucide-react'; -import { getRentProperty, getSaleProperty, bookReservation, checkAvailability } from '../../utils/api'; +import { getRentProperty, getSaleProperty, bookReservation, checkAvailability, getAvailableDateRanges } from '../../utils/api'; import { BuildingTypeKeys, PropertyStatusKeys, extractCity } from '../../enums'; // Map API response to the UI format @@ -134,6 +135,9 @@ export default function PropertyDetailsPage() { 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); useEffect(() => { const id = params.id; @@ -175,6 +179,22 @@ export default function PropertyDetailsPage() { 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]); + const formatCurrency = (amount) => { return amount?.toLocaleString() + ' ل.س'; }; @@ -187,6 +207,67 @@ export default function PropertyDetailsPage() { 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 () => { setBookingError(null); setBookingSuccess(false); @@ -238,6 +319,7 @@ export default function PropertyDetailsPage() { return (
+
@@ -490,42 +572,103 @@ export default function PropertyDetailsPage() { >

احجز هذا العقار

-
- -
- {[1, 3, 7, 14, 30].map(days => ( - - ))} + {/* Selected dates display */} +
+
+ من + {bookingDates.start || '—'} +
+
+ إلى + {bookingDates.end || '—'}
-
-
- - setBookingDates({ ...bookingDates, start: e.target.value })} - className="w-full px-4 py-3 border border-gray-200 rounded-xl focus:ring-2 focus:ring-gray-300 focus:border-gray-300" - /> + {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()} +
-
- - setBookingDates({ ...bookingDates, end: e.target.value })} - className="w-full px-4 py-3 border border-gray-200 rounded-xl focus:ring-2 focus:ring-gray-300 focus:border-gray-300" - /> + +
+ {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; + })()} +
+ +
+
+
+ متاح +
+
+
+ محدد +
+
+
+ غير متاح +
diff --git a/app/utils/api.js b/app/utils/api.js index f411921..e09737a 100644 --- a/app/utils/api.js +++ b/app/utils/api.js @@ -128,6 +128,11 @@ export async function getTopRecommendations(count = 10) { // ─── Reservations ─── +export async function getAvailableDateRanges(propertyId) { + console.log('[API] Fetching available dates for property:', propertyId); + return apiFetch(`/Reservations/GetAvailableDates/available/${propertyId}`); +} + export async function getReservations() { return apiFetch('/Reservations/GetReservations'); }