Add SMS campaigns

This commit is contained in:
2024-12-27 12:10:08 -05:00
parent adbaa75499
commit 7a224ee870
3 changed files with 200 additions and 142 deletions

View File

@@ -7,9 +7,19 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip";
import { DateTime } from "luxon";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { TIME_RANGES } from "@/lib/constants";
import { Mail, MessageSquare } from "lucide-react";
// Helper functions for formatting
const formatRate = (value) => {
const formatRate = (value, isSMS = false, hideForSMS = false) => {
if (isSMS && hideForSMS) return "N/A";
if (typeof value !== "number") return "0.0%";
return `${(value * 100).toFixed(1)}%`;
};
@@ -50,25 +60,46 @@ const MetricCell = ({
isMonetary = false,
showConversionRate = false,
totalRecipients = 0,
}) => (
<td className="p-2 text-center">
<div className="text-blue-600 dark:text-blue-400 text-lg font-semibold">
{isMonetary ? formatCurrency(value) : formatRate(value)}
</div>
<div className="text-gray-600 dark:text-gray-400 text-sm">
{count?.toLocaleString() || 0} {count === 1 ? "recipient" : "recipients"}
{showConversionRate &&
totalRecipients > 0 &&
` (${((count / totalRecipients) * 100).toFixed(2)}%)`}
</div>
</td>
);
isSMS = false,
hideForSMS = false,
}) => {
if (isSMS && hideForSMS) {
return (
<td className="p-2 text-center">
<div className="text-gray-500 dark:text-gray-400 text-lg font-semibold">N/A</div>
<div className="text-gray-400 dark:text-gray-500 text-sm">-</div>
</td>
);
}
const KlaviyoCampaigns = ({ className, timeRange = "last7days" }) => {
return (
<td className="p-2 text-center">
<div className="text-blue-600 dark:text-blue-400 text-lg font-semibold">
{isMonetary ? formatCurrency(value) : formatRate(value, isSMS, hideForSMS)}
</div>
<div className="text-gray-600 dark:text-gray-400 text-sm">
{count?.toLocaleString() || 0} {count === 1 ? "recipient" : "recipients"}
{showConversionRate &&
totalRecipients > 0 &&
` (${((count / totalRecipients) * 100).toFixed(2)}%)`}
</div>
</td>
);
};
const CHANNEL_OPTIONS = [
{ value: "all", label: "All Campaigns" },
{ value: "email", label: "Email Only" },
{ value: "sms", label: "SMS Only" },
];
const KlaviyoCampaigns = ({ className }) => {
const [campaigns, setCampaigns] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
const [searchTerm, setSearchTerm] = useState("");
const [selectedChannel, setSelectedChannel] = useState("all");
const [selectedTimeRange, setSelectedTimeRange] = useState("last7days");
const [sortConfig, setSortConfig] = useState({
key: "send_time",
direction: "desc",
@@ -77,7 +108,9 @@ const KlaviyoCampaigns = ({ className, timeRange = "last7days" }) => {
const fetchCampaigns = async () => {
try {
setIsLoading(true);
const response = await fetch(`/api/klaviyo/reporting/campaigns/${timeRange}`);
const response = await fetch(
`/api/klaviyo/reporting/campaigns/${selectedTimeRange}${selectedChannel !== 'all' ? `?channel=${selectedChannel}` : ''}`
);
if (!response.ok) {
throw new Error(`Failed to fetch campaigns: ${response.status}`);
@@ -98,7 +131,7 @@ const KlaviyoCampaigns = ({ className, timeRange = "last7days" }) => {
fetchCampaigns();
const interval = setInterval(fetchCampaigns, 10 * 60 * 1000); // Refresh every 10 minutes
return () => clearInterval(interval);
}, [timeRange]);
}, [selectedTimeRange, selectedChannel]);
// Sort campaigns
const sortedCampaigns = [...campaigns].sort((a, b) => {
@@ -109,10 +142,11 @@ const KlaviyoCampaigns = ({ className, timeRange = "last7days" }) => {
return direction * (a[sortConfig.key] - b[sortConfig.key]);
});
// Filter campaigns by search term
// Filter campaigns by search term and channel
const filteredCampaigns = sortedCampaigns.filter(
(campaign) =>
campaign?.name?.toLowerCase().includes((searchTerm || "").toLowerCase())
campaign?.name?.toLowerCase().includes((searchTerm || "").toLowerCase()) &&
(selectedChannel === "all" || campaign?.channel === selectedChannel)
);
if (isLoading) {
@@ -132,9 +166,37 @@ const KlaviyoCampaigns = ({ className, timeRange = "last7days" }) => {
<Card className="h-full bg-white dark:bg-gray-900">
{error && <ErrorAlert description={error} />}
<CardHeader className="pb-2">
<CardTitle className="text-xl font-semibold text-gray-900 dark:text-gray-100">
Email Campaigns
</CardTitle>
<div className="flex justify-between items-center">
<CardTitle className="text-xl font-semibold text-gray-900 dark:text-gray-100">
Campaigns
</CardTitle>
<div className="flex gap-2">
<Select value={selectedChannel} onValueChange={setSelectedChannel}>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select channel" />
</SelectTrigger>
<SelectContent>
{CHANNEL_OPTIONS.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
<Select value={selectedTimeRange} onValueChange={setSelectedTimeRange}>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select time range" />
</SelectTrigger>
<SelectContent>
{TIME_RANGES.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
</CardHeader>
<CardContent className="overflow-y-auto pl-4 max-h-[350px] mb-4">
<table className="w-full">
@@ -170,8 +232,15 @@ const KlaviyoCampaigns = ({ className, timeRange = "last7days" }) => {
<Tooltip>
<TooltipTrigger asChild>
<td className="p-2 align-top">
<div className="font-medium text-gray-900 dark:text-gray-100">
{campaign.name}
<div className="flex items-center gap-2">
<div className="font-medium text-gray-900 dark:text-gray-100">
{campaign.name}
</div>
{campaign.channel === 'sms' ? (
<MessageSquare className="h-4 w-4 text-gray-500" />
) : (
<Mail className="h-4 w-4 text-gray-500" />
)}
</div>
<div className="text-sm text-gray-500 dark:text-gray-400 truncate max-w-[300px]">
{campaign.subject}
@@ -201,21 +270,27 @@ const KlaviyoCampaigns = ({ className, timeRange = "last7days" }) => {
value={campaign.stats.delivery_rate}
count={campaign.stats.delivered}
totalRecipients={campaign.stats.recipients}
isSMS={campaign.channel === 'sms'}
/>
<MetricCell
value={campaign.stats.open_rate}
count={campaign.stats.opens_unique}
totalRecipients={campaign.stats.recipients}
isSMS={campaign.channel === 'sms'}
hideForSMS={true}
/>
<MetricCell
value={campaign.stats.click_rate}
count={campaign.stats.clicks_unique}
totalRecipients={campaign.stats.recipients}
isSMS={campaign.channel === 'sms'}
/>
<MetricCell
value={campaign.stats.click_to_open_rate}
count={campaign.stats.clicks_unique}
totalRecipients={campaign.stats.opens_unique}
isSMS={campaign.channel === 'sms'}
hideForSMS={true}
/>
<MetricCell
value={campaign.stats.conversion_value}
@@ -223,6 +298,7 @@ const KlaviyoCampaigns = ({ className, timeRange = "last7days" }) => {
isMonetary={true}
showConversionRate={true}
totalRecipients={campaign.stats.recipients}
isSMS={campaign.channel === 'sms'}
/>
</tr>
))}