cdk-ldk web ui updates (#1027)

This commit is contained in:
Erik
2025-09-03 16:46:49 +02:00
committed by GitHub
parent 734e62b04a
commit 39f256a648
7 changed files with 840 additions and 335 deletions

View File

@@ -15,7 +15,7 @@ async-trait.workspace = true
axum.workspace = true axum.workspace = true
cdk-common = { workspace = true, features = ["mint"] } cdk-common = { workspace = true, features = ["mint"] }
futures.workspace = true futures.workspace = true
tokio.workspace = true tokio.workspace = true
tokio-util.workspace = true tokio-util.workspace = true
tracing.workspace = true tracing.workspace = true
thiserror.workspace = true thiserror.workspace = true
@@ -29,6 +29,3 @@ tower-http.workspace = true
rust-embed = "8.5.0" rust-embed = "8.5.0"
serde_urlencoded = "0.7" serde_urlencoded = "0.7"
urlencoding = "2.1" urlencoding = "2.1"

View File

@@ -213,7 +213,7 @@ pub async fn post_open_channel(
} }
pub async fn close_channel_page( pub async fn close_channel_page(
State(_state): State<AppState>, State(state): State<AppState>,
query: Query<HashMap<String, String>>, query: Query<HashMap<String, String>>,
) -> Result<Html<String>, StatusCode> { ) -> Result<Html<String>, StatusCode> {
let channel_id = query.get("channel_id").unwrap_or(&"".to_string()).clone(); 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())); 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( let content = form_card(
"Close Channel", "Close Channel",
html! { html! {
p { "Are you sure you want to close this channel?" } p style="margin-bottom: 1.5rem;" { "Are you sure you want to close this channel?" }
div class="info-item" {
span class="info-label" { "User Channel ID:" } // Channel details in consistent format
span class="info-value" style="font-family: monospace; font-size: 0.85rem;" { (channel_id) } div class="channel-details" {
div class="detail-row" {
span class="detail-label" { "User Channel ID" }
span class="detail-value-amount" { (channel_id) }
}
div class="detail-row" {
span class="detail-label" { "Node ID" }
span class="detail-value-amount" { (node_id) }
}
@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)) }
}
}
} }
div class="info-item" {
span class="info-label" { "Node ID:" } form method="post" action="/channels/close" style="margin-top: 1rem; display: flex; justify-content: space-between; align-items: center;" {
span class="info-value" style="font-family: monospace; font-size: 0.85rem;" { (node_id) }
}
form method="post" action="/channels/close" style="margin-top: 1rem;" {
input type="hidden" name="channel_id" value=(channel_id) {} input type="hidden" name="channel_id" value=(channel_id) {}
input type="hidden" name="node_id" value=(node_id) {} input type="hidden" name="node_id" value=(node_id) {}
button type="submit" style="background: #dc3545;" { "Close Channel" } a href="/balance" { button type="button" class="button-secondary" { "Cancel" } }
" " button type="submit" class="button-destructive" { "Close Channel" }
a href="/balance" { button type="button" { "Cancel" } }
} }
}, },
); );
@@ -255,7 +271,7 @@ pub async fn close_channel_page(
} }
pub async fn force_close_channel_page( pub async fn force_close_channel_page(
State(_state): State<AppState>, State(state): State<AppState>,
query: Query<HashMap<String, String>>, query: Query<HashMap<String, String>>,
) -> Result<Html<String>, StatusCode> { ) -> Result<Html<String>, StatusCode> {
let channel_id = query.get("channel_id").unwrap_or(&"".to_string()).clone(); 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( let content = form_card(
"Force Close Channel", "Force Close Channel",
html! { html! {
div style="border: 2px solid #d63384; background-color: rgba(214, 51, 132, 0.1); padding: 1rem; margin-bottom: 1rem; border-radius: 0.5rem;" { 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: #d63384; margin: 0 0 0.5rem 0;" { "⚠️ Warning: Force Close" } h4 style="color: #f97316; margin: 0 0 0.5rem 0;" { "⚠️ Warning: Force Close" }
p style="color: #d63384; margin: 0; font-size: 0.9rem;" { p style="color: #f97316; margin: 0; font-size: 0.9rem;" {
"Force close should NOT be used if normal close is preferred. " "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. " "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." "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?" } p style="margin-bottom: 1.5rem;" { "Are you sure you want to force close this channel?" }
div class="info-item" {
span class="info-label" { "User Channel ID:" } // Channel details in consistent format
span class="info-value" style="font-family: monospace; font-size: 0.85rem;" { (channel_id) } div class="channel-details" {
div class="detail-row" {
span class="detail-label" { "User Channel ID" }
span class="detail-value-amount" { (channel_id) }
}
div class="detail-row" {
span class="detail-label" { "Node ID" }
span class="detail-value-amount" { (node_id) }
}
@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)) }
}
}
} }
div class="info-item" {
span class="info-label" { "Node ID:" } form method="post" action="/channels/force-close" style="margin-top: 1rem; display: flex; justify-content: space-between; align-items: center;" {
span class="info-value" style="font-family: monospace; font-size: 0.85rem;" { (node_id) }
}
form method="post" action="/channels/force-close" style="margin-top: 1rem;" {
input type="hidden" name="channel_id" value=(channel_id) {} input type="hidden" name="channel_id" value=(channel_id) {}
input type="hidden" name="node_id" value=(node_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" class="button-secondary" { "Cancel" } }
" " button type="submit" class="button-destructive" { "Force Close Channel" }
a href="/balance" { button type="button" { "Cancel" } }
} }
}, },
); );

