Skip to content

Commit bcf9ac1

Browse files
authored
Merge pull request #4040 from IntersectMBO/issue-4030
DRep search improvements #4030
2 parents a38ed7f + 1b47916 commit bcf9ac1

File tree

8 files changed

+293
-87
lines changed

8 files changed

+293
-87
lines changed

govtool/backend/sql/list-dreps.sql

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -317,10 +317,6 @@ WHERE
317317
(
318318
COALESCE(?, '') = '' OR
319319
(CASE WHEN LENGTH(?) % 2 = 0 AND ? ~ '^[0-9a-fA-F]+$' THEN drep_hash = ? ELSE false END) OR
320-
view ILIKE ? OR
321-
given_name ILIKE ? OR
322-
payment_address ILIKE ? OR
323-
objectives ILIKE ? OR
324-
motivations ILIKE ? OR
325-
qualifications ILIKE ?
320+
(CASE WHEN lower(?) ~ '^drep1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]+$' THEN view = lower(?) ELSE FALSE END) OR
321+
given_name ILIKE ?
326322
)

govtool/backend/src/VVA/DRep.hs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,9 @@ listDReps mSearchQuery = withPool $ \conn -> do
8686
, searchParam -- LENGTH(?)
8787
, searchParam -- AND ?
8888
, searchParam -- decode(?, 'hex')
89-
, "%" <> searchParam <> "%" -- dh.view
89+
, searchParam -- lower(?)
90+
, searchParam -- lower(?)
9091
, "%" <> searchParam <> "%" -- given_name
91-
, "%" <> searchParam <> "%" -- payment_address
92-
, "%" <> searchParam <> "%" -- objectives
93-
, "%" <> searchParam <> "%" -- motivations
94-
, "%" <> searchParam <> "%" -- qualifications
9592
) :: IO [DRepQueryResult])
9693

9794
timeZone <- liftIO getCurrentTimeZone

govtool/frontend/src/components/molecules/DataActionsBar.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Dispatch, FC, SetStateAction } from "react";
2-
import { Box, InputBase } from "@mui/material";
2+
import { Box, InputBase, IconButton } from "@mui/material";
33
import Search from "@mui/icons-material/Search";
4+
import CloseIcon from "@mui/icons-material/Close";
45

