diff --git a/src/components/Dashboard/AttendanceChart.jsx b/src/components/Dashboard/AttendanceOverview.jsx similarity index 97% rename from src/components/Dashboard/AttendanceChart.jsx rename to src/components/Dashboard/AttendanceOverview.jsx index 71d14e48..77fbee51 100644 --- a/src/components/Dashboard/AttendanceChart.jsx +++ b/src/components/Dashboard/AttendanceOverview.jsx @@ -100,9 +100,9 @@ const AttendanceOverview = () => { }; return ( -
+
{/* Header */} -
+
Attendance Overview

Role-wise present count

diff --git a/src/components/Dashboard/Dashboard.jsx b/src/components/Dashboard/Dashboard.jsx index 3d0f0a66..ad5ead8c 100644 --- a/src/components/Dashboard/Dashboard.jsx +++ b/src/components/Dashboard/Dashboard.jsx @@ -12,11 +12,11 @@ import Teams from "./Teams"; import TasksCard from "./Tasks"; import ProjectCompletionChart from "./ProjectCompletionChart"; import ProjectProgressChart from "./ProjectProgressChart"; -import ProjectOverview from "../Project/ProjectOverview"; -import AttendanceOverview from "./AttendanceChart"; +import AttendanceOverview from "./AttendanceOverview"; import ExpenseAnalysis from "./ExpenseAnalysis"; import ExpenseStatus from "./ExpenseStatus"; import ExpenseByProject from "./ExpenseByProject"; +import ProjectStatistics from "../Project/ProjectStatistics"; const Dashboard = () => { @@ -29,16 +29,16 @@ const Dashboard = () => {
{isAllProjectsSelected && (
- +
)}
- +
- +
{isAllProjectsSelected && ( @@ -46,32 +46,31 @@ const Dashboard = () => {
)} - - {!isAllProjectsSelected && ( -
- -
- )} -
-
-
- -
-
- -
-
- -
-
{!isAllProjectsSelected && (
)} + + {!isAllProjectsSelected && ( +
+ +
+ )} +
+
+ +
+
+ +
+
+ +
+
diff --git a/src/components/Dashboard/ExpenseAnalysis.jsx b/src/components/Dashboard/ExpenseAnalysis.jsx index 1fe27fae..3c6b0d08 100644 --- a/src/components/Dashboard/ExpenseAnalysis.jsx +++ b/src/components/Dashboard/ExpenseAnalysis.jsx @@ -7,11 +7,12 @@ import { FormProvider, useForm } from "react-hook-form"; import { formatCurrency, localToUtc } from "../../utils/appUtils"; import { useProjectName } from "../../hooks/useProjects"; import { SpinnerLoader } from "../common/Loader"; +import flatColors from "../Charts/flatColor"; const ExpenseAnalysis = () => { const projectId = useSelectedProject(); const [projectName, setProjectName] = useState("All Project"); - const { projectNames, loading } = useProjectName(); + const { projectNames } = useProjectName(); const methods = useForm({ defaultValues: { startDate: "", endDate: "" }, @@ -50,7 +51,7 @@ const ExpenseAnalysis = () => { labels, legend: { show: false }, dataLabels: { enabled: true, formatter: (val) => `${val.toFixed(0)}%` }, - colors: ["#7367F0", "#28C76F", "#FF9F43", "#EA5455", "#00CFE8", "#FF78B8"], + colors: flatColors, plotOptions: { pie: { donut: { @@ -79,22 +80,19 @@ const ExpenseAnalysis = () => { return ( <> -
-
+
Expense Breakdown
- {/*

Category Wise Expense Breakdown

*/}

{projectName}

-
+
- {/* Card body */}
{isLoading && (
{
)} - {!isLoading && report.length > 0 && ( <> {isFetching && ( @@ -123,50 +120,59 @@ const ExpenseAnalysis = () => {
)} -
- -
+
+ {/* Chart Column */} +
+ +
-
-
- {report.map((item, idx) => ( -
-
- - - + {/* Data/Legend Column */} +
+
+ {report.map((item, idx) => ( +
+
+ + {item.projectName} + + + {formatCurrency(item.totalApprovedAmount)} + +
-
- {item.projectName} - - {formatCurrency(item.totalApprovedAmount)} - -
-
- ))} + + ))} +
)}
- - {/* Header */} - ); }; diff --git a/src/components/Dashboard/ExpenseByProject.jsx b/src/components/Dashboard/ExpenseByProject.jsx index 9a0e5a95..c589bc45 100644 --- a/src/components/Dashboard/ExpenseByProject.jsx +++ b/src/components/Dashboard/ExpenseByProject.jsx @@ -92,7 +92,7 @@ const ExpenseByProject = () => {
{/* Header */}
-
+
Monthly Expense -

{projectName}

diff --git a/src/components/Dashboard/ExpenseStatus.jsx b/src/components/Dashboard/ExpenseStatus.jsx index d6fefe7d..a45fb343 100644 --- a/src/components/Dashboard/ExpenseStatus.jsx +++ b/src/components/Dashboard/ExpenseStatus.jsx @@ -103,7 +103,7 @@ const ExpenseStatus = () => {
= 3 ? "text-xl" : "text-2xl" + className={`text-royalblue ${countDigit(item?.count || 0) >= 3 ? "text-xl" : "text-xl" } text-gray-500`} > {item?.count || 0} @@ -122,7 +122,7 @@ const ExpenseStatus = () => { {isManageExpense && (
handleNavigate(EXPENSE_STATUS.process_pending)} + onClick={() => handleNavigate(EXPENSE_STATUS.payment_processed)} >
{
3 ? "text-" : "text-3xl" + className={`text-end text-royalblue ${countDigit(data?.totalAmount || 0) > 3 ? "text-xl" : "text-3xl" } text-md`} > {formatCurrency(data?.totalAmount || 0)} diff --git a/src/components/Dashboard/ProjectCompletionChart.jsx b/src/components/Dashboard/ProjectCompletionChart.jsx index 98ee1d3b..7cc4a092 100644 --- a/src/components/Dashboard/ProjectCompletionChart.jsx +++ b/src/components/Dashboard/ProjectCompletionChart.jsx @@ -1,11 +1,14 @@ -import React from "react"; +import React, { useState } from "react"; import HorizontalBarChart from "../Charts/HorizontalBarChart"; import { useProjects } from "../../hooks/useProjects"; +import { ITEMS_PER_PAGE } from "../../utils/constants"; const ProjectCompletionChart = () => { - const { data: projects = [], isLoading: loading, isError, error } = useProjects(); + const [currentPage, setCurrentPage] = useState(1); + const { data: projects, isLoading: loading, isError, error } = useProjects(currentPage, ITEMS_PER_PAGE); + console.log("Kartik", projects) // Bar chart logic const projectNames = projects?.map((p) => p.name) || []; const projectProgress = diff --git a/src/components/Layout/Sidebar.jsx b/src/components/Layout/Sidebar.jsx index b41cb654..1f4b63b8 100644 --- a/src/components/Layout/Sidebar.jsx +++ b/src/components/Layout/Sidebar.jsx @@ -99,7 +99,7 @@ const MenuItem = (item) => { className={`menu-link ${hasSubmenu ? "menu-toggle" : ""}`} target={item.link?.includes("http") ? "_blank" : undefined} > - + {item.icon && }
{item.name}
{item.available === false && (
diff --git a/src/components/PaymentRequest/ActionPaymentRequest.jsx b/src/components/PaymentRequest/ActionPaymentRequest.jsx index 18d30092..5f3cc1d9 100644 --- a/src/components/PaymentRequest/ActionPaymentRequest.jsx +++ b/src/components/PaymentRequest/ActionPaymentRequest.jsx @@ -40,6 +40,8 @@ const ActionPaymentRequest = ({ requestId }) => { error: PaymentModeError, } = usePaymentMode(); + console.log("Kartik", data) + const IsReview = useHasUserPermission(REVIEW_EXPENSE); const [imageLoaded, setImageLoaded] = useState({}); @@ -175,6 +177,16 @@ const ActionPaymentRequest = ({ requestId }) => { const newFiles = files.filter((_, i) => i !== index); setValue("billAttachments", newFiles, { shouldValidate: true }); }; + + const filteredPaymentModes = useMemo(() => { + return PaymentModes?.filter((mode) => { + if (mode.name === "Advance Payment" && data?.isAdvancePayment === false) { + return false; + } + return true; + }) || []; + }, [PaymentModes, data]); + return (
{IsPaymentProcess && nextStatusWithPermission?.length > 0 && ( @@ -219,7 +231,7 @@ const ActionPaymentRequest = ({ requestId }) => { {PaymentModeLoading ? ( ) : ( - PaymentModes?.map((payment) => ( + filteredPaymentModes?.map((payment) => ( diff --git a/src/components/PaymentRequest/PaymentRequestFilterChips.jsx b/src/components/PaymentRequest/PaymentRequestFilterChips.jsx new file mode 100644 index 00000000..201b75f9 --- /dev/null +++ b/src/components/PaymentRequest/PaymentRequestFilterChips.jsx @@ -0,0 +1,60 @@ +import React, { useMemo } from "react"; + +const PaymentRequestFilterChips = ({ filters, filterData, removeFilterChip, clearFilter }) => { + const data = filterData?.data || filterData || {}; + + const filterChips = useMemo(() => { + const chips = []; + + const addGroup = (ids, list, label, key) => { + if (!ids?.length) return; + const items = ids.map((id) => ({ + id, + name: list?.find((i) => i.id === id)?.name || id, + })); + chips.push({ key, label, items }); + }; + + addGroup(filters.createdByIds, data.createdBy, "Created By", "createdByIds"); + addGroup(filters.currencyIds, data.currency, "Currency", "currencyIds"); + addGroup(filters.expenseCategoryIds, data.expenseCategory, "Expense Category", "expenseCategoryIds"); + addGroup(filters.payees, data.payees, "Payees", "payees"); + addGroup(filters.projectIds, data.projects, "Projects", "projectIds"); + addGroup(filters.statusIds, data.status, "Status", "statusIds"); + + return chips; + }, [filters, filterData]); + + if (!filterChips.length) return null; + + return ( +
+ {filterChips.map((chipGroup) => ( +
+ {chipGroup.label}: + {chipGroup.items.map((item) => ( + + {item.name} +
+ ))} + +
+ ); +}; + +export default PaymentRequestFilterChips; diff --git a/src/components/PaymentRequest/PaymentRequestFilterPanel.jsx b/src/components/PaymentRequest/PaymentRequestFilterPanel.jsx index 65d788e9..9bbd24da 100644 --- a/src/components/PaymentRequest/PaymentRequestFilterPanel.jsx +++ b/src/components/PaymentRequest/PaymentRequestFilterPanel.jsx @@ -1,7 +1,7 @@ -import React, { useEffect, useState, useMemo } from "react"; +import React, { useEffect, useState, useMemo, forwardRef, useImperativeHandle } from "react"; import { FormProvider, useForm, Controller } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; -import { defaultPaymentRequestFilter,SearchPaymentRequestSchema } from "./PaymentRequestSchema"; +import { defaultPaymentRequestFilter, SearchPaymentRequestSchema } from "./PaymentRequestSchema"; import DateRangePicker, { DateRangePicker1 } from "../common/DateRangePicker"; import SelectMultiple from "../common/SelectMultiple"; @@ -13,7 +13,7 @@ import moment from "moment"; import { usePaymentRequestFilter } from "../../hooks/useExpense"; import { useLocation, useNavigate, useParams } from "react-router-dom"; -const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => { +const PaymentRequestFilterPanel = forwardRef(({ onApply, handleGroupBy, setFilterdata, clearFilter }, ref) => { const { status } = useParams(); const navigate = useNavigate(); const selectedProjectId = useSelector( @@ -38,10 +38,23 @@ const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => { const [selectedGroup, setSelectedGroup] = useState(groupByList[6]); const [resetKey, setResetKey] = useState(0); + const dynamicDefaultFilter = useMemo(() => { + return { + ...defaultPaymentRequestFilter, + projectIds: defaultPaymentRequestFilter.projectIds || [], + statusIds: status ? [status] : defaultPaymentRequestFilter.statusIds || [], + createdByIds: defaultPaymentRequestFilter.createdByIds || [], + currencyIds: defaultPaymentRequestFilter.currencyIds || [], + expenseCategoryIds: defaultPaymentRequestFilter.expenseCategoryIds || [], + payees: defaultPaymentRequestFilter.payees || [], + startDate: defaultPaymentRequestFilter.startDate, + endDate: defaultPaymentRequestFilter.endDate, + }; + }, [status, selectedProjectId]); const methods = useForm({ resolver: zodResolver(SearchPaymentRequestSchema), - defaultValues: defaultPaymentRequestFilter, + defaultValues: dynamicDefaultFilter, }); const { control, handleSubmit, reset, setValue, watch } = methods; @@ -52,12 +65,28 @@ const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => { }; - const handleGroupChange = (e) => { const group = groupByList.find((g) => g.id === e.target.value); if (group) setSelectedGroup(group); }; + useImperativeHandle(ref, () => ({ + resetFieldValue: (name, value) => { + // Reset specific field + if (value !== undefined) { + setValue(name, value); + } else { + reset({ ...methods.getValues(), [name]: defaultFilter[name] }); + } + }, + getValues: methods.getValues, // optional, to read current filter state + })); + + useEffect(() => { + if (data && setFilterdata) { + setFilterdata(data); + } + }, [data, setFilterdata]); const onSubmit = (formData) => { onApply({ @@ -179,7 +208,7 @@ const PaymentRequestFilterPanel = ({ onApply, handleGroupBy }) => { ))}
-
+