From 6c7aed68ccc89d3b8f796db654bbe8e0421c8973 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 1 Jan 2025 14:03:11 -0500 Subject: [PATCH] Fix grid spacing and fix statcards dialogs --- dashboard/src/App.jsx | 31 +-- .../src/components/dashboard/DateTime.jsx | 70 +++--- .../components/dashboard/MiniStatCards.jsx | 219 +++++++++++++++--- .../src/components/dashboard/StatCards.jsx | 2 + 4 files changed, 244 insertions(+), 78 deletions(-) diff --git a/dashboard/src/App.jsx b/dashboard/src/App.jsx index 294a9bd..d983ed4 100644 --- a/dashboard/src/App.jsx +++ b/dashboard/src/App.jsx @@ -63,34 +63,33 @@ const PinProtectedLayout = ({ children }) => { // Small Layout const SmallLayout = () => { const DATETIME_SCALE = 2; - const STATS_SCALE = 1.5; - + const STATS_SCALE = 1.6; + return ( -
-
- - - - -
+
+ + + + +
+
+
+ +
{ />
+ + {/* You can easily add more grid items here */}
); diff --git a/dashboard/src/components/dashboard/DateTime.jsx b/dashboard/src/components/dashboard/DateTime.jsx index fb28f97..0dae73e 100644 --- a/dashboard/src/components/dashboard/DateTime.jsx +++ b/dashboard/src/components/dashboard/DateTime.jsx @@ -347,8 +347,8 @@ const DateTimeWeatherDisplay = ({ scaleFactor = 1 }) => { ); return ( -
- {/* Time Display */} +
+ {/* Time Display */}
@@ -378,15 +378,15 @@ return ( {weather?.main && ( - - - = 18 || datetime.getHours() < 6 - ), - "flex items-center justify-center aspect-square cursor-pointer hover:brightness-110 transition-all relative" - )}> + + + = 18 || datetime.getHours() < 6 + ), + "flex items-center justify-center aspect-square cursor-pointer hover:brightness-110 transition-all relative" + )}>
{getWeatherIcon(weather.weather[0]?.id, datetime)} @@ -406,30 +406,30 @@ return (
)} -
-
- - {weather.alerts && ( - - - - {weather.alerts[0].event} - - +
+
+ + {weather.alerts && ( + + + + {weather.alerts[0].event} + + + )} + + +
)} - - - -)}
{/* Calendar Display */} @@ -442,7 +442,7 @@ return (
- ); +); }; export default DateTimeWeatherDisplay; \ No newline at end of file diff --git a/dashboard/src/components/dashboard/MiniStatCards.jsx b/dashboard/src/components/dashboard/MiniStatCards.jsx index 6cd8cd7..5ce5354 100644 --- a/dashboard/src/components/dashboard/MiniStatCards.jsx +++ b/dashboard/src/components/dashboard/MiniStatCards.jsx @@ -29,9 +29,18 @@ import { Package, AlertCircle, CircleDollarSign, + Loader2, } from "lucide-react"; import { Skeleton } from "@/components/ui/skeleton"; import { Tooltip, TooltipContent, TooltipTrigger, TooltipProvider } from "@/components/ui/tooltip"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; // Import the detail view components and utilities from StatCards import { @@ -46,6 +55,92 @@ import { SkeletonCard, } from "./StatCards"; +// Mini skeleton components +const MiniSkeletonChart = ({ type = "line" }) => ( +
+
+ {/* Grid lines */} + {[...Array(5)].map((_, i) => ( +
+ ))} + {/* Y-axis labels */} +
+ {[...Array(5)].map((_, i) => ( + + ))} +
+ {/* X-axis labels */} +
+ {[...Array(6)].map((_, i) => ( + + ))} +
+ {type === "bar" ? ( +
+ {[...Array(24)].map((_, i) => ( +
+ ))} +
+ ) : ( +
+
+
+
+
+ )} +
+
+); + +const MiniSkeletonTable = ({ rows = 8 }) => ( +
+ + + + + + + + + + + + + + + + {[...Array(rows)].map((_, i) => ( + + + + + + + + + + + + ))} + +
+
+); + const MiniStatCards = ({ timeRange: initialTimeRange = "today", startDate, @@ -195,6 +290,44 @@ const MiniStatCards = ({ return () => clearInterval(interval); }, [timeRange]); + // Add function to fetch detail data + const fetchDetailData = useCallback(async (metric) => { + if (detailData[metric]) return; + + setDetailDataLoading((prev) => ({ ...prev, [metric]: true })); + try { + const response = await axios.get("/api/klaviyo/events/stats/details", { + params: { + timeRange: "last30days", + metric, + daily: true, + }, + }); + + setDetailData((prev) => ({ ...prev, [metric]: response.data.stats })); + } catch (error) { + console.error(`Error fetching detail data for ${metric}:`, error); + } finally { + setDetailDataLoading((prev) => ({ ...prev, [metric]: false })); + } + }, [detailData]); + + // Add effect to load detail data when metric is selected + useEffect(() => { + if (selectedMetric) { + fetchDetailData(selectedMetric); + } + }, [selectedMetric, fetchDetailData]); + + // Add preload effect + useEffect(() => { + // Preload all detail data when component mounts + const metrics = ["revenue", "orders", "average_order", "shipping"]; + metrics.forEach(metric => { + fetchDetailData(metric); + }); + }, []); // eslint-disable-line react-hooks/exhaustive-deps + if (loading && !stats) { return ( @@ -227,8 +360,7 @@ const MiniStatCards = ({ const aovTrend = calculateAOVTrend(); return ( - - + <>
@@ -255,8 +387,7 @@ const MiniStatCards = ({
- - +
- setSelectedMetric(null)} - title={ - selectedMetric - ? `${selectedMetric - .split("_") - .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) - .join(" ")} Details` - : "" - } - > - {selectedMetric === "revenue" && } - {selectedMetric === "orders" && } - {selectedMetric === "average_order" && ( - - )} - {selectedMetric === "shipping" && } - -
- + setSelectedMetric(null)}> + +
+
+ + + {selectedMetric + ? `${selectedMetric + .split("_") + .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) + .join(" ")} Details` + : ""} + + +
+ {detailDataLoading[selectedMetric] ? ( +
+ {selectedMetric === "shipping" ? ( + + ) : ( + <> + + {selectedMetric === "orders" && ( +
+

Hourly Distribution

+ +
+ )} + + )} +
+ ) : ( +
+ {selectedMetric === "revenue" && ( + + )} + {selectedMetric === "orders" && ( + + )} + {selectedMetric === "average_order" && ( + + )} + {selectedMetric === "shipping" && ( + + )} +
+ )} +
+
+
+
+
+ ); }; diff --git a/dashboard/src/components/dashboard/StatCards.jsx b/dashboard/src/components/dashboard/StatCards.jsx index 8a07091..9e6190d 100644 --- a/dashboard/src/components/dashboard/StatCards.jsx +++ b/dashboard/src/components/dashboard/StatCards.jsx @@ -2149,6 +2149,8 @@ export { formatCurrency, formatPercentage, SkeletonCard, + SkeletonChart, + SkeletonTable, }; export default StatCards;