From 3cfd1d34d098b3c400655c78e6ce852b537a78b6 Mon Sep 17 00:00:00 2001 From: saksham1203 Date: Fri, 24 Feb 2023 05:56:16 +0000 Subject: [PATCH] Added search functonality to dashboard so user can search with Txn hash/Block no from there itself Signed-off-by: saksham1203 --- client/src/components/Lists/SearchByQuery.js | 144 +++++++++++++++++++ client/src/components/Main.js | 12 ++ client/src/components/View/DashboardView.js | 45 +++++- client/src/components/types/index.js | 6 + client/src/state/redux/tables/actions.js | 12 ++ client/src/state/redux/tables/operations.js | 42 ++++++ client/src/state/redux/tables/reducers.js | 27 ++++ client/src/state/redux/tables/selectors.js | 2 + client/src/state/redux/tables/types.js | 4 + 9 files changed, 292 insertions(+), 2 deletions(-) create mode 100644 client/src/components/Lists/SearchByQuery.js diff --git a/client/src/components/Lists/SearchByQuery.js b/client/src/components/Lists/SearchByQuery.js new file mode 100644 index 000000000..1d4ca8876 --- /dev/null +++ b/client/src/components/Lists/SearchByQuery.js @@ -0,0 +1,144 @@ + +import React, { useEffect, useState } from 'react' +import { txnListType } from '../types'; +import { IconButton, TextField, Select, MenuItem, InputAdornment, makeStyles } from '@material-ui/core'; +import SearchIcon from '@material-ui/icons/Search'; +import { withRouter } from 'react-router-dom'; +import Dialog from '@material-ui/core/Dialog'; +import TransactionView from '../View/TransactionView'; +import BlockView from '../View/BlockView'; + +const useStyles = makeStyles((theme) => ({ + searchField: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + '& .MuiOutlinedInput-input': { + padding: '16px 14px' + } + }, + searchInput: { + marginRight: theme.spacing(0), + '& > div': { + paddingRight: '24px !important', + } + }, + iconButton: { + height: 40, + width: 40, + color: '#21295c', + backgroundColor: '#b9d6e1', + borderRadius: 15 + } +})); + + +const SearchByQuery = (props) => { + let { txnList } = props; + let { blockSearch } = props; + const classes = useStyles(); + const options = ["Txn Hash", "Block No"] + const [search, setSearch] = useState("") + const [selectedOption, setSelectedOption] = useState("Txn Hash") + const [dialogOpen, setDialogOpen] = useState(false) + const [error, setError] = useState(props.searchError) + const [searchClick, setSearchClick] = useState(false); + + useEffect(() => { + if (props.searchError || searchClick) { + setSearchClick(false); setError(props.searchError) } + }, [props.searchError, searchClick]) + + const searchData = async () => { + if (selectedOption === "Txn Hash") { + await props.getTxnList(props.currentChannel, search) + } else if (selectedOption === "Block No") { + await props.getBlockSearch(props.currentChannel, search) + } + handleDialogOpen(); + setSearchClick(true) + } + + const handleSubmit = async (e) => { + e.preventDefault(); + if (!search || (selectedOption === "Block No" && (isNaN(search) || search.length > 9))) { + setError("Please enter valid txn hash/block no") + return + } + searchData(); + + } + + const handleDialogOpen = () => { + setDialogOpen(true) + + } + + const handleDialogClose = () => { + setDialogOpen(false) + } + + return ( +
+
+ + { setSearch(e.target.value); if (error) { setDialogOpen(false); setError(''); } }} + onKeyPress={(e) => e.key === 'Enter' && handleSubmit(e)} + label=" Search by Txn Hash / Block" + variant='outlined' + style={{ width: 550 }} + error={error} + helperText={error} + InputProps={{ + endAdornment: ( + + + + + + ) + }} + /> + + + {!error && selectedOption === 'Block No' ? + : + } + +
+ ) +} +SearchByQuery.propTypes = { + txnList: txnListType.isRequired +}; + + +export default withRouter(SearchByQuery) \ No newline at end of file diff --git a/client/src/components/Main.js b/client/src/components/Main.js index fa3ede620..f0ecb0fce 100644 --- a/client/src/components/Main.js +++ b/client/src/components/Main.js @@ -22,6 +22,8 @@ import { dashStatsType, getTransactionType, peerListType, + txnListType, + blockSearchType, peerStatusType, blockRangeSearchType, blockListSearchType, @@ -46,6 +48,8 @@ const { chaincodeListSelector, channelsSelector, peerListSelector, + txnListSelector, + blockSearchSelector, transactionSelector, transactionListSelector, blockRangeSearchSelector, @@ -81,6 +85,8 @@ export const Main = props => { dashStats, getTransaction, peerList, + txnList, + blockSearch, peerStatus, txnList,//s transaction, @@ -131,6 +137,8 @@ export const Main = props => { blockListSearch, dashStats, peerStatus, + txnList, + blockSearch, transactionByOrg, blockActivity }; @@ -242,6 +250,8 @@ Main.propTypes = { dashStats: dashStatsType.isRequired, getTransaction: getTransactionType.isRequired, peerList: peerListType.isRequired, + txnList: txnListType.isRequired, + blockSearch: blockSearchType.isRequired, peerStatus: peerStatusType.isRequired, transaction: transactionType.isRequired, transactionByOrg: transactionByOrgType.isRequired, @@ -256,6 +266,8 @@ const connectedComponent = connect( currentChannel: currentChannelSelector(state), dashStats: dashStatsSelector(state), peerList: peerListSelector(state), + txnList: txnListSelector(state), + blockSearch: blockSearchSelector(state), peerStatus: peerStatusSelector(state), transaction: transactionSelector(state), transactionByOrg: transactionByOrgSelector(state), diff --git a/client/src/components/View/DashboardView.js b/client/src/components/View/DashboardView.js index 52ba0120d..f82159fe7 100644 --- a/client/src/components/View/DashboardView.js +++ b/client/src/components/View/DashboardView.js @@ -16,8 +16,16 @@ import { blockListSearchType, dashStatsType, peerStatusType, + txnListType, + blockSearchType, transactionByOrgType } from '../types'; +import SearchByQuery from '../Lists/SearchByQuery'; +import { connect } from 'react-redux'; +import { currentChannelSelector } from '../../state/redux/charts/selectors'; +import { tableOperations } from '../../state/redux/tables'; + +const {txnList, blockSearch} =tableOperations /* istanbul ignore next */ const styles = theme => { @@ -34,6 +42,13 @@ const styles = theme => { marginLeft: '10%', marginRight: '10%' }, + dashboardSearch:{ + position: 'absolute', + width: '80%' + }, + search :{ + marginLeft:'10px' + }, blocks: { height: 175, marginBottom: 20, @@ -155,8 +170,11 @@ export class DashboardView extends Component { }; render() { - const { dashStats, peerStatus, blockActivity, transactionByOrg } = this.props; + const { dashStats, peerStatus, txnList, blockSearch, blockActivity, transactionByOrg } = this.props; const { hasDbError, notifications } = this.state; + var searchError = '' + if(typeof txnList==='string'){searchError='Txn not found'; } + else if(typeof blockSearch==='string'){searchError='Block not found'} if (hasDbError) { return (
+
+
+ +
+
@@ -269,7 +295,22 @@ DashboardView.propTypes = { blockListSearch: blockListSearchType.isRequired, dashStats: dashStatsType.isRequired, peerStatus: peerStatusType.isRequired, + txnList: txnListType.isRequired, + blockSearch: blockSearchType.isRequired, transactionByOrg: transactionByOrgType.isRequired }; -export default withStyles(styles)(DashboardView); +const mapStateToProps = state => { + return { + currentChannel: currentChannelSelector(state) + } +} +const mapDispatchToProps = { + getTxnList: txnList, + getBlockSearch: blockSearch +}; +const connectedComponent = connect( + mapStateToProps, + mapDispatchToProps +)(DashboardView) +export default withStyles(styles)(connectedComponent); diff --git a/client/src/components/types/index.js b/client/src/components/types/index.js index 681d9ba37..7a3549fa0 100644 --- a/client/src/components/types/index.js +++ b/client/src/components/types/index.js @@ -118,6 +118,8 @@ export const getChannelListType = func; export const getChannelsType = func; export const getDashStatsType = func; export const getPeerListType = func; +export const getTxnListType = func; +export const getBlockSearchType = func; export const getPeerStatusType = func; export const getTransactionInfoType = func; export const getTransactionListType = func; @@ -153,6 +155,10 @@ export const peerListType = arrayOf( }) ); +export const txnListType = any; + +export const blockSearchType = any; + export const peerStatusType = arrayOf( shape({ server_hostname: string, diff --git a/client/src/state/redux/tables/actions.js b/client/src/state/redux/tables/actions.js index fc84e4b28..a4fd49888 100644 --- a/client/src/state/redux/tables/actions.js +++ b/client/src/state/redux/tables/actions.js @@ -34,6 +34,16 @@ const getBlockRangeSearch = resp => ({ payload: resp.data }); +const getTxnList = resp => ({ + type: types.TXN_LIST, + payload: resp.data, +}); + +const getBlockSearch = resp => ({ + type: types.BLOCK_SEARCH, + payload: resp.data, +}); + const getTransaction = transaction => ({ type: types.TRANSACTION, payload: transaction, @@ -53,6 +63,8 @@ export default { getChannels, getPeerList, getBlockRangeSearch, + getTxnList, + getBlockSearch, getTransaction, getTransactionList, getBlockListSearch, diff --git a/client/src/state/redux/tables/operations.js b/client/src/state/redux/tables/operations.js index 29c679000..ec8cebb7d 100644 --- a/client/src/state/redux/tables/operations.js +++ b/client/src/state/redux/tables/operations.js @@ -89,6 +89,46 @@ const peerList = channel => dispatch => console.error(error); }); +const txnList = (channel, query) => dispatch => + get(`/api/fetchDataByTxnId/${channel}/${query}`) + .then(resp => { + if (resp.status === 500) { + dispatch( + actions.getErroMessage( + '500 Internal Server Error: The server has encountered an internal error and unable to complete your request' + ) + ); + } else if (resp.status === 400) { + dispatch(actions.getErroMessage(resp.error)); + } else { + dispatch(actions.getTxnList(resp)); + } + dispatch(actions.getBlockSearch({ data: {} })); + }) + .catch(error => { + console.error(error); + }); + +const blockSearch = (channel, query) => dispatch => + get(`/api/fetchDataByBlockNo/${channel}/${query}`) + .then(resp => { + if (resp.status === 500) { + dispatch( + actions.getErroMessage( + '500 Internal Server Error: The server has encountered an internal error and unable to complete your request' + ) + ); + } else if (resp.status === 400) { + dispatch(actions.getErroMessage(resp.error)); + } else { + dispatch(actions.getBlockSearch(resp)); + } + dispatch(actions.getTxnList({ data: {} })); + }) + .catch(error => { + console.error(error); + }); + /* istanbul ignore next */ const transaction = (channel, transactionId) => dispatch => get(`/api/transaction/${channel}/${transactionId}`) @@ -163,6 +203,8 @@ export default { chaincodeList, channels, peerList, + txnList, + blockSearch, transaction, transactionList, transactionListSearch, diff --git a/client/src/state/redux/tables/reducers.js b/client/src/state/redux/tables/reducers.js index cf2a22d43..61c485fcb 100644 --- a/client/src/state/redux/tables/reducers.js +++ b/client/src/state/redux/tables/reducers.js @@ -73,6 +73,31 @@ const blockRangeSearchReducer = (state = initialState, action = {}) => { return state; } }; + +const txnListReducer = (state = initialState, action = {}) => { + if (action.type === types.TXN_LIST) { + return { + rows: action.payload, + loaded: true, + errors: action.error, + }; + } else { + return state; + } +}; + +const blockSearchReducer = (state = initialState, action = {}) => { + if (action.type === types.BLOCK_SEARCH) { + return { + rows: action.payload, + loaded: true, + errors: action.error, + }; + } else { + return state; + } +}; + const transactionReducer = (state = initialState, action = {}) => { if (action.type === types.TRANSACTION) { return { @@ -118,6 +143,8 @@ const reducer = combineReducers({ channels: channelsReducer, peerList: peerListReducer, blockRangeSearch: blockRangeSearchReducer, + txnList: txnListReducer, + blockSearch: blockSearchReducer, transaction: transactionReducer, transactionList: transactionListReducer, blockListSearch: blockListSearchReducer, diff --git a/client/src/state/redux/tables/selectors.js b/client/src/state/redux/tables/selectors.js index 20b5d73b7..cfc178676 100644 --- a/client/src/state/redux/tables/selectors.js +++ b/client/src/state/redux/tables/selectors.js @@ -5,6 +5,8 @@ export const chaincodeListSelector = state => state.tables.chaincodeList.rows; export const channelsSelector = state => state.tables.channels.rows; export const peerListSelector = state => state.tables.peerList.rows; +export const txnListSelector = state => state.tables.txnList.rows; +export const blockSearchSelector = state => state.tables.blockSearch.rows; export const transactionSelector = state => state.tables.transaction.transaction; export const transactionListSelector = state => state.tables.transactionList.rows; export const transactionListSearchSelector = state => state.tables.transactionListSearch.rows; diff --git a/client/src/state/redux/tables/types.js b/client/src/state/redux/tables/types.js index de59be593..40f9b768e 100644 --- a/client/src/state/redux/tables/types.js +++ b/client/src/state/redux/tables/types.js @@ -8,6 +8,8 @@ const BLOCK_LIST = `${namespaces}/BLOCK_LIST`; const CHAINCODE_LIST = `${namespaces}/CHAINCODE_LIST`; const CHANNELS = `${namespaces}/CHANNELS`; const PEER_LIST = `${namespaces}/PEER_LIST`; +const TXN_LIST = "TXN_LISt"; +const BLOCK_SEARCH = "BLOCK_SEARCH"; const TRANSACTION = `${namespaces}/TRANSACTION`; const TRANSACTION_LIST = `${namespaces}/TRANSACTION_LIST`; const BLOCK_LIST_SEARCH = `${namespaces}/BLOCK_LIST_SEARCH`; @@ -21,6 +23,8 @@ export default { CHAINCODE_LIST, CHANNELS, PEER_LIST, + TXN_LIST, + BLOCK_SEARCH, TRANSACTION, TRANSACTION_LIST, BLOCK_LIST_SEARCH,