View File

@@ -3,7 +3,7 @@ use axum::http::StatusCode;
use axum::response::Html; use axum::response::Html;
use maud::html; use maud::html;
use crate::web::handlers::AppState; use crate::web::handlers::utils::AppState;
use crate::web::templates::{format_sats_as_btc, layout}; use crate::web::templates::{format_sats_as_btc, layout};
pub async fn balance_page(State(state): State<AppState>) -> Result<Html<String>, StatusCode> { 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! { html! {
h2 style="text-align: center; margin-bottom: 3rem;" { "Lightning" } 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;" { div class="card" style="margin-bottom: 2rem;" {
h2 { "Quick Actions" } h2 { "Quick Actions" }
div style="display: flex; gap: 1rem; margin-top: 1rem; flex-wrap: wrap;" { div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;" {
a href="/channels/open" style="text-decoration: none; flex: 1; min-width: 200px;" { // Open Channel Card
button class="button-primary" style="width: 100%;" { "Open Channel" } 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" } // 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" }
}
} }
a href="/payments/send" style="text-decoration: none; flex: 1; min-width: 200px;" {
button class="button-primary" style="width: 100%;" { "Make Lightning Payment" } // 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! { html! {
h2 style="text-align: center; margin-bottom: 3rem;" { "Lightning" } 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;" { div class="card" style="margin-bottom: 2rem;" {
h2 { "Quick Actions" } h2 { "Quick Actions" }
div style="display: flex; gap: 1rem; margin-top: 1rem; flex-wrap: wrap;" { div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;" {
a href="/channels/open" style="text-decoration: none; flex: 1; min-width: 200px;" { // Open Channel Card
button class="button-primary" style="width: 100%;" { "Open Channel" } 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" } // 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" }
}
} }
a href="/payments/send" style="text-decoration: none; flex: 1; min-width: 200px;" {
button class="button-primary" style="width: 100%;" { "Make Lightning Payment" } // 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,57 +146,72 @@ pub async fn balance_page(State(state): State<AppState>) -> Result<Html<String>,
} }
} }
div class="card" { // Channel Details header (outside card)
h2 { "Channel Details" } h2 class="section-header" { "Channel Details" }
// Channels list // Channels list
@for channel in &channels { @for (index, channel) in channels.iter().enumerate() {
div class="channel-item" { @let node_id = channel.counterparty_node_id.to_string();
div class="channel-header" { @let channel_number = index + 1;
span class="channel-id" { "Channel ID: " (channel.channel_id.to_string()) }
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 { @if channel.is_usable {
span class="status-badge status-active" { "Active" } span class="status-badge status-active" { "Active" }
} @else { } @else {
span class="status-badge status-inactive" { "Inactive" } 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()) } // 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)) }
div class="balance-label" { "Outbound" }
} }
@if let Some(short_channel_id) = channel.short_channel_id { div class="balance-item" {
div class="info-item" { div class="balance-amount" { (format_sats_as_btc(channel.inbound_capacity_msat / 1000)) }
span class="info-label" { "Short Channel ID" } div class="balance-label" { "Inbound" }
span class="info-value" { (short_channel_id.to_string()) }
}
} }
div class="balance-info" { div class="balance-item" {
div class="balance-item" { div class="balance-amount" { (format_sats_as_btc(channel.channel_value_sats)) }
div class="balance-amount" { (format_sats_as_btc(channel.outbound_capacity_msat / 1000)) } div class="balance-label" { "Total" }
div class="balance-label" { "Outbound" }
}
div class="balance-item" {
div class="balance-amount" { (format_sats_as_btc(channel.inbound_capacity_msat / 1000)) }
div class="balance-label" { "Inbound" }
}
div class="balance-item" {
div class="balance-amount" { (format_sats_as_btc(channel.channel_value_sats)) }
div class="balance-label" { "Total" }
}
} }
@if channel.is_usable { }
div style="margin-top: 1rem; display: flex; gap: 0.5rem;" {
a href=(format!("/channels/close?channel_id={}&node_id={}", channel.user_channel_id.0, channel.counterparty_node_id)) { // Action buttons
button style="background: #dc3545;" { "Close Channel" } @if channel.is_usable {
} div class="channel-actions" {
a href=(format!("/channels/force-close?channel_id={}&node_id={}", channel.user_channel_id.0, channel.counterparty_node_id)) { a href=(format!("/channels/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-secondary" { "Close Channel" }
} }
a href=(format!("/channels/force-close?channel_id={}&node_id={}", channel.user_channel_id.0, channel.counterparty_node_id)) {
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" }
} }
} }
} }
} }
} }
} }
}; };

