feat: pico css styling and not-found page.
Changes:
1. integrates pico minimal responsive css framework for light/dark
themes and overall better looks.
2. adds a not-found page for bad url queries.
3. catches axum/serde deserialization errors of user input and routes
them to the not-found page with a helpful hint (parse error msg)
This commit is contained in:
parent
e53d80d9fa
commit
0f9cbdf0b4
@ -1,7 +1,8 @@
|
|||||||
use crate::html::component::header::HeaderHtml;
|
use crate::html::component::header::HeaderHtml;
|
||||||
|
use crate::html::page::not_found::not_found_html_response;
|
||||||
use crate::model::app_state::AppState;
|
use crate::model::app_state::AppState;
|
||||||
use crate::model::path_block_selector::PathBlockSelector;
|
use crate::model::path_block_selector::PathBlockSelector;
|
||||||
use crate::rpc::block_info::block_info_with_value_worker;
|
use axum::extract::rejection::PathRejection;
|
||||||
use axum::extract::Path;
|
use axum::extract::Path;
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum::response::Html;
|
use axum::response::Html;
|
||||||
@ -10,18 +11,22 @@ use html_escaper::Escape;
|
|||||||
use html_escaper::Trusted;
|
use html_escaper::Trusted;
|
||||||
use neptune_core::rpc_server::BlockInfo;
|
use neptune_core::rpc_server::BlockInfo;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use tarpc::context;
|
||||||
|
|
||||||
pub async fn block_page(
|
pub async fn block_page(
|
||||||
Path(path_block_selector): Path<PathBlockSelector>,
|
user_input_maybe: Result<Path<PathBlockSelector>, PathRejection>,
|
||||||
state: State<Arc<AppState>>,
|
state: State<Arc<AppState>>,
|
||||||
) -> Result<Html<String>, Response> {
|
) -> Result<Html<String>, Response> {
|
||||||
|
let Path(path_block_selector) = user_input_maybe
|
||||||
|
.map_err(|e| not_found_html_response(state.clone(), Some(e.to_string())))?;
|
||||||
|
|
||||||
let value_path: Path<(PathBlockSelector, String)> = Path((path_block_selector, "".to_string()));
|
let value_path: Path<(PathBlockSelector, String)> = Path((path_block_selector, "".to_string()));
|
||||||
block_page_with_value(value_path, state).await
|
block_page_with_value(Ok(value_path), state).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[axum::debug_handler]
|
#[axum::debug_handler]
|
||||||
pub async fn block_page_with_value(
|
pub async fn block_page_with_value(
|
||||||
Path((path_block_selector, value)): Path<(PathBlockSelector, String)>,
|
user_input_maybe: Result<Path<(PathBlockSelector, String)>, PathRejection>,
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
) -> Result<Html<String>, Response> {
|
) -> Result<Html<String>, Response> {
|
||||||
#[derive(boilerplate::Boilerplate)]
|
#[derive(boilerplate::Boilerplate)]
|
||||||
@ -31,12 +36,32 @@ pub async fn block_page_with_value(
|
|||||||
block_info: BlockInfo,
|
block_info: BlockInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let Path((path_block_selector, value)) = user_input_maybe
|
||||||
|
.map_err(|e| not_found_html_response(State(state.clone()), Some(e.to_string())))?;
|
||||||
|
|
||||||
let header = HeaderHtml {
|
let header = HeaderHtml {
|
||||||
site_name: "Neptune Explorer".to_string(),
|
site_name: "Neptune Explorer".to_string(),
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let block_info = block_info_with_value_worker(state, path_block_selector, &value).await?;
|
let block_selector = path_block_selector
|
||||||
|
.as_block_selector(&value)
|
||||||
|
.map_err(|e| not_found_html_response(State(state.clone()), Some(e.to_string())))?;
|
||||||
|
|
||||||
|
let block_info = match state
|
||||||
|
.clone()
|
||||||
|
.rpc_client
|
||||||
|
.block_info(context::current(), block_selector)
|
||||||
|
.await
|
||||||
|
.map_err(|e| not_found_html_response(State(state.clone()), Some(e.to_string())))?
|
||||||
|
{
|
||||||
|
Some(info) => Ok(info),
|
||||||
|
None => Err(not_found_html_response(
|
||||||
|
State(state),
|
||||||
|
Some("Block does not exist".to_string()),
|
||||||
|
)),
|
||||||
|
}?;
|
||||||
|
|
||||||
let block_info_page = BlockInfoHtmlPage { header, block_info };
|
let block_info_page = BlockInfoHtmlPage { header, block_info };
|
||||||
Ok(Html(block_info_page.to_string()))
|
Ok(Html(block_info_page.to_string()))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
pub mod block;
|
pub mod block;
|
||||||
|
pub mod not_found;
|
||||||
pub mod root;
|
pub mod root;
|
||||||
pub mod utxo;
|
pub mod utxo;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use crate::html::component::header::HeaderHtml;
|
use crate::html::component::header::HeaderHtml;
|
||||||
use crate::http_util::not_found_err;
|
use crate::html::page::not_found::not_found_html_response;
|
||||||
use crate::http_util::rpc_err;
|
|
||||||
use crate::model::app_state::AppState;
|
use crate::model::app_state::AppState;
|
||||||
|
use axum::extract::rejection::PathRejection;
|
||||||
use axum::extract::Path;
|
use axum::extract::Path;
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum::response::Html;
|
use axum::response::Html;
|
||||||
@ -14,7 +14,7 @@ use tarpc::context;
|
|||||||
|
|
||||||
#[axum::debug_handler]
|
#[axum::debug_handler]
|
||||||
pub async fn utxo_page(
|
pub async fn utxo_page(
|
||||||
Path(index): Path<u64>,
|
index_maybe: Result<Path<u64>, PathRejection>,
|
||||||
State(state): State<Arc<AppState>>,
|
State(state): State<Arc<AppState>>,
|
||||||
) -> Result<Html<String>, Response> {
|
) -> Result<Html<String>, Response> {
|
||||||
#[derive(boilerplate::Boilerplate)]
|
#[derive(boilerplate::Boilerplate)]
|
||||||
@ -25,14 +25,22 @@ pub async fn utxo_page(
|
|||||||
digest: Digest,
|
digest: Digest,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let Path(index) = index_maybe
|
||||||
|
.map_err(|e| not_found_html_response(State(state.clone()), Some(e.to_string())))?;
|
||||||
|
|
||||||
let digest = match state
|
let digest = match state
|
||||||
.rpc_client
|
.rpc_client
|
||||||
.utxo_digest(context::current(), index)
|
.utxo_digest(context::current(), index)
|
||||||
.await
|
.await
|
||||||
.map_err(rpc_err)?
|
.map_err(|e| not_found_html_response(State(state.clone()), Some(e.to_string())))?
|
||||||
{
|
{
|
||||||
Some(digest) => digest,
|
Some(digest) => digest,
|
||||||
None => return Err(not_found_err()),
|
None => {
|
||||||
|
return Err(not_found_html_response(
|
||||||
|
State(state.clone()),
|
||||||
|
Some("The requested UTXO does not exist".to_string()),
|
||||||
|
))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let header = HeaderHtml {
|
let header = HeaderHtml {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
|
use axum::response::Html;
|
||||||
use axum::response::IntoResponse;
|
use axum::response::IntoResponse;
|
||||||
use axum::response::Response;
|
use axum::response::Response;
|
||||||
use tarpc::client::RpcError;
|
use tarpc::client::RpcError;
|
||||||
@ -10,6 +11,10 @@ pub fn not_found_err() -> Response {
|
|||||||
(StatusCode::NOT_FOUND, "Not Found".to_string()).into_response()
|
(StatusCode::NOT_FOUND, "Not Found".to_string()).into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn not_found_html_err(html: Html<String>) -> Response {
|
||||||
|
(StatusCode::NOT_FOUND, html).into_response()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn rpc_err(e: RpcError) -> Response {
|
pub fn rpc_err(e: RpcError) -> Response {
|
||||||
(StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response()
|
(StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,6 +58,13 @@ async fn main() -> Result<(), RpcError> {
|
|||||||
.route("/block/:selector/:value", get(block_page_with_value))
|
.route("/block/:selector/:value", get(block_page_with_value))
|
||||||
.route("/utxo/:value", get(utxo_page))
|
.route("/utxo/:value", get(utxo_page))
|
||||||
// -- Static files --
|
// -- Static files --
|
||||||
|
.route_service(
|
||||||
|
"/css/pico.min.css",
|
||||||
|
ServeFile::new(concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/templates/web/css/pico.min.css"
|
||||||
|
)),
|
||||||
|
)
|
||||||
.route_service(
|
.route_service(
|
||||||
"/css/styles.css",
|
"/css/styles.css",
|
||||||
ServeFile::new(concat!(
|
ServeFile::new(concat!(
|
||||||
|
|||||||
@ -1,42 +1,8 @@
|
|||||||
div.box h1,
|
|
||||||
div.box h2,
|
|
||||||
div.box h3 {
|
|
||||||
margin-top: 0px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.indent {
|
div.indent {
|
||||||
position: relative;
|
position: relative;
|
||||||
left: 20px;
|
left: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.box {
|
.center-text {
|
||||||
margin-bottom: 10px;
|
text-align: center;
|
||||||
border: solid 1px #ccc;
|
|
||||||
border-top-left-radius: 15px;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: arial, helvetica;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.alt {
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
padding: 10px;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.alt td,
|
|
||||||
th {
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.alt tr:nth-child(odd) td {
|
|
||||||
background: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.alt tr:nth-child(even) td {
|
|
||||||
background: #fff;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1,3 @@
|
|||||||
|
<header class="container">
|
||||||
<h1>{{self.site_name}} : {{self.state.network}}</h1>
|
<h1>{{self.site_name}} : {{self.state.network}}</h1>
|
||||||
|
</header>
|
||||||
@ -1,13 +1,19 @@
|
|||||||
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Neptune Block Explorer: Block Height {{self.block_info.height}}</title>
|
<title>Neptune Block Explorer: Block Height {{self.block_info.height}}</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/pico.min.css" media="screen" />
|
||||||
<link rel="stylesheet" type="text/css" href="/css/styles.css" media="screen" />
|
<link rel="stylesheet" type="text/css" href="/css/styles.css" media="screen" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
{{Trusted(self.header.to_string())}}
|
{{Trusted(self.header.to_string())}}
|
||||||
|
|
||||||
|
<main class="container">
|
||||||
|
|
||||||
|
<article>
|
||||||
<h2>Block height: {{self.block_info.height}}</h2>
|
<h2>Block height: {{self.block_info.height}}</h2>
|
||||||
|
|
||||||
<!-- special_block_notice -->
|
<!-- special_block_notice -->
|
||||||
@ -18,7 +24,7 @@
|
|||||||
<p>This is the Latest Block (tip)</p>
|
<p>This is the Latest Block (tip)</p>
|
||||||
%% }
|
%% }
|
||||||
|
|
||||||
<table class="alt">
|
<table class="striped">
|
||||||
<tr>
|
<tr>
|
||||||
<td>Digest</td>
|
<td>Digest</td>
|
||||||
<td>{{self.block_info.digest.to_hex()}}</td>
|
<td>{{self.block_info.digest.to_hex()}}</td>
|
||||||
@ -53,6 +59,10 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href="/">Home</a>
|
<a href="/">Home</a>
|
||||||
| <a href='/block/genesis'>Genesis</a>
|
| <a href='/block/genesis'>Genesis</a>
|
||||||
@ -70,5 +80,7 @@
|
|||||||
%% }
|
%% }
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
37
templates/web/html/page/not_found.html
Normal file
37
templates/web/html/page/not_found.html
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Neptune Block Explorer: Not Found</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/pico.min.css" media="screen" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/styles.css" media="screen" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<main class="container">
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<header class="center-text"><h3>Not found</h3></header>
|
||||||
|
<section>
|
||||||
|
These are not the droids you're looking for.
|
||||||
|
</section>
|
||||||
|
|
||||||
|
%% if self.error_msg.len() > 0 {
|
||||||
|
<section>
|
||||||
|
Hint: {{self.error_msg}}
|
||||||
|
</section>
|
||||||
|
%% }
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<p>
|
||||||
|
<a href="/">Home</a>
|
||||||
|
| <a href='/block/genesis'>Genesis</a>
|
||||||
|
| <a href='/block/tip'>Tip</a>
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -2,28 +2,36 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>Neptune Block Explorer: (network: {{self.network}})</title>
|
<title>Neptune Block Explorer: (network: {{self.network}})</title>
|
||||||
<link rel="stylesheet" type="text/css" href="/css/styles.css" media="screen" />
|
<link rel="stylesheet" type="text/css" href="/css/styles.css" media="screen" />
|
||||||
</head>
|
<link rel="stylesheet" type="text/css" href="/css/pico.min.css" media="screen" />
|
||||||
<body>
|
|
||||||
<h1>Neptune Block Explorer (network: {{self.network}})</h1>
|
|
||||||
<script>
|
<script>
|
||||||
function handle_submit(form){
|
function handle_submit(form) {
|
||||||
let value = form.height_or_digest.value;
|
let value = form.height_or_digest.value;
|
||||||
var is_digest = value.length == 80;
|
var is_digest = value.length == 80;
|
||||||
var type = is_digest ? "digest" : "height";
|
var type = is_digest ? "digest" : "height";
|
||||||
var uri = form.action + "/" + type + "/" + value;
|
var uri = form.action + "/" + type + "/" + value;
|
||||||
window.location.href = uri;
|
window.location.href = uri;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
function handle_utxo_submit(form) {
|
function handle_utxo_submit(form) {
|
||||||
let value = form.utxo.value;
|
let value = form.utxo.value;
|
||||||
var uri = form.action + "/" + value;
|
var uri = form.action + "/" + value;
|
||||||
window.location.href = uri;
|
window.location.href = uri;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="box">
|
</head>
|
||||||
<h3>Block Lookup</h3>
|
<body>
|
||||||
|
<header class="container">
|
||||||
|
<h1>Neptune Block Explorer (network: {{self.network}})</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container">
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<details open>
|
||||||
|
<summary>Block Lookup</summary>
|
||||||
<form action="/block" method="get" onsubmit="return handle_submit(this)">
|
<form action="/block" method="get" onsubmit="return handle_submit(this)">
|
||||||
Block height or digest:
|
Block height or digest:
|
||||||
<input type="text" size="80" name="height_or_digest"/>
|
<input type="text" size="80" name="height_or_digest"/>
|
||||||
@ -33,58 +41,70 @@ Block height or digest:
|
|||||||
Quick Lookup:
|
Quick Lookup:
|
||||||
<a href="/block/genesis">Genesis Block</a> |
|
<a href="/block/genesis">Genesis Block</a> |
|
||||||
<a href="/block/tip">Tip</a><br/>
|
<a href="/block/tip">Tip</a><br/>
|
||||||
</div>
|
</details>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article>
|
||||||
<div class="box">
|
<details>
|
||||||
<h3>Utxo Lookup</h3>
|
<summary>Utxo Lookup</summary>
|
||||||
<form action="/utxo" method="get" onsubmit="return handle_utxo_submit(this)">
|
<form action="/utxo" method="get" onsubmit="return handle_utxo_submit(this)">
|
||||||
Utxo index:
|
Utxo index:
|
||||||
<input type="text" size="10" name="utxo" />
|
<input type="text" size="10" name="utxo" />
|
||||||
<input type="submit" name="height" value="Lookup Utxo" />
|
<input type="submit" name="height" value="Lookup Utxo" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</details>
|
||||||
|
</article>
|
||||||
|
|
||||||
<h2>REST RPCs</h2>
|
<h2>REST RPCs</h2>
|
||||||
|
|
||||||
<div class="box">
|
<article>
|
||||||
|
<details>
|
||||||
<h3>/block_info</h3>
|
<summary>/block_info</summary>
|
||||||
<div class="indent">
|
<div class="indent">
|
||||||
<h4>Examples</h4>
|
<h4>Examples</h4>
|
||||||
|
|
||||||
<a href="/rpc/block_info/genesis">/rpc/block_info/genesis</a><br/>
|
<ul>
|
||||||
<a href="/rpc/block_info/tip">/rpc/block_info/tip</a><br/>
|
<li><a href="/rpc/block_info/genesis">/rpc/block_info/genesis</a></li>
|
||||||
<a href="/rpc/block_info/height/2">/rpc/block_info/height/2</a><br/>
|
<li><a href="/rpc/block_info/tip">/rpc/block_info/tip</a></li>
|
||||||
<a href="/rpc/block_info/digest/{{self.genesis_digest.to_hex()}}">/rpc/block_info/digest/{{self.genesis_digest.to_hex()}}</a><br/>
|
<li><a href="/rpc/block_info/height/2">/rpc/block_info/height/2</a></li>
|
||||||
<a href="/rpc/block_info/height_or_digest/1">/rpc/block_info/height_or_digest/1</a><br/>
|
<li><a href="/rpc/block_info/digest/{{self.genesis_digest.to_hex()}}">/rpc/block_info/digest/{{self.genesis_digest.to_hex()}}</a></li>
|
||||||
</div>
|
<li><a href="/rpc/block_info/height_or_digest/1">/rpc/block_info/height_or_digest/1</a></li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
</details>
|
||||||
|
</article>
|
||||||
|
|
||||||
<div class="box">
|
<article>
|
||||||
|
<details>
|
||||||
<h3>/block_digest</h3>
|
<summary>/block_digest</summary>
|
||||||
<div class="indent">
|
<div class="indent">
|
||||||
<h4>Examples</h4>
|
<h4>Examples</h4>
|
||||||
|
|
||||||
<a href="/rpc/block_digest/genesis">/rpc/block_digest/genesis</a><br/>
|
<ul>
|
||||||
<a href="/rpc/block_digest/tip">/rpc/block_digest/tip</a><br/>
|
<li><a href="/rpc/block_digest/genesis">/rpc/block_digest/genesis</a></li>
|
||||||
<a href="/rpc/block_digest/height/2">/rpc/block_digest/height/2</a><br/>
|
<li><a href="/rpc/block_digest/tip">/rpc/block_digest/tip</a></li>
|
||||||
<a href="/rpc/block_digest/digest/{{self.genesis_digest.to_hex()}}">/rpc/block_digest/digest/{{self.genesis_digest.to_hex()}}</a><br/>
|
<li><a href="/rpc/block_digest/height/2">/rpc/block_digest/height/2</a></li>
|
||||||
<a href="/rpc/block_digest/height_or_digest/{{self.genesis_digest.to_hex()}}">/rpc/block_digest/height_or_digest/{{self.genesis_digest.to_hex()}}</a><br/>
|
<li><a href="/rpc/block_digest/digest/{{self.genesis_digest.to_hex()}}">/rpc/block_digest/digest/{{self.genesis_digest.to_hex()}}</a></li>
|
||||||
</div>
|
<li><a href="/rpc/block_digest/height_or_digest/{{self.genesis_digest.to_hex()}}">/rpc/block_digest/height_or_digest/{{self.genesis_digest.to_hex()}}</a></li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
</details>
|
||||||
|
</article>
|
||||||
|
|
||||||
<div class="box">
|
<article>
|
||||||
|
<details>
|
||||||
<h3>/utxo_digest</h3>
|
<summary>/utxo_digest</summary>
|
||||||
<div class="indent">
|
<div class="indent">
|
||||||
<h4>Examples</h4>
|
<h4>Examples</h4>
|
||||||
|
|
||||||
<a href="/rpc/utxo_digest/2">/rpc/utxo_digest/2</a><br/>
|
<ul>
|
||||||
|
<li><a href="/rpc/utxo_digest/2">/rpc/utxo_digest/2</a><br/></li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
</details>
|
||||||
|
</article>
|
||||||
|
|
||||||
</div>
|
</main>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -2,14 +2,19 @@
|
|||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>Neptune Block Explorer: Utxo {{self.index}}</title>
|
<title>Neptune Block Explorer: Utxo {{self.index}}</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/pico.min.css" media="screen" />
|
||||||
<link rel="stylesheet" type="text/css" href="/css/styles.css" media="screen" />
|
<link rel="stylesheet" type="text/css" href="/css/styles.css" media="screen" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
{{Trusted(self.header.to_string())}}
|
{{Trusted(self.header.to_string())}}
|
||||||
|
|
||||||
<h3>Utxo Information</h3>
|
<main class="container">
|
||||||
<table class="alt">
|
|
||||||
|
<article>
|
||||||
|
<summary>Utxo Information</summary>
|
||||||
|
<table class="striped">
|
||||||
<tr>
|
<tr>
|
||||||
<td>Index</td>
|
<td>Index</td>
|
||||||
<td>{{self.index}}</td>
|
<td>{{self.index}}</td>
|
||||||
@ -19,12 +24,16 @@
|
|||||||
<td>{{self.digest.to_hex()}}</td>
|
<td>{{self.digest.to_hex()}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article>
|
||||||
<p>
|
<p>
|
||||||
<a href="/">Home</a>
|
<a href="/">Home</a>
|
||||||
| <a href='/block/genesis'>Genesis</a>
|
| <a href='/block/genesis'>Genesis</a>
|
||||||
| <a href='/block/tip'>Tip</a>
|
| <a href='/block/tip'>Tip</a>
|
||||||
</p>
|
</p>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user