mirror of
https://github.com/fullstack-cash/explorer.fullstack.cash.git
synced 2026-09-21 16:52:05 -07:00
Merge pull request #1 from Permissionless-Software-Foundation/dh-block-explorer
feat(explorer): Created a block explorer for P2WDB
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Row, Col, Box, Button } from 'adminlte-2-react'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
|
||||
let _this
|
||||
class Details extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
_this = this
|
||||
|
||||
this.state = {
|
||||
entry: {},
|
||||
entryData: {}
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { entry, entryData } = _this.state
|
||||
return (
|
||||
<>
|
||||
<Row>
|
||||
<Col sm={12}>
|
||||
<Box className=' border-none mt-2' loaded={!this.state.inFetch}>
|
||||
<Row className='text-center'>
|
||||
<Col sm={12}>
|
||||
<h1 id='SendTokens'>
|
||||
<FontAwesomeIcon
|
||||
className='title-icon'
|
||||
size='xs'
|
||||
icon='info-circle'
|
||||
/>
|
||||
<span>Details</span>
|
||||
</h1>
|
||||
{entry._id && (
|
||||
<Box className='border-none details-data-content'>
|
||||
<p>
|
||||
<b>IS VALID: </b>
|
||||
{entry.isValid}
|
||||
</p>
|
||||
<p>
|
||||
<b>APP ID: </b>
|
||||
{entry.appId || 'none'}
|
||||
</p>
|
||||
<p>
|
||||
<b>CREATE AT: </b>
|
||||
{new Date(entry.createdAt).toLocaleString()}
|
||||
</p>
|
||||
<p>
|
||||
<b>ID: </b>
|
||||
{entry._id}
|
||||
</p>
|
||||
<p>
|
||||
<b>HASH: </b>
|
||||
{entry.hash}
|
||||
</p>
|
||||
<p>
|
||||
<b>TRANSACTION ID : </b>
|
||||
{entry.key}
|
||||
</p>
|
||||
{entryData && (
|
||||
<>
|
||||
<b>VALUE : </b>
|
||||
<p>
|
||||
- <b>Message : </b> {entryData.message}
|
||||
</p>
|
||||
<p>
|
||||
- <b>Signature : </b> {entryData.signature}
|
||||
</p>
|
||||
<p>
|
||||
- <b>Data : </b> {entryData.data}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Col>
|
||||
<Col sm={12}>
|
||||
<div className='btn-wrapper'>
|
||||
<Button
|
||||
text='Close'
|
||||
type='primary'
|
||||
className='btn-lg btn-close-entry mr-1 ml-1 mt-1'
|
||||
onClick={_this.handleClose}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Box>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
_this.handleData()
|
||||
}
|
||||
|
||||
componentDidUpdate () {
|
||||
if (_this.props.entry._id !== _this.state.entry._id) {
|
||||
_this.handleData()
|
||||
}
|
||||
}
|
||||
|
||||
handleData () {
|
||||
try {
|
||||
const { entry } = _this.props
|
||||
_this.setState({
|
||||
entry,
|
||||
entryData: entry.value
|
||||
})
|
||||
} catch (err) {
|
||||
console.warn('Error in handleData()', err)
|
||||
}
|
||||
}
|
||||
|
||||
handleClose () {
|
||||
_this.props.onClose()
|
||||
}
|
||||
}
|
||||
Details.propTypes = {
|
||||
entry: PropTypes.object,
|
||||
onClose: PropTypes.func
|
||||
}
|
||||
export default Details
|
||||
@@ -0,0 +1,10 @@
|
||||
.btn-close-entry{
|
||||
width: 150px;
|
||||
}
|
||||
.action-handler{
|
||||
color: #3c8dbc;
|
||||
cursor: pointer;
|
||||
}
|
||||
.details-data-content{
|
||||
text-align: left;
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
import React from 'react'
|
||||
import { Row, Col, Content, Box, DataTable } from 'adminlte-2-react'
|
||||
import Details from './details'
|
||||
import './explorer.css'
|
||||
const axios = require('axios').default
|
||||
|
||||
const SERVER = 'https://p2wdb.fullstack.cash/'
|
||||
const EXPLORER_URL = 'https://explorer.bitcoin.com/bch/tx/'
|
||||
|
||||
let _this
|
||||
class Explorer extends React.Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
_this = this
|
||||
this.state = {
|
||||
showEntry: false,
|
||||
data: [],
|
||||
entries: [],
|
||||
entryData: null
|
||||
}
|
||||
|
||||
this.firstColumns = [
|
||||
{ title: 'Created At', data: 'createdAt' },
|
||||
{
|
||||
title: 'Transaction',
|
||||
data: 'txid',
|
||||
render: txid => (
|
||||
<span className='on-click-event action-handler'>
|
||||
{txid.subString}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: 'Hash',
|
||||
data: 'hash',
|
||||
render: hash => (
|
||||
<span className='on-click-event action-handler'>
|
||||
{hash.subString}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
{ title: 'App Id', data: 'appId' }
|
||||
]
|
||||
}
|
||||
|
||||
render () {
|
||||
const { data, entryData } = _this.state
|
||||
return (
|
||||
<Content
|
||||
title='P2WDB Explorer'
|
||||
subTitle='Block Explorer for P2WDB'
|
||||
browserTitle='P2WDB Explorer'
|
||||
>
|
||||
<Row>
|
||||
{entryData && (
|
||||
<Col xs={12}>
|
||||
<Details entry={entryData} onClose={_this.handleClose} />
|
||||
</Col>
|
||||
)}
|
||||
<Col xs={12}>
|
||||
<Box title='Block explorer for P2WDB'>
|
||||
<DataTable
|
||||
columns={_this.firstColumns}
|
||||
data={data}
|
||||
options={{
|
||||
paging: true,
|
||||
lengthChange: false,
|
||||
searching: false,
|
||||
ordering: false,
|
||||
info: true,
|
||||
autoWidth: false
|
||||
}}
|
||||
onClickEvents={{
|
||||
onClickEvent: (data, rowIdx, rowData) => {
|
||||
_this.handleEvents(data)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Col>
|
||||
</Row>
|
||||
</Content>
|
||||
)
|
||||
}
|
||||
|
||||
async componentDidMount () {
|
||||
_this.handleEntries()
|
||||
|
||||
// Get data and update table
|
||||
// every 20 seconds
|
||||
setInterval(() => {
|
||||
_this.handleEntries()
|
||||
}, 20000)
|
||||
}
|
||||
|
||||
async handleEntries () {
|
||||
const entries = await _this.getEntries()
|
||||
_this.generateDataTable(entries)
|
||||
}
|
||||
|
||||
// REST petition to Get data fron the pw2db
|
||||
async getEntries () {
|
||||
try {
|
||||
const options = {
|
||||
method: 'GET',
|
||||
url: `${SERVER}entry/all/${0}`,
|
||||
data: {}
|
||||
}
|
||||
const result = await axios.request(options)
|
||||
_this.setState({
|
||||
entries: result.data.data
|
||||
})
|
||||
return result.data.data
|
||||
} catch (err) {
|
||||
console.warn('Error in getEntries() ', err)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate table content
|
||||
generateDataTable (dataArr) {
|
||||
try {
|
||||
const data = []
|
||||
|
||||
for (let i = 0; i < dataArr.length; i++) {
|
||||
const entry = dataArr[i]
|
||||
const row = {
|
||||
// createdAt row data
|
||||
createdAt: new Date(entry.createdAt).toLocaleString(),
|
||||
// Transaction id row data
|
||||
txid: {
|
||||
subString: _this.cutString(entry.key),
|
||||
txid: entry.key
|
||||
},
|
||||
// Hash row data
|
||||
hash: {
|
||||
subString: _this.cutString(entry.hash),
|
||||
hash: entry.hash,
|
||||
data: entry
|
||||
},
|
||||
// App id row data
|
||||
appId: entry.appId || 'none'
|
||||
}
|
||||
data.push(row)
|
||||
}
|
||||
_this.setState({
|
||||
data
|
||||
})
|
||||
} catch (err) {
|
||||
console.warn('Error in generateDataTable() ', err)
|
||||
}
|
||||
}
|
||||
|
||||
cutString (txid) {
|
||||
try {
|
||||
const subTxid = txid.slice(0, 4)
|
||||
const subTxid2 = txid.slice(-4)
|
||||
return `${subTxid}...${subTxid2}`
|
||||
} catch (err) {
|
||||
console.warn('Error in cutString() ', err)
|
||||
}
|
||||
}
|
||||
|
||||
handleTxIdClick (txid) {
|
||||
try {
|
||||
window.open(`${EXPLORER_URL}${txid}`, '_blank')
|
||||
} catch (err) {
|
||||
console.warn('Error in handleTxIdClick() ', err)
|
||||
}
|
||||
}
|
||||
|
||||
handleHashClick (data) {
|
||||
try {
|
||||
data.isValid = data.isValid.toString()
|
||||
_this.setState({
|
||||
entryData: data
|
||||
})
|
||||
} catch (err) {
|
||||
_this.setState({
|
||||
entryData: null
|
||||
})
|
||||
console.warn('Error in handleHashClick() ', err)
|
||||
}
|
||||
}
|
||||
|
||||
handleEvents (eventData) {
|
||||
try {
|
||||
const { txid, hash, data } = eventData
|
||||
|
||||
if (txid) {
|
||||
_this.handleTxIdClick(txid)
|
||||
}
|
||||
if (hash && data) {
|
||||
_this.handleHashClick(data)
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Error in handleEvents() ', err)
|
||||
}
|
||||
}
|
||||
|
||||
handleClose () {
|
||||
_this.setState({
|
||||
entryData: null
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default Explorer
|
||||
@@ -24,11 +24,17 @@ import Wallet from 'gatsby-ipfs-web-wallet/src/components/admin-lte/wallet'
|
||||
import Tokens from 'gatsby-ipfs-web-wallet/src/components/admin-lte/tokens'
|
||||
import Configure from 'gatsby-ipfs-web-wallet/src/components/admin-lte/configure'
|
||||
import SendReceive from 'gatsby-ipfs-web-wallet/src/components/admin-lte/send-receive'
|
||||
import Explorer from '../../explorer'
|
||||
|
||||
const { Item } = Sidebar
|
||||
|
||||
const MenuComponents = props => {
|
||||
return [
|
||||
{
|
||||
key: 'Explorer',
|
||||
component: <Explorer key='Explorer' {...props} />,
|
||||
menuItem: <Item icon='fas-binoculars' key='Explorer' text='Explorer' />
|
||||
},
|
||||
{
|
||||
key: 'Tokens',
|
||||
component: <Tokens key='Tokens' {...props} />,
|
||||
@@ -58,9 +64,7 @@ const MenuComponents = props => {
|
||||
{
|
||||
key: 'TX History',
|
||||
component: <TXHistory key='TX History' {...props} />,
|
||||
menuItem: (
|
||||
<Item icon='fas-cog' key='TX History' text='TX History' />
|
||||
)
|
||||
menuItem: <Item icon='fas-cog' key='TX History' text='TX History' />
|
||||
},
|
||||
{
|
||||
key: 'Demo Component',
|
||||
|
||||
Reference in New Issue
Block a user