56
import { DataActionsFilters, DataActionsSorting } from "@molecules";
67
import { OrderActionsChip } from "./OrderActionsChip";
@@ -20,6 +21,7 @@ type DataActionsBarProps = {
2021
filtersTitle?: string;
2122
isFiltering?: boolean;
2223
searchText: string;
24+
placeholder?: string;
2325
setChosenFilters?: Dispatch<SetStateAction<string[]>>;
2426
setChosenSorting: Dispatch<SetStateAction<string>>;
2527
setFiltersOpen?: Dispatch<SetStateAction<boolean>>;
@@ -51,6 +53,7 @@ export const DataActionsBar: FC<DataActionsBarProps> = ({ ...props }) => {
5153
setSortOpen,
5254
sortOpen,
5355
sortOptions = [],
56+
placeholder = "Search...",
5457
} = props;
5558
const {
5659
palette: { boxShadow2 },
@@ -61,7 +64,7 @@ export const DataActionsBar: FC<DataActionsBarProps> = ({ ...props }) => {
6164
<InputBase
6265
inputProps={{ "data-testid": "search-input" }}
6366
onChange={(e) => setSearchText(e.target.value)}
64-
placeholder="Search..."
67+
placeholder={placeholder}
6568
value={searchText}
6669
startAdornment={
6770
<Search
@@ -73,6 +76,17 @@ export const DataActionsBar: FC<DataActionsBarProps> = ({ ...props }) => {
7376
}}
7477
/>
7578
}
79+
endAdornment={
80+
searchText && (
81+
<IconButton
82+
size="small"
83+
onClick={() => setSearchText("")}
84+
sx={{ ml: 1 }}
85+
>
86+
<CloseIcon fontSize="small" />
87+
</IconButton>
88+
)
89+
}
7690
sx={{
7791
bgcolor: "white",
7892
border: 1,
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import {
2+
UseInfiniteQueryOptions,
3+
useInfiniteQuery,
4+
useQuery,
5+
} from "react-query";
6+
import { useRef, useMemo } from "react";
7+
8+
import { QUERY_KEYS } from "@consts";
9+
import { useCardano } from "@context";
10+
import { GetDRepListArguments, getDRepList } from "@services";
11+
import { DRepData, Infinite } from "@/models";
12+
13+
const makeStatusKey = (status?: string[] | undefined) =>
14+
(status && status.length ? [...status].sort().join("|") : "__EMPTY__");
15+
16+
export const useGetDRepListInfiniteQuery = (
17+
{
18+
filters = [],
19+
pageSize = 10,
20+
searchPhrase,
21+
sorting,
22+
status,
23+
}: GetDRepListArguments,
24+
options?: UseInfiniteQueryOptions<Infinite<DRepData>>,
25+
) => {
26+
const { pendingTransaction } = useCardano();
27+
const totalsByStatusRef = useRef<Record<string, number>>({});
28+
const statusKey = useMemo(() => makeStatusKey(status), [status]);
29+
30+
const {
31+
data,
32+
isLoading,
33+
fetchNextPage,
34+
hasNextPage,
35+
isFetching,
36+
isFetchingNextPage,
37+
isPreviousData,
38+
} = useInfiniteQuery(
39+
[
40+
QUERY_KEYS.useGetDRepListInfiniteKey,
41+
(
42+
pendingTransaction.registerAsDirectVoter ||
43+
pendingTransaction.registerAsDrep ||
44+
pendingTransaction.retireAsDirectVoter ||
45+
pendingTransaction.retireAsDrep
46+
)?.transactionHash ?? "noPendingTransaction",
47+
filters.length ? filters : "",
48+
searchPhrase ?? "",
49+
sorting ?? "",
50+
status?.length ? status : "",
51+
],
52+
async ({ pageParam = 0 }) =>
53+
getDRepList({
54+
page: pageParam,
55+
pageSize,
56+
filters,
57+
searchPhrase,
58+
sorting,
59+
status,
60+
}),
61+
{
62+
getNextPageParam: (lastPage) => {
63+
if (lastPage.elements.length === 0) return undefined;
64+
return lastPage.page + 1;
65+
},
66+
enabled: options?.enabled,
67+
keepPreviousData: options?.keepPreviousData,
68+
onSuccess: (pagesData) => {
69+
if (!searchPhrase) {
70+
const firstPage = pagesData.pages?.[0];
71+
if (firstPage && typeof firstPage.total === "number") {
72+
totalsByStatusRef.current[statusKey] = firstPage.total;
73+
}
74+
}
75+
options?.onSuccess?.(pagesData);
76+
},
77+
},
78+
);
79+
80+
useQuery(
81+
[QUERY_KEYS.useGetDRepListInfiniteKey, "baseline", statusKey],
82+
async () => {
83+
const resp = await getDRepList({
84+
page: 0,
85+
pageSize: 1,
86+
filters,
87+
searchPhrase: "",
88+
sorting,
89+
status,
90+
});
91+
return resp;
92+
},
93+
{
94+
enabled:
95+
options?.enabled &&
96+
searchPhrase !== "" &&
97+
totalsByStatusRef.current[statusKey] === undefined,
98+
onSuccess: (resp) => {
99+
if (typeof resp.total === "number") {
100+
totalsByStatusRef.current[statusKey] = resp.total;
101+
}
102+
},
103+
},
104+
);
105+
106+
return {
107+
dRepListFetchNextPage: fetchNextPage,
108+
dRepListHasNextPage: hasNextPage,
109+
isDRepListFetching: isFetching,
110+
isDRepListFetchingNextPage: isFetchingNextPage,
111+
isDRepListLoading: isLoading,
112+
dRepData: data?.pages.flatMap((page) => page.elements),
113+
isPreviousData,
114+
dRepListTotal: data?.pages[0].total,
115+
dRepTotalsByStatus: totalsByStatusRef.current,
116+
dRepBaselineTotalForStatus: totalsByStatusRef.current[statusKey],
117+
};
118+
};
Lines changed: 83 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,108 @@
1-
import { UseInfiniteQueryOptions, useInfiniteQuery } from "react-query";
1+
import { useMemo, useRef } from "react";
2+
import { useQuery, UseQueryOptions } from "react-query";
23

34
import { QUERY_KEYS } from "@consts";
45
import { useCardano } from "@context";
56
import { GetDRepListArguments, getDRepList } from "@services";
67
import { DRepData, Infinite } from "@/models";
78

8-
export const useGetDRepListInfiniteQuery = (
9-
{
10-
filters = [],
11-
pageSize = 10,
12-
searchPhrase,
13-
sorting,
14-
status,
15-
}: GetDRepListArguments,
16-
options?: UseInfiniteQueryOptions<Infinite<DRepData>>,
17-
) => {
9+
const makeStatusKey = (status?: string[] | undefined) =>
10+
(status && status.length ? [...status].sort().join("|") : "__EMPTY__");
11+
12+
type PaginatedResult = {
13+
dRepData: DRepData[] | undefined;
14+
isLoading: boolean;
15+
isFetching: boolean;
16+
isPreviousData: boolean;
17+
total: number | undefined;
18+
baselineTotalForStatus: number | undefined;
19+
};
20+
21+
type Args = GetDRepListArguments & {
22+
page: number;
23+
pageSize?: number;
24+
};
25+
26+
export function useGetDRepListPaginatedQuery(
27+
{ page, pageSize = 10, filters = [], searchPhrase, sorting, status }: Args,
28+
options?: UseQueryOptions<Infinite<DRepData>>,
29+
): PaginatedResult {
1830
const { pendingTransaction } = useCardano();
31+
const totalsByStatusRef = useRef<Record<string, number>>({});
32+
const statusKey = useMemo(() => makeStatusKey(status), [status]);
1933

20-
const {
21-
data,
22-
isLoading,
23-
fetchNextPage,
24-
hasNextPage,
25-
isFetching,
26-
isFetchingNextPage,
27-
isPreviousData,
28-
} = useInfiniteQuery(
29-
[
30-
QUERY_KEYS.useGetDRepListInfiniteKey,
31-
(
32-
pendingTransaction.registerAsDirectVoter ||
33-
pendingTransaction.registerAsDrep ||
34-
pendingTransaction.retireAsDirectVoter ||
35-
pendingTransaction.retireAsDrep
36-
)?.transactionHash ?? "noPendingTransaction",
37-
filters.length ? filters : "",
38-
searchPhrase ?? "",
39-
sorting ?? "",
40-
status?.length ? status : "",
41-
],
42-
async ({ pageParam = 0 }) =>
34+
const queryKey = [
35+
QUERY_KEYS.useGetDRepListInfiniteKey,
36+
(
37+
pendingTransaction.registerAsDirectVoter ||
38+
pendingTransaction.registerAsDrep ||
39+
pendingTransaction.retireAsDirectVoter ||
40+
pendingTransaction.retireAsDrep
41+
)?.transactionHash ?? "noPendingTransaction",
42+
"paged",
43+
page,
44+
pageSize,
45+
filters.length ? filters : "",
46+
searchPhrase ?? "",
47+
sorting ?? "",
48+
status?.length ? status : "",
49+
];
50+
51+
const { data, isLoading, isFetching, isPreviousData } = useQuery(
52+
queryKey,
53+
async () =>
4354
getDRepList({
44-
page: pageParam,
55+
page,
4556
pageSize,
4657
filters,
4758
searchPhrase,
4859
sorting,
4960
status,
5061
}),
5162
{
52-
getNextPageParam: (lastPage) => {
53-
if (lastPage.elements.length === 0) {
54-
return undefined;
63+
keepPreviousData: true,
64+
enabled: options?.enabled,
65+
onSuccess: (resp) => {
66+
if (!searchPhrase && typeof resp?.total === "number") {
67+
totalsByStatusRef.current[statusKey] = resp.total;
5568
}
69+
options?.onSuccess?.(resp);
70+
},
71+
},
72+
);
5673

57-
return lastPage.page + 1;
74+
useQuery(
75+
[QUERY_KEYS.useGetDRepListInfiniteKey, "baseline", statusKey],
76+
async () => {
77+
const resp = await getDRepList({
78+
page: 0,
79+
pageSize: 1,
80+
filters,
81+
searchPhrase: "",
82+
sorting,
83+
status,
84+
});
85+
return resp;
86+
},
87+
{
88+
enabled:
89+
options?.enabled &&
90+
searchPhrase !== "" &&
91+
totalsByStatusRef.current[statusKey] === undefined,
92+
onSuccess: (resp) => {
93+
if (typeof resp.total === "number") {
94+
totalsByStatusRef.current[statusKey] = resp.total;
95+
}
5896
},
59-
enabled: options?.enabled,
60-
keepPreviousData: options?.keepPreviousData,
6197
},
6298
);
6399

64100
return {
65-
dRepListFetchNextPage: fetchNextPage,
66-
dRepListHasNextPage: hasNextPage,
67-
isDRepListFetching: isFetching,
68-
isDRepListFetchingNextPage: isFetchingNextPage,
69-
isDRepListLoading: isLoading,
70-
dRepData: data?.pages.flatMap((page) => page.elements),
101+
dRepData: data?.elements,
102+
isLoading,
103+
isFetching,
71104
isPreviousData,
105+
total: data?.total,
106+
baselineTotalForStatus: totalsByStatusRef.current[statusKey],
72107
};
73-
};
108+
}

govtool/frontend/src/hooks/queries/useGetDrepDetailsQuery.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { UseInfiniteQueryOptions } from "react-query";
22

33
import { Infinite, DRepData } from "@/models";
4-
import { useGetDRepListInfiniteQuery } from "./useGetDRepListQuery";
4+
import { useGetDRepListInfiniteQuery } from "./useGetDRepListInfiniteQuery";
55

66
export const useGetDRepDetailsQuery = (
77
dRepId: string | null | undefined,

govtool/frontend/src/i18n/locales/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@
314314
"myDelegationToYourself": "You have delegated <strong>₳ {{ada}}</strong> to yourself",
315315
"myDRep": "You have delegated <strong>₳ {{ada}}</strong> to this DRep",
316316
"listTitle": "Find a DRep",
317+
"searchBarPlaceholder": "Search for a DRep name or ID",
317318
"noConfidenceDefaultDescription": "Select this to signal no confidence in the current constitutional committee by voting NO on every proposal and voting YES to no confidence proposals",
318319
"noConfidenceDefaultTitle": "Signal No Confidence on Every Vote",
319320
"noResultsForTheSearchTitle": "No DReps found",

0 commit comments

Comments
 (0)