diff --git a/public/assets/css/core-extend.css b/public/assets/css/core-extend.css index 0df9f4d2..5b328e9b 100644 --- a/public/assets/css/core-extend.css +++ b/public/assets/css/core-extend.css @@ -24,4 +24,73 @@ .text-md-b { font-weight: normal; -} \ No newline at end of file +} +.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; } diff --git a/src/components/Dashboard/Attendance.jsx b/src/components/Dashboard/Attendance.jsx index 96e5d9dc..57c34a3a 100644 --- a/src/components/Dashboard/Attendance.jsx +++ b/src/components/Dashboard/Attendance.jsx @@ -31,7 +31,7 @@ const selectedProjectId = useSelectedProject()
-
Attendance
+
Attendance

Daily Attendance Data

diff --git a/src/components/Dashboard/AttendanceOverview.jsx b/src/components/Dashboard/AttendanceOverview.jsx index d25cfa3e..dba31a52 100644 --- a/src/components/Dashboard/AttendanceOverview.jsx +++ b/src/components/Dashboard/AttendanceOverview.jsx @@ -65,7 +65,7 @@ const AttendanceOverview = () => { }; return ( -
+
{/* Optional subtle loading overlay */} {isLoading && (
@@ -76,7 +76,7 @@ const AttendanceOverview = () => { {/* Header */}
-
Attendance Overview
+
Attendance Overview

Role-wise present count

@@ -98,7 +98,7 @@ const AttendanceOverview = () => {
{view === "chart" ? (
- +
) : (
diff --git a/src/components/Dashboard/Dashboard.jsx b/src/components/Dashboard/Dashboard.jsx index a65d2d4f..9653b8cd 100644 --- a/src/components/Dashboard/Dashboard.jsx +++ b/src/components/Dashboard/Dashboard.jsx @@ -30,7 +30,7 @@ const Dashboard = () => { const isAllProjectsSelected = projectId === null; return ( -
+
@@ -39,7 +39,7 @@ const Dashboard = () => {
-
+
diff --git a/src/components/Dashboard/ExpenseAnalysis.jsx b/src/components/Dashboard/ExpenseAnalysis.jsx index 1e03de40..48daf6b8 100644 --- a/src/components/Dashboard/ExpenseAnalysis.jsx +++ b/src/components/Dashboard/ExpenseAnalysis.jsx @@ -71,9 +71,9 @@ const { labels, series, total } = useMemo(() => { return ( <> -
+
-
Expense Breakdown
+
Expense Breakdown

Category Wise Expense Breakdown

@@ -109,7 +109,7 @@ const { labels, series, total } = useMemo(() => {
)} -
+
item.totalApprovedAmount || 0)} diff --git a/src/components/Dashboard/ExpenseByProject.jsx b/src/components/Dashboard/ExpenseByProject.jsx index 3d15a9b6..97cf6c7a 100644 --- a/src/components/Dashboard/ExpenseByProject.jsx +++ b/src/components/Dashboard/ExpenseByProject.jsx @@ -7,157 +7,158 @@ import { formatCurrency } from "../../utils/appUtils"; import { formatDate_DayMonth } from "../../utils/dateUtils"; const ExpenseByProject = () => { - const projectId = useSelector((store) => store.localVariables.projectId); - const [range, setRange] = useState("12M"); - const [selectedType, setSelectedType] = useState(""); - const [viewMode, setViewMode] = useState("Category"); - const [chartData, setChartData] = useState({ categories: [], data: [] }); + const projectId = useSelector((store) => store.localVariables.projectId); + const [range, setRange] = useState("12M"); + const [selectedType, setSelectedType] = useState(""); + const [viewMode, setViewMode] = useState("Category"); + const [chartData, setChartData] = useState({ categories: [], data: [] }); - const { ExpenseTypes, loading: typeLoading } = useExpenseType(); + const { ExpenseTypes, loading: typeLoading } = useExpenseType(); - const { data: expenseApiData, isLoading } = useExpenseDataByProject( - projectId, - selectedType, - range === "All" ? null : parseInt(range) - ); + const { data: expenseApiData, isLoading } = useExpenseDataByProject( + projectId, + selectedType, + range === "All" ? null : parseInt(range) + ); - useEffect(() => { - if (expenseApiData) { - const categories = expenseApiData.map( - (item) => formatDate_DayMonth(item.monthName, item.year) - ); - const data = expenseApiData.map((item) => item.total); - setChartData({ categories, data }); - } else { - setChartData({ categories: [], data: [] }); - } - }, [expenseApiData]); + useEffect(() => { + if (expenseApiData) { + const categories = expenseApiData.map((item) => + formatDate_DayMonth(item.monthName, item.year) + ); + const data = expenseApiData.map((item) => item.total); + setChartData({ categories, data }); + } else { + setChartData({ categories: [], data: [] }); + } + }, [expenseApiData]); - const getSelectedTypeName = () => { - if (!selectedType) return "All Types"; - const found = ExpenseTypes.find((t) => t.id === selectedType); - return found ? found.name : "All Types"; - }; + const getSelectedTypeName = () => { + if (!selectedType) return "All Types"; + const found = ExpenseTypes.find((t) => t.id === selectedType); + return found ? found.name : "All Types"; + }; - const options = { - chart: { type: "bar", toolbar: { show: false } }, - plotOptions: { bar: { horizontal: false, columnWidth: "55%", borderRadius: 4 } }, - dataLabels: { enabled: true, formatter: (val) => formatCurrency(val) }, - xaxis: { - categories: chartData.categories, - labels: { style: { fontSize: "12px" }, rotate: -45 }, - }, - tooltip: { - y: { - formatter: (val) => `${formatCurrency(val)} (${getSelectedTypeName()})`, - }, - }, + const options = { + chart: { type: "bar", toolbar: { show: false } }, + plotOptions: { + bar: { horizontal: false, columnWidth: "55%", borderRadius: 4 }, + }, + dataLabels: { enabled: true, formatter: (val) => formatCurrency(val) }, + xaxis: { + categories: chartData.categories, + labels: { style: { fontSize: "12px" }, rotate: -45 }, + }, + tooltip: { + y: { + formatter: (val) => `${formatCurrency(val)} (${getSelectedTypeName()})`, + }, + }, - annotations: { xaxis: [{ x: 0, strokeDashArray: 0, }] }, - fill: { opacity: 1 }, - colors: ["#2196f3"], - }; + annotations: { xaxis: [{ x: 0, strokeDashArray: 0 }] }, + fill: { opacity: 1 }, + colors: ["#2196f3"], + }; - const series = [ - { - name: getSelectedTypeName(), - data: chartData.data, - }, - ]; + const series = [ + { + name: getSelectedTypeName(), + data: chartData.data, + }, + ]; - return ( -
- {/* Header */} -
-
-
-
Monthly Expense -
-

Detailed project expenses

-
-
- -
    -
  • - -
  • -
  • - -
  • -
-
-
- - {/* Range Buttons + Expense Dropdown */} -
- {["1M", "3M", "6M", "12M", "All"].map((item) => ( - - ))} - {viewMode === "Category" && ( - - )} -
- -
- - {/* Chart */} -
+ {/* Header */} +
+
+
+
Monthly Expense -
+

Detailed project expenses

+
+
+
+ {viewMode} + +
    +
  • + +
  • +
  • + +
  • +
+
- ); + + {/* Range Buttons + Expense Dropdown */} +
+ {["1M", "3M", "6M", "12M", "All"].map((item) => ( + + ))} + {viewMode === "Category" && ( + + )} +
+
+ + {/* Chart */} +
+ {isLoading ? ( +

Loading chart...

+ ) : ( + + )} +
+
+ ); }; -export default ExpenseByProject; \ No newline at end of file +export default ExpenseByProject; diff --git a/src/components/Dashboard/ExpenseStatus.jsx b/src/components/Dashboard/ExpenseStatus.jsx index 985a41b2..a49c2cd7 100644 --- a/src/components/Dashboard/ExpenseStatus.jsx +++ b/src/components/Dashboard/ExpenseStatus.jsx @@ -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 ( <> -
+
Expense - By Status

{projectName}

@@ -41,15 +43,15 @@ const ExpenseStatus = () => {
-
+ {isManageExpense && (
- Project Spendings:{" "} - {`(All Processed Payments)`} + 3 ? "text-base":"text-lg" }`}>Project Spendings:{" "} + {`(All Processed Payments)`}
- + 3 ? "text-":"text-3xl" } text-md`}> {formatCurrency(data?.totalAmount)} -
+
)}
{[ @@ -70,7 +72,7 @@ const ExpenseStatus = () => { status: EXPENSE_STATUS.approve_pending, }, { - title: "Pending Reviewer", + title: "Pending Review", count: data?.reviewPending?.count, amount: data?.reviewPending?.totalAmount, icon: "bx bx-search-alt-2", @@ -88,11 +90,11 @@ const ExpenseStatus = () => { ].map((item, idx) => (
handleNavigate(item?.status)} >
-
+
@@ -106,8 +108,8 @@ const ExpenseStatus = () => {
{" "} - - {item?.count}{" "} + = 3 ? "text-xl" : "text-2xl"} text-gray-500`}> + {item?.count } diff --git a/src/utils/appUtils.js b/src/utils/appUtils.js index bbfd3636..69437dc1 100644 --- a/src/utils/appUtils.js +++ b/src/utils/appUtils.js @@ -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; +};