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() {
+
+ + + +
+