diff --git a/CHANGELOG.md b/CHANGELOG.md index 65568cb2b..cab44b57c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Features +- [#222](https://github.com/alleslabs/celatone-frontend/pull/222) Add proposals of an account - [#221](https://github.com/alleslabs/celatone-frontend/pull/221) Add codes of an account - [#223](https://github.com/alleslabs/celatone-frontend/pull/223) Newer version of token card and format mechanism - [#214](https://github.com/alleslabs/celatone-frontend/pull/214) Show code permission helper text in save new code modal @@ -48,16 +49,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [#189](https://github.com/alleslabs/celatone-frontend/pull/189) Add skeleton for the account details page - [#193](https://github.com/alleslabs/celatone-frontend/pull/193) Get data for account details page -### Bug fixes - -- [#219](https://github.com/alleslabs/celatone-frontend/pull/219) Fix asset value and price formatter -- [#217](https://github.com/alleslabs/celatone-frontend/pull/217) Fix state reset in Save New Code modal and no permission in migration - ### Improvements - [#207](https://github.com/alleslabs/celatone-frontend/pull/207) Add cta to submit public project in list page - [#206](https://github.com/alleslabs/celatone-frontend/pull/206) Refactor copy functionality into one component +### Bug fixes + +- [#219](https://github.com/alleslabs/celatone-frontend/pull/219) Fix asset value and price formatter +- [#217](https://github.com/alleslabs/celatone-frontend/pull/217) Fix state reset in Save New Code modal and no permission in migration + ## v1.0.1 ### Bug fixes diff --git a/src/lib/pages/contract-details/components/tables/related-proposals/RelatedProposalsHeader.tsx b/src/lib/components/table/proposals/ProposalsTableHeader.tsx similarity index 85% rename from src/lib/pages/contract-details/components/tables/related-proposals/RelatedProposalsHeader.tsx rename to src/lib/components/table/proposals/ProposalsTableHeader.tsx index ef1524aef..9b9dc8cff 100644 --- a/src/lib/pages/contract-details/components/tables/related-proposals/RelatedProposalsHeader.tsx +++ b/src/lib/components/table/proposals/ProposalsTableHeader.tsx @@ -3,7 +3,7 @@ import { Grid } from "@chakra-ui/react"; import { TableHeader } from "lib/components/table"; -export const RelatedProposalsHeader = ({ +export const ProposalsTableHeader = ({ templateColumns, }: { templateColumns: GridProps["templateColumns"]; @@ -14,7 +14,7 @@ export const RelatedProposalsHeader = ({ Proposal Title Status Vote Finish on - Resolve Block Height + Resolved Block Height Type Proposer diff --git a/src/lib/pages/contract-details/components/tables/related-proposals/RelatedProposalsRow.tsx b/src/lib/components/table/proposals/ProposalsTableRow.tsx similarity index 87% rename from src/lib/pages/contract-details/components/tables/related-proposals/RelatedProposalsRow.tsx rename to src/lib/components/table/proposals/ProposalsTableRow.tsx index 0bd1a42ce..53d2b0ee8 100644 --- a/src/lib/pages/contract-details/components/tables/related-proposals/RelatedProposalsRow.tsx +++ b/src/lib/components/table/proposals/ProposalsTableRow.tsx @@ -4,14 +4,14 @@ import { Flex, Grid, Text } from "@chakra-ui/react"; import { ExplorerLink } from "lib/components/ExplorerLink"; import { TableRow } from "lib/components/table"; import { useGetAddressType } from "lib/hooks"; -import type { ContractRelatedProposals } from "lib/types"; +import type { Proposal } from "lib/types"; import { ProposalStatus } from "lib/types"; import { dateFromNow, formatUTC } from "lib/utils"; import { StatusChip } from "./StatusChip"; -interface RelatedProposalsRowProps { - proposal: ContractRelatedProposals; +interface ProposalsTableRowProps { + proposal: Proposal; templateColumns: GridProps["templateColumns"]; } @@ -20,9 +20,9 @@ const VotingEndTimeRender = ({ depositEndTime, status, }: { - votingEndTime: ContractRelatedProposals["votingEndTime"]; - depositEndTime: ContractRelatedProposals["depositEndTime"]; - status: ContractRelatedProposals["status"]; + votingEndTime: Proposal["votingEndTime"]; + depositEndTime: Proposal["depositEndTime"]; + status: Proposal["status"]; }) => { if (status === ProposalStatus.INACTIVE) { return N/A; @@ -59,7 +59,7 @@ const ResolvedHeightRender = ({ resolvedHeight, isInactive, }: { - resolvedHeight: RelatedProposalsRowProps["proposal"]["resolvedHeight"]; + resolvedHeight: ProposalsTableRowProps["proposal"]["resolvedHeight"]; isInactive: boolean; }) => { if (isInactive) return N/A; @@ -80,10 +80,10 @@ const ResolvedHeightRender = ({ } }; -export const RelatedProposalsRow = ({ +export const ProposalsTableRow = ({ proposal, templateColumns, -}: RelatedProposalsRowProps) => { +}: ProposalsTableRowProps) => { const getAddressType = useGetAddressType(); const isInactive = proposal.status === ProposalStatus.INACTIVE; return ( diff --git a/src/lib/pages/contract-details/components/tables/related-proposals/StatusChip.tsx b/src/lib/components/table/proposals/StatusChip.tsx similarity index 82% rename from src/lib/pages/contract-details/components/tables/related-proposals/StatusChip.tsx rename to src/lib/components/table/proposals/StatusChip.tsx index 66dd8ac18..6676c4d27 100644 --- a/src/lib/pages/contract-details/components/tables/related-proposals/StatusChip.tsx +++ b/src/lib/components/table/proposals/StatusChip.tsx @@ -1,7 +1,7 @@ import { chakra, Tag } from "@chakra-ui/react"; import type { CSSProperties } from "react"; -import type { ContractRelatedProposals } from "lib/types"; +import type { Proposal } from "lib/types"; import { ProposalStatus } from "lib/types"; const StyledTag = chakra(Tag, { @@ -16,7 +16,7 @@ const StyledTag = chakra(Tag, { }); const getBgColor = ( - status: ContractRelatedProposals["status"] + status: Proposal["status"] ): CSSProperties["backgroundColor"] => { switch (status) { case ProposalStatus.DEPOSIT_PERIOD: @@ -34,11 +34,7 @@ const getBgColor = ( } }; -export const StatusChip = ({ - status, -}: { - status: ContractRelatedProposals["status"]; -}) => { +export const StatusChip = ({ status }: { status: Proposal["status"] }) => { return ( {status === ProposalStatus.INACTIVE ? "DepositFailed" : status} diff --git a/src/lib/components/table/proposals/index.tsx b/src/lib/components/table/proposals/index.tsx new file mode 100644 index 000000000..51018a458 --- /dev/null +++ b/src/lib/components/table/proposals/index.tsx @@ -0,0 +1,2 @@ +export * from "./ProposalsTableHeader"; +export * from "./ProposalsTableRow"; diff --git a/src/lib/gql/gql.ts b/src/lib/gql/gql.ts index 6bde28333..c2e743166 100644 --- a/src/lib/gql/gql.ts +++ b/src/lib/gql/gql.ts @@ -49,7 +49,7 @@ const documents = { types.GetRelatedProposalsByContractAddressPaginationDocument, "\n query getRelatedProposalsCountByContractAddress($contractAddress: String!) {\n contract_proposals_aggregate(\n where: { contract: { address: { _eq: $contractAddress } } }\n ) {\n aggregate {\n count\n }\n }\n }\n": types.GetRelatedProposalsCountByContractAddressDocument, - "\n query getProposalsByWalletAddressPagination(\n $walletAddress: String!\n $offset: Int!\n $pageSize: Int!\n ) {\n proposals(\n where: { account: { address: { _eq: $walletAddress } } }\n offset: $offset\n limit: $pageSize\n ) {\n title\n status\n voting_end_time\n deposit_end_time\n type\n id\n }\n }\n": + "\n query getProposalsByWalletAddressPagination(\n $walletAddress: String!\n $offset: Int!\n $pageSize: Int!\n ) {\n proposals(\n where: { account: { address: { _eq: $walletAddress } } }\n order_by: { id: desc }\n offset: $offset\n limit: $pageSize\n ) {\n title\n status\n voting_end_time\n deposit_end_time\n type\n id\n contract_proposals {\n resolved_height\n }\n code_proposals {\n resolved_height\n }\n }\n }\n": types.GetProposalsByWalletAddressPaginationDocument, "\n query getProposalsCountByWalletAddress($walletAddress: String!) {\n proposals_aggregate(\n where: { account: { address: { _eq: $walletAddress } } }\n ) {\n aggregate {\n count\n }\n }\n }\n": types.GetProposalsCountByWalletAddressDocument, @@ -129,8 +129,8 @@ export function graphql( source: "\n query getRelatedProposalsCountByContractAddress($contractAddress: String!) {\n contract_proposals_aggregate(\n where: { contract: { address: { _eq: $contractAddress } } }\n ) {\n aggregate {\n count\n }\n }\n }\n" ): typeof documents["\n query getRelatedProposalsCountByContractAddress($contractAddress: String!) {\n contract_proposals_aggregate(\n where: { contract: { address: { _eq: $contractAddress } } }\n ) {\n aggregate {\n count\n }\n }\n }\n"]; export function graphql( - source: "\n query getProposalsByWalletAddressPagination(\n $walletAddress: String!\n $offset: Int!\n $pageSize: Int!\n ) {\n proposals(\n where: { account: { address: { _eq: $walletAddress } } }\n offset: $offset\n limit: $pageSize\n ) {\n title\n status\n voting_end_time\n deposit_end_time\n type\n id\n }\n }\n" -): typeof documents["\n query getProposalsByWalletAddressPagination(\n $walletAddress: String!\n $offset: Int!\n $pageSize: Int!\n ) {\n proposals(\n where: { account: { address: { _eq: $walletAddress } } }\n offset: $offset\n limit: $pageSize\n ) {\n title\n status\n voting_end_time\n deposit_end_time\n type\n id\n }\n }\n"]; + source: "\n query getProposalsByWalletAddressPagination(\n $walletAddress: String!\n $offset: Int!\n $pageSize: Int!\n ) {\n proposals(\n where: { account: { address: { _eq: $walletAddress } } }\n order_by: { id: desc }\n offset: $offset\n limit: $pageSize\n ) {\n title\n status\n voting_end_time\n deposit_end_time\n type\n id\n contract_proposals {\n resolved_height\n }\n code_proposals {\n resolved_height\n }\n }\n }\n" +): typeof documents["\n query getProposalsByWalletAddressPagination(\n $walletAddress: String!\n $offset: Int!\n $pageSize: Int!\n ) {\n proposals(\n where: { account: { address: { _eq: $walletAddress } } }\n order_by: { id: desc }\n offset: $offset\n limit: $pageSize\n ) {\n title\n status\n voting_end_time\n deposit_end_time\n type\n id\n contract_proposals {\n resolved_height\n }\n code_proposals {\n resolved_height\n }\n }\n }\n"]; export function graphql( source: "\n query getProposalsCountByWalletAddress($walletAddress: String!) {\n proposals_aggregate(\n where: { account: { address: { _eq: $walletAddress } } }\n ) {\n aggregate {\n count\n }\n }\n }\n" ): typeof documents["\n query getProposalsCountByWalletAddress($walletAddress: String!) {\n proposals_aggregate(\n where: { account: { address: { _eq: $walletAddress } } }\n ) {\n aggregate {\n count\n }\n }\n }\n"]; diff --git a/src/lib/gql/graphql.ts b/src/lib/gql/graphql.ts index 3f85b8a9b..3150f4b48 100644 --- a/src/lib/gql/graphql.ts +++ b/src/lib/gql/graphql.ts @@ -7327,6 +7327,14 @@ export type GetProposalsByWalletAddressPaginationQuery = { deposit_end_time: any; type: string; id: number; + contract_proposals: Array<{ + __typename?: "contract_proposals"; + resolved_height?: number | null; + }>; + code_proposals: Array<{ + __typename?: "code_proposals"; + resolved_height?: number | null; + }>; }>; }; @@ -10783,6 +10791,20 @@ export const GetProposalsByWalletAddressPaginationDocument = { ], }, }, + { + kind: "Argument", + name: { kind: "Name", value: "order_by" }, + value: { + kind: "ObjectValue", + fields: [ + { + kind: "ObjectField", + name: { kind: "Name", value: "id" }, + value: { kind: "EnumValue", value: "desc" }, + }, + ], + }, + }, { kind: "Argument", name: { kind: "Name", value: "offset" }, @@ -10815,6 +10837,32 @@ export const GetProposalsByWalletAddressPaginationDocument = { }, { kind: "Field", name: { kind: "Name", value: "type" } }, { kind: "Field", name: { kind: "Name", value: "id" } }, + { + kind: "Field", + name: { kind: "Name", value: "contract_proposals" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "resolved_height" }, + }, + ], + }, + }, + { + kind: "Field", + name: { kind: "Name", value: "code_proposals" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "resolved_height" }, + }, + ], + }, + }, ], }, }, diff --git a/src/lib/model/account.ts b/src/lib/model/account.ts index 25e19291e..361e2b27d 100644 --- a/src/lib/model/account.ts +++ b/src/lib/model/account.ts @@ -19,7 +19,7 @@ export const useAccountDetailsTableCounts = (walletAddress: HumanAddr) => { useContractListCountByAdmin(walletAddress); const { data: contractsCount, refetch: refetchContractsCount } = useInstantiatedCountByUserQuery(walletAddress); - const { data: proposalCount, refetch: refetchProposalCount } = + const { data: proposalsCount, refetch: refetchProposalsCount } = useProposalsCountByWalletAddress(walletAddress); const { data: countTxs, refetch: refetchCountTxs } = useTxQueryCount( walletAddress, @@ -42,12 +42,12 @@ export const useAccountDetailsTableCounts = (walletAddress: HumanAddr) => { contractsAdminCount, contractsCount, countTxs, - proposalCount, + proposalsCount, }, refetchCodesCount, refetchContractsAdminCount, refetchContractsCount, refetchCountTxs, - refetchProposalCount, + refetchProposalsCount, }; }; diff --git a/src/lib/pages/account-details/components/tables/index.tsx b/src/lib/pages/account-details/components/tables/index.tsx index 8954eea12..d093be8e5 100644 --- a/src/lib/pages/account-details/components/tables/index.tsx +++ b/src/lib/pages/account-details/components/tables/index.tsx @@ -1,2 +1,3 @@ export * from "./codes"; export * from "./contracts"; +export * from "./proposals"; diff --git a/src/lib/pages/account-details/components/tables/proposals/ProposalsTable.tsx b/src/lib/pages/account-details/components/tables/proposals/ProposalsTable.tsx new file mode 100644 index 000000000..ef9db4335 --- /dev/null +++ b/src/lib/pages/account-details/components/tables/proposals/ProposalsTable.tsx @@ -0,0 +1,124 @@ +import { Box, Flex } from "@chakra-ui/react"; +import { observer } from "mobx-react-lite"; +import type { ChangeEvent } from "react"; + +import { Loading } from "lib/components/Loading"; +import { Pagination } from "lib/components/pagination"; +import { usePaginator } from "lib/components/pagination/usePaginator"; +import { EmptyState } from "lib/components/state/EmptyState"; +import { TableContainer } from "lib/components/table"; +import { + ProposalsTableHeader, + ProposalsTableRow, +} from "lib/components/table/proposals"; +import { TableTitle } from "lib/components/table/TableTitle"; +import { ViewMore } from "lib/components/table/ViewMore"; +import { useProposalsByWalletAddressPagination } from "lib/services/proposalService"; +import type { HumanAddr, Option } from "lib/types"; + +interface ProposalsTableProps { + walletAddress: HumanAddr; + scrollComponentId: string; + totalData: Option; + refetchCount: () => void; + onViewMore?: () => void; +} + +const ProposalsTableBody = observer( + ({ + walletAddress, + scrollComponentId, + totalData, + refetchCount, + onViewMore, + }: ProposalsTableProps) => { + const { + pagesQuantity, + currentPage, + setCurrentPage, + pageSize, + setPageSize, + offset, + } = usePaginator({ + total: totalData, + initialState: { + pageSize: 10, + currentPage: 1, + isDisabled: false, + }, + }); + const { data: proposals, isLoading } = + useProposalsByWalletAddressPagination( + walletAddress, + offset, + onViewMore ? 5 : pageSize + ); + + const onPageChange = (nextPage: number) => { + refetchCount(); + setCurrentPage(nextPage); + }; + + const onPageSizeChange = (e: ChangeEvent) => { + const size = Number(e.target.value); + refetchCount(); + setPageSize(size); + setCurrentPage(1); + }; + + const templateColumns = + "100px minmax(300px, 1fr) 150px 280px 180px 190px 160px"; + + if (isLoading) return ; + if (!proposals?.length) + return ( + + + + ); + return ( + <> + + + {proposals.map((proposal) => ( + + ))} + + {totalData && + (onViewMore + ? totalData > 5 && + : totalData > 10 && ( + + ))} + + ); + } +); + +export const ProposalsTable = ({ + totalData, + ...componentProps +}: ProposalsTableProps) => ( + + + + +); diff --git a/src/lib/pages/account-details/components/tables/proposals/index.tsx b/src/lib/pages/account-details/components/tables/proposals/index.tsx new file mode 100644 index 000000000..65dc025db --- /dev/null +++ b/src/lib/pages/account-details/components/tables/proposals/index.tsx @@ -0,0 +1 @@ +export * from "./ProposalsTable"; diff --git a/src/lib/pages/account-details/index.tsx b/src/lib/pages/account-details/index.tsx index fef12df56..8360f7b0f 100644 --- a/src/lib/pages/account-details/index.tsx +++ b/src/lib/pages/account-details/index.tsx @@ -22,9 +22,10 @@ import type { HumanAddr } from "lib/types"; import { getFirstQueryParam } from "lib/utils"; import { + AdminContractsTable, CodesTable, InstantiatedContractsTable, - AdminContractsTable, + ProposalsTable, } from "./components/tables"; enum TabIndex { @@ -53,7 +54,7 @@ const AccountDetailsBody = ({ accountAddress }: AccountDetailsBodyProps) => { refetchContractsAdminCount, refetchContractsCount, // refetchCountTxs, - // refetchProposalCount, + refetchProposalsCount, } = useAccountDetailsTableCounts(accountAddress); return ( @@ -122,8 +123,8 @@ const AccountDetailsBody = ({ accountAddress }: AccountDetailsBodyProps) => { Admins setTabIndex(TabIndex.Proposals)} > Proposals @@ -158,9 +159,13 @@ const AccountDetailsBody = ({ accountAddress }: AccountDetailsBodyProps) => { refetchCount={refetchContractsAdminCount} onViewMore={() => setTabIndex(TabIndex.Admins)} /> - Contract Admin - {/* TODO: replace with the truncated Proposals table */} - Opened Proposals + setTabIndex(TabIndex.Proposals)} + /> {/* TODO: replace with the full Delegations table */} @@ -199,8 +204,12 @@ const AccountDetailsBody = ({ accountAddress }: AccountDetailsBodyProps) => { /> - {/* TODO: replace with the full Proposals table */} - Proposals + diff --git a/src/lib/pages/contract-details/components/tables/related-proposals/index.tsx b/src/lib/pages/contract-details/components/tables/related-proposals/index.tsx index da5a615f2..110ccd187 100644 --- a/src/lib/pages/contract-details/components/tables/related-proposals/index.tsx +++ b/src/lib/pages/contract-details/components/tables/related-proposals/index.tsx @@ -4,12 +4,13 @@ import { NoTransactions } from "../NoTransactions"; import { Pagination } from "lib/components/pagination"; import { usePaginator } from "lib/components/pagination/usePaginator"; import { TableContainer } from "lib/components/table"; +import { + ProposalsTableHeader, + ProposalsTableRow, +} from "lib/components/table/proposals"; import { useRelatedProposalsByContractAddressPagination } from "lib/services/proposalService"; import type { ContractAddr, Option } from "lib/types"; -import { RelatedProposalsHeader } from "./RelatedProposalsHeader"; -import { RelatedProposalsRow } from "./RelatedProposalsRow"; - interface RelatedProposalsTableProps { contractAddress: ContractAddr; scrollComponentId: string; @@ -59,7 +60,7 @@ export const RelatedProposalsTable = ({ }; const templateColumns = - "100px minmax(300px, 1fr) 150px 330px 180px 160px 160px"; + "100px minmax(300px, 1fr) 150px 280px 180px 190px 160px"; if (!relatedProposals?.length) return ( @@ -67,15 +68,17 @@ export const RelatedProposalsTable = ({ ); return ( - - - {relatedProposals.map((proposal) => ( - - ))} + <> + + + {relatedProposals.map((proposal) => ( + + ))} + {totalData && totalData > 10 && ( )} - + ); }; diff --git a/src/lib/query/proposal.ts b/src/lib/query/proposal.ts index ee8151206..90a775881 100644 --- a/src/lib/query/proposal.ts +++ b/src/lib/query/proposal.ts @@ -48,6 +48,7 @@ export const getProposalsByWalletAddressPagination = graphql(` ) { proposals( where: { account: { address: { _eq: $walletAddress } } } + order_by: { id: desc } offset: $offset limit: $pageSize ) { @@ -57,6 +58,12 @@ export const getProposalsByWalletAddressPagination = graphql(` deposit_end_time type id + contract_proposals { + resolved_height + } + code_proposals { + resolved_height + } } } `); diff --git a/src/lib/services/proposalService.ts b/src/lib/services/proposalService.ts index 16783bbf3..4cfe89aa2 100644 --- a/src/lib/services/proposalService.ts +++ b/src/lib/services/proposalService.ts @@ -11,13 +11,12 @@ import { } from "lib/query"; import type { ContractAddr, - ContractRelatedProposals, HumanAddr, Option, ProposalStatus, ProposalType, Addr, - UserProposal, + Proposal, } from "lib/types"; import { parseDate } from "lib/utils"; @@ -25,7 +24,7 @@ export const useRelatedProposalsByContractAddressPagination = ( contractAddress: ContractAddr, offset: number, pageSize: number -): UseQueryResult => { +): UseQueryResult => { const { indexerGraphClient } = useCelatoneApp(); const queryFn = useCallback(async () => { @@ -36,7 +35,7 @@ export const useRelatedProposalsByContractAddressPagination = ( pageSize, }) .then(({ contract_proposals }) => - contract_proposals.map((proposal) => ({ + contract_proposals.map((proposal) => ({ proposalId: proposal.proposal_id, title: proposal.proposal.title, status: proposal.proposal.status as ProposalStatus, @@ -99,7 +98,7 @@ export const useProposalsByWalletAddressPagination = ( walletAddress: HumanAddr, offset: number, pageSize: number -): UseQueryResult => { +): UseQueryResult => { const { indexerGraphClient } = useCelatoneApp(); const queryFn = useCallback(async () => { return indexerGraphClient @@ -109,13 +108,17 @@ export const useProposalsByWalletAddressPagination = ( pageSize, }) .then(({ proposals }) => - proposals.map((proposal) => ({ + proposals.map((proposal) => ({ proposalId: proposal.id, title: proposal.title, status: proposal.status as ProposalStatus, votingEndTime: parseDate(proposal.voting_end_time), depositEndTime: parseDate(proposal.deposit_end_time), + resolvedHeight: + proposal.code_proposals.at(0)?.resolved_height || + proposal.contract_proposals.at(0)?.resolved_height, type: proposal.type as ProposalType, + proposer: walletAddress, })) ); }, [indexerGraphClient, offset, pageSize, walletAddress]); diff --git a/src/lib/types/proposal.ts b/src/lib/types/proposal.ts index a6881609f..131852e8e 100644 --- a/src/lib/types/proposal.ts +++ b/src/lib/types/proposal.ts @@ -19,16 +19,7 @@ export enum ProposalType { SUDO_CONTRACT = "SudoContract", } -export interface UserProposal { - proposalId: number; - title: string; - status: ProposalStatus; - votingEndTime: Date; - depositEndTime: Date; - type: ProposalType; -} - -export interface ContractRelatedProposals { +export interface Proposal { proposalId: number; title: string; status: ProposalStatus;