diff --git a/CHANGELOG.md b/CHANGELOG.md index f48c60aef..9a67b1afa 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 +- [#84](https://github.com/alleslabs/celatone-frontend/pull/84) Contract proposals table ui and wireup - [#82](https://github.com/alleslabs/celatone-frontend/pull/82) Add all codes page - [#83](https://github.com/alleslabs/celatone-frontend/pull/83) Add invalid code state - [#73](https://github.com/alleslabs/celatone-frontend/pull/73) Wireup migration table diff --git a/src/lib/data/queries.ts b/src/lib/data/queries.ts index 7ccfda54f..d1e37ed61 100644 --- a/src/lib/data/queries.ts +++ b/src/lib/data/queries.ts @@ -184,6 +184,46 @@ export const getMigrationHistoriesCountByContractAddress = graphql(` } `); +export const getRelatedProposalsByContractAddress = graphql(` + query getRelatedProposalsByContractAddress( + $contractAddress: String! + $offset: Int! + $pageSize: Int! + ) { + contract_proposals( + where: { contract: { address: { _eq: $contractAddress } } } + order_by: { proposal_id: desc } + offset: $offset + limit: $pageSize + ) { + proposal { + title + status + voting_end_time + deposit_end_time + type + account { + address + } + } + proposal_id + resolved_height + } + } +`); + +export const getRelatedProposalsCountByContractAddress = graphql(` + query getRelatedProposalsCountByContractAddress($contractAddress: String!) { + contract_proposals_aggregate( + where: { contract: { address: { _eq: $contractAddress } } } + ) { + aggregate { + count + } + } + } +`); + export const getContractListByCodeId = graphql(` query getContractListByCodeId($codeId: Int!, $offset: Int!, $pageSize: Int!) { contracts( diff --git a/src/lib/gql/gql.ts b/src/lib/gql/gql.ts index b6725c731..ce31f30e7 100644 --- a/src/lib/gql/gql.ts +++ b/src/lib/gql/gql.ts @@ -23,6 +23,10 @@ const documents = { types.GetMigrationHistoriesByContractAddressDocument, "\n query getMigrationHistoriesCountByContractAddress($contractAddress: String!) {\n contract_histories_aggregate(\n where: { contract: { address: { _eq: $contractAddress } } }\n ) {\n aggregate {\n count\n }\n }\n }\n": types.GetMigrationHistoriesCountByContractAddressDocument, + "\n query getRelatedProposalsByContractAddress(\n $contractAddress: String!\n $offset: Int!\n $pageSize: Int!\n ) {\n contract_proposals(\n where: { contract: { address: { _eq: $contractAddress } } }\n order_by: { proposal_id: desc }\n offset: $offset\n limit: $pageSize\n ) {\n proposal {\n title\n status\n voting_end_time\n deposit_end_time\n type\n account {\n address\n }\n }\n proposal_id\n resolved_height\n }\n }\n": + types.GetRelatedProposalsByContractAddressDocument, + "\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 getContractListByCodeId($codeId: Int!, $offset: Int!, $pageSize: Int!) {\n contracts(\n where: { code_id: { _eq: $codeId } }\n order_by: { transaction: { block: { timestamp: desc } } }\n offset: $offset\n limit: $pageSize\n ) {\n address\n label\n transaction {\n block {\n timestamp\n }\n account {\n address\n }\n }\n }\n }\n": types.GetContractListByCodeIdDocument, "\n query getContractListCountByCodeId($codeId: Int!) {\n contracts_aggregate(where: { code_id: { _eq: $codeId } }) {\n aggregate {\n count\n }\n }\n }\n": @@ -61,6 +65,12 @@ export function graphql( export function graphql( source: "\n query getMigrationHistoriesCountByContractAddress($contractAddress: String!) {\n contract_histories_aggregate(\n where: { contract: { address: { _eq: $contractAddress } } }\n ) {\n aggregate {\n count\n }\n }\n }\n" ): typeof documents["\n query getMigrationHistoriesCountByContractAddress($contractAddress: String!) {\n contract_histories_aggregate(\n where: { contract: { address: { _eq: $contractAddress } } }\n ) {\n aggregate {\n count\n }\n }\n }\n"]; +export function graphql( + source: "\n query getRelatedProposalsByContractAddress(\n $contractAddress: String!\n $offset: Int!\n $pageSize: Int!\n ) {\n contract_proposals(\n where: { contract: { address: { _eq: $contractAddress } } }\n order_by: { proposal_id: desc }\n offset: $offset\n limit: $pageSize\n ) {\n proposal {\n title\n status\n voting_end_time\n deposit_end_time\n type\n account {\n address\n }\n }\n proposal_id\n resolved_height\n }\n }\n" +): typeof documents["\n query getRelatedProposalsByContractAddress(\n $contractAddress: String!\n $offset: Int!\n $pageSize: Int!\n ) {\n contract_proposals(\n where: { contract: { address: { _eq: $contractAddress } } }\n order_by: { proposal_id: desc }\n offset: $offset\n limit: $pageSize\n ) {\n proposal {\n title\n status\n voting_end_time\n deposit_end_time\n type\n account {\n address\n }\n }\n proposal_id\n resolved_height\n }\n }\n"]; +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 getContractListByCodeId($codeId: Int!, $offset: Int!, $pageSize: Int!) {\n contracts(\n where: { code_id: { _eq: $codeId } }\n order_by: { transaction: { block: { timestamp: desc } } }\n offset: $offset\n limit: $pageSize\n ) {\n address\n label\n transaction {\n block {\n timestamp\n }\n account {\n address\n }\n }\n }\n }\n" ): typeof documents["\n query getContractListByCodeId($codeId: Int!, $offset: Int!, $pageSize: Int!) {\n contracts(\n where: { code_id: { _eq: $codeId } }\n order_by: { transaction: { block: { timestamp: desc } } }\n offset: $offset\n limit: $pageSize\n ) {\n address\n label\n transaction {\n block {\n timestamp\n }\n account {\n address\n }\n }\n }\n }\n"]; diff --git a/src/lib/gql/graphql.ts b/src/lib/gql/graphql.ts index 2d85c0d5d..f96d32909 100644 --- a/src/lib/gql/graphql.ts +++ b/src/lib/gql/graphql.ts @@ -6424,6 +6424,45 @@ export type GetMigrationHistoriesCountByContractAddressQuery = { }; }; +export type GetRelatedProposalsByContractAddressQueryVariables = Exact<{ + contractAddress: Scalars["String"]; + offset: Scalars["Int"]; + pageSize: Scalars["Int"]; +}>; + +export type GetRelatedProposalsByContractAddressQuery = { + __typename?: "query_root"; + contract_proposals: Array<{ + __typename?: "contract_proposals"; + proposal_id: number; + resolved_height?: number | null; + proposal: { + __typename?: "proposals"; + title: string; + status: any; + voting_end_time: any; + deposit_end_time: any; + type: string; + account?: { __typename?: "accounts"; address: string } | null; + }; + }>; +}; + +export type GetRelatedProposalsCountByContractAddressQueryVariables = Exact<{ + contractAddress: Scalars["String"]; +}>; + +export type GetRelatedProposalsCountByContractAddressQuery = { + __typename?: "query_root"; + contract_proposals_aggregate: { + __typename?: "contract_proposals_aggregate"; + aggregate?: { + __typename?: "contract_proposals_aggregate_fields"; + count: number; + } | null; + }; +}; + export type GetContractListByCodeIdQueryVariables = Exact<{ codeId: Scalars["Int"]; offset: Scalars["Int"]; @@ -7789,6 +7828,278 @@ export const GetMigrationHistoriesCountByContractAddressDocument = { GetMigrationHistoriesCountByContractAddressQuery, GetMigrationHistoriesCountByContractAddressQueryVariables >; +export const GetRelatedProposalsByContractAddressDocument = { + kind: "Document", + definitions: [ + { + kind: "OperationDefinition", + operation: "query", + name: { kind: "Name", value: "getRelatedProposalsByContractAddress" }, + variableDefinitions: [ + { + kind: "VariableDefinition", + variable: { + kind: "Variable", + name: { kind: "Name", value: "contractAddress" }, + }, + type: { + kind: "NonNullType", + type: { + kind: "NamedType", + name: { kind: "Name", value: "String" }, + }, + }, + }, + { + kind: "VariableDefinition", + variable: { + kind: "Variable", + name: { kind: "Name", value: "offset" }, + }, + type: { + kind: "NonNullType", + type: { kind: "NamedType", name: { kind: "Name", value: "Int" } }, + }, + }, + { + kind: "VariableDefinition", + variable: { + kind: "Variable", + name: { kind: "Name", value: "pageSize" }, + }, + type: { + kind: "NonNullType", + type: { kind: "NamedType", name: { kind: "Name", value: "Int" } }, + }, + }, + ], + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "contract_proposals" }, + arguments: [ + { + kind: "Argument", + name: { kind: "Name", value: "where" }, + value: { + kind: "ObjectValue", + fields: [ + { + kind: "ObjectField", + name: { kind: "Name", value: "contract" }, + value: { + kind: "ObjectValue", + fields: [ + { + kind: "ObjectField", + name: { kind: "Name", value: "address" }, + value: { + kind: "ObjectValue", + fields: [ + { + kind: "ObjectField", + name: { kind: "Name", value: "_eq" }, + value: { + kind: "Variable", + name: { + kind: "Name", + value: "contractAddress", + }, + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + { + kind: "Argument", + name: { kind: "Name", value: "order_by" }, + value: { + kind: "ObjectValue", + fields: [ + { + kind: "ObjectField", + name: { kind: "Name", value: "proposal_id" }, + value: { kind: "EnumValue", value: "desc" }, + }, + ], + }, + }, + { + kind: "Argument", + name: { kind: "Name", value: "offset" }, + value: { + kind: "Variable", + name: { kind: "Name", value: "offset" }, + }, + }, + { + kind: "Argument", + name: { kind: "Name", value: "limit" }, + value: { + kind: "Variable", + name: { kind: "Name", value: "pageSize" }, + }, + }, + ], + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "proposal" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "title" } }, + { + kind: "Field", + name: { kind: "Name", value: "status" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "voting_end_time" }, + }, + { + kind: "Field", + name: { kind: "Name", value: "deposit_end_time" }, + }, + { kind: "Field", name: { kind: "Name", value: "type" } }, + { + kind: "Field", + name: { kind: "Name", value: "account" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "address" }, + }, + ], + }, + }, + ], + }, + }, + { kind: "Field", name: { kind: "Name", value: "proposal_id" } }, + { + kind: "Field", + name: { kind: "Name", value: "resolved_height" }, + }, + ], + }, + }, + ], + }, + }, + ], +} as unknown as DocumentNode< + GetRelatedProposalsByContractAddressQuery, + GetRelatedProposalsByContractAddressQueryVariables +>; +export const GetRelatedProposalsCountByContractAddressDocument = { + kind: "Document", + definitions: [ + { + kind: "OperationDefinition", + operation: "query", + name: { + kind: "Name", + value: "getRelatedProposalsCountByContractAddress", + }, + variableDefinitions: [ + { + kind: "VariableDefinition", + variable: { + kind: "Variable", + name: { kind: "Name", value: "contractAddress" }, + }, + type: { + kind: "NonNullType", + type: { + kind: "NamedType", + name: { kind: "Name", value: "String" }, + }, + }, + }, + ], + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "contract_proposals_aggregate" }, + arguments: [ + { + kind: "Argument", + name: { kind: "Name", value: "where" }, + value: { + kind: "ObjectValue", + fields: [ + { + kind: "ObjectField", + name: { kind: "Name", value: "contract" }, + value: { + kind: "ObjectValue", + fields: [ + { + kind: "ObjectField", + name: { kind: "Name", value: "address" }, + value: { + kind: "ObjectValue", + fields: [ + { + kind: "ObjectField", + name: { kind: "Name", value: "_eq" }, + value: { + kind: "Variable", + name: { + kind: "Name", + value: "contractAddress", + }, + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + ], + selectionSet: { + kind: "SelectionSet", + selections: [ + { + kind: "Field", + name: { kind: "Name", value: "aggregate" }, + selectionSet: { + kind: "SelectionSet", + selections: [ + { kind: "Field", name: { kind: "Name", value: "count" } }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + ], +} as unknown as DocumentNode< + GetRelatedProposalsCountByContractAddressQuery, + GetRelatedProposalsCountByContractAddressQueryVariables +>; export const GetContractListByCodeIdDocument = { kind: "Document", definitions: [ diff --git a/src/lib/model/contract.ts b/src/lib/model/contract.ts index 079384041..c3c6ec165 100644 --- a/src/lib/model/contract.ts +++ b/src/lib/model/contract.ts @@ -16,6 +16,7 @@ import { useInstantiateDetailByContractQuery, useInstantiatedListByUserQuery, useMigrationHistoriesCountByContractAddress, + useRelatedProposalsCountByContractAddress, } from "lib/services/contractService"; import type { CodeLocalInfo } from "lib/stores/code"; import type { ContractInfo, ContractListInfo } from "lib/stores/contract"; @@ -145,12 +146,16 @@ export const useContractDetailsTableCounts = ( useExecuteTxsCountByContractAddress(contractAddress); const { data: migrationCount = 0, refetch: refetchMigration } = useMigrationHistoriesCountByContractAddress(contractAddress); + const { data: relatedProposalsCount = 0, refetch: refetchRelatedProposals } = + useRelatedProposalsCountByContractAddress(contractAddress); return { tableCounts: { executeCount, migrationCount, + relatedProposalsCount, }, refetchExecute, refetchMigration, + refetchRelatedProposals, }; }; diff --git a/src/lib/pages/contract-details/components/tables/execute/Execute.tsx b/src/lib/pages/contract-details/components/tables/execute/Execute.tsx index 6562fd2c9..4fbb5688d 100644 --- a/src/lib/pages/contract-details/components/tables/execute/Execute.tsx +++ b/src/lib/pages/contract-details/components/tables/execute/Execute.tsx @@ -85,16 +85,18 @@ export const ExecuteTable = ({ templateColumnsStyle={templateColumnsStyle} /> ))} - + {totalData > 10 && ( + + )} ); }; diff --git a/src/lib/pages/contract-details/components/tables/migration/MigrationHeader.tsx b/src/lib/pages/contract-details/components/tables/migration/MigrationHeader.tsx index 0a43aa201..a4baf3cb3 100644 --- a/src/lib/pages/contract-details/components/tables/migration/MigrationHeader.tsx +++ b/src/lib/pages/contract-details/components/tables/migration/MigrationHeader.tsx @@ -1,17 +1,7 @@ import type { GridProps } from "@chakra-ui/react"; -import { chakra, Grid, GridItem } from "@chakra-ui/react"; +import { Grid } from "@chakra-ui/react"; -const StyledGridItem = chakra(GridItem, { - baseStyle: { - color: "text.main", - fontSize: "12px", - fontWeight: 700, - py: 6, - px: 4, - borderY: "1px solid", - borderColor: "divider.main", - }, -}); +import { TableHeader } from "lib/components/table"; export const MigrationHeader = ({ templateColumns, @@ -20,12 +10,12 @@ export const MigrationHeader = ({ }) => { return ( - Code ID - Code Description - Sender - Block Height - Timestamp - Remark + Code ID + Code Description + Sender + Block Height + Timestamp + Remark ); }; diff --git a/src/lib/pages/contract-details/components/tables/migration/MigrationRow.tsx b/src/lib/pages/contract-details/components/tables/migration/MigrationRow.tsx index 17815c910..1ce0176ee 100644 --- a/src/lib/pages/contract-details/components/tables/migration/MigrationRow.tsx +++ b/src/lib/pages/contract-details/components/tables/migration/MigrationRow.tsx @@ -1,25 +1,13 @@ import type { GridProps } from "@chakra-ui/react"; -import { chakra, Flex, Grid, GridItem, Text } from "@chakra-ui/react"; +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 { ContractMigrationHistory } from "lib/types"; import { RemarkOperation } from "lib/types"; import { dateFromNow, formatUTC } from "lib/utils"; -const StyledGridItem = chakra(GridItem, { - baseStyle: { - color: "text.main", - fontSize: "14px", - fontWeight: 400, - p: 4, - display: "flex", - alignItems: "center", - borderBottom: "1px solid", - borderColor: "divider.main", - }, -}); - interface MigrationRowProps { templateColumns: GridProps["templateColumns"]; history: ContractMigrationHistory; @@ -36,6 +24,12 @@ const RemarkRender = ({ operation === RemarkOperation.CONTRACT_CODE_HISTORY_OPERATION_TYPE_GENESIS ) return Genesis; + + const prefix = + operation === RemarkOperation.CONTRACT_CODE_HISTORY_OPERATION_TYPE_INIT + ? "Instantiate" + : "Migrate"; + return ( -

{isGovernance ? "Through Proposal ID" : "Tx Hash"}

+

{isGovernance ? `${prefix} Proposal ID` : `${prefix} Tx`}

- + - - + + {history.codeDescription || ( No Description )} - - + + - - + + - - + + {formatUTC(history.timestamp)}

({dateFromNow(history.timestamp)})

-
- + + - + ); }; diff --git a/src/lib/pages/contract-details/components/tables/migration/index.tsx b/src/lib/pages/contract-details/components/tables/migration/index.tsx index 2228186fc..0bb5e2708 100644 --- a/src/lib/pages/contract-details/components/tables/migration/index.tsx +++ b/src/lib/pages/contract-details/components/tables/migration/index.tsx @@ -75,7 +75,7 @@ export const MigrationTable = ({ templateColumns={templateColumns} /> ))} - {migrationHistories.length > 10 && ( + {totalData > 10 && ( { + return ( + + Proposal ID + Proposal Title + Status + Vote Finish on + Resolve Block Height + Type + Proposer + + ); +}; diff --git a/src/lib/pages/contract-details/components/tables/related-proposals/RelatedProposalsRow.tsx b/src/lib/pages/contract-details/components/tables/related-proposals/RelatedProposalsRow.tsx new file mode 100644 index 000000000..60620abd8 --- /dev/null +++ b/src/lib/pages/contract-details/components/tables/related-proposals/RelatedProposalsRow.tsx @@ -0,0 +1,106 @@ +import type { GridProps } from "@chakra-ui/react"; +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 { ProposalStatus } from "lib/types"; +import { dateFromNow, formatUTC } from "lib/utils"; + +import { StatusChip } from "./StatusChip"; + +interface RelatedProposalsRowProps { + proposal: ContractRelatedProposals; + templateColumns: GridProps["templateColumns"]; +} + +const VotingEndTimeRender = ({ + votingEndTime, + depositEndTime, + status, +}: { + votingEndTime: ContractRelatedProposals["votingEndTime"]; + depositEndTime: ContractRelatedProposals["depositEndTime"]; + status: ContractRelatedProposals["status"]; +}) => { + const isDepositPeriod = status === ProposalStatus.DEPOSIT_PERIOD; + return ( + p:first-of-type": { + color: isDepositPeriod ? "text.dark" : "text.main", + mb: "2px", + }, + "& > p:last-of-type": { + color: "text.dark", + fontSize: "12px", + }, + }} + > +

{isDepositPeriod ? "Voting not started" : formatUTC(votingEndTime)}

+

+ ( + {isDepositPeriod + ? `Deposit Period ends in ${dateFromNow(depositEndTime)}` + : dateFromNow(votingEndTime)} + ) +

+
+ ); +}; + +export const RelatedProposalsRow = ({ + proposal, + templateColumns, +}: RelatedProposalsRowProps) => { + const getAddressType = useGetAddressType(); + return ( + + + + + {proposal.title} + + + + + + + + {proposal.resolvedHeight ? ( + + ) : ( + Pending + )} + + + {proposal.type} + + + {proposal.proposer ? ( + + ) : ( + "N/A" + )} + + + ); +}; diff --git a/src/lib/pages/contract-details/components/tables/related-proposals/StatusChip.tsx b/src/lib/pages/contract-details/components/tables/related-proposals/StatusChip.tsx new file mode 100644 index 000000000..728e9d2c9 --- /dev/null +++ b/src/lib/pages/contract-details/components/tables/related-proposals/StatusChip.tsx @@ -0,0 +1,43 @@ +import { chakra, Tag } from "@chakra-ui/react"; +import type { CSSProperties } from "react"; + +import type { ContractRelatedProposals } from "lib/types"; +import { ProposalStatus } from "lib/types"; + +const StyledTag = chakra(Tag, { + baseStyle: { + borderRadius: "16px", + fontSize: "12px", + fontWeight: 400, + color: "text.main", + height: "24px", + w: "fit-content", + }, +}); + +const getBgColor = ( + status: ContractRelatedProposals["status"] +): CSSProperties["backgroundColor"] => { + switch (status) { + case ProposalStatus.DEPOSIT_PERIOD: + return "#BA863A"; + case ProposalStatus.FAILED: + case ProposalStatus.REJECTED: + return "#F2605B"; + case ProposalStatus.PASSED: + return "#A1E58F"; + case ProposalStatus.VOTING_PERIOD: + return "#0288D1"; + case ProposalStatus.INACTIVE: + default: + return "rgba(173, 173, 173, 0.6)"; + } +}; + +export const StatusChip = ({ + status, +}: { + status: ContractRelatedProposals["status"]; +}) => { + return {status}; +}; 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 new file mode 100644 index 000000000..51efbf04a --- /dev/null +++ b/src/lib/pages/contract-details/components/tables/related-proposals/index.tsx @@ -0,0 +1,92 @@ +import { Flex } from "@chakra-ui/react"; +import type { ChangeEvent } from "react"; + +import { NoTransactions } from "../NoTransactions"; +import { Pagination } from "lib/components/pagination"; +import { usePaginator } from "lib/components/pagination/usePaginator"; +import { useRelatedProposalsByContractAddress } from "lib/services/contractService"; +import type { ContractAddr } from "lib/types"; + +import { RelatedProposalsHeader } from "./RelatedProposalsHeader"; +import { RelatedProposalsRow } from "./RelatedProposalsRow"; + +interface RelatedProposalsTableProps { + contractAddress: ContractAddr; + scrollComponentId: string; + totalData: number; + refetchCount: () => void; +} + +export const RelatedProposalsTable = ({ + contractAddress, + scrollComponentId, + totalData, + refetchCount, +}: RelatedProposalsTableProps) => { + const { + pagesQuantity, + currentPage, + setCurrentPage, + pageSize, + setPageSize, + offset, + } = usePaginator({ + total: totalData, + initialState: { + pageSize: 10, + currentPage: 1, + isDisabled: false, + }, + }); + + const { data: relatedProposals } = useRelatedProposalsByContractAddress( + contractAddress, + offset, + 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 330px 180px 140px 160px"; + + if (!relatedProposals?.length) + return ( + + ); + + return ( + + + {relatedProposals.map((proposal) => ( + + ))} + {totalData > 10 && ( + + )} + + ); +}; diff --git a/src/lib/pages/contract-details/index.tsx b/src/lib/pages/contract-details/index.tsx index fbebfc877..6371c4dfd 100644 --- a/src/lib/pages/contract-details/index.tsx +++ b/src/lib/pages/contract-details/index.tsx @@ -28,6 +28,7 @@ import { InstantiateInfo } from "./components/InstantiateInfo"; import { JsonInfo } from "./components/JsonInfo"; import { ExecuteTable } from "./components/tables/execute/Execute"; import { MigrationTable } from "./components/tables/migration"; +import { RelatedProposalsTable } from "./components/tables/related-proposals"; import { TokenSection } from "./components/TokenSection"; interface ContractDetailsBodyProps { @@ -39,9 +40,15 @@ const InvalidContract = () => ; const ContractDetailsBody = ({ contractAddress }: ContractDetailsBodyProps) => { const contractData = useContractData(contractAddress); const tableHeaderId = "contractDetailTableHeader"; - const { tableCounts, refetchExecute, refetchMigration } = - useContractDetailsTableCounts(contractAddress); + const { + tableCounts, + refetchExecute, + refetchMigration, + refetchRelatedProposals, + } = useContractDetailsTableCounts(contractAddress); + if (!contractData) return ; + return ( <> @@ -83,7 +90,9 @@ const ContractDetailsBody = ({ contractAddress }: ContractDetailsBodyProps) => { All Executes Migration - Related Proposals + + Related Proposals + {/* TODOs: Wireup with real table data, Make table component, and render each table with different data under each TabPanel */} @@ -109,9 +118,12 @@ const ContractDetailsBody = ({ contractAddress }: ContractDetailsBodyProps) => { /> - - Related Proposals Table - + diff --git a/src/lib/services/contractService.ts b/src/lib/services/contractService.ts index 100e0d02c..755c6b961 100644 --- a/src/lib/services/contractService.ts +++ b/src/lib/services/contractService.ts @@ -11,15 +11,20 @@ import { getExecuteTxsByContractAddress, getMigrationHistoriesCountByContractAddress, getMigrationHistoriesByContractAddress, + getRelatedProposalsCountByContractAddress, + getRelatedProposalsByContractAddress, } from "lib/data/queries"; import type { ContractInfo } from "lib/stores/contract"; import type { ContractAddr, ContractMigrationHistory, + ContractRelatedProposals, ExecuteTransaction, HumanAddr, MigrationRemark, Option, + ProposalStatus, + ProposalType, } from "lib/types"; import { parseDate, parseDateDefault, parseTxHash } from "lib/utils"; @@ -171,7 +176,7 @@ export const useMigrationHistoriesByContractAddress = ( ): UseQueryResult< Option[]> > => { - const queryFn = useCallback(() => { + const queryFn = useCallback(async () => { return indexerGraphClient .request(getMigrationHistoriesByContractAddress, { contractAddress, @@ -208,7 +213,7 @@ export const useMigrationHistoriesByContractAddress = ( export const useMigrationHistoriesCountByContractAddress = ( contractAddress: ContractAddr ): UseQueryResult> => { - const queryFn = useCallback(() => { + const queryFn = useCallback(async () => { return indexerGraphClient .request(getMigrationHistoriesCountByContractAddress, { contractAddress, @@ -224,3 +229,61 @@ export const useMigrationHistoriesCountByContractAddress = ( enabled: !!contractAddress, }); }; + +export const useRelatedProposalsByContractAddress = ( + contractAddress: ContractAddr, + offset: number, + pageSize: number +): UseQueryResult> => { + const queryFn = useCallback(async () => { + return indexerGraphClient + .request(getRelatedProposalsByContractAddress, { + contractAddress, + offset, + pageSize, + }) + .then(({ contract_proposals }) => + contract_proposals.map((proposal) => ({ + proposalId: proposal.proposal_id, + title: proposal.proposal.title, + status: proposal.proposal.status as ProposalStatus, + votingEndTime: parseDate(proposal.proposal.voting_end_time), + depositEndTime: parseDate(proposal.proposal.deposit_end_time), + resolvedHeight: proposal.resolved_height, + type: proposal.proposal.type as ProposalType, + proposer: proposal.proposal.account?.address as + | HumanAddr + | ContractAddr, + })) + ); + }, [contractAddress, offset, pageSize]); + + return useQuery( + ["related_proposals", contractAddress, offset, pageSize], + queryFn, + { + keepPreviousData: true, + enabled: !!contractAddress, + } + ); +}; + +export const useRelatedProposalsCountByContractAddress = ( + contractAddress: ContractAddr +): UseQueryResult> => { + const queryFn = useCallback(async () => { + return indexerGraphClient + .request(getRelatedProposalsCountByContractAddress, { + contractAddress, + }) + .then( + ({ contract_proposals_aggregate }) => + contract_proposals_aggregate.aggregate?.count + ); + }, [contractAddress]); + + return useQuery(["related_proposals_count", contractAddress], queryFn, { + keepPreviousData: true, + enabled: !!contractAddress, + }); +}; diff --git a/src/lib/types/contract.ts b/src/lib/types/contract.ts index 572124ec6..f1beea6c4 100644 --- a/src/lib/types/contract.ts +++ b/src/lib/types/contract.ts @@ -28,3 +28,32 @@ export interface ContractMigrationHistory { timestamp: Date; remark: MigrationRemark; } + +export enum ProposalStatus { + DEPOSIT_PERIOD = "DepositPeriod", + VOTING_PERIOD = "VotingPeriod", + PASSED = "Passed", + REJECTED = "Rejected", + FAILED = "Failed", + INACTIVE = "Inactive", +} + +export enum ProposalType { + STORE_CODE = "StoreCode", + INSTANTIATE_CONTRACT = "InstantiateContract", + MIGRATE_CONTRACT = "MigrateContract", + UPDATE_ADMIN = "UpdateAdmin", + CLEAR_ADMIN = "ClearAdmin", + EXECUTE_CONTRACT = "ExecuteContract", +} + +export interface ContractRelatedProposals { + proposalId: number; + title: string; + status: ProposalStatus; + votingEndTime: Date; + depositEndTime: Date; + resolvedHeight: number | null | undefined; + type: ProposalType; + proposer: HumanAddr | ContractAddr | undefined; +}