webapp/src/components/events/PurchaseTicketDialog.vue
padreug 0324cf8ec5 feat: Centralize configuration management for Nostr and API settings
- Introduce a new config module to manage Nostr relays, admin pubkeys, and API settings.
- Update components to utilize the centralized config instead of environment variables directly.
- Refactor relevant files to improve maintainability and reduce reliance on environment variables.
2025-07-02 19:47:55 +02:00

163 lines
No EOL
4.7 KiB
Vue

<!-- eslint-disable vue/multi-word-component-names -->
<script setup lang="ts">
import { ref } from 'vue'
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import QRCode from 'qrcode'
import { config } from '@/lib/config'
interface Props {
event: {
id: string
name: string
price_per_ticket: number
currency: string
}
isOpen: boolean
}
interface Emits {
(e: 'update:isOpen', value: boolean): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const name = ref('')
const email = ref('')
const isLoading = ref(false)
const paymentHash = ref('')
const paymentRequest = ref('')
const qrCode = ref('')
const error = ref('')
async function generateQRCode(bolt11: string) {
try {
qrCode.value = await QRCode.toDataURL(`lightning:${bolt11}`)
} catch (err) {
console.error('Failed to generate QR code:', err)
}
}
async function handleSubmit() {
if (!name.value || !email.value) {
error.value = 'Please fill out all fields'
return
}
isLoading.value = true
error.value = ''
const apiUrl = `${config.api.baseUrl}/events/api/v1/tickets/${props.event.id}/${encodeURIComponent(name.value)}/${encodeURIComponent(email.value)}`
console.log('Calling API:', apiUrl)
try {
const response = await fetch(apiUrl, {
headers: {
'Accept': 'application/json',
'X-API-KEY': config.api.key
}
})
console.log('Response status:', response.status)
const responseText = await response.text()
console.log('Response body:', responseText)
if (!response.ok) {
let errorMessage = 'Failed to generate payment request'
try {
const errorData = JSON.parse(responseText)
errorMessage = errorData.detail || errorMessage
} catch (e) {
console.error('Failed to parse error response:', e)
}
throw new Error(errorMessage)
}
let data
try {
data = JSON.parse(responseText)
} catch (e) {
console.error('Failed to parse response as JSON:', e)
throw new Error('Invalid response format from server')
}
if (!data.payment_hash || !data.payment_request) {
console.error('Invalid response data:', data)
throw new Error('Invalid payment data received')
}
paymentHash.value = data.payment_hash
paymentRequest.value = data.payment_request
await generateQRCode(data.payment_request)
} catch (err) {
console.error('Error generating ticket:', err)
error.value = err instanceof Error ? err.message : 'An error occurred while processing your request'
} finally {
isLoading.value = false
}
}
function handleOpenLightningWallet() {
if (paymentRequest.value) {
window.location.href = `lightning:${paymentRequest.value}`
}
}
function handleClose() {
emit('update:isOpen', false)
// Reset form state
name.value = ''
email.value = ''
paymentHash.value = ''
paymentRequest.value = ''
qrCode.value = ''
error.value = ''
}
</script>
<template>
<Dialog :open="isOpen" @update:open="handleClose">
<DialogContent class="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Purchase Ticket</DialogTitle>
<DialogDescription>
Purchase a ticket for {{ event.name }} for {{ event.price_per_ticket }} {{ event.currency }}
</DialogDescription>
</DialogHeader>
<div v-if="!paymentHash" class="grid gap-4 py-4">
<div class="grid gap-2">
<Label for="name">Name</Label>
<Input id="name" v-model="name" placeholder="Enter your name" :disabled="isLoading" />
</div>
<div class="grid gap-2">
<Label for="email">Email</Label>
<Input id="email" v-model="email" type="email" placeholder="Enter your email" :disabled="isLoading" />
</div>
<div v-if="error" class="text-sm text-destructive">
{{ error }}
</div>
</div>
<div v-else class="py-4 flex flex-col items-center gap-4">
<img :src="qrCode" alt="Lightning payment QR code" class="w-64 h-64" />
<div class="text-center space-y-2">
<p class="text-sm text-muted-foreground">Scan with your Lightning wallet to pay</p>
<Button variant="outline" @click="handleOpenLightningWallet">
Open in Lightning Wallet
</Button>
</div>
</div>
<div v-if="!paymentHash" class="flex justify-end">
<Button type="submit" :disabled="isLoading" @click="handleSubmit">
<span v-if="isLoading" class="animate-spin mr-2"></span>
Continue to Payment
</Button>
</div>
</DialogContent>
</Dialog>
</template>