-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
486 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import withAuth from "contexts/Auth"; | ||
import Icon from "components/Icon"; | ||
import QueryLoader from "components/QueryLoader"; | ||
import Filter from "./Filter"; | ||
import Table from "./Table"; | ||
import usePagination from "./usePagination"; | ||
|
||
function BankingApplications() { | ||
const { | ||
data, | ||
hasMore, | ||
isError, | ||
isLoading, | ||
isLoadingNextPage, | ||
query, | ||
loadNextPage, | ||
onQueryChange, | ||
setParams, | ||
isFetching, | ||
} = usePagination(); | ||
|
||
const isLoadingOrError = isLoading || isLoadingNextPage || isError; | ||
|
||
return ( | ||
<div className="grid grid-cols-[1fr_auto] content-start gap-y-4 lg:gap-y-8 lg:gap-x-3 relative padded-container pt-8 lg:pt-20 pb-8"> | ||
<h1 className="text-center text-3xl max-lg:font-work col-span-full max-lg:mb-4"> | ||
Banking Applications | ||
</h1> | ||
<div className="relative flex gap-x-3 items-center border border-prim w-full bg-white dark:bg-blue-d6 rounded"> | ||
<Icon | ||
type="Search" | ||
size={24} | ||
className="text-gray-d2 dark:text-gray absolute top-1/2 -translate-y-1/2 left-3" | ||
/> | ||
<input | ||
disabled={isError} | ||
className="p-3 pl-10 placeholder:text-gray-d1 dark:placeholder:text-gray bg-transparent w-full outline-none disabled:bg-gray-l3 dark:disabled:bg-bluegray-d1" | ||
type="text" | ||
placeholder="Search applications" | ||
value={query} | ||
onChange={(e) => onQueryChange(e.target.value)} | ||
/> | ||
</div> | ||
<Filter | ||
isDisabled={isLoadingOrError || isFetching} | ||
setParams={setParams} | ||
classes="max-lg:col-span-full max-lg:w-full" | ||
/> | ||
<QueryLoader | ||
queryState={{ | ||
data: data?.items, | ||
isLoading: isLoading, | ||
isError: isError, | ||
}} | ||
messages={{ | ||
loading: "Loading applications...", | ||
error: "Failed to get applications", | ||
empty: "No applications found.", | ||
}} | ||
> | ||
{(applications) => ( | ||
<div className="grid col-span-full overflow-x-auto"> | ||
<Table | ||
applications={applications} | ||
hasMore={hasMore} | ||
onLoadMore={loadNextPage} | ||
disabled={isLoadingOrError} | ||
isLoading={isLoadingNextPage} | ||
/> | ||
</div> | ||
)} | ||
</QueryLoader> | ||
</div> | ||
); | ||
} | ||
export default withAuth(BankingApplications, ["ap-admin"]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import { Popover } from "@headlessui/react"; | ||
import { FC, FormEventHandler } from "react"; | ||
import { FormValues } from "./types"; | ||
import Icon from "components/Icon"; | ||
import { Field } from "components/form"; | ||
import StatusDropdown from "./StatusDropdown"; | ||
|
||
type Props = { | ||
submit: FormEventHandler<HTMLFormElement>; | ||
onReset: FormEventHandler<HTMLFormElement>; | ||
classes?: string; | ||
}; | ||
|
||
const Form: FC<Props> = ({ onReset, submit, classes = "" }) => { | ||
return ( | ||
<Popover.Panel | ||
as="form" | ||
onSubmit={submit} | ||
onReset={onReset} | ||
className={`${classes} grid content-start gap-4 w-full rounded border border-prim bg-white dark:bg-blue-d5`} | ||
> | ||
<div className="lg:hidden relative text-[1.25rem] px-4 py-3 -mb-4 font-bold uppercase"> | ||
<span className="text-orange">Filters</span> | ||
<Popover.Button className="absolute top-1/2 -translate-y-1/2 right-2"> | ||
<Icon type="Close" size={33} /> | ||
</Popover.Button> | ||
</div> | ||
<Field<FormValues> name="endowmentID" label="Endowment ID" /> | ||
<StatusDropdown classes="px-4 lg:px-6 max-lg:mb-4" /> | ||
|
||
<div className="max-lg:row-start-2 flex gap-x-4 items-center justify-between max-lg:px-4 max-lg:py-3 p-6 lg:mt-2 bg-orange-l6 dark:bg-blue-d7 border-y lg:border-t border-prim"> | ||
<h3 className="uppercase lg:hidden">Filter by</h3> | ||
<button | ||
type="reset" | ||
className="text-orange underline text-sm max-lg:ml-auto" | ||
> | ||
Reset filters | ||
</button> | ||
<button | ||
type="submit" | ||
className="btn btn-orange px-6 py-2 rounded-sm text-xs font-work font-bold uppercase" | ||
> | ||
Apply filters | ||
</button> | ||
</div> | ||
</Popover.Panel> | ||
); | ||
}; | ||
export default Form; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { FormValues as FV } from "./types"; | ||
import { Selector } from "components/Selector"; | ||
import { statuses } from "./constants"; | ||
|
||
export default function StatusDropdown({ classes = "" }) { | ||
return ( | ||
<div className={classes + " grid gap-2"}> | ||
<label className="text-sm">Status</label> | ||
<Selector<FV, "status", string> | ||
name="status" | ||
classes={{ button: "dark:bg-blue-d6" }} | ||
options={statuses} | ||
/> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { BankingApplicationStatus } from "types/aws"; | ||
import { OptionType } from "types/components"; | ||
|
||
export const statuses: OptionType<BankingApplicationStatus>[] = [ | ||
{ label: "Rejected", value: "rejected" }, | ||
{ label: "Under Review", value: "under-review" }, | ||
{ label: "Approved", value: "approved" }, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { Popover } from "@headlessui/react"; | ||
import { yupResolver } from "@hookform/resolvers/yup"; | ||
import { FormEventHandler, useRef } from "react"; | ||
import { FormProvider, useForm } from "react-hook-form"; | ||
import { FormValues as FV } from "./types"; | ||
import { BankingApplicationsQueryParams } from "types/aws"; | ||
import Icon, { DrawerIcon } from "components/Icon"; | ||
import { cleanObject } from "helpers/cleanObject"; | ||
import Form from "./Form"; | ||
import { schema } from "./schema"; | ||
|
||
type Props = { | ||
classes?: string; | ||
setParams: React.Dispatch< | ||
React.SetStateAction<BankingApplicationsQueryParams> | ||
>; | ||
isDisabled: boolean; | ||
}; | ||
|
||
export default function Filter({ setParams, classes = "", isDisabled }: Props) { | ||
const buttonRef = useRef<HTMLButtonElement | null>(null); | ||
|
||
const methods = useForm<FV>({ | ||
mode: "onChange", | ||
reValidateMode: "onChange", | ||
resolver: yupResolver(schema), | ||
defaultValues: { | ||
endowmentID: "", | ||
status: { label: "Under Review", value: "under-review" }, | ||
}, | ||
}); | ||
|
||
const { handleSubmit, reset } = methods; | ||
|
||
async function submit(data: FV) { | ||
setParams( | ||
cleanObject({ | ||
status: data.status.value, | ||
endowmentID: +data.endowmentID, | ||
requestor: "bg-admin", | ||
}) | ||
); | ||
buttonRef.current?.click(); | ||
} | ||
|
||
const onReset: FormEventHandler<HTMLFormElement> = () => { | ||
reset(); | ||
setParams({ requestor: "bg-admin" }); | ||
buttonRef.current?.click(); | ||
}; | ||
return ( | ||
<Popover className={`${classes} flex relative items-center`}> | ||
<Popover.Button | ||
ref={buttonRef} | ||
disabled={isDisabled} | ||
className="w-full lg:w-[22.3rem] flex justify-center items-center p-3 rounded bg-orange text-white lg:dark:text-gray lg:text-gray-d1 lg:bg-white lg:dark:bg-blue-d6 lg:justify-between disabled:bg-gray lg:disabled:bg-gray-l3 lg:dark:disabled:bg-bluegray-d1 lg:border lg:border-prim" | ||
> | ||
{({ open }) => ( | ||
<> | ||
<Icon className="lg:hidden" type="Filter" size={20} /> | ||
<div className="uppercase font-semibold text-[0.9375rem] "> | ||
Filter | ||
</div> | ||
<DrawerIcon isOpen={open} className="hidden lg:inline" size={21} /> | ||
</> | ||
)} | ||
</Popover.Button> | ||
|
||
<FormProvider {...methods}> | ||
<Form | ||
submit={handleSubmit(submit)} | ||
onReset={onReset} | ||
classes="max-lg:fixed max-lg:inset-x-0 max-lg:top-0 lg:mt-1 absolute top-full z-20" | ||
/> | ||
</FormProvider> | ||
</Popover> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { ObjectSchema, number, object } from "yup"; | ||
import { FormValues } from "./types"; | ||
import { SchemaShape } from "schemas/types"; | ||
import { optionType } from "schemas/shape"; | ||
|
||
export const schema = object<any, SchemaShape<FormValues>>({ | ||
endowmentID: number() | ||
.typeError("must be a number") | ||
.integer("must be integer") | ||
.min(1), | ||
status: optionType(), | ||
}) as ObjectSchema<FormValues>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { BankingApplicationStatus } from "types/aws"; | ||
import { OptionType } from "types/components"; | ||
|
||
export type FormValues = { | ||
endowmentID: string; | ||
status: OptionType<BankingApplicationStatus>; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import LoaderRing from "components/LoaderRing"; | ||
|
||
export default function LoadMoreBtn({ | ||
onLoadMore, | ||
disabled, | ||
isLoading, | ||
}: { | ||
onLoadMore(): void; | ||
disabled: boolean; | ||
isLoading: boolean; | ||
}) { | ||
return ( | ||
<button | ||
type="button" | ||
onClick={onLoadMore} | ||
disabled={disabled} | ||
className="flex items-center justify-center gap-3 uppercase text-sm font-bold rounded-b w-full h-12 hover:bg-orange-l5 dark:hover:bg-blue-d3 active:bg-orange-l4 dark:active:bg-blue-d2 disabled:bg-gray-l3 disabled:text-gray aria-disabled:bg-gray-l3 aria-disabled:dark:bg-bluegray disabled:dark:bg-bluegray" | ||
> | ||
{isLoading ? ( | ||
<> | ||
<LoaderRing thickness={10} classes="w-6" /> Loading... | ||
</> | ||
) : ( | ||
"Load More" | ||
)} | ||
</button> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { Link } from "react-router-dom"; | ||
import { TableProps } from "./types"; | ||
import { BankingApplicationStatus } from "types/aws"; | ||
import Icon from "components/Icon"; | ||
import TableSection, { Cells } from "components/TableSection"; | ||
import { appRoutes } from "constants/routes"; | ||
import LoadMoreBtn from "./LoadMoreBtn"; | ||
|
||
export default function Table({ | ||
applications, | ||
classes = "", | ||
disabled, | ||
isLoading, | ||
hasMore, | ||
onLoadMore, | ||
}: TableProps) { | ||
return ( | ||
<table | ||
className={`${classes} w-full text-sm rounded border border-separate border-spacing-0 border-prim`} | ||
> | ||
<TableSection | ||
type="thead" | ||
rowClass="bg-orange-l6 dark:bg-blue-d7 divide-x divide-prim" | ||
> | ||
<Cells | ||
type="th" | ||
cellClass="px-3 py-4 text-xs uppercase font-semibold text-left first:rounded-tl last:rounded-tr" | ||
> | ||
<>ID</> | ||
<>Endowment</> | ||
<>Bank name</> | ||
<>Account number</> | ||
<>Currency</> | ||
<>Bank Statement</> | ||
<>Date created</> | ||
<>Status</> | ||
<span className="flex justify-center">Details</span> | ||
</Cells> | ||
</TableSection> | ||
<TableSection | ||
type="tbody" | ||
rowClass="even:bg-orange-l6 dark:odd:bg-blue-d6 dark:even:bg-blue-d7 divide-x divide-prim" | ||
selectedClass="bg-orange-l5 dark:bg-blue-d4" | ||
> | ||
{applications | ||
.map((row) => ( | ||
<Cells | ||
key={row.wiseRecipientID} | ||
type="td" | ||
cellClass={`p-3 border-t border-prim max-w-[256px] truncate ${ | ||
hasMore ? "" : "first:rounded-bl last:rounded-br" | ||
}`} | ||
> | ||
<>{row.wiseRecipientID}</> | ||
<>{row.endowmentId}</> | ||
<>{new Date(row.dateCreated).toLocaleDateString()}</> | ||
<>{row.status}</> | ||
<td className="text-center"> | ||
<Status status={row.status} /> | ||
</td> | ||
<Link | ||
to={appRoutes.applications + `/${row.wiseRecipientID}`} | ||
className="text-center w-full inline-block hover:text-orange active:text-orange-d1" | ||
> | ||
<Icon | ||
size={24} | ||
type="Folder" | ||
title="application details" | ||
className="inline-block" | ||
/> | ||
</Link> | ||
</Cells> | ||
)) | ||
.concat( | ||
hasMore ? ( | ||
<td | ||
colSpan={9} | ||
key="load-more-btn" | ||
className="border-t border-prim rounded-b" | ||
> | ||
<LoadMoreBtn | ||
onLoadMore={onLoadMore} | ||
disabled={disabled} | ||
isLoading={isLoading} | ||
/> | ||
</td> | ||
) : ( | ||
[] | ||
) | ||
)} | ||
</TableSection> | ||
</table> | ||
); | ||
} | ||
|
||
const bg: { [key in BankingApplicationStatus]: string } = { | ||
approved: "bg-green", | ||
"under-review": "bg-gray-d1", | ||
rejected: "bg-red", | ||
}; | ||
|
||
const text: { [key in BankingApplicationStatus]: string } = { | ||
"under-review": "Under review", | ||
rejected: "Rejected", | ||
approved: "Approved", | ||
}; | ||
|
||
function Status({ status }: { status: BankingApplicationStatus }) { | ||
return ( | ||
<p | ||
className={`${bg[status]} rounded px-3 py-1 inline-block uppercase text-xs text-white`} | ||
> | ||
{text[status]} | ||
</p> | ||
); | ||
} |
Oops, something went wrong.