Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8abee4eb25 | |||
| 1d6960c31e | |||
| b93604162e | |||
| 56acd6414a |
@@ -6,50 +6,44 @@ import {Formik, Form} from 'formik'
|
||||
import * as Yup from "yup";
|
||||
|
||||
const initialValues = {
|
||||
title: "",
|
||||
marital_status: "",
|
||||
agent_id: "",
|
||||
bvn: "",
|
||||
first_name: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
surname: "",
|
||||
dob: "",
|
||||
second_name: "",
|
||||
spouse_bvn: "",
|
||||
salary_acct: "",
|
||||
confirm_salary_acct: false,
|
||||
qualification: "",
|
||||
doe: "",
|
||||
gl: "",
|
||||
ippis: "",
|
||||
employer_name: "",
|
||||
designation: "",
|
||||
checked: false
|
||||
};
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
title: Yup.string()
|
||||
.required("Required"),
|
||||
marital_status: Yup.string()
|
||||
.required("Required"),
|
||||
agent_id: Yup.string()
|
||||
.required("Required"),
|
||||
bvn: Yup.string()
|
||||
.required("BVN is required")
|
||||
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(11, "must be 11 digits")
|
||||
.max(11, "must be 11 digits"),
|
||||
first_name: Yup.string()
|
||||
.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"),
|
||||
qualification: Yup.string()
|
||||
.required("Required"),
|
||||
phone: Yup.string()
|
||||
doe: Yup.string()
|
||||
.required("BVN is required"),
|
||||
gl: Yup.string()
|
||||
.required("Required"),
|
||||
email: Yup.string()
|
||||
.required("Required")
|
||||
.email("Wrong email format"),
|
||||
surname: Yup.string()
|
||||
ippis: Yup.string(),
|
||||
employer_name: Yup.string()
|
||||
.required("Required"),
|
||||
dob: Yup.string()
|
||||
designation: 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"),
|
||||
});
|
||||
|
||||
|
||||
@@ -103,27 +97,30 @@ const EmployerValidation: React.FC= () => {
|
||||
</p>
|
||||
</div>
|
||||
<div className="w-full my-10">
|
||||
<p className="mb-1 text-[12px] font-bold">Confirms Employee's salary account number</p>
|
||||
<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">
|
||||
<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}
|
||||
onChange={props.handleChange}
|
||||
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
|
||||
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">
|
||||
@@ -146,7 +143,7 @@ const EmployerValidation: React.FC= () => {
|
||||
<InputCompOne
|
||||
parentInputClass="w-full"
|
||||
parentClass="w-full md:max-w-[25rem]"
|
||||
name="title"
|
||||
name="qualification"
|
||||
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 +151,22 @@ const EmployerValidation: React.FC= () => {
|
||||
select={true}
|
||||
selectClass="w-full h-[36px] rounded-[6px]"
|
||||
selectOptions={titleOptions}
|
||||
selectValue={props.values.title}
|
||||
selectValue={props.values.qualification}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.title && props.touched.title) ? props.errors.title : ''}
|
||||
error={(props.errors.qualification && props.touched.qualification) ? props.errors.qualification : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full md:max-w-[25rem]"
|
||||
label="Applicant's Date of Employment"
|
||||
name="dob"
|
||||
name="doe"
|
||||
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.doe}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.dob && props.touched.dob) ? props.errors.dob : ''}
|
||||
error={(props.errors.doe && props.touched.doe) ? props.errors.doe : ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -178,26 +175,26 @@ const EmployerValidation: React.FC= () => {
|
||||
<InputCompOne
|
||||
parentClass="w-full md:max-w-[25rem]"
|
||||
label="Applicant's Grade Level"
|
||||
name="first_name"
|
||||
name="gl"
|
||||
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.gl}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.first_name && props.touched.first_name) ? props.errors.first_name : ''}
|
||||
error={(props.errors.gl && props.touched.gl) ? props.errors.gl : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full md:max-w-[25rem]"
|
||||
label="Applicant's IPPIS Number (optional)"
|
||||
name="phone"
|
||||
name="ippis"
|
||||
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}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.phone && props.touched.phone) ? props.errors.phone : ''}
|
||||
error={(props.errors.ippis && props.touched.ippis) ? props.errors.ippis : ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -219,30 +216,30 @@ const EmployerValidation: React.FC= () => {
|
||||
<InputCompOne
|
||||
parentClass="w-full md:max-w-[25rem]"
|
||||
label="Employer's Name"
|
||||
name="first_name"
|
||||
name="employer_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.employer_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.first_name && props.touched.first_name) ? props.errors.first_name : ''}
|
||||
error={(props.errors.employer_name && props.touched.employer_name) ? props.errors.employer_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>
|
||||
|
||||
|
||||
@@ -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,
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -45,6 +45,10 @@ export default function DashboardAuth() {
|
||||
navigate(RouteHandler.loginpage, {state:{application_uid}, replace:true})
|
||||
return
|
||||
}
|
||||
if(!application_uid){ // IF NO TOKEN || 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