Compare commits

..

No commits in common. "93c1fb18449b12f72b035c00f3478d5ff30718e3" and "379d9ecb8bcae5365b154908416a742c59bcadf5" have entirely different histories.

12 changed files with 80 additions and 289 deletions

View File

@ -21,7 +21,7 @@ export const ReportTask = ({ report, closeModal, refetch }) => {
required_error: "Completed Work must be a number",
invalid_type_error: "Completed Work must be a number",
})
.min(0, "Completed Work must be greater than 0")
.min(1, "Completed Work must be greater than 0")
.max(maxPending, {
message: `Completed task cannot exceed total pending tasks: ${maxPending}`,
})

View File

@ -1,79 +0,0 @@
import React from "react";
import ReactApexChart from "react-apexcharts";
const ProgressDonutChart = ({ completed = 0, planned = 1 }) => {
const percentage = planned > 0 ? Math.round((completed / planned) * 100) : 0;
const options = {
chart: {
height: 10,
type: "radialBar",
toolbar: { show: false },
},
plotOptions: {
radialBar: {
startAngle: 0,
endAngle: 360,
hollow: {
margin: 0,
size: "10%",
background: "#fff",
dropShadow: {
enabled: true,
top: 0,
left: 0,
blur: 3,
opacity: 0.45,
},
},
track: {
background: "#f5f5f5",
strokeWidth: "10%",
dropShadow: { enabled: false },
},
dataLabels: {
show: true,
name: {
offsetY: -10,
color: "#888",
fontSize: "14px",
},
value: {
formatter: (val) => `${val}%`,
color: "#111",
fontSize: "11px",
show: true,
},
},
},
},
fill: {
type: "gradient",
gradient: {
shade: "dark",
type: "horizontal",
shadeIntensity: 0.5,
gradientToColors: ["#ABE5A1"],
opacityFrom: 1,
opacityTo: 1,
stops: [0, 100],
},
},
stroke: {
lineCap: "round",
},
};
return (
<div id="chart">
<ReactApexChart
options={options}
series={[percentage]}
type="radialBar"
height={100}
/>
</div>
);
};
export default ProgressDonutChart;

View File

