From d039d5a98455ede912f2ddf611a9b23687203282 Mon Sep 17 00:00:00 2001
From: xmrcheckout
Date: Thu, 26 Mar 2026 14:14:26 +0000
Subject: [PATCH] Avoid false Monero outage states in UI
---
ui/app/(app)/dashboard/page.tsx | 70 ++++++++++++++-----
.../(marketing)/invoice/[invoiceId]/page.tsx | 69 +++++++++++++-----
ui/app/i/[invoiceId]/page.tsx | 69 +++++++++++++-----
3 files changed, 151 insertions(+), 57 deletions(-)
diff --git a/ui/app/(app)/dashboard/page.tsx b/ui/app/(app)/dashboard/page.tsx
index 256b473..bdea75c 100644
--- a/ui/app/(app)/dashboard/page.tsx
+++ b/ui/app/(app)/dashboard/page.tsx
@@ -85,6 +85,51 @@ type SystemStatusSummary = {
last_reconcile_completed_at: string | null;
last_reconcile_error: string | null;
};
+
+const walletRpcBadge = (systemStatus: SystemStatusSummary | null) => {
+ if (!systemStatus) {
+ return {
+ className: "bg-amber-100 text-amber-900",
+ label: "Wallet RPC unavailable",
+ };
+ }
+ if (systemStatus.wallet_rpc === "ok") {
+ return {
+ className: "bg-emerald-100 text-emerald-900",
+ label: "Wallet RPC ok",
+ };
+ }
+ return {
+ className: "bg-red-100 text-red-700",
+ label: "Wallet RPC down",
+ };
+};
+
+const daemonBadge = (systemStatus: SystemStatusSummary | null) => {
+ if (!systemStatus) {
+ return {
+ className: "bg-amber-100 text-amber-900",
+ label: "Daemon unavailable",
+ };
+ }
+ if (systemStatus.daemon === "ok") {
+ return {
+ className: "bg-emerald-100 text-emerald-900",
+ label: "Daemon connected",
+ };
+ }
+ if (systemStatus.daemon === "unknown") {
+ return {
+ className: "bg-amber-100 text-amber-900",
+ label: "Daemon not configured",
+ };
+ }
+ return {
+ className: "bg-red-100 text-red-700",
+ label: "Daemon down",
+ };
+};
+
export default async function DashboardPage({
searchParams,
}: {
@@ -259,6 +304,8 @@ export default async function DashboardPage({
const confirmedCount = allInvoices.filter(
(invoice) => invoice.status === "confirmed"
).length;
+ const walletStatusBadge = walletRpcBadge(systemStatus);
+ const daemonStatusBadge = daemonBadge(systemStatus);
const lastReconcileCompleted = systemStatus?.last_reconcile_completed_at
? formatRelativeTime(systemStatus.last_reconcile_completed_at)
: null;
@@ -377,29 +424,14 @@ export default async function DashboardPage({
- Wallet RPC {systemStatus?.wallet_rpc === "ok" ? "ok" : "down"}
+ {walletStatusBadge.label}
- Daemon{" "}
- {systemStatus?.daemon === "ok"
- ? "connected"
- : systemStatus?.daemon === "unknown"
- ? "not configured"
- : "down"}
+ {daemonStatusBadge.label}
diff --git a/ui/app/(marketing)/invoice/[invoiceId]/page.tsx b/ui/app/(marketing)/invoice/[invoiceId]/page.tsx
index 3bd7cdb..08d5cc1 100644
--- a/ui/app/(marketing)/invoice/[invoiceId]/page.tsx
+++ b/ui/app/(marketing)/invoice/[invoiceId]/page.tsx
@@ -47,6 +47,50 @@ type SystemStatusResponse = {
last_reconcile_error: string | null;
};
+const walletRpcBadge = (systemStatus: SystemStatusResponse | null) => {
+ if (!systemStatus) {
+ return {
+ className: "bg-amber-100 text-amber-900",
+ label: "Wallet RPC unavailable",
+ };
+ }
+ if (systemStatus.wallet_rpc === "ok") {
+ return {
+ className: "bg-emerald-100 text-emerald-900",
+ label: "Wallet RPC ok",
+ };
+ }
+ return {
+ className: "bg-red-100 text-red-700",
+ label: "Wallet RPC down",
+ };
+};
+
+const daemonBadge = (systemStatus: SystemStatusResponse | null) => {
+ if (!systemStatus) {
+ return {
+ className: "bg-amber-100 text-amber-900",
+ label: "Daemon unavailable",
+ };
+ }
+ if (systemStatus.daemon === "ok") {
+ return {
+ className: "bg-emerald-100 text-emerald-900",
+ label: "Daemon connected",
+ };
+ }
+ if (systemStatus.daemon === "unknown") {
+ return {
+ className: "bg-amber-100 text-amber-900",
+ label: "Daemon not configured",
+ };
+ }
+ return {
+ className: "bg-red-100 text-red-700",
+ label: "Daemon down",
+ };
+};
+
const formatStatus = (status: InvoiceStatus) => {
if (status === "payment_detected") {
return "Payment detected";
@@ -219,6 +263,8 @@ export default async function InvoiceStatusDetailPage({
const createdTimestamp = formatTimestamp(invoice.created_at);
const expiresTimestamp = formatTimestamp(invoice.expires_at);
const lastReconcileCompleted = formatTimestamp(systemStatus?.last_reconcile_completed_at ?? null);
+ const walletStatusBadge = walletRpcBadge(systemStatus);
+ const daemonStatusBadge = daemonBadge(systemStatus);
return (
@@ -284,29 +330,14 @@ export default async function InvoiceStatusDetailPage({
- Wallet RPC {systemStatus?.wallet_rpc === "ok" ? "ok" : "down"}
+ {walletStatusBadge.label}
- Daemon{" "}
- {systemStatus?.daemon === "ok"
- ? "connected"
- : systemStatus?.daemon === "unknown"
- ? "not configured"
- : "down"}
+ {daemonStatusBadge.label}
diff --git a/ui/app/i/[invoiceId]/page.tsx b/ui/app/i/[invoiceId]/page.tsx
index eedba79..00b7226 100644
--- a/ui/app/i/[invoiceId]/page.tsx
+++ b/ui/app/i/[invoiceId]/page.tsx
@@ -62,6 +62,50 @@ type SystemStatusResponse = {
last_reconcile_error: string | null;
};
+const walletRpcBadge = (systemStatus: SystemStatusResponse | null) => {
+ if (!systemStatus) {
+ return {
+ className: "bg-amber-100 text-amber-900",
+ label: "Wallet RPC unavailable",
+ };
+ }
+ if (systemStatus.wallet_rpc === "ok") {
+ return {
+ className: "bg-emerald-100 text-emerald-900",
+ label: "Wallet RPC ok",
+ };
+ }
+ return {
+ className: "bg-red-100 text-red-700",
+ label: "Wallet RPC down",
+ };
+};
+
+const daemonBadge = (systemStatus: SystemStatusResponse | null) => {
+ if (!systemStatus) {
+ return {
+ className: "bg-amber-100 text-amber-900",
+ label: "Daemon unavailable",
+ };
+ }
+ if (systemStatus.daemon === "ok") {
+ return {
+ className: "bg-emerald-100 text-emerald-900",
+ label: "Daemon connected",
+ };
+ }
+ if (systemStatus.daemon === "unknown") {
+ return {
+ className: "bg-amber-100 text-amber-900",
+ label: "Daemon not configured",
+ };
+ }
+ return {
+ className: "bg-red-100 text-red-700",
+ label: "Daemon down",
+ };
+};
+
const formatStatus = (status: InvoiceStatus) => {
if (status === "payment_detected") {
return "Payment detected";
@@ -161,6 +205,8 @@ export default async function BtcpayModalInvoicePage({
const createdTimestamp = formatTimestamp(invoice.created_at);
const expiresTimestamp = formatTimestamp(invoice.expires_at);
const lastReconcileCompleted = formatTimestamp(systemStatus?.last_reconcile_completed_at ?? null);
+ const walletStatusBadge = walletRpcBadge(systemStatus);
+ const daemonStatusBadge = daemonBadge(systemStatus);
const isBtcpayInvoice = Boolean(invoice.btcpay_amount && invoice.btcpay_currency);
const useClassicCheckout =
isBtcpayInvoice && invoice.btcpay_checkout_style === "btcpay_classic";
@@ -268,29 +314,14 @@ export default async function BtcpayModalInvoicePage({
- Wallet RPC {systemStatus?.wallet_rpc === "ok" ? "ok" : "down"}
+ {walletStatusBadge.label}
- Daemon{" "}
- {systemStatus?.daemon === "ok"
- ? "connected"
- : systemStatus?.daemon === "unknown"
- ? "not configured"
- : "down"}
+ {daemonStatusBadge.label}