All files / src/features/ChangeLog/hooks useListUsers.tsx

70% Statements 21/30
47.36% Branches 18/38
90% Functions 9/10
74.07% Lines 20/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112                                          117x   117x             22x     22x     1x 1x               23x 23x     23x                   22x 20x 6x       22x                           22x 1x 1x       22x 20x 6x                           22x                    
import { customInstance } from "@services/api";
import { useInfiniteQuery } from "react-query";
import { QKEY_GET_CHANGELOG_USERS } from "@constants/queryKeys";
import { useMemo, useState } from "react";
import Placeholder from "@assets/images/avatar-placeholder.png";
import { GENERAL_STALE_TIME } from "@features/Merchants/MerchantSidePanel/constants";
 
type UserType = {
  userAccID: number;
  accID: number;
  email: string;
  firstName: string;
  lastName: string;
  imageURL: string;
};
 
type QueryReturnType = {
  total: number;
  data: UserType[] | null;
};
 
const BOTTOM_OFFSET_PX = 8;
 
const useListUsers = ({
  merchantId,
  enabled = true,
}: {
  merchantId: number;
  enabled?: boolean;
}) => {
  const [searchValue, setSearchValue] = useState("");
 
  const { data, isLoading, isFetchingNextPage, fetchNextPage, hasNextPage } =
    useInfiniteQuery<QueryReturnType>(
      [QKEY_GET_CHANGELOG_USERS, merchantId, searchValue],
      async ({ pageParam = 1 }) => {
        const searchQuery = searchValue ? `&q="${searchValue}"` : "";
        return customInstance({
          url: `/merchants/${merchantId}/changelogs/users?page=${pageParam}${searchQuery}`,
          method: "GET",
        });
      },
      {
        enabled: !!merchantId && enabled,
        getNextPageParam: (lastPage, allPages) => {
          const totalFetched = allPages.reduce(
            (sum, p) => sum + (p.data?.length ?? 0),
            0,
          );
          return totalFetched < lastPage.total
            ? allPages.length + 1
            : undefined;
        },
        refetchOnWindowFocus: false,
        refetchOnMount: false,
        staleTime: GENERAL_STALE_TIME,
      },
    );
 
  const allUsers = useMemo(() => {
    return (
      data?.pages?.flatMap((page) => page.data ?? []).filter(Boolean) ?? []
    );
  }, [data?.pages]);
 
  const handleScroll = (e: React.UIEvent<HTMLElement> | undefined) => {
    // Guard against undefined event or currentTarget
    if (!e || !e.currentTarget) return;
    if (isFetchingNextPage || !hasNextPage) return;
 
    const { scrollTop, clientHeight, scrollHeight } = e.currentTarget;
 
    const isNearBottom =
      scrollHeight - (scrollTop + clientHeight) <= BOTTOM_OFFSET_PX;
    if (isNearBottom) {
      fetchNextPage();
    }
  };
 
  const handleSearch = (value: string) => {
    const newValue = value ?? "";
    Eif (searchValue === newValue) return;
    setSearchValue(newValue);
  };
 
  const formattedData = useMemo(() => {
    return (
      allUsers?.map((category) => ({
        label:
          category.firstName && category.lastName
            ? `${category.firstName} ${category.lastName}`
            : category.email,
        value: category.userAccID,
        id: category.userAccID,
        imageURL: category.imageURL
          ? `${category.imageURL}/thumb`
          : Placeholder,
      })) ?? []
    );
  }, [allUsers]);
 
  return {
    data: formattedData,
    handleScroll,
    handleSearch,
    isLoading: isLoading || isFetchingNextPage,
    searchValue,
  };
};
 
export default useListUsers;