diff --git a/components/modals/profit-analysis-modal.tsx b/components/modals/profit-analysis-modal.tsx
new file mode 100644
index 0000000..f8e2447
--- /dev/null
+++ b/components/modals/profit-analysis-modal.tsx
@@ -0,0 +1,265 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
+import { Button } from "@/components/ui/button";
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
+import { Badge } from "@/components/ui/badge";
+import { TrendingUp, TrendingDown, Calculator, DollarSign } from "lucide-react";
+import { toast } from "sonner";
+import { apiRequest } from "@/lib/api";
+
+interface ProfitAnalysisModalProps {
+ open: boolean;
+ onClose: () => void;
+ productId: string;
+ productName: string;
+}
+
+interface ProfitData {
+ productId: string;
+ name: string;
+ costPerUnit: number;
+ pricing: Array<{
+ minQuantity: number;
+ pricePerUnit: number;
+ }>;
+ profitMargins: Array<{
+ minQuantity: number;
+ pricePerUnit: number;
+ profit: number | null;
+ profitMargin: number | null;
+ markup: number | null;
+ }>;
+ summary: {
+ hasCostData: boolean;
+ averageProfit: number | null;
+ averageProfitMargin: number | null;
+ averageMarkup: number | null;
+ };
+}
+
+export const ProfitAnalysisModal: React.FC
= ({
+ open,
+ onClose,
+ productId,
+ productName,
+}) => {
+ const [profitData, setProfitData] = useState(null);
+ const [loading, setLoading] = useState(false);
+
+ useEffect(() => {
+ if (open && productId) {
+ fetchProfitAnalysis();
+ }
+ }, [open, productId]);
+
+ const fetchProfitAnalysis = async () => {
+ try {
+ setLoading(true);
+ const response = await apiRequest(`/products/${productId}/profit-analysis`);
+ setProfitData(response);
+ } catch (error) {
+ console.error("Error fetching profit analysis:", error);
+ toast.error("Failed to load profit analysis");
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const formatCurrency = (amount: number | null) => {
+ if (amount === null) return "N/A";
+ return `£${amount.toFixed(2)}`;
+ };
+
+ const formatPercentage = (percentage: number | null) => {
+ if (percentage === null) return "N/A";
+ return `${percentage.toFixed(1)}%`;
+ };
+
+ const getProfitColor = (profit: number | null) => {
+ if (profit === null) return "text-muted-foreground";
+ return profit >= 0 ? "text-green-600" : "text-red-600";
+ };
+
+ const getProfitIcon = (profit: number | null) => {
+ if (profit === null) return Calculator;
+ return profit >= 0 ? TrendingUp : TrendingDown;
+ };
+
+ if (loading) {
+ return (
+
+ );
+ }
+
+ if (!profitData) {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+};
diff --git a/components/tables/product-table.tsx b/components/tables/product-table.tsx
index 0d398e5..1f552c1 100644
--- a/components/tables/product-table.tsx
+++ b/components/tables/product-table.tsx
@@ -1,5 +1,5 @@
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
-import { Edit, Trash, AlertTriangle, CheckCircle, AlertCircle } from "lucide-react";
+import { Edit, Trash, AlertTriangle, CheckCircle, AlertCircle, Calculator } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Product } from "@/models/products";
import { Badge } from "@/components/ui/badge";
@@ -11,6 +11,7 @@ interface ProductTableProps {
onEdit: (product: Product) => void;
onDelete: (productId: string) => void;
onToggleEnabled: (productId: string, enabled: boolean) => void;
+ onProfitAnalysis?: (productId: string, productName: string) => void;
getCategoryNameById: (categoryId: string) => string;
}
@@ -20,6 +21,7 @@ const ProductTable = ({
onEdit,
onDelete,
onToggleEnabled,
+ onProfitAnalysis,
getCategoryNameById
}: ProductTableProps) => {
@@ -93,6 +95,17 @@ const ProductTable = ({
/>
+ {onProfitAnalysis && (
+
+ )}
diff --git a/lib/types/index.ts b/lib/types/index.ts
index 8d8dad7..19c2634 100644
--- a/lib/types/index.ts
+++ b/lib/types/index.ts
@@ -47,6 +47,7 @@ export interface Product {
enabled?: boolean
pricing: PricingTier[]
image?: string | File | null
+ costPerUnit?: number // Cost price for profit calculations
}
export interface ProductData extends Product {}