Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
435 changes: 307 additions & 128 deletions apps/web/app/(dashboard)/dashboard/analytics/page.tsx

Large diffs are not rendered by default.

100 changes: 18 additions & 82 deletions apps/web/app/(dashboard)/dashboard/billing/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,89 +12,27 @@ import {
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Separator } from "@/components/ui/separator";
import { Check, CreditCard, Sparkles, Building2 } from "lucide-react";
import { Check, CreditCard, Sparkles, Building2, Zap } from "lucide-react";
import { useSession } from "@/lib/auth-client";
import { PLANS, type Plan } from "@/lib/pricing";

interface PlanFeature {
label: string;
included: boolean;
}
const PLAN_ICONS: Record<string, React.ReactNode> = {
free: <Sparkles className="h-5 w-5" />,
pro: <CreditCard className="h-5 w-5" />,
scale: <Zap className="h-5 w-5" />,
enterprise: <Building2 className="h-5 w-5" />,
};

interface PlanCard {
key: string;
name: string;
price: string;
period: string;
description: string;
features: PlanFeature[];
cta: string;
popular?: boolean;
icon: React.ReactNode;
function ctaFor(key: string, current: string): string {
if (key === current) return "Current Plan";
if (key === "enterprise") return "Contact Sales";
return `Upgrade to ${PLANS.find((p) => p.key === key)?.name ?? key}`;
}

const plans: PlanCard[] = [
{
key: "free",
name: "Free",
price: "$0",
period: "forever",
description: "Perfect for exploring Vectorless and building prototypes.",
icon: <Sparkles className="h-5 w-5" />,
features: [
{ label: "500 queries / month", included: true },
{ label: "500 pages ingested / month", included: true },
{ label: "25 documents stored", included: true },
{ label: "100 MB storage", included: true },
{ label: "20 queries / min rate limit", included: true },
{ label: "Community support", included: true },
{ label: "MCP server access", included: true },
],
cta: "Current Plan",
},
{
key: "pro",
name: "Pro",
price: "$49",
period: "/ month",
description:
"For teams and individuals using Vectorless in production.",
icon: <CreditCard className="h-5 w-5" />,
popular: true,
features: [
{ label: "20,000 queries / month", included: true },
{ label: "10,000 pages ingested / month", included: true },
{ label: "500 documents stored", included: true },
{ label: "10 GB storage", included: true },
{ label: "200 queries / min rate limit", included: true },
{ label: "Priority email support", included: true },
{ label: "OAuth app integrations", included: true },
],
cta: "Upgrade to Pro",
},
{
key: "enterprise",
name: "Enterprise",
price: "Custom",
period: "",
description:
"For organizations with custom requirements, SLAs, and dedicated support.",
icon: <Building2 className="h-5 w-5" />,
features: [
{ label: "Unlimited queries", included: true },
{ label: "Unlimited ingestion", included: true },
{ label: "Unlimited documents", included: true },
{ label: "Unlimited storage", included: true },
{ label: "1,000 queries / min rate limit", included: true },
{ label: "Dedicated support + SLA", included: true },
{ label: "Custom integrations", included: true },
],
cta: "Contact Sales",
},
];

export default function BillingPage() {
const { data: session } = useSession();
const [currentPlan] = useState("free"); // TODO: Fetch from API
const plans: Plan[] = PLANS;

return (
<div className="space-y-6">
Expand Down Expand Up @@ -133,7 +71,7 @@ export default function BillingPage() {
</Card>

{/* Plan Cards */}
<div className="grid gap-6 md:grid-cols-3">
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-4">
{plans.map((plan) => {
const isCurrent = plan.key === currentPlan;

Expand All @@ -156,7 +94,7 @@ export default function BillingPage() {

<CardHeader className="text-center pt-8">
<div className="mx-auto mb-2 flex h-10 w-10 items-center justify-center rounded-full bg-muted">
{plan.icon}
{PLAN_ICONS[plan.key]}
</div>
<CardTitle className="font-display text-xl">
{plan.name}
Expand All @@ -181,13 +119,11 @@ export default function BillingPage() {
<ul className="space-y-2.5">
{plan.features.map((feature) => (
<li
key={feature.label}
key={feature}
className="flex items-start gap-2 text-sm"
>
<Check className="h-4 w-4 text-emerald-500 mt-0.5 shrink-0" />
<span className="text-muted-foreground">
{feature.label}
</span>
<span className="text-muted-foreground">{feature}</span>
</li>
))}
</ul>
Expand All @@ -205,7 +141,7 @@ export default function BillingPage() {
}
disabled={isCurrent}
>
{isCurrent ? "Current Plan" : plan.cta}
{ctaFor(plan.key, currentPlan)}
</Button>
</CardFooter>
</Card>
Expand Down
Loading
Loading