From 6c86c3076631a898ca60db0a59e6447148c2a91a Mon Sep 17 00:00:00 2001
From: Haritha Randunu
Date: Fri, 9 Oct 2026 14:51:12 +0530
Subject: [PATCH] feat: enhance inventory tabs with statistical cards for
better insights
---
.../hexa/views/inventory-grn-tab.tsx | 19 +++++++++++++-
.../hexa/views/inventory-products-tab.tsx | 25 ++++++++++++++++++-
.../hexa/views/inventory-returns-tab.tsx | 21 +++++++++++++++-
.../hexa/views/inventory-sales-tab.tsx | 19 +++++++++++++-
.../views/inventory-stock-tracking-tab.tsx | 14 ++++++++++-
.../hexa/views/inventory-suppliers-tab.tsx | 12 ++++++++-
6 files changed, 104 insertions(+), 6 deletions(-)
diff --git a/src/components/hexa/views/inventory-grn-tab.tsx b/src/components/hexa/views/inventory-grn-tab.tsx
index 45ad31b..891ac75 100644
--- a/src/components/hexa/views/inventory-grn-tab.tsx
+++ b/src/components/hexa/views/inventory-grn-tab.tsx
@@ -1,7 +1,7 @@
"use client"
import * as React from "react"
-import { EyeIcon, PlusIcon, TrashIcon, TruckIcon } from "lucide-react"
+import { CalendarClockIcon, DollarSignIcon, EyeIcon, PackageIcon, PlusIcon, TrashIcon, TruckIcon } from "lucide-react"
import { toast } from "sonner"
import { createGrn, getGrn, getGrns, getProducts, getSuppliers } from "@/lib/api/inventory"
@@ -26,6 +26,7 @@ import { Textarea } from "@/components/ui/textarea"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { DataTable, type DataTableColumn } from "@/components/hexa/data-table"
import { PageSkeleton } from "@/components/hexa/page-skeleton"
+import { StatCard } from "@/components/hexa/stat-card"
function formatDate(iso: string) {
return new Date(iso).toLocaleString(undefined, {
@@ -316,6 +317,15 @@ function InventoryGrnTab() {
const [dialogOpen, setDialogOpen] = React.useState(false)
const [detailGrnId, setDetailGrnId] = React.useState(null)
+ const totalValue = grns.reduce((sum, g) => sum + g.totalAmount, 0)
+ const now = new Date()
+ const thisMonthGrns = grns.filter((g) => {
+ const d = new Date(g.receivedAt)
+ return d.getFullYear() === now.getFullYear() && d.getMonth() === now.getMonth()
+ })
+ const thisMonthValue = thisMonthGrns.reduce((sum, g) => sum + g.totalAmount, 0)
+ const avgValue = grns.length ? totalValue / grns.length : 0
+
const columns: DataTableColumn[] = [
{
key: "grnNumber",
@@ -371,6 +381,13 @@ function InventoryGrnTab() {
+
+
+
+
+
+
+
(null)
const [detailProductId, setDetailProductId] = React.useState(null)
+ const activeProducts = products.filter((p) => p.isActive).length
+ const outOfStock = products.filter((p) => p.quantityOnHand <= 0).length
+ const inventoryValue = products.reduce((sum, p) => sum + p.costPrice * p.quantityOnHand, 0)
+
async function handleCreate(values: ProductFormValues, imageFile: File | null) {
const input: ProductInput = {
name: values.name.trim(),
@@ -393,6 +409,13 @@ function InventoryProductsTab() {
+
+
+
+
+
+
+
{products.length === 0 ? (
diff --git a/src/components/hexa/views/inventory-returns-tab.tsx b/src/components/hexa/views/inventory-returns-tab.tsx
index cff7260..9725953 100644
--- a/src/components/hexa/views/inventory-returns-tab.tsx
+++ b/src/components/hexa/views/inventory-returns-tab.tsx
@@ -1,7 +1,7 @@
"use client"
import * as React from "react"
-import { EyeIcon, PlusIcon, RotateCcwIcon } from "lucide-react"
+import { CalendarClockIcon, EyeIcon, PackagePlusIcon, PlusIcon, RotateCcwIcon, TriangleAlertIcon } from "lucide-react"
import { toast } from "sonner"
import { createStockReturn, getGrns, getProducts, getSales, getStockReturn, getStockReturns } from "@/lib/api/inventory"
@@ -30,6 +30,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip
import { DataTable, type DataTableColumn } from "@/components/hexa/data-table"
import { InfoBanner } from "@/components/hexa/info-banner"
import { PageSkeleton } from "@/components/hexa/page-skeleton"
+import { StatCard } from "@/components/hexa/stat-card"
const RETURN_CATEGORY_OPTIONS: { label: string; value: ReturnCategory }[] = [
{ label: "Expired", value: "Expired" },
@@ -367,6 +368,17 @@ function InventoryReturnsTab() {
const [dialogOpen, setDialogOpen] = React.useState(false)
const [detailReturnId, setDetailReturnId] = React.useState
(null)
+ const writeOffs = returns.filter((r) => !addsBackToStock(r.category))
+ const customerReturns = returns.filter((r) => addsBackToStock(r.category))
+ const writeOffValue = writeOffs.reduce((sum, r) => sum + r.amount, 0)
+ const now = new Date()
+ const thisMonthWriteOffValue = writeOffs
+ .filter((r) => {
+ const d = new Date(r.returnedAt)
+ return d.getFullYear() === now.getFullYear() && d.getMonth() === now.getMonth()
+ })
+ .reduce((sum, r) => sum + r.amount, 0)
+
const columns: DataTableColumn[] = [
{
key: "product",
@@ -429,6 +441,13 @@ function InventoryReturnsTab() {
+
+
+
+
+
+
+
(null)
+ const totalRevenue = sales.reduce((sum, s) => sum + s.totalAmount, 0)
+ const today = new Date()
+ const todaySales = sales.filter((s) => {
+ const d = new Date(s.soldAt)
+ return d.toDateString() === today.toDateString()
+ })
+ const todayRevenue = todaySales.reduce((sum, s) => sum + s.totalAmount, 0)
+ const avgSaleValue = sales.length ? totalRevenue / sales.length : 0
+
const columns: DataTableColumn[] = [
{
key: "saleNumber",
@@ -414,6 +424,13 @@ function InventorySalesTab() {
+
+
+
+
+
+
+
({ label: p.name, value: p.productId }))]
+ const stockInValue = filtered.filter((m) => m.type === "GRN").reduce((sum, m) => sum + m.amount, 0)
+ const stockOutValue = filtered.filter((m) => m.type === "Sale").reduce((sum, m) => sum + m.amount, 0)
+ const writeOffValue = filtered.filter((m) => m.type === "Return" && m.quantityChange < 0).reduce((sum, m) => sum + m.amount, 0)
+
const columns: DataTableColumn[] = [
{ key: "date", header: "Date", cell: (row) => formatDate(row.date) },
{
@@ -138,6 +143,13 @@ function InventoryStockTrackingTab() {
+
+
+
+
+
+
+
diff --git a/src/components/hexa/views/inventory-suppliers-tab.tsx b/src/components/hexa/views/inventory-suppliers-tab.tsx
index 0ebe646..cfd010a 100644
--- a/src/components/hexa/views/inventory-suppliers-tab.tsx
+++ b/src/components/hexa/views/inventory-suppliers-tab.tsx
@@ -1,7 +1,7 @@
"use client"
import * as React from "react"
-import { PlusIcon, TruckIcon } from "lucide-react"
+import { CheckCircle2Icon, PlusIcon, TruckIcon, XCircleIcon } from "lucide-react"
import { toast } from "sonner"
import { createSupplier, getSuppliers } from "@/lib/api/inventory"
@@ -22,6 +22,7 @@ import { Label } from "@/components/ui/label"
import { Spinner } from "@/components/ui/spinner"
import { DataTable, type DataTableColumn } from "@/components/hexa/data-table"
import { PageSkeleton } from "@/components/hexa/page-skeleton"
+import { StatCard } from "@/components/hexa/stat-card"
import { StatusBadge } from "@/components/hexa/status-badge"
import type { Supplier } from "@/lib/models/inventory"
@@ -115,6 +116,9 @@ function InventorySuppliersTab() {
const suppliers = suppliersFetch.data ?? []
const [dialogOpen, setDialogOpen] = React.useState(false)
+ const activeSuppliers = suppliers.filter((s) => s.isActive).length
+ const inactiveSuppliers = suppliers.length - activeSuppliers
+
async function handleCreate(values: SupplierFormValues) {
const input: SupplierInput = {
name: values.name.trim(),
@@ -171,6 +175,12 @@ function InventorySuppliersTab() {
+
+
+
+
+
+