import { useState, useEffect } from 'react'
import axios from 'axios'
import {
BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer,
PieChart, Pie, Cell, LineChart, Line, Legend
} from 'recharts'
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8085/api'
// Color palette
const COLORS = ['#0d6efd', '#198754', '#ffc107', '#dc3545', '#6c757d', '#0dcaf0', '#6610f2', '#d63384']
function Summary({ startDate, endDate }) {
const [analytics, setAnalytics] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
fetchAnalytics()
}, [startDate, endDate])
const fetchAnalytics = async () => {
setLoading(true)
setError(null)
try {
const params = {}
if (startDate) params.start = startDate.toISOString()
if (endDate) params.end = endDate.toISOString()
const response = await axios.get(`${API_BASE}/analytics`, { params })
setAnalytics(response.data)
} catch (err) {
console.error('Error fetching analytics:', err)
setError('Failed to load analytics')
} finally {
setLoading(false)
}
}
const formatDuration = (seconds) => {
const hours = Math.floor(seconds / 3600)
const minutes = Math.floor((seconds % 3600) / 60)
if (hours > 0) return `${hours}h ${minutes}m`
return `${minutes}m`
}
const formatHour = (hour) => {
if (hour === 0) return '12 AM'
if (hour === 12) return '12 PM'
return hour < 12 ? `${hour} AM` : `${hour - 12} PM`
}
const formatPhoneNumber = (phone) => {
if (!phone) return ''
// Remove all non-digit characters
const digits = phone.replace(/\D/g, '')
// Format as (XXX) XXX-XXXX if 10 digits, or +X (XXX) XXX-XXXX if 11
if (digits.length === 10) {
return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`
} else if (digits.length === 11 && digits[0] === '1') {
return `+1 (${digits.slice(1, 4)}) ${digits.slice(4, 7)}-${digits.slice(7)}`
}
return phone
}
if (loading) {
return (
Loading...
Loading analytics...
)
}
if (error) {
return (
)
}
if (!analytics) return null
// Prepare data for message type pie chart
const messageTypeData = [
{ name: 'Sent', value: analytics.total_sent },
{ name: 'Received', value: analytics.total_received }
].filter(d => d.value > 0)
// Prepare data for call type pie chart
const callTypeData = [
{ name: 'Incoming', value: analytics.incoming_calls },
{ name: 'Outgoing', value: analytics.outgoing_calls },
{ name: 'Missed', value: analytics.missed_calls }
].filter(d => d.value > 0)
// Prepare top contacts data with display names
const topContactsData = (analytics.top_contacts || []).slice(0, 8).map(c => ({
...c,
displayName: c.contact_name || formatPhoneNumber(c.address) || c.address
}))
return (
{/* Summary Stats Cards */}
{(analytics.total_sms + analytics.total_mms).toLocaleString()}
Total Messages
{analytics.total_calls.toLocaleString()}
Total Calls
{formatDuration(analytics.total_call_duration)}
Call Duration
{Math.round(analytics.avg_message_length)}
Avg Chars/Msg
{/* Charts Row 1: Sent/Received + Top Contacts */}
{/* Sent vs Received Pie */}
Sent vs Received
{messageTypeData.length > 0 ? (
`${(percent * 100).toFixed(0)}%`}
labelLine={true}
>
{messageTypeData.map((entry, index) => (
|
))}
[value.toLocaleString(), name]} />
) : (
No message data
)}
{/* Top Contacts */}
Top Contacts
{topContactsData.length > 0 ? (
[value.toLocaleString(), 'Messages']}
labelFormatter={(label) => label}
/>
) : (
No contact data
)}
{/* Charts Row 2: Hourly Distribution */}
Messages by Time of Day
{analytics.hourly_distribution && analytics.hourly_distribution.some(h => h.count > 0) ? (
formatHour(hour)}
formatter={(value) => [value.toLocaleString(), 'Messages']}
/>
) : (
No hourly data
)}
{/* Charts Row 3: Daily Trend */}
Message Trend Over Time
{analytics.daily_trend && analytics.daily_trend.length > 0 ? (
{
const d = new Date(date)
return `${d.getMonth() + 1}/${d.getDate()}`
}}
interval="preserveStartEnd"
/>
new Date(date).toLocaleDateString()}
formatter={(value) => [value.toLocaleString(), 'Messages']}
/>
) : (
No trend data
)}
{/* Call Statistics */}
{analytics.total_calls > 0 && (
Call Breakdown
{callTypeData.length > 0 ? (
`${name}: ${value}`}
>
{callTypeData.map((entry, index) => (
|
))}
) : (
No call data
)}
)}
)
}
export default Summary