Overview
The Analytics component provides a comprehensive data visualization dashboard that displays key business metrics and order analytics. It uses Recharts for interactive visualizations and integrates with Redux to analyze pizza catalog and order history data.
Location : src/components/dashboard/Analytics.tsx
Features
Revenue metrics : Total revenue, order count, and average order value
Price comparison chart : Horizontal bar chart showing all pizza prices
Category distribution : Pie chart visualizing order distribution by category
Responsive design : Adapts from mobile to desktop layouts
Empty states : Helpful messages when no data is available
Interactive tooltips : Hover states on chart elements
Framer Motion animations : Smooth hover effects on stat cards
Props
This component has no props. It reads data directly from Redux.
Redux State
Required State Slices
interface RootState {
pizza : {
pizzas : Pizza [];
};
order : {
orderHistory : Order [];
};
}
Redux Integration
import { useAppSelector } from '../../store/index.ts' ;
const { pizzas } = useAppSelector (( state ) => state . pizza );
const { orderHistory } = useAppSelector (( state ) => state . order );
Usage Example
import Analytics from './components/dashboard/Analytics' ;
function DashboardPage () {
return (
< div className = "container mx-auto px-4 py-8" >
< h1 className = "text-4xl font-black mb-8" > Analytics Dashboard </ h1 >
< Analytics />
</ div >
);
}
Component Structure
The component is organized into two main sections:
Stats Overview : Three metric cards showing key performance indicators
Charts Grid : Two-column layout with price chart and category distribution
< div className = "space-y-8 mb-12" >
{ /* Stats Overview */ }
< div className = "grid grid-cols-1 md:grid-cols-3 gap-6" >
{ /* Revenue, Orders, Avg Order Value */ }
</ div >
{ /* Charts Grid */ }
< div className = "grid grid-cols-1 lg:grid-cols-2 gap-8" >
{ /* Price Chart */ }
{ /* Category Distribution */ }
</ div >
</ div >
Statistics Cards
Three animated stat cards display key metrics:
Total Revenue
const totalRevenue = orderHistory . reduce (
( acc , order ) => acc + order . finalTotal ,
0
);
< motion.div
whileHover = { { y: - 5 } }
className = "bg-white/60 backdrop-blur-md p-6 rounded-3xl border border-white/20 shadow-xl flex items-center gap-4"
>
< div className = "p-4 bg-orange-100 text-orange-600 rounded-2xl" >
< DollarSign size = { 24 } />
</ div >
< div >
< p className = "text-[10px] font-black uppercase tracking-widest text-gray-400" >
Total Revenue
</ p >
< p className = "text-2xl font-black text-gray-900" >
$ { totalRevenue . toFixed ( 2 ) }
</ p >
</ div >
</ motion.div >
Orders Confirmed
const totalOrders = orderHistory . length ;
< motion.div
whileHover = { { y: - 5 } }
className = "bg-white/60 backdrop-blur-md p-6 rounded-3xl border border-white/20 shadow-xl flex items-center gap-4"
>
< div className = "p-4 bg-blue-100 text-blue-600 rounded-2xl" >
< Activity size = { 24 } />
</ div >
< div >
< p className = "text-[10px] font-black uppercase tracking-widest text-gray-400" >
Orders Confirmed
</ p >
< p className = "text-2xl font-black text-gray-900" > { totalOrders } </ p >
</ div >
</ motion.div >
Average Order Value
const avgOrderValue = totalOrders > 0 ? totalRevenue / totalOrders : 0 ;
< motion.div
whileHover = { { y: - 5 } }
className = "bg-white/60 backdrop-blur-md p-6 rounded-3xl border border-white/20 shadow-xl flex items-center gap-4"
>
< div className = "p-4 bg-green-100 text-green-600 rounded-2xl" >
< TrendingUp size = { 24 } />
</ div >
< div >
< p className = "text-[10px] font-black uppercase tracking-widest text-gray-400" >
Avg. Order Value
</ p >
< p className = "text-2xl font-black text-gray-900" >
$ { avgOrderValue . toFixed ( 2 ) }
</ p >
</ div >
</ motion.div >
Price Comparison Chart
Horizontal bar chart showing pizza prices sorted from highest to lowest:
Data Preparation
const priceData = pizzas . map ( p => ({
name: p . name ,
price: p . price
})). sort (( a , b ) => b . price - a . price );
Chart Implementation
< div className = "bg-white/60 backdrop-blur-md p-8 rounded-4xl border border-white/20 shadow-2xl" >
< div className = "flex items-center gap-3 mb-8" >
< Activity className = "text-orange-600" size = { 24 } />
< h3 className = "text-lg font-black uppercase tracking-tighter text-gray-900" >
Pizza Price Comparison
</ h3 >
</ div >
< div className = "h-[300px] w-full" >
< ResponsiveContainer width = "100%" height = "100%" >
< BarChart data = { priceData } layout = "vertical" margin = { { left: 20 } } >
< CartesianGrid strokeDasharray = "3 3" horizontal = { false } stroke = "#e5e7eb" />
< XAxis type = "number" hide />
< YAxis
dataKey = "name"
type = "category"
axisLine = { false }
tickLine = { false }
width = { 100 }
tick = { { fontSize: 10 , fontWeight: 700 , fill: '#4b5563' } }
/>
< Tooltip
cursor = { { fill: '#fff7ed' } }
contentStyle = { {
borderRadius: '16px' ,
border: 'none' ,
boxShadow: '0 10px 15px -3px rgb(0 0 0 / 0.1)' ,
fontSize: '12px' ,
fontWeight: 800
} }
/>
< Bar dataKey = "price" fill = "#ea580c" radius = { [ 0 , 10 , 10 , 0 ] } barSize = { 20 } />
</ BarChart >
</ ResponsiveContainer >
</ div >
</ div >
Chart Configuration :
Layout : vertical for horizontal bars
Grid : Vertical lines only (horizontal={false})
Y-axis : Shows pizza names (100px width)
X-axis : Hidden (hide={true})
Bars : Orange fill (#ea580c) with rounded right corners
Tooltip : Styled with rounded corners and shadow
Category Distribution Chart
Pie chart visualizing order distribution by pizza category:
Data Preparation
const categoryCounts : Record < string , number > = {};
orderHistory . forEach ( order => {
order . items . forEach ( item => {
const cat = item . pizza . category ;
categoryCounts [ cat ] = ( categoryCounts [ cat ] || 0 ) + item . quantity ;
});
});
const categoryData = Object . entries ( categoryCounts ). map (([ name , value ]) => ({
name ,
value
}));
Data Structure :
Aggregates quantities across all order items
Groups by pizza category
Converts to array format for Recharts
Chart Implementation
< div className = "bg-white/60 backdrop-blur-md p-8 rounded-4xl border border-white/20 shadow-2xl" >
< div className = "flex items-center gap-3 mb-8" >
< PieChartIcon className = "text-orange-600" size = { 24 } />
< h3 className = "text-lg font-black uppercase tracking-tighter text-gray-900" >
Order Distribution
</ h3 >
</ div >
< div className = "h-[300px] w-full" >
{ categoryData . length > 0 ? (
< ResponsiveContainer width = "100%" height = "100%" >
< PieChart >
< Pie
data = { categoryData }
cx = "50%"
cy = "50%"
innerRadius = { 60 }
outerRadius = { 100 }
paddingAngle = { 8 }
dataKey = "value"
>
{ categoryData . map (( _entry , index ) => (
< Cell
key = { `cell- ${ index } ` }
fill = { COLORS [ index % COLORS . length ] }
stroke = "none"
/>
)) }
</ Pie >
< Tooltip
contentStyle = { {
borderRadius: '16px' ,
border: 'none' ,
boxShadow: '0 4px 6px -1px rgb(0 0 0 / 0.1)'
} }
/>
< Legend
verticalAlign = "bottom"
height = { 36 }
iconType = "circle"
formatter = { ( value ) => (
< span className = "text-[10px] font-black uppercase tracking-widest text-gray-500 ml-2" >
{ value }
</ span >
) }
/>
</ PieChart >
</ ResponsiveContainer >
) : (
{ /* Empty state */ }
) }
</ div >
</ div >
Color Palette
const COLORS = [ '#ea580c' , '#f97316' , '#fbbf24' , '#f59e0b' , '#dc2626' ];
Orange-red gradient palette for visual consistency.
Chart Configuration :
Type : Donut chart (innerRadius={60})
Outer radius : 100px
Padding angle : 8 degrees between segments
Colors : Cycled from COLORS array
Legend : Bottom-aligned with custom formatting
Empty State
Shown when no orders exist:
< div className = "h-full flex flex-col items-center justify-center text-gray-400" >
< PieChartIcon size = { 48 } strokeWidth = { 1 } className = "mb-4 opacity-20" />
< p className = "text-sm font-bold uppercase tracking-widest" > No orders yet </ p >
< p className = "text-[10px]" > Analytics will appear here after your first order </ p >
</ div >
Recharts Integration
The component uses Recharts library for data visualization:
import {
BarChart , Bar , XAxis , YAxis , CartesianGrid , Tooltip , ResponsiveContainer ,
PieChart , Pie , Cell , Legend
} from 'recharts' ;
Chart Components
Wrapper that makes charts responsive to container size
Container for bar chart with configurable layout and margins
Container for pie/donut charts
Interactive tooltip shown on hover
Chart legend with customizable formatting
Animations
Stat Card Hover
Framer Motion provides lift effect:
< motion.div whileHover = { { y: - 5 } } >
Cards lift 5px upward on hover for tactile feedback.
Chart Animations
Recharts provides built-in animations:
Bars animate from left to right on mount
Pie segments animate with rotation
Tooltips fade in smoothly
Responsive Design
Stats Grid
grid-cols-1 md: grid-cols-3
Mobile: Stacked vertically
Tablet+: Three columns
Charts Grid
grid-cols-1 lg: grid-cols-2
Mobile/Tablet: Stacked vertically
Desktop: Two columns side-by-side
Chart Sizing
< div className = "h-[300px] w-full" >
< ResponsiveContainer width = "100%" height = "100%" >
Fixed height (300px) with 100% width for consistent proportions.
Visual Design
Glass Morphism Cards
bg-white /60 backdrop-blur-md border border-white /20 shadow-xl
Icon Backgrounds
Colored circular backgrounds for stat icons:
bg-orange-100 text-orange-600 /* Revenue */
bg-blue-100 text-blue-600 /* Orders */
bg-green-100 text-green-600 /* Avg Value */
Typography
Stat labels: text-[10px] font-black uppercase tracking-widest
Stat values: text-2xl font-black
Chart titles: text-lg font-black uppercase tracking-tighter
Data Processing
All calculations happen in the component:
// Computed on every render (consider useMemo for large datasets)
const priceData = pizzas . map ( /* ... */ ). sort ( /* ... */ );
const categoryData = Object . entries ( categoryCounts ). map ( /* ... */ );
For production with large datasets, wrap in useMemo:
import { useMemo } from 'react' ;
const priceData = useMemo (() =>
pizzas . map ( p => ({ name: p . name , price: p . price }))
. sort (( a , b ) => b . price - a . price ),
[ pizzas ]
);
Complete Example
Full dashboard page with navigation:
import { useState } from 'react' ;
import Analytics from './components/dashboard/Analytics' ;
import { useAppSelector } from './store' ;
function DashboardPage () {
const orderHistory = useAppSelector (( state ) => state . order . orderHistory );
return (
< div className = "min-h-screen bg-gradient-to-br from-orange-50 to-red-50" >
< div className = "container mx-auto px-4 py-8" >
{ /* Header */ }
< div className = "mb-8" >
< h1 className = "text-4xl font-black text-gray-900 mb-2" >
Analytics Dashboard
</ h1 >
< p className = "text-gray-600" >
Viewing { orderHistory . length } confirmed orders
</ p >
</ div >
{ /* Analytics component */ }
< Analytics />
{ /* Additional sections */ }
< div className = "mt-12" >
< h2 className = "text-2xl font-black mb-6" > Recent Orders </ h2 >
{ /* Order list */ }
</ div >
</ div >
</ div >
);
}
Data Flow
OrderSummary (confirms order)
↓
addOrderToHistory (Redux action)
↓
orderHistory state updated
↓
Analytics component re-renders
↓
Charts updated with new data
Accessibility
Semantic HTML : Proper heading hierarchy
ARIA labels : Charts have descriptive titles
Color contrast : Text meets WCAG standards
Keyboard navigation : Interactive chart elements are keyboard accessible
Screen reader support : Recharts provides basic ARIA support
Icon Usage
Lucide React icons throughout:
import {
TrendingUp , // Avg order value
PieChart as PieChartIcon , // Category chart title
DollarSign , // Revenue
Activity // Orders count & price chart
} from 'lucide-react' ;
OrderSummary Creates orders that populate analytics data
Overview Component library architecture
External Dependencies
Recharts
Full documentation: recharts.org
Framer Motion
npm install framer-motion
Used for stat card hover animations.
Source Reference
View the complete implementation: src/components/dashboard/Analytics.tsx:1-169