Compare commits

..

2 Commits

+25 -55
View File
@@ -6,7 +6,6 @@ import { getInvoice, getInvoiceBolt11 } from "@/lib/api"
import { tenantNeedsPaymentSetup } from "@/lib/hooks" import { tenantNeedsPaymentSetup } from "@/lib/hooks"
type PayStatus = "idle" | "loading" | "success" | "error" type PayStatus = "idle" | "loading" | "success" | "error"
type Bolt11Status = "idle" | "loading" | "ready" | "error"
type PaymentInvoice = { type PaymentInvoice = {
id: string id: string
@@ -22,34 +21,20 @@ type PaymentDialogProps = {
export default function PaymentDialog(props: PaymentDialogProps) { export default function PaymentDialog(props: PaymentDialogProps) {
const [bolt11, setBolt11] = createSignal("") const [bolt11, setBolt11] = createSignal("")
const [qrDataUrl, setQrDataUrl] = createSignal("") const [qrDataUrl, setQrDataUrl] = createSignal("")
const [bolt11Status, setBolt11Status] = createSignal<Bolt11Status>("idle")
const [bolt11Error, setBolt11Error] = createSignal("")
const [payStatus, setPayStatus] = createSignal<PayStatus>("idle") const [payStatus, setPayStatus] = createSignal<PayStatus>("idle")
const [payError, setPayError] = createSignal("") const [payError, setPayError] = createSignal("")
const [showSetup, setShowSetup] = createSignal(false) const [showSetup, setShowSetup] = createSignal(false)
const [showPaymentSetup, setShowPaymentSetup] = createSignal(false) const [showPaymentSetup, setShowPaymentSetup] = createSignal(false)
async function loadBolt11() { createEffect(async () => {
if (!props.invoice.id) return if (!props.open || !props.invoice.id) return
setBolt11Status("loading")
setBolt11Error("")
setBolt11("")
setQrDataUrl("")
try { try {
const { bolt11: invoice } = await getInvoiceBolt11(props.invoice.id) const { bolt11: invoice } = await getInvoiceBolt11(props.invoice.id)
setBolt11(invoice) setBolt11(invoice)
setQrDataUrl(await QRCode.toDataURL(invoice, { width: 256, margin: 2 })) setQrDataUrl(await QRCode.toDataURL(invoice, { width: 256, margin: 2 }))
setBolt11Status("ready") } catch {
} catch (e) { // bolt11 generation may fail
setBolt11Status("error")
setBolt11Error(e instanceof Error ? e.message : "Failed to generate Lightning invoice")
} }
}
createEffect(() => {
if (!props.open || !props.invoice.id) return
void loadBolt11()
}) })
function copyBolt11() { function copyBolt11() {
@@ -77,8 +62,6 @@ export default function PaymentDialog(props: PaymentDialogProps) {
function handleClose() { function handleClose() {
setPayStatus("idle") setPayStatus("idle")
setPayError("") setPayError("")
setBolt11Status("idle")
setBolt11Error("")
setBolt11("") setBolt11("")
setQrDataUrl("") setQrDataUrl("")
setShowSetup(false) setShowSetup(false)
@@ -121,46 +104,33 @@ export default function PaymentDialog(props: PaymentDialogProps) {
when={payStatus() === "success"} when={payStatus() === "success"}
fallback={ fallback={
<div class="w-full space-y-3"> <div class="w-full space-y-3">
<Show when={bolt11Status() === "idle" || bolt11Status() === "loading"}> <Show
<div class="flex items-center justify-center py-12 text-sm text-gray-400">Generating invoice...</div> when={qrDataUrl()}
fallback={<div class="flex items-center justify-center py-12 text-sm text-gray-400">Generating invoice...</div>}
>
<img src={qrDataUrl()} alt="Lightning invoice QR code" class="mx-auto rounded-lg" />
</Show> </Show>
<Show when={bolt11Status() === "error"}> <Show when={bolt11()}>
<div class="rounded-lg border border-red-200 bg-red-50 p-4"> <div class="flex rounded-lg border border-gray-300">
<p class="text-sm font-medium text-red-700">Unable to generate invoice</p> <input
<p class="mt-1 text-xs text-red-600 wrap-break-word">{bolt11Error()}</p> type="text"
readOnly
value={bolt11()}
class="min-w-0 flex-1 rounded-l-lg border-0 px-3 py-2 text-xs text-gray-500 bg-transparent focus:outline-none"
/>
<button <button
type="button" type="button"
onClick={() => void loadBolt11()} class="flex items-center px-3 text-gray-400 hover:text-gray-700"
class="mt-3 inline-flex items-center rounded-lg bg-red-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-red-700" onClick={copyBolt11}
title="Copy invoice"
> >
Retry <svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" />
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" />
</svg>
</button> </button>
</div> </div>
</Show> </Show>
<Show when={bolt11Status() === "ready"}>
<img src={qrDataUrl()} alt="Lightning invoice QR code" class="mx-auto rounded-lg" />
<Show when={bolt11()}>
<div class="flex rounded-lg border border-gray-300">
<input
type="text"
readOnly
value={bolt11()}
class="min-w-0 flex-1 rounded-l-lg border-0 px-3 py-2 text-xs text-gray-500 bg-transparent focus:outline-none"
/>
<button
type="button"
class="flex items-center px-3 text-gray-400 hover:text-gray-700"
onClick={copyBolt11}
title="Copy invoice"
>
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" />
<path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" />
</svg>
</button>
</div>
</Show>
</Show>
</div> </div>
} }
> >
@@ -218,7 +188,7 @@ export default function PaymentDialog(props: PaymentDialogProps) {
<button <button
type="button" type="button"
onClick={checkPayment} onClick={checkPayment}
disabled={payStatus() === "loading" || bolt11Status() !== "ready"} disabled={payStatus() === "loading"}
class="py-2 px-4 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg-blue-700 disabled:opacity-50 transition-colors" class="py-2 px-4 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg-blue-700 disabled:opacity-50 transition-colors"
> >
{payStatus() === "loading" ? "Checking..." : "Complete Payment"} {payStatus() === "loading" ? "Checking..." : "Complete Payment"}