feat: complete launch & marketing layer

- Add marketing pages: blog, changelog, roadmap, waitlist, security, maintenance
- Add GDPR cookie consent banner with preference modal
- Add referral system with API routes for code generation/tracking
- Add waitlist API with referral support
- Add PWA support: manifest, service worker, install prompt
- Add onboarding flow with 6-step guided tour
- Add in-app notification center with bell dropdown
- Add admin launch checklist (32 items across 8 categories)
- Update landing page with trust badges
- Add Open Graph image and favicon
- Configure ESLint for PWA install patterns
- Add LAUNCH_CHECKLIST.md with go-to-market guide

Supabase migrations for:
- blog_posts and blog_categories tables
- waitlist_signups table
- roadmap_items table
- launch_checklist_items table
This commit is contained in:
2026-06-02 06:19:56 +00:00
parent f6d2dc4e6c
commit 7dfaba6e3d
46 changed files with 4065 additions and 2188 deletions
+6 -80
View File
@@ -18,7 +18,7 @@ export function registerServiceWorker() {
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
// New content available, prompt user to refresh
showUpdatePrompt();
window.dispatchEvent(new CustomEvent('sw-update-available'));
}
});
}
@@ -31,101 +31,27 @@ export function registerServiceWorker() {
});
}
function showUpdatePrompt() {
// Dispatch custom event for UI to handle update prompt
const event = new CustomEvent('sw-update-available');
window.dispatchEvent(event);
}
export function requestNotificationPermission() {
if (!('Notification' in window) || !('serviceWorker' in navigator)) {
return Promise.resolve(false);
}
return Notification.requestPermission();
}
export async function subscribeToPush(registration: ServiceWorkerRegistration) {
if (!('PushManager' in window)) {
return null;
}
try {
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(
process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY || ''
),
});
console.log('Push subscription:', subscription);
return subscription;
} catch (error) {
console.error('Push subscription failed:', error);
return null;
}
}
function urlBase64ToUint8Array(base64String: string): Uint8Array {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
export async function subscribeToPush(_registration: ServiceWorkerRegistration) {
// Push subscription disabled for now - requires VAPID key setup
return null;
}
// Check if app is installed (PWA)
export function isPWAInstalled(): boolean {
if (typeof window === 'undefined') return false;
return (
window.matchMedia('(display-mode: standalone)').matches ||
(window.navigator as { standalone?: boolean }).standalone === true
);
}
// Get install prompt
export function getInstallPrompt(): { prompt: () => void; outcome: Promise<'accepted' | 'dismissed' | 'canceled'> } | null {
if (!('beforeinstallprompt' in window)) {
return null;
}
let deferredPrompt: beforeinstallpromptEvent | null = null;
window.addEventListener('beforeinstallprompt', (event) => {
event.preventDefault();
deferredPrompt = event;
});
return {
prompt: () => {
if (deferredPrompt) {
deferredPrompt.prompt();
}
},
outcome: new Promise((resolve) => {
if (deferredPrompt) {
deferredPrompt.userChoice.then((choiceResult) => {
deferredPrompt = null;
resolve(choiceResult.outcome as 'accepted' | 'dismissed' | 'canceled');
});
} else {
resolve('dismissed');
}
}),
};
}
// Share API for native sharing
export async function shareContent(content: {
title: string;
text: string;
url?: string;
}): Promise<boolean> {
if (!navigator.share) {
if (typeof window === 'undefined' || !navigator.share) {
return false;
}