Add missing columns and fix formatting

This commit is contained in:
2024-12-27 11:29:06 -05:00
parent 59fdb221cb
commit adbaa75499

View File

@@ -1,5 +1,4 @@
import React, { useState, useEffect } from "react";
import axios from "axios";
import React, { useState, useEffect, useRef } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
Tooltip,
@@ -8,24 +7,6 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip";
import { DateTime } from "luxon";
import { Loader2, AlertCircle } from "lucide-react";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { TIME_RANGES } from "@/lib/constants";
// Helper functions for formatting
const formatRate = (value) => {
@@ -55,6 +36,13 @@ const TableSkeleton = () => (
</div>
);
// Error alert component
const ErrorAlert = ({ description }) => (
<div className="p-4 mb-4 text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-900/10 rounded-lg">
{description}
</div>
);
// MetricCell component for displaying campaign metrics
const MetricCell = ({
value,
@@ -76,45 +64,58 @@ const MetricCell = ({
</td>
);
const KlaviyoCampaigns = ({
className,
timeRange = "last7days",
onTimeRangeChange,
title = "Email Campaigns",
description
}) => {
const KlaviyoCampaigns = ({ className, timeRange = "last7days" }) => {
const [campaigns, setCampaigns] = useState([]);
const [loading, setLoading] = useState(true);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
const [selectedTimeRange, setSelectedTimeRange] = useState(timeRange);
useEffect(() => {
fetchCampaigns();
}, [selectedTimeRange]);
const [searchTerm, setSearchTerm] = useState("");
const [sortConfig, setSortConfig] = useState({
key: "send_time",
direction: "desc",
});
const fetchCampaigns = async () => {
try {
setLoading(true);
setIsLoading(true);
const response = await fetch(`/api/klaviyo/reporting/campaigns/${timeRange}`);
if (!response.ok) {
throw new Error(`Failed to fetch campaigns: ${response.status}`);
}
const data = await response.json();
setCampaigns(data.data || []);
setError(null);
const response = await axios.get(`/api/klaviyo/reporting/campaigns/${selectedTimeRange}`);
setCampaigns(response.data.data || []);
} catch (error) {
console.error("Error fetching campaigns:", error);
setError(error.message);
} catch (err) {
console.error("Error fetching campaigns:", err);
setError(err.message);
} finally {
setLoading(false);
setIsLoading(false);
}
};
const handleTimeRangeChange = (value) => {
setSelectedTimeRange(value);
if (onTimeRangeChange) {
onTimeRangeChange(value);
}
};
useEffect(() => {
fetchCampaigns();
const interval = setInterval(fetchCampaigns, 10 * 60 * 1000); // Refresh every 10 minutes
return () => clearInterval(interval);
}, [timeRange]);
if (loading) {
// Sort campaigns
const sortedCampaigns = [...campaigns].sort((a, b) => {
const direction = sortConfig.direction === "desc" ? -1 : 1;
if (sortConfig.key === "send_time") {
return direction * (DateTime.fromISO(a.send_time) - DateTime.fromISO(b.send_time));
}
return direction * (a[sortConfig.key] - b[sortConfig.key]);
});
// Filter campaigns by search term
const filteredCampaigns = sortedCampaigns.filter(
(campaign) =>
campaign?.name?.toLowerCase().includes((searchTerm || "").toLowerCase())
);
if (isLoading) {
return (
<Card className="h-full bg-white dark:bg-gray-900">
<CardHeader>
@@ -129,85 +130,73 @@ const KlaviyoCampaigns = ({
return (
<Card className="h-full bg-white dark:bg-gray-900">
{error && (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertTitle>Error</AlertTitle>
<AlertDescription>Failed to load campaigns: {error}</AlertDescription>
</Alert>
)}
{error && <ErrorAlert description={error} />}
<CardHeader className="pb-2">
<div className="flex justify-between items-start">
<CardTitle className="text-xl font-semibold text-gray-900 dark:text-gray-100">
{title}
</CardTitle>
<Select value={selectedTimeRange} onValueChange={handleTimeRangeChange}>
<SelectTrigger className="w-[130px]">
<SelectValue placeholder="Select time range" />
</SelectTrigger>
<SelectContent>
{TIME_RANGES.map((range) => (
<SelectItem key={range.value} value={range.value}>
{range.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<CardTitle className="text-xl font-semibold text-gray-900 dark:text-gray-100">
Email Campaigns
</CardTitle>
</CardHeader>
<CardContent className="overflow-y-auto pl-4 max-h-[350px]">
<Table>
<TableHeader>
<TableRow>
<TableHead className="font-medium">Campaign</TableHead>
<TableHead className="text-center font-medium">Delivery</TableHead>
<TableHead className="text-center font-medium">Opens</TableHead>
<TableHead className="text-center font-medium">Clicks</TableHead>
<TableHead className="text-center font-medium">Orders</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{campaigns.map((campaign) => (
<TableRow key={campaign.id}>
<TableCell className="align-top">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div>
<div className="font-medium text-gray-900 dark:text-gray-100">
{campaign.name || "Unnamed Campaign"}
</div>
<div className="text-sm text-gray-500 dark:text-gray-400 truncate max-w-[300px]">
{campaign.subject || "No subject"}
</div>
<div className="text-xs text-gray-400 dark:text-gray-500">
{campaign.send_time
? DateTime.fromISO(campaign.send_time).toLocaleString(
DateTime.DATETIME_MED
)
: "No date"}
</div>
<CardContent className="overflow-y-auto pl-4 max-h-[350px] mb-4">
<table className="w-full">
<thead>
<tr>
<th className="p-2 text-left font-medium sticky top-0 bg-white dark:bg-gray-900 z-10 text-gray-900 dark:text-gray-100">
Campaign
</th>
<th className="p-2 font-medium text-center sticky top-0 bg-white dark:bg-gray-900 z-10 text-gray-900 dark:text-gray-100">
Delivery
</th>
<th className="p-2 font-medium text-center sticky top-0 bg-white dark:bg-gray-900 z-10 text-gray-900 dark:text-gray-100">
Opens
</th>
<th className="p-2 font-medium text-center sticky top-0 bg-white dark:bg-gray-900 z-10 text-gray-900 dark:text-gray-100">
Clicks
</th>
<th className="p-2 font-medium text-center sticky top-0 bg-white dark:bg-gray-900 z-10 text-gray-900 dark:text-gray-100">
CTR
</th>
<th className="p-2 font-medium text-center sticky top-0 bg-white dark:bg-gray-900 z-10 text-gray-900 dark:text-gray-100">
Orders
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-gray-800">
{filteredCampaigns.map((campaign) => (
<tr
key={campaign.id}
className="hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors"
>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<td className="p-2 align-top">
<div className="font-medium text-gray-900 dark:text-gray-100">
{campaign.name}
</div>
</TooltipTrigger>
<TooltipContent
side="top"
className="break-words bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 border dark:border-gray-700"
>
<p className="font-medium">
{campaign.name || "Unnamed Campaign"}
</p>
<p>{campaign.subject || "No subject"}</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
<div className="text-sm text-gray-500 dark:text-gray-400 truncate max-w-[300px]">
{campaign.subject}
</div>
<div className="text-xs text-gray-400 dark:text-gray-500">
{campaign.send_time
? DateTime.fromISO(campaign.send_time).toLocaleString(
DateTime.DATETIME_MED
)
? DateTime.fromISO(campaign.send_time).toLocaleString(DateTime.DATETIME_MED)
: "No date"}
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</TableCell>
</div>
</td>
</TooltipTrigger>
<TooltipContent
side="top"
className="break-words bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 border dark:border-gray-700"
>
<p className="font-medium">{campaign.name}</p>
<p>{campaign.subject}</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
{campaign.send_time
? DateTime.fromISO(campaign.send_time).toLocaleString(DateTime.DATETIME_MED)
: "No date"}
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<MetricCell
value={campaign.stats.delivery_rate}
count={campaign.stats.delivered}
@@ -223,6 +212,11 @@ const KlaviyoCampaigns = ({
count={campaign.stats.clicks_unique}
totalRecipients={campaign.stats.recipients}
/>
<MetricCell
value={campaign.stats.click_to_open_rate}
count={campaign.stats.clicks_unique}
totalRecipients={campaign.stats.opens_unique}
/>
<MetricCell
value={campaign.stats.conversion_value}
count={campaign.stats.conversion_uniques}
@@ -230,10 +224,10 @@ const KlaviyoCampaigns = ({
showConversionRate={true}
totalRecipients={campaign.stats.recipients}
/>
</TableRow>
</tr>
))}
</TableBody>
</Table>
</tbody>
</table>
</CardContent>
</Card>
);