| "use client"; |
|
|
| import { useEffect, useState } from "react"; |
| import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; |
| import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; |
| import { TrendingUp, TrendingDown, Mail, MousePointer, Reply } from "lucide-react"; |
| import { useApi } from "@/hooks/use-api"; |
|
|
| interface CampaignMetrics { |
| totalSent: number; |
| delivered: number; |
| opened: number; |
| clicked: number; |
| replied: number; |
| bounced: number; |
| deliveryRate: number; |
| openRate: number; |
| clickRate: number; |
| replyRate: number; |
| } |
|
|
| interface AnalyticsDashboardProps { |
| userId?: string; |
| } |
|
|
| export function AnalyticsDashboard({ userId }: AnalyticsDashboardProps) { |
| const [metrics, setMetrics] = useState<CampaignMetrics | null>(null); |
| const { get, loading } = useApi<CampaignMetrics>(); |
|
|
| useEffect(() => { |
| async function fetchAnalytics() { |
| const data = await get("/api/analytics"); |
| if (data) { |
| setMetrics(data); |
| } |
| } |
|
|
| fetchAnalytics(); |
| }, [get, userId]); |
|
|
| if (loading) { |
| return ( |
| <div className="space-y-6"> |
| <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4"> |
| {Array.from({ length: 4 }).map((_, i) => ( |
| <Card key={i}> |
| <CardHeader className="space-y-2"> |
| <div className="h-4 w-24 bg-muted animate-pulse rounded" /> |
| <div className="h-8 w-32 bg-muted animate-pulse rounded" /> |
| </CardHeader> |
| </Card> |
| ))} |
| </div> |
| </div> |
| ); |
| } |
|
|
| |
| const data = metrics || { |
| totalSent: 0, |
| delivered: 0, |
| opened: 0, |
| clicked: 0, |
| replied: 0, |
| bounced: 0, |
| deliveryRate: 0, |
| openRate: 0, |
| clickRate: 0, |
| replyRate: 0, |
| }; |
|
|
| return ( |
| <div className="space-y-6"> |
| <div> |
| <h2 className="text-3xl font-bold tracking-tight">Analytics Dashboard</h2> |
| <p className="text-muted-foreground"> |
| Track your email campaign performance and metrics |
| </p> |
| </div> |
| |
| <Tabs defaultValue="overview" className="space-y-4"> |
| <TabsList> |
| <TabsTrigger value="overview">Overview</TabsTrigger> |
| <TabsTrigger value="campaigns">Campaigns</TabsTrigger> |
| <TabsTrigger value="engagement">Engagement</TabsTrigger> |
| </TabsList> |
| |
| <TabsContent value="overview" className="space-y-4"> |
| {/* Key Metrics */} |
| <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4"> |
| <Card> |
| <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> |
| <CardTitle className="text-sm font-medium">Emails Sent</CardTitle> |
| <Mail className="h-4 w-4 text-muted-foreground" /> |
| </CardHeader> |
| <CardContent> |
| <div className="text-2xl font-bold">{data.totalSent.toLocaleString()}</div> |
| <p className="text-xs text-muted-foreground"> |
| {data.delivered} delivered ({data.deliveryRate.toFixed(1)}%) |
| </p> |
| </CardContent> |
| </Card> |
| |
| <Card> |
| <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> |
| <CardTitle className="text-sm font-medium">Open Rate</CardTitle> |
| {data.openRate > 25 ? ( |
| <TrendingUp className="h-4 w-4 text-green-600" /> |
| ) : ( |
| <TrendingDown className="h-4 w-4 text-red-600" /> |
| )} |
| </CardHeader> |
| <CardContent> |
| <div className="text-2xl font-bold">{data.openRate.toFixed(1)}%</div> |
| <p className="text-xs text-muted-foreground"> |
| {data.opened} of {data.delivered} emails |
| </p> |
| </CardContent> |
| </Card> |
| |
| <Card> |
| <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> |
| <CardTitle className="text-sm font-medium">Click Rate</CardTitle> |
| <MousePointer className="h-4 w-4 text-muted-foreground" /> |
| </CardHeader> |
| <CardContent> |
| <div className="text-2xl font-bold">{data.clickRate.toFixed(1)}%</div> |
| <p className="text-xs text-muted-foreground"> |
| {data.clicked} clicks |
| </p> |
| </CardContent> |
| </Card> |
| |
| <Card> |
| <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> |
| <CardTitle className="text-sm font-medium">Reply Rate</CardTitle> |
| <Reply className="h-4 w-4 text-muted-foreground" /> |
| </CardHeader> |
| <CardContent> |
| <div className="text-2xl font-bold">{data.replyRate.toFixed(1)}%</div> |
| <p className="text-xs text-muted-foreground"> |
| {data.replied} replies |
| </p> |
| </CardContent> |
| </Card> |
| </div> |
| |
| {/* Detailed Metrics */} |
| <Card> |
| <CardHeader> |
| <CardTitle>Performance Breakdown</CardTitle> |
| <CardDescription>Detailed metrics for your campaigns</CardDescription> |
| </CardHeader> |
| <CardContent className="space-y-4"> |
| <div className="space-y-2"> |
| <div className="flex items-center justify-between"> |
| <span className="text-sm font-medium">Delivery Rate</span> |
| <span className="text-sm text-muted-foreground">{data.deliveryRate.toFixed(1)}%</span> |
| </div> |
| <div className="h-2 rounded-full bg-muted overflow-hidden"> |
| <div |
| className="h-full bg-green-600 transition-all" |
| style={{ width: `${data.deliveryRate}%` }} |
| /> |
| </div> |
| </div> |
| |
| <div className="space-y-2"> |
| <div className="flex items-center justify-between"> |
| <span className="text-sm font-medium">Open Rate</span> |
| <span className="text-sm text-muted-foreground">{data.openRate.toFixed(1)}%</span> |
| </div> |
| <div className="h-2 rounded-full bg-muted overflow-hidden"> |
| <div |
| className="h-full bg-blue-600 transition-all" |
| style={{ width: `${data.openRate}%` }} |
| /> |
| </div> |
| </div> |
| |
| <div className="space-y-2"> |
| <div className="flex items-center justify-between"> |
| <span className="text-sm font-medium">Click Rate</span> |
| <span className="text-sm text-muted-foreground">{data.clickRate.toFixed(1)}%</span> |
| </div> |
| <div className="h-2 rounded-full bg-muted overflow-hidden"> |
| <div |
| className="h-full bg-purple-600 transition-all" |
| style={{ width: `${data.clickRate}%` }} |
| /> |
| </div> |
| </div> |
| |
| <div className="space-y-2"> |
| <div className="flex items-center justify-between"> |
| <span className="text-sm font-medium">Reply Rate</span> |
| <span className="text-sm text-muted-foreground">{data.replyRate.toFixed(1)}%</span> |
| </div> |
| <div className="h-2 rounded-full bg-muted overflow-hidden"> |
| <div |
| className="h-full bg-orange-600 transition-all" |
| style={{ width: `${data.replyRate}%` }} |
| /> |
| </div> |
| </div> |
| |
| {data.bounced > 0 && ( |
| <div className="pt-4 border-t"> |
| <div className="flex items-center justify-between text-sm"> |
| <span className="text-muted-foreground">Bounced Emails</span> |
| <span className="font-medium text-destructive">{data.bounced}</span> |
| </div> |
| </div> |
| )} |
| </CardContent> |
| </Card> |
| </TabsContent> |
| |
| <TabsContent value="campaigns" className="space-y-4"> |
| <Card> |
| <CardHeader> |
| <CardTitle>Campaign Overview</CardTitle> |
| <CardDescription>Performance metrics across all campaigns</CardDescription> |
| </CardHeader> |
| <CardContent> |
| <div className="text-center py-8 text-muted-foreground"> |
| <p>Campaign comparison data will be available once you have multiple active campaigns.</p> |
| </div> |
| </CardContent> |
| </Card> |
| </TabsContent> |
| |
| <TabsContent value="engagement" className="space-y-4"> |
| <Card> |
| <CardHeader> |
| <CardTitle>Engagement Metrics</CardTitle> |
| <CardDescription>Track how recipients interact with your emails</CardDescription> |
| </CardHeader> |
| <CardContent> |
| <div className="space-y-4"> |
| <div className="grid gap-4 md:grid-cols-3"> |
| <div className="space-y-2"> |
| <p className="text-sm font-medium">Total Engagements</p> |
| <p className="text-3xl font-bold">{data.opened + data.clicked + data.replied}</p> |
| </div> |
| <div className="space-y-2"> |
| <p className="text-sm font-medium">Unique Opens</p> |
| <p className="text-3xl font-bold">{data.opened}</p> |
| </div> |
| <div className="space-y-2"> |
| <p className="text-sm font-medium">Link Clicks</p> |
| <p className="text-3xl font-bold">{data.clicked}</p> |
| </div> |
| </div> |
| </div> |
| </CardContent> |
| </Card> |
| </TabsContent> |
| </Tabs> |
| </div> |
| ); |
| } |
|
|