Merge pull request 'React Query Integration for Server State Sync in Clinet' (#245) from react-query into main
Reviewed-on: #245
This commit is contained in:
commit
58fe364821
55
package-lock.json
generated
55
package-lock.json
generated
@ -11,6 +11,8 @@
|
||||
"@hookform/resolvers": "^3.10.0",
|
||||
"@microsoft/signalr": "^8.0.7",
|
||||
"@reduxjs/toolkit": "^2.5.0",
|
||||
"@tanstack/react-query": "^5.81.2",
|
||||
"@tanstack/react-query-devtools": "^5.81.2",
|
||||
"@types/web": "^0.0.216",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"apexcharts": "^4.5.0",
|
||||
@ -1423,6 +1425,59 @@
|
||||
"@swc/counter": "^0.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-core": {
|
||||
"version": "5.81.2",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.81.2.tgz",
|
||||
"integrity": "sha512-QLYkPdrudoMATDFa3MiLEwRhNnAlzHWDf0LKaXUqJd0/+QxN8uTPi7bahRlxoAyH0UbLMBdeDbYzWALj7THOtw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-devtools": {
|
||||
"version": "5.81.2",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.81.2.tgz",
|
||||
"integrity": "sha512-jCeJcDCwKfoyyBXjXe9+Lo8aTkavygHHsUHAlxQKKaDeyT0qyQNLKl7+UyqYH2dDF6UN/14873IPBHchcsU+Zg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-query": {
|
||||
"version": "5.81.2",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.81.2.tgz",
|
||||
"integrity": "sha512-pe8kFlTrL2zFLlcAj2kZk9UaYYHDk9/1hg9EBaoO3cxDhOZf1FRGJeziSXKrVZyxIfs7b3aoOj/bw7Lie0mDUg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.81.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-query-devtools": {
|
||||
"version": "5.81.2",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.81.2.tgz",
|
||||
"integrity": "sha512-TX0OQ4cbgX6z2uN8c9x0QUNbyePGyUGdcgrGnV6TYEJc7KPT8PqeASuzoA5NGw1CiMGvyFAkIGA2KipvhM9d1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/query-devtools": "5.81.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tanstack/react-query": "^5.81.2",
|
||||
"react": "^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||
|
||||
@ -14,6 +14,8 @@
|
||||
"@hookform/resolvers": "^3.10.0",
|
||||
"@microsoft/signalr": "^8.0.7",
|
||||
"@reduxjs/toolkit": "^2.5.0",
|
||||
"@tanstack/react-query": "^5.81.2",
|
||||
"@tanstack/react-query-devtools": "^5.81.2",
|
||||
"@types/web": "^0.0.216",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"apexcharts": "^4.5.0",
|
||||
|
||||
20
src/App.tsx
20
src/App.tsx
@ -1,21 +1,25 @@
|
||||
import { DireProvider } from "./Context/DireContext";
|
||||
import AppRoutes from "./router/AppRoutes";
|
||||
import { ToastContainer } from "react-toastify";
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
||||
import { queryClient } from "./layouts/AuthLayout";
|
||||
|
||||
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<div className="app">
|
||||
<DireProvider>
|
||||
<AppRoutes />
|
||||
</DireProvider>
|
||||
|
||||
<ToastContainer>
|
||||
|
||||
</ToastContainer>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<DireProvider>
|
||||
<AppRoutes />
|
||||
</DireProvider>
|
||||
|
||||
<ToastContainer />
|
||||
<ReactQueryDevtools initialIsOpen={false} />
|
||||
</QueryClientProvider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App
|
||||
export default App;
|
||||
|
||||
@ -8,7 +8,7 @@ import WorkAreaModel from "../Project/Infrastructure/WorkAreaModel";
|
||||
import TaskModel from "../Project/Infrastructure/TaskModel";
|
||||
import ProjectRepository from "../../repositories/ProjectRepository";
|
||||
import Breadcrumb from "../../components/common/Breadcrumb";
|
||||
import {useProjectDetails, useProjects} from "../../hooks/useProjects";
|
||||
import {useProjectDetails, useProjectInfra, useProjects} from "../../hooks/useProjects";
|
||||
import {useHasUserPermission} from "../../hooks/useHasUserPermission";
|
||||
import {MANAGE_PROJECT_INFRA} from "../../utils/constants";
|
||||
import {useDispatch, useSelector} from "react-redux";
|
||||
@ -21,17 +21,14 @@ const InfraPlanning = () =>
|
||||
{
|
||||
const {profile: LoggedUser, refetch : fetchData} = useProfile()
|
||||
const dispatch = useDispatch()
|
||||
const {projects,loading:project_listLoader,error:projects_error} = useProjects()
|
||||
|
||||
const selectedProject = useSelector((store)=>store.localVariables.projectId)
|
||||
const {projectInfra, isLoading, error} = useProjectInfra( selectedProject )
|
||||
|
||||
|
||||
|
||||
const ManageInfra = useHasUserPermission( MANAGE_PROJECT_INFRA )
|
||||
const {projects_Details, loading: project_deatilsLoader, error: project_error,refetch} = useProjectDetails( selectedProject )
|
||||
const reloadedData = useSelector( ( store ) => store.localVariables.reload )
|
||||
|
||||
// useEffect( () =>
|
||||
// {
|
||||
// dispatch(setProjectId(projects[0]?.id))
|
||||
// }, [ projects ] )
|
||||
|
||||
|
||||
useEffect( () =>
|
||||
{
|
||||
@ -47,34 +44,20 @@ const InfraPlanning = () =>
|
||||
<div className="col-md-12 col-lg-12 col-xl-12 order-0 mb-4">
|
||||
<div className="card">
|
||||
<div className="card-body" style={{ padding: "0.5rem" }}>
|
||||
<div className="align-items-center">
|
||||
{/* <div className="row ">
|
||||
<div className="col-sm-3 col-8 text-start mb-1">
|
||||
<select name="DataTables_Table_0_length"
|
||||
aria-controls="DataTables_Table_0"
|
||||
className="form-select form-select-sm"
|
||||
value={selectedProject}
|
||||
onChange={(e)=>dispatch(setProjectId(e.target.value))}
|
||||
aria-label=""
|
||||
>
|
||||
{(project_listLoader || projects.length < 0) && <option value="Loading..." disabled>Loading...</option> }
|
||||
|
||||
{!project_listLoader && projects?.map((project)=>(
|
||||
<option key={project.id} value={project.id}>{project.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div> */}
|
||||
{ManageInfra ? (
|
||||
<div className="align-items-center">
|
||||
<div className="row ">
|
||||
{project_deatilsLoader && ( <p>Loading...</p> )}
|
||||
{( !project_deatilsLoader && projects_Details?.buildings.length === 0 ) && ( <p>No Result Found</p> )}
|
||||
|
||||
|
||||
|
||||
{(!project_deatilsLoader && projects_Details?.buildings?.length > 0) && (<InfraTable buildings={projects_Details?.buildings} projectId={projects_Details.id}/>)}
|
||||
{isLoading && ( <p>Loading...</p> )}
|
||||
{( !isLoading && projectInfra?.length === 0 ) && ( <p>No Result Found</p> )}
|
||||
{(!isLoading && projectInfra?.length > 0) && (<InfraTable buildings={projectInfra} projectId={selectedProject}/>)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center">
|
||||
<i className="fa-solid fa-triangle-exclamation fs-5"></i>
|
||||
<p>Access Denied: You don't have permission to perform this action. !</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,21 +1,29 @@
|
||||
import React, { useState,useEffect } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { formatDate } from "../../utils/dateUtils";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
import showToast from "../../services/toastService";
|
||||
import { TasksRepository } from "../../repositories/TaskRepository";
|
||||
import { useReportTask } from "../../hooks/useTasks";
|
||||
|
||||
export const ReportTask = ({ report, closeModal, refetch }) => {
|
||||
const [loading, setloading] = useState(false);
|
||||
export const ReportTask = ({ report, closeModal }) => {
|
||||
const { mutate: reportTask, isPending } = useReportTask({
|
||||
onSuccessCallback: () => {
|
||||
reset();
|
||||
closeModal();
|
||||
},
|
||||
});
|
||||
|
||||
const maxPending =
|
||||
report?.workItem?.plannedWork - report?.workItem?.completedWork;
|
||||
|
||||
const schema = z.object({
|
||||
completedTask: z
|
||||
.preprocess(
|
||||
(val) => (val === "" || val === null || Number.isNaN(val) ? undefined : Number(val)),
|
||||
completedTask: z.preprocess(
|
||||
(val) =>
|
||||
val === "" || val === null || Number.isNaN(val)
|
||||
? undefined
|
||||
: Number(val),
|
||||
z
|
||||
.number({
|
||||
required_error: "Completed Work must be a number",
|
||||
@ -26,49 +34,35 @@ export const ReportTask = ({ report, closeModal, refetch }) => {
|
||||
message: `Completed task cannot exceed total pending tasks: ${maxPending}`,
|
||||
})
|
||||
),
|
||||
comment: z.string().min(1, "Comment cannot be empty"),
|
||||
});
|
||||
comment: z.string().min(1, "Comment cannot be empty"),
|
||||
});
|
||||
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: {errors},
|
||||
reset
|
||||
formState: { errors },
|
||||
reset,
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: { completedTask: 0, comment: "" },
|
||||
});
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (report) {
|
||||
reset({ completedTask: 0, comment: "" }); // optional: customize default if needed
|
||||
}
|
||||
}, [report, reset]);
|
||||
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
try {
|
||||
setloading(true);
|
||||
const reportData = {
|
||||
...data,
|
||||
id: report?.id,
|
||||
reportedDate: new Date().toISOString(),
|
||||
checkList: [],
|
||||
};
|
||||
|
||||
let response = await TasksRepository.reportTask(reportData);
|
||||
showToast("Task Reported Successfully.", "success");
|
||||
refetch();
|
||||
reset()
|
||||
setloading(false);
|
||||
closeModal();
|
||||
} catch ( error )
|
||||
{
|
||||
const msg = error.response.data.message || error.message || "Error Occur During Api Call"
|
||||
showToast(msg, "error");
|
||||
useEffect(() => {
|
||||
if (report) {
|
||||
reset({ completedTask: 0, comment: "" });
|
||||
}
|
||||
}, [report, reset]);
|
||||
|
||||
const onSubmit = (data) => {
|
||||
|
||||
const reportData = {
|
||||
...data,
|
||||
id: report?.id,
|
||||
reportedDate: new Date().toISOString(),
|
||||
checkList: [],
|
||||
};
|
||||
|
||||
reportTask({ reportData, workAreaId: report?.workItem?.workArea?.id });
|
||||
};
|
||||
const handleClose = () => {
|
||||
closeModal();
|
||||
@ -76,153 +70,130 @@ useEffect(() => {
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
|
||||
<div className="container m-0">
|
||||
<div className="text-center">
|
||||
<p className="fs-6 fw-semibold">Report Task</p>
|
||||
</div>
|
||||
<div className="mb-1 row text-start">
|
||||
<label
|
||||
htmlFor="html5-text-input"
|
||||
className="col-md-4 col-form-label"
|
||||
>
|
||||
Assigned Date :{" "}
|
||||
</label>
|
||||
<div className="col-md-8 text-start">
|
||||
<label className="col-md-2 col-form-label">
|
||||
{formatDate(report?.assignmentDate)}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-1 row text-start">
|
||||
<label
|
||||
htmlFor="html5-search-input"
|
||||
className="col-md-4 col-form-label"
|
||||
>
|
||||
Assigned By :{" "}
|
||||
</label>
|
||||
<div className="col-md-8 text-start">
|
||||
<label className=" col-form-label">{` ${report?.assignedBy.firstName} ${report?.assignedBy.lastName}`}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-1 row text-start">
|
||||
<label
|
||||
htmlFor="html5-email-input"
|
||||
className="col-md-4 col-form-label"
|
||||
>
|
||||
Wrok Area :
|
||||
</label>
|
||||
<div className="col-md-8 text-start text-wrap">
|
||||
<label className=" col-form-label">
|
||||
{" "}
|
||||
{report?.workItem?.workArea?.floor?.building?.name}{" "}
|
||||
<i className="bx bx-chevron-right"></i>{" "}
|
||||
{report?.workItem?.workArea?.floor?.floorName}{" "}
|
||||
<i className="bx bx-chevron-right"> </i>
|
||||
{report?.workItem?.workArea?.areaName}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-1 row text-start">
|
||||
<label
|
||||
htmlFor="html5-email-input"
|
||||
className="col-md-4 col-form-label"
|
||||
>
|
||||
Activity :
|
||||
</label>
|
||||
<div className="col-md-8 text-start text-wrap">
|
||||
<label className=" col-form-label">
|
||||
{report?.workItem?.activityMaster.activityName}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-1 row text-start">
|
||||
<label
|
||||
htmlFor="html5-email-input"
|
||||
className="col-md-4 col-form-label"
|
||||
>
|
||||
Team Size :
|
||||
</label>
|
||||
<div className="col-md-8 text-start text-wrap">
|
||||
<label className=" col-form-label">
|
||||
{report?.teamMembers?.length}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-1 row text-start">
|
||||
<label
|
||||
htmlFor="html5-email-input"
|
||||
className="col-md-4 col-form-label"
|
||||
>
|
||||
Assigned :
|
||||
</label>
|
||||
<div className="col-md-8 text-start text-wrap">
|
||||
<label className=" col-form-label">
|
||||
{report?.plannedTask} of{" "}
|
||||
{report?.workItem.plannedWork -
|
||||
report?.workItem.completedWork}{" "}
|
||||
Pending
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="mb-1 row text-start">
|
||||
<label
|
||||
htmlFor="html5-email-input"
|
||||
className="col-md-4 col-form-label"
|
||||
>
|
||||
Completed Work
|
||||
</label>
|
||||
<div className="col-md-8 text-start text-wrap">
|
||||
<input
|
||||
{...register("completedTask", { valueAsNumber: true })}
|
||||
id="smallInput"
|
||||
className="form-control form-control-sm"
|
||||
type="number"
|
||||
placeholder="Completed Work"
|
||||
/>
|
||||
{errors.completedTask && (
|
||||
<div className="danger-text">
|
||||
{errors.completedTask.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-1 row text-start">
|
||||
<label
|
||||
htmlFor="html5-email-input"
|
||||
className="col-md-4 col-form-label"
|
||||
>
|
||||
Comment
|
||||
</label>
|
||||
<div className="col-md-8 text-start text-wrap">
|
||||
<textarea
|
||||
{...register("comment")}
|
||||
className="form-control"
|
||||
id="exampleFormControlTextarea1"
|
||||
rows="1"
|
||||
placeholder="Enter comment"
|
||||
/>
|
||||
{errors.comment && (
|
||||
<div className="danger-text">{errors.comment.message}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 text-center my-2">
|
||||
<button type="submit" className="btn btn-sm btn-primary me-3">
|
||||
{loading ? "Please wait" : "Submit Report"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-label-secondary"
|
||||
onClick={handleClose}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div className="mb-1 row text-start">
|
||||
<label htmlFor="html5-text-input" className="col-md-4 col-form-label">
|
||||
Assigned Date :{" "}
|
||||
</label>
|
||||
<div className="col-md-8 text-start">
|
||||
<label className="col-md-2 col-form-label">
|
||||
{formatDate(report?.assignmentDate)}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-1 row text-start">
|
||||
<label htmlFor="html5-search-input" className="col-md-4 col-form-label">
|
||||
Assigned By :{" "}
|
||||
</label>
|
||||
<div className="col-md-8 text-start">
|
||||
<label className=" col-form-label">{` ${report?.assignedBy.firstName} ${report?.assignedBy.lastName}`}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-1 row text-start">
|
||||
<label htmlFor="html5-email-input" className="col-md-4 col-form-label">
|
||||
Wrok Area :
|
||||
</label>
|
||||
<div className="col-md-8 text-start text-wrap">
|
||||
<label className=" col-form-label">
|
||||
{" "}
|
||||
{report?.workItem?.workArea?.floor?.building?.name}{" "}
|
||||
<i className="bx bx-chevron-right"></i>{" "}
|
||||
{report?.workItem?.workArea?.floor?.floorName}{" "}
|
||||
<i className="bx bx-chevron-right"> </i>
|
||||
{report?.workItem?.workArea?.areaName}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-1 row text-start">
|
||||
<label htmlFor="html5-email-input" className="col-md-4 col-form-label">
|
||||
Activity :
|
||||
</label>
|
||||
<div className="col-md-8 text-start text-wrap">
|
||||
<label className=" col-form-label">
|
||||
{report?.workItem?.activityMaster.activityName}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-1 row text-start">
|
||||
<label htmlFor="html5-email-input" className="col-md-4 col-form-label">
|
||||
Team Size :
|
||||
</label>
|
||||
<div className="col-md-8 text-start text-wrap">
|
||||
<label className=" col-form-label">
|
||||
{report?.teamMembers?.length}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-1 row text-start">
|
||||
<label htmlFor="html5-email-input" className="col-md-4 col-form-label">
|
||||
Assigned :
|
||||
</label>
|
||||
<div className="col-md-8 text-start text-wrap">
|
||||
<label className=" col-form-label">
|
||||
{report?.plannedTask} of{" "}
|
||||
{report?.workItem.plannedWork - report?.workItem.completedWork}{" "}
|
||||
Pending
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="mb-1 row text-start">
|
||||
<label
|
||||
htmlFor="html5-email-input"
|
||||
className="col-md-4 col-form-label"
|
||||
>
|
||||
Completed Work
|
||||
</label>
|
||||
<div className="col-md-8 text-start text-wrap">
|
||||
<input
|
||||
{...register("completedTask", { valueAsNumber: true })}
|
||||
id="smallInput"
|
||||
className="form-control form-control-sm"
|
||||
type="number"
|
||||
placeholder="Completed Work"
|
||||
/>
|
||||
{errors.completedTask && (
|
||||
<div className="danger-text">{errors.completedTask.message}</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div className="mb-1 row text-start">
|
||||
<label
|
||||
htmlFor="html5-email-input"
|
||||
className="col-md-4 col-form-label"
|
||||
>
|
||||
Comment
|
||||
</label>
|
||||
<div className="col-md-8 text-start text-wrap">
|
||||
<textarea
|
||||
{...register("comment")}
|
||||
className="form-control"
|
||||
id="exampleFormControlTextarea1"
|
||||
rows="1"
|
||||
placeholder="Enter comment"
|
||||
/>
|
||||
{errors.comment && (
|
||||
<div className="danger-text">{errors.comment.message}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 text-center my-2">
|
||||
<button type="submit" className="btn btn-sm btn-primary me-3" disabled={isPending}>
|
||||
{isPending ? "Please wait" : "Submit Report"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-label-secondary"
|
||||
onClick={handleClose}
|
||||
disabled={isPending}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -9,7 +9,7 @@ import Avatar from "../common/Avatar";
|
||||
import { getBgClassFromHash } from "../../utils/projectStatus";
|
||||
import { cacheData, getCachedData } from "../../slices/apiDataManager";
|
||||
import ImagePreview from "../common/ImagePreview";
|
||||
import { useAuditStatus } from "../../hooks/useTasks";
|
||||
import { useAuditStatus, useSubmitTaskComment } from "../../hooks/useTasks";
|
||||
|
||||
const ReportTaskComments = ({
|
||||
commentsData,
|
||||
@ -48,7 +48,18 @@ const ReportTaskComments = ({
|
||||
const [loading, setloading] = useState(false);
|
||||
const [comments, setComment] = useState([]);
|
||||
const { status, loading: auditStatusLoading } = useAuditStatus();
|
||||
const [IsNeededSubTask, setIsNeededSubTask] = useState(false);
|
||||
const [ IsNeededSubTask, setIsNeededSubTask ] = useState( false );
|
||||
const { submitComment, isPending } = useSubmitTaskComment({
|
||||
actionAllow,
|
||||
onSuccessCallback: (data) => {
|
||||
setComment((prev) => [...prev, data]);
|
||||
reset();
|
||||
if ( actionAllow )
|
||||
{
|
||||
handleCloseAction(IsNeededSubTask);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
watch,
|
||||
@ -88,70 +99,16 @@ const ReportTaskComments = ({
|
||||
}
|
||||
}, [comments]);
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
let payload = {
|
||||
...data,
|
||||
[actionAllow ? "id" : "taskAllocationId"]: commentsData?.id,
|
||||
...(actionAllow ? {} : { commentDate: new Date().toISOString() }),
|
||||
};
|
||||
|
||||
try {
|
||||
setloading(true);
|
||||
const resp = actionAllow
|
||||
? await TasksRepository.auditTask(payload)
|
||||
: await TasksRepository.taskComments(payload);
|
||||
|
||||
setComment((prevItems) => [...prevItems, resp.data]);
|
||||
|
||||
const taskList = getCachedData("taskList");
|
||||
|
||||
if (actionAllow) {
|
||||
handleCloseAction(IsNeededSubTask);
|
||||
showToast(
|
||||
"Review submitted successfully. Record has been updated.",
|
||||
"success"
|
||||
);
|
||||
} else {
|
||||
if (taskList && taskList.data) {
|
||||
const updatedTaskList = taskList.data.map((task) => {
|
||||
if (task.id === resp.data.taskAllocationId) {
|
||||
const existingComments = Array.isArray(task.comments)
|
||||
? task.comments
|
||||
: [];
|
||||
return {
|
||||
...task,
|
||||
comments: [...existingComments, resp.data],
|
||||
};
|
||||
}
|
||||
return task;
|
||||
});
|
||||
|
||||
cacheData("taskList", {
|
||||
data: updatedTaskList,
|
||||
projectId: taskList.projectId,
|
||||
});
|
||||
}
|
||||
showToast("Successfully Sent", "success");
|
||||
}
|
||||
|
||||
reset();
|
||||
setloading(false);
|
||||
} catch (error) {
|
||||
setloading(false);
|
||||
showToast(
|
||||
error.response.data?.message || "Something went wrong",
|
||||
"error"
|
||||
);
|
||||
}
|
||||
const onSubmit = (formData) => {
|
||||
submitComment({ data: formData, commentsData });
|
||||
};
|
||||
|
||||
const selectedAuditStatus = watch("workStatus");
|
||||
|
||||
useEffect(() => {
|
||||
reset({
|
||||
approvedTask: defaultCompletedTask || 0,
|
||||
});
|
||||
}, [ defaultCompletedTask ] );
|
||||
}, [defaultCompletedTask]);
|
||||
return (
|
||||
<div className="p-2 p-sm-1">
|
||||
<div className="modal-body p-sm-4 p-0">
|
||||
@ -233,7 +190,6 @@ const ReportTaskComments = ({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="d-flex align-items-center flex-wrap">
|
||||
<p className="fw-bold text-start m-0 me-1">
|
||||
@ -260,50 +216,50 @@ const ReportTaskComments = ({
|
||||
<div className="fw-normal ms-2">{commentsData?.description}</div>
|
||||
</div>
|
||||
|
||||
|
||||
{commentsData?.approvedBy && (
|
||||
<>
|
||||
<hr className="my-1"/>
|
||||
<div className="row">
|
||||
|
||||
<div className="col-12 col-sm-6">
|
||||
{commentsData.approvedBy && (
|
||||
<div className="fw-bold text-start d-flex align-items-center">
|
||||
<i className="bx bx-user-check bx-lg me-1"></i>
|
||||
<span className="me-2">Approved By:</span>
|
||||
<div className="d-flex align-items-center fw-normal">
|
||||
<Avatar
|
||||
firstName={commentsData.approvedBy.firstName}
|
||||
lastName={commentsData.approvedBy.lastName}
|
||||
size="xs"
|
||||
className="me-1"
|
||||
/>
|
||||
{commentsData.approvedBy.firstName +
|
||||
" " +
|
||||
commentsData.approvedBy.lastName}
|
||||
</div>
|
||||
<hr className="my-1" />
|
||||
<div className="row">
|
||||
<div className="col-12 col-sm-6">
|
||||
{commentsData.approvedBy && (
|
||||
<div className="fw-bold text-start d-flex align-items-center">
|
||||
<i className="bx bx-user-check bx-lg me-1"></i>
|
||||
<span className="me-2">Approved By:</span>
|
||||
<div className="d-flex align-items-center fw-normal">
|
||||
<Avatar
|
||||
firstName={commentsData.approvedBy.firstName}
|
||||
lastName={commentsData.approvedBy.lastName}
|
||||
size="xs"
|
||||
className="me-1"
|
||||
/>
|
||||
{commentsData.approvedBy.firstName +
|
||||
" " +
|
||||
commentsData.approvedBy.lastName}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-sm-6">
|
||||
{commentsData?.workStatus != null && (
|
||||
<div className="fw-bold my-2 text-start d-flex align-items-center">
|
||||
<i className="bx bx-time-five me-2"></i>
|
||||
<span className="me-2">Work Status :</span>
|
||||
<span className="fw-normal">
|
||||
{commentsData?.workStatus.name}
|
||||
{/* {commentsData?.} */}
|
||||
</span>
|
||||
<div className="col-12 col-sm-6">
|
||||
{commentsData?.workStatus != null && (
|
||||
<div className="fw-bold my-2 text-start d-flex align-items-center">
|
||||
<i className="bx bx-time-five me-2"></i>
|
||||
<span className="me-2">Work Status :</span>
|
||||
<span className="fw-normal">
|
||||
{commentsData?.workStatus.name}
|
||||
{/* {commentsData?.} */}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 d-flex">
|
||||
<span className="fw-bold">Total Approved : </span><span className="ms-2">{commentsData?.completedTask }</span>
|
||||
</div>
|
||||
</> )}
|
||||
|
||||
<div className="col-12 d-flex">
|
||||
<span className="fw-bold">Total Approved : </span>
|
||||
<span className="ms-2">{commentsData?.completedTask}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{commentsData?.reportedPreSignedUrls?.length > 0 && (
|
||||
<>
|
||||
<p className="fw-bold m-0 text-start">
|
||||
@ -319,54 +275,53 @@ const ReportTaskComments = ({
|
||||
)}
|
||||
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="text-start">
|
||||
{( actionAllow && !commentsData.approvedBy ) && (
|
||||
{actionAllow && !commentsData.approvedBy && (
|
||||
<>
|
||||
<div className="row align-items-end my-1">
|
||||
<div className="col-6 col-sm-4 text-start">
|
||||
<label>Completed</label>
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
{...register("approvedTask")}
|
||||
type="number"
|
||||
/>
|
||||
{errors.approvedTask && (
|
||||
<p className="danger-text m-0">
|
||||
{errors.approvedTask.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-6 col-sm-4 text-center align-items-end m-0">
|
||||
<label htmlFor="workStatus" className="form-label">
|
||||
Audit Status
|
||||
</label>
|
||||
<select
|
||||
id="workStatus"
|
||||
className={`form-select form-select-sm`}
|
||||
{...register("workStatus")}
|
||||
defaultValue=""
|
||||
onChange={(e) => setValue("workStatus", e.target.value)}
|
||||
>
|
||||
<option value="" disabled>
|
||||
Select Status
|
||||
</option>
|
||||
{auditStatusLoading ? (
|
||||
<option disabled>Loading...</option>
|
||||
) : (
|
||||
status.map((stat) => (
|
||||
<option key={stat.id} value={stat.id}>
|
||||
{stat.name}
|
||||
</option>
|
||||
))
|
||||
<div className="row align-items-end my-1">
|
||||
<div className="col-6 col-sm-4 text-start">
|
||||
<label>Completed</label>
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
{...register("approvedTask")}
|
||||
type="number"
|
||||
/>
|
||||
{errors.approvedTask && (
|
||||
<p className="danger-text m-0">
|
||||
{errors.approvedTask.message}
|
||||
</p>
|
||||
)}
|
||||
</select>
|
||||
{errors.workStatus && (
|
||||
<div className="danger-text">
|
||||
{errors.workStatus.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-6 col-sm-4 text-center align-items-end m-0">
|
||||
<label htmlFor="workStatus" className="form-label">
|
||||
Audit Status
|
||||
</label>
|
||||
<select
|
||||
id="workStatus"
|
||||
className={`form-select form-select-sm`}
|
||||
{...register("workStatus")}
|
||||
defaultValue=""
|
||||
onChange={(e) => setValue("workStatus", e.target.value)}
|
||||
>
|
||||
<option value="" disabled>
|
||||
Select Status
|
||||
</option>
|
||||
{auditStatusLoading ? (
|
||||
<option disabled>Loading...</option>
|
||||
) : (
|
||||
status.map((stat) => (
|
||||
<option key={stat.id} value={stat.id}>
|
||||
{stat.name}
|
||||
</option>
|
||||
))
|
||||
)}
|
||||
</select>
|
||||
{errors.workStatus && (
|
||||
<div className="danger-text">
|
||||
{errors.workStatus.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</>
|
||||
)}
|
||||
<i className="bx bx-comment-detail me-2"></i>
|
||||
@ -382,10 +337,16 @@ const ReportTaskComments = ({
|
||||
)}
|
||||
<div
|
||||
className={` ${
|
||||
(actionAllow && !commentsData.approvedBy)? " d-flex justify-content-between" : "text-end"
|
||||
actionAllow && !commentsData.approvedBy
|
||||
? " d-flex justify-content-between"
|
||||
: "text-end"
|
||||
} mt-2`}
|
||||
>
|
||||
<div className={`form-check ${!(actionAllow && !commentsData.approvedBy) && "d-none"} `}>
|
||||
<div
|
||||
className={`form-check ${
|
||||
!(actionAllow && !commentsData.approvedBy) && "d-none"
|
||||
} `}
|
||||
>
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
@ -403,15 +364,16 @@ const ReportTaskComments = ({
|
||||
className="btn btn-sm btn-secondary"
|
||||
onClick={closeModal}
|
||||
data-bs-dismiss="modal"
|
||||
disabled={isPending}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-sm btn-primary ms-2"
|
||||
disabled={loading}
|
||||
disabled={isPending}
|
||||
>
|
||||
{loading
|
||||
{isPending
|
||||
? actionAllow
|
||||
? "Please Wait..."
|
||||
: "Send..."
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
import showToast from "../../services/toastService";
|
||||
import ProjectRepository from "../../repositories/ProjectRepository";
|
||||
import { useTaskById } from "../../hooks/useTasks";
|
||||
import {useManageTask} from "../../hooks/useProjects";
|
||||
|
||||
const subTaskSchema = z.object({
|
||||
activityId: z.string().min(1, "Activity is required"),
|
||||
@ -23,7 +24,6 @@ const SubTask = ({ activity, onClose }) => {
|
||||
const [categoryData, setCategoryData] = useState([]);
|
||||
const { activities, loading } = useActivitiesMaster();
|
||||
const { categories, categoryLoading } = useWorkCategoriesMaster();
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const { Task, loading: TaskLoading } = useTaskById(activity?.id);
|
||||
const {
|
||||
register,
|
||||
@ -37,13 +37,19 @@ const SubTask = ({ activity, onClose }) => {
|
||||
});
|
||||
const selectedActivityId = watch("activityId");
|
||||
const selectedActivity = activities?.find((a) => a.id === selectedActivityId);
|
||||
const {mutate:createSubTask,isPending } = useManageTask( {
|
||||
onSuccessCallback: () =>
|
||||
{
|
||||
showToast("Sub Task Created Successfully","success")
|
||||
reset();
|
||||
onClose();
|
||||
}
|
||||
} )
|
||||
|
||||
// Set categories when fetched
|
||||
useEffect(() => {
|
||||
setCategoryData(categories);
|
||||
}, [categories]);
|
||||
|
||||
// Set initial values from activity
|
||||
useEffect(() => {
|
||||
if (!TaskLoading && (Task?.workItem || activity)) {
|
||||
reset({
|
||||
@ -76,22 +82,8 @@ const SubTask = ({ activity, onClose }) => {
|
||||
parentTaskId: activity?.id,
|
||||
comment: formData.comment,
|
||||
};
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
await ProjectRepository.manageProjectTasks([payload]);
|
||||
showToast("SubTask Created Successfully", "success");
|
||||
setIsSubmitting(false);
|
||||
reset();
|
||||
onClose();
|
||||
} catch (error) {
|
||||
setIsSubmitting(false);
|
||||
const msg =
|
||||
error.response.message ||
|
||||
error.message ||
|
||||
"Error occured during API calling";
|
||||
showToast(msg, "error");
|
||||
}
|
||||
|
||||
createSubTask([payload])
|
||||
};
|
||||
return (
|
||||
<div className="container-xxl my-1">
|
||||
@ -221,15 +213,15 @@ const SubTask = ({ activity, onClose }) => {
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-sm btn-primary me-2"
|
||||
disabled={isSubmitting}
|
||||
disabled={isPending}
|
||||
>
|
||||
{isSubmitting ? "Please wait..." : "Submit"}
|
||||
{isPending ? "Please wait..." : "Submit"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-secondary"
|
||||
onClick={() => onClose()}
|
||||
disabled={isSubmitting}
|
||||
disabled={isPending}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
|
||||
@ -19,7 +19,7 @@ import { useProfile } from "../../hooks/useProfile";
|
||||
const ManageBucket = () => {
|
||||
const { profile } = useProfile();
|
||||
const [bucketList, setBucketList] = useState([]);
|
||||
const { employeesList } = useAllEmployees(false);
|
||||
const {employeesList} = useAllEmployees( false );
|
||||
const [selectedEmployee, setSelectEmployee] = useState([]);
|
||||
const { buckets, loading, refetch } = useBuckets();
|
||||
const [action_bucket, setAction_bucket] = useState(false);
|
||||
|
||||
@ -9,24 +9,26 @@ import { useDispatch } from "react-redux";
|
||||
import { changeMaster } from "../../slices/localVariablesSlice";
|
||||
import { Link, useNavigate, useParams } from "react-router-dom";
|
||||
import { formatDate } from "../../utils/dateUtils";
|
||||
import { useEmployeeProfile } from "../../hooks/useEmployees";
|
||||
import { useEmployeeProfile, useUpdateEmployee } from "../../hooks/useEmployees";
|
||||
import {
|
||||
cacheData,
|
||||
clearCacheKey,
|
||||
getCachedData,
|
||||
} from "../../slices/apiDataManager";
|
||||
import { clearApiCacheKey } from "../../slices/apiCacheSlice";
|
||||
import {useMutation} from "@tanstack/react-query";
|
||||
|
||||
const mobileNumberRegex = /^[0-9]\d{9}$/;
|
||||
|
||||
const ManageEmployee = ({ employeeId, onClosed }) => {
|
||||
const ManageEmployee = ({ employeeId, onClosed,IsAllEmployee }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { mutate: updateEmployee, isPending } = useUpdateEmployee();
|
||||
|
||||
// const { employeeId } = useParams();
|
||||
const {
|
||||
employee,
|
||||
error,
|
||||
loading: empLoading,
|
||||
refetch
|
||||
} = useEmployeeProfile(employeeId);
|
||||
|
||||
useEffect(() => {
|
||||
@ -128,7 +130,12 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
|
||||
.min(1, { message: "Phone Number is required" })
|
||||
.regex(mobileNumberRegex, { message: "Invalid phone number " }),
|
||||
jobRoleId: z.string().min(1, { message: "Role is required" }),
|
||||
});
|
||||
} );
|
||||
|
||||
useEffect( () =>
|
||||
{
|
||||
refetch()
|
||||
},[])
|
||||
|
||||
const {
|
||||
register,
|
||||
@ -162,39 +169,20 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
|
||||
});
|
||||
|
||||
const AadharNumberValue = watch("aadharNumber") || "";
|
||||
|
||||
const onSubmit = (data) => {
|
||||
if (data.email === "") {
|
||||
data.email = null;
|
||||
}
|
||||
|
||||
const onSubmit = (data) => {
|
||||
setLoading(true);
|
||||
if (data.email == "") {
|
||||
data.email = null;
|
||||
}
|
||||
EmployeeRepository.manageEmployee(data)
|
||||
.then((response) => {
|
||||
cacheData("employeeProfileInfo", data);
|
||||
showToast(
|
||||
`Employee details ${data.id == null ? "created" : "updated"
|
||||
} successfully.`,
|
||||
"success"
|
||||
);
|
||||
clearCacheKey("employeeListByProject");
|
||||
clearCacheKey("allEmployeeList");
|
||||
clearCacheKey("allInactiveEmployeeList");
|
||||
clearCacheKey("employeeProfile");
|
||||
updateEmployee({...data,IsAllEmployee},{
|
||||
onSuccess: () => {
|
||||
reset();
|
||||
onClosed();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
setLoading(false);
|
||||
reset();
|
||||
// navigation("/employees");
|
||||
onClosed();
|
||||
})
|
||||
.catch((error) => {
|
||||
const message =
|
||||
error?.response?.data?.message ||
|
||||
error?.message ||
|
||||
"Error occured during api calling";
|
||||
showToast(message, "error");
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && !error && employee) {
|
||||
@ -224,7 +212,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
|
||||
phoneNumber: currentEmployee.phoneNumber || "",
|
||||
jobRoleId: currentEmployee.jobRoleId?.toString() || "",
|
||||
}
|
||||
: {} // Empty object resets the form
|
||||
: {}
|
||||
);
|
||||
setCurrentAddressLength(currentEmployee?.currentAddress?.length || 0);
|
||||
setPermanentAddressLength(currentEmployee?.permanentAddress?.length || 0);
|
||||
@ -232,44 +220,31 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
{/* <div className="c">
|
||||
{!currentEmployee && empLoading && employeeId && (
|
||||
<p>Loading Employee Data...</p>
|
||||
)} */}
|
||||
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="p-sm-5 p-2 position-relative">
|
||||
{/* Cross button */}
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close position-absolute top-0 end-0 m-2"
|
||||
aria-label="Close"
|
||||
onClick={onClosed}
|
||||
></button>
|
||||
<div className="text-center"><p className="fs-6 fw-semibold"> {employee ? "Update Employee" : "Create Employee"}</p></div>
|
||||
<div className="row mb-3">
|
||||
<div className="col-sm-4">
|
||||
{" "}
|
||||
<div className="form-text text-start">First Name</div>
|
||||
<input
|
||||
type="text"
|
||||
name="FirstName"
|
||||
{...register("firstName")}
|
||||
className="form-control form-control-sm"
|
||||
id="firstName"
|
||||
placeholder="First Name"
|
||||
/>
|
||||
{errors.firstName && (
|
||||
<div
|
||||
className="danger-text text-start"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.firstName.message}
|
||||
</div>
|
||||
)}
|
||||
</div>{" "}
|
||||
<div className="col-sm-4">
|
||||
<div className="form-text text-start">Middle Name</div>
|
||||
<form onSubmit={handleSubmit( onSubmit )} className="p-sm-0 p-2">
|
||||
<div className="text-center"><p className="fs-6 fw-semibold"> {employee ? "Update Employee" : "Create Employee"}</p> </div>
|
||||
<div className="row mb-3">
|
||||
<div className="col-sm-4">
|
||||
{" "}
|
||||
<div className="form-text text-start">First Name</div>
|
||||
<input
|
||||
type="text"
|
||||
name="FirstName"
|
||||
{...register("firstName")}
|
||||
className="form-control form-control-sm"
|
||||
id="firstName"
|
||||
placeholder="First Name"
|
||||
/>
|
||||
{errors.firstName && (
|
||||
<div
|
||||
className="danger-text text-start"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.firstName.message}
|
||||
</div>
|
||||
)}
|
||||
</div>{" "}
|
||||
<div className="col-sm-4">
|
||||
<div className="form-text text-start">Middle Name</div>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
@ -622,9 +597,9 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
|
||||
aria-label="manage employee"
|
||||
type="submit"
|
||||
className="btn btn-sm btn-primary"
|
||||
disabled={isloading}
|
||||
disabled={isPending}
|
||||
>
|
||||
{isloading
|
||||
{isPending
|
||||
? "Please Wait..."
|
||||
: employeeId
|
||||
? "Update"
|
||||
@ -634,8 +609,8 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
|
||||
<button
|
||||
aria-label="manage employee"
|
||||
type="reset"
|
||||
className="btn btn-sm btn-primary ms-2"
|
||||
disabled={isloading}
|
||||
className="btn btn-sm btn-secondary ms-2"
|
||||
disabled={isPending}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import useMaster from "../../hooks/masterHook/useMaster";
|
||||
import React, { useEffect, useRef, useState, useMemo } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { RolesRepository } from "../../repositories/MastersRepository";
|
||||
import { useEmployeeRoles } from "../../hooks/useEmployees";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
import { useEmployeeRoles, useUpdateEmployeeRoles } from "../../hooks/useEmployees";
|
||||
import useMaster from "../../hooks/masterHook/useMaster";
|
||||
import { changeMaster } from "../../slices/localVariablesSlice";
|
||||
import { RolesRepository } from "../../repositories/MastersRepository";
|
||||
import showToast from "../../services/toastService";
|
||||
|
||||
const formSchema = z.object({
|
||||
@ -15,44 +16,17 @@ const formSchema = z.object({
|
||||
|
||||
const ManageRole = ( {employeeId, onClosed} ) =>
|
||||
{
|
||||
|
||||
const disptach = useDispatch();
|
||||
useEffect(()=>{
|
||||
disptach(changeMaster("Application Role"));
|
||||
},[disptach])
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { employeeRoles, loading } = useEmployeeRoles(employeeId);
|
||||
const { data, loading: roleLoading } = useMaster();
|
||||
const dispatch = useDispatch();
|
||||
const formStateRef = useRef({});
|
||||
|
||||
const buildDefaultRoles = () => {
|
||||
const defaults = {};
|
||||
data.forEach((role) => {
|
||||
const isRoleEnabled = employeeRoles?.some(
|
||||
(empRole) => empRole.roleId === role.id
|
||||
);
|
||||
defaults[role.id] = isRoleEnabled;
|
||||
});
|
||||
|
||||
return defaults;
|
||||
};
|
||||
|
||||
const [initialRoles, setInitialRoles] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
if (employeeRoles && data) {
|
||||
if (employeeRoles.length > 0) {
|
||||
const updatedRoles = buildDefaultRoles();
|
||||
setInitialRoles(updatedRoles);
|
||||
} else {
|
||||
setInitialRoles({});
|
||||
}
|
||||
} else {
|
||||
setInitialRoles({});
|
||||
}
|
||||
}, [employeeRoles, data]);
|
||||
|
||||
|
||||
dispatch(changeMaster("Application Role"));
|
||||
}, [dispatch]);
|
||||
|
||||
|
||||
const { employeeRoles = [], loading: empLoading } = useEmployeeRoles(employeeId);
|
||||
const { data: roles = [], loading: roleLoading } = useMaster();
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
@ -60,167 +34,123 @@ useEffect(()=>{
|
||||
reset,
|
||||
} = useForm({
|
||||
resolver: zodResolver(formSchema),
|
||||
|
||||
defaultValues: { selectedRole: {} },
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!data) return;
|
||||
|
||||
const updatedRoles = {};
|
||||
data.forEach((role) => {
|
||||
const isRoleEnabled = employeeRoles?.some(
|
||||
const {
|
||||
updateRoles,
|
||||
isPending : isLoading,
|
||||
isError,
|
||||
error,
|
||||
} = useUpdateEmployeeRoles({
|
||||
onClose: onClosed,
|
||||
resetForm: reset,
|
||||
});
|
||||
// Prepare default form values based on roles and current assignments
|
||||
const selectedRoleDefaults = useMemo(() => {
|
||||
const defaults = {};
|
||||
roles.forEach((role) => {
|
||||
const enabled = employeeRoles.some(
|
||||
(empRole) => empRole.roleId === role.id && empRole.isEnabled
|
||||
);
|
||||
updatedRoles[role.id] = isRoleEnabled || false;
|
||||
defaults[role.id] = enabled;
|
||||
});
|
||||
|
||||
setInitialRoles(updatedRoles);
|
||||
|
||||
reset({
|
||||
selectedRole: updatedRoles,
|
||||
});
|
||||
}, [employeeRoles, data, reset]);
|
||||
|
||||
// const onSubmit = (formdata) => {
|
||||
// setIsLoading(true);
|
||||
// const result = [];
|
||||
|
||||
// const selectedRoles = formdata.selectedRole;
|
||||
|
||||
// for (const [roleId, isChecked] of Object.entries(selectedRoles)) {
|
||||
// const existingRole = employeeRoles?.find((role) => role.roleId === roleId);
|
||||
// const wasChecked = !!existingRole?.isEnabled;
|
||||
|
||||
// // Only push if the checked status has changed
|
||||
// if (isChecked !== wasChecked) {
|
||||
// result.push({
|
||||
// id: existingRole?.id || "00000000-0000-0000-0000-000000000000",
|
||||
// employeeId,
|
||||
// roleId,
|
||||
// isEnabled: isChecked,
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (result.length === 0) {
|
||||
// showToast("No changes made", "info");
|
||||
// setIsLoading(false);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// console.log(result);
|
||||
|
||||
// RolesRepository.createEmployeeRoles(result)
|
||||
// .then(() => {
|
||||
// showToast("Roles updated successfully", "success");
|
||||
// setIsLoading(false);
|
||||
// reset();
|
||||
// onClosed();
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// console.error(err);
|
||||
// showToast(err.message, "error");
|
||||
// setIsLoading(false);
|
||||
// });
|
||||
// };
|
||||
|
||||
const onSubmit = (formdata) => {
|
||||
setIsLoading(true);
|
||||
const result = [];
|
||||
const selectedRoles = formdata.selectedRole;
|
||||
|
||||
for (const [roleId, isChecked] of Object.entries(selectedRoles)) {
|
||||
const existingRole = employeeRoles?.find((role) => role.roleId === roleId);
|
||||
const wasChecked = !!existingRole?.isEnabled;
|
||||
|
||||
// Only push if the checked status has changed
|
||||
return defaults;
|
||||
}, [roles, employeeRoles]);
|
||||
|
||||
// Avoid infinite loop by comparing previous form values
|
||||
useEffect(() => {
|
||||
const prev = JSON.stringify(formStateRef.current);
|
||||
const next = JSON.stringify(selectedRoleDefaults);
|
||||
|
||||
if (prev !== next) {
|
||||
formStateRef.current = selectedRoleDefaults;
|
||||
reset({ selectedRole: selectedRoleDefaults });
|
||||
}
|
||||
}, [selectedRoleDefaults, reset]);
|
||||
|
||||
const onSubmit = async (formData) => {
|
||||
// setIsLoading(true);
|
||||
|
||||
const updates = [];
|
||||
const selected = formData.selectedRole;
|
||||
|
||||
for (const [roleId, isChecked] of Object.entries(selected)) {
|
||||
const existing = employeeRoles.find((r) => r.roleId === roleId);
|
||||
const wasChecked = !!existing?.isEnabled;
|
||||
|
||||
if (isChecked !== wasChecked) {
|
||||
result.push({
|
||||
id: existingRole?.id || "00000000-0000-0000-0000-000000000000",
|
||||
updates.push({
|
||||
id: existing?.id || "00000000-0000-0000-0000-000000000000",
|
||||
employeeId,
|
||||
roleId,
|
||||
isEnabled: isChecked,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (result.length === 0) {
|
||||
|
||||
if (updates.length === 0) {
|
||||
showToast("No changes made", "info");
|
||||
setIsLoading(false);
|
||||
// setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(result);
|
||||
|
||||
RolesRepository.createEmployeeRoles(result)
|
||||
.then(() => {
|
||||
showToast("Roles updated successfully", "success");
|
||||
setIsLoading(false);
|
||||
reset();
|
||||
onClosed();
|
||||
})
|
||||
.catch((error) => {
|
||||
const message = error?.response?.data?.message || error?.message || "Error occured during api calling"
|
||||
showToast(message, "error");
|
||||
setIsLoading(false);
|
||||
});
|
||||
|
||||
// try {
|
||||
// await RolesRepository.createEmployeeRoles(updates);
|
||||
// showToast("Roles updated successfully", "success");
|
||||
// reset();
|
||||
// onClosed();
|
||||
// } catch (err) {
|
||||
// const message =
|
||||
// err?.response?.data?.message || err?.message || "Error occurred while updating roles";
|
||||
// showToast(message, "error");
|
||||
// } finally {
|
||||
// setIsLoading(false);
|
||||
// }
|
||||
updateRoles(updates);
|
||||
};
|
||||
|
||||
|
||||
const isLoadingData = roleLoading || empLoading;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`modal fade `}
|
||||
id="managerole-modal"
|
||||
tabIndex="-1"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div className="modal-dialog modal-simple modal-md d-flex align-items-center justify-content-center">
|
||||
<div className="modal-content ">
|
||||
<div className="modal-body">
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
></button>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="text-start my-0">
|
||||
<div className="text-start mb-3">
|
||||
<p className="lead">Select Roles</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="d-flex flex-wrap justify-content-between align-items-center pb-5 "
|
||||
style={{ maxHeight: "70vh", overflowY: "scroll" }}
|
||||
>
|
||||
{roleLoading && <p>Loading...</p>}
|
||||
{loading && <p>Loading...</p>}
|
||||
{data &&
|
||||
data.map((item) => (
|
||||
<div className="col-md-6 col-lg-4 mb-4" key={item.id}>
|
||||
{isLoadingData ? (
|
||||
<p>Loading...</p>
|
||||
) : (
|
||||
<div
|
||||
className="d-flex flex-wrap justify-content-between pb-4"
|
||||
style={{ maxHeight: "70vh", overflowY: "auto" }}
|
||||
>
|
||||
{roles.map((role) => (
|
||||
<div className="col-md-6 col-lg-4 mb-3" key={role.id}>
|
||||
<div className="form-check ms-2 text-start">
|
||||
<input
|
||||
className="form-check-input"
|
||||
type="checkbox"
|
||||
id={item.id}
|
||||
{...register(`selectedRole.${item.id}`)}
|
||||
|
||||
id={role.id}
|
||||
{...register(`selectedRole.${role.id}`)}
|
||||
/>
|
||||
<label
|
||||
className="form-check-label text-bold"
|
||||
htmlFor={item.id}
|
||||
>
|
||||
<small>{item.role || "--"}</small>
|
||||
<label className="form-check-label" htmlFor={role.id}>
|
||||
<small>{role.role || "--"}</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{errors.selected && (
|
||||
<div className="text-center">{errors.selected.message}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="col-12 text-center">
|
||||
<button type="submit" className="btn btn-sm btn-primary me-3">
|
||||
{isLoading ? "Please Wait" : "Submit"}
|
||||
{errors.selectedRole && (
|
||||
<div className="text-danger text-center">
|
||||
{errors.selectedRole.message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="text-center mt-3">
|
||||
<button type="submit" className="btn btn-sm btn-primary me-3" disabled={isLoading}>
|
||||
{isLoading ? "Please Wait..." : "Submit"}
|
||||
</button>
|
||||
<button
|
||||
type="reset"
|
||||
@ -232,11 +162,7 @@ useEffect(()=>{
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ManageRole;
|
||||
export default ManageRole;
|
||||
|
||||
@ -20,9 +20,9 @@ import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import { MANAGE_PROJECT } from "../../utils/constants";
|
||||
|
||||
const Header = () => {
|
||||
const { profile } = useProfile();
|
||||
const {profile} = useProfile();
|
||||
const location = useLocation();
|
||||
const dispatch = useDispatch(changeMaster("Job Role"));
|
||||
const dispatch = useDispatch();
|
||||
const { data, loading } = useMaster();
|
||||
const navigate = useNavigate();
|
||||
const HasManageProjectPermission = useHasUserPermission(MANAGE_PROJECT);
|
||||
@ -121,10 +121,10 @@ const Header = () => {
|
||||
[fetchData,projectNames,selectedProject]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
eventBus.on("assign_project_one", handler);
|
||||
return () => eventBus.off("assign_project_one", handler);
|
||||
}, [handler]);
|
||||
// useEffect(() => {
|
||||
// eventBus.on("assign_project_one", handler);
|
||||
// return () => eventBus.off("assign_project_one", handler);
|
||||
// }, [handler]);
|
||||
|
||||
const newProjectHandler = useCallback(
|
||||
async (msg) => {
|
||||
@ -140,10 +140,26 @@ const Header = () => {
|
||||
[HasManageProjectPermission,projectNames]
|
||||
);
|
||||
|
||||
// useEffect(() => {
|
||||
// eventBus.on("project", newProjectHandler);
|
||||
// return () => eventBus.off("project", newProjectHandler);
|
||||
// }, [handler]);
|
||||
|
||||
useDispatch( () =>
|
||||
{
|
||||
dispatch(changeMaster("Job Role"))
|
||||
},[])
|
||||
useEffect(() => {
|
||||
eventBus.on("project", newProjectHandler);
|
||||
return () => eventBus.off("project", newProjectHandler);
|
||||
}, [handler]);
|
||||
eventBus.on("assign_project_one", handler);
|
||||
eventBus.on("project", newProjectHandler);
|
||||
|
||||
return () => {
|
||||
eventBus.off("assign_project_one", handler);
|
||||
eventBus.off("project", newProjectHandler);
|
||||
};
|
||||
}, [handler, newProjectHandler]);
|
||||
|
||||
|
||||
return (
|
||||
<nav
|
||||
className="layout-navbar container-fluid mb-3 navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
|
||||
@ -167,7 +183,6 @@ const Header = () => {
|
||||
<>
|
||||
<i
|
||||
className="rounded-circle bx bx-building-house bx-sm-lg bx-md"
|
||||
|
||||
></i>
|
||||
<div className="btn-group">
|
||||
<button
|
||||
@ -215,6 +230,7 @@ const Header = () => {
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{isProjectPath && (<span className=" fs-5 align-items-center"><i className="rounded-circle bx bx-building-house bx-sm-lg bx-md me-2"></i>{displayText}</span>)}
|
||||
|
||||
<ul className="navbar-nav flex-row align-items-center ms-md-auto">
|
||||
<li className="nav-item dropdown-shortcuts navbar-dropdown dropdown me-2 me-xl-0">
|
||||
|
||||
@ -1,79 +1,49 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import moment from "moment";
|
||||
import { getProjectStatusName } from "../../utils/projectStatus";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import { MANAGE_PROJECT } from "../../utils/constants";
|
||||
import { cacheData, getCachedData } from "../../slices/apiDataManager";
|
||||
import {useProjectDetails, useUpdateProject} from "../../hooks/useProjects";
|
||||
import {useParams} from "react-router-dom";
|
||||
import {useHasUserPermission} from "../../hooks/useHasUserPermission";
|
||||
import {MANAGE_PROJECT} from "../../utils/constants";
|
||||
import GlobalModel from "../common/GlobalModel";
|
||||
import ManageProjectInfo from "./ManageProjectInfo";
|
||||
import ProjectRepository from "../../repositories/ProjectRepository";
|
||||
import showToast from "../../services/toastService";
|
||||
|
||||
const AboutProject = ({ data }) => {
|
||||
const [CurrentProject, setCurrentProject] = useState(data);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentProject(data);
|
||||
}, [data]);
|
||||
|
||||
const manageProject = useHasUserPermission(MANAGE_PROJECT);
|
||||
|
||||
const handleShow = () => setShowModal(true);
|
||||
const handleClose = () => setShowModal(false);
|
||||
const handleFormSubmit = (updatedProject, setLoading) => {
|
||||
if ( CurrentProject?.id )
|
||||
import {useQueryClient} from "@tanstack/react-query";
|
||||
const AboutProject = () =>
|
||||
{
|
||||
const [ IsOpenModal, setIsOpenModal ] = useState( false )
|
||||
const {mutate: UpdateProjectDetails, isPending} = useUpdateProject( {
|
||||
onSuccessCallback: () =>
|
||||
{
|
||||
|
||||
ProjectRepository.updateProject(CurrentProject.id, updatedProject)
|
||||
.then((response) => {
|
||||
const updatedProjectData = {
|
||||
...CurrentProject,
|
||||
...response.data,
|
||||
building: CurrentProject.building,
|
||||
};
|
||||
setCurrentProject(updatedProject);
|
||||
|
||||
cacheData(`projectinfo-${CurrentProject.id}`, updatedProjectData);
|
||||
const projects_list = getCachedData("projectslist");
|
||||
if (projects_list) {
|
||||
const updatedProjectsList = projects_list.map((project) =>
|
||||
project.id === CurrentProject.id
|
||||
? {
|
||||
...project,
|
||||
...response.data,
|
||||
// tenant:project.tenant
|
||||
}
|
||||
: project
|
||||
);
|
||||
|
||||
cacheData("projectslist", updatedProjectsList);
|
||||
}
|
||||
|
||||
showToast( "Project updated successfully.", "success" );
|
||||
setLoading(false);
|
||||
setShowModal(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
showToast(error.message, "error");
|
||||
});
|
||||
setIsOpenModal(false)
|
||||
}
|
||||
};
|
||||
} )
|
||||
const ClientQuery = useQueryClient()
|
||||
const {projectId} = useParams();
|
||||
const manageProject = useHasUserPermission(MANAGE_PROJECT);
|
||||
const {projects_Details, isLoading, error,refetch} = useProjectDetails( projectId )
|
||||
const handleFormSubmit = ( updatedProject ) =>
|
||||
{
|
||||
if ( projects_Details?.id )
|
||||
{
|
||||
UpdateProjectDetails({ projectId: projects_Details?.id,updatedData: updatedProject,
|
||||
} );
|
||||
refetch()
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`modal fade ${showModal ? "show" : ""}`}
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: showModal ? "block" : "none" }}
|
||||
aria-hidden={!showModal}
|
||||
>
|
||||
<ManageProjectInfo
|
||||
project={CurrentProject}
|
||||
handleSubmitForm={handleFormSubmit}
|
||||
onClose={handleClose}
|
||||
></ManageProjectInfo>
|
||||
</div>
|
||||
{data && (
|
||||
{IsOpenModal && (
|
||||
<GlobalModel isOpen={IsOpenModal} closeModal={()=>setIsOpenModal(false)}>
|
||||
<ManageProjectInfo
|
||||
project={projects_Details}
|
||||
handleSubmitForm={handleFormSubmit}
|
||||
onClose={() => setIsOpenModal( false )}
|
||||
isPending={isPending}
|
||||
/>
|
||||
</GlobalModel>
|
||||
)}
|
||||
{projects_Details && (
|
||||
<>
|
||||
<div className="card mb-6">
|
||||
<div className="card-header text-start">
|
||||
<h6 className="card-action-title mb-0">
|
||||
@ -87,19 +57,19 @@ const AboutProject = ({ data }) => {
|
||||
<li className="d-flex align-items-center mb-3">
|
||||
<i className="bx bx-cog"></i>
|
||||
<span className="fw-medium mx-2">Name:</span>{" "}
|
||||
<span>{data.name}</span>
|
||||
<span>{projects_Details.name}</span>
|
||||
</li>
|
||||
<li className="d-flex align-items-center mb-3">
|
||||
<i className="bx bx-fingerprint"></i>
|
||||
<span className="fw-medium mx-2">Nick Name:</span>{" "}
|
||||
<span> {data.shortName} </span>
|
||||
<span> {projects_Details.shortName} </span>
|
||||
</li>
|
||||
<li className="d-flex align-items-center mb-3">
|
||||
<i className="bx bx-check"></i>
|
||||
<span className="fw-medium mx-2">Start Date:</span>{" "}
|
||||
<span>
|
||||
{data.startDate
|
||||
? moment(data.startDate).format("DD-MMM-YYYY")
|
||||
{projects_Details.startDate
|
||||
? moment(projects_Details.startDate).format("DD-MMM-YYYY")
|
||||
: "N/A"}
|
||||
</span>
|
||||
</li>
|
||||
@ -107,31 +77,31 @@ const AboutProject = ({ data }) => {
|
||||
<i className="bx bx-stop-circle"></i>{" "}
|
||||
<span className="fw-medium mx-2">End Date:</span>{" "}
|
||||
<span>
|
||||
{data.endDate
|
||||
? moment(data.endDate).format("DD-MMM-YYYY")
|
||||
{projects_Details.endDate
|
||||
? moment(projects_Details.endDate).format("DD-MMM-YYYY")
|
||||
: "N/A"}
|
||||
</span>
|
||||
</li>
|
||||
<li className="d-flex align-items-center mb-3">
|
||||
<i className="bx bx-trophy"></i>
|
||||
<span className="fw-medium mx-2">Status:</span>{" "}
|
||||
<span>{getProjectStatusName(data.projectStatusId)}</span>
|
||||
<span>{projects_Details?.projectStatus?.status}</span>
|
||||
</li>
|
||||
<li className="d-flex align-items-center mb-3">
|
||||
<i className="bx bx-user"></i>
|
||||
<span className="fw-medium mx-2">Contact:</span>{" "}
|
||||
<span>{data.contactPerson}</span>
|
||||
<span>{projects_Details.contactPerson}</span>
|
||||
</li>
|
||||
<li className="d-flex flex-column align-items-start mb-3">
|
||||
<div className="d-flex align-items-center">
|
||||
<i className="bx bx-flag"></i>
|
||||
<span className="fw-medium mx-2">Address:</span>
|
||||
{data.projectAddress?.length <= 20 && (
|
||||
<span>{data.projectAddress}</span>
|
||||
{projects_Details.projectAddress?.length <= 20 && (
|
||||
<span>{projects_Details.projectAddress}</span>
|
||||
)}
|
||||
</div>
|
||||
{data.projectAddress?.length > 20 && (
|
||||
<div className="ms-4 text-start">{data.projectAddress}</div>
|
||||
{projects_Details.projectAddress?.length > 20 && (
|
||||
<div className="ms-4 text-start">{projects_Details.projectAddress}</div>
|
||||
)}
|
||||
</li>
|
||||
|
||||
@ -144,7 +114,7 @@ const AboutProject = ({ data }) => {
|
||||
}`}
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#edit-project-modal"
|
||||
onClick={handleShow}
|
||||
onClick={()=>setIsOpenModal(true)}
|
||||
>
|
||||
Modify Details
|
||||
</button>
|
||||
@ -152,10 +122,12 @@ const AboutProject = ({ data }) => {
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
{!data && <span>loading...</span>}
|
||||
{isLoading && <span>loading...</span>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -93,7 +93,7 @@ const AssignEmployeeTable = ({
|
||||
onChange={handleRoleSelect}
|
||||
>
|
||||
{/* <option value="">Select a Job Role</option> */}
|
||||
{jobRoles.map((role) => (
|
||||
{jobRoles?.map((role) => (
|
||||
<option key={role.id} value={role.id}>
|
||||
{role.name}
|
||||
</option>
|
||||
|
||||
@ -11,13 +11,12 @@ import { TasksRepository } from "../../repositories/ProjectRepository";
|
||||
import showToast from "../../services/toastService";
|
||||
import { useProjectDetails } from "../../hooks/useProjects";
|
||||
import eventBus from "../../services/eventBus";
|
||||
import { useCreateTask } from "../../hooks/useTasks";
|
||||
|
||||
const AssignTask = ({ assignData, onClose, setAssigned }) => {
|
||||
const maxPlanned =
|
||||
assignData?.workItem?.workItem?.plannedWork -
|
||||
assignData?.workItem?.workItem?.completedWork;
|
||||
|
||||
// Zod schema for form validation
|
||||
assignData?.workItem?.plannedWork -
|
||||
assignData?.workItem?.completedWork;
|
||||
const schema = z.object({
|
||||
selectedEmployees: z
|
||||
.array(z.string())
|
||||
@ -41,15 +40,16 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
|
||||
const [isHelpVisibleTarget, setIsHelpVisibleTarget] = useState(false);
|
||||
const helpPopupRefTarget = useRef(null);
|
||||
const [isHelpVisible, setIsHelpVisible] = useState(false);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const { mutate: assignTask, isPending: isSubmitting } = useCreateTask({
|
||||
onSuccessCallback: () => {
|
||||
closedModel();
|
||||
},
|
||||
});
|
||||
|
||||
// Refs for Bootstrap Popovers
|
||||
const infoRef = useRef(null);
|
||||
const infoRef1 = useRef(null);
|
||||
|
||||
// Initialize Bootstrap Popovers on component mount
|
||||
useEffect(() => {
|
||||
// Check if Bootstrap is available globally
|
||||
if (typeof bootstrap !== "undefined") {
|
||||
if (infoRef.current) {
|
||||
new bootstrap.Popover(infoRef.current, {
|
||||
@ -71,8 +71,7 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
|
||||
} else {
|
||||
console.warn("Bootstrap is not available. Popovers might not function.");
|
||||
}
|
||||
}, []); // Empty dependency array ensures this runs once on mount
|
||||
// Redux state and hooks
|
||||
}, []);
|
||||
const selectedProject = useSelector(
|
||||
(store) => store.localVariables.projectId
|
||||
);
|
||||
@ -82,14 +81,11 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
|
||||
recallEmployeeData,
|
||||
} = useEmployeesAllOrByProjectId(selectedProject, false);
|
||||
const dispatch = useDispatch();
|
||||
const { loading } = useMaster(); // Assuming this is for jobRoleData loading
|
||||
const jobRoleData = getCachedData("Job Role");
|
||||
const { loading } = useMaster();
|
||||
const {data:jobRoleData} = useMaster();
|
||||
|
||||
// Local component states
|
||||
const [selectedRole, setSelectedRole] = useState("all");
|
||||
const [displayedSelection, setDisplayedSelection] = useState(""); // This state is not updated in the provided code, consider if it's still needed or how it should be updated
|
||||
|
||||
// React Hook Form setup
|
||||
const [displayedSelection, setDisplayedSelection] = useState("");
|
||||
const {
|
||||
handleSubmit,
|
||||
control,
|
||||
@ -97,50 +93,43 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
|
||||
watch,
|
||||
formState: { errors },
|
||||
reset,
|
||||
trigger, // <--- IMPORTANT: Destructure 'trigger' here
|
||||
trigger,
|
||||
} = useForm({
|
||||
defaultValues: {
|
||||
selectedEmployees: [],
|
||||
description: "",
|
||||
plannedTask: "",
|
||||
},
|
||||
resolver: zodResolver(schema), // Integrate Zod schema with react-hook-form
|
||||
resolver: zodResolver(schema),
|
||||
});
|
||||
|
||||
// Handler for employee checkbox changes
|
||||
const handleCheckboxChange = (event, user) => {
|
||||
const isChecked = event.target.checked;
|
||||
let updatedSelectedEmployees = watch("selectedEmployees") || []; // Get current selected employees from form state
|
||||
let updatedSelectedEmployees = watch("selectedEmployees") || [];
|
||||
|
||||
if (isChecked) {
|
||||
// Add employee if checked and not already in the list
|
||||
if (!updatedSelectedEmployees.includes(user.id)) {
|
||||
updatedSelectedEmployees = [...updatedSelectedEmployees, user.id];
|
||||
}
|
||||
} else {
|
||||
// Remove employee if unchecked
|
||||
updatedSelectedEmployees = updatedSelectedEmployees.filter(
|
||||
updatedSelectedEmployees = updatedSelectedEmployees?.filter(
|
||||
(id) => id !== user.id
|
||||
);
|
||||
}
|
||||
// Update the form state with the new list of selected employees
|
||||
setValue("selectedEmployees", updatedSelectedEmployees);
|
||||
trigger("selectedEmployees"); // <--- IMPORTANT: Trigger validation here
|
||||
trigger("selectedEmployees");
|
||||
};
|
||||
|
||||
// Effect to dispatch action for Job Role master data
|
||||
useEffect(() => {
|
||||
dispatch(changeMaster("Job Role"));
|
||||
// Cleanup function to reset selected role when component unmounts or dispatch changes
|
||||
|
||||
return () => setSelectedRole("all");
|
||||
}, [dispatch]);
|
||||
|
||||
// Handler for role filter change
|
||||
const handleRoleChange = (event) => {
|
||||
setSelectedRole(event.target.value);
|
||||
};
|
||||
|
||||
// Filter employees based on selected role
|
||||
const filteredEmployees =
|
||||
selectedRole === "all"
|
||||
? employees
|
||||
@ -148,38 +137,21 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
|
||||
(emp) => String(emp.jobRoleId || "") === selectedRole
|
||||
);
|
||||
|
||||
// Form submission handler
|
||||
const onSubmit = async (data) => {
|
||||
const selectedEmployeeIds = data.selectedEmployees;
|
||||
setIsSubmitting(true);
|
||||
// Prepare taskTeam data (only IDs are needed for the backend based on previous context)
|
||||
const taskTeamWithDetails = selectedEmployeeIds
|
||||
.map((empId) => {
|
||||
return empId; // Return just the ID as per previous discussions
|
||||
})
|
||||
.filter(Boolean); // Ensure no nulls if employee not found (though unlikely with current logic)
|
||||
|
||||
// Format data for API call
|
||||
const onSubmit = (data) => {
|
||||
const selectedEmployeeIds = data.selectedEmployees;
|
||||
|
||||
const taskTeamWithDetails = selectedEmployeeIds?.map((empId) => empId)?.filter(Boolean);
|
||||
const formattedData = {
|
||||
taskTeam: taskTeamWithDetails,
|
||||
plannedTask: data.plannedTask,
|
||||
description: data.description,
|
||||
assignmentDate: new Date().toISOString(), // Current date/time
|
||||
workItemId: assignData?.workItem?.workItem.id,
|
||||
assignmentDate: new Date().toISOString(),
|
||||
workItemId: assignData?.workItem.id,
|
||||
};
|
||||
|
||||
try {
|
||||
await TasksRepository.assignTask(formattedData);
|
||||
setIsSubmitting( false );
|
||||
showToast("Task Assined Successfully.", "success");
|
||||
closedModel();
|
||||
} catch (error) {
|
||||
setIsSubmitting(false);
|
||||
showToast("Something went wrong. Please try again.", "error");
|
||||
}
|
||||
assignTask({payload:formattedData,workAreaId:assignData?.workArea?.id});
|
||||
};
|
||||
|
||||
// Handler to close the modal and reset form
|
||||
const closedModel = () => {
|
||||
reset();
|
||||
onClose();
|
||||
@ -193,10 +165,10 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
|
||||
<span className="text-dark text-start d-flex align-items-center flex-wrap form-text">
|
||||
<span className="me-2 m-0 font-bold">Work Location :</span>
|
||||
{[
|
||||
assignData?.building?.name,
|
||||
assignData?.building?.buildingName,
|
||||
assignData?.floor?.floorName,
|
||||
assignData?.workArea?.areaName,
|
||||
assignData?.workItem?.workItem?.activityMaster?.activityName,
|
||||
assignData?.workItem?.activityMaster?.activityName,
|
||||
]
|
||||
.filter(Boolean) // Filter out any undefined/null values
|
||||
.map((item, index, array) => (
|
||||
@ -359,7 +331,7 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
|
||||
"selectedEmployees",
|
||||
updatedSelected
|
||||
);
|
||||
trigger("selectedEmployees"); // <--- IMPORTANT: Trigger validation on removing badge
|
||||
trigger("selectedEmployees");
|
||||
}}
|
||||
>
|
||||
<i className="icon-base bx bx-x icon-md "></i>
|
||||
@ -376,11 +348,10 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
|
||||
{!loading && errors.selectedEmployees && (
|
||||
<div className="danger-text mt-1">
|
||||
<p>{errors.selectedEmployees.message}</p>{" "}
|
||||
{/* Use message from Zod schema */}
|
||||
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Pending Task of Activity section */}
|
||||
<div className="col-md text-start mx-0 px-0">
|
||||
<div className="form-check form-check-inline mt-3 px-1">
|
||||
<label
|
||||
@ -393,12 +364,12 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
|
||||
htmlFor="inlineCheckbox1"
|
||||
>
|
||||
<strong>
|
||||
{assignData?.workItem?.workItem?.plannedWork -
|
||||
assignData?.workItem?.workItem?.completedWork}
|
||||
{assignData?.workItem?.plannedWork -
|
||||
assignData?.workItem?.completedWork}
|
||||
</strong>{" "}
|
||||
<u>
|
||||
{
|
||||
assignData?.workItem?.workItem?.activityMaster
|
||||
assignData?.workItem?.activityMaster
|
||||
?.unitOfMeasurement
|
||||
}
|
||||
</u>
|
||||
|
||||
@ -4,7 +4,8 @@ const Building = ({
|
||||
toggleBuilding,
|
||||
expandedBuildings,
|
||||
getContent,
|
||||
}) => {
|
||||
} ) =>
|
||||
{
|
||||
return (
|
||||
<React.Fragment key={building.id}>
|
||||
<tr className="overflow-auto">
|
||||
@ -20,7 +21,7 @@ const Building = ({
|
||||
>
|
||||
<div className="row table-responsive">
|
||||
<h6 style={{ marginBottom: "0px", fontSize: "14px" }}>
|
||||
{building.name}
|
||||
{building.buildingName}
|
||||
{expandedBuildings.includes(building.id) ? (
|
||||
<i className="bx bx-chevron-down"></i>
|
||||
) : (
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useEffect, useMemo } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import ProjectRepository from "../../../repositories/ProjectRepository";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useProjectDetails } from "../../../hooks/useProjects";
|
||||
import { getCachedData } from "../../../slices/apiDataManager";
|
||||
import showToast from "../../../services/toastService";
|
||||
import { useManageProjectInfra } from "../../../hooks/useProjects";
|
||||
import useSelect from "../../common/useSelect";
|
||||
|
||||
// Zod validation schema
|
||||
const buildingSchema = z.object({
|
||||
Id: z.string().optional(),
|
||||
name: z.string().min(1, "Building name is required"),
|
||||
@ -18,194 +17,165 @@ const buildingSchema = z.object({
|
||||
.max(160, "Description cannot exceed 160 characters"),
|
||||
});
|
||||
|
||||
const BuildingModel = ({
|
||||
project,
|
||||
onClose,
|
||||
onSubmit,
|
||||
clearTrigger,
|
||||
onClearComplete,
|
||||
editingBuilding = null,
|
||||
}) => {
|
||||
const BuildingModel = ({ project, onClose, editingBuilding = null }) => {
|
||||
const selectedProject = useSelector(
|
||||
(store) => store.localVariables.projectId
|
||||
);
|
||||
const [buildings, setBuildings] = useState([]);
|
||||
const projects_Details = getCachedData("projectInfo");
|
||||
const [formData, setFormData] = useState({
|
||||
id: "",
|
||||
name: "",
|
||||
description: "",
|
||||
projectId: project?.id,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (clearTrigger) {
|
||||
setFormData({ id: null, name: "", description: "", projectId: project.id });
|
||||
onClearComplete();
|
||||
} else if (editingBuilding) {
|
||||
setFormData({ ...editingBuilding, projectId: project.id });
|
||||
}
|
||||
|
||||
return () => {
|
||||
setValue("name", "");
|
||||
};
|
||||
}, [clearTrigger, onClearComplete, editingBuilding, project?.id]);
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
setValue,
|
||||
watch,
|
||||
reset,
|
||||
getValues,
|
||||
} = useForm({
|
||||
resolver: zodResolver(buildingSchema),
|
||||
defaultValues: formData, // Set default values from formData state
|
||||
});
|
||||
|
||||
const handleBuildingChange = (e) => {
|
||||
const selectedBuilding = project.buildings.find(
|
||||
(b) => b.id === +e.target.value
|
||||
);
|
||||
if (selectedBuilding) {
|
||||
setFormData({ ...selectedBuilding, projectId: project.id });
|
||||
setValue("name", selectedBuilding.name); // Update name field
|
||||
setValue("description", selectedBuilding.description); // Update description field
|
||||
} else {
|
||||
setFormData({ id: null, name: "", description: "", projectId: project.id });
|
||||
setValue("name", "");
|
||||
setValue("description", "");
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmitHandler = async (data) => {
|
||||
if (String(data.Id) === "0") {
|
||||
data.Id = null;
|
||||
}
|
||||
onSubmit({ ...data, projectId: project.id });
|
||||
reset({
|
||||
defaultValues: {
|
||||
Id: "0",
|
||||
name: "",
|
||||
description: "",
|
||||
});
|
||||
if (data.Id !== null) {
|
||||
showToast("Building updated successfully.", "success");
|
||||
} else {
|
||||
showToast("Building created successfully.", "success");
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
const watchedId = watch("Id");
|
||||
|
||||
const { mutate: ManageBuilding, isPending } = useManageProjectInfra({
|
||||
onSuccessCallback: (data, variables) => {
|
||||
showToast(
|
||||
watchedId != "0"
|
||||
? "Building updated Successfully"
|
||||
: "Building created Successfully",
|
||||
"success"
|
||||
);
|
||||
reset({ Id: "0", name: "", description: "" });
|
||||
// onClose?.();
|
||||
},
|
||||
});
|
||||
|
||||
const sortedBuildings = useMemo(() => {
|
||||
return (project || [])
|
||||
.filter((b) => b?.buildingName)
|
||||
.sort((a, b) => a.buildingName.localeCompare(b?.buildingName));
|
||||
}, [project]);
|
||||
|
||||
useEffect(() => {
|
||||
if(projects_Details){
|
||||
setBuildings(projects_Details.data?.buildings);
|
||||
if (!watchedId || watchedId === "0") {
|
||||
setValue("name", "");
|
||||
setValue("description", "");
|
||||
} else {
|
||||
const selected = sortedBuildings.find((b) => String(b.id) === watchedId);
|
||||
if (selected) {
|
||||
setValue("name", selected.buildingName || "");
|
||||
setValue("description", selected.description || "");
|
||||
}
|
||||
}
|
||||
}, [projects_Details]);
|
||||
}, [watchedId, sortedBuildings, setValue]);
|
||||
|
||||
useEffect(() => {
|
||||
if (editingBuilding) {
|
||||
reset({
|
||||
Id: String(editingBuilding.id),
|
||||
name: editingBuilding.name,
|
||||
description: editingBuilding.description,
|
||||
});
|
||||
}
|
||||
}, [editingBuilding]);
|
||||
|
||||
const onSubmitHandler = (data) => {
|
||||
const payload = {
|
||||
...data,
|
||||
Id: data.Id === "0" ? null : data.Id,
|
||||
projectId: selectedProject,
|
||||
};
|
||||
|
||||
let infraObject = [
|
||||
{
|
||||
building: payload,
|
||||
floor: null,
|
||||
workArea: null,
|
||||
},
|
||||
];
|
||||
ManageBuilding({ infraObject, projectId: selectedProject });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="modal-dialog modal-lg modal-simple modal-edit-user">
|
||||
<div className="modal-content">
|
||||
<div className="modal-body">
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
onClick={() => {
|
||||
onClose();
|
||||
reset(); // Call reset here
|
||||
}}
|
||||
></button>
|
||||
<h5 className="text-center mb-2">
|
||||
Manage Buildings - {project?.name}
|
||||
</h5>
|
||||
<form onSubmit={handleSubmit(onSubmitHandler)} className="row g-2">
|
||||
<div className="col-12">
|
||||
<label className="form-label">Select Building</label>
|
||||
<select
|
||||
{...register("Id")}
|
||||
className="select2 form-select form-select-sm"
|
||||
onChange={(e) => {
|
||||
handleBuildingChange(e);
|
||||
}}
|
||||
>
|
||||
<option value="0">Add New Building</option>
|
||||
|
||||
{project?.buildings?.length > 0 ? (
|
||||
project.buildings
|
||||
.filter((building) => building?.name)
|
||||
.sort((a, b) => {
|
||||
const nameA = a.name || "";
|
||||
const nameB = b.name || "";
|
||||
return nameA?.localeCompare(nameB);
|
||||
})
|
||||
.map((building) => (
|
||||
<option key={building.id} value={building.id}>
|
||||
{building.name}
|
||||
</option>
|
||||
))
|
||||
) : (
|
||||
<option disabled>No buildings found</option>
|
||||
)}
|
||||
</select>
|
||||
{errors.Id && (
|
||||
<span className="danger-text">{errors.Id.message}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-12">
|
||||
<label className="form-label">
|
||||
{formData.id ? "Rename Building Name" : "New Building Name"}
|
||||
</label>
|
||||
<input
|
||||
{...register("name")}
|
||||
type="text"
|
||||
className="form-control form-control-sm"
|
||||
/>
|
||||
{errors.name && (
|
||||
<span className="danger-text">{errors.name.message}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-12">
|
||||
<label className="form-label">Description</label>
|
||||
<textarea
|
||||
{...register("description")}
|
||||
maxLength="160"
|
||||
rows="5"
|
||||
className="form-control form-control-sm"
|
||||
/>
|
||||
{errors.description && (
|
||||
<span className="danger-text">
|
||||
{errors.description.message}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-12 text-center">
|
||||
<button type="submit" className="btn btn-sm btn-primary me-3">
|
||||
{formData.id && getValues("name")
|
||||
? "Edit Building"
|
||||
: "Add Building"}
|
||||
</button>
|
||||
<button
|
||||
type="reset"
|
||||
className="btn btn-sm btn-label-secondary"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
onClick={() => {
|
||||
onClose();
|
||||
reset(); // Call reset here
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<form onSubmit={handleSubmit(onSubmitHandler)} className="row g-2">
|
||||
<h5 className="text-center mb-2">Manage Buildings </h5>
|
||||
<div className="col-12">
|
||||
<label className="form-label">Select Building</label>
|
||||
<select
|
||||
{...register("Id")}
|
||||
className="select2 form-select form-select-sm"
|
||||
>
|
||||
<option value="0">Add New Building</option>
|
||||
{sortedBuildings.length > 0 ? (
|
||||
sortedBuildings.map((b) => (
|
||||
<option key={b.id} value={b.id}>
|
||||
{b.buildingName}
|
||||
</option>
|
||||
))
|
||||
) : (
|
||||
<option disabled>No buildings found</option>
|
||||
)}
|
||||
</select>
|
||||
{errors.Id && <span className="danger-text">{errors.Id.message}</span>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Name */}
|
||||
<div className="col-12">
|
||||
<label className="form-label">
|
||||
{watchedId !== "0" ? "Rename Building Name" : "New Building Name"}
|
||||
</label>
|
||||
<input
|
||||
{...register("name")}
|
||||
type="text"
|
||||
className="form-control form-control-sm"
|
||||
/>
|
||||
{errors.name && (
|
||||
<span className="danger-text">{errors.name.message}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<div className="col-12">
|
||||
<label className="form-label">Description</label>
|
||||
<textarea
|
||||
{...register("description")}
|
||||
rows="5"
|
||||
maxLength="160"
|
||||
className="form-control form-control-sm"
|
||||
/>
|
||||
{errors.description && (
|
||||
<span className="danger-text">{errors.description.message}</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-12 text-center">
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-sm btn-primary me-3"
|
||||
disabled={isPending}
|
||||
>
|
||||
{isPending
|
||||
? "Please wait..."
|
||||
: watchedId !== "0"
|
||||
? "Edit Building"
|
||||
: "Add Building"}
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="reset"
|
||||
className="btn btn-sm btn-label-secondary"
|
||||
disabled={isPending}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
reset();
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default BuildingModel;
|
||||
|
||||
|
||||
|
||||
@ -1,17 +1,15 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useActivitiesMaster, useWorkCategoriesMaster } from "../../../hooks/masterHook/useMaster";
|
||||
import { useProjectDetails } from "../../../hooks/useProjects";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import ProjectRepository from "../../../repositories/ProjectRepository";
|
||||
|
||||
import {
|
||||
cacheData,
|
||||
clearCacheKey,
|
||||
getCachedData,
|
||||
} from "../../../slices/apiDataManager";
|
||||
useActivitiesMaster,
|
||||
useWorkCategoriesMaster,
|
||||
} from "../../../hooks/masterHook/useMaster";
|
||||
import { useManageTask } from "../../../hooks/useProjects";
|
||||
import { cacheData, getCachedData } from "../../../slices/apiDataManager";
|
||||
import { refreshData } from "../../../slices/localVariablesSlice";
|
||||
import showToast from "../../../services/toastService";
|
||||
|
||||
@ -20,18 +18,13 @@ const taskSchema = z
|
||||
activityID: z.string().min(1, "Activity is required"),
|
||||
workCategoryId: z.string().min(1, "Work Category is required"),
|
||||
plannedWork: z.number().min(1, "Planned Work must be greater than 0"),
|
||||
completedWork: z.number().min( 0, "Completed Work must be greater than 0" ),
|
||||
comment:z.string()
|
||||
completedWork: z.number().min(0, "Completed Work must be ≥ 0"),
|
||||
comment: z.string(),
|
||||
})
|
||||
.refine(
|
||||
(data) =>
|
||||
data.completedWork === undefined ||
|
||||
data.completedWork <= data.plannedWork,
|
||||
{
|
||||
message: "Completed Work cannot be greater than Planned Work",
|
||||
path: ["completedWork"], // error will show next to this field
|
||||
}
|
||||
);
|
||||
.refine((data) => data.completedWork <= data.plannedWork, {
|
||||
message: "Completed Work cannot be greater than Planned Work",
|
||||
path: ["completedWork"],
|
||||
});
|
||||
|
||||
const EditActivityModal = ({
|
||||
workItem,
|
||||
@ -39,29 +32,12 @@ const EditActivityModal = ({
|
||||
building,
|
||||
floor,
|
||||
onClose,
|
||||
}) => {
|
||||
const selectedProject = useSelector(
|
||||
(store) => store.localVariables.projectId
|
||||
);
|
||||
const defaultModel = {
|
||||
activityID: 0,
|
||||
workCategoryId: 0,
|
||||
plannedWork: 0,
|
||||
completedWork: 0,
|
||||
comment:""
|
||||
};
|
||||
|
||||
const { projects_Details, refetch } = useProjectDetails(selectedProject);
|
||||
const [ActivityUnit, setActivityUnit] = useState("");
|
||||
const { activities, loading, error } = useActivitiesMaster();
|
||||
const { categories, categoryLoading, categoryError } =
|
||||
useWorkCategoriesMaster();
|
||||
const [formData, setFormData] = useState(defaultModel);
|
||||
} ) =>
|
||||
{
|
||||
|
||||
const { activities, loading: loadingActivities } = useActivitiesMaster();
|
||||
const { categories, loading: loadingCategories } = useWorkCategoriesMaster();
|
||||
const [selectedActivity, setSelectedActivity] = useState(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [activityData, setActivityData] = useState([]);
|
||||
const [categoryData, setCategoryData] = useState([]);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const {
|
||||
register,
|
||||
@ -73,359 +49,214 @@ const EditActivityModal = ({
|
||||
watch,
|
||||
} = useForm({
|
||||
resolver: zodResolver(taskSchema),
|
||||
defaultValues: defaultModel,
|
||||
defaultValues: {
|
||||
activityID: "",
|
||||
workCategoryId: "",
|
||||
plannedWork: 0,
|
||||
completedWork: 0,
|
||||
comment: "",
|
||||
},
|
||||
});
|
||||
|
||||
const handleActivityChange = (e) => {
|
||||
const selectedId = String(e.target.value);
|
||||
const selected = activityData.find((a) => a.id === selectedId);
|
||||
setSelectedActivity(selected || null);
|
||||
setValue("activityID", selectedId);
|
||||
};
|
||||
|
||||
const handleCategoryChange = (e) => {
|
||||
const selectedId = String(e.target.value);
|
||||
const category = categoryData.find((b) => b.id === selectedId);
|
||||
setSelectedCategory(category || null);
|
||||
setValue("workCategoryId", selectedId);
|
||||
};
|
||||
|
||||
const onSubmitForm = async ( data ) =>
|
||||
const { mutate: UpdateTask, isPending } = useManageTask({
|
||||
onSuccessCallback: (response) =>
|
||||
{
|
||||
setIsSubmitting(true)
|
||||
const updatedProject = { ...projects_Details };
|
||||
const finalData = {
|
||||
showToast( response?.message, "success" )
|
||||
onClose()
|
||||
}
|
||||
} );
|
||||
|
||||
|
||||
|
||||
const activityID = watch("activityID");
|
||||
|
||||
const sortedActivities = useMemo(
|
||||
() =>
|
||||
[...(activities || [])].sort((a, b) =>
|
||||
a.activityName?.localeCompare(b.activityName)
|
||||
),
|
||||
[activities]
|
||||
);
|
||||
|
||||
const sortedCategories = useMemo(
|
||||
() => [...(categories || [])].sort((a, b) => a.name?.localeCompare(b.name)),
|
||||
[categories]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!workItem) return;
|
||||
console.log(workItem)
|
||||
reset({
|
||||
activityID: String(
|
||||
workItem?.workItem?.activityId || workItem?.activityMaster?.id
|
||||
),
|
||||
workCategoryId: String(
|
||||
workItem?.workItem?.workCategoryId || workItem?.workCategoryMaster?.id
|
||||
),
|
||||
plannedWork:
|
||||
workItem?.workItem?.plannedWork || workItem?.plannedWork || 0,
|
||||
completedWork:
|
||||
workItem?.workItem?.completedWork || workItem?.completedWork || 0,
|
||||
comment: workItem?.workItem?.description || workItem?.description || "",
|
||||
});
|
||||
}, [workItem?.id,selectedActivity]);
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const selected = activities?.find((a) => a.id === activityID);
|
||||
setSelectedActivity( selected || null );
|
||||
}, [activityID, activities]);
|
||||
|
||||
const onSubmitForm = (data) =>
|
||||
{
|
||||
const payload = {
|
||||
...data,
|
||||
id: workItem?.workItem?.id ?? workItem?.id,
|
||||
buildingID: building?.id,
|
||||
floorId: floor?.id,
|
||||
workAreaId: workArea?.id,
|
||||
};
|
||||
|
||||
ProjectRepository.manageProjectTasks([finalData])
|
||||
.then((response) => {
|
||||
if (response?.data[0]) {
|
||||
const { workItemId, workItem } = response.data[0];
|
||||
|
||||
let finalUpdatedWorkItem = null;
|
||||
const newProject = {
|
||||
...updatedProject,
|
||||
buildings: updatedProject.buildings.map((building) =>
|
||||
building.id === finalData.buildingID
|
||||
? {
|
||||
...building,
|
||||
floors: building.floors.map((floor) =>
|
||||
floor.id === finalData.floorId
|
||||
? {
|
||||
...floor,
|
||||
workAreas: floor.workAreas.map((workArea) =>
|
||||
workArea.id === workItem?.workAreaId
|
||||
? {
|
||||
...workArea,
|
||||
workItems: (() => {
|
||||
const exists = workArea.workItems.some(
|
||||
(item) =>
|
||||
String(
|
||||
item?.workItem?.id ?? item?.id
|
||||
) === String(finalData.id)
|
||||
);
|
||||
|
||||
finalUpdatedWorkItem = workItem;
|
||||
|
||||
return exists
|
||||
? workArea.workItems.map((item) =>
|
||||
String(
|
||||
item?.workItem?.id ?? item?.id
|
||||
) === String(finalData.id)
|
||||
? workItem
|
||||
: item
|
||||
)
|
||||
: [...workArea.workItems, workItem];
|
||||
})(),
|
||||
}
|
||||
: workArea
|
||||
),
|
||||
}
|
||||
: floor
|
||||
),
|
||||
}
|
||||
: building
|
||||
),
|
||||
};
|
||||
cacheData("projectInfo", {
|
||||
projectId: newProject.id,
|
||||
data: newProject,
|
||||
});
|
||||
resetForm();
|
||||
dispatch( refreshData( true ) );
|
||||
setIsSubmitting(false)
|
||||
showToast("Activity Updated Successfully","success")
|
||||
onClose();
|
||||
}
|
||||
})
|
||||
.catch( ( error ) =>
|
||||
{
|
||||
setIsSubmitting(false)
|
||||
const message = error.response.data.message || error.message || "Error Occured During Api Call"
|
||||
showToast( message, "error" );
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
setFormData(defaultModel);
|
||||
setSelectedActivity(null);
|
||||
reset(defaultModel);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
reset({
|
||||
activityID: workItem?.workItem?.activityId || workItem?.activityId || 0,
|
||||
workCategoryId:
|
||||
workItem?.workItem?.workCategoryId || workItem?.workCategoryId || 0,
|
||||
plannedWork:
|
||||
workItem?.workItem?.plannedWork || workItem?.plannedWork || 0,
|
||||
completedWork:
|
||||
workItem?.workItem?.completedWork || workItem?.completedWork || 0,
|
||||
comment:
|
||||
workItem?.workItem?.description || workItem?.description || ""
|
||||
});
|
||||
return () => reset();
|
||||
}, [activities, workItem]);
|
||||
|
||||
const ISselectedActivity = watch("activityID");
|
||||
useEffect(() => {
|
||||
if (ISselectedActivity) {
|
||||
const selected = activities.find((a) => a.id === ISselectedActivity);
|
||||
setSelectedActivity(selected || null);
|
||||
setActivityUnit(selected?.unitOfMeasurement);
|
||||
}
|
||||
}, [ISselectedActivity, activities]);
|
||||
|
||||
UpdateTask([payload])
|
||||
}
|
||||
return (
|
||||
<div className="modal-dialog modal-lg modal-simple modal-edit-user">
|
||||
<div className="modal-content">
|
||||
<div className="modal-body">
|
||||
<div className="row">
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
aria-label="Close"
|
||||
onClick={onClose}
|
||||
/>
|
||||
<div className="text-center mb-1">
|
||||
<h5 className="mb-1">Manage Task</h5>
|
||||
</div>
|
||||
<form className="row g-2" onSubmit={handleSubmit(onSubmitForm)}>
|
||||
{/* Select Building */}
|
||||
<div className="col-6 col-md-6">
|
||||
<label className="form-label" htmlFor="buildingID">
|
||||
Select Building
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control form-control-sm"
|
||||
value={building?.name}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<form className="row g-2 p-2 p-md-1" onSubmit={handleSubmit(onSubmitForm)}>
|
||||
<div className="text-center mb-1">
|
||||
<h5 className="mb-1">Manage Task</h5>
|
||||
</div>
|
||||
|
||||
{/* Select Floor */}
|
||||
<div className="col-6 col-md-6">
|
||||
<label className="form-label" htmlFor="floorId">
|
||||
Select Floor
|
||||
</label>
|
||||
<div className="row g-2">
|
||||
<div className="col-12 col-md-6">
|
||||
<label className="form-label">Select Building</label>
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
value={building?.buildingName}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
className="form-control form-control-sm"
|
||||
value={floor?.floorName}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label" htmlFor="workAreaId">
|
||||
Select Work Area
|
||||
</label>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
className="form-control form-control-sm"
|
||||
value={workArea?.areaName}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Select Activity */}
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label" htmlFor="activityID">
|
||||
Select Activity
|
||||
</label>
|
||||
<select
|
||||
id="activityID"
|
||||
className="form-select form-select-sm"
|
||||
{...register("activityID")}
|
||||
>
|
||||
{loading ? (
|
||||
<option value="">Loading...</option>
|
||||
) : (
|
||||
<option disabled>Select Activity</option>
|
||||
)}
|
||||
{activities &&
|
||||
activities.length > 0 &&
|
||||
activities
|
||||
.slice()
|
||||
.sort((a, b) =>
|
||||
(a.activityName || "")?.localeCompare(
|
||||
b.activityName || ""
|
||||
)
|
||||
)
|
||||
.map((activity) => (
|
||||
<option key={activity.id} value={activity.id}>
|
||||
{activity.activityName}
|
||||
</option>
|
||||
))}
|
||||
{!loading && activities.length === 0 && (
|
||||
<option disabled>No activities available</option>
|
||||
)}
|
||||
</select>
|
||||
|
||||
{errors.activityID && (
|
||||
<p className="danger-text">{errors.activityID.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Select Category */}
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label" htmlFor="activityID">
|
||||
Select Work Category
|
||||
</label>
|
||||
<select
|
||||
id="workCategoryId"
|
||||
className="form-select form-select-sm"
|
||||
{...register("workCategoryId")}
|
||||
>
|
||||
{loading ? (
|
||||
<option value="">Loading...</option>
|
||||
) : (
|
||||
<option disabled>Select Category</option>
|
||||
)}
|
||||
{categories &&
|
||||
categories.length > 0 &&
|
||||
categories
|
||||
.slice()
|
||||
.sort((a, b) =>
|
||||
(a.name || "")?.localeCompare(
|
||||
b.name || ""
|
||||
)
|
||||
)
|
||||
.map((category) => (
|
||||
<option key={category.id} value={category.id}>
|
||||
{category.name}
|
||||
</option>
|
||||
))}
|
||||
{!loading && categories.length === 0 && (
|
||||
<option disabled>No categories available</option>
|
||||
)}
|
||||
</select>
|
||||
|
||||
{errors.workCategoryId && (
|
||||
<p className="danger-text">{errors.workCategoryId.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Planned Work */}
|
||||
{/* {ISselectedActivity && ( */}
|
||||
<div className="col-5 col-md-5">
|
||||
<label className="form-label" htmlFor="plannedWork">
|
||||
Planned Work
|
||||
</label>
|
||||
<input
|
||||
{...register("plannedWork", { valueAsNumber: true })}
|
||||
type="number"
|
||||
className="form-control form-control-sm me-2"
|
||||
placeholder="Planned Work"
|
||||
/>
|
||||
|
||||
{errors.plannedWork && (
|
||||
<p className="danger-text">{errors.plannedWork.message}</p>
|
||||
)}
|
||||
</div>
|
||||
{/* )} */}
|
||||
|
||||
{/* Completed Work */}
|
||||
{/* {ISselectedActivity && ( */}
|
||||
<div className="col-5 col-md-5">
|
||||
<label className="form-label" htmlFor="completedWork">
|
||||
Completed Work
|
||||
</label>
|
||||
<input
|
||||
{...register("completedWork", { valueAsNumber: true })}
|
||||
type="number"
|
||||
className="form-control form-control-sm me-2"
|
||||
placeholder="Completed Work"
|
||||
disabled={getValues("completedWork") > 0}
|
||||
/>
|
||||
{errors.completedWork && (
|
||||
<p className="danger-text">{errors.completedWork.message}</p>
|
||||
)}
|
||||
</div>
|
||||
{/* )} */}
|
||||
|
||||
{/* Unit */}
|
||||
{/* {ISselectedActivity && ( */}
|
||||
<div className="col-2 col-md-2">
|
||||
<label className="form-label" htmlFor="unit">
|
||||
Unit
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
disabled
|
||||
className="form-control form-control-sm me-2"
|
||||
value={selectedActivity?.unitOfMeasurement || ""}
|
||||
/>
|
||||
</div>
|
||||
{/* )} */}
|
||||
|
||||
|
||||
<div className="col-12">
|
||||
<label
|
||||
className="form-text fs-7 m-1 text-lg text-dark"
|
||||
htmlFor="descriptionTextarea"
|
||||
>
|
||||
Comment
|
||||
</label>
|
||||
<textarea
|
||||
{...register("comment")}
|
||||
className="form-control"
|
||||
id="descriptionTextarea"
|
||||
rows="2"
|
||||
/>
|
||||
{errors.comment && (
|
||||
<div className="danger-text">
|
||||
{errors.comment.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-12 text-center">
|
||||
<button type="submit" className="btn btn-sm btn-primary me-3" disabled={activities.length === 0 || isSubmitting}>
|
||||
{isSubmitting ? "Please Wait.." : "Edit Task"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-label-secondary"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div className="col-12 col-md-6">
|
||||
<label className="form-label">Select Floor</label>
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
value={floor?.floorName}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-12">
|
||||
<label className="form-label">Select Work Area</label>
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
value={workArea?.areaName}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-12">
|
||||
<label className="form-label">Select Activity</label>
|
||||
<select
|
||||
{...register("activityID")}
|
||||
className="form-select form-select-sm"
|
||||
>
|
||||
<option disabled>Select Activity</option>
|
||||
{loadingActivities ? (
|
||||
<option>Loading...</option>
|
||||
) : (
|
||||
sortedActivities.map((a) => (
|
||||
<option key={a.id} value={a.id}>
|
||||
{a.activityName}
|
||||
</option>
|
||||
))
|
||||
)}
|
||||
</select>
|
||||
{errors.activityID && (
|
||||
<p className="danger-text">{errors.activityID.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-12">
|
||||
<label className="form-label">Select Work Category</label>
|
||||
<select
|
||||
{...register("workCategoryId")}
|
||||
className="form-select form-select-sm"
|
||||
>
|
||||
<option disabled>Select Category</option>
|
||||
{loadingCategories ? (
|
||||
<option>Loading...</option>
|
||||
) : (
|
||||
sortedCategories.map((c) => (
|
||||
<option key={c.id} value={c.id}>
|
||||
{c.name}
|
||||
</option>
|
||||
))
|
||||
)}
|
||||
</select>
|
||||
{errors.workCategoryId && (
|
||||
<p className="danger-text">{errors.workCategoryId.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-5">
|
||||
<label className="form-label">Planned Work</label>
|
||||
<input
|
||||
{...register("plannedWork", { valueAsNumber: true })}
|
||||
type="number"
|
||||
className="form-control form-control-sm"
|
||||
/>
|
||||
{errors.plannedWork && (
|
||||
<p className="danger-text">{errors.plannedWork.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-5">
|
||||
<label className="form-label">Completed Work</label>
|
||||
<input
|
||||
{...register("completedWork", { valueAsNumber: true })}
|
||||
type="number"
|
||||
disabled={getValues("completedWork") > 0}
|
||||
className="form-control form-control-sm"
|
||||
/>
|
||||
{errors.completedWork && (
|
||||
<p className="danger-text">{errors.completedWork.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-2">
|
||||
<label className="form-label">Unit</label>
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
disabled
|
||||
value={selectedActivity?.unitOfMeasurement || ""}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-12">
|
||||
<label className="form-label">Comment</label>
|
||||
<textarea {...register("comment")} rows="2" className="form-control" />
|
||||
{errors.comment && (
|
||||
<div className="danger-text">{errors.comment.message}</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-12 text-center">
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-sm btn-primary me-2"
|
||||
disabled={isPending}
|
||||
>
|
||||
{isPending ? "Please Wait..." : "Edit Task"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-label-secondary"
|
||||
onClick={onClose}
|
||||
disabled={isPending}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -16,13 +16,7 @@ const Floor = ({ floor, workAreas, forBuilding }) => {
|
||||
<tr>
|
||||
<td colSpan="4" className="text-start table-cell">
|
||||
<div className="row ps-2">
|
||||
{/* <div className="col-1 col-md-1 d-flex justify-content-between align-items-center " >
|
||||
<button
|
||||
className="btn me-2"
|
||||
>
|
||||
</button>
|
||||
|
||||
</div> */}
|
||||
<div className="col-12 ps-8">
|
||||
<div className="row">
|
||||
<div className="d-flex col-5">
|
||||
|
||||
@ -1,236 +1,189 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import showToast from "../../../services/toastService";
|
||||
import { useManageProjectInfra } from "../../../hooks/useProjects";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
// Schema
|
||||
const floorSchema = z.object({
|
||||
buildingId: z
|
||||
.string()
|
||||
.refine((val) => val !== "0", {
|
||||
message: "Building is required",
|
||||
}),
|
||||
.refine((val) => val !== "0", { message: "Building is required" }),
|
||||
id: z.string().optional(),
|
||||
floorName: z.string().min(1, "Floor Name is required"),
|
||||
});
|
||||
|
||||
const defaultModel = {
|
||||
const defaultValues = {
|
||||
id: "0",
|
||||
floorName: "",
|
||||
buildingId: "0",
|
||||
};
|
||||
|
||||
const FloorModel = ({
|
||||
project,
|
||||
onClose,
|
||||
onSubmit,
|
||||
clearTrigger,
|
||||
onClearComplete,
|
||||
}) => {
|
||||
const [formData, setFormData] = useState(defaultModel);
|
||||
const [selectedBuilding, setSelectedBuilding] = useState({});
|
||||
const [buildings, setBuildings] = useState(project?.buildings || []);
|
||||
const FloorModel = ({ project, onClose, onSubmit }) => {
|
||||
const selectedProject = useSelector(
|
||||
(store) => store.localVariables.projectId
|
||||
);
|
||||
const [selectedBuilding, setSelectedBuilding] = useState(null);
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
reset,
|
||||
watch,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
defaultValues,
|
||||
resolver: zodResolver(floorSchema),
|
||||
defaultValues: defaultModel,
|
||||
});
|
||||
const watchId = watch("id");
|
||||
const watchBuildingId = watch("buildingId");
|
||||
const { mutate: ManageFloor, isPending } = useManageProjectInfra({
|
||||
onSuccessCallback: (data, variables) => {
|
||||
showToast(
|
||||
watchId != "0"
|
||||
? "Floor updated Successfully"
|
||||
: "Floor created Successfully",
|
||||
"success"
|
||||
);
|
||||
reset({ id: "0", floorName: ""});
|
||||
// onClose?.();
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (clearTrigger) {
|
||||
reset(defaultModel);
|
||||
onClearComplete();
|
||||
}
|
||||
}, [clearTrigger, onClearComplete, reset]);
|
||||
reset(defaultValues);
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const building = project?.find((b) => b.id === watchBuildingId);
|
||||
setSelectedBuilding(building || null);
|
||||
}, [watchBuildingId, project]);
|
||||
|
||||
const handleBuildigChange = (e) => {
|
||||
const buildingId = e.target.value;
|
||||
const building = buildings.find((b) => b.id === String(buildingId));
|
||||
|
||||
if (building) {
|
||||
setSelectedBuilding(building);
|
||||
setFormData({
|
||||
id: "",
|
||||
floorName: "",
|
||||
buildingId: building.id,
|
||||
});
|
||||
setValue("buildingId", building.id, { shouldValidate: true }); // ✅ trigger validation
|
||||
setValue("id", "0");
|
||||
} else {
|
||||
setSelectedBuilding({});
|
||||
setFormData({
|
||||
id: "",
|
||||
floorName: "",
|
||||
buildingId: "0",
|
||||
});
|
||||
setValue("buildingId", "0", { shouldValidate: true }); // ✅ trigger validation
|
||||
}
|
||||
const handleBuildingChange = (e) => {
|
||||
const id = e.target.value;
|
||||
setValue("buildingId", id, { shouldValidate: true });
|
||||
setValue("id", "0");
|
||||
setValue("floorName", "");
|
||||
};
|
||||
|
||||
const handleFloorChange = (e) => {
|
||||
const id = e.target.value;
|
||||
const floor = selectedBuilding.floors?.find((b) => b.id === String(id));
|
||||
setValue("id", id);
|
||||
|
||||
const floor = selectedBuilding?.floors?.find((f) => f.id === id);
|
||||
if (floor) {
|
||||
setFormData({
|
||||
id: floor.id,
|
||||
floorName: floor.floorName,
|
||||
buildingId: selectedBuilding.id,
|
||||
});
|
||||
setValue("floorName", floor.floorName);
|
||||
} else {
|
||||
setFormData({
|
||||
id: "0",
|
||||
floorName: "",
|
||||
buildingId: selectedBuilding.id,
|
||||
});
|
||||
setValue("floorName", "");
|
||||
}
|
||||
};
|
||||
|
||||
const onFormSubmit = (data) => {
|
||||
if (data.id === "0") {
|
||||
data.id = null;
|
||||
}
|
||||
const isEdit = data.id !== "0";
|
||||
const payload = {
|
||||
...data,
|
||||
id: isEdit ? data.id : null,
|
||||
};
|
||||
let infraObject = [
|
||||
{
|
||||
building: null,
|
||||
floor: payload,
|
||||
workArea: null,
|
||||
},
|
||||
];
|
||||
|
||||
onSubmit(data);
|
||||
reset({ floorName: "" });
|
||||
|
||||
if (data.id !== null) {
|
||||
showToast("Floor updated successfully.", "success");
|
||||
} else {
|
||||
showToast("Floor created successfully.", "success");
|
||||
}
|
||||
ManageFloor({ infraObject, projectId: selectedProject });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="modal-dialog modal-lg modal-simple modal-edit-user">
|
||||
<div className="modal-content">
|
||||
<div className="modal-body">
|
||||
<div className="row">
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
aria-label="Close"
|
||||
onClick={onClose}
|
||||
/>
|
||||
<div className="text-center mb-1">
|
||||
<h5 className="mb-1">Manage Floors - {project.name}</h5>
|
||||
</div>
|
||||
<form className="row g-2" onSubmit={handleSubmit(onFormSubmit)}>
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label" htmlFor="buildingId">
|
||||
Select Building
|
||||
</label>
|
||||
<select
|
||||
id="buildingId"
|
||||
className="select2 form-select form-select-sm"
|
||||
aria-label="Select Building"
|
||||
{...register("buildingId")}
|
||||
onChange={handleBuildigChange}
|
||||
>
|
||||
<option value="0">Select Building</option>
|
||||
{buildings?.length > 0 &&
|
||||
buildings
|
||||
.filter((building) => building?.name)
|
||||
.sort((a, b) =>
|
||||
(a.name || "")?.localeCompare(b.name || "")
|
||||
)
|
||||
.map((building) => (
|
||||
<option key={building.id} value={building.id}>
|
||||
{building.name}
|
||||
</option>
|
||||
))}
|
||||
{buildings?.length === 0 && (
|
||||
<option disabled>No buildings found</option>
|
||||
)}
|
||||
</select>
|
||||
{errors.buildingId && (
|
||||
<p className="text-danger">{errors.buildingId.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{formData.buildingId !== "0" && (
|
||||
<>
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label">Select Floor</label>
|
||||
<select
|
||||
id="id"
|
||||
className="select2 form-select form-select-sm"
|
||||
aria-label="Select Floor"
|
||||
{...register("id")}
|
||||
onChange={handleFloorChange}
|
||||
>
|
||||
<option value="0">Add New Floor</option>
|
||||
{selectedBuilding?.floors?.length > 0 &&
|
||||
[...selectedBuilding.floors]
|
||||
.filter((floor) => floor?.floorName)
|
||||
.sort((a, b) =>
|
||||
(a.floorName || "")?.localeCompare(
|
||||
b.floorName || ""
|
||||
)
|
||||
)
|
||||
.map((floor) => (
|
||||
<option key={floor.id} value={floor.id}>
|
||||
{floor.floorName}
|
||||
</option>
|
||||
))}
|
||||
{selectedBuilding?.floors?.length === 0 && (
|
||||
<option disabled>No floors found</option>
|
||||
)}
|
||||
</select>
|
||||
{errors.id && (
|
||||
<p className="text-danger">{errors.id.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label">
|
||||
{formData.id !== "0" ? "Modify " : "Enter "} Floor Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="floorName"
|
||||
className="form-control form-control-sm me-2"
|
||||
placeholder="Floor Name"
|
||||
{...register("floorName")}
|
||||
/>
|
||||
{errors.floorName && (
|
||||
<p className="text-danger">
|
||||
{errors.floorName.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="col-12 text-center">
|
||||
<button type="submit" className="btn btn-sm btn-primary me-3">
|
||||
{formData.id !== "0" && formData.id !== ""
|
||||
? "Edit Floor"
|
||||
: "Add Floor"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-label-secondary"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<form className="row g-2" onSubmit={handleSubmit(onFormSubmit)}>
|
||||
<div className="text-center mb-1">
|
||||
<h5 className="mb-1">Manage Floor</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12">
|
||||
<label className="form-label">Select Building</label>
|
||||
<select
|
||||
{...register("buildingId")}
|
||||
className="form-select form-select-sm"
|
||||
onChange={handleBuildingChange}
|
||||
>
|
||||
<option value="0">Select Building</option>
|
||||
{project?.length > 0 &&
|
||||
project
|
||||
.filter((b) => b.buildingName)
|
||||
.sort((a, b) => a.buildingName.localeCompare(b.buildingName))
|
||||
.map((b) => (
|
||||
<option key={b.id} value={b.id}>
|
||||
{b.buildingName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{errors.buildingId && (
|
||||
<p className="danger-text">{errors.buildingId.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{watchBuildingId !== "0" && (
|
||||
<>
|
||||
<div className="col-12">
|
||||
<label className="form-label">Select Floor</label>
|
||||
<select
|
||||
{...register("id")}
|
||||
className="form-select form-select-sm"
|
||||
onChange={handleFloorChange}
|
||||
>
|
||||
<option value="0">Add New Floor</option>
|
||||
{selectedBuilding?.floors?.length > 0 &&
|
||||
selectedBuilding.floors
|
||||
.filter((f) => f.floorName)
|
||||
.sort((a, b) => a.floorName.localeCompare(b.floorName))
|
||||
.map((f) => (
|
||||
<option key={f.id} value={f.id}>
|
||||
{f.floorName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="col-12">
|
||||
<label className="form-label">
|
||||
{watchId !== "0" ? "Edit Floor Name" : "New Floor Name"}
|
||||
</label>
|
||||
<input
|
||||
{...register("floorName")}
|
||||
className="form-control form-control-sm"
|
||||
placeholder="Floor Name"
|
||||
/>
|
||||
{errors.floorName && (
|
||||
<p className="danger-text">{errors.floorName.message}</p>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className="col-12 text-center">
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-sm btn-primary me-3"
|
||||
disabled={isPending}
|
||||
>
|
||||
{isPending
|
||||
? "Please Wait"
|
||||
: watchId !== "0"
|
||||
? "Edit Floor"
|
||||
: "Add Floor"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-label-secondary"
|
||||
disabled={isPending}
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
getCachedData,
|
||||
} from "../../../slices/apiDataManager";
|
||||
|
||||
const InfraTable = ({ buildings, projectId, signalRHandler }) => {
|
||||
const InfraTable = ({ buildings, projectId}) => {
|
||||
const [projectBuilding, setProjectBuilding] = useState([]);
|
||||
const [expandedBuildings, setExpandedBuildings] = useState([]);
|
||||
const [showFloorModal, setShowFloorModal] = useState(false);
|
||||
@ -100,14 +100,7 @@ const InfraTable = ({ buildings, projectId, signalRHandler }) => {
|
||||
No floors have been added yet. Start by adding floors to manage
|
||||
this building.
|
||||
</p>
|
||||
{/* <button
|
||||
type="button"
|
||||
className="btn btn-xs btn-primary"
|
||||
onClick={() => handleAddFloor(building)}
|
||||
>
|
||||
<i className="bx bx-plus-circle me-2"></i>
|
||||
Add Floors
|
||||
</button> */}
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -120,34 +113,34 @@ const InfraTable = ({ buildings, projectId, signalRHandler }) => {
|
||||
}
|
||||
}, [buildings]);
|
||||
|
||||
const handler = useCallback(
|
||||
(msg) => {
|
||||
if (msg.projectIds.some((item) => item == projectId)) {
|
||||
try {
|
||||
ProjectRepository.getProjectByprojectId(projectId)
|
||||
.then((response) => {
|
||||
cacheData("projectInfo", {
|
||||
projectId: projectId,
|
||||
data: response.data,
|
||||
});
|
||||
setProjectBuilding(response?.data?.buildings);
|
||||
signalRHandler?.(response?.data);
|
||||
showToast(msg.message, "info");
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
[buildings]
|
||||
);
|
||||
useEffect(() => {
|
||||
eventBus.on("infra", handler);
|
||||
return () => eventBus.off("infra", handler);
|
||||
}, [handler]);
|
||||
// const handler = useCallback(
|
||||
// (msg) => {
|
||||
// if (msg.projectIds.some((item) => item == projectId)) {
|
||||
// try {
|
||||
// ProjectRepository.getProjectByprojectId(projectId)
|
||||
// .then((response) => {
|
||||
// cacheData("projectInfo", {
|
||||
// projectId: projectId,
|
||||
// data: response.data,
|
||||
// });
|
||||
// setProjectBuilding(response?.data?.buildings);
|
||||
// signalRHandler?.(response?.data);
|
||||
// showToast(msg.message, "info");
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// console.error(error);
|
||||
// });
|
||||
// } catch (e) {
|
||||
// console.error(e);
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// [buildings]
|
||||
// );
|
||||
// useEffect(() => {
|
||||
// eventBus.on("infra", handler);
|
||||
// return () => eventBus.off("infra", handler);
|
||||
// }, [handler]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import {
|
||||
useActivitiesMaster,
|
||||
useWorkCategoriesMaster,
|
||||
} from "../../../hooks/masterHook/useMaster";
|
||||
import { useManageTask } from "../../../hooks/useProjects";
|
||||
import showToast from "../../../services/toastService";
|
||||
|
||||
const taskSchema = z.object({
|
||||
buildingID: z.string().min(1, "Building is required"),
|
||||
@ -14,433 +16,267 @@ const taskSchema = z.object({
|
||||
activityID: z.string().min(1, "Activity is required"),
|
||||
workCategoryId: z.string().min(1, "Work Category is required"),
|
||||
plannedWork: z.number().min(1, "Planned Work must be greater than 0"),
|
||||
completedWork: z.number().min( 0, "Completed Work must be greater than 0" ),
|
||||
comment:z.string(),
|
||||
completedWork: z.number().min(0, "Completed Work must be 0 or more"),
|
||||
comment: z.string(),
|
||||
});
|
||||
|
||||
const defaultModel = {
|
||||
id: null,
|
||||
buildingID: "", // Changed from "0"
|
||||
floorId: "", // Changed from "0"
|
||||
workAreaId: "", // Changed from "0"
|
||||
activityID: "", // Changed from null
|
||||
workCategoryId: "", // Kept as empty
|
||||
buildingID: "",
|
||||
floorId: "",
|
||||
workAreaId: "",
|
||||
activityID: "",
|
||||
workCategoryId: "",
|
||||
plannedWork: 0,
|
||||
completedWork: 0,
|
||||
comment: ""
|
||||
comment: "",
|
||||
};
|
||||
|
||||
|
||||
const TaskModel = ({
|
||||
project,
|
||||
onSubmit,
|
||||
clearTrigger,
|
||||
onClearComplete,
|
||||
onClose,
|
||||
}) => {
|
||||
const [formData, setFormData] = useState(defaultModel);
|
||||
const [selectedBuilding, setSelectedBuilding] = useState(null);
|
||||
const [selectedFloor, setSelectedFloor] = useState(null);
|
||||
const [selectedWorkArea, setSelectedWorkArea] = useState(null);
|
||||
const [selectedActivity, setSelectedActivity] = useState(null);
|
||||
const [selectedCategory, setSelectedCategory] = useState(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [activityData, setActivityData] = useState([]);
|
||||
const [categoryData, setCategoryData] = useState([]);
|
||||
const { activities, loading, error } = useActivitiesMaster();
|
||||
const { categories, categoryLoading, categoryError } =
|
||||
useWorkCategoriesMaster();
|
||||
const TaskModel = ({ project, onSubmit, onClose }) => {
|
||||
const { activities, loading: activityLoading } = useActivitiesMaster();
|
||||
const { categories, categoryLoading } = useWorkCategoriesMaster();
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
reset,
|
||||
watch,
|
||||
setValue,
|
||||
reset,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: zodResolver(taskSchema),
|
||||
defaultValues: defaultModel,
|
||||
});
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [activityData, setActivityData] = useState([]);
|
||||
const [categoryData, setCategoryData] = useState([]);
|
||||
|
||||
const watchBuildingId = watch("buildingID");
|
||||
const watchFloorId = watch("floorId");
|
||||
const watchWorkAreaId = watch("workAreaId");
|
||||
const watchActivityId = watch("activityID");
|
||||
const watchCategoryId = watch("workCategoryId");
|
||||
|
||||
const selectedBuilding = project?.find((b) => b.id === watchBuildingId);
|
||||
const selectedFloor = selectedBuilding?.floors?.find(
|
||||
(f) => f.id === watchFloorId
|
||||
);
|
||||
const selectedWorkArea = selectedFloor?.workAreas?.find(
|
||||
(w) => w.id === watchWorkAreaId
|
||||
);
|
||||
const selectedActivity = activityData?.find((a) => a.id === watchActivityId);
|
||||
const selectedCategory = categoryData?.find((c) => c.id === watchCategoryId);
|
||||
|
||||
const { mutate: CreateTask, isPending } = useManageTask({
|
||||
onSuccessCallback: ( response ) =>
|
||||
{
|
||||
showToast( response?.message, "success" )
|
||||
onClose?.()
|
||||
},
|
||||
});
|
||||
useEffect(() => {
|
||||
resetForm();
|
||||
reset(defaultModel);
|
||||
}, []);
|
||||
|
||||
const handleBuildingChange = (e) => {
|
||||
const { value } = e.target;
|
||||
const building = project.buildings.find((b) => b.id === String(value));
|
||||
setSelectedBuilding(building);
|
||||
setSelectedFloor(null);
|
||||
setSelectedWorkArea(null);
|
||||
setSelectedActivity(null);
|
||||
reset({
|
||||
...defaultModel,
|
||||
buildingID: value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleFloorChange = (e) => {
|
||||
const { value } = e.target;
|
||||
const floor = selectedBuilding.floors.find((b) => b.id === String(value));
|
||||
setSelectedFloor(floor);
|
||||
setSelectedWorkArea(null);
|
||||
setSelectedActivity(null);
|
||||
reset((prev) => ({
|
||||
...prev,
|
||||
floorId: value,
|
||||
workAreaId: "",
|
||||
activityID: "",
|
||||
workCategoryId: categoryData?.[0]?.id?.toString() ?? "",
|
||||
}));
|
||||
};
|
||||
|
||||
const handleWorkAreaChange = (e) => {
|
||||
const { value } = e.target;
|
||||
const workArea = selectedFloor.workAreas.find(
|
||||
(b) => b.id === String(value)
|
||||
);
|
||||
setSelectedWorkArea(workArea);
|
||||
reset((prev) => ({
|
||||
...prev,
|
||||
workAreaId: String(value),
|
||||
}));
|
||||
};
|
||||
|
||||
const handleActivityChange = (e) => {
|
||||
const { value } = e.target;
|
||||
const activity = activityData.find((b) => b.id === String(value));
|
||||
setSelectedActivity(activity);
|
||||
reset((prev) => ({
|
||||
...prev,
|
||||
activityID: String(value),
|
||||
}));
|
||||
};
|
||||
|
||||
const handleCategoryChange = (e) => {
|
||||
const { value } = e.target;
|
||||
const category = categoryData.find((b) => b.id === String(value));
|
||||
setSelectedCategory(category);
|
||||
reset((prev) => ({
|
||||
...prev,
|
||||
workCategoryId: String(value),
|
||||
}));
|
||||
};
|
||||
|
||||
const onSubmitForm = async (data) => {
|
||||
|
||||
setIsSubmitting(true);
|
||||
await onSubmit(data);
|
||||
setValue("plannedWork", 0);
|
||||
setValue("completedWork", 0);
|
||||
setValue("activityID", 0);
|
||||
setValue("workCategoryId", categoryData?.[0]?.id?.toString() ?? "");
|
||||
setIsSubmitting(false);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
setFormData(defaultModel);
|
||||
setSelectedBuilding(null);
|
||||
setSelectedFloor(null);
|
||||
setSelectedWorkArea(null);
|
||||
setSelectedActivity(null);
|
||||
setSelectedCategory(categoryData?.[0]?.id?.toString() ?? "");
|
||||
reset(defaultModel);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && Array.isArray(activities) && activities.length > 0) {
|
||||
if (Array.isArray(activities) && activities.length > 0) {
|
||||
setActivityData(activities);
|
||||
}
|
||||
}, [activities, loading]);
|
||||
}, [activities]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!categoryLoading &&
|
||||
Array.isArray(categories) &&
|
||||
categories.length > 0
|
||||
) {
|
||||
const newCategories = categories?.slice()?.sort((a, b) => {
|
||||
const nameA = a?.name || "";
|
||||
const nameB = b?.name || "";
|
||||
return nameA?.localeCompare(nameB);
|
||||
});
|
||||
setCategoryData(newCategories);
|
||||
setSelectedCategory(newCategories[0])
|
||||
if (Array.isArray(categories) && categories.length > 0) {
|
||||
const sorted = [...categories].sort((a, b) =>
|
||||
(a?.name || "").localeCompare(b?.name || "")
|
||||
);
|
||||
setCategoryData(sorted);
|
||||
setValue("workCategoryId", sorted?.[0]?.id?.toString() ?? "");
|
||||
}
|
||||
}, [categories, categoryLoading]);
|
||||
}, [categories]);
|
||||
|
||||
const onSubmitForm = async (data) => {
|
||||
const payload = [data];
|
||||
CreateTask(payload);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="modal-dialog modal-lg modal-simple modal-edit-user">
|
||||
<div className="modal-content">
|
||||
<div className="modal-body">
|
||||
<div className="row">
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
aria-label="Close"
|
||||
onClick={onClose}
|
||||
/>
|
||||
<div className="text-center mb-1">
|
||||
<h5 className="mb-1">Manage Task</h5>
|
||||
</div>
|
||||
<form className="row g-2" onSubmit={handleSubmit(onSubmitForm)}>
|
||||
{/* Select Building */}
|
||||
<div className="col-6 col-md-6">
|
||||
<label className="form-label" htmlFor="buildingID">
|
||||
Select Building
|
||||
</label>
|
||||
<select
|
||||
id="buildingID"
|
||||
className="form-select form-select-sm"
|
||||
{...register("buildingID")}
|
||||
onChange={handleBuildingChange}
|
||||
>
|
||||
<option value="">Select Building</option>
|
||||
{project.buildings
|
||||
?.filter((building) => building?.name) // Ensure valid name
|
||||
?.sort((a, b) => a.name?.localeCompare(b.name))
|
||||
?.map((building) => (
|
||||
<option key={building.id} value={building.id}>
|
||||
{building.name}
|
||||
</option>
|
||||
))}
|
||||
|
||||
{project.buildings?.filter((building) => building?.name)
|
||||
.length === 0 && (
|
||||
<option disabled>No buildings found</option>
|
||||
)}
|
||||
</select>
|
||||
{errors.buildingID && (
|
||||
<p className="danger-text">{errors.buildingID.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Select Floor */}
|
||||
{selectedBuilding && (
|
||||
<div className="col-6 col-md-6">
|
||||
<label className="form-label" htmlFor="floorId">
|
||||
Select Floor
|
||||
</label>
|
||||
<select
|
||||
id="floorId"
|
||||
className="form-select form-select-sm"
|
||||
{...register("floorId")}
|
||||
onChange={handleFloorChange}
|
||||
>
|
||||
<option value="">Select Floor</option>
|
||||
{selectedBuilding.floors
|
||||
?.filter(
|
||||
(floor) =>
|
||||
floor?.floorName && Array.isArray(floor.workAreas)
|
||||
)
|
||||
?.sort((a, b) => a.floorName?.localeCompare(b.floorName))
|
||||
?.map((floor) => (
|
||||
<option key={floor.id} value={floor.id}>
|
||||
{floor.floorName} - ({floor.workAreas.length} Work
|
||||
Areas)
|
||||
</option>
|
||||
))}
|
||||
|
||||
{selectedBuilding.floors?.filter(
|
||||
(floor) =>
|
||||
floor?.floorName && Array.isArray(floor.workAreas)
|
||||
).length === 0 && <option disabled>No floors found</option>}
|
||||
</select>
|
||||
{errors.floorId && (
|
||||
<p className="danger-text">{errors.floorId.message}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedFloor && (
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label" htmlFor="workAreaId">
|
||||
Select Work Area
|
||||
</label>
|
||||
<select
|
||||
id="workAreaId"
|
||||
className="form-select form-select-sm"
|
||||
{...register("workAreaId")}
|
||||
onChange={handleWorkAreaChange}
|
||||
>
|
||||
<option value="">Select Work Area</option>
|
||||
{selectedFloor.workAreas
|
||||
?.filter((workArea) => workArea?.areaName)
|
||||
?.sort((a, b) => a.areaName?.localeCompare(b.areaName))
|
||||
?.map((workArea) => (
|
||||
<option key={workArea.id} value={workArea.id}>
|
||||
{workArea.areaName}
|
||||
</option>
|
||||
))}
|
||||
|
||||
{selectedFloor.workAreas?.filter(
|
||||
(workArea) => workArea?.areaName
|
||||
).length === 0 && (
|
||||
<option disabled>No work areas found</option>
|
||||
)}
|
||||
</select>
|
||||
{errors.workAreaId && (
|
||||
<p className="danger-text">{errors.workAreaId.message}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedWorkArea && (
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label">Select Activity</label>
|
||||
<select
|
||||
id="activityID"
|
||||
className="form-select form-select-sm"
|
||||
{...register("activityID")}
|
||||
onChange={handleActivityChange}
|
||||
>
|
||||
<option value="">Select Activity</option>
|
||||
{activityData &&
|
||||
activityData.length > 0 &&
|
||||
activityData
|
||||
?.slice()
|
||||
?.sort((a, b) => {
|
||||
const nameA = a?.activityName || "";
|
||||
const nameB = b?.activityName || "";
|
||||
return nameA?.localeCompare(nameB);
|
||||
})
|
||||
?.map((activity) => (
|
||||
<option key={activity.id} value={activity.id}>
|
||||
{activity.activityName ||
|
||||
`Unnamed (id: ${activity.id})`}
|
||||
</option>
|
||||
))}
|
||||
{!loading && activities.length === 0 && (
|
||||
<option disabled>No activities available</option>
|
||||
)}
|
||||
{loading && <option disabled>Loading...</option>}
|
||||
</select>
|
||||
|
||||
{errors.activityID && (
|
||||
<p className="danger-text">{errors.activityID.message}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedWorkArea && (
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label">Select Work Category</label>
|
||||
<select
|
||||
id="workCategoryId"
|
||||
className="form-select form-select-sm"
|
||||
{...register("workCategoryId")}
|
||||
onChange={handleCategoryChange}
|
||||
>
|
||||
{categoryData &&
|
||||
categoryData.length > 0 &&
|
||||
categoryData
|
||||
?.map((category) => (
|
||||
<option key={category.id} value={category.id}>
|
||||
{category.name || `Unnamed (id: ${category.id})`}
|
||||
</option>
|
||||
))}
|
||||
{!categoryLoading && categories.length === 0 && (
|
||||
<option disabled>No activities available</option>
|
||||
)}
|
||||
{categoryLoading && <option disabled>Loading...</option>}
|
||||
</select>
|
||||
|
||||
{errors.workCategoryId && (
|
||||
<p className="danger-text">
|
||||
{errors.workCategoryId.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedActivity && selectedCategory && (
|
||||
<div className="col-5 col-md-5">
|
||||
<label className="form-label" htmlFor="plannedWork">
|
||||
{formData.id !== "0" ? "Modify " : "Enter "} Planned Work
|
||||
</label>
|
||||
<input
|
||||
{...register("plannedWork", { valueAsNumber: true })}
|
||||
type="number"
|
||||
className="form-control form-control-sm me-2"
|
||||
placeholder="Planned Work"
|
||||
/>
|
||||
{errors.plannedWork && (
|
||||
<p className="danger-text">{errors.plannedWork.message}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedActivity && selectedCategory && (
|
||||
<div className="col-5 col-md-5">
|
||||
<label className="form-label" htmlFor="completedWork">
|
||||
{formData.id !== "0" ? "Modify " : "Enter "} Completed Work
|
||||
</label>
|
||||
<input
|
||||
{...register("completedWork", { valueAsNumber: true })}
|
||||
type="number"
|
||||
className="form-control form-control-sm me-2"
|
||||
placeholder="Completed Work"
|
||||
/>
|
||||
{errors.completedWork && (
|
||||
<p className="danger-text">
|
||||
{errors.completedWork.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedActivity && selectedCategory && (
|
||||
<div className="col-2 col-md-2">
|
||||
<label className="form-label" htmlFor="unit">
|
||||
Unit
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
disabled
|
||||
className="form-control form-control-sm me-2"
|
||||
value={selectedActivity?.unitOfMeasurement || ""}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{selectedActivity && selectedCategory && (
|
||||
<div className="col-12">
|
||||
<label
|
||||
className="form-text fs-7 m-1 text-lg text-dark"
|
||||
htmlFor="descriptionTextarea"
|
||||
>
|
||||
Comment
|
||||
</label>
|
||||
<textarea
|
||||
{...register("comment")}
|
||||
className="form-control"
|
||||
id="descriptionTextarea"
|
||||
rows="2"
|
||||
/>
|
||||
{errors.comment && (
|
||||
<div className="danger-text">
|
||||
{errors.comment.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="col-12 text-center">
|
||||
<button type="submit" className="btn btn-sm btn-primary me-3">
|
||||
{isSubmitting ? "Please Wait.." : "Add Task"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-label-secondary"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<form className="row g-2" onSubmit={handleSubmit(onSubmitForm)}>
|
||||
<div className="text-center mb-1">
|
||||
<h5 className="mb-1">Manage Task</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-6">
|
||||
<label className="form-label">Select Building</label>
|
||||
<select
|
||||
className="form-select form-select-sm"
|
||||
{...register("buildingID")}
|
||||
>
|
||||
<option value="">Select Building</option>
|
||||
{project
|
||||
?.filter((b) => b?.buildingName)
|
||||
?.sort((a, b) => a?.buildingName.localeCompare(b.buildingName))
|
||||
?.map((b) => (
|
||||
<option key={b.id} value={b.id}>
|
||||
{b.buildingName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{errors.buildingID && (
|
||||
<p className="danger-text">{errors.buildingID.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{selectedBuilding && (
|
||||
<div className="col-6">
|
||||
<label className="form-label">Select Floor</label>
|
||||
<select
|
||||
className="form-select form-select-sm"
|
||||
{...register("floorId")}
|
||||
>
|
||||
<option value="">Select Floor</option>
|
||||
{selectedBuilding.floors
|
||||
?.sort((a, b) => a.floorName.localeCompare(b.floorName))
|
||||
?.map((f) => (
|
||||
<option key={f.id} value={f.id}>
|
||||
{f.floorName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{errors.floorId && (
|
||||
<p className="danger-text">{errors.floorId.message}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedFloor && (
|
||||
<div className="col-12">
|
||||
<label className="form-label">Select Work Area</label>
|
||||
<select
|
||||
className="form-select form-select-sm"
|
||||
{...register("workAreaId")}
|
||||
>
|
||||
<option value="">Select Work Area</option>
|
||||
{selectedFloor.workAreas
|
||||
?.sort((a, b) => a.areaName.localeCompare(b.areaName))
|
||||
?.map((w) => (
|
||||
<option key={w.id} value={w.id}>
|
||||
{w.areaName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{errors.workAreaId && (
|
||||
<p className="danger-text">{errors.workAreaId.message}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedWorkArea && (
|
||||
<div className="col-12">
|
||||
<label className="form-label">Select Activity</label>
|
||||
<select
|
||||
className="form-select form-select-sm"
|
||||
{...register("activityID")}
|
||||
>
|
||||
<option value="">Select Activity</option>
|
||||
{activityData.map((a) => (
|
||||
<option key={a.id} value={a.id}>
|
||||
{a.activityName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{errors.activityID && (
|
||||
<p className="danger-text">{errors.activityID.message}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedWorkArea && (
|
||||
<div className="col-12">
|
||||
<label className="form-label">Select Work Category</label>
|
||||
<select
|
||||
className="form-select form-select-sm"
|
||||
{...register("workCategoryId")}
|
||||
>
|
||||
{categoryData.map((c) => (
|
||||
<option key={c.id} value={c.id}>
|
||||
{c.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{errors.workCategoryId && (
|
||||
<p className="danger-text">{errors.workCategoryId.message}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedActivity && selectedCategory && (
|
||||
<>
|
||||
<div className="col-5">
|
||||
<label className="form-label">Planned Work</label>
|
||||
<input
|
||||
type="number"
|
||||
className="form-control form-control-sm"
|
||||
{...register("plannedWork", { valueAsNumber: true })}
|
||||
/>
|
||||
{errors.plannedWork && (
|
||||
<p className="danger-text">{errors.plannedWork.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-5">
|
||||
<label className="form-label">Completed Work</label>
|
||||
<input
|
||||
type="number"
|
||||
className="form-control form-control-sm"
|
||||
{...register("completedWork", { valueAsNumber: true })}
|
||||
/>
|
||||
{errors.completedWork && (
|
||||
<p className="danger-text">{errors.completedWork.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-2">
|
||||
<label className="form-label">Unit</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control form-control-sm"
|
||||
disabled
|
||||
value={selectedActivity?.unitOfMeasurement || ""}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{selectedActivity && selectedCategory && (
|
||||
<div className="col-12">
|
||||
<label className="form-label">Comment</label>
|
||||
<textarea
|
||||
className="form-control"
|
||||
rows="2"
|
||||
{...register("comment")}
|
||||
/>
|
||||
{errors.comment && (
|
||||
<p className="danger-text">{errors.comment.message}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="col-12 text-center">
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-sm btn-primary me-3"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? "Please Wait..." : "Add Task"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-label-secondary"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default TaskModel;
|
||||
export default TaskModel;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import WorkItem from "./WorkItem";
|
||||
import { useProjectDetails } from "../../../hooks/useProjects";
|
||||
import { cacheData, getCachedData } from "../../../slices/apiDataManager";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useProjectDetails, useProjectTasks } from "../../../hooks/useProjects";
|
||||
import { cacheData } from "../../../slices/apiDataManager";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { refreshData } from "../../../slices/localVariablesSlice";
|
||||
import ProjectRepository from "../../../repositories/ProjectRepository";
|
||||
import showToast from "../../../services/toastService";
|
||||
@ -13,216 +13,122 @@ import {
|
||||
MANAGE_TASK,
|
||||
} from "../../../utils/constants";
|
||||
import { useParams } from "react-router-dom";
|
||||
import ProgressDonutChart from "../../Charts/ProgressDonutChart";
|
||||
import ProgressBar from "../../common/ProgressBar";
|
||||
import { componentsToColor } from "pdf-lib";
|
||||
import {formatNumber} from "../../../utils/dateUtils";
|
||||
|
||||
const WorkArea = ({ workArea, floor, forBuilding }) => {
|
||||
const [workItems, setWorkItems] = useState([]);
|
||||
const selectedProject = useSelector((store) => store.localVariables.projectId);
|
||||
const { projects_Details, loading } = useProjectDetails(selectedProject);
|
||||
const [IsExpandedArea, setIsExpandedArea] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const [Project, setProject] = useState();
|
||||
const { projectId } = useParams();
|
||||
|
||||
const ManageTasks = useHasUserPermission(MANAGE_TASK);
|
||||
const ManageInfra = useHasUserPermission(MANAGE_PROJECT_INFRA);
|
||||
const ManageAndAssignTak = useHasUserPermission(ASSIGN_REPORT_TASK);
|
||||
|
||||
const { ProjectTaskList, isLoading } = useProjectTasks(workArea.id, IsExpandedArea);
|
||||
|
||||
const [workAreaStatus, setWorkAreaStatus] = useState({
|
||||
completed: 0,
|
||||
planned: 100,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const totalCompleted = workItems.reduce(
|
||||
(sum, i) => sum + (i.workItem?.completedWork || 0),
|
||||
setProject(projects_Details);
|
||||
}, [projects_Details]);
|
||||
|
||||
useEffect(() => {
|
||||
const totalCompleted = ProjectTaskList?.reduce(
|
||||
(sum, i) => sum + (i?.workItem?.completedWork || 0),
|
||||
0
|
||||
);
|
||||
const totalPlanned = workItems.reduce(
|
||||
(sum, i) => sum + (i.workItem?.plannedWork || 0),
|
||||
const totalPlanned = ProjectTaskList?.reduce(
|
||||
(sum, i) => sum + (i?.workItem?.plannedWork || 0),
|
||||
0
|
||||
);
|
||||
const percent =
|
||||
totalPlanned > 0 ? (totalCompleted / totalPlanned) * 100 : 0;
|
||||
//setPercentComplete(Math.min(percent, 100)); // cap at 100%
|
||||
setWorkAreaStatus({ completed: totalCompleted, planned: totalPlanned });
|
||||
}, [workItems]);
|
||||
}, [ProjectTaskList]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const project = getCachedData("projectInfo");
|
||||
setProject(project);
|
||||
const collapseElement = document.getElementById(`collapse-${workArea.id}`);
|
||||
|
||||
if (!project || !forBuilding?.id || !floor?.id || !workArea?.id) return;
|
||||
const building = project.buildings?.find((b) => b.id === forBuilding.id);
|
||||
const floors = building?.floors?.find((f) => f.id === floor.id);
|
||||
const workAreas = floor?.workAreas?.find((wa) => wa.id === workArea.id);
|
||||
setWorkItems(workAreas?.workItems || []);
|
||||
}, [workArea, floor, floor]);
|
||||
const handleShown = () => setIsExpandedArea(true);
|
||||
const handleHidden = () => setIsExpandedArea(false);
|
||||
|
||||
const HanldeDeleteActivity = async (workItemId) => {
|
||||
try {
|
||||
const updatedProject = { ...Project.data };
|
||||
const response = await ProjectRepository.deleteProjectTask(workItemId);
|
||||
const newProject = {
|
||||
...updatedProject,
|
||||
buildings: updatedProject?.buildings.map((building) =>
|
||||
building?.id === building?.id
|
||||
? {
|
||||
...building,
|
||||
floors: building?.floors?.map((floor) =>
|
||||
floor.id === floor?.id
|
||||
? {
|
||||
...floor,
|
||||
workAreas: floor.workAreas.map((workArea) =>
|
||||
workArea.id === workArea?.id
|
||||
? {
|
||||
...workArea,
|
||||
workItems: workArea.workItems.filter(
|
||||
(item) =>
|
||||
String(item?.workItem?.id ?? item?.id) !==
|
||||
String(workItemId)
|
||||
),
|
||||
}
|
||||
: workArea
|
||||
),
|
||||
}
|
||||
: floor
|
||||
),
|
||||
}
|
||||
: building
|
||||
),
|
||||
};
|
||||
|
||||
cacheData("projectInfo", {
|
||||
projectId: newProject.id,
|
||||
data: newProject,
|
||||
});
|
||||
|
||||
dispatch(refreshData(true));
|
||||
|
||||
showToast("Activity Deleted Successfully", "success");
|
||||
} catch (error) {
|
||||
const message =
|
||||
error.response?.data?.message ||
|
||||
error.message ||
|
||||
"An unexpected error occurred";
|
||||
showToast(message, "error");
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const toggleButtons = document.querySelectorAll(".accordion-button");
|
||||
|
||||
toggleButtons.forEach((btn) => {
|
||||
const icon = btn.querySelector(".toggle-icon");
|
||||
|
||||
btn.addEventListener("click", () => {
|
||||
setTimeout(() => {
|
||||
if (btn.classList.contains("collapsed")) {
|
||||
icon.classList.remove("bx-minus-circle");
|
||||
icon.classList.add("bx-plus-circle");
|
||||
} else {
|
||||
icon.classList.remove("bx-plus-circle");
|
||||
icon.classList.add("bx-minus-circle");
|
||||
}
|
||||
}, 300); // allow Bootstrap collapse to complete
|
||||
});
|
||||
});
|
||||
collapseElement?.addEventListener("shown.bs.collapse", handleShown);
|
||||
collapseElement?.addEventListener("hidden.bs.collapse", handleHidden);
|
||||
|
||||
return () => {
|
||||
toggleButtons.forEach((btn) => {
|
||||
btn.removeEventListener("click", () => {});
|
||||
});
|
||||
collapseElement?.removeEventListener("shown.bs.collapse", handleShown);
|
||||
collapseElement?.removeEventListener("hidden.bs.collapse", handleHidden);
|
||||
};
|
||||
}, []);
|
||||
}, [workArea.id]);
|
||||
|
||||
return (
|
||||
<React.Fragment key={workArea.id}>
|
||||
<tr>
|
||||
<td colSpan="4" className="p-0">
|
||||
<div
|
||||
className="accordion border-none"
|
||||
id={`accordion-${workArea.id}`}
|
||||
>
|
||||
<div className="accordion border-none" id={`accordion-${workArea.id}`}>
|
||||
<div className="accordion-item background border-0">
|
||||
{/* Accordion Header */}
|
||||
<p
|
||||
className="accordion-header mb-0"
|
||||
id={`heading-${workArea.id}`}
|
||||
>
|
||||
<p className="accordion-header mb-0" id={`heading-${workArea.id}`}>
|
||||
<button
|
||||
className={`accordion-button text-start px-2 py-2 custom-accordion-btn ${
|
||||
workItems && workItems.length > 0 ? "collapsed" : "disabled"
|
||||
}`}
|
||||
className="accordion-button text-start px-2 py-2 custom-accordion-btn collapsed"
|
||||
type="button"
|
||||
data-bs-toggle={
|
||||
workItems && workItems.length > 0 ? "collapse" : ""
|
||||
}
|
||||
data-bs-target={
|
||||
workItems && workItems.length > 0
|
||||
? `#collapse-${workArea.id}`
|
||||
: undefined
|
||||
}
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target={`#collapse-${workArea.id}`}
|
||||
aria-expanded="false"
|
||||
aria-controls={`collapse-${workArea.id}`}
|
||||
disabled={!(workItems && workItems.length > 0)}
|
||||
>
|
||||
<i
|
||||
className={`bx me-2 toggle-icon ${
|
||||
workItems && workItems.length > 0
|
||||
? "bx-plus-circle"
|
||||
: "bx-block"
|
||||
IsExpandedArea ? "bx-minus-circle" : "bx-plus-circle"
|
||||
}`}
|
||||
style={{
|
||||
fontSize: "1.2rem",
|
||||
color:
|
||||
workItems && workItems.length > 0 ? "" : "transparent",
|
||||
color: "black",
|
||||
}}
|
||||
></i>
|
||||
|
||||
<div className="d-flex justify-content-start row w-100 align-items-center">
|
||||
<div className="d-flex col-5">
|
||||
<span className="fw-semibold text-primary small">
|
||||
Floor:
|
||||
</span>
|
||||
<span className="fw-normal text-darkgreen small px-2">
|
||||
<span className="fw-semibold text-primary small">Floor:</span>
|
||||
<span className="fw-normal text-darkgreen small px-2">
|
||||
{floor.floorName}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-start col-5">
|
||||
<span className="fw-semibold text-primary small">
|
||||
Work Area:
|
||||
</span>
|
||||
<span className="fw-normal text-darkgreen small px-2">
|
||||
<span className="fw-semibold text-primary small">Work Area:</span>
|
||||
<span className="fw-normal text-darkgreen small px-2">
|
||||
{workArea.areaName}
|
||||
</span>
|
||||
</div>
|
||||
{workArea?.workItems?.length > 0 && (
|
||||
|
||||
<div className="col-2">
|
||||
<ProgressBar
|
||||
completedWork={workAreaStatus.completed}
|
||||
plannedWork={workAreaStatus.planned}
|
||||
completedWork={formatNumber(workArea?.completedWork)}
|
||||
plannedWork={formatNumber(workArea?.plannedWork)}
|
||||
className="m-0 text-info"
|
||||
></ProgressBar>
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
</p>
|
||||
|
||||
{/* Accordion Body */}
|
||||
{workItems && workItems.length > 0 && (
|
||||
<div
|
||||
id={`collapse-${workArea.id}`}
|
||||
className="accordion-collapse collapse"
|
||||
aria-labelledby={`heading-${workArea.id}`}
|
||||
>
|
||||
<div className="accordion-body px-1">
|
||||
<div
|
||||
id={`collapse-${workArea.id}`}
|
||||
className="accordion-collapse collapse"
|
||||
aria-labelledby={`heading-${workArea.id}`}
|
||||
>
|
||||
<div className="accordion-body px-1">
|
||||
{isLoading ? (
|
||||
<div className="text-center py-2 text-muted">Loading activities...</div>
|
||||
) : ProjectTaskList?.length > 0 ? (
|
||||
<table className="table table-sm mx-1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="infra-activity-table-header-first">
|
||||
Activity
|
||||
</th>
|
||||
<th className="infra-activity-table-header-first">Activity</th>
|
||||
<th className="infra-activity-table-header d-sm-table-cell d-md-none">
|
||||
Status
|
||||
</th>
|
||||
@ -235,11 +141,8 @@ const WorkArea = ({ workArea, floor, forBuilding }) => {
|
||||
<th className="infra-activity-table-header d-none d-md-table-cell">
|
||||
Today's Planned
|
||||
</th>
|
||||
<th className="infra-activity-table-header">
|
||||
Progress
|
||||
</th>
|
||||
{(ManageInfra ||
|
||||
(!projectId && ManageAndAssignTak)) && (
|
||||
<th className="infra-activity-table-header">Progress</th>
|
||||
{(ManageInfra || (!projectId && ManageAndAssignTak)) && (
|
||||
<th className="infra-activity-table-header text-end">
|
||||
<span className="px-2">Actions</span>
|
||||
</th>
|
||||
@ -247,21 +150,24 @@ const WorkArea = ({ workArea, floor, forBuilding }) => {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="table-border-bottom-0">
|
||||
{workArea.workItems.map((workItem) => (
|
||||
{ProjectTaskList.map((workItem,index) => (
|
||||
<WorkItem
|
||||
key={workItem.workItemId}
|
||||
key={workItem.workItemId || `fallback-${index}`}
|
||||
workItem={workItem}
|
||||
forBuilding={forBuilding}
|
||||
forFloor={floor}
|
||||
forWorkArea={workArea}
|
||||
deleteHandleTask={HanldeDeleteActivity}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center text-muted py-3">
|
||||
No activities available for this work area.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@ -269,4 +175,5 @@ const WorkArea = ({ workArea, floor, forBuilding }) => {
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default WorkArea;
|
||||
|
||||
@ -1,39 +1,31 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { set, useForm } from "react-hook-form";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
import showToast from "../../../services/toastService";
|
||||
import { useManageProjectInfra } from "../../../hooks/useProjects";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
// Zod schema for form validation
|
||||
const workAreaSchema = z.object({
|
||||
id: z.string().nonempty("Floor is required"),
|
||||
|
||||
buildingId: z.string().nonempty("Building is required"),
|
||||
floorId: z.string().nonempty("Floor is required"),
|
||||
areaName: z
|
||||
.string()
|
||||
.nonempty("Work Area Name is required")
|
||||
.min(3, "Name must be at least 3 characters long"),
|
||||
id: z.string().optional(),
|
||||
buildingId: z.string().refine((val) => val !== "0", {
|
||||
message: "Building is required",
|
||||
}),
|
||||
floorId: z.string().refine((val)=>val !== "0",{message:"Floor is required"}),
|
||||
areaName: z.string().min(3, "Work Area Name must be at least 3 characters"),
|
||||
});
|
||||
|
||||
// Default form data
|
||||
const defaultModel = {
|
||||
id: "0",
|
||||
areaName: "",
|
||||
buildingId: "0",
|
||||
floorId: "0",
|
||||
areaName: "",
|
||||
};
|
||||
|
||||
const WorkAreaModel = ({
|
||||
project,
|
||||
onSubmit,
|
||||
clearTrigger,
|
||||
onClearComplete,
|
||||
onClose,
|
||||
}) => {
|
||||
const WorkAreaModel = ({ project, onSubmit, onClose }) => {
|
||||
const [selectedBuilding, setSelectedBuilding] = useState(null);
|
||||
const [selectedFloor, setSelectedFloor] = useState(null);
|
||||
const [selectdWorkArea, setWorkArea] = useState();
|
||||
|
||||
const selectedProject = useSelector((store)=>store.localVariables.projectId)
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
@ -42,259 +34,168 @@ const WorkAreaModel = ({
|
||||
reset,
|
||||
watch,
|
||||
} = useForm({
|
||||
resolver: zodResolver(workAreaSchema), // Use Zod resolver for validation
|
||||
resolver: zodResolver(workAreaSchema),
|
||||
defaultValues: defaultModel,
|
||||
});
|
||||
|
||||
const floorId = watch("floorId"); // Watch the floorId for conditional rendering
|
||||
const watchBuildingId = watch("buildingId");
|
||||
const watchFloorId = watch("floorId");
|
||||
const watchWorkAreaId = watch("id");
|
||||
const { mutate: ManageWorkArea, isPending } = useManageProjectInfra({
|
||||
onSuccessCallback: (data, variables) => {
|
||||
showToast(
|
||||
watchWorkAreaId != "0"
|
||||
? "Wrok Area updated Successfully"
|
||||
: "Work Area created Successfully",
|
||||
"success"
|
||||
);
|
||||
reset({ id: "0", buildingId: "0", areaName: "", floorId: "0" });
|
||||
// onClose?.();
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (clearTrigger) {
|
||||
reset(defaultModel); // Reset form to initial state
|
||||
setSelectedBuilding(null);
|
||||
setSelectedFloor(null);
|
||||
onClearComplete();
|
||||
}
|
||||
}, [clearTrigger, onClearComplete, reset]);
|
||||
const building = project?.find((b) => b.id === watchBuildingId);
|
||||
setSelectedBuilding(building || null);
|
||||
|
||||
const handleWorkAreaChange = (e) => {
|
||||
const { value } = e.target;
|
||||
|
||||
if (value === "0") {
|
||||
setValue("id", "0"); // Create New Work Area
|
||||
if (building) {
|
||||
const floor = building.floors?.find((f) => f.id === watchFloorId);
|
||||
setSelectedFloor(floor || null);
|
||||
setValue("areaName", "");
|
||||
|
||||
setWorkArea(String(0));
|
||||
} else {
|
||||
const workArea = selectedFloor?.workAreas.find(
|
||||
(b) => b.id === String(value)
|
||||
);
|
||||
if (workArea) {
|
||||
setValue("id", String(workArea.id)); // Set id as a string
|
||||
setValue("areaName", workArea.areaName);
|
||||
setWorkArea(String(workArea.id));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleFloorChange = (e) => {
|
||||
const { value } = e.target;
|
||||
const floor = selectedBuilding?.floors.find((b) => b.id === String(value));
|
||||
|
||||
if (floor) {
|
||||
setSelectedFloor(floor);
|
||||
setValue("floorId", floor.id); // Update floorId
|
||||
setValue("id", "0"); // Reset Work Area ID for new area creation
|
||||
setValue("areaName", ""); // Reset Work Area Name when changing floor
|
||||
} else {
|
||||
setSelectedFloor(null);
|
||||
setValue("floorId", "0");
|
||||
setValue("id", "0"); // Reset Work Area ID
|
||||
setValue("areaName", ""); // Reset Work Area Name
|
||||
setValue("areaName", "");
|
||||
}
|
||||
}, [watchBuildingId, watchFloorId]);
|
||||
|
||||
const handleWrokAreaChange = (e) => {
|
||||
const workAreaId = e.target.value;
|
||||
setValue("id", workAreaId);
|
||||
const area = selectedFloor?.workAreas.find((w) => w.id === workAreaId);
|
||||
if (area) {
|
||||
setValue("areaName", area.areaName);
|
||||
} else {
|
||||
setValue("areaName", "");
|
||||
}
|
||||
};
|
||||
|
||||
const handleBuildingChange = (e) => {
|
||||
const { value } = e.target;
|
||||
const building = project?.buildings.find((b) => b.id === String(value));
|
||||
setSelectedBuilding(building);
|
||||
setSelectedFloor(null); // Reset selected floor on building change
|
||||
reset(defaultModel); // Reset the form when a new building is selected
|
||||
};
|
||||
useEffect(() => {
|
||||
reset(defaultModel);
|
||||
}, []);
|
||||
|
||||
const onSubmitForm = (data) => {
|
||||
let WorkArea = {
|
||||
id: data.id == "0" ? null : data.id,
|
||||
const onSubmitForm = ( data ) =>
|
||||
{
|
||||
const payload = {
|
||||
id: data.id === "0" ? null : data.id,
|
||||
areaName: data.areaName,
|
||||
floorId: data.floorId,
|
||||
buildingId: data.buildingId,
|
||||
};
|
||||
onSubmit(WorkArea);
|
||||
let infraObject = [
|
||||
{
|
||||
building: null,
|
||||
floor: null,
|
||||
workArea: payload,
|
||||
},
|
||||
];
|
||||
|
||||
reset({
|
||||
id: "0",
|
||||
areaName: "",
|
||||
});
|
||||
if (WorkArea.id !== null) {
|
||||
showToast("WorkArea updated successfully.", "success");
|
||||
} else {
|
||||
showToast("WorkArea created successfully.", "success");
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
reset(defaultModel);
|
||||
setSelectedFloor(null);
|
||||
setSelectedBuilding(null);
|
||||
onClose();
|
||||
ManageWorkArea({ infraObject, projectId: selectedProject });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="modal-dialog modal-lg modal-simple modal-edit-user">
|
||||
<div className="modal-content">
|
||||
<div className="modal-body">
|
||||
<div className="row">
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
aria-label="Close"
|
||||
onClick={onClose}
|
||||
/>
|
||||
<div className="text-center mb-1">
|
||||
<h5 className="mb-1">Manage Work Area</h5>
|
||||
</div>
|
||||
<form className="row g-2" onSubmit={handleSubmit(onSubmitForm)}>
|
||||
{/* Building Selection */}
|
||||
<div className="col-6 col-md-6">
|
||||
<label className="form-label" htmlFor="buildingId">
|
||||
Select Building
|
||||
</label>
|
||||
<select
|
||||
id="buildingId"
|
||||
name="buildingId"
|
||||
className="select2 form-select form-select-sm"
|
||||
{...register("buildingId")}
|
||||
onChange={handleBuildingChange}
|
||||
>
|
||||
<option value="0">Select Building</option>
|
||||
{project?.buildings
|
||||
?.filter((building) => building?.name)
|
||||
?.sort((a, b) => {
|
||||
const nameA = a.name || "";
|
||||
const nameB = b.name || "";
|
||||
return nameA?.localeCompare(nameB);
|
||||
})
|
||||
?.map((building) => (
|
||||
<option key={building.id} value={building.id}>
|
||||
{building.name}
|
||||
</option>
|
||||
))}
|
||||
|
||||
{project?.buildings?.filter((building) => building?.name)
|
||||
.length === 0 && (
|
||||
<option disabled>No buildings found</option>
|
||||
)}
|
||||
</select>
|
||||
{errors.buildingId && <span>{errors.buildingId.message}</span>}
|
||||
</div>
|
||||
|
||||
{/* Floor Selection */}
|
||||
{selectedBuilding && selectedBuilding.buildingId !== "0" && (
|
||||
<div className="col-6 col-md-6">
|
||||
<label className="form-label" htmlFor="floorId">
|
||||
Select Floor
|
||||
</label>
|
||||
<select
|
||||
id="floorId"
|
||||
name="floorId"
|
||||
className="select2 form-select form-select-sm"
|
||||
{...register("floorId")}
|
||||
onChange={handleFloorChange}
|
||||
>
|
||||
<option value="0">Select Floor</option>
|
||||
{selectedBuilding.floors
|
||||
?.filter((floor) => floor?.floorName)
|
||||
?.sort((a, b) => {
|
||||
const nameA = a.floorName || "";
|
||||
const nameB = b.floorName || "";
|
||||
return nameA?.localeCompare(nameB);
|
||||
})
|
||||
?.map((floor) => (
|
||||
<option key={floor.id} value={floor.id}>
|
||||
{floor.floorName}
|
||||
</option>
|
||||
))}
|
||||
|
||||
{selectedBuilding.floors?.filter(
|
||||
(floor) => floor?.floorName
|
||||
).length === 0 && <option disabled>No floors found</option>}
|
||||
</select>
|
||||
{errors.floorId && <span>{errors.floorId.message}</span>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Work Area Selection or Creation */}
|
||||
{floorId !== "0" && (
|
||||
<>
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label">Select Work Area</label>
|
||||
<select
|
||||
id="workAreaId"
|
||||
name="workAreaId"
|
||||
className="select2 form-select form-select-sm"
|
||||
{...register("id")}
|
||||
onChange={handleWorkAreaChange}
|
||||
>
|
||||
<option value="0">Create New Work Area</option>
|
||||
{selectedFloor?.workAreas
|
||||
?.filter((workArea) => workArea?.areaName)
|
||||
?.sort((a, b) => {
|
||||
const nameA = a.areaName || "";
|
||||
const nameB = b.areaName || "";
|
||||
return nameA?.localeCompare(nameB);
|
||||
})
|
||||
?.map((workArea) => (
|
||||
<option key={workArea.id} value={workArea.id}>
|
||||
{workArea.areaName}
|
||||
</option>
|
||||
))}
|
||||
|
||||
{selectedFloor?.workAreas?.filter(
|
||||
(workArea) => workArea?.areaName
|
||||
).length === 0 && (
|
||||
<option disabled>No work areas found</option>
|
||||
)}
|
||||
</select>
|
||||
{errors.id && <span>{errors.id.message}</span>}
|
||||
</div>
|
||||
|
||||
{/* Work Area Name Input */}
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label" htmlFor="areaName">
|
||||
{watch("id") === "0"
|
||||
? "Enter Work Area Name"
|
||||
: "Modify Work Area Name"}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="areaName"
|
||||
name="areaName"
|
||||
className="form-control form-control-sm"
|
||||
placeholder="Work Area"
|
||||
{...register("areaName")}
|
||||
/>
|
||||
{errors.areaName && (
|
||||
<span className="danger-text">
|
||||
{errors.areaName.message}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Submit and Cancel Buttons */}
|
||||
<div className="col-12 text-center">
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-sm btn-primary me-3"
|
||||
>
|
||||
{watch("id") === "0" ? "Add Work Area" : "Edit Work Area"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-label-secondary"
|
||||
onClick={handleCancel}
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<form className="row g-2 p-2 p-md-1" onSubmit={handleSubmit(onSubmitForm)}>
|
||||
<div className="text-center mb-1">
|
||||
<h5 className="mb-1">Manage Work Area</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 col-sm-6">
|
||||
<label className="form-label">Select Building</label>
|
||||
<select
|
||||
{...register("buildingId")}
|
||||
className="form-select form-select-sm"
|
||||
>
|
||||
<option value="0">Select Building</option>
|
||||
{project?.map((b) => (
|
||||
<option key={b.id} value={b.id}>
|
||||
{b.buildingName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{errors.buildingId && (
|
||||
<p className="danger-text">{errors.buildingId.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{watchBuildingId !== "0" && (
|
||||
<div className="col-12 col-sm-6">
|
||||
<label className="form-label">Select Floor</label>
|
||||
<select
|
||||
{...register("floorId")}
|
||||
className="form-select form-select-sm"
|
||||
>
|
||||
<option value="0">
|
||||
{selectedBuilding?.floor?.length > 0
|
||||
? "NO Floor Found"
|
||||
: "Select Floor"}
|
||||
</option>
|
||||
|
||||
{selectedBuilding?.floors?.map((f) => (
|
||||
<option key={f.id} value={f.id}>
|
||||
{f.floorName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{errors.floorId && (
|
||||
<p className="danger-text">{errors.floorId.message}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{watchFloorId !== "0" && (
|
||||
<>
|
||||
<div className="col-12">
|
||||
<label className="form-label">Select Work Area</label>
|
||||
<select
|
||||
{...register("id")}
|
||||
className="form-select form-select-sm"
|
||||
onChange={handleWrokAreaChange}
|
||||
>
|
||||
<option value="0">Create New Work Area</option>
|
||||
{selectedFloor?.workAreas?.length > 0 &&
|
||||
selectedFloor?.workAreas?.map((w) => (
|
||||
<option key={w.id} value={w.id}>
|
||||
{w.areaName}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="col-12">
|
||||
<label className="form-label">
|
||||
{watchWorkAreaId === "0"
|
||||
? "Enter Work Area Name"
|
||||
: "Edit Work Area Name"}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control form-control-sm"
|
||||
placeholder="Work Area"
|
||||
{...register("areaName")}
|
||||
/>
|
||||
{errors.areaName && (
|
||||
<p className="danger-text">{errors.areaName.message}</p>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="col-12 text-center">
|
||||
<button type="submit" className="btn btn-sm btn-primary me-3" disabled={isPending}>
|
||||
{isPending ? "Please Wait.." : watchWorkAreaId === "0" ? "Add Work Area" : "Update Work Area"}
|
||||
</button>
|
||||
<button type="button" className="btn btn-sm btn-label-secondary" disabled={isPending} onClick={onClose}>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -10,16 +10,16 @@ import {
|
||||
} from "../../../utils/constants";
|
||||
import ConfirmModal from "../../common/ConfirmModal";
|
||||
import ProjectRepository from "../../../repositories/ProjectRepository";
|
||||
import { useProjectDetails } from "../../../hooks/useProjects";
|
||||
import { useDeleteProjectTask, useProjectDetails } from "../../../hooks/useProjects";
|
||||
import showToast from "../../../services/toastService";
|
||||
import {
|
||||
cacheData,
|
||||
clearCacheKey,
|
||||
getCachedData,
|
||||
} from "../../../slices/apiDataManager";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { refreshData } from "../../../slices/localVariablesSlice";
|
||||
import GlobalModel from "../../common/GlobalModel";
|
||||
import {useDeleteMasterItem} from "../../../hooks/masterHook/useMaster";
|
||||
|
||||
const WorkItem = ({
|
||||
workItem,
|
||||
@ -39,18 +39,24 @@ const WorkItem = ({
|
||||
const ManageAndAssignTak = useHasUserPermission(ASSIGN_REPORT_TASK);
|
||||
const [loadingDelete, setLoadingDelete] = useState(false);
|
||||
const project = getCachedData("projectInfo");
|
||||
const dispatch = useDispatch();
|
||||
|
||||
|
||||
const openModal = () => setIsModalOpen(true);
|
||||
const closeModal = () => setIsModalOpen(false);
|
||||
const closeModal = () => setIsModalOpen( false );
|
||||
|
||||
const showModalDelete = () => setShowModal2(true);
|
||||
const closeModalDelete = () => setShowModal2(false);
|
||||
const getProgress = (planned, completed) => {
|
||||
return (completed * 100) / planned + "%";
|
||||
};
|
||||
|
||||
const {mutate:DeleteTask,isPending } = useDeleteProjectTask(() => {
|
||||
closeModalDelete?.();
|
||||
});
|
||||
|
||||
const handleAssignTask = () => {
|
||||
setItemName("");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setNewWorkItem(workItem);
|
||||
}, [workItem]);
|
||||
@ -79,17 +85,15 @@ const WorkItem = ({
|
||||
tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el));
|
||||
}, []);
|
||||
|
||||
const showModal1 = () => setShowModal(true);
|
||||
const closeModal1 = () => setShowModal(false);
|
||||
const showModalDelete = () => setShowModal2(true);
|
||||
const closeModalDelete = () => setShowModal2(false);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const handleSubmit = async () => {
|
||||
setLoadingDelete(true);
|
||||
let WorkItemId = workItem.workItemId || workItem.id;
|
||||
deleteHandleTask(WorkItemId);
|
||||
setLoadingDelete(false);
|
||||
closeModalDelete();
|
||||
DeleteTask({workItemId:WorkItemId,workAreaId:forWorkArea?.id})
|
||||
|
||||
};
|
||||
|
||||
const PlannedWork =
|
||||
@ -105,21 +109,15 @@ const WorkItem = ({
|
||||
)}
|
||||
|
||||
{showModal && (
|
||||
<div
|
||||
className={`modal fade ${showModal ? "show" : ""}`}
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: showModal ? "block" : "none" }}
|
||||
aria-hidden={!showModal}
|
||||
>
|
||||
<GlobalModel isOpen={showModal} size="lg" closeModal={()=>setShowModal(false)}>
|
||||
<EditActivityModal
|
||||
workItem={workItem}
|
||||
workArea={forWorkArea}
|
||||
building={forBuilding}
|
||||
floor={forFloor}
|
||||
onClose={closeModal1}
|
||||
onClose={()=>setShowModal(false)}
|
||||
/>
|
||||
</div>
|
||||
</GlobalModel>
|
||||
)}
|
||||
|
||||
{showModal2 && (
|
||||
@ -169,7 +167,7 @@ const WorkItem = ({
|
||||
: "NA"}
|
||||
</td>
|
||||
|
||||
{/* Category - visible on medium and above */}
|
||||
|
||||
<td className="text-center table-cell-small d-none d-md-table-cell">
|
||||
<span className="fw-light">
|
||||
{hasWorkItem
|
||||
@ -204,7 +202,6 @@ const WorkItem = ({
|
||||
: "NA"}
|
||||
</td>
|
||||
|
||||
{/* Progress Bar - always visible */}
|
||||
<td className="text-center " style={{ width: "15%" }}>
|
||||
<div className="progress p-0">
|
||||
<div
|
||||
@ -231,7 +228,6 @@ const WorkItem = ({
|
||||
</div>
|
||||
</td>
|
||||
|
||||
{/* Actions - always visible */}
|
||||
{(ManageInfra ||
|
||||
(!projectId &&
|
||||
ManageAndAssignTak &&
|
||||
@ -255,7 +251,7 @@ const WorkItem = ({
|
||||
<i
|
||||
className="bx bxs-edit text-secondary cursor-pointer"
|
||||
title="Edit"
|
||||
onClick={showModal1}
|
||||
onClick={()=>setShowModal(true)}
|
||||
role="button"
|
||||
></i>
|
||||
<i
|
||||
@ -297,7 +293,7 @@ const WorkItem = ({
|
||||
<li>
|
||||
<a
|
||||
className="dropdown-item d-flex align-items-center"
|
||||
onClick={showModal1}
|
||||
onClick={()=>setShowModal(true) }
|
||||
>
|
||||
<i className="bx bxs-edit text-secondary me-2"></i> Edit
|
||||
</a>
|
||||
|
||||
@ -3,19 +3,6 @@ import { useForm, Controller } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { z } from "zod";
|
||||
|
||||
// const currentDate = new Date().toISOString().split("T")[0];
|
||||
|
||||
const ACTIVE_STATUS_ID = "b74da4c2-d07e-46f2-9919-e75e49b12731";
|
||||
|
||||
const DEFAULT_EMPTY_STATUS_ID = "00000000-0000-0000-0000-000000000000";
|
||||
/**
|
||||
|
||||
* Formats a given date string into 'YYYY-MM-DD' format.
|
||||
* If the date is invalid or not provided, it defaults to the current date.
|
||||
* @param {string} date - The date string to format.
|
||||
* @returns {string} The formatted date string.
|
||||
*/
|
||||
|
||||
const currentDate = new Date().toLocaleDateString('en-CA');
|
||||
const formatDate = (date) => {
|
||||
if (!date) {
|
||||
@ -25,15 +12,16 @@ const formatDate = (date) => {
|
||||
if (isNaN(d.getTime())) {
|
||||
return currentDate;
|
||||
}
|
||||
// return d.toISOString().split("T")[0];
|
||||
return d.toLocaleDateString('en-CA');
|
||||
};
|
||||
const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
|
||||
const ManageProjectInfo = ({ project, handleSubmitForm, onClose,isPending }) => {
|
||||
const [CurrentProject, setCurrentProject] = useState();
|
||||
const [isloading, setLoading] = useState(false);
|
||||
const [addressLength, setAddressLength] = useState(0);
|
||||
const maxAddressLength = 500;
|
||||
|
||||
const ACTIVE_STATUS_ID = "b74da4c2-d07e-46f2-9919-e75e49b12731";
|
||||
const DEFAULT_EMPTY_STATUS_ID = "00000000-0000-0000-0000-000000000000";
|
||||
|
||||
const projectSchema = z
|
||||
.object({
|
||||
...(project?.id ? { id: z.string().optional() } : {}),
|
||||
@ -105,18 +93,15 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
|
||||
reset(
|
||||
project
|
||||
? {
|
||||
id: project?.id || "",
|
||||
name: project?.name || "",
|
||||
shortName: project?.shortName || "",
|
||||
contactPerson: project?.contactPerson || "",
|
||||
projectAddress: project?.projectAddress || "",
|
||||
startDate: formatDate(project?.startDate) || "",
|
||||
endDate: formatDate(project?.endDate) || "",
|
||||
// projectStatusId: String(project.projectStatusId) || "00000000-0000-0000-0000-000000000000",
|
||||
projectStatusId: project?.projectStatusId && project.projectStatusId !== DEFAULT_EMPTY_STATUS_ID
|
||||
? String(project.projectStatusId)
|
||||
: ACTIVE_STATUS_ID,
|
||||
}
|
||||
id: project?.id || "",
|
||||
name: project?.name || "",
|
||||
shortName: project?.shortName || "",
|
||||
contactPerson: project?.contactPerson || "",
|
||||
projectAddress: project?.projectAddress || "",
|
||||
startDate: formatDate(project?.startDate) || "",
|
||||
endDate: formatDate(project?.endDate) || "",
|
||||
projectStatusId: String(project?.projectStatus?.id) || "00000000-0000-0000-0000-000000000000",
|
||||
}
|
||||
: {}
|
||||
);
|
||||
setAddressLength(project?.projectAddress?.length || 0);
|
||||
@ -130,9 +115,10 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
|
||||
|
||||
*/
|
||||
|
||||
const onSubmitForm = (updatedProject) => {
|
||||
setLoading(true);
|
||||
handleSubmitForm(updatedProject, setLoading, reset);
|
||||
const onSubmitForm = ( updatedProject ) =>
|
||||
{
|
||||
|
||||
handleSubmitForm(updatedProject);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
@ -144,27 +130,15 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
|
||||
projectAddress: project?.projectAddress || "",
|
||||
startDate: formatDate(project?.startDate) || currentDate,
|
||||
endDate: formatDate(project?.endDate) || currentDate,
|
||||
// projectStatusId: String(project?.projectStatusId || "00000000-0000-0000-0000-000000000000"),
|
||||
projectStatusId: project?.projectStatusId && project.projectStatusId !== DEFAULT_EMPTY_STATUS_ID
|
||||
? String(project.projectStatusId)
|
||||
: ACTIVE_STATUS_ID,
|
||||
projectStatusId: String(project?.projectStatus?.id || "00000000-0000-0000-0000-000000000000"),
|
||||
});
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="modal-dialog modal-lg modal-simple mx-sm-auto mx-1 edit-project-modal"
|
||||
role="document"
|
||||
>
|
||||
<div className="modal-content">
|
||||
<div className="modal-body p-sm-4 p-0">
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
onClick={handleCancel}
|
||||
aria-label="Close"
|
||||
></button>
|
||||
|
||||
<div className="p-sm-2 p-2">
|
||||
|
||||
<div className="text-center mb-2">
|
||||
<h5 className="mb-2">
|
||||
{project?.id ? "Edit Project" : "Create Project"}
|
||||
@ -179,7 +153,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
|
||||
type="text"
|
||||
id="name"
|
||||
name="name"
|
||||
className="form-control"
|
||||
className="form-control form-control-sm"
|
||||
placeholder="Project Name"
|
||||
{...register("name")}
|
||||
/>
|
||||
@ -200,7 +174,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
|
||||
type="text"
|
||||
id="shortName"
|
||||
name="shortName"
|
||||
className="form-control"
|
||||
className="form-control form-control-sm"
|
||||
placeholder="Short Name"
|
||||
{...register("shortName")}
|
||||
/>
|
||||
@ -221,7 +195,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
|
||||
type="text"
|
||||
id="contactPerson"
|
||||
name="contactPerson"
|
||||
className="form-control"
|
||||
className="form-control form-control-sm"
|
||||
placeholder="Contact Person"
|
||||
maxLength={50}
|
||||
{...register("contactPerson")}
|
||||
@ -283,7 +257,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
|
||||
<select
|
||||
id="modalEditUserStatus"
|
||||
name="modalEditUserStatus"
|
||||
className="select2 form-select"
|
||||
className="select2 form-select form-select-sm"
|
||||
aria-label="Default select example"
|
||||
{...register("projectStatusId", {
|
||||
required: "Status is required",
|
||||
@ -339,22 +313,21 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
|
||||
)}
|
||||
</div>
|
||||
<div className="col-12 text-center">
|
||||
<button type="submit" className="btn btn-sm btn-primary me-3" disabled={isloading}>
|
||||
{isloading ? "Please Wait..." : project?.id ? "Update" : "Submit"}
|
||||
<button type="submit" className="btn btn-sm btn-primary me-3" disabled={isPending}>
|
||||
{isPending ? "Please Wait..." : project?.id ? "Update" : "Submit"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-label-secondary"
|
||||
onClick={handleCancel}
|
||||
aria-label="Close"
|
||||
disabled={isPending}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import moment from "moment";
|
||||
import { getDateDifferenceInDays } from "../../utils/dateUtils";
|
||||
import { formatNumber, getDateDifferenceInDays } from "../../utils/dateUtils";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useProjectDetails } from "../../hooks/useProjects";
|
||||
import { useProjectDetails, useUpdateProject } from "../../hooks/useProjects";
|
||||
import ManageProjectInfo from "./ManageProjectInfo";
|
||||
import ProjectRepository from "../../repositories/ProjectRepository";
|
||||
import { cacheData, getCachedData } from "../../slices/apiDataManager";
|
||||
@ -13,29 +13,36 @@ import {
|
||||
getProjectStatusColor,
|
||||
getProjectStatusName,
|
||||
} from "../../utils/projectStatus";
|
||||
import GlobalModel from "../common/GlobalModel";
|
||||
|
||||
const ProjectCard = ({ projectData, recall }) => {
|
||||
const [projectInfo, setProjectInfo] = useState(projectData);
|
||||
const [projectDetails, setProjectDetails] = useState(null);
|
||||
const [ projectInfo, setProjectInfo ] = useState( projectData );
|
||||
const { projects_Details, loading, error, refetch } = useProjectDetails(
|
||||
projectInfo?.id,false
|
||||
);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
const ManageProject = useHasUserPermission(MANAGE_PROJECT);
|
||||
const [modifyProjectLoading, setMdifyProjectLoading] = useState(false);
|
||||
const {
|
||||
mutate: updateProject,
|
||||
isPending,
|
||||
isSuccess,
|
||||
isError,
|
||||
} = useUpdateProject({
|
||||
onSuccessCallback: () => {
|
||||
setShowModal(false);
|
||||
},
|
||||
})
|
||||
|
||||
useEffect(()=>{
|
||||
setProjectInfo(projectData);
|
||||
},[projectData])
|
||||
// console.log("in card view",projectInfo);
|
||||
const handleShow = async () => {
|
||||
}, [ projectData ] )
|
||||
|
||||
const handleShow = async () => {
|
||||
try {
|
||||
setMdifyProjectLoading(true);
|
||||
const response = await ProjectRepository.getProjectByprojectId(
|
||||
projectInfo.id
|
||||
);
|
||||
setProjectDetails(response.data);
|
||||
setMdifyProjectLoading(false);
|
||||
const { data } = await refetch();
|
||||
setShowModal(true);
|
||||
} catch (error) {
|
||||
} catch (err) {
|
||||
showToast("Failed to load project details", "error");
|
||||
}
|
||||
};
|
||||
@ -54,64 +61,30 @@ const ProjectCard = ({ projectData, recall }) => {
|
||||
};
|
||||
|
||||
const handleFormSubmit = (updatedProject) => {
|
||||
if (projectInfo?.id) {
|
||||
ProjectRepository.updateProject(projectInfo.id, updatedProject)
|
||||
.then((response) => {
|
||||
const updatedProjectData = {
|
||||
...projectInfo,
|
||||
...response.data,
|
||||
building: projectDetails?.building,
|
||||
};
|
||||
|
||||
setProjectInfo(updatedProject);
|
||||
|
||||
if (getCachedData(`projectinfo-${projectInfo.id}`)) {
|
||||
cacheData(`projectinfo-${projectInfo.id}`, updatedProjectData);
|
||||
}
|
||||
|
||||
const projects_list = getCachedData("projectslist");
|
||||
if (projects_list) {
|
||||
const updatedProjectsList = projects_list.map((project) =>
|
||||
project.id === projectInfo.id
|
||||
? {
|
||||
...project,
|
||||
...response.data,
|
||||
// tenant: project.tenant
|
||||
}
|
||||
: project
|
||||
);
|
||||
cacheData("projectslist", updatedProjectsList);
|
||||
}
|
||||
recall(getCachedData("projectslist"));
|
||||
showToast("Project updated successfully.", "success");
|
||||
setShowModal(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
showToast(error.message, "error");
|
||||
});
|
||||
}
|
||||
};
|
||||
if (projectInfo?.id) {
|
||||
updateProject({
|
||||
projectId: projectInfo.id,
|
||||
updatedData: updatedProject,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{showModal && projectDetails && (
|
||||
<div
|
||||
className="modal fade show"
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: "block" }}
|
||||
aria-hidden="false"
|
||||
>
|
||||
<ManageProjectInfo
|
||||
project={projectDetails}
|
||||
|
||||
{showModal && projects_Details && (
|
||||
<GlobalModel isOpen={showModal} closeModal={handleClose}>
|
||||
<ManageProjectInfo
|
||||
project={projects_Details}
|
||||
handleSubmitForm={handleFormSubmit}
|
||||
onClose={handleClose}
|
||||
isPending={isPending}
|
||||
/>
|
||||
</div>
|
||||
</GlobalModel>
|
||||
)}
|
||||
|
||||
<div className="col-md-6 col-lg-4 col-xl-4 order-0 mb-4">
|
||||
<div className="card cursor-pointer">
|
||||
<div className={`card cursor-pointer ${isPending ? "bg-light opacity-50 pointer-events-none" : ""}`}>
|
||||
<div className="card-header pb-4">
|
||||
<div className="d-flex align-items-start">
|
||||
<div className="d-flex align-items-center">
|
||||
@ -143,23 +116,23 @@ const ProjectCard = ({ projectData, recall }) => {
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
{modifyProjectLoading ? (
|
||||
<div
|
||||
className="spinner-border spinner-border-sm text-secondary"
|
||||
role="status"
|
||||
>
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
) : (
|
||||
<i
|
||||
className="bx bx-dots-vertical-rounded bx-sm text-muted"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-offset="0,8"
|
||||
data-bs-placement="top"
|
||||
data-bs-custom-class="tooltip-dark"
|
||||
title="More Action"
|
||||
></i>
|
||||
)}
|
||||
{loading ? (
|
||||
<div
|
||||
className="spinner-border spinner-border-sm text-secondary"
|
||||
role="status"
|
||||
>
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
) : (
|
||||
<i
|
||||
className="bx bx-dots-vertical-rounded bx-sm text-muted"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-offset="0,8"
|
||||
data-bs-placement="top"
|
||||
data-bs-custom-class="tooltip-dark"
|
||||
title="More Action"
|
||||
></i>
|
||||
)}
|
||||
</button>
|
||||
<ul className="dropdown-menu dropdown-menu-end">
|
||||
<li>
|
||||
@ -251,7 +224,7 @@ const ProjectCard = ({ projectData, recall }) => {
|
||||
</div>
|
||||
<div className="d-flex justify-content-between align-items-center mb-2">
|
||||
<small className="text-body">
|
||||
Task: {projectInfo.completedWork} / {projectInfo.plannedWork}
|
||||
Task: {formatNumber(projectInfo.completedWork)} / {formatNumber(projectInfo.plannedWork)}
|
||||
</small>
|
||||
<small className="text-body">
|
||||
{Math.floor(
|
||||
|
||||
@ -8,7 +8,6 @@ import TaskModel from "./Infrastructure/TaskModel";
|
||||
import ProjectRepository, {
|
||||
TasksRepository,
|
||||
} from "../../repositories/ProjectRepository";
|
||||
import ProjectModal from "./ProjectModal";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import { MANAGE_PROJECT_INFRA } from "../../utils/constants";
|
||||
import InfraTable from "./Infrastructure/InfraTable";
|
||||
@ -17,334 +16,38 @@ import {
|
||||
clearCacheKey,
|
||||
getCachedData,
|
||||
} from "../../slices/apiDataManager";
|
||||
import { useProjectDetails } from "../../hooks/useProjects";
|
||||
import { useProjectDetails, useProjectInfra } from "../../hooks/useProjects";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { refreshData } from "../../slices/localVariablesSlice";
|
||||
import eventBus from "../../services/eventBus";
|
||||
import {useParams} from "react-router-dom";
|
||||
import GlobalModel from "../common/GlobalModel";
|
||||
|
||||
const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
|
||||
const ProjectInfra = ( {data, onDataChange, eachSiteEngineer} ) =>
|
||||
{
|
||||
const {projectId} = useParams()
|
||||
const reloadedData = useSelector((store) => store.localVariables.reload);
|
||||
const [expandedBuildings, setExpandedBuildings] = useState([]);
|
||||
const [ expandedBuildings, setExpandedBuildings ] = useState( [] );
|
||||
const {projectInfra,isLoading,error} = useProjectInfra(projectId)
|
||||
const { projects_Details, refetch, loading } = useProjectDetails(data?.id);
|
||||
const [project, setProject] = useState(projects_Details);
|
||||
const [modalConfig, setModalConfig] = useState({ type: null, data: null });
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [ project, setProject ] = useState( projects_Details );
|
||||
const ManageInfra = useHasUserPermission(MANAGE_PROJECT_INFRA);
|
||||
|
||||
const [isBuildingModalOpen, setIsBuildingModalOpen] = useState(false);
|
||||
const [isFloorModalOpen, setIsFloorModalOpen] = useState(false);
|
||||
const [isWorkAreaModelOpen, setIsWorkAreaModalOpen] = useState(false);
|
||||
const [isTaskModelOpen, setIsTaskModalOpen] = useState(false);
|
||||
const [isAssignRoleModal, setIsAssingRoleModal] = useState(false);
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
const [clearFormTrigger, setClearFormTrigger] = useState(false);
|
||||
const [CurrentBuilding, setCurrentBuilding] = useState("");
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const [showModalFloor, setshowModalFloor] = useState(false);
|
||||
const [showModalWorkArea, setshowModalWorkArea] = useState(false);
|
||||
const [showModalTask, setshowModalTask] = useState(false);
|
||||
const [showModalBuilding, setshowModalBuilding] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
setProject(projects_Details);
|
||||
setProject(projectInfra);
|
||||
}, [data, projects_Details]);
|
||||
|
||||
const openFloorModel = (projectData) => {
|
||||
setIsFloorModalOpen(true);
|
||||
};
|
||||
const closeFloorModel = () => {
|
||||
setIsFloorModalOpen(false);
|
||||
};
|
||||
|
||||
const openAssignModel = (assignData) => {
|
||||
setCurrentBuilding(assignData);
|
||||
setIsAssingRoleModal(true);
|
||||
};
|
||||
const openBuildingModel = (projectData) => {
|
||||
setIsBuildingModalOpen(true);
|
||||
};
|
||||
|
||||
const closeBuildingModel = () => {
|
||||
setIsBuildingModalOpen(false);
|
||||
};
|
||||
|
||||
const handleBuildingModelFormSubmit = (buildingmodel) => {
|
||||
if (buildingmodel.id == "" || buildingmodel.id == 0)
|
||||
delete buildingmodel.id;
|
||||
let data = [
|
||||
{
|
||||
building: buildingmodel,
|
||||
floor: null,
|
||||
workArea: null,
|
||||
},
|
||||
];
|
||||
submitData(data);
|
||||
};
|
||||
const handleFloorModelFormSubmit = (updatedFloor) => {
|
||||
if (updatedFloor.id == "") delete updatedFloor.id;
|
||||
submitData([
|
||||
{
|
||||
building: null,
|
||||
floor: updatedFloor,
|
||||
workArea: null,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
const openWorkAreaModel = (projectData) => {
|
||||
setIsWorkAreaModalOpen(true);
|
||||
};
|
||||
|
||||
const closeWorkAreaModel = () => {
|
||||
setIsWorkAreaModalOpen(false);
|
||||
};
|
||||
const handleWorkAreaModelFormSubmit = (updatedModel) => {
|
||||
if (updatedModel.id == "") delete updatedModel.id;
|
||||
submitData([
|
||||
{
|
||||
building: null,
|
||||
floor: null,
|
||||
workArea: updatedModel,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
const openTaskModel = (projectData) => {
|
||||
setIsTaskModalOpen(true);
|
||||
};
|
||||
|
||||
const closeTaskModel = () => {
|
||||
setIsTaskModalOpen(false);
|
||||
};
|
||||
|
||||
const handleTaskModelFormSubmit = (updatedModel) => {
|
||||
if (updatedModel.id == "") updatedModel.id = null;
|
||||
const updatedProject = { ...project };
|
||||
|
||||
ProjectRepository.manageProjectTasks([updatedModel])
|
||||
.then((response) => {
|
||||
onDataChange("task-change");
|
||||
showToast("Details updated successfully.", "success");
|
||||
// setClearFormTrigger( true );
|
||||
if (response?.data[0]) {
|
||||
const { workItemId, workItem } = response.data[0];
|
||||
|
||||
const updatedBuildings = updatedProject.buildings.map((building) =>
|
||||
building.id == updatedModel.buildingID
|
||||
? {
|
||||
...building,
|
||||
floors: building.floors.map((floor) =>
|
||||
floor.id == updatedModel.floorId
|
||||
? {
|
||||
...floor,
|
||||
workAreas: floor.workAreas.map((workArea) =>
|
||||
workArea.id === workItem?.workAreaId
|
||||
? {
|
||||
...workArea,
|
||||
workItems: workArea.workItems.some(
|
||||
(existingItem) =>
|
||||
existingItem.workItemId ===
|
||||
workItem.workItemId
|
||||
)
|
||||
? [...workArea.workItems] // Create a new array to trigger re-render
|
||||
: [...workArea.workItems, workItem],
|
||||
}
|
||||
: workArea
|
||||
),
|
||||
}
|
||||
: floor
|
||||
),
|
||||
}
|
||||
: building
|
||||
);
|
||||
updatedProject.buildings = updatedBuildings;
|
||||
// workItem update, but having local state issue there for needed to calling api
|
||||
clearCacheKey("projectInfo");
|
||||
refetch();
|
||||
|
||||
cacheData("projectInfo", {
|
||||
projectId: updatedProject.id,
|
||||
data: updatedProject,
|
||||
});
|
||||
|
||||
setProject(updatedProject);
|
||||
// closeTaskModel()
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
showToast(error.message, "error");
|
||||
});
|
||||
};
|
||||
|
||||
const submitData = async (infraObject) => {
|
||||
try {
|
||||
let response = await ProjectRepository.manageProjectInfra(infraObject);
|
||||
const entity = response.data;
|
||||
|
||||
const updatedProject = { ...project };
|
||||
// Handle the building data
|
||||
if (entity.building) {
|
||||
const { id, name, description } = entity.building;
|
||||
const updatedBuildings = updatedProject?.buildings?.map((building) =>
|
||||
building.id === id ? { ...building, name, description } : building
|
||||
);
|
||||
|
||||
// Add building if it doesn't exist
|
||||
if (!updatedProject.buildings.some((building) => building.id === id)) {
|
||||
updatedBuildings.push({
|
||||
id: id,
|
||||
name,
|
||||
description,
|
||||
floors: [],
|
||||
});
|
||||
}
|
||||
|
||||
updatedProject.buildings = updatedBuildings;
|
||||
|
||||
// Update the cache for buildings
|
||||
cacheData("projectInfo", {
|
||||
projectId: updatedProject.id,
|
||||
data: updatedProject,
|
||||
});
|
||||
setProject((prevProject) => ({
|
||||
...prevProject,
|
||||
buildings: updatedBuildings,
|
||||
}));
|
||||
// closeBuildingModel()
|
||||
}
|
||||
// Handle the floor data
|
||||
else if (entity.floor) {
|
||||
const { buildingId, id, floorName } = entity.floor;
|
||||
const updatedBuildings = updatedProject?.buildings?.map((building) =>
|
||||
building.id == buildingId
|
||||
? {
|
||||
...building,
|
||||
floors: building.floors
|
||||
.map((floor) =>
|
||||
floor.id === id
|
||||
? {
|
||||
...floor,
|
||||
floorName, // Update the floor name only
|
||||
// Keep other properties as they are (including workArea)
|
||||
}
|
||||
: floor
|
||||
)
|
||||
// Add the new floor if it doesn't already exist
|
||||
.concat(
|
||||
!building.floors.some((floor) => floor.id === id)
|
||||
? [{ id: id, floorName, workAreas: [] }] // New floor added with workArea set to null
|
||||
: []
|
||||
),
|
||||
}
|
||||
: building
|
||||
);
|
||||
|
||||
updatedProject.buildings = updatedBuildings;
|
||||
|
||||
// Cache the updated project
|
||||
cacheData("projectInfo", {
|
||||
projectId: updatedProject.id,
|
||||
data: updatedProject,
|
||||
});
|
||||
setProject(updatedProject);
|
||||
// closeFloorModel()
|
||||
}
|
||||
// Handle the work area data
|
||||
else if (entity.workArea) {
|
||||
let buildingId = infraObject[0].workArea.buildingId;
|
||||
|
||||
const { floorId, areaName, id } = entity.workArea;
|
||||
// Check if the workArea exists, otherwise create a new one
|
||||
const updatedBuildings = updatedProject.buildings.map((building) =>
|
||||
building.id == buildingId
|
||||
? {
|
||||
...building,
|
||||
floors: building.floors.map((floor) =>
|
||||
floor.id == floorId
|
||||
? {
|
||||
...floor,
|
||||
workAreas: floor.workAreas.some(
|
||||
(workArea) => workArea.id === id
|
||||
)
|
||||
? floor.workAreas.map((workArea) =>
|
||||
workArea.id === id
|
||||
? { ...workArea, areaName }
|
||||
: workArea
|
||||
)
|
||||
: [
|
||||
...floor.workAreas,
|
||||
{ id, areaName, workItems: [] },
|
||||
],
|
||||
}
|
||||
: floor
|
||||
),
|
||||
}
|
||||
: building
|
||||
);
|
||||
|
||||
updatedProject.buildings = updatedBuildings;
|
||||
|
||||
// Update the cache for work areas
|
||||
cacheData("projectInfo", {
|
||||
projectId: updatedProject.id,
|
||||
data: updatedProject,
|
||||
});
|
||||
setProject(updatedProject);
|
||||
// closeWorkAreaModel()
|
||||
}
|
||||
// Handle the task (workItem) data
|
||||
else {
|
||||
console.error("Unsupported data type for submitData", entity);
|
||||
}
|
||||
} catch (Err) {
|
||||
showToast("Somthing wrong", "error");
|
||||
}
|
||||
};
|
||||
|
||||
const toggleBuilding = (id) => {
|
||||
setExpandedBuildings((prev) =>
|
||||
prev.includes(id) ? prev.filter((bid) => bid !== id) : [...prev, id]
|
||||
);
|
||||
};
|
||||
|
||||
const handleModalData = (type, modaldata) => {
|
||||
setModalConfig({ type: type, data: modaldata });
|
||||
};
|
||||
const openModal = () => {
|
||||
const modalElement = document.getElementById("building-model");
|
||||
const modal = new Modal(modalElement, {
|
||||
backdrop: false,
|
||||
keyboard: true,
|
||||
focus: true,
|
||||
});
|
||||
modal.show();
|
||||
};
|
||||
const closeModal = () => {
|
||||
setIsModalOpen(false);
|
||||
setModalConfig(null);
|
||||
|
||||
const modalElement = document.getElementById("building-model");
|
||||
if (modalElement) {
|
||||
modalElement.classList.remove("show"); // Remove modal visibility class
|
||||
modalElement.style.display = "none"; // Hide the modal element
|
||||
}
|
||||
|
||||
document.body.classList.remove("modal-open"); // Remove modal-open class from body
|
||||
|
||||
// Remove the modal backdrop
|
||||
const backdropElement = document.querySelector(".modal-backdrop");
|
||||
if (backdropElement) {
|
||||
backdropElement.classList.remove("modal-backdrop"); // Remove backdrop class
|
||||
backdropElement.style.display = "none"; // Hide the backdrop element
|
||||
}
|
||||
document.body.style.overflow = "auto";
|
||||
};
|
||||
|
||||
const handleShow = () => setShowModal(true);
|
||||
const handleClose = () => setShowModal(false);
|
||||
useEffect(() => {
|
||||
if (reloadedData) {
|
||||
refetch();
|
||||
dispatch(refreshData(false));
|
||||
}
|
||||
}, [reloadedData]);
|
||||
// useEffect(() => {
|
||||
// if (reloadedData) {
|
||||
// refetch();
|
||||
// dispatch(refreshData(false));
|
||||
// }
|
||||
// }, [reloadedData]);
|
||||
|
||||
const signalRHandler = (response) => {
|
||||
setProject(response);
|
||||
@ -352,82 +55,30 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`modal fade ${showModal ? "show" : ""}`}
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: showModal ? "block" : "none" }}
|
||||
aria-hidden={!showModal}
|
||||
>
|
||||
{showModalBuilding && <GlobalModel isOpen={showModalBuilding} size="md" closeModal={() => setshowModalBuilding( false )}>
|
||||
<BuildingModel
|
||||
project={project}
|
||||
onClose={handleClose}
|
||||
onSubmit={handleBuildingModelFormSubmit}
|
||||
clearTrigger={clearFormTrigger}
|
||||
onClearComplete={() => setClearFormTrigger(false)}
|
||||
></BuildingModel>
|
||||
</div>
|
||||
{isFloorModalOpen && (
|
||||
<div
|
||||
className="modal fade show"
|
||||
id="floor-model"
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: "block" }}
|
||||
aria-hidden="false"
|
||||
>
|
||||
<FloorModel
|
||||
project={project}
|
||||
onClose={closeFloorModel}
|
||||
onSubmit={handleFloorModelFormSubmit}
|
||||
clearTrigger={clearFormTrigger}
|
||||
onClearComplete={() => setClearFormTrigger(false)}
|
||||
project={projectInfra}
|
||||
onClose={() => setshowModalBuilding( false )}
|
||||
/>
|
||||
</GlobalModel>}
|
||||
{showModalFloor && <GlobalModel isOpen={showModalFloor} size="md" closeModal={()=>setshowModalFloor(false)}>
|
||||
<FloorModel
|
||||
project={projectInfra}
|
||||
onClose={()=>setshowModalFloor(false)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isWorkAreaModelOpen && (
|
||||
<div
|
||||
className="modal fade show"
|
||||
id="work-area-model"
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: "block" }}
|
||||
aria-hidden="false"
|
||||
>
|
||||
</GlobalModel>}
|
||||
{showModalWorkArea && <GlobalModel isOpen={showModalWorkArea} size="lg" closeModal={()=>setshowModalWorkArea(false)} >
|
||||
<WorkAreaModel
|
||||
project={project}
|
||||
onClose={closeWorkAreaModel}
|
||||
onSubmit={handleWorkAreaModelFormSubmit}
|
||||
clearTrigger={clearFormTrigger}
|
||||
onClearComplete={() => setClearFormTrigger(false)}
|
||||
project={projectInfra}
|
||||
onClose={()=>setshowModalWorkArea(false)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isTaskModelOpen && (
|
||||
<div
|
||||
className="modal fade show"
|
||||
id="task-model"
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: "block" }}
|
||||
aria-hidden="false"
|
||||
>
|
||||
<TaskModel
|
||||
project={project}
|
||||
onClose={closeTaskModel}
|
||||
onSubmit={handleTaskModelFormSubmit}
|
||||
clearTrigger={clearFormTrigger}
|
||||
onClearComplete={() => setClearFormTrigger(false)}
|
||||
</GlobalModel>}
|
||||
{showModalTask && ( <GlobalModel isOpen={showModalTask} size="lg" closeModal={()=>setshowModalTask(false)}>
|
||||
<TaskModel
|
||||
project={projectInfra}
|
||||
onClose={()=>setshowModalTask(false)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isModalOpen && (
|
||||
<ProjectModal modalConfig={modalConfig} closeModal={closeModal} />
|
||||
)}
|
||||
|
||||
</GlobalModel>)}
|
||||
<div className="col-md-12 col-lg-12 col-xl-12 order-0 mb-4">
|
||||
<div className="card">
|
||||
<div className="card-body" style={{ padding: "0.5rem" }}>
|
||||
@ -441,15 +92,15 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
|
||||
<button
|
||||
type="button"
|
||||
className="link-button link-button-sm m-1 "
|
||||
onClick={handleShow}
|
||||
onClick={()=>setshowModalBuilding(true)}
|
||||
>
|
||||
<i className="bx bx-plus-circle me-2"></i>
|
||||
<i className="bx bx-plus-circle me-2"></i>
|
||||
Manage Building
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="link-button m-1"
|
||||
onClick={() => openFloorModel()}
|
||||
onClick={()=>setshowModalFloor(true)}
|
||||
>
|
||||
<i className="bx bx-plus-circle me-2"></i>
|
||||
Manage Floors
|
||||
@ -457,7 +108,7 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
|
||||
<button
|
||||
type="button"
|
||||
className="link-button m-1"
|
||||
onClick={() => openWorkAreaModel()}
|
||||
onClick={() => setshowModalWorkArea(true)}
|
||||
>
|
||||
<i className="bx bx-plus-circle me-2"></i>
|
||||
Manage Work Areas
|
||||
@ -465,7 +116,7 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
|
||||
<button
|
||||
type="button"
|
||||
className="link-button m-1"
|
||||
onClick={() => openTaskModel()}
|
||||
onClick={()=>setshowModalTask(true)}
|
||||
>
|
||||
<i className="bx bx-plus-circle me-2"></i>
|
||||
Create Tasks
|
||||
@ -473,15 +124,16 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="row ">
|
||||
{loading && <p>Loading....</p>}
|
||||
{project && project.buildings?.length > 0 && (
|
||||
{isLoading && <p>Loading....</p>}
|
||||
{projectInfra && projectInfra?.length > 0 && (
|
||||
<InfraTable
|
||||
buildings={project?.buildings}
|
||||
projectId={project.id}
|
||||
handleFloor={submitData}
|
||||
signalRHandler = {signalRHandler}
|
||||
buildings={projectInfra}
|
||||
projectId={projectId}
|
||||
// handleFloor={submitData}
|
||||
// signalRHandler ={signalRHandler}
|
||||
/>
|
||||
)}
|
||||
{!isLoading && projectInfra?.length == 0 && <div className="mt-5"><p>No Infra Avaiable</p></div>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -23,7 +23,6 @@ const ProjectModal = ({modalConfig,closeModal}) => {
|
||||
></button>
|
||||
<div className="text-center mb-2"></div>
|
||||
|
||||
{/* Modal Component */}
|
||||
|
||||
{modalConfig?.type === "assignRole" && <AssignRole assignData={modalConfig?.data} onClose={closeModal} />}
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ const ProjectNav = ({ onPillClick, activePill }) => {
|
||||
className={`nav-link ${activePill === "profile" ? "active" : ""}`}
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault(); // Prevent page reload
|
||||
e.preventDefault();
|
||||
onPillClick("profile");
|
||||
}}
|
||||
>
|
||||
@ -29,7 +29,7 @@ const ProjectNav = ({ onPillClick, activePill }) => {
|
||||
className={`nav-link ${activePill === "teams" ? "active" : ""}`}
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault(); // Prevent page reload
|
||||
e.preventDefault();
|
||||
onPillClick("teams");
|
||||
}}
|
||||
>
|
||||
@ -41,27 +41,14 @@ const ProjectNav = ({ onPillClick, activePill }) => {
|
||||
className={`nav-link ${activePill === "infra" ? "active" : ""}`}
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault(); // Prevent page reload
|
||||
e.preventDefault();
|
||||
onPillClick("infra");
|
||||
}}
|
||||
>
|
||||
<i className="bx bx-grid-alt bx-sm me-1_5"></i> <span className="d-none d-md-inline">Infrastructure</span>
|
||||
</a>
|
||||
</li>
|
||||
{/* <li className="nav-item">
|
||||
<a
|
||||
className={`nav-link ${
|
||||
activePill === "workplan" ? "active" : ""
|
||||
}`}
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault(); // Prevent page reload
|
||||
onPillClick("workplan");
|
||||
}}
|
||||
>
|
||||
<i className="bx bx-link bx-sm me-1_5"></i> Work Plan
|
||||
</a>
|
||||
</li> */}
|
||||
|
||||
<li className="nav-item">
|
||||
<a
|
||||
className={`nav-link ${
|
||||
|
||||
@ -3,61 +3,14 @@ import {
|
||||
useEmployeesByProjectAllocated,
|
||||
useProjects,
|
||||
} from "../../hooks/useProjects";
|
||||
import { formatNumber } from "../../utils/dateUtils";
|
||||
import ProgressBar from "../common/ProgressBar";
|
||||
|
||||
const ProjectOverview = ({ project }) => {
|
||||
const { projects } = useProjects();
|
||||
const getProgress = (planned, completed) => {
|
||||
return (completed * 100) / planned + "%";
|
||||
};
|
||||
const getProgressInNumber = (planned, completed) => {
|
||||
var number = (completed * 100) / planned;
|
||||
return FormattedNumber(number);
|
||||
};
|
||||
|
||||
const project_detail = projects.find((pro) => pro.id == project);
|
||||
|
||||
// Utility function to check if a number has a decimal part
|
||||
const hasDecimal = (num) => {
|
||||
// Convert to string and check for a decimal point
|
||||
// Or, check if the number is not equal to its integer part
|
||||
return num % 1 !== 0;
|
||||
};
|
||||
|
||||
// FormattedNumber component to display numbers with conditional decimal places
|
||||
function FormattedNumber(value, locale = "en-US") {
|
||||
// Ensure the value is a number
|
||||
const numericValue = parseFloat(value);
|
||||
|
||||
// Handle non-numeric values gracefully
|
||||
if (isNaN(numericValue)) {
|
||||
return <span>Invalid Number</span>;
|
||||
}
|
||||
|
||||
let options = {};
|
||||
|
||||
// Determine formatting options based on whether the number has a decimal part
|
||||
if (hasDecimal(numericValue)) {
|
||||
// If it has a decimal, format to exactly two decimal places
|
||||
options = {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
};
|
||||
} else {
|
||||
// If it's a whole number, format to zero decimal places
|
||||
options = {
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0,
|
||||
};
|
||||
}
|
||||
|
||||
// Use Intl.NumberFormat for robust and locale-aware formatting
|
||||
const formattedString = new Intl.NumberFormat(locale, options).format(
|
||||
numericValue
|
||||
);
|
||||
|
||||
return formattedString;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="card mb-6">
|
||||
<div className="card-header text-start">
|
||||
@ -72,12 +25,12 @@ const ProjectOverview = ({ project }) => {
|
||||
<li className="d-flex align-items-center mb-3">
|
||||
<i className="bx bx-check"></i>
|
||||
<span className="fw-medium mx-2">Task Planned:</span>{" "}
|
||||
<span>{project_detail?.plannedWork}</span>
|
||||
<span>{formatNumber(project_detail?.plannedWork)}</span>
|
||||
</li>
|
||||
<li className="d-flex align-items-center mb-3">
|
||||
<i className="bx bx-star"></i>
|
||||
<span className="fw-medium mx-2">Task Completed:</span>{" "}
|
||||
<span>{project_detail?.completedWork}</span>
|
||||
<span>{formatNumber(project_detail?.completedWork)}</span>
|
||||
</li>
|
||||
<li className="d-flex align-items-center mb-3">
|
||||
<i className="bx bx-user"></i>
|
||||
@ -89,16 +42,21 @@ const ProjectOverview = ({ project }) => {
|
||||
<>
|
||||
<div className="d-flex text-end mb-2 mt-5">
|
||||
<small className="text-body text-muted ">
|
||||
{Math.floor(
|
||||
{/* {Math.floor(
|
||||
getProgressInNumber(
|
||||
project_detail.plannedWork,
|
||||
|
||||
project_detail.completedWork
|
||||
)
|
||||
) || 0}{" "}
|
||||
) || 0}{" "} */}
|
||||
{
|
||||
(formatNumber(project_detail.plannedWork),
|
||||
"/",
|
||||
formatNumber(project_detail.completedWork))
|
||||
}
|
||||
% Completed
|
||||
</small>
|
||||
</div>
|
||||
<div
|
||||
{/* <div
|
||||
className="progress mb-4 rounded"
|
||||
style={{ height: "8px" }}
|
||||
>
|
||||
@ -115,7 +73,12 @@ const ProjectOverview = ({ project }) => {
|
||||
aria-valuemin="0"
|
||||
aria-valuemax={project_detail.plannedWork}
|
||||
></div>
|
||||
</div>
|
||||
</div> */}
|
||||
<ProgressBar
|
||||
completedWork={formatNumber(project_detail?.completedWork)}
|
||||
plannedWork={formatNumber(project_detail?.plannedWork)}
|
||||
className="m-0 text-info"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import MapUsers from "./MapUsers";
|
||||
import { Link, NavLink, useNavigate } from "react-router-dom";
|
||||
import { Link, NavLink, useNavigate, useParams } from "react-router-dom";
|
||||
|
||||
import showToast from "../../services/toastService";
|
||||
import Avatar from "../common/Avatar";
|
||||
@ -14,8 +14,11 @@ import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import { ASSIGN_TO_PROJECT } from "../../utils/constants";
|
||||
import ConfirmModal from "../common/ConfirmModal";
|
||||
import eventBus from "../../services/eventBus";
|
||||
import {useEmployeesByProjectAllocated, useManageProjectAllocation} from "../../hooks/useProjects";
|
||||
|
||||
const Teams = ({ project }) => {
|
||||
const Teams = () =>
|
||||
{
|
||||
const {projectId} = useParams()
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { data, loading } = useMaster();
|
||||
@ -26,87 +29,64 @@ const Teams = ({ project }) => {
|
||||
const [filteredEmployees, setFilteredEmployees] = useState([]);
|
||||
const [removingEmployeeId, setRemovingEmployeeId] = useState(null);
|
||||
const [assignedLoading, setAssignedLoading] = useState(false);
|
||||
const [ employeeLodaing, setEmployeeLoading ] = useState( false );
|
||||
const [ activeEmployee, setActiveEmployee ] = useState( true )
|
||||
const [deleteEmployee,setDeleteEmplyee] = useState(null)
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const HasAssignUserPermission = useHasUserPermission( ASSIGN_TO_PROJECT );
|
||||
const[IsDeleteModal,setIsDeleteModal] = useState(false)
|
||||
const [ IsDeleteModal, setIsDeleteModal ] = useState( false )
|
||||
const {projectEmployees, loading:employeeLodaing, refetch} = useEmployeesByProjectAllocated( projectId )
|
||||
const {
|
||||
mutate: submitAllocations,
|
||||
isPending,
|
||||
isSuccess,
|
||||
isError,
|
||||
} = useManageProjectAllocation({
|
||||
onSuccessCallback: () => {
|
||||
setRemovingEmployeeId(null);
|
||||
setAssignedLoading(false);
|
||||
setDeleteEmplyee(null);
|
||||
closeDeleteModal();
|
||||
},
|
||||
onErrorCallback: () => {
|
||||
closeDeleteModal();
|
||||
},
|
||||
});
|
||||
|
||||
const fetchEmployees = async () => {
|
||||
try {
|
||||
setEmployeeLoading(true);
|
||||
|
||||
// if (!empRoles) {
|
||||
ProjectRepository.getProjectAllocation(project.id)
|
||||
.then((response) => {
|
||||
setEmployees(response.data);
|
||||
setFilteredEmployees( response.data.filter( ( emp ) => emp.isActive ) );
|
||||
setEmployeeLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
setError("Failed to fetch data.");
|
||||
setEmployeeLoading(false);
|
||||
});
|
||||
} catch (err) {
|
||||
setError("Failed to fetch activities.");
|
||||
}
|
||||
};
|
||||
|
||||
const submitAllocations = (items,added) => {
|
||||
ProjectRepository.manageProjectAllocation(items)
|
||||
.then((response) => {
|
||||
fetchEmployees();
|
||||
if ( added )
|
||||
{
|
||||
showToast("Employee Assigned Successfully", "success");
|
||||
}else{
|
||||
showToast("Removed Employee Successfully", "success");
|
||||
}
|
||||
setRemovingEmployeeId(null);
|
||||
setAssignedLoading( false );
|
||||
setDeleteEmplyee( null )
|
||||
closeDeleteModal()
|
||||
})
|
||||
.catch((error) => {
|
||||
const message = error.response.data.message || error.message || "Error Occured during Api Call";
|
||||
showToast( message, "error" );
|
||||
closeDeleteModal()
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const removeAllocation = (item) => {
|
||||
setRemovingEmployeeId(item.id);
|
||||
submitAllocations([
|
||||
setRemovingEmployeeId(item.id);
|
||||
|
||||
submitAllocations({
|
||||
items: [
|
||||
{
|
||||
empID: item.employeeId,
|
||||
jobRoleId: item.jobRoleId,
|
||||
projectId: project.id,
|
||||
projectId: projectId,
|
||||
status: false,
|
||||
},
|
||||
] ,false);
|
||||
|
||||
};
|
||||
],
|
||||
added: false,
|
||||
});
|
||||
};
|
||||
|
||||
const handleEmpAlicationFormSubmit = (allocaionObj) => {
|
||||
let items = allocaionObj.map((item) => {
|
||||
return {
|
||||
empID: item.empID,
|
||||
jobRoleId: item.jobRoleId,
|
||||
projectId: project.id,
|
||||
projectId: projectId,
|
||||
status: true,
|
||||
};
|
||||
});
|
||||
|
||||
submitAllocations(items, true);
|
||||
submitAllocations({ items, added: true });
|
||||
|
||||
// Force switch to active view after assignment
|
||||
setActiveEmployee(true);
|
||||
setFilteredEmployees(employees.filter((emp) => emp.isActive));
|
||||
|
||||
// Also update dropdown select if needed
|
||||
const dropdown = document.querySelector('select[name="DataTables_Table_0_length"]');
|
||||
if (dropdown) dropdown.value = "true";
|
||||
};
|
||||
@ -146,8 +126,12 @@ const Teams = ({ project }) => {
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
fetchEmployees();
|
||||
}, []);
|
||||
if ( projectEmployees )
|
||||
{
|
||||
setEmployees(projectEmployees);
|
||||
setFilteredEmployees( projectEmployees?.filter( ( emp ) => emp.isActive ) );
|
||||
}
|
||||
}, [projectEmployees,employeeLodaing]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
@ -176,11 +160,11 @@ const Teams = ({ project }) => {
|
||||
|
||||
const handler = useCallback(
|
||||
(msg) => {
|
||||
if (msg.projectIds.some((item) => item === project.id)) {
|
||||
fetchEmployees();
|
||||
if (msg.projectIds.some((item) => item === projectId)) {
|
||||
refetch();
|
||||
}
|
||||
},
|
||||
[]
|
||||
[projectId, refetch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@ -191,9 +175,9 @@ const Teams = ({ project }) => {
|
||||
const employeeHandler = useCallback(
|
||||
(msg) => {
|
||||
if(filteredEmployees.some((item) => item.employeeId == msg.employeeId)){
|
||||
fetchEmployees();
|
||||
refetch();
|
||||
}
|
||||
},[filteredEmployees]
|
||||
},[filteredEmployees, refetch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@ -211,7 +195,7 @@ const Teams = ({ project }) => {
|
||||
aria-hidden="true"
|
||||
>
|
||||
<MapUsers
|
||||
projectId={project?.id}
|
||||
projectId={projectId}
|
||||
onClose={onModelClose}
|
||||
empJobRoles={empJobRoles}
|
||||
onSubmit={handleEmpAlicationFormSubmit}
|
||||
@ -240,7 +224,7 @@ const Teams = ({ project }) => {
|
||||
message={"Are you sure you want delete?"}
|
||||
onSubmit={removeAllocation}
|
||||
onClose={closeDeleteModal}
|
||||
loading={employeeLodaing}
|
||||
loading={isPending}
|
||||
paramData={deleteEmployee}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -48,9 +48,8 @@
|
||||
}
|
||||
|
||||
.img-preview img {
|
||||
max-width: 100%;
|
||||
max-height: 80vh;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
width: 100%;
|
||||
height: auto;
|
||||
aspect-ratio: 16 / 10;
|
||||
object-fit: cover;
|
||||
}
|
||||
@ -7,6 +7,7 @@ import { MasterRespository } from "../../repositories/MastersRepository";
|
||||
import { clearApiCacheKey } from "../../slices/apiCacheSlice";
|
||||
import { getCachedData, cacheData } from "../../slices/apiDataManager";
|
||||
import showToast from "../../services/toastService";
|
||||
import {useCreateActivity} from "../../hooks/masterHook/useMaster";
|
||||
|
||||
const schema = z.object({
|
||||
activityName: z.string().min(1, { message: "Activity Name is required" }),
|
||||
@ -23,101 +24,96 @@ const schema = z.object({
|
||||
});
|
||||
|
||||
const CreateActivity = ({ onClose }) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const maxDescriptionLength = 255;
|
||||
const { mutate: createActivity, isPending: isLoading } = useCreateActivity(() => onClose?.());
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
setValue,
|
||||
clearErrors,
|
||||
setError,
|
||||
getValues,
|
||||
reset,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
activityName: "",
|
||||
unitOfMeasurement: "",
|
||||
checkList: [],
|
||||
},
|
||||
});
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
setValue,
|
||||
clearErrors,
|
||||
setError,
|
||||
getValues,
|
||||
reset,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
activityName: "",
|
||||
unitOfMeasurement: "",
|
||||
checkList: [],
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
fields: checkListItems,
|
||||
append,
|
||||
remove,
|
||||
} = useFieldArray({
|
||||
control,
|
||||
name: "checkList",
|
||||
});
|
||||
|
||||
const {
|
||||
fields: checkListItems,
|
||||
append,
|
||||
remove,
|
||||
} = useFieldArray({
|
||||
control,
|
||||
name: "checkList",
|
||||
});
|
||||
const addChecklistItem = useCallback(() => {
|
||||
const values = getValues("checkList");
|
||||
const lastIndex = checkListItems.length - 1;
|
||||
|
||||
// Form submission handler
|
||||
const onSubmit = (data) => {
|
||||
setIsLoading(true);
|
||||
|
||||
MasterRespository.createActivity(data)
|
||||
.then( ( resp ) =>
|
||||
{
|
||||
|
||||
const cachedData = getCachedData("Activity");
|
||||
const updatedData = [ ...cachedData, resp?.data ];
|
||||
cacheData("Activity", updatedData);
|
||||
showToast("Activity Successfully Added.", "success");
|
||||
setIsLoading(false);
|
||||
handleClose()
|
||||
})
|
||||
.catch((error) => {
|
||||
showToast(error.message, "error");
|
||||
setIsLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const addChecklistItem = () => {
|
||||
const values = getValues("checkList");
|
||||
const lastIndex = checkListItems.length - 1;
|
||||
if (
|
||||
checkListItems.length > 0 &&
|
||||
(!values?.[lastIndex] || values[lastIndex].description.trim() === "")
|
||||
) {
|
||||
setError(`checkList.${lastIndex}.description`, {
|
||||
type: "manual",
|
||||
message: "Please fill this checklist item before adding another.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
clearErrors(`checkList.${lastIndex}.description`);
|
||||
append({
|
||||
id: null,
|
||||
description: "",
|
||||
isMandatory: false,
|
||||
if (
|
||||
checkListItems.length > 0 &&
|
||||
(!values?.[lastIndex] || values[lastIndex].description.trim() === "")
|
||||
) {
|
||||
setError(`checkList.${lastIndex}.description`, {
|
||||
type: "manual",
|
||||
message: "Please fill this checklist item before adding another.",
|
||||
});
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
const removeChecklistItem = (index) => {
|
||||
remove(index);
|
||||
};
|
||||
clearErrors(`checkList.${lastIndex}.description`);
|
||||
append({ id: null, description: "", isMandatory: false });
|
||||
}, [checkListItems, getValues, append, setError, clearErrors]);
|
||||
|
||||
const removeChecklistItem = useCallback((index) => {
|
||||
remove(index);
|
||||
}, [remove]);
|
||||
|
||||
const handleChecklistChange = (index, value) => {
|
||||
setValue(`checkList.${index}`, value);
|
||||
};
|
||||
const handleChecklistChange = useCallback((index, value) => {
|
||||
setValue(`checkList.${index}`, value);
|
||||
}, [setValue]);
|
||||
|
||||
const handleClose = () => {
|
||||
reset();
|
||||
onClose();
|
||||
};
|
||||
const onSubmit = (formData) => {
|
||||
createActivity(formData);
|
||||
};
|
||||
// const onSubmit = (data) => {
|
||||
// setIsLoading(true);
|
||||
|
||||
// MasterRespository.createActivity(data)
|
||||
// .then( ( resp ) =>
|
||||
// {
|
||||
|
||||
// const cachedData = getCachedData("Activity");
|
||||
// const updatedData = [ ...cachedData, resp?.data ];
|
||||
// cacheData("Activity", updatedData);
|
||||
// showToast("Activity Successfully Added.", "success");
|
||||
// setIsLoading(false);
|
||||
// handleClose()
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// showToast(error.message, "error");
|
||||
// setIsLoading(false);
|
||||
// });
|
||||
// };
|
||||
const handleClose = useCallback(() => {
|
||||
reset();
|
||||
onClose();
|
||||
}, [reset, onClose]);
|
||||
|
||||
useEffect(() => {
|
||||
const tooltipTriggerList = Array.from(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
||||
tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el));
|
||||
}, []);
|
||||
|
||||
// for tooltip
|
||||
useEffect(() => {
|
||||
const tooltipTriggerList = Array.from(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
||||
tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el));
|
||||
}, []);
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
{/* <h6>Create Activity</h6> */}
|
||||
|
||||
@ -6,6 +6,7 @@ import { MasterRespository } from '../../repositories/MastersRepository';
|
||||
import { clearApiCacheKey } from '../../slices/apiCacheSlice';
|
||||
import { getCachedData,cacheData } from '../../slices/apiDataManager';
|
||||
import showToast from '../../services/toastService';
|
||||
import {useCreateContactCategory} from '../../hooks/masterHook/useMaster';
|
||||
|
||||
|
||||
const schema = z.object({
|
||||
@ -16,51 +17,52 @@ const schema = z.object({
|
||||
|
||||
const CreateContactCategory = ({onClose}) => {
|
||||
|
||||
const[isLoading,setIsLoading] = useState(false)
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },reset
|
||||
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
description: "",
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (data) => {
|
||||
setIsLoading(true)
|
||||
MasterRespository.createContactCategory(data).then((resp)=>{
|
||||
setIsLoading(false)
|
||||
resetForm()
|
||||
const cachedData = getCachedData("Contact Category");
|
||||
const updatedData = [...cachedData, resp?.data];
|
||||
cacheData("Contact Category", updatedData);
|
||||
showToast("Contact Category Added successfully.", "success");
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
reset,
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
description: "",
|
||||
},
|
||||
});
|
||||
|
||||
onClose()
|
||||
}).catch((error)=>{
|
||||
showToast(error?.response?.data?.message, "error");
|
||||
setIsLoading(false)
|
||||
})
|
||||
};
|
||||
const resetForm = () => {
|
||||
reset({
|
||||
name: "",
|
||||
description: ""
|
||||
});
|
||||
setDescriptionLength(0);
|
||||
}
|
||||
const [descriptionLength, setDescriptionLength] = useState(0);
|
||||
const maxDescriptionLength = 255;
|
||||
|
||||
useEffect(()=>{
|
||||
return ()=>resetForm()
|
||||
},[])
|
||||
|
||||
const [descriptionLength, setDescriptionLength] = useState(0);
|
||||
const maxDescriptionLength = 255;
|
||||
const { mutate: createContactCategory, isPending: isLoading } = useCreateContactCategory(() => onClose?.());
|
||||
|
||||
const onSubmit = (payload) => {
|
||||
createContactCategory(payload);
|
||||
};
|
||||
// const onSubmit = (data) => {
|
||||
// setIsLoading(true)
|
||||
// MasterRespository.createContactCategory(data).then((resp)=>{
|
||||
// setIsLoading(false)
|
||||
// resetForm()
|
||||
// const cachedData = getCachedData("Contact Category");
|
||||
// const updatedData = [...cachedData, resp?.data];
|
||||
// cacheData("Contact Category", updatedData);
|
||||
// showToast("Contact Category Added successfully.", "success");
|
||||
|
||||
// onClose()
|
||||
// }).catch((error)=>{
|
||||
// showToast(error?.response?.data?.message, "error");
|
||||
// setIsLoading(false)
|
||||
// })
|
||||
// };
|
||||
const resetForm = () => {
|
||||
reset({ name: "", description: "" });
|
||||
setDescriptionLength(0);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
return () => resetForm();
|
||||
}, []);
|
||||
|
||||
return (<>
|
||||
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="col-12 col-md-12">
|
||||
|
||||
@ -6,6 +6,7 @@ import { MasterRespository } from '../../repositories/MastersRepository';
|
||||
import { clearApiCacheKey } from '../../slices/apiCacheSlice';
|
||||
import { getCachedData,cacheData } from '../../slices/apiDataManager';
|
||||
import showToast from '../../services/toastService';
|
||||
import {useCreateContactTag} from '../../hooks/masterHook/useMaster';
|
||||
|
||||
|
||||
const schema = z.object({
|
||||
@ -15,53 +16,53 @@ const schema = z.object({
|
||||
});
|
||||
|
||||
const CreateContactTag = ({onClose}) => {
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
reset,
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
description: "",
|
||||
},
|
||||
});
|
||||
|
||||
const[isLoading,setIsLoading] = useState(false)
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },reset
|
||||
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
description: "",
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (data) => {
|
||||
setIsLoading(true)
|
||||
MasterRespository.createContactTag(data).then((resp)=>{
|
||||
setIsLoading(false)
|
||||
resetForm()
|
||||
debugger
|
||||
const cachedData = getCachedData("Contact Tag");
|
||||
const updatedData = [...cachedData, resp?.data];
|
||||
cacheData("Contact Tag", updatedData);
|
||||
showToast("Contact Tag Added successfully.", "success");
|
||||
console.log(getCachedData("Contact Tag"))
|
||||
onClose()
|
||||
}).catch((error)=>{
|
||||
showToast(error?.response?.data?.message, "error");
|
||||
setIsLoading(false)
|
||||
})
|
||||
};
|
||||
const resetForm = () => {
|
||||
reset({
|
||||
name: "",
|
||||
description: ""
|
||||
});
|
||||
setDescriptionLength(0);
|
||||
}
|
||||
const [descriptionLength, setDescriptionLength] = useState(0);
|
||||
const maxDescriptionLength = 255;
|
||||
|
||||
useEffect(()=>{
|
||||
return ()=>resetForm()
|
||||
},[])
|
||||
|
||||
const [descriptionLength, setDescriptionLength] = useState(0);
|
||||
const maxDescriptionLength = 255;
|
||||
const { mutate: createContactTag, isPending: isLoading } = useCreateContactTag(() => onClose?.());
|
||||
|
||||
const onSubmit = (payload) => {
|
||||
createContactTag(payload);
|
||||
};
|
||||
// const onSubmit = (data) => {
|
||||
// setIsLoading(true)
|
||||
// MasterRespository.createContactTag(data).then((resp)=>{
|
||||
// setIsLoading(false)
|
||||
// resetForm()
|
||||
// debugger
|
||||
// const cachedData = getCachedData("Contact Tag");
|
||||
// const updatedData = [...cachedData, resp?.data];
|
||||
// cacheData("Contact Tag", updatedData);
|
||||
// showToast("Contact Tag Added successfully.", "success");
|
||||
// console.log(getCachedData("Contact Tag"))
|
||||
// onClose()
|
||||
// }).catch((error)=>{
|
||||
// showToast(error?.response?.data?.message, "error");
|
||||
// setIsLoading(false)
|
||||
// })
|
||||
// };
|
||||
const resetForm = () => {
|
||||
reset({ name: "", description: "" });
|
||||
setDescriptionLength(0);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
return () => resetForm();
|
||||
}, []);
|
||||
|
||||
return (<>
|
||||
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="col-12 col-md-12">
|
||||
|
||||
@ -6,6 +6,7 @@ import { MasterRespository } from '../../repositories/MastersRepository';
|
||||
import { clearApiCacheKey } from '../../slices/apiCacheSlice';
|
||||
import { getCachedData,cacheData } from '../../slices/apiDataManager';
|
||||
import showToast from '../../services/toastService';
|
||||
import {useCreateJobRole} from '../../hooks/masterHook/useMaster';
|
||||
|
||||
|
||||
const schema = z.object({
|
||||
@ -16,58 +17,75 @@ const schema = z.object({
|
||||
|
||||
const CreateJobRole = ({onClose}) => {
|
||||
|
||||
const[isLoading,setIsLoading] = useState(false)
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },reset
|
||||
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
role: "",
|
||||
description: "",
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (data) => {
|
||||
setIsLoading(true)
|
||||
const result = {
|
||||
name: data.role,
|
||||
description: data.description,
|
||||
};
|
||||
|
||||
MasterRespository.createJobRole(result).then((resp)=>{
|
||||
setIsLoading(false)
|
||||
resetForm()
|
||||
const cachedData = getCachedData("Job Role");
|
||||
const updatedData = [...cachedData, resp?.data];
|
||||
cacheData("Job Role", updatedData);
|
||||
showToast("JobRole Added successfully.", "success");
|
||||
const maxDescriptionLength = 255;
|
||||
const [descriptionLength, setDescriptionLength] = useState(0);
|
||||
|
||||
onClose()
|
||||
}).catch((error)=>{
|
||||
showToast(error.message, "error");
|
||||
setIsLoading(false)
|
||||
})
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
reset,
|
||||
watch,
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
role: "",
|
||||
description: "",
|
||||
},
|
||||
});
|
||||
|
||||
const resetForm = () => {
|
||||
reset({ role: "", description: "" });
|
||||
setDescriptionLength(0);
|
||||
};
|
||||
|
||||
const { mutate: createJobRole, isPending:isLoading } = useCreateJobRole(() => {
|
||||
onClose?.();
|
||||
} );
|
||||
|
||||
|
||||
// const onSubmit = (data) => {
|
||||
// setIsLoading(true)
|
||||
// const result = {
|
||||
// name: data.role,
|
||||
// description: data.description,
|
||||
// };
|
||||
|
||||
// MasterRespository.createJobRole(result).then((resp)=>{
|
||||
// setIsLoading(false)
|
||||
// resetForm()
|
||||
// const cachedData = getCachedData("Job Role");
|
||||
// const updatedData = [...cachedData, resp?.data];
|
||||
// cacheData("Job Role", updatedData);
|
||||
// showToast("JobRole Added successfully.", "success");
|
||||
|
||||
// onClose()
|
||||
// }).catch((error)=>{
|
||||
// showToast(error.message, "error");
|
||||
// setIsLoading(false)
|
||||
// })
|
||||
|
||||
|
||||
// };
|
||||
const onSubmit = (data) => {
|
||||
const payload = {
|
||||
name: data.role,
|
||||
description: data.description,
|
||||
};
|
||||
const resetForm = () => {
|
||||
reset({
|
||||
role: "",
|
||||
description: ""
|
||||
});
|
||||
setDescriptionLength(0);
|
||||
}
|
||||
createJobRole(payload);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const sub = watch((values) => {
|
||||
setDescriptionLength(values.description?.length || 0);
|
||||
});
|
||||
return () => sub.unsubscribe();
|
||||
}, [watch]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => resetForm();
|
||||
}, []);
|
||||
|
||||
useEffect(()=>{
|
||||
return ()=>resetForm()
|
||||
},[])
|
||||
|
||||
const [descriptionLength, setDescriptionLength] = useState(0);
|
||||
const maxDescriptionLength = 255;
|
||||
return (<>
|
||||
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
|
||||
{/* <div className="col-12 col-md-12">
|
||||
|
||||
@ -1,21 +1,19 @@
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { useFeatures } from "../../hooks/useMasterRole";
|
||||
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { set, z } from 'zod';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from "react-hook-form";
|
||||
import { set, z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { MasterRespository } from "../../repositories/MastersRepository";
|
||||
import { cacheData, getCachedData } from "../../slices/apiDataManager";
|
||||
import showToast from "../../services/toastService";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
import { useCreateApplicationRole } from "../../hooks/masterHook/useMaster";
|
||||
|
||||
const schema = z.object({
|
||||
role: z.string().min(1, { message: "Role is required" }),
|
||||
description: z.string().min(1, { message: "Description is required" })
|
||||
description: z
|
||||
.string()
|
||||
.min(1, { message: "Description is required" })
|
||||
.max(255, { message: "Description cannot exceed 255 characters" }),
|
||||
|
||||
selectedPermissions: z
|
||||
@ -23,21 +21,22 @@ const schema = z.object({
|
||||
.min(1, { message: "Please select at least one permission" }),
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
const CreateRole = ({ modalType, onClose }) => {
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const maxDescriptionLength = 255;
|
||||
const [descriptionLength, setDescriptionLength] = useState(0);
|
||||
|
||||
const popoverRefs = useRef([]);
|
||||
const { masterFeatures } = useFeatures()
|
||||
const { masterFeatures } = useFeatures();
|
||||
const { mutate: submitNewApplicationRole, isPending: isLoading } =
|
||||
useCreateApplicationRole(() => {
|
||||
onClose?.();
|
||||
});
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
|
||||
reset,
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
@ -48,9 +47,7 @@ const CreateRole = ({ modalType, onClose }) => {
|
||||
});
|
||||
|
||||
const onSubmit = (values) => {
|
||||
setIsLoading(true)
|
||||
const result = {
|
||||
|
||||
const payload = {
|
||||
role: values.role,
|
||||
description: values.description,
|
||||
featuresPermission: values.selectedPermissions.map((id) => ({
|
||||
@ -59,27 +56,40 @@ const CreateRole = ({ modalType, onClose }) => {
|
||||
})),
|
||||
};
|
||||
|
||||
MasterRespository.createRole(result).then((resp) => {
|
||||
setIsLoading(false)
|
||||
const cachedData = getCachedData("Application Role");
|
||||
const updatedData = [...cachedData, resp.data];
|
||||
cacheData("Application Role", updatedData);
|
||||
showToast("Application Role Added successfully.", "success");
|
||||
onClose()
|
||||
}).catch((err) => {
|
||||
|
||||
showToast(err?.response?.data?.message, "error");
|
||||
setIsLoading(false)
|
||||
onClose()
|
||||
})
|
||||
|
||||
|
||||
submitNewApplicationRole(payload);
|
||||
};
|
||||
const [descriptionLength, setDescriptionLength] = useState(0);
|
||||
const maxDescriptionLength = 255;
|
||||
|
||||
// const onSubmit = (values) => {
|
||||
// setIsLoading(true)
|
||||
// const result = {
|
||||
|
||||
// role: values.role,
|
||||
// description: values.description,
|
||||
// featuresPermission: values.selectedPermissions.map((id) => ({
|
||||
// id,
|
||||
// isEnabled: true,
|
||||
// })),
|
||||
// };
|
||||
|
||||
// MasterRespository.createRole(result).then((resp) => {
|
||||
// setIsLoading(false)
|
||||
// const cachedData = getCachedData("Application Role");
|
||||
// const updatedData = [...cachedData, resp.data];
|
||||
// cacheData("Application Role", updatedData);
|
||||
// showToast("Application Role Added successfully.", "success");
|
||||
// onClose()
|
||||
// }).catch((err) => {
|
||||
|
||||
// showToast(err?.response?.data?.message, "error");
|
||||
// setIsLoading(false)
|
||||
// onClose()
|
||||
// })
|
||||
|
||||
// };
|
||||
useEffect(() => {
|
||||
setDescriptionLength(0);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
popoverRefs.current.forEach((el) => {
|
||||
if (el) {
|
||||
@ -87,14 +97,13 @@ const CreateRole = ({ modalType, onClose }) => {
|
||||
trigger: "focus",
|
||||
placement: "right",
|
||||
html: true,
|
||||
content: el.getAttribute("data-bs-content"), // use inline content from attribute
|
||||
content: el.getAttribute("data-bs-content"),
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [masterFeatures]);
|
||||
|
||||
return (
|
||||
|
||||
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
|
||||
{/* <div className="col-12 col-md-12">
|
||||
<label className="fs-5 text-dark text-center d-flex align-items-center justify-content-center flex-wrap">Create Application Role</label>
|
||||
@ -102,23 +111,24 @@ const CreateRole = ({ modalType, onClose }) => {
|
||||
</div> */}
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label">Role</label>
|
||||
<input type="text"
|
||||
<input
|
||||
type="text"
|
||||
{...register("role")}
|
||||
className={`form-control ${errors.role ? 'is-invalids' : ''}`}
|
||||
className={`form-control ${errors.role ? "is-invalids" : ""}`}
|
||||
/>
|
||||
{errors.role && <p className="text-danger">{errors.role.message}</p>}
|
||||
</div>
|
||||
|
||||
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label" htmlFor="description">Description</label>
|
||||
<label className="form-label" htmlFor="description">
|
||||
Description
|
||||
</label>
|
||||
<textarea
|
||||
rows="3"
|
||||
{...register("description")}
|
||||
className={`form-control ${errors.description ? 'is-invalids' : ''}`}
|
||||
className={`form-control ${errors.description ? "is-invalids" : ""}`}
|
||||
onChange={(e) => {
|
||||
setDescriptionLength(e.target.value.length);
|
||||
// Also update react-hook-form's value
|
||||
register("description").onChange(e);
|
||||
}}
|
||||
></textarea>
|
||||
@ -131,26 +141,26 @@ const CreateRole = ({ modalType, onClose }) => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
<div className="col-12 col-md-12">
|
||||
|
||||
|
||||
<div className="col-12 col-md-12 border">
|
||||
{masterFeatures.map((feature, featureIndex) => (
|
||||
|
||||
<React.Fragment key={feature.id}>
|
||||
<div className="row my-1" key={feature.id} style={{ marginLeft: "0px" }}>
|
||||
|
||||
|
||||
<div className="col-12 col-md-3 d-flex text-start align-items-start" style={{ wordWrap: 'break-word' }}>
|
||||
<div
|
||||
className="row my-1"
|
||||
key={feature.id}
|
||||
style={{ marginLeft: "0px" }}
|
||||
>
|
||||
<div
|
||||
className="col-12 col-md-3 d-flex text-start align-items-start"
|
||||
style={{ wordWrap: "break-word" }}
|
||||
>
|
||||
<span className="fs">{feature.name}</span>
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-md-1"></div>
|
||||
|
||||
|
||||
<div className="col-12 col-md-8 d-flex justify-content-start align-items-center flex-wrap">
|
||||
{feature.featurePermissions.map((perm, permIndex) => {
|
||||
const refIndex = (featureIndex * 10) + permIndex;
|
||||
const refIndex = featureIndex * 10 + permIndex;
|
||||
return (
|
||||
<div className="d-flex me-3 mb-2" key={perm.id}>
|
||||
<label className="form-check-label" htmlFor={perm.id}>
|
||||
@ -163,15 +173,14 @@ const CreateRole = ({ modalType, onClose }) => {
|
||||
/>
|
||||
{perm.name}
|
||||
</label>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<div
|
||||
key={refIndex}
|
||||
ref={(el) =>
|
||||
(popoverRefs.current[refIndex] = el)
|
||||
}
|
||||
ref={(el) => (popoverRefs.current[refIndex] = el)}
|
||||
tabIndex="0"
|
||||
className="d-flex align-items-center avatar-group justify-content-center"
|
||||
data-bs-toggle="popover" refIndex
|
||||
data-bs-toggle="popover"
|
||||
refIndex
|
||||
data-bs-trigger="focus"
|
||||
data-bs-placement="right"
|
||||
data-bs-html="true"
|
||||
@ -182,23 +191,26 @@ const CreateRole = ({ modalType, onClose }) => {
|
||||
`}
|
||||
>
|
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" className="bi bi-info-circle" viewBox="0 0 16 16">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="13"
|
||||
height="13"
|
||||
fill="currentColor"
|
||||
className="bi bi-info-circle"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z" />
|
||||
<path d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.547 1.11l1.91-2.011c.241-.256.384-.592.287-.984-.172-.439-.58-.827-1.13-.967a.664.664 0 0 1-.58-.309l-.15-.241-.002-.002zM8 4c-.535 0-.943.372-.943.836 0 .464.408.836.943.836.535 0 .943-.372.943-.836 0-.464-.408-.836-.943-.836z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<hr className="hr my-1 py-1" />
|
||||
</React.Fragment>
|
||||
|
||||
))}
|
||||
{errors.selectedPermissions && (
|
||||
<p className="text-danger">{errors.selectedPermissions.message}</p>
|
||||
@ -206,33 +218,23 @@ const CreateRole = ({ modalType, onClose }) => {
|
||||
{!masterFeatures && <p>Loading...</p>}
|
||||
</div>
|
||||
|
||||
|
||||
{
|
||||
masterFeatures && (
|
||||
|
||||
<div className="col-12 text-center">
|
||||
<button type="submit" className="btn btn-sm btn-primary me-3">
|
||||
{isLoading ? "Please Wait..." : "Submit"}
|
||||
</button>
|
||||
<button
|
||||
type="reset"
|
||||
className="btn btn-sm btn-label-secondary"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{masterFeatures && (
|
||||
<div className="col-12 text-center">
|
||||
<button type="submit" className="btn btn-sm btn-primary me-3">
|
||||
{isLoading ? "Please Wait..." : "Submit"}
|
||||
</button>
|
||||
<button
|
||||
type="reset"
|
||||
className="btn btn-sm btn-label-secondary"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateRole;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import { MasterRespository } from '../../repositories/MastersRepository';
|
||||
import { clearApiCacheKey } from '../../slices/apiCacheSlice';
|
||||
import { getCachedData,cacheData } from '../../slices/apiDataManager';
|
||||
import showToast from '../../services/toastService';
|
||||
import {useCreateWorkCategory} from '../../hooks/masterHook/useMaster';
|
||||
|
||||
|
||||
const schema = z.object({
|
||||
@ -16,58 +17,64 @@ const schema = z.object({
|
||||
|
||||
const CreateWorkCategory = ({onClose}) => {
|
||||
|
||||
const[isLoading,setIsLoading] = useState(false)
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },reset
|
||||
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
description: "",
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (data) => {
|
||||
setIsLoading(true)
|
||||
// const result = {
|
||||
// name: data.name,
|
||||
// description: data.description,
|
||||
// };
|
||||
|
||||
MasterRespository.createWorkCategory(data).then((resp)=>{
|
||||
setIsLoading(false)
|
||||
resetForm()
|
||||
const cachedData = getCachedData("Work Category");
|
||||
const updatedData = [...cachedData, resp?.data];
|
||||
cacheData("Work Category", updatedData);
|
||||
showToast("Work Category Added successfully.", "success");
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
reset,
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
name: "",
|
||||
description: "",
|
||||
},
|
||||
});
|
||||
|
||||
onClose()
|
||||
}).catch((error)=>{
|
||||
showToast(error?.response?.data?.message, "error");
|
||||
setIsLoading(false)
|
||||
})
|
||||
const [descriptionLength, setDescriptionLength] = useState(0);
|
||||
const maxDescriptionLength = 255;
|
||||
|
||||
const { mutate: createWorkCategory, isPending: isLoading } = useCreateWorkCategory(() => {
|
||||
resetForm();
|
||||
onClose?.();
|
||||
});
|
||||
|
||||
const onSubmit = (payload) => {
|
||||
createWorkCategory(payload)
|
||||
};
|
||||
|
||||
// const onSubmit = (data) => {
|
||||
// setIsLoading(true)
|
||||
|
||||
|
||||
// MasterRespository.createWorkCategory(data).then((resp)=>{
|
||||
// setIsLoading(false)
|
||||
// resetForm()
|
||||
// const cachedData = getCachedData("Work Category");
|
||||
// const updatedData = [...cachedData, resp?.data];
|
||||
// cacheData("Work Category", updatedData);
|
||||
// showToast("Work Category Added successfully.", "success");
|
||||
|
||||
// onClose()
|
||||
// }).catch((error)=>{
|
||||
// showToast(error?.response?.data?.message, "error");
|
||||
// setIsLoading(false)
|
||||
// })
|
||||
|
||||
|
||||
};
|
||||
const resetForm = () => {
|
||||
reset({
|
||||
name: "",
|
||||
description: ""
|
||||
});
|
||||
setDescriptionLength(0);
|
||||
}
|
||||
// };
|
||||
|
||||
useEffect(()=>{
|
||||
return ()=>resetForm()
|
||||
},[])
|
||||
|
||||
const [descriptionLength, setDescriptionLength] = useState(0);
|
||||
const maxDescriptionLength = 255;
|
||||
const resetForm = () => {
|
||||
reset({
|
||||
name: "",
|
||||
description: "",
|
||||
});
|
||||
setDescriptionLength(0);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
return () => resetForm();
|
||||
}, []);
|
||||
|
||||
return (<>
|
||||
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="col-12 col-md-12">
|
||||
|
||||
@ -5,6 +5,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import {MasterRespository} from "../../repositories/MastersRepository";
|
||||
import showToast from "../../services/toastService";
|
||||
import {getCachedData,cacheData} from "../../slices/apiDataManager";
|
||||
import {useUpdateActivity} from "../../hooks/masterHook/useMaster";
|
||||
|
||||
|
||||
const schema = z.object({
|
||||
@ -23,111 +24,107 @@ const schema = z.object({
|
||||
|
||||
|
||||
const UpdateActivity = ({ activityData, onClose }) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { mutate: updateActivity, isPending: isLoading } = useUpdateActivity(() => onClose?.());
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
setValue,
|
||||
reset,
|
||||
setError,
|
||||
clearErrors,
|
||||
getValues,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
id:activityData.id,
|
||||
activityName: activityData.activityName,
|
||||
unitOfMeasurement: activityData.unitOfMeasurement,
|
||||
checkLists: activityData.checkLists || [],
|
||||
},
|
||||
});
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
setValue,
|
||||
reset,
|
||||
setError,
|
||||
clearErrors,
|
||||
getValues,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
id: activityData?.id,
|
||||
activityName: activityData?.activityName,
|
||||
unitOfMeasurement: activityData?.unitOfMeasurement,
|
||||
checkList: activityData?.checkLists || [],
|
||||
},
|
||||
});
|
||||
|
||||
const { fields: checkListItems, append, remove } = useFieldArray({
|
||||
control,
|
||||
name: "checkList",
|
||||
});
|
||||
const { fields: checkListItems, append, remove } = useFieldArray({
|
||||
control,
|
||||
name: "checkList",
|
||||
});
|
||||
|
||||
// Load initial data
|
||||
useEffect(() => {
|
||||
if (activityData) {
|
||||
reset( {
|
||||
id:activityData.id,
|
||||
activityName: activityData.activityName,
|
||||
unitOfMeasurement: activityData.unitOfMeasurement,
|
||||
checkList: activityData.checkLists || [],
|
||||
});
|
||||
}
|
||||
}, [activityData]);
|
||||
useEffect(() => {
|
||||
if (activityData) {
|
||||
reset({
|
||||
id: activityData.id,
|
||||
activityName: activityData.activityName,
|
||||
unitOfMeasurement: activityData.unitOfMeasurement,
|
||||
checkList: activityData.checkLists || [],
|
||||
});
|
||||
}
|
||||
}, [activityData, reset]);
|
||||
|
||||
|
||||
const handleChecklistChange = (index, value) => {
|
||||
setValue(`checkList.${index}`, value);
|
||||
const addChecklistItem = () => {
|
||||
const values = getValues("checkList");
|
||||
const lastIndex = checkListItems.length - 1;
|
||||
|
||||
if (
|
||||
checkListItems.length > 0 &&
|
||||
(!values?.[lastIndex] || values[lastIndex].description.trim() === "")
|
||||
) {
|
||||
setError(`checkList.${lastIndex}.description`, {
|
||||
type: "manual",
|
||||
message: "Please fill this checklist item before adding another.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
clearErrors(`checkList.${lastIndex}.description`);
|
||||
append({ id: null, description: "", isMandatory: false });
|
||||
};
|
||||
|
||||
const removeChecklistItem = (index) => {
|
||||
remove(index);
|
||||
};
|
||||
|
||||
const handleChecklistChange = (index, value) => {
|
||||
setValue(`checkList.${index}`, value);
|
||||
};
|
||||
|
||||
const onSubmit = (formData) => {
|
||||
const payload = { ...formData, id: activityData.id };
|
||||
updateActivity({ id: activityData.id, payload });
|
||||
};
|
||||
|
||||
// Submit handler
|
||||
const onSubmit = async(data) => {
|
||||
setIsLoading(true);
|
||||
// const onSubmit = async(data) => {
|
||||
// setIsLoading(true);
|
||||
|
||||
const Activity = {...data, id:activityData.id}
|
||||
try
|
||||
{
|
||||
const response = await MasterRespository.updateActivity( activityData?.id, Activity );
|
||||
const updatedActivity = response.data;
|
||||
const cachedData = getCachedData("Activity")
|
||||
// const Activity = {...data, id:activityData.id}
|
||||
// try
|
||||
// {
|
||||
// const response = await MasterRespository.updateActivity( activityData?.id, Activity );
|
||||
// const updatedActivity = response.data;
|
||||
// const cachedData = getCachedData("Activity")
|
||||
|
||||
if (cachedData) {
|
||||
const updatedActivities = cachedData.map((activity) =>
|
||||
activity.id === updatedActivity.id ? { ...activity, ...updatedActivity } : activity
|
||||
);
|
||||
// if (cachedData) {
|
||||
// const updatedActivities = cachedData.map((activity) =>
|
||||
// activity.id === updatedActivity.id ? { ...activity, ...updatedActivity } : activity
|
||||
// );
|
||||
|
||||
cacheData( "Activity", updatedActivities );
|
||||
onClose()
|
||||
}
|
||||
setIsLoading( false )
|
||||
showToast("Activity Successfully Updated", "success");
|
||||
} catch ( err )
|
||||
{
|
||||
setIsLoading( false )
|
||||
// cacheData( "Activity", updatedActivities );
|
||||
// onClose()
|
||||
// }
|
||||
// setIsLoading( false )
|
||||
// showToast("Activity Successfully Updated", "success");
|
||||
// } catch ( err )
|
||||
// {
|
||||
// setIsLoading( false )
|
||||
|
||||
showToast("error.message", "error");
|
||||
}
|
||||
};
|
||||
// showToast("error.message", "error");
|
||||
// }
|
||||
// };
|
||||
|
||||
|
||||
|
||||
// Add new checklist item
|
||||
const addChecklistItem = () => {
|
||||
const values = getValues("checkList");
|
||||
const lastIndex = checkListItems.length - 1;
|
||||
|
||||
if (
|
||||
checkListItems.length > 0 &&
|
||||
(!values?.[lastIndex] || values[lastIndex].description.trim() === "")
|
||||
) {
|
||||
setError(`checkList.${lastIndex}.description`, {
|
||||
type: "manual",
|
||||
message: "Please fill this checklist item before adding another.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
clearErrors(`checkList.${lastIndex}.description`);
|
||||
append({ id: null, description: "", isMandatory: false });
|
||||
};
|
||||
|
||||
const removeChecklistItem = (index) => {
|
||||
remove(index);
|
||||
};
|
||||
|
||||
|
||||
// for tooltip
|
||||
useEffect(() => {
|
||||
const tooltipTriggerList = Array.from(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
||||
tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el));
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const tooltipTriggerList = Array.from(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
||||
tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
@ -232,7 +229,7 @@ const UpdateActivity = ({ activityData, onClose }) => {
|
||||
className="btn btn-xs btn-primary mt-2"
|
||||
onClick={addChecklistItem}
|
||||
>
|
||||
<i class="bx bx-plus-circle" data-bs-toggle="tooltip"
|
||||
<i className="bx bx-plus-circle" data-bs-toggle="tooltip"
|
||||
title="Add Check"
|
||||
data-bs-original-title="Add check" ></i>
|
||||
</button>
|
||||
@ -256,4 +253,4 @@ const UpdateActivity = ({ activityData, onClose }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default UpdateActivity;
|
||||
export default UpdateActivity;
|
||||
@ -6,6 +6,7 @@ import { MasterRespository } from '../../repositories/MastersRepository';
|
||||
import { clearApiCacheKey } from '../../slices/apiCacheSlice';
|
||||
import { getCachedData,cacheData } from '../../slices/apiDataManager';
|
||||
import showToast from '../../services/toastService';
|
||||
import {useUpdateContactCategory} from '../../hooks/masterHook/useMaster';
|
||||
|
||||
|
||||
const schema = z.object({
|
||||
@ -15,65 +16,72 @@ const schema = z.object({
|
||||
});
|
||||
|
||||
const EditContactCategory= ({data,onClose}) => {
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
reset,
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
name: data?.name || "",
|
||||
description: data?.description || "",
|
||||
},
|
||||
});
|
||||
|
||||
const[isLoading,setIsLoading] = useState(false)
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },reset
|
||||
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
name: data?.name || "",
|
||||
description:data?.description || "",
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (formdata) => {
|
||||
setIsLoading(true)
|
||||
const result = {
|
||||
id:data?.id,
|
||||
name: formdata?.name,
|
||||
description: formdata.description,
|
||||
};
|
||||
const [descriptionLength, setDescriptionLength] = useState(data?.description?.length || 0);
|
||||
const maxDescriptionLength = 255;
|
||||
|
||||
const { mutate: updateContactCategory, isPending: isLoading } = useUpdateContactCategory(() => onClose?.());
|
||||
|
||||
const onSubmit = (formData) => {
|
||||
const payload = {
|
||||
id: data?.id,
|
||||
name: formData.name,
|
||||
description: formData.description,
|
||||
};
|
||||
|
||||
updateContactCategory({id:data?.id,payload});
|
||||
};
|
||||
// const onSubmit = (formdata) => {
|
||||
// setIsLoading(true)
|
||||
// const result = {
|
||||
// id:data?.id,
|
||||
// name: formdata?.name,
|
||||
// description: formdata.description,
|
||||
// };
|
||||
|
||||
|
||||
|
||||
MasterRespository.updateContactCategory(data?.id,result).then((resp)=>{
|
||||
setIsLoading(false)
|
||||
showToast("Contact Category Updated successfully.", "success");
|
||||
const cachedData = getCachedData("Contact Category");
|
||||
if (cachedData) {
|
||||
// MasterRespository.updateContactCategory(data?.id,result).then((resp)=>{
|
||||
// setIsLoading(false)
|
||||
// showToast("Contact Category Updated successfully.", "success");
|
||||
// const cachedData = getCachedData("Contact Category");
|
||||
// if (cachedData) {
|
||||
|
||||
const updatedData = cachedData.map((category) =>
|
||||
category.id === data?.id ? { ...category, ...resp.data } : category
|
||||
);
|
||||
cacheData("Contact Category", updatedData);
|
||||
}
|
||||
// const updatedData = cachedData.map((category) =>
|
||||
// category.id === data?.id ? { ...category, ...resp.data } : category
|
||||
// );
|
||||
// cacheData("Contact Category", updatedData);
|
||||
// }
|
||||
|
||||
onClose()
|
||||
}).catch((error)=>{
|
||||
showToast(error?.response?.data?.message, "error")
|
||||
setIsLoading(false)
|
||||
})
|
||||
// onClose()
|
||||
// }).catch((error)=>{
|
||||
// showToast(error?.response?.data?.message, "error")
|
||||
// setIsLoading(false)
|
||||
// })
|
||||
|
||||
};
|
||||
const resetForm = () => {
|
||||
reset({
|
||||
name: "",
|
||||
description: ""
|
||||
});
|
||||
setDescriptionLength(0);
|
||||
}
|
||||
// };
|
||||
|
||||
useEffect(()=>{
|
||||
return ()=>resetForm()
|
||||
},[])
|
||||
|
||||
const [descriptionLength, setDescriptionLength] = useState(0);
|
||||
const maxDescriptionLength = 255;
|
||||
const resetForm = () => {
|
||||
reset({ name: "", description: "" });
|
||||
setDescriptionLength(0);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
return () => resetForm();
|
||||
}, []);
|
||||
|
||||
return (<>
|
||||
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="col-12 col-md-12">
|
||||
|
||||
@ -6,6 +6,7 @@ import { MasterRespository } from '../../repositories/MastersRepository';
|
||||
import { clearApiCacheKey } from '../../slices/apiCacheSlice';
|
||||
import { getCachedData,cacheData } from '../../slices/apiDataManager';
|
||||
import showToast from '../../services/toastService';
|
||||
import {useUpdateContactTag} from '../../hooks/masterHook/useMaster';
|
||||
|
||||
|
||||
const schema = z.object({
|
||||
@ -15,65 +16,72 @@ const schema = z.object({
|
||||
});
|
||||
|
||||
const EditContactTag= ({data,onClose}) => {
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
reset
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
name: data?.name || "",
|
||||
description: data?.description || "",
|
||||
},
|
||||
});
|
||||
|
||||
const[isLoading,setIsLoading] = useState(false)
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },reset
|
||||
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
name: data?.name || "",
|
||||
description:data?.description || "",
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (formdata) => {
|
||||
setIsLoading(true)
|
||||
const result = {
|
||||
id:data?.id,
|
||||
name: formdata?.name,
|
||||
description: formdata.description,
|
||||
};
|
||||
const [descriptionLength, setDescriptionLength] = useState(data?.description?.length || 0);
|
||||
const maxDescriptionLength = 255;
|
||||
|
||||
const { mutate: updateContactTag, isPending: isLoading } = useUpdateContactTag(() => onClose?.());
|
||||
|
||||
const onSubmit = (formData) => {
|
||||
const payload = {
|
||||
id: data?.id,
|
||||
name: formData.name,
|
||||
description: formData.description,
|
||||
};
|
||||
debugger
|
||||
updateContactTag({ id: data?.id, payload} );
|
||||
}
|
||||
// const onSubmit = (formdata) => {
|
||||
// setIsLoading(true)
|
||||
// const result = {
|
||||
// id:data?.id,
|
||||
// name: formdata?.name,
|
||||
// description: formdata.description,
|
||||
// };
|
||||
|
||||
|
||||
|
||||
MasterRespository.updateContactTag(data?.id,result).then((resp)=>{
|
||||
setIsLoading(false)
|
||||
showToast("Contact Tag Updated successfully.", "success");
|
||||
const cachedData = getCachedData("Contact Tag");
|
||||
if (cachedData) {
|
||||
// MasterRespository.updateContactTag(data?.id,result).then((resp)=>{
|
||||
// setIsLoading(false)
|
||||
// showToast("Contact Tag Updated successfully.", "success");
|
||||
// const cachedData = getCachedData("Contact Tag");
|
||||
// if (cachedData) {
|
||||
|
||||
const updatedData = cachedData.map((category) =>
|
||||
category.id === data?.id ? { ...category, ...resp.data } : category
|
||||
);
|
||||
cacheData("Contact Tag", updatedData);
|
||||
}
|
||||
// const updatedData = cachedData.map((category) =>
|
||||
// category.id === data?.id ? { ...category, ...resp.data } : category
|
||||
// );
|
||||
// cacheData("Contact Tag", updatedData);
|
||||
// }
|
||||
|
||||
onClose()
|
||||
}).catch((error)=>{
|
||||
showToast(error?.response?.data?.message, "error")
|
||||
setIsLoading(false)
|
||||
})
|
||||
// onClose()
|
||||
// }).catch((error)=>{
|
||||
// showToast(error?.response?.data?.message, "error")
|
||||
// setIsLoading(false)
|
||||
// })
|
||||
|
||||
};
|
||||
const resetForm = () => {
|
||||
reset({
|
||||
name: "",
|
||||
description: ""
|
||||
});
|
||||
setDescriptionLength(0);
|
||||
}
|
||||
// };
|
||||
|
||||
useEffect(()=>{
|
||||
return ()=>resetForm()
|
||||
},[])
|
||||
|
||||
const [descriptionLength, setDescriptionLength] = useState(0);
|
||||
const maxDescriptionLength = 255;
|
||||
const resetForm = () => {
|
||||
reset({ name: "", description: "" });
|
||||
setDescriptionLength(0);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
return () => resetForm();
|
||||
}, []);
|
||||
|
||||
return (<>
|
||||
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="col-12 col-md-12">
|
||||
|
||||
@ -5,6 +5,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { MasterRespository } from '../../repositories/MastersRepository';
|
||||
import { cacheData,getCachedData } from '../../slices/apiDataManager';
|
||||
import showToast from '../../services/toastService';
|
||||
import {useUpdateJobRole} from '../../hooks/masterHook/useMaster';
|
||||
|
||||
|
||||
|
||||
@ -17,63 +18,81 @@ const schema = z.object({
|
||||
|
||||
|
||||
const EditJobRole = ({data,onClose}) => {
|
||||
|
||||
const[isLoading,setIsLoading] = useState(false)
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors } ,reset
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
role: data?.name || "",
|
||||
description:data?.description || "",
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (formdata) => {
|
||||
setIsLoading(true)
|
||||
const result = {
|
||||
id:data?.id,
|
||||
name: formdata?.role,
|
||||
description: formdata.description,
|
||||
};
|
||||
|
||||
|
||||
|
||||
MasterRespository.updateJobRole(data?.id,result).then((resp)=>{
|
||||
setIsLoading(false)
|
||||
showToast("JobRole Update successfully.", "success");
|
||||
const cachedData = getCachedData("Job Role");
|
||||
if (cachedData) {
|
||||
|
||||
const updatedData = cachedData.map((role) =>
|
||||
role.id === data?.id ? { ...role, ...resp.data } : role
|
||||
);
|
||||
cacheData("Job Role", updatedData);
|
||||
}
|
||||
|
||||
onClose()
|
||||
}).catch((error)=>{
|
||||
showToast(error.message, "error")
|
||||
setIsLoading(false)
|
||||
})
|
||||
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
reset({
|
||||
role: data?.name,
|
||||
description: data?.description
|
||||
});
|
||||
setDescriptionLength(data?.description?.length || 0);
|
||||
}, [data, reset]);
|
||||
|
||||
const [descriptionLength, setDescriptionLength] = useState(data?.description?.length || 0);
|
||||
const [descriptionLength, setDescriptionLength] = useState(data?.description?.length || 0);
|
||||
const maxDescriptionLength = 255;
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
reset,
|
||||
watch,
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
role: data?.name || "",
|
||||
description: data?.description || "",
|
||||
},
|
||||
});
|
||||
|
||||
const { mutate: updateJobRole, isPendin:isLoading } = useUpdateJobRole(() => {
|
||||
onClose?.();
|
||||
});
|
||||
// const onSubmit = (formdata) => {
|
||||
// setIsLoading(true)
|
||||
// const result = {
|
||||
// id:data?.id,
|
||||
// name: formdata?.role,
|
||||
// description: formdata.description,
|
||||
// };
|
||||
|
||||
|
||||
|
||||
// MasterRespository.updateJobRole(data?.id,result).then((resp)=>{
|
||||
// setIsLoading(false)
|
||||
// showToast("JobRole Update successfully.", "success");
|
||||
// const cachedData = getCachedData("Job Role");
|
||||
// if (cachedData) {
|
||||
|
||||
// const updatedData = cachedData.map((role) =>
|
||||
// role.id === data?.id ? { ...role, ...resp.data } : role
|
||||
// );
|
||||
// cacheData("Job Role", updatedData);
|
||||
// }
|
||||
|
||||
// onClose()
|
||||
// }).catch((error)=>{
|
||||
// showToast(error.message, "error")
|
||||
// setIsLoading(false)
|
||||
// })
|
||||
|
||||
// };
|
||||
|
||||
const onSubmit = (formData) => {
|
||||
updateJobRole({
|
||||
id: data?.id,
|
||||
payload: {
|
||||
id: data?.id,
|
||||
name: formData.role,
|
||||
description: formData.description,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
reset({
|
||||
role: data?.name || "",
|
||||
description: data?.description || "",
|
||||
});
|
||||
setDescriptionLength(data?.description?.length || 0);
|
||||
}, [data, reset]);
|
||||
|
||||
useEffect(() => {
|
||||
const sub = watch((values) => {
|
||||
setDescriptionLength(values.description?.length || 0);
|
||||
});
|
||||
return () => sub.unsubscribe();
|
||||
}, [watch]);
|
||||
|
||||
return (<>
|
||||
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
|
||||
|
||||
@ -7,6 +7,7 @@ import { useFeatures } from "../../hooks/useMasterRole";
|
||||
import { MasterRespository } from "../../repositories/MastersRepository";
|
||||
import { cacheData, getCachedData } from "../../slices/apiDataManager";
|
||||
import showToast from "../../services/toastService";
|
||||
import {useUpdateApplicationRole} from "../../hooks/masterHook/useMaster";
|
||||
|
||||
|
||||
|
||||
@ -27,128 +28,149 @@ const updateSchema = z.object({
|
||||
|
||||
|
||||
const EditMaster = ({ master, onClose }) => {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const { masterFeatures } = useFeatures()
|
||||
const popoverRefs = useRef([]);
|
||||
const maxDescriptionLength = 255;
|
||||
const popoverRefs = useRef([]);
|
||||
const { masterFeatures } = useFeatures();
|
||||
const { mutate: updateApplicationRole, isPending: isLoading } = useUpdateApplicationRole(() => onClose?.());
|
||||
|
||||
const buildDefaultPermissions = () => {
|
||||
const defaults = {};
|
||||
masterFeatures.forEach((feature) => {
|
||||
feature.featurePermissions.forEach((perm) => {
|
||||
const existing = master?.item?.featurePermission?.find(
|
||||
(p) => p.id === perm.id
|
||||
);
|
||||
defaults[perm.id] = existing ? existing.isEnabled : false;
|
||||
});
|
||||
const buildDefaultPermissions = () => {
|
||||
const defaults = {};
|
||||
masterFeatures.forEach((feature) => {
|
||||
feature.featurePermissions.forEach((perm) => {
|
||||
const existing = master?.item?.featurePermission?.find(p => p.id === perm.id);
|
||||
defaults[perm.id] = existing?.isEnabled || false;
|
||||
});
|
||||
return defaults;
|
||||
|
||||
};
|
||||
|
||||
|
||||
const initialPermissions = buildDefaultPermissions();
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors, dirtyFields },
|
||||
setError, reset
|
||||
} = useForm({
|
||||
resolver: zodResolver(updateSchema),
|
||||
defaultValues: {
|
||||
role: master?.item?.role,
|
||||
description: master?.item?.description,
|
||||
permissions: initialPermissions,
|
||||
},
|
||||
});
|
||||
return defaults;
|
||||
};
|
||||
|
||||
const onSubmit = (data) => {
|
||||
setIsLoading(true)
|
||||
const existingIds = new Set(
|
||||
master?.item?.featurePermission?.map((p) => p.id)
|
||||
);
|
||||
const initialPermissions = buildDefaultPermissions();
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors, dirtyFields },
|
||||
setError,
|
||||
reset,
|
||||
} = useForm({
|
||||
resolver: zodResolver(updateSchema),
|
||||
defaultValues: {
|
||||
role: master?.item?.role || "",
|
||||
description: master?.item?.description || "",
|
||||
permissions: initialPermissions,
|
||||
},
|
||||
});
|
||||
|
||||
const updatedPermissions = Object.entries(data.permissions)
|
||||
.filter(([id, value]) => {
|
||||
if (existingIds.has(id)) return true;
|
||||
const [descriptionLength, setDescriptionLength] = useState(master?.item?.description?.length || 0);
|
||||
|
||||
return (
|
||||
value === true ||
|
||||
(dirtyFields.permissions && dirtyFields.permissions[id])
|
||||
);
|
||||
})
|
||||
.map(([id, value]) => ({ id, isEnabled: value }));
|
||||
const onSubmit = (data) => {
|
||||
const existingIds = new Set(master?.item?.featurePermission?.map(p => p.id));
|
||||
|
||||
|
||||
if (updatedPermissions.length === 0) {
|
||||
setError("permissions", {
|
||||
type: "manual",
|
||||
message: "At least one permission must be selected.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedRole = {
|
||||
id: master?.item?.id,
|
||||
role: data.role,
|
||||
description: data.description,
|
||||
featuresPermission: updatedPermissions,
|
||||
};
|
||||
MasterRespository.updateRoles(master?.item?.id, updatedRole).then((resp) => {
|
||||
setIsLoading(false)
|
||||
|
||||
|
||||
const cachedData = getCachedData("Application Role");
|
||||
|
||||
if (cachedData) {
|
||||
|
||||
const updatedData = cachedData.map((role) =>
|
||||
role.id === resp.data?.id ? { ...role, ...resp.data } : role
|
||||
);
|
||||
|
||||
cacheData("Application Role", updatedData);
|
||||
}
|
||||
showToast("Application Role Updated successfully.", "success");
|
||||
setIsLoading(false)
|
||||
onClose()
|
||||
}).catch((Err) => {
|
||||
showToast(Err.message, "error");
|
||||
setIsLoading(false)
|
||||
const updatedPermissions = Object.entries(data.permissions)
|
||||
.filter(([id, value]) => {
|
||||
return existingIds.has(id) || value === true || (dirtyFields.permissions?.[id]);
|
||||
})
|
||||
.map(([id, value]) => ({ id, isEnabled: value }));
|
||||
|
||||
if (updatedPermissions.length === 0) {
|
||||
setError("permissions", {
|
||||
type: "manual",
|
||||
message: "At least one permission must be selected.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
id: master?.item?.id,
|
||||
role: data.role,
|
||||
description: data.description,
|
||||
featuresPermission: updatedPermissions,
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
reset({
|
||||
role: master?.item?.role,
|
||||
description: master?.item?.description,
|
||||
permissions: initialPermissions,
|
||||
});
|
||||
setDescriptionLength(master?.item?.description?.length || 0);
|
||||
}, [master, reset]);
|
||||
updateApplicationRole({ id: payload.id, payload });
|
||||
};
|
||||
// const onSubmit = (data) => {
|
||||
// setIsLoading(true)
|
||||
// const existingIds = new Set(
|
||||
// master?.item?.featurePermission?.map((p) => p.id)
|
||||
// );
|
||||
|
||||
const [descriptionLength, setDescriptionLength] = useState(master?.item?.description?.length || 0);
|
||||
const maxDescriptionLength = 255;
|
||||
|
||||
useEffect(() => {
|
||||
popoverRefs.current.forEach((el) => {
|
||||
if (el) {
|
||||
new bootstrap.Popover(el, {
|
||||
trigger: "focus",
|
||||
placement: "right",
|
||||
html: true,
|
||||
content: el.getAttribute("data-bs-content"), // use inline content from attribute
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [masterFeatures]);
|
||||
// const updatedPermissions = Object.entries(data.permissions)
|
||||
// .filter(([id, value]) => {
|
||||
// if (existingIds.has(id)) return true;
|
||||
|
||||
// return (
|
||||
// value === true ||
|
||||
// (dirtyFields.permissions && dirtyFields.permissions[id])
|
||||
// );
|
||||
// })
|
||||
// .map(([id, value]) => ({ id, isEnabled: value }));
|
||||
|
||||
|
||||
// if (updatedPermissions.length === 0) {
|
||||
// setError("permissions", {
|
||||
// type: "manual",
|
||||
// message: "At least one permission must be selected.",
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const updatedRole = {
|
||||
// id: master?.item?.id,
|
||||
// role: data.role,
|
||||
// description: data.description,
|
||||
// featuresPermission: updatedPermissions,
|
||||
// };
|
||||
// MasterRespository.updateRoles(master?.item?.id, updatedRole).then((resp) => {
|
||||
// setIsLoading(false)
|
||||
|
||||
|
||||
// const cachedData = getCachedData("Application Role");
|
||||
|
||||
// if (cachedData) {
|
||||
|
||||
// const updatedData = cachedData.map((role) =>
|
||||
// role.id === resp.data?.id ? { ...role, ...resp.data } : role
|
||||
// );
|
||||
|
||||
// cacheData("Application Role", updatedData);
|
||||
// }
|
||||
// showToast("Application Role Updated successfully.", "success");
|
||||
// setIsLoading(false)
|
||||
// onClose()
|
||||
// }).catch((Err) => {
|
||||
// showToast(Err.message, "error");
|
||||
// setIsLoading(false)
|
||||
// })
|
||||
|
||||
// };
|
||||
useEffect(() => {
|
||||
reset({
|
||||
role: master?.item?.role || "",
|
||||
description: master?.item?.description || "",
|
||||
permissions: buildDefaultPermissions(),
|
||||
});
|
||||
setDescriptionLength(master?.item?.description?.length || 0);
|
||||
}, [master, reset]);
|
||||
|
||||
useEffect(() => {
|
||||
popoverRefs.current.forEach((el) => {
|
||||
if (el) {
|
||||
new bootstrap.Popover(el, {
|
||||
trigger: "focus",
|
||||
placement: "right",
|
||||
html: true,
|
||||
content: el.getAttribute("data-bs-content"),
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [masterFeatures]);
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<form className="row g-2 " onSubmit={handleSubmit(onSubmit)}>
|
||||
{/* <div className="col-12 col-md-12">
|
||||
<label className="fs-5 text-dark text-center d-flex align-items-center justify-content-center flex-wrap">Edit Application Role</label>
|
||||
</div> */}
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label">Role</label>
|
||||
<input type="text"
|
||||
|
||||
@ -5,6 +5,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { MasterRespository } from '../../repositories/MastersRepository';
|
||||
import { cacheData,getCachedData } from '../../slices/apiDataManager';
|
||||
import showToast from '../../services/toastService';
|
||||
import {useUpdateWorkCategory} from '../../hooks/masterHook/useMaster';
|
||||
|
||||
|
||||
|
||||
@ -18,69 +19,75 @@ const schema = z.object({
|
||||
|
||||
const EditWorkCategory = ({data,onClose}) => {
|
||||
|
||||
const[isLoading,setIsLoading] = useState(false)
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors } ,reset
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
name: data?.name || "",
|
||||
description:data?.description || "",
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = (formdata) => {
|
||||
setIsLoading(true)
|
||||
const result = {
|
||||
id:data?.id,
|
||||
name: formdata?.name,
|
||||
description: formdata.description,
|
||||
};
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
reset,
|
||||
} = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
name: data?.name || "",
|
||||
description: data?.description || "",
|
||||
},
|
||||
});
|
||||
|
||||
const [descriptionLength, setDescriptionLength] = useState(data?.description?.length || 0);
|
||||
const maxDescriptionLength = 255;
|
||||
|
||||
const { mutate: updateWorkCategory, isPending: isLoading } = useUpdateWorkCategory(() => onClose?.());
|
||||
|
||||
const onSubmit = (formdata) => {
|
||||
const payload = {
|
||||
id: data?.id,
|
||||
name: formdata.name,
|
||||
description: formdata.description,
|
||||
};
|
||||
|
||||
updateWorkCategory({id:data?.id,payload});
|
||||
};
|
||||
|
||||
// const onSubmit = (formdata) => {
|
||||
// setIsLoading(true)
|
||||
// const result = {
|
||||
// id:data?.id,
|
||||
// name: formdata?.name,
|
||||
// description: formdata.description,
|
||||
// };
|
||||
|
||||
|
||||
|
||||
MasterRespository.updateWorkCategory(data?.id,result).then((resp)=>{
|
||||
setIsLoading(false)
|
||||
showToast("Work Category Update successfully.", "success");
|
||||
const cachedData = getCachedData("Work Category");
|
||||
if (cachedData) {
|
||||
// MasterRespository.updateWorkCategory(data?.id,result).then((resp)=>{
|
||||
// setIsLoading(false)
|
||||
// showToast("Work Category Update successfully.", "success");
|
||||
// const cachedData = getCachedData("Work Category");
|
||||
// if (cachedData) {
|
||||
|
||||
const updatedData = cachedData.map((category) =>
|
||||
category.id === data?.id ? { ...category, ...resp.data } : category
|
||||
);
|
||||
cacheData("Work Category", updatedData);
|
||||
}
|
||||
// const updatedData = cachedData.map((category) =>
|
||||
// category.id === data?.id ? { ...category, ...resp.data } : category
|
||||
// );
|
||||
// cacheData("Work Category", updatedData);
|
||||
// }
|
||||
|
||||
onClose()
|
||||
}).catch((error)=>{
|
||||
showToast(error?.response?.data?.message, "error")
|
||||
setIsLoading(false)
|
||||
})
|
||||
// onClose()
|
||||
// }).catch((error)=>{
|
||||
// showToast(error?.response?.data?.message, "error")
|
||||
// setIsLoading(false)
|
||||
// })
|
||||
|
||||
};
|
||||
// };
|
||||
useEffect(() => {
|
||||
reset({
|
||||
name: data?.name || "",
|
||||
description: data?.description || "",
|
||||
});
|
||||
setDescriptionLength(data?.description?.length || 0);
|
||||
}, [data, reset]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
reset({
|
||||
name: data?.name,
|
||||
description: data?.description
|
||||
});
|
||||
setDescriptionLength(data?.description?.length || 0);
|
||||
}, [data, reset]);
|
||||
|
||||
const [descriptionLength, setDescriptionLength] = useState(data?.description?.length || 0);
|
||||
const maxDescriptionLength = 255;
|
||||
|
||||
|
||||
return (<>
|
||||
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
|
||||
{/* <div className="col-12 col-md-12">
|
||||
<label className="fs-5 text-dark text-center d-flex align-items-center justify-content-center flex-wrap">Edit Work Category</label>
|
||||
</div>
|
||||
*/}
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label">Category Name</label>
|
||||
<input type="text"
|
||||
|
||||
@ -8,8 +8,8 @@ import EditJobRole from "./EditJobRole";
|
||||
import CreateActivity from "./CreateActivity";
|
||||
import EditActivity from "./EditActivity";
|
||||
import ConfirmModal from "../common/ConfirmModal";
|
||||
import {MasterRespository} from "../../repositories/MastersRepository";
|
||||
import {cacheData, getCachedData} from "../../slices/apiDataManager";
|
||||
import { MasterRespository } from "../../repositories/MastersRepository";
|
||||
import { cacheData, getCachedData } from "../../slices/apiDataManager";
|
||||
import showToast from "../../services/toastService";
|
||||
import CreateWorkCategory from "./CreateWorkCategory";
|
||||
import EditWorkCategory from "./EditWorkCategory";
|
||||
@ -17,35 +17,55 @@ import CreateCategory from "./CreateContactCategory";
|
||||
import CreateContactTag from "./CreateContactTag";
|
||||
import EditContactCategory from "./EditContactCategory";
|
||||
import EditContactTag from "./EditContactTag";
|
||||
|
||||
import { useDeleteMasterItem } from "../../hooks/masterHook/useMaster";
|
||||
|
||||
const MasterModal = ({ modaldata, closeModal }) => {
|
||||
const [ isDeleteModalOpen, setIsDeleteModalOpen ] = useState( false );
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
const { mutate: deleteMasterItem, isPending } = useDeleteMasterItem();
|
||||
|
||||
const handleSelectedMasterDeleted = async () =>
|
||||
{
|
||||
const deleteFn = MasterRespository[modaldata.masterType];
|
||||
if (!deleteFn) {
|
||||
showToast(`No delete strategy defined for master type`,"error");
|
||||
return false;
|
||||
// const handleSelectedMasterDeleted = async () =>
|
||||
// {
|
||||
// debugger
|
||||
// const deleteFn = MasterRespository[modaldata.masterType];
|
||||
// if (!deleteFn) {
|
||||
// showToast(`No delete strategy defined for master type`,"error");
|
||||
// return false;
|
||||
// }
|
||||
// try
|
||||
// {
|
||||
// const response = await deleteFn( modaldata?.item?.id );
|
||||
// const selected_cachedData = getCachedData( modaldata?.masterType );
|
||||
// const updated_master = selected_cachedData?.filter(item => item.id !== modaldata?.item.id);
|
||||
// cacheData( modaldata?.masterType, updated_master )
|
||||
|
||||
// showToast(`${modaldata?.masterType} is deleted successfully`, "success");
|
||||
// handleCloseDeleteModal()
|
||||
|
||||
// } catch ( error )
|
||||
// {
|
||||
// const message = error.response.data.message || error.message || "Error occured api during call"
|
||||
// showToast(message, "success");
|
||||
// }
|
||||
// }
|
||||
|
||||
const handleSelectedMasterDeleted = () => {
|
||||
if (!modaldata?.masterType || !modaldata?.item?.id) {
|
||||
showToast("Missing master type or item", "error");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
const response = await deleteFn( modaldata?.item?.id );
|
||||
const selected_cachedData = getCachedData( modaldata?.masterType );
|
||||
const updated_master = selected_cachedData?.filter(item => item.id !== modaldata?.item.id);
|
||||
cacheData( modaldata?.masterType, updated_master )
|
||||
|
||||
showToast(`${modaldata?.masterType} is deleted successfully`, "success");
|
||||
handleCloseDeleteModal()
|
||||
|
||||
} catch ( error )
|
||||
{
|
||||
const message = error.response.data.message || error.message || "Error occured api during call"
|
||||
showToast(message, "success");
|
||||
}
|
||||
}
|
||||
|
||||
deleteMasterItem(
|
||||
{
|
||||
masterType: modaldata.masterType,
|
||||
item: modaldata.item,
|
||||
validateFn: modaldata.validateFn, // optional
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
handleCloseDeleteModal();
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (modaldata?.modalType === "delete") {
|
||||
@ -55,7 +75,7 @@ const MasterModal = ({ modaldata, closeModal }) => {
|
||||
|
||||
const handleCloseDeleteModal = () => {
|
||||
setIsDeleteModalOpen(false);
|
||||
closeModal();
|
||||
closeModal();
|
||||
};
|
||||
|
||||
if (modaldata?.modalType === "delete" && isDeleteModalOpen) {
|
||||
@ -88,7 +108,9 @@ const MasterModal = ({ modaldata, closeModal }) => {
|
||||
>
|
||||
<div
|
||||
className={`modal-dialog mx-sm-auto mx-1 ${
|
||||
["Application Role", "Edit-Application Role"].includes(modaldata?.modalType)
|
||||
["Application Role", "Edit-Application Role"].includes(
|
||||
modaldata?.modalType
|
||||
)
|
||||
? "modal-lg"
|
||||
: "modal-md"
|
||||
} modal-simple`}
|
||||
@ -96,18 +118,21 @@ const MasterModal = ({ modaldata, closeModal }) => {
|
||||
<div className="modal-content">
|
||||
<div className="modal-body p-sm-4 p-0">
|
||||
<div className="d-flex justify-content-between">
|
||||
<h6>{ `${modaldata?.modalType} `}</h6>
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
onClick={closeModal}
|
||||
></button>
|
||||
</div>
|
||||
<h6>{`${modaldata?.modalType} `}</h6>
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
onClick={closeModal}
|
||||
></button>
|
||||
</div>
|
||||
|
||||
{modaldata.modalType === "Application Role" && (
|
||||
<CreateRole masmodalType={modaldata.masterType} onClose={closeModal} />
|
||||
<CreateRole
|
||||
masmodalType={modaldata.masterType}
|
||||
onClose={closeModal}
|
||||
/>
|
||||
)}
|
||||
{modaldata.modalType === "Edit-Application Role" && (
|
||||
<EditRole master={modaldata} onClose={closeModal} />
|
||||
@ -122,7 +147,10 @@ const MasterModal = ({ modaldata, closeModal }) => {
|
||||
<CreateActivity onClose={closeModal} />
|
||||
)}
|
||||
{modaldata.modalType === "Edit-Activity" && (
|
||||
<EditActivity activityData={modaldata.item} onClose={closeModal} />
|
||||
<EditActivity
|
||||
activityData={modaldata.item}
|
||||
onClose={closeModal}
|
||||
/>
|
||||
)}
|
||||
{modaldata.modalType === "Work Category" && (
|
||||
<CreateWorkCategory onClose={closeModal} />
|
||||
@ -133,10 +161,10 @@ const MasterModal = ({ modaldata, closeModal }) => {
|
||||
{modaldata.modalType === "Contact Category" && (
|
||||
<CreateCategory data={modaldata.item} onClose={closeModal} />
|
||||
)}
|
||||
{modaldata.modalType === "Edit-Contact Category" && (
|
||||
{modaldata.modalType === "Edit-Contact Category" && (
|
||||
<EditContactCategory data={modaldata.item} onClose={closeModal} />
|
||||
)}
|
||||
{modaldata.modalType === "Contact Tag" && (
|
||||
{modaldata.modalType === "Contact Tag" && (
|
||||
<CreateContactTag data={modaldata.item} onClose={closeModal} />
|
||||
)}
|
||||
{modaldata.modalType === "Edit-Contact Tag" && (
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import {useState,useEffect} from "react"
|
||||
import {useState,useEffect, useCallback} from "react"
|
||||
import { MasterRespository } from "../../repositories/MastersRepository";
|
||||
import { cacheData,getCachedData } from "../../slices/apiDataManager";
|
||||
import { useSelector } from "react-redux";
|
||||
import {useMutation, useQueries, useQuery, useQueryClient} from "@tanstack/react-query";
|
||||
import showToast from "../../services/toastService";
|
||||
|
||||
|
||||
|
||||
@ -9,214 +11,687 @@ import { useSelector } from "react-redux";
|
||||
|
||||
|
||||
|
||||
export const useFeatures = () =>
|
||||
useMasterData("masterFeatures", MasterRespository.getFeatures);
|
||||
|
||||
export const useMasterRole = () =>
|
||||
useMasterData("masterRole", MasterRespository.getRoles);
|
||||
// const useMaster = () => {
|
||||
|
||||
|
||||
const useMaster = (isMa) => {
|
||||
|
||||
const selectedMaster = useSelector((store)=>store.localVariables.selectedMaster);
|
||||
const [data, setData] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
if (!selectedMaster) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
const cachedData = getCachedData(selectedMaster);
|
||||
if (cachedData) {
|
||||
// const selectedMaster = useSelector((store)=>store.localVariables.selectedMaster);
|
||||
// const [data, setData] = useState([]);
|
||||
// const [loading, setLoading] = useState(true);
|
||||
// const [error, setError] = useState("");
|
||||
// useEffect(() => {
|
||||
// const fetchData = async () => {
|
||||
// if (!selectedMaster) return;
|
||||
// setLoading(true);
|
||||
// try {
|
||||
// const cachedData = getCachedData(selectedMaster);
|
||||
// if (cachedData) {
|
||||
|
||||
setData(cachedData);
|
||||
// setData(cachedData);
|
||||
|
||||
} else {
|
||||
let response;
|
||||
switch (selectedMaster) {
|
||||
case "Application Role":
|
||||
response = await MasterRespository.getRoles();
|
||||
response = response.data;
|
||||
break;
|
||||
case "Job Role":
|
||||
response = await MasterRespository.getJobRole();
|
||||
response = response.data
|
||||
break;
|
||||
case "Activity":
|
||||
response = await MasterRespository.getActivites();
|
||||
response = response.data
|
||||
break;
|
||||
case "Work Category":
|
||||
response = await MasterRespository.getWorkCategory();
|
||||
response = response.data
|
||||
break;
|
||||
case "Contact Category":
|
||||
response = await MasterRespository.getContactCategory();
|
||||
response = response.data
|
||||
break;
|
||||
case "Contact Tag":
|
||||
response = await MasterRespository.getContactTag();
|
||||
response = response.data
|
||||
break;
|
||||
case "Status":
|
||||
response = [{description: null,featurePermission: null,id: "02dd4761-363c-49ed-8851-3d2489a3e98d",status:"status 1"},{description: null,featurePermission: null,id: "03dy9761-363c-49ed-8851-3d2489a3e98d",status:"status 2"},{description: null,featurePermission: null,id: "03dy7761-263c-49ed-8851-3d2489a3e98d",status:"Status 3"}];
|
||||
break;
|
||||
default:
|
||||
response = [];
|
||||
}
|
||||
// } else {
|
||||
// let response;
|
||||
// switch (selectedMaster) {
|
||||
// case "Application Role":
|
||||
// response = await MasterRespository.getRoles();
|
||||
// response = response.data;
|
||||
// break;
|
||||
// case "Job Role":
|
||||
// response = await MasterRespository.getJobRole();
|
||||
// response = response.data
|
||||
// break;
|
||||
// case "Activity":
|
||||
// response = await MasterRespository.getActivites();
|
||||
// response = response.data
|
||||
// break;
|
||||
// case "Work Category":
|
||||
// response = await MasterRespository.getWorkCategory();
|
||||
// response = response.data
|
||||
// break;
|
||||
// case "Contact Category":
|
||||
// response = await MasterRespository.getContactCategory();
|
||||
// response = response.data
|
||||
// break;
|
||||
// case "Contact Tag":
|
||||
// response = await MasterRespository.getContactTag();
|
||||
// response = response.data
|
||||
// break;
|
||||
// case "Status":
|
||||
// response = [{description: null,featurePermission: null,id: "02dd4761-363c-49ed-8851-3d2489a3e98d",status:"status 1"},{description: null,featurePermission: null,id: "03dy9761-363c-49ed-8851-3d2489a3e98d",status:"status 2"},{description: null,featurePermission: null,id: "03dy7761-263c-49ed-8851-3d2489a3e98d",status:"Status 3"}];
|
||||
// break;
|
||||
// default:
|
||||
// response = [];
|
||||
// }
|
||||
|
||||
if (response) {
|
||||
setData(response);
|
||||
cacheData(selectedMaster, response);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
// if (response) {
|
||||
// setData(response);
|
||||
// cacheData(selectedMaster, response);
|
||||
// }
|
||||
// }
|
||||
// } catch (err) {
|
||||
// setError("Failed to fetch data.");
|
||||
// } finally {
|
||||
// setLoading(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
if ( selectedMaster )
|
||||
{
|
||||
// if ( selectedMaster )
|
||||
// {
|
||||
|
||||
fetchData();
|
||||
}
|
||||
// fetchData();
|
||||
// }
|
||||
|
||||
}, [selectedMaster]);
|
||||
// }, [selectedMaster]);
|
||||
|
||||
|
||||
|
||||
return { data, loading, error }
|
||||
// return { data, loading, error }
|
||||
// };
|
||||
|
||||
|
||||
|
||||
|
||||
// export const useActivitiesMaster = () =>
|
||||
// {
|
||||
// const [ activities, setActivites ] = useState( [] )
|
||||
// const [ loading, setloading ] = useState( false );
|
||||
// const [ error, setError ] = useState()
|
||||
// const fetchActivities =async () => {
|
||||
// setloading(true);
|
||||
// try {
|
||||
// const response = await MasterRespository.getActivites();
|
||||
// setActivites(response.data);
|
||||
// cacheData( "ActivityMaster", response.data );
|
||||
// setloading(false);
|
||||
// } catch (err) {
|
||||
// setError(err);
|
||||
// setloading(false);
|
||||
// }
|
||||
// }
|
||||
// useEffect( () =>
|
||||
// {
|
||||
// const cacheddata = getCachedData( "ActivityMaster" );
|
||||
// if ( !cacheddata )
|
||||
// {
|
||||
// fetchActivities()
|
||||
// } else
|
||||
// {
|
||||
// setActivites(cacheddata);
|
||||
// }
|
||||
// }, [] )
|
||||
|
||||
// return {activities,loading,error}
|
||||
// }
|
||||
|
||||
// export const useWorkCategoriesMaster = () =>
|
||||
// {
|
||||
// const [ categories, setCategories ] = useState( [] )
|
||||
// const [ categoryLoading, setloading ] = useState( false );
|
||||
// const [ categoryError, setError ] = useState( "" )
|
||||
|
||||
// const fetchCategories =async () => {
|
||||
// const cacheddata = getCachedData("Work Category");
|
||||
|
||||
// if (!cacheddata) {
|
||||
// setloading(true);
|
||||
// try {
|
||||
// const response = await MasterRespository.getWorkCategory();
|
||||
// setCategories(response.data);
|
||||
// cacheData("Work Category", response.data);
|
||||
// } catch (err) {
|
||||
// setError(err);
|
||||
// console.log(err);
|
||||
// } finally {
|
||||
// setloading(false);
|
||||
// }
|
||||
// } else {
|
||||
// setCategories(cacheddata);
|
||||
// }
|
||||
// }
|
||||
// useEffect( () =>
|
||||
// {
|
||||
// fetchCategories()
|
||||
// }, [] )
|
||||
|
||||
// return {categories,categoryLoading,categoryError}
|
||||
// }
|
||||
|
||||
// export const useContactCategory = () =>
|
||||
// {
|
||||
// const [ contactCategory, setContactCategory ] = useState( [] )
|
||||
// const [ loading, setLoading ] = useState( false )
|
||||
// const [ Error, setError ] = useState()
|
||||
|
||||
// const fetchConatctCategory = async() =>
|
||||
// {
|
||||
// const cache_Category = getCachedData( "Contact Category" );
|
||||
// if ( !cache_Category )
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// let resp = await MasterRespository.getContactCategory();
|
||||
// setContactCategory( resp.data );
|
||||
// cacheData("Contact Category",resp.data)
|
||||
// } catch ( error )
|
||||
// {
|
||||
// setError(error)
|
||||
// }
|
||||
// } else
|
||||
// {
|
||||
// setContactCategory(cache_Category)
|
||||
// }
|
||||
// }
|
||||
|
||||
// useEffect( () =>
|
||||
// {
|
||||
// fetchConatctCategory()
|
||||
// }, [] )
|
||||
// return { contactCategory,loading,Error}
|
||||
// }
|
||||
// export const useContactTags = () => {
|
||||
// const [contactTags, setContactTags] = useState([]);
|
||||
// const [loading, setLoading] = useState(false);
|
||||
// const [error, setError] = useState(null);
|
||||
|
||||
// useEffect(() => {
|
||||
// const fetchContactTag = async () => {
|
||||
// const cache_Tags = getCachedData("Contact Tag");
|
||||
|
||||
// if (!cache_Tags) {
|
||||
// setLoading(true);
|
||||
// try {
|
||||
// const resp = await MasterRespository.getContactTag();
|
||||
// setContactTags(resp.data);
|
||||
// cacheData("Contact Tag", resp.data);
|
||||
// } catch (err) {
|
||||
// setError(err);
|
||||
// } finally {
|
||||
// setLoading(false);
|
||||
// }
|
||||
// } else {
|
||||
// setContactTags(cache_Tags);
|
||||
// }
|
||||
// };
|
||||
|
||||
// fetchContactTag();
|
||||
// }, []);
|
||||
|
||||
// return { contactTags, loading, error };
|
||||
// };
|
||||
|
||||
// Separate matser-------------
|
||||
|
||||
export const useActivitiesMaster = () =>
|
||||
{
|
||||
const { data:activities=[],isLoading:loading,error} = useQuery( {
|
||||
queryKey: [ "ActivityMaster" ],
|
||||
queryFn: async() =>
|
||||
{
|
||||
const response = await MasterRespository.getActivites();
|
||||
return response.data
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(error?.response?.data?.message || error.message || "Failed to fetch ActivityMasters", "error");
|
||||
},
|
||||
} )
|
||||
return {activities,loading,error}
|
||||
}
|
||||
export const useWorkCategoriesMaster = () => {
|
||||
const {
|
||||
data: categories = [],
|
||||
isLoading: categoryLoading,
|
||||
error: categoryError,
|
||||
} = useQuery({
|
||||
queryKey: ["Work Category"],
|
||||
queryFn: async () => {
|
||||
const response = await MasterRespository.getWorkCategory();
|
||||
return response.data;
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(
|
||||
error?.response?.data?.message ||
|
||||
error.message ||
|
||||
"Failed to fetch work categories",
|
||||
"error"
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return { categories, categoryLoading, categoryError };
|
||||
};
|
||||
|
||||
|
||||
export const useContactCategory = () =>
|
||||
{
|
||||
const {data:contactCategory=[],isLoading:loading,error:Error } = useQuery( {
|
||||
queryKey: [ "Contact Category" ],
|
||||
queryFn: async () =>
|
||||
{
|
||||
let resp = await MasterRespository.getContactCategory();
|
||||
return resp.data
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(error?.response?.data?.message || error.message || "Failed to fetch Contact categories", "error");
|
||||
},
|
||||
} )
|
||||
return { contactCategory,loading,Error}
|
||||
}
|
||||
|
||||
export const useContactTags = () => {
|
||||
const {
|
||||
data: contactTags = [],
|
||||
isLoading: loading,
|
||||
error,
|
||||
} = useQuery({
|
||||
queryKey: ["Contact Tag"],
|
||||
queryFn: async () => {
|
||||
const res = await MasterRespository.getContactTag();
|
||||
return res.data;
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(
|
||||
error?.response?.data?.message ||
|
||||
error.message ||
|
||||
"Failed to fetch Contact Tag",
|
||||
"error"
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return { contactTags, loading, error };
|
||||
};
|
||||
|
||||
// ===Application Masters Query=================================================
|
||||
|
||||
const fetchMasterData = async (masterType) => {
|
||||
switch (masterType) {
|
||||
case "Application Role":
|
||||
return (await MasterRespository.getRoles()).data;
|
||||
case "Job Role":
|
||||
return (await MasterRespository.getJobRole()).data;
|
||||
case "Activity":
|
||||
return (await MasterRespository.getActivites()).data;
|
||||
case "Work Category":
|
||||
return (await MasterRespository.getWorkCategory()).data;
|
||||
case "Contact Category":
|
||||
return (await MasterRespository.getContactCategory()).data;
|
||||
case "Contact Tag":
|
||||
return (await MasterRespository.getContactTag()).data;
|
||||
case "Status":
|
||||
return [
|
||||
{
|
||||
description: null,
|
||||
featurePermission: null,
|
||||
id: "02dd4761-363c-49ed-8851-3d2489a3e98d",
|
||||
status: "status 1",
|
||||
},
|
||||
{
|
||||
description: null,
|
||||
featurePermission: null,
|
||||
id: "03dy9761-363c-49ed-8851-3d2489a3e98d",
|
||||
status: "status 2",
|
||||
},
|
||||
{
|
||||
description: null,
|
||||
featurePermission: null,
|
||||
id: "03dy7761-263c-49ed-8851-3d2489a3e98d",
|
||||
status: "Status 3",
|
||||
},
|
||||
];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const useMaster = () => {
|
||||
const selectedMaster = useSelector((store) => store.localVariables.selectedMaster);
|
||||
const queryFn = useCallback(() => fetchMasterData(selectedMaster), [selectedMaster]);
|
||||
const {
|
||||
data = [],
|
||||
isLoading,
|
||||
error,
|
||||
} = useQuery({
|
||||
queryKey: ["masterData", selectedMaster],
|
||||
queryFn,
|
||||
enabled: !!selectedMaster,
|
||||
staleTime: 1000 * 60 * 10,
|
||||
refetchOnWindowFocus: false,
|
||||
onError: (error) => {
|
||||
showToast(error?.response?.data?.message || error.message || `Failed to fetch ${selectedMaster} Maseter`, "error");
|
||||
},
|
||||
});
|
||||
|
||||
return { data, loading: isLoading, error };
|
||||
};
|
||||
|
||||
export default useMaster;
|
||||
|
||||
export const useActivitiesMaster = () =>
|
||||
{
|
||||
const [ activities, setActivites ] = useState( [] )
|
||||
const [ loading, setloading ] = useState( false );
|
||||
const [ error, setError ] = useState()
|
||||
const fetchActivities =async () => {
|
||||
setloading(true);
|
||||
try {
|
||||
const response = await MasterRespository.getActivites();
|
||||
setActivites(response.data);
|
||||
cacheData( "ActivityMaster", response.data );
|
||||
setloading(false);
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
setloading(false);
|
||||
}
|
||||
}
|
||||
useEffect( () =>
|
||||
{
|
||||
const cacheddata = getCachedData( "ActivityMaster" );
|
||||
if ( !cacheddata )
|
||||
{
|
||||
fetchActivities()
|
||||
} else
|
||||
{
|
||||
setActivites(cacheddata);
|
||||
}
|
||||
}, [] )
|
||||
|
||||
return {activities,loading,error}
|
||||
}
|
||||
// ================================Mutation====================================
|
||||
|
||||
export const useWorkCategoriesMaster = () =>
|
||||
{
|
||||
const [ categories, setCategories ] = useState( [] )
|
||||
const [ categoryLoading, setloading ] = useState( false );
|
||||
const [ categoryError, setError ] = useState( "" )
|
||||
|
||||
const fetchCategories =async () => {
|
||||
const cacheddata = getCachedData("Work Category");
|
||||
|
||||
if (!cacheddata) {
|
||||
setloading(true);
|
||||
try {
|
||||
const response = await MasterRespository.getWorkCategory();
|
||||
setCategories(response.data);
|
||||
cacheData("Work Category", response.data);
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
console.log(err);
|
||||
} finally {
|
||||
setloading(false);
|
||||
}
|
||||
} else {
|
||||
setCategories(cacheddata);
|
||||
}
|
||||
}
|
||||
useEffect( () =>
|
||||
{
|
||||
fetchCategories()
|
||||
}, [] )
|
||||
|
||||
return {categories,categoryLoading,categoryError}
|
||||
}
|
||||
|
||||
export const useContactCategory = () =>
|
||||
// Job Role-----------------------------------
|
||||
export const useUpdateJobRole = (onSuccessCallback, onErrorCallback) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ({ id, payload }) => {
|
||||
const response = await MasterRespository.updateJobRole(id, payload);
|
||||
return response.data;
|
||||
},
|
||||
onSuccess: (data, variables) => {
|
||||
showToast("JobRole updated successfully.", "success");
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["masterData", "Job Role"],
|
||||
});
|
||||
|
||||
if (onSuccessCallback) onSuccessCallback(data);
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(error.message || "Something went wrong", "error");
|
||||
if (onErrorCallback) onErrorCallback(error);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useCreateJobRole = (onSuccessCallback) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (payload) => {
|
||||
const response = await MasterRespository.createJobRole(payload);
|
||||
return response.data;
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
showToast("JobRole added successfully.", "success");
|
||||
|
||||
queryClient.invalidateQueries({queryKey:["masterData", "Job Role"]});
|
||||
|
||||
if (onSuccessCallback) onSuccessCallback(data);
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(error.message || "Something went wrong", "error");
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Application Role-------------------------------------------
|
||||
|
||||
export const useCreateApplicationRole = (onSuccessCallback) =>
|
||||
{
|
||||
const [ contactCategory, setContactCategory ] = useState( [] )
|
||||
const [ loading, setLoading ] = useState( false )
|
||||
const [ Error, setError ] = useState()
|
||||
|
||||
const fetchConatctCategory = async() =>
|
||||
{
|
||||
const cache_Category = getCachedData( "Contact Category" );
|
||||
if ( !cache_Category )
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation( {
|
||||
mutationFn: async ( payload ) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
let resp = await MasterRespository.getContactCategory();
|
||||
setContactCategory( resp.data );
|
||||
cacheData("Contact Category",resp.data)
|
||||
} catch ( error )
|
||||
{
|
||||
setError(error)
|
||||
}
|
||||
} else
|
||||
const resp = await MasterRespository.createRole( payload );
|
||||
return resp.data;
|
||||
},
|
||||
onSuccess: ( data ) =>
|
||||
{
|
||||
setContactCategory(cache_Category)
|
||||
queryClient.invalidateQueries( {queryKey:[ "masterData", "Application Role" ]} )
|
||||
showToast( "Application Role added successfully", "success" );
|
||||
if(onSuccessCallback) onSuccessCallback(data)
|
||||
},
|
||||
onError: ( error ) =>
|
||||
{
|
||||
showToast(error.message || "Something went wrong", "error");
|
||||
}
|
||||
}
|
||||
|
||||
useEffect( () =>
|
||||
{
|
||||
fetchConatctCategory()
|
||||
}, [] )
|
||||
return { contactCategory,loading,Error}
|
||||
})
|
||||
}
|
||||
export const useContactTags = () => {
|
||||
const [contactTags, setContactTags] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchContactTag = async () => {
|
||||
const cache_Tags = getCachedData("Contact Tag");
|
||||
export const useUpdateApplicationRole = (onSuccessCallback) =>
|
||||
{
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
if (!cache_Tags) {
|
||||
setLoading(true);
|
||||
try {
|
||||
const resp = await MasterRespository.getContactTag();
|
||||
setContactTags(resp.data);
|
||||
cacheData("Contact Tag", resp.data);
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
} else {
|
||||
setContactTags(cache_Tags);
|
||||
return useMutation({
|
||||
mutationFn: async ( {id, payload} ) =>
|
||||
{
|
||||
const response = await MasterRespository.updateRoles(id,payload);
|
||||
return response.data;
|
||||
},
|
||||
onSuccess: (data, variables) => {
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["masterData", "Application Role"],
|
||||
});
|
||||
showToast("Application Role updated successfully.", "success");
|
||||
|
||||
if (onSuccessCallback) onSuccessCallback(data);
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(error.message || "Something went wrong", "error");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Activity------------------------------
|
||||
export const useCreateActivity = (onSuccessCallback) =>
|
||||
{
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation( {
|
||||
mutationFn: async ( payload ) =>
|
||||
{
|
||||
const resp = await MasterRespository.createActivity(payload);
|
||||
return resp.data;
|
||||
},
|
||||
onSuccess: ( data ) =>
|
||||
{
|
||||
queryClient.invalidateQueries( {queryKey:[ "masterData", "Activity" ]} )
|
||||
showToast( "Activity added successfully", "success" );
|
||||
if(onSuccessCallback) onSuccessCallback(data)
|
||||
},
|
||||
onError: ( error ) =>
|
||||
{
|
||||
showToast(error.message || "Something went wrong", "error");
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const useUpdateActivity = (onSuccessCallback) =>
|
||||
{
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ( {id, payload} ) =>
|
||||
{
|
||||
const response = await MasterRespository.updateActivity(id,payload);
|
||||
return response.data;
|
||||
},
|
||||
onSuccess: (data, variables) => {
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["masterData", "Activity"],
|
||||
});
|
||||
showToast("Activity updated successfully.", "success");
|
||||
|
||||
if (onSuccessCallback) onSuccessCallback(data);
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(error.message || "Something went wrong", "error");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//-----Create work Category-------------------------------
|
||||
export const useCreateWorkCategory = (onSuccessCallback) =>
|
||||
{
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation( {
|
||||
mutationFn: async ( payload ) =>
|
||||
{
|
||||
const resp = await MasterRespository.createWorkCategory(payload);
|
||||
return resp.data;
|
||||
},
|
||||
onSuccess: ( data ) =>
|
||||
{
|
||||
queryClient.invalidateQueries({queryKey: [ "masterData", "Work Category" ]} )
|
||||
showToast( "Work Category added successfully", "success" );
|
||||
if(onSuccessCallback) onSuccessCallback(data)
|
||||
},
|
||||
onError: ( error ) =>
|
||||
{
|
||||
showToast(error.message || "Something went wrong", "error");
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const useUpdateWorkCategory = (onSuccessCallback) =>
|
||||
{
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ( {id, payload} ) =>
|
||||
{
|
||||
const response = await MasterRespository.updateWorkCategory(id,payload);
|
||||
return response.data;
|
||||
},
|
||||
onSuccess: (data, variables) => {
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["masterData", "Work Category"],
|
||||
});
|
||||
showToast("Work Category updated successfully.", "success");
|
||||
|
||||
if (onSuccessCallback) onSuccessCallback(data);
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(error.message || "Something went wrong", "error");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
//-- Contact Category---------------------------
|
||||
|
||||
|
||||
export const useCreateContactCategory = (onSuccessCallback) =>
|
||||
{
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation( {
|
||||
mutationFn: async ( payload ) =>
|
||||
{
|
||||
const resp = await MasterRespository.createContactCategory(payload);
|
||||
return resp.data;
|
||||
},
|
||||
onSuccess: ( data ) =>
|
||||
{
|
||||
queryClient.invalidateQueries( {queryKey:[ "masterData", "Contact Category" ]} )
|
||||
showToast( "Contact Category added successfully", "success" );
|
||||
if(onSuccessCallback) onSuccessCallback(data)
|
||||
},
|
||||
onError: ( error ) =>
|
||||
{
|
||||
showToast(error.message || "Something went wrong", "error");
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const useUpdateContactCategory = (onSuccessCallback) =>
|
||||
{
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ( {id, payload} ) =>
|
||||
{
|
||||
const response = await MasterRespository.updateContactCategory(id,payload);
|
||||
return response.data;
|
||||
},
|
||||
onSuccess: (data, variables) => {
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["masterData", "Contact Category"],
|
||||
});
|
||||
showToast("Contact Category updated successfully.", "success");
|
||||
|
||||
if (onSuccessCallback) onSuccessCallback(data);
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(error.message || "Something went wrong", "error");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// ---------Contact Tag-------------------
|
||||
|
||||
export const useCreateContactTag = (onSuccessCallback) =>
|
||||
{
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation( {
|
||||
mutationFn: async ( payload ) =>
|
||||
{
|
||||
const resp = await MasterRespository.createContactTag(payload);
|
||||
return resp.data;
|
||||
},
|
||||
onSuccess: ( data ) =>
|
||||
{
|
||||
queryClient.invalidateQueries( {queryKey:[ "masterData", "Contact Tag" ]} )
|
||||
showToast( "Contact Tag added successfully", "success" );
|
||||
if(onSuccessCallback) onSuccessCallback(data)
|
||||
},
|
||||
onError: ( error ) =>
|
||||
{
|
||||
showToast(error.message || "Something went wrong", "error");
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const useUpdateContactTag = (onSuccessCallback) =>
|
||||
{
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ( {id, payload} ) =>
|
||||
{
|
||||
debugger
|
||||
const response = await MasterRespository.updateContactTag(id,payload);
|
||||
return response.data;
|
||||
},
|
||||
onSuccess: (data, variables) => {
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["masterData", "Contact Tag"],
|
||||
});
|
||||
showToast("Contact Tag updated successfully.", "success");
|
||||
|
||||
if (onSuccessCallback) onSuccessCallback(data);
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(error.message || "Something went wrong", "error");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// -Delete Master --------
|
||||
export const useDeleteMasterItem = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ( {masterType, item} ) =>
|
||||
{
|
||||
const deleteFn = MasterRespository[masterType];
|
||||
if (!deleteFn) {
|
||||
throw new Error(`No delete strategy defined for master type: ${masterType}`);
|
||||
}
|
||||
};
|
||||
|
||||
fetchContactTag();
|
||||
}, []);
|
||||
await deleteFn(item.id);
|
||||
return { masterType };
|
||||
},
|
||||
|
||||
return { contactTags, loading, error };
|
||||
onSuccess: ({ masterType }) => {
|
||||
queryClient.invalidateQueries({ queryKey: ["masterData", masterType] });
|
||||
|
||||
showToast(`${masterType} deleted successfully.`, "success");
|
||||
},
|
||||
|
||||
onError: (error) => {
|
||||
const message =
|
||||
error?.response?.data?.message || error?.message || "Error occurred during deletion";
|
||||
showToast(message, "error");
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -3,236 +3,274 @@ import { cacheData, getCachedData } from "../slices/apiDataManager";
|
||||
import { RolesRepository } from "../repositories/MastersRepository";
|
||||
import EmployeeRepository from "../repositories/EmployeeRepository";
|
||||
import ProjectRepository from "../repositories/ProjectRepository";
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import showToast from "../services/toastService";
|
||||
import {useSelector} from "react-redux";
|
||||
import {store} from "../store/store";
|
||||
import {queryClient} from "../layouts/AuthLayout";
|
||||
|
||||
export const useAllEmployees = (showInactive) => {
|
||||
const [employeesList, setEmployeeList] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState();
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
let EmployeeList_cached = getCachedData("AllEmployees");
|
||||
if (!EmployeeList_cached) {
|
||||
setLoading(true);
|
||||
const response = await EmployeeRepository.getAllEmployeeList(showInactive);
|
||||
cacheData("AllEmployees", response.data);
|
||||
setEmployeeList(response.data);
|
||||
setLoading(false);
|
||||
} else {
|
||||
setEmployeeList(EmployeeList_cached);
|
||||
setLoading(false);
|
||||
}
|
||||
} catch (error) {
|
||||
setError("Failed to fetch data.");
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
|
||||
// Query ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
export const useAllEmployees = ( showInactive ) =>
|
||||
{
|
||||
const {
|
||||
data = [],
|
||||
isLoading,
|
||||
error,
|
||||
refetch, // optional if you want recall functionality
|
||||
} = useQuery({
|
||||
queryKey: ['allEmployee', showInactive],
|
||||
queryFn: async () => {
|
||||
const res = await EmployeeRepository.getAllEmployeeList(showInactive);
|
||||
return res.data;
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
employeesList: data,
|
||||
loading: isLoading,
|
||||
error,
|
||||
recallEmployeeData: refetch,
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
return { employeesList, loading, error };
|
||||
};
|
||||
|
||||
export const useEmployees = (selectedProject) => {
|
||||
const [employees, setEmployeeList] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [projects, setProjects] = useState([]);
|
||||
// ManageBucket.jsx
|
||||
export const useEmployees = ( selectedProject ) =>
|
||||
{
|
||||
|
||||
const fetchData = async (projectid) => {
|
||||
try {
|
||||
let EmployeeByProject_Cache = getCachedData("employeeListByProject");
|
||||
if (
|
||||
!EmployeeByProject_Cache ||
|
||||
!EmployeeByProject_Cache.projectId === projectid
|
||||
) {
|
||||
EmployeeRepository.getEmployeeListByproject(projectid)
|
||||
.then((response) => {
|
||||
setEmployeeList(response);
|
||||
cacheData("employeeListByProject", {
|
||||
data: response,
|
||||
projectId: projectid,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
setError("Failed to fetch data.");
|
||||
});
|
||||
} else {
|
||||
setEmployeeList(EmployeeByProject_Cache.data);
|
||||
}
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data.");
|
||||
setLoading(false);
|
||||
}
|
||||
const {
|
||||
data = [],
|
||||
isLoading,
|
||||
error,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
queryKey: ["employeeListByProject", selectedProject],
|
||||
queryFn: async () => {
|
||||
const res = await EmployeeRepository.getEmployeeListByproject(selectedProject);
|
||||
return res.data || res;
|
||||
},
|
||||
enabled: !!selectedProject,
|
||||
});
|
||||
|
||||
return {
|
||||
employees: data,
|
||||
loading: isLoading,
|
||||
projects: [], // if needed, pass this separately or manage from another hook
|
||||
reCallAllEmployee: refetch,
|
||||
error,
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedProject) {
|
||||
fetchData(selectedProject);
|
||||
}
|
||||
}, [selectedProject]);
|
||||
|
||||
return { employees, loading, projects, reCallAllEmployee };
|
||||
};
|
||||
|
||||
// ManageRole.jsx
|
||||
export const useEmployeeRoles = (employeeId) => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState();
|
||||
const [employeeRoles, setEmployeeRoles] = useState([]);
|
||||
const fetchData = async (employeeid) => {
|
||||
try {
|
||||
let response = await RolesRepository.getEmployeeRoles(employeeid);
|
||||
setEmployeeRoles(response.data);
|
||||
cacheData("employeelist", response.data);
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data.");
|
||||
setEmployeeRoles([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
const {
|
||||
data = [],
|
||||
isLoading: loading,
|
||||
error,
|
||||
} = useQuery({
|
||||
queryKey: ['employeeRoles', employeeId],
|
||||
queryFn: async () => {
|
||||
const res = await RolesRepository.getEmployeeRoles(employeeId);
|
||||
return res.data;
|
||||
},
|
||||
enabled: !!employeeId,
|
||||
});
|
||||
|
||||
return {
|
||||
employeeRoles: data,
|
||||
loading,
|
||||
error,
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (employeeId) {
|
||||
fetchData(employeeId);
|
||||
}
|
||||
}, [employeeId]);
|
||||
|
||||
return { employeeRoles, loading, error };
|
||||
};
|
||||
|
||||
// EmployeeProfile.jsx
|
||||
export const useEmployeesByProject = (projectId) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState();
|
||||
const [employees, setEmployees] = useState([]);
|
||||
const {
|
||||
data = [],
|
||||
isLoading: loading,
|
||||
error,
|
||||
refetch: recallProjectEmplloyee,
|
||||
} = useQuery({
|
||||
queryKey: ['projectEmployees', projectId],
|
||||
queryFn: async () => {
|
||||
const res = await ProjectRepository.getEmployeesByProject(projectId);
|
||||
return res.data;
|
||||
},
|
||||
enabled: !!projectId,
|
||||
});
|
||||
|
||||
const fetchData = async () => {
|
||||
const Employees_cache = getCachedData("employeeListByProject");
|
||||
if (!Employees_cache || Employees_cache.projectId !== projectId) {
|
||||
setEmployees(true);
|
||||
ProjectRepository.getEmployeesByProject(projectId)
|
||||
.then((response) => {
|
||||
setEmployees(response.data);
|
||||
cacheData("employeeListByProject", {
|
||||
data: response.data,
|
||||
projectId,
|
||||
});
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
setError("Failed to fetch data.");
|
||||
setLoading(false);
|
||||
});
|
||||
} else {
|
||||
setEmployees(Employees_cache.data);
|
||||
setLoading(false);
|
||||
}
|
||||
return {
|
||||
employees: data,
|
||||
loading,
|
||||
error,
|
||||
recallProjectEmplloyee,
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(projectId);
|
||||
}, [projectId]);
|
||||
|
||||
return { employees, loading, error, recallProjectEmplloyee: fetchData };
|
||||
};
|
||||
|
||||
// EmployeeList.jsx
|
||||
export const useEmployeesAllOrByProjectId = (projectId, showInactive) => {
|
||||
const [employees, setEmployees] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const isAllEmployees = !projectId && projectId !== undefined;
|
||||
|
||||
const fetchData = async (showInactive) => {
|
||||
if ( projectId )
|
||||
{
|
||||
const Employees_cache = getCachedData("employeeListByProject");
|
||||
if (!Employees_cache || Employees_cache.projectId !== projectId) {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await ProjectRepository.getEmployeesByProject(
|
||||
projectId
|
||||
);
|
||||
setEmployees(response.data);
|
||||
cacheData("employeeListByProject", {
|
||||
data: response.data,
|
||||
projectId,
|
||||
});
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data.");
|
||||
setLoading(false);
|
||||
}
|
||||
} else {
|
||||
setEmployees(Employees_cache.data);
|
||||
setLoading(false);
|
||||
}
|
||||
} else
|
||||
{
|
||||
const cacheKey = showInactive
|
||||
? "allInactiveEmployeeList"
|
||||
: "allEmployeeList";
|
||||
const queryKey = isAllEmployees
|
||||
? ['allEmployees', showInactive]
|
||||
: ['projectEmployees', projectId];
|
||||
|
||||
try {
|
||||
const response = await EmployeeRepository.getAllEmployeeList(
|
||||
showInactive
|
||||
);
|
||||
setEmployees(response.data);
|
||||
cacheData(cacheKey, { data: response.data });
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data.");
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
const queryFn = async () => {
|
||||
if (isAllEmployees) {
|
||||
const res = await EmployeeRepository.getAllEmployeeList(showInactive);
|
||||
return res.data;
|
||||
} else {
|
||||
const res = await ProjectRepository.getEmployeesByProject(projectId);
|
||||
return res.data;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(showInactive); // Fetch data when the component mounts or projectId changes
|
||||
}, [projectId]); // Re-fetch when projectId changes
|
||||
const {
|
||||
data: employees = [],
|
||||
isLoading,
|
||||
error,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
queryKey,
|
||||
queryFn,
|
||||
enabled: isAllEmployees || !!projectId,
|
||||
});
|
||||
|
||||
return {
|
||||
employees,
|
||||
loading: isLoading,
|
||||
error,
|
||||
recallEmployeeData: refetch,
|
||||
};
|
||||
};
|
||||
|
||||
// ManageEmployee.jsx
|
||||
export const useEmployeeProfile = ( employeeId ) =>
|
||||
{
|
||||
const isEnabled = !!employeeId;
|
||||
const {
|
||||
data = null,
|
||||
isLoading: loading,
|
||||
error,
|
||||
refetch
|
||||
} = useQuery({
|
||||
queryKey: ['employeeProfile', employeeId],
|
||||
queryFn: async () => {
|
||||
if (!employeeId) return null;
|
||||
const res = await EmployeeRepository.getEmployeeProfile(employeeId);
|
||||
return res.data;
|
||||
},
|
||||
enabled: isEnabled,
|
||||
});
|
||||
|
||||
return {
|
||||
employee: data,
|
||||
loading,
|
||||
error,
|
||||
recallEmployeeData: fetchData,
|
||||
refetch
|
||||
};
|
||||
};
|
||||
|
||||
export const useEmployeeProfile = (employeeId) => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState();
|
||||
const [employee, setEmployees] = useState(null);
|
||||
|
||||
const fetchData = async () => {
|
||||
if (!employeeId) {
|
||||
// Reset the state if no employeeId (e.g., opening for 'add' mode)
|
||||
setEmployees(null);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
const Employee_cache = getCachedData("employeeProfile");
|
||||
if (!Employee_cache || Employee_cache.employeeId !== employeeId) {
|
||||
EmployeeRepository.getEmployeeProfile(employeeId)
|
||||
.then((response) => {
|
||||
setEmployees(response.data);
|
||||
cacheData("employeeProfile", { data: response.data, employeeId });
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
setError("Failed to fetch data.");
|
||||
setLoading(false);
|
||||
});
|
||||
} else {
|
||||
setEmployees(Employee_cache.data);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
// Mutation------------------------------------------------------------------
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [employeeId]);
|
||||
|
||||
return { employee, loading, error };
|
||||
|
||||
export const useUpdateEmployee = () =>
|
||||
{
|
||||
const selectedProject = useSelector((store)=>store.localVariables.projectId)
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (employeeData) => EmployeeRepository.manageEmployee(employeeData),
|
||||
onSuccess: (_, variables) => {
|
||||
const id = variables.id || variables.employeeId;
|
||||
const isAllEmployee = variables.IsAllEmployee;
|
||||
|
||||
// Cache invalidation
|
||||
queryClient.invalidateQueries( {queryKey:[ 'allEmployees'] });
|
||||
// queryClient.invalidateQueries(['employeeProfile', id]);
|
||||
queryClient.invalidateQueries( {queryKey: [ 'projectEmployees' ]} );
|
||||
queryClient.removeQueries( {queryKey: [ "empListByProjectAllocated" ]} );
|
||||
|
||||
// queryClient.invalidateQueries( {queryKey:[ 'employeeListByProject']} );
|
||||
showToast( `Employee ${ id ? 'updated' : 'created' } successfully`, 'success' );
|
||||
},
|
||||
onError: (error) => {
|
||||
const msg = error?.response?.data?.message || error.message || 'Something went wrong';
|
||||
showToast(msg, 'error');
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const useSuspendEmployee = ({ setIsDeleteModalOpen, setemployeeLodaing }) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id) => {
|
||||
setemployeeLodaing(true);
|
||||
return EmployeeRepository.deleteEmployee(id);
|
||||
},
|
||||
|
||||
onSuccess: () => {
|
||||
showToast("Employee deleted successfully.", "success");
|
||||
|
||||
// queryClient.invalidateQueries( ['allEmployee',false]);
|
||||
queryClient.invalidateQueries( {queryKey: [ 'projectEmployees' ]} );
|
||||
queryClient.invalidateQueries( {queryKey:[ 'employeeListByProject' ,selectedProject]} );
|
||||
|
||||
setIsDeleteModalOpen(false);
|
||||
},
|
||||
|
||||
onError: (error) => {
|
||||
const message =
|
||||
error.response?.data?.message ||
|
||||
error.message ||
|
||||
"An unexpected error occurred";
|
||||
showToast(message, "error");
|
||||
setIsDeleteModalOpen(false);
|
||||
},
|
||||
|
||||
onSettled: () => {
|
||||
setemployeeLodaing(false);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// Manage Role
|
||||
|
||||
|
||||
export const useUpdateEmployeeRoles = ({ onClose, resetForm, onSuccessCallback } = {}) => {
|
||||
const queryClient = useQueryClient();
|
||||
const mutation = useMutation({
|
||||
mutationFn: (updates) => RolesRepository.createEmployeeRoles(updates),
|
||||
onSuccess: () => {
|
||||
showToast("Roles updated successfully", "success");
|
||||
|
||||
resetForm?.();
|
||||
onClose?.();
|
||||
onSuccessCallback?.();
|
||||
|
||||
queryClient.invalidateQueries( {queryKey: [ "employeeRoles" ]} );
|
||||
queryClient.invalidateQueries( {queryKey: [ "profile" ]} );
|
||||
},
|
||||
onError: (err) => {
|
||||
const message =
|
||||
err?.response?.data?.message || err?.message || "Error occurred while updating roles";
|
||||
showToast(message, "error");
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
updateRoles: mutation.mutate,
|
||||
isPending: mutation.isPending,
|
||||
isError: mutation.isError,
|
||||
error: mutation.error,
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { cacheData,getCachedData } from "../slices/apiDataManager";
|
||||
import { MasterRespository } from "../repositories/MastersRepository";
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
export const useMasterRole =()=>{
|
||||
|
||||
@ -43,40 +43,55 @@ export const useMasterRole =()=>{
|
||||
return {masterRole,loading}
|
||||
}
|
||||
|
||||
export const useFeatures =()=> {
|
||||
const [masterFeatures, setMasterFeatures] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
// export const useFeatures =()=> {
|
||||
// const [masterFeatures, setMasterFeatures] = useState([]);
|
||||
// const [loading, setLoading] = useState(true);
|
||||
// const [error, setError] = useState("");
|
||||
|
||||
const fetchData = async () => {
|
||||
// const fetchData = async () => {
|
||||
|
||||
try {
|
||||
const features_cache = getCachedData("masterFeatures");
|
||||
if (!features_cache) {
|
||||
MasterRespository.getFeatures()
|
||||
.then((response) => {
|
||||
setMasterFeatures(response.data);
|
||||
// try {
|
||||
// const features_cache = getCachedData("masterFeatures");
|
||||
// if (!features_cache) {
|
||||
// MasterRespository.getFeatures()
|
||||
// .then((response) => {
|
||||
// setMasterFeatures(response.data);
|
||||
|
||||
cacheData("features", response.data);
|
||||
setLoading(false)
|
||||
})
|
||||
.catch((error) => {
|
||||
setError("Failed to fetch data.");
|
||||
});
|
||||
}else{
|
||||
if (!masterFeatures.length) setMasterFeatures(features_cache);
|
||||
}
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
// cacheData("features", response.data);
|
||||
// setLoading(false)
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// setError("Failed to fetch data.");
|
||||
// });
|
||||
// }else{
|
||||
// if (!masterFeatures.length) setMasterFeatures(features_cache);
|
||||
// }
|
||||
// } catch (err) {
|
||||
// setError("Failed to fetch data.");
|
||||
// } finally {
|
||||
// setLoading(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
fetchData();
|
||||
},[])
|
||||
// useEffect(()=>{
|
||||
// fetchData();
|
||||
// },[])
|
||||
|
||||
return{masterFeatures,loading}
|
||||
}
|
||||
// return{masterFeatures,loading}
|
||||
// }
|
||||
|
||||
// -----------------Query- -------------------------
|
||||
export const useFeatures = () => {
|
||||
const {data=[],isLoading,error} = useQuery({
|
||||
queryKey: ["masterFeatures"],
|
||||
queryFn: async () => {
|
||||
const response = await MasterRespository.getFeatures();
|
||||
return response.data;
|
||||
},
|
||||
|
||||
} );
|
||||
return {
|
||||
masterFeatures:data,loading:isLoading,error
|
||||
}
|
||||
};
|
||||
@ -14,7 +14,7 @@ const usePagination = (data, itemsPerPage) => {
|
||||
setCurrentPage(pageNumber);
|
||||
};
|
||||
|
||||
return { currentPage, totalPages, currentItems, paginate };
|
||||
return { currentPage, totalPages, currentItems, paginate,setCurrentPage };
|
||||
};
|
||||
|
||||
export default usePagination;
|
||||
|
||||
@ -3,60 +3,99 @@ import AuthRepository from "../repositories/AuthRepository";
|
||||
import {cacheData, cacheProfileData, getCachedData, getCachedProfileData} from "../slices/apiDataManager";
|
||||
import {useSelector} from "react-redux";
|
||||
import eventBus from "../services/eventBus";
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
let hasFetched = false;
|
||||
let hasReceived = false;
|
||||
|
||||
export const useProfile = () => {
|
||||
const loggedUser = useSelector( ( store ) => store.globalVariables.loginUser );
|
||||
const [profile, setProfile] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
// export const useProfile = () => {
|
||||
// const loggedUser = useSelector( ( store ) => store.globalVariables.loginUser );
|
||||
// const [profile, setProfile] = useState(null);
|
||||
// const [loading, setLoading] = useState(false);
|
||||
// const [error, setError] = useState("");
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
let response = await AuthRepository.profile();
|
||||
setProfile(response.data);
|
||||
cacheProfileData(response.data);
|
||||
} catch (error) {
|
||||
setError("Failed to fetch data.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
// const fetchData = async () => {
|
||||
// try {
|
||||
// setLoading(true);
|
||||
// let response = await AuthRepository.profile();
|
||||
// setProfile(response.data);
|
||||
// cacheProfileData(response.data);
|
||||
// } catch (error) {
|
||||
// setError("Failed to fetch data.");
|
||||
// } finally {
|
||||
// setLoading(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
const validation = () => {
|
||||
if (!hasFetched) {
|
||||
hasFetched = true;
|
||||
if (!loggedUser) {
|
||||
fetchData();
|
||||
} else {
|
||||
setProfile(loggedUser);
|
||||
}
|
||||
}
|
||||
// const validation = () => {
|
||||
// if (!hasFetched) {
|
||||
// hasFetched = true;
|
||||
// if (!loggedUser) {
|
||||
// fetchData();
|
||||
// } else {
|
||||
// setProfile(loggedUser);
|
||||
// }
|
||||
// }
|
||||
|
||||
setProfile(loggedUser);
|
||||
}
|
||||
// setProfile(loggedUser);
|
||||
// }
|
||||
|
||||
useEffect(() => {
|
||||
validation();
|
||||
}, [loggedUser]);
|
||||
// useEffect(() => {
|
||||
// validation();
|
||||
// }, [loggedUser]);
|
||||
|
||||
const handler = useCallback(
|
||||
(data) => {
|
||||
if(!getCachedData("hasReceived")){
|
||||
cacheData("hasReceived", true);
|
||||
hasFetched = false;
|
||||
validation();
|
||||
}
|
||||
},[]
|
||||
);
|
||||
// const handler = useCallback(
|
||||
// (data) => {
|
||||
// if(!getCachedData("hasReceived")){
|
||||
// cacheData("hasReceived", true);
|
||||
// hasFetched = false;
|
||||
// validation();
|
||||
// }
|
||||
// },[]
|
||||
// );
|
||||
|
||||
// useEffect(() => {
|
||||
// eventBus.on("assign_project_one", handler);
|
||||
// return () => eventBus.off("assign_project_one", handler);
|
||||
// }, [handler]);
|
||||
|
||||
// return { profile, loading, error };
|
||||
// };
|
||||
|
||||
export const useProfile = () => {
|
||||
const loggedUser = useSelector((store) => store.globalVariables.loginUser);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const {
|
||||
data: profile,
|
||||
error,
|
||||
isLoading,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
queryKey: ["profile"],
|
||||
queryFn: async () => {
|
||||
const response = await AuthRepository.profile();
|
||||
cacheProfileData(response.data);
|
||||
return response.data;
|
||||
},
|
||||
initialData: loggedUser || undefined,
|
||||
enabled: !loggedUser,
|
||||
staleTime: 10 * 60 * 1000,
|
||||
});
|
||||
|
||||
const handler = useCallback(() => {
|
||||
queryClient.invalidateQueries({ queryKey: ["profile"] });
|
||||
}, [queryClient]);
|
||||
|
||||
useEffect(() => {
|
||||
eventBus.on("assign_project_one", handler);
|
||||
return () => eventBus.off("assign_project_one", handler);
|
||||
}, [handler]);
|
||||
|
||||
return { profile, loading, error };
|
||||
return {
|
||||
profile,
|
||||
loading: isLoading,
|
||||
error,
|
||||
refetch,
|
||||
};
|
||||
};
|
||||
|
||||
@ -6,194 +6,538 @@ import { useDispatch, useSelector } from "react-redux";
|
||||
import { setProjectId } from "../slices/localVariablesSlice";
|
||||
import EmployeeList from "../components/Directory/EmployeeList";
|
||||
import eventBus from "../services/eventBus";
|
||||
import {Mutation, useMutation, useQuery, useQueryClient} from "@tanstack/react-query";
|
||||
import showToast from "../services/toastService";
|
||||
|
||||
// export const useProjects = () => {
|
||||
// const loggedUser = useSelector((store) => store.globalVariables.loginUser);
|
||||
// const [projects, setProjects] = useState([]);
|
||||
// const [loading, setLoading] = useState(true);
|
||||
// const [error, setError] = useState("");
|
||||
|
||||
// const fetchData = async () => {
|
||||
// const projectIds = loggedUser?.projects || [];
|
||||
|
||||
// const filterProjects = (projectsList) => {
|
||||
// return projectsList
|
||||
// .filter((proj) => projectIds.includes(String(proj.id)))
|
||||
// .sort((a, b) => a?.name?.localeCompare(b.name));
|
||||
// };
|
||||
|
||||
// const projects_cache = getCachedData("projectslist");
|
||||
|
||||
// if (!projects_cache) {
|
||||
// setLoading(true);
|
||||
// try {
|
||||
// const response = await ProjectRepository.getProjectList();
|
||||
// const allProjects = response.data;
|
||||
// const filtered = filterProjects(allProjects);
|
||||
// setProjects(filtered);
|
||||
// cacheData("projectslist", allProjects);
|
||||
// } catch (err) {
|
||||
// setError("Failed to fetch data.");
|
||||
// } finally {
|
||||
// setLoading(false);
|
||||
// }
|
||||
// } else {
|
||||
// if (!projects.length) {
|
||||
// const filtered = filterProjects(projects_cache);
|
||||
// setProjects(filtered);
|
||||
// setLoading(false);
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// useEffect(() => {
|
||||
// if (loggedUser) {
|
||||
// fetchData();
|
||||
// }
|
||||
// }, [loggedUser]);
|
||||
|
||||
// return { projects, loading, error, refetch: fetchData };
|
||||
// };
|
||||
|
||||
// export const useEmployeesByProjectAllocated = (selectedProject) => {
|
||||
// const [projectEmployees, setEmployeeList] = useState([]);
|
||||
// const [loading, setLoading] = useState(true);
|
||||
// const [projects, setProjects] = useState([]);
|
||||
|
||||
// const fetchData = async (projectid) => {
|
||||
// try {
|
||||
// let EmployeeByProject_Cache = getCachedData("empListByProjectAllocated");
|
||||
// if (
|
||||
// !EmployeeByProject_Cache ||
|
||||
// !EmployeeByProject_Cache.projectId === projectid
|
||||
// ) {
|
||||
// let response = await ProjectRepository.getProjectAllocation(projectid);
|
||||
// setEmployeeList(response.data);
|
||||
// cacheData("empListByProjectAllocated", {
|
||||
// data: response.data,
|
||||
// projectId: projectid,
|
||||
// });
|
||||
// setLoading(false);
|
||||
// } else {
|
||||
// setEmployeeList(EmployeeByProject_Cache.data);
|
||||
// setLoading(false);
|
||||
// }
|
||||
// } catch (err) {
|
||||
// setError("Failed to fetch data.");
|
||||
// setLoading(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
// useEffect(() => {
|
||||
// if (selectedProject) {
|
||||
// fetchData(selectedProject);
|
||||
// }
|
||||
// }, [selectedProject]);
|
||||
|
||||
// return { projectEmployees, loading, projects };
|
||||
// };
|
||||
|
||||
// export const useProjectDetails = (projectId) => {
|
||||
// const { profile } = useProfile();
|
||||
// const [projects_Details, setProject_Details] = useState(null);
|
||||
// const [loading, setLoading] = useState(true);
|
||||
// const [error, setError] = useState("");
|
||||
|
||||
// const fetchData = async () => {
|
||||
// setLoading(true);
|
||||
|
||||
// const project_cache = getCachedData("projectInfo");
|
||||
// if (!project_cache || project_cache?.projectId != projectId) {
|
||||
// ProjectRepository.getProjectByprojectId(projectId)
|
||||
// .then((response) => {
|
||||
// setProject_Details(response.data);
|
||||
// cacheData("projectInfo", {
|
||||
// projectId: projectId,
|
||||
// data: response.data,
|
||||
// });
|
||||
// setLoading(false);
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// console.error(error);
|
||||
// setError("Failed to fetch data.");
|
||||
// setLoading(false);
|
||||
// });
|
||||
// } else {
|
||||
// setProject_Details(project_cache.data);
|
||||
// setLoading(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
// useEffect(() => {
|
||||
// if (profile && projectId != undefined) {
|
||||
// fetchData();
|
||||
// }
|
||||
// }, [projectId, profile]);
|
||||
|
||||
// return { projects_Details, loading, error, refetch: fetchData };
|
||||
// };
|
||||
|
||||
// export const useProjectsByEmployee = (employeeId) => {
|
||||
// const [projectList, setProjectList] = useState([]);
|
||||
// const [loading, setLoading] = useState(false);
|
||||
// const [error, setError] = useState("");
|
||||
|
||||
// const fetchProjects = async (id) => {
|
||||
// try {
|
||||
// setLoading(true);
|
||||
// setError(""); // clear previous error
|
||||
// const res = await ProjectRepository.getProjectsByEmployee(id);
|
||||
// setProjectList(res.data);
|
||||
// cacheData("ProjectsByEmployee", { data: res.data, employeeId: id });
|
||||
// setLoading(false);
|
||||
// } catch (err) {
|
||||
// setError(err?.message || "Failed to fetch projects");
|
||||
// setLoading(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
// useEffect(() => {
|
||||
// if (!employeeId) return;
|
||||
|
||||
// const cache_project = getCachedData("ProjectsByEmployee");
|
||||
|
||||
// if (!cache_project?.data || cache_project?.employeeId !== employeeId) {
|
||||
// fetchProjects(employeeId);
|
||||
// } else {
|
||||
// setProjectList(cache_project.data);
|
||||
// }
|
||||
// }, [employeeId]);
|
||||
|
||||
// return {
|
||||
// projectList,
|
||||
// loading,
|
||||
// error,
|
||||
// refetch: fetchProjects,
|
||||
// };
|
||||
// };
|
||||
|
||||
// export const useProjectName = () => {
|
||||
// const [loading, setLoading] = useState(true);
|
||||
// const [projectNames, setProjectName] = useState([]);
|
||||
// const [Error, setError] = useState();
|
||||
// const dispatch = useDispatch();
|
||||
|
||||
// const fetchData = async () => {
|
||||
// try {
|
||||
// let response = await ProjectRepository.projectNameList();
|
||||
// setProjectName(response.data);
|
||||
// cacheData("basicProjectNameList", response.data);
|
||||
// setLoading(false);
|
||||
// if(response.data.length === 1){
|
||||
// dispatch(setProjectId(response.data[0]?.id));
|
||||
// }
|
||||
// } catch (err) {
|
||||
// setError("Failed to fetch data.");
|
||||
// setLoading(false);
|
||||
// }
|
||||
// };
|
||||
// useEffect(() => {
|
||||
// fetchData();
|
||||
// }, []);
|
||||
|
||||
// return { projectNames, loading, Error, fetchData };
|
||||
// };
|
||||
|
||||
// ------------------------------Query-------------------
|
||||
|
||||
export const useProjects = () => {
|
||||
const loggedUser = useSelector((store) => store.globalVariables.loginUser);
|
||||
const [projects, setProjects] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const fetchData = async () => {
|
||||
const projectIds = loggedUser?.projects || [];
|
||||
|
||||
const filterProjects = (projectsList) => {
|
||||
return projectsList
|
||||
.filter((proj) => projectIds.includes(String(proj.id)))
|
||||
.sort((a, b) => a?.name?.localeCompare(b.name));
|
||||
};
|
||||
|
||||
const projects_cache = getCachedData("projectslist");
|
||||
|
||||
if (!projects_cache) {
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await ProjectRepository.getProjectList();
|
||||
const allProjects = response.data;
|
||||
const filtered = filterProjects(allProjects);
|
||||
setProjects(filtered);
|
||||
cacheData("projectslist", allProjects);
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data.");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
} else {
|
||||
if (!projects.length) {
|
||||
const filtered = filterProjects(projects_cache);
|
||||
setProjects(filtered);
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if (loggedUser) {
|
||||
fetchData();
|
||||
}
|
||||
}, [loggedUser]);
|
||||
|
||||
return { projects, loading, error, refetch: fetchData };
|
||||
};
|
||||
|
||||
export const useEmployeesByProjectAllocated = (selectedProject) => {
|
||||
const [projectEmployees, setEmployeeList] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [projects, setProjects] = useState([]);
|
||||
|
||||
const fetchData = async (projectid) => {
|
||||
try {
|
||||
let EmployeeByProject_Cache = getCachedData("empListByProjectAllocated");
|
||||
if (
|
||||
!EmployeeByProject_Cache ||
|
||||
!EmployeeByProject_Cache.projectId === projectid
|
||||
) {
|
||||
let response = await ProjectRepository.getProjectAllocation(projectid);
|
||||
setEmployeeList(response.data);
|
||||
cacheData("empListByProjectAllocated", {
|
||||
data: response.data,
|
||||
projectId: projectid,
|
||||
});
|
||||
setLoading(false);
|
||||
} else {
|
||||
setEmployeeList(EmployeeByProject_Cache.data);
|
||||
setLoading(false);
|
||||
}
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data.");
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedProject) {
|
||||
fetchData(selectedProject);
|
||||
}
|
||||
}, [selectedProject]);
|
||||
|
||||
return { projectEmployees, loading, projects };
|
||||
};
|
||||
|
||||
export const useProjectDetails = (projectId) => {
|
||||
const { profile } = useProfile();
|
||||
const [projects_Details, setProject_Details] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
|
||||
const project_cache = getCachedData("projectInfo");
|
||||
if (!project_cache || project_cache?.projectId != projectId) {
|
||||
ProjectRepository.getProjectByprojectId(projectId)
|
||||
.then((response) => {
|
||||
setProject_Details(response.data);
|
||||
cacheData("projectInfo", {
|
||||
projectId: projectId,
|
||||
data: response.data,
|
||||
});
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
setError("Failed to fetch data.");
|
||||
setLoading(false);
|
||||
});
|
||||
} else {
|
||||
setProject_Details(project_cache.data);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (profile && projectId != undefined) {
|
||||
fetchData();
|
||||
}
|
||||
}, [projectId, profile]);
|
||||
|
||||
return { projects_Details, loading, error, refetch: fetchData };
|
||||
};
|
||||
|
||||
export const useProjectsByEmployee = (employeeId) => {
|
||||
const [projectList, setProjectList] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const fetchProjects = async (id) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(""); // clear previous error
|
||||
const res = await ProjectRepository.getProjectsByEmployee(id);
|
||||
setProjectList(res.data);
|
||||
cacheData("ProjectsByEmployee", { data: res.data, employeeId: id });
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
setError(err?.message || "Failed to fetch projects");
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!employeeId) return;
|
||||
|
||||
const cache_project = getCachedData("ProjectsByEmployee");
|
||||
|
||||
if (!cache_project?.data || cache_project?.employeeId !== employeeId) {
|
||||
fetchProjects(employeeId);
|
||||
} else {
|
||||
setProjectList(cache_project.data);
|
||||
}
|
||||
}, [employeeId]);
|
||||
const {
|
||||
data: projects = [],
|
||||
isLoading: loading,
|
||||
error,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
queryKey: ['ProjectsList'],
|
||||
queryFn: async () => {
|
||||
const response = await ProjectRepository.getProjectList();
|
||||
return response.data;
|
||||
},
|
||||
enabled: !!loggedUser,
|
||||
});
|
||||
|
||||
return {
|
||||
projectList,
|
||||
projects,
|
||||
loading,
|
||||
error,
|
||||
refetch: fetchProjects,
|
||||
refetch,
|
||||
};
|
||||
};
|
||||
|
||||
export const useProjectName = () => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [projectNames, setProjectName] = useState([]);
|
||||
const [Error, setError] = useState();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
let response = await ProjectRepository.projectNameList();
|
||||
setProjectName(response.data);
|
||||
cacheData("basicProjectNameList", response.data);
|
||||
setLoading(false);
|
||||
if(response.data.length === 1){
|
||||
dispatch(setProjectId(response.data[0]?.id));
|
||||
}
|
||||
} catch (err) {
|
||||
setError("Failed to fetch data.");
|
||||
setLoading(false);
|
||||
export const useEmployeesByProjectAllocated = (selectedProject) =>
|
||||
{
|
||||
const {data = [], isLoading, refetch, error} = useQuery( {
|
||||
queryKey: ["empListByProjectAllocated", selectedProject ],
|
||||
queryFn: async () =>
|
||||
{
|
||||
const res = await ProjectRepository.getProjectAllocation( selectedProject );
|
||||
return res.data || res
|
||||
},
|
||||
enabled: !!selectedProject,
|
||||
onError: ( error ) =>
|
||||
{
|
||||
showToast(error.message || "Error while Fetching project Allocated Employees", "error");
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
} )
|
||||
|
||||
return {
|
||||
projectEmployees: data,
|
||||
loading:isLoading,
|
||||
error,
|
||||
refetch
|
||||
}
|
||||
}
|
||||
|
||||
return { projectNames, loading, Error, fetchData };
|
||||
export const useProjectDetails = ( projectId,isAuto = true ) =>
|
||||
{
|
||||
const {data: projects_Details, isLoading, error, refetch} = useQuery( {
|
||||
queryKey: [ "projectInfo", projectId ],
|
||||
queryFn: async () =>
|
||||
{
|
||||
const res = await ProjectRepository.getProjectByprojectId( projectId );
|
||||
return res.data || res;
|
||||
},
|
||||
enabled: !!projectId && isAuto,
|
||||
onError: ( error ) =>
|
||||
{
|
||||
showToast(error.message || "Error while Fetching project Details", "error");
|
||||
}
|
||||
} )
|
||||
return { projects_Details, loading:isLoading, error, refetch };
|
||||
}
|
||||
|
||||
export const useProjectsByEmployee = (employeeId) =>
|
||||
{
|
||||
const { data:projectNameList =[],isLoading,error,refetch} = useQuery( {
|
||||
queryKey: [ "ProjectsByEmployee", employeeId ],
|
||||
queryFn: async () =>
|
||||
{
|
||||
const res = await ProjectRepository.getProjectsByEmployee( employeeId );
|
||||
return res.data || res;
|
||||
},
|
||||
enabled: !!employeeId,
|
||||
onError: ( error ) =>
|
||||
{
|
||||
showToast(error.message || "Error while Fetching project Employee", "error");
|
||||
}
|
||||
})
|
||||
return {projectList, loading:isLoading,error,refetch }
|
||||
}
|
||||
|
||||
export const useProjectName = () =>
|
||||
{
|
||||
const {data = [],isLoading,error,refetch} = useQuery( {
|
||||
queryKey: [ "basicProjectNameList" ],
|
||||
queryFn: async () =>
|
||||
{
|
||||
const res = await ProjectRepository.projectNameList();
|
||||
return res.data || res;
|
||||
},
|
||||
onError: ( error ) =>
|
||||
{
|
||||
showToast(error.message || "Error while Fetching project Name", "error");
|
||||
}
|
||||
} )
|
||||
return {projectNames:data,loading:isLoading,Error:error,refetch}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const useProjectInfra = (projectId) => {
|
||||
const {
|
||||
data: projectInfra,
|
||||
isLoading,
|
||||
error,
|
||||
} = useQuery({
|
||||
queryKey: ["ProjectInfra", projectId],
|
||||
queryFn: async () => {
|
||||
const res = await ProjectRepository.getProjectInfraByproject(projectId);
|
||||
return res.data;
|
||||
},
|
||||
enabled: !!projectId ,
|
||||
onError: (error) => {
|
||||
showToast(error.message || "Error while fetching project infra", "error");
|
||||
},
|
||||
});
|
||||
|
||||
return { projectInfra, isLoading, error };
|
||||
};
|
||||
|
||||
|
||||
export const useProjectTasks = (workAreaId,IsExpandedArea=false) =>
|
||||
{
|
||||
const { data:ProjectTaskList,isLoading,error } = useQuery( {
|
||||
queryKey: [ "WorkItems",workAreaId ],
|
||||
queryFn: async () =>
|
||||
{
|
||||
const res = await ProjectRepository.getProjectTasksByWorkArea(workAreaId);
|
||||
return res.data;
|
||||
},
|
||||
enabled: !!workAreaId && !!IsExpandedArea,
|
||||
onError: ( error ) =>
|
||||
{
|
||||
showToast(error.message || "Error while Fetching project Tasks", "error");
|
||||
}
|
||||
} )
|
||||
return {ProjectTaskList,isLoading,error}
|
||||
}
|
||||
|
||||
|
||||
// -- -------------Mutation-------------------------------
|
||||
|
||||
|
||||
|
||||
export const useCreateProject = ({ onSuccessCallback }) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (newProject) => {
|
||||
const res = await ProjectRepository.manageProject(newProject);
|
||||
return res.data;
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
// Invalidate the cache
|
||||
queryClient.invalidateQueries( {queryKey: [ 'ProjectsList' ]} );
|
||||
queryClient.invalidateQueries({queryKey:['basicProjectNameList']});
|
||||
|
||||
// Emit event for consumers (like useProjects or others)
|
||||
eventBus.emit("project", {
|
||||
keyword: "Create_Project",
|
||||
response: data,
|
||||
});
|
||||
|
||||
showToast("Project Created successfully.", "success");
|
||||
|
||||
if (onSuccessCallback) {
|
||||
onSuccessCallback(data);
|
||||
}
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(error.message || "Error while creating project", "error");
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export const useUpdateProject = ({ onSuccessCallback }) => {
|
||||
const queryClient = useQueryClient();
|
||||
const {
|
||||
mutate,
|
||||
isPending,
|
||||
isSuccess,
|
||||
isError,
|
||||
} = useMutation({
|
||||
mutationFn: async ( {projectId, updatedData} ) =>
|
||||
{
|
||||
return await ProjectRepository.updateProject(projectId, updatedData);
|
||||
},
|
||||
|
||||
onSuccess: ( data, variables ) =>
|
||||
{
|
||||
const { projectId } = variables;
|
||||
|
||||
queryClient.invalidateQueries({queryKey:["ProjectsList"]});
|
||||
queryClient.invalidateQueries( {queryKey: [ "projectInfo", projectId ]} );
|
||||
queryClient.invalidateQueries({queryKey:['basicProjectNameList']});
|
||||
|
||||
eventBus.emit("project", {
|
||||
keyword: "Update_Project",
|
||||
response: data,
|
||||
});
|
||||
|
||||
showToast("Project updated successfully.", "success");
|
||||
|
||||
if (onSuccessCallback) {
|
||||
onSuccessCallback(data);
|
||||
}
|
||||
},
|
||||
|
||||
onError: ( error ) =>
|
||||
{
|
||||
console.log(error)
|
||||
showToast(error?.message || "Error while updating project", "error");
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
mutate,
|
||||
isPending,
|
||||
isSuccess,
|
||||
isError,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
export const useManageProjectInfra = ( {onSuccessCallback} ) =>
|
||||
{
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ( {infraObject, projectId} ) =>
|
||||
{
|
||||
return await ProjectRepository.manageProjectInfra(infraObject);
|
||||
},
|
||||
onSuccess: ( data, variables ) =>
|
||||
{
|
||||
const { projectId } = variables;
|
||||
queryClient.invalidateQueries({queryKey:["ProjectInfra", projectId]});
|
||||
if (onSuccessCallback) onSuccessCallback(data,variables);
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(error.message || "Failed to update Project Infra", "error");
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export const useManageProjectAllocation = ({
|
||||
onSuccessCallback,
|
||||
onErrorCallback,
|
||||
}) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const {
|
||||
mutate,
|
||||
isPending,
|
||||
isSuccess,
|
||||
isError,
|
||||
} = useMutation({
|
||||
mutationFn: async ( {items} ) =>
|
||||
{
|
||||
const response = await ProjectRepository.manageProjectAllocation(items);
|
||||
return response.data;
|
||||
},
|
||||
onSuccess: (data, variables, context) => {
|
||||
queryClient.invalidateQueries({queryKey:['empListByProjectAllocated']});
|
||||
queryClient.removeQueries({queryKey:["projectEmployees"]})
|
||||
if (variables?.added) {
|
||||
showToast('Employee Assigned Successfully', 'success');
|
||||
} else {
|
||||
showToast('Removed Employee Successfully', 'success');
|
||||
}
|
||||
|
||||
if (onSuccessCallback) onSuccessCallback(data, context);
|
||||
},
|
||||
onError: (error) => {
|
||||
const message =
|
||||
error?.response?.data?.message || error.message || 'Error occurred during API call';
|
||||
showToast(message, 'error');
|
||||
if (onErrorCallback) onErrorCallback(error);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
mutate,
|
||||
isPending,
|
||||
isSuccess,
|
||||
isError,
|
||||
};
|
||||
};
|
||||
|
||||
export const useManageTask = ({onSuccessCallback}) =>
|
||||
{
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation( {
|
||||
mutationFn: async ( payload ) => await ProjectRepository.manageProjectTasks( payload ),
|
||||
onSuccess: ( data, variables ) =>
|
||||
{
|
||||
queryClient.invalidateQueries({ queryKey: ["WorkItems"] })
|
||||
if (onSuccessCallback) onSuccessCallback(data);
|
||||
},
|
||||
onError: (error) =>
|
||||
{
|
||||
const message =
|
||||
error?.response?.data?.message || error.message || 'Error occurred during API call';
|
||||
showToast(message, 'error');
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
export const useDeleteProjectTask = (onSuccessCallback) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async ( {workItemId, workAreaId} ) =>
|
||||
{
|
||||
return await ProjectRepository.deleteProjectTask(workItemId);
|
||||
},
|
||||
onSuccess: ( _, variables ) =>
|
||||
{
|
||||
showToast("Task deleted successfully", "success");
|
||||
queryClient.invalidateQueries({queryKey:[ "WorkItems",variables.workAreaId]});
|
||||
if (onSuccessCallback) onSuccessCallback();
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(
|
||||
error?.response?.data?.message || error.message || "Failed to delete task",
|
||||
"error"
|
||||
);
|
||||
if (onSuccessCallback) onSuccessCallback();
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -2,103 +2,192 @@ import { useEffect, useState } from "react";
|
||||
import { TasksRepository } from "../repositories/TaskRepository";
|
||||
import { cacheData, getCachedData } from "../slices/apiDataManager";
|
||||
import {MasterRespository} from "../repositories/MastersRepository";
|
||||
// import {formatDate} from "../utils/dateUtils";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import showToast from "../services/toastService";
|
||||
import {useSelector} from "react-redux";
|
||||
|
||||
|
||||
// ---------Query---------------------------------
|
||||
|
||||
export const useTaskList = (projectId, dateFrom, toDate) => {
|
||||
const [TaskList, setTaskList] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const enabled = !!projectId && !!dateFrom && !!toDate;
|
||||
|
||||
const fetchList = async (projectId, dateFrom, toDate) => {
|
||||
const taskList_cached = getCachedData("taskList");
|
||||
// if (!taskList_cached || taskList_cached?.projectId !== projectId) {
|
||||
try {
|
||||
setLoading(true);
|
||||
const resp = await TasksRepository.getTaskList(
|
||||
projectId,
|
||||
dateFrom,
|
||||
toDate
|
||||
);
|
||||
setTaskList(resp.data);
|
||||
cacheData("taskList", { projectId: projectId, data: resp.data });
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
setLoading(false);
|
||||
setError(err);
|
||||
}
|
||||
// } else {
|
||||
// setTaskList(taskList_cached.data);
|
||||
// }
|
||||
};
|
||||
useEffect( () =>
|
||||
{
|
||||
const {
|
||||
data: TaskList = [],
|
||||
isLoading: loading,
|
||||
error,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
queryKey: ["taskList", projectId, dateFrom, toDate],
|
||||
queryFn: async () => {
|
||||
const response = await TasksRepository.getTaskList(
|
||||
projectId,
|
||||
dateFrom,
|
||||
toDate
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
enabled,
|
||||
|
||||
});
|
||||
|
||||
if (projectId && dateFrom && toDate) {
|
||||
fetchList(projectId, dateFrom, toDate);
|
||||
}
|
||||
|
||||
}, [projectId, dateFrom, toDate]);
|
||||
return { TaskList, loading, error, refetch };
|
||||
};
|
||||
|
||||
return { TaskList, loading, error, refetch:fetchList};
|
||||
export const useTaskById = (TaskId) => {
|
||||
const {
|
||||
data: Task = null,
|
||||
isLoading: loading,
|
||||
error,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
queryKey: ["taskDetails", TaskId],
|
||||
queryFn: async () => {
|
||||
const res = await TasksRepository.getTaskById(TaskId);
|
||||
return res.data;
|
||||
},
|
||||
enabled: !!TaskId,
|
||||
});
|
||||
|
||||
return { Task, loading, error, refetch };
|
||||
};
|
||||
|
||||
// export const useActivities = () => {
|
||||
// return useQuery({
|
||||
// queryKey: ["activitiesMaster"],
|
||||
// queryFn: async () => {
|
||||
// const response = await ActivityRepository.getActivities();
|
||||
// return response.data;
|
||||
// },
|
||||
|
||||
// });
|
||||
// };
|
||||
|
||||
export const useAuditStatus = () => {
|
||||
const {
|
||||
data: status = [],
|
||||
isLoading: loading,
|
||||
error,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
queryKey: ["AuditStatus"],
|
||||
queryFn: async () => {
|
||||
const res = await MasterRespository.getAuditStatus();
|
||||
return res.data;
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
return { status, loading, error, refetch };
|
||||
};
|
||||
|
||||
|
||||
export const useTaskById = (TaskId) =>
|
||||
// -----------------------Mutation------------------------
|
||||
const toDate = new Date().toISOString().split('T')[0];
|
||||
const dateFrom = new Date(Date.now() - 6 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
|
||||
|
||||
|
||||
|
||||
export const useReportTask = ( {onSuccessCallback, onErrorCallback} = {} ) =>
|
||||
{
|
||||
const [Task, setTask] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [ error, setError ] = useState( null );
|
||||
|
||||
|
||||
|
||||
const fetchTask = async(TaskId) =>
|
||||
{
|
||||
try
|
||||
const queryClient = useQueryClient();
|
||||
const {
|
||||
mutate,
|
||||
isPending,
|
||||
isSuccess,
|
||||
isError,
|
||||
error,
|
||||
} = useMutation({
|
||||
mutationFn: async ( {reportData,workAreaId} ) =>
|
||||
{
|
||||
let res = await TasksRepository.getTaskById( TaskId );
|
||||
setTask( res.data );
|
||||
|
||||
} catch ( error )
|
||||
debugger
|
||||
return await TasksRepository.reportTask(reportData);
|
||||
},
|
||||
onSuccess: ( data, variables ) =>
|
||||
{
|
||||
setError(err)
|
||||
}
|
||||
}
|
||||
useEffect( () =>
|
||||
{
|
||||
if ( TaskId )
|
||||
{
|
||||
fetchTask(TaskId)
|
||||
}
|
||||
}, [ TaskId ] )
|
||||
return { Task,loading}
|
||||
}
|
||||
const {workAreaId} = variables;
|
||||
queryClient.invalidateQueries( {queryKey: [ "taskList" ]} );
|
||||
queryClient.invalidateQueries( {queryKey: [ "WorkItems", workAreaId ]} );
|
||||
queryClient.invalidateQueries( {queryKey: [ 'ProjectsList' ]} );
|
||||
showToast( "Task Reported Successfully.", "success" );
|
||||
if (onSuccessCallback) onSuccessCallback(data);
|
||||
},
|
||||
onError: (error) => {
|
||||
const msg =
|
||||
error?.response?.data?.message || error.message || "Error occurred during API call";
|
||||
showToast( msg, "error" );
|
||||
},
|
||||
});
|
||||
|
||||
export const useAuditStatus = () =>
|
||||
return {
|
||||
mutate,
|
||||
isPending,
|
||||
isSuccess,
|
||||
isError,
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export const useSubmitTaskComment = ({ actionAllow, onSuccessCallback }) => {
|
||||
const queryClient = useQueryClient();
|
||||
const { mutate, isPending } = useMutation({
|
||||
mutationFn: async ({ data, commentsData }) => {
|
||||
const payload = {
|
||||
...data,
|
||||
[actionAllow ? "id" : "taskAllocationId"]: commentsData?.id,
|
||||
...(actionAllow ? {} : { commentDate: new Date().toISOString() }),
|
||||
};
|
||||
|
||||
const response = actionAllow
|
||||
? await TasksRepository.auditTask(payload)
|
||||
: await TasksRepository.taskComments(payload);
|
||||
|
||||
return response.data;
|
||||
},
|
||||
|
||||
onSuccess: ( data,variables ) =>
|
||||
{
|
||||
|
||||
const workAreaId = variables?.commentsData?.workItem?.workArea?.id;
|
||||
queryClient.invalidateQueries({ queryKey: ["taskList"] });
|
||||
if (actionAllow) {
|
||||
showToast( "Review submitted successfully.", "success" );
|
||||
|
||||
} else
|
||||
{
|
||||
showToast("Comment sent successfully.", "success");
|
||||
}
|
||||
|
||||
onSuccessCallback?.(data);
|
||||
},
|
||||
|
||||
onError: (error) => {
|
||||
const msg = error?.response?.data?.message || error.message || "Error during API call";
|
||||
showToast(msg, "error");
|
||||
},
|
||||
});
|
||||
|
||||
return { submitComment: mutate, isPending };
|
||||
};
|
||||
|
||||
export const useCreateTask = ( {onSuccessCallback, onErrorCallback} = {} ) =>
|
||||
{
|
||||
const [ status, setStatus ] = useState( [] );
|
||||
const [ error, setError ] = useState( '' );
|
||||
const [ loading, setLoading ] = useState( false )
|
||||
|
||||
const fetchStatus = async() =>
|
||||
{
|
||||
try
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async ({payload,workAreaId}) => {
|
||||
return await TasksRepository.assignTask(payload);
|
||||
},
|
||||
onSuccess: ( _, variables ) =>
|
||||
{
|
||||
const res = await MasterRespository.getAuditStatus()
|
||||
setStatus( res.data )
|
||||
cacheData("AuditStatus",res.data)
|
||||
} catch ( err )
|
||||
queryClient.invalidateQueries( {queryKey: [ "taskList" ]} );
|
||||
queryClient.invalidateQueries( {queryKey: [ "WorkItems", variables?.workAreaId ]} );
|
||||
showToast( "Task Assigned Successfully.", "success" );
|
||||
if (onSuccessCallback) onSuccessCallback(variables);
|
||||
},
|
||||
onError: ( error ) =>
|
||||
{
|
||||
setError(err)
|
||||
}
|
||||
}
|
||||
useEffect(() => {
|
||||
const cache_status = getCachedData('AuditStatus');
|
||||
if (cache_status) {
|
||||
setStatus(cache_status);
|
||||
} else {
|
||||
fetchStatus();
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {status,error,loading}
|
||||
}
|
||||
showToast("Something went wrong. Please try again.", "error");
|
||||
if (onErrorCallback) onErrorCallback(error);
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -1,5 +1,17 @@
|
||||
import React from "react";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import {Outlet} from "react-router-dom";
|
||||
import {QueryClient} from '@tanstack/react-query';
|
||||
|
||||
export const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: 5 * 60 * 1000, // 5 min: data considered fresh
|
||||
refetchOnWindowFocus: true, // refresh on tab switch
|
||||
refetchOnReconnect: true, // re-fetch if network was lost
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const AuthLayout = () => {
|
||||
return (
|
||||
|
||||
@ -9,10 +9,11 @@ import ReportTaskComments from "../../components/Activities/ReportTaskComments";
|
||||
import DateRangePicker from "../../components/common/DateRangePicker";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
import moment from "moment";
|
||||
import FilterIcon from "../../components/common/FilterIcon"; // Import the FilterIcon component
|
||||
import FilterIcon from "../../components/common/FilterIcon";
|
||||
import GlobalModel from "../../components/common/GlobalModel";
|
||||
import AssignTask from "../../components/Project/AssignTask";
|
||||
import SubTask from "../../components/Activities/SubTask";
|
||||
import {formatNumber} from "../../utils/dateUtils";
|
||||
|
||||
const DailyTask = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
@ -20,14 +21,7 @@ const DailyTask = () => {
|
||||
const selectedProject = useSelector(
|
||||
(store) => store.localVariables.projectId
|
||||
);
|
||||
const {
|
||||
projects,
|
||||
loading: project_loading,
|
||||
error: projects_Error,
|
||||
} = useProjects();
|
||||
|
||||
const [initialized, setInitialized] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [filters, setFilters] = useState({
|
||||
selectedBuilding: "",
|
||||
@ -35,23 +29,7 @@ const DailyTask = () => {
|
||||
selectedActivities: [],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!project_loading && projects.length > 0 && !initialized) {
|
||||
if (projectIdFromUrl) {
|
||||
dispatch(setProjectId(projectIdFromUrl));
|
||||
} else if (selectedProject === 1 || selectedProject === undefined) {
|
||||
dispatch(setProjectId(projects[0].id));
|
||||
}
|
||||
setInitialized(true);
|
||||
}
|
||||
}, [
|
||||
project_loading,
|
||||
projects,
|
||||
projectIdFromUrl,
|
||||
selectedProject,
|
||||
initialized,
|
||||
dispatch,
|
||||
]);
|
||||
|
||||
|
||||
const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" });
|
||||
|
||||
@ -61,10 +39,11 @@ const DailyTask = () => {
|
||||
error: task_error,
|
||||
refetch,
|
||||
} = useTaskList(
|
||||
initialized ? selectedProject : null,
|
||||
initialized ? dateRange.startDate : null,
|
||||
initialized ? dateRange.endDate : null
|
||||
);
|
||||
selectedProject || null,
|
||||
dateRange?.startDate || null,
|
||||
dateRange?.endDate || null
|
||||
);
|
||||
|
||||
|
||||
const [TaskLists, setTaskLists] = useState([]);
|
||||
const [dates, setDates] = useState([]);
|
||||
@ -83,8 +62,8 @@ const DailyTask = () => {
|
||||
}
|
||||
|
||||
if (filters.selectedFloors.length > 0) {
|
||||
filteredTasks = filteredTasks.filter((task) =>
|
||||
filters.selectedFloors.includes(
|
||||
filteredTasks = filteredTasks?.filter((task) =>
|
||||
filters.selectedFloors?.includes(
|
||||
task?.workItem?.workArea?.floor?.floorName
|
||||
)
|
||||
);
|
||||
@ -103,9 +82,9 @@ const DailyTask = () => {
|
||||
}
|
||||
}, [
|
||||
TaskList,
|
||||
filters.selectedBuilding,
|
||||
filters.selectedFloors,
|
||||
filters.selectedActivities,
|
||||
filters?.selectedBuilding,
|
||||
filters?.selectedFloors,
|
||||
filters?.selectedActivities,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -150,43 +129,32 @@ const DailyTask = () => {
|
||||
const handlecloseModal = () =>
|
||||
{
|
||||
setIsModalOpen( false )
|
||||
refetch(selectedProject, dateRange.startDate, dateRange.endDate);
|
||||
// refetch();
|
||||
}
|
||||
const handleProjectChange = (e) => {
|
||||
const newProjectId = e.target.value;
|
||||
dispatch(setProjectId(newProjectId));
|
||||
setTaskLists([]);
|
||||
setFilters({
|
||||
selectedBuilding: "",
|
||||
selectedFloors: [],
|
||||
selectedActivities: [],
|
||||
});
|
||||
};
|
||||
|
||||
const handleCloseAction = (IsSubTask) => {
|
||||
if (IsSubTask) {
|
||||
setIsSubTaskNeeded(true);
|
||||
setIsModalOpenComment(false);
|
||||
} else {
|
||||
refetch(selectedProject, dateRange.startDate, dateRange.endDate);
|
||||
// refetch();
|
||||
setIsModalOpenComment(false);
|
||||
}
|
||||
};
|
||||
const hanleCloseSubTask = () => {
|
||||
setIsSubTaskNeeded(false);
|
||||
setComment( null );
|
||||
refetch(selectedProject, dateRange.startDate, dateRange.endDate);
|
||||
// refetch();
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
|
||||
{isModalOpen && <GlobalModel isOpen={isModalOpen} size="md" closeModal={handlecloseModal} >
|
||||
<ReportTask
|
||||
report={selectedTask}
|
||||
closeModal={handlecloseModal}
|
||||
refetch={refetch}
|
||||
// refetch={refetch}
|
||||
/>
|
||||
</GlobalModel>}
|
||||
|
||||
@ -255,7 +223,7 @@ const DailyTask = () => {
|
||||
</thead>
|
||||
<tbody className="table-border-bottom-0">
|
||||
{/* --- Spinner when tasks are loading --- */}
|
||||
{(task_loading || project_loading) && (
|
||||
{task_loading && (
|
||||
<tr>
|
||||
<td colSpan={6} className="text-center">
|
||||
{" "}
|
||||
@ -272,7 +240,6 @@ const DailyTask = () => {
|
||||
</tr>
|
||||
)}
|
||||
{!task_loading &&
|
||||
!project_loading &&
|
||||
TaskLists.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={6} className="text-center">
|
||||
@ -337,9 +304,7 @@ const DailyTask = () => {
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{task.plannedTask || "NA"} /
|
||||
{task.workItem.plannedWork -
|
||||
task.workItem.completedWork}
|
||||
{formatNumber(task.plannedTask)} / {formatNumber(task.workItem.plannedWork - task.workItem.completedWork)}
|
||||
</td>
|
||||
<td>{task.completedTask}</td>
|
||||
<td>
|
||||
@ -413,7 +378,7 @@ const DailyTask = () => {
|
||||
} more`}
|
||||
>
|
||||
<span className="avatar-initial rounded-circle bg-label-secondary pull-up">
|
||||
+{task.teamMembers.length - 3}
|
||||
+ {task.teamMembers.length - 3}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -1,110 +1,9 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import "../../components/Project/ProjectInfra.css";
|
||||
|
||||
import ProjectRepository from "../../repositories/ProjectRepository";
|
||||
import React from "react";
|
||||
import Breadcrumb from "../../components/common/Breadcrumb";
|
||||
import InfraPlanning from "../../components/Activities/InfraPlanning";
|
||||
import { cacheData, getCachedData } from "../../slices/apiDataManager";
|
||||
import { useProfile } from "../../hooks/useProfile";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useProjectDetails, useProjects } from "../../hooks/useProjects";
|
||||
import { setProjectId } from "../../slices/localVariablesSlice";
|
||||
import showToast from "../../services/toastService";
|
||||
|
||||
|
||||
const TaskPlannng = () => {
|
||||
const { profile } = useProfile();
|
||||
const {
|
||||
projects,
|
||||
loading: project_listLoader,
|
||||
error: projects_error,
|
||||
} = useProjects();
|
||||
const dispatch = useDispatch();
|
||||
const selectedProject = useSelector(
|
||||
(store) => store.localVariables.projectId
|
||||
);
|
||||
|
||||
|
||||
const [project, setProject] = useState(null);
|
||||
const [projectDetails, setProjectDetails] = useState(null);
|
||||
const [activities, setActivities] = useState(null);
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const fetchActivities = async () => {
|
||||
try {
|
||||
const activities_cache = getCachedData("activitiesMaster");
|
||||
|
||||
if (!activities_cache) {
|
||||
ActivityeRepository.getActivities()
|
||||
.then((response) => {
|
||||
setActivities(response.data);
|
||||
cacheData("activitiesMaster", response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
setError("Failed to fetch data.");
|
||||
});
|
||||
} else {
|
||||
setActivities(activities_cache);
|
||||
}
|
||||
} catch (err) {
|
||||
setError("Failed to fetch activities.");
|
||||
} finally {
|
||||
// setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const project_cache = getCachedData("projectInfo");
|
||||
if (!project_cache || !project_cache.projectId == selectedProject) {
|
||||
ProjectRepository.getProjectByprojectId(selectedProject)
|
||||
.then((response) => {
|
||||
setProjectDetails(response);
|
||||
setProject(response);
|
||||
cacheData("projectInfo", {
|
||||
data: response.data,
|
||||
projectId: selectedProject,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
const message =
|
||||
error.response?.data?.message ||
|
||||
error.message ||
|
||||
"An unexpected error occurred";
|
||||
showToast( message, "error" );
|
||||
});
|
||||
} else {
|
||||
setProjectDetails(project_cache);
|
||||
}
|
||||
} catch (err) {
|
||||
setError( "Failed to fetch data." );
|
||||
const message =
|
||||
error.response?.data?.message ||
|
||||
error.message ||
|
||||
"An unexpected error occurred";
|
||||
showToast( message, "error" );
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const [activePill, setActivePill] = useState("profile");
|
||||
|
||||
const handlePillClick = (pillKey) => {
|
||||
setActivePill(pillKey);
|
||||
};
|
||||
|
||||
const handleDataChange = (data) => {
|
||||
fetchData();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (projects.length != 0 && selectedProject) {
|
||||
fetchData();
|
||||
fetchActivities();
|
||||
}
|
||||
}, [selectedProject]);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -115,17 +14,7 @@ const TaskPlannng = () => {
|
||||
{ label: "Daily Task Planning" }
|
||||
]}
|
||||
></Breadcrumb>
|
||||
{project_listLoader && <p>Loading..</p>}
|
||||
{!project_listLoader && projects.length === 0 && (
|
||||
<p>No Project Found.</p>
|
||||
)}
|
||||
{!project_listLoader && projects.length > 0 && (
|
||||
<InfraPlanning
|
||||
data={projectDetails}
|
||||
activityMaster={activities}
|
||||
onDataChange={handleDataChange}
|
||||
/>
|
||||
)}
|
||||
<InfraPlanning/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -5,10 +5,10 @@ import { Link, NavLink, useNavigate } from "react-router-dom";
|
||||
import Avatar from "../../components/common/Avatar";
|
||||
import Breadcrumb from "../../components/common/Breadcrumb";
|
||||
import ManageEmp from "../../components/Employee/ManageRole";
|
||||
import { useEmployeesAllOrByProjectId } from "../../hooks/useEmployees";
|
||||
import { useProjects } from "../../hooks/useProjects"; // Keep if you use projects elsewhere
|
||||
import { useProfile } from "../../hooks/useProfile"; // Keep if you use profile elsewhere
|
||||
import { hasUserPermission } from "../../utils/authUtils"; // Keep if you use this elsewhere
|
||||
import { useEmployeesAllOrByProjectId, useSuspendEmployee } from "../../hooks/useEmployees";
|
||||
import { useProjects } from "../../hooks/useProjects";
|
||||
import { useProfile } from "../../hooks/useProfile";
|
||||
import { hasUserPermission } from "../../utils/authUtils";
|
||||
import { ITEMS_PER_PAGE, MANAGE_EMPLOYEES } from "../../utils/constants";
|
||||
import { clearCacheKey } from "../../slices/apiDataManager";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
@ -26,6 +26,7 @@ import { useSelector } from "react-redux";
|
||||
import eventBus from "../../services/eventBus";
|
||||
import { newlineChars } from "pdf-lib";
|
||||
import GlobalModel from "../../components/common/GlobalModel";
|
||||
import usePagination from "../../hooks/usePagination";
|
||||
|
||||
const EmployeeList = () => {
|
||||
const selectedProjectId = useSelector(
|
||||
@ -38,14 +39,15 @@ const EmployeeList = () => {
|
||||
|
||||
const { employees, loading, setLoading, error, recallEmployeeData } =
|
||||
useEmployeesAllOrByProjectId(
|
||||
showAllEmployees ? null : selectedProjectId, // Use selectedProjectId here
|
||||
showAllEmployees ? null : selectedProjectId,
|
||||
showInactive
|
||||
);
|
||||
|
||||
const [employeeList, setEmployeeList] = useState([]);
|
||||
const [modelConfig, setModelConfig] = useState();
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [itemsPerPage] = useState(ITEMS_PER_PAGE);
|
||||
const [ modelConfig, setModelConfig ] = useState();
|
||||
const [EmpForManageRole,setEmpForManageRole] = useState(null)
|
||||
// const [currentPage, setCurrentPage] = useState(1);
|
||||
// const [itemsPerPage] = useState(ITEMS_PER_PAGE);
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const [filteredData, setFilteredData] = useState([]);
|
||||
@ -53,135 +55,131 @@ const EmployeeList = () => {
|
||||
const [selectedEmployeeId, setSelecedEmployeeId] = useState(null);
|
||||
const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
const [selectedEmpFordelete, setSelectedEmpFordelete] = useState(null);
|
||||
const [employeeLodaing, setemployeeLodaing] = useState(false);
|
||||
const [ employeeLodaing, setemployeeLodaing ] = useState( false );
|
||||
const {
|
||||
mutate: suspendEmployee,
|
||||
isPending: empLodaing
|
||||
} = useSuspendEmployee({
|
||||
setIsDeleteModalOpen,
|
||||
setemployeeLodaing
|
||||
} );
|
||||
|
||||
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
/**
|
||||
* Applies the search filter to a given array of employee data.
|
||||
* @param {Array} data - The array of employee objects to filter.
|
||||
* @param {string} text - The search text.
|
||||
* @returns {Array} The filtered array.
|
||||
*/
|
||||
|
||||
const applySearchFilter = (data, text) => {
|
||||
if (!text) {
|
||||
return data;
|
||||
}
|
||||
const lowercasedText = text.toLowerCase().trim(); // Ensure search text is trimmed and lowercase
|
||||
if (!text) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return data.filter((item) => {
|
||||
// **IMPROVED FULL NAME CONSTRUCTION**
|
||||
const firstName = item.firstName || "";
|
||||
const middleName = item.middleName || "";
|
||||
const lastName = item.lastName || "";
|
||||
const lowercasedText = text.toLowerCase().trim();
|
||||
|
||||
// Join parts, then trim any excess spaces if a middle name is missing
|
||||
const fullName = `${firstName} ${middleName} ${lastName}`.toLowerCase().trim().replace(/\s+/g, ' ');
|
||||
return data.filter((item) => {
|
||||
const firstName = item.firstName || "";
|
||||
const middleName = item.middleName || "";
|
||||
const lastName = item.lastName || "";
|
||||
|
||||
const email = item.email ? item.email.toLowerCase() : "";
|
||||
const phoneNumber = item.phoneNumber ? item.phoneNumber.toLowerCase() : "";
|
||||
const jobRole = item.jobRole ? item.jobRole.toLowerCase() : "";
|
||||
const fullName = `${firstName} ${middleName} ${lastName}`
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace(/\s+/g, " ");
|
||||
|
||||
const email = item.email?.toLowerCase() || "";
|
||||
const phoneNumber = item.phoneNumber?.toLowerCase() || "";
|
||||
const jobRole = item.jobRole?.toLowerCase() || "";
|
||||
|
||||
return (
|
||||
fullName.includes(lowercasedText) ||
|
||||
email.includes(lowercasedText) ||
|
||||
phoneNumber.includes(lowercasedText) ||
|
||||
jobRole.includes(lowercasedText)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
fullName.includes(lowercasedText) ||
|
||||
email.includes(lowercasedText) ||
|
||||
phoneNumber.includes(lowercasedText) ||
|
||||
jobRole.includes(lowercasedText)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const handleSearch = (e) => {
|
||||
const value = e.target.value;
|
||||
setSearchText(value);
|
||||
setCurrentPage(1);
|
||||
};
|
||||
useEffect(() => {
|
||||
const filtered = applySearchFilter(employeeList, searchText);
|
||||
setFilteredData(filtered);
|
||||
}, [searchText, employeeList]);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentPage(1);
|
||||
if (!loading && Array.isArray(employees)) {
|
||||
const sorted = [...employees].sort((a, b) => {
|
||||
const nameA = `${a.firstName || ""}${a.middleName || ""}${a.lastName || ""}`.toLowerCase();
|
||||
const nameB = `${b.firstName || ""}${b.middleName || ""}${b.lastName || ""}`.toLowerCase();
|
||||
return nameA?.localeCompare(nameB);
|
||||
});
|
||||
|
||||
setEmployeeList(sorted);
|
||||
const results = applySearchFilter(sorted, searchText);
|
||||
setFilteredData(results);
|
||||
} else if (!loading && !employees) {
|
||||
setEmployeeList([]);
|
||||
setFilteredData([]);
|
||||
}
|
||||
}, [loading, employees, showAllEmployees, searchText, selectedProjectId]); // Add selectedProjectId to dependencies
|
||||
|
||||
const displayData = filteredData;
|
||||
const indexOfLastItem = currentPage * itemsPerPage;
|
||||
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
|
||||
const currentItems = Array.isArray(displayData)
|
||||
? displayData.slice(indexOfFirstItem, indexOfLastItem)
|
||||
: [];
|
||||
|
||||
const paginate = (pageNumber) => setCurrentPage(pageNumber);
|
||||
const totalPages = Array.isArray(displayData)
|
||||
? Math.ceil(displayData.length / itemsPerPage)
|
||||
: 0;
|
||||
|
||||
const openModal = () => {
|
||||
const displayData = searchText ? filteredData : employeeList;
|
||||
const { currentPage, totalPages, currentItems, paginate,setCurrentPage } = usePagination(
|
||||
displayData,
|
||||
ITEMS_PER_PAGE
|
||||
);
|
||||
const openModal = () => {
|
||||
setIsCreateModalOpen(true);
|
||||
};
|
||||
};
|
||||
|
||||
// const closeModal = () => {
|
||||
// setIsCreateModalOpen(false);
|
||||
|
||||
// const modalElement = document.getElementById("managerole-modal");
|
||||
// if (modalElement && !showModal) {
|
||||
// modalElement.classList.remove("show");
|
||||
// modalElement.style.display = "none";
|
||||
// document.body.classList.remove("modal-open");
|
||||
// document.querySelector(".modal-backdrop")?.remove();
|
||||
// }
|
||||
// setShowModal(false);
|
||||
// clearCacheKey("employeeProfile");
|
||||
// recallEmployeeData(showInactive, showAllEmployees ? null : selectedProjectId); // Use selectedProjectId here
|
||||
// };
|
||||
// const handleShow = () => setShowModal(true);
|
||||
// const handleClose = () => setShowModal( false );
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && Array.isArray(employees)) {
|
||||
const sorted = [...employees].sort((a, b) => {
|
||||
const nameA = `${a.firstName || ""}${a.middleName || ""}${a.lastName || ""}`.toLowerCase();
|
||||
const nameB = `${b.firstName || ""}${b.middleName || ""}${b.lastName || ""}`.toLowerCase();
|
||||
return nameA?.localeCompare(nameB);
|
||||
});
|
||||
|
||||
setEmployeeList((prevList) => {
|
||||
const prevJSON = JSON.stringify(prevList);
|
||||
const nextJSON = JSON.stringify(sorted);
|
||||
if (prevJSON !== nextJSON) {
|
||||
return sorted;
|
||||
}
|
||||
return prevList;
|
||||
});
|
||||
|
||||
setFilteredData((prev) => {
|
||||
const prevJSON = JSON.stringify(prev);
|
||||
const nextJSON = JSON.stringify(sorted);
|
||||
if (prevJSON !== nextJSON) {
|
||||
return sorted;
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
|
||||
// set currentPage to 1 only if needed
|
||||
setCurrentPage((prevPage) => (prevPage !== 1 ? 1 : prevPage));
|
||||
}
|
||||
}, [loading, employees, selectedProjectId, showAllEmployees]);
|
||||
|
||||
const closeModal = () => {
|
||||
setIsCreateModalOpen(false);
|
||||
|
||||
const modalElement = document.getElementById("managerole-modal");
|
||||
if (modalElement && !showModal) {
|
||||
modalElement.classList.remove("show");
|
||||
modalElement.style.display = "none";
|
||||
document.body.classList.remove("modal-open");
|
||||
document.querySelector(".modal-backdrop")?.remove(); // Use optional chaining for safety
|
||||
}
|
||||
setShowModal(false);
|
||||
clearCacheKey("employeeProfile");
|
||||
recallEmployeeData(showInactive, showAllEmployees ? null : selectedProjectId); // Use selectedProjectId here
|
||||
};
|
||||
const handleShow = () => setShowModal(true);
|
||||
const handleClose = () => setShowModal(false);
|
||||
|
||||
const suspendEmployee = (id) => {
|
||||
setemployeeLodaing(true);
|
||||
EmployeeRepository.deleteEmployee(id)
|
||||
.then((response) => {
|
||||
showToast("Employee deleted successfully.", "success");
|
||||
clearCacheKey("employeeListByProject");
|
||||
clearCacheKey("allEmployeeList");
|
||||
clearCacheKey("allInactiveEmployeeList");
|
||||
clearCacheKey("employeeProfile");
|
||||
// Recall data based on current filter states after deletion to refresh the table
|
||||
recallEmployeeData(showInactive, showAllEmployees ? null : selectedProjectId); // Use selectedProjectId here
|
||||
setemployeeLodaing(false);
|
||||
setIsDeleteModalOpen(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
const message =
|
||||
error.response?.data?.message ||
|
||||
error.message ||
|
||||
"An unexpected error occurred";
|
||||
showToast(message, "error");
|
||||
setemployeeLodaing(false);
|
||||
setIsDeleteModalOpen(false);
|
||||
});
|
||||
};
|
||||
|
||||
const handleConfigData = (config) => {
|
||||
setModelConfig(config);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (modelConfig !== null) {
|
||||
openModal();
|
||||
}
|
||||
}, [modelConfig, isCreateModalOpen]);
|
||||
// useEffect(() => {
|
||||
// if (modelConfig !== null) {
|
||||
// openModal();
|
||||
// }
|
||||
// }, [modelConfig, isCreateModalOpen]);
|
||||
|
||||
const tableRef = useRef(null);
|
||||
const handleExport = (type) => {
|
||||
@ -215,9 +213,6 @@ const handleAllEmployeesToggle = (e) => {
|
||||
setShowInactive(false);
|
||||
setShowAllEmployees(isChecked);
|
||||
|
||||
if (!isChecked) {
|
||||
setSelectedProject(selectedProjectId || "");
|
||||
}
|
||||
};
|
||||
|
||||
const handleEmployeeModel = (id) => {
|
||||
@ -253,34 +248,18 @@ const handleAllEmployeesToggle = (e) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{isCreateModalOpen && (
|
||||
<ManageEmp employeeId={modelConfig} onClosed={closeModal} />
|
||||
{EmpForManageRole && (
|
||||
<GlobalModel isOpen={EmpForManageRole} closeModal={() => setEmpForManageRole( null )}>
|
||||
<ManageEmp employeeId={EmpForManageRole} onClosed={()=>setEmpForManageRole(null)} />
|
||||
</GlobalModel>
|
||||
)}
|
||||
{/* {showModal && (<div
|
||||
className={`modal fade ${showModal ? "show" : ""} `}
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: showModal ? "block" : "none" }}
|
||||
aria-hidden={!showModal}
|
||||
>
|
||||
<div className="modal-dialog modal-xl modal-dialog-centered ">
|
||||
<div
|
||||
className="modal-content overflow-y-auto overflow-x-hidden"
|
||||
style={{ maxHeight: "90vh" }}
|
||||
>
|
||||
<ManageEmployee
|
||||
employeeId={selectedEmployeeId}
|
||||
onClosed={closeModal}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div> )} */}
|
||||
|
||||
{showModal && (
|
||||
<GlobalModel isOpen={showModal} size="lg" closeModal={()=>setShowModal(false)}>
|
||||
<ManageEmployee
|
||||
employeeId={selectedEmployeeId}
|
||||
onClosed={()=>setShowModal(false)}
|
||||
onClosed={() => setShowModal( false )}
|
||||
IsAllEmployee={showAllEmployees}
|
||||
/>
|
||||
</GlobalModel>
|
||||
)}
|
||||
@ -594,91 +573,90 @@ const handleAllEmployeesToggle = (e) => {
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td className=" d-none d-md-table-cell">
|
||||
{moment(item.joiningDate)?.format("DD-MMM-YYYY")}
|
||||
</td>
|
||||
<td>
|
||||
{/* Assuming 'isActive' property exists to determine status */}
|
||||
{item.isActive ? (
|
||||
<span
|
||||
className="badge bg-label-success"
|
||||
text-capitalized=""
|
||||
>
|
||||
Active
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
className="badge bg-label-danger"
|
||||
text-capitalized=""
|
||||
>
|
||||
Inactive
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
{Manage_Employee && (
|
||||
<td className="text-end">
|
||||
<div className="dropdown">
|
||||
<button
|
||||
className="btn btn-icon dropdown-toggle hide-arrow"
|
||||
data-bs-toggle="dropdown"
|
||||
>
|
||||
<i className="bx bx-dots-vertical-rounded bx-md"></i>
|
||||
</button>
|
||||
<div className="dropdown-menu dropdown-menu-end">
|
||||
<button
|
||||
onClick={() =>
|
||||
navigate(`/employee/${item.id}`)
|
||||
}
|
||||
className="dropdown-item py-1"
|
||||
>
|
||||
<i className="bx bx-detail bx-sm"></i> View
|
||||
</button>
|
||||
<button
|
||||
className="dropdown-item py-1"
|
||||
onClick={() => {
|
||||
handleEmployeeModel(item.id);
|
||||
}}
|
||||
>
|
||||
<i className="bx bx-edit bx-sm"></i> Edit
|
||||
</button>
|
||||
{!item.isSystem && (
|
||||
<>
|
||||
<button
|
||||
className="dropdown-item py-1"
|
||||
onClick={() =>
|
||||
handleOpenDelete(item.id)
|
||||
}
|
||||
>
|
||||
<i className="bx bx-task-x bx-sm"></i>{" "}
|
||||
Suspend
|
||||
</button>
|
||||
<button
|
||||
className="dropdown-item py-1"
|
||||
type="button"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#managerole-modal"
|
||||
onClick={() =>
|
||||
handleConfigData(item.id)
|
||||
}
|
||||
>
|
||||
<i className="bx bx-cog bx-sm"></i>{" "}
|
||||
Manage Role
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<td className=" d-none d-md-table-cell">
|
||||
{moment(item.joiningDate)?.format("DD-MMM-YYYY")}
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
))}
|
||||
<td>
|
||||
{showInactive ? (
|
||||
<span
|
||||
className="badge bg-label-danger"
|
||||
text-capitalized=""
|
||||
>
|
||||
Inactive
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
className="badge bg-label-success"
|
||||
text-capitalized=""
|
||||
>
|
||||
Active
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
{Manage_Employee && (
|
||||
<td className="text-end">
|
||||
<div className="dropdown">
|
||||
<button
|
||||
className="btn btn-icon dropdown-toggle hide-arrow"
|
||||
data-bs-toggle="dropdown"
|
||||
>
|
||||
<i className="bx bx-dots-vertical-rounded bx-md"></i>
|
||||
</button>
|
||||
<div className="dropdown-menu dropdown-menu-end">
|
||||
<button
|
||||
onClick={() =>
|
||||
navigate(`/employee/${item.id}`)
|
||||
}
|
||||
className="dropdown-item py-1"
|
||||
>
|
||||
<i className="bx bx-detail bx-sm"></i> View
|
||||
</button>
|
||||
<button
|
||||
className="dropdown-item py-1"
|
||||
onClick={() => {
|
||||
handleEmployeeModel(item.id);
|
||||
}}
|
||||
>
|
||||
<i className="bx bx-edit bx-sm"></i> Edit
|
||||
</button>
|
||||
{!item.isSystem && (
|
||||
<>
|
||||
<button
|
||||
className="dropdown-item py-1"
|
||||
onClick={() =>
|
||||
handleOpenDelete(item.id)
|
||||
}
|
||||
>
|
||||
<i className="bx bx-task-x bx-sm"></i>{" "}
|
||||
Suspend
|
||||
</button>
|
||||
<button
|
||||
className="dropdown-item py-1"
|
||||
type="button"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#managerole-modal"
|
||||
onClick={() =>
|
||||
setEmpForManageRole(item.id)
|
||||
}
|
||||
>
|
||||
<i className="bx bx-cog bx-sm"></i>{" "}
|
||||
Manage Role
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style={{ width: "1%" }}></div>
|
||||
|
||||
{/* Pagination */}
|
||||
{!loading && displayData.length > itemsPerPage && (
|
||||
{!loading && displayData.length > ITEMS_PER_PAGE && (
|
||||
<nav aria-label="Page">
|
||||
<ul className="pagination pagination-sm justify-content-end py-1">
|
||||
<li
|
||||
|
||||
@ -20,6 +20,7 @@ import Avatar from "../../components/common/Avatar";
|
||||
import AttendancesEmployeeRecords from "./AttendancesEmployeeRecords";
|
||||
import ManageEmployee from "../../components/Employee/ManageEmployee";
|
||||
import { useChangePassword } from "../../components/Context/ChangePasswordContext";
|
||||
import GlobalModel from "../../components/common/GlobalModel";
|
||||
|
||||
const EmployeeProfile = () => {
|
||||
const { profile } = useProfile();
|
||||
@ -39,11 +40,7 @@ const EmployeeProfile = () => {
|
||||
setActivePill(pillKey);
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
setShowModal(false);
|
||||
fetchEmployeeProfile(employeeId);
|
||||
};
|
||||
const handleShow = () => setShowModal(true);
|
||||
|
||||
|
||||
const fetchEmployeeProfile = async (employeeID) => {
|
||||
try {
|
||||
@ -104,24 +101,10 @@ const EmployeeProfile = () => {
|
||||
const { openChangePassword } = useChangePassword();
|
||||
return (
|
||||
<>
|
||||
{" "}
|
||||
{showModal && (
|
||||
<div
|
||||
className={`modal fade ${showModal ? "show" : ""} `}
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: showModal ? "block" : "none" }}
|
||||
aria-hidden={!showModal}
|
||||
>
|
||||
<div className="modal-dialog modal-xl modal-dialog-centered ">
|
||||
<div
|
||||
className="modal-content overflow-y-auto overflow-x-hidden"
|
||||
style={{ maxHeight: "90vh" }}
|
||||
>
|
||||
<ManageEmployee employeeId={employeeId} onClosed={closeModal} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<GlobalModel size="lg" isOpen={showModal} closeModal={()=>setShowModal(false)}>
|
||||
<ManageEmployee employeeId={employeeId} onClosed={()=>setShowModal(false)} />
|
||||
</GlobalModel>
|
||||
)}
|
||||
<div className="container-fluid">
|
||||
<Breadcrumb
|
||||
@ -251,7 +234,7 @@ const EmployeeProfile = () => {
|
||||
</div>
|
||||
<button
|
||||
className="btn btn-primary btn-block"
|
||||
onClick={() => handleShow()}
|
||||
onClick={()=>setShowModal(true)}
|
||||
>
|
||||
Edit Profile
|
||||
</button>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect, useMemo } from "react";
|
||||
import Breadcrumb from "../../components/common/Breadcrumb";
|
||||
import MasterModal from "../../components/master/MasterModal";
|
||||
import { mastersList} from "../../data/masters";
|
||||
@ -9,92 +9,80 @@ import MasterTable from "./MasterTable";
|
||||
import { getCachedData } from "../../slices/apiDataManager";
|
||||
import {useHasUserPermission} from "../../hooks/useHasUserPermission";
|
||||
import { MANAGE_MASTER } from "../../utils/constants";
|
||||
import {useQueryClient} from "@tanstack/react-query";
|
||||
|
||||
|
||||
const MasterPage = () => {
|
||||
const [modalConfig, setModalConfig] = useState({ modalType: "", item: null, masterType: null });
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [filteredResults, setFilteredResults] = useState([]);
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
|
||||
const [modalConfig, setmodalConfig] = useState({modalType: "", item: null, masterType:null });
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [ filteredResults, setFilteredResults ] = useState( [] );
|
||||
const hasMasterPermission = useHasUserPermission( MANAGE_MASTER )
|
||||
const dispatch = useDispatch();
|
||||
const selectedMaster = useSelector((store)=>store.localVariables.selectedMaster)
|
||||
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
|
||||
const openModal = () => {
|
||||
setIsCreateModalOpen(true);
|
||||
const hasMasterPermission = useHasUserPermission(MANAGE_MASTER);
|
||||
const dispatch = useDispatch();
|
||||
const selectedMaster = useSelector((store) => store.localVariables.selectedMaster);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { data: masterData = [], loading, error, RecallApi } = useMaster();
|
||||
|
||||
const openModal = () => setIsCreateModalOpen(true);
|
||||
|
||||
const closeModal = () => {
|
||||
setIsCreateModalOpen(false);
|
||||
setModalConfig(null);
|
||||
|
||||
// Clean up Bootstrap modal manually
|
||||
const modalEl = document.getElementById('master-modal');
|
||||
modalEl?.classList.remove('show');
|
||||
if (modalEl) modalEl.style.display = 'none';
|
||||
|
||||
document.body.classList.remove('modal-open');
|
||||
document.body.style.overflow = 'auto';
|
||||
|
||||
document.querySelectorAll('.modal-backdrop').forEach((el) => el.remove());
|
||||
};
|
||||
|
||||
const handleModalData = (modalType, item, masterType = selectedMaster) => {
|
||||
setModalConfig({ modalType, item, masterType });
|
||||
};
|
||||
|
||||
const handleSearch = (e) => {
|
||||
const value = e.target.value.toLowerCase();
|
||||
setSearchTerm(value);
|
||||
|
||||
if (!masterData?.length) return;
|
||||
|
||||
const results = masterData.filter((item) =>
|
||||
Object.values(item).some(
|
||||
(field) => field?.toString().toLowerCase().includes(value)
|
||||
)
|
||||
);
|
||||
setFilteredResults(results);
|
||||
};
|
||||
const displayData = useMemo(() => {
|
||||
if (searchTerm) return filteredResults;
|
||||
return queryClient.getQueryData(["masterData", selectedMaster]) || masterData;
|
||||
}, [searchTerm, filteredResults, selectedMaster, masterData]);
|
||||
|
||||
const columns = useMemo(() => {
|
||||
if (!displayData?.length) return [];
|
||||
return Object.keys(displayData[0]).map((key) => ({
|
||||
key,
|
||||
label: key.toUpperCase(),
|
||||
}));
|
||||
}, [displayData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (modalConfig) openModal();
|
||||
}, [modalConfig]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
setIsCreateModalOpen(false);
|
||||
closeModal();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const closeModal = () => {
|
||||
setIsCreateModalOpen(false);
|
||||
setmodalConfig(null);
|
||||
|
||||
|
||||
const modalElement = document.getElementById('master-modal');
|
||||
if (modalElement) {
|
||||
modalElement.classList.remove('show');
|
||||
modalElement.style.display = 'none';
|
||||
document.body.classList.remove('modal-open');
|
||||
|
||||
|
||||
const backdropElement = document.querySelector('.modal-backdrop');
|
||||
if (backdropElement) {
|
||||
backdropElement.classList.remove('modal-backdrop');
|
||||
backdropElement.style.display = 'none';
|
||||
}
|
||||
}
|
||||
const modalBackdropElement = document.querySelector('.modal-backdrop');
|
||||
if (modalBackdropElement) {
|
||||
modalBackdropElement.remove();
|
||||
}
|
||||
document.body.style.overflow = 'auto';
|
||||
|
||||
};
|
||||
|
||||
const {data:masterData, loading,error , RecallApi} = useMaster();
|
||||
|
||||
|
||||
const handleSearch = (e) => {
|
||||
const value = e.target.value.toLowerCase();
|
||||
setSearchTerm(value);
|
||||
|
||||
if (!masterData.length) return;
|
||||
|
||||
const results = masterData.filter((item) =>
|
||||
Object.values(item).some((field) =>
|
||||
field && field.toString().toLowerCase().includes(value)
|
||||
)
|
||||
);
|
||||
|
||||
setFilteredResults(results);
|
||||
};
|
||||
|
||||
const displayData = searchTerm ? filteredResults : getCachedData(selectedMaster) ? getCachedData(selectedMaster) : masterData
|
||||
|
||||
const columns = displayData?.length
|
||||
? Object.keys(displayData[0]).map((key) => ({ key, label: key.toUpperCase() }))
|
||||
: [];
|
||||
|
||||
const handleModalData =(modalType,item,masterType = selectedMaster)=>{
|
||||
setmodalConfig({ modalType: modalType, item:item,masterType:masterType });
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (modalConfig !== null) {
|
||||
openModal();
|
||||
}
|
||||
|
||||
}, [ modalConfig, isCreateModalOpen ] );
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
setIsCreateModalOpen(false)
|
||||
closeModal();
|
||||
};
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -36,34 +36,35 @@ const ProjectDetails = () => {
|
||||
projects_Details,
|
||||
loading: projectLoading,
|
||||
error: ProjectError,
|
||||
refetch
|
||||
} = useProjectDetails(projectId);
|
||||
const dispatch = useDispatch();
|
||||
const [project, setProject] = useState(null);
|
||||
const [projectDetails, setProjectDetails] = useState(null);
|
||||
// const [projectDetails, setProjectDetails] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
const fetchData = async () => {
|
||||
const project_cache = getCachedData("projectInfo");
|
||||
if (!project_cache || project_cache?.projectId !== projectId) {
|
||||
ProjectRepository.getProjectByprojectId(projectId)
|
||||
.then((response) => {
|
||||
setProjectDetails(response.data);
|
||||
setProject(response.data);
|
||||
cacheData("projectInfo", { projectId, data: response.data });
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
setError("Failed to fetch data.");
|
||||
setLoading(false);
|
||||
});
|
||||
} else {
|
||||
setProjectDetails(project_cache.data);
|
||||
setProject(project_cache.data);
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
// const fetchData = async () => {
|
||||
// const project_cache = getCachedData("projectInfo");
|
||||
// if (!project_cache || project_cache?.projectId !== projectId) {
|
||||
// ProjectRepository.getProjectByprojectId(projectId)
|
||||
// .then((response) => {
|
||||
// setProjectDetails(response.data);
|
||||
// setProject(response.data);
|
||||
// cacheData("projectInfo", { projectId, data: response.data });
|
||||
// setLoading(false);
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// console.error(error);
|
||||
// setError("Failed to fetch data.");
|
||||
// setLoading(false);
|
||||
// });
|
||||
// } else {
|
||||
// setProjectDetails(project_cache.data);
|
||||
// setProject(project_cache.data);
|
||||
// setLoading(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
const [activePill, setActivePill] = useState("profile");
|
||||
|
||||
@ -80,31 +81,14 @@ const ProjectDetails = () => {
|
||||
switch (activePill) {
|
||||
case "profile": {
|
||||
return (
|
||||
<>
|
||||
<div className="row">
|
||||
<div className="col-lg-4 col-md-5 mt-5">
|
||||
{/* About User */}
|
||||
<AboutProject data={projectDetails}></AboutProject>
|
||||
<ProjectOverview project={projectId} />
|
||||
{/* About User */}
|
||||
</div>
|
||||
<div className="col-lg-8 col-md-5 mt-5">
|
||||
{/* Profile Overview */}
|
||||
<ProjectProgressChart
|
||||
ShowAllProject="false"
|
||||
DefaultRange="1M"
|
||||
/>
|
||||
{/* Profile Overview */}
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-xl-4 col-lg-5 col-md-5 mt-5">
|
||||
<AboutProject ></AboutProject>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-xl-4 col-lg-5 col-md-5 ">
|
||||
{/* Profile Overview */}
|
||||
{/* <ProjectOverview project={projectId} /> */}
|
||||
{/* Profile Overview */}
|
||||
</div>
|
||||
<div className="col-xl-4 col-lg-5 col-md-5 mt-5">
|
||||
<ProjectOverview project={projectId} />
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
case "teams": {
|
||||
@ -112,7 +96,7 @@ const ProjectDetails = () => {
|
||||
<div className="row">
|
||||
<div className="col-lg-12 col-xl-12">
|
||||
{/* Teams */}
|
||||
<Teams project={projectDetails}></Teams>
|
||||
<Teams ></Teams>
|
||||
{/* Teams */}
|
||||
</div>
|
||||
</div>
|
||||
@ -122,7 +106,7 @@ const ProjectDetails = () => {
|
||||
case "infra": {
|
||||
return (
|
||||
<ProjectInfra
|
||||
data={projectDetails}
|
||||
data={projects_Details}
|
||||
onDataChange={handleDataChange}
|
||||
></ProjectInfra>
|
||||
);
|
||||
@ -131,7 +115,7 @@ const ProjectDetails = () => {
|
||||
case "workplan": {
|
||||
return (
|
||||
<WorkPlan
|
||||
data={projectDetails}
|
||||
data={projects_Details}
|
||||
onDataChange={handleDataChange}
|
||||
></WorkPlan>
|
||||
);
|
||||
@ -140,7 +124,7 @@ const ProjectDetails = () => {
|
||||
case "directory": {
|
||||
return (
|
||||
<div className="row">
|
||||
<Directory IsPage={false} prefernceContacts={projectDetails.id} />
|
||||
<Directory IsPage={false} prefernceContacts={projects_Details.id} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -152,29 +136,16 @@ const ProjectDetails = () => {
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(setProjectId(projectId));
|
||||
setProject(projects_Details);
|
||||
setProjectDetails(projects_Details);
|
||||
|
||||
}, [projects_Details, projectId]);
|
||||
|
||||
const handler = useCallback(
|
||||
(msg) => {
|
||||
if (msg.keyword === "Update_Project" && project.id === msg.response.id) {
|
||||
clearCacheKey("projectInfo");
|
||||
ProjectRepository.getProjectByprojectId(projectId)
|
||||
.then((response) => {
|
||||
setProjectDetails(response.data);
|
||||
setProject(response.data);
|
||||
cacheData("projectInfo", { projectId, data: response.data });
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
setError("Failed to fetch data.");
|
||||
setLoading(false);
|
||||
});
|
||||
if (msg.keyword === "Update_Project" && projects_Details.id === msg.response.id) {
|
||||
refetch()
|
||||
}
|
||||
},
|
||||
[project, handleDataChange]
|
||||
[projects_Details, handleDataChange]
|
||||
);
|
||||
useEffect(() => {
|
||||
eventBus.on("project", handler);
|
||||
|
||||
@ -3,10 +3,13 @@ import ProjectCard from "../../components/Project/ProjectCard";
|
||||
import ManageProjectInfo from "../../components/Project/ManageProjectInfo";
|
||||
import Breadcrumb from "../../components/common/Breadcrumb";
|
||||
import ProjectRepository from "../../repositories/ProjectRepository";
|
||||
import { useProjects } from "../../hooks/useProjects";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useProjects, useCreateProject } from "../../hooks/useProjects";
|
||||
import showToast from "../../services/toastService";
|
||||
import { getCachedData, cacheData, clearCacheKey } from "../../slices/apiDataManager";
|
||||
// import {
|
||||
// getCachedData,
|
||||
// cacheData,
|
||||
// clearCacheKey,
|
||||
// } from "../../slices/apiDataManager";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import { useProfile } from "../../hooks/useProfile";
|
||||
import { ITEMS_PER_PAGE, MANAGE_PROJECT } from "../../utils/constants";
|
||||
@ -14,26 +17,33 @@ import ProjectListView from "./ProjectListView";
|
||||
import eventBus from "../../services/eventBus";
|
||||
import { clearApiCacheKey } from "../../slices/apiCacheSlice";
|
||||
import { defaultCheckBoxAppearanceProvider } from "pdf-lib";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import usePagination from "../../hooks/usePagination";
|
||||
import GlobalModel from "../../components/common/GlobalModel";
|
||||
|
||||
const ProjectList = () => {
|
||||
const { profile: loginUser } = useProfile();
|
||||
const [listView, setListView] = useState(false);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
|
||||
const { projects, loading, error, refetch } = useProjects();
|
||||
const [projectList, setProjectList] = useState([]);
|
||||
const HasManageProjectPermission = useHasUserPermission(MANAGE_PROJECT);
|
||||
const [HasManageProject, setHasManageProject] = useState(
|
||||
HasManageProjectPermission
|
||||
);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [itemsPerPage] = useState(ITEMS_PER_PAGE);
|
||||
const HasManageProjectPermission = useHasUserPermission(MANAGE_PROJECT);
|
||||
const [HasManageProject, setHasManageProject] = useState(HasManageProjectPermission);
|
||||
|
||||
const { mutate: createProject,isPending } = useCreateProject({
|
||||
onSuccessCallback: () => {
|
||||
setShowModal(false);
|
||||
},
|
||||
});
|
||||
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [selectedStatuses, setSelectedStatuses] = useState([
|
||||
"b74da4c2-d07e-46f2-9919-e75e49b12731",
|
||||
"603e994b-a27f-4e5d-a251-f3d69b0498ba",
|
||||
"ef1c356e-0fe0-42df-a5d3-8daee355492d",
|
||||
"cdad86aa-8a56-4ff4-b633-9c629057dfef",
|
||||
"33deaef9-9af1-4f2a-b443-681ea0d04f81",
|
||||
]);
|
||||
|
||||
@ -53,48 +63,29 @@ const ProjectList = () => {
|
||||
.filter((statusId) => grouped[statusId])
|
||||
.flatMap((statusId) =>
|
||||
grouped[statusId].sort((a, b) =>
|
||||
a.name.toLowerCase()?.localeCompare(b.name.toLowerCase())
|
||||
a.name.toLowerCase().localeCompare(b.name.toLowerCase())
|
||||
)
|
||||
);
|
||||
setProjectList(sortedGrouped);
|
||||
|
||||
setProjectList((prev) => {
|
||||
const isSame = JSON.stringify(prev) === JSON.stringify(sortedGrouped);
|
||||
return isSame ? prev : sortedGrouped;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
sortingProject(projects);
|
||||
}, [projects, loginUser?.projects, loading]);
|
||||
if (!loading && projects) {
|
||||
sortingProject(projects);
|
||||
}
|
||||
}, [projects, loading, selectedStatuses]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loginUser) {
|
||||
setHasManageProject(HasManageProjectPermission);
|
||||
} else {
|
||||
setHasManageProject(false);
|
||||
}
|
||||
setHasManageProject(loginUser ? HasManageProjectPermission : false);
|
||||
}, [loginUser, HasManageProjectPermission]);
|
||||
|
||||
const handleSubmitForm = (newProject, setloading, reset) => {
|
||||
ProjectRepository.manageProject(newProject)
|
||||
.then((response) => {
|
||||
const cachedProjects = getCachedData("projectslist") || [];
|
||||
const updatedProjects = [...cachedProjects, response.data];
|
||||
cacheData("projectslist", updatedProjects);
|
||||
setProjectList((prev) => [...prev, response.data]);
|
||||
setloading(false);
|
||||
reset();
|
||||
sortingProject(getCachedData("projectslist"));
|
||||
showToast("Project Created successfully.", "success");
|
||||
setShowModal(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
showToast(error.message, "error");
|
||||
setShowModal(false);
|
||||
});
|
||||
};
|
||||
|
||||
const handleReFresh = () => {
|
||||
if (!projects || projects.length === 0) {
|
||||
refetch();
|
||||
}
|
||||
const handleSubmitForm = (newProject) => {
|
||||
createProject(newProject);
|
||||
};
|
||||
|
||||
const handleStatusChange = (statusId) => {
|
||||
@ -118,13 +109,14 @@ const ProjectList = () => {
|
||||
return matchesStatus && matchesSearch;
|
||||
});
|
||||
|
||||
const indexOfLastItem = currentPage * itemsPerPage;
|
||||
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
|
||||
const currentItems = filteredProjects.slice(
|
||||
indexOfFirstItem,
|
||||
indexOfLastItem
|
||||
);
|
||||
const totalPages = Math.ceil(filteredProjects.length / itemsPerPage);
|
||||
const totalPages = Math.ceil(filteredProjects.length / ITEMS_PER_PAGE);
|
||||
|
||||
const {
|
||||
currentItems,
|
||||
currentPage,
|
||||
paginate,
|
||||
setCurrentPage,
|
||||
} = usePagination(filteredProjects, ITEMS_PER_PAGE);
|
||||
|
||||
useEffect(() => {
|
||||
const tooltipTriggerList = Array.from(
|
||||
@ -133,67 +125,19 @@ const ProjectList = () => {
|
||||
tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el));
|
||||
}, []);
|
||||
|
||||
const handler = useCallback(
|
||||
async (msg) => {
|
||||
if (HasManageProject && msg.keyword === "Create_Project") {
|
||||
const updatedProjects = [...projectList, msg.response];
|
||||
cacheData("projectslist", updatedProjects);
|
||||
setProjectList(updatedProjects);
|
||||
sortingProject(updatedProjects);
|
||||
}
|
||||
if (
|
||||
msg.keyword === "Update_Project" &&
|
||||
projectList.some((item) => item.id === msg.response.id)
|
||||
) {
|
||||
ProjectRepository.getProjectList()
|
||||
.then((response) => {
|
||||
cacheData("projectslist", response?.data);
|
||||
sortingProject(response?.data);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e)
|
||||
});
|
||||
}
|
||||
},
|
||||
[HasManageProject, projectList, sortingProject]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
eventBus.on("project", handler);
|
||||
return () => eventBus.off("project", handler);
|
||||
}, [handler]);
|
||||
|
||||
const assignProjectHandler = useCallback(
|
||||
async (data) => {
|
||||
clearCacheKey("projectslist");
|
||||
await refetch();
|
||||
|
||||
sortingProject(projects);
|
||||
},
|
||||
[refetch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
eventBus.on("assign_project_one", assignProjectHandler);
|
||||
return () => eventBus.off("assign_project_one", assignProjectHandler);
|
||||
}, [handler]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`modal fade ${showModal ? "show" : ""}`}
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: showModal ? "block" : "none" }}
|
||||
aria-hidden={!showModal}
|
||||
>
|
||||
<ManageProjectInfo
|
||||
|
||||
{showModal && (
|
||||
<GlobalModel isOpen={showModal} closeModal={handleClose}>
|
||||
<ManageProjectInfo
|
||||
project={null}
|
||||
handleSubmitForm={handleSubmitForm}
|
||||
onClose={handleClose}
|
||||
isPending={isPending}
|
||||
/>
|
||||
</div>
|
||||
</GlobalModel>
|
||||
)}
|
||||
|
||||
<div className="container-fluid">
|
||||
<Breadcrumb
|
||||
@ -262,6 +206,10 @@ const ProjectList = () => {
|
||||
id: "b74da4c2-d07e-46f2-9919-e75e49b12731",
|
||||
label: "Active",
|
||||
},
|
||||
{
|
||||
id: "cdad86aa-8a56-4ff4-b633-9c629057dfef",
|
||||
label:"In Progress"
|
||||
},
|
||||
{
|
||||
id: "603e994b-a27f-4e5d-a251-f3d69b0498ba",
|
||||
label: "On Hold",
|
||||
@ -318,11 +266,11 @@ const ProjectList = () => {
|
||||
{listView ? (
|
||||
<div className="card cursor-pointer">
|
||||
<div className="card-body p-2">
|
||||
<div className="table-responsive text-nowrap py-2 ">
|
||||
<div className="table-responsive text-nowrap py-2 " style={{minHeight:"400px"}}>
|
||||
<table className="table m-3">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="text-start" colSpan={5}>
|
||||
<th className="text-start" colSpan={5} >
|
||||
Project Name
|
||||
</th>
|
||||
<th className="mx-2 text-start">Contact Person</th>
|
||||
@ -345,6 +293,10 @@ const ProjectList = () => {
|
||||
id: "b74da4c2-d07e-46f2-9919-e75e49b12731",
|
||||
label: "Active",
|
||||
},
|
||||
{
|
||||
id: "cdad86aa-8a56-4ff4-b633-9c629057dfef",
|
||||
label:"In Progress"
|
||||
},
|
||||
{
|
||||
id: "603e994b-a27f-4e5d-a251-f3d69b0498ba",
|
||||
label: "On Hold",
|
||||
@ -386,8 +338,8 @@ const ProjectList = () => {
|
||||
</thead>
|
||||
<tbody className="table-border-bottom-0 overflow-auto ">
|
||||
{currentItems.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan="12" className="text-center py-4">
|
||||
<tr className="text-center">
|
||||
<td colSpan="12" rowSpan='12'style={{height:"200px"}} >
|
||||
No projects found
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import moment from "moment";
|
||||
import { useProjects } from "../../hooks/useProjects";
|
||||
import {
|
||||
useProjectDetails,
|
||||
useProjects,
|
||||
useUpdateProject,
|
||||
} from "../../hooks/useProjects";
|
||||
import {
|
||||
getProjectStatusName,
|
||||
getProjectStatusColor,
|
||||
@ -14,25 +18,36 @@ import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import ManageProjectInfo from "../../components/Project/ManageProjectInfo";
|
||||
import showToast from "../../services/toastService";
|
||||
import { getCachedData, cacheData } from "../../slices/apiDataManager";
|
||||
import GlobalModel from "../../components/common/GlobalModel";
|
||||
import {formatNumber} from "../../utils/dateUtils";
|
||||
|
||||
const ProjectListView = ({ projectData, recall }) => {
|
||||
const [projectInfo, setProjectInfo] = useState(projectData);
|
||||
const [projectDetails, setProjectDetails] = useState(null);
|
||||
const { projects_Details, loading, error, refetch } = useProjectDetails(
|
||||
projectInfo?.id,false
|
||||
);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
const ManageProject = useHasUserPermission(MANAGE_PROJECT);
|
||||
useEffect(()=>{
|
||||
setProjectInfo(projectData);
|
||||
},[projectData])
|
||||
useEffect(() => {
|
||||
setProjectInfo(projectData);
|
||||
}, [projectData]);
|
||||
const {
|
||||
mutate: updateProject,
|
||||
isPending,
|
||||
isSuccess,
|
||||
isError,
|
||||
} = useUpdateProject({
|
||||
onSuccessCallback: () => {
|
||||
setShowModal(false);
|
||||
},
|
||||
})
|
||||
|
||||
const handleShow = async () => {
|
||||
try {
|
||||
const response = await ProjectRepository.getProjectByprojectId(
|
||||
projectInfo.id
|
||||
);
|
||||
setProjectDetails(response.data);
|
||||
const { data } = await refetch();
|
||||
setShowModal(true);
|
||||
} catch (error) {
|
||||
} catch (err) {
|
||||
showToast("Failed to load project details", "error");
|
||||
}
|
||||
};
|
||||
@ -52,63 +67,25 @@ const ProjectListView = ({ projectData, recall }) => {
|
||||
|
||||
const handleFormSubmit = (updatedProject) => {
|
||||
if (projectInfo?.id) {
|
||||
ProjectRepository.updateProject(projectInfo.id, updatedProject)
|
||||
.then((response) => {
|
||||
const updatedProjectData = {
|
||||
...projectInfo,
|
||||
...response.data,
|
||||
building: projectDetails?.building,
|
||||
};
|
||||
|
||||
setProjectInfo(updatedProjectData);
|
||||
|
||||
if (getCachedData(`projectinfo-${projectInfo.id}`)) {
|
||||
cacheData(`projectinfo-${projectInfo.id}`, updatedProjectData);
|
||||
}
|
||||
|
||||
const projects_list = getCachedData("projectslist");
|
||||
if (projects_list) {
|
||||
const updatedProjectsList = projects_list.map((project) =>
|
||||
project.id === projectInfo.id
|
||||
? {
|
||||
...project,
|
||||
...response.data,
|
||||
// tenant: project.tenant
|
||||
}
|
||||
: project
|
||||
);
|
||||
cacheData("projectslist", updatedProjectsList);
|
||||
}
|
||||
recall(getCachedData("projectslist"));
|
||||
showToast("Project updated successfully.", "success");
|
||||
setShowModal(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
showToast(error.message, "error");
|
||||
});
|
||||
updateProject({
|
||||
projectId: projectInfo.id,
|
||||
updatedData: updatedProject,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{showModal && projectDetails && (
|
||||
<tr>
|
||||
<td
|
||||
className="modal fade show"
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: "block" }}
|
||||
aria-hidden="false"
|
||||
>
|
||||
<ManageProjectInfo
|
||||
project={projectDetails}
|
||||
{showModal && projects_Details && (
|
||||
<GlobalModel isOpen={showModal} closeModal={handleClose}> <ManageProjectInfo
|
||||
project={projects_Details}
|
||||
handleSubmitForm={handleFormSubmit}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
onClose={handleClose}
|
||||
isPending={isPending}
|
||||
/></GlobalModel>
|
||||
)}
|
||||
|
||||
<tr className="py-8">
|
||||
<tr className={`py-8 ${isPending ? "bg-light opacity-50 pointer-events-none" : ""} `}>
|
||||
<td className="text-start" colSpan={5}>
|
||||
<span
|
||||
className="text-primary cursor-pointer"
|
||||
@ -132,7 +109,7 @@ const ProjectListView = ({ projectData, recall }) => {
|
||||
? moment(projectInfo.endDate).format("DD-MMM-YYYY")
|
||||
: "NA"}
|
||||
</td>
|
||||
<td className="mx-2 text-center small">{projectInfo.plannedWork}</td>
|
||||
<td className="mx-2 text-center small">{formatNumber(projectInfo.plannedWork)}</td>
|
||||
<td className="py-6 mx-2 text-start small align-items-center">
|
||||
<ProgressBar
|
||||
plannedWork={projectInfo.plannedWork}
|
||||
@ -162,14 +139,23 @@ const ProjectListView = ({ projectData, recall }) => {
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i
|
||||
className="bx bx-dots-vertical-rounded bx-sm text-muted"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-offset="0,8"
|
||||
data-bs-placement="top"
|
||||
data-bs-custom-class="tooltip-dark"
|
||||
title="More Action"
|
||||
></i>
|
||||
{loading ? (
|
||||
<div
|
||||
className="spinner-border spinner-border-sm text-secondary"
|
||||
role="status"
|
||||
>
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
) : (
|
||||
<i
|
||||
className="bx bx-dots-vertical-rounded bx-sm text-muted"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-offset="0,8"
|
||||
data-bs-placement="top"
|
||||
data-bs-custom-class="tooltip-dark"
|
||||
title="More Action"
|
||||
></i>
|
||||
)}
|
||||
</button>
|
||||
<ul className="dropdown-menu dropdown-menu-end">
|
||||
<li>
|
||||
|
||||
@ -41,7 +41,7 @@ export const MasterRespository = {
|
||||
"Activity": ( id ) => api.delete( `/api/master/activity/delete/${ id }` ),
|
||||
"Application Role":(id)=>api.delete(`/api/roles/${id}`),
|
||||
"Work Category": ( id ) => api.delete( `api/master/work-category/${ id }` ),
|
||||
"Contact Category": ( id ) => api.delete( `/api/master/contact-category` ),
|
||||
"Contact Category": ( id ) => api.delete( `/api/master/contact-category/${id}` ),
|
||||
"Contact Tag" :(id)=>api.delete(`/api/master/contact-tag/${id}`),
|
||||
|
||||
getWorkCategory:() => api.get(`/api/master/work-categories`),
|
||||
|
||||
@ -23,7 +23,11 @@ const ProjectRepository = {
|
||||
deleteProject: ( id ) => api.delete( `/projects/${ id }` ),
|
||||
getProjectsByEmployee: ( id ) => api.get( `/api/project/assigned-projects/${ id }` ),
|
||||
updateProjectsByEmployee:(id,data)=>api.post(`/api/project/assign-projects/${id}`,data),
|
||||
projectNameList:()=>api.get("/api/project/list/basic")
|
||||
projectNameList: () => api.get( "/api/project/list/basic" ),
|
||||
|
||||
getProjectDetails:(id)=>api.get(`/api/project/details/${id}`),
|
||||
getProjectInfraByproject: ( id ) => api.get( `/api/project/infra-details/${ id }` ),
|
||||
getProjectTasksByWorkArea:(id)=>api.get(`/api/project/tasks/${id}`)
|
||||
};
|
||||
|
||||
export const TasksRepository = {
|
||||
|
||||
@ -8,7 +8,8 @@ import showToast from "./toastService";
|
||||
import eventBus from "./eventBus";
|
||||
import { useSelector } from "react-redux";
|
||||
import { clearApiCacheKey } from "../slices/apiCacheSlice";
|
||||
import { BASE_URL } from "../utils/constants";
|
||||
import {BASE_URL} from "../utils/constants";
|
||||
import { queryClient } from "../layouts/AuthLayout";
|
||||
const base_Url = BASE_URL;
|
||||
let connection = null;
|
||||
|
||||
@ -57,7 +58,8 @@ export function startSignalR(loggedUser) {
|
||||
data.keyword == "Create_Project" ||
|
||||
data.keyword == "Update_Project"
|
||||
) {
|
||||
clearCacheKey("projectslist");
|
||||
// clearCacheKey("projectslist");
|
||||
queryClient.invalidateQueries(['projectslist']);
|
||||
eventBus.emit("project", data);
|
||||
}
|
||||
|
||||
@ -77,20 +79,37 @@ export function startSignalR(loggedUser) {
|
||||
}
|
||||
// if created or updated infra
|
||||
if (data.keyword == "Infra") {
|
||||
clearCacheKey("projectInfo");
|
||||
eventBus.emit("infra", data);
|
||||
queryClient.removeQueries({queryKey:["ProjectInfra"]})
|
||||
// eventBus.emit("infra", data);
|
||||
}
|
||||
if (data.keyword == "Task_Report") {
|
||||
queryClient.removeQueries({queryKey:["Infra"]})
|
||||
// eventBus.emit("infra", data);
|
||||
}
|
||||
|
||||
if ( data.keyword == "WorkItem" )
|
||||
{
|
||||
queryClient.removeQueries({queryKey:["WorkItems"]})
|
||||
}
|
||||
|
||||
// if created or updated Employee
|
||||
if (data.keyword == "Employee") {
|
||||
clearCacheKey("employeeListByProject");
|
||||
clearCacheKey("allEmployeeList");
|
||||
clearCacheKey("allInactiveEmployeeList");
|
||||
clearCacheKey("employeeProfile");
|
||||
// clearCacheKey("employeeListByProject");
|
||||
// clearCacheKey("allEmployeeList");
|
||||
// clearCacheKey("allInactiveEmployeeList");
|
||||
// clearCacheKey("employeeProfile");
|
||||
clearCacheKey("Attendance");
|
||||
clearCacheKey("regularizedList")
|
||||
clearCacheKey("AttendanceLogs")
|
||||
eventBus.emit("employee", data);
|
||||
|
||||
// ---we can do also----
|
||||
// queryClient.removeQueries(['allEmployee', true]);
|
||||
// but best practies is refetch
|
||||
queryClient.invalidateQueries(['allEmployee', true]);
|
||||
queryClient.invalidateQueries(['allEmployee', false]);
|
||||
queryClient.invalidateQueries(['employeeProfile', data.response?.employeeId]);
|
||||
queryClient.invalidateQueries(['employeeListByProject']); // optional if scope
|
||||
eventBus.emit("employee", data);
|
||||
}
|
||||
|
||||
if (data.keyword == "Task_Report") {
|
||||
|
||||
@ -62,6 +62,11 @@ export const checkIfCurrentDate = (dateString) => {
|
||||
|
||||
return currentDate?.getTime() === inputDate?.getTime();
|
||||
};
|
||||
|
||||
export const formatNumber = (num) => {
|
||||
if (num == null || isNaN(num)) return "NA";
|
||||
return Number.isInteger(num) ? num : num.toFixed(2);
|
||||
};
|
||||
export const formatUTCToLocalTime = (datetime) =>{
|
||||
return moment.utc(datetime).local().format("MMMM DD, YYYY [at] hh:mm A");
|
||||
}
|
||||
}
|
||||
@ -24,7 +24,7 @@ export const getProjectStatusColor = (statusId) => {
|
||||
case "33deaef9-9af1-4f2a-b443-681ea0d04f81":
|
||||
return "bg-label-secondary";
|
||||
case "cdad86aa-8a56-4ff4-b633-9c629057dfef":
|
||||
return "bg-label-success";
|
||||
return "bg-label-primary";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user