index.php hinzugefügt
This commit is contained in:
698
index.php
Normal file
698
index.php
Normal file
@ -0,0 +1,698 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user