Popup free generatoren
This commit is contained in:
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
99
src/components/marketing/PostDownloadPopup.tsx
Normal file
99
src/components/marketing/PostDownloadPopup.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user