Compare commits

...

8 Commits

Author SHA1 Message Date
victorAnumudu 69b0f8a54d added reference strict validation 2024-09-17 19:19:47 +01:00
ameye ceb1658cdc Merge branch 'payment-page-update' of DigiFi/digifi-www into master 2024-09-04 20:22:49 +00:00
victorAnumudu 5a7adf4537 page page updated 2024-09-04 20:59:50 +01:00
victor.ebuka 45d5bd870f Merge branch 'fixed-bug' of DigiFi/digifi-www into master 2024-09-03 23:08:17 +00:00
victorAnumudu 263c6740c5 fixed bug 2024-09-04 00:07:26 +01:00
victor.ebuka 07c1a8ae06 Merge branch 'error-fix' of DigiFi/digifi-www into master 2024-09-03 22:48:41 +00:00
victorAnumudu bc2167e67a build error fixed 2024-09-03 23:47:31 +01:00
ameye 4d6a7380bb Merge branch 'highest-education' of DigiFi/digifi-www into master 2024-09-03 17:00:09 +00:00
20 changed files with 642 additions and 217 deletions
+69 -50
View File
@@ -3,13 +3,13 @@ import NairaBag from '../../assets/images/dashboard/naira-bag.png';
import {useNavigate} from 'react-router-dom' import {useNavigate} from 'react-router-dom'
import { Button, Icons } from '../'; import { Button, Icons } from '../';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import PendingList from '../paginated-list/PendingList';
import { PendingTableList } from '../../core/models'; import { PendingTableList } from '../../core/models';
import { NewDateTimeFormatter } from '../../lib/NewDateTimeFormatter'; import { NewDateTimeFormatter } from '../../lib/NewDateTimeFormatter';
import { getUserPendingLoanList } from '../../core/apiRequest'; import { getUserPendingLoanList } from '../../core/apiRequest';
import {FormatAmount} from '../../lib/FormatAmount' import {FormatAmount} from '../../lib/FormatAmount'
import PendingLoanPopout from './PendingLoanPopout'; import PendingLoanPopout from './PendingLoanPopout';
import { RouteHandler } from '../../router/routes'; import { RouteHandler } from '../../router/routes';
import TableWrapper from '../tableWrapper/TableWrapper';
export interface DashBoardCardProps { export interface DashBoardCardProps {
title?: string; title?: string;
@@ -101,7 +101,7 @@ const DashboardHomeIntro: FC<DashboardHomeIntroProps> = ({
const [userLoanList, setUserLoanList] = useState<{ const [userLoanList, setUserLoanList] = useState<{
loading: boolean; loading: boolean;
data: PendingTableList; data: Array<PendingTableList>;
}>({ loading: true, data: [] }); }>({ loading: true, data: [] });
useEffect(() => { useEffect(() => {
@@ -120,6 +120,7 @@ const DashboardHomeIntro: FC<DashboardHomeIntroProps> = ({
}) })
.catch((err) => { .catch((err) => {
setUserLoanList({ loading: false, data: [] }); setUserLoanList({ loading: false, data: [] });
console.log(err)
}); });
}, []); }, []);
@@ -169,58 +170,76 @@ const DashboardHomeIntro: FC<DashboardHomeIntroProps> = ({
)} )}
{userLoanList.loading ? null : ( {userLoanList.loading ? null : (
<div className="mt-5 w-full"> <div className="mt-5 w-full">
<PendingList <TableWrapper
data={userLoanList.data} data={userLoanList.data}
itemsPerPage={5} itemsPerPage={7}
tableTitle="Current Applications"
> >
{(data: any) => ( {({ data }:{data:any}) => (
<div className="w-full p-4 rounded-lg shadow-lg bg-white overflow-x-auto min-h-[250px] max-h-[450px]"> <>
<table className="text-[12px] sm:text-base w-full table-auto"> <div className='w-full h-[420px] overflow-auto'>
<thead> <table className="py-2 w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
<tr className="text-left border-b-2"> <thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<th className="px-1 py-4">Date</th> <tr>
<th className="px-1 py-4 text-right">Amount</th> <th scope="col" className="px-4 py-2">
<th className="px-1 py-4 text-center min-w-[110px]"> Date
Payment Term </th>
</th> <th scope="col" className="px-4 py-2">
<th className="px-1 py-4 text-center">Status</th> Amount
<th className="px-1 py-4 text-right">Action</th> </th>
</tr> <th scope="col" className="px-4 py-2">
</thead> Payment Term
<tbody> </th>
{data.map((item: any, index: any) => ( <th scope="col" className="px-4 py-2">
<tr key={index || item} className="even:bg-slate-100"> Status
<td className="px-1 py-2"> </th>
{NewDateTimeFormatter(item?.added)} <th scope="col" className="px-4 py-2">
</td> Action
<td className="px-1 py-2 text-right"> </th>
{FormatAmount(item?.loan_amount)} </tr>
</td> </thead>
<td className="px-1 py-2 text-center"> <tbody>
{item?.payment_month} {(data && data.length > 0) ? data?.map((item:any) => (
</td> <tr key={item?.application_uid} className="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600">
<td className="px-1 py-2 text-center"> <td className="px-3 py-2">
<button {NewDateTimeFormatter(item?.added)}
className={`${!item?.status_text?.button && 'pointer-events-none border-0'} border p-2`} </td>
onClick={()=>setLoanPopout({show:true, data:item})} <td className="px-3 py-2">
> {FormatAmount(item?.loan_amount)}
{item?.status_text?.text || 'Pending'} </td>
</button> <td className="px-3 py-2">
</td> {item?.payment_month}
<td className="flex justify-end px-1 py-2 text-right"> </td>
<button className="flex flex-nowrap items-center px-2 py-1 border-2 border-black" onClick={()=>navigate(RouteHandler.dashboardPayments, {state:{application_uid: item?.application_uid}})}> <td className="px-3 py-2">
View <button
<Icons name="arrow-right" /> className={`${!item?.status_text?.button && 'pointer-events-none border-0'} border p-2`}
</button> onClick={()=>setLoanPopout({show:true, data:item})}
</td> >
</tr> {item?.status_text?.text || 'Pending'}
))} </button>
</tbody> </td>
<td className="px-3 py-2 flex gap-2">
<button className="flex flex-nowrap items-center px-2 py-1 border-2 border-black" onClick={()=>navigate(RouteHandler.dashboardReference, {state:{application_uid: item?.application_uid}})}>
View
<Icons name="arrow-right" />
</button>
</td>
</tr>
))
:
<tr className="w-3 p-3">
<td className="px-3 py-2" colSpan={5}>
<div className="flex justify-center items-center">
No Record Found
</div>
</td>
</tr>
}
</tbody>
</table> </table>
</div> </div>
)} </>
</PendingList> )}
</TableWrapper>
</div> </div>
)} )}
</div> </div>
+99 -60
View File
@@ -1,72 +1,111 @@
import { InputCompOne } from "..";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { useSelector } from "react-redux";
import {Formik, Form} from 'formik'
import * as Yup from "yup";
import { RouteHandler } from "../../router/routes"; import { RouteHandler } from "../../router/routes";
import { Button, InputCompOne } from "..";
// To get the validation schema
const validationSchema = Yup.object().shape({
firstname: Yup.string()
.required("Required"),
lastname: Yup.string()
.required("Required"),
internal_email : Yup.string().required("Required").email("Invalid"),
});
export default function DashboardProfile() { export default function DashboardProfile() {
let navigate = useNavigate(); let navigate = useNavigate();
const navigateToProfile = () => navigate(RouteHandler.dashboardHome); const navigateToHome = () => navigate(RouteHandler.dashboardHome);
const { userDetails } = useSelector((state:any) => state?.userDetails); // GETS USER DETAILS
const initialValues = {
firstname: userDetails.firstname,
lastname: userDetails.lastname,
internal_email: userDetails.internal_email
};
//FUNCTION TO HANDLE SUBMIT
const handleSubmit = (values:any) => {
console.log('good', values)
};
return ( return (
<div className="w-full"> <div className="w-full">
<div className='my-[2rem] flex items-center'> <div className='my-[2rem] flex items-center'>
<button onClick={navigateToProfile} className='w-6 h-6 text-lg flex justify-center items-center rounded-full bg-gray-500'>&lt;</button> <button onClick={navigateToHome} className='py-2 px-4 text-lg text-white flex justify-center items-center bg-[#5C2684]'>&lt; Back</button>
</div>
<div className="max-w-[25.875rem] w-full p-4 rounded-xl flex flex-col gap-1 bg-[#FBB700]/30">
<InputCompOne
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
name="applyIshInput"
label="Full name"
labelClass="font-bold text-[1.125rem]"
input
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
placeholder="John James"
/>
<InputCompOne
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
name="applyIshInput"
label="Phone number"
labelClass="font-bold text-[1.125rem]"
input
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
placeholder="07000000000"
/>
<InputCompOne
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
name="applyIshInput"
label="Residential address"
labelClass="font-bold text-[1.125rem]"
input
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
placeholder="Somewhere in lagos"
/>
<InputCompOne
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
name="applyIshInput"
label="Select your state"
labelClass="font-bold text-[1.125rem]"
input
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
placeholder="Lagos"
/>
<InputCompOne
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
name="applyIshInput"
label="Email address"
labelClass="font-bold text-[1.125rem]"
input
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
placeholder="johndoe@gmail.com"
/>
<InputCompOne
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
name="applyIshInput"
label="Date of birth"
labelClass="font-bold text-[1.125rem]"
input
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
placeholder="12/10/1994"
/>
</div> </div>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{(props)=>(
<Form>
<div className="flex flex-col lg:flex-row items-start gap-[2rem]">
<div className='w-full lg:max-w-[30rem] flex flex-col gap-[2rem]'>
<InputCompOne
parentClass="w-full"
name="firstname"
floatLabel="Firstname"
// labelClass="font-bold text-[1.125rem]"
input
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
placeholder="Mr. Mark John"
value={props.values.firstname}
onChange={props.handleChange}
// error={(props.errors.firstname && props.touched.firstname) ? props.errors.firstname : ''}
/>
<InputCompOne
parentClass="w-full"
name="internal_email"
floatLabel="Email"
// labelClass="font-bold text-[1.125rem]"
input
// disabled={true}
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
placeholder="Mr. Mark John"
value={props.values.internal_email}
onChange={props.handleChange}
// error={(props.errors.internal_email && props.touched.internal_email) ? props.errors.internal_email : ''}
/>
</div>
<div className='w-full lg:max-w-[30rem] flex flex-col gap-[2rem]'>
<InputCompOne
parentClass="w-full"
name="lastname"
floatLabel="Lastname"
// labelClass="font-bold text-[1.125rem]"
input
// disabled={true}
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
placeholder="Mr. Mark John"
value={props.values.lastname}
onChange={props.handleChange}
// error={(props.errors.lastname && props.touched.lastname) ? props.errors.lastname : ''}
/>
</div>
</div>
<div className='hidden w-full lg:max-w-[416px] flex flex-col gap-[2rem]'>
<div className="w-full">
<Button
className="my-4 btn-Y text-black w-full h-11"
text="Update"
type="submit"
/>
</div>
</div>
</Form>
)}
</Formik>
</div> </div>
); );
} }
@@ -1,4 +1,4 @@
import React, {useState} from 'react' import {useState} from 'react'
import ModalWrapper from '../modal/ModalWrapper' import ModalWrapper from '../modal/ModalWrapper'
import { PendingTableList } from '../../core/models' import { PendingTableList } from '../../core/models'
import { NewDateTimeFormatter } from '../../lib/NewDateTimeFormatter' import { NewDateTimeFormatter } from '../../lib/NewDateTimeFormatter'
@@ -19,7 +19,7 @@ export default function PendingLoanPopout({data, action}:Props<PendingTableList>
msg: string msg: string
}>({ loading: false, status: false, msg: ''}); }>({ loading: false, status: false, msg: ''});
const handleAddCard = (appID:string) => { const handleAddCard = (appID:string | undefined) => {
let reqData = { let reqData = {
application_uid: appID application_uid: appID
@@ -8,9 +8,9 @@ import { getEmployer } from '../../../core/apiRequest';
import CustomSpinner from '../../CustomSpinner'; import CustomSpinner from '../../CustomSpinner';
import { FormatAmount } from '../../../lib/FormatAmount'; import { FormatAmount } from '../../../lib/FormatAmount';
type Props = { // type Props = {
handleNextStep?:(value:{})=>any // handleNextStep?:(value:{})=>any
} // }
// type EmployerProps = { // type EmployerProps = {
// loading?: boolean, // loading?: boolean,
@@ -87,7 +87,7 @@ const validationSchema = Yup.object().shape({
}), }),
}); });
export default function EmploymentDetail({handleNextStep}:Props) { export default function EmploymentDetail() {
const [employerList, setEmployerList] = useState<any>({ const [employerList, setEmployerList] = useState<any>({
loading: true, loading: true,
@@ -96,21 +96,13 @@ export default function EmploymentDetail({handleNextStep}:Props) {
//FUNCTION TO HANDLE SUBMIT //FUNCTION TO HANDLE SUBMIT
const handleSubmit = (values:any) => { const handleSubmit = () => {
// if(values.employer_uid){
// let employer_uid = values.employer_uid
// delete values.employer_uid
// handleNextStep({employer_uid, employment: values})
// }else{
// handleNextStep({employment: values})
// }
console.log('good') console.log('good')
}; };
useEffect(()=>{ useEffect(()=>{
getEmployer().then(res => { getEmployer().then(res => {
setEmployerList({loading:false, data:res?.data?.employer}) setEmployerList({loading:false, data:res?.data?.employer})
// console.log('RES', res)
}).catch(err => { }).catch(err => {
console.log(err) console.log(err)
setEmployerList({loading:false, data:{}}) setEmployerList({loading:false, data:{}})
@@ -178,7 +170,7 @@ export default function EmploymentDetail({handleNextStep}:Props) {
placeholder="Mr. Mark John" placeholder="Mr. Mark John"
value={props.values.name} value={props.values.name}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.name && props.touched.name) ? props.errors.name : ''} // error={(props.errors.name && props.touched.name) ? props.errors.name : ''}
/> />
<InputCompOne <InputCompOne
parentClass="w-full" parentClass="w-full"
@@ -191,7 +183,7 @@ export default function EmploymentDetail({handleNextStep}:Props) {
placeholder="example@gmail.com" placeholder="example@gmail.com"
value={props.values.official_email} value={props.values.official_email}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.official_email && props.touched.official_email) ? props.errors.official_email : ''} // error={(props.errors.official_email && props.touched.official_email) ? props.errors.official_email : ''}
/> />
<InputCompOne <InputCompOne
parentClass="w-full" parentClass="w-full"
@@ -204,7 +196,7 @@ export default function EmploymentDetail({handleNextStep}:Props) {
selectOptions={props.values.industry} selectOptions={props.values.industry}
selectValue={props.values.industry} selectValue={props.values.industry}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.industry && props.touched.industry) ? props.errors.industry : ''} // error={(props.errors.industry && props.touched.industry) ? props.errors.industry : ''}
/> />
<InputCompOne <InputCompOne
parentClass="w-full" parentClass="w-full"
@@ -217,7 +209,7 @@ export default function EmploymentDetail({handleNextStep}:Props) {
selectOptions={props.values.job_sector} selectOptions={props.values.job_sector}
selectValue={props.values.job_sector} selectValue={props.values.job_sector}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.job_sector && props.touched.job_sector) ? props.errors.job_sector : ''} // error={(props.errors.job_sector && props.touched.job_sector) ? props.errors.job_sector : ''}
/> />
</div> </div>
</div> </div>
@@ -234,7 +226,7 @@ export default function EmploymentDetail({handleNextStep}:Props) {
placeholder="Software Engineer" placeholder="Software Engineer"
value={props.values.job_title} value={props.values.job_title}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.job_title && props.touched.job_title) ? props.errors.job_title : ''} // error={(props.errors.job_title && props.touched.job_title) ? props.errors.job_title : ''}
/> />
<InputCompOne <InputCompOne
parentClass="w-full" parentClass="w-full"
@@ -247,7 +239,7 @@ export default function EmploymentDetail({handleNextStep}:Props) {
selectOptions={props.values.highest_eductaion} selectOptions={props.values.highest_eductaion}
selectValue={props.values.highest_eductaion} selectValue={props.values.highest_eductaion}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.highest_eductaion && props.touched.highest_eductaion) ? props.errors.highest_eductaion : ''} // error={(props.errors.highest_eductaion && props.touched.highest_eductaion) ? props.errors.highest_eductaion : ''}
/> />
<div className="w-full flex flex-col sm:flex-row items-center gap-4"> <div className="w-full flex flex-col sm:flex-row items-center gap-4">
<InputCompOne <InputCompOne
@@ -262,7 +254,7 @@ export default function EmploymentDetail({handleNextStep}:Props) {
placeholder="12/12/2015" placeholder="12/12/2015"
value={props.values.start_date} value={props.values.start_date}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.start_date && props.touched.start_date) ? props.errors.start_date : ''} // error={(props.errors.start_date && props.touched.start_date) ? props.errors.start_date : ''}
/> />
<InputCompOne <InputCompOne
parentClass="w-full" parentClass="w-full"
@@ -276,7 +268,7 @@ export default function EmploymentDetail({handleNextStep}:Props) {
placeholder="30th of every month" placeholder="30th of every month"
value={(props.values.salary_date)} value={(props.values.salary_date)}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.salary_date && props.touched.salary_date) ? props.errors.salary_date : ''} // error={(props.errors.salary_date && props.touched.salary_date) ? props.errors.salary_date : ''}
/> />
</div> </div>
<div className="w-full flex flex-col sm:flex-row items-center gap-4"> <div className="w-full flex flex-col sm:flex-row items-center gap-4">
@@ -291,7 +283,7 @@ export default function EmploymentDetail({handleNextStep}:Props) {
placeholder="1,200,000" placeholder="1,200,000"
value={FormatAmount(props.values.annual_salary)} value={FormatAmount(props.values.annual_salary)}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.annual_salary && props.touched.annual_salary) ? props.errors.annual_salary : ''} // error={(props.errors.annual_salary && props.touched.annual_salary) ? props.errors.annual_salary : ''}
/> />
<InputCompOne <InputCompOne
parentClass="w-full" parentClass="w-full"
@@ -304,7 +296,7 @@ export default function EmploymentDetail({handleNextStep}:Props) {
placeholder="100,000" placeholder="100,000"
value={FormatAmount(props.values.net_montlty)} value={FormatAmount(props.values.net_montlty)}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.net_montlty && props.touched.net_montlty) ? props.errors.net_montlty : ''} // error={(props.errors.net_montlty && props.touched.net_montlty) ? props.errors.net_montlty : ''}
/> />
</div> </div>
<InputCompOne <InputCompOne
@@ -318,7 +310,7 @@ export default function EmploymentDetail({handleNextStep}:Props) {
placeholder="LS/001/005" placeholder="LS/001/005"
value={props.values.employee_id} value={props.values.employee_id}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.employee_id && props.touched.employee_id) ? props.errors.employee_id : ''} // error={(props.errors.employee_id && props.touched.employee_id) ? props.errors.employee_id : ''}
/> />
<div className="hidden w-full"> <div className="hidden w-full">
<Button <Button
@@ -341,33 +333,33 @@ export default function EmploymentDetail({handleNextStep}:Props) {
interface SelectOption { // interface SelectOption {
loading: boolean; // loading: boolean;
data: {value: string; // data: {value: string;
label: string}[] // label: string}[]
} // }
const jobSector: SelectOption = { // const jobSector: SelectOption = {
loading: false, // loading: false,
data: [ // data: [
{ value: "", label: "Please Select" }, // { value: "", label: "Please Select" },
{ value: "private (non academic)", label: "Private (non academic)" }, // { value: "private (non academic)", label: "Private (non academic)" },
] // ]
} // }
const industry: SelectOption = { // const industry: SelectOption = {
loading: false, // loading: false,
data: [ // data: [
{ value: "", label: "Please Select" }, // { value: "", label: "Please Select" },
{ value: "engineering", label: "Engineering" }, // { value: "engineering", label: "Engineering" },
] // ]
} // }
const highestEductaion: SelectOption = { // const highestEductaion: SelectOption = {
loading: false, // loading: false,
data: [ // data: [
{ value: "", label: "Please Select" }, // { value: "", label: "Please Select" },
{ value: "b.sc + professional qualification", label: "B.Sc + Professional Qualification" }, // { value: "b.sc + professional qualification", label: "B.Sc + Professional Qualification" },
] // ]
} // }
@@ -42,16 +42,20 @@ 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"),
ref_two_name: Yup.string() ref_two_name: Yup.string()
.required("Required"), .notOneOf([Yup.ref('ref_name')], "Name cannot be the same")
.required("Required"),
ref_two_email: Yup.string() ref_two_email: Yup.string()
.email("Invalid") .email("Invalid")
.notOneOf([Yup.ref('ref_email')], "Email cannot be the same")
.required("Required"), .required("Required"),
ref_two_phone_number: Yup.string() ref_two_phone_number: Yup.string()
.notOneOf([Yup.ref('ref_phone_number')], "Phone number cannot be the same")
.required("Required"), .required("Required"),
ref_two_relationship: Yup.string() ref_two_relationship: Yup.string()
.required("Required"), .required("Required"),
ref_two_bvn: Yup.string() ref_two_bvn: Yup.string()
.required("BVN is required") .notOneOf([Yup.ref('ref_bvn')], "BVN number cannot be the same")
.required("Required")
.test("no-e", "Invalid number", (value:any) => { .test("no-e", "Invalid number", (value:any) => {
if (value && /^[0-9]*$/.test(value) == false) { if (value && /^[0-9]*$/.test(value) == false) {
return false; return false;
@@ -7,6 +7,18 @@ import { RouteHandler } from '../../../router/routes';
import { getLoanDetail } from '../../../core/apiRequest'; import { getLoanDetail } from '../../../core/apiRequest';
import CustomSpinner from '../../CustomSpinner'; import CustomSpinner from '../../CustomSpinner';
interface LoanDetail {
loan_amount: string;
payment_month: string;
sales_agent: string;
[key: string]: any; // to accommodate any additional properties
}
// interface InitialValues {
// loan_amount: string;
// payment_month: string;
// sales_agent: string;
// }
const initialValues = { const initialValues = {
loan_amount: "", loan_amount: "",
@@ -29,36 +41,48 @@ const validationSchema = Yup.object().shape({
sales_agent: Yup.string() sales_agent: Yup.string()
}); });
type LocationState = {
application_uid: string
}
export default function ReferenceDetails() { export default function ReferenceDetails() {
const location = useLocation() const location = useLocation()
const navigate = useNavigate() const navigate = useNavigate()
const applicationUID = location?.state?.application_uid // const applicationUID = location?.state?.application_uid
const stateExist = location?.state as LocationState
//FUNCTION TO HANDLE SUBMIT //FUNCTION TO HANDLE SUBMIT
const handleSubmit = (values:{}) => { const handleSubmit = (values:{}) => {
// handleNextStep(values) // handleNextStep(values)
console.log(values)
}; };
const [loanDetail, setLoanDetail] = useState<any>({ const [loanDetail, setLoanDetail] = useState<{loading:boolean, data:LoanDetail}>({
loading: true, loading: true,
data: {} data: {
loan_amount: '',
payment_month: '',
sales_agent: '',
},
}) })
useEffect(()=>{ useEffect(()=>{
if(!applicationUID){ if(!stateExist){
navigate(RouteHandler.dashboardHome) navigate(RouteHandler.dashboardHome)
return
} }
getLoanDetail({application_uid:applicationUID}).then(res => { getLoanDetail({application_uid:stateExist.application_uid}).then(res => {
setLoanDetail({loading:false, data:res?.data?.loan}) setLoanDetail({loading:false, data:res?.data?.loan})
}).catch(err => { }).catch(err => {
console.log(err) console.log(err)
setLoanDetail({loading:false, data:{}}) setLoanDetail((prev:any) => ({...prev, loading:false}))
}) })
},[]) },[])
const formInitialValue = (loanDetail.loading || Object.keys(loanDetail?.data)?.length < 1) ? initialValues : loanDetail?.data // const formInitialValue:LoanDetail = (loanDetail.loading) ? initialValues : loanDetail?.data
const formInitialValue = (loanDetail.loading || Object.keys(loanDetail?.data)?.length < 1) ? initialValues : {...initialValues, ...loanDetail?.data}
return ( return (
<> <>
{loanDetail.loading ? {loanDetail.loading ?
@@ -86,8 +110,9 @@ export default function ReferenceDetails() {
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem] text-right" inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem] text-right"
placeholder="350,000" placeholder="350,000"
value={props.values.loan_amount} value={props.values.loan_amount}
disabled={true}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.loan_amount && props.touched.loan_amount) ? props.errors.loan_amount : ''} // error={(props.errors.loan_amount && props.touched.loan_amount) ? props.errors.loan_amount : ''}
/> />
<InputCompOne <InputCompOne
parentClass="w-full sm:max-w-[10rem] flex flex-col gap-4" parentClass="w-full sm:max-w-[10rem] flex flex-col gap-4"
@@ -95,11 +120,12 @@ export default function ReferenceDetails() {
label="Months?" label="Months?"
labelClass="font-bold text-[1.125rem]" labelClass="font-bold text-[1.125rem]"
select={true} select={true}
disabled={true}
selectClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]" selectClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
selectOptions={paymentMonth} selectOptions={props.values.payment_month}
selectValue={props.values.payment_month} selectValue={props.values.payment_month}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.payment_month && props.touched.payment_month) ? props.errors.payment_month : ''} // error={(props.errors.payment_month && props.touched.payment_month) ? props.errors.payment_month : ''}
/> />
<InputCompOne <InputCompOne
parentClass="w-full sm:max-w-[10rem] flex flex-col gap-4" parentClass="w-full sm:max-w-[10rem] flex flex-col gap-4"
@@ -108,11 +134,12 @@ export default function ReferenceDetails() {
labelClass="font-bold text-[1.125rem]" labelClass="font-bold text-[1.125rem]"
floatLabel='Enter agent ID' floatLabel='Enter agent ID'
input input
disabled={true}
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]" inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
placeholder="Agent ID" placeholder="Agent ID"
value={props.values.sales_agent} value={props.values.sales_agent}
onChange={props.handleChange} onChange={props.handleChange}
error={(props.errors.sales_agent && props.touched.sales_agent) ? props.errors.sales_agent : ''} // error={(props.errors.sales_agent && props.touched.sales_agent) ? props.errors.sales_agent : ''}
/> />
</div> </div>
<Button <Button
@@ -130,20 +157,20 @@ export default function ReferenceDetails() {
); );
} }
interface SelectOption { // interface SelectOption {
loading: boolean; // loading: boolean;
data: {value: string; // data: {value: string;
label: string}[] // label: string}[]
} // }
const paymentMonth: SelectOption = { // const paymentMonth: SelectOption = {
loading: false, // loading: false,
data: [ // data: [
{ value: "", label: "Please Select" }, // { value: "", label: "Please Select" },
{ value: "6", label: "6 Months" }, // { value: "6", label: "6 Months" },
{ value: "12", label: "12 Months" }, // { value: "12", label: "12 Months" },
{ value: "18", label: "18 Months" }, // { value: "18", label: "18 Months" },
{ value: "24", label: "24 Months" }, // { value: "24", label: "24 Months" },
] // ]
} // }
+30
View File
@@ -0,0 +1,30 @@
type Props ={
text: string
onClick?: ()=>any
className?: string
shrinkAside?: boolean
icon?: string
loading?: boolean
disabled?: boolean
}
export default function MainBtn({
onClick,
className,
text,
shrinkAside,
icon,
loading,
disabled
}:Props) {
return (
<button
disabled={disabled}
className={`py-3 px-4 rounded-md ${className || ''} ${(disabled || loading) && 'opacity-60'}`}
onClick={onClick}
>
{icon && <i className={`fa-solid ${icon}`}></i>}
{shrinkAside ? '' : loading? 'Loading...' : text}
</button>
)
}
+62 -5
View File
@@ -1,7 +1,64 @@
import React from 'react' import {useEffect, useState} from 'react'
import { getPaymentDetails } from '../../core/apiRequest'
export default function Payment() { type Props = {
return ( reference: string | null
<div>Payment Page</div>
)
} }
// type PaymentPayloads = {
// uid?: string
// event?: string
// customer_code?: string
// plan_name?: string
// plan_code?: string
// subscription_code?: string | null,
// amount?: string
// authorization_code?: string
// gateway_response?: string
// gateway_status?: string
// reference?: string
// added?: string
// }
export default function Payment({reference}:Props) {
const [paymentDetails, setPaymentDetails] = useState<any>({
loading: true,
data: {}
})
useEffect(()=>{
getPaymentDetails({reference}).then(res => {
setPaymentDetails({loading:false, data:res?.data?.payment})
console.log(res?.data?.payment)
}).catch(err => {
setPaymentDetails({loading:false, data:{}})
console.log(err)
})
},[])
return (
<div className='w-full'>
<div className='p-4'>
<h1 className='p-2 mb-3 text-2xl'>Confirmation</h1>
<div className='p-8 w-full max-w-2xl bg-white shadow-md rounded-md'>
{paymentDetails.loading ?
<p>Loading...</p>
:
(paymentDetails?.data && Object.keys(paymentDetails?.data).length > 0) ?
<>
{Object.keys(paymentDetails?.data).map((item) => (
<div key={item} className='p-2 flex gap-1'>
<p className='w-64 font-semibold'>{item}</p>
<p className=''>{paymentDetails?.data[item]}</p>
</div>
))}
</>
:
<p className='p-2'>No Payment Found!</p>
}
</div>
</div>
</div>
)
}
@@ -2,7 +2,7 @@ import { ReactNode, useEffect, useState } from "react";
import { PendingTableList } from "../../core/models"; import { PendingTableList } from "../../core/models";
type PaginatedListProps = { type PaginatedListProps = {
data: PendingTableList, data: Array<PendingTableList>,
itemsPerPage?: number, itemsPerPage?: number,
filterItem?: string[], filterItem?: string[],
tableTitle?: string, tableTitle?: string,
@@ -28,12 +28,12 @@ export default function PendingList({
const handlePrev = () => { const handlePrev = () => {
if (currentPage != 0) { if (currentPage != 0) {
setCurrentPage((prev) => prev - numberOfSelection); setCurrentPage((prev:any) => prev - numberOfSelection);
} }
}; };
const handleNext = () => { const handleNext = () => {
if (currentPage < data.length) { if (currentPage < data.length) {
setCurrentPage((prev) => prev + numberOfSelection); setCurrentPage((prev:any) => prev + numberOfSelection);
} }
}; };
+3 -3
View File
@@ -17,7 +17,7 @@ export interface InputCompOneProps {
selectValue?: string; selectValue?: string;
input?: boolean; input?: boolean;
select?: boolean; select?: boolean;
selectOptions?: {loading:boolean, data:{ [index: string]: string; }[]}; selectOptions?: {loading:boolean, data:{ [index: string]: string; }[]} | any;
inputType?: string; inputType?: string;
inputClass?: string; inputClass?: string;
parentInputClass?: string; parentInputClass?: string;
@@ -110,14 +110,14 @@ const InputCompOne = forwardRef<HTMLInputElement, InputCompOneProps>(
: selectOptions.data.length && name == 'employer_uid' ? : selectOptions.data.length && name == 'employer_uid' ?
<> <>
<option value=''>Please Select</option> <option value=''>Please Select</option>
{selectOptions.data.map(({ uid, name }) => ( {selectOptions.data.map(({ uid, name }:any) => (
<option key={uid} value={uid}> <option key={uid} value={uid}>
{name} {name}
</option> </option>
))} ))}
</> </>
: selectOptions.data.length && name != 'employer_uid' ? : selectOptions.data.length && name != 'employer_uid' ?
selectOptions.data.map(({ value, label }) => ( selectOptions.data.map(({ value, label }:any) => (
<option key={value} value={value}> <option key={value} value={value}>
{label} {label}
</option> </option>
@@ -0,0 +1,149 @@
import { ReactNode, useEffect, useState } from "react";
import MainBtn from "../MainBtn";
const data1:{ name: string; email: string; status: string; location: string; }[] = [];
type PaginatedListProps = {
data: any,
itemsPerPage: number,
filterItem?: string[],
titleClass?:string,
children: (data:any) => ReactNode;
}
export default function TableWrapper({
data = data1,
itemsPerPage = 5,
filterItem,
children,
}:PaginatedListProps) {
const [isLoading, setIsLoading] = useState(true)
const [searchTerm, setSearchTerm] = useState("");
const [filteredData, setFilteredData] = useState(data);
const [currentPage, setCurrentPage] = useState(0);
const [newData, setNewData] = useState<{ name: string; email: string; status: string; location: string; }[]>([]);
const numberOfSelection = itemsPerPage;
const handlePrev = () => {
if (currentPage != 0) {
setCurrentPage((prev:any) => prev - numberOfSelection);
}
};
const handleNext = () => {
if (currentPage < data.length) {
setCurrentPage((prev:any) => prev + numberOfSelection);
}
};
const handleSearch = ({ target: { value } }:{target: {value:string}}, name:string) => {
setSearchTerm(value);
let newFilteredData:any = data.filter((item:any) =>
item[name].toLowerCase().startsWith(value.toLowerCase())
);
setFilteredData(newFilteredData);
setCurrentPage(0);
};
useEffect(() => {
setIsLoading(true)
setTimeout(()=>{
setNewData(
filteredData?.slice(currentPage, numberOfSelection + currentPage)
);
setIsLoading(false)
},1000)
}, [currentPage, filteredData]);
useEffect(()=>{
setCurrentPage(0)
},[itemsPerPage])
return (
<div className="p-2 w-full bg-white border-b dark:bg-gray-800 rounded-md">
{data.length > 0 && filterItem && (
<div className="mb-10 flex justify-end items-center gap-2">
{filterItem.map((item, index) => (
<label
key={index}
className="flex flex-col sm:flex-row items-center gap-2 text-slate-600 dark:text-slate-100 transition-all duration-500"
>
Search by {item[0].toUpperCase() + item.slice(1)}
<input
name={item}
type="text"
className="py-1 px-2 text-sm min-w-[100px] text-black dark:text-white bg-white dark:bg-slate-800 rounded-full border-0 outline-none ring-1 ring-slate-300 dark:ring-white transition-all duration-500"
value={searchTerm}
onChange={(e) => {
handleSearch(e, item);
}}
/>
</label>
))}
</div>
)}
<div className="flex flex-col">
<div className="w-full">
{children({ data: newData })}
</div>
{/* PAGINATION BUTTON */}
{(newData.length > 0 && data.length > itemsPerPage) &&
<div className='p-2 w-full flex flex-col lg:flex-row justify-center items-center gap-3 md:gap-6'>
<div className="text-sm text-center lg:text-left font-normal text-gray-500 dark:text-gray-400 block w-full">Showing <span className="font-semibold text-gray-900 dark:text-white">
{isLoading ? '----' : `${currentPage + 1}-${currentPage + numberOfSelection >= data.length ? data.length : (currentPage + numberOfSelection)}`}</span> of <span className="font-semibold text-gray-900 dark:text-white">{data.length}</span>
</div>
<div className='flex items-center gap-3 md:gap-6'>
<MainBtn
onClick={handlePrev}
text='Prev'
className={`${currentPage == 0 ? 'bg-sky-600/50 pointer-events-none' : 'bg-sky-600'} text-white-light`}
disabled={isLoading}
/>
<MainBtn
onClick={handleNext}
text='Next'
className={`${currentPage + numberOfSelection >= data.length ? 'bg-sky-600/50 pointer-events-none' : 'bg-sky-600'} text-white-light`}
disabled={isLoading}
/>
</div>
</div>
}
</div>
{/* show prev and next button if data exist */}
{/* {data.length > 0 && (
{data.length && data.map((item, index)=>{
item = item
if(index%itemsPerPage == 0 && index >= currentPage && index <= currentPage+itemsPerPage){
return (
<button
key={index}
onClick={handleNext}
className={`w-6 h-6 md:w-12 md:h-12 text-sm md:text-lg rounded-full flex justify-center items-center border transition-all duration-300 ${
currentPage != index
? "text-slate-400 border-slate-400 dark:text-slate-400 dark:border-slate-400"
: "text-slate-600 border-slate-600 dark:text-white dark:border-white pointer-events-none"
}`}
>
{index/itemsPerPage +1}
</button>
)
}
})}
</div>
)} */}
{isLoading && <TableIsLoading />}
</div>
);
}
const TableIsLoading = () => {
return (
<div className="w-full fixed z-[991] inset-0 flex justify-center items-center bg-white/50">
<p className="rounded-md shadow-md p-4 bg-white/90 dark:bg-gray-900 text-brown dark:text-white">Loading...</p>
</div>
)
}
+17
View File
@@ -67,6 +67,15 @@ export const getLoanDetail = (postData:any) => {
return getAuxEnd(`/loan/loandetail?uid=${reqData?.uid}&application_uid=${reqData?.application_uid}`, null) return getAuxEnd(`/loan/loandetail?uid=${reqData?.uid}&application_uid=${reqData?.application_uid}`, null)
} }
// FUNCTION TO GET PAYMENT DETAILS
export const getPaymentDetails = (postData:any) => {
let reqData = {
uid: localStorage.getItem('uid'),
...postData
}
return getAuxEnd(`/payment/status?uid=${reqData?.uid}&reference=${reqData?.reference}`, null)
}
// FUNCTION TO ADD CARD // FUNCTION TO ADD CARD
export const addCard = (postData:any) => { export const addCard = (postData:any) => {
let reqData = { let reqData = {
@@ -74,4 +83,12 @@ export const addCard = (postData:any) => {
...postData ...postData
} }
return postAuxEnd('/addcard', reqData) return postAuxEnd('/addcard', reqData)
}
// FUNCTION TO GET USER AGREEMENTS LIST
export const getAgreementsList = () => {
let reqData = {
uid: localStorage.getItem('uid'),
}
return getAuxEnd(`/agreements?uid=${reqData?.uid}`, null)
} }
+1 -1
View File
@@ -29,4 +29,4 @@ export type PendingTableList = {
button?: boolean button?: boolean
advise?: string advise?: string
} }
}[]; };
+1 -1
View File
@@ -207,7 +207,7 @@ const asideLinks: AsideLinksType = [
}, },
{ {
name: 'Reference Details', name: 'Reference Details',
link: '/dashboard/payments', link: "/dashboard/reference",
icon: 'dash-icon', icon: 'dash-icon',
nestedLink: [], nestedLink: [],
}, },
+92 -1
View File
@@ -1,5 +1,96 @@
import {useEffect, useState} from 'react'
import TableWrapper from "../components/tableWrapper/TableWrapper";
import { getAgreementsList } from '../core/apiRequest';
import { NewDateTimeFormatter } from '../lib/NewDateTimeFormatter';
import { FormatAmount } from '../lib/FormatAmount';
export default function DashboardLegalsPage() { export default function DashboardLegalsPage() {
const [agreementList, setAgreementList] = useState({loading:true, data:[]})
useEffect(()=>{
getAgreementsList()
.then((res) => {
if (!res || !res.data) {
setAgreementList({ loading: false, data: [] });
return;
}
setAgreementList({ loading: false, data: res?.data });
})
.catch((err) => {
setAgreementList({ loading: false, data: [] });
console.log(err)
});
},[])
return ( return (
<div>DashboardLegals</div> <div className='w-full'>
<h1 className='my-3 text-3xl font-bold'>Agreements</h1>
{agreementList.loading ? null : (
<div className="mt-5 w-full">
<TableWrapper
data={agreementList.data}
itemsPerPage={7}
>
{({ data }:{data:any}) => (
<>
<div className='w-full min-h-[300px] overflow-auto'>
<table className="py-2 w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th scope="col" className="px-4 py-2">
Date
</th>
<th scope="col" className="px-4 py-2">
Amount
</th>
<th scope="col" className="px-4 py-2">
Payment Term
</th>
<th scope="col" className="px-4 py-2">
Status
</th>
</tr>
</thead>
<tbody>
{(data && data.length > 0) ? data?.map((item:any) => (
<tr key={item.loan_uid} className="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600">
<td className="px-3 py-2">
{NewDateTimeFormatter(item?.added)}
</td>
<td className="px-3 py-2">
{FormatAmount(item?.loan_amount)}
</td>
<td className="px-3 py-2">
{item?.payment_month}
</td>
<td className="px-3 py-2">
<button
className={`${!item?.status_text?.button && 'pointer-events-none border-0'} border p-2`}
// onClick={()=>setLoanPopout({show:true, data:item})}
>
{item?.status_text?.text || 'Pending'}
</button>
</td>
</tr>
))
:
<tr className="w-3 p-3">
<td className="px-3 py-2" colSpan={5}>
<div className="flex justify-center items-center">
No Record Found
</div>
</td>
</tr>
}
</tbody>
</table>
</div>
</>
)}
</TableWrapper>
</div>
)}
</div>
) )
} }
@@ -1,6 +1,6 @@
import ReferenceDetails from "../components/Dashboard/referenceDetails/ReferenceDetails"; import ReferenceDetails from "../components/Dashboard/referenceDetails/ReferenceDetails";
export default function DashboardpaymentsPage() { export default function DashboardReferencePage() {
return ( return (
<> <>
<ReferenceDetails /> <ReferenceDetails />
+3 -3
View File
@@ -1,4 +1,4 @@
import React, {useEffect} from 'react' import {useEffect} from 'react'
import {useLocation, useNavigate} from 'react-router-dom' import {useLocation, useNavigate} from 'react-router-dom'
import Payment from '../components/Payment/Payment' import Payment from '../components/Payment/Payment'
import { RouteHandler } from '../router/routes' import { RouteHandler } from '../router/routes'
@@ -9,7 +9,7 @@ export default function PaymentPage() {
const queryParams = new URLSearchParams(location?.search); const queryParams = new URLSearchParams(location?.search);
const trxRef = queryParams.get("trxref"); const trxRef = queryParams.get("trxref");
const reference = queryParams.get("reference"); const reference = queryParams.get("reference");
console.log('LOC', trxRef, reference) // console.log('TEST', trxRef, reference)
useEffect(()=>{ useEffect(()=>{
if(!trxRef || !reference){ if(!trxRef || !reference){
@@ -18,7 +18,7 @@ export default function PaymentPage() {
},[]) },[])
return ( return (
<> <>
<Payment /> <Payment reference={reference} />
</> </>
) )
} }
+2 -2
View File
@@ -5,7 +5,7 @@ import DashboardHomePage from "./DashboardHomePage";
import DashboardLegalsPage from "./DashboardLegalsPage"; import DashboardLegalsPage from "./DashboardLegalsPage";
import DashboardProfilePage from "./DashboardProfilePage"; import DashboardProfilePage from "./DashboardProfilePage";
import DashboardVerificationPage from "./DashboardVerificationPage"; import DashboardVerificationPage from "./DashboardVerificationPage";
import DashboardpaymentsPage from "./DashboardPaymentsPage"; import DashboardReferencePage from "./DashboardReferencePage";
import TermsAndConditionPage from "./TermsAndConditionPage"; import TermsAndConditionPage from "./TermsAndConditionPage";
import PersonalBankingPage from "./PersonalBankingPage"; import PersonalBankingPage from "./PersonalBankingPage";
import BusinessBankingPage from "./BusinessBankingPage"; import BusinessBankingPage from "./BusinessBankingPage";
@@ -21,7 +21,7 @@ export {
DashboardLegalsPage, DashboardLegalsPage,
DashboardProfilePage, DashboardProfilePage,
DashboardVerificationPage, DashboardVerificationPage,
DashboardpaymentsPage, DashboardReferencePage,
TermsAndConditionPage, TermsAndConditionPage,
PersonalBankingPage, PersonalBankingPage,
BusinessBankingPage, BusinessBankingPage,
+3 -3
View File
@@ -8,7 +8,7 @@ import {
DashboardLegalsPage, DashboardLegalsPage,
DashboardProfilePage, DashboardProfilePage,
DashboardVerificationPage, DashboardVerificationPage,
DashboardpaymentsPage, DashboardReferencePage,
TermsAndConditionPage, TermsAndConditionPage,
BusinessBankingPage, BusinessBankingPage,
CooperateBankingPage, CooperateBankingPage,
@@ -60,8 +60,8 @@ const Routers = () => {
element={<DashboardVerificationPage />} element={<DashboardVerificationPage />}
/> />
<Route <Route
path={RouteHandler.dashboardPayments} path={RouteHandler.dashboardReference}
element={<DashboardpaymentsPage />} element={<DashboardReferencePage />}
/> />
<Route <Route
path={RouteHandler.dashboardLegals} path={RouteHandler.dashboardLegals}
+1 -1
View File
@@ -9,7 +9,7 @@ export class RouteHandler {
static dashboardHome = "/dashboard/home"; static dashboardHome = "/dashboard/home";
static dashboardProfile = "/dashboard/profile"; static dashboardProfile = "/dashboard/profile";
static dashboardVerification = "/dashboard/verification"; static dashboardVerification = "/dashboard/verification";
static dashboardPayments = "/dashboard/payments"; static dashboardReference = "/dashboard/reference";
static dashboardLegals = "/dashboard/legals"; static dashboardLegals = "/dashboard/legals";
static termsAndConditions = "/terms-and-conditions"; static termsAndConditions = "/terms-and-conditions";
static paymentpage = "/payment"; static paymentpage = "/payment";