Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e88f36bd95 | |||
| 4fd35acc2b | |||
| 1a4d78700c | |||
| 31e1a8aedb | |||
| 9a3ecc3bb6 | |||
| 8abee4eb25 | |||
| 1d6960c31e | |||
| b93604162e | |||
| 56acd6414a |
@@ -6,4 +6,8 @@ TWITTER_URL=https://twitter.com
|
||||
INSTAGRAM_URL=https://www.instagram.com
|
||||
|
||||
# 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
@@ -6,4 +6,8 @@ VITE_TWITTER_URL=https://twitter.com
|
||||
VITE_INSTAGRAM_URL=https://www.instagram.com
|
||||
|
||||
# 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
@@ -6,4 +6,8 @@ TWITTER_URL=https://twitter.com
|
||||
INSTAGRAM_URL=https://www.instagram.com
|
||||
|
||||
# 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'
|
||||
@@ -8,7 +8,7 @@ export default function Footer() {
|
||||
<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">
|
||||
<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>
|
||||
<div className="footer-social-icons flex justify-end items-center gap-2">
|
||||
{renderSocialLinks()}
|
||||
|
||||
@@ -1,83 +1,90 @@
|
||||
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 * as Yup from "yup";
|
||||
import { RequestStatus, StoreState } from "../../core/models";
|
||||
import ErrorMsg from "../shared/ErrorMsg";
|
||||
import { verifyEmployee } from "../../core/apiRequest";
|
||||
|
||||
const initialValues = {
|
||||
title: "",
|
||||
marital_status: "",
|
||||
agent_id: "",
|
||||
bvn: "",
|
||||
first_name: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
surname: "",
|
||||
dob: "",
|
||||
second_name: "",
|
||||
spouse_bvn: "",
|
||||
// salary_acct: "",
|
||||
confirm_salary_acct: false,
|
||||
education: "",
|
||||
applicant_date: "",
|
||||
grade: "",
|
||||
ippis_number: "",
|
||||
employers_name: "",
|
||||
designation: "",
|
||||
checked: false
|
||||
};
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
title: Yup.string()
|
||||
// salary_acct: Yup.string()
|
||||
// .required("Required")
|
||||
// .test("no-e", "Invalid number", (value:any) => {
|
||||
// if (value && /^[0-9]*$/.test(value) == false) {
|
||||
// return false;
|
||||
// }
|
||||
// return true;
|
||||
// })
|
||||
// .min(10, "must be 10 digits")
|
||||
// .max(10, "must be 10 digits"),
|
||||
confirm_salary_acct: Yup.bool() // use bool instead of boolean
|
||||
.oneOf([true], "You must check the box"),
|
||||
education: Yup.string()
|
||||
.required("Required"),
|
||||
marital_status: Yup.string()
|
||||
applicant_date: Yup.string()
|
||||
.required("BVN is required"),
|
||||
grade: Yup.string()
|
||||
.required("Required"),
|
||||
agent_id: Yup.string()
|
||||
ippis_number: Yup.string(),
|
||||
employers_name: Yup.string()
|
||||
.required("Required"),
|
||||
bvn: Yup.string()
|
||||
.required("BVN is required")
|
||||
.test("no-e", "Invalid number", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.min(11, "must be 11 digits")
|
||||
.max(11, "must be 11 digits"),
|
||||
first_name: Yup.string()
|
||||
designation: Yup.string()
|
||||
.required("Required"),
|
||||
phone: Yup.string()
|
||||
.required("Required"),
|
||||
email: Yup.string()
|
||||
.required("Required")
|
||||
.email("Wrong email format"),
|
||||
surname: Yup.string()
|
||||
.required("Required"),
|
||||
dob: Yup.string()
|
||||
.required("Required"),
|
||||
checked: Yup.bool(),
|
||||
checked: Yup.bool() // use bool instead of boolean
|
||||
.oneOf([true], "You must accept that the information here is correct"),
|
||||
});
|
||||
|
||||
type Props = {
|
||||
requestStatus: RequestStatus
|
||||
setRequestStatus: (value:RequestStatus)=>void
|
||||
}
|
||||
|
||||
const EmployerValidation: React.FC= () => {
|
||||
// const inputRef = useRef<HTMLInputElement>(null);
|
||||
const EmployerValidation: React.FC= ({requestStatus, setRequestStatus}:Props) => {
|
||||
|
||||
// const handleInput = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
// const { name, value } = e.target as HTMLInputElement;
|
||||
const { userDetails } = useSelector((state:StoreState) => state.userDetails);
|
||||
|
||||
// if (name === "bvn") {
|
||||
// const isNumeric = /^[0-9]+$/.test(value);
|
||||
|
||||
// if (isNumeric) {
|
||||
// if (value.length === 10) {
|
||||
// setHideOTPComponent(false);
|
||||
// } else {
|
||||
// setHideOTPComponent(true);
|
||||
// }
|
||||
// } else {
|
||||
// console.log("Invalid BVN");
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
const { state:{application_uid, verify_uid} } = useLocation();
|
||||
|
||||
const initialValuesNew = {...initialValues, application_uid, verify_uid}
|
||||
|
||||
//FUNCTION TO HANDLE SUBMIT
|
||||
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 (
|
||||
@@ -90,46 +97,46 @@ const EmployerValidation: React.FC= () => {
|
||||
<p className="text-[10px]">(This is to be filled and validated by the employer of the applicant)</p>
|
||||
</div>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
initialValues={initialValuesNew}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<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 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>
|
||||
</div>
|
||||
<div className="w-full my-10">
|
||||
<p className="mb-1 text-[12px] font-bold">Confirms Employee's salary account number</p>
|
||||
<div className="w-full flex items-center gap-8">
|
||||
<InputCompOne
|
||||
parentClass="w-full max-w-[25rem]"
|
||||
// label="First Name"
|
||||
name="first_name"
|
||||
name="salary_acct"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.first_name}
|
||||
value='0011223344'
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.first_name && props.touched.first_name) ? props.errors.first_name : ''}
|
||||
disabled={true}
|
||||
// error={(props.errors.first_name && props.touched.first_name) ? props.errors.first_name : ''}
|
||||
/>
|
||||
<input
|
||||
type='checkbox'
|
||||
name="checked"
|
||||
name="confirm_salary_acct"
|
||||
className='w-6 h-6 p-2 accent-purple-600 text-purple-600 bg-gray-100 border-gray-300 rounded-lg focus:ring-purple-500'
|
||||
onChange={props.handleChange}
|
||||
/>
|
||||
</div>
|
||||
{(props.errors.confirm_salary_acct && props.touched.confirm_salary_acct) && <span className='text-[10px] text-red-500'>{props.errors.confirm_salary_acct}</span> }
|
||||
</div>
|
||||
|
||||
<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 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 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>
|
||||
@@ -146,7 +153,7 @@ const EmployerValidation: React.FC= () => {
|
||||
<InputCompOne
|
||||
parentInputClass="w-full"
|
||||
parentClass="w-full md:max-w-[25rem]"
|
||||
name="title"
|
||||
name="education"
|
||||
label="Applicant's Educational Qualification"
|
||||
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)`}
|
||||
@@ -154,22 +161,22 @@ const EmployerValidation: React.FC= () => {
|
||||
select={true}
|
||||
selectClass="w-full h-[36px] rounded-[6px]"
|
||||
selectOptions={titleOptions}
|
||||
selectValue={props.values.title}
|
||||
selectValue={props.values.education}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.title && props.touched.title) ? props.errors.title : ''}
|
||||
error={(props.errors.education && props.touched.education) ? props.errors.education : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full md:max-w-[25rem]"
|
||||
label="Applicant's Date of Employment"
|
||||
name="dob"
|
||||
name="applicant_date"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-base md:text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputType='date'
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem] px-3"
|
||||
value={props.values.dob}
|
||||
value={props.values.applicant_date}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.dob && props.touched.dob) ? props.errors.dob : ''}
|
||||
error={(props.errors.applicant_date && props.touched.applicant_date) ? props.errors.applicant_date : ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -178,26 +185,26 @@ const EmployerValidation: React.FC= () => {
|
||||
<InputCompOne
|
||||
parentClass="w-full md:max-w-[25rem]"
|
||||
label="Applicant's Grade Level"
|
||||
name="first_name"
|
||||
name="grade"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-base md:text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.first_name}
|
||||
value={props.values.grade}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.first_name && props.touched.first_name) ? props.errors.first_name : ''}
|
||||
error={(props.errors.grade && props.touched.grade) ? props.errors.grade : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full md:max-w-[25rem]"
|
||||
label="Applicant's IPPIS Number (optional)"
|
||||
name="phone"
|
||||
name="ippis_number"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-base md:text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.phone}
|
||||
value={props.values.ippis_number}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.phone && props.touched.phone) ? props.errors.phone : ''}
|
||||
error={(props.errors.ippis_number && props.touched.ippis_number) ? props.errors.ippis_number : ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -219,41 +226,50 @@ const EmployerValidation: React.FC= () => {
|
||||
<InputCompOne
|
||||
parentClass="w-full md:max-w-[25rem]"
|
||||
label="Employer's Name"
|
||||
name="first_name"
|
||||
name="employers_name"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-base md:text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
labelSpan={`(your full name)`}
|
||||
labelSpanClass='text-[10px]'
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.first_name}
|
||||
value={props.values.employers_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.first_name && props.touched.first_name) ? props.errors.first_name : ''}
|
||||
error={(props.errors.employers_name && props.touched.employers_name) ? props.errors.employers_name : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full md:max-w-[25rem]"
|
||||
label="Desgination"
|
||||
name="phone"
|
||||
label="Designation"
|
||||
name="designation"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-base md:text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
labelSpan={`(your position)`}
|
||||
labelSpanClass='text-[10px]'
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.phone}
|
||||
value={props.values.designation}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.phone && props.touched.phone) ? props.errors.phone : ''}
|
||||
error={(props.errors.designation && props.touched.designation) ? props.errors.designation : ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
{/* submit button */}
|
||||
<div className="mt-24">
|
||||
<div className="mt-24 flex items-center gap-2">
|
||||
<Button
|
||||
className="mt-8 btn-R bg-[#5A2C82]"
|
||||
className="btn-R bg-[#5A2C82]"
|
||||
text="Submit"
|
||||
type="submit"
|
||||
/>
|
||||
{requestStatus.loading &&
|
||||
<CustomSpinner />
|
||||
}
|
||||
</div>
|
||||
<div className='w-full'>
|
||||
<ErrorMsg
|
||||
message={requestStatus.message}
|
||||
status={requestStatus.status}
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
|
||||
@@ -1,14 +1,34 @@
|
||||
import {useState} from "react";
|
||||
import { RequestStatus } from "../../core/models";
|
||||
import EmployerValidation from "./EmployerValidation";
|
||||
|
||||
const StartValidation = () => {
|
||||
|
||||
const [requestStatus, setRequestStatus] = useState<RequestStatus>({loading:false, status:null, message:'', data:{}})
|
||||
|
||||
return (
|
||||
<>
|
||||
<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.status ?
|
||||
<div className={`w-full overflow-y-auto bg-top bg-cover`}>
|
||||
<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 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>
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { useState } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Button, CustomSpinner, FloatLabelInput } from "..";
|
||||
import CustomModal from "../modal/CustomModal";
|
||||
import { useNavigate, useLocation } from "react-router-dom";
|
||||
import { RouteHandler } from "../../router/routes";
|
||||
import { RequestStatus } from "../../core/models";
|
||||
import { employerLogin } from "../../core/apiRequest";
|
||||
import ErrorMsg from "../shared/ErrorMsg";
|
||||
|
||||
|
||||
type FormType = {
|
||||
@@ -23,6 +24,7 @@ export default function Login() {
|
||||
const {state} = useLocation()
|
||||
|
||||
const [modal, setModal] = useState<boolean>(false)
|
||||
const [expiredLinkModal, setExpiredLinkModal] = useState<boolean>(false)
|
||||
|
||||
const [requestStatus, setRequestStatus] = useState<RequestStatus>({loading:false, status:null, message:'', data:{}})
|
||||
|
||||
@@ -50,7 +52,7 @@ export default function Login() {
|
||||
employerLogin(reqData).then(res => {
|
||||
// console.log('RES', res)
|
||||
if(!res?.data?.call_return){
|
||||
setRequestStatus({loading:false, status:false, message:'Email/Password is wrong', data:{}})
|
||||
setRequestStatus({loading:false, status:false, message:res?.data?.status_message || 'Invalid Details', data:{}})
|
||||
return setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:null, message:'', data:{}})
|
||||
},4000)
|
||||
@@ -66,6 +68,12 @@ export default function Login() {
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
if(!state?.application_uid){
|
||||
setExpiredLinkModal(true)
|
||||
}
|
||||
},[])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`w-full overflow-y-auto bg-top bg-cover`}>
|
||||
@@ -73,57 +81,70 @@ export default function Login() {
|
||||
<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">
|
||||
<div className="mb-4">
|
||||
<h1 className="text-2xl text-center font-bold leading-3 tracking-wide text-black dark:text-black">
|
||||
Welcome!
|
||||
</h1>
|
||||
<p className="text-xl mt-4 text-center font-medium text-black dark:text-black">
|
||||
Please login with your email and default password provided to you
|
||||
{expiredLinkModal &&
|
||||
<p className="text-xl mb-4 text-center font-medium text-red-500 dark:text-black">
|
||||
Invalid Link, Please proceed to your email and click on the link to continue
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
<div className={`${expiredLinkModal && 'hidden'} w-full`}>
|
||||
<div className="mb-4">
|
||||
<h1 className="text-2xl text-center font-bold leading-3 tracking-wide text-black dark:text-black">
|
||||
Welcome!
|
||||
</h1>
|
||||
<p className="text-xl mt-4 text-center font-medium text-black dark:text-black">
|
||||
Please login with your email and default password provided to you
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
{/* INPUTS */}
|
||||
<div className="w-full">
|
||||
<div className="relative my-2 py-2">
|
||||
<FloatLabelInput
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
placeHolder="Email"
|
||||
labelName="Email"
|
||||
value={formDetails.email}
|
||||
inputClass=""
|
||||
onChange={handleFormChange}
|
||||
/>
|
||||
{/* INPUTS */}
|
||||
<div className="w-full">
|
||||
<div className="relative my-2 py-2">
|
||||
<FloatLabelInput
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
placeHolder="Email"
|
||||
labelName="Email"
|
||||
value={formDetails.email}
|
||||
inputClass=""
|
||||
onChange={handleFormChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="relative my-2 py-2">
|
||||
<FloatLabelInput
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
placeHolder="Password"
|
||||
labelName="Password"
|
||||
value={formDetails.password}
|
||||
inputClass=""
|
||||
onChange={handleFormChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative my-2 py-2">
|
||||
<FloatLabelInput
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
placeHolder="Password"
|
||||
labelName="Password"
|
||||
value={formDetails.password}
|
||||
inputClass=""
|
||||
onChange={handleFormChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 w-full flex items-center gap-2">
|
||||
<Button
|
||||
text="Enter"
|
||||
className="rounded-md w-full sm:w-2/5 text-xl capitalize font-bold"
|
||||
onClick={loginFxn}
|
||||
disabled={!formDetails.password || !formDetails.email || requestStatus.loading}
|
||||
/>
|
||||
{requestStatus.loading &&
|
||||
<CustomSpinner />
|
||||
}
|
||||
{/* <Link to='' className='text-black text-sm'>Forget your password?</Link> */}
|
||||
<div className="mt-10 w-full flex items-center gap-2">
|
||||
<Button
|
||||
text="Enter"
|
||||
className="rounded-md w-full sm:w-2/5 text-xl capitalize font-bold"
|
||||
onClick={loginFxn}
|
||||
disabled={!formDetails.password || !formDetails.email || requestStatus.loading}
|
||||
/>
|
||||
{requestStatus.loading &&
|
||||
<CustomSpinner />
|
||||
}
|
||||
{/* <Link to='' className='text-black text-sm'>Forget your password?</Link> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-full'>
|
||||
<ErrorMsg
|
||||
message={requestStatus.message}
|
||||
status={requestStatus.status}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -136,7 +157,7 @@ export default function Login() {
|
||||
<div className="px-5 md:px-10 w-full flex justify-end">
|
||||
<button
|
||||
className="font-bold text-[11px] lg:text-[13px] text-[#5A2C82]"
|
||||
onClick={()=>{navigate(RouteHandler.otppage, { state: {verify_uid: requestStatus?.data?.verify_uid, application_uid: requestStatus?.data?.records?.application_uid }, replace:true })}}
|
||||
onClick={()=>{navigate(RouteHandler.otppage, { state: {verify_uid: requestStatus?.data?.verify_uid, application_uid: state?.application_uid }, replace:true })}}
|
||||
>
|
||||
Ok
|
||||
</button>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Button, CustomSpinner } from "..";
|
||||
import { useLocation, useNavigate } from "react-router-dom";
|
||||
import { RouteHandler } from "../../router/routes";
|
||||
@@ -6,6 +6,7 @@ import { updateUserDetails } from "../../store/UserDetails";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { verifyOTP } from "../../core/apiRequest";
|
||||
import { RequestStatus } from "../../core/models";
|
||||
import ErrorMsg from "../shared/ErrorMsg";
|
||||
|
||||
type FormType = {
|
||||
[index: string] : string
|
||||
@@ -38,6 +39,23 @@ export default function Login() {
|
||||
setValues((prev:FormType) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === "Backspace") {
|
||||
const { name } = e.target as HTMLInputElement;
|
||||
if(values[name]){
|
||||
setValues((prev: FormType) => ({ ...prev, [name]: "" }));
|
||||
}else{
|
||||
const keys = Object.keys(values)
|
||||
for(let i=keys.length-1; i>=0; i--){
|
||||
if(values[keys[i]]){
|
||||
setValues((prev: FormType) => ({ ...prev, [keys[i]]: "" }));
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
let reqData = {
|
||||
verify_uid: state?.verify_uid,
|
||||
@@ -54,9 +72,9 @@ export default function Login() {
|
||||
}
|
||||
setRequestStatus({loading:false, status:true, message:'OTP Verified', data:res?.data})
|
||||
localStorage.setItem('token', state?.verify_uid)
|
||||
let data = {firstname:'firstname', lastname:'lastname', uid:'28273737646466464'}
|
||||
dispatch(updateUserDetails(data));
|
||||
navigate(`/${state?.application_uid}`, {replace:true})
|
||||
// let data = {firstname:'firstname', lastname:'lastname', uid:'28273737646466464'}
|
||||
dispatch(updateUserDetails(res?.data?.records));
|
||||
navigate(`/${state?.application_uid}`, {state:{'application_uid':state?.application_uid, 'verify_uid':state?.verify_uid}, replace:true})
|
||||
}).catch(err =>{
|
||||
setRequestStatus({loading:false, status:false, message:'something went wrong', data:{}})
|
||||
console.log(err)
|
||||
@@ -110,6 +128,7 @@ export default function Login() {
|
||||
value={values.otp1}
|
||||
onChange={handleChange}
|
||||
maxLength={1}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
<input
|
||||
id='otp2'
|
||||
@@ -120,6 +139,7 @@ export default function Login() {
|
||||
value={values.otp2}
|
||||
onChange={handleChange}
|
||||
maxLength={1}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
<input
|
||||
id='otp3'
|
||||
@@ -130,6 +150,7 @@ export default function Login() {
|
||||
value={values.otp3}
|
||||
onChange={handleChange}
|
||||
maxLength={1}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
<input
|
||||
id='otp4'
|
||||
@@ -140,6 +161,7 @@ export default function Login() {
|
||||
value={values.otp4}
|
||||
onChange={handleChange}
|
||||
maxLength={1}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -159,6 +181,13 @@ export default function Login() {
|
||||
{/* <Link to='#' className='text-black text-sm'>Forget your password?</Link> */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full'>
|
||||
<ErrorMsg
|
||||
message={requestStatus.message}
|
||||
status={requestStatus.status}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import React from 'react'
|
||||
|
||||
type Props = {
|
||||
size?: string
|
||||
width?: string
|
||||
height?: string
|
||||
}
|
||||
|
||||
export default function CustomSpinner({size='8'}:Props) {
|
||||
let width = `w-${size}`
|
||||
let height = `h-${size}`
|
||||
export default function CustomSpinner({width='w-6', height='h-6'}:Props) {
|
||||
|
||||
return (
|
||||
<div role="status">
|
||||
<svg aria-hidden="true" className={`inline ${width} ${height} text-gray-200 animate-spin dark:text-gray-600 fill-blue-600`} viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
type Error = {
|
||||
message?: string
|
||||
status?: boolean
|
||||
}
|
||||
|
||||
export default function ErrorMsg({message, status}:Error) {
|
||||
return (
|
||||
<div className={`${!message && 'hidden'} w-full`}>
|
||||
<p className={`${status ? 'text-green-600' : 'text-red-500'} pt-2 text-base text-center`}>
|
||||
{message && message}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -15,4 +15,12 @@ export const verifyOTP = (postData:any) => {
|
||||
...postData
|
||||
}
|
||||
return postAuxEnd('/otp', reqData)
|
||||
}
|
||||
|
||||
// FUNCTION TO VERIFY OTP AND LOGIN
|
||||
export const verifyEmployee = (postData:any) => {
|
||||
let reqData = {
|
||||
...postData
|
||||
}
|
||||
return postAuxEnd('/verify', reqData)
|
||||
}
|
||||
@@ -14,4 +14,8 @@ export interface APIResponse {
|
||||
password: string
|
||||
username: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface StoreState {
|
||||
[index:string]: string
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import { updateUserDetails } from '../../store/UserDetails';
|
||||
import { RouteHandler } from '../../router/routes';
|
||||
|
||||
import Logo from '../../assets/images/logo.png'
|
||||
import { StoreState } from '../../core/models';
|
||||
|
||||
export default function DashboardAuth() {
|
||||
|
||||
@@ -15,7 +16,7 @@ export default function DashboardAuth() {
|
||||
const dispatch = useDispatch()
|
||||
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)
|
||||
|
||||
@@ -45,6 +46,10 @@ export default function DashboardAuth() {
|
||||
navigate(RouteHandler.loginpage, {state:{application_uid}, replace:true})
|
||||
return
|
||||
}
|
||||
if(!application_uid){ // APPLICATION UID RETURN TO LOGIN PAGE
|
||||
navigate(RouteHandler.loginpage, {replace:true})
|
||||
return
|
||||
}
|
||||
const getUser = () => { // FUNCTION TO GET USER BY ID
|
||||
let data = {firstname:'firstname', lastname:'lastname', uid:'28273737646466464'}
|
||||
setLoading(false)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
import { Route, Routes, Navigate } from "react-router-dom";
|
||||
import { RouteHandler } from "./routes";
|
||||
import { DashboardAuth, Layout } from "../layouts";
|
||||
|
||||
@@ -12,7 +12,8 @@ import {
|
||||
const Routers = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path='auth' element={<Layout />}>
|
||||
<Route path='/auth' element={<Layout />}>
|
||||
<Route exact path={'auth'} element={<LoginPage />} />
|
||||
<Route path={RouteHandler.loginpage} element={<LoginPage />} />
|
||||
<Route path={RouteHandler.otppage} element={<OTPPage />} />
|
||||
</Route>
|
||||
@@ -22,7 +23,7 @@ const Routers = () => {
|
||||
<Route element={<DashboardAuth />}>
|
||||
<Route path={RouteHandler.homepage} element={<StartValidationPage />} />
|
||||
</Route>
|
||||
<Route path="*" element={<>Error Page</>} />
|
||||
<Route path="*" element={<Navigate to={RouteHandler.loginpage} />} />
|
||||
</Routes>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user