Merge branch 'main' of http://45.93.137.91:3000/Rahaf/SweetHome
Some checks failed
Build frontend / build (push) Failing after 43s
Some checks failed
Build frontend / build (push) Failing after 43s
This commit is contained in:
@ -49,6 +49,9 @@ import { getRentProperty, getSaleProperty, bookReservation, checkAvailability, g
|
|||||||
import AuthService from '../../services/AuthService';
|
import AuthService from '../../services/AuthService';
|
||||||
import { useFavorites } from '@/app/contexts/FavoritesContext';
|
import { useFavorites } from '@/app/contexts/FavoritesContext';
|
||||||
import { BuildingTypeKeys, PropertyStatusKeys, extractCity } from '../../enums';
|
import { BuildingTypeKeys, PropertyStatusKeys, extractCity } from '../../enums';
|
||||||
|
import RatingForm from '../../components/ratings/RatingForm.js';
|
||||||
|
import RatingList from '../../components/ratings/RatingList.js';
|
||||||
|
import StarRating from '../../components/ratings/StarRating.js';
|
||||||
|
|
||||||
// Copy to clipboard that works on HTTP too
|
// Copy to clipboard that works on HTTP too
|
||||||
async function copyToClipboard(text) {
|
async function copyToClipboard(text) {
|
||||||
@ -177,6 +180,11 @@ export default function PropertyDetailsPage() {
|
|||||||
const [favLoading, setFavLoading] = useState(false);
|
const [favLoading, setFavLoading] = useState(false);
|
||||||
const [selectingEnd, setSelectingEnd] = useState(false);
|
const [selectingEnd, setSelectingEnd] = useState(false);
|
||||||
const [showLoginDialog, setShowLoginDialog] = useState(false);
|
const [showLoginDialog, setShowLoginDialog] = useState(false);
|
||||||
|
const [showRatingForm, setShowRatingForm] = useState(false);
|
||||||
|
const [currentRating, setCurrentRating] = useState(0);
|
||||||
|
const [currentComment, setCurrentComment] = useState('');
|
||||||
|
const [userRating, setUserRating] = useState(null);
|
||||||
|
const [canRate, setCanRate] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const id = params.id;
|
const id = params.id;
|
||||||
@ -218,20 +226,29 @@ export default function PropertyDetailsPage() {
|
|||||||
fetchProperty();
|
fetchProperty();
|
||||||
}, [params.id]);
|
}, [params.id]);
|
||||||
|
|
||||||
// Fetch available date ranges
|
// Fetch user rating and check if they can rate
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!property) return;
|
async function fetchUserRatingAndCheck() {
|
||||||
const propId = property._raw?.id || params.id;
|
if (!property || !AuthService.isAuthenticated()) return;
|
||||||
console.log('[Property] Fetching available dates for:', propId);
|
|
||||||
getAvailableDateRanges(propId)
|
try {
|
||||||
.then((data) => {
|
// Check if user has already rated
|
||||||
const ranges = Array.isArray(data) ? data : [];
|
const rating = await getUserPropertyRating(property._raw?.id || parseInt(params.id), AuthService.getUserId());
|
||||||
console.log('[Property] Available date ranges:', ranges);
|
if (rating) {
|
||||||
setAvailableRanges(ranges);
|
setUserRating(rating);
|
||||||
})
|
setCurrentRating(rating.rating);
|
||||||
.catch((err) => {
|
setCurrentComment(rating.comment || '');
|
||||||
console.warn('[Property] Failed to fetch available dates:', err);
|
}
|
||||||
});
|
|
||||||
|
// Check if user can rate (e.g., after renting)
|
||||||
|
const canRateProperty = await canRateProperty(property._raw?.id || parseInt(params.id), AuthService.getUserId());
|
||||||
|
setCanRate(canRateProperty);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[Property] Failed to fetch user rating:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchUserRatingAndCheck();
|
||||||
}, [property, params.id]);
|
}, [property, params.id]);
|
||||||
|
|
||||||
// Set Open Graph meta tags dynamically for Facebook/Twitter sharing
|
// Set Open Graph meta tags dynamically for Facebook/Twitter sharing
|
||||||
@ -754,6 +771,10 @@ export default function PropertyDetailsPage() {
|
|||||||
{property.reviewList.map((review, idx) => (
|
{property.reviewList.map((review, idx) => (
|
||||||
<div key={idx} className="border-b border-gray-100 last:border-0 pb-4 last:pb-0">
|
<div key={idx} className="border-b border-gray-100 last:border-0 pb-4 last:pb-0">
|
||||||
<div className="flex justify-between items-start mb-2">
|
<div className="flex justify-between items-start mb-2">
|
||||||
|
<div className="flex items-start gap-2">
|
||||||
|
<div className="w-10 h-10 bg-gray-100 rounded-full flex items-center justify-center flex-shrink-0">
|
||||||
|
<User className="w-6 h-6 text-gray-600" />
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="font-bold text-gray-900">{review.user}</span>
|
<span className="font-bold text-gray-900">{review.user}</span>
|
||||||
<div className="flex items-center gap-1 mt-1">
|
<div className="flex items-center gap-1 mt-1">
|
||||||
@ -762,6 +783,7 @@ export default function PropertyDetailsPage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<span className="text-sm text-gray-500">{review.date}</span>
|
<span className="text-sm text-gray-500">{review.date}</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-600">{review.comment}</p>
|
<p className="text-gray-600">{review.comment}</p>
|
||||||
@ -771,24 +793,32 @@ export default function PropertyDetailsPage() {
|
|||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{property.rules && property.rules.length > 0 && (
|
{/* New Rating Components */}
|
||||||
|
{AuthService.isAuthenticated() && canRate && !userRating && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.65 }}
|
||||||
|
className="bg-amber-50 border-2 border-amber-200 rounded-xl p-4 text-center cursor-pointer hover:border-amber-300 transition-all"
|
||||||
|
onClick={() => setShowRatingForm(true)}
|
||||||
|
>
|
||||||
|
<Star className="w-8 h-8 text-amber-500 mx-auto mb-2" />
|
||||||
|
<h3 className="font-bold text-amber-700 mb-2">قيّم هذا العقار</h3>
|
||||||
|
<p className="text-sm text-amber-600">شارك تجربتك مع المستأجرين الآخرين</p>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ delay: 0.7 }}
|
transition={{ delay: 0.7 }}
|
||||||
className="bg-white rounded-2xl p-6 shadow-sm border border-gray-100"
|
className="bg-white rounded-2xl p-6 shadow-sm border border-gray-200"
|
||||||
>
|
>
|
||||||
<h2 className="text-xl font-bold mb-4 text-gray-900">قوانين المنزل</h2>
|
<RatingList
|
||||||
<ul className="space-y-2">
|
propertyId={property._raw?.id || parseInt(params.id)}
|
||||||
{property.rules.map((rule, idx) => (
|
userId={AuthService.getUserId()}
|
||||||
<li key={idx} className="flex items-center gap-2 text-gray-600">
|
/>
|
||||||
<div className="w-1.5 h-1.5 bg-gray-400 rounded-full" />
|
|
||||||
{rule}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
|||||||
Reference in New Issue
Block a user