Skip to content

Commit

Permalink
feat: add blueprint list (#385)
Browse files Browse the repository at this point in the history
  • Loading branch information
pedroferreira1 authored Feb 26, 2025
1 parent 0d9c4fe commit eeea785
Show file tree
Hide file tree
Showing 17 changed files with 798 additions and 47 deletions.
2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import ErrorMessage from './components/error/ErrorMessage';
import WebSocketHandler from './WebSocketHandler';
import NanoContractDetail from './screens/nano/NanoContractDetail';
import BlueprintDetail from './screens/nano/BlueprintDetail';
import BlueprintList from './screens/nano/BlueprintList';
import {
apiLoadErrorUpdate,
dashboardUpdate,
Expand Down Expand Up @@ -167,6 +168,7 @@ function Root() {
path="/blueprint/detail/:blueprint_id"
element={<NavigationRoute internalScreen={BlueprintDetail} />}
/>
<Route path="/blueprints/" element={<NavigationRoute internalScreen={BlueprintList} />} />
<Route path="" element={<NavigationRoute internalScreen={DashboardTx} />} />
</Routes>
</BrowserRouter>
Expand Down
72 changes: 72 additions & 0 deletions src/api/nanoApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,78 @@ const nanoApi = {
);
});
},

/**
* Get built in blueprint list
*
* @param {number | null} count Number of elements to get the list
* @param {string | null} after ID of the blueprint to get as reference for after pagination
* @param {string | null} before ID of the blueprint to get as reference for before pagination
* @param {string | null} search Blueprint ID to search for in the API
*
* For more details, see full node api docs
*/
getBuiltInBlueprintList(count, after, before, search) {
const data = {};
if (count) {
data.count = count;
}
if (after) {
data.after = after;
}
if (before) {
data.before = before;
}
if (search) {
data.search = search;
}
return requestExplorerServiceV1
.get(`node_api/nc_builtin_blueprints`, { params: data })
.then(res => res.data)
.catch(err => {
throw new Error(
err?.data?.message || err?.message || `Unknown error on get builtin blueprint list`
);
});
},

/**
* Get on chain blueprint list
*
* @param {number | null} count Number of elements to get the list
* @param {string | null} after ID of the blueprint to get as reference for after pagination
* @param {string | null} before ID of the blueprint to get as reference for before pagination
* @param {string | null} search Blueprint ID to search for in the API
* @param {string | null} order Order of the sorting ('asc' or 'desc')
*
* For more details, see full node api docs
*/
getOnChainBlueprintList(count, after, before, search, order) {
const data = {};
if (count) {
data.count = count;
}
if (after) {
data.after = after;
}
if (before) {
data.before = before;
}
if (search) {
data.search = search;
}
if (order) {
data.order = order;
}
return requestExplorerServiceV1
.get(`node_api/nc_on_chain_blueprints`, { params: data })
.then(res => res.data)
.catch(err => {
throw new Error(
err?.data?.message || err?.message || `Unknown error on get on chain blueprint list`
);
});
},
};

export default nanoApi;
5 changes: 4 additions & 1 deletion src/components/AddressHistory.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import EllipsiCell from './EllipsiCell';
import { ReactComponent as RowBottomIcon } from '../assets/images/leading-icon.svg';
import { ReactComponent as RowTopIcon } from '../assets/images/leading-top-icon.svg';
import { COLORS } from '../constants';
import { useIsMobile } from '../hooks';

