Added API for rating
All checks were successful
Build frontend / build (push) Successful in 54s

This commit is contained in:
Rahaf
2026-04-26 13:46:30 +03:00
parent 97126c5776
commit 5d3ead55ca
7 changed files with 2223 additions and 1552 deletions

View File

@ -0,0 +1,317 @@
// import { useState } from 'react';
// import { motion } from 'framer-motion';
// import { Star, Edit2, X, Check, Clock } from 'lucide-react';
// import StarRating from './StarRating.js';
// import toast, { Toaster } from 'react-hot-toast';
// import { rateProperty, rateCustomer, getUserPropertyRating, canRateProperty } from '../../utils/ratings.js';
// const RatingForm = ({
// propertyId,
// userId,
// propertyOwner = false,
// initialRating = 0,
// initialComment = '',
// onSubmitSuccess
// }) => {
// const [rating, setRating] = useState(initialRating);
// const [comment, setComment] = useState(initialComment);
// const [loading, setLoading] = useState(false);
// const [showForm, setShowForm] = useState(false);
// const [userRating, setUserRating] = useState(null);
// // Check if user has already rated
// useState(() => {
// async function fetchUserRating() {
// try {
// const rating = await getUserPropertyRating(propertyId, userId);
// if (rating) {
// setUserRating(rating);
// setRating(rating.rating);
// setComment(rating.comment || '');
// }
// } catch (error) {
// console.error('[RatingForm] Failed to fetch user rating:', error);
// }
// }
// if (propertyId && userId) {
// fetchUserRating();
// }
// }, [propertyId, userId]);
// const handleSubmit = async (e) => {
// e.preventDefault();
// if (!rating) {
// toast.error('يرجى إعطاء تقييم من 1 إلى 5 نجوم');
// return;
// }
// setLoading(true);
// try {
// const ratingData = {
// propertyId,
// customerId: userId,
// rating,
// comment: comment.trim() || null
// };
// await rateProperty(ratingData);
// toast.success('تم إرسال التقييم بنجاح!');
// // Reset form
// setRating(0);
// setComment('');
// setShowForm(false);
// if (onSubmitSuccess) {
// onSubmitSuccess();
// }
// } catch (error) {
// console.error('[RatingForm] Failed to submit rating:', error);
// toast.error('حدث خطأ أثناء إرسال التقييم. يرجى المحاولة مرة أخرى.');
// } finally {
// setLoading(false);
// }
// };
// const handleEdit = () => {
// setShowForm(true);
// setRating(userRating?.rating || 0);
// setComment(userRating?.comment || '');
// };
// const handleCancel = () => {
// setShowForm(false);
// setRating(userRating?.rating || 0);
// setComment(userRating?.comment || '');
// };
// if (!propertyId || !userId) {
// return null;
// }
// return (
// <div className="space-y-4">
// <Toaster position="top-center" reverseOrder={false} />
// {/* Display existing rating */}
// {userRating && !showForm && (
// <motion.div
// initial={{ opacity: 0, y: 20 }}
// animate={{ opacity: 1, y: 0 }}
// className="bg-gray-50 rounded-xl p-4 border border-gray-200"
// >
// <div className="flex items-center justify-between mb-3">
// <div className="flex items-center gap-2">
// <Star className="w-5 h-5 text-amber-500" />
// <span className="font-medium text-gray-900">{userRating.rating}</span>
// <span className="text-sm text-gray-500">من 5</span>
// </div>
// <button
// onClick={handleEdit}
// className="px-3 py-1 bg-gray-100 text-gray-700 rounded-full text-sm hover:bg-gray-200 transition-colors flex items-center gap-1"
// >
// <Edit2 className="w-4 h-4" />
// تعديل
// </button>
// </div>
// {userRating.comment && (
// <div className="text-gray-600 text-sm mb-3 line-clamp-3">
// "{userRating.comment}"
// </div>
// )}
// <div className="flex items-center gap-2 text-xs text-gray-400">
// <Clock className="w-3 h-3" />
// <span>{userRating.createdAt ? new Date(userRating.createdAt).toLocaleDateString('ar-SA') : ''}</span>
// </div>
// </motion.div>
// )}
// {/* Rating form */}
// {showForm && (
// <motion.div
// initial={{ opacity: 0, y: 20 }}
// animate={{ opacity: 1, y: 0 }}
// className="bg-white rounded-xl p-6 border border-gray-200 shadow-sm"
// >
// <form onSubmit={handleSubmit}>
// <div className="mb-4">
// <label className="block text-sm font-medium text-gray-700 mb-2">
// تقييمك للعقار
// </label>
// <div className="flex items-center gap-2">
// <StarRating
// rating={rating}
// onRatingChange={setRating}
// readOnly={false}
// size={28}
// color="#ffc107"
// />
// <span className="text-lg font-bold text-gray-900">{rating || '1'}</span>
// <span className="text-sm text-gray-400">/5</span>
// </div>
// </div>
// <div className="mb-4">
// <label className="block text-sm font-medium text-gray-700 mb-2">
// تعليق (اختياري)
// </label>
// <textarea
// rows="3"
// value={comment}
// onChange={(e) => setComment(e.target.value)}
// placeholder="شارك تجربتك مع العقار..."
// className="w-full px-3 py-2 border border-gray-200 rounded-lg focus:ring-2 focus:ring-amber-500 focus:border-amber-500 transition-all resize-none"
// />
// </div>
// <div className="flex gap-3">
// <button
// type="button"
// onClick={handleCancel}
// disabled={loading}
// className="flex-1 px-4 py-2 bg-gray-100 text-gray-700 rounded-lg font-medium hover:bg-gray-200 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
// >
// إلغاء
// </button>
// <button
// type="submit"
// disabled={loading || !rating}
// className="flex-1 px-4 py-2 bg-amber-500 text-white rounded-lg font-medium hover:bg-amber-600 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-1"
// >
// {loading ? (
// <div className="w-4 h-4 border-2 border-white/50 border-t-white rounded-full animate-spin" />
// ) : (
// <Check className="w-5 h-5" />
// )}
// {loading ? 'إرسال' : 'إرسال التقييم'}
// </button>
// </div>
// </form>
// </motion.div>
// )}
// {/* Add rating button */}
// {!userRating && !showForm && (
// <motion.div
// initial={{ opacity: 0, y: 20 }}
// animate={{ opacity: 1, y: 0 }}
// className="bg-amber-50 border-2 border-amber-200 rounded-xl p-4 text-center cursor-pointer hover:border-amber-300 transition-all"
// onClick={() => setShowForm(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>
// )}
// </div>
// );
// };
// export default RatingForm;
'use client';
import { useState } from 'react';
import { X, Loader2 } from 'lucide-react';
import toast from 'react-hot-toast';
import StarRating from './StarRating';
import { addPropertyRating } from '../../utils/ratings';
const RatingField = ({ label, value, onChange }) => (
<div className="space-y-2">
<label className="block text-sm font-medium text-gray-700">{label} <span className="text-red-500">*</span></label>
<StarRating rating={value} onRatingChange={onChange} size={28} />
{value === 0 && <p className="text-xs text-red-500">مطلوب</p>}
</div>
);
export default function PropertyRatingForm({ reservationId, onSuccess, onCancel }) {
const [cleanRating, setCleanRating] = useState(0);
const [servicesRating, setServicesRating] = useState(0);
const [ownerBehaviorRating, setOwnerBehaviorRating] = useState(0);
const [experienceRating, setExperienceRating] = useState(0);
const [comment, setComment] = useState('');
const [loading, setLoading] = useState(false);
const validate = () => {
if (cleanRating === 0) return 'نظافة العقار';
if (servicesRating === 0) return 'جودة الخدمات';
if (ownerBehaviorRating === 0) return 'سلوك المالك';
if (experienceRating === 0) return 'التجربة العامة';
return null;
};
const handleSubmit = async (e) => {
e.preventDefault();
const missing = validate();
if (missing) {
toast.error(`يرجى تقييم: ${missing}`);
return;
}
setLoading(true);
try {
await addPropertyRating({
reservationId,
cleanRating,
servicesRating,
ownerBehaviorRating,
experienceRating,
comment: comment.trim() || null,
});
toast.success('تم إرسال التقييم بنجاح!');
onSuccess?.();
} catch (err) {
console.error(err);
toast.error('حدث خطأ، حاول مرة أخرى');
} finally {
setLoading(false);
}
};
return (
<div className="bg-white rounded-2xl shadow-lg border border-gray-200 p-6 max-w-lg mx-auto">
<div className="flex justify-between items-center mb-4">
<h3 className="text-xl font-bold text-gray-900">تقييم العقار</h3>
{onCancel && (
<button onClick={onCancel} className="text-gray-400 hover:text-gray-600">
<X size={20} />
</button>
)}
</div>
<form onSubmit={handleSubmit} className="space-y-5">
<RatingField label="نظافة العقار" value={cleanRating} onChange={setCleanRating} />
<RatingField label="جودة الخدمات" value={servicesRating} onChange={setServicesRating} />
<RatingField label="سلوك المالك" value={ownerBehaviorRating} onChange={setOwnerBehaviorRating} />
<RatingField label="التجربة العامة" value={experienceRating} onChange={setExperienceRating} />
<div>
<label className="block text-sm font-medium text-gray-700">تعليق (اختياري)</label>
<textarea
rows={3}
value={comment}
onChange={(e) => setComment(e.target.value)}
className="w-full mt-1 px-4 py-2 border border-gray-300 rounded-xl focus:ring-2 focus:ring-amber-500"
placeholder="شارك تجربتك..."
/>
</div>
<button
type="submit"
disabled={loading}
className="w-full bg-amber-500 hover:bg-amber-600 text-white font-bold py-3 rounded-xl transition flex items-center justify-center gap-2 disabled:opacity-50"
>
{loading && <Loader2 className="w-5 h-5 animate-spin" />}
{loading ? 'جاري الإرسال...' : 'إرسال التقييم'}
</button>
</form>
</div>
);
}