Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CFE-42, 43] Feat(pages): add search to voted proposals #860

Merged
merged 10 commits into from
Apr 2, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Features

- [#860](https://github.com/alleslabs/celatone-frontend/pull/860) Add voted proposals in voted tab
- [#862](https://github.com/alleslabs/celatone-frontend/pull/862) View failed reason
- [#853](https://github.com/alleslabs/celatone-frontend/pull/853) Add voted proposals in overview
- [#847](https://github.com/alleslabs/celatone-frontend/pull/847) Add amp proposal details page
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { EmptyState, ErrorFetching } from "lib/components/state";
import { useDebounce } from "lib/hooks";
import type { ProposalAnswerCountsResponse } from "lib/services/proposal";
import { useProposalValidatorVotes } from "lib/services/proposalService";
import { ProposalValidatorVoteType } from "lib/types";
import { ProposalVoteType } from "lib/types";
import type { Option, ProposalValidatorVote } from "lib/types";

import { ValidatorVotesTableHeader } from "./ValidatorVotesTableHeader";
Expand Down Expand Up @@ -112,8 +112,8 @@ export const ValidatorVotesTable = ({
isProposalResolved,
onViewMore,
}: ValidatorVotesTableProps) => {
const [answerFilter, setAnswerFilter] = useState<ProposalValidatorVoteType>(
ProposalValidatorVoteType.ALL
const [answerFilter, setAnswerFilter] = useState<ProposalVoteType>(
ProposalVoteType.ALL
);
const [search, setSearch] = useState("");
const debouncedSearch = useDebounce(search);
Expand Down Expand Up @@ -148,45 +148,45 @@ export const ValidatorVotesTable = ({
}, [data, setTotalData]);

const isSearching =
debouncedSearch !== "" || answerFilter !== ProposalValidatorVoteType.ALL;
debouncedSearch !== "" || answerFilter !== ProposalVoteType.ALL;

const totalValidators = answers?.totalValidators ?? 0;

const answerOptions = useMemo(
() => [
{
label: `All votes (${totalValidators})`,
value: ProposalValidatorVoteType.ALL,
value: ProposalVoteType.ALL,
disabled: false,
},
{
label: `Yes (${answers?.yes ?? 0})`,
value: ProposalValidatorVoteType.YES,
value: ProposalVoteType.YES,
disabled: false,
},
{
label: `No (${answers?.no ?? 0})`,
value: ProposalValidatorVoteType.NO,
value: ProposalVoteType.NO,
disabled: false,
},
{
label: `No with veto (${answers?.noWithVeto ?? 0})`,
value: ProposalValidatorVoteType.NO_WITH_VETO,
value: ProposalVoteType.NO_WITH_VETO,
disabled: false,
},
{
label: `Abstain (${answers?.abstain ?? 0})`,
value: ProposalValidatorVoteType.ABSTAIN,
value: ProposalVoteType.ABSTAIN,
disabled: false,
},
{
label: `Weighted (${answers?.weighted ?? 0})`,
value: ProposalValidatorVoteType.WEIGHTED,
value: ProposalVoteType.WEIGHTED,
disabled: false,
},
{
label: `Did not vote (${answers?.didNotVote ?? 0})`,
value: ProposalValidatorVoteType.DID_NOT_VOTE,
value: ProposalVoteType.DID_NOT_VOTE,
disabled: false,
},
],
Expand All @@ -199,27 +199,17 @@ export const ValidatorVotesTable = ({
setSearch(e.target.value);
};

const handleOnAnswerFilterChange = (newAnswer: ProposalValidatorVoteType) => {
const handleOnAnswerFilterChange = (newAnswer: ProposalVoteType) => {
setCurrentPage(1);
setAnswerFilter(newAnswer);
};

const onPageChange = (nextPage: number) => {
setCurrentPage(nextPage);
};

const onPageSizeChange = (e: ChangeEvent<HTMLSelectElement>) => {
const size = Number(e.target.value);
setPageSize(size);
setCurrentPage(1);
};

return (
<Box id={tableHeaderId}>
{fullVersion && (
<Grid gap={4} templateColumns={{ base: "1fr", md: "240px auto" }}>
<GridItem>
<SelectInput<ProposalValidatorVoteType>
<SelectInput<ProposalVoteType>
formLabel="Filter by Answer"
options={answerOptions}
onChange={handleOnAnswerFilterChange}
Expand Down Expand Up @@ -254,8 +244,12 @@ export const ValidatorVotesTable = ({
offset={offset}
totalData={data?.total ?? 0}
pageSize={pageSize}
onPageChange={onPageChange}
onPageSizeChange={onPageSizeChange}
onPageChange={setCurrentPage}
onPageSizeChange={(e) => {
const size = Number(e.target.value);
setPageSize(size);
setCurrentPage(1);
}}
/>
)}
{onViewMore && !!totalValidators && totalValidators > 10 && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { EmptyState, ErrorFetching } from "lib/components/state";
import { useDebounce } from "lib/hooks";
import type { ProposalAnswerCountsResponse } from "lib/services/proposal";
import { useProposalVotes } from "lib/services/proposalService";
import { ProposalVoteType } from "lib/types";
import type { Option, ProposalVote } from "lib/types";

import { ProposalVotesTableHeader } from "./ProposalVotesTableHeader";
Expand Down Expand Up @@ -87,16 +88,6 @@ interface ProposalVotesTableProps {
onViewMore?: () => void;
}

// pass it to api
enum AnswerType {
ALL = "all",
YES = "yes",
NO = "no",
NO_WITH_VETO = "no_with_veto",
ABSTAIN = "abstain",
WEIGHTED = "weighted",
}

const tableHeaderId = "proposalVotesTable";

export const ProposalVotesTable = ({
Expand All @@ -105,7 +96,9 @@ export const ProposalVotesTable = ({
fullVersion,
onViewMore,
}: ProposalVotesTableProps) => {
const [answerFilter, setAnswerFilter] = useState<AnswerType>(AnswerType.ALL);
const [answerFilter, setAnswerFilter] = useState<ProposalVoteType>(
ProposalVoteType.ALL
);
const [search, setSearch] = useState("");
const debouncedSearch = useDebounce(search);

Expand Down Expand Up @@ -134,40 +127,41 @@ export const ProposalVotesTable = ({
{ onSuccess: ({ total }) => setTotalData(total) }
);

const isSearching = debouncedSearch !== "" || answerFilter !== AnswerType.ALL;
const isSearching =
debouncedSearch !== "" || answerFilter !== ProposalVoteType.ALL;

const total = answers?.total ?? 0;

const answerOptions = useMemo(
() => [
{
label: `All votes (${total})`,
value: AnswerType.ALL,
value: ProposalVoteType.ALL,
disabled: false,
},
{
label: `Yes (${answers?.yes ?? 0})`,
value: AnswerType.YES,
value: ProposalVoteType.YES,
disabled: false,
},
{
label: `No (${answers?.no ?? 0})`,
value: AnswerType.NO,
value: ProposalVoteType.NO,
disabled: false,
},
{
label: `No with veto (${answers?.noWithVeto ?? 0})`,
value: AnswerType.NO_WITH_VETO,
value: ProposalVoteType.NO_WITH_VETO,
disabled: false,
},
{
label: `Abstain (${answers?.abstain ?? 0})`,
value: AnswerType.ABSTAIN,
value: ProposalVoteType.ABSTAIN,
disabled: false,
},
{
label: `Weighted (${answers?.weighted ?? 0})`,
value: AnswerType.WEIGHTED,
value: ProposalVoteType.WEIGHTED,
disabled: false,
},
],
Expand All @@ -180,27 +174,17 @@ export const ProposalVotesTable = ({
setSearch(e.target.value);
};

const handleOnAnswerFilterChange = (newAnswer: AnswerType) => {
const handleOnAnswerFilterChange = (newAnswer: ProposalVoteType) => {
setCurrentPage(1);
setAnswerFilter(newAnswer);
};

const onPageChange = (nextPage: number) => {
setCurrentPage(nextPage);
};

const onPageSizeChange = (e: ChangeEvent<HTMLSelectElement>) => {
const size = Number(e.target.value);
setPageSize(size);
setCurrentPage(1);
};

return (
<Box id={tableHeaderId}>
{fullVersion && (
<Grid gap={4} templateColumns={{ base: "1fr", md: "240px auto" }}>
<GridItem>
<SelectInput<AnswerType>
<SelectInput<ProposalVoteType>
formLabel="Filter by Answer"
options={answerOptions}
onChange={handleOnAnswerFilterChange}
Expand Down Expand Up @@ -234,8 +218,12 @@ export const ProposalVotesTable = ({
offset={offset}
totalData={data?.total ?? 0}
pageSize={pageSize}
onPageChange={onPageChange}
onPageSizeChange={onPageSizeChange}
onPageChange={setCurrentPage}
onPageSizeChange={(e) => {
const size = Number(e.target.value);
setPageSize(size);
setCurrentPage(1);
}}
/>
)}
{onViewMore && !!total && total > 10 && (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { Alert, Flex, Text } from "@chakra-ui/react";
import { Alert, Flex, Grid, GridItem, Text } from "@chakra-ui/react";
import type { ChangeEvent } from "react";
import { useMemo, useState } from "react";

import { useMobile } from "lib/app-provider";
import { SelectInput } from "lib/components/forms";
import { CustomIcon } from "lib/components/icon";
import InputWithIcon from "lib/components/InputWithIcon";
import { Pagination } from "lib/components/pagination";
import { usePaginator } from "lib/components/pagination/usePaginator";
import { TableTitle, ViewMore } from "lib/components/table";
import { useDebounce } from "lib/hooks";
import { useValidatorVotedProposals } from "lib/services/validatorService";
import { ProposalVoteType } from "lib/types";
import type { ValidatorAddr } from "lib/types";

import { VotedProposalsTableBody } from "./VotedProposalsTableBody";
Expand All @@ -23,6 +29,11 @@ export const VotedProposalsTable = ({
}: VotedProposalsTableProps) => {
const isMobile = useMobile();
const isMobileOverview = isMobile && !!onViewMore;
const [answerFilter, setAnswerFilter] = useState<ProposalVoteType>(
ProposalVoteType.ALL
);
const [search, setSearch] = useState("");
const debouncedSearch = useDebounce(search);

const {
pagesQuantity,
Expand All @@ -44,11 +55,62 @@ export const VotedProposalsTable = ({
validatorAddress,
onViewMore ? 5 : pageSize,
offset,
{
onSuccess: ({ total }) => setTotalData(total),
}
answerFilter,
debouncedSearch,
{ onSuccess: ({ total }) => setTotalData(total) }
);

const answerOptions = useMemo(
() => [
{
label: `All votes`,
value: ProposalVoteType.ALL,
evilpeach marked this conversation as resolved.
Show resolved Hide resolved
disabled: false,
},
{
label: `Yes`,
value: ProposalVoteType.YES,
disabled: false,
},
{
label: `No`,
value: ProposalVoteType.NO,
disabled: false,
},
{
label: `No with veto`,
value: ProposalVoteType.NO_WITH_VETO,
disabled: false,
},
{
label: `Abstain`,
value: ProposalVoteType.ABSTAIN,
disabled: false,
},
{
label: `Weighted`,
value: ProposalVoteType.WEIGHTED,
disabled: false,
},
{
label: `Did not vote`,
value: ProposalVoteType.DID_NOT_VOTE,
disabled: false,
},
],
[]
);

const handleOnSearchChange = (e: ChangeEvent<HTMLInputElement>) => {
setCurrentPage(1);
setSearch(e.target.value);
};

const handleOnAnswerFilterChange = (newAnswer: ProposalVoteType) => {
setCurrentPage(1);
setAnswerFilter(newAnswer);
};

return isMobileOverview ? (
<Flex
backgroundColor="gray.900"
Expand All @@ -66,13 +128,36 @@ export const VotedProposalsTable = ({
<Flex direction="column" gap={6}>
<TableTitle title="Voted Proposals" count={data?.total ?? 0} mb={0} />
{!onViewMore && (
<Alert variant="info" gap={4} display={{ base: "none", md: "flex" }}>
<CustomIcon boxSize={4} name="info-circle-solid" />
<Text variant="body2" color="text.dark">
Kindly note that the validator may not have voted on the proposal
due to ineligibility, such as being recently added to the network.
</Text>
</Alert>
<>
<Alert variant="info" gap={4} display={{ base: "none", md: "flex" }}>
<CustomIcon boxSize={4} name="info-circle-solid" />
<Text variant="body2" color="text.dark">
Kindly note that the validator may not have voted on the proposal
due to ineligibility, such as being recently added to the network.
</Text>
</Alert>
<Grid gap={4} templateColumns={{ base: "1fr", md: "240px auto" }}>
<GridItem>
<SelectInput<ProposalVoteType>
formLabel="Filter by vote answer"
options={answerOptions}
onChange={handleOnAnswerFilterChange}
labelBgColor="background.main"
initialSelected={answerFilter}
popoverBgColor="gray.800"
disableMaxH
/>
</GridItem>
<GridItem>
<InputWithIcon
placeholder="Search with proposal ID or proposal title..."
value={search}
onChange={handleOnSearchChange}
size="lg"
/>
</GridItem>
</Grid>
</>
)}
<VotedProposalsTableBody
data={data}
Expand Down
Loading
Loading