From b77b742fef829c7c5540612e3a6bbf0f1eb8763e Mon Sep 17 00:00:00 2001 From: Gaurav Singh Date: Fri, 6 Sep 2024 15:36:12 +0530 Subject: [PATCH] feat(ID-532): Add infinite scroll to organisation list_NEW (#765) * chore: change a file * fix: ci issues: * chore: delete gen files --- .../containers/billingplans.list/columns.tsx | 2 +- .../containers/organisations.list/index.tsx | 3 +- ui/src/contexts/App.tsx | 73 ++++++++++++++----- ui/src/routes.tsx | 1 + 4 files changed, 57 insertions(+), 22 deletions(-) diff --git a/ui/src/containers/billingplans.list/columns.tsx b/ui/src/containers/billingplans.list/columns.tsx index e009879ac..367f5dcd7 100644 --- a/ui/src/containers/billingplans.list/columns.tsx +++ b/ui/src/containers/billingplans.list/columns.tsx @@ -1,6 +1,6 @@ +import { Link } from "react-router-dom"; import { ApsaraColumnDef } from "@raystack/apsara"; import { V1Beta1Plan } from "@raystack/frontier"; -import { Link } from "react-router-dom"; export const getColumns: () => ApsaraColumnDef[] = () => { return [ diff --git a/ui/src/containers/organisations.list/index.tsx b/ui/src/containers/organisations.list/index.tsx index 4754fd856..191ec1f2f 100644 --- a/ui/src/containers/organisations.list/index.tsx +++ b/ui/src/containers/organisations.list/index.tsx @@ -9,7 +9,7 @@ import { AppContext } from "~/contexts/App"; type ContextType = { organisation: V1Beta1Organization | null }; export default function OrganisationList() { - const { organizations, isLoading } = useContext(AppContext); + const { organizations, isLoading, loadMoreOrganizations } = useContext(AppContext); const tableStyle = organizations?.length ? { width: "100%" } @@ -26,6 +26,7 @@ export default function OrganisationList() { parentStyle={{ height: "calc(100vh - 60px)" }} style={tableStyle} isLoading={isLoading} + onLoadMore={loadMoreOrganizations} > diff --git a/ui/src/contexts/App.tsx b/ui/src/contexts/App.tsx index 515d49844..0d3c265e2 100644 --- a/ui/src/contexts/App.tsx +++ b/ui/src/contexts/App.tsx @@ -13,6 +13,9 @@ import { } from "@raystack/frontier"; import { useFrontier } from "@raystack/frontier/react"; +// Setting this to 1000 initially till APIs support filters and sorting. +const page_size = 1000 + type OrgMap = Record; interface AppContextValue { @@ -23,6 +26,7 @@ interface AppContextValue { plans: V1Beta1Plan[]; platformUsers?: V1Beta1ListPlatformUsersResponse; fetchPlatformUsers: () => void; + loadMoreOrganizations: () => void; } const AppContextDefaultValue = { @@ -36,6 +40,7 @@ const AppContextDefaultValue = { serviceusers: [], }, fetchPlatformUsers: () => {}, + loadMoreOrganizations: () => {} }; export const AppContext = createContext( @@ -61,34 +66,61 @@ export const AppContextProvider: React.FC = function ({ const [platformUsers, setPlatformUsers] = useState(); + const [page, setPage] = useState(1); + const [enabledOrgHasMoreData, setEnabledOrgHasMoreData] = useState(true); + const [disabledOrgHasMoreData, setDisabledOrgHasMoreData] = useState(true); + const isUserEmpty = R.either(R.isEmpty, R.isNil)(user); - useEffect(() => { - async function getOrganizations() { - setIsOrgListLoading(true); - try { - const [orgResp, disabledOrgResp] = await Promise.all([ - client?.adminServiceListAllOrganizations(), - client?.adminServiceListAllOrganizations({ state: "disabled" }), + const fetchOrganizations = useCallback(async () => { + if (!enabledOrgHasMoreData && !disabledOrgHasMoreData) return; + + setIsOrgListLoading(true); + try { + const [orgResp, disabledOrgResp] = await Promise.all([ + client?.adminServiceListAllOrganizations({ page_num: page, page_size }), + client?.adminServiceListAllOrganizations({ state: "disabled", page_num: page, page_size }), + ]); + + if (orgResp?.data?.organizations?.length) { + setEnabledOrganizations((prev: V1Beta1Organization[]) => [ + ...prev, + ...(orgResp.data.organizations || []), ]); - if (orgResp?.data?.organizations) { - setEnabledOrganizations(orgResp?.data?.organizations); - } - if (disabledOrgResp?.data?.organizations) { - setDisabledOrganizations(disabledOrgResp?.data?.organizations); - } - setIsAdmin(true); - } catch (error) { - setIsAdmin(false); - } finally { - setIsOrgListLoading(false); + } else { + setEnabledOrgHasMoreData(false); + } + + if (disabledOrgResp?.data?.organizations?.length) { + setDisabledOrganizations((prev: V1Beta1Organization[]) => [ + ...prev, + ...(disabledOrgResp.data.organizations || []), + ]); + } else { + setDisabledOrgHasMoreData(false); } + setIsAdmin(true); + } catch (error) { + console.error(error); + setIsAdmin(false); + setEnabledOrgHasMoreData(false); + setDisabledOrgHasMoreData(false); + } finally { + setIsOrgListLoading(false); + } + }, [client, page, enabledOrgHasMoreData, disabledOrgHasMoreData]); + + const loadMoreOrganizations = () => { + if (!isOrgListLoading && (enabledOrgHasMoreData || disabledOrgHasMoreData)) { + setPage((prevPage: number) => prevPage + 1); } + }; + useEffect(() => { if (!isUserEmpty) { - getOrganizations(); + fetchOrganizations(); } - }, [client, isUserEmpty]); + }, [client, isUserEmpty, page, fetchOrganizations]); const fetchPlatformUsers = useCallback(async () => { setIsPlatformUsersLoading(true); @@ -147,6 +179,7 @@ export const AppContextProvider: React.FC = function ({ plans, platformUsers, fetchPlatformUsers, + loadMoreOrganizations, }} > {children} diff --git a/ui/src/routes.tsx b/ui/src/routes.tsx index 7770690a6..ff30fbe82 100644 --- a/ui/src/routes.tsx +++ b/ui/src/routes.tsx @@ -63,6 +63,7 @@ export default memo(function AppRoutes() { const isUserEmpty = R.either(R.isEmpty, R.isNil)(user); return isLoading ? ( + // The global full page loading state is causing issues with infinite scroll. Remove this in future ) : isUserEmpty ? (