Merge branch 'OnFieldWork_V1' of https://git.marcoaiot.com/admin/marco.pms.web into Adding_Chips_Expense
This commit is contained in:
commit
ab5e0b2f79
@ -21,3 +21,76 @@
|
||||
.text-md {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.text-md-b {
|
||||
font-weight: normal;
|
||||
}
|
||||
.text-xxs { font-size: 0.55rem; } /* 8px */
|
||||
.text-xs { font-size: 0.75rem; } /* 12px */
|
||||
.text-sm { font-size: 0.875rem; } /* 14px */
|
||||
.text-base { font-size: 1rem; } /* 16px */
|
||||
.text-lg { font-size: 1.125rem; } /* 18px */
|
||||
.text-xl { font-size: 1.25rem; } /* 20px */
|
||||
.text-2xl { font-size: 1.5rem; } /* 24px */
|
||||
.text-3xl { font-size: 1.875rem; } /* 30px */
|
||||
.text-4xl { font-size: 2.25rem; } /* 36px */
|
||||
.text-5xl { font-size: 3rem; } /* 48px */
|
||||
.text-6xl { font-size: 3.75rem; } /* 60px */
|
||||
.text-7xl { font-size: 4.5rem; } /* 72px */
|
||||
.text-8xl { font-size: 6rem; } /* 96px */
|
||||
.text-9xl { font-size: 8rem; } /* 128px */
|
||||
|
||||
|
||||
/* */
|
||||
|
||||
.w-0 { width: 0px; }
|
||||
.w-px { width: 1px; }
|
||||
.w-1 { width: 0.25rem; } /* 4px */
|
||||
.w-2 { width: 0.5rem; } /* 8px */
|
||||
.w-3 { width: 0.75rem; } /* 12px */
|
||||
.w-4 { width: 1rem; } /* 16px */
|
||||
.w-5 { width: 1.25rem; } /* 20px */
|
||||
.w-6 { width: 1.5rem; } /* 24px */
|
||||
.w-8 { width: 2rem; } /* 32px */
|
||||
.w-10 { width: 2.5rem; } /* 40px */
|
||||
.w-12 { width: 3rem; } /* 48px */
|
||||
.w-16 { width: 4rem; } /* 64px */
|
||||
.w-20 { width: 5rem; } /* 80px */
|
||||
.w-24 { width: 6rem; } /* 96px */
|
||||
.w-32 { width: 8rem; } /* 128px */
|
||||
.w-40 { width: 10rem; } /* 160px */
|
||||
.w-48 { width: 12rem; } /* 192px */
|
||||
.w-56 { width: 14rem; } /* 224px */
|
||||
.w-64 { width: 16rem; } /* 256px */
|
||||
.w-auto { width: auto; }
|
||||
.w-full { width: 100%; }
|
||||
.w-screen{ width: 100vw; }
|
||||
.w-min { width: min-content; }
|
||||
.w-max { width: max-content; }
|
||||
|
||||
|
||||
|
||||
.h-0 { height: 0px; }
|
||||
.h-px { height: 1px; }
|
||||
.h-1 { height: 0.25rem; } /* 4px */
|
||||
.h-2 { height: 0.5rem; } /* 8px */
|
||||
.h-3 { height: 0.75rem; } /* 12px */
|
||||
.h-4 { height: 1rem; } /* 16px */
|
||||
.h-5 { height: 1.25rem; } /* 20px */
|
||||
.h-6 { height: 1.5rem; } /* 24px */
|
||||
.h-8 { height: 2rem; } /* 32px */
|
||||
.h-10 { height: 2.5rem; } /* 40px */
|
||||
.h-12 { height: 3rem; } /* 48px */
|
||||
.h-16 { height: 4rem; } /* 64px */
|
||||
.h-20 { height: 5rem; } /* 80px */
|
||||
.h-24 { height: 6rem; } /* 96px */
|
||||
.h-32 { height: 8rem; } /* 128px */
|
||||
.h-40 { height: 10rem; } /* 160px */
|
||||
.h-48 { height: 12rem; } /* 192px */
|
||||
.h-56 { height: 14rem; } /* 224px */
|
||||
.h-64 { height: 16rem; } /* 256px */
|
||||
.h-auto { height: auto; }
|
||||
.h-full { height: 100%; }
|
||||
.h-screen{ height: 100vh; }
|
||||
.h-min { height: min-content; }
|
||||
.h-max { height: max-content; }
|
||||
|
@ -31,7 +31,7 @@ const selectedProjectId = useSelectedProject()
|
||||
<div className="card-header mb-1 pb-0">
|
||||
<div className="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<div className="card-title mb-0 text-start">
|
||||
<h5 className="mb-1">Attendance</h5>
|
||||
<h5 className="mb-1 card-title">Attendance</h5>
|
||||
<p className="card-subtitle">Daily Attendance Data</p>
|
||||
</div>
|
||||
|
||||
|
@ -65,7 +65,7 @@ const AttendanceOverview = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white p-4 rounded shadow d-flex flex-column position-relative">
|
||||
<div className="bg-white p-4 rounded shadow d-flex flex-column position-relative " >
|
||||
{/* Optional subtle loading overlay */}
|
||||
{isLoading && (
|
||||
<div className="position-absolute w-100 h-100 d-flex align-items-center justify-content-center bg-white bg-opacity-50 z-index-1">
|
||||
@ -76,7 +76,7 @@ const AttendanceOverview = () => {
|
||||
{/* Header */}
|
||||
<div className="d-flex justify-content-between align-items-center mb-3">
|
||||
<div className="card-title mb-0 text-start">
|
||||
<h5 className="mb-1 fw-bold">Attendance Overview</h5>
|
||||
<h5 className="mb-1 card-title">Attendance Overview</h5>
|
||||
<p className="card-subtitle">Role-wise present count</p>
|
||||
</div>
|
||||
<div className="d-flex gap-2">
|
||||
@ -98,7 +98,7 @@ const AttendanceOverview = () => {
|
||||
<div className="flex-grow-1 d-flex align-items-center justify-content-center">
|
||||
{view === "chart" ? (
|
||||
<div className="w-100">
|
||||
<ReactApexChart options={chartOptions} series={chartSeries} type="bar" height={400} />
|
||||
<ReactApexChart options={chartOptions} series={chartSeries} type="bar" height={300} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="table-responsive w-100" style={{ maxHeight: "350px", overflowY: "auto" }}>
|
||||
|
@ -30,7 +30,7 @@ const Dashboard = () => {
|
||||
const isAllProjectsSelected = projectId === null;
|
||||
|
||||
return (
|
||||
<div className="container-xxl flex-grow-1 container-p-y">
|
||||
<div className="container-fluid py-5">
|
||||
<div className="row mb-6 g-6">
|
||||
<div className="col-12 col-xl-8">
|
||||
<div className="card h-100">
|
||||
@ -39,7 +39,7 @@ const Dashboard = () => {
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-xl-4 col-md-6">
|
||||
<div className="card h-100">
|
||||
<div className="card ">
|
||||
<ExpenseStatus />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -71,9 +71,9 @@ const { labels, series, total } = useMemo(() => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="card-header d-flex justify-content-between mt-2 align-items-center ">
|
||||
<div className="card-header d-flex justify-content-between align-items-center ">
|
||||
<div>
|
||||
<h5 className="mb-1 fw-bold text-start">Expense Breakdown</h5>
|
||||
<h5 className="mb-1 card-title text-start">Expense Breakdown</h5>
|
||||
<p className="card-subtitle">Category Wise Expense Breakdown</p>
|
||||
</div>
|
||||
|
||||
@ -109,7 +109,7 @@ const { labels, series, total } = useMemo(() => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="d-flex justify-content-center mb-8">
|
||||
<div className="d-flex justify-content-center mb-3">
|
||||
<Chart
|
||||
options={donutOptions}
|
||||
series={report.map((item) => item.totalApprovedAmount || 0)}
|
||||
|
@ -23,8 +23,8 @@ const ExpenseByProject = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (expenseApiData) {
|
||||
const categories = expenseApiData.map(
|
||||
(item) => formatDate_DayMonth(item.monthName, item.year)
|
||||
const categories = expenseApiData.map((item) =>
|
||||
formatDate_DayMonth(item.monthName, item.year)
|
||||
);
|
||||
const data = expenseApiData.map((item) => item.total);
|
||||
setChartData({ categories, data });
|
||||
@ -41,7 +41,9 @@ const ExpenseByProject = () => {
|
||||
|
||||
const options = {
|
||||
chart: { type: "bar", toolbar: { show: false } },
|
||||
plotOptions: { bar: { horizontal: false, columnWidth: "55%", borderRadius: 4 } },
|
||||
plotOptions: {
|
||||
bar: { horizontal: false, columnWidth: "55%", borderRadius: 4 },
|
||||
},
|
||||
dataLabels: { enabled: true, formatter: (val) => formatCurrency(val) },
|
||||
xaxis: {
|
||||
categories: chartData.categories,
|
||||
@ -53,7 +55,7 @@ const ExpenseByProject = () => {
|
||||
},
|
||||
},
|
||||
|
||||
annotations: { xaxis: [{ x: 0, strokeDashArray: 0, }] },
|
||||
annotations: { xaxis: [{ x: 0, strokeDashArray: 0 }] },
|
||||
fill: { opacity: 1 },
|
||||
colors: ["#2196f3"],
|
||||
};
|
||||
@ -66,12 +68,12 @@ const ExpenseByProject = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="card shadow-sm ">
|
||||
<div className="card shadow-sm rounded ">
|
||||
{/* Header */}
|
||||
<div className="card-header">
|
||||
<div className="d-flex justify-content-start align-items-center mb-3 mt-3">
|
||||
<div>
|
||||
<h5 className="mb-1 me-6 fw-bold">Monthly Expense -</h5>
|
||||
<h5 className="mb-1 me-6 card-title">Monthly Expense -</h5>
|
||||
<p className="card-subtitle me-5 mb-0">Detailed project expenses</p>
|
||||
</div>
|
||||
<div className="btn-group mb-4 ms-n8">
|
||||
@ -115,7 +117,8 @@ const ExpenseByProject = () => {
|
||||
{["1M", "3M", "6M", "12M", "All"].map((item) => (
|
||||
<button
|
||||
key={item}
|
||||
className={`border-0 px-2 py-1 text-sm rounded ${range === item
|
||||
className={`border-0 px-2 py-1 text-sm rounded ${
|
||||
range === item
|
||||
? "text-white bg-primary"
|
||||
: "text-body bg-transparent"
|
||||
}`}
|
||||
@ -142,18 +145,16 @@ const ExpenseByProject = () => {
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Chart */}
|
||||
<div
|
||||
className="card-body bg-white text-dark p-3"
|
||||
style={{ minHeight: "400px" }}
|
||||
className="card-body bg-white text-dark p-3 rounded"
|
||||
>
|
||||
{isLoading ? (
|
||||
<p>Loading chart...</p>
|
||||
) : (
|
||||
<Chart options={options} series={series} type="bar" height={400} />
|
||||
<Chart options={options} series={series} type="bar" height={235} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,9 +3,10 @@ import { useExpense } from "../../hooks/useExpense";
|
||||
import { useExpenseStatus } from "../../hooks/useDashboard_Data";
|
||||
import { useSelectedProject } from "../../slices/apiDataManager";
|
||||
import { useProjectName } from "../../hooks/useProjects";
|
||||
import { formatCurrency } from "../../utils/appUtils";
|
||||
import { EXPENSE_STATUS } from "../../utils/constants";
|
||||
import { countDigit, formatCurrency } from "../../utils/appUtils";
|
||||
import { EXPENSE_MANAGE, EXPENSE_STATUS } from "../../utils/constants";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
|
||||
const ExpenseStatus = () => {
|
||||
const [projectName, setProjectName] = useState("All Project");
|
||||
@ -13,6 +14,7 @@ const ExpenseStatus = () => {
|
||||
const { projectNames, loading } = useProjectName();
|
||||
const { data, isPending, error } = useExpenseStatus(selectedProject);
|
||||
const navigate = useNavigate();
|
||||
const isManageExpense = useHasUserPermission(EXPENSE_MANAGE)
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedProject && projectNames?.length) {
|
||||
@ -32,7 +34,7 @@ const ExpenseStatus = () => {
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className="card-header d-flex justify-content-between text-start">
|
||||
<div className="card-header d-flex justify-content-between text-start ">
|
||||
<div className="m-0">
|
||||
<h5 className="card-title mb-1">Expense - By Status</h5>
|
||||
<p className="card-subtitle m-0 ">{projectName}</p>
|
||||
@ -41,13 +43,15 @@ const ExpenseStatus = () => {
|
||||
|
||||
<div className="card-body ">
|
||||
<div className=" py-0 text-start mb-2">
|
||||
<div className="d-flex justify-content-between align-items-center">
|
||||
<span className="fs-5"> Project Spendings:</span>{" "}
|
||||
<span className="text-end text-royalblue text-md">
|
||||
{isManageExpense && ( <div className="d-flex justify-content-between align-items-center">
|
||||
<div className="d-block">
|
||||
<span className={`fs-semibold d-block ${countDigit(data?.totalAmount) > 3 ? "text-base":"text-lg" }`}>Project Spendings:</span>{" "}
|
||||
<small className="d-block text-xxs text-gary-80">{`(All Processed Payments)`}</small>
|
||||
</div>
|
||||
<span className={`text-end text-royalblue ${countDigit(data?.totalAmount) > 3 ? "text-":"text-3xl" } text-md`}>
|
||||
{formatCurrency(data?.totalAmount)}
|
||||
</span>
|
||||
</div>
|
||||
<small className=" text-gary-80">{`(All Processed Payments)`}</small>
|
||||
</div>)}
|
||||
</div>
|
||||
<div className="report-list text-start">
|
||||
{[
|
||||
@ -60,7 +64,7 @@ const ExpenseStatus = () => {
|
||||
status: EXPENSE_STATUS.payment_pending,
|
||||
},
|
||||
{
|
||||
title: "Pending Approver",
|
||||
title: "Pending Approve",
|
||||
count: data?.processPending?.count,
|
||||
amount: data?.processPending?.totalAmount,
|
||||
icon: "fa-solid fa-check",
|
||||
@ -68,10 +72,10 @@ const ExpenseStatus = () => {
|
||||
status: EXPENSE_STATUS.approve_pending,
|
||||
},
|
||||
{
|
||||
title: "Pending Reviewer",
|
||||
title: "Pending Review",
|
||||
count: data?.reviewPending?.count,
|
||||
amount: data?.reviewPending?.totalAmount,
|
||||
icon: "bx bx-file",
|
||||
icon: "bx bx-search-alt-2",
|
||||
iconColor: "text-secondary",
|
||||
status: EXPENSE_STATUS.review_pending,
|
||||
},
|
||||
@ -86,26 +90,26 @@ const ExpenseStatus = () => {
|
||||
].map((item, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="report-list-item rounded-2 mb-4 bg-lighter px-2 py-3 cursor-pointer"
|
||||
onClick={()=>handleNavigate(item.status)}
|
||||
className="report-list-item rounded-2 mb-4 bg-lighter px-2 py-1 cursor-pointer"
|
||||
onClick={() => handleNavigate(item?.status)}
|
||||
>
|
||||
<div className="d-flex align-items-center">
|
||||
<div className="report-list-icon shadow-xs me-4">
|
||||
<div className="report-list-icon shadow-xs me-2">
|
||||
<span className="d-inline-flex align-items-center justify-content-center rounded-circle border p-2">
|
||||
<i className={`${item.icon} ${item.iconColor} bx-lg`}></i>
|
||||
<i className={`${item?.icon} ${item?.iconColor} bx-lg`}></i>
|
||||
</span>
|
||||
</div>
|
||||
<div className="d-flex justify-content-between align-items-center w-100 flex-wrap gap-2">
|
||||
<div className="d-flex flex-column gap-2">
|
||||
<span className="fw-bold">{item.title}</span>
|
||||
<small className="mb-0 text-primary">
|
||||
{formatCurrency(item.amount)}
|
||||
</small>
|
||||
<span className="fw-bold">{item?.title}</span>
|
||||
{item?.amount ? <small className="mb-0 text-primary">
|
||||
{formatCurrency(item?.amount)}
|
||||
</small> :""}
|
||||
</div>
|
||||
<div className="">
|
||||
{" "}
|
||||
<small className="text-muted fs-semibold text-royalblue text-md">
|
||||
{item.count}{" "}
|
||||
<small className={`text-royalblue ${countDigit(item?.count) >= 3 ? "text-xl" : "text-2xl"} text-gray-500`}>
|
||||
{item?.count }
|
||||
</small>
|
||||
<small className="text-muted fs-semibold text-royalblue text-md">
|
||||
<i className="bx bx-chevron-right"></i>
|
||||
|
@ -36,7 +36,7 @@ const ExpenseFilterPanel = ({ onApply, handleGroupBy, setFilterdata }) => {
|
||||
].sort((a, b) => a.name.localeCompare(b.name));
|
||||
}, []);
|
||||
|
||||
const [selectedGroup, setSelectedGroup] = useState(groupByList[0]);
|
||||
const [selectedGroup, setSelectedGroup] = useState(groupByList[6]);
|
||||
const [resetKey, setResetKey] = useState(0);
|
||||
|
||||
const dynamicDefaultFilter = useMemo(() => {
|
||||
|
@ -10,7 +10,11 @@ import {
|
||||
EXPENSE_REJECTEDBY,
|
||||
ITEMS_PER_PAGE,
|
||||
} from "../../utils/constants";
|
||||
import { formatCurrency, getColorNameFromHex, useDebounce } from "../../utils/appUtils";
|
||||
import {
|
||||
formatCurrency,
|
||||
getColorNameFromHex,
|
||||
useDebounce,
|
||||
} from "../../utils/appUtils";
|
||||
import { ExpenseTableSkeleton } from "./ExpenseSkeleton";
|
||||
import ConfirmModal from "../common/ConfirmModal";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
@ -62,39 +66,61 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
|
||||
const groupByField = (items, field) => {
|
||||
return items.reduce((acc, item) => {
|
||||
let key;
|
||||
let displayField;
|
||||
|
||||
switch (field) {
|
||||
case "transactionDate":
|
||||
key = item?.transactionDate?.split("T")[0];
|
||||
displayField = "Transaction Date";
|
||||
break;
|
||||
case "status":
|
||||
key = item?.status?.displayName || "Unknown";
|
||||
displayField = "Status";
|
||||
break;
|
||||
case "submittedBy":
|
||||
key = `${item?.createdBy?.firstName ?? ""} ${item.createdBy?.lastName ?? ""
|
||||
key = `${item?.createdBy?.firstName ?? ""} ${
|
||||
item.createdBy?.lastName ?? ""
|
||||
}`.trim();
|
||||
displayField = "Submitted By";
|
||||
break;
|
||||
case "project":
|
||||
key = item?.project?.name || "Unknown Project";
|
||||
displayField = "Project";
|
||||
break;
|
||||
case "paymentMode":
|
||||
key = item?.paymentMode?.name || "Unknown Mode";
|
||||
displayField = "Payment Mode";
|
||||
break;
|
||||
case "expensesType":
|
||||
key = item?.expensesType?.name || "Unknown Type";
|
||||
displayField = "Expense Type";
|
||||
break;
|
||||
case "createdAt":
|
||||
key = item?.createdAt?.split("T")[0] || "Unknown Type";
|
||||
key = item?.createdAt?.split("T")[0] || "Unknown Date";
|
||||
displayField = "Created Date";
|
||||
break;
|
||||
default:
|
||||
key = "Others";
|
||||
displayField = "Others";
|
||||
}
|
||||
if (!acc[key]) acc[key] = [];
|
||||
acc[key]?.push(item);
|
||||
|
||||
const groupKey = `${field}_${key}`; // unique key for object property
|
||||
if (!acc[groupKey]) {
|
||||
acc[groupKey] = { key, displayField, items: [] };
|
||||
}
|
||||
|
||||
acc[groupKey].items.push(item);
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
const expenseColumns = [
|
||||
{
|
||||
key: "expenseUId",
|
||||
label: "Expense Id",
|
||||
getValue: (e) => e.expenseUId|| "N/A",
|
||||
align: "text-start mx-2",
|
||||
},
|
||||
{
|
||||
key: "expensesType",
|
||||
label: "Expense Type",
|
||||
@ -138,11 +164,7 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
|
||||
{
|
||||
key: "amount",
|
||||
label: "Amount",
|
||||
getValue: (e) => (
|
||||
<>
|
||||
{formatCurrency(e?.amount)}
|
||||
</>
|
||||
),
|
||||
getValue: (e) => <>{formatCurrency(e?.amount)}</>,
|
||||
isAlwaysVisible: true,
|
||||
align: "text-end",
|
||||
},
|
||||
@ -167,7 +189,11 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
|
||||
const grouped = groupBy
|
||||
? groupByField(data?.data ?? [], groupBy)
|
||||
: { All: data?.data ?? [] };
|
||||
const IsGroupedByDate = ["transactionDate", "createdAt"]?.includes(groupBy);
|
||||
const IsGroupedByDate = [
|
||||
{ key: "transactionDate", displayField: "Transaction Date" },
|
||||
{ key: "createdAt", displayField: "created Date" },
|
||||
]?.includes(groupBy);
|
||||
|
||||
const canEditExpense = (expense) => {
|
||||
return (
|
||||
(expense?.status?.id === EXPENSE_DRAFT ||
|
||||
@ -236,18 +262,24 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
|
||||
</thead>
|
||||
<tbody>
|
||||
{Object.keys(grouped).length > 0 ? (
|
||||
Object.entries(grouped).map(([group, expenses]) => (
|
||||
<React.Fragment key={group}>
|
||||
Object.values(grouped).map(({ key, displayField, items }) => (
|
||||
<React.Fragment key={key}>
|
||||
<tr className="tr-group text-dark">
|
||||
<td colSpan={8} className="text-start">
|
||||
<strong>
|
||||
<div className="d-flex align-items-center">
|
||||
{" "}
|
||||
<small className="fs-6 ">
|
||||
{displayField} :{" "}
|
||||
</small>{" "}
|
||||
<small className="fs-6 ms-3">
|
||||
{IsGroupedByDate
|
||||
? formatUTCToLocalTime(group)
|
||||
: group}
|
||||
</strong>
|
||||
? formatUTCToLocalTime(key)
|
||||
: key}
|
||||
</small>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{expenses.map((expense) => (
|
||||
{items?.map((expense) => (
|
||||
<tr key={expense.id}>
|
||||
{expenseColumns.map(
|
||||
(col) =>
|
||||
@ -284,7 +316,6 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => {
|
||||
}
|
||||
></i>
|
||||
)}
|
||||
|
||||
{canDetetExpense(expense) && (
|
||||
<i
|
||||
className="bx bx-trash text-danger cursor-pointer"
|
||||
|
@ -106,11 +106,12 @@ const ViewExpense = ({ ExpenseId }) => {
|
||||
|
||||
return (
|
||||
<form className="container px-3" onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="row mb-3">
|
||||
<div className="col-12 mb-3">
|
||||
<h5 className="fw-semibold">Expense Details</h5>
|
||||
<div className="row mb-1">
|
||||
<div className="col-12 mb-1">
|
||||
<h5 className="fw-semibold m-0">Expense Details</h5>
|
||||
<hr />
|
||||
</div>
|
||||
<div className="col-12 text-start fw-semibold my-2">{data?.expenseUId}</div>
|
||||
{/* Row 1 */}
|
||||
<div className="col-md-6 mb-3">
|
||||
<div className="d-flex">
|
||||
|
@ -96,8 +96,14 @@ export function localToUtc(dateString) {
|
||||
export const formatCurrency = (amount, currency = "INR", locale = "en-US") => {
|
||||
return new Intl.NumberFormat(locale, {
|
||||
style: "currency",
|
||||
notation: "compact",
|
||||
compactDisplay: "short",
|
||||
currency: currency,
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(amount);
|
||||
};
|
||||
|
||||
export const countDigit = (num) => {
|
||||
return Math.abs(num).toString().length;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user