diff --git a/App/app/(portal)/dashboard/page.tsx b/App/app/(portal)/dashboard/page.tsx
index b4ca973..7ae3e26 100644
--- a/App/app/(portal)/dashboard/page.tsx
+++ b/App/app/(portal)/dashboard/page.tsx
@@ -3,8 +3,8 @@ import { db } from "@/lib/db";
import { StatCard } from "@/components/dashboard/stat-card";
import { SpendCharts } from "@/components/dashboard/spend-charts";
import { PoStatusBadge } from "@/components/po/po-status-badge";
-import { formatCurrency, formatDate, POST_APPROVAL_STATUSES } from "@/lib/utils";
-import { FileText, Clock, CheckCircle, DollarSign } from "lucide-react";
+import { formatCurrency, formatCompactINR, formatDate, POST_APPROVAL_STATUSES } from "@/lib/utils";
+import { FileText, Clock, CheckCircle, DollarSign, IndianRupee } from "lucide-react";
import Link from "next/link";
import type { Metadata } from "next";
@@ -182,7 +182,7 @@ async function ManagerDashboard() {
-
+
{/* Recent approved POs */}
diff --git a/App/lib/utils.ts b/App/lib/utils.ts
index e4d767d..2158edb 100644
--- a/App/lib/utils.ts
+++ b/App/lib/utils.ts
@@ -12,6 +12,30 @@ export function formatCurrency(amount: number | string, currency = "INR"): strin
);
}
+// Compact INR formatter using the Indian short scale (lakh = 1e5, crore = 1e7).
+// Produces readable abbreviations for dashboard stat cards, e.g. ₹2 Cr, ₹49 L,
+// ₹75 K, ₹500. Values are rounded to at most 2 decimals with trailing zeros
+// trimmed (₹2.5 Cr, not ₹2.50 Cr). Negative amounts keep their sign.
+export function formatCompactINR(amount: number | string): string {
+ const n = Number(amount);
+ if (!Number.isFinite(n)) return "₹0";
+
+ const sign = n < 0 ? "-" : "";
+ const abs = Math.abs(n);
+
+ const format = (value: number, suffix: string) => {
+ const rounded = Math.round(value * 100) / 100;
+ // Trim trailing zeros: 2 -> "2", 2.5 -> "2.5", 2.05 -> "2.05".
+ const text = rounded.toFixed(2).replace(/\.?0+$/, "");
+ return `${sign}₹${text}${suffix}`;
+ };
+
+ if (abs >= 1e7) return format(abs / 1e7, " Cr");
+ if (abs >= 1e5) return format(abs / 1e5, " L");
+ if (abs >= 1e3) return format(abs / 1e3, " K");
+ return format(abs, "");
+}
+
export function formatDate(date: Date | string): string {
return new Intl.DateTimeFormat("en-US", {
year: "numeric",
diff --git a/App/tests/unit/utils.test.ts b/App/tests/unit/utils.test.ts
index 5ba3680..13dc12e 100644
--- a/App/tests/unit/utils.test.ts
+++ b/App/tests/unit/utils.test.ts
@@ -1,6 +1,6 @@
import { describe, it, expect } from "vitest";
import {
- formatCurrency, formatDate, formatDateTime,
+ formatCurrency, formatCompactINR, formatDate, formatDateTime,
generatePoNumber, PO_STATUS_LABELS, PO_STATUS_VARIANTS,
} from "@/lib/utils";
@@ -32,6 +32,55 @@ describe("formatCurrency", () => {
});
});
+describe("formatCompactINR", () => {
+ it("abbreviates crore amounts with Cr", () => {
+ expect(formatCompactINR(20000000)).toBe("₹2 Cr");
+ });
+
+ it("abbreviates lakh amounts with L", () => {
+ expect(formatCompactINR(4900000)).toBe("₹49 L");
+ });
+
+ it("abbreviates thousand amounts with K", () => {
+ expect(formatCompactINR(75000)).toBe("₹75 K");
+ });
+
+ it("renders sub-thousand amounts without a suffix", () => {
+ expect(formatCompactINR(500)).toBe("₹500");
+ });
+
+ it("formats zero as ₹0", () => {
+ expect(formatCompactINR(0)).toBe("₹0");
+ });
+
+ it("trims trailing zeros but keeps significant decimals", () => {
+ expect(formatCompactINR(25000000)).toBe("₹2.5 Cr");
+ expect(formatCompactINR(4950000)).toBe("₹49.5 L");
+ });
+
+ it("rounds to at most two decimals", () => {
+ expect(formatCompactINR(12345678)).toBe("₹1.23 Cr");
+ });
+
+ it("uses the right unit at boundaries", () => {
+ expect(formatCompactINR(100000)).toBe("₹1 L");
+ expect(formatCompactINR(10000000)).toBe("₹1 Cr");
+ expect(formatCompactINR(1000)).toBe("₹1 K");
+ });
+
+ it("accepts string input", () => {
+ expect(formatCompactINR("4900000")).toBe("₹49 L");
+ });
+
+ it("preserves the sign for negative amounts", () => {
+ expect(formatCompactINR(-4900000)).toBe("-₹49 L");
+ });
+
+ it("handles non-finite input gracefully", () => {
+ expect(formatCompactINR(NaN)).toBe("₹0");
+ });
+});
+
describe("formatDate", () => {
it("returns a readable date string", () => {
const result = formatDate(new Date("2026-04-29"));