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 (

{error}

) } 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

{/* 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