Compare commits

...

5 Commits

Author SHA1 Message Date
victorAnumudu a31b36686d verify bvn auto api calling bug fixed 2024-04-27 02:36:40 +01:00
CHIEFSOFT\ameye 00f4e1b565 extra host 2024-04-26 19:31:33 -04:00
ameye 6d302def04 Merge branch 'added-axios-package' of DigiFi/digifi-www into master 2024-04-26 23:18:29 +00:00
victorAnumudu 82dd11a772 added axios package and api for start bvn verification 2024-04-26 23:41:41 +01:00
ameye 7e9c395f4a Merge branch 'form-validation-contd' of DigiFi/digifi-www into master 2024-04-24 12:32:27 +00:00
8 changed files with 163 additions and 59 deletions
+5 -2
View File
@@ -11,10 +11,13 @@ services:
ports: ports:
- 6030:5173 - 6030:5173
expose: expose:
- "5173" - "5173"
extra_hosts:
- digifi-apidev.chiefsoft.net:10.10.33.15
- backend.wrenchboard.api.test:10.10.33.15
environment: environment:
- PORT=${DIGIFI_PORT} - PORT=${DIGIFI_PORT}
tty: true tty: true
stdin_open: true stdin_open: true
volumes: volumes:
src: src:
+1
View File
@@ -11,6 +11,7 @@
}, },
"dependencies": { "dependencies": {
"@reduxjs/toolkit": "^2.2.1", "@reduxjs/toolkit": "^2.2.1",
"axios": "^1.6.8",
"clsx": "2.1.0", "clsx": "2.1.0",
"formik": "2.4.5", "formik": "2.4.5",
"react": "^18.2.0", "react": "^18.2.0",
+68 -50
View File
@@ -5,6 +5,9 @@ import { InputCompOne } from "..";
import {useNavigate} from 'react-router-dom' import {useNavigate} from 'react-router-dom'
import { RouteHandler } from "../../router/routes"; import { RouteHandler } from "../../router/routes";
import { validateBVN, verifyOTP } from "../../core/apiRequest";
import { RequestStatus } from "../../core/models";
// To get the validation schema // To get the validation schema
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
bvn: Yup.string() bvn: Yup.string()
@@ -18,7 +21,11 @@ const validationSchema = Yup.object().shape({
.min(11, "must be 11 digits") .min(11, "must be 11 digits")
.max(11, "must be 11 digits"), .max(11, "must be 11 digits"),
otp: Yup.string() 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) => { .test("no-e", "Invalid number", (value:any) => {
if (value && /^[0-9]*$/.test(value) == false) { if (value && /^[0-9]*$/.test(value) == false) {
return false; return false;
@@ -41,50 +48,63 @@ let initialValues = {
otp: '', otp: '',
}; };
type ValidBVN = {
verification_id:string
valid: undefined | boolean
}
const LetsGetStarted: React.FC = () => { const LetsGetStarted: React.FC = () => {
const navigate = useNavigate() const navigate = useNavigate()
// const [pinValues, setPinValues] = React.useState({ // const [pinValues, setPinValues] = React.useState({
// bvn: "", // bvn: "",
// otp: "", // otp: "",
// }); // });
// const otpInputRef = React.useRef<HTMLInputElement>(null);
const [hideOTPComponent, setHideOTPComponent] = React.useState<boolean>(true); const [requestStatusBVN, setRequestStatusBVN] = React.useState<RequestStatus>({loading:false, status:undefined, message:''});
const firstInputRef = React.useRef<HTMLInputElement>(null);
const secondInputRef = React.useRef<HTMLInputElement>(null);
// const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { const [bvnIsValid, setBvnIsValid] = React.useState<ValidBVN>({
// let { name, value } = e.target as HTMLInputElement; verification_id: '',
valid: undefined
});
// setPinValues((prev) => ({ ...prev, [name]: value }));
// };
const handleInput = (e: React.FormEvent<HTMLInputElement>) => { // e: React.FormEvent<HTMLInputElement>
let { name, value } = e.target as HTMLInputElement; // let { value } = e.target as HTMLInputElement;
const bvnValidation = (values:any) => { // Function to Validate BVN
if (name === "bvn") { let bvn = values.bvn
const regex = /^[0-9]+$/; setRequestStatusBVN({loading:true, status:false, message:''})
validateBVN({bvn}).then(res => {
if (regex.test(value)) { if(!res || !res.data.call_return){
if (value?.length == 11) { setBvnIsValid({verification_id:'', valid: false})
setHideOTPComponent(false); setRequestStatusBVN({loading:false, status:false, message:'unable to verify BVN'})
// secondInputRef.current?.focus(); return setTimeout(()=>{
} else setHideOTPComponent(true); setRequestStatusBVN({loading:false, status:false, message:''})
} else { }, 4000)
console.log("object not found");
} }
} 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) => { const handleSubmit = (values:any) => { // Function to VERIFY OTP AND LOGIN USER
console.log('values', values) // console.log('values', values)
navigate(RouteHandler.dashboardHome, {replace:true}) verifyOTP({...values, verification_id:bvnIsValid.verification_id}).then(res=>{
console.log(res.data)
navigate(RouteHandler.dashboardHome, {replace:true})
}).catch(err => {
console.log(err)
})
}; };
return ( return (
<Formik <Formik
initialValues={initialValues} initialValues={initialValues}
validationSchema={validationSchema} validationSchema={validationSchema}
onSubmit={handleSubmit} onSubmit={bvnIsValid.valid ? handleSubmit : bvnValidation}
> >
{(props:any) => ( {(props:any) => (
<Form className=""> <Form className="">
@@ -96,25 +116,25 @@ const LetsGetStarted: React.FC = () => {
</h1> </h1>
</div> </div>
<div className="mx-auto flex flex-col gap-8 max-w-[31.625rem] "> <div className="mx-auto flex flex-col gap-8 max-w-[31.625rem] ">
<InputCompOne <div className='w-full'>
parentClass="flex flex-col gap-2" <InputCompOne
label="Enter Your BVN " parentClass="flex flex-col gap-2"
name="bvn" label="Enter Your BVN "
parentInputClass="w-full" name="bvn"
labelSpan="( To get your BVN, dial *565*0# )" parentInputClass="w-full"
labelSpanClass="text-[13px] text-[#5a5a5a] font-semibold" labelSpan="( To get your BVN, dial *565*0# )"
placeholder="Enter your BVN" labelSpanClass="text-[13px] text-[#5a5a5a] font-semibold"
labelClass="font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#282828] mb-[2px] flex item-center gap-[4px]" placeholder="Enter your BVN"
input labelClass="font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#282828] mb-[2px] flex item-center gap-[4px]"
inputClass="w-full h-[3.625rem] rounded bg-[#EFEFEF] px-4" input
value={props.values.bvn} inputClass="w-full h-[3.625rem] rounded bg-[#EFEFEF] px-4"
onChange={props.handleChange} value={props.values.bvn}
onInput={handleInput} onChange={props.handleChange}
ref={firstInputRef} error={(props.errors.bvn && props.touched.bvn) && props.errors.bvn}
maxLength={11} />
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>
{!hideOTPComponent && ( {bvnIsValid.valid && (
<InputCompOne <InputCompOne
parentClass="flex flex-col gap-2" parentClass="flex flex-col gap-2"
label="Enter OTP " label="Enter OTP "
@@ -128,21 +148,19 @@ const LetsGetStarted: React.FC = () => {
inputClass="w-full h-[3.625rem] rounded bg-[#EFEFEF] px-4" inputClass="w-full h-[3.625rem] rounded bg-[#EFEFEF] px-4"
value={props.values.otp} value={props.values.otp}
onChange={props.handleChange} onChange={props.handleChange}
onInput={handleInput}
ref={secondInputRef}
maxLength={11}
error={(props.errors.otp && props.touched.otp) && props.errors.otp} error={(props.errors.otp && props.touched.otp) && props.errors.otp}
/> />
)} )}
<button <button
type='submit' 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" 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 Enter
</button> </button>
{hideOTPComponent ? ( {bvnIsValid.valid || bvnIsValid.valid == undefined ? (
<p className="text-[#5C2684] mt-[1.5625rem] w-fit"> <p className="text-[#5C2684] mt-[1.5625rem] w-fit">
***Every personal information attached to your BVN is safe and ***Every personal information attached to your BVN is safe and
secure. It is only important for us to verify your information and secure. It is only important for us to verify your information and
+19
View File
@@ -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)
}
+49
View File
@@ -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-------------------------------------------------------"
);
}
});
}
+7
View File
@@ -0,0 +1,7 @@
export interface RequestStatus {
loading?:boolean
status?:boolean | undefined
message?:string
name?:string
data?:{}[] | [any] | {}
}
+4 -5
View File
@@ -1,17 +1,16 @@
import { useState } from "react"; 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 Logo from "../../assets/icons/logo.svg";
import { Icons } from "../../components"; import { Icons } from "../../components";
type Props = { type Props = {
asideDisplay?: () => void; asideDisplay?: () => void;
logoutUser: () => void
}; };
export default function Aside({ asideDisplay }: Props) { export default function Aside({ asideDisplay, logoutUser }: Props) {
const { pathname } = useLocation(); const { pathname } = useLocation();
const navigate = useNavigate();
const [openNestedLink, setOpenNestedLink] = useState<{ name: string | null }>( const [openNestedLink, setOpenNestedLink] = useState<{ name: string | null }>(
{ name: "" } { name: "" }
); );
@@ -115,7 +114,7 @@ export default function Aside({ asideDisplay }: Props) {
<div className="w-full flex justify-center items-center flex-col gap-3"> <div className="w-full flex justify-center items-center flex-col gap-3">
<button <button
className="py-3 px-6 bg-red-100 text-red-500 font-medium rounded-md w-full" 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 Log out
</button> </button>
@@ -1,8 +1,12 @@
import { ReactNode, useState, useEffect } from "react"; import { ReactNode, useState, useEffect } from "react";
import { RouteHandler } from "../../router/routes";
import { useNavigate } from "react-router-dom";
import Aside from "./Aside"; import Aside from "./Aside";
export default function DashboardLayout({ children }: { children: ReactNode }) { export default function DashboardLayout({ children }: { children: ReactNode }) {
const navigate = useNavigate();
const [showAside, setShowAside] = useState<boolean>(false); const [showAside, setShowAside] = useState<boolean>(false);
const asideDisplay = (): void => { const asideDisplay = (): void => {
setShowAside((prev) => !prev); setShowAside((prev) => !prev);
@@ -30,17 +34,21 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
// return child; // return child;
// }); // });
const logoutUser = () => {
navigate(RouteHandler.letsGetStarted, {replace:true})
}
return ( return (
<div className="w-full max-w-[2000px] mx-auto h-screen flex bg-[#020202] text-black"> <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 className="max-w-[18.75rem] w-full bg-white hidden md:block border-r-2 border-[#E6E6E6]">
<Aside /> <Aside logoutUser={logoutUser} />
</aside> </aside>
<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 ${ 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%]" showAside ? "left-0" : "-left-[200%]"
}`} }`}
> >
<Aside asideDisplay={asideDisplay} /> <Aside logoutUser={logoutUser} asideDisplay={asideDisplay} />
</aside> </aside>
<main className="dash-bg-image bg-[#F9F9F9] relative w-full overflow-y-auto overflow-x-hidden"> <main className="dash-bg-image bg-[#F9F9F9] relative w-full overflow-y-auto overflow-x-hidden">