import React, { useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { ArrowLeft, MessageSquare, Star, Heart, TrendingUp, Filter, Download, Shield, CheckCircle, XCircle, Eye, EyeOff, Trash2 } from 'lucide-react'; import { toast } from 'react-toastify'; import { format } from 'date-fns'; import { Button, Card, Loading } from '../../components/common'; import { AdminAuthenticatedImage } from '../../components/admin/AdminAuthenticatedImage'; import { FeedbackSettings } from '../../components/admin'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { eventsService } from '../../services/events.service'; import { feedbackService } from '../../services/feedback.service'; import type { PhotoFeedback, FeedbackAnalytics } from '../../services/feedback.service'; export const EventFeedbackPage: React.FC = () => { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const queryClient = useQueryClient(); const { t } = useTranslation(); const [activeTab, setActiveTab] = useState<'settings' | 'feedback' | 'analytics' | 'moderation'>('settings'); const [feedbackFilter, setFeedbackFilter] = useState({ type: '', status: '', page: 1, limit: 20 }); // Fetch event details const { data: event, isLoading: eventLoading } = useQuery({ queryKey: ['event', id], queryFn: () => eventsService.getEvent(id!), enabled: !!id }); // Fetch feedback settings const { data: settings, isLoading: settingsLoading } = useQuery({ queryKey: ['feedback-settings', id], queryFn: () => feedbackService.getEventFeedbackSettings(id!), enabled: !!id }); // Fetch feedback list const { data: feedbackData, isLoading: feedbackLoading } = useQuery({ queryKey: ['event-feedback', id, feedbackFilter], queryFn: () => feedbackService.getEventFeedback(id!, feedbackFilter), enabled: !!id && activeTab === 'feedback' }); // Fetch analytics const { data: analytics, isLoading: analyticsLoading } = useQuery({ queryKey: ['feedback-analytics', id], queryFn: () => feedbackService.getEventFeedbackAnalytics(id!), enabled: !!id && activeTab === 'analytics' }); // Update settings mutation const updateSettingsMutation = useMutation({ mutationFn: (newSettings: any) => feedbackService.updateEventFeedbackSettings(id!, newSettings), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['feedback-settings', id] }); toast.success(t('feedback.settingsUpdated', 'Feedback settings updated')); }, onError: () => { toast.error(t('feedback.settingsUpdateError', 'Failed to update settings')); } }); // Moderate feedback mutation const moderateMutation = useMutation({ mutationFn: ({ feedbackId, action }: { feedbackId: string; action: 'approve' | 'hide' | 'reject' }) => feedbackService.moderateFeedback(feedbackId, action), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['event-feedback', id] }); toast.success(t('feedback.moderated', 'Feedback moderated')); } }); // Delete feedback mutation const deleteMutation = useMutation({ mutationFn: (feedbackId: string) => feedbackService.deleteFeedback(feedbackId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['event-feedback', id] }); toast.success(t('feedback.deleted', 'Feedback deleted')); } }); // Export feedback const handleExport = async (format: 'json' | 'csv') => { try { const data = await feedbackService.exportEventFeedback(id!, format); if (format === 'csv') { const blob = new Blob([data], { type: 'text/csv' }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `feedback-${event?.slug || id}.csv`; a.click(); } else { const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `feedback-${event?.slug || id}.json`; a.click(); } toast.success(t('feedback.exported', 'Feedback exported')); } catch (error) { toast.error(t('feedback.exportError', 'Failed to export feedback')); } }; if (eventLoading || settingsLoading) { return ; } if (!event) { return
{t('events.notFound', 'Event not found')}
; } return (
{/* Header */}

{t('feedback.title', 'Feedback Management')}

{event.event_name} • {event.slug}

{/* Tabs */}
{/* Content */} {activeTab === 'settings' && settings && ( updateSettingsMutation.mutate(newSettings)} /> )} {activeTab === 'feedback' && (
{/* Filters */}
{/* Feedback List */} {feedbackLoading ? ( ) : feedbackData?.feedback?.length === 0 ? (
{t('feedback.noFeedback', 'No feedback found')}
) : (
{feedbackData?.feedback?.map((item: PhotoFeedback) => (
{item.photo_id && (
)}
{item.feedback_type === 'rating' && } {item.feedback_type === 'like' && } {item.feedback_type === 'comment' && } {item.guest_name || t('feedback.anonymous', 'Anonymous')} {item.guest_email && ( ({item.guest_email}) )}
{item.rating && (
{[1, 2, 3, 4, 5].map((star) => ( ))}
)} {item.comment_text && (

{item.comment_text}

)}

{format(new Date(item.created_at), 'PPpp')}

{item.feedback_type === 'comment' && !item.is_approved && ( <> )} {item.is_hidden && ( )}
))}
)} {/* Pagination */} {feedbackData?.pagination && feedbackData.pagination.pages > 1 && (
{t('common.pageOf', 'Page {{current}} of {{total}}', { current: feedbackFilter.page, total: feedbackData.pagination.pages })}
)}
)} {activeTab === 'analytics' && (
{analyticsLoading ? ( ) : analytics ? ( <> {/* Summary Stats */}

{(analytics.summary.average_rating || 0).toFixed(1)}

{t('feedback.avgRating', 'Average Rating')}

{t('feedback.totalRatings', '{{count}} ratings', { count: analytics.summary.total_ratings })}

{analytics.summary.total_likes}

{t('feedback.totalLikes', 'Total Likes')}

{analytics.summary.total_comments}

{t('feedback.totalComments', 'Total Comments')}

{analytics.summary.pending_moderation > 0 && (

{t('feedback.pendingModeration', '{{count}} pending', { count: analytics.summary.pending_moderation })}

)}

{analytics.summary.total_feedback}

{t('feedback.totalInteractions', 'Total Interactions')}

{/* Top Rated Photos */} {analytics.topRated.length > 0 && (

{t('feedback.topRated', 'Top Rated Photos')}

{analytics.topRated.map((photo) => (
{photo.filename}
{[1, 2, 3, 4, 5].map((star) => ( ))}
{Number(photo.average_rating).toFixed(1)} ({photo.feedback_count})
))}
)} {/* Recent Comments */} {analytics.recentComments.length > 0 && (

{t('feedback.recentComments', 'Recent Comments')}

{analytics.recentComments.map((comment, idx) => (

{comment.comment_text}

{comment.guest_name} • {comment.filename} • {format(new Date(comment.created_at), 'PP')}

))}
)} ) : null}
)} {activeTab === 'moderation' && (

{t('feedback.wordFilters', 'Word Filters')}

{t('feedback.wordFiltersDesc', 'Manage blocked words for comment moderation')}

)}
); };