Merge branch 'Issue_May_2W' of https://git.marcoaiot.com/admin/marco.pms.web into pramod_Bug#221
This commit is contained in:
commit
31286eff0a
@ -189,3 +189,7 @@
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
padding-left: 50px;
|
padding-left: 50px;
|
||||||
} */
|
} */
|
||||||
|
|
||||||
|
.small-text{
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
2
public/assets/vendor/css/core.css
vendored
2
public/assets/vendor/css/core.css
vendored
@ -29002,7 +29002,6 @@ li:not(:first-child) .dropdown-item,
|
|||||||
/* Only for menu example */
|
/* Only for menu example */
|
||||||
.menu-collapsed:not(:hover) {
|
.menu-collapsed:not(:hover) {
|
||||||
inline-size: var(--bs-menu-collapsed-width);
|
inline-size: var(--bs-menu-collapsed-width);
|
||||||
/* Custom for sneat only */
|
|
||||||
}
|
}
|
||||||
.menu-collapsed:not(:hover) .menu-inner > .menu-item {
|
.menu-collapsed:not(:hover) .menu-inner > .menu-item {
|
||||||
inline-size: var(--bs-menu-collapsed-width);
|
inline-size: var(--bs-menu-collapsed-width);
|
||||||
@ -29682,7 +29681,6 @@ li:not(:first-child) .dropdown-item,
|
|||||||
)
|
)
|
||||||
.layout-menu.menu-vertical {
|
.layout-menu.menu-vertical {
|
||||||
inline-size: var(--bs-menu-collapsed-width);
|
inline-size: var(--bs-menu-collapsed-width);
|
||||||
/* Custom for sneat only */
|
|
||||||
}
|
}
|
||||||
.layout-menu-collapsed:not(
|
.layout-menu-collapsed:not(
|
||||||
.layout-menu-hover,
|
.layout-menu-hover,
|
||||||
|
@ -186,7 +186,6 @@
|
|||||||
|
|
||||||
> .menu-item {
|
> .menu-item {
|
||||||
margin: $menu-item-spacer 0;
|
margin: $menu-item-spacer 0;
|
||||||
// Sneat menu-link spacing
|
|
||||||
.menu-link {
|
.menu-link {
|
||||||
margin: $menu-vertical-link-margin-y $menu-vertical-link-margin-x;
|
margin: $menu-vertical-link-margin-y $menu-vertical-link-margin-x;
|
||||||
}
|
}
|
||||||
@ -197,7 +196,6 @@
|
|||||||
.menu-block {
|
.menu-block {
|
||||||
padding: $menu-vertical-link-padding-y $menu-vertical-link-padding-x;
|
padding: $menu-vertical-link-padding-y $menu-vertical-link-padding-x;
|
||||||
}
|
}
|
||||||
// Sneat menu-header spacing
|
|
||||||
.menu-header {
|
.menu-header {
|
||||||
margin: $menu-vertical-header-margin-y 0 $menu-vertical-header-margin-y * 0.5 0;
|
margin: $menu-vertical-header-margin-y 0 $menu-vertical-header-margin-y * 0.5 0;
|
||||||
padding: $menu-vertical-link-padding-y $menu-vertical-link-padding-x * 2 $menu-vertical-link-padding-y
|
padding: $menu-vertical-link-padding-y $menu-vertical-link-padding-x * 2 $menu-vertical-link-padding-y
|
||||||
@ -276,7 +274,7 @@
|
|||||||
|
|
||||||
// Vertical Menu Collapsed
|
// Vertical Menu Collapsed
|
||||||
// *******************************************************************************
|
// *******************************************************************************
|
||||||
// ! Updated menu collapsed styles for sneat in this mixin
|
// ! Updated menu collapsed styles for in this mixin
|
||||||
@mixin layout-menu-collapsed() {
|
@mixin layout-menu-collapsed() {
|
||||||
width: $menu-collapsed-width;
|
width: $menu-collapsed-width;
|
||||||
|
|
||||||
@ -312,7 +310,6 @@
|
|||||||
top: 1.1875rem;
|
top: 1.1875rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Custom for sneat only
|
|
||||||
.menu-block {
|
.menu-block {
|
||||||
&::before {
|
&::before {
|
||||||
bottom: 0.75rem;
|
bottom: 0.75rem;
|
||||||
|
@ -27,15 +27,21 @@ const ReportTaskComments = ({ commentsData, closeModal }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const containerRef = useRef(null);
|
const containerRef = useRef(null);
|
||||||
|
const firstRender = useRef(true);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setComment(commentsData?.comments);
|
setComment(commentsData?.comments);
|
||||||
}, [commentsData]);
|
}, [commentsData]);
|
||||||
|
|
||||||
|
// Scroll logic: scroll to bottom when new comments are added
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (containerRef.current) {
|
if (!firstRender.current && containerRef.current) {
|
||||||
containerRef.current.scrollTop = containerRef.current.scrollHeight;
|
containerRef.current.scrollTop = containerRef.current.scrollHeight;
|
||||||
|
} else {
|
||||||
|
firstRender.current = false; // Mark the first render as complete
|
||||||
}
|
}
|
||||||
}, [comments]);
|
}, [comments]); // Run this when comments array is updated
|
||||||
|
|
||||||
const onSubmit = async (data) => {
|
const onSubmit = async (data) => {
|
||||||
let sendComment = {
|
let sendComment = {
|
||||||
@ -70,9 +76,10 @@ const ReportTaskComments = ({ commentsData, closeModal }) => {
|
|||||||
// closeModal();
|
// closeModal();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setloading(false);
|
setloading(false);
|
||||||
showToast(error.response.data?.message || "Something wrong", "error");
|
showToast(error.response.data?.message || "Something went wrong", "error");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="modal-dialog modal-lg modal-simple report-task-comments-modal mx-sm-auto mx-1"
|
className="modal-dialog modal-lg modal-simple report-task-comments-modal mx-sm-auto mx-1"
|
||||||
@ -86,7 +93,22 @@ const ReportTaskComments = ({ commentsData, closeModal }) => {
|
|||||||
onClick={closeModal}
|
onClick={closeModal}
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
></button>
|
></button>
|
||||||
<p className="fs-6 text-dark text-start">
|
<p className="fs-6 text-dark text-start m-0">Activity Summary</p>
|
||||||
|
<p className="small-text text-start my-2">
|
||||||
|
{comments && comments[0]?.comment}
|
||||||
|
</p>
|
||||||
|
<p className="fw-bold my-2 text-start">
|
||||||
|
Assigned By :
|
||||||
|
<span className=" ms-2">
|
||||||
|
{commentsData?.assignedBy.firstName +
|
||||||
|
" " +
|
||||||
|
commentsData?.assignedBy.lastName}
|
||||||
|
</span>{" "}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p className="fw-bold my-2 text-start">
|
||||||
|
Loaction :
|
||||||
|
<span className="fw-normal ms-2 text-start">
|
||||||
{`${commentsData?.workItem?.workArea?.floor?.building?.name}`}{" "}
|
{`${commentsData?.workItem?.workArea?.floor?.building?.name}`}{" "}
|
||||||
<i className="bx bx-chevron-right"></i>{" "}
|
<i className="bx bx-chevron-right"></i>{" "}
|
||||||
{`${commentsData?.workItem?.workArea?.floor?.floorName} `}{" "}
|
{`${commentsData?.workItem?.workArea?.floor?.floorName} `}{" "}
|
||||||
@ -94,53 +116,33 @@ const ReportTaskComments = ({ commentsData, closeModal }) => {
|
|||||||
{`${commentsData?.workItem?.workArea?.areaName}`}
|
{`${commentsData?.workItem?.workArea?.areaName}`}
|
||||||
<i className="bx bx-chevron-right"></i>
|
<i className="bx bx-chevron-right"></i>
|
||||||
{` ${commentsData?.workItem?.activityMaster?.activityName}`}
|
{` ${commentsData?.workItem?.activityMaster?.activityName}`}
|
||||||
</p>
|
|
||||||
|
|
||||||
<ul
|
|
||||||
className="list-grouph px-0 mx-0 overflow-auto"
|
|
||||||
ref={containerRef}
|
|
||||||
style={{ maxHeight: "400px" }}
|
|
||||||
>
|
|
||||||
{comments &&
|
|
||||||
comments?.map((data) => {
|
|
||||||
const fullName = `${data?.employee?.firstName} ${data?.employee?.lastName}`;
|
|
||||||
const bgClass = getBgClassFromHash(fullName);
|
|
||||||
return (
|
|
||||||
<li
|
|
||||||
className={`list-group-item list-group-item-action my-2 p-1`}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`li-wrapper d-flex justify-content-start align-items-start my-0 `}
|
|
||||||
>
|
|
||||||
<div className="avatar avatar-xs me-1">
|
|
||||||
<span
|
|
||||||
className={`avatar-initial rounded-circle bg-label-primary}`}
|
|
||||||
>
|
|
||||||
{`${data?.employee?.firstName?.slice(
|
|
||||||
0,
|
|
||||||
1
|
|
||||||
)} ${data?.employee?.lastName?.slice(0, 1)}`}
|
|
||||||
</span>
|
</span>
|
||||||
|
</p>
|
||||||
|
<p className="fw-bold my-2 text-start">
|
||||||
|
Planned Work: {commentsData?.plannedTask}
|
||||||
|
</p>
|
||||||
|
<p className="fw-bold my-2 text-start">
|
||||||
|
{" "}
|
||||||
|
Completed Work : {commentsData?.completedTask}
|
||||||
|
</p>
|
||||||
|
<div className="d-flex align-items-center flex-wrap">
|
||||||
|
<p className="fw-bold text-start m-0 me-1">Team:</p>
|
||||||
|
<div className="d-flex flex-wrap align-items-center gap-2">
|
||||||
|
{commentsData?.teamMembers?.map((member, idx) => (
|
||||||
|
<span key={idx} className="d-flex align-items-center">
|
||||||
|
<Avatar
|
||||||
|
firstName={member?.firstName}
|
||||||
|
lastName={member?.lastName}
|
||||||
|
size="xs"
|
||||||
|
/>
|
||||||
|
{member?.firstName + " " + member?.lastName}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={` text-start py-0 `}>
|
<form onSubmit={handleSubmit(onSubmit)} className="text-start">
|
||||||
<p className={`mb-0 text-${bgClass}`}>{fullName}</p>
|
<label className="fw-bold text-start my-1">Add comment :</label>
|
||||||
<p
|
|
||||||
className=" text-muted m-0 "
|
|
||||||
style={{ fontSize: "10px" }}
|
|
||||||
>
|
|
||||||
{moment.utc(data?.commentDate).local().fromNow()}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p className={`ms-6 text-start mb-0 text-body`}>
|
|
||||||
{data?.comment}
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
|
||||||
<textarea
|
<textarea
|
||||||
{...register("comment")}
|
{...register("comment")}
|
||||||
className="form-control"
|
className="form-control"
|
||||||
@ -165,6 +167,47 @@ const ReportTaskComments = ({ commentsData, closeModal }) => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<ul
|
||||||
|
className="list-group px-0 mx-0 overflow-auto border-0"
|
||||||
|
// ref={containerRef} // auto scroll according data
|
||||||
|
style={{ maxHeight: "200px" }}
|
||||||
|
>
|
||||||
|
{comments &&
|
||||||
|
comments
|
||||||
|
?.slice()
|
||||||
|
.reverse()
|
||||||
|
.map((data, idx) => {
|
||||||
|
const fullName = `${data?.employee?.firstName} ${data?.employee?.lastName}`;
|
||||||
|
const bgClass = getBgClassFromHash(fullName);
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
className={`list-group-item list-group-item-action border-none my-1 p-1`}
|
||||||
|
key={idx}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`li-wrapper d-flex justify-content-start align-items-start my-0`}
|
||||||
|
>
|
||||||
|
<div className="avatar avatar-xs me-1">
|
||||||
|
<span
|
||||||
|
className={`avatar-initial rounded-circle bg-label-primary`}
|
||||||
|
>
|
||||||
|
{`${data?.employee?.firstName?.slice(0, 1)} ${data?.employee?.lastName?.slice(0, 1)}`}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={`text-start py-0`}>
|
||||||
|
<p className={`mb-0 text-${bgClass}`}>{fullName}</p>
|
||||||
|
<p className="text-muted m-0" style={{ fontSize: "10px" }}>
|
||||||
|
{moment.utc(data?.commentDate).local().fromNow()}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className={`ms-6 text-start mb-0 text-body`}>{data?.comment}</p>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,7 +10,7 @@ import { changeMaster } from "../../slices/localVariablesSlice";
|
|||||||
import { Link, useNavigate, useParams } from "react-router-dom";
|
import { Link, useNavigate, useParams } from "react-router-dom";
|
||||||
import { formatDate } from "../../utils/dateUtils";
|
import { formatDate } from "../../utils/dateUtils";
|
||||||
import { useEmployeeProfile } from "../../hooks/useEmployees";
|
import { useEmployeeProfile } from "../../hooks/useEmployees";
|
||||||
import { clearCacheKey, getCachedData } from "../../slices/apiDataManager";
|
import { cacheData, clearCacheKey, getCachedData } from "../../slices/apiDataManager";
|
||||||
import { clearApiCacheKey } from "../../slices/apiCacheSlice";
|
import { clearApiCacheKey } from "../../slices/apiCacheSlice";
|
||||||
|
|
||||||
const mobileNumberRegex = /^[0-9]\d{9}$/;
|
const mobileNumberRegex = /^[0-9]\d{9}$/;
|
||||||
@ -166,6 +166,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
|
|||||||
}
|
}
|
||||||
EmployeeRepository.manageEmployee(data)
|
EmployeeRepository.manageEmployee(data)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
cacheData("employeeProfileInfo", data);
|
||||||
showToast(
|
showToast(
|
||||||
`Employee details ${
|
`Employee details ${
|
||||||
data.id == null ? "created" : "updated"
|
data.id == null ? "created" : "updated"
|
||||||
@ -177,6 +178,7 @@ const ManageEmployee = ({ employeeId, onClosed }) => {
|
|||||||
clearCacheKey("allInactiveEmployeeList");
|
clearCacheKey("allInactiveEmployeeList");
|
||||||
clearCacheKey("employeeProfile");
|
clearCacheKey("employeeProfile");
|
||||||
|
|
||||||
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
reset();
|
reset();
|
||||||
// navigation("/employees");
|
// navigation("/employees");
|
||||||
|
@ -58,7 +58,7 @@ const LayoutMenu = () => {
|
|||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
<span className="app-brand-text demo menu-text fw-bolder ms-2">Sneat</span>
|
<span className="app-brand-text demo menu-text fw-bolder ms-2">Marco</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="javascript:void(0);" className="layout-menu-toggle menu-link text-large ms-auto d-block d-xl-none">
|
<a href="javascript:void(0);" className="layout-menu-toggle menu-link text-large ms-auto d-block d-xl-none">
|
||||||
|
@ -3,7 +3,7 @@ import { useForm } from "react-hook-form";
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { useActivitiesMaster } from "../../../hooks/masterHook/useMaster";
|
import { useActivitiesMaster, useWorkCategoriesMaster } from "../../../hooks/masterHook/useMaster";
|
||||||
import { useProjectDetails } from "../../../hooks/useProjects";
|
import { useProjectDetails } from "../../../hooks/useProjects";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import ProjectRepository from "../../../repositories/ProjectRepository";
|
import ProjectRepository from "../../../repositories/ProjectRepository";
|
||||||
@ -18,6 +18,7 @@ import showToast from "../../../services/toastService";
|
|||||||
const taskSchema = z
|
const taskSchema = z
|
||||||
.object({
|
.object({
|
||||||
activityID: z.string().min(1, "Activity is required"),
|
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"),
|
plannedWork: z.number().min(1, "Planned Work must be greater than 0"),
|
||||||
completedWork: z.number().min(0, "Completed Work must be greater than 0"),
|
completedWork: z.number().min(0, "Completed Work must be greater than 0"),
|
||||||
})
|
})
|
||||||
@ -43,17 +44,21 @@ const EditActivityModal = ({
|
|||||||
);
|
);
|
||||||
const defaultModel = {
|
const defaultModel = {
|
||||||
activityID: 0,
|
activityID: 0,
|
||||||
|
workCategoryId: 0,
|
||||||
plannedWork: 0,
|
plannedWork: 0,
|
||||||
completedWork: 0,
|
completedWork: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const { projects_Details, refetch } = useProjectDetails(selectedProject);
|
const { projects_Details, refetch } = useProjectDetails(selectedProject);
|
||||||
const [ActivityUnit,setActivityUnit]= useState("")
|
const [ActivityUnit, setActivityUnit] = useState("");
|
||||||
const { activities, loading, error } = useActivitiesMaster();
|
const { activities, loading, error } = useActivitiesMaster();
|
||||||
|
const { categories, categoryLoading, categoryError } =
|
||||||
|
useWorkCategoriesMaster();
|
||||||
const [formData, setFormData] = useState(defaultModel);
|
const [formData, setFormData] = useState(defaultModel);
|
||||||
const [selectedActivity, setSelectedActivity] = useState(null);
|
const [selectedActivity, setSelectedActivity] = useState(null);
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const [activityData, setActivityData] = useState([]);
|
const [activityData, setActivityData] = useState([]);
|
||||||
|
const [categoryData, setCategoryData] = useState([]);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -70,12 +75,19 @@ const EditActivityModal = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const handleActivityChange = (e) => {
|
const handleActivityChange = (e) => {
|
||||||
const selectedId = Number(e.target.value);
|
const selectedId = String(e.target.value);
|
||||||
const selected = activityData.find((a) => a.id === selectedId);
|
const selected = activityData.find((a) => a.id === selectedId);
|
||||||
setSelectedActivity(selected || null);
|
setSelectedActivity(selected || null);
|
||||||
setValue("activityID", selectedId);
|
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 onSubmitForm = async ( data ) =>
|
||||||
{
|
{
|
||||||
setIsSubmitting(true)
|
setIsSubmitting(true)
|
||||||
@ -167,6 +179,8 @@ const EditActivityModal = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reset({
|
reset({
|
||||||
activityID: workItem?.workItem?.activityId || workItem?.activityId || 0,
|
activityID: workItem?.workItem?.activityId || workItem?.activityId || 0,
|
||||||
|
workCategoryId:
|
||||||
|
workItem?.workItem?.workCategoryId || workItem?.workCategoryId || 0,
|
||||||
plannedWork:
|
plannedWork:
|
||||||
workItem?.workItem?.plannedWork || workItem?.plannedWork || 0,
|
workItem?.workItem?.plannedWork || workItem?.plannedWork || 0,
|
||||||
completedWork:
|
completedWork:
|
||||||
@ -180,11 +194,10 @@ const EditActivityModal = ({
|
|||||||
if (ISselectedActivity) {
|
if (ISselectedActivity) {
|
||||||
const selected = activities.find((a) => a.id === ISselectedActivity);
|
const selected = activities.find((a) => a.id === ISselectedActivity);
|
||||||
setSelectedActivity(selected || null);
|
setSelectedActivity(selected || null);
|
||||||
setActivityUnit(selected?.unitOfMeasurement)
|
setActivityUnit(selected?.unitOfMeasurement);
|
||||||
}
|
}
|
||||||
}, [ISselectedActivity, activities]);
|
}, [ISselectedActivity, activities]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="modal-dialog modal-lg modal-simple modal-edit-user">
|
<div className="modal-dialog modal-lg modal-simple modal-edit-user">
|
||||||
<div className="modal-content">
|
<div className="modal-content">
|
||||||
@ -279,6 +292,45 @@ const EditActivityModal = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</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 */}
|
{/* Planned Work */}
|
||||||
{/* {ISselectedActivity && ( */}
|
{/* {ISselectedActivity && ( */}
|
||||||
<div className="col-5 col-md-5">
|
<div className="col-5 col-md-5">
|
||||||
|
@ -2,13 +2,17 @@ import React, { useState, useEffect } from "react";
|
|||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import {useActivitiesMaster} from "../../../hooks/masterHook/useMaster";
|
import {
|
||||||
|
useActivitiesMaster,
|
||||||
|
useWorkCategoriesMaster,
|
||||||
|
} from "../../../hooks/masterHook/useMaster";
|
||||||
|
|
||||||
const taskSchema = z.object({
|
const taskSchema = z.object({
|
||||||
buildingID: z.string().min(1, "Building is required"),
|
buildingID: z.string().min(1, "Building is required"),
|
||||||
floorId: z.string().min(1, "Floor is required"),
|
floorId: z.string().min(1, "Floor is required"),
|
||||||
workAreaId: z.string().min(1, "Work Area is required"),
|
workAreaId: z.string().min(1, "Work Area is required"),
|
||||||
activityID: z.string().min(1, "Activity is required"),
|
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"),
|
plannedWork: z.number().min(1, "Planned Work must be greater than 0"),
|
||||||
completedWork: z.number().min(0, "Completed Work must be greater than 0"),
|
completedWork: z.number().min(0, "Completed Work must be greater than 0"),
|
||||||
});
|
});
|
||||||
@ -19,6 +23,7 @@ const defaultModel = {
|
|||||||
floorId: "0",
|
floorId: "0",
|
||||||
workAreaId: "0",
|
workAreaId: "0",
|
||||||
activityID: null,
|
activityID: null,
|
||||||
|
workCategoryId: "",
|
||||||
plannedWork: 0,
|
plannedWork: 0,
|
||||||
completedWork: 0,
|
completedWork: 0,
|
||||||
};
|
};
|
||||||
@ -30,15 +35,18 @@ const TaskModel = ({
|
|||||||
onClearComplete,
|
onClearComplete,
|
||||||
onClose,
|
onClose,
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const [formData, setFormData] = useState(defaultModel);
|
const [formData, setFormData] = useState(defaultModel);
|
||||||
const [selectedBuilding, setSelectedBuilding] = useState(null);
|
const [selectedBuilding, setSelectedBuilding] = useState(null);
|
||||||
const [selectedFloor, setSelectedFloor] = useState(null);
|
const [selectedFloor, setSelectedFloor] = useState(null);
|
||||||
const [selectedWorkArea, setSelectedWorkArea] = useState(null);
|
const [selectedWorkArea, setSelectedWorkArea] = useState(null);
|
||||||
const [selectedActivity, setSelectedActivity] = useState(null);
|
const [selectedActivity, setSelectedActivity] = useState(null);
|
||||||
|
const [selectedCategory, setSelectedCategory] = useState(null);
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const [activityData, setActivityData] = useState([]);
|
const [activityData, setActivityData] = useState([]);
|
||||||
|
const [categoryData, setCategoryData] = useState([]);
|
||||||
const { activities, loading, error } = useActivitiesMaster();
|
const { activities, loading, error } = useActivitiesMaster();
|
||||||
|
const { categories, categoryLoading, categoryError } =
|
||||||
|
useWorkCategoriesMaster();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@ -79,6 +87,7 @@ const TaskModel = ({
|
|||||||
floorId: value,
|
floorId: value,
|
||||||
workAreaId: 0,
|
workAreaId: 0,
|
||||||
activityID: 0,
|
activityID: 0,
|
||||||
|
workCategoryId: categoryData?.[0]?.id?.toString() ?? "",
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -104,13 +113,24 @@ const TaskModel = ({
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSubmitForm = async ( data ) =>
|
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) => {
|
||||||
|
console.log(data);
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
await onSubmit(data);
|
await onSubmit(data);
|
||||||
setValue("plannedWork", 0);
|
setValue("plannedWork", 0);
|
||||||
setValue("completedWork", 0);
|
setValue("completedWork", 0);
|
||||||
setValue("activityID",0)
|
setValue("activityID", 0);
|
||||||
|
setValue("workCategoryId", categoryData?.[0]?.id?.toString() ?? "");
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -120,15 +140,32 @@ const TaskModel = ({
|
|||||||
setSelectedFloor(null);
|
setSelectedFloor(null);
|
||||||
setSelectedWorkArea(null);
|
setSelectedWorkArea(null);
|
||||||
setSelectedActivity(null);
|
setSelectedActivity(null);
|
||||||
|
setSelectedCategory(categoryData?.[0]?.id?.toString() ?? "");
|
||||||
reset(defaultModel);
|
reset(defaultModel);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && Array.isArray(activities) && activities.length > 0) {
|
if (!loading && Array.isArray(activities) && activities.length > 0) {
|
||||||
|
|
||||||
setActivityData(activities);
|
setActivityData(activities);
|
||||||
}
|
}
|
||||||
}, [activities, loading]);
|
}, [activities, loading]);
|
||||||
|
|
||||||
|
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])
|
||||||
|
}
|
||||||
|
}, [categories, categoryLoading]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="modal-dialog modal-lg modal-simple modal-edit-user">
|
<div className="modal-dialog modal-lg modal-simple modal-edit-user">
|
||||||
<div className="modal-content">
|
<div className="modal-content">
|
||||||
@ -247,9 +284,7 @@ const TaskModel = ({
|
|||||||
|
|
||||||
{selectedWorkArea && (
|
{selectedWorkArea && (
|
||||||
<div className="col-12 col-md-12">
|
<div className="col-12 col-md-12">
|
||||||
<label className="form-label" >
|
<label className="form-label">Select Activity</label>
|
||||||
Select Activity
|
|
||||||
</label>
|
|
||||||
<select
|
<select
|
||||||
id="activityID"
|
id="activityID"
|
||||||
className="form-select form-select-sm"
|
className="form-select form-select-sm"
|
||||||
@ -257,28 +292,25 @@ const TaskModel = ({
|
|||||||
onChange={handleActivityChange}
|
onChange={handleActivityChange}
|
||||||
>
|
>
|
||||||
<option value="0">Select Activity</option>
|
<option value="0">Select Activity</option>
|
||||||
{activityData && activityData.length > 0 && (
|
{activityData &&
|
||||||
|
activityData.length > 0 &&
|
||||||
activityData
|
activityData
|
||||||
?.slice()
|
?.slice()
|
||||||
?.sort( ( a, b ) =>
|
?.sort((a, b) => {
|
||||||
{
|
|
||||||
const nameA = a?.activityName || "";
|
const nameA = a?.activityName || "";
|
||||||
const nameB = b?.activityName || "";
|
const nameB = b?.activityName || "";
|
||||||
return nameA.localeCompare(nameB);
|
return nameA.localeCompare(nameB);
|
||||||
})
|
})
|
||||||
?.map((activity) => (
|
?.map((activity) => (
|
||||||
<option key={activity.id} value={activity.id}>
|
<option key={activity.id} value={activity.id}>
|
||||||
{
|
{activity.activityName ||
|
||||||
activity.activityName ||
|
|
||||||
|
|
||||||
`Unnamed (id: ${activity.id})`}
|
`Unnamed (id: ${activity.id})`}
|
||||||
</option>
|
</option>
|
||||||
) )
|
))}
|
||||||
) }
|
{!loading && activities.length === 0 && (
|
||||||
{(!loading && activities.length === 0 )&& (
|
|
||||||
<option disabled>No activities available</option>
|
<option disabled>No activities available</option>
|
||||||
)}
|
)}
|
||||||
{loading && ( <option disabled>Loading...</option>)}
|
{loading && <option disabled>Loading...</option>}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
{errors.activityID && (
|
{errors.activityID && (
|
||||||
@ -287,7 +319,38 @@ const TaskModel = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{selectedActivity && (
|
{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">
|
<div className="col-5 col-md-5">
|
||||||
<label className="form-label" htmlFor="plannedWork">
|
<label className="form-label" htmlFor="plannedWork">
|
||||||
{formData.id !== "0" ? "Modify " : "Enter "} Planned Work
|
{formData.id !== "0" ? "Modify " : "Enter "} Planned Work
|
||||||
@ -304,7 +367,7 @@ const TaskModel = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{selectedActivity && (
|
{selectedActivity && selectedCategory && (
|
||||||
<div className="col-5 col-md-5">
|
<div className="col-5 col-md-5">
|
||||||
<label className="form-label" htmlFor="completedWork">
|
<label className="form-label" htmlFor="completedWork">
|
||||||
{formData.id !== "0" ? "Modify " : "Enter "} Completed Work
|
{formData.id !== "0" ? "Modify " : "Enter "} Completed Work
|
||||||
@ -324,7 +387,7 @@ const TaskModel = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Unit */}
|
{/* Unit */}
|
||||||
{selectedActivity && (
|
{selectedActivity && selectedCategory && (
|
||||||
<div className="col-2 col-md-2">
|
<div className="col-2 col-md-2">
|
||||||
<label className="form-label" htmlFor="unit">
|
<label className="form-label" htmlFor="unit">
|
||||||
Unit
|
Unit
|
||||||
@ -340,9 +403,7 @@ const TaskModel = ({
|
|||||||
|
|
||||||
<div className="col-12 text-center">
|
<div className="col-12 text-center">
|
||||||
<button type="submit" className="btn btn-sm btn-primary me-3">
|
<button type="submit" className="btn btn-sm btn-primary me-3">
|
||||||
{isSubmitting
|
{isSubmitting ? "Please Wait.." : "Add Task"}
|
||||||
? "Please Wait.."
|
|
||||||
: "Add Task"}
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -121,6 +121,9 @@ const WorkArea = ({ workArea, floor, forBuilding }) => {
|
|||||||
<th className="infra-activity-table-header d-sm-none d-sm-table-cell">
|
<th className="infra-activity-table-header d-sm-none d-sm-table-cell">
|
||||||
Status
|
Status
|
||||||
</th>
|
</th>
|
||||||
|
<th className="infra-activity-table-header-first">
|
||||||
|
Category
|
||||||
|
</th>
|
||||||
{/* for greather than mobile view ************* */}
|
{/* for greather than mobile view ************* */}
|
||||||
<th className="infra-activity-table-header d-none d-md-table-cell">
|
<th className="infra-activity-table-header d-none d-md-table-cell">
|
||||||
Planned
|
Planned
|
||||||
|
@ -161,6 +161,15 @@ const WorkItem = ( {
|
|||||||
? NewWorkItem?.workItem?.plannedWork || workItem?.plannedWork
|
? NewWorkItem?.workItem?.plannedWork || workItem?.plannedWork
|
||||||
: "NA"}
|
: "NA"}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
<td className="text-start table-cell-small">
|
||||||
|
<span className="fw-light">
|
||||||
|
{hasWorkItem
|
||||||
|
? NewWorkItem?.workItem?.workCategoryMaster?.name ||
|
||||||
|
workItem.workCategoryMaster?.name || "NA"
|
||||||
|
: "NA"}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
{/* for greather than mobile view ************* */}
|
{/* for greather than mobile view ************* */}
|
||||||
<td className="text-center d-none d-md-table-cell">
|
<td className="text-center d-none d-md-table-cell">
|
||||||
{hasWorkItem
|
{hasWorkItem
|
||||||
|
@ -117,3 +117,36 @@ export const useActivitiesMaster = () =>
|
|||||||
|
|
||||||
return {activities,loading,error}
|
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}
|
||||||
|
}
|
@ -15,11 +15,8 @@ export const AuthWrapper = ({ children }) => {
|
|||||||
className="app-brand-link gap-2"
|
className="app-brand-link gap-2"
|
||||||
>
|
>
|
||||||
<span className="app-brand-logo demo">
|
<span className="app-brand-logo demo">
|
||||||
<img src="/img/brand/marco.png" alt="sneat-logo" />
|
<img src="/img/brand/marco.png" alt="marco-logo" />
|
||||||
</span>
|
</span>
|
||||||
{/* <span className="app-brand-text demo text-body fw-bold">
|
|
||||||
Sneat
|
|
||||||
</span> */}
|
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
{children}
|
{children}
|
||||||
|
@ -16,6 +16,7 @@ import { ComingSoonPage } from "../Misc/ComingSoonPage";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import Avatar from "../../components/common/Avatar";
|
import Avatar from "../../components/common/Avatar";
|
||||||
import AttendancesEmployeeRecords from "./AttendancesEmployeeRecords";
|
import AttendancesEmployeeRecords from "./AttendancesEmployeeRecords";
|
||||||
|
import ManageEmployee from "../../components/Employee/ManageEmployee";
|
||||||
const EmployeeProfile = () => {
|
const EmployeeProfile = () => {
|
||||||
const projectID = useSelector((store) => store.localVariables.projectId);
|
const projectID = useSelector((store) => store.localVariables.projectId);
|
||||||
const { employeeId } = useParams();
|
const { employeeId } = useParams();
|
||||||
@ -26,11 +27,18 @@ const EmployeeProfile = () => {
|
|||||||
const tab = SearchParams.get("for");
|
const tab = SearchParams.get("for");
|
||||||
const [activePill, setActivePill] = useState(tab);
|
const [activePill, setActivePill] = useState(tab);
|
||||||
const [currentEmployee, setCurrentEmployee] = useState();
|
const [currentEmployee, setCurrentEmployee] = useState();
|
||||||
|
const [showModal, setShowModal] = useState(false);
|
||||||
|
|
||||||
const handlePillClick = (pillKey) => {
|
const handlePillClick = (pillKey) => {
|
||||||
setActivePill(pillKey);
|
setActivePill(pillKey);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
setShowModal(false);
|
||||||
|
fetchEmployeeProfile(employeeId);
|
||||||
|
};
|
||||||
|
const handleShow = () => setShowModal(true);
|
||||||
|
|
||||||
const fetchEmployeeProfile = async (employeeID) => {
|
const fetchEmployeeProfile = async (employeeID) => {
|
||||||
try {
|
try {
|
||||||
const resp = await EmployeeRepository.getEmployeeProfile(employeeID);
|
const resp = await EmployeeRepository.getEmployeeProfile(employeeID);
|
||||||
@ -89,6 +97,26 @@ const EmployeeProfile = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
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>)}
|
||||||
|
|
||||||
<div className="container-xxl flex-grow-1 container-p-y">
|
<div className="container-xxl flex-grow-1 container-p-y">
|
||||||
<Breadcrumb
|
<Breadcrumb
|
||||||
data={[
|
data={[
|
||||||
@ -212,7 +240,7 @@ const EmployeeProfile = () => {
|
|||||||
<button
|
<button
|
||||||
className="btn btn-primary btn-block"
|
className="btn btn-primary btn-block"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
navigate(`/employee/manage/${currentEmployee?.id}`)
|
handleShow()
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Edit Profile
|
Edit Profile
|
||||||
@ -238,6 +266,8 @@ const EmployeeProfile = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user