5 Commits

10 changed files with 144 additions and 80 deletions
+5 -1
View File
@@ -6,4 +6,8 @@ TWITTER_URL=https://twitter.com
INSTAGRAM_URL=https://www.instagram.com INSTAGRAM_URL=https://www.instagram.com
# BACKEND END POINTS # BACKEND END POINTS
VITE_USERS_ENDPOINT='https://digifi-apidev.chiefsoft.net/employment/v1' VITE_USERS_ENDPOINT='https://digifi-apidev.chiefsoft.net/employment/v1'
#BANK NAME
VITE_BANK_NAME='First City Monument Bank'
VITE_BANK_NAME_SHORT='FCMB'
+5 -1
View File
@@ -6,4 +6,8 @@ VITE_TWITTER_URL=https://twitter.com
VITE_INSTAGRAM_URL=https://www.instagram.com VITE_INSTAGRAM_URL=https://www.instagram.com
# BACKEND END POINTS # BACKEND END POINTS
VITE_USERS_ENDPOINT='https://digifi-apidev.chiefsoft.net/employment/v1' VITE_USERS_ENDPOINT='https://digifi-apidev.chiefsoft.net/employment/v1'
#BANK NAME
VITE_BANK_NAME='First City Monument Bank'
VITE_BANK_NAME_SHORT='FCMB'
+5 -1
View File
@@ -6,4 +6,8 @@ TWITTER_URL=https://twitter.com
INSTAGRAM_URL=https://www.instagram.com INSTAGRAM_URL=https://www.instagram.com
# BACKEND END POINTS # BACKEND END POINTS
VITE_USERS_ENDPOINT='https://digifi-apidev.chiefsoft.net/employment/v1' VITE_USERS_ENDPOINT='https://digifi-apidev.chiefsoft.net/employment/v1'
#BANK NAME
VITE_BANK_NAME='First City Monument Bank'
VITE_BANK_NAME_SHORT='FCMB'
+1 -1
View File
@@ -8,7 +8,7 @@ export default function Footer() {
<div className="w-full h-16 bg-[F7F7F7] flex items-center"> <div className="w-full h-16 bg-[F7F7F7] flex items-center">
<div className="containerMode flex justify-center md:justify-between items-center flex-wrap gap-2"> <div className="containerMode flex justify-center md:justify-between items-center flex-wrap gap-2">
<p className="text-[.9375rem] tracking-[2%] font-semibold text-[#969696]"> <p className="text-[.9375rem] tracking-[2%] font-semibold text-[#969696]">
{date} @ First City Monument Bank Limited {date} @ {import.meta.env.VITE_BANK_NAME} Limited
</p> </p>
<div className="footer-social-icons flex justify-end items-center gap-2"> <div className="footer-social-icons flex justify-end items-center gap-2">
{renderSocialLinks()} {renderSocialLinks()}
@@ -1,44 +1,49 @@
import React from "react"; import React from "react";
import { useSelector } from "react-redux";
import { useLocation } from "react-router-dom";
import { Button, InputCompOne } from ".."; import { Button, CustomSpinner, InputCompOne } from "..";
import {Formik, Form} from 'formik' import {Formik, Form} from 'formik'
import * as Yup from "yup"; import * as Yup from "yup";
import { RequestStatus, StoreState } from "../../core/models";
import ErrorMsg from "../shared/ErrorMsg";
import { verifyEmployee } from "../../core/apiRequest";
const initialValues = { const initialValues = {
salary_acct: "", // salary_acct: "",
confirm_salary_acct: false, confirm_salary_acct: false,
qualification: "", education: "",
doe: "", applicant_date: "",
gl: "", grade: "",
ippis: "", ippis_number: "",
employer_name: "", employers_name: "",
designation: "", designation: "",
checked: false checked: false
}; };
// To get the validation schema // To get the validation schema
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
salary_acct: Yup.string() // salary_acct: Yup.string()
.required("Required") // .required("Required")
.test("no-e", "Invalid number", (value:any) => { // .test("no-e", "Invalid number", (value:any) => {
if (value && /^[0-9]*$/.test(value) == false) { // if (value && /^[0-9]*$/.test(value) == false) {
return false; // return false;
} // }
return true; // return true;
}) // })
.min(10, "must be 10 digits") // .min(10, "must be 10 digits")
.max(10, "must be 10 digits"), // .max(10, "must be 10 digits"),
confirm_salary_acct: Yup.bool() // use bool instead of boolean confirm_salary_acct: Yup.bool() // use bool instead of boolean
.oneOf([true], "You must check the box"), .oneOf([true], "You must check the box"),
qualification: Yup.string() education: Yup.string()
.required("Required"), .required("Required"),
doe: Yup.string() applicant_date: Yup.string()
.required("BVN is required"), .required("BVN is required"),
gl: Yup.string() grade: Yup.string()
.required("Required"), .required("Required"),
ippis: Yup.string(), ippis_number: Yup.string(),
employer_name: Yup.string() employers_name: Yup.string()
.required("Required"), .required("Required"),
designation: Yup.string() designation: Yup.string()
.required("Required"), .required("Required"),
@@ -46,32 +51,40 @@ const validationSchema = Yup.object().shape({
.oneOf([true], "You must accept that the information here is correct"), .oneOf([true], "You must accept that the information here is correct"),
}); });
type Props = {
requestStatus: RequestStatus
setRequestStatus: (value:RequestStatus)=>void
}
const EmployerValidation: React.FC= () => { const EmployerValidation: React.FC= ({requestStatus, setRequestStatus}:Props) => {
// const inputRef = useRef<HTMLInputElement>(null);
// const handleInput = (e: React.FormEvent<HTMLInputElement>) => { const { userDetails } = useSelector((state:StoreState) => state.userDetails);
// const { name, value } = e.target as HTMLInputElement;
// if (name === "bvn") { const { state:{application_uid, verify_uid} } = useLocation();
// const isNumeric = /^[0-9]+$/.test(value);
// if (isNumeric) {
// if (value.length === 10) {
// setHideOTPComponent(false);
// } else {
// setHideOTPComponent(true);
// }
// } else {
// console.log("Invalid BVN");
// }
// }
// };
const initialValuesNew = {...initialValues, application_uid, verify_uid}
//FUNCTION TO HANDLE SUBMIT //FUNCTION TO HANDLE SUBMIT
const handleSubmit = (values:any) => { const handleSubmit = (values:any) => {
console.log(values) delete values.checked
delete values.confirm_salary_acct
setRequestStatus({loading:true, status:null, message:'', data:{}})
verifyEmployee(values).then(res => {
console.log('RES', res)
// if(res.status != '200'){
// setRequestStatus({loading:false, status:false, message:'verification failed', data:{}})
// return setTimeout(()=>{
// setRequestStatus({loading:false, status:false, message:'', data:{}})
// },2000)
// }
setRequestStatus({loading:false, status:true, message:'verification sucessful', data:{}})
}).catch(() => {
setRequestStatus({loading:false, status:false, message:'Something went wrong, try again!', data:{}})
setTimeout(()=>{
setRequestStatus({loading:false, status:false, message:'', data:{}})
},2000)
})
}; };
return ( return (
@@ -84,33 +97,30 @@ const EmployerValidation: React.FC= () => {
<p className="text-[10px]">(This is to be filled and validated by the employer of the applicant)</p> <p className="text-[10px]">(This is to be filled and validated by the employer of the applicant)</p>
</div> </div>
<Formik <Formik
initialValues={initialValues} initialValues={initialValuesNew}
validationSchema={validationSchema} validationSchema={validationSchema}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
{(props)=>( {(props)=>(
<Form> <Form>
<div className="w-full"> <div className="w-full">
<p className="my-10 tracking-wide leading-relaxed">We hereby confirm that <span className="font-bold">Mr Victor Badmus (The Applicant)</span> who applied for our loan facility is a permanent and confirmed (Non-contract) staff of <span className="font-bold">Globalcom Nigeria limited</span> and that he is actively on the company's payroll <p className="my-10 tracking-wide leading-relaxed">We hereby confirm that <span className="font-bold">Mr {userDetails.firstname} {userDetails.lastname} (The Applicant)</span> who applied for our loan facility is a permanent and confirmed (Non-contract) staff of <span className="font-bold">{userDetails.employer_name}</span> and that he is actively on the company's payroll
</p> </p>
<p className="my-10 tracking-wide leading-relaxed">We hereby irrevocably and unconditionally undertake to domicile his/her salaries to First City Monument Bank Ltd (FCMB) Accounts number. <p className="my-10 tracking-wide leading-relaxed">We hereby irrevocably and unconditionally undertake to domicile his/her salaries to {import.meta.env.VITE_BANK_NAME} Ltd ({import.meta.env.VITE_BANK_NAME_SHORT}) Accounts number.
</p> </p>
</div> </div>
<div className="w-full my-10"> <div className="w-full my-10">
<p className="mb-1 text-[12px] font-bold flex items-center gap-4">Confirms Employee's salary account number
{(props.errors.salary_acct && props.touched.salary_acct) && <span className='text-[10px] text-red-500'>{props.errors.salary_acct}</span> }
</p>
<div className="w-full flex items-center gap-8"> <div className="w-full flex items-center gap-8">
<InputCompOne <InputCompOne
parentClass="w-full max-w-[25rem]" parentClass="w-full max-w-[25rem]"
// label="First Name"
name="salary_acct" name="salary_acct"
parentInputClass="w-full" parentInputClass="w-full"
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]" labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
input input
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]" inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
value={props.values.first_name} value='0011223344'
onChange={props.handleChange} onChange={props.handleChange}
disabled={true}
// error={(props.errors.first_name && props.touched.first_name) ? props.errors.first_name : ''} // error={(props.errors.first_name && props.touched.first_name) ? props.errors.first_name : ''}
/> />
<input <input
@@ -124,9 +134,9 @@ const EmployerValidation: React.FC= () => {
</div> </div>
<div className="my-10"> <div className="my-10">
<p className="my-2 tracking-wide leading-relaxed">We further undertake that we shall not change the domiciliation of the applicant's account from FCMB throughout the tenor of the loan and any extension thereof unless with your written consent stating that the applicant is no longer indebted to the Bank. <p className="my-2 tracking-wide leading-relaxed">We further undertake that we shall not change the domiciliation of the applicant's account from {import.meta.env.VITE_BANK_NAME_SHORT} throughout the tenor of the loan and any extension thereof unless with your written consent stating that the applicant is no longer indebted to the Bank.
</p> </p>
<p className="my-2 tracking-wide leading-relaxed">We agree to notigy you within 14 days in the event of transfer of the applicant from his/her present location, any change in the employment status or disengagement from the service of our organization or death of the applicant and shall pay his/her benefits, if applicable (excluding Pension Contribution) to the designated FCMB Account number above or issue a cheque or bank draft in favour of We further consent to any additional salary plus facility enhancement that may be obtained by the Beneficiary from your Bank subject to the Bank carrying out a due diligence as to the status of the Beneficiary's employment with our company prior to the approval of the facility Our commitment/consent and undertaking as stated above extends to cover additinal facilities of any kind that may be obtained from your Bank by any of the above mentioned Beneficiary(ies). <p className="my-2 tracking-wide leading-relaxed">We agree to notigy you within 14 days in the event of transfer of the applicant from his/her present location, any change in the employment status or disengagement from the service of our organization or death of the applicant and shall pay his/her benefits, if applicable (excluding Pension Contribution) to the designated {import.meta.env.VITE_BANK_NAME_SHORT} Account number above or issue a cheque or bank draft in favour of We further consent to any additional salary plus facility enhancement that may be obtained by the Beneficiary from your Bank subject to the Bank carrying out a due diligence as to the status of the Beneficiary's employment with our company prior to the approval of the facility Our commitment/consent and undertaking as stated above extends to cover additinal facilities of any kind that may be obtained from your Bank by any of the above mentioned Beneficiary(ies).
</p> </p>
<p className="my-2 tracking-wide leading-relaxed">This Undertaking is given in good faith and shall remain irrevocable except with the written consent of the Bank. <p className="my-2 tracking-wide leading-relaxed">This Undertaking is given in good faith and shall remain irrevocable except with the written consent of the Bank.
</p> </p>
@@ -143,7 +153,7 @@ const EmployerValidation: React.FC= () => {
<InputCompOne <InputCompOne
parentInputClass="w-full" parentInputClass="w-full"
parentClass="w-full md:max-w-[25rem]" parentClass="w-full md:max-w-[25rem]"
name="qualification" name="education"
label="Applicant's Educational Qualification" label="Applicant's Educational Qualification"
labelClass="font-bold text-base md:text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]" labelClass="font-bold text-base md:text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
labelSpan={`(Please select the emplyee's highest qualification)`} labelSpan={`(Please select the emplyee's highest qualification)`}
@@ -151,22 +161,22 @@ const EmployerValidation: React.FC= () => {
select={true} select={true}
selectClass="w-full h-[36px] rounded-[6px]" selectClass="w-full h-[36px] rounded-[6px]"
selectOptions={titleOptions} selectOptions={titleOptions}
selectValue={props.values.qualification} selectValue={props.values.education}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.qualification && props.touched.qualification) ? props.errors.qualification : ''} error={(props.errors.education && props.touched.education) ? props.errors.education : ''}
/> />
<InputCompOne <InputCompOne
parentClass="w-full md:max-w-[25rem]" parentClass="w-full md:max-w-[25rem]"
label="Applicant's Date of Employment" label="Applicant's Date of Employment"
name="doe" name="applicant_date"
parentInputClass="w-full" parentInputClass="w-full"
labelClass="font-bold text-base md:text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]" labelClass="font-bold text-base md:text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
input input
inputType='date' inputType='date'
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem] px-3" inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem] px-3"
value={props.values.doe} value={props.values.applicant_date}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.doe && props.touched.doe) ? props.errors.doe : ''} error={(props.errors.applicant_date && props.touched.applicant_date) ? props.errors.applicant_date : ''}
/> />
</div> </div>
@@ -175,26 +185,26 @@ const EmployerValidation: React.FC= () => {
<InputCompOne <InputCompOne
parentClass="w-full md:max-w-[25rem]" parentClass="w-full md:max-w-[25rem]"
label="Applicant's Grade Level" label="Applicant's Grade Level"
name="gl" name="grade"
parentInputClass="w-full" parentInputClass="w-full"
labelClass="font-bold text-base md:text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]" labelClass="font-bold text-base md:text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
input input
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]" inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
value={props.values.gl} value={props.values.grade}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.gl && props.touched.gl) ? props.errors.gl : ''} error={(props.errors.grade && props.touched.grade) ? props.errors.grade : ''}
/> />
<InputCompOne <InputCompOne
parentClass="w-full md:max-w-[25rem]" parentClass="w-full md:max-w-[25rem]"
label="Applicant's IPPIS Number (optional)" label="Applicant's IPPIS Number (optional)"
name="ippis" name="ippis_number"
parentInputClass="w-full" parentInputClass="w-full"
labelClass="font-bold text-base md:text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]" labelClass="font-bold text-base md:text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
input input
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]" inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
value={props.values.ippis} value={props.values.ippis_number}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.ippis && props.touched.ippis) ? props.errors.ippis : ''} error={(props.errors.ippis_number && props.touched.ippis_number) ? props.errors.ippis_number : ''}
/> />
</div> </div>
@@ -216,16 +226,16 @@ const EmployerValidation: React.FC= () => {
<InputCompOne <InputCompOne
parentClass="w-full md:max-w-[25rem]" parentClass="w-full md:max-w-[25rem]"
label="Employer's Name" label="Employer's Name"
name="employer_name" name="employers_name"
parentInputClass="w-full" parentInputClass="w-full"
labelClass="font-bold text-base md:text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]" labelClass="font-bold text-base md:text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
labelSpan={`(your full name)`} labelSpan={`(your full name)`}
labelSpanClass='text-[10px]' labelSpanClass='text-[10px]'
input input
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]" inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
value={props.values.employer_name} value={props.values.employers_name}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.employer_name && props.touched.employer_name) ? props.errors.employer_name : ''} error={(props.errors.employers_name && props.touched.employers_name) ? props.errors.employers_name : ''}
/> />
<InputCompOne <InputCompOne
parentClass="w-full md:max-w-[25rem]" parentClass="w-full md:max-w-[25rem]"
@@ -245,12 +255,21 @@ const EmployerValidation: React.FC= () => {
{/* submit button */} {/* submit button */}
<div className="mt-24"> <div className="mt-24 flex items-center gap-2">
<Button <Button
className="mt-8 btn-R bg-[#5A2C82]" className="btn-R bg-[#5A2C82]"
text="Submit" text="Submit"
type="submit" type="submit"
/> />
{requestStatus.loading &&
<CustomSpinner />
}
</div>
<div className='w-full'>
<ErrorMsg
message={requestStatus.message}
status={requestStatus.status}
/>
</div> </div>
</Form> </Form>
)} )}
+23 -3
View File
@@ -1,14 +1,34 @@
import {useState} from "react";
import { RequestStatus } from "../../core/models";
import EmployerValidation from "./EmployerValidation"; import EmployerValidation from "./EmployerValidation";
const StartValidation = () => { const StartValidation = () => {
const [requestStatus, setRequestStatus] = useState<RequestStatus>({loading:false, status:null, message:'', data:{}})
return ( return (
<> <>
<div className="my-10 bg-white w-full rounded-2xl border-2 border-black"> {requestStatus.status ?
<div className="w-full p-5 sm:p-10 lg:p-20"> <div className={`w-full overflow-y-auto bg-top bg-cover`}>
<EmployerValidation /> <div className="w-full flex justify-center">
<div className="w-full md:max-w-[570px]">
<div className="bg-white w-full rounded-2xl border-2 border-black">
<div className="w-full p-5 sm:p-10 lg:p-20 flex flex-col justify-between items-center h-full">
<p className="text-xl mb-4 text-center font-medium text-red-500 dark:text-black">
Thank you for completing the process, we will continue further processing with the information provided
</p>
</div>
</div>
</div>
</div> </div>
</div> </div>
:
<div className="my-10 bg-white w-full rounded-2xl border-2 border-black">
<div className="w-full p-5 sm:p-10 lg:p-20">
<EmployerValidation requestStatus={requestStatus} setRequestStatus={setRequestStatus} />
</div>
</div>
}
</> </>
); );
}; };
+3 -3
View File
@@ -72,9 +72,9 @@ export default function Login() {
} }
setRequestStatus({loading:false, status:true, message:'OTP Verified', data:res?.data}) setRequestStatus({loading:false, status:true, message:'OTP Verified', data:res?.data})
localStorage.setItem('token', state?.verify_uid) localStorage.setItem('token', state?.verify_uid)
let data = {firstname:'firstname', lastname:'lastname', uid:'28273737646466464'} // let data = {firstname:'firstname', lastname:'lastname', uid:'28273737646466464'}
dispatch(updateUserDetails(data)); dispatch(updateUserDetails(res?.data?.records));
navigate(`/${state?.application_uid}`, {replace:true}) navigate(`/${state?.application_uid}`, {state:{'application_uid':state?.application_uid, 'verify_uid':state?.verify_uid}, replace:true})
}).catch(err =>{ }).catch(err =>{
setRequestStatus({loading:false, status:false, message:'something went wrong', data:{}}) setRequestStatus({loading:false, status:false, message:'something went wrong', data:{}})
console.log(err) console.log(err)
+8
View File
@@ -15,4 +15,12 @@ export const verifyOTP = (postData:any) => {
...postData ...postData
} }
return postAuxEnd('/otp', reqData) return postAuxEnd('/otp', reqData)
}
// FUNCTION TO VERIFY OTP AND LOGIN
export const verifyEmployee = (postData:any) => {
let reqData = {
...postData
}
return postAuxEnd('/verify', reqData)
} }
+4
View File
@@ -14,4 +14,8 @@ export interface APIResponse {
password: string password: string
username: string username: string
} }
}
export interface StoreState {
[index:string]: string
} }
@@ -8,6 +8,7 @@ import { updateUserDetails } from '../../store/UserDetails';
import { RouteHandler } from '../../router/routes'; import { RouteHandler } from '../../router/routes';
import Logo from '../../assets/images/logo.png' import Logo from '../../assets/images/logo.png'
import { StoreState } from '../../core/models';
export default function DashboardAuth() { export default function DashboardAuth() {
@@ -15,7 +16,7 @@ export default function DashboardAuth() {
const dispatch = useDispatch() const dispatch = useDispatch()
const {application_uid} = useParams() const {application_uid} = useParams()
const { userDetails } = useSelector((state:any) => state?.userDetails); // CHECKS IF USER Details are avaliable const { userDetails } = useSelector((state:StoreState) => state?.userDetails); // CHECKS IF USER Details are avaliable
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
@@ -45,7 +46,7 @@ export default function DashboardAuth() {
navigate(RouteHandler.loginpage, {state:{application_uid}, replace:true}) navigate(RouteHandler.loginpage, {state:{application_uid}, replace:true})
return return
} }
if(!application_uid){ // IF NO TOKEN || UID RETURN TO LOGIN PAGE if(!application_uid){ // APPLICATION UID RETURN TO LOGIN PAGE
navigate(RouteHandler.loginpage, {replace:true}) navigate(RouteHandler.loginpage, {replace:true})
return return
} }