Improve product image handling and add costPerUnit
All checks were successful
Build Frontend / build (push) Successful in 1m10s
All checks were successful
Build Frontend / build (push) Successful in 1m10s
Added a utility to generate product image URLs, ensuring images are displayed correctly in the product table. Updated the Product model to include an optional costPerUnit field. Minor UI and code formatting improvements were made for consistency.
This commit is contained in:
@@ -45,7 +45,7 @@ const ProfitAnalysisModal = dynamic(() => import("@/components/modals/profit-ana
|
|||||||
|
|
||||||
function ProductTableSkeleton() {
|
function ProductTableSkeleton() {
|
||||||
return (
|
return (
|
||||||
<Card className="animate-in fade-in duration-500">
|
<Card className="animate-in fade-in duration-500 border-border/40 bg-background/50 backdrop-blur-sm shadow-sm">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<Skeleton className="h-6 w-32" />
|
<Skeleton className="h-6 w-32" />
|
||||||
@@ -152,7 +152,7 @@ export default function ProductsPage() {
|
|||||||
const [importModalOpen, setImportModalOpen] = useState(false);
|
const [importModalOpen, setImportModalOpen] = useState(false);
|
||||||
const [addProductOpen, setAddProductOpen] = useState(false);
|
const [addProductOpen, setAddProductOpen] = useState(false);
|
||||||
const [profitAnalysisOpen, setProfitAnalysisOpen] = useState(false);
|
const [profitAnalysisOpen, setProfitAnalysisOpen] = useState(false);
|
||||||
const [selectedProductForAnalysis, setSelectedProductForAnalysis] = useState<{id: string, name: string} | null>(null);
|
const [selectedProductForAnalysis, setSelectedProductForAnalysis] = useState<{ id: string, name: string } | null>(null);
|
||||||
|
|
||||||
// Fetch products and categories
|
// Fetch products and categories
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
Table,
|
import {
|
||||||
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
TableCell,
|
TableCell,
|
||||||
TableHead,
|
TableHead,
|
||||||
@@ -13,7 +14,7 @@ import {
|
|||||||
AlertCircle,
|
AlertCircle,
|
||||||
Calculator,
|
Calculator,
|
||||||
Copy,
|
Copy,
|
||||||
PackageOffset,
|
PackageX,
|
||||||
Archive
|
Archive
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
@@ -23,6 +24,13 @@ import { Switch } from "@/components/ui/switch";
|
|||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { motion, AnimatePresence } from "framer-motion";
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
|
|
||||||
|
const getProductImageUrl = (product: Product) => {
|
||||||
|
if (!product.image) return null;
|
||||||
|
if (typeof product.image === 'string' && product.image.startsWith('http')) return product.image;
|
||||||
|
// Use the API endpoint to serve the image
|
||||||
|
return `${process.env.NEXT_PUBLIC_API_URL}/products/${product._id}/image`;
|
||||||
|
};
|
||||||
|
|
||||||
interface ProductTableProps {
|
interface ProductTableProps {
|
||||||
products: Product[];
|
products: Product[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
@@ -82,9 +90,13 @@ const ProductTable = ({
|
|||||||
>
|
>
|
||||||
<TableCell className="font-medium">
|
<TableCell className="font-medium">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="h-8 w-8 rounded bg-muted/50 flex items-center justify-center text-muted-foreground">
|
<div className="h-8 w-8 rounded bg-muted/50 flex items-center justify-center text-muted-foreground overflow-hidden relative">
|
||||||
{product.image ? (
|
{getProductImageUrl(product) ? (
|
||||||
<img src={product.image} alt={product.name} className="h-full w-full object-cover rounded" />
|
<img
|
||||||
|
src={getProductImageUrl(product)!}
|
||||||
|
alt={product.name}
|
||||||
|
className="h-full w-full object-cover"
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-xs font-bold">{product.name.charAt(0).toUpperCase()}</span>
|
<span className="text-xs font-bold">{product.name.charAt(0).toUpperCase()}</span>
|
||||||
)}
|
)}
|
||||||
@@ -227,7 +239,7 @@ const ProductTable = ({
|
|||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={6} className="h-32 text-center text-muted-foreground">
|
<TableCell colSpan={6} className="h-32 text-center text-muted-foreground">
|
||||||
<div className="flex flex-col items-center justify-center gap-2">
|
<div className="flex flex-col items-center justify-center gap-2">
|
||||||
<PackageOffset className="h-8 w-8 opacity-50" />
|
<PackageX className="h-8 w-8 opacity-50" />
|
||||||
<p>No active products found</p>
|
<p>No active products found</p>
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|||||||
@@ -15,4 +15,5 @@ export interface Product {
|
|||||||
pricePerUnit: number;
|
pricePerUnit: number;
|
||||||
}>;
|
}>;
|
||||||
image?: string | File | null | undefined;
|
image?: string | File | null | undefined;
|
||||||
|
costPerUnit?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user