import { apiFetch } from '../api'; import React, { useState, useEffect } from 'react'; import { Star, MessageSquare, TrendingUp, ChevronRight, RefreshCw, PieChart as PieChartIcon, ThumbsUp, X, Clock, BarChart3 } from 'lucide-react'; import { PieChart, Pie, Cell, Tooltip, ResponsiveContainer, BarChart, Bar, XAxis, YAxis, CartesianGrid } from 'recharts'; import { format } from 'date-fns'; import { motion, AnimatePresence } from 'framer-motion'; import Pagination from '../components/Pagination'; interface RatedItem { name: string; average: number; count: number; } interface ItemDetail { rating: number; comment: string; date: string; userName?: string; orderNumber?: string; } interface ItemStats { averageRating: number; totalReviews: number; distribution: Array<{ rating: number; count: number; }>; } interface FeedbackStats { averageRating: number; distribution: Array<{ rating: number; count: number; }>; ratedItems: RatedItem[]; } const Feedback: React.FC = () => { const [stats, setStats] = useState(null); const [loading, setLoading] = useState(true); // Feedbacks list states const [feedbacks, setFeedbacks] = useState([]); const [page, setPage] = useState(0); const [totalElements, setTotalElements] = useState(0); const pageSize = 10; // Modal states const [selectedItem, setSelectedItem] = useState(null); const [itemDetails, setItemDetails] = useState([]); const [itemStats, setItemStats] = useState(null); const [detailsLoading, setItemDetailsLoading] = useState(false); const [detailsPage, setDetailsPage] = useState(0); const [detailsTotal, setDetailsTotal] = useState(0); const detailsPageSize = 5; const fetchStats = async () => { setLoading(true); try { const response = await apiFetch(`http://${window.location.hostname}:8080/api/feedback/stats`); const data = await response.json(); setStats(data); } catch (error) { console.error('Error fetching feedback stats:', error); } finally { setLoading(false); } }; const fetchFeedbacks = async () => { try { const response = await apiFetch(`http://${window.location.hostname}:8080/api/feedback?page=${page}&size=${pageSize}`); const data = await response.json(); setFeedbacks(data?.content || []); setTotalElements(data?.totalElements || 0); } catch (error) { console.error('Error fetching feedbacks:', error); setFeedbacks([]); } }; const fetchItemData = async (itemName: string, pageNum: number) => { setItemDetailsLoading(true); try { const [detailsRes, statsRes] = await Promise.all([ fetch(`http://${window.location.hostname}:8080/api/feedback/item-details?productName=${encodeURIComponent(itemName)}&page=${pageNum}&size=${detailsPageSize}`), fetch(`http://${window.location.hostname}:8080/api/feedback/item-stats?productName=${encodeURIComponent(itemName)}`) ]); const detailsData = await detailsRes.json(); const statsData = await statsRes.json(); setItemDetails(detailsData?.content || []); setDetailsTotal(detailsData?.totalElements || 0); setItemStats(statsData); } catch (error) { console.error('Error fetching item feedback data:', error); setItemDetails([]); } finally { setItemDetailsLoading(false); } }; useEffect(() => { fetchStats(); }, []); useEffect(() => { fetchFeedbacks(); }, [page]); useEffect(() => { if (selectedItem) { fetchItemData(selectedItem.name, detailsPage); } }, [selectedItem, detailsPage]); const COLORS = ['#ef4444', '#f59e0b', '#facc15', '#84cc16', '#22c55e']; const pieData = (stats?.distribution || []).map(d => ({ name: `${d.rating} Stars`, value: d.count })).sort((a, b) => a.name.localeCompare(b.name)); const renderStars = (rating: number, size = 12) => { return (
{[1, 2, 3, 4, 5].map((star) => ( ))}
); }; const handleItemClick = (item: RatedItem) => { setSelectedItem(item); setDetailsPage(0); setItemDetails([]); }; return (

Food Quality Feedback

Item-wise performance monitoring and anonymous reviews

{!stats ? (

Aggregating quality data...

) : (
{/* Summary Cards */}

Global Average

{stats.averageRating.toFixed(1)} / 5.0

Rated Products

{stats?.ratedItems?.length || 0}

Highest Rated

{stats?.ratedItems?.[0]?.name || 'N/A'}

{stats?.ratedItems?.[0]?.average?.toFixed(1) || '0.0'} avg rating

{/* Rating Distribution Chart */}

Rating Distribution

{pieData.map((_, index) => ( ))}
{/* Quality Trend / Top items chart */}

Top Performing Foods

{/* Food Items List */}

Product Performance Log

{(stats?.ratedItems || []).map((item, index) => ( handleItemClick(item)} > ))}
Food Item Average Rating Total Reviews Actions
{item.name}
Primary Menu
{item.average.toFixed(1)} {renderStars(Math.round(item.average))}
{item.count} Feedbacks
)} {/* Details Modal */} {selectedItem && (
setSelectedItem(null)} className="absolute inset-0 bg-slate-900/60 backdrop-blur-sm" />

{selectedItem.name}

{selectedItem.average.toFixed(1)}
{selectedItem.count} TOTAL REVIEWS
{/* Item Specific Breakdown */} {itemStats && (

Rating Breakdown

{[5, 4, 3, 2, 1].map(rating => { const dist = itemStats.distribution.find(d => d.rating === rating); const percentage = itemStats.totalReviews > 0 ? ((dist?.count || 0) / itemStats.totalReviews) * 100 : 0; return (
{rating} Stars
{dist?.count || 0}
) })}
)}

Customer Remarks

{detailsLoading && itemDetails.length === 0 ? (

Loading reviews...

) : itemDetails.map((detail, idx) => (
{format(new Date(detail.date), 'dd MMM yyyy, hh:mm a')}
{renderStars(detail.rating, 18)}
REMARK

"{detail.comment || 'No specific remark provided.'}"

))} {!detailsLoading && itemDetails.length === 0 && (
No detailed reviews found.
)}
{detailsTotal > detailsPageSize && (
)}
)}
); }; export default Feedback;