revops + onboarding

This commit is contained in:
Timo Knuth
2026-04-22 20:00:44 +02:00
parent ce724662d4
commit 7d2724b65d
37 changed files with 5073 additions and 1286 deletions

View File

@@ -7,12 +7,15 @@ import { StatsGrid } from '@/components/dashboard/StatsGrid';
import { QRCodeCard } from '@/components/dashboard/QRCodeCard';
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button';
import { Badge } from '@/components/ui/Badge';
import { useTranslation } from '@/hooks/useTranslation';
import { useCsrf } from '@/hooks/useCsrf';
import { showToast } from '@/components/ui/Toast';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '@/components/ui/Dialog';
import { QrCode } from 'lucide-react';
import { Badge } from '@/components/ui/Badge';
import { useTranslation } from '@/hooks/useTranslation';
import { useCsrf } from '@/hooks/useCsrf';
import { showToast } from '@/components/ui/Toast';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from '@/components/ui/Dialog';
import { QrCode } from 'lucide-react';
import { trackEvent, identifyUser } from '@/components/PostHogProvider';
import { FREE_DYNAMIC_QR_LIMIT } from '@/lib/plans';
import { OnboardingChecklist } from '@/components/dashboard/OnboardingChecklist';
interface QRCodeData {
id: string;
@@ -44,7 +47,8 @@ export default function DashboardPage() {
conversionRate: 0,
uniqueScans: 0,
});
const [analyticsData, setAnalyticsData] = useState<any>(null);
const [analyticsData, setAnalyticsData] = useState<any>(null);
const [onboardingState, setOnboardingState] = useState<any>(null);
const blogPosts = [
@@ -117,12 +121,11 @@ export default function DashboardPage() {
// Store in localStorage for consistency
localStorage.setItem('user', JSON.stringify(user));
const { identifyUser, trackEvent } = await import('@/components/PostHogProvider');
identifyUser(user.id, {
email: user.email,
name: user.name,
plan: user.plan || 'FREE',
provider: 'google',
identifyUser(user.id, {
email: user.email,
name: user.name,
plan: user.plan || 'FREE',
provider: 'google',
});
trackEvent(isNewUser ? 'user_signup' : 'user_login', {
@@ -143,25 +146,35 @@ export default function DashboardPage() {
}, [searchParams, router]);
// Check for successful payment and verify session
useEffect(() => {
const success = searchParams.get('success');
if (success === 'true') {
const verifySession = async () => {
try {
const response = await fetch('/api/stripe/verify-session', {
method: 'POST',
});
if (response.ok) {
const data = await response.json();
setUserPlan(data.plan);
setUpgradedPlan(data.plan);
setShowUpgradeDialog(true);
// Remove success parameter from URL
router.replace('/dashboard');
} else {
console.error('Failed to verify session:', await response.text());
}
useEffect(() => {
const success = searchParams.get('success');
const sessionId = searchParams.get('session_id');
if (success === 'true' && sessionId) {
const verifySession = async () => {
try {
const response = await fetch('/api/stripe/verify-session', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ sessionId }),
});
if (response.ok) {
const data = await response.json();
setUserPlan(data.plan);
setUpgradedPlan(data.plan);
setShowUpgradeDialog(true);
trackEvent('upgrade_completed', {
plan: data.plan,
source: 'stripe_checkout',
});
// Remove success parameter from URL
router.replace('/dashboard');
} else {
console.error('Failed to verify session:', await response.text());
}
} catch (error) {
console.error('Error verifying session:', error);
}
@@ -212,13 +225,19 @@ export default function DashboardPage() {
setUserPlan(userData.plan || 'FREE');
}
// Fetch analytics data for trends (last 30 days = month comparison)
const analyticsResponse = await fetch('/api/analytics/summary?range=30');
if (analyticsResponse.ok) {
const analytics = await analyticsResponse.json();
setAnalyticsData(analytics);
}
} catch (error) {
// Fetch analytics data for trends (last 30 days = month comparison)
const analyticsResponse = await fetch('/api/analytics/summary?range=30');
if (analyticsResponse.ok) {
const analytics = await analyticsResponse.json();
setAnalyticsData(analytics);
}
const onboardingResponse = await fetch('/api/onboarding');
if (onboardingResponse.ok) {
const onboardingData = await onboardingResponse.json();
setOnboardingState(onboardingData);
}
} catch (error) {
console.error('Error fetching data:', error);
setQrCodes([]);
setStats({
@@ -341,9 +360,11 @@ export default function DashboardPage() {
</div>
</div>
{/* Stats Grid */}
<StatsGrid
stats={stats}
{/* Stats Grid */}
<OnboardingChecklist state={onboardingState} />
<StatsGrid
stats={stats}
trends={{
totalScans: analyticsData?.summary.scansTrend,
comparisonPeriod: analyticsData?.summary.comparisonPeriod || 'month'
@@ -393,8 +414,8 @@ export default function DashboardPage() {
<QrCode className="w-12 h-12 text-gray-300 mx-auto mb-4" />
<h3 className="text-lg font-semibold text-gray-700 mb-2">Create your first QR code</h3>
<p className="text-gray-500 mb-6 max-w-sm mx-auto">
You have 3 free dynamic QR codes. They redirect wherever you want and track every scan.
</p>
You have {FREE_DYNAMIC_QR_LIMIT} free dynamic QR codes. They redirect wherever you want and track every scan.
</p>
<Link href="/create">
<Button>Create QR Code it takes 90 seconds</Button>
</Link>
@@ -521,4 +542,4 @@ export default function DashboardPage() {
</Dialog>
</div>
);
}
}