Popup free generatoren

This commit is contained in:
Timo Knuth
2026-04-16 19:34:26 +02:00
parent 673eaf7fd3
commit 231a85ffa4
22 changed files with 225 additions and 1 deletions

View File

@@ -11,6 +11,7 @@ import { showToast } from '@/components/ui/Toast';
import { cn } from '@/lib/utils';
import { toPng, toSvg, toBlob } from 'html-to-image';
import { trackEvent } from '@/components/PostHogProvider';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors
const BRAND = {
@@ -54,6 +55,7 @@ export default function BarcodeGeneratorClient() {
const [lineColor, setLineColor] = useState('#000000');
const [frameType, setFrameType] = useState('none');
const [error, setError] = useState<string | null>(null);
const [showPopup, setShowPopup] = useState(false);
const barcodeRef = useRef<HTMLDivElement>(null);
@@ -109,6 +111,7 @@ export default function BarcodeGeneratorClient() {
document.body.removeChild(link);
showToast(`Barcode downloaded as ${extension.toUpperCase()}`, 'success');
if (shouldShowDownloadPopup()) setShowPopup(true);
trackEvent('barcode_downloaded', {
format: format,
extension: extension,
@@ -118,7 +121,6 @@ export default function BarcodeGeneratorClient() {
console.error('Download failed', err);
showToast('Download failed', 'error');
}
};
const copyBarcode = async () => {
if (!barcodeRef.current) return;
@@ -160,6 +162,8 @@ export default function BarcodeGeneratorClient() {
];
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -453,5 +457,6 @@ export default function BarcodeGeneratorClient() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -13,6 +13,7 @@ import {
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors
const BRAND = {
@@ -45,6 +46,7 @@ export default function PhoneGenerator() {
const [phone, setPhone] = useState('');
const [qrColor, setQrColor] = useState(BRAND.richBlue);
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -74,6 +76,7 @@ export default function PhoneGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -82,6 +85,8 @@ export default function PhoneGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -243,5 +248,6 @@ export default function PhoneGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -15,6 +15,7 @@ import { Input } from '@/components/ui/Input';
import { Select } from '@/components/ui/Select';
import { cn } from '@/lib/utils';
import AdBanner from '@/components/ads/AdBanner';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors
const BRAND = {
@@ -58,6 +59,7 @@ export default function CryptoGenerator() {
const [qrMode, setQrMode] = useState<'universal' | 'wallet'>('universal');
const [qrColor, setQrColor] = useState('#F7931A');
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -123,6 +125,7 @@ export default function CryptoGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -131,6 +134,8 @@ export default function CryptoGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -370,5 +375,6 @@ export default function CryptoGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -14,6 +14,7 @@ import {
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors
const BRAND = {
@@ -43,6 +44,7 @@ const FRAME_OPTIONS = [
export default function EmailGenerator() {
const [formData, setFormData] = useState({
const [showPopup, setShowPopup] = useState(false);
email: '',
subject: '',
body: ''
@@ -88,6 +90,7 @@ export default function EmailGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -100,6 +103,8 @@ export default function EmailGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -292,5 +297,6 @@ export default function EmailGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -15,6 +15,7 @@ import {
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors
const BRAND = {
@@ -51,6 +52,7 @@ export default function EventGenerator() {
const [qrColor, setQrColor] = useState(BRAND.primary);
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -102,6 +104,7 @@ export default function EventGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -110,6 +113,8 @@ export default function EventGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -326,5 +331,6 @@ export default function EventGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -14,6 +14,7 @@ import {
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors
const BRAND = {
@@ -46,6 +47,7 @@ export default function FacebookGenerator() {
const [url, setUrl] = useState('');
const [qrColor, setQrColor] = useState('#1877F2'); // Default to FB Blue
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -73,6 +75,7 @@ export default function FacebookGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -81,6 +84,8 @@ export default function FacebookGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -243,5 +248,6 @@ export default function FacebookGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -14,6 +14,7 @@ import {
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors
const BRAND = {
@@ -46,6 +47,7 @@ export default function GeolocationGenerator() {
const [longitude, setLongitude] = useState('');
const [qrColor, setQrColor] = useState(BRAND.primary);
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -76,6 +78,7 @@ export default function GeolocationGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -101,6 +104,8 @@ export default function GeolocationGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -288,5 +293,6 @@ export default function GeolocationGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -6,6 +6,7 @@ import { toPng } from 'html-to-image';
import { Star, Download, AlertCircle } from 'lucide-react';
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
const QR_COLORS = [
{ name: 'Google Blue', value: '#1A73E8' },
@@ -41,6 +42,7 @@ export default function GoogleReviewGenerator() {
const [qrColor, setQrColor] = useState('#1A73E8');
const [frameType, setFrameType] = useState('review');
const [error, setError] = useState('');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -76,12 +78,15 @@ export default function GoogleReviewGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const frameLabel = FRAME_OPTIONS.find(f => f.id === frameType && f.id !== 'none')?.label ?? null;
const isReady = reviewUrl && !error && isValidGoogleReviewLink(reviewUrl);
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
<div className="bg-white rounded-3xl shadow-2xl shadow-slate-900/10 overflow-hidden border border-slate-100">
<div className="grid lg:grid-cols-2">
@@ -209,5 +214,6 @@ export default function GoogleReviewGenerator() {
</div>
</div>
</div>
</>
);
}

View File

@@ -13,6 +13,7 @@ import {
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors
const BRAND = {
@@ -45,6 +46,7 @@ export default function InstagramGenerator() {
const [username, setUsername] = useState('');
const [qrColor, setQrColor] = useState('#E1306C');
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -78,6 +80,7 @@ export default function InstagramGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -86,6 +89,8 @@ export default function InstagramGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -248,5 +253,6 @@ export default function InstagramGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -14,6 +14,7 @@ import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { Select } from '@/components/ui/Select';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors - PayPal Blue
const BRAND = {
@@ -64,6 +65,7 @@ export default function PayPalGenerator() {
const [currency, setCurrency] = useState('EUR');
const [qrColor, setQrColor] = useState(BRAND.primary);
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -114,6 +116,7 @@ export default function PayPalGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -122,6 +125,8 @@ export default function PayPalGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -338,5 +343,6 @@ export default function PayPalGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -13,6 +13,7 @@ import {
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors
const BRAND = {
@@ -46,6 +47,7 @@ export default function SMSGenerator() {
const [message, setMessage] = useState('');
const [qrColor, setQrColor] = useState(BRAND.primary);
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -76,6 +78,7 @@ export default function SMSGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -84,6 +87,8 @@ export default function SMSGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -262,5 +267,6 @@ export default function SMSGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -14,6 +14,7 @@ import {
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors - Microsoft Teams Purple
const BRAND = {
@@ -53,6 +54,7 @@ export default function TeamsGenerator() {
const [userEmail, setUserEmail] = useState('');
const [qrColor, setQrColor] = useState(BRAND.primary);
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -96,6 +98,7 @@ export default function TeamsGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -104,6 +107,8 @@ export default function TeamsGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -314,5 +319,6 @@ export default function TeamsGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -13,6 +13,7 @@ import {
} from 'lucide-react';
import { Button } from '@/components/ui/Button';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors
const BRAND = {
@@ -45,6 +46,7 @@ export default function TextGenerator() {
const [text, setText] = useState('');
const [qrColor, setQrColor] = useState(BRAND.richBlue);
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -72,6 +74,7 @@ export default function TextGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -80,6 +83,8 @@ export default function TextGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -241,5 +246,6 @@ export default function TextGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -14,6 +14,7 @@ import {
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors
const BRAND = {
@@ -45,6 +46,7 @@ export default function TiktokGenerator() {
const [username, setUsername] = useState('');
const [qrColor, setQrColor] = useState('#000000');
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -78,6 +80,7 @@ export default function TiktokGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -86,6 +89,8 @@ export default function TiktokGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -248,5 +253,6 @@ export default function TiktokGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -13,6 +13,7 @@ import {
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors
const BRAND = {
@@ -45,6 +46,7 @@ export default function TwitterGenerator() {
const [username, setUsername] = useState('');
const [qrColor, setQrColor] = useState('#000000');
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -78,6 +80,7 @@ export default function TwitterGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -86,6 +89,8 @@ export default function TwitterGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -248,5 +253,6 @@ export default function TwitterGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -15,6 +15,7 @@ import {
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors
const BRAND = {
@@ -46,6 +47,7 @@ export default function URLGenerator() {
const [url, setUrl] = useState('');
const [qrColor, setQrColor] = useState(BRAND.primary);
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -72,6 +74,7 @@ export default function URLGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -80,6 +83,8 @@ export default function URLGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -241,5 +246,6 @@ export default function URLGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -18,6 +18,7 @@ import {
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors
const BRAND = {
@@ -63,6 +64,7 @@ export default function VCardGenerator() {
const [qrColor, setQrColor] = useState(BRAND.primary);
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -108,6 +110,7 @@ export default function VCardGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -116,6 +119,8 @@ export default function VCardGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-6xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -344,5 +349,6 @@ export default function VCardGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -15,6 +15,7 @@ import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { cn } from '@/lib/utils';
import { Textarea } from '@/components/ui/Textarea';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
@@ -49,6 +50,7 @@ export default function WhatsappGenerator() {
const [message, setMessage] = useState('');
const [qrColor, setQrColor] = useState('#25D366');
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -83,6 +85,7 @@ export default function WhatsappGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -91,6 +94,8 @@ export default function WhatsappGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -263,5 +268,6 @@ export default function WhatsappGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -17,6 +17,7 @@ import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { Select } from '@/components/ui/Select';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors
const BRAND = {
@@ -55,6 +56,7 @@ export default function WiFiGenerator() {
// Customization
const [qrColor, setQrColor] = useState(BRAND.primary);
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -84,6 +86,7 @@ export default function WiFiGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -92,6 +95,8 @@ export default function WiFiGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -303,5 +308,6 @@ export default function WiFiGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -13,6 +13,7 @@ import {
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors
const BRAND = {
@@ -44,6 +45,7 @@ export default function YoutubeGenerator() {
const [url, setUrl] = useState('');
const [qrColor, setQrColor] = useState('#FF0000');
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -71,6 +73,7 @@ export default function YoutubeGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -79,6 +82,8 @@ export default function YoutubeGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -241,5 +246,6 @@ export default function YoutubeGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -13,6 +13,7 @@ import {
import { Button } from '@/components/ui/Button';
import { Input } from '@/components/ui/Input';
import { cn } from '@/lib/utils';
import PostDownloadPopup, { shouldShowDownloadPopup } from '@/components/marketing/PostDownloadPopup';
// Brand Colors - Zoom Blue
const BRAND = {
@@ -46,6 +47,7 @@ export default function ZoomGenerator() {
const [useDirectLink, setUseDirectLink] = useState(false); // Default to web URL for compatibility
const [qrColor, setQrColor] = useState(BRAND.primary);
const [frameType, setFrameType] = useState('none');
const [showPopup, setShowPopup] = useState(false);
const qrRef = useRef<HTMLDivElement>(null);
@@ -103,6 +105,7 @@ export default function ZoomGenerator() {
} catch (err) {
console.error('Download failed', err);
}
if (shouldShowDownloadPopup()) setShowPopup(true);
};
const getFrameLabel = () => {
@@ -111,6 +114,8 @@ export default function ZoomGenerator() {
};
return (
<>
<PostDownloadPopup open={showPopup} onClose={() => setShowPopup(false)} />
<div className="w-full max-w-5xl mx-auto px-4 md:px-6">
{/* Main Generator Card */}
@@ -298,5 +303,6 @@ export default function ZoomGenerator() {
</Link>
</div>
</div>
</>
);
}

View File

@@ -0,0 +1,99 @@
'use client';
import React, { useEffect, useRef } from 'react';
import Link from 'next/link';
import { X, Zap, BarChart2, RefreshCw, Palette } from 'lucide-react';
import { Button } from '@/components/ui/Button';
interface PostDownloadPopupProps {
open: boolean;
onClose: () => void;
}
const BENEFITS = [
{ icon: RefreshCw, text: 'Edit the link anytime — QR stays the same' },
{ icon: BarChart2, text: 'See who scans, when & where' },
{ icon: Palette, text: 'Custom colors, logo & frames' },
{ icon: Zap, text: 'Free plan included — upgrade anytime for more' },
];
const LS_KEY = 'qrm_download_popup_seen';
export function shouldShowDownloadPopup(): boolean {
try { return !localStorage.getItem(LS_KEY); } catch { return false; }
}
export function markDownloadPopupSeen(): void {
try { localStorage.setItem(LS_KEY, '1'); } catch { /* ignore */ }
}
export default function PostDownloadPopup({ open, onClose }: PostDownloadPopupProps) {
const overlayRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!open) return;
markDownloadPopupSeen();
const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };
document.addEventListener('keydown', onKey);
return () => document.removeEventListener('keydown', onKey);
}, [open, onClose]);
if (!open) return null;
return (
<div
ref={overlayRef}
className="fixed inset-0 z-50 flex items-center justify-center p-4"
style={{ backgroundColor: 'rgba(15, 23, 42, 0.6)', backdropFilter: 'blur(4px)' }}
onClick={(e) => { if (e.target === overlayRef.current) onClose(); }}
>
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-md overflow-hidden animate-in fade-in zoom-in-95 duration-200">
{/* Header */}
<div className="relative bg-gradient-to-br from-[#4F46E5] to-[#7C3AED] p-6 text-white text-center">
<button
onClick={onClose}
className="absolute top-4 right-4 text-white/70 hover:text-white transition-colors"
aria-label="Close"
>
<X className="w-5 h-5" />
</button>
<div className="w-12 h-12 bg-white/20 rounded-2xl flex items-center justify-center mx-auto mb-3">
<Zap className="w-6 h-6 text-white" />
</div>
<h2 className="text-xl font-bold">Your QR code is downloading!</h2>
<p className="text-white/80 text-sm mt-1">
Want to make it smarter for free?
</p>
</div>
{/* Benefits */}
<div className="p-6 space-y-3">
{BENEFITS.map(({ icon: Icon, text }) => (
<div key={text} className="flex items-center gap-3">
<div className="w-8 h-8 rounded-xl bg-indigo-50 flex items-center justify-center shrink-0">
<Icon className="w-4 h-4 text-[#4F46E5]" />
</div>
<span className="text-sm text-slate-700">{text}</span>
</div>
))}
</div>
{/* CTAs */}
<div className="px-6 pb-6 space-y-3">
<Link href="/signup" onClick={onClose} className="block">
<Button className="w-full bg-[#4F46E5] hover:bg-[#4338CA] text-white h-12 text-base font-semibold shadow-lg">
Create Free Account
</Button>
</Link>
<button
onClick={onClose}
className="w-full text-sm text-slate-400 hover:text-slate-600 transition-colors py-1"
>
No thanks, keep it static
</button>
</div>
</div>
</div>
);
}