mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-18 21:25:09 +01:00
cdk-ldk web ui updates (#1027)
This commit is contained in:
@@ -29,6 +29,3 @@ tower-http.workspace = true
|
||||
rust-embed = "8.5.0"
|
||||
serde_urlencoded = "0.7"
|
||||
urlencoding = "2.1"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ pub async fn post_open_channel(
|
||||
}
|
||||
|
||||
pub async fn close_channel_page(
|
||||
State(_state): State<AppState>,
|
||||
State(state): State<AppState>,
|
||||
query: Query<HashMap<String, String>>,
|
||||
) -> Result<Html<String>, StatusCode> {
|
||||
let channel_id = query.get("channel_id").unwrap_or(&"".to_string()).clone();
|
||||
@@ -229,24 +229,40 @@ pub async fn close_channel_page(
|
||||
return Ok(Html(layout("Close Channel Error", content).into_string()));
|
||||
}
|
||||
|
||||
// Get channel information for amount display
|
||||
let channels = state.node.inner.list_channels();
|
||||
let channel = channels
|
||||
.iter()
|
||||
.find(|c| c.user_channel_id.0.to_string() == channel_id);
|
||||
|
||||
let content = form_card(
|
||||
"Close Channel",
|
||||
html! {
|
||||
p { "Are you sure you want to close this channel?" }
|
||||
div class="info-item" {
|
||||
span class="info-label" { "User Channel ID:" }
|
||||
span class="info-value" style="font-family: monospace; font-size: 0.85rem;" { (channel_id) }
|
||||
p style="margin-bottom: 1.5rem;" { "Are you sure you want to close this channel?" }
|
||||
|
||||
// Channel details in consistent format
|
||||
div class="channel-details" {
|
||||
div class="detail-row" {
|
||||
span class="detail-label" { "User Channel ID" }
|
||||
span class="detail-value-amount" { (channel_id) }
|
||||
}
|
||||
div class="info-item" {
|
||||
span class="info-label" { "Node ID:" }
|
||||
span class="info-value" style="font-family: monospace; font-size: 0.85rem;" { (node_id) }
|
||||
div class="detail-row" {
|
||||
span class="detail-label" { "Node ID" }
|
||||
span class="detail-value-amount" { (node_id) }
|
||||
}
|
||||
form method="post" action="/channels/close" style="margin-top: 1rem;" {
|
||||
@if let Some(ch) = channel {
|
||||
div class="detail-row" {
|
||||
span class="detail-label" { "Channel Amount" }
|
||||
span class="detail-value-amount" { (format_sats_as_btc(ch.channel_value_sats)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
form method="post" action="/channels/close" style="margin-top: 1rem; display: flex; justify-content: space-between; align-items: center;" {
|
||||
input type="hidden" name="channel_id" value=(channel_id) {}
|
||||
input type="hidden" name="node_id" value=(node_id) {}
|
||||
button type="submit" style="background: #dc3545;" { "Close Channel" }
|
||||
" "
|
||||
a href="/balance" { button type="button" { "Cancel" } }
|
||||
a href="/balance" { button type="button" class="button-secondary" { "Cancel" } }
|
||||
button type="submit" class="button-destructive" { "Close Channel" }
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -255,7 +271,7 @@ pub async fn close_channel_page(
|
||||
}
|
||||
|
||||
pub async fn force_close_channel_page(
|
||||
State(_state): State<AppState>,
|
||||
State(state): State<AppState>,
|
||||
query: Query<HashMap<String, String>>,
|
||||
) -> Result<Html<String>, StatusCode> {
|
||||
let channel_id = query.get("channel_id").unwrap_or(&"".to_string()).clone();
|
||||
@@ -273,32 +289,48 @@ pub async fn force_close_channel_page(
|
||||
));
|
||||
}
|
||||
|
||||
// Get channel information for amount display
|
||||
let channels = state.node.inner.list_channels();
|
||||
let channel = channels
|
||||
.iter()
|
||||
.find(|c| c.user_channel_id.0.to_string() == channel_id);
|
||||
|
||||
let content = form_card(
|
||||
"Force Close Channel",
|
||||
html! {
|
||||
div style="border: 2px solid #d63384; background-color: rgba(214, 51, 132, 0.1); padding: 1rem; margin-bottom: 1rem; border-radius: 0.5rem;" {
|
||||
h4 style="color: #d63384; margin: 0 0 0.5rem 0;" { "⚠️ Warning: Force Close" }
|
||||
p style="color: #d63384; margin: 0; font-size: 0.9rem;" {
|
||||
div style="border: 2px solid #f97316; background-color: rgba(249, 115, 22, 0.1); padding: 1rem; margin-bottom: 1rem; border-radius: 0.5rem;" {
|
||||
h4 style="color: #f97316; margin: 0 0 0.5rem 0;" { "⚠️ Warning: Force Close" }
|
||||
p style="color: #f97316; margin: 0; font-size: 0.9rem;" {
|
||||
"Force close should NOT be used if normal close is preferred. "
|
||||
"Force close will immediately broadcast the latest commitment transaction and may result in delayed fund recovery. "
|
||||
"Only use this if the channel counterparty is unresponsive or there are other issues preventing normal closure."
|
||||
}
|
||||
}
|
||||
p { "Are you sure you want to force close this channel?" }
|
||||
div class="info-item" {
|
||||
span class="info-label" { "User Channel ID:" }
|
||||
span class="info-value" style="font-family: monospace; font-size: 0.85rem;" { (channel_id) }
|
||||
p style="margin-bottom: 1.5rem;" { "Are you sure you want to force close this channel?" }
|
||||
|
||||
// Channel details in consistent format
|
||||
div class="channel-details" {
|
||||
div class="detail-row" {
|
||||
span class="detail-label" { "User Channel ID" }
|
||||
span class="detail-value-amount" { (channel_id) }
|
||||
}
|
||||
div class="info-item" {
|
||||
span class="info-label" { "Node ID:" }
|
||||
span class="info-value" style="font-family: monospace; font-size: 0.85rem;" { (node_id) }
|
||||
div class="detail-row" {
|
||||
span class="detail-label" { "Node ID" }
|
||||
span class="detail-value-amount" { (node_id) }
|
||||
}
|
||||
form method="post" action="/channels/force-close" style="margin-top: 1rem;" {
|
||||
@if let Some(ch) = channel {
|
||||
div class="detail-row" {
|
||||
span class="detail-label" { "Channel Amount" }
|
||||
span class="detail-value-amount" { (format_sats_as_btc(ch.channel_value_sats)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
form method="post" action="/channels/force-close" style="margin-top: 1rem; display: flex; justify-content: space-between; align-items: center;" {
|
||||
input type="hidden" name="channel_id" value=(channel_id) {}
|
||||
input type="hidden" name="node_id" value=(node_id) {}
|
||||
button type="submit" style="background: #d63384;" { "Force Close Channel" }
|
||||
" "
|
||||
a href="/balance" { button type="button" { "Cancel" } }
|
||||
a href="/balance" { button type="button" class="button-secondary" { "Cancel" } }
|
||||
button type="submit" class="button-destructive" { "Force Close Channel" }
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ use axum::http::StatusCode;
|
||||
use axum::response::Html;
|
||||
use maud::html;
|
||||
|
||||
use crate::web::handlers::AppState;
|
||||
use crate::web::handlers::utils::AppState;
|
||||
use crate::web::templates::{format_sats_as_btc, layout};
|
||||
|
||||
pub async fn balance_page(State(state): State<AppState>) -> Result<Html<String>, StatusCode> {
|
||||
@@ -26,18 +26,35 @@ pub async fn balance_page(State(state): State<AppState>) -> Result<Html<String>,
|
||||
html! {
|
||||
h2 style="text-align: center; margin-bottom: 3rem;" { "Lightning" }
|
||||
|
||||
// Quick Actions section - matching dashboard style
|
||||
// Quick Actions section - individual cards
|
||||
div class="card" style="margin-bottom: 2rem;" {
|
||||
h2 { "Quick Actions" }
|
||||
div style="display: flex; gap: 1rem; margin-top: 1rem; flex-wrap: wrap;" {
|
||||
a href="/channels/open" style="text-decoration: none; flex: 1; min-width: 200px;" {
|
||||
button class="button-primary" style="width: 100%;" { "Open Channel" }
|
||||
div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;" {
|
||||
// Open Channel Card
|
||||
div class="quick-action-card" {
|
||||
h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Open Channel" }
|
||||
p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Create a new Lightning Network channel to connect with another node." }
|
||||
a href="/channels/open" style="text-decoration: none;" {
|
||||
button class="button-outline" { "Open Channel" }
|
||||
}
|
||||
a href="/invoices" style="text-decoration: none; flex: 1; min-width: 200px;" {
|
||||
button class="button-primary" style="width: 100%;" { "Create Invoice" }
|
||||
}
|
||||
a href="/payments/send" style="text-decoration: none; flex: 1; min-width: 200px;" {
|
||||
button class="button-primary" style="width: 100%;" { "Make Lightning Payment" }
|
||||
|
||||
// Create Invoice Card
|
||||
div class="quick-action-card" {
|
||||
h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Create Invoice" }
|
||||
p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Generate a Lightning invoice to receive payments from other users or services." }
|
||||
a href="/invoices" style="text-decoration: none;" {
|
||||
button class="button-outline" { "Create Invoice" }
|
||||
}
|
||||
}
|
||||
|
||||
// Make Payment Card
|
||||
div class="quick-action-card" {
|
||||
h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Make Lightning Payment" }
|
||||
p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Send Lightning payments to other users using invoices. BOLT 11 & 12 supported." }
|
||||
a href="/invoices" style="text-decoration: none;" {
|
||||
button class="button-outline" { "Make Payment" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,18 +90,35 @@ pub async fn balance_page(State(state): State<AppState>) -> Result<Html<String>,
|
||||
html! {
|
||||
h2 style="text-align: center; margin-bottom: 3rem;" { "Lightning" }
|
||||
|
||||
// Quick Actions section - matching dashboard style
|
||||
// Quick Actions section - individual cards
|
||||
div class="card" style="margin-bottom: 2rem;" {
|
||||
h2 { "Quick Actions" }
|
||||
div style="display: flex; gap: 1rem; margin-top: 1rem; flex-wrap: wrap;" {
|
||||
a href="/channels/open" style="text-decoration: none; flex: 1; min-width: 200px;" {
|
||||
button class="button-primary" style="width: 100%;" { "Open Channel" }
|
||||
div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;" {
|
||||
// Open Channel Card
|
||||
div class="quick-action-card" {
|
||||
h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Open Channel" }
|
||||
p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Create a new Lightning channel by connecting with another node." }
|
||||
a href="/channels/open" style="text-decoration: none;" {
|
||||
button class="button-outline" { "Open Channel" }
|
||||
}
|
||||
a href="/invoices" style="text-decoration: none; flex: 1; min-width: 200px;" {
|
||||
button class="button-primary" style="width: 100%;" { "Create Invoice" }
|
||||
}
|
||||
a href="/payments/send" style="text-decoration: none; flex: 1; min-width: 200px;" {
|
||||
button class="button-primary" style="width: 100%;" { "Make Lightning Payment" }
|
||||
|
||||
// Create Invoice Card
|
||||
div class="quick-action-card" {
|
||||
h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Create Invoice" }
|
||||
p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Generate a Lightning invoice to receive payments." }
|
||||
a href="/invoices" style="text-decoration: none;" {
|
||||
button class="button-outline" { "Create Invoice" }
|
||||
}
|
||||
}
|
||||
|
||||
// Make Payment Card
|
||||
div class="quick-action-card" {
|
||||
h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Make Lightning Payment" }
|
||||
p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Send Lightning payments to other users using invoices." }
|
||||
a href="/payments/send" style="text-decoration: none;" {
|
||||
button class="button-outline" { "Make Payment" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,30 +146,45 @@ pub async fn balance_page(State(state): State<AppState>) -> Result<Html<String>,
|
||||
}
|
||||
}
|
||||
|
||||
div class="card" {
|
||||
h2 { "Channel Details" }
|
||||
// Channel Details header (outside card)
|
||||
h2 class="section-header" { "Channel Details" }
|
||||
|
||||
// Channels list
|
||||
@for channel in &channels {
|
||||
div class="channel-item" {
|
||||
div class="channel-header" {
|
||||
span class="channel-id" { "Channel ID: " (channel.channel_id.to_string()) }
|
||||
@for (index, channel) in channels.iter().enumerate() {
|
||||
@let node_id = channel.counterparty_node_id.to_string();
|
||||
@let channel_number = index + 1;
|
||||
|
||||
div class="channel-box" {
|
||||
// Channel number as prominent header
|
||||
div class="channel-alias" { (format!("Channel {}", channel_number)) }
|
||||
|
||||
// Channel details in left-aligned format
|
||||
div class="channel-details" {
|
||||
div class="detail-row" {
|
||||
span class="detail-label" { "Channel ID" }
|
||||
span class="detail-value-amount" { (channel.channel_id.to_string()) }
|
||||
}
|
||||
@if let Some(short_channel_id) = channel.short_channel_id {
|
||||
div class="detail-row" {
|
||||
span class="detail-label" { "Short Channel ID" }
|
||||
span class="detail-value-amount" { (short_channel_id.to_string()) }
|
||||
}
|
||||
}
|
||||
div class="detail-row" {
|
||||
span class="detail-label" { "Node ID" }
|
||||
span class="detail-value-amount" { (node_id) }
|
||||
}
|
||||
div class="detail-row" {
|
||||
span class="detail-label" { "Status" }
|
||||
@if channel.is_usable {
|
||||
span class="status-badge status-active" { "Active" }
|
||||
} @else {
|
||||
span class="status-badge status-inactive" { "Inactive" }
|
||||
}
|
||||
}
|
||||
div class="info-item" {
|
||||
span class="info-label" { "Counterparty" }
|
||||
span class="info-value" style="font-family: monospace; font-size: 0.85rem;" { (channel.counterparty_node_id.to_string()) }
|
||||
}
|
||||
@if let Some(short_channel_id) = channel.short_channel_id {
|
||||
div class="info-item" {
|
||||
span class="info-label" { "Short Channel ID" }
|
||||
span class="info-value" { (short_channel_id.to_string()) }
|
||||
}
|
||||
}
|
||||
|
||||
// Balance information cards (keeping existing style)
|
||||
div class="balance-info" {
|
||||
div class="balance-item" {
|
||||
div class="balance-amount" { (format_sats_as_btc(channel.outbound_capacity_msat / 1000)) }
|
||||
@@ -150,20 +199,20 @@ pub async fn balance_page(State(state): State<AppState>) -> Result<Html<String>,
|
||||
div class="balance-label" { "Total" }
|
||||
}
|
||||
}
|
||||
|
||||
// Action buttons
|
||||
@if channel.is_usable {
|
||||
div style="margin-top: 1rem; display: flex; gap: 0.5rem;" {
|
||||
div class="channel-actions" {
|
||||
a href=(format!("/channels/close?channel_id={}&node_id={}", channel.user_channel_id.0, channel.counterparty_node_id)) {
|
||||
button style="background: #dc3545;" { "Close Channel" }
|
||||
button class="button-secondary" { "Close Channel" }
|
||||
}
|
||||
a href=(format!("/channels/force-close?channel_id={}&node_id={}", channel.user_channel_id.0, channel.counterparty_node_id)) {
|
||||
button style="background: #d63384;" title="Force close should not be used if normal close is preferred. Force close will broadcast the latest commitment transaction immediately." { "Force Close" }
|
||||
button class="button-destructive" title="Force close should not be used if normal close is preferred. Force close will broadcast the latest commitment transaction immediately." { "Force Close" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -79,15 +79,26 @@ pub async fn onchain_page(
|
||||
let mut content = html! {
|
||||
h2 style="text-align: center; margin-bottom: 3rem;" { "On-chain" }
|
||||
|
||||
// Quick Actions section - matching dashboard style
|
||||
// Quick Actions section - individual cards
|
||||
div class="card" style="margin-bottom: 2rem;" {
|
||||
h2 { "Quick Actions" }
|
||||
div style="display: flex; gap: 1rem; margin-top: 1rem; flex-wrap: wrap;" {
|
||||
a href="/onchain?action=receive" style="text-decoration: none; flex: 1; min-width: 200px;" {
|
||||
button class="button-primary" style="width: 100%;" { "Receive Bitcoin" }
|
||||
div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;" {
|
||||
// Receive Bitcoin Card
|
||||
div class="quick-action-card" {
|
||||
h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Receive Bitcoin" }
|
||||
p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Generate a new Bitcoin address to receive on-chain payments from other users or services." }
|
||||
a href="/onchain?action=receive" style="text-decoration: none;" {
|
||||
button class="button-outline" { "Receive Bitcoin" }
|
||||
}
|
||||
}
|
||||
|
||||
// Send Bitcoin Card
|
||||
div class="quick-action-card" {
|
||||
h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Send Bitcoin" }
|
||||
p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Send Bitcoin to another address on the blockchain. Standard on-chain transactions." }
|
||||
a href="/onchain?action=send" style="text-decoration: none;" {
|
||||
button class="button-outline" { "Send Bitcoin" }
|
||||
}
|
||||
a href="/onchain?action=send" style="text-decoration: none; flex: 1; min-width: 200px;" {
|
||||
button class="button-primary" style="width: 100%;" { "Send Bitcoin" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,15 +124,26 @@ pub async fn onchain_page(
|
||||
content = html! {
|
||||
h2 style="text-align: center; margin-bottom: 3rem;" { "On-chain" }
|
||||
|
||||
// Quick Actions section - matching dashboard style
|
||||
// Quick Actions section - individual cards
|
||||
div class="card" style="margin-bottom: 2rem;" {
|
||||
h2 { "Quick Actions" }
|
||||
div style="display: flex; gap: 1rem; margin-top: 1rem; flex-wrap: wrap;" {
|
||||
a href="/onchain?action=receive" style="text-decoration: none; flex: 1; min-width: 200px;" {
|
||||
button class="button-primary" style="width: 100%;" { "Receive Bitcoin" }
|
||||
div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;" {
|
||||
// Receive Bitcoin Card
|
||||
div class="quick-action-card" {
|
||||
h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Receive Bitcoin" }
|
||||
p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Generate a new Bitcoin address to receive on-chain payments from other users or services." }
|
||||
a href="/onchain?action=receive" style="text-decoration: none;" {
|
||||
button class="button-outline" { "Receive Bitcoin" }
|
||||
}
|
||||
}
|
||||
|
||||
// Send Bitcoin Card
|
||||
div class="quick-action-card" {
|
||||
h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Send Bitcoin" }
|
||||
p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Send Bitcoin to another address on the blockchain. Standard on-chain transactions." }
|
||||
a href="/onchain?action=send" style="text-decoration: none;" {
|
||||
button class="button-outline" { "Send Bitcoin" }
|
||||
}
|
||||
a href="/onchain?action=send" style="text-decoration: none; flex: 1; min-width: 200px;" {
|
||||
button class="button-primary" style="width: 100%;" { "Send Bitcoin" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,7 +163,7 @@ pub async fn onchain_page(
|
||||
}
|
||||
input type="hidden" id="send_action" name="send_action" value="send" {}
|
||||
div style="display: flex; justify-content: space-between; gap: 1rem; margin-top: 2rem;" {
|
||||
a href="/onchain" { button type="button" { "Cancel" } }
|
||||
a href="/onchain" { button type="button" class="button-secondary" { "Cancel" } }
|
||||
div style="display: flex; gap: 0.5rem;" {
|
||||
button type="submit" onclick="document.getElementById('send_action').value='send'" { "Send Payment" }
|
||||
button type="submit" onclick="document.getElementById('send_action').value='send_all'; document.getElementById('amount_sat').value=''" { "Send All" }
|
||||
@@ -171,15 +193,26 @@ pub async fn onchain_page(
|
||||
content = html! {
|
||||
h2 style="text-align: center; margin-bottom: 3rem;" { "On-chain" }
|
||||
|
||||
// Quick Actions section - matching dashboard style
|
||||
// Quick Actions section - individual cards
|
||||
div class="card" style="margin-bottom: 2rem;" {
|
||||
h2 { "Quick Actions" }
|
||||
div style="display: flex; gap: 1rem; margin-top: 1rem; flex-wrap: wrap;" {
|
||||
a href="/onchain?action=receive" style="text-decoration: none; flex: 1; min-width: 200px;" {
|
||||
button class="button-primary" style="width: 100%;" { "Receive Bitcoin" }
|
||||
div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;" {
|
||||
// Receive Bitcoin Card
|
||||
div class="quick-action-card" {
|
||||
h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Receive Bitcoin" }
|
||||
p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Generate a new Bitcoin address to receive on-chain payments from other users or services." }
|
||||
a href="/onchain?action=receive" style="text-decoration: none;" {
|
||||
button class="button-outline" { "Receive Bitcoin" }
|
||||
}
|
||||
}
|
||||
|
||||
// Send Bitcoin Card
|
||||
div class="quick-action-card" {
|
||||
h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Send Bitcoin" }
|
||||
p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Send Bitcoin to another address on the blockchain. Standard on-chain transactions." }
|
||||
a href="/onchain?action=send" style="text-decoration: none;" {
|
||||
button class="button-outline" { "Send Bitcoin" }
|
||||
}
|
||||
a href="/onchain?action=send" style="text-decoration: none; flex: 1; min-width: 200px;" {
|
||||
button class="button-primary" style="width: 100%;" { "Send Bitcoin" }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,7 +224,7 @@ pub async fn onchain_page(
|
||||
form method="post" action="/onchain/new-address" {
|
||||
p style="margin-bottom: 2rem;" { "Click the button below to generate a new Bitcoin address for receiving on-chain payments." }
|
||||
div style="display: flex; justify-content: space-between; gap: 1rem;" {
|
||||
a href="/onchain" { button type="button" { "Cancel" } }
|
||||
a href="/onchain" { button type="button" class="button-secondary" { "Cancel" } }
|
||||
button class="button-primary" type="submit" { "Generate New Address" }
|
||||
}
|
||||
}
|
||||
@@ -345,7 +378,7 @@ pub async fn onchain_confirm_page(
|
||||
div class="card" {
|
||||
div style="display: flex; justify-content: space-between; gap: 1rem; margin-top: 2rem;" {
|
||||
a href="/onchain?action=send" {
|
||||
button type="button" class="button-secondary" { "← Cancel" }
|
||||
button type="button" class="button-secondary" { "Cancel" }
|
||||
}
|
||||
div style="display: flex; gap: 0.5rem;" {
|
||||
a href=(confirmation_url) {
|
||||
|
||||
@@ -60,34 +60,259 @@ pub fn layout(title: &str, content: Markup) -> Markup {
|
||||
|
||||
/* Dark mode using system preference */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary: 210 40% 98%;
|
||||
--primary-foreground: 222.2 84% 4.9%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 212.7 26.8% 83.9%;
|
||||
body {
|
||||
background: linear-gradient(rgb(23, 25, 29), rgb(18, 19, 21));
|
||||
}
|
||||
|
||||
/* Dark mode colors */
|
||||
--fg-primary: #f8fafc;
|
||||
--fg-muted: #94a3b8;
|
||||
:root {
|
||||
--background: 0 0% 0%;
|
||||
--foreground: 0 0% 100%;
|
||||
--card: 0 0% 0%;
|
||||
--card-foreground: 0 0% 100%;
|
||||
--popover: 0 0% 0%;
|
||||
--popover-foreground: 0 0% 100%;
|
||||
--primary: 0 0% 100%;
|
||||
--primary-foreground: 0 0% 0%;
|
||||
--secondary: 0 0% 20%;
|
||||
--secondary-foreground: 0 0% 100%;
|
||||
--muted: 0 0% 20%;
|
||||
--muted-foreground: 0 0% 70%;
|
||||
--accent: 0 0% 20%;
|
||||
--accent-foreground: 0 0% 100%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 0% 100%;
|
||||
--border: 0 0% 20%;
|
||||
--input: 0 0% 20%;
|
||||
--ring: 0 0% 83.9%;
|
||||
|
||||
/* Dark mode text hierarchy colors */
|
||||
--text-primary: #ffffff;
|
||||
--text-secondary: #e6e6e6;
|
||||
--text-tertiary: #cccccc;
|
||||
--text-quaternary: #b3b3b3;
|
||||
--text-muted: #999999;
|
||||
--text-muted-2: #888888;
|
||||
--text-muted-3: #666666;
|
||||
--text-muted-4: #333333;
|
||||
--text-subtle: #1a1a1a;
|
||||
|
||||
/* Header text colors for dark mode */
|
||||
--header-title: #ffffff;
|
||||
--header-subtitle: #e2e8f0;
|
||||
--header-subtitle: #e6e6e6;
|
||||
}
|
||||
|
||||
/* Dark mode box styling - no borders, subtle background */
|
||||
.card {
|
||||
background-color: rgba(255, 255, 255, 0.03) !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.channel-box {
|
||||
background-color: rgba(255, 255, 255, 0.03) !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
background-color: rgba(255, 255, 255, 0.03) !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.balance-item {
|
||||
background-color: rgba(255, 255, 255, 0.03) !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.node-info-main-container {
|
||||
background-color: rgba(255, 255, 255, 0.03) !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.node-avatar {
|
||||
background-color: rgba(255, 255, 255, 0.03) !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
/* Text hierarchy colors */
|
||||
.section-header {
|
||||
color: var(--text-primary) !important;
|
||||
}
|
||||
|
||||
.channel-alias {
|
||||
color: var(--text-primary) !important;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
color: var(--text-muted) !important;
|
||||
}
|
||||
|
||||
.detail-value, .detail-value-amount {
|
||||
color: var(--text-secondary) !important;
|
||||
}
|
||||
|
||||
.metric-label, .balance-label {
|
||||
color: var(--text-muted) !important;
|
||||
}
|
||||
|
||||
.metric-value, .balance-amount {
|
||||
color: var(--text-primary) !important;
|
||||
}
|
||||
|
||||
/* Page headers and section titles */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: var(--text-primary) !important;
|
||||
}
|
||||
|
||||
/* Form card titles */
|
||||
.form-card h2, .form-card h3 {
|
||||
color: var(--text-primary) !important;
|
||||
}
|
||||
|
||||
/* Quick action cards styling */
|
||||
.quick-action-card {
|
||||
background-color: rgba(255, 255, 255, 0.03) !important;
|
||||
border: none !important;
|
||||
border-radius: 0.75rem !important;
|
||||
padding: 1.5rem !important;
|
||||
}
|
||||
|
||||
/* Dark mode outline button styling */
|
||||
.button-outline {
|
||||
background-color: transparent !important;
|
||||
color: var(--text-primary) !important;
|
||||
border: 1px solid var(--text-muted) !important;
|
||||
}
|
||||
|
||||
.button-outline:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2) !important;
|
||||
}
|
||||
|
||||
/* Navigation dark mode styling */
|
||||
nav {
|
||||
background-color: transparent !important;
|
||||
border-top: none !important;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* New Header Layout Styles */
|
||||
.header-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.header-avatar {
|
||||
flex-shrink: 0;
|
||||
background-color: hsl(var(--muted) / 0.3);
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
padding: 0.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.header-avatar-image {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: calc(var(--radius) - 2px);
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.node-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
padding-top: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.node-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
border-radius: 50%;
|
||||
background-color: #10b981;
|
||||
box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.2);
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.node-title {
|
||||
font-size: 1.875rem;
|
||||
font-weight: 600;
|
||||
color: var(--header-title);
|
||||
margin: 0;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.node-subtitle {
|
||||
font-size: 0.875rem;
|
||||
color: var(--header-subtitle);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Responsive header */
|
||||
@media (max-width: 768px) {
|
||||
.header-content {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.node-title {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
nav a {
|
||||
color: var(--text-muted) !important;
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
color: var(--text-secondary) !important;
|
||||
background-color: rgba(255, 255, 255, 0.05) !important;
|
||||
}
|
||||
|
||||
nav a.active {
|
||||
color: var(--text-primary) !important;
|
||||
background-color: rgba(255, 255, 255, 0.08) !important;
|
||||
}
|
||||
|
||||
nav a.active:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,13 +360,13 @@ pub fn layout(title: &str, content: Markup) -> Markup {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
border-bottom: 1px solid hsl(var(--border));
|
||||
margin-bottom: 3rem;
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
height: 400px; /* Fixed height for better proportion */
|
||||
height: 200px; /* Reduced height for more compact header */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
/* Dark mode header background - using different image */
|
||||
@@ -153,14 +378,17 @@ pub fn layout(title: &str, content: Markup) -> Markup {
|
||||
|
||||
/* Ensure text is positioned properly */
|
||||
header .container {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
position: relative;
|
||||
top: auto;
|
||||
left: auto;
|
||||
transform: none;
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
padding: 0 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@@ -183,7 +411,7 @@ pub fn layout(title: &str, content: Markup) -> Markup {
|
||||
|
||||
@media (max-width: 768px) {
|
||||
header {
|
||||
height: 300px; /* Smaller height on mobile */
|
||||
height: 150px; /* Smaller height on mobile */
|
||||
}
|
||||
|
||||
header .container {
|
||||
@@ -424,14 +652,17 @@ pub fn layout(title: &str, content: Markup) -> Markup {
|
||||
}
|
||||
|
||||
.button-destructive {
|
||||
background-color: hsl(var(--destructive));
|
||||
color: hsl(var(--destructive-foreground));
|
||||
background-color: transparent !important;
|
||||
color: #DC2626 !important;
|
||||
border: 1px solid #DC2626 !important;
|
||||
}
|
||||
|
||||
.button-destructive:hover {
|
||||
background-color: hsl(var(--destructive) / 0.9);
|
||||
background-color: rgba(220, 38, 38, 0.2) !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.button-sm {
|
||||
height: 2rem;
|
||||
border-radius: calc(var(--radius) - 4px);
|
||||
@@ -694,7 +925,7 @@ pub fn layout(title: &str, content: Markup) -> Markup {
|
||||
border: 1px solid hsl(var(--border));
|
||||
}
|
||||
|
||||
/* Legacy status classes */
|
||||
/* Status badge classes - consistent with payment type badges */
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -706,16 +937,24 @@ pub fn layout(title: &str, content: Markup) -> Markup {
|
||||
}
|
||||
|
||||
.status-active {
|
||||
background-color: hsl(142.1 70.6% 45.3%);
|
||||
color: hsl(355.7 78% 98.4%);
|
||||
background-color: hsl(142.1 70.6% 45.3% / 0.1);
|
||||
color: hsl(142.1 70.6% 45.3%);
|
||||
border: 1px solid hsl(142.1 70.6% 45.3% / 0.2);
|
||||
}
|
||||
|
||||
.status-inactive {
|
||||
background-color: hsl(var(--destructive));
|
||||
color: hsl(var(--destructive-foreground));
|
||||
background-color: hsl(0 84.2% 60.2% / 0.1);
|
||||
color: hsl(0 84.2% 60.2%);
|
||||
border: 1px solid hsl(0 84.2% 60.2% / 0.2);
|
||||
}
|
||||
|
||||
.channel-item {
|
||||
.status-pending {
|
||||
background-color: hsl(215.4 16.3% 46.9% / 0.1);
|
||||
color: hsl(215.4 16.3% 46.9%);
|
||||
border: 1px solid hsl(215.4 16.3% 46.9% / 0.2);
|
||||
}
|
||||
|
||||
.channel-box {
|
||||
background-color: hsl(var(--card));
|
||||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius);
|
||||
@@ -723,23 +962,87 @@ pub fn layout(title: &str, content: Markup) -> Markup {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.channel-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.section-header {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: hsl(var(--foreground));
|
||||
margin-bottom: 1.5rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.channel-alias {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--foreground));
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.channel-details {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
margin-bottom: 0.75rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.channel-id {
|
||||
.detail-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-weight: 500;
|
||||
color: hsl(var(--muted-foreground));
|
||||
font-size: 0.875rem;
|
||||
min-width: 120px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
color: hsl(var(--foreground));
|
||||
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Courier New', monospace;
|
||||
font-size: 0.875rem;
|
||||
color: hsl(var(--muted-foreground));
|
||||
word-break: break-all;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.detail-value-amount {
|
||||
color: hsl(var(--foreground));
|
||||
font-size: 0.875rem;
|
||||
word-break: break-all;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.channel-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 1rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.channel-actions {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.balance-info {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
@@ -908,6 +1211,30 @@ pub fn layout(title: &str, content: Markup) -> Markup {
|
||||
border-color: hsl(var(--primary));
|
||||
}
|
||||
|
||||
/* Dark mode specific styling for payment filter tabs */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.payment-filter-tab {
|
||||
background-color: rgba(255, 255, 255, 0.03) !important;
|
||||
border-color: var(--text-muted) !important;
|
||||
color: var(--text-muted) !important;
|
||||
}
|
||||
|
||||
.payment-filter-tab:hover {
|
||||
background-color: rgba(255, 255, 255, 0.08) !important;
|
||||
color: var(--text-secondary) !important;
|
||||
}
|
||||
|
||||
.payment-filter-tab.active {
|
||||
background-color: rgba(255, 255, 255, 0.12) !important;
|
||||
color: var(--text-primary) !important;
|
||||
border-color: var(--text-secondary) !important;
|
||||
}
|
||||
|
||||
.payment-filter-tab.active:hover {
|
||||
background-color: rgba(255, 255, 255, 0.15) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.payment-type-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -1086,7 +1413,7 @@ pub fn layout(title: &str, content: Markup) -> Markup {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
align-items: flex-start;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.node-info-main-container {
|
||||
@@ -1099,6 +1426,7 @@ pub fn layout(title: &str, content: Markup) -> Markup {
|
||||
border-radius: var(--radius);
|
||||
padding: 1.5rem;
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.node-info-left {
|
||||
@@ -1170,6 +1498,7 @@ pub fn layout(title: &str, content: Markup) -> Markup {
|
||||
width: 280px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.node-metrics .card {
|
||||
@@ -1177,16 +1506,28 @@ pub fn layout(title: &str, content: Markup) -> Markup {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.node-metrics .metrics-container {
|
||||
flex-direction: column;
|
||||
margin: 1rem 0 0 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
gap: 1rem;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.node-metrics .metric-card {
|
||||
min-width: auto;
|
||||
padding: 1rem;
|
||||
height: fit-content;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Mobile responsive design for node info */
|
||||
@@ -1300,19 +1641,70 @@ pub fn layout(title: &str, content: Markup) -> Markup {
|
||||
body {
|
||||
header {
|
||||
div class="container" {
|
||||
h1 { "CDK LDK Node" }
|
||||
p class="subtitle" { "Lightning Network Node Management" }
|
||||
div class="header-content" {
|
||||
div class="header-left" {
|
||||
div class="header-avatar" {
|
||||
img src="/static/images/nut.png" alt="CDK LDK Node Icon" class="header-avatar-image";
|
||||
}
|
||||
div class="node-info" {
|
||||
div class="node-status" {
|
||||
span class="status-indicator status-running" {}
|
||||
span class="status-text" { "Running" }
|
||||
}
|
||||
h1 class="node-title" { "CDK LDK Node" }
|
||||
span class="node-subtitle" { "Cashu Mint & Lightning Network Node Management" }
|
||||
}
|
||||
}
|
||||
div class="header-right" {
|
||||
// Right side content can be added here later if needed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nav {
|
||||
div class="container" {
|
||||
ul {
|
||||
li { a href="/" { "Dashboard" } }
|
||||
li { a href="/balance" { "Lightning" } }
|
||||
li { a href="/onchain" { "On-chain" } }
|
||||
li { a href="/invoices" { "Invoices" } }
|
||||
li { a href="/payments" { "All Payments" } }
|
||||
li {
|
||||
a href="/" {
|
||||
svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 0.5rem;" {
|
||||
path d="M15.6 2.7a10 10 0 1 0 5.7 5.7" {}
|
||||
circle cx="12" cy="12" r="2" {}
|
||||
path d="M13.4 10.6 19 5" {}
|
||||
}
|
||||
"Dashboard"
|
||||
}
|
||||
}
|
||||
li {
|
||||
a href="/balance" {
|
||||
svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 0.5rem;" {
|
||||
path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z" {}
|
||||
}
|
||||
"Lightning"
|
||||
}
|
||||
}
|
||||
li {
|
||||
a href="/onchain" {
|
||||
svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 0.5rem;" {
|
||||
path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" {}
|
||||
path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" {}
|
||||
}
|
||||
"On-chain"
|
||||
}
|
||||
}
|
||||
li {
|
||||
a href="/payments" {
|
||||
svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" style="margin-right: 0.5rem;" {
|
||||
path d="M12 18H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5" {}
|
||||
path d="m16 19 3 3 3-3" {}
|
||||
path d="M18 12h.01" {}
|
||||
path d="M19 16v6" {}
|
||||
path d="M6 12h.01" {}
|
||||
circle cx="12" cy="12" r="2" {}
|
||||
}
|
||||
"All Payments"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1320,6 +1712,8 @@ pub fn layout(title: &str, content: Markup) -> Markup {
|
||||
main class="container" {
|
||||
(content)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn payment_list_item(
|
||||
let status_class = match status {
|
||||
"Succeeded" => "status-active",
|
||||
"Failed" => "status-inactive",
|
||||
"Pending" => "status-badge",
|
||||
"Pending" => "status-pending",
|
||||
_ => "status-badge",
|
||||
};
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 43 KiB |
Reference in New Issue
Block a user