marco.pms.web/src/components/Employee/ManageEmployee.jsx

613 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import useMaster from "../../hooks/masterHook/useMaster";
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,
useUpdateEmployee,
} from "../../hooks/useEmployees";
import Label from "../common/Label";
import DatePicker from "../common/DatePicker";
import { defatEmployeeObj, employeeSchema } from "./EmployeeSchema";
import { useOrganizationsList } from "../../hooks/useOrganization";
import { ITEMS_PER_PAGE } from "../../utils/constants";
const ManageEmployee = ({ employeeId, onClosed, IsAllEmployee }) => {
const dispatch = useDispatch();
const { mutate: updateEmployee, isPending } = useUpdateEmployee();
const {
data: organzationList,
isLoading,
isError,
error: EempError,
} = useOrganizationsList(ITEMS_PER_PAGE, 1, true);
const {
employee,
error,
loading: empLoading,
refetch,
} = useEmployeeProfile(employeeId);
useEffect(() => {
dispatch(changeMaster("Job Role"));
}, [employeeId]);
const [disabledEmail, setDisabledEmail] = useState(false);
const { data: job_role, loading } = useMaster();
const [isloading, setLoading] = useState(false);
const navigation = useNavigate();
const [currentEmployee, setCurrentEmployee] = useState(null);
const [currentAddressLength, setCurrentAddressLength] = useState(0);
const [permanentAddressLength, setPermanentAddressLength] = useState(0);
useEffect(() => {
refetch();
}, []);
const {
register,
control,
handleSubmit,
watch,
formState: { errors },
reset,
getValues,
} = useForm({
resolver: zodResolver(employeeSchema),
defaultValues: defatEmployeeObj,
mode: "onChange",
});
const AadharNumberValue = watch("aadharNumber") || "";
const onSubmit = (data) => {
if (data.email === "") {
data.email = null;
}
const payload = { ...data, IsAllEmployee };
if (employeeId) {
payload.id = employeeId;
}
updateEmployee(payload, {
onSuccess: () => {
reset();
onClosed();
},
});
};
useEffect(() => {
if (!loading && !error && employee) {
setCurrentEmployee(employee);
}
}, [loading, error, employee]);
useEffect(() => {
reset(
currentEmployee
? {
id: currentEmployee.id || null,
firstName: currentEmployee.firstName || "",
middleName: currentEmployee.middleName || "",
lastName: currentEmployee.lastName || "",
email: currentEmployee.email || "",
currentAddress: currentEmployee.currentAddress || "",
birthDate: formatDate(currentEmployee.birthDate) || "",
joiningDate: formatDate(currentEmployee.joiningDate) || "",
emergencyPhoneNumber: currentEmployee.emergencyPhoneNumber || "",
emergencyContactPerson:
currentEmployee.emergencyContactPerson || "",
aadharNumber: currentEmployee.aadharNumber || "",
gender: currentEmployee.gender || "",
panNumber: currentEmployee.panNumber || "",
permanentAddress: currentEmployee.permanentAddress || "",
phoneNumber: currentEmployee.phoneNumber || "",
jobRoleId: currentEmployee.jobRoleId?.toString() || "",
organizationId: currentEmployee.organizationId || "",
hasApplicationAccess: currentEmployee.hasApplicationAccess || false,
}
: {}
);
setCurrentAddressLength(currentEmployee?.currentAddress?.length || 0);
setPermanentAddressLength(currentEmployee?.permanentAddress?.length || 0);
}, [currentEmployee, reset]);
const hasAccessAplication = watch("hasApplicationAccess");
return (
<>
<form onSubmit={handleSubmit(onSubmit)} className="p-sm-0 p-2">
<div className="text-center">
<p className="fs-5 fw-semibold">
{" "}
{employee ? "Update Employee" : "Create Employee"}
</p>{" "}
</div>
<div className="row mb-3">
<div className="col-sm-4">
<Label className="form-text text-start" required>
First Name
</Label>
<input
type="text"
name="firstName"
{...register("firstName", {
required: "First name is required",
pattern: {
value: /^[A-Za-z\s]+$/, // Only letters and spaces
message: "Only letters are allowed",
},
})}
className="form-control form-control-sm"
id="firstName"
placeholder="First Name"
onInput={(e) => {
e.target.value = e.target.value.replace(/[^A-Za-z\s]/g, "");
}}
/>
{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"
{...register("middleName", {
pattern: {
value: /^[A-Za-z\s]+$/, // Only letters and spaces
message: "Only letters are allowed",
},
})}
className="form-control form-control-sm"
id="middleName"
placeholder="Middle Name"
onInput={(e) => {
e.target.value = e.target.value.replace(/[^A-Za-z\s]/g, "");
}}
/>
{errors.middleName && (
<div
className="danger-text text-start "
style={{ fontSize: "12px" }}
>
{errors.middleName.message}
</div>
)}
</div>
<div className="col-sm-4">
<Label className="form-text text-start" required>
Last Name
</Label>
<input
type="text"
{...register("lastName", {
pattern: {
value: /^[A-Za-z\s]+$/, // Only letters and spaces
message: "Only letters are allowed",
},
})}
className="form-control form-control-sm"
id="lastName"
placeholder="Last Name"
onInput={(e) => {
e.target.value = e.target.value.replace(/[^A-Za-z\s]/g, "");
}}
/>
{errors.lastName && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.lastName.message}
</div>
)}
</div>
</div>
<div className="row mb-3">
<div className="col-sm-6">
<Label
htmlFor="email"
className="text-start form-text"
required={hasAccessAplication}
>
Email
</Label>
<input
type="email"
id="email"
{...register("email")}
className="form-control form-control-sm"
placeholder="example@domain.com"
maxLength={80}
aria-describedby="Email"
disabled={!!currentEmployee?.email}
/>
{errors.email && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.email.message}
</div>
)}
</div>
<div className="col-sm-6">
<Label className="form-text text-start" required>
Phone Number
</Label>
<input
type="text"
keyboardType="numeric"
id="phoneNumber"
{...register("phoneNumber")}
className="form-control form-control-sm"
placeholder="Phone Number"
inputMode="numeric"
maxLength={10}
/>
{errors.phoneNumber && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.phoneNumber.message}
</div>
)}
</div>
</div>
<div className="row mb-3"></div>
<div className="row mb-3">
<div className="col-sm-4">
<Label className="form-text text-start" required>
Gender
</Label>
<div className="input-group">
<select
className="form-select form-select-sm "
{...register("gender")}
id="gender"
aria-label=""
>
<option disabled value="">
Select Gender
</option>
<option value="Male">Male </option>
<option value="Female">Female</option>
<option value="Other">Other</option>
</select>
</div>
{errors.gender && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.gender.message}
</div>
)}
</div>
<div className="col-sm-4">
<Label className="form-text text-start" required>
Birth Date
</Label>
<div className="input-group">
<DatePicker
name="birthDate"
control={control} // from useForm()
placeholder="DD-MM-YYYY"
maxDate={new Date()} // restrict future dates (e.g., birth date must be today or past)
className="w-100"
/>
</div>
{errors.birthDate && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.birthDate.message}
</div>
)}
</div>
<div className="col-sm-4">
<Label className="form-text text-start" required>
Joining Date
</Label>
<div className="input-group">
<DatePicker
name="joiningDate"
control={control} // from useForm()
placeholder="DD-MM-YYYY"
// For joining date, we dont block future dates
className="w-100"
/>
</div>
{errors.joiningDate && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.joiningDate.message}
</div>
)}
</div>
</div>
<div className="row mb-3">
<div className="col-sm-6">
<Label className="form-text text-start" required>
Current Address
</Label>
<textarea
id="currentAddress"
className="form-control form-control-sm"
placeholder="Current Address"
aria-label="Current Address"
aria-describedby="basic-icon-default-message2"
{...register("currentAddress")}
maxLength={500}
onChange={(e) => {
setCurrentAddressLength(e.target.value.length);
register("currentAddress").onChange(e);
}}
></textarea>
<div className="text-end muted">
<small> {500 - currentAddressLength} characters left</small>
</div>
{errors.currentAddress && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.currentAddress.message}
</div>
)}
</div>
<div className="col-sm-6">
<Label className="form-text text-start" required>
Permanent Address
</Label>
<textarea
id="permanentAddress"
className="form-control form-control-sm"
placeholder="Permanent Address"
aria-label="Permanent Address"
aria-describedby="basic-icon-default-message2"
{...register("permanentAddress")}
maxLength={500}
onChange={(e) => {
setPermanentAddressLength(e.target.value.length);
register("permanentAddress").onChange(e);
}}
></textarea>
<div className="text-end muted">
<small>{500 - permanentAddressLength} characters left</small>
</div>
{errors.permanentAddress && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.permanentAddress.message}
</div>
)}
</div>
</div>
{/* -------------- */}
<div className="row mb-3">
<div className="col-sm-6">
<Label className="form-text text-start" required>
Organization
</Label>
<div className="input-group">
<select
className="form-select form-select-sm"
{...register("organizationId")}
id="organizationId"
aria-label=""
>
<option disabled value="">
Select Organization
</option>
{organzationList?.data
.sort((a, b) => a?.name?.localeCompare(b?.name))
.map((item) => (
<option value={item?.id} key={item?.id}>
{item?.name}
</option>
))}
</select>
</div>
{errors.organizationId && (
<div
className="danger-text text-start justify-content-center"
style={{ fontSize: "12px" }}
>
{errors.organizationId.message}
</div>
)}
</div>
<div className="col-sm-6 d-flex align-items-center mt-2">
<label className="form-check-label d-flex align-items-center">
<input
type="checkbox"
className="form-check-input me-2"
{...register("hasApplicationAccess")}
/>
Has Application Access ?
</label>
</div>
</div>
{/* --------------- */}
<div className="row mb-3">
{" "}
<div className="divider">
<div className="divider-text">Other Information</div>
</div>
</div>
<div className="row mb-3">
<div className="col-sm-4">
<Label className="form-text text-start" required>
Official Designation
</Label>
<div className="input-group">
<select
className="form-select form-select-sm"
{...register("jobRoleId")}
id="jobRoleId"
aria-label=""
>
<option disabled value="">
Select Role
</option>
{[...job_role]
.sort((a, b) => a?.name?.localeCompare(b.name))
.map((item) => (
<option value={item?.id} key={item.id}>
{item?.name}{" "}
</option>
))}
</select>
</div>
{errors.jobRoleId && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.jobRoleId.message}
</div>
)}
</div>
<div className="col-sm-4">
<Label className="form-text text-start" required>
Emergency Contact Person
</Label>
<input
type="text"
{...register("emergencyContactPerson")}
className="form-control form-control-sm"
id="emergencyContactPerson"
maxLength={50}
placeholder="Contact Person"
/>
{errors.emergencyContactPerson && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.emergencyContactPerson.message}
</div>
)}
</div>
<div className="col-sm-4">
<Label className="form-text text-start" required>
Emergency Phone Number
</Label>
<input
type="text"
{...register("emergencyPhoneNumber")}
className="form-control form-control-sm phone-mask"
id="emergencyPhoneNumber"
placeholder="Phone Number"
inputMode="numeric"
maxLength={10}
/>
{errors.emergencyPhoneNumber && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.emergencyPhoneNumber.message}
</div>
)}
</div>
</div>
<div className="row mb-3 d-none">
<div className="col-sm-6">
<div className="form-text text-start">AADHAR Number</div>
<input
type="text"
{...register("aadharNumber")}
className="form-control form-control-sm"
id="aadharNumber"
placeholder="AADHAR Number"
maxLength={12}
inputMode="numeric"
/>
{errors.aadharNumber && (
<div className="danger-text text-start">
{errors.aadharNumber.message}
</div>
)}
</div>
<div className="col-sm-6 d-none">
<div className="form-text text-start">PAN Number</div>
<input
type="text"
{...register("panNumber")}
className="form-control form-control-sm"
id="panNumber"
placeholder="PAN Number"
maxLength={10}
/>
{errors.panNumber && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.panNumber.message}
</div>
)}
</div>
</div>
<div className="row text-end">
<div className="col-sm-12">
<button
aria-label="manage employee"
type="reset"
className="btn btn-sm btn-label-secondary me-2"
disabled={isPending}
>
Clear
</button>
<button
aria-label="manage employee"
type="submit"
className="btn btn-sm btn-primary"
disabled={isPending}
>
{isPending ? "Please Wait..." : employeeId ? "Update" : "Create"}
</button>
</div>
</div>
</form>
</>
);
};
export default ManageEmployee;