698 lines
27 KiB
PHP
698 lines
27 KiB
PHP
import React, { useState, useEffect, useRef } from 'react';
|
|
import { QrCode, Link, MessageSquare, User, Wifi, Download, Copy, Check } from 'lucide-react';
|
|
|
|
const TRANSLATIONS = {
|
|
"de-DE": {
|
|
"appTitle": "QR-Code Generator",
|
|
"appDescription": "Erstelle QR-Codes für URLs, Text und Kontaktinformationen",
|
|
"urlTab": "URL",
|
|
"textTab": "Text",
|
|
"contactTab": "Kontakt",
|
|
"wifiTab": "WLAN",
|
|
"enterUrl": "URL eingeben",
|
|
"enterText": "Text eingeben",
|
|
"contactInformation": "Kontaktinformationen",
|
|
"wifiConfiguration": "WLAN-Konfiguration",
|
|
"websiteUrl": "Website-URL",
|
|
"urlPlaceholder": "beispiel.de oder https://beispiel.de",
|
|
"urlHelp": "Geben Sie eine Website-URL ein. Falls Sie http:// weglassen, fügen wir automatisch https:// hinzu.",
|
|
"textContent": "Textinhalt",
|
|
"textPlaceholder": "Beliebigen Text eingeben um QR-Code zu erstellen...",
|
|
"firstName": "Vorname",
|
|
"firstNamePlaceholder": "Max",
|
|
"lastName": "Nachname",
|
|
"lastNamePlaceholder": "Mustermann",
|
|
"phoneNumber": "Telefonnummer",
|
|
"phonePlaceholder": "+49 (0) 123 456789",
|
|
"emailAddress": "E-Mail-Adresse",
|
|
"emailPlaceholder": "max.mustermann@beispiel.de",
|
|
"organization": "Organisation",
|
|
"organizationPlaceholder": "Firmenname",
|
|
"website": "Website",
|
|
"websitePlaceholder": "https://beispiel.de",
|
|
"clearAllFields": "Alle Felder löschen",
|
|
"networkName": "Netzwerkname (SSID)",
|
|
"networkNamePlaceholder": "Mein WLAN-Netzwerk",
|
|
"password": "Passwort",
|
|
"passwordPlaceholder": "WLAN-Passwort",
|
|
"securityType": "Sicherheitstyp",
|
|
"hidden": "Verstecktes Netzwerk",
|
|
"hiddenHelp": "Ankreuzen, falls dies ein verstecktes Netzwerk ist",
|
|
"generatedQrCode": "Generierter QR-Code",
|
|
"scanQrCode": "Scannen Sie diesen QR-Code mit Ihrem Gerät",
|
|
"fillFormPrompt": "Füllen Sie das Formular aus, um Ihren QR-Code zu erstellen",
|
|
"download": "Herunterladen",
|
|
"copyData": "Daten kopieren",
|
|
"copied": "Kopiert!",
|
|
"qrCodeData": "QR-Code Daten:",
|
|
"footerText": "QR-Codes sofort erstellen • Keine Datenspeicherung • Kostenlos",
|
|
"qrCodeAlt": "Generierter QR-Code"
|
|
},
|
|
"en-US": {
|
|
"appTitle": "QR Code Generator",
|
|
"appDescription": "Generate QR codes for URLs, text, and contact information",
|
|
"urlTab": "URL",
|
|
"textTab": "Text",
|
|
"contactTab": "Contact",
|
|
"wifiTab": "WiFi",
|
|
"enterUrl": "Enter URL",
|
|
"enterText": "Enter Text",
|
|
"contactInformation": "Contact Information",
|
|
"wifiConfiguration": "WiFi Configuration",
|
|
"websiteUrl": "Website URL",
|
|
"urlPlaceholder": "example.com or https://example.com",
|
|
"urlHelp": "Enter a website URL. If you don't include http://, we'll add https:// automatically.",
|
|
"textContent": "Text Content",
|
|
"textPlaceholder": "Enter any text to generate QR code...",
|
|
"firstName": "First Name",
|
|
"firstNamePlaceholder": "John",
|
|
"lastName": "Last Name",
|
|
"lastNamePlaceholder": "Doe",
|
|
"phoneNumber": "Phone Number",
|
|
"phonePlaceholder": "+1 (555) 123-4567",
|
|
"emailAddress": "Email Address",
|
|
"emailPlaceholder": "john.doe@example.com",
|
|
"organization": "Organization",
|
|
"organizationPlaceholder": "Company Name",
|
|
"website": "Website",
|
|
"websitePlaceholder": "https://example.com",
|
|
"clearAllFields": "Clear All Fields",
|
|
"networkName": "Network Name (SSID)",
|
|
"networkNamePlaceholder": "My WiFi Network",
|
|
"password": "Password",
|
|
"passwordPlaceholder": "WiFi password",
|
|
"securityType": "Security Type",
|
|
"hidden": "Hidden Network",
|
|
"hiddenHelp": "Check if this is a hidden network",
|
|
"generatedQrCode": "Generated QR Code",
|
|
"scanQrCode": "Scan this QR code with your device",
|
|
"fillFormPrompt": "Fill in the form to generate your QR code",
|
|
"download": "Download",
|
|
"copyData": "Copy Data",
|
|
"copied": "Copied!",
|
|
"qrCodeData": "QR Code Data:",
|
|
"footerText": "Generate QR codes instantly • No data stored • Free to use",
|
|
"qrCodeAlt": "Generated QR Code"
|
|
},
|
|
"es-ES": {
|
|
"appTitle": "Generador de Códigos QR",
|
|
"appDescription": "Genera códigos QR para URLs, texto e información de contacto",
|
|
"urlTab": "URL",
|
|
"textTab": "Texto",
|
|
"contactTab": "Contacto",
|
|
"wifiTab": "WiFi",
|
|
"enterUrl": "Ingresa URL",
|
|
"enterText": "Ingresa Texto",
|
|
"contactInformation": "Información de Contacto",
|
|
"wifiConfiguration": "Configuración WiFi",
|
|
"websiteUrl": "URL del Sitio Web",
|
|
"urlPlaceholder": "ejemplo.com o https://ejemplo.com",
|
|
"urlHelp": "Ingresa una URL de sitio web. Si no incluyes http://, agregaremos https:// automáticamente.",
|
|
"textContent": "Contenido de Texto",
|
|
"textPlaceholder": "Ingresa cualquier texto para generar código QR...",
|
|
"firstName": "Nombre",
|
|
"firstNamePlaceholder": "Juan",
|
|
"lastName": "Apellido",
|
|
"lastNamePlaceholder": "Pérez",
|
|
"phoneNumber": "Número de Teléfono",
|
|
"phonePlaceholder": "+1 (555) 123-4567",
|
|
"emailAddress": "Dirección de Correo",
|
|
"emailPlaceholder": "juan.perez@ejemplo.com",
|
|
"organization": "Organización",
|
|
"organizationPlaceholder": "Nombre de la Empresa",
|
|
"website": "Sitio Web",
|
|
"websitePlaceholder": "https://ejemplo.com",
|
|
"clearAllFields": "Limpiar Todos los Campos",
|
|
"networkName": "Nombre de Red (SSID)",
|
|
"networkNamePlaceholder": "Mi Red WiFi",
|
|
"password": "Contraseña",
|
|
"passwordPlaceholder": "Contraseña WiFi",
|
|
"securityType": "Tipo de Seguridad",
|
|
"hidden": "Red Oculta",
|
|
"hiddenHelp": "Marcar si esta es una red oculta",
|
|
"generatedQrCode": "Código QR Generado",
|
|
"scanQrCode": "Escanea este código QR con tu dispositivo",
|
|
"fillFormPrompt": "Completa el formulario para generar tu código QR",
|
|
"download": "Descargar",
|
|
"copyData": "Copiar Datos",
|
|
"copied": "¡Copiado!",
|
|
"qrCodeData": "Datos del Código QR:",
|
|
"footerText": "Genera códigos QR al instante • No se almacenan datos • Gratis",
|
|
"qrCodeAlt": "Código QR Generado"
|
|
}
|
|
};
|
|
|
|
const appLocale = '{{APP_LOCALE}}';
|
|
const browserLocale = navigator.languages?.[0] || navigator.language || 'en-US';
|
|
const findMatchingLocale = (locale) => {
|
|
if (TRANSLATIONS[locale]) return locale;
|
|
const lang = locale.split('-')[0];
|
|
const match = Object.keys(TRANSLATIONS).find(key => key.startsWith(lang + '-'));
|
|
return match || 'en-US';
|
|
};
|
|
const locale = (appLocale !== '{{APP_LOCALE}}') ? findMatchingLocale(appLocale) : findMatchingLocale(browserLocale);
|
|
// Standardmäßig auf Deutsch setzen
|
|
const defaultLocale = 'de-DE';
|
|
const t = (key) => TRANSLATIONS[locale]?.[key] || TRANSLATIONS[defaultLocale]?.[key] || TRANSLATIONS['en-US']?.[key] || key;
|
|
|
|
const QRCodeGenerator = () => {
|
|
const [activeTab, setActiveTab] = useState('url');
|
|
const [qrData, setQrData] = useState('');
|
|
const [copied, setCopied] = useState(false);
|
|
const qrContainerRef = useRef(null);
|
|
|
|
// Form states for different types
|
|
const [urlInput, setUrlInput] = useState('');
|
|
const [textInput, setTextInput] = useState('');
|
|
const [contactInfo, setContactInfo] = useState({
|
|
firstName: '',
|
|
lastName: '',
|
|
phone: '',
|
|
email: '',
|
|
organization: '',
|
|
url: ''
|
|
});
|
|
const [wifiInfo, setWifiInfo] = useState({
|
|
ssid: '',
|
|
password: '',
|
|
security: 'WPA',
|
|
hidden: false
|
|
});
|
|
|
|
// QR Code generation using QRious library via CDN
|
|
const generateQRCode = async (text) => {
|
|
if (!text.trim()) {
|
|
if (qrContainerRef.current) {
|
|
qrContainerRef.current.innerHTML = '';
|
|
}
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Load QRious library dynamically
|
|
if (!window.QRious) {
|
|
const script = document.createElement('script');
|
|
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/qrious/4.0.2/qrious.min.js';
|
|
script.onload = () => {
|
|
createQR(text);
|
|
};
|
|
document.head.appendChild(script);
|
|
} else {
|
|
createQR(text);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error loading QR library:', error);
|
|
// Fallback to Google Charts API
|
|
generateFallbackQR(text);
|
|
}
|
|
};
|
|
|
|
const createQR = (text) => {
|
|
if (!qrContainerRef.current) return;
|
|
|
|
try {
|
|
// Clear previous QR code
|
|
qrContainerRef.current.innerHTML = '';
|
|
|
|
// Create canvas element
|
|
const canvas = document.createElement('canvas');
|
|
qrContainerRef.current.appendChild(canvas);
|
|
|
|
// Generate QR code
|
|
const qr = new window.QRious({
|
|
element: canvas,
|
|
value: text,
|
|
size: 300,
|
|
background: 'white',
|
|
foreground: 'black',
|
|
level: 'M'
|
|
});
|
|
|
|
// Style the canvas
|
|
canvas.className = 'w-full h-auto rounded-xl shadow-lg bg-white';
|
|
canvas.style.maxWidth = '300px';
|
|
canvas.style.height = 'auto';
|
|
|
|
} catch (error) {
|
|
console.error('Error creating QR code:', error);
|
|
generateFallbackQR(text);
|
|
}
|
|
};
|
|
|
|
const generateFallbackQR = (text) => {
|
|
if (!qrContainerRef.current) return;
|
|
|
|
// Clear previous content
|
|
qrContainerRef.current.innerHTML = '';
|
|
|
|
// Create img element for fallback
|
|
const img = document.createElement('img');
|
|
const encodedData = encodeURIComponent(text);
|
|
img.src = `https://chart.googleapis.com/chart?chs=300x300&cht=qr&chl=${encodedData}&choe=UTF-8`;
|
|
img.alt = t('qrCodeAlt');
|
|
img.className = 'w-full h-auto rounded-xl shadow-lg bg-white p-4';
|
|
img.style.maxWidth = '300px';
|
|
img.style.height = 'auto';
|
|
|
|
// Add error handling for the fallback image
|
|
img.onerror = () => {
|
|
// If Google Charts also fails, try QR Server API
|
|
img.src = `https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${encodedData}&format=png&margin=10`;
|
|
};
|
|
|
|
qrContainerRef.current.appendChild(img);
|
|
};
|
|
|
|
const formatUrl = (url) => {
|
|
if (!url.trim()) return '';
|
|
|
|
// Add protocol if missing
|
|
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
|
return 'https://' + url;
|
|
}
|
|
return url;
|
|
};
|
|
|
|
const generateVCard = (contact) => {
|
|
const vcard = `BEGIN:VCARD
|
|
VERSION:3.0
|
|
FN:${contact.firstName} ${contact.lastName}
|
|
N:${contact.lastName};${contact.firstName};;;
|
|
ORG:${contact.organization}
|
|
TEL:${contact.phone}
|
|
EMAIL:${contact.email}
|
|
URL:${contact.url}
|
|
END:VCARD`;
|
|
return vcard;
|
|
};
|
|
|
|
const generateWiFiQR = (wifi) => {
|
|
// WiFi QR code format: WIFI:T:WPA;S:mynetwork;P:mypass;H:false;;
|
|
const security = wifi.security || 'WPA';
|
|
const hidden = wifi.hidden ? 'true' : 'false';
|
|
return `WIFI:T:${security};S:${wifi.ssid};P:${wifi.password};H:${hidden};;`;
|
|
};
|
|
|
|
useEffect(() => {
|
|
let data = '';
|
|
|
|
switch (activeTab) {
|
|
case 'url':
|
|
data = formatUrl(urlInput);
|
|
break;
|
|
case 'text':
|
|
data = textInput;
|
|
break;
|
|
case 'contact':
|
|
if (contactInfo.firstName || contactInfo.lastName || contactInfo.phone || contactInfo.email) {
|
|
data = generateVCard(contactInfo);
|
|
}
|
|
break;
|
|
case 'wifi':
|
|
if (wifiInfo.ssid) {
|
|
data = generateWiFiQR(wifiInfo);
|
|
}
|
|
break;
|
|
default:
|
|
data = '';
|
|
}
|
|
|
|
setQrData(data);
|
|
generateQRCode(data);
|
|
}, [activeTab, urlInput, textInput, contactInfo, wifiInfo]);
|
|
|
|
const downloadQRCode = () => {
|
|
if (!qrData) return;
|
|
|
|
const canvas = qrContainerRef.current?.querySelector('canvas');
|
|
const img = qrContainerRef.current?.querySelector('img');
|
|
|
|
if (canvas) {
|
|
// Download from canvas
|
|
const link = document.createElement('a');
|
|
link.download = `qr-code-${activeTab}.png`;
|
|
link.href = canvas.toDataURL();
|
|
link.click();
|
|
} else if (img) {
|
|
// Download from image
|
|
const link = document.createElement('a');
|
|
link.download = `qr-code-${activeTab}.png`;
|
|
link.href = img.src;
|
|
link.click();
|
|
}
|
|
};
|
|
|
|
const copyToClipboard = async () => {
|
|
if (qrData) {
|
|
try {
|
|
await navigator.clipboard.writeText(qrData);
|
|
setCopied(true);
|
|
setTimeout(() => setCopied(false), 2000);
|
|
} catch (err) {
|
|
console.error('Failed to copy text: ', err);
|
|
}
|
|
}
|
|
};
|
|
|
|
const resetForm = () => {
|
|
setUrlInput('');
|
|
setTextInput('');
|
|
setContactInfo({
|
|
firstName: '',
|
|
lastName: '',
|
|
phone: '',
|
|
email: '',
|
|
organization: '',
|
|
url: ''
|
|
});
|
|
setWifiInfo({
|
|
ssid: '',
|
|
password: '',
|
|
security: 'WPA',
|
|
hidden: false
|
|
});
|
|
setQrData('');
|
|
if (qrContainerRef.current) {
|
|
qrContainerRef.current.innerHTML = '';
|
|
}
|
|
};
|
|
|
|
const tabs = [
|
|
{ id: 'url', label: t('urlTab'), icon: Link },
|
|
{ id: 'text', label: t('textTab'), icon: MessageSquare },
|
|
{ id: 'contact', label: t('contactTab'), icon: User },
|
|
{ id: 'wifi', label: t('wifiTab'), icon: Wifi }
|
|
];
|
|
|
|
return (
|
|
<div className="min-h-screen bg-white p-4">
|
|
<div className="max-w-4xl mx-auto">
|
|
<div className="text-center mb-8">
|
|
<div className="inline-flex items-center justify-center w-16 h-16 bg-red-800 rounded-lg mb-4">
|
|
<QrCode className="w-8 h-8 text-white" />
|
|
</div>
|
|
<h1 className="text-4xl font-bold text-red-800 mb-2">
|
|
{t('appTitle')}
|
|
</h1>
|
|
<p className="text-gray-600 text-lg">{t('appDescription')}</p>
|
|
</div>
|
|
|
|
<div className="bg-white rounded-lg shadow-lg border border-red-800 overflow-hidden">
|
|
{/* Tab Navigation */}
|
|
<div className="border-b border-gray-200">
|
|
<nav className="flex">
|
|
{tabs.map((tab) => {
|
|
const IconComponent = tab.icon;
|
|
return (
|
|
<button
|
|
key={tab.id}
|
|
onClick={() => setActiveTab(tab.id)}
|
|
className={`flex-1 flex items-center justify-center gap-2 px-6 py-4 text-sm font-medium transition-all duration-200 ${
|
|
activeTab === tab.id
|
|
? 'text-red-800 border-b-2 border-red-800 bg-red-50'
|
|
: 'text-gray-500 hover:text-gray-700 hover:bg-gray-50'
|
|
}`}
|
|
>
|
|
<IconComponent className="w-4 h-4" />
|
|
{tab.label}
|
|
</button>
|
|
);
|
|
})}
|
|
</nav>
|
|
</div>
|
|
|
|
<div className="p-8">
|
|
<div className="grid lg:grid-cols-2 gap-8">
|
|
{/* Input Section */}
|
|
<div className="space-y-6">
|
|
<h2 className="text-2xl font-semibold text-gray-800 mb-4">
|
|
{activeTab === 'url' && t('enterUrl')}
|
|
{activeTab === 'text' && t('enterText')}
|
|
{activeTab === 'contact' && t('contactInformation')}
|
|
{activeTab === 'wifi' && t('wifiConfiguration')}
|
|
</h2>
|
|
|
|
{/* URL Input */}
|
|
{activeTab === 'url' && (
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('websiteUrl')}
|
|
</label>
|
|
<input
|
|
type="url"
|
|
value={urlInput}
|
|
onChange={(e) => setUrlInput(e.target.value)}
|
|
placeholder={t('urlPlaceholder')}
|
|
className="w-full px-4 py-3 border border-red-800 rounded-lg focus:ring-2 focus:ring-red-800 focus:border-transparent transition-all duration-200"
|
|
/>
|
|
<p className="text-xs text-gray-500 mt-1">
|
|
{t('urlHelp')}
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
{/* WiFi Input */}
|
|
{activeTab === 'wifi' && (
|
|
<div className="space-y-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('networkName')}
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={wifiInfo.ssid}
|
|
onChange={(e) => setWifiInfo({...wifiInfo, ssid: e.target.value})}
|
|
placeholder={t('networkNamePlaceholder')}
|
|
className="w-full px-4 py-3 border border-red-800 rounded-lg focus:ring-2 focus:ring-red-800 focus:border-transparent transition-all duration-200"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('password')}
|
|
</label>
|
|
<input
|
|
type="password"
|
|
value={wifiInfo.password}
|
|
onChange={(e) => setWifiInfo({...wifiInfo, password: e.target.value})}
|
|
placeholder={t('passwordPlaceholder')}
|
|
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-purple-500 focus:border-transparent transition-all duration-200"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('securityType')}
|
|
</label>
|
|
<select
|
|
value={wifiInfo.security}
|
|
onChange={(e) => setWifiInfo({...wifiInfo, security: e.target.value})}
|
|
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-purple-500 focus:border-transparent transition-all duration-200"
|
|
>
|
|
<option value="WPA">WPA/WPA2</option>
|
|
<option value="WEP">WEP</option>
|
|
<option value="nopass">Offen (Kein Passwort)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-3">
|
|
<input
|
|
type="checkbox"
|
|
id="hidden"
|
|
checked={wifiInfo.hidden}
|
|
onChange={(e) => setWifiInfo({...wifiInfo, hidden: e.target.checked})}
|
|
className="w-4 h-4 text-red-800 bg-gray-100 border-red-800 rounded focus:ring-red-800 focus:ring-2"
|
|
/>
|
|
<label htmlFor="hidden" className="text-sm font-medium text-gray-700">
|
|
{t('hidden')}
|
|
</label>
|
|
</div>
|
|
<p className="text-xs text-gray-500">
|
|
{t('hiddenHelp')}
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
{/* Text Input */}
|
|
{activeTab === 'text' && (
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('textContent')}
|
|
</label>
|
|
<textarea
|
|
value={textInput}
|
|
onChange={(e) => setTextInput(e.target.value)}
|
|
placeholder={t('textPlaceholder')}
|
|
rows={4}
|
|
className="w-full px-4 py-3 border border-red-800 rounded-lg focus:ring-2 focus:ring-red-800 focus:border-transparent transition-all duration-200 resize-none"
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* Contact Input */}
|
|
{activeTab === 'contact' && (
|
|
<div className="space-y-4">
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('firstName')}
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={contactInfo.firstName}
|
|
onChange={(e) => setContactInfo({...contactInfo, firstName: e.target.value})}
|
|
placeholder={t('firstNamePlaceholder')}
|
|
className="w-full px-4 py-3 border border-red-800 rounded-lg focus:ring-2 focus:ring-red-800 focus:border-transparent transition-all duration-200"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('lastName')}
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={contactInfo.lastName}
|
|
onChange={(e) => setContactInfo({...contactInfo, lastName: e.target.value})}
|
|
placeholder={t('lastNamePlaceholder')}
|
|
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-purple-500 focus:border-transparent transition-all duration-200"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('phoneNumber')}
|
|
</label>
|
|
<input
|
|
type="tel"
|
|
value={contactInfo.phone}
|
|
onChange={(e) => setContactInfo({...contactInfo, phone: e.target.value})}
|
|
placeholder={t('phonePlaceholder')}
|
|
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-purple-500 focus:border-transparent transition-all duration-200"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('emailAddress')}
|
|
</label>
|
|
<input
|
|
type="email"
|
|
value={contactInfo.email}
|
|
onChange={(e) => setContactInfo({...contactInfo, email: e.target.value})}
|
|
placeholder={t('emailPlaceholder')}
|
|
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-purple-500 focus:border-transparent transition-all duration-200"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('organization')}
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={contactInfo.organization}
|
|
onChange={(e) => setContactInfo({...contactInfo, organization: e.target.value})}
|
|
placeholder={t('organizationPlaceholder')}
|
|
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-purple-500 focus:border-transparent transition-all duration-200"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
{t('website')}
|
|
</label>
|
|
<input
|
|
type="url"
|
|
value={contactInfo.url}
|
|
onChange={(e) => setContactInfo({...contactInfo, url: e.target.value})}
|
|
placeholder={t('websitePlaceholder')}
|
|
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-purple-500 focus:border-transparent transition-all duration-200"
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<button
|
|
onClick={resetForm}
|
|
className="w-full px-6 py-3 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-all duration-200 font-medium"
|
|
>
|
|
{t('clearAllFields')}
|
|
</button>
|
|
</div>
|
|
|
|
{/* QR Code Display Section */}
|
|
<div className="flex flex-col items-center space-y-6">
|
|
<h2 className="text-2xl font-semibold text-gray-800">{t('generatedQrCode')}</h2>
|
|
|
|
<div className="bg-red-50 border border-red-800 rounded-lg p-8 w-full max-w-sm">
|
|
{qrData ? (
|
|
<div className="text-center">
|
|
<div ref={qrContainerRef} className="flex justify-center">
|
|
{/* QR code will be dynamically inserted here */}
|
|
</div>
|
|
<p className="text-sm text-gray-600 mt-4">
|
|
{t('scanQrCode')}
|
|
</p>
|
|
</div>
|
|
) : (
|
|
<div className="text-center py-16">
|
|
<QrCode className="w-16 h-16 text-gray-300 mx-auto mb-4" />
|
|
<p className="text-gray-500">
|
|
{t('fillFormPrompt')}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{qrData && (
|
|
<div className="flex gap-4 w-full max-w-sm">
|
|
<button
|
|
onClick={downloadQRCode}
|
|
className="flex-1 flex items-center justify-center gap-2 px-6 py-3 bg-red-800 text-white rounded-lg hover:bg-red-700 transition-all duration-200 font-medium shadow-lg"
|
|
>
|
|
<Download className="w-4 h-4" />
|
|
{t('download')}
|
|
</button>
|
|
|
|
<button
|
|
onClick={copyToClipboard}
|
|
className="flex-1 flex items-center justify-center gap-2 px-6 py-3 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-all duration-200 font-medium"
|
|
>
|
|
{copied ? (
|
|
<>
|
|
<Check className="w-4 h-4 text-green-600" />
|
|
{t('copied')}
|
|
</>
|
|
) : (
|
|
<>
|
|
<Copy className="w-4 h-4" />
|
|
{t('copyData')}
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
{qrData && (
|
|
<div className="w-full max-w-sm">
|
|
<h3 className="text-sm font-medium text-gray-700 mb-2">{t('qrCodeData')}</h3>
|
|
<div className="bg-gray-100 rounded-lg p-3 text-xs text-gray-600 max-h-32 overflow-y-auto">
|
|
<pre className="whitespace-pre-wrap break-words">{qrData}</pre>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="text-center mt-8 text-gray-500 text-sm">
|
|
<p>© {new Date().getFullYear()} <a href="https://wachtel-it.de" target="_blank" rel="noopener noreferrer" className="text-red-800 hover:underline">Philipp Wachtel</a></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default QRCodeGenerator; |