Initial digitCRM project with backend and frontend
This commit is contained in:
192
frontend/client/src/pages/Analytics.tsx
Normal file
192
frontend/client/src/pages/Analytics.tsx
Normal file
@@ -0,0 +1,192 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { analyticsAPI } from '@/lib/api';
|
||||
import { BarChart, Bar, PieChart, Pie, Cell, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, LineChart, Line } from 'recharts';
|
||||
import DashboardLayout from '@/components/DashboardLayout';
|
||||
|
||||
const COLORS = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899'];
|
||||
|
||||
export default function Analytics() {
|
||||
const [contactsByStatus, setContactsByStatus] = useState<any[]>([]);
|
||||
const [contactsByType, setContactsByType] = useState<any[]>([]);
|
||||
const [ticketsByStatus, setTicketsByStatus] = useState<any[]>([]);
|
||||
const [ticketsByPriority, setTicketsByPriority] = useState<any[]>([]);
|
||||
const [feedbackByRating, setFeedbackByRating] = useState<any[]>([]);
|
||||
const [campaignPerformance, setCampaignPerformance] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
fetchAnalytics();
|
||||
}, []);
|
||||
|
||||
const fetchAnalytics = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const [
|
||||
statusRes,
|
||||
typeRes,
|
||||
ticketsStatusRes,
|
||||
ticketsPriorityRes,
|
||||
feedbackRes,
|
||||
campaignRes,
|
||||
] = await Promise.all([
|
||||
analyticsAPI.getContactsByStatus(),
|
||||
analyticsAPI.getContactsByType(),
|
||||
analyticsAPI.getTicketsByStatus(),
|
||||
analyticsAPI.getTicketsByPriority(),
|
||||
analyticsAPI.getFeedbackByRating(),
|
||||
analyticsAPI.getCampaignPerformance(),
|
||||
]);
|
||||
|
||||
setContactsByStatus(statusRes.data);
|
||||
setContactsByType(typeRes.data);
|
||||
setTicketsByStatus(ticketsStatusRes.data);
|
||||
setTicketsByPriority(ticketsPriorityRes.data);
|
||||
setFeedbackByRating(feedbackRes.data);
|
||||
setCampaignPerformance(campaignRes.data);
|
||||
} catch (error) {
|
||||
console.error('Error fetching analytics:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<div className="text-center py-12">Зареждане...</div>
|
||||
</DashboardLayout>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<div className="space-y-6">
|
||||
<h1 className="text-3xl font-bold">Аналитика</h1>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{/* Contacts by Status */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Контакти по статус</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={contactsByStatus}
|
||||
dataKey="Count"
|
||||
nameKey="Status"
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
outerRadius={80}
|
||||
label
|
||||
>
|
||||
{contactsByStatus.map((entry, index) => (
|
||||
<Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
|
||||
))}
|
||||
</Pie>
|
||||
<Tooltip />
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Contacts by Type */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Контакти по тип</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<BarChart data={contactsByType}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="ContactType" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Bar dataKey="Count" fill="#3b82f6" />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Tickets by Status */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Билети по статус</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<BarChart data={ticketsByStatus}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="Status" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Bar dataKey="Count" fill="#10b981" />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Tickets by Priority */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Билети по приоритет</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<BarChart data={ticketsByPriority}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="Priority" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Bar dataKey="Count" fill="#f59e0b" />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Feedback by Rating */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Обратна връзка по рейтинг</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ResponsiveContainer width="100%" height={300}>
|
||||
<BarChart data={feedbackByRating}>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="Rating" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Bar dataKey="Count" fill="#ef4444" />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Campaign Performance */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Производителност на кампаниите</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4 max-h-80 overflow-y-auto">
|
||||
{campaignPerformance.map((campaign, index) => (
|
||||
<div key={index} className="border-b pb-4 last:border-b-0">
|
||||
<h4 className="font-semibold text-sm">{campaign.CampaignName}</h4>
|
||||
<div className="grid grid-cols-2 gap-2 mt-2 text-xs text-muted-foreground">
|
||||
<div>Изпратени: {campaign.Sent}</div>
|
||||
<div>Отворени: {campaign.Opened}</div>
|
||||
<div>Кликнати: {campaign.Clicked}</div>
|
||||
<div>Open Rate: {campaign.OpenRate}%</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</DashboardLayout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user