diff --git a/client/src/components/Lists/Channels.js b/client/src/components/Lists/Channels.js index 84d5bb34f..6747d4ca6 100644 --- a/client/src/components/Lists/Channels.js +++ b/client/src/components/Lists/Channels.js @@ -1,21 +1,76 @@ /** * SPDX-License-Identifier: Apache-2.0 */ - +import { withStyles } from '@material-ui/core/styles'; import React, { Component } from 'react'; import matchSorter from 'match-sorter'; import ReactTable from '../Styled/Table'; -import { channelsType } from '../types'; -import { - E006, - E007, - E008 -} from './constants'; +import { channelPeerDataType, channelsType } from '../types'; +import ChannelEndorserView from '../View/ChannelEndorserView'; +import ChannelCommitterView from '../View/ChannelCommitterView'; +import Dialog from '@material-ui/core/Dialog'; +import { E006, E007, E008 } from './constants'; import { Info } from '@material-ui/icons'; import moment from 'moment'; - +const styles = theme => { + const { type } = theme.palette; + const dark = type === 'dark'; + return { + partialHash: { + textAlign: 'center', + position: 'relative !important', + '&:hover $fullHash': { + display: 'block', + position: 'absolute !important', + padding: '4px 4px', + backgroundColor: dark ? '#5e558e' : '#000000', + marginTop: -30, + marginLeft: -215, + borderRadius: 8, + color: '#ffffff', + opacity: dark ? 1 : undefined + }, + '&:hover $lastFullHash': { + display: 'block', + position: 'absolute !important', + padding: '4px 4px', + backgroundColor: dark ? '#5e558e' : '#000000', + marginTop: -30, + marginLeft: -415, + borderRadius: 8, + color: '#ffffff', + opacity: dark ? 1 : undefined + } + } + }; +}; class Channels extends Component { - reactTableSetup = () => [ + constructor(props) { + super(props); + this.state = { + dialogOpen: false, + dialogOpenEndorser: false, + sourceDialog: false + }; + } + + handleDialogOpenCommitter = async currentChannel => { + await this.props.getChannelPeerData(currentChannel); + this.setState({ dialogOpen: true }); + }; + + handleDialogOpen = async currentChannel => { + await this.props.getChannelPeerData(currentChannel); + this.setState({ dialogOpenEndorser: true }); + }; + handleDialogCloseCommitter = () => { + this.setState({ dialogOpen: false }); + }; + handleDialogClose = () => { + this.setState({ dialogOpenEndorser: false }); + }; + + reactTableSetup = classes => [ { Header: 'ID', accessor: 'id', @@ -42,12 +97,14 @@ class Channels extends Component { filterAll: true }, { - Header: - Total Blocks - - - - , + Header: ( + + Total Blocks + + + + + ), accessor: 'totalBlocks', filterMethod: (filter, rows) => matchSorter( @@ -59,12 +116,14 @@ class Channels extends Component { filterAll: true }, { - Header: - Blocks - - - - , + Header: ( + + Blocks + + + + + ), accessor: 'blocks', filterMethod: (filter, rows) => matchSorter( @@ -77,12 +136,14 @@ class Channels extends Component { width: 125 }, { - Header: - Transactions - - - - , + Header: ( + + Transactions + + + + + ), accessor: 'transactions', filterMethod: (filter, rows) => matchSorter( @@ -94,41 +155,151 @@ class Channels extends Component { filterAll: true, width: 125 }, + { + Header: Committers, + accessor: 'channel_members.committers', + filterMethod: (filter, rows) => + matchSorter( + rows, + filter.value, + { keys: ['channel_members.committers'] }, + { threshold: matchSorter.rankings.SIMPLEMATCH } + ), + filterAll: true, + Cell: ({ value }) => { + const cts = value.slice(0, 5); + const rcc = value.length - cts.length; + + return ( + + {cts.map((committer, i) => ( + <> + {value.length > 1 && `${i + 1}. `} + {i > 0 && ' '} + {committer} +
+ + ))} + {rcc > 0 && ( + +
+ + this.handleDialogOpenCommitter(this.props.currentChannel, rcc) + } + className={classes.partialHash} + href="#/channels" + > + See All + +
+ )} +
+ ); + } + }, + { + Header: Endorsers, + accessor: 'channel_members.endorsers', + filterMethod: (filter, rows) => + matchSorter( + rows, + filter.value, + { keys: ['channel_members.endorsers'] }, + { threshold: matchSorter.rankings.SIMPLEMATCH } + ), + Cell: ({ value }) => { + const etc = value.slice(0, 5); + const rec = value.length - etc.length; + + return ( + + {etc.map((endorser, i) => ( + <> + {i > 0 && ' '} + {i + 1}. {endorser} +
+ + ))} + {rec > 0 && ( + +
+ this.handleDialogOpen(this.props.currentChannel, rec)} + className={classes.partialHash} + href="#/channels" + > + See All + +
+ )} +
+ ); + } + }, { Header: 'Timestamp', accessor: 'createdat', filterMethod: (filter, rows) => - matchSorter( - rows, - filter.value, - { keys: ['createdat'] }, - { threshold: matchSorter.rankings.SIMPLEMATCH } - ), + matchSorter( + rows, + filter.value, + { keys: ['createdat'] }, + { threshold: matchSorter.rankings.SIMPLEMATCH } + ), filterAll: true, - Cell: ({ value }) => - moment.utc(value).format('YYYY-MM-DD, HH:mm:ss UTC') + Cell: ({ value }) => moment.utc(value).format('YYYY-MM-DD, HH:mm:ss UTC') } ]; render() { - const { channels } = this.props; + const { channels, channelPeerData, classes } = this.props; + const { dialogOpen, dialogOpenEndorser } = this.state; return (
= 5} /> + + + + + + +
); } } Channels.propTypes = { - channels: channelsType.isRequired + channels: channelsType.isRequired, + channelPeerData: channelPeerDataType }; - -export default Channels; +Channels.defaultProps = { + channelPeerData: null +}; +export default withStyles(styles)(Channels); diff --git a/client/src/components/Lists/SearchByQuery.js b/client/src/components/Lists/SearchByQuery.js index a38b65372..8d8b31f77 100644 --- a/client/src/components/Lists/SearchByQuery.js +++ b/client/src/components/Lists/SearchByQuery.js @@ -4,6 +4,8 @@ import React, { useEffect, useState } from 'react'; import { txnListType } from '../types'; +import { blockHashTypee } from '../types'; +import { blockTxnIdType } from '../types'; import { IconButton, TextField, @@ -17,6 +19,8 @@ import { withRouter } from 'react-router-dom'; import Dialog from '@material-ui/core/Dialog'; import TransactionView from '../View/TransactionView'; import BlockView from '../View/BlockView'; +import BlockHashView from '../View/BlockHashView'; +import BlockTxIdView from '../View/BlockTxIdView'; const useStyles = makeStyles(theme => ({ searchField: { @@ -51,11 +55,13 @@ const useStyles = makeStyles(theme => ({ const SearchByQuery = props => { let { txnList } = props; + let { blockHashList } = props; + let { blockTxnIdList } = props; let { blockSearch } = props; const classes = useStyles(); - const options = ['Txn Hash', 'Block No']; + const options = ['Txn Hash', 'Block No', 'Block Hash', 'Block By Txn ID']; const [search, setSearch] = useState(''); - const [selectedOption, setSelectedOption] = useState('Txn Hash'); + const [selectedOp, setSelectedOp] = useState('Txn Hash'); const [dialogOpen, setDialogOpen] = useState(false); const [error, setError] = useState(''); const [searchClick, setSearchClick] = useState(false); @@ -69,21 +75,25 @@ const SearchByQuery = props => { }, [searchClick]); const searchData = async () => { - if (selectedOption === 'Txn Hash') { + if (selectedOp === 'Txn Hash') { await props.getTxnList(props.currentChannel, search); - } else if (selectedOption === 'Block No') { + } else if (selectedOp === 'Block No') { await props.getBlockSearch(props.currentChannel, search); + } else if (selectedOp === 'Block Hash') { + await props.getBlockHash(props.currentChannel, search); + } else if (selectedOp === 'Block By Txn ID') { + console.log('entered'); + await props.getBlockByTxnId(props.currentChannel, search); } setSearchClick(true); }; - const handleSubmit = async e => { e.preventDefault(); if ( !search || - (selectedOption === 'Block No' && (isNaN(search) || search.length > 9)) + (selectedOp === 'Block No' && (isNaN(search) || search.length > 9)) ) { - setError('Please enter valid txn hash/block no'); + setError('Please enter valid txn hash/block no/block hash/block by txn Id'); return; } searchData(); @@ -112,7 +122,7 @@ const SearchByQuery = props => { } }} onKeyPress={e => e.key === 'Enter' && handleSubmit(e)} - label=" Search by Txn Hash / Block" + label=" Search by Txn Hash / Block no / BlockHash / Block by Txn Id" variant="outlined" fullWidth error={error} @@ -122,10 +132,10 @@ const SearchByQuery = props => { startAdornment: (