diff --git a/src/components/AdvancePayment/AdvancePaymentList.jsx b/src/components/AdvancePayment/AdvancePaymentList.jsx index 450c73a2..409a9d28 100644 --- a/src/components/AdvancePayment/AdvancePaymentList.jsx +++ b/src/components/AdvancePayment/AdvancePaymentList.jsx @@ -1,13 +1,53 @@ -import React from "react"; +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 }); - // Handle no employee selected + + 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; - - return { - id: r.id, - description: r.title || "-", - projectName: r.project?.name || "-", - createdAt: r.createdAt, - credit, - debit, - balance: currentBalance, - }; - }); - const columns = [ + { + key: "date", + label: ( + <> + Date + + ), + align: "text-start", + }, { key: "description", label: "Description", align: "text-start" }, + { key: "credit", label: ( @@ -84,6 +109,7 @@ const AdvancePaymentList = ({ employeeId }) => { ), align: "text-end", }, + { key: "balance", label: ( @@ -103,6 +129,17 @@ const AdvancePaymentList = ({ employeeId }) => {
); } + 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 (
@@ -129,17 +166,25 @@ 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 || "-"} @@ -152,21 +197,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/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/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/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/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", + })} + + + ) : ( + <> + )} +
      +
      -
      -
      + ); }; 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",