Clone
QRCodeModal.vue
<template>
  <div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50" @click.self="$emit('close')">
    <div class="bg-white rounded-2xl shadow-xl max-w-md w-full p-6">
      <div class="flex justify-between items-start mb-4">
        <h3 class="text-xl font-semibold text-gray-900">QR Code</h3>
        <button
          @click="$emit('close')"
          class="text-gray-400 hover:text-gray-600 transition"
        >
          <XIcon class="h-6 w-6" />
        </button>
      </div>

      <div class="space-y-4">
        <div class="bg-gray-50 rounded-lg p-4">
          <p class="text-sm text-gray-600 mb-2">{{ entry.name }}</p>
          <p class="text-xs text-gray-500">{{ shortUrl }}</p>
        </div>

        <div class="flex justify-center">
          <div v-if="loading" class="py-8">
            <Loader2Icon class="h-8 w-8 text-gray-400 animate-spin" />
          </div>
          <img
            v-else
            :src="qrCodeUrl"
            alt="QR Code"
            class="w-64 h-64"
          />
        </div>

        <div class="flex space-x-3">
          <button
            @click="downloadQR"
            class="flex-1 bg-indigo-600 text-white py-2 px-4 rounded-lg font-semibold hover:bg-indigo-700 transition flex items-center justify-center"
          >
            <DownloadIcon class="h-4 w-4 mr-2" />
            Download
          </button>
          <button
            @click="copyQR"
            class="flex-1 bg-gray-100 text-gray-700 py-2 px-4 rounded-lg font-semibold hover:bg-gray-200 transition flex items-center justify-center"
          >
            <CopyIcon class="h-4 w-4 mr-2" />
            Copy
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted } from 'vue'
import { X as XIcon, Loader2 as Loader2Icon, Download as DownloadIcon, Copy as CopyIcon } from 'lucide-vue-next'
import { generateQRCodeWithLogo } from '../services/qrcode'

const props = defineProps({
  entry: {
    type: Object,
    required: true
  }
})

const emit = defineEmits(['close'])

const loading = ref(true)
const qrCodeUrl = ref('')

const shortUrl = computed(() => {
  return `${import.meta.env.VITE_SHORT_URL || 'http://localhost:8787'}/${props.entry.slug}`
})

onMounted(async () => {
  try {
    // Get full logo URL if logo exists
    const logoUrl = props.entry.logo_url ? getFullLogoUrl(props.entry.logo_url) : null
    qrCodeUrl.value = await generateQRCodeWithLogo(shortUrl.value, logoUrl)
  } catch (error) {
    console.error('Failed to generate QR code:', error)
    // Try without logo as fallback
    const { default: QRCode } = await import('qrcode')
    qrCodeUrl.value = await QRCode.toDataURL(shortUrl.value, {
      errorCorrectionLevel: 'H',
      width: 512,
      margin: 2
    })
  } finally {
    loading.value = false
  }
})

function getFullLogoUrl(logoUrl) {
  // If it's a relative URL starting with /api/logo, prepend the backend URL
  if (logoUrl?.startsWith('/api/logo')) {
    return `${import.meta.env.VITE_API_URL?.replace('/api', '') || 'http://localhost:8787'}${logoUrl}`
  }
  return logoUrl
}

function downloadQR() {
  const link = document.createElement('a')
  link.download = `qr-${props.entry.slug}.png`
  link.href = qrCodeUrl.value
  link.click()
}

async function copyQR() {
  try {
    const blob = await fetch(qrCodeUrl.value).then(r => r.blob())
    await navigator.clipboard.write([
      new ClipboardItem({ 'image/png': blob })
    ])
    alert('QR code copied to clipboard!')
  } catch (error) {
    alert('Failed to copy QR code')
  }
}
</script>