Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 333ada0a1c | |||
| bb718953ad | |||
| a31b36686d | |||
| 00f4e1b565 | |||
| 6d302def04 | |||
| 82dd11a772 | |||
| 7e9c395f4a | |||
| 2cb5c471f6 | |||
| 4b008f6785 | |||
| f632099128 | |||
| 122eb31732 | |||
| 378ff4a625 | |||
| f88b6df24c |
+7
-2
@@ -11,8 +11,13 @@ services:
|
||||
ports:
|
||||
- 6030:5173
|
||||
expose:
|
||||
- "5173"
|
||||
- "5173"
|
||||
extra_hosts:
|
||||
- digifi-apidev.chiefsoft.net:10.10.33.15
|
||||
- backend.wrenchboard.api.test:10.10.33.15
|
||||
environment:
|
||||
- PORT=${DIGIFI_PORT}
|
||||
tty: true
|
||||
stdin_open: true
|
||||
volumes:
|
||||
src:
|
||||
src:
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "^2.2.1",
|
||||
"axios": "^1.6.8",
|
||||
"clsx": "2.1.0",
|
||||
"formik": "2.4.5",
|
||||
"react": "^18.2.0",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
@@ -1,6 +1,7 @@
|
||||
import React, { FC } from "react";
|
||||
import NairaBag from "../../assets/images/dashboard/naira-bag.png";
|
||||
import { Button } from "../";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
export interface DashBoardCardProps {
|
||||
title?: string;
|
||||
@@ -73,11 +74,12 @@ interface DashboardHomeIntroProps {
|
||||
}
|
||||
|
||||
const DashboardHomeIntro: FC<DashboardHomeIntroProps> = ({ handleNextStep, step }) => {
|
||||
const { userDetails } = useSelector((state:any) => state?.userDetails); // CHECKS IF USER Details are avaliable
|
||||
return (
|
||||
<>
|
||||
{step == 1 ?
|
||||
<>
|
||||
<h1 className="font-bold my-5 text-2xl">Hello, Olanrewaju</h1>
|
||||
<h1 className="font-bold my-5 text-2xl">Hello, {userDetails.firstname}</h1>
|
||||
<div className="group w-full lg:w-[27.8125rem] h-[12.75rem] mt-7 ">
|
||||
<DashBoardCard
|
||||
cardClass="bg-[#5C2684] relative"
|
||||
@@ -95,7 +97,7 @@ const DashboardHomeIntro: FC<DashboardHomeIntroProps> = ({ handleNextStep, step
|
||||
</>
|
||||
:
|
||||
<>
|
||||
<h1 className="font-bold my-5 text-2xl">Welcome Back, Olanrewaju</h1>
|
||||
<h1 className="font-bold my-5 text-2xl">Welcome Back, {userDetails.firstname}</h1>
|
||||
<div className="group w-full lg:w-[27.8125rem] h-[12.75rem] mt-7 ">
|
||||
<DashBoardCard
|
||||
cardClass="bg-[#5C2684] relative"
|
||||
|
||||
@@ -32,7 +32,15 @@ const validationSchema = Yup.object().shape({
|
||||
ref_relationship: Yup.string()
|
||||
.required("Required"),
|
||||
ref_bvn: Yup.string()
|
||||
.required("Required"),
|
||||
.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"),
|
||||
ref_two_name: Yup.string()
|
||||
.required("Required"),
|
||||
ref_two_email: Yup.string()
|
||||
@@ -43,7 +51,15 @@ const validationSchema = Yup.object().shape({
|
||||
ref_two_relationship: Yup.string()
|
||||
.required("Required"),
|
||||
ref_two_bvn: Yup.string()
|
||||
.required("Required"),
|
||||
.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"),
|
||||
});
|
||||
|
||||
export default function DashboardHomeRefereeInfo({handleNextStep}:Props) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import DebitAccount from "./DebitAccount";
|
||||
|
||||
|
||||
const ApplicantsAttestation: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,61 +1,87 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import InputDetails from "./IntroDetails";
|
||||
import OTPSection from "./OtpSection";
|
||||
import SpouseDetails from "./SpouseDetails";
|
||||
import { Button } from "..";
|
||||
import React from "react";
|
||||
|
||||
// interface Option {
|
||||
// value: string;
|
||||
// label: string;
|
||||
// }
|
||||
import { Button, InputCompOne } from "..";
|
||||
|
||||
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: "",
|
||||
};
|
||||
|
||||
// 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")
|
||||
.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()
|
||||
.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"),
|
||||
});
|
||||
|
||||
interface BasicInfoProps {
|
||||
inputValues: {
|
||||
title: string;
|
||||
marital: string;
|
||||
agentId: string;
|
||||
bvn: string;
|
||||
firstName: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
surname: string;
|
||||
dob: string;
|
||||
secondName: string;
|
||||
spouseBVN: string;
|
||||
};
|
||||
setInputValues: React.Dispatch<React.SetStateAction<any>>;
|
||||
handleNextStep: any;
|
||||
}
|
||||
|
||||
const BasicInfo: React.FC<BasicInfoProps> = ({
|
||||
inputValues,
|
||||
setInputValues,
|
||||
handleNextStep,
|
||||
}) => {
|
||||
const [hideOTPComponent, setHideOTPComponent] = useState<boolean>(false);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
// const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const handleChange = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
const { name, value } = e.target as HTMLInputElement;
|
||||
setInputValues((prev: typeof inputValues) => ({ ...prev, [name]: value }));
|
||||
};
|
||||
// const handleInput = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
// const { name, value } = e.target as HTMLInputElement;
|
||||
|
||||
const handleInput = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
const { name, value } = e.target as HTMLInputElement;
|
||||
// if (name === "bvn") {
|
||||
// const isNumeric = /^[0-9]+$/.test(value);
|
||||
|
||||
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");
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
if (isNumeric) {
|
||||
if (value.length === 10) {
|
||||
setHideOTPComponent(false);
|
||||
} else {
|
||||
setHideOTPComponent(true);
|
||||
}
|
||||
} else {
|
||||
console.log("Invalid BVN");
|
||||
}
|
||||
}
|
||||
|
||||
//FUNCTION TO HANDLE SUBMIT
|
||||
const handleSubmit = (values:any) => {
|
||||
console.log(values)
|
||||
handleNextStep()
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -64,38 +90,222 @@ const BasicInfo: React.FC<BasicInfoProps> = ({
|
||||
<h1 className="font-semibold text-[2.375rem] text-[#5C2684] my-[.5rem]">
|
||||
Let’s Get You Started
|
||||
</h1>
|
||||
<form>
|
||||
<InputDetails
|
||||
inputValues={inputValues}
|
||||
handleChange={handleChange}
|
||||
handleInput={handleInput}
|
||||
inputRef={inputRef}
|
||||
/>
|
||||
{!hideOTPComponent && (
|
||||
<>
|
||||
<OTPSection
|
||||
inputValues={inputValues}
|
||||
handleChange={handleChange}
|
||||
handleInput={handleInput}
|
||||
inputRef={inputRef}
|
||||
/>
|
||||
<SpouseDetails
|
||||
inputValues={inputValues}
|
||||
handleChange={handleChange}
|
||||
handleInput={handleInput}
|
||||
inputRef={inputRef}
|
||||
/>
|
||||
<Button
|
||||
className="mt-8 btn-R bg-[#5A2C82]"
|
||||
text="Enter"
|
||||
type="button"
|
||||
onClick={handleNextStep}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</form>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
BASIC INFORMATION
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-8 grid grid-cols-2">
|
||||
<div className="flex flex-col gap-4 max-w-[15.6875rem]">
|
||||
<InputCompOne
|
||||
parentInputClass="max-w-[224px] w-full"
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="title"
|
||||
label="Title"
|
||||
labelClass="font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
select={true}
|
||||
selectClass="w-full h-[36px] rounded-[6px]"
|
||||
selectOptions={titleOptions}
|
||||
selectValue={props.values.title}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.title && props.touched.title) ? props.errors.title : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentInputClass="max-w-[224px] w-full"
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="marital_status"
|
||||
label="Marital Status"
|
||||
labelClass="font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
select={true}
|
||||
selectClass="w-full h-[36px] rounded-[6px]"
|
||||
selectOptions={maritalStatusOptions}
|
||||
selectValue={props.values.marital_status}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.marital_status && props.touched.marital_status) ? props.errors.marital_status : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentInputClass="max-w-[224px] w-full"
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="agent_id"
|
||||
label="Direct Sales Agent ID"
|
||||
labelClass="font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
select={true}
|
||||
selectClass="w-full h-[36px] rounded-[6px]"
|
||||
selectOptions={{loading: false, data:[{ value: "", label: "Select" }, { value: "dd", label: "AB001" }]}}
|
||||
selectValue={props.values.agent_id}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.agent_id && props.touched.agent_id) ? props.errors.agent_id : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
label="BVN"
|
||||
name="bvn"
|
||||
parentInputClass="w-full"
|
||||
labelSpan="( To get your BVN, dial *565*0# )"
|
||||
labelSpanClass="text-[11px] text-[#7a7373]"
|
||||
placeholder="Enter your BVN"
|
||||
labelClass="font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#5C2684] mb-[2px] gap-[2px]"
|
||||
input={true}
|
||||
inputClass="w-full h-[36px] bg-[#EFEFEF] rounded-[6px]"
|
||||
value={props.values.bvn}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.bvn && props.touched.bvn) ? props.errors.bvn : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-5">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="First Name"
|
||||
name="first_name"
|
||||
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 : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Phone Number"
|
||||
name="phone"
|
||||
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.phone}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.phone && props.touched.phone) ? props.errors.phone : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Email Address"
|
||||
name="email"
|
||||
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.email}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.email && props.touched.email) ? props.errors.email : ''}
|
||||
/>
|
||||
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Surname"
|
||||
name="surname"
|
||||
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] px-3"
|
||||
value={props.values.surname}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.surname && props.touched.surname) ? props.errors.surname : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Date of Birth"
|
||||
name="dob"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold 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}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.dob && props.touched.dob) ? props.errors.dob : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Second Name"
|
||||
name="second_name"
|
||||
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] px-3"
|
||||
value={props.values.second_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.second_name && props.touched.second_name) ? props.errors.second_name : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full'>
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5 mt-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
SPOUSE DETAILS ( If not applicable, please move to the next stage )
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-8 grid grid-cols-2">
|
||||
<div className="flex flex-col gap-4 max-w-[15.6875rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="BVN"
|
||||
name="spouse_bvn"
|
||||
parentInputClass="w-full"
|
||||
labelSpan="( To get your BVN, dial *565*0# )"
|
||||
labelSpanClass="text-[11px] text-[#7a7373]"
|
||||
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.spouse_bvn}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.spouse_bvn && props.touched.spouse_bvn) ? props.errors.spouse_bvn : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<>
|
||||
<Button
|
||||
className="mt-8 btn-R bg-[#5A2C82]"
|
||||
text="Enter"
|
||||
type="submit"
|
||||
/>
|
||||
</>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BasicInfo;
|
||||
|
||||
|
||||
|
||||
interface SelectOption {
|
||||
loading: boolean;
|
||||
data: {value: string;
|
||||
label: string}[]
|
||||
}
|
||||
|
||||
const maritalStatusOptions: SelectOption = {
|
||||
loading: false,
|
||||
data: [
|
||||
{ value: "", label: "Select" },
|
||||
{ value: "single", label: "Single" },
|
||||
{ value: "married", label: "Married" },
|
||||
{ value: "divorced", label: "Divorced" },
|
||||
{ value: "widowed", label: "Widowed" },
|
||||
]
|
||||
}
|
||||
|
||||
const titleOptions: SelectOption = {
|
||||
loading: false,
|
||||
data: [
|
||||
{ value: "", label: "Select" },
|
||||
{ value: "ms", label: "Ms" },
|
||||
{ value: "mr", label: "Mr" },
|
||||
{ value: "miss", label: "Miss" },
|
||||
{ value: "mrs", label: "Mrs" },
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,8 +3,40 @@ import {useNavigate} from 'react-router-dom'
|
||||
import { Button, InputCompOne } from "..";
|
||||
import { RouteHandler } from "../../router/routes";
|
||||
|
||||
import {Formik, Form} from 'formik'
|
||||
import * as Yup from "yup";
|
||||
|
||||
const initialValues = {
|
||||
disburse_account: "",
|
||||
bank_name: "",
|
||||
account_name: "",
|
||||
account_number: "",
|
||||
checked: false
|
||||
};
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
disburse_account: Yup.string()
|
||||
.required("Required"),
|
||||
bank_name: Yup.string()
|
||||
.required("Required"),
|
||||
account_name: Yup.string()
|
||||
.required("Required"),
|
||||
account_number: Yup.string()
|
||||
.required("Required"),
|
||||
checked: Yup.bool() // use bool instead of boolean
|
||||
.oneOf([true], "You must accept the terms and conditions")
|
||||
});
|
||||
|
||||
const DebitAccount: React.FC = () => {
|
||||
const navigate = useNavigate()
|
||||
|
||||
//FUNCTION TO HANDLE SUBMIT
|
||||
const handleSubmit = (values:any) => {
|
||||
console.log(values)
|
||||
navigate(RouteHandler.letsGetStarted, {replace:true})
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-full rounded py-3 mb-9 bg-[#5C2684] px-5">
|
||||
@@ -12,78 +44,101 @@ const DebitAccount: React.FC = () => {
|
||||
CREDIT ACCOUNT ( Your account to receive your loan )
|
||||
</p>
|
||||
</div>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[29.4375rem] w-full my-5 ml-5"
|
||||
label="Disbursement Account Number "
|
||||
name="disbursementAccountNumber"
|
||||
labelSpan="( Your FCMB Account )"
|
||||
labelSpanClass="text-[12px] text-[#5C2684] ml-1"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[36px] bg-[#EFEFEF] px-[2px] rounded-[6px]"
|
||||
/>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[29.4375rem] w-full my-5 ml-5"
|
||||
label="Disbursement Account Number "
|
||||
name="disburse_account"
|
||||
labelSpan="( Your FCMB Account )"
|
||||
labelSpanClass="text-[12px] text-[#5C2684] ml-1"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[36px] bg-[#EFEFEF] rounded-[6px]"
|
||||
value={props.values.disburse_account}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.disburse_account && props.touched.disburse_account) ? props.errors.disburse_account : ''}
|
||||
/>
|
||||
|
||||
<div className="mt-9 flex flex-col gap-9">
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
DEBIT ACCOUNT ( Your salary account for monthly repayment )
|
||||
</p>
|
||||
</div>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[471px] w-full ml-5"
|
||||
label="Bank Name"
|
||||
name="bankName"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[36px] bg-[#EFEFEF] px-[2px] rounded-[6px]"
|
||||
/>
|
||||
<div className="mt-9 flex flex-col gap-9">
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
DEBIT ACCOUNT ( Your salary account for monthly repayment )
|
||||
</p>
|
||||
</div>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[471px] w-full ml-5"
|
||||
label="Bank Name"
|
||||
name="bank_name"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[36px] bg-[#EFEFEF] rounded-[6px]"
|
||||
value={props.values.bank_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.bank_name && props.touched.bank_name) ? props.errors.bank_name : ''}
|
||||
/>
|
||||
|
||||
<div className="flex items-center gap-[59px]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[471px] w-full ml-5"
|
||||
label="Account Number"
|
||||
name="accountNumber"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[36px] bg-[#EFEFEF] px-[2px] rounded-[6px]"
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[471px] w-full ml-5"
|
||||
label="Account Name"
|
||||
name="accountName"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[36px] bg-[#EFEFEF] px-[2px] rounded-[6px]"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-[59px]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[471px] w-full ml-5"
|
||||
label="Account Number"
|
||||
name="account_number"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[36px] bg-[#EFEFEF] rounded-[6px]"
|
||||
value={props.values.account_number}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.account_number && props.touched.account_number) ? props.errors.account_number : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[471px] w-full ml-5"
|
||||
label="Account Name"
|
||||
name="account_name"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[36px] bg-[#EFEFEF] rounded-[6px]"
|
||||
value={props.values.account_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.account_name && props.touched.account_name) ? props.errors.account_name : ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="max-w-[578px] flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
// checked={true}
|
||||
defaultChecked
|
||||
// onChange={onChange}
|
||||
className="form-checkbox h-[25px] w-[25px] rounded-sm text-[#5c2684] "
|
||||
style={{ backgroundColor: "#5C2684" }}
|
||||
/>
|
||||
<label className="ml-2 text-gray-700">
|
||||
I have read, understood and accept the{" "}
|
||||
<span className="text-[#4545CB]">applicant's attestation</span> and
|
||||
all the <span className="text-[#4545CB]">terms and conditions</span>{" "}
|
||||
for FCMB premium salary loan.
|
||||
</label>
|
||||
</div>
|
||||
<Button
|
||||
className="my-8 max-w-[33.875rem] btn-R bg-[#5A2C82] w-full h-11"
|
||||
text="Apply"
|
||||
type="button"
|
||||
onClick={()=>navigate(RouteHandler.letsGetStarted, {replace:true})}
|
||||
/>
|
||||
</div>
|
||||
<div className="max-w-[578px] flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
// checked={true}
|
||||
|
||||
name='checked'
|
||||
onChange={props.handleChange}
|
||||
className="form-checkbox h-[25px] w-[25px] rounded-sm text-[#5c2684] "
|
||||
style={{ backgroundColor: "#5C2684" }}
|
||||
/>
|
||||
<label className="ml-2 text-gray-700">
|
||||
I have read, understood and accept the{" "}
|
||||
<span className="text-[#4545CB]">applicant's attestation</span> and
|
||||
all the <span className="text-[#4545CB]">terms and conditions</span>{" "}
|
||||
for FCMB premium salary loan.
|
||||
</label>
|
||||
{props.errors.checked && props.touched.checked && <span className='text-[10px] text-red-500'>{props.errors.checked}</span>}
|
||||
</div>
|
||||
<Button
|
||||
className="my-8 max-w-[33.875rem] btn-R bg-[#5A2C82] w-full h-11"
|
||||
text="Apply"
|
||||
type="submit"
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
import React from "react";
|
||||
import { InputCompOne } from "..";
|
||||
|
||||
const EmploymentDetails: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
EMPLOYMENT DETAILS
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[17.9375rem] w-full"
|
||||
label="Job Title"
|
||||
name="jobTitle"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
<div className="flex items-center gap-[3.6875rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[23.1875rem] w-full"
|
||||
label="Employer’s Name"
|
||||
name="employerName"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[23.1875rem] w-full"
|
||||
label="Employer’s Official Email"
|
||||
name="employerOfficialEmail"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-[9rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[17.9375rem] w-full"
|
||||
label="Resumption Date"
|
||||
name="resumptionDate"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[17.9375rem] w-full"
|
||||
label="Employee ID."
|
||||
name="employeeID"
|
||||
labelSpan="Upload your work ID"
|
||||
labelSpanClass="text-[11px] text-[#7a7373]"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-[3.6875rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[23.1875rem] w-full"
|
||||
label="Salary ( Gross annual income )"
|
||||
name="salaryGross"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[23.1875rem] w-full"
|
||||
label="Salary ( Net monthly Income )"
|
||||
name="salaryNet"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
</div>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Salary Payment Date"
|
||||
name="salary-payment-date"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmploymentDetails;
|
||||
@@ -1,35 +1,9 @@
|
||||
import React from "react";
|
||||
import BasicInfo from "./BasicInfo";
|
||||
import YourAreAlmostThere from "./YourAreAlmostThere";
|
||||
import LoanAmountComp from "./LoanAmountComp";
|
||||
import ApplicantsAttestation from "./ApplicantsAttestation";
|
||||
|
||||
const GetStarted = ({handleNextStep, step}:{handleNextStep:any, step:string|number|any}) => {
|
||||
// const [step, setStep] = React.useState(1);
|
||||
|
||||
// const handleNextStep = () => {
|
||||
// if (step < 5) {
|
||||
// setStep(step + 1);
|
||||
// }
|
||||
// };
|
||||
|
||||
// const handlePreviousStep: React.MouseEvent<HTMLButtonElement> = () => {
|
||||
// setStep(step - 1);
|
||||
// };
|
||||
|
||||
const [inputValues, setInputValues] = React.useState({
|
||||
title: "",
|
||||
marital: "",
|
||||
agentId: "",
|
||||
bvn: "",
|
||||
firstName: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
surname: "",
|
||||
dob: "",
|
||||
secondName: "",
|
||||
spouseBVN: "",
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="w-full flex items-center justify-center">
|
||||
@@ -38,8 +12,6 @@ const GetStarted = ({handleNextStep, step}:{handleNextStep:any, step:string|numb
|
||||
<main>
|
||||
{step === 2 && (
|
||||
<BasicInfo
|
||||
inputValues={inputValues}
|
||||
setInputValues={setInputValues}
|
||||
handleNextStep={handleNextStep}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
import React from "react";
|
||||
import InputCompOne from "../shared/InputCompOne";
|
||||
|
||||
interface SelectOption {
|
||||
loading: boolean;
|
||||
data: {value: string;
|
||||
label: string}[]
|
||||
}
|
||||
|
||||
interface InputSectionProps {
|
||||
inputValues: {
|
||||
title: string;
|
||||
marital: string;
|
||||
agentId: string;
|
||||
bvn: string;
|
||||
};
|
||||
handleChange: (e: React.FormEvent<HTMLInputElement>) => void;
|
||||
handleInput: (e: React.FormEvent<HTMLInputElement>) => void;
|
||||
inputRef: React.RefObject<HTMLInputElement>;
|
||||
}
|
||||
|
||||
const InputSection: React.FC<InputSectionProps> = ({
|
||||
inputValues,
|
||||
handleChange,
|
||||
handleInput,
|
||||
inputRef,
|
||||
}) => {
|
||||
const basicInfoInputFields = [
|
||||
{
|
||||
label: "Title",
|
||||
name: "title",
|
||||
parentInputClass: "max-w-[224px] w-full",
|
||||
labelClass:
|
||||
"font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#5C2684] mb-[2px]",
|
||||
select: true,
|
||||
selectClass: "w-full h-[36px] rounded-[6px]",
|
||||
selectOptions: titleOptions,
|
||||
value: inputValues.title,
|
||||
},
|
||||
{
|
||||
label: "Marital Status",
|
||||
name: "marital",
|
||||
parentInputClass: "max-w-[224px] w-full",
|
||||
labelClass:
|
||||
"font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#5C2684] mb-[2px]",
|
||||
select: true,
|
||||
selectClass: "w-full h-[36px] rounded-[6px]",
|
||||
selectOptions: maritalStatusOptions,
|
||||
value: inputValues.marital,
|
||||
onInput: handleInput,
|
||||
},
|
||||
{
|
||||
label: "Direct Sales Agent ID",
|
||||
name: "agentId",
|
||||
parentInputClass: "max-w-[224px] w-full",
|
||||
labelClass:
|
||||
"font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#5C2684] mb-[2px]",
|
||||
select: true,
|
||||
selectClass: "w-full h-[36px] rounded-[6px]",
|
||||
selectOptions: {loading: false, data:[{ value: "", label: "Select" }]},
|
||||
value: inputValues.agentId,
|
||||
onInput: handleInput,
|
||||
},
|
||||
{
|
||||
label: "BVN",
|
||||
name: "bvn",
|
||||
parentInputClass: " w-full",
|
||||
labelSpan: "( To get your BVN, dial *565*0# )",
|
||||
labelSpanClass: "text-[11px] text-[#7a7373]",
|
||||
placeholder: "Enter your BVN",
|
||||
labelClass:
|
||||
"font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#5C2684] mb-[2px] gap-[2px]",
|
||||
input: true,
|
||||
inputClass: "w-full h-[36px] bg-[#EFEFEF] px-[2px] rounded-[6px]",
|
||||
value: inputValues.bvn,
|
||||
onInput: handleInput,
|
||||
maxLength: 10,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
BASIC INFORMATION
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-8 grid grid-cols-2">
|
||||
<div className="flex flex-col gap-4 max-w-[15.6875rem]">
|
||||
{basicInfoInputFields.map((field, index) => (
|
||||
<InputCompOne
|
||||
key={index}
|
||||
label={field.label}
|
||||
name={field.name}
|
||||
parentInputClass={field.parentInputClass}
|
||||
labelClass={field.labelClass}
|
||||
select={field.select}
|
||||
selectClass={field.selectClass}
|
||||
selectOptions={field.selectOptions}
|
||||
value={field.value}
|
||||
onChange={handleChange}
|
||||
onInput={field.onInput}
|
||||
placeholder={field.placeholder}
|
||||
labelSpan={field.labelSpan}
|
||||
labelSpanClass={field.labelSpanClass}
|
||||
input={field.input}
|
||||
inputClass={field.inputClass}
|
||||
maxLength={field.maxLength}
|
||||
ref={inputRef}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default InputSection;
|
||||
|
||||
const maritalStatusOptions: SelectOption = {
|
||||
loading: false,
|
||||
data: [
|
||||
{ value: "", label: "Select" },
|
||||
{ value: "single", label: "Single" },
|
||||
{ value: "married", label: "Married" },
|
||||
{ value: "divorced", label: "Divorced" },
|
||||
{ value: "widowed", label: "Widowed" },
|
||||
]
|
||||
}
|
||||
|
||||
const titleOptions: SelectOption = {
|
||||
loading: false,
|
||||
data: [
|
||||
{ value: "", label: "Select" },
|
||||
{ value: "ms", label: "Ms" },
|
||||
{ value: "mr", label: "Mr" },
|
||||
{ value: "miss", label: "Miss" },
|
||||
{ value: "mrs", label: "Mrs" },
|
||||
]
|
||||
}
|
||||
@@ -1,20 +1,56 @@
|
||||
import React from "react";
|
||||
import { Button, InputCompOne } from "..";
|
||||
|
||||
interface SliderProps {
|
||||
handleSliderChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
value: number;
|
||||
}
|
||||
import {Formik, Form} from 'formik'
|
||||
import * as Yup from "yup";
|
||||
|
||||
const initialValues = {
|
||||
monthly_salary: "",
|
||||
loan_amount: "",
|
||||
duration: 6
|
||||
};
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
duration: Yup.number()
|
||||
.required("Required"),
|
||||
monthly_salary: Yup.string()
|
||||
.required("Required")
|
||||
.test("no-e", "Invalid", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
loan_amount: Yup.string()
|
||||
.required("Required")
|
||||
.test("no-e", "Invalid", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
});
|
||||
|
||||
// interface SliderProps {
|
||||
// handleSliderChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
// value: number;
|
||||
// }
|
||||
|
||||
interface LoanAmountProps {
|
||||
handleNextStep: any;
|
||||
}
|
||||
|
||||
const LoanAmountComp: React.FC<LoanAmountProps> = ({ handleNextStep }) => {
|
||||
const [value, setValue] = React.useState(6);
|
||||
// const [value, setValue] = React.useState(6);
|
||||
|
||||
const handleSliderChange = (e: any) => {
|
||||
setValue(e.target.value);
|
||||
// const handleSliderChange = (e: any) => {
|
||||
// setValue(e.target.value);
|
||||
// };
|
||||
|
||||
//FUNCTION TO HANDLE SUBMIT
|
||||
const handleSubmit = (values:any) => {
|
||||
console.log(values)
|
||||
handleNextStep()
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -35,95 +71,132 @@ const LoanAmountComp: React.FC<LoanAmountProps> = ({ handleNextStep }) => {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-[45px] justify-center ml-[40px] mb-[40px]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[29.4375rem] w-full"
|
||||
label="Your Monthly Salary*"
|
||||
name="salary"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[51px] bg-[#EFEFEF] rounded-[6px] placeholder:text-green-600 placeholder:font-bold px-4"
|
||||
placeholder="150,000"
|
||||
/>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<div className="flex flex-col gap-[45px] justify-center ml-[40px] mb-[40px]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[29.4375rem] w-full"
|
||||
label="Your Monthly Salary*"
|
||||
name="monthly_salary"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[51px] bg-[#EFEFEF] rounded-[6px] placeholder:text-green-600 placeholder:font-bold px-4"
|
||||
placeholder="150,000"
|
||||
value={props.values.monthly_salary}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.monthly_salary && props.touched.monthly_salary) ? props.errors.monthly_salary : ''}
|
||||
/>
|
||||
|
||||
<div className="w-full border-[.1875rem] rounded-xl border-black min-h-[884px] py-4 px-8 max-w-[784px]">
|
||||
<p className="leading-[22px] tracking-[3%] text-[#5C2684] w-[729px] mb-[2.625rem]">
|
||||
The maximum amount you can apply for on this offer is based on the
|
||||
information you shared with us in your loan application. We have
|
||||
made this offer to suit your monthly remuneration and to enable you
|
||||
pay your loan on-time
|
||||
</p>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[45.8125rem] w-full mb-3"
|
||||
label="How much do you want to apply for?"
|
||||
name="loan"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[51px] bg-[#EFEFEF] rounded-[6px] placeholder:text-green-600 placeholder:font-bold px-9"
|
||||
placeholder="350,000"
|
||||
/>
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<div className=" h-[68px] flex flex-col py-1 px-[13px] shadow-md text-[#5C2684]">
|
||||
<span>Minimum Offer:</span>
|
||||
<p>
|
||||
<b>N</b>100,000
|
||||
</p>
|
||||
</div>
|
||||
<div className="w-full border-[.1875rem] rounded-xl border-black min-h-[884px] py-4 px-8 max-w-[784px]">
|
||||
<p className="leading-[22px] tracking-[3%] text-[#5C2684] w-[729px] mb-[2.625rem]">
|
||||
The maximum amount you can apply for on this offer is based on the
|
||||
information you shared with us in your loan application. We have
|
||||
made this offer to suit your monthly remuneration and to enable you
|
||||
pay your loan on-time
|
||||
</p>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[45.8125rem] w-full mb-3"
|
||||
label="How much do you want to apply for?"
|
||||
name="loan_amount"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[51px] bg-[#EFEFEF] rounded-[6px] placeholder:text-green-600 placeholder:font-bold px-9"
|
||||
placeholder="350,000"
|
||||
value={props.values.loan_amount}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.loan_amount && props.touched.loan_amount) ? props.errors.loan_amount : ''}
|
||||
/>
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<div className=" h-[68px] flex flex-col py-1 px-[13px] shadow-md text-[#5C2684]">
|
||||
<span>Minimum Offer:</span>
|
||||
<p>
|
||||
<b>N</b>100,000
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="h-[68px] flex flex-col py-1 px-[13px] shadow-md text-[#5C2684]">
|
||||
<span>Maximum Offer:</span>
|
||||
<p>
|
||||
<b>N</b>500,000
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[68px] flex flex-col py-1 px-[13px] shadow-md text-[#5C2684]">
|
||||
<span>Maximum Offer:</span>
|
||||
<p>
|
||||
<b>N</b>500,000
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Slider handleSliderChange={handleSliderChange} value={value} />
|
||||
<>
|
||||
<div className="flex flex-col items-start mt-11 mb-16">
|
||||
<p className="text-lg font-semibold">For how many months? <span className='text-[10px] text-red-500'>{(props.errors.duration && props.touched.duration) ? props.errors.duration : ''}</span></p>
|
||||
<div className="w-full">
|
||||
<input
|
||||
type="range"
|
||||
min="6"
|
||||
max="24"
|
||||
value={props.values.duration}
|
||||
onChange={props.handleChange}
|
||||
className="slider w-full h-2 bg-gray-300 rounded-lg appearance-none cursor-pointer"
|
||||
style={{
|
||||
background: `linear-gradient(90deg, #6B21A8 ${
|
||||
((props.values.duration - 6) / 18) * 100
|
||||
}%, #D1D5DB ${((props.values.duration - 6) / 18) * 100}%)`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 text-lg font-semibold text-gray-700 w-full flex items-center text-center justify-center">
|
||||
{props.values.duration} months
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
<div className="w-full flex items-center justify-center flex-col">
|
||||
<div className="w-[279px] h-[130px] mb-[76px] flex items-center justify-center flex-col">
|
||||
<p className="text-[#FBB700]">Your Monthly Repayment</p>
|
||||
<p>N</p>
|
||||
</div>
|
||||
<Button
|
||||
className="max-w-[462px] w-full bg-[#5C2684] rounded h-[2.75rem]"
|
||||
text="Submit"
|
||||
type="button"
|
||||
onClick={handleNextStep}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full flex items-center justify-center flex-col">
|
||||
<div className="w-[279px] h-[130px] mb-[76px] flex items-center justify-center flex-col">
|
||||
<p className="text-[#FBB700]">Your Monthly Repayment</p>
|
||||
<p>N</p>
|
||||
</div>
|
||||
<Button
|
||||
className="max-w-[462px] w-full bg-[#5C2684] rounded h-[2.75rem]"
|
||||
text="Submit"
|
||||
type='submit'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoanAmountComp;
|
||||
|
||||
const Slider: React.FC<SliderProps> = ({ handleSliderChange, value }) => {
|
||||
return (
|
||||
<div className="flex flex-col items-start mt-11 mb-16">
|
||||
<p className="text-lg font-semibold">For how many months?</p>
|
||||
<div className="w-full">
|
||||
<input
|
||||
type="range"
|
||||
min="6"
|
||||
max="24"
|
||||
value={value}
|
||||
onChange={handleSliderChange}
|
||||
className="slider w-full h-2 bg-gray-300 rounded-lg appearance-none cursor-pointer"
|
||||
style={{
|
||||
background: `linear-gradient(90deg, #6B21A8 ${
|
||||
((value - 6) / 18) * 100
|
||||
}%, #D1D5DB ${((value - 6) / 18) * 100}%)`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 text-lg font-semibold text-gray-700 w-full flex items-center text-center justify-center">
|
||||
{value} months
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
// const Slider: React.FC<SliderProps> = ({ handleSliderChange, value }) => {
|
||||
// return (
|
||||
// <div className="flex flex-col items-start mt-11 mb-16">
|
||||
// <p className="text-lg font-semibold">For how many months?</p>
|
||||
// <div className="w-full">
|
||||
// <input
|
||||
// type="range"
|
||||
// min="6"
|
||||
// max="24"
|
||||
// value={value}
|
||||
// onChange={handleSliderChange}
|
||||
// className="slider w-full h-2 bg-gray-300 rounded-lg appearance-none cursor-pointer"
|
||||
// style={{
|
||||
// background: `linear-gradient(90deg, #6B21A8 ${
|
||||
// ((value - 6) / 18) * 100
|
||||
// }%, #D1D5DB ${((value - 6) / 18) * 100}%)`,
|
||||
// }}
|
||||
// />
|
||||
// </div>
|
||||
// <div className="mt-4 text-lg font-semibold text-gray-700 w-full flex items-center text-center justify-center">
|
||||
// {value} months
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
import React from "react";
|
||||
import InputCompOne from "../shared/InputCompOne";
|
||||
|
||||
interface OTPSectionProps {
|
||||
inputValues: {
|
||||
firstName: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
surname: string;
|
||||
dob: string;
|
||||
secondName: string;
|
||||
};
|
||||
handleChange: (e: React.FormEvent<HTMLInputElement>) => void;
|
||||
handleInput: (e: React.FormEvent<HTMLInputElement>) => void;
|
||||
inputRef: React.RefObject<HTMLInputElement>;
|
||||
}
|
||||
|
||||
const OTPSection: React.FC<OTPSectionProps> = ({
|
||||
inputValues,
|
||||
handleChange,
|
||||
handleInput,
|
||||
inputRef,
|
||||
}) => {
|
||||
return (
|
||||
<div className="mt-5">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="First Name"
|
||||
name="firstName"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
value={inputValues.firstName}
|
||||
onChange={handleChange}
|
||||
onInput={handleInput}
|
||||
ref={inputRef}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Phone Number"
|
||||
name="phone"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
value={inputValues.phone}
|
||||
onChange={handleChange}
|
||||
onInput={handleInput}
|
||||
ref={inputRef}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Email Address"
|
||||
name="email"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
value={inputValues.email}
|
||||
onChange={handleChange}
|
||||
onInput={handleInput}
|
||||
ref={inputRef}
|
||||
/>
|
||||
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Surname"
|
||||
name="surname"
|
||||
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] px-[.125rem] rounded-[.375rem] px-3"
|
||||
value={inputValues.surname}
|
||||
onChange={handleChange}
|
||||
onInput={handleInput}
|
||||
ref={inputRef}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Date of Birth"
|
||||
name="dob"
|
||||
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] px-[.125rem] rounded-[.375rem] px-3"
|
||||
value={inputValues.dob}
|
||||
onChange={handleChange}
|
||||
onInput={handleInput}
|
||||
ref={inputRef}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Second Name"
|
||||
name="secondName"
|
||||
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] px-[.125rem] rounded-[.375rem] px-3"
|
||||
value={inputValues.secondName}
|
||||
onChange={handleChange}
|
||||
onInput={handleInput}
|
||||
ref={inputRef}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OTPSection;
|
||||
@@ -1,126 +0,0 @@
|
||||
import React from "react";
|
||||
import { InputCompOne } from "..";
|
||||
|
||||
const ReferenceDetails: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
REFERENCE DETAILS ( Must be 18 years and above )
|
||||
</p>
|
||||
</div>
|
||||
<div className="">
|
||||
<div className="flex flex-col gap-[3.4375rem]">
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-[6.5625rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Name"
|
||||
name="referenceName"
|
||||
labelSpan="1st reference"
|
||||
labelSpanClass="text-[12px] text-[#5C2684] ml-1"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Relationship with He/She"
|
||||
name="referenceRelationship"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-[6.5625rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Phone Number"
|
||||
name="referencePhoneNumber"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Email Address"
|
||||
name="referenceEmail"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
</div>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="BVN"
|
||||
name="ReferenceBvn"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-[6.5625rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Name"
|
||||
name="referenceName2"
|
||||
labelSpan="2nd reference"
|
||||
labelSpanClass="text-[12px] text-[#5C2684] ml-[4px]"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Relationship with He/She"
|
||||
name="referenceRelationship2"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-[6.5625rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Phone Number"
|
||||
name="referencePhoneNumber2"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Email Address"
|
||||
name="referenceEmail2"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
</div>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="BVN"
|
||||
name="ReferenceBvn2"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ReferenceDetails;
|
||||
@@ -1,49 +0,0 @@
|
||||
import React from "react";
|
||||
import { InputCompOne } from "..";
|
||||
|
||||
interface SpouseDetailsProps {
|
||||
inputValues: {
|
||||
spouseBVN: string;
|
||||
};
|
||||
handleChange: (e: React.FormEvent<HTMLInputElement>) => void;
|
||||
handleInput: (e: React.FormEvent<HTMLInputElement>) => void;
|
||||
inputRef: React.RefObject<HTMLInputElement>;
|
||||
}
|
||||
|
||||
const SpouseDetails: React.FC<SpouseDetailsProps> = ({
|
||||
inputValues,
|
||||
handleChange,
|
||||
handleInput,
|
||||
inputRef,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5 mt-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
SPOUSE DETAILS ( If not applicable, please move to the next stage )
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-8 grid grid-cols-2">
|
||||
<div className="flex flex-col gap-4 max-w-[15.6875rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="BVN"
|
||||
name="spouseBVN"
|
||||
parentInputClass="w-full"
|
||||
labelSpan="( To get your BVN, dial *565*0# )"
|
||||
labelSpanClass="text-[11px] text-[#7a7373]"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] px-[.125rem] rounded-[.375rem]"
|
||||
value={inputValues.spouseBVN}
|
||||
onChange={handleChange}
|
||||
onInput={handleInput}
|
||||
ref={inputRef}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SpouseDetails;
|
||||
@@ -1,29 +1,400 @@
|
||||
import React from "react";
|
||||
// import { useNavigate } from "react-router-dom";
|
||||
import EmploymentDetails from "./EmploymentDetails";
|
||||
import ReferenceDetails from "./ReferenceDetails";
|
||||
import { Button } from "..";
|
||||
import { Button, InputCompOne } from "..";
|
||||
|
||||
import {Formik, Form} from 'formik'
|
||||
import * as Yup from "yup";
|
||||
|
||||
const initialValues = {
|
||||
job_title: "",
|
||||
employer_name: "",
|
||||
employer_email:"",
|
||||
date_of_resumption: "",
|
||||
employee_id: "",
|
||||
annual_income: "",
|
||||
monthly_salary: "",
|
||||
salary_payment_date: "",
|
||||
ref_name: "",
|
||||
ref_email: "",
|
||||
ref_number: "",
|
||||
ref_relationship: "",
|
||||
ref_bvn: "",
|
||||
ref_two_name: "",
|
||||
ref_two_email: "",
|
||||
ref_two_number: "",
|
||||
ref_two_relationship: "",
|
||||
ref_two_bvn: "",
|
||||
};
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
job_title: Yup.string()
|
||||
.required("Required"),
|
||||
employer_name: Yup.string()
|
||||
.required("Required"),
|
||||
date_of_resumption: Yup.string()
|
||||
.required("Required"),
|
||||
employer_email: Yup.string()
|
||||
.email("Invalid")
|
||||
.required("Required"),
|
||||
annual_income: Yup.string()
|
||||
.required("Required")
|
||||
.test("no-e", "Invalid", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
monthly_salary: Yup.string()
|
||||
.required("Required")
|
||||
.test("no-e", "Invalid", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
salary_payment_date: Yup.string()
|
||||
.required("Required"),
|
||||
employee_id: Yup.string()
|
||||
.required("Required"),
|
||||
ref_name: Yup.string()
|
||||
.required("Required"),
|
||||
ref_email: Yup.string()
|
||||
.email("Invalid")
|
||||
.required("Required"),
|
||||
ref_number: Yup.string()
|
||||
.required("Required"),
|
||||
ref_relationship: Yup.string()
|
||||
.required("Required"),
|
||||
ref_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"),
|
||||
ref_two_name: Yup.string()
|
||||
.required("Required"),
|
||||
ref_two_email: Yup.string()
|
||||
.email("Invalid")
|
||||
.required("Required"),
|
||||
ref_two_number: Yup.string()
|
||||
.required("Required"),
|
||||
ref_two_relationship: Yup.string()
|
||||
.required("Required"),
|
||||
ref_two_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"),
|
||||
});
|
||||
|
||||
interface YourAreAlmostThereProps {
|
||||
handleNextStep: any;
|
||||
}
|
||||
|
||||
const YourAreAlmostThere: React.FC<YourAreAlmostThereProps> = ({ handleNextStep }) => {
|
||||
|
||||
//FUNCTION TO HANDLE SUBMIT
|
||||
const handleSubmit = (values:any) => {
|
||||
console.log(values)
|
||||
handleNextStep()
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className="font-semibold text-[2.375rem] text-[#5C2684] my-[.5rem]">
|
||||
You’re almost there
|
||||
</h1>
|
||||
<form action="" className="flex flex-col gap-6">
|
||||
<EmploymentDetails />
|
||||
<ReferenceDetails />
|
||||
<Button
|
||||
className="my-8 max-w-[20.3125rem] btn-R bg-[#5A2C82]"
|
||||
text="Continue"
|
||||
type="button"
|
||||
onClick={handleNextStep}
|
||||
/>
|
||||
</form>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<div className="flex flex-col gap-6">
|
||||
<>
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
EMPLOYMENT DETAILS
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[17.9375rem] w-full"
|
||||
label="Job Title"
|
||||
name="job_title"
|
||||
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.job_title}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.job_title && props.touched.job_title) ? props.errors.job_title : ''}
|
||||
/>
|
||||
<div className="flex items-center gap-[3.6875rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[23.1875rem] w-full"
|
||||
label="Employer’s Name"
|
||||
name="employer_name"
|
||||
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.employer_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.employer_name && props.touched.employer_name) ? props.errors.employer_name : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[23.1875rem] w-full"
|
||||
label="Employer’s Official Email"
|
||||
name="employer_email"
|
||||
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.employer_email}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.employer_email && props.touched.employer_email) ? props.errors.employer_email : ''}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-[9rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[17.9375rem] w-full"
|
||||
label="Resumption Date"
|
||||
name="date_of_resumption"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold 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]"
|
||||
value={props.values.date_of_resumption}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.date_of_resumption && props.touched.date_of_resumption) ? props.errors.date_of_resumption : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[17.9375rem] w-full"
|
||||
label="Employee ID."
|
||||
name="employee_id"
|
||||
labelSpan="Upload your work ID"
|
||||
labelSpanClass="text-[11px] text-[#7a7373]"
|
||||
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.employee_id}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.employee_id && props.touched.employee_id) ? props.errors.employee_id : ''}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-[3.6875rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[23.1875rem] w-full"
|
||||
label="Salary ( Gross annual income )"
|
||||
name="annual_income"
|
||||
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.annual_income}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.annual_income && props.touched.annual_income) ? props.errors.annual_income : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[23.1875rem] w-full"
|
||||
label="Salary ( Net monthly Income )"
|
||||
name="monthly_salary"
|
||||
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.monthly_salary}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.monthly_salary && props.touched.monthly_salary) ? props.errors.monthly_salary : ''}
|
||||
/>
|
||||
</div>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Salary Payment Date"
|
||||
name="salary_payment_date"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold 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]"
|
||||
value={props.values.salary_payment_date}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.salary_payment_date && props.touched.salary_payment_date) ? props.errors.salary_payment_date : ''}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
||||
<>
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
REFERENCE DETAILS ( Must be 18 years and above )
|
||||
</p>
|
||||
</div>
|
||||
<div className="">
|
||||
<div className="flex flex-col gap-[3.4375rem]">
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-[6.5625rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Name"
|
||||
name="ref_name"
|
||||
labelSpan="1st reference"
|
||||
labelSpanClass="text-[12px] text-[#5C2684] ml-1"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
value={props.values.ref_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_name && props.touched.ref_name) ? props.errors.ref_name : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Relationship with He/She"
|
||||
name="ref_relationship"
|
||||
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] px-[.125rem] rounded-[.375rem]"
|
||||
value={props.values.ref_relationship}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_relationship && props.touched.ref_relationship) ? props.errors.ref_relationship : ''}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-[6.5625rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Phone Number"
|
||||
name="ref_number"
|
||||
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.ref_number}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_number && props.touched.ref_number) ? props.errors.ref_number : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Email Address"
|
||||
name="ref_email"
|
||||
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.ref_email}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_email && props.touched.ref_email) ? props.errors.ref_email : ''}
|
||||
/>
|
||||
</div>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="BVN"
|
||||
name="ref_bvn"
|
||||
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.ref_bvn}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_bvn && props.touched.ref_bvn) ? props.errors.ref_bvn : ''}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-[6.5625rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Name"
|
||||
name="ref_two_name"
|
||||
labelSpan="2nd reference"
|
||||
labelSpanClass="text-[12px] text-[#5C2684] ml-[4px]"
|
||||
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.ref_two_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_two_name && props.touched.ref_two_name) ? props.errors.ref_two_name : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Relationship with He/She"
|
||||
name="ref_two_relationship"
|
||||
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.ref_two_relationship}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_two_relationship && props.touched.ref_two_relationship) ? props.errors.ref_two_relationship : ''}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-[6.5625rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Phone Number"
|
||||
name="ref_two_number"
|
||||
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.ref_two_number}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_two_number && props.touched.ref_two_number) ? props.errors.ref_two_number : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Email Address"
|
||||
name="ref_two_email"
|
||||
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.ref_two_email}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_two_email && props.touched.ref_two_email) ? props.errors.ref_two_email : ''}
|
||||
/>
|
||||
</div>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="BVN"
|
||||
name="ref_two_bvn"
|
||||
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.ref_two_bvn}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_two_bvn && props.touched.ref_two_bvn) ? props.errors.ref_two_bvn : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
<Button
|
||||
className="my-8 max-w-[20.3125rem] btn-R bg-[#5A2C82]"
|
||||
text="Continue"
|
||||
type="submit"
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,6 +5,12 @@ import { InputCompOne } from "..";
|
||||
import {useNavigate} from 'react-router-dom'
|
||||
import { RouteHandler } from "../../router/routes";
|
||||
|
||||
import { useDispatch } from "react-redux";
|
||||
import { updateUserDetails } from "../../store/UserDetails";
|
||||
|
||||
import { validateBVN, verifyOTP } from "../../core/apiRequest";
|
||||
import { RequestStatus } from "../../core/models";
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
bvn: Yup.string()
|
||||
@@ -18,7 +24,11 @@ const validationSchema = Yup.object().shape({
|
||||
.min(11, "must be 11 digits")
|
||||
.max(11, "must be 11 digits"),
|
||||
otp: Yup.string()
|
||||
.required("OTP is required")
|
||||
// .when('require_otp', {
|
||||
// is: true,
|
||||
// then: Yup.string().required("OTP is required")
|
||||
// })
|
||||
// .required("OTP is required")
|
||||
.test("no-e", "Invalid number", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
@@ -41,50 +51,80 @@ let initialValues = {
|
||||
otp: '',
|
||||
};
|
||||
|
||||
type ValidBVN = {
|
||||
verification_id:string
|
||||
valid: undefined | boolean
|
||||
}
|
||||
|
||||
const LetsGetStarted: React.FC = () => {
|
||||
const dispatch = useDispatch()
|
||||
const navigate = useNavigate()
|
||||
// const [pinValues, setPinValues] = React.useState({
|
||||
// bvn: "",
|
||||
// otp: "",
|
||||
// });
|
||||
// const otpInputRef = React.useRef<HTMLInputElement>(null);
|
||||
|
||||
const [hideOTPComponent, setHideOTPComponent] = React.useState<boolean>(true);
|
||||
const firstInputRef = React.useRef<HTMLInputElement>(null);
|
||||
const secondInputRef = React.useRef<HTMLInputElement>(null);
|
||||
const [requestStatusBVN, setRequestStatusBVN] = React.useState<RequestStatus>({loading:false, status:undefined, message:''});
|
||||
|
||||
// const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
// let { name, value } = e.target as HTMLInputElement;
|
||||
const [requestStatusOTP, setRequestStatusOTP] = React.useState<RequestStatus>({loading:false, status:undefined, message:''});
|
||||
|
||||
// setPinValues((prev) => ({ ...prev, [name]: value }));
|
||||
// };
|
||||
const [bvnIsValid, setBvnIsValid] = React.useState<ValidBVN>({
|
||||
verification_id: '',
|
||||
valid: undefined
|
||||
});
|
||||
|
||||
const handleInput = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
let { name, value } = e.target as HTMLInputElement;
|
||||
|
||||
if (name === "bvn") {
|
||||
const regex = /^[0-9]+$/;
|
||||
|
||||
if (regex.test(value)) {
|
||||
if (value?.length == 11) {
|
||||
setHideOTPComponent(false);
|
||||
// secondInputRef.current?.focus();
|
||||
} else setHideOTPComponent(true);
|
||||
} else {
|
||||
console.log("object not found");
|
||||
// e: React.FormEvent<HTMLInputElement>
|
||||
// let { value } = e.target as HTMLInputElement;
|
||||
const bvnValidation = (values:any) => { // Function to Validate BVN
|
||||
let bvn = values.bvn
|
||||
setRequestStatusBVN({loading:true, status:false, message:''})
|
||||
validateBVN({bvn}).then(res => {
|
||||
if(!res || !res.data.call_return){
|
||||
setBvnIsValid({verification_id:'', valid: false})
|
||||
setRequestStatusBVN({loading:false, status:false, message:'unable to verify BVN'})
|
||||
return setTimeout(()=>{
|
||||
setRequestStatusBVN({loading:false, status:false, message:''})
|
||||
}, 4000)
|
||||
}
|
||||
}
|
||||
setBvnIsValid({verification_id:res.data.verification_id, valid: true})
|
||||
setRequestStatusBVN({loading:false, status:true, message:'verified'})
|
||||
}).catch(err => {
|
||||
setBvnIsValid({verification_id:'', valid: false})
|
||||
console.log(err)
|
||||
})
|
||||
};
|
||||
|
||||
const handleSubmit = (values:any) => {
|
||||
console.log('values', values)
|
||||
navigate(RouteHandler.dashboardHome, {replace:true})
|
||||
const handleSubmit = (values:any) => { // Function to VERIFY OTP AND LOGIN USER
|
||||
setRequestStatusOTP({loading:true, status:false, message:''})
|
||||
// console.log('values', values)
|
||||
verifyOTP({...values, verification_id:bvnIsValid.verification_id}).then(res=>{
|
||||
if(!res || !res.data.call_return){
|
||||
setRequestStatusOTP({loading:false, status:false, message:'wrong otp'})
|
||||
return setTimeout(()=>{
|
||||
setRequestStatusOTP({loading:false, status:false, message:''})
|
||||
},4000)
|
||||
}
|
||||
// console.log(res.data)
|
||||
localStorage.setItem('token', res.data.token)
|
||||
localStorage.setItem('uid', res.data.uid)
|
||||
dispatch(updateUserDetails({ ...res.data }));
|
||||
navigate(RouteHandler.dashboardHome, {replace:true})
|
||||
}).catch(err => {
|
||||
setRequestStatusOTP({loading:false, status:false, message:'something went wrong, try again'})
|
||||
console.log(err)
|
||||
return setTimeout(()=>{
|
||||
setRequestStatusOTP({loading:false, status:false, message:''})
|
||||
},4000)
|
||||
})
|
||||
};
|
||||
|
||||
return (
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
onSubmit={bvnIsValid.valid ? handleSubmit : bvnValidation}
|
||||
>
|
||||
{(props:any) => (
|
||||
<Form className="">
|
||||
@@ -96,25 +136,25 @@ const LetsGetStarted: React.FC = () => {
|
||||
</h1>
|
||||
</div>
|
||||
<div className="mx-auto flex flex-col gap-8 max-w-[31.625rem] ">
|
||||
<InputCompOne
|
||||
parentClass="flex flex-col gap-2"
|
||||
label="Enter Your BVN "
|
||||
name="bvn"
|
||||
parentInputClass="w-full"
|
||||
labelSpan="( To get your BVN, dial *565*0# )"
|
||||
labelSpanClass="text-[13px] text-[#5a5a5a] font-semibold"
|
||||
placeholder="Enter your BVN"
|
||||
labelClass="font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#282828] mb-[2px] flex item-center gap-[4px]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] rounded bg-[#EFEFEF] px-4"
|
||||
value={props.values.bvn}
|
||||
onChange={props.handleChange}
|
||||
onInput={handleInput}
|
||||
ref={firstInputRef}
|
||||
maxLength={11}
|
||||
error={(props.errors.bvn && props.touched.bvn) && props.errors.bvn}
|
||||
/>
|
||||
{!hideOTPComponent && (
|
||||
<div className='w-full'>
|
||||
<InputCompOne
|
||||
parentClass="flex flex-col gap-2"
|
||||
label="Enter Your BVN "
|
||||
name="bvn"
|
||||
parentInputClass="w-full"
|
||||
labelSpan="( To get your BVN, dial *565*0# )"
|
||||
labelSpanClass="text-[13px] text-[#5a5a5a] font-semibold"
|
||||
placeholder="Enter your BVN"
|
||||
labelClass="font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#282828] mb-[2px] flex item-center gap-[4px]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] rounded bg-[#EFEFEF] px-4"
|
||||
value={props.values.bvn}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.bvn && props.touched.bvn) && props.errors.bvn}
|
||||
/>
|
||||
<p className={`p-2 ${!requestStatusBVN.status ? 'text-red-500' : 'text-emerald-500'}`}>{requestStatusBVN.loading ? 'verifying...' : requestStatusBVN.message}</p>
|
||||
</div>
|
||||
{bvnIsValid.valid && (
|
||||
<InputCompOne
|
||||
parentClass="flex flex-col gap-2"
|
||||
label="Enter OTP "
|
||||
@@ -128,21 +168,20 @@ const LetsGetStarted: React.FC = () => {
|
||||
inputClass="w-full h-[3.625rem] rounded bg-[#EFEFEF] px-4"
|
||||
value={props.values.otp}
|
||||
onChange={props.handleChange}
|
||||
onInput={handleInput}
|
||||
ref={secondInputRef}
|
||||
maxLength={11}
|
||||
error={(props.errors.otp && props.touched.otp) && props.errors.otp}
|
||||
/>
|
||||
)}
|
||||
<button
|
||||
type='submit'
|
||||
className="w-full h-[3.625rem] rounded bg-[#FBB700] rounded-2 px-4 text-[18px] text-[#282828] font-semibold disabled:text-[#282828] disabled:text-opacity-50"
|
||||
disabled={!props.values.otp}
|
||||
disabled={requestStatusBVN.loading || (!props.values.otp && bvnIsValid.valid)}
|
||||
>
|
||||
Enter
|
||||
</button>
|
||||
|
||||
<p className={`p-2 ${!requestStatusOTP.status ? 'text-red-500' : 'text-emerald-500'}`}>{requestStatusOTP.message}</p>
|
||||
|
||||
{hideOTPComponent ? (
|
||||
{bvnIsValid.valid || bvnIsValid.valid == undefined ? (
|
||||
<p className="text-[#5C2684] mt-[1.5625rem] w-fit">
|
||||
***Every personal information attached to your BVN is safe and
|
||||
secure. It is only important for us to verify your information and
|
||||
|
||||
@@ -58,7 +58,7 @@ const InputCompOne = forwardRef<HTMLInputElement, InputCompOneProps>(
|
||||
return (
|
||||
<div className={parentClass}>
|
||||
{label && (
|
||||
<label htmlFor={label ? label : floatLabel} className={`flex gap-2 items-center ${labelClass}`}>
|
||||
<label htmlFor={label ? label : floatLabel} className={`flex gap-2 items-center flex-wrap ${labelClass}`}>
|
||||
{label}
|
||||
{labelSpan && <span className={labelSpanClass}>{labelSpan}</span>}
|
||||
{error && label && <span className='text-[10px] text-red-500'>{error}</span>}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { postAuxEnd } from "./axiosCall";
|
||||
|
||||
|
||||
// FUNCTION TO START BVN VALIDATION
|
||||
export const validateBVN = (postData:any) => {
|
||||
let reqData = {
|
||||
...postData
|
||||
}
|
||||
return postAuxEnd('https://digifi-apidev.chiefsoft.net/digiusers/v1/bvn', reqData)
|
||||
}
|
||||
|
||||
|
||||
// FUNCTION TO VERIFY OTP AND LOGIN
|
||||
export const verifyOTP = (postData:any) => {
|
||||
let reqData = {
|
||||
...postData
|
||||
}
|
||||
return postAuxEnd('https://digifi-apidev.chiefsoft.net/digiusers/v1/bvn/verify', reqData)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import axios from "axios";
|
||||
|
||||
export function postAuxEnd(uri:string, reqData:any):Promise<any> {
|
||||
// const endPoint = import.meta.env.REACT_APP_USERS_ENDPOINT + uri;
|
||||
const formData = new FormData();
|
||||
for (let value in reqData) {
|
||||
formData.append(value, reqData[value]);
|
||||
}
|
||||
return axios.post(uri, formData)
|
||||
.then((response:{}) => {
|
||||
// if (response.data.internal_return == "-9999") {
|
||||
// localStorage.clear();
|
||||
// window.location.href = `/login?sessionExpired=true`;
|
||||
// }
|
||||
return response;
|
||||
})
|
||||
.catch((error:any) => {
|
||||
if (error.response) {
|
||||
//response status is an error code
|
||||
console.log(
|
||||
"ERROR-------------------------------------------------------"
|
||||
);
|
||||
console.log(error.response.status);
|
||||
console.log(
|
||||
"ERROR-------------------------------------------------------"
|
||||
);
|
||||
} else if (error.request) {
|
||||
//response not received though the request was sent
|
||||
console.log(
|
||||
"ERROR2-------------------------------------------------------"
|
||||
);
|
||||
console.log(error?.request);
|
||||
console.log(
|
||||
"ERROR2-------------------------------------------------------"
|
||||
);
|
||||
} else {
|
||||
//an error occurred when setting up the request
|
||||
console.log(
|
||||
"ERROR3-------------------------------------------------------"
|
||||
);
|
||||
console.log(error);
|
||||
console.log(
|
||||
"ERROR3-------------------------------------------------------"
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
export interface RequestStatus {
|
||||
loading?:boolean
|
||||
status?:boolean | undefined
|
||||
message?:string
|
||||
name?:string
|
||||
data?:{}[] | [any] | {}
|
||||
}
|
||||
|
||||
|
||||
export interface User {
|
||||
firstname?:string
|
||||
lastname?:string
|
||||
last_login?:string
|
||||
message?:string
|
||||
token?:string
|
||||
uid?:string
|
||||
call_return?:string
|
||||
}
|
||||
@@ -1,17 +1,16 @@
|
||||
import { useState } from "react";
|
||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import Logo from "../../assets/icons/logo.svg";
|
||||
import { Icons } from "../../components";
|
||||
|
||||
type Props = {
|
||||
asideDisplay?: () => void;
|
||||
logoutUser: () => void
|
||||
};
|
||||
|
||||
export default function Aside({ asideDisplay }: Props) {
|
||||
export default function Aside({ asideDisplay, logoutUser }: Props) {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [openNestedLink, setOpenNestedLink] = useState<{ name: string | null }>(
|
||||
{ name: "" }
|
||||
);
|
||||
@@ -115,7 +114,7 @@ export default function Aside({ asideDisplay }: Props) {
|
||||
<div className="w-full flex justify-center items-center flex-col gap-3">
|
||||
<button
|
||||
className="py-3 px-6 bg-red-100 text-red-500 font-medium rounded-md w-full"
|
||||
onClick={() => navigate("/login", { replace: true })}
|
||||
onClick={() => logoutUser()}
|
||||
>
|
||||
Log out
|
||||
</button>
|
||||
|
||||
@@ -1,11 +1,52 @@
|
||||
|
||||
import {useState, useEffect} from 'react'
|
||||
import DashboardLayout from "./DashboardLayout";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { Outlet, useNavigate } from "react-router-dom";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { RouteHandler } from '../../router/routes';
|
||||
import { updateUserDetails } from '../../store/UserDetails';
|
||||
|
||||
import Logo from '../../assets/images/logo.png'
|
||||
|
||||
export default function DashboardAuth() {
|
||||
|
||||
const navigate = useNavigate()
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const { userDetails } = useSelector((state:any) => state?.userDetails); // CHECKS IF USER Details are avaliable
|
||||
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
useEffect(()=>{
|
||||
let token = localStorage.getItem('token')
|
||||
if(!token){
|
||||
navigate(RouteHandler.letsGetStarted, {replace:true})
|
||||
return
|
||||
}
|
||||
const getUserByToken = () => {
|
||||
let data = {firstname:'firstname', lastname:'lastname', uid:'28273737646466464'}
|
||||
setTimeout(()=>{
|
||||
setLoading(false)
|
||||
dispatch(updateUserDetails({...data}));
|
||||
},4000)
|
||||
}
|
||||
if(!Object.keys(userDetails).length){
|
||||
getUserByToken()
|
||||
}
|
||||
},[])
|
||||
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<Outlet />
|
||||
</DashboardLayout>
|
||||
<>
|
||||
{loading && !Object.keys(userDetails).length ?
|
||||
<div className='w-full h-screen flex flex-col justify-center items-center gap-4'>
|
||||
<img className='animate-pulse' src={Logo} alt='Logo' />
|
||||
<p className='animate-pulse'>loading...</p>
|
||||
</div>
|
||||
:
|
||||
<DashboardLayout>
|
||||
<Outlet />
|
||||
</DashboardLayout>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
import { ReactNode, useState, useEffect } from "react";
|
||||
import { RouteHandler } from "../../router/routes";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import Aside from "./Aside";
|
||||
|
||||
export default function DashboardLayout({ children }: { children: ReactNode }) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [showAside, setShowAside] = useState<boolean>(false);
|
||||
const asideDisplay = (): void => {
|
||||
setShowAside((prev) => !prev);
|
||||
@@ -30,17 +34,22 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
|
||||
// return child;
|
||||
// });
|
||||
|
||||
const logoutUser = () => {
|
||||
localStorage.clear()
|
||||
navigate(RouteHandler.letsGetStarted, {replace:true})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-[2000px] mx-auto h-screen flex bg-[#020202] text-black">
|
||||
<aside className="max-w-[18.75rem] w-full bg-white hidden md:block border-r-2 border-[#E6E6E6]">
|
||||
<Aside />
|
||||
<Aside logoutUser={logoutUser} />
|
||||
</aside>
|
||||
<aside
|
||||
className={`max-w-[18.75rem] w-full md:hidden bg-white border-r-2 border-[#E6E6E6] fixed top-0 bottom-0 z-50 transition-all duration-500 ${
|
||||
showAside ? "left-0" : "-left-[200%]"
|
||||
}`}
|
||||
>
|
||||
<Aside asideDisplay={asideDisplay} />
|
||||
<Aside logoutUser={logoutUser} asideDisplay={asideDisplay} />
|
||||
</aside>
|
||||
<main className="dash-bg-image bg-[#F9F9F9] relative w-full overflow-y-auto overflow-x-hidden">
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ interface GetStartedLayoutProps {
|
||||
const GetStartedLayout: React.FC<GetStartedLayoutProps> = ({ children }) => {
|
||||
return (
|
||||
<div className="containerMode mb-[5.4375rem]">
|
||||
<div className='sticky top-0 bg-white'>
|
||||
<div className='sticky z-50 top-0 bg-white'>
|
||||
<Header hideSidebar={true} hideMenu={true} />
|
||||
</div>
|
||||
<div className="flex flex-col min-h-[70vh] justify-between">
|
||||
|
||||
+6
-1
@@ -4,10 +4,15 @@ import { BrowserRouter } from "react-router-dom";
|
||||
import App from "./App.tsx";
|
||||
import "./index.css";
|
||||
|
||||
import { Provider } from "react-redux";
|
||||
import store from "./store/store";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
</BrowserRouter>
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
const initialState = {
|
||||
userDetails: {},
|
||||
};
|
||||
|
||||
export const userSlice = createSlice({
|
||||
name: "userDetails",
|
||||
initialState,
|
||||
reducers: {
|
||||
updateUserDetails: (state, action) => {
|
||||
state.userDetails = { ...action.payload };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Action creators are generated for each case reducer function
|
||||
export const { updateUserDetails } = userSlice.actions;
|
||||
|
||||
export default userSlice.reducer;
|
||||
@@ -0,0 +1,9 @@
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
|
||||
import userDetailReducer from "./UserDetails";
|
||||
|
||||
export default configureStore({
|
||||
reducer: {
|
||||
userDetails: userDetailReducer,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user