From 28f15f649f9d9cf374595b1ef1fd7d7de5fcfd03 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Thu, 6 Nov 2025 13:17:31 +0530 Subject: [PATCH 1/2] update advance trsnaction table --- .../AdvancePayment/AdvancePaymentList.jsx | 91 +++++++++++++----- src/components/Expenses/ExpenseList.jsx | 2 +- .../ManageRecurringExpense.jsx | 20 ---- src/components/common/EmployeeSearchInput.jsx | 1 + .../common/MultiEmployeeSearchInput.jsx | 6 +- src/hooks/useExpense.js | 3 +- .../AdvancePayment/AdvancePaymentPage.jsx | 94 ++++++++++++++----- 7 files changed, 147 insertions(+), 70 deletions(-) diff --git a/src/components/AdvancePayment/AdvancePaymentList.jsx b/src/components/AdvancePayment/AdvancePaymentList.jsx index e14f9c06..c3aa10f6 100644 --- a/src/components/AdvancePayment/AdvancePaymentList.jsx +++ b/src/components/AdvancePayment/AdvancePaymentList.jsx @@ -1,14 +1,24 @@ -import React from "react"; -import { useExpenseTransactions } from "../../hooks/useExpense"; +import React, { useEffect, useMemo } from "react"; +import { + + useExpenseTransactions, +} from "../../hooks/useExpense"; import Error from "../common/Error"; import { formatUTCToLocalTime } from "../../utils/dateUtils"; import Loader, { SpinnerLoader } from "../common/Loader"; +import { useForm, useFormContext } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; +import { employee } from "../../data/masters"; +import { useAdvancePaymentContext } from "../../pages/AdvancePayment/AdvancePaymentPage"; +import { formatFigure } from "../../utils/appUtils"; + + const AdvancePaymentList = ({ employeeId }) => { + const { setBalance} = useAdvancePaymentContext() const { data, isError, isLoading, error, isFetching } = useExpenseTransactions(employeeId, { enabled: !!employeeId }); - console.log(data) - // Handle no employee selected if (!employeeId) { return (
{ ); } + const records = Array.isArray(data) ? data : []; let currentBalance = 0; @@ -53,7 +64,7 @@ const AdvancePaymentList = ({ employeeId }) => { const credit = isCredit ? r.amount : 0; const debit = !isCredit ? Math.abs(r.amount) : 0; currentBalance += credit - debit; - + setBalance(currentBalance); return { id: r.id, description: r.title || "-", @@ -61,12 +72,23 @@ const AdvancePaymentList = ({ employeeId }) => { createdAt: r.createdAt, credit, debit, + financeUId:r.financeUId, balance: currentBalance, }; }); const columns = [ + { + key: "date", + label: ( + <> + Date + + ), + align: "text-start", + }, { key: "description", label: "Description", align: "text-start" }, + { key: "credit", label: ( @@ -85,6 +107,7 @@ const AdvancePaymentList = ({ employeeId }) => { ), align: "text-end", }, + { key: "balance", label: ( @@ -104,7 +127,18 @@ const AdvancePaymentList = ({ employeeId }) => {
); } - console.log("Kartik", rowsWithBalance) + const DecideCreditOrDebit = ({ financeUId }) => { + if (!financeUId) return null; + + const prefix = financeUId?.substring(0, 2).toUpperCase(); + + if (prefix === "PR") return +; + if (prefix === "EX") return -; + + return null; +}; + + return (
@@ -131,17 +165,22 @@ const AdvancePaymentList = ({ employeeId }) => { ) ) : col.key === "debit" ? ( row.amount < 0 ? ( - {Math.abs(row.amount).toLocaleString("en-IN")} + + {Math.abs(row.amount).toLocaleString("en-IN")} + ) : ( "-" ) ) : col.key === "balance" ? ( - {row.currentBalance?.toLocaleString("en-IN")} +
+ {formatFigure(row.currentBalance)} +
+ ) : col.key === "date" ? ( + + {formatUTCToLocalTime(row.paidAt)} + ) : ( -
- - {formatUTCToLocalTime(row.paidAt)} - +
{row.project?.name || "-"} @@ -154,21 +193,31 @@ const AdvancePaymentList = ({ employeeId }) => { )) ) : (
- )} - - - - + + + diff --git a/src/components/Expenses/ExpenseList.jsx b/src/components/Expenses/ExpenseList.jsx index d7a0a1b4..50b7632e 100644 --- a/src/components/Expenses/ExpenseList.jsx +++ b/src/components/Expenses/ExpenseList.jsx @@ -267,7 +267,7 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => { Object.values(grouped).map(({ key, displayField, items }) => ( -
+ No advance payment records found.
Final Balance - {currentBalance.toLocaleString("en-IN", { - style: "currency", - currency: "INR", - })} +
+ {" "} +
+ Final Balance +
+
+
+ {currentBalance.toLocaleString("en-IN", { + style: "currency", + currency: "INR", + })} +
+
{" "} diff --git a/src/components/RecurringExpense/ManageRecurringExpense.jsx b/src/components/RecurringExpense/ManageRecurringExpense.jsx index 01fa07eb..8788f0e9 100644 --- a/src/components/RecurringExpense/ManageRecurringExpense.jsx +++ b/src/components/RecurringExpense/ManageRecurringExpense.jsx @@ -403,26 +403,6 @@ function ManageRecurringExpense({ closeModal, requestToEdit = null }) {
- {/* Notify */} - {/*
-
- - - {errors.notifyTo && ( - - {errors.notifyTo.message} - - )} -
-
*/}
diff --git a/src/components/common/EmployeeSearchInput.jsx b/src/components/common/EmployeeSearchInput.jsx index c4c68db2..f4369bf7 100644 --- a/src/components/common/EmployeeSearchInput.jsx +++ b/src/components/common/EmployeeSearchInput.jsx @@ -32,6 +32,7 @@ const EmployeeSearchInput = ({ const found = employees.data.find((emp) => emp.id === value); if (found && forAll) { setSearch(`${found.firstName} ${found.lastName}`); + sessionStorage.setItem("transaction-empId",found?.id) } } }, [value, employees?.data, forAll]); diff --git a/src/components/common/MultiEmployeeSearchInput.jsx b/src/components/common/MultiEmployeeSearchInput.jsx index 23a3a5a0..c5501890 100644 --- a/src/components/common/MultiEmployeeSearchInput.jsx +++ b/src/components/common/MultiEmployeeSearchInput.jsx @@ -53,7 +53,7 @@ const MultiEmployeeSearchInput = ({ }, [value, employees?.data, forAll]); const handleSelect = (employee) => { - if (!selectedEmployees.find((emp) => emp.email === employee.email)) { + if (!selectedEmployees.find((emp) => emp.id === employee.id)) { const newSelected = [...selectedEmployees, employee]; setSelectedEmployees(newSelected); // Store emails instead of IDs @@ -150,11 +150,11 @@ const MultiEmployeeSearchInput = ({ ) : ( employees?.data ?.filter( - (emp) => !selectedEmployees.find((e) => e.email === emp.email) + (emp) => !selectedEmployees.find((e) => e.id === emp.id) ) .map((emp) => (
  • handleSelect(emp)} diff --git a/src/hooks/useExpense.js b/src/hooks/useExpense.js index 34b9c7ac..464247b2 100644 --- a/src/hooks/useExpense.js +++ b/src/hooks/useExpense.js @@ -434,7 +434,8 @@ export const useExpenseTransactions = (employeeId)=>{ const resp = await ExpenseRepository.GetTranctionList(employeeId); return resp.data }, - enabled:!!employeeId + enabled:!!employeeId, + keepPreviousData:true, }) } //#endregion diff --git a/src/pages/AdvancePayment/AdvancePaymentPage.jsx b/src/pages/AdvancePayment/AdvancePaymentPage.jsx index d03ac49a..9c080ab8 100644 --- a/src/pages/AdvancePayment/AdvancePaymentPage.jsx +++ b/src/pages/AdvancePayment/AdvancePaymentPage.jsx @@ -1,45 +1,91 @@ -import React from "react"; +import React, { + createContext, + useContext, + useEffect, + useMemo, + useState, +} from "react"; import Breadcrumb from "../../components/common/Breadcrumb"; import { useEmployee } from "../../hooks/useEmployees"; import EmployeeSearchInput from "../../components/common/EmployeeSearchInput"; import { useForm } from "react-hook-form"; import Label from "../../components/common/Label"; import AdvancePaymentList from "../../components/AdvancePayment/AdvancePaymentList"; +import { employee } from "../../data/masters"; +import { formatFigure } from "../../utils/appUtils"; +export const AdvancePaymentContext = createContext(); +export const useAdvancePaymentContext = () => { + const context = useContext(AdvancePaymentContext); + if (!context) { + throw new Error( + "useAdvancePaymentContext must be used within an AdvancePaymentProvider" + ); + } + return context; +}; const AdvancePaymentPage = () => { - const { control, watch } = useForm({ + const [balance, setBalance] = useState(null); + const { control, reset, watch } = useForm({ defaultValues: { employeeId: "", }, }); const selectedEmployeeId = watch("employeeId"); + useEffect(() => { + const selectedEmpoyee = sessionStorage.getItem("transaction-empId"); + reset({ + employeeId: selectedEmpoyee || "", + }); + }, [reset]); return ( -
    - -
    -
    -
    -
    - -
    + +
    + +
    +
    +
    +
    + +
    +
    +
    + {balance ? ( + <> + + 0 ? "text-success" : "text-danger" + } fs-5 fw-bold ms-1`} + > + {formatFigure(balance, { + type: "currency", + currency: "INR", + })} + + + ) : ( + <> + )} +
    +
    -
    -
    + ); }; From 72e5cf0bbec35c8e854807c309bb51b350c09476 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Thu, 6 Nov 2025 15:48:53 +0530 Subject: [PATCH 2/2] display all time lien from darft --- .../AdvancePayment/AdvancePaymentList.jsx | 100 +++++++++--------- src/components/Expenses/ViewExpense.jsx | 4 +- .../PaymentRequest/PaymentRequestSchema.js | 48 ++++----- .../PaymentRequest/PaymentStatusLogs.jsx | 16 ++- src/components/common/TimeLine.jsx | 16 ++- src/utils/constants.jsx | 2 +- 6 files changed, 95 insertions(+), 91 deletions(-) diff --git a/src/components/AdvancePayment/AdvancePaymentList.jsx b/src/components/AdvancePayment/AdvancePaymentList.jsx index c3aa10f6..409a9d28 100644 --- a/src/components/AdvancePayment/AdvancePaymentList.jsx +++ b/src/components/AdvancePayment/AdvancePaymentList.jsx @@ -1,8 +1,5 @@ import React, { useEffect, useMemo } from "react"; -import { - - useExpenseTransactions, -} from "../../hooks/useExpense"; +import { useExpenseTransactions } from "../../hooks/useExpense"; import Error from "../common/Error"; import { formatUTCToLocalTime } from "../../utils/dateUtils"; import Loader, { SpinnerLoader } from "../common/Loader"; @@ -13,12 +10,44 @@ import { employee } from "../../data/masters"; import { useAdvancePaymentContext } from "../../pages/AdvancePayment/AdvancePaymentPage"; import { formatFigure } from "../../utils/appUtils"; - - const AdvancePaymentList = ({ employeeId }) => { - const { setBalance} = useAdvancePaymentContext() + const { setBalance } = useAdvancePaymentContext(); const { data, isError, isLoading, error, isFetching } = useExpenseTransactions(employeeId, { enabled: !!employeeId }); + + const records = Array.isArray(data) ? data : []; + + let currentBalance = 0; + const rowsWithBalance = records.map((r) => { + const isCredit = r.amount > 0; + const credit = isCredit ? r.amount : 0; + const debit = !isCredit ? Math.abs(r.amount) : 0; + currentBalance += credit - debit; + return { + id: r.id, + description: r.title || "-", + projectName: r.project?.name || "-", + createdAt: r.createdAt, + credit, + debit, + financeUId: r.financeUId, + balance: currentBalance, + }; + }); + + useEffect(() => { + if (!employeeId) { + setBalance(null); + return; + } + + if (rowsWithBalance.length > 0) { + setBalance(rowsWithBalance[rowsWithBalance.length - 1].balance); + } else { + setBalance(0); + } + }, [employeeId, data, setBalance]); + if (!employeeId) { return (
    { ); } - // Handle loading state if (isLoading || isFetching) { return (
    { ); } - // Handle error state if (isError) { return ( -
    - {error?.status === 404 ? ( - "No advance payment transactions found." - ) : ( - - )} +
    + {error?.status === 404 + ? "No advance payment transactions found." + : }
    ); } - - - const records = Array.isArray(data) ? data : []; - - let currentBalance = 0; - const rowsWithBalance = records.map((r) => { - const isCredit = r.amount > 0; - const credit = isCredit ? r.amount : 0; - const debit = !isCredit ? Math.abs(r.amount) : 0; - currentBalance += credit - debit; - setBalance(currentBalance); - return { - id: r.id, - description: r.title || "-", - projectName: r.project?.name || "-", - createdAt: r.createdAt, - credit, - debit, - financeUId:r.financeUId, - balance: currentBalance, - }; - }); - const columns = [ { key: "date", label: ( <> - Date + Date ), align: "text-start", @@ -127,17 +129,16 @@ const AdvancePaymentList = ({ employeeId }) => {
    ); } - const DecideCreditOrDebit = ({ financeUId }) => { - if (!financeUId) return null; + const DecideCreditOrDebit = ({ financeUId }) => { + if (!financeUId) return null; - const prefix = financeUId?.substring(0, 2).toUpperCase(); + const prefix = financeUId?.substring(0, 2).toUpperCase(); - if (prefix === "PR") return +; - if (prefix === "EX") return -; - - return null; -}; + if (prefix === "PR") return +; + if (prefix === "EX") return -; + return null; + }; return (
    @@ -173,7 +174,10 @@ const AdvancePaymentList = ({ employeeId }) => { ) ) : col.key === "balance" ? (
    - {formatFigure(row.currentBalance)} + + + {formatFigure(row.currentBalance)} +
    ) : col.key === "date" ? ( diff --git a/src/components/Expenses/ViewExpense.jsx b/src/components/Expenses/ViewExpense.jsx index d6bb87e7..b3256a62 100644 --- a/src/components/Expenses/ViewExpense.jsx +++ b/src/components/Expenses/ViewExpense.jsx @@ -117,7 +117,7 @@ const ViewExpense = ({ ExpenseId }) => {
    -
    +
    {data?.expenseUId} @@ -525,7 +525,7 @@ const ViewExpense = ({ ExpenseId }) => {
    -
    +
    {" "}

    TimeLine

    diff --git a/src/components/PaymentRequest/PaymentRequestSchema.js b/src/components/PaymentRequest/PaymentRequestSchema.js index 8c0e7141..abf194ca 100644 --- a/src/components/PaymentRequest/PaymentRequestSchema.js +++ b/src/components/PaymentRequest/PaymentRequestSchema.js @@ -27,31 +27,27 @@ export const PaymentRequestSchema = (expenseTypes, isItself) => { .refine((val) => /^\d+(\.\d{1,2})?$/.test(val.toString()), { message: "Amount must have at most 2 decimal places", }), - - billAttachments: z - .array( - z.object({ - fileName: z.string().min(1, { message: "Filename is required" }), - base64Data: z.string().nullable(), - contentType: z - .string() - .refine((val) => ALLOWED_TYPES.includes(val), { - message: "Only PDF, PNG, JPG, or JPEG files are allowed", - }), - documentId: z.string().optional(), - fileSize: z.number().max(MAX_FILE_SIZE, { - message: "File size must be less than or equal to 5MB", - }), - description: z.string().optional(), - isActive: z.boolean().default(true), - }) - ) - , - }) - }; + + billAttachments: z.array( + z.object({ + fileName: z.string().min(1, { message: "Filename is required" }), + base64Data: z.string().nullable(), + contentType: z.string().refine((val) => ALLOWED_TYPES.includes(val), { + message: "Only PDF, PNG, JPG, or JPEG files are allowed", + }), + documentId: z.string().optional(), + fileSize: z.number().max(MAX_FILE_SIZE, { + message: "File size must be less than or equal to 5MB", + }), + description: z.string().optional(), + isActive: z.boolean().default(true), + }) + ), + }); +}; export const defaultPaymentRequest = { - title:"", + title: "", description: "", payee: "", currencyId: "", @@ -63,7 +59,6 @@ export const defaultPaymentRequest = { billAttachments: [], }; - export const SearchPaymentRequestSchema = z.object({ projectIds: z.array(z.string()).optional(), statusIds: z.array(z.string()).optional(), @@ -86,7 +81,6 @@ export const defaultPaymentRequestFilter = { endDate: null, }; - export const PaymentRequestActionScheam = ( isTransaction = false, transactionDate @@ -149,9 +143,9 @@ export const defaultPRActionValues = { paidTransactionId: null, paidAt: null, paidById: null, - tdsPercentage:"0", + tdsPercentage: "0", baseAmount: null, - taxAmount:null, + taxAmount: null, }; export const RequestedExpenseSchema = z.object({ diff --git a/src/components/PaymentRequest/PaymentStatusLogs.jsx b/src/components/PaymentRequest/PaymentStatusLogs.jsx index 9abb3fa5..15e666a0 100644 --- a/src/components/PaymentRequest/PaymentStatusLogs.jsx +++ b/src/components/PaymentRequest/PaymentStatusLogs.jsx @@ -5,7 +5,6 @@ import Timeline from "../common/TimeLine"; import moment from "moment"; import { getColorNameFromHex } from "../../utils/appUtils"; const PaymentStatusLogs = ({ data }) => { - const [visibleCount, setVisibleCount] = useState(4); const sortedLogs = useMemo(() => { if (!data?.updateLogs) return []; @@ -14,18 +13,16 @@ const PaymentStatusLogs = ({ data }) => { ); }, [data?.updateLogs]); - const logsToShow = useMemo( - () => sortedLogs.slice(0, visibleCount), - [sortedLogs, visibleCount] - ); + const timelineData = useMemo(() => { - return logsToShow.map((log, index) => ({ - id: index + 1, + return sortedLogs.map((log, index) => ({ + id: log.id, title: log.nextStatus?.name || "Status Updated", description: log.nextStatus?.description || "", timeAgo: log.updatedAt, color: getColorNameFromHex(log.nextStatus?.color) || "primary", + userComment:log.comment, users: log.updatedBy ? [ { @@ -37,14 +34,13 @@ const PaymentStatusLogs = ({ data }) => { ] : [], })); - }, [logsToShow]); + }, [sortedLogs]); const handleShowMore = () => { setVisibleCount((prev) => prev + 4); }; - return ( -
    +
    {/*
    {logsToShow.map((log) => (
    diff --git a/src/components/common/TimeLine.jsx b/src/components/common/TimeLine.jsx index 52316866..de3d67ad 100644 --- a/src/components/common/TimeLine.jsx +++ b/src/components/common/TimeLine.jsx @@ -5,6 +5,13 @@ import { formatUTCToLocalTime } from "../../utils/dateUtils"; import moment from "moment"; const Timeline = ({ items = [], transparent = true }) => { + if(items.length === 0){ + return ( +
    +

    Not Action yet

    +
    + ) + } return (
      { >
      -
      +
      {item.title}
      {moment.utc(item.timeAgo).local().fromNow()}
      - {item.description &&

      {item.description}

      } + {item.description &&

      {item.description}

      } {item.attachments && item.attachments.length > 0 && (
      @@ -54,7 +61,7 @@ const Timeline = ({ items = [], transparent = true }) => { )} {item.users && item.users.length > 0 && ( -
      +
        {item.users.map((user, i) => (
      • @@ -82,8 +89,11 @@ const Timeline = ({ items = [], transparent = true }) => { {item.users[0].role}
      )} + +
      )} +
      {item.userComment &&

      {item.userComment}

      }
      ))} diff --git a/src/utils/constants.jsx b/src/utils/constants.jsx index 433c906a..fe0daa46 100644 --- a/src/utils/constants.jsx +++ b/src/utils/constants.jsx @@ -64,7 +64,7 @@ export const APPROVE_EXPENSE = "eaafdd76-8aac-45f9-a530-315589c6deca"; export const PROCESS_EXPENSE = "ea5a1529-4ee8-4828-80ea-0e23c9d4dd11"; -export const EXPENSE_MANAGE = "ea5a1529-4ee8-4828-80ea-0e23c9d4dd11"; +export const EXPENSE_MANAGE = "bdee29a2-b73b-402d-8dd1-c4b1f81ccbc3"; export const EXPENSE_REJECTEDBY = [ "965eda62-7907-4963-b4a1-657fb0b2724b",