279 lines
10 KiB
JavaScript
279 lines
10 KiB
JavaScript
|
class ReviewSystem {
|
||
|
constructor() {
|
||
|
this.reviewForm = document.getElementById('review-form');
|
||
|
this.reviewsContainer = document.getElementById('reviews-container');
|
||
|
this.ratingInput = document.getElementById('rating-input');
|
||
|
|
||
|
this.initializeReviews();
|
||
|
}
|
||
|
|
||
|
initializeReviews() {
|
||
|
// تهيئة نظام التقييم بالنجوم
|
||
|
this.initializeStarRating();
|
||
|
|
||
|
// معالجة إرسال المراجعة
|
||
|
this.reviewForm?.addEventListener('submit', (e) => this.handleReviewSubmit(e));
|
||
|
|
||
|
// تهيئة فلترة المراجعات
|
||
|
this.initializeReviewFilters();
|
||
|
|
||
|
// تحميل المراجعات الحالية
|
||
|
this.loadReviews();
|
||
|
}
|
||
|
|
||
|
initializeStarRating() {
|
||
|
const ratingStars = document.querySelectorAll('.rating-stars i');
|
||
|
|
||
|
ratingStars.forEach((star, index) => {
|
||
|
star.addEventListener('mouseover', () => {
|
||
|
this.updateStars(ratingStars, index);
|
||
|
});
|
||
|
|
||
|
star.addEventListener('click', () => {
|
||
|
this.ratingInput.value = index + 1;
|
||
|
star.classList.add('selected');
|
||
|
});
|
||
|
});
|
||
|
|
||
|
// إعادة تعيين النجوم عند إزالة المؤشر
|
||
|
document.querySelector('.rating-stars')?.addEventListener('mouseleave', () => {
|
||
|
this.updateStars(ratingStars, this.ratingInput.value - 1);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
updateStars(stars, activeIndex) {
|
||
|
stars.forEach((star, index) => {
|
||
|
star.classList.toggle('active', index <= activeIndex);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
async handleReviewSubmit(e) {
|
||
|
e.preventDefault();
|
||
|
const formData = new FormData(e.target);
|
||
|
|
||
|
try {
|
||
|
const response = await fetch('/api/reviews.php', {
|
||
|
method: 'POST',
|
||
|
body: formData
|
||
|
});
|
||
|
|
||
|
if (!response.ok) throw new Error('فشل إرسال المراجعة');
|
||
|
|
||
|
const review = await response.json();
|
||
|
this.addReviewToDOM(review);
|
||
|
this.showNotification('تم إضافة مراجعتك بنجاح');
|
||
|
e.target.reset();
|
||
|
this.updateStars(document.querySelectorAll('.rating-stars i'), -1);
|
||
|
} catch (error) {
|
||
|
console.error('Review submission error:', error);
|
||
|
this.showNotification('حدث خطأ أثناء إرسال المراجعة', 'error');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
initializeReviewFilters() {
|
||
|
const filterButtons = document.querySelectorAll('.review-filter');
|
||
|
filterButtons.forEach(button => {
|
||
|
button.addEventListener('click', () => {
|
||
|
filterButtons.forEach(btn => btn.classList.remove('active'));
|
||
|
button.classList.add('active');
|
||
|
this.filterReviews(button.dataset.rating);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
// تهيئة الترتيب
|
||
|
document.getElementById('review-sort')?.addEventListener('change', (e) => {
|
||
|
this.sortReviews(e.target.value);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
async loadReviews() {
|
||
|
try {
|
||
|
const productId = this.reviewsContainer?.dataset.productId;
|
||
|
const response = await fetch(`/api/reviews.php?product_id=${productId}`);
|
||
|
|
||
|
if (!response.ok) throw new Error('فشل تحميل المراجعات');
|
||
|
|
||
|
const reviews = await response.json();
|
||
|
this.displayReviews(reviews);
|
||
|
} catch (error) {
|
||
|
console.error('Reviews loading error:', error);
|
||
|
this.showNotification('حدث خطأ أثناء تحميل المراجعات', 'error');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
displayReviews(reviews) {
|
||
|
if (!this.reviewsContainer) return;
|
||
|
|
||
|
if (reviews.length === 0) {
|
||
|
this.reviewsContainer.innerHTML = '<p class="no-reviews">لا توجد مراجعات بعد</p>';
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.reviewsContainer.innerHTML = reviews.map(review => `
|
||
|
<div class="review-card" data-rating="${review.rating}">
|
||
|
<div class="review-header">
|
||
|
<div class="reviewer-info">
|
||
|
<img src="${review.userAvatar}" alt="${review.userName}" class="reviewer-avatar">
|
||
|
<div>
|
||
|
<h4>${review.userName}</h4>
|
||
|
<div class="review-rating">
|
||
|
${this.generateStars(review.rating)}
|
||
|
</div>
|
||
|
<span class="review-date">${review.date}</span>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="review-verification">
|
||
|
${review.verified ? '<span class="verified-badge"><i class="fas fa-check-circle"></i> شراء موثق</span>' : ''}
|
||
|
</div>
|
||
|
</div>
|
||
|
<div class="review-content">
|
||
|
<p>${review.content}</p>
|
||
|
${review.images ? this.generateImageGallery(review.images) : ''}
|
||
|
</div>
|
||
|
<div class="review-actions">
|
||
|
<button class="helpful-btn" onclick="reviewSystem.markHelpful('${review.id}')">
|
||
|
<i class="far fa-thumbs-up"></i>
|
||
|
<span class="helpful-count">${review.helpfulCount}</span>
|
||
|
</button>
|
||
|
<button class="report-btn" onclick="reviewSystem.reportReview('${review.id}')">
|
||
|
<i class="far fa-flag"></i>
|
||
|
</button>
|
||
|
</div>
|
||
|
</div>
|
||
|
`).join('');
|
||
|
}
|
||
|
|
||
|
generateStars(rating) {
|
||
|
return Array(5).fill().map((_, index) => `
|
||
|
<i class="fas fa-star ${index < rating ? 'filled' : ''}"></i>
|
||
|
`).join('');
|
||
|
}
|
||
|
|
||
|
generateImageGallery(images) {
|
||
|
return `
|
||
|
<div class="review-images">
|
||
|
${images.map(image => `
|
||
|
<img src="${image}" alt="صورة المراجعة" onclick="reviewSystem.showImageModal('${image}')">
|
||
|
`).join('')}
|
||
|
</div>
|
||
|
`;
|
||
|
}
|
||
|
|
||
|
filterReviews(rating) {
|
||
|
const reviews = document.querySelectorAll('.review-card');
|
||
|
|
||
|
reviews.forEach(review => {
|
||
|
if (rating === 'all' || review.dataset.rating === rating) {
|
||
|
review.style.display = 'block';
|
||
|
} else {
|
||
|
review.style.display = 'none';
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
sortReviews(criteria) {
|
||
|
const reviews = Array.from(document.querySelectorAll('.review-card'));
|
||
|
const container = this.reviewsContainer;
|
||
|
|
||
|
if (!container) return;
|
||
|
|
||
|
reviews.sort((a, b) => {
|
||
|
switch (criteria) {
|
||
|
case 'newest':
|
||
|
return new Date(b.querySelector('.review-date').textContent) -
|
||
|
new Date(a.querySelector('.review-date').textContent);
|
||
|
case 'highest':
|
||
|
return b.dataset.rating - a.dataset.rating;
|
||
|
case 'lowest':
|
||
|
return a.dataset.rating - b.dataset.rating;
|
||
|
case 'helpful':
|
||
|
return parseInt(b.querySelector('.helpful-count').textContent) -
|
||
|
parseInt(a.querySelector('.helpful-count').textContent);
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
reviews.forEach(review => container.appendChild(review));
|
||
|
}
|
||
|
|
||
|
async markHelpful(reviewId) {
|
||
|
try {
|
||
|
const response = await fetch('/api/helpful-review.php', {
|
||
|
method: 'POST',
|
||
|
headers: {
|
||
|
'Content-Type': 'application/json'
|
||
|
},
|
||
|
body: JSON.stringify({ reviewId })
|
||
|
});
|
||
|
|
||
|
if (!response.ok) throw new Error('فشل تسجيل المساعدة');
|
||
|
|
||
|
const data = await response.json();
|
||
|
document.querySelector(`[data-review-id="${reviewId}"] .helpful-count`).textContent = data.helpfulCount;
|
||
|
this.showNotification('شكراً لتقييمك');
|
||
|
} catch (error) {
|
||
|
console.error('Helpful marking error:', error);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
reportReview(reviewId) {
|
||
|
const reason = prompt('يرجى ذكر سبب الإبلاغ عن هذه المراجعة:');
|
||
|
if (!reason) return;
|
||
|
|
||
|
fetch('/api/report-review.php', {
|
||
|
method: 'POST',
|
||
|
headers: {
|
||
|
'Content-Type': 'application/json'
|
||
|
},
|
||
|
body: JSON.stringify({ reviewId, reason })
|
||
|
})
|
||
|
.then(response => {
|
||
|
if (response.ok) {
|
||
|
this.showNotification('شكراً لإبلاغك. سنراجع المحتوى.');
|
||
|
}
|
||
|
})
|
||
|
.catch(error => {
|
||
|
console.error('Review report error:', error);
|
||
|
this.showNotification('حدث خطأ أثناء الإبلاغ', 'error');
|
||
|
});
|
||
|
}
|
||
|
|
||
|
showImageModal(imageSrc) {
|
||
|
const modal = document.createElement('div');
|
||
|
modal.className = 'image-modal';
|
||
|
modal.innerHTML = `
|
||
|
<div class="modal-content">
|
||
|
<img src="${imageSrc}" alt="صورة المراجعة">
|
||
|
<button class="close-modal">×</button>
|
||
|
</div>
|
||
|
`;
|
||
|
|
||
|
document.body.appendChild(modal);
|
||
|
modal.querySelector('.close-modal').addEventListener('click', () => modal.remove());
|
||
|
modal.addEventListener('click', (e) => {
|
||
|
if (e.target === modal) modal.remove();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
showNotification(message, type = 'success') {
|
||
|
const notification = document.createElement('div');
|
||
|
notification.className = `review-notification ${type}`;
|
||
|
notification.textContent = message;
|
||
|
|
||
|
document.body.appendChild(notification);
|
||
|
|
||
|
setTimeout(() => {
|
||
|
notification.classList.add('show');
|
||
|
}, 100);
|
||
|
|
||
|
setTimeout(() => {
|
||
|
notification.classList.remove('show');
|
||
|
setTimeout(() => notification.remove(), 300);
|
||
|
}, 2000);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// تهيئة نظام المراجعات
|
||
|
const reviewSystem = new ReviewSystem();
|