Files
inventory/inventory/src/components/forecasting/columns.tsx

196 lines
5.7 KiB
TypeScript

import { ColumnDef } from "@tanstack/react-table";
import { ArrowUpDown, ChevronDown, ChevronRight } from "lucide-react";
import { Button } from "@/components/ui/button";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
interface Product {
pid: string;
sku: string;
title: string;
stock_quantity: number;
daily_sales_avg: number;
forecast_units: number;
forecast_revenue: number;
confidence_level: number;
}
export interface ForecastItem {
category: string;
categoryPath: string;
avgDailySales: number;
totalSold: number;
numProducts: number;
avgPrice: number;
avgTotalSold: number;
products?: Product[];
}
export const columns: ColumnDef<ForecastItem>[] = [
{
id: "expander",
header: () => null,
cell: ({ row }) => {
return row.getCanExpand() ? (
<Button
variant="ghost"
onClick={() => row.toggleExpanded()}
className="p-0 h-auto"
>
{row.getIsExpanded() ? <ChevronDown className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />}
</Button>
) : null;
},
},
{
accessorKey: "category",
header: "Category",
cell: ({ row }) => (
<div>
<div className="font-medium">{row.original.category}</div>
{row.original.categoryPath && (
<div className="text-sm text-muted-foreground">
{row.original.categoryPath}
</div>
)}
</div>
),
},
{
accessorKey: "avgDailySales",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
className="whitespace-nowrap"
>
Avg Daily Sales
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
cell: ({ row }) => {
const value = row.getValue("avgDailySales") as number;
return value?.toFixed(2) || "0.00";
},
},
{
accessorKey: "totalSold",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Total Sold
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
cell: ({ row }) => {
const value = row.getValue("totalSold") as number;
return value?.toLocaleString() || "0";
},
},
{
accessorKey: "numProducts",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
className="whitespace-nowrap"
>
# Products
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
cell: ({ row }) => {
const value = row.getValue("numProducts") as number;
return value?.toLocaleString() || "0";
},
},
{
accessorKey: "avgTotalSold",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
className="whitespace-nowrap"
>
Avg Total Sold
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
cell: ({ row }) => {
const value = row.getValue("avgTotalSold") as number;
return value?.toFixed(2) || "0.00";
},
},
{
accessorKey: "avgPrice",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
className="whitespace-nowrap"
>
Avg Price
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
);
},
cell: ({ row }) => {
const value = row.getValue("avgPrice") as number;
return `$${value?.toFixed(2) || "0.00"}`;
},
},
];
export const renderSubComponent = ({ row }: { row: any }) => {
const products = row.original.products || [];
return (
<ScrollArea className="h-[400px] w-full rounded-md border p-4">
<Table>
<TableHeader>
<TableRow>
<TableHead>Product</TableHead>
<TableHead className="text-right">Stock</TableHead>
<TableHead className="text-right">Daily Sales</TableHead>
<TableHead className="text-right">Forecast Units</TableHead>
<TableHead className="text-right">Forecast Revenue</TableHead>
<TableHead className="text-right">Confidence</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{products.map((product: Product) => (
<TableRow key={product.pid}>
<TableCell>
<a
href={`https://backend.acherryontop.com/product/${product.pid}`}
target="_blank"
rel="noopener noreferrer"
className="hover:underline"
>
{product.title}
</a>
<div className="text-sm text-muted-foreground">{product.sku}</div>
</TableCell>
<TableCell className="text-right">{product.stock_quantity}</TableCell>
<TableCell className="text-right">{product.daily_sales_avg.toFixed(1)}</TableCell>
<TableCell className="text-right">{product.forecast_units.toFixed(1)}</TableCell>
<TableCell className="text-right">{product.forecast_revenue.toFixed(2)}</TableCell>
<TableCell className="text-right">{product.confidence_level.toFixed(1)}%</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</ScrollArea>
);
};