Skip to content

Commit a1979b9

Browse files
committed
payments screen
1 parent a6ffdf9 commit a1979b9

5 files changed

Lines changed: 294 additions & 1 deletion

File tree

packages/dashboard/src/app/(layout)/components/AppSidebar.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client"
22
import Link from "next/link"
33
import { usePathname } from "next/navigation"
4-
import { AlertCircleIcon, Book, Globe, KeyRound, Layers } from 'lucide-react'
4+
import {AlertCircleIcon, Book, CreditCard, Globe, KeyRound, Layers} from 'lucide-react'
55
import Image from "next/image"
66
import {
77
Sidebar,
@@ -102,6 +102,15 @@ export function AppSidebar() {
102102
</Link>
103103
</SidebarMenuButton>
104104
</SidebarMenuItem>
105+
{FEATURES.SIDEBAR.SHOW_USAGE && <SidebarMenuItem>
106+
<SidebarMenuButton asChild isActive={isActive("/subscription")}>
107+
<Link href="/subscription">
108+
<CreditCard className="mr-2 h-4 w-4" />
109+
Subscription
110+
</Link>
111+
</SidebarMenuButton>
112+
</SidebarMenuItem>}
113+
105114
<GiveFeedbackLink />
106115
</SidebarMenu>
107116
</SidebarGroupContent>
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
"use client";
2+
3+
import {useState} from "react";
4+
import {Button} from "@/components/ui/button";
5+
import {Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle} from "@/components/ui/card";
6+
import {CheckCircle, XCircle} from "lucide-react";
7+
8+
export default function PlansComparison({initialSubscription}) {
9+
const [subscription, setSubscription] = useState(initialSubscription);
10+
const [billingCycle, setBillingCycle] = useState(initialSubscription.billingCycle);
11+
12+
// Handle upgrade
13+
const handleUpgrade = async () => {
14+
try {
15+
// In a real app, you would call an API endpoint
16+
// await fetch('/api/subscription/upgrade', {
17+
// method: 'POST',
18+
// body: JSON.stringify({ plan: 'pro', billingCycle })
19+
// });
20+
21+
setSubscription({
22+
...subscription,
23+
type: "pro",
24+
requests: {
25+
used: subscription.requests.used,
26+
total: 10000
27+
},
28+
billingCycle: billingCycle,
29+
renewalDate: billingCycle === "monthly" ? "June 14, 2025" : "May 14, 2026"
30+
});
31+
} catch (error) {
32+
console.error("Error upgrading subscription:", error);
33+
}
34+
};
35+
36+
// Toggle billing cycle
37+
const changeBillingCycle = (cycle) => {
38+
setBillingCycle(cycle);
39+
};
40+
41+
return (
42+
<>
43+
<div className="flex justify-end mb-4">
44+
<div className="bg-gray-100 p-1 rounded-md inline-flex items-center">
45+
<Button
46+
variant={billingCycle === "monthly" ? "default" : "ghost"}
47+
className="h-8 text-sm"
48+
onClick={() => changeBillingCycle("monthly")}
49+
>
50+
Monthly
51+
</Button>
52+
<Button
53+
variant={billingCycle === "yearly" ? "default" : "ghost"}
54+
className="h-8 text-sm"
55+
onClick={() => changeBillingCycle("yearly")}
56+
>
57+
Yearly (2 months free)
58+
</Button>
59+
</div>
60+
</div>
61+
62+
<div className="grid md:grid-cols-2 gap-6">
63+
{/* Free Plan */}
64+
<Card>
65+
<CardHeader>
66+
<CardTitle>Free Plan</CardTitle>
67+
<CardDescription>Basic features for starters</CardDescription>
68+
</CardHeader>
69+
<CardContent className="space-y-4">
70+
<div className="text-2xl font-bold">$0<span
71+
className="text-base font-normal text-gray-500">/month</span></div>
72+
<ul className="space-y-2">
73+
<li className="flex items-center">
74+
<CheckCircle className="mr-2 h-5 w-5 text-green-500"/>
75+
<span>100 API requests per month</span>
76+
</li>
77+
<li className="flex items-center">
78+
<CheckCircle className="mr-2 h-5 w-5 text-green-500"/>
79+
<span>Basic API gateway functionality</span>
80+
</li>
81+
<li className="flex items-center">
82+
<CheckCircle className="mr-2 h-5 w-5 text-green-500"/>
83+
<span>Endpoints to MCP</span>
84+
</li>
85+
<li className="flex items-center">
86+
<XCircle className="mr-2 h-5 w-5 text-gray-300"/>
87+
<span>Incidents alerts</span>
88+
</li>
89+
</ul>
90+
</CardContent>
91+
<CardFooter>
92+
93+
</CardFooter>
94+
</Card>
95+
96+
<Card>
97+
<CardHeader>
98+
<CardTitle>Pro Plan</CardTitle>
99+
<CardDescription>Advanced features for professionals</CardDescription>
100+
</CardHeader>
101+
<CardContent className="space-y-4">
102+
<div className="flex flex-row items-center gap-4">
103+
<div className="text-2xl font-bold">
104+
{billingCycle === "monthly" ? "$20" : "$200"}
105+
<span className="text-base font-normal text-gray-500">
106+
/{billingCycle === "monthly" ? "month" : "year"}
107+
</span>
108+
</div>
109+
{billingCycle === "yearly" && (
110+
<div className="text-sm text-green-500 mt-1.5">2 months free</div>
111+
)}
112+
</div>
113+
<ul className="space-y-2">
114+
<li className="flex items-center">
115+
<CheckCircle className="mr-2 h-5 w-5 text-green-500"/>
116+
<span>10,000 API requests per month</span>
117+
</li>
118+
<li className="flex items-center">
119+
<CheckCircle className="mr-2 h-5 w-5 text-green-500"/>
120+
<span>Advanced API Gateway functionality</span>
121+
</li>
122+
<li className="flex items-center">
123+
<CheckCircle className="mr-2 h-5 w-5 text-green-500"/>
124+
<span>Priority support</span>
125+
</li>
126+
<li className="flex items-center">
127+
<CheckCircle className="mr-2 h-5 w-5 text-green-500"/>
128+
<span>Incident alerts</span>
129+
</li>
130+
</ul>
131+
</CardContent>
132+
<CardFooter>
133+
{subscription.type === "pro" ? (
134+
<Button disabled variant="outline" className="w-full">Current Plan</Button>
135+
) : (
136+
<Button onClick={handleUpgrade} className="w-full">Upgrade Now</Button>
137+
)}
138+
</CardFooter>
139+
</Card>
140+
</div>
141+
</>
142+
);
143+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import { Badge } from "@/components/ui/badge";
5+
import { Progress } from "@/components/ui/progress";
6+
import { Button } from "@/components/ui/button";
7+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
8+
9+
export default function SubscriptionCard({ initialSubscription }) {
10+
const [subscription, setSubscription] = useState(initialSubscription);
11+
12+
// Calculate usage percentage
13+
const usagePercentage = Math.round((subscription.requests.used / subscription.requests.total) * 100);
14+
15+
// Handle upgrade
16+
const handleUpgrade = async () => {
17+
try {
18+
// In a real app, you would call an API endpoint
19+
// await fetch('/api/subscription/upgrade', { method: 'POST' });
20+
21+
setSubscription({
22+
...subscription,
23+
type: "pro",
24+
requests: {
25+
used: subscription.requests.used,
26+
total: 10000
27+
},
28+
renewalDate: "June 14, 2025",
29+
billingCycle: "monthly"
30+
});
31+
} catch (error) {
32+
console.error("Error upgrading subscription:", error);
33+
}
34+
};
35+
36+
// Handle cancel
37+
const handleCancel = async () => {
38+
if (window.confirm("Are you sure you want to cancel your subscription?")) {
39+
try {
40+
// In a real app, you would call an API endpoint
41+
// await fetch('/api/subscription/cancel', { method: 'POST' });
42+
43+
setSubscription({
44+
...subscription,
45+
type: "free",
46+
requests: {
47+
used: subscription.requests.used,
48+
total: 100
49+
}
50+
});
51+
} catch (error) {
52+
console.error("Error canceling subscription:", error);
53+
}
54+
}
55+
};
56+
57+
return (
58+
<Card className="w-full">
59+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
60+
<div>
61+
<CardTitle className="text-xl flex items-center">
62+
<div>
63+
{subscription.type === "pro" ? "Pro Plan" : "Free Plan"}
64+
</div>
65+
<div className="flex items-center">
66+
{subscription.type === "pro" && (
67+
<Badge className="ml-3 bg-green-500 hover:bg-green-600">Active</Badge>
68+
)}
69+
</div>
70+
</CardTitle>
71+
<CardDescription>
72+
{subscription.type === "pro"
73+
? `Renews on ${subscription.renewalDate} (${subscription.billingCycle === "monthly" ? "Monthly" : "Yearly"})`
74+
: "Limited features"}
75+
</CardDescription>
76+
</div>
77+
<div className="flex space-x-2">
78+
{subscription.type === "pro" ? (
79+
<Button variant="outline" onClick={handleCancel}>
80+
Cancel Subscription
81+
</Button>
82+
) : (
83+
<Button onClick={handleUpgrade}>Upgrade</Button>
84+
)}
85+
</div>
86+
</CardHeader>
87+
<CardContent className="pt-4">
88+
<div className="mb-2 flex justify-between items-center">
89+
<div className="text-sm text-gray-500">API Requests</div>
90+
<div className="text-sm font-medium">
91+
{subscription.requests.used} / {subscription.requests.total}
92+
</div>
93+
</div>
94+
<Progress value={usagePercentage} className="h-2" />
95+
</CardContent>
96+
</Card>
97+
);
98+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Separator } from "@/components/ui/separator";
2+
import SubscriptionCard from "./components/SubscriptionCard";
3+
import PlansComparison from "./components/PlansComparison";
4+
5+
export default async function SubscriptionPage() {
6+
// In a real app, you would fetch this data server-side
7+
const userData = {
8+
subscription: {
9+
type: "free", // "free" or "pro"
10+
requests: {
11+
used: 2345,
12+
total: 10000
13+
},
14+
renewalDate: "June 14, 2025",
15+
billingCycle: "yearly" // "monthly" or "yearly"
16+
}
17+
};
18+
19+
return (
20+
<div className="container mx-auto">
21+
<div className="flex justify-between items-center">
22+
<div>
23+
<h1 className="text-3xl font-bold tracking-tight">Manage Subscription</h1>
24+
</div>
25+
</div>
26+
<Separator className="my-4" />
27+
<div className="space-y-6">
28+
{/* Current Subscription Section */}
29+
<div>
30+
<h2 className="text-xl font-semibold mb-4">Current Subscription</h2>
31+
<SubscriptionCard initialSubscription={userData.subscription} />
32+
</div>
33+
34+
{/* Plans Comparison Section */}
35+
<div>
36+
<h2 className="text-xl font-semibold mb-4">Plans Comparison</h2>
37+
<PlansComparison initialSubscription={userData.subscription} />
38+
</div>
39+
</div>
40+
</div>
41+
);
42+
}

packages/dashboard/src/config/features.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const FEATURES = {
77
SIDEBAR: {
88
SHOW_USAGE: !isSelfHosted,
99
SHOW_REGION: !isSelfHosted,
10+
SHOW_SUBSCRIPTIONS: !isSelfHosted,
1011
},
1112
ANALYTICS: {
1213
ENABLE_SENTRY: !isSelfHosted,

0 commit comments

Comments
 (0)