'use client' import { useEffect, useState } from 'react' import Link from 'next/link' import { motion } from 'framer-motion' import { useAuth } from '@/lib/auth/context' import { initiateOAuthFlow, initiateSignupFlow } from '@/lib/api/oauth' import { listSites, deleteSite, type Site } from '@/lib/api/sites' import { getStats } from '@/lib/api/stats' import type { Stats } from '@/lib/api/stats' import { getSubscription, type SubscriptionDetails } from '@/lib/api/billing' import { LoadingOverlay } from '@ciphera-net/ui' import SiteList from '@/components/sites/SiteList' import { Button } from '@ciphera-net/ui' import { BarChartIcon, LockIcon, ZapIcon, CheckCircleIcon, XIcon, GlobeIcon } from '@ciphera-net/ui' import { toast } from '@ciphera-net/ui' import { getAuthErrorMessage } from '@ciphera-net/ui' import { getSitesLimitForPlan } from '@/lib/plans' function DashboardPreview() { return (
{/* * Glow behind the image */}
{/* * Static Container */}
{/* * Header of the fake browser window */}
{/* * Placeholder for actual dashboard screenshot - replace src with real image later */}

Dashboard Preview

) } function ComparisonSection() { return (

Why choose Pulse?

The lightweight, privacy-friendly alternative.

{[ { feature: "Cookie Banner Required", pulse: false, ga: true }, { feature: "GDPR Compliant", pulse: true, ga: "Complex" }, { feature: "Script Size", pulse: "< 1 KB", ga: "45 KB+" }, { feature: "Data Ownership", pulse: "Yours", ga: "Google's" }, ].map((row, i) => ( ))}
Feature Pulse Google Analytics
{row.feature} {row.pulse === true ? ( ) : row.pulse === false ? ( No ) : ( {row.pulse} )} {row.ga === true ? ( Yes ) : ( {row.ga} )}
) } type SiteStatsMap = Record export default function HomePage() { const { user, loading: authLoading } = useAuth() const [sites, setSites] = useState([]) const [sitesLoading, setSitesLoading] = useState(true) const [siteStats, setSiteStats] = useState({}) const [subscription, setSubscription] = useState(null) const [subscriptionLoading, setSubscriptionLoading] = useState(false) const [showFinishSetupBanner, setShowFinishSetupBanner] = useState(true) useEffect(() => { if (user?.org_id) { loadSites() loadSubscription() } }, [user]) useEffect(() => { if (sites.length === 0) { setSiteStats({}) return } let cancelled = false const today = new Date().toISOString().split('T')[0] const emptyStats: Stats = { pageviews: 0, visitors: 0, bounce_rate: 0, avg_duration: 0 } const load = async () => { const results = await Promise.allSettled( sites.map(async (site) => { const statsRes = await getStats(site.id, today, today) return { siteId: site.id, stats: statsRes } }) ) if (cancelled) return const map: SiteStatsMap = {} results.forEach((r, i) => { const site = sites[i] if (r.status === 'fulfilled') { map[site.id] = { stats: r.value.stats } } else { map[site.id] = { stats: emptyStats } } }) setSiteStats(map) } load() return () => { cancelled = true } }, [sites]) useEffect(() => { if (typeof window === 'undefined') return if (localStorage.getItem('pulse_welcome_completed') === 'true') setShowFinishSetupBanner(false) }, [user?.org_id]) useEffect(() => { if (typeof window === 'undefined') return const params = new URLSearchParams(window.location.search) if (params.get('trial_started') === '1') { toast.success('Your trial is active. You can add sites and start tracking.') params.delete('trial_started') const newUrl = params.toString() ? `${window.location.pathname}?${params}` : window.location.pathname window.history.replaceState({}, '', newUrl) } }, []) const loadSites = async () => { try { setSitesLoading(true) const data = await listSites() setSites(Array.isArray(data) ? data : []) } catch (error: unknown) { toast.error(getAuthErrorMessage(error) || 'Failed to load your sites') setSites([]) } finally { setSitesLoading(false) } } const loadSubscription = async () => { try { setSubscriptionLoading(true) const sub = await getSubscription() setSubscription(sub) } catch { setSubscription(null) } finally { setSubscriptionLoading(false) } } const handleDelete = async (id: string) => { if (!confirm('Are you sure you want to delete this site? This action cannot be undone.')) { return } try { await deleteSite(id) toast.success('Site deleted successfully') loadSites() } catch (error: unknown) { toast.error(getAuthErrorMessage(error) || 'Failed to delete site') } } if (authLoading) { return } if (!user) { return (
{/* * --- 1. ATMOSPHERE (Background) --- */}
{/* * Top-left Orange Glow */}
{/* * Bottom-right Neutral Glow */}
{/* * Grid Pattern with Radial Mask */}
{/* * --- 2. BADGE --- */} Privacy-First Analytics {/* * --- 3. HEADLINE --- */}
Simple analytics for
privacy-conscious {/* * SVG Underline from Main Site */} {' '}apps.
Respect your users' privacy while getting the insights you need. No cookies, no IP tracking, fully GDPR compliant. {/* * --- 4. CTAs --- */}
{/* * NEW: DASHBOARD PREVIEW */} {/* * --- 5. GLASS CARDS --- */}
{[ { icon: LockIcon, title: "Privacy First", desc: "We don't track personal data. No IP addresses, no fingerprints, no cookies." }, { icon: BarChartIcon, title: "Simple Insights", desc: "Get the metrics that matter without the clutter. Page views, visitors, and sources." }, { icon: ZapIcon, title: "Lightweight", desc: "Our script is less than 1kb. It won't slow down your site or affect your SEO." } ].map((feature, i) => (

{feature.title}

{feature.desc}

))}
{/* * NEW: COMPARISON SECTION */} {/* * NEW: CTA BOTTOM */}

Ready to switch?

No credit card required • Cancel anytime

) } // * Wait for organization context before rendering SiteList to avoid "Organization Required" flash if (user && !user.org_id) { return } return (
{showFinishSetupBanner && (

Finish setting up your workspace and add your first site.

)}

Your Sites

Manage your analytics sites and view insights.

{(() => { const siteLimit = getSitesLimitForPlan(subscription?.plan_id) const atLimit = siteLimit != null && sites.length >= siteLimit return atLimit ? (
Limit reached ({sites.length}/{siteLimit})
) : null })() ?? ( )}
{/* * Global Overview - min-h ensures no layout shift when Plan & usage loads */}

Total Sites

{sites.length}

Total Visitors (24h)

{sites.length === 0 || Object.keys(siteStats).length < sites.length ? '--' : Object.values(siteStats).reduce((sum, { stats }) => sum + (stats?.visitors ?? 0), 0).toLocaleString()}

Plan & usage

{subscriptionLoading ? (
) : subscription ? ( <>

{(() => { const raw = subscription.plan_id?.startsWith('price_') ? 'Pro' : subscription.plan_id === 'free' || !subscription.plan_id ? 'Free' : subscription.plan_id const label = raw === 'Free' || raw === 'Pro' ? raw : raw.charAt(0).toUpperCase() + raw.slice(1) return `${label} Plan` })()}

{(typeof subscription.sites_count === 'number' || (subscription.pageview_limit > 0 && typeof subscription.pageview_usage === 'number') || (subscription.next_invoice_amount_due != null && subscription.next_invoice_currency && !subscription.cancel_at_period_end && (subscription.subscription_status === 'active' || subscription.subscription_status === 'trialing'))) && (

{typeof subscription.sites_count === 'number' && ( Sites: {(() => { const limit = getSitesLimitForPlan(subscription.plan_id) return limit != null && typeof subscription.sites_count === 'number' ? `${subscription.sites_count}/${limit}` : subscription.sites_count })()} )} {typeof subscription.sites_count === 'number' && (subscription.pageview_limit > 0 && typeof subscription.pageview_usage === 'number') && ' · '} {subscription.pageview_limit > 0 && typeof subscription.pageview_usage === 'number' && ( Pageviews: {subscription.pageview_usage.toLocaleString()}/{subscription.pageview_limit.toLocaleString()} )} {subscription.next_invoice_amount_due != null && subscription.next_invoice_currency && !subscription.cancel_at_period_end && (subscription.subscription_status === 'active' || subscription.subscription_status === 'trialing') && ( Renews {(() => { const ts = subscription.next_invoice_period_end ?? subscription.current_period_end const d = ts ? new Date(typeof ts === 'number' ? ts * 1000 : ts) : null const dateStr = d && !Number.isNaN(d.getTime()) && d.getTime() !== 0 ? d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }) : null const amount = (subscription.next_invoice_amount_due / 100).toLocaleString('en-US', { style: 'currency', currency: subscription.next_invoice_currency.toUpperCase(), }) return dateStr ? `${dateStr} for ${amount}` : amount })()} )}

)}
{subscription.has_payment_method ? ( Manage billing ) : ( Upgrade )}
) : (

Free Plan

)}
{!sitesLoading && sites.length === 0 && (

Add your first site

Connect a domain to start collecting privacy-friendly analytics. You can add more sites later from the dashboard.

)} {(sitesLoading || sites.length > 0) && ( )}
) }