From 50d010005609047b239ff64657fc5ea25671f032 Mon Sep 17 00:00:00 2001
From: NotII <46204250+NotII@users.noreply.github.com>
Date: Tue, 26 Aug 2025 21:36:08 +0100
Subject: [PATCH] Improve profit analytics chart and modal data handling
Enhanced ProfitAnalyticsChart to display more robust skeleton loaders and improved summary card logic with fallbacks for missing data. Updated the chart to distinguish between tracked and total revenue, and improved cost data coverage display. In the profit analysis modal, profit margin tiers are now sorted by minimum quantity. Updated the ProfitOverview interface to include revenueFromTrackedProducts.
---
components/analytics/ProfitAnalyticsChart.tsx | 111 ++++++++++++++++--
components/modals/profit-analysis-modal.tsx | 4 +-
lib/services/profit-analytics-service.ts | 1 +
public/git-info.json | 4 +-
4 files changed, 104 insertions(+), 16 deletions(-)
diff --git a/components/analytics/ProfitAnalyticsChart.tsx b/components/analytics/ProfitAnalyticsChart.tsx
index 18cec61..30889cc 100644
--- a/components/analytics/ProfitAnalyticsChart.tsx
+++ b/components/analytics/ProfitAnalyticsChart.tsx
@@ -16,7 +16,7 @@ import {
import { useToast } from "@/hooks/use-toast";
import { formatGBP } from "@/utils/format";
import { getProfitOverview, type ProfitOverview } from "@/lib/services/profit-analytics-service";
-import { TableSkeleton } from './SkeletonLoaders';
+import { Skeleton } from "@/components/ui/skeleton";
interface ProfitAnalyticsChartProps {
timeRange: string;
@@ -63,7 +63,81 @@ export default function ProfitAnalyticsChart({ timeRange, hideNumbers = false }:
}, [timeRange, toast]);
if (isLoading) {
- return ;
+ return (
+
+
+
+
+ Profit Analytics
+
+
+ Track your actual profits based on sales and cost data
+
+
+
+
+ {/* Summary Cards Skeleton */}
+
+ {[...Array(4)].map((_, i) => (
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+ {/* Coverage Card Skeleton */}
+
+
+
+
+
+
+
+
+
+
+ {/* Products List Skeleton */}
+
+
+
+
+
+
+
+ {[...Array(3)].map((_, i) => (
+
+ ))}
+
+
+
+
+
+
+ );
}
if (error || !data) {
@@ -111,6 +185,14 @@ export default function ProfitAnalyticsChart({ timeRange, hideNumbers = false }:
}
const profitDirection = data.summary.totalProfit >= 0;
+
+ // Fallback for backwards compatibility
+ const revenueFromTracked = data.summary.revenueFromTrackedProducts || data.summary.totalRevenue || 0;
+ const totalRevenue = data.summary.totalRevenue || 0;
+ const totalCost = data.summary.totalCost || 0;
+ const totalProfit = data.summary.totalProfit || 0;
+ const productsWithCostData = data.summary.productsWithCostData || 0;
+ const totalProductsSold = data.summary.totalProductsSold || 0;
return (
@@ -118,14 +200,17 @@ export default function ProfitAnalyticsChart({ timeRange, hideNumbers = false }:
- Total Revenue
+ Revenue (Tracked)
- {maskValue(formatGBP(data.summary.totalRevenue))}
+ {maskValue(formatGBP(revenueFromTracked))}
- From {data.summary.totalProductsSold} items sold
+ From {productsWithCostData} tracked items
+
+
+ Total revenue: {maskValue(formatGBP(totalRevenue))}
@@ -136,10 +221,10 @@ export default function ProfitAnalyticsChart({ timeRange, hideNumbers = false }:
- {maskValue(formatGBP(data.summary.totalCost))}
+ {maskValue(formatGBP(totalCost))}
- From {data.summary.productsWithCostData} tracked items
+ From {productsWithCostData} tracked items
@@ -157,10 +242,10 @@ export default function ProfitAnalyticsChart({ timeRange, hideNumbers = false }:
) : (
)}
- {maskValue(formatGBP(data.summary.totalProfit))}
+ {maskValue(formatGBP(totalProfit))}
- {maskValue(`${data.summary.overallProfitMargin.toFixed(1)}%`)} margin
+ {maskValue(`${(data.summary.overallProfitMargin || 0).toFixed(1)}%`)} margin
@@ -171,7 +256,7 @@ export default function ProfitAnalyticsChart({ timeRange, hideNumbers = false }:
- {maskValue(formatGBP(data.summary.averageProfitPerUnit))}
+ {maskValue(formatGBP(data.summary.averageProfitPerUnit || 0))}
Per unit sold
@@ -194,18 +279,18 @@ export default function ProfitAnalyticsChart({ timeRange, hideNumbers = false }:
- {hideNumbers ? "**%" : `${data.summary.costDataCoverage.toFixed(1)}%`}
+ {hideNumbers ? "**%" : `${(data.summary.costDataCoverage || 0).toFixed(1)}%`}
- {hideNumbers ? "** / **" : `${data.summary.productsWithCostData} / ${data.summary.totalProductsSold}`}
+ {hideNumbers ? "** / **" : `${productsWithCostData} / ${totalProductsSold}`}
diff --git a/components/modals/profit-analysis-modal.tsx b/components/modals/profit-analysis-modal.tsx
index 3c8ead0..83e04fd 100644
--- a/components/modals/profit-analysis-modal.tsx
+++ b/components/modals/profit-analysis-modal.tsx
@@ -205,7 +205,9 @@ export const ProfitAnalysisModal: React.FC = ({
- {profitData.profitMargins.map((tier, index) => {
+ {profitData.profitMargins
+ .sort((a, b) => a.minQuantity - b.minQuantity)
+ .map((tier, index) => {
const ProfitIcon = getProfitIcon(tier.profit);
const totalProfitForMinQty = tier.profit !== null ? tier.profit * tier.minQuantity : null;
diff --git a/lib/services/profit-analytics-service.ts b/lib/services/profit-analytics-service.ts
index ae67545..da88c12 100644
--- a/lib/services/profit-analytics-service.ts
+++ b/lib/services/profit-analytics-service.ts
@@ -4,6 +4,7 @@ export interface ProfitOverview {
period: string;
summary: {
totalRevenue: number;
+ revenueFromTrackedProducts: number;
totalCost: number;
totalProfit: number;
overallProfitMargin: number;
diff --git a/public/git-info.json b/public/git-info.json
index b406936..eca2f83 100644
--- a/public/git-info.json
+++ b/public/git-info.json
@@ -1,4 +1,4 @@
{
- "commitHash": "f19ddc4",
- "buildTime": "2025-08-07T22:45:52.166Z"
+ "commitHash": "6a2cd9a",
+ "buildTime": "2025-08-26T20:09:00.025Z"
}
\ No newline at end of file