"use client"; import { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; import { FileText, Users, Banknote, Receipt, CalendarDays, Loader2, ExternalLink, Check, AlertCircle, RefreshCw, } from "lucide-react"; import { createCheckoutSession, createCustomerPortalSession, getSubscription, } from "@/lib/supabase/stripe-actions"; import { METERS, type MeterId } from "@/lib/stripe-config"; const MODULE_ICONS: Record = { invoices: FileText, employees: Users, "payroll-runs": Banknote, expenses: Receipt, "leave-requests": CalendarDays, }; const USAGE_DISCLOSURE: Record = { invoices: "One use is counted each time you create a new invoice. Draft, sent, paid, or overdue invoices all count equally. Editing an existing invoice doesn't add another charge.", employees: "One use is counted each time you create a new active employee record. Duplicates count once. Deleting an employee doesn't remove the charge for that month.", "payroll-runs": "One use is counted each time you execute a payroll run (click 'Confirm Payroll'). The number of employees in the run doesn't affect the count — one run, one charge.", expenses: "One use is counted each time you create a new expense record. Draft and submitted expenses count equally. Editing doesn't add another charge.", "leave-requests": "One use is counted each time you submit a new leave request. Editing, approving, or rejecting a request doesn't add additional charges.", }; export default function PricingContent({ showLayout = true }: { showLayout?: boolean }) { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [stripeError, setStripeError] = useState(false); const [subscription, setSubscription] = useState<{ status: string; cancelAtPeriodEnd: boolean; } | null>(null); const [usage, setUsage] = useState<{ invoices: number; employees: number; payrollRuns: number; estimatedCost: number; } | null>(null); useEffect(() => { // Fetch subscription status getSubscription() .then((s) => { if (s) setSubscription({ status: s.status, cancelAtPeriodEnd: s.cancelAtPeriodEnd }); }) .catch(() => {}); // Fetch usage counts from Supabase Promise.all([ fetch("/api/usage").then((r) => r.ok ? r.json() : null).catch(() => null), ]).then(([usageData]) => { if (usageData) setUsage(usageData); }).catch(() => {}); }, []); const isSubscribed = subscription?.status === "active" || subscription?.status === "trialing"; const handleActivate = async () => { setLoading(true); setError(null); try { const result = await createCheckoutSession(); if (result.error === "already_subscribed") { const portal = await createCustomerPortalSession(); if (portal.error) { setError(portal.error); return; } window.location.href = portal.url; return; } if (result.error) { setError(result.error); return; } window.location.href = result.url; } catch (e: any) { // Stripe init failure — silent, disable button setStripeError(true); } finally { setLoading(false); } }; const handleManageBilling = async () => { setLoading(true); setError(null); try { const result = await createCustomerPortalSession(); if (result.error) { setError(result.error); return; } window.location.href = result.url; } catch { setError("Failed to open billing portal"); } finally { setLoading(false); } }; const modules = Object.values(METERS); return (
{/* Hero */}

Simple, honest pricing.

You only pay when you use a feature. No subscriptions, no surprises.

{/* Subscription Status */} {isSubscribed && (

Billing active

{subscription?.cancelAtPeriodEnd ? "Cancels at end of period" : "Your usage is being metered"}

)} {/* Error */} {error && (
{error}
)} {/* Usage Summary Card */} {usage && (

Current usage

Resets on the 1st

Invoices

{usage.invoices}

Employees

{usage.employees}

Payroll runs

{usage.payrollRuns}

Est. bill

${(usage.estimatedCost / 100).toFixed(2)}

)} {/* Module Cards */}
{modules.map((meter) => { const Icon = MODULE_ICONS[meter.id]; return (

{meter.name}

{meter.description}

${(meter.pricePerUnit / 100).toFixed(meter.pricePerUnit < 100 ? 2 : 0)} per use
{/* Expandable disclosure */} What counts as a use?

{USAGE_DISCLOSURE[meter.id]}

); })}
{/* Activate Billing — Bottom CTA */} {showLayout && (

Ready to simplify your business?

Start free. Pay only when you use a feature.

{/* Activate Billing Button */}

You won't be charged until you exceed the free tier. Cancel anytime from settings.

)}
); }