@ -5,13 +5,12 @@ import useMaster from "../../hooks/masterHook/useMaster";
import { useForm, Controller } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { clearCacheKey, getCachedData } from "../../slices/apiDataManager";
import { getCachedData } from "../../slices/apiDataManager";
import { useEmployeesAllOrByProjectId } from "../../hooks/useEmployees";
import { TasksRepository } from "../../repositories/ProjectRepository";
import showToast from "../../services/toastService";
import { useProjectDetails } from "../../hooks/useProjects";
const AssignRoleModel = ({ assignData, onClose, setAssigned }) => {
const AssignRoleModel = ({ assignData, onClose }) => {
// Calculate maxPlanned based on assignData
const maxPlanned =
assignData?.workItem?.workItem?.plannedWork -
@ -51,11 +50,11 @@ const AssignRoleModel = ({ assignData, onClose, setAssigned }) => {
// Initialize Bootstrap Popovers on component mount
useEffect(() => {
// Check if Bootstrap is available globally
if (typeof bootstrap !== "undefined") {
if (typeof bootstrap !== 'undefined') {
if (infoRef.current) {
new bootstrap.Popover(infoRef.current, {
trigger: "focus",
placement: "right",
trigger: 'focus',
placement: 'right',
html: true,
content: `<div>Total Pending tasks of the Activity</div>`,
});
@ -63,8 +62,8 @@ const AssignRoleModel = ({ assignData, onClose, setAssigned }) => {
if (infoRef1.current) {
new bootstrap.Popover(infoRef1.current, {
trigger: "focus",
placement: "right",
trigger: 'focus',
placement: 'right',
html: true,
content: `<div>Target task for today</div>`,
});
@ -73,6 +72,7 @@ const AssignRoleModel = ({ assignData, onClose, setAssigned }) => {
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
@ -173,8 +173,6 @@ const AssignRoleModel = ({ assignData, onClose, setAssigned }) => {
await TasksRepository.assignTask(formattedData);
showToast("Task Successfully Assigned", "success"); // Show success toast
reset(); // Reset form fields
clearCacheKey("projectInfo");
setAssigned(formattedData.plannedTask)
onClose(); // Close the modal
} catch (error) {
console.error("Error assigning task:", error); // Log the full error for debugging
@ -207,7 +205,7 @@ const AssignRoleModel = ({ assignData, onClose, setAssigned }) => {
<div className="mb-1">
<p className="mb-0">
<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>
<p className="me-2 m-0 font-bold">Work Location :</p>
{[
assignData?.building?.name,
assignData?.floor?.floorName,
@ -305,9 +303,7 @@ const AssignRoleModel = ({ assignData, onClose, setAssigned }) => {
type="checkbox"
id={`employee-${emp?.id}`}
value={emp.id}
checked={field.value?.includes(
emp.id
)}
checked={field.value?.includes(emp.id)}
onChange={(e) => {
handleCheckboxChange(e, emp);
}}
@ -315,10 +311,7 @@ const AssignRoleModel = ({ assignData, onClose, setAssigned }) => {
)}
/>
<div className="flex-grow-1">
<p
className="mb-0"
style={{ fontSize: "13px" }}
>
<p className="mb-0" style={{ fontSize: "13px" }}>
{emp.firstName} {emp.lastName}
</p>
<small
@ -340,9 +333,7 @@ const AssignRoleModel = ({ assignData, onClose, setAssigned }) => {
})
) : (
<div className="col-12">
<p className="text-center">
No employees found for the selected role.
</p>
<p className="text-center">No employees found for the selected role.</p>
</div>
)}
</div>
@ -396,37 +387,23 @@ const AssignRoleModel = ({ assignData, onClose, setAssigned }) => {
{!loading && errors.selectedEmployees && (
<div className="danger-text mt-1">
<p>{errors.selectedEmployees.message}</p>{" "}
{/* Use message from Zod schema */}
<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
className="form-text text-dark align-items-center d-flex"
htmlFor="inlineCheckbox1"
>
<label className="form-text text-dark align-items-center d-flex" htmlFor="inlineCheckbox1">
Pending Task of Activity :
<label
className="form-check-label fs-7 ms-4"
htmlFor="inlineCheckbox1"
>
<label className="form-check-label fs-7 ms-4" htmlFor="inlineCheckbox1">
<strong>
{assignData?.workItem?.workItem?.plannedWork -
assignData?.workItem?.workItem?.completedWork}
{assignData?.workItem?.workItem?.plannedWork - assignData?.workItem?.workItem?.completedWork}
</strong>{" "}
<u>
{
assignData?.workItem?.workItem?.activityMaster
?.unitOfMeasurement
}
</u>
<u>{assignData?.workItem?.workItem?.activityMaster?.unitOfMeasurement}</u>
</label>
<div
style={{ display: "flex", alignItems: "center" }}
>
<div style={{ display: "flex", alignItems: "center" }}>
<div
ref={infoRef}
tabIndex="0"
@ -462,14 +439,10 @@ const AssignRoleModel = ({ assignData, onClose, setAssigned }) => {
className="text-dark text-start d-flex align-items-center flex-wrap form-text"
htmlFor="inlineCheckbox1"
>
<span>Target for Today</span>&nbsp;
<span style={{ marginLeft: "46px" }}>:</span>
<span>Target for Today</span>&nbsp;<span style={{ marginLeft: '46px' }}>:</span>
</label>
</div>
<div
className="form-check form-check-inline col-sm-3 mt-2"
style={{ marginLeft: "-28px" }}
>
<div className="form-check form-check-inline col-sm-3 mt-2" style={{ marginLeft: '-28px' }}>
<Controller
name="plannedTask"
control={control}
@ -482,18 +455,10 @@ const AssignRoleModel = ({ assignData, onClose, setAssigned }) => {
id="defaultFormControlInput"
aria-describedby="defaultFormControlHelp"
/>
<span style={{ paddingLeft: "6px" }}>
{
assignData?.workItem?.workItem?.activityMaster
?.unitOfMeasurement
}
<span style={{ paddingLeft: '6px' }}>
{assignData?.workItem?.workItem?.activityMaster?.unitOfMeasurement}
</span>
<div
style={{
display: "flex",
alignItems: "center",
}}
>
<div style={{ display: "flex", alignItems: "center" }}>
<div
ref={infoRef1}
tabIndex="0"
@ -524,20 +489,16 @@ const AssignRoleModel = ({ assignData, onClose, setAssigned }) => {
</div>
{errors.plannedTask && (
<div className="danger-text mt-1">
{errors.plannedTask.message}
</div>
<div className="danger-text mt-1">{errors.plannedTask.message}</div>
)}
{isHelpVisible && (
<div
className="position-absolute bg-white border p-2 rounded shadow"
style={{ zIndex: 10, marginLeft: "10px" }}
style={{ zIndex: 10, marginLeft: '10px' }}
>
{/* Add your help content here */}
<p className="mb-0">
Enter the target value for today's task.
</p>
<p className="mb-0">Enter the target value for today's task.</p>
</div>
)}
</div>

View File

@ -13,10 +13,10 @@ const Floor = ({ floor, workAreas, forBuilding }) => {
/>
))
) : (
<tr>
<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 " >
<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"
>
@ -25,7 +25,7 @@ const Floor = ({ floor, workAreas, forBuilding }) => {
</div> */}
<div className="col-12 ps-8">
<div className="row">
<div className="d-flex col-5">
<div className="col">
{" "}
<span className="fw-semibold text-primary">
Floor:&nbsp;
@ -34,13 +34,6 @@ const Floor = ({ floor, workAreas, forBuilding }) => {
{floor.floorName}
</span>
</div>
<div className="text-start col-5">
{" "}
<span className="fw-semibold text-primary">
Work Area:&nbsp;
</span>{" "}
<span className="fw-normal text-danger">Not Available</span>
</div>
</div>
</div>
</div>

View File

@ -68,6 +68,7 @@ const InfraTable = ({ buildings }) => {
showToast("Failed to save floor", "error");
}
} catch (err) {
showToast("Error occurred while saving floor", "error");
}
};
@ -94,14 +95,14 @@ const InfraTable = ({ buildings }) => {
No floors have been added yet. Start by adding floors to manage
this building.
</p>
{/* <button
<button
type="button"
className="btn btn-xs btn-primary"
onClick={() => handleAddFloor(building)}
>
<i className="bx bx-plus-circle me-2"></i>
Add Floors
</button> */}
</button>
</div>
</td>
</tr>

View File

@ -6,46 +6,20 @@ import { useDispatch } from "react-redux";
import { refreshData } from "../../../slices/localVariablesSlice";
import ProjectRepository from "../../../repositories/ProjectRepository";
import showToast from "../../../services/toastService";
import { useHasUserPermission } from "../../../hooks/useHasUserPermission";
import {
ASSIGN_REPORT_TASK,
MANAGE_PROJECT_INFRA,
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 {useHasUserPermission} from "../../../hooks/useHasUserPermission";
import {ASSIGN_REPORT_TASK, MANAGE_PROJECT_INFRA, MANAGE_TASK} from "../../../utils/constants";
import {useParams} from "react-router-dom";
const WorkArea = ({ workArea, floor, forBuilding }) => {
const [workItems, setWorkItems] = useState([]);
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 [workAreaStatus, setWorkAreaStatus] = useState({
completed: 0,
planned: 100,
});
useEffect(() => {
const totalCompleted = workItems.reduce(
(sum, i) => sum + (i.workItem?.completedWork || 0),
0
);
const totalPlanned = workItems.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]);
const [ Project, setProject ] = useState();
const {projectId} = useParams();
const ManageTasks = useHasUserPermission(MANAGE_TASK);
const ManageInfra = useHasUserPermission(MANAGE_PROJECT_INFRA);
const ManageAndAssignTak = useHasUserPermission( ASSIGN_REPORT_TASK );
useEffect(() => {
const project = getCachedData("projectInfo");
@ -179,32 +153,21 @@ const WorkArea = ({ workArea, floor, forBuilding }) => {
}}
></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">
<div className="d-flex justify-content-start gap-12">
<div className="d-flex">
<span className="fw-semibold small">Floor:&nbsp;</span>
<span className="fw-normal text-darkgreen small">
{floor.floorName}
</span>
</div>
<div className="text-start col-5">
<div className="text-start ">
<span className="fw-semibold text-primary small">
Work Area:
Work Area:&nbsp;
</span>
<span className="fw-normal text-darkgreen small px-2">
<span className="fw-normal text-darkgreen small">
{workArea.areaName}
</span>
</div>
{workArea?.workItems?.length > 0 && (
<div className="col-2">
<ProgressBar
completedWork={workAreaStatus.completed}
plannedWork={workAreaStatus.planned}
className="m-0 text-info"
></ProgressBar>
</div>
)}
</div>
</button>
</p>
@ -232,14 +195,10 @@ const WorkArea = ({ workArea, floor, forBuilding }) => {
<th className="infra-activity-table-header d-none d-md-table-cell">
Completed/Planned
</th>
<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)) && (
{( ManageInfra || ( !projectId && ManageAndAssignTak ) ) && (
<th className="infra-activity-table-header text-end">
<span className="px-2">Actions</span>
</th>

View File

@ -21,6 +21,7 @@ import { useDispatch } from "react-redux";
import { refreshData } from "../../../slices/localVariablesSlice";
const WorkItem = ({
key,
workItem,
forBuilding,
forFloor,
@ -54,15 +55,6 @@ const WorkItem = ({
setNewWorkItem(workItem);
}, [workItem]);
const refreshWorkItem = (plannedTask) =>{
if (workItem) {
const updated = {
...workItem,
todaysAssigned: (workItem.todaysAssigned || 0) + plannedTask,
};
setNewWorkItem(updated);
}
}
let assigndata = {
building: forBuilding,
floor: forFloor,
@ -105,7 +97,7 @@ const WorkItem = ({
style={{ display: isModalOpen ? "block" : "none" }}
aria-hidden={!isModalOpen}
>
<AssignRoleModel assignData={assigndata} onClose={closeModal} setAssigned={refreshWorkItem} />
<AssignRoleModel assignData={assigndata} onClose={closeModal} />
</div>
)}
@ -149,7 +141,7 @@ const WorkItem = ({
</div>
)}
<tr key={NewWorkItem?.workItemId}>
<tr key={key}>
{/* Activity Name - always visible */}
<td className="text-start table-cell-small">
<i className="bx bx-right-arrow-alt"></i>
@ -199,16 +191,6 @@ const WorkItem = ({
: "NA"}
</td>
<td className="text-center d-none d-md-table-cell">
{hasWorkItem
? `${
NewWorkItem?.todaysAssigned ??
workItem?.todaysAssigned ??
"0"
}`
: "NA"}
</td>
{/* Progress Bar - always visible */}
<td className="text-center " style={{ width: "15%" }}>
<div className="progress p-0">

View File

@ -90,26 +90,18 @@ const Teams = ({ project }) => {
};
const handleEmpAlicationFormSubmit = (allocaionObj) => {
let items = allocaionObj.map((item) => {
return {
empID: item.empID,
jobRoleId: item.jobRoleId,
projectId: project.id,
status: true,
};
});
submitAllocations(items, 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";
};
let items = allocaionObj.map((item) => {
return {
empID: item.empID,
jobRoleId: item.jobRoleId,
projectId: project.id,
status: true,
};
});
submitAllocations( items ,true);
};
const getRole = (jobRoleId) => {
if (loading) return "Loading...";
@ -341,12 +333,8 @@ const Teams = ({ project }) => {
</tbody>
</table>
)}
{!employeeLodaing && filteredEmployees.length === 0 && (
<div className="text-center text-muted py-3">
{activeEmployee
? "No active employees assigned to the project"
: "No inactive employees assigned to the project"}
</div>
{(!employeeLodaing && employees.length == 0 )&& (
<span>No employees assigned to the project</span>
)}
</div>
</div>

View File

@ -25,6 +25,7 @@ const MasterModal = ({ modaldata, closeModal }) => {
const handleSelectedMasterDeleted = async () =>
{
const deleteFn = MasterRespository[modaldata.masterType];
if (!deleteFn) {
showToast(`No delete strategy defined for master type`,"error");
return false;
@ -139,7 +140,7 @@ const MasterModal = ({ modaldata, closeModal }) => {
{modaldata.modalType === "Contact Tag" && (
<CreateContactTag data={modaldata.item} onClose={closeModal} />
)}
{modaldata.modalType === "Edit-Contact Tag" && (
{modaldata.modalType === "Edit-Contact Tag" && (
<EditContactTag data={modaldata.item} onClose={closeModal} />
)}
</div>

View File

@ -59,28 +59,13 @@ const AttendancesEmployeeRecords = ({ employee }) => {
.sort(sortByName);
const group5 = data.filter((d) => d.activity === 5).sort(sortByName);
// const sortedFinalList = [
// ...group1,
// ...group2,
// ...group3,
// ...group4,
// ...group5,
// ];
const uniqueMap = new Map();
[...group1, ...group2, ...group3, ...group4, ...group5].forEach((rec) => {
const date = moment(rec.checkInTime || rec.checkOutTime).format("YYYY-MM-DD");
const key = `${rec.employeeId}-${date}`;
const existing = uniqueMap.get(key);
if (!existing || new Date(rec.checkInTime || rec.checkOutTime) > new Date(existing.checkInTime || existing.checkOutTime)) {
uniqueMap.set(key, rec);
}
});
const sortedFinalList = [...uniqueMap.values()].sort((a, b) =>
new Date(b.checkInTime || b.checkOutTime) - new Date(a.checkInTime || a.checkOutTime)
);
const sortedFinalList = [
...group1,
...group2,
...group3,
...group4,
...group5,
];
const currentDate = new Date().toLocaleDateString("en-CA");
const { currentPage, totalPages, currentItems, paginate } = usePagination(

View File

@ -398,7 +398,7 @@ const EmployeeList = () => {
</div>
</div>
</div>
<div className={`text-end mb-2 ${selectedProject ? 'd-none' : ''}`}>
<div className="text-end mb-2">
<label className="switch switch-primary">
<input
type="checkbox"

View File

@ -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`),