Add filters

This commit is contained in:
2024-12-21 19:48:53 -05:00
parent 56abb5e188
commit fdfc4656ed

View File

@@ -40,6 +40,14 @@ import {
SelectValue,
} from "@/components/ui/select";
import { Skeleton } from "@/components/ui/skeleton";
import { Button } from "@/components/ui/button";
import { Separator } from "@/components/ui/separator";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
const METRIC_IDS = {
PLACED_ORDER: "Y8cqcF",
@@ -1121,6 +1129,23 @@ const EventFeed = ({
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [lastUpdate, setLastUpdate] = useState(null);
const [activeEventTypes, setActiveEventTypes] = useState({
[METRIC_IDS.PLACED_ORDER]: true,
[METRIC_IDS.SHIPPED_ORDER]: true,
[METRIC_IDS.ACCOUNT_CREATED]: true,
[METRIC_IDS.CANCELED_ORDER]: true,
[METRIC_IDS.PAYMENT_REFUNDED]: true,
[METRIC_IDS.NEW_BLOG_POST]: true,
});
const [orderFilters, setOrderFilters] = useState({
hasPreorder: false,
localPickup: false,
isOnHold: false,
hasDigiItem: false,
hasNotions: false,
hasGiftCard: false,
stillOwes: false,
});
const fetchEvents = useCallback(async () => {
try {
@@ -1168,40 +1193,357 @@ const EventFeed = ({
};
}, [fetchEvents]);
const filteredEvents = useMemo(() => {
// Check if any order property filters are active
const hasActiveOrderFilters = Object.values(orderFilters).some(filter => filter);
return events.filter(event => {
// First check event type filter
if (!activeEventTypes[event.metric_id]) {
return false;
}
// Then check order property filters if any are active
if (hasActiveOrderFilters) {
if (event.metric_id !== METRIC_IDS.PLACED_ORDER) return false;
const details = event.event_properties || {};
if (orderFilters.hasPreorder && !details.HasPreorder) return false;
if (orderFilters.localPickup && !details.LocalPickup) return false;
if (orderFilters.isOnHold && !details.IsOnHold) return false;
if (orderFilters.hasDigiItem && !details.HasDigiItem) return false;
if (orderFilters.hasNotions && !details.HasNotions) return false;
if (orderFilters.hasGiftCard && !details.HasDigitalGC) return false;
if (orderFilters.stillOwes && !details.StillOwes) return false;
}
return true;
});
}, [events, activeEventTypes, orderFilters]);
// Calculate counts for event types and order properties
const counts = useMemo(() => {
const eventTypeCounts = {
[METRIC_IDS.PLACED_ORDER]: 0,
[METRIC_IDS.SHIPPED_ORDER]: 0,
[METRIC_IDS.ACCOUNT_CREATED]: 0,
[METRIC_IDS.CANCELED_ORDER]: 0,
[METRIC_IDS.PAYMENT_REFUNDED]: 0,
[METRIC_IDS.NEW_BLOG_POST]: 0,
};
const orderPropertyCounts = {
hasPreorder: 0,
localPickup: 0,
isOnHold: 0,
hasDigiItem: 0,
hasNotions: 0,
hasGiftCard: 0,
stillOwes: 0,
};
events.forEach(event => {
// Count event types
if (event.metric_id) {
eventTypeCounts[event.metric_id]++;
}
// Count order properties
if (event.metric_id === METRIC_IDS.PLACED_ORDER) {
const details = event.event_properties || {};
if (details.HasPreorder) orderPropertyCounts.hasPreorder++;
if (details.LocalPickup) orderPropertyCounts.localPickup++;
if (details.IsOnHold) orderPropertyCounts.isOnHold++;
if (details.HasDigiItem) orderPropertyCounts.hasDigiItem++;
if (details.HasNotions) orderPropertyCounts.hasNotions++;
if (details.HasDigitalGC) orderPropertyCounts.hasGiftCard++;
if (details.StillOwes) orderPropertyCounts.stillOwes++;
}
});
return {
eventTypes: eventTypeCounts,
orderProperties: orderPropertyCounts,
};
}, [events]);
const handleOrderPropertyClick = (property) => {
setOrderFilters(prev => {
// If clicking the active filter, clear all filters
if (prev[property]) {
return {
hasPreorder: false,
localPickup: false,
isOnHold: false,
hasDigiItem: false,
hasNotions: false,
hasGiftCard: false,
stillOwes: false,
};
}
// Otherwise, set only this filter to true
return {
hasPreorder: property === 'hasPreorder',
localPickup: property === 'localPickup',
isOnHold: property === 'isOnHold',
hasDigiItem: property === 'hasDigiItem',
hasNotions: property === 'hasNotions',
hasGiftCard: property === 'hasGiftCard',
stillOwes: property === 'stillOwes',
};
});
};
return (
<Card className="flex flex-col h-full bg-white dark:bg-gray-900/60 backdrop-blur-sm">
<CardHeader className="p-6 pb-0">
<div className="flex justify-between items-center">
<div className="flex justify-between items-start">
<div>
<CardTitle className="text-xl font-semibold text-gray-900 dark:text-gray-100">{title}</CardTitle>
</div>
{lastUpdate && (
<span className="text-sm text-muted-foreground">
<CardDescription>
Last updated: {format(lastUpdate, "hh:mm a")}
</span>
</CardDescription>
)}
</div>
<div className="flex flex-wrap gap-1">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant={activeEventTypes[METRIC_IDS.PLACED_ORDER] ? "default" : "outline"}
size="sm"
onClick={() => setActiveEventTypes(prev => ({
...prev,
[METRIC_IDS.PLACED_ORDER]: !prev[METRIC_IDS.PLACED_ORDER]
}))}
className="h-8 w-8 p-0"
>
<Package className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<div className="flex items-center gap-2">
<span>Orders</span>
<span className="text-muted-foreground">
{counts.eventTypes[METRIC_IDS.PLACED_ORDER]}
</span>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant={activeEventTypes[METRIC_IDS.SHIPPED_ORDER] ? "default" : "outline"}
size="sm"
onClick={() => setActiveEventTypes(prev => ({
...prev,
[METRIC_IDS.SHIPPED_ORDER]: !prev[METRIC_IDS.SHIPPED_ORDER]
}))}
className="h-8 w-8 p-0"
>
<Truck className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<div className="flex items-center gap-2">
<span>Shipments</span>
<span className="text-muted-foreground">
{counts.eventTypes[METRIC_IDS.SHIPPED_ORDER]}
</span>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant={activeEventTypes[METRIC_IDS.ACCOUNT_CREATED] ? "default" : "outline"}
size="sm"
onClick={() => setActiveEventTypes(prev => ({
...prev,
[METRIC_IDS.ACCOUNT_CREATED]: !prev[METRIC_IDS.ACCOUNT_CREATED]
}))}
className="h-8 w-8 p-0"
>
<UserPlus className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<div className="flex items-center gap-2">
<span>Accounts</span>
<span className="text-muted-foreground">
{counts.eventTypes[METRIC_IDS.ACCOUNT_CREATED]}
</span>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant={activeEventTypes[METRIC_IDS.CANCELED_ORDER] ? "default" : "outline"}
size="sm"
onClick={() => setActiveEventTypes(prev => ({
...prev,
[METRIC_IDS.CANCELED_ORDER]: !prev[METRIC_IDS.CANCELED_ORDER]
}))}
className="h-8 w-8 p-0"
>
<XCircle className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<div className="flex items-center gap-2">
<span>Cancellations</span>
<span className="text-muted-foreground">
{counts.eventTypes[METRIC_IDS.CANCELED_ORDER]}
</span>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant={activeEventTypes[METRIC_IDS.PAYMENT_REFUNDED] ? "default" : "outline"}
size="sm"
onClick={() => setActiveEventTypes(prev => ({
...prev,
[METRIC_IDS.PAYMENT_REFUNDED]: !prev[METRIC_IDS.PAYMENT_REFUNDED]
}))}
className="h-8 w-8 p-0"
>
<DollarSign className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<div className="flex items-center gap-2">
<span>Refunds</span>
<span className="text-muted-foreground">
{counts.eventTypes[METRIC_IDS.PAYMENT_REFUNDED]}
</span>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant={activeEventTypes[METRIC_IDS.NEW_BLOG_POST] ? "default" : "outline"}
size="sm"
onClick={() => setActiveEventTypes(prev => ({
...prev,
[METRIC_IDS.NEW_BLOG_POST]: !prev[METRIC_IDS.NEW_BLOG_POST]
}))}
className="h-8 w-8 p-0"
>
<FileText className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<div className="flex items-center gap-2">
<span>Blog Posts</span>
<span className="text-muted-foreground">
{counts.eventTypes[METRIC_IDS.NEW_BLOG_POST]}
</span>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
</div>
{/* Order Property Filters */}
<div className="flex flex-wrap gap-2 justify-center mt-4">
<span
onClick={() => handleOrderPropertyClick('hasPreorder')}
className={`px-2 py-1 ${
orderFilters.hasPreorder
? 'bg-blue-800 text-blue-100'
: 'bg-blue-100 dark:bg-blue-900/20 text-blue-800 dark:text-blue-300'
} rounded-full text-xs font-medium cursor-help`}
>
Pre-order {counts.orderProperties.hasPreorder > 0 && `(${counts.orderProperties.hasPreorder})`}
</span>
<span
onClick={() => handleOrderPropertyClick('localPickup')}
className={`px-2 py-1 ${
orderFilters.localPickup
? 'bg-purple-800 text-purple-100'
: 'bg-purple-100 dark:bg-purple-900/20 text-purple-800 dark:text-purple-300'
} rounded-full text-xs font-medium cursor-help`}
>
Local {counts.orderProperties.localPickup > 0 && `(${counts.orderProperties.localPickup})`}
</span>
<span
onClick={() => handleOrderPropertyClick('isOnHold')}
className={`px-2 py-1 ${
orderFilters.isOnHold
? 'bg-yellow-800 text-yellow-100'
: 'bg-yellow-100 dark:bg-yellow-900/20 text-yellow-800 dark:text-yellow-300'
} rounded-full text-xs font-medium cursor-help`}
>
On Hold {counts.orderProperties.isOnHold > 0 && `(${counts.orderProperties.isOnHold})`}
</span>
<span
onClick={() => handleOrderPropertyClick('hasDigiItem')}
className={`px-2 py-1 ${
orderFilters.hasDigiItem
? 'bg-green-800 text-green-100'
: 'bg-green-100 dark:bg-green-900/20 text-green-800 dark:text-green-300'
} rounded-full text-xs font-medium cursor-help`}
>
Digital {counts.orderProperties.hasDigiItem > 0 && `(${counts.orderProperties.hasDigiItem})`}
</span>
<span
onClick={() => handleOrderPropertyClick('hasNotions')}
className={`px-2 py-1 ${
orderFilters.hasNotions
? 'bg-pink-800 text-pink-100'
: 'bg-pink-100 dark:bg-pink-900/20 text-pink-800 dark:text-pink-300'
} rounded-full text-xs font-medium cursor-help`}
>
Notions {counts.orderProperties.hasNotions > 0 && `(${counts.orderProperties.hasNotions})`}
</span>
<span
onClick={() => handleOrderPropertyClick('hasGiftCard')}
className={`px-2 py-1 ${
orderFilters.hasGiftCard
? 'bg-indigo-800 text-indigo-100'
: 'bg-indigo-100 dark:bg-indigo-900/20 text-indigo-800 dark:text-indigo-300'
} rounded-full text-xs font-medium cursor-help`}
>
Gift Card {counts.orderProperties.hasGiftCard > 0 && `(${counts.orderProperties.hasGiftCard})`}
</span>
<span
onClick={() => handleOrderPropertyClick('stillOwes')}
className={`px-2 py-1 ${
orderFilters.stillOwes
? 'bg-red-800 text-red-100'
: 'bg-red-100 dark:bg-red-900/20 text-red-800 dark:text-red-300'
} rounded-full text-xs font-medium cursor-help`}
>
Owes {counts.orderProperties.stillOwes > 0 && `(${counts.orderProperties.stillOwes})`}
</span>
</div>
</CardHeader>
<CardContent className="p-6 pt-4 flex-1 overflow-hidden">
<ScrollArea className="h-full">
{loading && !events.length ? (
<div className="space-y-4 w-full">
{[...Array(5)].map((_, i) => (
<div key={i} className="flex items-center space-x-4 p-4 border-b dark:border-gray-800">
<div className="rounded-full">
<Skeleton className="h-10 w-10 rounded-full" />
</div>
<div className="flex-1 min-w-0 space-y-2">
<Skeleton className="h-4 w-[200px]" />
<Skeleton className="h-3 w-[150px]" />
</div>
<div className="flex items-center space-x-2">
<Skeleton className="h-3 w-16" />
<Skeleton className="h-4 w-4" />
</div>
</div>
))}
</div>
<LoadingState />
) : error ? (
<div className="flex flex-col items-center justify-center p-6 text-center">
<AlertCircle className="w-12 h-12 text-red-500 mb-4" />
@@ -1218,7 +1560,7 @@ const EventFeed = ({
Try Again
</button>
</div>
) : !events || events.length === 0 ? (
) : !filteredEvents || filteredEvents.length === 0 ? (
<div className="h-full flex flex-col items-center justify-center py-16 px-4">
<div className="bg-gray-100 dark:bg-gray-800 rounded-full p-3 mb-4">
<Activity className="h-8 w-8 text-muted-foreground" />
@@ -1232,7 +1574,7 @@ const EventFeed = ({
</div>
) : (
<div className="divide-y divide-gray-100 dark:divide-gray-800">
{events.map((event) => (
{filteredEvents.map((event) => (
<EventCard key={event.id} event={event} />
))}
</div>