View File

@@ -79,15 +79,26 @@ pub async fn onchain_page(
let mut content = html! { let mut content = html! {
h2 style="text-align: center; margin-bottom: 3rem;" { "On-chain" } 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;" { div class="card" style="margin-bottom: 2rem;" {
h2 { "Quick Actions" } h2 { "Quick Actions" }
div style="display: flex; gap: 1rem; margin-top: 1rem; flex-wrap: wrap;" { div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;" {
a href="/onchain?action=receive" style="text-decoration: none; flex: 1; min-width: 200px;" { // Receive Bitcoin Card
button class="button-primary" style="width: 100%;" { "Receive Bitcoin" } 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" }
}
} }
a href="/onchain?action=send" style="text-decoration: none; flex: 1; min-width: 200px;" {
button class="button-primary" style="width: 100%;" { "Send 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" }
}
} }
} }
} }
@@ -113,15 +124,26 @@ pub async fn onchain_page(
content = html! { content = html! {
h2 style="text-align: center; margin-bottom: 3rem;" { "On-chain" } 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;" { div class="card" style="margin-bottom: 2rem;" {
h2 { "Quick Actions" } h2 { "Quick Actions" }
div style="display: flex; gap: 1rem; margin-top: 1rem; flex-wrap: wrap;" { div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;" {
a href="/onchain?action=receive" style="text-decoration: none; flex: 1; min-width: 200px;" { // Receive Bitcoin Card
button class="button-primary" style="width: 100%;" { "Receive Bitcoin" } 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" }
}
} }
a href="/onchain?action=send" style="text-decoration: none; flex: 1; min-width: 200px;" {
button class="button-primary" style="width: 100%;" { "Send 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" }
}
} }
} }
} }
@@ -141,7 +163,7 @@ pub async fn onchain_page(
} }
input type="hidden" id="send_action" name="send_action" value="send" {} input type="hidden" id="send_action" name="send_action" value="send" {}
div style="display: flex; justify-content: space-between; gap: 1rem; margin-top: 2rem;" { 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;" { 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'" { "Send Payment" }
button type="submit" onclick="document.getElementById('send_action').value='send_all'; document.getElementById('amount_sat').value=''" { "Send All" } 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! { content = html! {
h2 style="text-align: center; margin-bottom: 3rem;" { "On-chain" } 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;" { div class="card" style="margin-bottom: 2rem;" {
h2 { "Quick Actions" } h2 { "Quick Actions" }
div style="display: flex; gap: 1rem; margin-top: 1rem; flex-wrap: wrap;" { div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;" {
a href="/onchain?action=receive" style="text-decoration: none; flex: 1; min-width: 200px;" { // Receive Bitcoin Card
button class="button-primary" style="width: 100%;" { "Receive Bitcoin" } 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" }
}
} }
a href="/onchain?action=send" style="text-decoration: none; flex: 1; min-width: 200px;" {
button class="button-primary" style="width: 100%;" { "Send 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" }
}
} }
} }
} }
@@ -191,7 +224,7 @@ pub async fn onchain_page(
form method="post" action="/onchain/new-address" { 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." } 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;" { 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" } button class="button-primary" type="submit" { "Generate New Address" }
} }
} }
@@ -345,7 +378,7 @@ pub async fn onchain_confirm_page(
div class="card" { div class="card" {
div style="display: flex; justify-content: space-between; gap: 1rem; margin-top: 2rem;" { div style="display: flex; justify-content: space-between; gap: 1rem; margin-top: 2rem;" {
a href="/onchain?action=send" { 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;" { div style="display: flex; gap: 0.5rem;" {
a href=(confirmation_url) { a href=(confirmation_url) {

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,7 @@ pub fn payment_list_item(
let status_class = match status { let status_class = match status {
"Succeeded" => "status-active", "Succeeded" => "status-active",
"Failed" => "status-inactive", "Failed" => "status-inactive",
"Pending" => "status-badge", "Pending" => "status-pending",
_ => "status-badge", _ => "status-badge",
}; };

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 43 KiB