icons/bilder
This commit is contained in:
parent
749cabf0bf
commit
e7478a4af7
|
|
@ -33,6 +33,11 @@ export default function CreatePage() {
|
||||||
const [cornerStyle, setCornerStyle] = useState('square');
|
const [cornerStyle, setCornerStyle] = useState('square');
|
||||||
const [size, setSize] = useState(200);
|
const [size, setSize] = useState(200);
|
||||||
|
|
||||||
|
// Logo state
|
||||||
|
const [logoUrl, setLogoUrl] = useState('');
|
||||||
|
const [logoSize, setLogoSize] = useState(24);
|
||||||
|
const [excavate, setExcavate] = useState(true);
|
||||||
|
|
||||||
// QR preview
|
// QR preview
|
||||||
const [qrDataUrl, setQrDataUrl] = useState('');
|
const [qrDataUrl, setQrDataUrl] = useState('');
|
||||||
|
|
||||||
|
|
@ -150,6 +155,48 @@ export default function CreatePage() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleLogoUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
if (file) {
|
||||||
|
if (file.size > 10 * 1024 * 1024) { // 10MB limit (soft limit for upload, will be resized)
|
||||||
|
showToast('Logo file size too large (max 10MB)', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (evt) => {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const maxDimension = 500; // Resize to max 500px
|
||||||
|
let width = img.width;
|
||||||
|
let height = img.height;
|
||||||
|
|
||||||
|
if (width > maxDimension || height > maxDimension) {
|
||||||
|
if (width > height) {
|
||||||
|
height = Math.round((height * maxDimension) / width);
|
||||||
|
width = maxDimension;
|
||||||
|
} else {
|
||||||
|
width = Math.round((width * maxDimension) / height);
|
||||||
|
height = maxDimension;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx?.drawImage(img, 0, 0, width, height);
|
||||||
|
|
||||||
|
// Compress to JPEG/PNG with reduced quality to save space
|
||||||
|
const dataUrl = canvas.toDataURL(file.type === 'image/png' ? 'image/png' : 'image/jpeg', 0.8);
|
||||||
|
setLogoUrl(dataUrl);
|
||||||
|
};
|
||||||
|
img.src = evt.target?.result as string;
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
@ -167,6 +214,12 @@ export default function CreatePage() {
|
||||||
backgroundColor: canCustomizeColors ? backgroundColor : '#FFFFFF',
|
backgroundColor: canCustomizeColors ? backgroundColor : '#FFFFFF',
|
||||||
cornerStyle,
|
cornerStyle,
|
||||||
size,
|
size,
|
||||||
|
imageSettings: (canCustomizeColors && logoUrl) ? {
|
||||||
|
src: logoUrl,
|
||||||
|
height: logoSize,
|
||||||
|
width: logoSize,
|
||||||
|
excavate,
|
||||||
|
} : undefined,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -488,6 +541,90 @@ export default function CreatePage() {
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
{/* Logo Section */}
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<CardTitle>Logo</CardTitle>
|
||||||
|
{!canCustomizeColors && (
|
||||||
|
<Badge variant="warning">PRO Feature</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
{!canCustomizeColors && (
|
||||||
|
<div className="p-4 bg-blue-50 border border-blue-200 rounded-lg mb-4">
|
||||||
|
<p className="text-sm text-blue-900">
|
||||||
|
<strong>Upgrade to PRO</strong> to add logos to your QR codes.
|
||||||
|
</p>
|
||||||
|
<Link href="/pricing">
|
||||||
|
<Button variant="primary" size="sm" className="mt-2">
|
||||||
|
Upgrade Now
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
Upload Logo
|
||||||
|
</label>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
onChange={handleLogoUpload}
|
||||||
|
disabled={!canCustomizeColors}
|
||||||
|
className="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
/>
|
||||||
|
{logoUrl && (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => {
|
||||||
|
setLogoUrl('');
|
||||||
|
setLogoSize(40);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Remove
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{logoUrl && (
|
||||||
|
<>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
Logo Size: {logoSize}px
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min="20"
|
||||||
|
max="70"
|
||||||
|
value={logoSize}
|
||||||
|
onChange={(e) => setLogoSize(Number(e.target.value))}
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={excavate}
|
||||||
|
onChange={(e) => setExcavate(e.target.checked)}
|
||||||
|
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
|
||||||
|
id="excavate-checkbox"
|
||||||
|
/>
|
||||||
|
<label htmlFor="excavate-checkbox" className="ml-2 block text-sm text-gray-900">
|
||||||
|
Excavate background (remove dots behind logo)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right: Preview */}
|
{/* Right: Preview */}
|
||||||
|
|
@ -505,7 +642,13 @@ export default function CreatePage() {
|
||||||
size={200}
|
size={200}
|
||||||
fgColor={foregroundColor}
|
fgColor={foregroundColor}
|
||||||
bgColor={backgroundColor}
|
bgColor={backgroundColor}
|
||||||
level="M"
|
level="H"
|
||||||
|
imageSettings={logoUrl ? {
|
||||||
|
src: logoUrl,
|
||||||
|
height: logoSize,
|
||||||
|
width: logoSize,
|
||||||
|
excavate: excavate,
|
||||||
|
} : undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,13 @@ END:VCARD`;
|
||||||
size={96}
|
size={96}
|
||||||
fgColor={qr.style?.foregroundColor || '#000000'}
|
fgColor={qr.style?.foregroundColor || '#000000'}
|
||||||
bgColor={qr.style?.backgroundColor || '#FFFFFF'}
|
bgColor={qr.style?.backgroundColor || '#FFFFFF'}
|
||||||
level="M"
|
level="H"
|
||||||
|
imageSettings={qr.style?.imageSettings ? {
|
||||||
|
src: qr.style.imageSettings.src,
|
||||||
|
height: qr.style.imageSettings.height * (96 / 200), // Scale logo for smaller QR
|
||||||
|
width: qr.style.imageSettings.width * (96 / 200),
|
||||||
|
excavate: qr.style.imageSettings.excavate,
|
||||||
|
} : undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue