added validation for aadhar, pan Number, email, phone number and address length.

This commit is contained in:
Pramod Mahajan 2025-04-03 00:22:24 +05:30
parent 21ca99455b
commit e6a687c8e2
4 changed files with 33 additions and 139 deletions

View File

@ -13,7 +13,7 @@ import { useEmployeeProfile } from "../../hooks/useEmployees";
import { clearCacheKey, getCachedData } from "../../slices/apiDataManager"; import { clearCacheKey, getCachedData } from "../../slices/apiDataManager";
import {clearApiCacheKey} from "../../slices/apiCacheSlice"; import {clearApiCacheKey} from "../../slices/apiCacheSlice";
const mobileNumberRegex = /^(?:\d{10}|\d{3}[-\s]?\d{3}[-\s]?\d{4})$/; const mobileNumberRegex = /^[7-9]\d{9}$/;
const ManageEmployee = () => { const ManageEmployee = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -41,9 +41,17 @@ const ManageEmployee = () => {
Email: z.string().optional(), Email: z.string().optional(),
CurrentAddress: z CurrentAddress: z
.string() .string()
.min(1, { message: "Current Address is required" }), .min(1, { message: "Current Address is required" }).max(150, { message: "Address cannot exceed 150 characters" }),
BirthDate: z.string().min(1, { message: "Birth Date is required" }), BirthDate: z.string().min(1, { message: "Birth Date is required" }).refine((date, ctx) => {
JoiningDate: z.string().min(1, { message: "Joining Date is required" }), return new Date(date) <= new Date();
}, {
message: "Birth date cannot be in the future",
}),
JoiningDate: z.string().min(1, { message: "Joining Date is required" }).refine((date, ctx) => {
return new Date(date) <= new Date();
}, {
message: "Joining date cannot be in the future",
}),
EmergencyPhoneNumber: z EmergencyPhoneNumber: z
.string() .string()
.min(1, { message: "Phone Number is required" }) .min(1, { message: "Phone Number is required" })
@ -52,7 +60,7 @@ const ManageEmployee = () => {
.string() .string()
.min(1, { message: "Emergency Contact Person is required" }), .min(1, { message: "Emergency Contact Person is required" }),
AadharNumber: z.string() AadharNumber: z.string()
.regex(/^\d{12}$/, "Aadhar card must be exactly 12 digits long") // Regex to ensure only 12 digits .regex(/^\d{12}$/, "Aadhar card must be exactly 12 digits long")
.nonempty("Aadhar card is required"), .nonempty("Aadhar card is required"),
Gender: z Gender: z
.string() .string()
@ -60,61 +68,22 @@ const ManageEmployee = () => {
.refine((val) => val !== "Select Gender", { .refine((val) => val !== "Select Gender", {
message: "Please select a gender", message: "Please select a gender",
}), }),
PanNumber: z.string().optional(), PanNumber: z
.string()
.optional()
.refine((val) => !val || /^[A-Z]{5}[0-9]{4}[A-Z]{1}$/.test(val), {
message: "Invalid PAN number",
}),
PeramnentAddress: z PeramnentAddress: z
.string() .string()
.min(1, { message: "Permanent Address is required" }), .min(1, { message: "Permanent Address is required" }).max(150, { message: "Address cannot exceed 150 characters" }),
PhoneNumber: z PhoneNumber: z
.string() .string()
.min(1, { message: "Phone Number is required" }) .min(1, { message: "Phone Number is required" })
.regex(mobileNumberRegex, { message: "Invalid phone number " }), .regex(mobileNumberRegex, { message: "Invalid phone number " }),
JobRoleId: z.string().min(1, { message: "Role is required" }), JobRoleId: z.string().min(1, { message: "Role is required" }),
// Documents: z
// .array(z.unknown())
// // .optional()
// .default([])
// .refine((value) => value.length > 0, {
// message: "Documents is required",
// })
// .refine((value) => {
// return value.every((file) => file.size <= 5000000); //this required if want
// }, "Each file must be less than 5MB")
// .refine((value) => {
// if (!value || value.length === 0) return true;
// return value.every((file) =>
// ["application/pdf", "application/msword", "image/jpeg", "image/png"].includes(file.type)
// );
// }, "Only PDF, Word, and image files are allowed"),
// Photo: z
// .unknown()
// .default(null)
// .refine((value) => value !== undefined && value !== null, {
// message: "Photo is required",
// })
// .refine((value) => value?.size <= 5000000, { //this required if want
// message: "The file must be less than 5MB", //size
// })
// .refine((value) => {
// if (!value || value.length === 0) return true;
// return value && ["image/jpeg", "image/png", "image/jpg"].includes(value?.type);
// }, { message: "Only JPEG and PNG images are allowed" }),
}) })
.superRefine((data, ctx) => {
if (!data.AadharNumber && !data.PanNumber) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Either Aadhar Number or Pan Number is required.",
path: ["AadharNumber"], // Add error to AadharNumber
});
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "Either Aadhar Number or Pan Number is required.",
path: ["PanNumber"], // Add error to PanNumber
});
}
});
const { const {
register, register,
@ -142,8 +111,6 @@ const ManageEmployee = () => {
PeramnentAddress: currentEmployee?.peramnentAddress || "", PeramnentAddress: currentEmployee?.peramnentAddress || "",
PhoneNumber: currentEmployee?.phoneNumber || "", PhoneNumber: currentEmployee?.phoneNumber || "",
JobRoleId: currentEmployee?.jobRoleId || "", JobRoleId: currentEmployee?.jobRoleId || "",
// Documents: currentEmployee?.documents || [],
// Photo: currentEmployee?.photo || null
}, },
}); });
@ -175,13 +142,11 @@ const ManageEmployee = () => {
EmployeeRepository.manageEmployee(formDataToSend) EmployeeRepository.manageEmployee(formDataToSend)
.then( ( response ) => .then( ( response ) =>
{ {
showToast("Employee details updated successfully.", "success" ); showToast("Employee details updated successfully.", "success" );
clearCacheKey("employeeListByProject") clearCacheKey("employeeListByProject")
clearCacheKey("allEmployeeList") clearCacheKey( "allEmployeeList" )
navigation("/employees");
setLoading(false); setLoading(false);
navigation("/employees");
}) })
.catch((error) => { .catch((error) => {
showToast(error.message, "error"); showToast(error.message, "error");
@ -218,12 +183,9 @@ const ManageEmployee = () => {
PeramnentAddress: currentEmployee.peramnentAddress || "", PeramnentAddress: currentEmployee.peramnentAddress || "",
PhoneNumber: currentEmployee.phoneNumber || "", PhoneNumber: currentEmployee.phoneNumber || "",
JobRoleId: currentEmployee.jobRoleId?.toString() || "", JobRoleId: currentEmployee.jobRoleId?.toString() || "",
// Documents: currentEmployee.documents || [],
// Photo: currentEmployee.photo || null,
} }
: {} // Empty object resets the form : {} // Empty object resets the form
); );
// if(currentEmployee && currentEmployee.email) setDisabledEmail(true)
}, [currentEmployee, reset]); }, [currentEmployee, reset]);
return ( return (
@ -235,12 +197,11 @@ const ManageEmployee = () => {
<h6 className="mb-0"> <h6 className="mb-0">
{employee ? "Update Employee" : "Create Employee"} {employee ? "Update Employee" : "Create Employee"}
</h6> </h6>
{/* <h6 className="cursor-pointer" onClick={() => navigation( "/employees" )}><i class='bx bx-arrow-back'></i>Back</h6> */}
<button className="btn btn-sm btn-secondary btn-prev" onClick={() => navigation( "/employees" )} > <span className="cursor-pointer fs-6" data-htm="true" data-bs-toggle="tooltip"
<i className="icon-base bx bx-left-arrow-alt me-sm-2 me-0 scaleX-n1-rtl"></i> data-bs-offset="0,6"
<span className="align-middle d-sm-inline-block d-none">Back</span> data-bs-placement="top"
</button> data-bs-html="true" title="Move Back" onClick={()=>navigation("/employees")}><i class='bx bxs-chevron-left'></i> Back</span>
</div> </div>
<div className="card-body"> <div className="card-body">
{!currentEmployee && empLoading && ( {!currentEmployee && empLoading && (
@ -310,8 +271,6 @@ const ManageEmployee = () => {
<div className="row mb-3"> <div className="row mb-3">
<div className="col-sm-6"> <div className="col-sm-6">
<div className="form-text text-start">Email</div> <div className="form-text text-start">Email</div>
{/* <div className="input-group input-group-merge"> */}
<input <input
type="email" type="email"
id="Email" id="Email"
@ -550,9 +509,9 @@ const ManageEmployee = () => {
id="AadharNumber" id="AadharNumber"
placeholder="AADHAR Number" placeholder="AADHAR Number"
/> />
{errors.root?.AadharNumber && ( {errors.AadharNumber && (
<div className="danger-text text-start"> <div className="danger-text text-start">
{errors.AadharNumber.root?.message} {errors.AadharNumber.message}
</div> </div>
)} )}
</div> </div>
@ -566,67 +525,9 @@ const ManageEmployee = () => {
id="PanNumber" id="PanNumber"
placeholder="PAN Number" placeholder="PAN Number"
/> />
{/* {errors.PanNumber && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.PanNumber.message}</div>} */} {errors.PanNumber && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.PanNumber.message}</div>}
</div> </div>
{(errors.AadharNumber || errors.PanNumber ) && (
<div className="danger-text text-start">
{errors.AadharNumber?.message ||errors.PanNumber?.message
}
</div>
)}
</div> </div>
{/* <div className="row mb-3">
<div className="col-sm-12">
<div className="form-text text-start">Upload Photo</div>
<Controller
name="Photo"
control={control}
render={({ field }) => (
<input
type="file"
accept="image/*"
className="form-control form-control-sm"
onChange={(e) => {
field.onChange(e.target.files[0]);
}}
/>
)}
/>
{errors.Photo && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.Photo.message}</div>}
</div>
</div> */}
{/* <div className="row mb-3">
<div className="col-sm-12">
<div className="form-text text-start">Upload Documents</div>
<Controller
name="Documents"
control={control}
render={({ field }) => (
<input
type="file"
accept=".doc,.docx,.pdf,image/*"
multiple
className="form-control form-control-sm"
onChange={(e) => {
field.onChange(Array.from(e.target.files));
}}
/>
)}
/>
<div className="form-text text-start mt-1">
{getValues("Documents") && Array.from(getValues("Documents")).map((item, index) => (
<span key={index} className="ms-1 mt-1 badge rounded-pill bg-secondary">
{item.name}
</span>
))}
</div>
{errors.Documents && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.Documents.message}</div>}
</div>
</div> */}
{employeeId && ( {employeeId && (
<div className="row mb-3 d-none"> <div className="row mb-3 d-none">

View File

@ -60,13 +60,11 @@ const AttendancePage = () =>
modalElement.style.display = 'none'; modalElement.style.display = 'none';
document.body.classList.remove('modal-open'); document.body.classList.remove('modal-open');
document.querySelector('.modal-backdrop').remove(); document.querySelector('.modal-backdrop').remove();
} }
}; };
const handleSubmit = ( formData ) =>{ const handleSubmit = ( formData ) =>{
dispatch( markCurrentAttendance( formData ) ).then( ( action ) => dispatch( markCurrentAttendance( formData ) ).then( ( action ) =>
{ {
const updatedAttendance = attendances.map(item => const updatedAttendance = attendances.map(item =>

View File

@ -17,7 +17,6 @@ const MasterPage = () => {
const [searchTerm, setSearchTerm] = useState(''); const [searchTerm, setSearchTerm] = useState('');
const [ filteredResults, setFilteredResults ] = useState( [] ); const [ filteredResults, setFilteredResults ] = useState( [] );
const hasMasterPermission = useHasUserPermission( MANAGE_MASTER ) const hasMasterPermission = useHasUserPermission( MANAGE_MASTER )
console.log(hasMasterPermission)
const dispatch = useDispatch(); const dispatch = useDispatch();
const selectedMaster = useSelector((store)=>store.localVariables.selectedMaster) const selectedMaster = useSelector((store)=>store.localVariables.selectedMaster)

View File

@ -44,7 +44,6 @@ const MasterTable = ( {data, columns, loading, handleModalData} ) =>
> >
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
<th>{ selectedMaster} Name</th> <th>{ selectedMaster} Name</th>
<th>{selectedMaster } Description</th> <th>{selectedMaster } Description</th>
@ -73,12 +72,9 @@ const MasterTable = ( {data, columns, loading, handleModalData} ) =>
) : ( ) : (
item[col.key] !== undefined && item[col.key] !== null ? item[col.key] : " --- " item[col.key] !== undefined && item[col.key] !== null ? item[col.key] : " --- "
)} )}
</td> </td>
))} ))}
<td className={`${!hasMasterPermission && 'd-none'}`} > <td className={`${!hasMasterPermission && 'd-none'}`} >
{/* className={` ${hasUserPermission('660131a4-788c-4739-a082-cbbf7879cbf2') ? "":"d-none"}`}> */}
<button <button
aria-label="Modify" aria-label="Modify"
type="button" type="button"