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