From 48f314eac42d8bdf8f938704faf7d048c5cf422e Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Fri, 21 Nov 2025 15:28:31 +0530 Subject: [PATCH 01/64] added signalR for project Branch --- src/components/ServiceProject/Jobs.jsx | 13 +---- src/hooks/useServiceProject.jsx | 70 ++++++++++++-------------- src/services/signalRService.js | 3 ++ 3 files changed, 38 insertions(+), 48 deletions(-) diff --git a/src/components/ServiceProject/Jobs.jsx b/src/components/ServiceProject/Jobs.jsx index de308a6f..f0e1cb1e 100644 --- a/src/components/ServiceProject/Jobs.jsx +++ b/src/components/ServiceProject/Jobs.jsx @@ -66,19 +66,10 @@ const Jobs = () => {
diff --git a/src/hooks/useServiceProject.jsx b/src/hooks/useServiceProject.jsx index 82bc5713..057140b7 100644 --- a/src/hooks/useServiceProject.jsx +++ b/src/hooks/useServiceProject.jsx @@ -59,8 +59,8 @@ export const useCreateServiceProject = (onSuccessCallback) => { onError: (error) => { showToast( error?.response?.data?.message || - error.message || - "Failed to delete task", + error.message || + "Failed to delete task", "error" ); }, @@ -84,8 +84,8 @@ export const useUpdateServiceProject = (onSuccessCallback) => { onError: (error) => { showToast( error?.response?.data?.message || - error.message || - "Failed to update project", + error.message || + "Failed to update project", "error" ); }, @@ -110,8 +110,8 @@ export const useActiveInActiveServiceProject = (onSuccessCallback) => { onError: (error) => { showToast( error?.response?.data?.message || - error.message || - "Failed to update project", + error.message || + "Failed to update project", "error" ); }, @@ -138,8 +138,8 @@ export const useAllocationServiceProjectTeam = (onSuccessCallback) => { onError: (error) => { showToast( error?.response?.data?.message || - error.message || - "Failed to update project", + error.message || + "Failed to update project", "error" ); }, @@ -148,11 +148,6 @@ export const useAllocationServiceProjectTeam = (onSuccessCallback) => { //#endregion - - - - - //#region Service Jobs export const useServiceProjectJobs = ( @@ -163,7 +158,14 @@ export const useServiceProjectJobs = ( isArchive ) => { return useQuery({ - queryKey: ["serviceProjectJobs", pageSize, pageNumber, isActive, project, isArchive], + queryKey: [ + "serviceProjectJobs", + pageSize, + pageNumber, + isActive, + project, + isArchive, + ], queryFn: async () => { const resp = await ServiceProjectRepository.GetJobList( pageSize, @@ -230,8 +232,8 @@ export const useAddCommentJob = (onSuccessCallback) => { onError: (error) => { showToast( error?.response?.data?.message || - error.message || - "Failed to update project", + error.message || + "Failed to update project", "error" ); }, @@ -254,8 +256,8 @@ export const useCreateServiceProjectJob = (onSuccessCallback) => { onError: (error) => { showToast( error?.response?.data?.message || - error.message || - "Failed to update project", + error.message || + "Failed to update project", "error" ); }, @@ -284,27 +286,19 @@ export const useUpdateServiceProjectJob = (onSuccessCallback) => { } }, - onError: (error) => { showToast( error?.response?.data?.message || - error.message || - "Failed to update project", + error.message || + "Failed to update project", "error" ); }, }); }; - //#endregion - - - - - - //#region Branch export const useBranches = ( projectId, @@ -343,8 +337,8 @@ export const useBranchTypes = () => { const resp = await ServiceProjectRepository.GetBranchTypeList(); return resp.data; }, - }) -} + }); +}; export const useBranchDetails = (id) => { return useQuery({ @@ -353,9 +347,9 @@ export const useBranchDetails = (id) => { const resp = await ServiceProjectRepository.GetBranchDetail(id); return resp.data; }, - enabled: !!id - }) -} + enabled: !!id, + }); +}; export const useCreateBranch = (onSuccessCallBack) => { const queryClient = useQueryClient(); @@ -401,7 +395,6 @@ export const useUpdateBranch = (onSuccessCallBack) => { }); }; - export const useDeleteBranch = () => { const queryClient = useQueryClient(); @@ -411,14 +404,17 @@ export const useDeleteBranch = () => { onSuccess: (_, variable) => { queryClient.invalidateQueries({ queryKey: ["branches"] }); - showToast(`Branch ${variable.isActive ? "restored" : "deleted"} successfully`, "success"); + showToast( + `Branch ${variable.isActive ? "restored" : "deleted"} successfully`, + "success" + ); }, onError: (error) => { showToast( error?.response?.data?.message || - error.message || - "Failed to delete branch", + error.message || + "Failed to delete branch", "error" ); }, diff --git a/src/services/signalRService.js b/src/services/signalRService.js index 9b96b95a..067dc1a2 100644 --- a/src/services/signalRService.js +++ b/src/services/signalRService.js @@ -150,6 +150,9 @@ export function startSignalR(loggedUser) { queryClient.invalidateQueries(["serviceProjects"]); queryClient.invalidateQueries(["serviceProject"]); } + if (keyword === "Project_Branch") { + queryClient.invalidateQueries(["branches"]); + } if (keyword === "Service_Project_Allocation") { queryClient.invalidateQueries(["serviceProjectTeam"]); From 5747d4ec71014817edd54a6d79911e14606dcb3f Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Mon, 24 Nov 2025 10:35:03 +0530 Subject: [PATCH 02/64] Status Dropdown Should Auto-Close After Job Status Update --- .../ServiceProject/ServiceProjectJob/ChangeStatus.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ServiceProject/ServiceProjectJob/ChangeStatus.jsx b/src/components/ServiceProject/ServiceProjectJob/ChangeStatus.jsx index 935c621d..60d289f9 100644 --- a/src/components/ServiceProject/ServiceProjectJob/ChangeStatus.jsx +++ b/src/components/ServiceProject/ServiceProjectJob/ChangeStatus.jsx @@ -31,7 +31,7 @@ const ChangeStatus = ({ statusId, projectId, jobId, popUpId }) => { } = methods; const { mutate: UpdateStatus, isPending } = useUpdateServiceProjectJob(() => { - // handleClose(); + handleClose(); }); const onSubmit = (formData) => { const payload = From 482b5a1680f79ac7ea34bcb7a5dc05b98005b177 Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Mon, 24 Nov 2025 11:02:24 +0530 Subject: [PATCH 03/64] Adding lIst view. --- .../ServiceProjectTeam/ServiceProjectList.jsx | 227 ++++++++++++++++++ .../ServiceProject/ServiceProjectDisplay.jsx | 4 +- 2 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 src/components/ServiceProject/ServiceProjectTeam/ServiceProjectList.jsx diff --git a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectList.jsx b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectList.jsx new file mode 100644 index 00000000..e79750f0 --- /dev/null +++ b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectList.jsx @@ -0,0 +1,227 @@ +import React, { useState } from "react"; +import { MANAGE_PROJECT,PROJECT_STATUS } from "../../../utils/constants"; +import { useProjects } from "../../../hooks/useProjects"; +import { formatNumber,formatUTCToLocalTime } from "../../../utils/dateUtils"; +import ProgressBar from "../../common/ProgressBar"; +import { getProjectStatusColor,getProjectStatusName } from "../../../utils/projectStatus"; +import { useDispatch } from "react-redux"; +import { setProjectId } from "../../../slices/localVariablesSlice"; +import { useNavigate } from "react-router-dom"; +import { useHasUserPermission } from "../../../hooks/useHasUserPermission"; +import { useProjectContext } from "../../../pages/project/ProjectPage"; +import usePagination from "../../../hooks/usePagination"; +import Pagination from "../../common/Pagination"; + +const ServiceProjectList = ({ data, currentPage, totalPages, paginate }) => { + const dispatch = useDispatch(); + const navigate = useNavigate(); + const { setMangeProject } = useProjectContext(); + // const { data, isLoading, isError, error } = useProjects(); + + // check Permissions + const canManageProject = useHasUserPermission(MANAGE_PROJECT); + + const projectColumns = [ + { + key: "projectName", + label: "Project Name", + className: "text-start py-3", + getValue: (p) => ( +
{ + dispatch(setProjectId(p.id)); + navigate(`/projects/details`); + }} + > + {p.shortName ? `${p.name} (${p.shortName})` : p.name} +
+ ), + }, + { + key: "contactPerson", + label: "Contact Person", + className: "text-start small", + getValue: (p) => `${p?.contactPerson ?? ""}`.trim() || "N/A", + }, + { + key: "startDate", + label: "Start Date", + className: "text-center small", + getValue: (p) => formatUTCToLocalTime(p?.startDate) || "N/A", + }, + { + key: "deadline", + label: "Deadline", + className: "text-center small", + getValue: (p) => formatUTCToLocalTime(p?.endDate) || "N/A", + }, + { + key: "task", + label: "Task", + colSpan: 2, + className: "text-center small", + getValue: (p) => formatNumber(p?.plannedWork) || "0", + }, + { + key: "progress", + label: "Progress", + className: "text-start small", + + getValue: (p) => ( + + ), + }, + { + key: "status", + label: "Status", + className: "text-center small", + isFilter: true, + customRender: (_, selectedStatuses, handleStatusChange) => ( +
+ +
    + {PROJECT_STATUS.map(({ id, label }) => ( +
  • +
    + handleStatusChange(id)} + /> + +
    +
  • + ))} +
+
+ ), + getValue: (p) => ( + + {getProjectStatusName(p.projectStatusId)} + + ), + }, + ]; + + const handleViewActivities = (project) => { + dispatch(setProjectId(project)); + navigate(`/activities/records?project=${project}`); + }; + + return ( +
+
+ + + + {projectColumns.map((col) => ( + + ))} + + + + + {data?.map((project) => ( + + {projectColumns.map((col) => ( + + ))} + + + ))} + +
+ {col.label} + Action
+ {col.getValue + ? col.getValue(project) + : project[col.key] || "N/A"} + + +
+
+ + +
+ ); +}; + +export default ServiceProjectList; diff --git a/src/pages/ServiceProject/ServiceProjectDisplay.jsx b/src/pages/ServiceProject/ServiceProjectDisplay.jsx index 1d59f1f2..528a0683 100644 --- a/src/pages/ServiceProject/ServiceProjectDisplay.jsx +++ b/src/pages/ServiceProject/ServiceProjectDisplay.jsx @@ -11,6 +11,7 @@ import GlobalModel from "../../components/common/GlobalModel"; import ManageServiceProject from "../../components/ServiceProject/ManageServiceProject"; import { SpinnerLoader } from "../../components/common/Loader"; import ServiceProjectCard from "../../components/ServiceProject/ServiceProjectTeam/ServiceProjectCard"; +import ServiceProjectList from "../../components/ServiceProject/ServiceProjectTeam/ServiceProjectList"; const ServiceProjectDisplay = ({ listView ,selectedStatuses }) => { const [currentPage, setCurrentPage] = useState(1); @@ -49,7 +50,8 @@ const ServiceProjectDisplay = ({ listView ,selectedStatuses }) => { return (
{listView ? ( -

List

+ //

List

+ ) : ( filteredProjects?.map((project) => ( From 6dde240926ef3c5c67e53df0b6a5e822bd226500 Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Mon, 24 Nov 2025 12:15:37 +0530 Subject: [PATCH 04/64] Creating a list view for Services. --- .../ServiceProjectTeam/ServiceProjectCard.jsx | 21 +- .../ServiceProjectTeam/ServiceProjectList.jsx | 369 ++++++++---------- .../ServiceProject/ServiceProjectDisplay.jsx | 13 +- 3 files changed, 172 insertions(+), 231 deletions(-) diff --git a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectCard.jsx b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectCard.jsx index e6f39b36..21f77a4a 100644 --- a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectCard.jsx +++ b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectCard.jsx @@ -26,13 +26,6 @@ const ServiceProjectCard = ({ project, isCore = true }) => { const ManageProject = useHasUserPermission(MANAGE_PROJECT); const { setMangeProject, setManageServiceProject } = useProjectContext(); - const getProgress = (planned, completed) => { - return (completed * 100) / planned + "%"; - }; - const getProgressInNumber = (planned, completed) => { - return (completed * 100) / planned; - }; - const handleClose = () => setShowModal(false); const handleViewProject = () => { @@ -43,10 +36,6 @@ const ServiceProjectCard = ({ project, isCore = true }) => { navigate(`/service-projects/${project.id}`); } }; - const handleViewActivities = () => { - dispatch(setProjectId(project.id)); - navigate(`/activities/records?project=${project.id}`); - }; const handleManage = () => { if (isCore) { setMangeProject({ @@ -68,6 +57,8 @@ const ServiceProjectCard = ({ project, isCore = true }) => { DeleteProject(projectId, false); }; + + return ( <> { Modify - {isCore && ( -
  • - - - Activities - -
  • - )} {!isCore && (
  • diff --git a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectList.jsx b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectList.jsx index e79750f0..9ec5e822 100644 --- a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectList.jsx +++ b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectList.jsx @@ -1,9 +1,9 @@ import React, { useState } from "react"; -import { MANAGE_PROJECT,PROJECT_STATUS } from "../../../utils/constants"; +import { MANAGE_PROJECT, PROJECT_STATUS } from "../../../utils/constants"; import { useProjects } from "../../../hooks/useProjects"; -import { formatNumber,formatUTCToLocalTime } from "../../../utils/dateUtils"; +import { formatNumber, formatUTCToLocalTime } from "../../../utils/dateUtils"; import ProgressBar from "../../common/ProgressBar"; -import { getProjectStatusColor,getProjectStatusName } from "../../../utils/projectStatus"; +import { getProjectStatusColor, getProjectStatusName } from "../../../utils/projectStatus"; import { useDispatch } from "react-redux"; import { setProjectId } from "../../../slices/localVariablesSlice"; import { useNavigate } from "react-router-dom"; @@ -12,216 +12,169 @@ import { useProjectContext } from "../../../pages/project/ProjectPage"; import usePagination from "../../../hooks/usePagination"; import Pagination from "../../common/Pagination"; -const ServiceProjectList = ({ data, currentPage, totalPages, paginate }) => { - const dispatch = useDispatch(); - const navigate = useNavigate(); - const { setMangeProject } = useProjectContext(); - // const { data, isLoading, isError, error } = useProjects(); +const ServiceProjectList = ({ data, currentPage, totalPages, paginate, isCore = true }) => { + const dispatch = useDispatch(); + const navigate = useNavigate(); + const { setMangeProject, setManageServiceProject } = useProjectContext(); + const handleClose = () => setShowModal(false); - // check Permissions - const canManageProject = useHasUserPermission(MANAGE_PROJECT); + // check Permissions + const canManageProject = useHasUserPermission(MANAGE_PROJECT); - const projectColumns = [ - { - key: "projectName", - label: "Project Name", - className: "text-start py-3", - getValue: (p) => ( -
    { + const projectColumns = [ + { + key: "projectName", + label: "Project Name", + className: "text-start py-3", + getValue: (p) => ( +
    { + dispatch(setProjectId(p.id)); + navigate(`/service-projects/${p.id}`); + }} + > + {p.shortName ? `${p.name} (${p.shortName})` : p.name} +
    + ), + }, + { + key: "client.contactPerson", + label: "Contact Person", + className: "text-start small", + getValue: (p) => p.client?.contactPerson || "N/A", + }, + { + key: "assignedDate", + label: "Assign Date", + className: "text-center small", + getValue: (p) => formatUTCToLocalTime(p.assignedDate), + }, + { + key: "status", + label: "Status", + className: "text-center small", + getValue: (p) => ( + + {p.status?.status} + + ), + }, + ]; + + const handleViewProject = (p) => { + if (isCore) { dispatch(setProjectId(p.id)); navigate(`/projects/details`); - }} - > - {p.shortName ? `${p.name} (${p.shortName})` : p.name} + } else { + navigate(`/service-projects/${p.id}`); + } + }; + + const handleManage = (p) => { + if (isCore) { + setMangeProject({ + isOpen: true, + Project: p.id, + }); + } else { + setManageServiceProject({ + isOpen: true, + project: p.id, + }); + } + }; + + + return ( +
    +
    + + + + {projectColumns.map((col) => ( + + ))} + + + + + {data?.map((project) => ( + + {projectColumns.map((col) => ( + + ))} + + + ))} + +
    + {col.label} + Action
    + {col.getValue + ? col.getValue(project) + : project[col.key] || "N/A"} + + +
    +
    + +
    - ), - }, - { - key: "contactPerson", - label: "Contact Person", - className: "text-start small", - getValue: (p) => `${p?.contactPerson ?? ""}`.trim() || "N/A", - }, - { - key: "startDate", - label: "Start Date", - className: "text-center small", - getValue: (p) => formatUTCToLocalTime(p?.startDate) || "N/A", - }, - { - key: "deadline", - label: "Deadline", - className: "text-center small", - getValue: (p) => formatUTCToLocalTime(p?.endDate) || "N/A", - }, - { - key: "task", - label: "Task", - colSpan: 2, - className: "text-center small", - getValue: (p) => formatNumber(p?.plannedWork) || "0", - }, - { - key: "progress", - label: "Progress", - className: "text-start small", - - getValue: (p) => ( - - ), - }, - { - key: "status", - label: "Status", - className: "text-center small", - isFilter: true, - customRender: (_, selectedStatuses, handleStatusChange) => ( -
    - -
      - {PROJECT_STATUS.map(({ id, label }) => ( -
    • -
      - handleStatusChange(id)} - /> - -
      -
    • - ))} -
    -
    - ), - getValue: (p) => ( - - {getProjectStatusName(p.projectStatusId)} - - ), - }, - ]; - - const handleViewActivities = (project) => { - dispatch(setProjectId(project)); - navigate(`/activities/records?project=${project}`); - }; - - return ( -
    -
    - - - - {projectColumns.map((col) => ( - - ))} - - - - - {data?.map((project) => ( - - {projectColumns.map((col) => ( - - ))} - - - ))} - -
    - {col.label} - Action
    - {col.getValue - ? col.getValue(project) - : project[col.key] || "N/A"} - - -
    -
    - - -
    - ); + ); }; export default ServiceProjectList; diff --git a/src/pages/ServiceProject/ServiceProjectDisplay.jsx b/src/pages/ServiceProject/ServiceProjectDisplay.jsx index 528a0683..01dc2c14 100644 --- a/src/pages/ServiceProject/ServiceProjectDisplay.jsx +++ b/src/pages/ServiceProject/ServiceProjectDisplay.jsx @@ -13,7 +13,7 @@ import { SpinnerLoader } from "../../components/common/Loader"; import ServiceProjectCard from "../../components/ServiceProject/ServiceProjectTeam/ServiceProjectCard"; import ServiceProjectList from "../../components/ServiceProject/ServiceProjectTeam/ServiceProjectList"; -const ServiceProjectDisplay = ({ listView ,selectedStatuses }) => { +const ServiceProjectDisplay = ({ listView, selectedStatuses }) => { const [currentPage, setCurrentPage] = useState(1); const { manageServiceProject, setManageServiceProject } = useProjectContext(); @@ -50,15 +50,20 @@ const ServiceProjectDisplay = ({ listView ,selectedStatuses }) => { return (
    {listView ? ( - //

    List

    - + ) : ( filteredProjects?.map((project) => ( )) - )} +
    Date: Mon, 24 Nov 2025 12:45:51 +0530 Subject: [PATCH 05/64] Adding Message for Card and list view in Services and Infra --- src/components/Project/ProjectCardView.jsx | 7 +- src/components/Project/ProjectListView.jsx | 151 ++++++++++-------- .../ServiceProjectTeam/ServiceProjectList.jsx | 128 ++++++++------- 3 files changed, 162 insertions(+), 124 deletions(-) diff --git a/src/components/Project/ProjectCardView.jsx b/src/components/Project/ProjectCardView.jsx index cb44fd2a..3c439fbc 100644 --- a/src/components/Project/ProjectCardView.jsx +++ b/src/components/Project/ProjectCardView.jsx @@ -8,7 +8,12 @@ const ProjectCardView = ({ data, currentPage, totalPages, paginate }) => { return (
    {data?.length === 0 && ( -

    No projects found.

    +
    +

    No Infra projects found.

    +
    )} {data?.map((project) => ( diff --git a/src/components/Project/ProjectListView.jsx b/src/components/Project/ProjectListView.jsx index 2741801e..acda1d5b 100644 --- a/src/components/Project/ProjectListView.jsx +++ b/src/components/Project/ProjectListView.jsx @@ -143,77 +143,94 @@ const ProjectListView = ({ data, currentPage, totalPages, paginate }) => { - {data?.map((project) => ( - - {projectColumns.map((col) => ( - - {col.getValue - ? col.getValue(project) - : project[col.key] || "N/A"} - - ))} - -
    - - +
    + + + )) + ) : ( + + + No Infra projects available - ))} + )}
    diff --git a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectList.jsx b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectList.jsx index 9ec5e822..afc48bad 100644 --- a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectList.jsx +++ b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectList.jsx @@ -105,66 +105,82 @@ const ServiceProjectList = ({ data, currentPage, totalPages, paginate, isCore = - {data?.map((project) => ( - - {projectColumns.map((col) => ( - - {col.getValue - ? col.getValue(project) - : project[col.key] || "N/A"} - - ))} - -
    - - -
    + {col.getValue ? col.getValue(project) : project[col.key] || "N/A"} + + ))} + + + + + )) + ) : ( + + + No Service projects available - ))} + )} +
    From c669eb90c37df7fc6dbe5023cbe8af84204bc66d Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Mon, 24 Nov 2025 12:53:34 +0530 Subject: [PATCH 06/64] Correction in List view alignment. --- src/components/Project/ProjectListView.jsx | 4 ++-- src/pages/ServiceProject/ServiceProjectDisplay.jsx | 2 +- src/pages/project/ProjectsDisplay.jsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Project/ProjectListView.jsx b/src/components/Project/ProjectListView.jsx index acda1d5b..9d0c6acb 100644 --- a/src/components/Project/ProjectListView.jsx +++ b/src/components/Project/ProjectListView.jsx @@ -126,8 +126,8 @@ const ProjectListView = ({ data, currentPage, totalPages, paginate }) => { return (
    -
    - +
    +
    {projectColumns.map((col) => ( diff --git a/src/pages/ServiceProject/ServiceProjectDisplay.jsx b/src/pages/ServiceProject/ServiceProjectDisplay.jsx index 01dc2c14..b0226eff 100644 --- a/src/pages/ServiceProject/ServiceProjectDisplay.jsx +++ b/src/pages/ServiceProject/ServiceProjectDisplay.jsx @@ -48,7 +48,7 @@ const ServiceProjectDisplay = ({ listView, selectedStatuses }) => { ); return ( -
    +
    {listView ? ( +
    {listView ? ( Date: Mon, 24 Nov 2025 13:04:17 +0530 Subject: [PATCH 07/64] Changes. --- src/pages/ServiceProject/ServiceProjectDisplay.jsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/pages/ServiceProject/ServiceProjectDisplay.jsx b/src/pages/ServiceProject/ServiceProjectDisplay.jsx index b0226eff..50671cfd 100644 --- a/src/pages/ServiceProject/ServiceProjectDisplay.jsx +++ b/src/pages/ServiceProject/ServiceProjectDisplay.jsx @@ -48,21 +48,19 @@ const ServiceProjectDisplay = ({ listView, selectedStatuses }) => {
    ); return ( -
    +
    {listView ? ( - + isCore={false} /> ) : ( filteredProjects?.map((project) => ( )) - )} + )}
    Date: Mon, 24 Nov 2025 15:53:10 +0530 Subject: [PATCH 08/64] Adding search functionality in Projectpage. --- src/hooks/useProjects.js | 7 ++++--- src/hooks/useServiceProject.jsx | 7 ++++--- src/pages/ServiceProject/ServiceProjectDisplay.jsx | 7 +++++-- src/pages/project/ProjectPage.jsx | 5 ++++- src/pages/project/ProjectsDisplay.jsx | 5 +++-- src/repositories/ProjectRepository.jsx | 4 ++-- src/repositories/ServiceProjectRepository.jsx | 4 ++-- 7 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/hooks/useProjects.js b/src/hooks/useProjects.js index 75467399..973d8b94 100644 --- a/src/hooks/useProjects.js +++ b/src/hooks/useProjects.js @@ -20,14 +20,15 @@ export const useCurrentService = () => { // ------------------------------Query------------------- -export const useProjects = (pageSize, pageNumber) => { +export const useProjects = (pageSize, pageNumber,searchString) => { const loggedUser = useSelector((store) => store.globalVariables.loginUser); return useQuery({ - queryKey: ["ProjectsList", pageSize, pageNumber], + queryKey: ["ProjectsList", pageSize, pageNumber,searchString], queryFn: async () => { const response = await ProjectRepository.getProjectList( pageSize, - pageNumber + pageNumber, + searchString, ); return response?.data; }, diff --git a/src/hooks/useServiceProject.jsx b/src/hooks/useServiceProject.jsx index 82bc5713..9319181a 100644 --- a/src/hooks/useServiceProject.jsx +++ b/src/hooks/useServiceProject.jsx @@ -8,13 +8,14 @@ import { ServiceProjectRepository } from "../repositories/ServiceProjectReposito import showToast from "../services/toastService"; //#region Service Project -export const useServiceProjects = (pageSize, pageNumber) => { +export const useServiceProjects = (pageSize, pageNumber, searchString) => { return useQuery({ - queryKey: ["serviceProjects", pageSize, pageNumber], + queryKey: ["serviceProjects", pageSize, pageNumber, searchString], queryFn: async () => { const response = await ServiceProjectRepository.GetServiceProjects( pageSize, - pageNumber + pageNumber, + searchString ); return response.data; }, diff --git a/src/pages/ServiceProject/ServiceProjectDisplay.jsx b/src/pages/ServiceProject/ServiceProjectDisplay.jsx index 50671cfd..71750599 100644 --- a/src/pages/ServiceProject/ServiceProjectDisplay.jsx +++ b/src/pages/ServiceProject/ServiceProjectDisplay.jsx @@ -12,14 +12,17 @@ import ManageServiceProject from "../../components/ServiceProject/ManageServiceP import { SpinnerLoader } from "../../components/common/Loader"; import ServiceProjectCard from "../../components/ServiceProject/ServiceProjectTeam/ServiceProjectCard"; import ServiceProjectList from "../../components/ServiceProject/ServiceProjectTeam/ServiceProjectList"; +import { useDebounce } from "../../utils/appUtils"; -const ServiceProjectDisplay = ({ listView, selectedStatuses }) => { +const ServiceProjectDisplay = ({ listView, selectedStatuses, searchTerm }) => { const [currentPage, setCurrentPage] = useState(1); const { manageServiceProject, setManageServiceProject } = useProjectContext(); + const debouncedSearch = useDebounce(searchTerm, 500); const { data, isLoading, isError, error } = useServiceProjects( ITEMS_PER_PAGE, - currentPage + currentPage, + debouncedSearch ); const paginate = (page) => { if (page >= 1 && page <= (data?.totalPages ?? 1)) { diff --git a/src/pages/project/ProjectPage.jsx b/src/pages/project/ProjectPage.jsx index d9557f48..1689fbb0 100644 --- a/src/pages/project/ProjectPage.jsx +++ b/src/pages/project/ProjectPage.jsx @@ -45,6 +45,8 @@ const ProjectPage = () => { return storedValue === 'true'; }); const HasManageProject = useHasUserPermission(MANAGE_PROJECT); + const [currentPage, setCurrentPage] = useState(1); + const [selectedStatuses, setSelectedStatuses] = useState( PROJECT_STATUS.map((s) => s.id) @@ -198,7 +200,8 @@ const ProjectPage = () => { {coreProjects ? : : }
    diff --git a/src/pages/project/ProjectsDisplay.jsx b/src/pages/project/ProjectsDisplay.jsx index 175f07e5..d01228c8 100644 --- a/src/pages/project/ProjectsDisplay.jsx +++ b/src/pages/project/ProjectsDisplay.jsx @@ -10,6 +10,7 @@ import { useServiceProjects } from "../../hooks/useServiceProject"; import { ITEMS_PER_PAGE, PROJECT_STATUS } from "../../utils/constants"; import usePagination from "../../hooks/usePagination"; import ManageProjectInfo from "../../components/Project/ManageProjectInfo"; +import { useDebounce } from "../../utils/appUtils"; const ProjectsDisplay = ({ listView, @@ -26,8 +27,8 @@ const ProjectsDisplay = ({ } = useProjectContext(); const [projectList, setProjectList] = useState([]); - - const { data, isLoading, isError, error } = useProjects(ITEMS_PER_PAGE, 1); + const debouncedSearch = useDebounce(searchTerm, 500); + const { data, isLoading, isError, error } = useProjects(ITEMS_PER_PAGE, 1, debouncedSearch); const filteredProjects = data?.data?.filter((project) => { diff --git a/src/repositories/ProjectRepository.jsx b/src/repositories/ProjectRepository.jsx index 9894feae..6759b885 100644 --- a/src/repositories/ProjectRepository.jsx +++ b/src/repositories/ProjectRepository.jsx @@ -2,8 +2,8 @@ import { api } from "../utils/axiosClient"; const ProjectRepository = { - getProjectList: (pageSize, pageNumber) => - api.get(`/api/project/list?pageSize=${pageSize}&pageNumber=${pageNumber}`), + getProjectList: (pageSize, pageNumber,searchString) => + api.get(`/api/project/list?pageSize=${pageSize}&pageNumber=${pageNumber}&searchString=${searchString}`), getProjectByprojectId: (projetid) => api.get(`/api/project/details/${projetid}`), diff --git a/src/repositories/ServiceProjectRepository.jsx b/src/repositories/ServiceProjectRepository.jsx index f043bcc0..ff3b9d3b 100644 --- a/src/repositories/ServiceProjectRepository.jsx +++ b/src/repositories/ServiceProjectRepository.jsx @@ -4,9 +4,9 @@ import { api } from "../utils/axiosClient"; export const ServiceProjectRepository = { //#region Service Project CreateServiceProject: (data) => api.post("/api/ServiceProject/create", data), - GetServiceProjects: (pageSize, pageNumber) => + GetServiceProjects: (pageSize, pageNumber,searchString) => api.get( - `/api/ServiceProject/list?pageSize=${pageSize}&pageNumber=${pageNumber}` + `/api/ServiceProject/list?pageSize=${pageSize}&pageNumber=${pageNumber}&searchString=${searchString}` ), GetServiceProject: (id) => api.get(`/api/ServiceProject/details/${id}`), UpdateServiceProject: (id, data) => From bb37fd704492f21b85a510b2b051831cda1c6097 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Mon, 24 Nov 2025 16:37:18 +0530 Subject: [PATCH 09/64] set align for Service Project List card --- .../ServiceProjectTeam/ServiceProjectList.jsx | 363 +++++++++--------- .../ServiceProject/ServiceProjectDisplay.jsx | 10 +- src/pages/project/ProjectPage.jsx | 62 +-- 3 files changed, 232 insertions(+), 203 deletions(-) diff --git a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectList.jsx b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectList.jsx index afc48bad..b86d4939 100644 --- a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectList.jsx +++ b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectList.jsx @@ -3,7 +3,10 @@ import { MANAGE_PROJECT, PROJECT_STATUS } from "../../../utils/constants"; import { useProjects } from "../../../hooks/useProjects"; import { formatNumber, formatUTCToLocalTime } from "../../../utils/dateUtils"; import ProgressBar from "../../common/ProgressBar"; -import { getProjectStatusColor, getProjectStatusName } from "../../../utils/projectStatus"; +import { + getProjectStatusColor, + getProjectStatusName, +} from "../../../utils/projectStatus"; import { useDispatch } from "react-redux"; import { setProjectId } from "../../../slices/localVariablesSlice"; import { useNavigate } from "react-router-dom"; @@ -12,185 +15,195 @@ import { useProjectContext } from "../../../pages/project/ProjectPage"; import usePagination from "../../../hooks/usePagination"; import Pagination from "../../common/Pagination"; -const ServiceProjectList = ({ data, currentPage, totalPages, paginate, isCore = true }) => { - const dispatch = useDispatch(); - const navigate = useNavigate(); - const { setMangeProject, setManageServiceProject } = useProjectContext(); - const handleClose = () => setShowModal(false); +const ServiceProjectList = ({ + data, + currentPage, + totalPages, + paginate, + isCore = true, +}) => { + const dispatch = useDispatch(); + const navigate = useNavigate(); + const { setMangeProject, setManageServiceProject } = useProjectContext(); + const handleClose = () => setShowModal(false); - // check Permissions - const canManageProject = useHasUserPermission(MANAGE_PROJECT); + // check Permissions + const canManageProject = useHasUserPermission(MANAGE_PROJECT); - const projectColumns = [ - { - key: "projectName", - label: "Project Name", - className: "text-start py-3", - getValue: (p) => ( -
    { - dispatch(setProjectId(p.id)); - navigate(`/service-projects/${p.id}`); - }} - > - {p.shortName ? `${p.name} (${p.shortName})` : p.name} -
    - ), - }, - { - key: "client.contactPerson", - label: "Contact Person", - className: "text-start small", - getValue: (p) => p.client?.contactPerson || "N/A", - }, - { - key: "assignedDate", - label: "Assign Date", - className: "text-center small", - getValue: (p) => formatUTCToLocalTime(p.assignedDate), - }, - { - key: "status", - label: "Status", - className: "text-center small", - getValue: (p) => ( - - {p.status?.status} - - ), - }, - ]; - - const handleViewProject = (p) => { - if (isCore) { + const projectColumns = [ + { + key: "projectName", + label: "Project Name", + className: "text-start py-3", + getValue: (p) => ( +
    { dispatch(setProjectId(p.id)); - navigate(`/projects/details`); - } else { navigate(`/service-projects/${p.id}`); - } - }; - - const handleManage = (p) => { - if (isCore) { - setMangeProject({ - isOpen: true, - Project: p.id, - }); - } else { - setManageServiceProject({ - isOpen: true, - project: p.id, - }); - } - }; - - - return ( -
    -
    -
    - - - {projectColumns.map((col) => ( - - ))} - - - - - {data?.length > 0 ? ( - data.map((project) => ( - - {projectColumns.map((col) => ( - - ))} - - - )) - ) : ( - - - - )} - - -
    - {col.label} - Action
    - {col.getValue ? col.getValue(project) : project[col.key] || "N/A"} - - -
    - No Service projects available -
    -
    - - + }} + > + {p.shortName ? `${p.name} (${p.shortName})` : p.name}
    - ); + ), + }, + { + key: "client.contactPerson", + label: "Contact Person", + className: "text-start small", + getValue: (p) => p.client?.contactPerson || "N/A", + }, + { + key: "assignedDate", + label: "Assign Date", + className: "text-center small", + getValue: (p) => formatUTCToLocalTime(p.assignedDate), + }, + { + key: "status", + label: "Status", + className: "text-center small", + getValue: (p) => ( + + {p.status?.status} + + ), + }, + ]; + + const handleViewProject = (p) => { + if (isCore) { + dispatch(setProjectId(p.id)); + navigate(`/projects/details`); + } else { + navigate(`/service-projects/${p.id}`); + } + }; + + const handleManage = (p) => { + if (isCore) { + setMangeProject({ + isOpen: true, + Project: p.id, + }); + } else { + setManageServiceProject({ + isOpen: true, + project: p.id, + }); + } + }; + + return ( +
    +
    +
    + + + + {projectColumns.map((col) => ( + + ))} + + + + + {data?.length > 0 ? ( + data.map((project) => ( + + {projectColumns.map((col) => ( + + ))} + + + )) + ) : ( + + + + )} + +
    + {col.label} + Action
    + {col.getValue + ? col.getValue(project) + : project[col.key] || "N/A"} + + +
    + No Service projects available +
    +
    + + +
    +
    + ); }; export default ServiceProjectList; diff --git a/src/pages/ServiceProject/ServiceProjectDisplay.jsx b/src/pages/ServiceProject/ServiceProjectDisplay.jsx index 71750599..6bb9299d 100644 --- a/src/pages/ServiceProject/ServiceProjectDisplay.jsx +++ b/src/pages/ServiceProject/ServiceProjectDisplay.jsx @@ -51,19 +51,20 @@ const ServiceProjectDisplay = ({ listView, selectedStatuses, searchTerm }) => {
    ); return ( -
    +
    +
    {listView ? ( - ) : ( + ) : filteredProjects?.length > 0 ? ( filteredProjects?.map((project) => ( )) - - )} + + ):(

    No Service projects available

    )}
    { )}
    +
    ); }; diff --git a/src/pages/project/ProjectPage.jsx b/src/pages/project/ProjectPage.jsx index 1689fbb0..f72b995c 100644 --- a/src/pages/project/ProjectPage.jsx +++ b/src/pages/project/ProjectPage.jsx @@ -41,13 +41,12 @@ const ProjectPage = () => { const [listView, setListView] = useState(false); const [searchTerm, setSearchTerm] = useState(""); const [coreProjects, setCoreProjects] = useState(() => { - const storedValue = sessionStorage.getItem('whichProjectDisplay'); - return storedValue === 'true'; + const storedValue = sessionStorage.getItem("whichProjectDisplay"); + return storedValue === "true"; }); const HasManageProject = useHasUserPermission(MANAGE_PROJECT); const [currentPage, setCurrentPage] = useState(1); - const [selectedStatuses, setSelectedStatuses] = useState( PROJECT_STATUS.map((s) => s.id) ); @@ -66,13 +65,11 @@ const ProjectPage = () => { manageServiceProject, }; - const handleToggleProject = (value) => { setCoreProjects(value); sessionStorage.setItem("whichProjectDisplay", String(value)); }; - return (
    @@ -92,8 +89,9 @@ const ProjectPage = () => { {/* Service Project Button */} -
    - {/* RIGHT SIDE — SEARCH + CARD/LIST + DROPDOWN */}
    - {/* Search */}
    {
    - {coreProjects ? : } + {coreProjects ? ( + + ) : ( + + )}
    ); From f13005a031ead8d0bac51e4dba2226746c1d7b9e Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Mon, 24 Nov 2025 19:48:03 +0530 Subject: [PATCH 10/64] fixed side issue collapsing --- index.html | 4 ++-- public/assets/css/default.css | 2 +- public/assets/js/main.js | 36 +++++++++++++++++++++++++++++++ src/components/Layout/Sidebar.jsx | 22 +++++++++++++------ 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/index.html b/index.html index 24a9ef36..525634c5 100644 --- a/index.html +++ b/index.html @@ -1,7 +1,7 @@ - - + diff --git a/public/assets/css/default.css b/public/assets/css/default.css index 8503dc87..6e2cafb7 100644 --- a/public/assets/css/default.css +++ b/public/assets/css/default.css @@ -31,7 +31,7 @@ } .app-brand-text { - font-size: 1.75rem; + font-size: 1rem; letter-spacing: -0.5px; /* text-transform: lowercase; */ } diff --git a/public/assets/js/main.js b/public/assets/js/main.js index cc356fa7..272f0369 100644 --- a/public/assets/js/main.js +++ b/public/assets/js/main.js @@ -148,5 +148,41 @@ function Main () { wheelPropagation: false }); } + }; +document.addEventListener("DOMContentLoaded", function () { + const html = document.documentElement; + + /****************************** + * SIDEBAR HOVER BEHAVIOR + ******************************/ + document.addEventListener("mouseover", function (e) { + const isInsideSidebar = e.target.closest("#layout-menu"); + + if (isInsideSidebar && html.classList.contains("layout-menu-collapsed")) { + html.classList.add("layout-menu-hover"); + } + }); + + document.addEventListener("mouseout", function (e) { + const leftSidebar = !e.relatedTarget || !e.relatedTarget.closest("#layout-menu"); + + if (leftSidebar) { + html.classList.remove("layout-menu-hover"); + } + }); + + /****************************** + * TOGGLE MENU BUTTON OVERRIDE + ******************************/ + document.body.addEventListener("click", function (e) { + const btn = e.target.closest(".layout-menu-toggle"); + if (!btn) return; + + e.preventDefault(); + + html.classList.toggle("layout-menu-collapsed"); + html.classList.remove("layout-menu-hover"); + }); +}); diff --git a/src/components/Layout/Sidebar.jsx b/src/components/Layout/Sidebar.jsx index 1f4b63b8..4d51ddf2 100644 --- a/src/components/Layout/Sidebar.jsx +++ b/src/components/Layout/Sidebar.jsx @@ -27,18 +27,26 @@ const Sidebar = () => { - - + + OnFieldWork logo + + + + OnField + Work + .com - OnField - Work - .com - +
    From 92d17167b172eae2376573412577d3396c2121c0 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Tue, 25 Nov 2025 10:11:43 +0530 Subject: [PATCH 11/64] added zoom in-out --- src/components/Expenses/PreviewDocument.jsx | 98 +++++++++++++-------- 1 file changed, 62 insertions(+), 36 deletions(-) diff --git a/src/components/Expenses/PreviewDocument.jsx b/src/components/Expenses/PreviewDocument.jsx index 7ea0c3ec..2f0e796b 100644 --- a/src/components/Expenses/PreviewDocument.jsx +++ b/src/components/Expenses/PreviewDocument.jsx @@ -1,54 +1,80 @@ import { useState } from "react"; + const PreviewDocument = ({ imageUrl }) => { const [loading, setLoading] = useState(true); const [rotation, setRotation] = useState(0); + const [scale, setScale] = useState(1); + + const zoomIn = () => setScale((prev) => Math.min(prev + 0.2, 3)); + const zoomOut = () => setScale((prev) => Math.max(prev - 0.2, 0.4)); + const resetAll = () => { + setRotation(0); + setScale(1); + }; return ( - <> -
    + <> +
    setRotation((prev) => prev + 90)} > -
    -
    - - {loading && ( -
    Loading...
    - )} -
    - Full View setLoading(false)} - /> + + +
    -
    - +
    + {loading && ( +
    + Loading... +
    + )} + +
    + Full View setLoading(false)} + /> +
    + +
    + +
    -
    - + ); }; + + export default PreviewDocument; From 822ff1a7e4c25f5efa40fc95e1bf133f39b2ee80 Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Tue, 25 Nov 2025 11:20:19 +0530 Subject: [PATCH 12/64] UI Alignment in Service Card view. --- .../ServiceProjectTeam/ServiceProjectCard.jsx | 4 +- src/pages/project/ProjectPage.jsx | 116 ++++++++++++------ 2 files changed, 78 insertions(+), 42 deletions(-) diff --git a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectCard.jsx b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectCard.jsx index 21f77a4a..2bba2c36 100644 --- a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectCard.jsx +++ b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectCard.jsx @@ -57,7 +57,7 @@ const ServiceProjectCard = ({ project, isCore = true }) => { DeleteProject(projectId, false); }; - + return ( <> @@ -89,7 +89,7 @@ const ServiceProjectCard = ({ project, isCore = true }) => { > {project?.shortName ? project?.shortName : project?.name} -
    +
    {project?.shortName ? project?.name : ""}
    diff --git a/src/pages/project/ProjectPage.jsx b/src/pages/project/ProjectPage.jsx index f72b995c..234e3838 100644 --- a/src/pages/project/ProjectPage.jsx +++ b/src/pages/project/ProjectPage.jsx @@ -1,4 +1,4 @@ -import React, { createContext, useContext, useEffect, useState } from "react"; +import React, { createContext, useContext, useEffect, useRef, useState } from "react"; import Breadcrumb from "../../components/common/Breadcrumb"; import { ITEMS_PER_PAGE, @@ -36,7 +36,7 @@ const ProjectPage = () => { isOpen: false, project: null, }); - + const dropdownRef = useRef(null); const [projectList, setProjectList] = useState([]); const [listView, setListView] = useState(false); const [searchTerm, setSearchTerm] = useState(""); @@ -46,6 +46,7 @@ const ProjectPage = () => { }); const HasManageProject = useHasUserPermission(MANAGE_PROJECT); const [currentPage, setCurrentPage] = useState(1); + const [open, setOpen] = useState(false); const [selectedStatuses, setSelectedStatuses] = useState( PROJECT_STATUS.map((s) => s.id) @@ -70,6 +71,16 @@ const ProjectPage = () => { sessionStorage.setItem("whichProjectDisplay", String(value)); }; + useEffect(() => { + const handleClickOutside = (event) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { + setOpen(false); + } + }; + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, []); + return (
    @@ -89,9 +100,8 @@ const ProjectPage = () => { {/* Service Project Button */}
    {/* Dropdown Filter */} -
    -
    + -
      - {PROJECT_STATUS.map(({ id, label }) => ( -
    • -
      - handleStatusChange(id)} - /> - -
      -
    • - ))} -
    + {selectedStatuses.length !== PROJECT_STATUS.length && ( + + {PROJECT_STATUS.length - selectedStatuses.length} + + )} +
    + + {open && ( +
      e.stopPropagation()} // IMPORTANT + > + {PROJECT_STATUS.map(({ id, label }) => ( +
    • +
      + e.stopPropagation()} // IMPORTANT + onChange={() => handleStatusChange(id)} + /> + +
      +
    • + ))} +
    + )}
    + + + {HasManageProject && ( +
    + {index < newTenantConfig.length - 1 && ( +
    + )} + + ); + })} +
    +
    + +
    + {newTenantConfig[activeTab].component} +
    +
    +
    +
    + ); +}; + +export default ManagePurchase; diff --git a/src/components/purchase/PurchasePartyDetails.jsx b/src/components/purchase/PurchasePartyDetails.jsx new file mode 100644 index 00000000..d7b63533 --- /dev/null +++ b/src/components/purchase/PurchasePartyDetails.jsx @@ -0,0 +1,31 @@ +import React from "react"; +import { useAppFormContext } from "../../hooks/appHooks/useAppForm"; + +const PurchasePartyDetails = ({ onNext }) => { + const { + register, + control, + trigger, + formState: { errors }, + } = useAppFormContext(); + + const handleNext = async () => { + const valid = await trigger([ + "title", + "projectId", + "organizationId", + "supplier", + "billingAddress", + "shippingAddress", + "purchaseOrderNumber", + "purchaseOrderDate", + "porformaInvoiceNo", + ]); + if (valid) { + onNext(); + } + }; + return
    ; +}; + +export default PurchasePartyDetails; diff --git a/src/components/purchase/PurchasePaymentDetails.jsx b/src/components/purchase/PurchasePaymentDetails.jsx new file mode 100644 index 00000000..fde938d4 --- /dev/null +++ b/src/components/purchase/PurchasePaymentDetails.jsx @@ -0,0 +1,11 @@ +import React from 'react' + +const PurchasePaymentDetails = () => { + return ( +
    + +
    + ) +} + +export default PurchasePaymentDetails diff --git a/src/components/purchase/PurchaseSchema.jsx b/src/components/purchase/PurchaseSchema.jsx new file mode 100644 index 00000000..04a5dc02 --- /dev/null +++ b/src/components/purchase/PurchaseSchema.jsx @@ -0,0 +1,127 @@ +import { z } from "zod"; + +export const AttachmentSchema = (allowedContentType, maxSizeAllowedInMB) => { + const allowedTypes = normalizeAllowedContentTypes(allowedContentType); + + return z.object({ + fileName: z.string().min(1, { message: "File name is required" }), + base64Data: z.string().min(1, { message: "File data is required" }), + contentType: z + .string() + .min(1, { message: "MIME type is required" }) + .refine( + (val) => (allowedTypes.length ? allowedTypes.includes(val) : true), + { + message: `File type must be one of: ${allowedTypes.join(", ")}`, + } + ), + fileSize: z + .number() + .int() + .nonnegative("fileSize must be ≥ 0") + .max( + (maxSizeAllowedInMB ?? 25) * 1024 * 1024, + `fileSize must be ≤ ${maxSizeAllowedInMB ?? 25}MB` + ), + description: z.string().optional().default(""), + isActive: z.boolean(), + }); +}; + +export const PurchaseSchema = z.object({ + title: z.string().min(1, { message: "Title is required" }), + projectId: z.string().min(1, { message: "Project is required" }), + organizationId: z.string().min(1, { message: "Organization is required" }), + billingAddress: z.string().min(1, { message: "Address is required" }), + shippingAddress: z.string().min(1, { message: "Address is required" }), + purchaseOrderNumber: z.string().nullable(), + purchaseOrderDate: z.string().nullable(), + supplier: z.string().min(1, { message: "Supplier is required" }), + porformaInvoiceNo: z.string().nullable(), + // Supplier Details + + invoiceNo: z.string().min(1, { message: "Invoice No is required" }), + invoiceDate: z.string().min(1, { message: "Date is required" }), + ewayBillNo: z.string().min(1, { message: "E-Way Bill No is required" }), + ewayBillDate: z.string().min(1, { message: "E-Way Bill Date is required" }), + irnNo: z.string().min(1, { message: "IRN is required" }), + ackDate: z.string().min(1, { message: "Date is required" }), + ackNo: z.string().min(1, { message: "acknowledgement No is required" }), + + // Payment Detail + baseAmount: z.string().min(1, { message: "Base amount is required" }), + taxAmount: z.string().min(1, { message: "Tax amount is required" }), + totalAmount: z.string().min(1, { message: "Total amount is required" }), + paymentDueDate: z.string().nullable(), + TransportCharges: z.string().nullable(), + description: z.string().min(1, { message: "description is required" }), +}); + +// deliveryChallanNo: z +// .string() +// .min(1, { message: "Delivery Challan No is required" }), +// deliveryDate: z.string().min(1, { message: "Delevery Date is required" }), +// shippingAddress: z.string().min(1, { message: "Delevery Date is required" }), + +export const defaultPurchaseValue = { + title: null, + projectId: null, + organizationId: null, + billingAddress: null, + shippingAddress: null, + purchaseOrderNumber: null, + purchaseOrderDate: null, + supplier: null, + porformaInvoiceNo: null, + + invoiceNo: null, + invoiceDate: null, + ewayBillNo: null, + ewayBillDate: null, + irnNo: null, + ackDate: null, + ackNo: null, + + baseAmount: null, + taxAmount: null, + totalAmount: null, + paymentDueDate: null, + TransportCharges: null, + description: null, +}; + +export const getStepFields = (stepIndex) => { + const stepFieldMap = { + 0: [ + "title", + "projectId", + "organizationId", + "supplier", + "billingAddress", + "shippingAddress", + "purchaseOrderNumber", + "purchaseOrderDate", + "porformaInvoiceNo", + ], + 1: [ + "invoiceNo", + "invoiceDate", + "ewayBillNo", + "ewayBillDate", + "irnNo", + "ackNo", + "taxId", + "ackDate", + ], + 2: [ + "baseAmount", + "taxAmount", + "totalAmount", + "TransportCharges", + "paymentDueDate", + "description", + ], + }; + + return stepFieldMap[stepIndex] || []; +}; diff --git a/src/components/purchase/PurchaseTansportDetails.jsx b/src/components/purchase/PurchaseTansportDetails.jsx new file mode 100644 index 00000000..483845e6 --- /dev/null +++ b/src/components/purchase/PurchaseTansportDetails.jsx @@ -0,0 +1,11 @@ +import React from 'react' + +const PurchaseTansportDetails = () => { + return ( +
    + +
    + ) +} + +export default PurchaseTansportDetails diff --git a/src/hooks/appHooks/useAppForm.js b/src/hooks/appHooks/useAppForm.js index ea751205..f4ccbac0 100644 --- a/src/hooks/appHooks/useAppForm.js +++ b/src/hooks/appHooks/useAppForm.js @@ -1,6 +1,6 @@ -import { useForm, Controller,FormProvider } from "react-hook-form"; +import { useForm, Controller, FormProvider, useFormContext } from "react-hook-form"; export const useAppForm = (config) => useForm(config); export const AppFormProvider = FormProvider; export const AppFormController = Controller; - +export const useAppFormContext = useFormContext; diff --git a/src/pages/purchase/PurchasePage.jsx b/src/pages/purchase/PurchasePage.jsx new file mode 100644 index 00000000..706c4a9b --- /dev/null +++ b/src/pages/purchase/PurchasePage.jsx @@ -0,0 +1,57 @@ +import React, { createContext, useContext, useState } from "react"; +import Breadcrumb from "../../components/common/Breadcrumb"; +import showToast from "../../services/toastService"; +import GlobalModel from "../../components/common/GlobalModel"; +import ManagePurchase from "../../components/purchase/ManagePurchase"; + +export const PurchaseContext = createContext(); +export const usePurchaseContext = () => { + let context = useContext(PurchaseContext); + + if (!context) { + showToast("Please use Innne cntext", "warning"); + window.location = "/dashboard"; + } +}; +const PurchasePage = () => { + const [addePurchase, setAddedPurchase] = useState(false); + + const contextValue = {}; + return ( + +
    + +
    +
    + + + +
    +
    + + {/* MOdals */} + + setAddedPurchase(false)} + > + () => setAddedPurchase(false)} /> + +
    +
    + ); +}; + +export default PurchasePage; diff --git a/src/router/AppRoutes.jsx b/src/router/AppRoutes.jsx index dc556a6e..bee02723 100644 --- a/src/router/AppRoutes.jsx +++ b/src/router/AppRoutes.jsx @@ -62,6 +62,7 @@ import AdvancePaymentPage from "../pages/AdvancePayment/AdvancePaymentPage"; import ServiceProjectDetail from "../pages/ServiceProject/ServiceProjectDetail"; import ManageJob from "../components/ServiceProject/ServiceProjectJob/ManageJob"; import AdvancePaymentPage1 from "../pages/AdvancePayment/AdvancePaymentPage1"; +import PurchasePage from "../pages/purchase/PurchasePage"; const router = createBrowserRouter( [ { @@ -113,6 +114,8 @@ const router = createBrowserRouter( { path: "/activities/task", element: }, { path: "/activities/reports", element: }, { path: "/gallary", element: }, + + // Finance { path: "/expenses/:status?/:project?", element: }, { path: "/expenses", element: }, { path: "/payment-request", element: }, @@ -120,6 +123,10 @@ const router = createBrowserRouter( { path: "/advance-payment", element: }, { path: "/advance-payment/:employeeId", element: }, { path: "/collection", element: }, + + // Purchases and Inventory + { path: "/purchase-invoice", element: }, + // Administration { path: "/masters", element: }, { path: "/tenants", element: }, { path: "/tenants/new-tenant", element: }, From 82c1dc4b8e9fafb882114e730c156629e1cedc27 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Wed, 26 Nov 2025 12:29:40 +0530 Subject: [PATCH 15/64] created purchase form --- public/assets/css/core-extend.css | 12 + .../common/Forms/SelectFieldServerSide.jsx | 12 +- src/components/common/InputSuggestion.jsx | 1 - src/components/purchase/ManagePurchase.jsx | 138 ++++++---- .../purchase/PurchasePartyDetails.jsx | 241 ++++++++++++++++-- .../purchase/PurchasePaymentDetails.jsx | 148 ++++++++++- src/components/purchase/PurchaseSchema.jsx | 105 ++++---- .../purchase/PurchaseTansportDetails.jsx | 11 - .../purchase/PurchaseTransportDetails.jsx | 131 ++++++++++ 9 files changed, 659 insertions(+), 140 deletions(-) delete mode 100644 src/components/purchase/PurchaseTansportDetails.jsx create mode 100644 src/components/purchase/PurchaseTransportDetails.jsx diff --git a/public/assets/css/core-extend.css b/public/assets/css/core-extend.css index b05f71c1..127de638 100644 --- a/public/assets/css/core-extend.css +++ b/public/assets/css/core-extend.css @@ -11,6 +11,18 @@ top: var(--sticky-top, 0px) !important; z-index: 1025; } +.form-control-md { + min-height: calc(1.6em + 0.65rem + calc(var(--bs-border-width) * 2)); + padding: 0.18rem 0.60rem; + font-size: 0.875rem; /* ~14px */ + border-radius: var(--bs-border-radius); +} + +.form-control-md::file-selector-button { + padding: 0.32rem 0.75rem; + margin: -0.32rem -0.75rem; + margin-inline-end: 0.75rem; +} /* ===========================% Background_Colors %========================================================== */ diff --git a/src/components/common/Forms/SelectFieldServerSide.jsx b/src/components/common/Forms/SelectFieldServerSide.jsx index 47b1d5ad..d60be88b 100644 --- a/src/components/common/Forms/SelectFieldServerSide.jsx +++ b/src/components/common/Forms/SelectFieldServerSide.jsx @@ -200,7 +200,9 @@ export const SelectProjectField = ({ isFullObject = false, isMultiple = false, isAllProject = false, - disabled + disabled, + className, + errors }) => { const [searchText, setSearchText] = useState(""); const debounce = useDebounce(searchText, 300); @@ -302,6 +304,10 @@ export const SelectProjectField = ({ {displayText} + + {errors?.projectId && ( +
    {errors.projectId.message}
    + )} {/* DROPDOWN */} {open && ( @@ -377,6 +383,7 @@ export const SelectFieldSearch = ({ isMultiple = false, hookParams, useFetchHook, + errors=null, }) => { const [searchText, setSearchText] = useState(""); const debounce = useDebounce(searchText, 300); @@ -512,6 +519,9 @@ export const SelectFieldSearch = ({ {displayText} + {errors && ( +
    {errors.message}
    + )} {/* DROPDOWN */} {open && ( diff --git a/src/components/common/InputSuggestion.jsx b/src/components/common/InputSuggestion.jsx index 0a79fa35..b586abe1 100644 --- a/src/components/common/InputSuggestion.jsx +++ b/src/components/common/InputSuggestion.jsx @@ -50,7 +50,6 @@ const InputSuggestions = ({ {filteredList.map((org) => (
  • { const [activeTab, setActiveTab] = useState(0); const [completedTabs, setCompletedTabs] = useState([]); - const newTenantConfig = [ + const stepsConfig = [ { name: "Contact Info", icon: "bx bx-user bx-md", @@ -24,29 +25,29 @@ const ManagePurchase = () => { name: "Organization", icon: "bx bx-buildings bx-md", subtitle: "Organization Details", - component:
    Invoice & Transport Details
    , + component: , }, { - name: "SubScription", + name: "Subscription", icon: "bx bx-star bx-md", - component:
    Payment & Financials
    , + subtitle: "Payment & Financials", + component: , }, ]; + const purchaseOrder = useAppForm({ resolver: zodResolver(PurchaseSchema), - defaultJobValue: defaultPurchaseValue, + defaultValues: defaultPurchaseValue, + mode: "onChange", }); - const getCurrentTrigger = () => - activeTab === 2 ? subscriptionForm.trigger : tenantForm.trigger; const handleNext = async () => { const currentStepFields = getStepFields(activeTab); - const trigger = getCurrentTrigger(); - const valid = await trigger(currentStepFields); + const valid = await purchaseOrder.trigger(currentStepFields); if (valid) { setCompletedTabs((prev) => [...new Set([...prev, activeTab])]); - setActiveTab((prev) => Math.min(prev + 1, newTenantConfig.length - 1)); + setActiveTab((prev) => Math.min(prev + 1, stepsConfig.length - 1)); } }; @@ -54,59 +55,84 @@ const ManagePurchase = () => { setActiveTab((prev) => Math.max(prev - 1, 0)); }; - const onsubmit = (formData) => {}; + const onSubmit = (formData) => { + console.log("PURCHASE DATA:", formData); + }; + return (
    - {/* New Parchase */} -
    - {newTenantConfig - .filter((step) => step.name.toLowerCase() !== "congratulation") - .map((step, index) => { - const isActive = activeTab === index; - const isCompleted = completedTabs.includes(index); + {/* Header */} +
    + {stepsConfig.map((step, index) => { + const isActive = activeTab === index; + const isCompleted = completedTabs.includes(index); - return ( - -
    - -
    - {index < newTenantConfig.length - 1 && ( -
    - )} -
    - ); - })} + + +
    + + {index < stepsConfig.length - 1 && ( +
    + )} + + ); + })}
    + + {/* Content */}
    - -
    - {newTenantConfig[activeTab].component} + + + {stepsConfig[activeTab].component} + +
    + + + {activeTab < stepsConfig.length - 1 ? ( + + ) : ( + + )} +
    diff --git a/src/components/purchase/PurchasePartyDetails.jsx b/src/components/purchase/PurchasePartyDetails.jsx index d7b63533..4870d38a 100644 --- a/src/components/purchase/PurchasePartyDetails.jsx +++ b/src/components/purchase/PurchasePartyDetails.jsx @@ -1,31 +1,232 @@ import React from "react"; -import { useAppFormContext } from "../../hooks/appHooks/useAppForm"; +import { AppFormController, useAppFormContext } from "../../hooks/appHooks/useAppForm"; +import Label from "../common/Label"; +import DatePicker from "../common/DatePicker"; +import { + SelectFieldSearch, + SelectProjectField, +} from "../common/Forms/SelectFieldServerSide"; +import { useOrganization, useOrganizationsList } from "../../hooks/useOrganization"; +import { ITEMS_PER_PAGE } from "../../utils/constants"; -const PurchasePartyDetails = ({ onNext }) => { +const PurchasePartyDetails = () => { const { register, control, - trigger, + setValue, + watch, formState: { errors }, } = useAppFormContext(); - const handleNext = async () => { - const valid = await trigger([ - "title", - "projectId", - "organizationId", - "supplier", - "billingAddress", - "shippingAddress", - "purchaseOrderNumber", - "purchaseOrderDate", - "porformaInvoiceNo", - ]); - if (valid) { - onNext(); - } - }; - return
    ; + + return ( +
    + {/* Title */} +
    + + + + + {errors?.title && ( +
    {errors.title.message}
    + )} +
    + + {/* Project ID */} +
    + + setValue("projectId", val, { + shouldDirty: true, + shouldValidate: true, + }) + } + errors={errors} + /> +
    + + {/* Organization */} +
    + + {/* */} + ( + + )} + /> + +
    + + {/* Supplier */} +
    + + + + + {errors?.supplierId && ( +
    {errors.supplierId.message}
    + )} +
    + + {/* Billing Address */} +
    + + + @@ -536,7 +503,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
    -
    +
    */} {/* {/* Title and Advance Payment */} -
    +
  • ))} - + )} {error && {error}} From 54ca80743651c6d3c6d02f85f98e9c3b5e431bb2 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Wed, 26 Nov 2025 17:52:22 +0530 Subject: [PATCH 27/64] removed double class name --- src/components/common/InputSuggestion.jsx | 1 - src/components/purchase/PurchaseList.jsx | 11 +++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 src/components/purchase/PurchaseList.jsx diff --git a/src/components/common/InputSuggestion.jsx b/src/components/common/InputSuggestion.jsx index 0a79fa35..b586abe1 100644 --- a/src/components/common/InputSuggestion.jsx +++ b/src/components/common/InputSuggestion.jsx @@ -50,7 +50,6 @@ const InputSuggestions = ({ {filteredList.map((org) => (
  • { + return ( +
    + +
    + ) +} + +export default PurchaseList From 4dbfe2bd483f5501c40b74a86dda3315467f7017 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Wed, 26 Nov 2025 17:53:21 +0530 Subject: [PATCH 28/64] Merge branch 'Project_Branch_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Project_Branch_Management From 80717f0458a3774f92a1379d7126bb65c085fd89 Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Wed, 26 Nov 2025 18:02:26 +0530 Subject: [PATCH 29/64] Showing message on the center of the page in Branches. --- .../ServiceProjectBranch/ServiceBranch.jsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/ServiceProject/ServiceProjectBranch/ServiceBranch.jsx b/src/components/ServiceProject/ServiceProjectBranch/ServiceBranch.jsx index c1d17365..63bdb09c 100644 --- a/src/components/ServiceProject/ServiceProjectBranch/ServiceBranch.jsx +++ b/src/components/ServiceProject/ServiceProjectBranch/ServiceBranch.jsx @@ -237,14 +237,17 @@ const ServiceBranch = () => { !isError && (!data?.data || data.data.length === 0) && ( - - No Branch Found + +
    + No Branch Found +
    )} + From a1a8dd44474225a7815ddabbebc3b70a7b0b1387 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Wed, 26 Nov 2025 18:37:05 +0530 Subject: [PATCH 30/64] added zoom in and size bar collaspeing --- src/components/Expenses/Filelist.jsx | 2 +- src/components/Expenses/PreviewDocument.jsx | 197 ++++++++++++++------ src/components/Expenses/ViewExpense.jsx | 2 +- src/pages/Expense/ExpensePage.jsx | 8 +- 4 files changed, 149 insertions(+), 60 deletions(-) diff --git a/src/components/Expenses/Filelist.jsx b/src/components/Expenses/Filelist.jsx index 35a4a986..e9a09b2b 100644 --- a/src/components/Expenses/Filelist.jsx +++ b/src/components/Expenses/Filelist.jsx @@ -72,7 +72,7 @@ export const FilelistView = ({ files, viewFile }) => { e.preventDefault(); viewFile({ IsOpen: true, - Image: file.preSignedUrl, + Image: files, }); }} > diff --git a/src/components/Expenses/PreviewDocument.jsx b/src/components/Expenses/PreviewDocument.jsx index 2f0e796b..142ee65c 100644 --- a/src/components/Expenses/PreviewDocument.jsx +++ b/src/components/Expenses/PreviewDocument.jsx @@ -1,80 +1,169 @@ -import { useState } from "react"; +import { useState, useRef, useEffect } from "react"; +const PreviewDocument = ({ files = [] }) => { + const images = Array.isArray(files) ? files : [files]; -const PreviewDocument = ({ imageUrl }) => { + const [index, setIndex] = useState(0); const [loading, setLoading] = useState(true); const [rotation, setRotation] = useState(0); const [scale, setScale] = useState(1); + const [position, setPosition] = useState({ x: 0, y: 0 }); + const [dragging, setDragging] = useState(false); + + const startPos = useRef({ x: 0, y: 0 }); + + const MIN_ZOOM = 0.4; + const MAX_ZOOM = 3; + + const currentImage = images[index]; + + // Reset on image change + useEffect(() => { + setRotation(0); + setScale(1); + setPosition({ x: 0, y: 0 }); + setLoading(true); + }, [index]); + + const zoomIn = () => setScale((prev) => Math.min(prev + 0.2, MAX_ZOOM)); + const zoomOut = () => setScale((prev) => Math.max(prev - 0.2, MIN_ZOOM)); - const zoomIn = () => setScale((prev) => Math.min(prev + 0.2, 3)); - const zoomOut = () => setScale((prev) => Math.max(prev - 0.2, 0.4)); const resetAll = () => { setRotation(0); setScale(1); + setPosition({ x: 0, y: 0 }); }; + const nextImage = () => { + if (index < images.length - 1) setIndex((i) => i + 1); + }; + + const prevImage = () => { + if (index > 0) setIndex((i) => i - 1); + }; + + const handleWheel = (e) => { + e.preventDefault(); + + if (e.ctrlKey) { + const delta = e.deltaY > 0 ? -0.1 : 0.1; + setScale((prev) => { + let next = prev + delta; + if (next < MIN_ZOOM) next = MIN_ZOOM; + if (next > MAX_ZOOM) next = MAX_ZOOM; + return next; + }); + } else { + if (e.deltaY > 0) nextImage(); + else prevImage(); + } + }; + + const handleMouseDown = (e) => { + setDragging(true); + startPos.current = { + x: e.clientX - position.x, + y: e.clientY - position.y, + }; + }; + + const handleMouseMove = (e) => { + if (!dragging) return; + + setPosition({ + x: e.clientX - startPos.current.x, + y: e.clientY - startPos.current.y, + }); + }; + + const handleMouseUp = () => setDragging(false); + + const handleDoubleClick = () => resetAll(); + return ( <> -
    - setRotation((prev) => prev + 90)} - > - - - - + {/* Top Controls */} +
    + {/* Left */} +
    + setRotation((prev) => prev + 90)} + title="Rotate" + /> + + + +
    - {loading && ( -
    - Loading... -
    - )} + {loading &&
    Loading...
    } -
    - Full View setLoading(false)} - /> + Preview setLoading(false)} + /> +
    + +
    +
    + Scroll = change image | Double click = reset
    - -
    - +
    + + + {index + 1} / {images.length} + +
    ); }; - - export default PreviewDocument; diff --git a/src/components/Expenses/ViewExpense.jsx b/src/components/Expenses/ViewExpense.jsx index 8c032fc0..93304240 100644 --- a/src/components/Expenses/ViewExpense.jsx +++ b/src/components/Expenses/ViewExpense.jsx @@ -393,7 +393,7 @@ const tdsPercentage = Number(watch("tdsPercentage")) || 0; if (isImage) { setDocumentView({ IsOpen: true, - Image: doc.preSignedUrl, + Images: data?.documents, }); } }} diff --git a/src/pages/Expense/ExpensePage.jsx b/src/pages/Expense/ExpensePage.jsx index e7ac3638..d7b2b594 100644 --- a/src/pages/Expense/ExpensePage.jsx +++ b/src/pages/Expense/ExpensePage.jsx @@ -62,7 +62,7 @@ const ExpensePage = () => { const [ViewDocument, setDocumentView] = useState({ IsOpen: false, - Image: null, + Images: null, }); const IsCreatedAble = useHasUserPermission(CREATE_EXEPENSE); @@ -208,10 +208,10 @@ const ExpensePage = () => { setDocumentView({ IsOpen: false, Image: null })} + key={ViewDocument.Images ?? "doc"} + closeModal={() => setDocumentView({ IsOpen: false, Images: null })} > - + )}
    From ea0e38f100372003868ba8a973e0c10d1a3169fe Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Wed, 26 Nov 2025 18:41:23 +0530 Subject: [PATCH 31/64] removed console --- src/components/RecurringExpense/RecurringExpenseList.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/RecurringExpense/RecurringExpenseList.jsx b/src/components/RecurringExpense/RecurringExpenseList.jsx index a4c45fba..c1bb9e13 100644 --- a/src/components/RecurringExpense/RecurringExpenseList.jsx +++ b/src/components/RecurringExpense/RecurringExpenseList.jsx @@ -166,7 +166,6 @@ const RecurringExpenseList = ({ search, filterStatuses }) => { } ); }; -console.log("Tanish",filteredData) return ( <> {IsDeleteModalOpen && ( From 0eb3cc61a3612c6fdcfef5cfa2273e54e8f9033f Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Wed, 26 Nov 2025 18:56:36 +0530 Subject: [PATCH 32/64] removed unused code --- src/components/purchase/PurchaseList.jsx | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 src/components/purchase/PurchaseList.jsx diff --git a/src/components/purchase/PurchaseList.jsx b/src/components/purchase/PurchaseList.jsx deleted file mode 100644 index 28dd0ef3..00000000 --- a/src/components/purchase/PurchaseList.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react' - -const PurchaseList = () => { - return ( -
    - -
    - ) -} - -export default PurchaseList From 271787812b276fd609e7ce68654614919b8c24b2 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Wed, 26 Nov 2025 19:15:50 +0530 Subject: [PATCH 33/64] added get api for purchases --- src/components/purchase/PurchaseList.jsx | 13 +++++++++++++ src/repositories/PurchaseRepository.jsx | 2 ++ 2 files changed, 15 insertions(+) create mode 100644 src/components/purchase/PurchaseList.jsx diff --git a/src/components/purchase/PurchaseList.jsx b/src/components/purchase/PurchaseList.jsx new file mode 100644 index 00000000..2fe90c2b --- /dev/null +++ b/src/components/purchase/PurchaseList.jsx @@ -0,0 +1,13 @@ +import React from 'react' + +const PurchaseList = ({purchaseId}) => { + + return ( +
    + +
    + ) +} + +export default PurchaseList; +`` \ No newline at end of file diff --git a/src/repositories/PurchaseRepository.jsx b/src/repositories/PurchaseRepository.jsx index eb50340f..2b49ad8f 100644 --- a/src/repositories/PurchaseRepository.jsx +++ b/src/repositories/PurchaseRepository.jsx @@ -2,4 +2,6 @@ import { api } from "../utils/axiosClient"; export const PurchaseRepository = { CreatePurchase: (data) => api.post("/api/PurchaseInvoice/create", data), + GetPurchaseList: (pageSize, pageNumber, isActive, filter, searchString) => + api.get(`/api/PurchaseInvoice/list`), }; From 909022b03451a92494566d6eef1b73f7c7e603a6 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Wed, 26 Nov 2025 20:25:59 +0530 Subject: [PATCH 34/64] added purchase list --- src/components/purchase/PurchaseList.jsx | 36 +++++++++++++++++----- src/hooks/usePurchase.jsx | 39 +++++++++++++++++++++++- src/pages/purchase/PurchasePage.jsx | 3 +- src/repositories/PurchaseRepository.jsx | 11 ++++++- 4 files changed, 78 insertions(+), 11 deletions(-) diff --git a/src/components/purchase/PurchaseList.jsx b/src/components/purchase/PurchaseList.jsx index 2fe90c2b..d6d3fd97 100644 --- a/src/components/purchase/PurchaseList.jsx +++ b/src/components/purchase/PurchaseList.jsx @@ -1,13 +1,33 @@ -import React from 'react' - -const PurchaseList = ({purchaseId}) => { +import React, { useState } from "react"; +import { usePurchasesList } from "../../hooks/usePurchase"; +import { ITEMS_PER_PAGE } from "../../utils/constants"; +import Pagination from "../common/Pagination"; +const PurchaseList = () => { + const [currentPage, setCurrentPage] = useState(1); + const { data, isLoading, isError, error } = usePurchasesList( + ITEMS_PER_PAGE, + currentPage, + true, + {} + ); + const paginate = (page) => { + if (page >= 1 && page <= (data?.totalPages ?? 1)) { + setCurrentPage(page); + } + }; return ( -
    - +
    + {data?.data?.length > 0 && ( + + )}
    - ) -} + ); +}; export default PurchaseList; -`` \ No newline at end of file +``; diff --git a/src/hooks/usePurchase.jsx b/src/hooks/usePurchase.jsx index 88f4d9cc..ce9dd87c 100644 --- a/src/hooks/usePurchase.jsx +++ b/src/hooks/usePurchase.jsx @@ -1,8 +1,45 @@ -import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { PurchaseRepository } from "../repositories/PurchaseRepository"; import showToast from "../services/toastService"; +export const usePurchasesList = ( + pageSize, + pageNumber, + isActive, + filter, + searchString +) => { + return useQuery({ + queryKey: [ + "purchase_list", + pageSize, + pageNumber, + isActive, + filter, + searchString, + ], + queryFn: async () => { + const resp = await PurchaseRepository.GetPurchaseList( + pageSize, + pageNumber, + isActive, + filter, + searchString + ); + return resp.data; + }, + }); +}; +export const usePurchase = (id) => { + return useQuery({ + queryKey: ["purchase", id], + queryFn: async () => { + const resp = await PurchaseRepository.GetPurchase(id); + return resp.data; + }, + }); +}; export const useCreatePurchaseInvoice = (onSuccessCallback) => { const queryClient = useQueryClient(); diff --git a/src/pages/purchase/PurchasePage.jsx b/src/pages/purchase/PurchasePage.jsx index 6a3e508d..7beeaa96 100644 --- a/src/pages/purchase/PurchasePage.jsx +++ b/src/pages/purchase/PurchasePage.jsx @@ -3,6 +3,7 @@ import Breadcrumb from "../../components/common/Breadcrumb"; import showToast from "../../services/toastService"; import GlobalModel from "../../components/common/GlobalModel"; import ManagePurchase from "../../components/purchase/ManagePurchase"; +import PurchaseList from "../../components/purchase/PurchaseList"; export const PurchaseContext = createContext(); export const usePurchaseContext = () => { @@ -40,7 +41,7 @@ const PurchasePage = () => {
    - {/* MOdals */} + api.post("/api/PurchaseInvoice/create", data), GetPurchaseList: (pageSize, pageNumber, isActive, filter, searchString) => - api.get(`/api/PurchaseInvoice/list`), + api.get( + `/api/PurchaseInvoice/list?pageSize=${pageSize}&pageNumber=${pageNumber}&isActive=${isActive}&filter=${filter}&searchString=${searchString}` + ), + + GetPurchase:(id)=>api.get(`/api/PurchaseInvoice/details/${id}`) }; + +// const filterPayload = JSON.stringify({ +// sortFilters, +// groupByColumn: groupBy || null, +// }); From 528255c2bdff7713d1193b8efc721104f3fcc47b Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Thu, 27 Nov 2025 12:42:40 +0530 Subject: [PATCH 35/64] added view deatils of purchases --- src/components/purchase/PurchaseList.jsx | 117 ++++++++++- src/components/purchase/Purchasetable.jsx | 60 ++++++ src/components/purchase/ViewPurchase.jsx | 227 ++++++++++++++++++++++ src/hooks/usePurchase.jsx | 1 + src/pages/purchase/PurchasePage.jsx | 56 +++++- src/repositories/PurchaseRepository.jsx | 4 +- 6 files changed, 449 insertions(+), 16 deletions(-) create mode 100644 src/components/purchase/Purchasetable.jsx create mode 100644 src/components/purchase/ViewPurchase.jsx diff --git a/src/components/purchase/PurchaseList.jsx b/src/components/purchase/PurchaseList.jsx index d6d3fd97..672bccef 100644 --- a/src/components/purchase/PurchaseList.jsx +++ b/src/components/purchase/PurchaseList.jsx @@ -2,22 +2,130 @@ import React, { useState } from "react"; import { usePurchasesList } from "../../hooks/usePurchase"; import { ITEMS_PER_PAGE } from "../../utils/constants"; import Pagination from "../common/Pagination"; +import { PurchaseColumn } from "./Purchasetable"; +import { SpinnerLoader } from "../common/Loader"; +import { useDebounce } from "../../utils/appUtils"; +import { usePurchaseContext } from "../../pages/purchase/PurchasePage"; -const PurchaseList = () => { +const PurchaseList = ({ searchString }) => { + const { setViewPurchase } = usePurchaseContext(); const [currentPage, setCurrentPage] = useState(1); - const { data, isLoading, isError, error } = usePurchasesList( + const debounceSearch = useDebounce(searchString, 300); + const { data, isLoading } = usePurchasesList( ITEMS_PER_PAGE, currentPage, true, - {} + {}, + debounceSearch ); + const paginate = (page) => { if (page >= 1 && page <= (data?.totalPages ?? 1)) { setCurrentPage(page); } }; + + const visibleColumns = PurchaseColumn.filter((col) => !col.hidden); + return ( -
    +
    +
    + + + + {visibleColumns.map((col) => ( + + ))} + + + + + + {/* LOADING */} + {isLoading && ( + + + + )} + {!isLoading && data?.data?.length === 0 && ( + + + + )} + + {!isLoading && + data?.data?.map((item, index) => ( + + {visibleColumns.map((col) => ( + + ))} + + + ))} + +
    +
    {col.label}
    +
    Action
    +
    + +
    +
    + No Data Found +
    + {col.render ? col.render(item) : item[col.key] || "NA"} + + +
    +
    + {data?.data?.length > 0 && ( { }; export default PurchaseList; -``; diff --git a/src/components/purchase/Purchasetable.jsx b/src/components/purchase/Purchasetable.jsx new file mode 100644 index 00000000..c2c5fe5b --- /dev/null +++ b/src/components/purchase/Purchasetable.jsx @@ -0,0 +1,60 @@ +import { formatFigure, getColorNameFromHex } from "../../utils/appUtils"; + +export const PurchaseColumn = [ + { + key: "purchaseInvoiceUId", + label: "Invoice Id", + + className: "text-start", + render: (item) => ( +
    + {item?.purchaseInvoiceUId || "NA"} +
    + ), + }, + { + key: "title", + label: "Title", + + className: "text-start", + render: (item) => ( +
    + {item?.title || "NA"} +
    + ), + }, + { + key: "project", + label: "Project", + className: "text-start d-none d-sm-table-cell", + render: (item) => {item?.project?.name || "NA"}, + }, + { + key: "supplier", + label: "Supplier", + className: "text-start d-none d-sm-table-cell", + render: (item) => {item?.supplier?.name || "NA"}, + }, + { + key: "status", + label: "Status", + className: "text-start", + render: (item) => { + let color = getColorNameFromHex(item.status?.color); + return ( + + + {item?.status.displayName || "NA"} + + ); + }, + }, + { + key: "totalAmount", + label: "Total Amount", + className: " text-end w-min", + render: (item) => ( + {formatFigure(item?.totalAmount, { type: "currency" })} + ), + }, +]; diff --git a/src/components/purchase/ViewPurchase.jsx b/src/components/purchase/ViewPurchase.jsx new file mode 100644 index 00000000..583066ca --- /dev/null +++ b/src/components/purchase/ViewPurchase.jsx @@ -0,0 +1,227 @@ +import React from "react"; +import { usePurchase } from "../../hooks/usePurchase"; +import { SpinnerLoader } from "../common/Loader"; +import { formatUTCToLocalTime } from "../../utils/dateUtils"; +import { getColorNameFromHex } from "../../utils/appUtils"; + +const ViewPurchase = ({ purchaseId }) => { + const { data, isLoading, isError, error } = usePurchase(purchaseId); + + if (isLoading) return ; + if (isError) return ; + + return ( +
    +
    +
    +

    Purchase Details

    +
    +
    + Purchase No: +

    {data?.purchaseInvoiceUId}

    +
    + +
    + Title: +

    {data?.title}

    +
    + +
    + Status: + + + {data?.status.displayName || "NA"} + +
    + +
    + Description: +

    {data?.description}

    +
    +
    +
    +
    + +
    +
    +

    Project

    + +
    + Name: +

    {data?.project?.name}

    +
    + +
    + Organization{" "} +

    {data?.organization?.name}

    +
    + +
    +
    + Email: +

    {data?.organization?.email}

    +
    +
    + Contact:{" "} +

    + {data?.organization?.contactNumber} +

    +
    +
    +
    + Address:{" "} +

    {data?.organization?.address}

    +
    +
    +
    + +
    +
    +

    Supplier

    + +
    + Name: +

    {data?.supplier?.name}

    +
    + +
    + Contact Person: +

    {data?.supplier?.contactPerson}

    +
    + +
    +
    + Email: +

    {data?.supplier?.email}

    +
    + +
    + Contact: +

    {data?.supplier?.contactNumber}

    +
    +
    + +
    + Address: +

    {data?.supplier?.address}

    +
    +
    +
    +
    +
    +

    Invoice Details

    + +
    + {/* Left column */} +
    +
    + Invoice No: +

    {data?.invoiceNumber}

    +
    + +
    + Proforma No: +

    {data?.proformaInvoiceNumber}

    +
    + +
    + E-Way Bill: +

    {data?.eWayBillNumber}

    +
    + +
    + PO No: +

    {data?.purchaseOrderNumber}

    +
    +
    + + {/* Right column */} +
    +
    + Invoice Date: +

    + {formatUTCToLocalTime(data?.invoiceDate)} +

    +
    + +
    + Proforma Date: +

    + {formatUTCToLocalTime(data?.proformaInvoiceDate)} +

    +
    + +
    + E-Way Date: +

    + {formatUTCToLocalTime(data?.eWayBillDate)} +

    +
    + +
    + PO Date: +

    + {formatUTCToLocalTime(data?.purchaseOrderDate)} +

    +
    +
    +
    +
    +
    + +
    +
    +

    Amount Summary

    + +
    + Base Amount +

    ₹ {data?.baseAmount}

    +
    + +
    + Tax +

    ₹ {data?.taxAmount}

    +
    + +
    + Transport +

    ₹ {data?.transportCharges}

    +
    + +
    + +
    + Total + ₹ {data?.totalAmount} +
    + +
    + Due Date: +

    + {formatUTCToLocalTime(data?.paymentDueDate)} +

    +
    +
    +
    + +
    +
    +
    +

    Billing Address

    + +

    {data?.billingAddress || "-"}

    +
    +
    + +
    +
    +

    Shipping Address

    +

    {data?.shippingAddress || "-"}

    +
    +
    +
    +
    + ); +}; + +export default ViewPurchase; diff --git a/src/hooks/usePurchase.jsx b/src/hooks/usePurchase.jsx index ce9dd87c..2cd4fc7f 100644 --- a/src/hooks/usePurchase.jsx +++ b/src/hooks/usePurchase.jsx @@ -38,6 +38,7 @@ export const usePurchase = (id) => { const resp = await PurchaseRepository.GetPurchase(id); return resp.data; }, + enabled: !!id, }); }; diff --git a/src/pages/purchase/PurchasePage.jsx b/src/pages/purchase/PurchasePage.jsx index 7beeaa96..95c00523 100644 --- a/src/pages/purchase/PurchasePage.jsx +++ b/src/pages/purchase/PurchasePage.jsx @@ -4,6 +4,7 @@ import showToast from "../../services/toastService"; import GlobalModel from "../../components/common/GlobalModel"; import ManagePurchase from "../../components/purchase/ManagePurchase"; import PurchaseList from "../../components/purchase/PurchaseList"; +import ViewPurchase from "../../components/purchase/ViewPurchase"; export const PurchaseContext = createContext(); export const usePurchaseContext = () => { @@ -13,11 +14,20 @@ export const usePurchaseContext = () => { showToast("Please use Innne cntext", "warning"); window.location = "/dashboard"; } + return context; }; const PurchasePage = () => { + const [searchText, setSearchText] = useState(""); const [addePurchase, setAddedPurchase] = useState(false); + const [viewPurchaseState, setViewPurchase] = useState({ + isOpen: false, + purchaseId: null, + }); - const contextValue = {}; + const contextValue = { + setViewPurchase, + }; + console.log(ViewPurchase); return (
    @@ -30,7 +40,20 @@ const PurchasePage = () => { />
    - +
    + {" "} + +
    +
    - + + {addePurchase && ( + setAddedPurchase(false)} + > + setAddedPurchase(false)} /> + + )} - setAddedPurchase(false)} - > - setAddedPurchase(false)} /> - + {viewPurchaseState.isOpen && ( + + setViewPurchase({ isOpen: false, purchaseId: null }) + } + > + + + )}
    ); diff --git a/src/repositories/PurchaseRepository.jsx b/src/repositories/PurchaseRepository.jsx index be3d871b..de4a033e 100644 --- a/src/repositories/PurchaseRepository.jsx +++ b/src/repositories/PurchaseRepository.jsx @@ -7,7 +7,9 @@ export const PurchaseRepository = { `/api/PurchaseInvoice/list?pageSize=${pageSize}&pageNumber=${pageNumber}&isActive=${isActive}&filter=${filter}&searchString=${searchString}` ), - GetPurchase:(id)=>api.get(`/api/PurchaseInvoice/details/${id}`) + GetPurchase: (id) => api.get(`/api/PurchaseInvoice/details/${id}`), + UpdatePurchase: (id, data) => + api.patch(`/api/PurchaseInvoice/edit/${id}`, data), }; // const filterPayload = JSON.stringify({ From 339a5ff257b0c1e6d89bfd34752417ce3e8a29c1 Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Thu, 27 Nov 2025 15:56:19 +0530 Subject: [PATCH 36/64] UI alignment in Purchase Invoice popup and changes in Jobs Arcieve and Archieve button. --- src/components/ServiceProject/Jobs.jsx | 31 ++- .../ServiceProjectJob/ManageJob.jsx | 31 +-- src/components/purchase/ViewPurchase.jsx | 210 ++++++++++-------- 3 files changed, 151 insertions(+), 121 deletions(-) diff --git a/src/components/ServiceProject/Jobs.jsx b/src/components/ServiceProject/Jobs.jsx index f0e1cb1e..edec54d1 100644 --- a/src/components/ServiceProject/Jobs.jsx +++ b/src/components/ServiceProject/Jobs.jsx @@ -62,15 +62,27 @@ const Jobs = () => {
    - {/* LEFT — Tabs */} + {/* LEFT — Archive / Unarchive Toggle */}
    - +
    + + + +
    {/* RIGHT — New Job button */} @@ -79,9 +91,10 @@ const Jobs = () => { className="btn btn-sm btn-primary" onClick={() => setManageJob({ isOpen: true, jobId: null })} > - New Job + New Job
    +
    {/* Job List */} diff --git a/src/components/ServiceProject/ServiceProjectJob/ManageJob.jsx b/src/components/ServiceProject/ServiceProjectJob/ManageJob.jsx index cc2066e0..70cacaf9 100644 --- a/src/components/ServiceProject/ServiceProjectJob/ManageJob.jsx +++ b/src/components/ServiceProject/ServiceProjectJob/ManageJob.jsx @@ -172,6 +172,20 @@ const ManageJob = ({ Job }) => {
    +
    + setValue("projectBranchId", val)} + valueKey="id" + labelKey="branchName" + hookParams={[projectId, true, 10, 1]} + useFetchHook={useBranches} + isMultiple={false} + disabled={Job} + /> +
    { placeholder="Enter Tag" />
    -
    - setValue("projectBranchId", val)} - valueKey="id" - labelKey="branchName" - hookParams={[projectId, true, 10, 1]} - useFetchHook={useBranches} - isMultiple={false} - disabled={Job} - /> -
    +