diff --git a/dashboard/src/components/dashboard/EventFeed.jsx b/dashboard/src/components/dashboard/EventFeed.jsx index 5aed17d..d84995d 100644 --- a/dashboard/src/components/dashboard/EventFeed.jsx +++ b/dashboard/src/components/dashboard/EventFeed.jsx @@ -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 ( -
+
{title} + {lastUpdate && ( + + Last updated: {format(lastUpdate, "hh:mm a")} + + )}
- {lastUpdate && ( - - Last updated: {format(lastUpdate, "hh:mm a")} - - )} +
+ + + + + + +
+ Orders + + {counts.eventTypes[METRIC_IDS.PLACED_ORDER]} + +
+
+
+
+ + + + + + + +
+ Shipments + + {counts.eventTypes[METRIC_IDS.SHIPPED_ORDER]} + +
+
+
+
+ + + + + + + +
+ Accounts + + {counts.eventTypes[METRIC_IDS.ACCOUNT_CREATED]} + +
+
+
+
+ + + + + + + +
+ Cancellations + + {counts.eventTypes[METRIC_IDS.CANCELED_ORDER]} + +
+
+
+
+ + + + + + + +
+ Refunds + + {counts.eventTypes[METRIC_IDS.PAYMENT_REFUNDED]} + +
+
+
+
+ + + + + + + +
+ Blog Posts + + {counts.eventTypes[METRIC_IDS.NEW_BLOG_POST]} + +
+
+
+
+
+
+ + {/* Order Property Filters */} +
+ 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})`} + + 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})`} + + 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})`} + + 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})`} + + 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})`} + + 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})`} + + 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})`} +
+ {loading && !events.length ? ( -
- {[...Array(5)].map((_, i) => ( -
-
- -
-
- - -
-
- - -
-
- ))} -
+ ) : error ? (
@@ -1218,7 +1560,7 @@ const EventFeed = ({ Try Again
- ) : !events || events.length === 0 ? ( + ) : !filteredEvents || filteredEvents.length === 0 ? (
@@ -1232,7 +1574,7 @@ const EventFeed = ({
) : (
- {events.map((event) => ( + {filteredEvents.map((event) => ( ))}