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 [size, setSize] = useState(200);
|
||||
|
||||
// Logo state
|
||||
const [logoUrl, setLogoUrl] = useState('');
|
||||
const [logoSize, setLogoSize] = useState(24);
|
||||
const [excavate, setExcavate] = useState(true);
|
||||
|
||||
// QR preview
|
||||
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) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
|
|
@ -167,6 +214,12 @@ export default function CreatePage() {
|
|||
backgroundColor: canCustomizeColors ? backgroundColor : '#FFFFFF',
|
||||
cornerStyle,
|
||||
size,
|
||||
imageSettings: (canCustomizeColors && logoUrl) ? {
|
||||
src: logoUrl,
|
||||
height: logoSize,
|
||||
width: logoSize,
|
||||
excavate,
|
||||
} : undefined,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -488,6 +541,90 @@ export default function CreatePage() {
|
|||
</div>
|
||||
</CardContent>
|
||||
</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>
|
||||
|
||||
{/* Right: Preview */}
|
||||
|
|
@ -505,7 +642,13 @@ export default function CreatePage() {
|
|||
size={200}
|
||||
fgColor={foregroundColor}
|
||||
bgColor={backgroundColor}
|
||||
level="M"
|
||||
level="H"
|
||||
imageSettings={logoUrl ? {
|
||||
src: logoUrl,
|
||||
height: logoSize,
|
||||
width: logoSize,
|
||||
excavate: excavate,
|
||||
} : undefined}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -200,7 +200,13 @@ END:VCARD`;
|
|||
size={96}
|
||||
fgColor={qr.style?.foregroundColor || '#000000'}
|
||||
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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue