Enhance ReceiveDialog.vue with UI improvements and responsive design

- Updated styling for various components to improve visual hierarchy and responsiveness, including adjustments to padding, font sizes, and element spacing.
- Enhanced form field labels and descriptions for better clarity and accessibility.
- Improved error message display and button sizes for a more consistent user experience.
- Refined layout for invoice details and payment status messages to ensure better readability across different screen sizes.

These changes enhance the overall user experience by providing a more polished and responsive interface for managing Bitcoin payments.
This commit is contained in:
padreug 2025-09-18 22:06:46 +02:00
parent 7b762a1e4b
commit ef818baed6

View file

@ -181,65 +181,72 @@ function formatExpiry(seconds: number): string {
<template>
<Dialog :open="props.open" @update:open="onOpenChange">
<DialogContent class="sm:max-w-2xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle class="flex items-center gap-2">
<QrCode class="h-5 w-5" />
<DialogContent class="sm:max-w-2xl max-h-[95vh] overflow-y-auto p-4 sm:p-6">
<DialogHeader class="pb-2 sm:pb-4">
<DialogTitle class="flex items-center gap-2 text-lg sm:text-xl">
<QrCode class="h-4 w-4 sm:h-5 sm:w-5" />
Receive Bitcoin
</DialogTitle>
<DialogDescription>
<DialogDescription class="text-sm sm:text-base">
Create a Lightning invoice to receive Bitcoin payments.
</DialogDescription>
</DialogHeader>
<!-- Invoice Creation Form -->
<div v-if="!createdInvoice" class="space-y-4">
<form @submit="onSubmit" class="space-y-4">
<div v-if="!createdInvoice" class="space-y-3 sm:space-y-4">
<form @submit="onSubmit" class="space-y-3 sm:space-y-4">
<FormField v-slot="{ componentField }" name="amount">
<FormItem>
<FormLabel>Amount (sats) *</FormLabel>
<FormLabel class="text-sm font-medium">Amount (sats) *</FormLabel>
<FormControl>
<Input
type="number"
min="1"
max="10000000"
placeholder="Enter amount in sats"
class="h-9 sm:h-10"
v-bind="componentField"
/>
</FormControl>
<FormDescription>Amount to request in satoshis</FormDescription>
<FormDescription class="text-xs sm:text-sm">Amount to request in satoshis</FormDescription>
<FormMessage />
</FormItem>
</FormField>
<FormField v-slot="{ componentField }" name="memo">
<FormItem>
<FormLabel>Description</FormLabel>
<FormLabel class="text-sm font-medium">Description</FormLabel>
<FormControl>
<Textarea
placeholder="e.g., Payment for services (optional)"
class="min-h-[60px] sm:min-h-[80px] text-sm resize-none"
v-bind="componentField"
/>
</FormControl>
<FormDescription>What is this payment for? (optional)</FormDescription>
<FormDescription class="text-xs sm:text-sm">What is this payment for? (optional)</FormDescription>
<FormMessage />
</FormItem>
</FormField>
<div v-if="error" class="text-destructive text-sm">
<div v-if="error" class="text-destructive text-xs sm:text-sm p-2 sm:p-3 bg-destructive/10 rounded-md">
{{ error }}
</div>
<div class="flex gap-2 pt-4">
<div class="flex gap-2 pt-2 sm:pt-4">
<Button
type="submit"
:disabled="!isFormValid || isCreating"
class="flex-1"
class="flex-1 h-9 sm:h-10 text-sm sm:text-base"
>
<Loader2 v-if="isCreating" class="w-4 h-4 mr-2 animate-spin" />
{{ isCreating ? 'Creating Invoice...' : 'Create Invoice' }}
<Loader2 v-if="isCreating" class="w-3 h-3 sm:w-4 sm:h-4 mr-2 animate-spin" />
{{ isCreating ? 'Creating...' : 'Create Invoice' }}
</Button>
<Button type="button" variant="outline" @click="closeDialog">
<Button
type="button"
variant="outline"
@click="closeDialog"
class="h-9 sm:h-10 px-3 sm:px-4 text-sm sm:text-base"
>
Cancel
</Button>
</div>
@ -247,91 +254,92 @@ function formatExpiry(seconds: number): string {
</div>
<!-- Created Invoice Display -->
<div v-else class="space-y-6">
<div v-else class="space-y-4 sm:space-y-6">
<!-- Invoice Details -->
<div class="bg-muted/50 rounded-lg p-4 space-y-3">
<div class="bg-muted/50 rounded-lg p-3 sm:p-4 space-y-2 sm:space-y-3">
<div class="flex items-center justify-between">
<h4 class="font-medium">Invoice Details</h4>
<div class="text-sm text-muted-foreground">
<h4 class="font-medium text-sm sm:text-base">Invoice Details</h4>
<div class="text-xs sm:text-sm text-muted-foreground">
Expires in {{ formatExpiry(createdInvoice.expiry || 3600) }}
</div>
</div>
<div class="grid grid-cols-2 gap-4 text-sm">
<div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2 sm:gap-4 text-sm">
<div class="flex justify-between sm:block">
<span class="text-muted-foreground">Amount:</span>
<span class="font-medium ml-2">{{ createdInvoice.amount }} sats</span>
<span class="font-medium sm:ml-2">{{ createdInvoice.amount }} sats</span>
</div>
<div>
<div class="flex justify-between sm:block">
<span class="text-muted-foreground">Status:</span>
<span class="font-medium ml-2" :class="statusColor">{{ statusText }}</span>
<span class="font-medium sm:ml-2" :class="statusColor">{{ statusText }}</span>
</div>
</div>
<div>
<span class="text-muted-foreground text-sm">Description:</span>
<p class="font-medium">{{ createdInvoice.memo }}</p>
<span class="text-muted-foreground text-xs sm:text-sm">Description:</span>
<p class="font-medium text-sm break-words">{{ createdInvoice.memo }}</p>
</div>
</div>
<!-- QR Code Display -->
<div v-if="paymentStatus === 'pending'" class="flex flex-col items-center space-y-4">
<div v-if="isLoadingQR" class="w-64 h-64 flex items-center justify-center bg-muted rounded-lg">
<Loader2 class="h-8 w-8 animate-spin text-muted-foreground" />
<div v-if="paymentStatus === 'pending'" class="flex flex-col items-center space-y-3 sm:space-y-4">
<div v-if="isLoadingQR" class="w-48 h-48 sm:w-64 sm:h-64 flex items-center justify-center bg-muted rounded-lg">
<Loader2 class="h-6 w-6 sm:h-8 sm:w-8 animate-spin text-muted-foreground" />
</div>
<div v-else-if="qrCode" class="bg-white p-4 rounded-lg">
<div v-else-if="qrCode" class="bg-white p-2 sm:p-4 rounded-lg">
<img
:src="qrCode"
alt="Lightning Invoice QR Code"
class="w-64 h-64"
class="w-48 h-48 sm:w-64 sm:h-64"
/>
</div>
<div v-else class="w-64 h-64 flex items-center justify-center bg-muted rounded-lg">
<QrCode class="h-12 w-12 text-muted-foreground opacity-50" />
<div v-else class="w-48 h-48 sm:w-64 sm:h-64 flex items-center justify-center bg-muted rounded-lg">
<QrCode class="h-8 w-8 sm:h-12 sm:w-12 text-muted-foreground opacity-50" />
</div>
</div>
<!-- Payment Success Message -->
<div v-else-if="paymentStatus === 'confirmed'" class="flex flex-col items-center space-y-4">
<div class="w-64 h-64 flex flex-col items-center justify-center bg-green-50 dark:bg-green-950/20 rounded-lg border border-green-200 dark:border-green-800">
<Check class="h-16 w-16 text-green-600 dark:text-green-400 mb-4" />
<div class="text-center">
<p class="text-lg font-semibold text-green-800 dark:text-green-200">Payment Received!</p>
<div v-else-if="paymentStatus === 'confirmed'" class="flex flex-col items-center space-y-3 sm:space-y-4">
<div class="w-48 h-48 sm:w-64 sm:h-64 flex flex-col items-center justify-center bg-green-50 dark:bg-green-950/20 rounded-lg border border-green-200 dark:border-green-800">
<Check class="h-12 w-12 sm:h-16 sm:w-16 text-green-600 dark:text-green-400 mb-2 sm:mb-4" />
<div class="text-center px-2">
<p class="text-base sm:text-lg font-semibold text-green-800 dark:text-green-200">Payment Received!</p>
<p class="text-sm text-green-600 dark:text-green-400 mt-1">{{ createdInvoice.amount }} sats</p>
</div>
</div>
</div>
<!-- Payment Failed Message -->
<div v-else-if="paymentStatus === 'failed'" class="flex flex-col items-center space-y-4">
<div class="w-64 h-64 flex flex-col items-center justify-center bg-red-50 dark:bg-red-950/20 rounded-lg border border-red-200 dark:border-red-800">
<svg class="h-16 w-16 text-red-600 dark:text-red-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<div v-else-if="paymentStatus === 'failed'" class="flex flex-col items-center space-y-3 sm:space-y-4">
<div class="w-48 h-48 sm:w-64 sm:h-64 flex flex-col items-center justify-center bg-red-50 dark:bg-red-950/20 rounded-lg border border-red-200 dark:border-red-800">
<svg class="h-12 w-12 sm:h-16 sm:w-16 text-red-600 dark:text-red-400 mb-2 sm:mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
</svg>
<div class="text-center">
<p class="text-lg font-semibold text-red-800 dark:text-red-200">Payment Failed</p>
<p class="text-sm text-red-600 dark:text-red-400 mt-1">Invoice has expired or failed</p>
<div class="text-center px-2">
<p class="text-base sm:text-lg font-semibold text-red-800 dark:text-red-200">Payment Failed</p>
<p class="text-xs sm:text-sm text-red-600 dark:text-red-400 mt-1">Invoice has expired or failed</p>
</div>
</div>
</div>
<!-- Lightning Invoice (BOLT11) -->
<div class="space-y-2">
<Label>Lightning Invoice (BOLT11)</Label>
<div class="flex gap-2">
<div class="space-y-1 sm:space-y-2">
<Label class="text-sm font-medium">Lightning Invoice (BOLT11)</Label>
<div class="flex gap-1 sm:gap-2">
<Input
:key="`bolt11-${createdInvoice?.payment_hash}`"
:model-value="createdInvoice?.payment_request || createdInvoice?.bolt11 || ''"
readonly
class="font-mono text-xs"
class="font-mono text-xs h-8 sm:h-9"
/>
<Button
variant="outline"
size="sm"
@click="copyToClipboard(createdInvoice.payment_request || createdInvoice.bolt11, 'bolt11')"
class="h-8 w-8 sm:h-9 sm:w-auto sm:px-3 p-0 sm:p-2"
>
<Check v-if="copiedField === 'bolt11'" class="h-4 w-4 text-green-600" />
<Copy v-else class="h-4 w-4" />
<Check v-if="copiedField === 'bolt11'" class="h-3 w-3 sm:h-4 sm:w-4 text-green-600" />
<Copy v-else class="h-3 w-3 sm:h-4 sm:w-4" />
</Button>
</div>
<p class="text-xs text-muted-foreground">
@ -340,32 +348,40 @@ function formatExpiry(seconds: number): string {
</div>
<!-- Payment Hash -->
<div class="space-y-2">
<Label>Payment Hash</Label>
<div class="flex gap-2">
<div class="space-y-1 sm:space-y-2">
<Label class="text-sm font-medium">Payment Hash</Label>
<div class="flex gap-1 sm:gap-2">
<Input
:key="`hash-${createdInvoice?.payment_hash}`"
:model-value="createdInvoice?.payment_hash || ''"
readonly
class="font-mono text-xs"
class="font-mono text-xs h-8 sm:h-9"
/>
<Button
variant="outline"
size="sm"
@click="copyToClipboard(createdInvoice.payment_hash, 'hash')"
class="h-8 w-8 sm:h-9 sm:w-auto sm:px-3 p-0 sm:p-2"
>
<Check v-if="copiedField === 'hash'" class="h-4 w-4 text-green-600" />
<Copy v-else class="h-4 w-4" />
<Check v-if="copiedField === 'hash'" class="h-3 w-3 sm:h-4 sm:w-4 text-green-600" />
<Copy v-else class="h-3 w-3 sm:h-4 sm:w-4" />
</Button>
</div>
</div>
<!-- Action Buttons -->
<div class="flex gap-2 pt-4">
<Button @click="createAnother" variant="outline" class="flex-1">
<div class="flex gap-2 pt-3 sm:pt-4">
<Button
@click="createAnother"
variant="outline"
class="flex-1 h-9 sm:h-10 text-sm sm:text-base"
>
Create Another
</Button>
<Button @click="closeDialog" class="flex-1">
<Button
@click="closeDialog"
class="flex-1 h-9 sm:h-10 text-sm sm:text-base"
>
Done
</Button>
</div>