// Review System Component class ReviewSystem { static init() { console.log('ReviewSystem component initialized'); this.setupReviewSubmission(); this.setupPeerAgreement(); this.loadSampleReviews(); } static setupReviewSubmission() { const reviewButtons = document.querySelectorAll('button'); reviewButtons.forEach(btn => { if (btn.textContent.includes('Write Review')) { btn.addEventListener('click', () => this.showReviewModal()); } }); } static showReviewModal() { // Create review submission modal const modal = this.createReviewModal(); document.body.appendChild(modal); setTimeout(() => { modal.classList.remove('opacity-0'); modal.classList.add('opacity-100'); }, 10); } static createReviewModal() { const modal = document.createElement('div'); modal.className = 'fixed inset-0 bg-gray-600 bg-opacity-50 flex items-center justify-center z-50 opacity-0 transition-opacity duration-300'; modal.innerHTML = `

Write a Review

${[1, 2, 3, 4, 5].map(star => ` `).join('')}
${this.getReviewDimensions().map(dim => `
0
`).join('')}
Review will be eligible after 14 days
`; this.setupModalInteractions(modal); return modal; } static getReviewDimensions() { return [ { key: 'quality', label: 'Quality' }, { key: 'packaging', label: 'Packaging' }, { key: 'support', label: 'Customer Support' }, { key: 'value', label: 'Value for Money' }, { key: 'durability', label: 'Durability' } ]; } static setupModalInteractions(modal) { // Star rating interactions const stars = modal.querySelectorAll('.star-rating'); let currentRating = 0; stars.forEach((star, index) => { star.addEventListener('click', () => { currentRating = index + 1; this.updateStarDisplay(stars, currentRating); }); star.addEventListener('mouseenter', () => { this.updateStarDisplay(stars, index + 1); }); }); modal.addEventListener('mouseleave', () => { this.updateStarDisplay(stars, currentRating); }); // Dimension rating interactions const dimensionButtons = modal.querySelectorAll('.dimension-positive, .dimension-negative'); const dimensionValues = {}; dimensionButtons.forEach(btn => { btn.addEventListener('click', () => { const dimension = btn.dataset.dimension; const value = parseInt(btn.dataset.value); dimensionValues[dimension] = value; this.updateDimensionDisplay(modal, dimensionValues); }); }); // Replace icons setTimeout(() => { if (window.feather) { window.feather.replace(); } }, 0); } static updateStarDisplay(stars, rating) { stars.forEach((star, index) => { const icon = star.querySelector('i'); if (index < rating) { icon.className = 'h-6 w-6 text-yellow-400 fill-current'; } else { icon.className = 'h-6 w-6 text-gray-300'; } }); } static updateDimensionDisplay(modal, values) { Object.entries(values).forEach(([dimension, value]) => { const valueElement = modal.querySelector(`.dimension-value[data-dimension="${dimension}"]`); if (valueElement) { valueElement.textContent = value > 0 ? `+${value}` : value; valueElement.className = `text-sm dimension-value ${value > 0 ? 'text-green-600' : 'text-red-600'}`; } }); } static submitReview(reviewData) { // Validate review if (!this.validateReview(reviewData)) { return { success: false, error: 'Review validation failed' }; } // Calculate review maturity timestamp const maturityDays = this.getMaturityDays(reviewData.category || 'general'); const maturityTimestamp = new Date(Date.now() + maturityDays * 24 * 60 * 60 * 1000).toISOString(); const review = { id: this.generateReviewId(), userId: window.OneSearchApp?.user?.id || 'anonymous', productId: reviewData.productId, rating: reviewData.rating, title: reviewData.title, content: reviewData.content, dimensions: reviewData.dimensions, perceivedValue: reviewData.perceivedValue, salePrice: reviewData.salePrice, verifiedPurchase: true, maturityTimestamp, createdAt: new Date().toISOString(), peerAgreements: [] }; // Store review (in real implementation, this would go to the database) this.storeReview(review); return { success: true, review }; } static validateReview(reviewData) { // Check minimum requirements for review validation if (!reviewData.rating || reviewData.rating < 1) return false; if (!reviewData.content || reviewData.content.length < 50) return false; // Check if at least 3 dimensions are rated const ratedDimensions = Object.values(reviewData.dimensions || {}).filter(v => v !== 0); if (ratedDimensions.length < 3) return false; // For negative reviews, require more detail const averageDimension = Object.values(reviewData.dimensions || {}).reduce((a, b) => a + b, 0) / Object.values(reviewData.dimensions || {}).length; if (averageDimension < 0 && reviewData.content.length < 100) return false; return true; } static getMaturityDays(category) { const maturityPeriods = { electronics: 30, consumables: 14, services: 7, furniture: 21, general: 14 }; return maturityPeriods[category] || 14; } static setupPeerAgreement() { // Setup peer agreement/disagreement handlers document.addEventListener('click', (e) => { if (e.target.closest('.agreement-btn')) { const button = e.target.closest('.agreement-btn'); const reviewId = button.closest('.review-card')?.dataset.reviewId; const action = button.dataset.action; if (reviewId && action) { this.handlePeerAgreement(reviewId, action, button); } } }); } static handlePeerAgreement(reviewId, action, buttonElement) { const userId = window.OneSearchApp?.user?.id; if (!userId) { if (window.OneSearchApp) { window.OneSearchApp.showNotification('Please sign in to participate in peer review', 'warning'); } return; } // Check if user has already voted on this review const existingVote = this.getExistingVote(reviewId, userId); if (existingVote && existingVote.action === action) { // Remove vote if clicking the same action this.removeVote(reviewId, userId); this.updateVoteDisplay(buttonElement, action, -1); } else if (existingVote) { // Change vote this.updateVote(reviewId, userId, action); this.updateVoteDisplay(buttonElement, action, 1); this.updateOppositeVote(buttonElement, action, -1); } else { // New vote this.addVote(reviewId, userId, action); this.updateVoteDisplay(buttonElement, action, 1); } // Trigger review weight recalculation if significant change this.checkForReviewFlag(reviewId); } static getExistingVote(reviewId, userId) { // Get user's existing vote on this review const votes = JSON.parse(localStorage.getItem(`reviewVotes_${reviewId}`) || '[]'); return votes.find(vote => vote.userId === userId) || null; } static addVote(reviewId, userId, action) { const votes = JSON.parse(localStorage.getItem(`reviewVotes_${reviewId}`) || '[]'); votes.push({ userId, action, timestamp: new Date().toISOString(), userTrust: window.OneSearchApp?.user?.profileTrust || 0.5 }); localStorage.setItem(`reviewVotes_${reviewId}`, JSON.stringify(votes)); } static updateVote(reviewId, userId, newAction) { const votes = JSON.parse(localStorage.getItem(`reviewVotes_${reviewId}`) || '[]'); const voteIndex = votes.findIndex(vote => vote.userId === userId); if (voteIndex !== -1) { votes[voteIndex].action = newAction; localStorage.setItem(`reviewVotes_${reviewId}`, JSON.stringify(votes)); } } static removeVote(reviewId, userId) { const votes = JSON.parse(localStorage.getItem(`reviewVotes_${reviewId}`) || '[]'); const filteredVotes = votes.filter(vote => vote.userId !== userId); localStorage.setItem(`reviewVotes_${reviewId}`, JSON.stringify(filteredVotes)); } static updateVoteDisplay(buttonElement, action, delta) { const countSpan = buttonElement.querySelector('span'); if (countSpan) { const currentCount = parseInt(countSpan.textContent.match(/\d+/)[0]) || 0; const newCount = Math.max(0, currentCount + delta); countSpan.textContent = countSpan.textContent.replace(/\d+/, newCount); } } static updateOppositeVote(buttonElement, action, delta) { const isAgree = action === 'agree'; const oppositeButton = buttonElement.closest('.peer-agreements').querySelector( isAgree ? '.agreement-disagree' : '.agreement-agree' ); if (oppositeButton) { this.updateVoteDisplay(oppositeButton, action, delta); } } static checkForReviewFlag(reviewId) { const votes = JSON.parse(localStorage.getItem(`reviewVotes_${reviewId}`) || '[]'); const disagreeVotes = votes.filter(vote => vote.action === 'disagree'); const highTrustDisagrees = disagreeVotes.filter(vote => vote.userTrust > 0.8); if (highTrustDisagrees.length >= 3) { this.flagReviewForAudit(reviewId, 'Multiple high-trust users disagree'); } } static flagReviewForAudit(reviewId, reason) { // Mark review for audit const flaggedReviews = JSON.parse(localStorage.getItem('flaggedReviews') || '[]'); flaggedReviews.push({ reviewId, reason, flaggedAt: new Date().toISOString(), status: 'pending_review' }); localStorage.setItem('flaggedReviews', JSON.stringify(flaggedReviews)); if (window.OneSearchApp) { window.OneSearchApp.showNotification('Review flagged for audit', 'info'); } } static storeReview(review) { // Store review in local storage for demo purposes const reviews = JSON.parse(localStorage.getItem('reviews') || '[]'); reviews.push(review); localStorage.setItem('reviews', JSON.stringify(reviews)); } static loadSampleReviews() { // Load and display sample reviews (already in HTML) // This method can be extended to load dynamic review data } static generateReviewId() { return 'review_' + Date.now().toString(36) + Math.random().toString(36).substring(2, 8); } static getReviewWeight(reviewId) { // Calculate review weight based on peer agreements const votes = JSON.parse(localStorage.getItem(`reviewVotes_${reviewId}`) || '[]'); let weight = 1.0; // Base weight votes.forEach(vote => { if (vote.action === 'agree') { weight += vote.userTrust * 0.1; } else if (vote.action === 'disagree') { weight -= vote.userTrust * 0.15; } }); return Math.max(0.1, Math.min(2.0, weight)); // Clamp between 0.1 and 2.0 } static exportReviewData(reviewId) { const review = this.getReviewById(reviewId); const votes = JSON.parse(localStorage.getItem(`reviewVotes_${reviewId}`) || '[]'); return { review, votes, weight: this.getReviewWeight(reviewId), maturityTimestamp: review.maturityTimestamp, flags: this.getReviewFlags(reviewId) }; } static getReviewById(reviewId) { const reviews = JSON.parse(localStorage.getItem('reviews') || '[]'); return reviews.find(review => review.id === reviewId); } static getReviewFlags(reviewId) { const flaggedReviews = JSON.parse(localStorage.getItem('flaggedReviews') || '[]'); return flaggedReviews.filter(flag => flag.reviewId === reviewId); } } // Export for use in main app if (typeof window !== 'undefined') { window.ReviewSystem = ReviewSystem; } if (typeof module !== 'undefined' && module.exports) { module.exports = ReviewSystem; }