'use client'; import React from 'react'; import { QRCodeSVG } from 'qrcode.react'; import Barcode from 'react-barcode'; import { Card, CardContent } from '@/components/ui/Card'; import { Badge } from '@/components/ui/Badge'; import { Dropdown, DropdownItem } from '@/components/ui/Dropdown'; import { formatDate } from '@/lib/utils'; interface QRCodeCardProps { qr: { id: string; title: string; type: 'STATIC' | 'DYNAMIC'; contentType: string; content?: any; slug: string; createdAt: string; scans?: number; style?: any; }; onEdit: (id: string) => void; onDelete: (id: string) => void; } export const QRCodeCard: React.FC = ({ qr, onEdit, onDelete, }) => { // For dynamic QR codes, use the redirect URL for tracking // For static QR codes, use the direct URL from content const baseUrl = process.env.NEXT_PUBLIC_APP_URL || (typeof window !== 'undefined' ? window.location.origin : 'http://localhost:3050'); // Get the QR URL based on type let qrUrl = ''; // SIMPLE FIX: For STATIC QR codes, ALWAYS use the direct content if (qr.type === 'STATIC') { // Extract the actual URL/content based on contentType if (qr.contentType === 'URL' && qr.content?.url) { qrUrl = qr.content.url; } else if (qr.contentType === 'PHONE' && qr.content?.phone) { qrUrl = `tel:${qr.content.phone}`; } else if (qr.contentType === 'VCARD') { // VCARD content needs to be formatted properly qrUrl = `BEGIN:VCARD VERSION:3.0 FN:${qr.content.firstName || ''} ${qr.content.lastName || ''} N:${qr.content.lastName || ''};${qr.content.firstName || ''};;; ${qr.content.organization ? `ORG:${qr.content.organization}` : ''} ${qr.content.title ? `TITLE:${qr.content.title}` : ''} ${qr.content.email ? `EMAIL:${qr.content.email}` : ''} ${qr.content.phone ? `TEL:${qr.content.phone}` : ''} END:VCARD`; } else if (qr.contentType === 'GEO' && qr.content) { const lat = qr.content.latitude || 0; const lon = qr.content.longitude || 0; const label = qr.content.label ? `?q=${encodeURIComponent(qr.content.label)}` : ''; qrUrl = `geo:${lat},${lon}${label}`; } else if (qr.contentType === 'TEXT' && qr.content?.text) { qrUrl = qr.content.text; } else if (qr.content?.qrContent) { // Fallback to qrContent if it exists qrUrl = qr.content.qrContent; } else { // Last resort fallback qrUrl = `${baseUrl}/r/${qr.slug}`; } console.log(`STATIC QR [${qr.title}]: ${qrUrl}`); } else { // DYNAMIC QR codes always use redirect for tracking qrUrl = `${baseUrl}/r/${qr.slug}`; console.log(`DYNAMIC QR [${qr.title}]: ${qrUrl}`); } const downloadQR = async (format: 'png' | 'svg') => { // Use the tight download wrapper const container = document.querySelector(`#qr-download-${qr.id}`); if (!container) return; // Dynamic import of html-to-image const { toPng } = await import('html-to-image'); try { if (format === 'png') { const dataUrl = await toPng(container as HTMLElement, { cacheBust: true, pixelRatio: 3, backgroundColor: '#ffffff' // White background for clean export }); const link = document.createElement('a'); link.download = `${qr.title.replace(/\s+/g, '-').toLowerCase()}.png`; link.href = dataUrl; link.click(); } else { // For SVG, if no frame, export just the QR code SVG for vector quality // If frame exists, use toPng as fallback since HTML-to-SVG is complex if (qr.style?.frameType && qr.style.frameType !== 'none') { // Frame exists - use PNG for full capture const dataUrl = await toPng(container as HTMLElement, { cacheBust: true, pixelRatio: 3, backgroundColor: '#ffffff' }); const link = document.createElement('a'); link.download = `${qr.title.replace(/\s+/g, '-').toLowerCase()}.png`; link.href = dataUrl; link.click(); } else { // No frame - export clean SVG from the svg wrapper const svgContainer = document.querySelector(`#qr-svg-${qr.id}`); const svg = svgContainer?.querySelector('svg'); if (svg) { let svgData = new XMLSerializer().serializeToString(svg); if (qr.style?.cornerStyle === 'rounded') { const width = svg.getAttribute('width') || '96'; const height = svg.getAttribute('height') || '96'; const borderRadius = 10; svgData = ` ${svgData} `; } const blob = new Blob([svgData], { type: 'image/svg+xml' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${qr.title.replace(/\s+/g, '-').toLowerCase()}.svg`; a.click(); URL.revokeObjectURL(url); } } } } catch (err) { console.error('Download failed:', err); } }; return (

{qr.title}

{qr.type}
} > window.location.href = `/qr/${qr.id}`}>View Details downloadQR('png')}>Download PNG downloadQR('svg')}>Download SVG {qr.type === 'DYNAMIC' && ( onEdit(qr.id)}>Edit )} onDelete(qr.id)} className="text-red-600"> Delete
{/* Download wrapper - tightly wraps content */}
{/* Frame Label */} {qr.style?.frameType && qr.style.frameType !== 'none' && (
{qr.style.frameType === 'scanme' ? 'Scan Me' : qr.style.frameType === 'website' ? 'Website' : qr.style.frameType === 'visit' ? 'Visit' : qr.style.frameType === 'callme' ? 'Call Me' : qr.style.frameType === 'call' ? 'Call' : qr.style.frameType === 'findus' ? 'Find Us' : qr.style.frameType === 'navigate' ? 'Navigate' : qr.style.frameType === 'contact' ? 'Contact' : qr.style.frameType === 'save' ? 'Save' : qr.style.frameType === 'textme' ? 'Text Me' : qr.style.frameType === 'message' ? 'Message' : qr.style.frameType === 'chatme' ? 'Chat Me' : qr.style.frameType === 'whatsapp' ? 'WhatsApp' : qr.style.frameType === 'read' ? 'Read' : qr.style.frameType === 'info' ? 'Info' : qr.style.frameType.charAt(0).toUpperCase() + qr.style.frameType.slice(1)}
)}
{qr.contentType === 'BARCODE' && qr.type === 'STATIC' ? ( ) : ( )}
Type: {qr.contentType}
{qr.type === 'DYNAMIC' && (
Scans: {qr.scans || 0}
)}
Created: {formatDate(qr.createdAt)}
{qr.type === 'DYNAMIC' && (

📊 Dynamic QR: Tracks scans via {baseUrl}/r/{qr.slug}

)} {/* Feedback Button - only for FEEDBACK type */} {qr.contentType === 'FEEDBACK' && ( )}
); };