const mapStateToProps = state => ({
decimalPlaces: state.serverInfo.decimal_places,
Expand Down Expand Up @@ -174,6 +175,8 @@ class AddressHistory extends SortableTable {
}

renderNewTableBodyUi() {
const isMobile = useIsMobile();
const ellipsisCount = isMobile ? 4 : 12;
return this.props.data.map(tx => {
let statusElement = '';
let trClass = '';
Expand Down Expand Up @@ -224,7 +227,7 @@ class AddressHistory extends SortableTable {
<tr key={tx.tx_id} className={trClass} onClick={_e => this.props.onRowClicked(tx.tx_id)}>
<td className="pe-3">{hathorLib.transactionUtils.getTxType(tx)}</td>
<td className="pe-3">
<EllipsiCell id={tx.tx_id} />
<EllipsiCell id={tx.tx_id} countBefore={ellipsisCount} countAfter={ellipsisCount} />
</td>
<td className="pe-3 td-mobile date-cell">
{dateFormatter.parseTimestampNewUi(tx.timestamp)}
Expand Down
8 changes: 3 additions & 5 deletions src/components/EllipsiCell.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@
*/

import React from 'react';
import { useIsMobile } from '../hooks';

const EllipsiCell = ({ id }) => {
const isMobile = useIsMobile();
const idStart = id.substring(0, isMobile ? 4 : 12);
const idEnd = id.substring(id.length - (isMobile ? 4 : 12), id.length);
const EllipsiCell = ({ id, countBefore, countAfter }) => {
const idStart = id.substring(0, countBefore);
const idEnd = id.substring(id.length - countAfter, id.length);

return (
<div className="id-cell">
Expand Down
18 changes: 18 additions & 0 deletions src/components/Navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,24 @@ function Navigation() {
</div>
</ul>
)}
<li className="nav-item dropdown">
<span
className="nav-link dropdown-toggle custom-dropdown-toggle"
id="navbarDropdown"
role="button"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Nano
<ArrorDownNavItem className="dropdown-icon" />
</span>
<div className="dropdown-menu" aria-labelledby="navbarDropdown">
<NavLink to="/blueprints/?type=built-in" exact className="nav-link">
Blueprints List
</NavLink>
</div>
</li>
<li className="nav-item">
<NavLink
to="/network"
Expand Down
21 changes: 21 additions & 0 deletions src/components/Sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ function Sidebar({ close, open }) {
const theme = useSelector(state => state.theme);
const sidebarRef = useRef(null);
const [tokensOpen, setTokensOpen] = useState(false);
const [nanoOpen, setNanoOpen] = useState(false);
const [toolsOpen, setToolsOpen] = useState(false);

useEffect(() => {
Expand Down Expand Up @@ -95,6 +96,26 @@ function Sidebar({ close, open }) {
)}
</span>
)}
<li className="nav-item item-sidebar">
<span onClick={() => setNanoOpen(!nanoOpen)}>
Nano{' '}
<ArrorDownNavItem
style={{ marginLeft: '5px', rotate: toolsOpen ? '180deg' : '0deg' }}
className="dropdown-icon"
/>
</span>
{nanoOpen && (
<div>
<ul style={{ listStyleType: 'none', padding: 0, margin: 0 }}>
<li>
<NavLink to="/blueprints/?type=built-in" exact className="nav-link">
Blueprints List
</NavLink>
</li>
</ul>
</div>
)}
</li>
<li className="nav-item item-sidebar">
<NavLink
to="/network"
Expand Down
11 changes: 8 additions & 3 deletions src/components/SortableTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ class SortableTable extends React.Component {

renderTable(content) {
return this.props.newUiEnabled ? (
<table className="table-stylized table-tokens" id="">
<table
className={`table-stylized ${
this.props.tableClass ? this.props.tableClass : 'table-tokens'
}`}
>
{content}
</table>
) : (
Expand Down Expand Up @@ -150,7 +154,7 @@ class SortableTable extends React.Component {
if (this.props.calculatingPage) {
return 'page-item disable-button';
}
return 'page-item active';
return 'page-item';
})()}
>
<button onClick={e => this.props.onNextPageClicked(e)} className="page-link">
Expand Down Expand Up @@ -196,7 +200,7 @@ class SortableTable extends React.Component {
* order: If sorted field must be ordered asc or desc
* tableHeaderClicked: This indicates that user wants data to be sorted by a determined field
* calculatingPage: Indicates if next page is being retrieved from explorer-service
* tableClasses: Extra classes to add to the table element
* tableClass: CSS class to add to the table element. The default value is 'table-tokens'.
*/
SortableTable.propTypes = {
data: PropTypes.array.isRequired,
Expand All @@ -209,6 +213,7 @@ SortableTable.propTypes = {
tableHeaderClicked: PropTypes.func,
sortBy: PropTypes.string,
order: PropTypes.string,
tableClass: PropTypes.string,
};

export default SortableTable;
38 changes: 38 additions & 0 deletions src/components/nano/BuiltInBlueprintsTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import SortableTable from '../SortableTable';
import EllipsiCell from '../EllipsiCell';

// XXX We should use function component with SortableTable as a component
// but renderTableHead and renderTableBody are implemented and not
// expected as a props, so it demands a bigger refactor
class BuiltInBlueprintsTable extends SortableTable {
renderTableHead() {
return (
<tr>
<th className="d-lg-table-cell">BLUEPRINT ID</th>
<th className="d-lg-table-cell">NAME</th>
</tr>
);
}

renderTableBody() {
return this.props.data.map(blueprint => {
return (
<tr key={blueprint.id} onClick={_e => this.props.handleClickRow(blueprint.id)}>
<td className="d-lg-table-cell pe-3">
{this.props.isMobile ? (
<EllipsiCell id={blueprint.id} countBefore={10} countAfter={10} />
) : (
blueprint.id
)}
</td>
<td className="d-lg-table-cell pe-3">{blueprint.name}</td>
</tr>
);
});
}
}

BuiltInBlueprintsTable.propTypes = SortableTable.propTypes;

export default BuiltInBlueprintsTable;
76 changes: 76 additions & 0 deletions src/components/nano/OnChainBlueprintsTable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React from 'react';
import SortableTable from '../SortableTable';
import EllipsiCell from '../EllipsiCell';
import dateFormatter from '../../utils/date';

// XXX We should use function component with SortableTable as a component
// but renderTableHead and renderTableBody are implemented and not
// expected as a props, so it demands a bigger refactor
class OnChainBlueprintsTable extends SortableTable {
/*
* Get header of first column. In the mobile we show the element
* of two columns in one.
*/
getFirstHeaderName() {
if (this.props.isMobile) {
return 'BLUEPRINT ID AND NAME';
}

return 'BLUEPRINT ID';
}

renderTableHead() {
return (
<tr>
<th className="d-lg-table-cell">{this.getFirstHeaderName()}</th>
{!this.props.isMobile && <th className="d-lg-table-cell">NAME</th>}
<th
className="d-lg-table-cell sortable"
onClick={e => this.props.tableHeaderClicked(e, 'created_at')}
>
CREATED AT {this.getArrow('created_at')}
</th>
</tr>
);
}

renderMobileRow(blueprint) {
return (
<>
<td className="d-lg-table-cell pe-3">
<EllipsiCell id={blueprint.id} countBefore={10} countAfter={10} />
<p className="mt-2 mb-0">{blueprint.name}</p>
</td>
<td className="d-lg-table-cell pe-3">
{dateFormatter.parseTimestampNewUi(blueprint.created_at)}
</td>
</>
);
}

renderDesktopRow(blueprint) {
return (
<>
<td className="d-lg-table-cell pe-3">{blueprint.id}</td>
<td className="d-lg-table-cell pe-3">{blueprint.name}</td>
<td className="d-lg-table-cell pe-3">
{dateFormatter.parseTimestampNewUi(blueprint.created_at)}
</td>
</>
);
}

renderTableBody() {
return this.props.data.map(blueprint => {
return (
<tr key={blueprint.id} onClick={_e => this.props.handleClickRow(blueprint.id)}>
{this.props.isMobile ? this.renderMobileRow(blueprint) : this.renderDesktopRow(blueprint)}
</tr>
);
});
}
}

OnChainBlueprintsTable.propTypes = SortableTable.propTypes;

export default OnChainBlueprintsTable;
4 changes: 3 additions & 1 deletion src/components/token/TokenBalanceRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ function TokenBalanceRow({ tokenId, address, total, unlocked, locked }) {

const renderNewUi = () => (
<tr onClick={onRowClicked}>
<td className="d-lg-table-cell pe-3">{isMobile ? <EllipsiCell id={address} /> : address}</td>
<td className="d-lg-table-cell pe-3">
{isMobile ? <EllipsiCell id={address} countBefore={4} countAfter={4} /> : address}
</td>
<td className="d-lg-table-cell pe-3">{numberUtils.prettyValue(total, decimalPlaces)}</td>
<td className="d-lg-table-cell pe-3 td-mobile">
{numberUtils.prettyValue(unlocked, decimalPlaces)}
Expand Down
4 changes: 2 additions & 2 deletions src/components/token/TokenRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ function TokenRow({ token }) {
isMobile ? (
<tr onClick={_e => onRowClicked(token.uid)}>
<td className="d-lg-table-cell pe-3">
<EllipsiCell id={token.uid} />
<EllipsiCell id={token.uid} countBefore={4} countAfter={4} />
</td>
<td className="d-lg-table-cell pe-3">{token.name}</td>
</tr>
) : (
<tr onClick={_e => onRowClicked(token.uid)}>
<td className="d-lg-table-cell pe-3">
<EllipsiCell id={token.uid} />
<EllipsiCell id={token.uid} countBefore={12} countAfter={12} />
</td>
<td className="d-lg-table-cell pe-3">{token.name}</td>
<td className="d-lg-table-cell pe-3">
Expand Down
2 changes: 1 addition & 1 deletion src/components/token/TokensTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class TokensTable extends SortableTable {
className="d-lg-table-cell sortable th-table-token-mobile"
onClick={e => this.props.tableHeaderClicked(e, 'transaction_timestamp')}
>
Created At
Created At {this.getArrow('transaction_timestamp')}
</th>
</tr>
) : (
Expand Down
Loading

0 comments on commit eeea785

Please sign in to comment.