Skip to content

Commit 0e432f8

Browse files
Merge pull request #9 from TravelMateAI:feat/subscription-page
Feat/subscription-page
2 parents 8b698ea + d5a474f commit 0e432f8

7 files changed

Lines changed: 384 additions & 13 deletions

File tree

package-lock.json

Lines changed: 110 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"@radix-ui/react-dialog": "^1.1.1",
1616
"@radix-ui/react-dropdown-menu": "^2.1.1",
1717
"@radix-ui/react-label": "^2.1.0",
18-
"@radix-ui/react-slot": "^1.1.0",
18+
"@radix-ui/react-slot": "^1.2.3",
1919
"@radix-ui/react-tabs": "^1.1.0",
2020
"@radix-ui/react-toast": "^1.2.1",
2121
"@radix-ui/react-tooltip": "^1.1.2",

src/app/(main)/Navbar.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,28 @@
1+
"use client";
2+
13
import SearchField from "@/components/SearchField";
24
import UserButton from "@/components/UserButton";
35
import Link from "next/link";
4-
import LanguageSwitcher from "../../components/LanguageSwitcher"; // Adjusted path
6+
import LanguageSwitcher from "../../components/LanguageSwitcher";
7+
import SubscriptionModal from "./SubscriptionModal"; // 👈 Import your modal
58

69
export default function Navbar() {
710
return (
811
<header className="sticky top-0 z-10 bg-card shadow-sm">
9-
<div className="mx-auto flex max-w-7xl flex-wrap items-center justify-center gap-5 px-5 py-3">
10-
<Link href="/" className="text-2xl font-bold text-primary">
11-
GoTogether
12-
</Link>
12+
<div className="mx-auto flex max-w-7xl flex-wrap items-center justify-between gap-5 px-5 py-3">
13+
{/* Left: Modal Button + Logo */}
14+
<div className="flex items-center gap-3">
15+
<SubscriptionModal /> {/* 👈 Trigger is inside this component */}
16+
<Link href="/" className="text-2xl font-bold text-primary">
17+
GoTogether
18+
</Link>
19+
</div>
20+
21+
{/* Center: Search */}
1322
<SearchField />
14-
<div className="flex items-center gap-3 sm:ms-auto"> {/* Wrapper div for UserButton and LanguageSwitcher */}
23+
24+
{/* Right: Language Switcher & User */}
25+
<div className="flex items-center gap-3 sm:ms-auto">
1526
<LanguageSwitcher />
1627
<UserButton />
1728
</div>
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import { Button } from "@/components/ui/button";
5+
import {
6+
Card,
7+
CardContent,
8+
CardDescription,
9+
CardFooter,
10+
CardHeader,
11+
CardTitle,
12+
} from "@/components/ui/card";
13+
import { Badge } from "@/components/ui/badge";
14+
import {
15+
Dialog,
16+
DialogContent,
17+
DialogHeader,
18+
DialogTitle,
19+
DialogTrigger,
20+
} from "@/components/ui/dialog";
21+
import { cn } from "@/lib/utils";
22+
import { CheckCircle2, Zap, ShieldCheck, UserCheck } from "lucide-react";
23+
24+
export default function SubscriptionModal() {
25+
const [open, setOpen] = useState(false);
26+
27+
const plans = [
28+
{
29+
name: "Free",
30+
price: "$0/mo",
31+
features: [
32+
{ label: "Access to basic features", icon: CheckCircle2 },
33+
{ label: "Limited support", icon: UserCheck },
34+
{ label: "Community access", icon: ShieldCheck },
35+
],
36+
cta: "Start Free",
37+
popular: false,
38+
},
39+
{
40+
name: "Pro",
41+
price: "$10/mo",
42+
features: [
43+
{ label: "Access to Pro features", icon: Zap },
44+
{ label: "Priority email support", icon: ShieldCheck },
45+
{ label: "Advanced analytics", icon: CheckCircle2 },
46+
],
47+
cta: "Get Pro",
48+
popular: true,
49+
},
50+
{
51+
name: "Plus",
52+
price: "$25/mo",
53+
features: [
54+
{ label: "All features unlocked", icon: Zap },
55+
{ label: "24/7 phone support", icon: ShieldCheck },
56+
{ label: "Dedicated account manager", icon: UserCheck },
57+
],
58+
cta: "Go Plus",
59+
popular: false,
60+
},
61+
];
62+
63+
return (
64+
<Dialog open={open} onOpenChange={setOpen}>
65+
<DialogTrigger asChild>
66+
<Button
67+
variant="outline"
68+
className="font-semibold"
69+
aria-label="Open subscription plans"
70+
>
71+
Plans
72+
</Button>
73+
</DialogTrigger>
74+
<DialogContent className="max-w-6xl p-6 sm:p-8">
75+
<DialogHeader>
76+
<DialogTitle className="text-center text-3xl font-extrabold text-primary">
77+
Choose the Right Plan for You
78+
</DialogTitle>
79+
</DialogHeader>
80+
<div className="mt-6 flex flex-col gap-6 sm:flex-row sm:flex-wrap sm:justify-center">
81+
{plans.map((plan) => (
82+
<Card
83+
key={plan.name}
84+
className={cn(
85+
"relative flex w-full flex-col justify-between border shadow-lg transition-transform hover:scale-[1.03] sm:w-72",
86+
plan.popular && "border-primary ring-2 ring-primary",
87+
)}
88+
>
89+
<CardHeader className="p-6 text-center">
90+
{plan.popular && (
91+
<Badge
92+
className="absolute right-4 top-4 animate-pulse"
93+
variant="outline"
94+
>
95+
⭐ Best Value
96+
</Badge>
97+
)}
98+
<CardTitle className="text-2xl font-bold">
99+
{plan.name}
100+
</CardTitle>
101+
<CardDescription className="mt-1 text-lg text-muted-foreground">
102+
{plan.price}
103+
</CardDescription>
104+
</CardHeader>
105+
<CardContent className="p-6">
106+
<ul className="space-y-4 text-left text-sm">
107+
{plan.features.map((feature, idx) => (
108+
<li key={idx} className="flex items-center gap-3">
109+
<feature.icon className="h-5 w-5 text-green-600" />
110+
<span>{feature.label}</span>
111+
</li>
112+
))}
113+
</ul>
114+
</CardContent>
115+
<CardFooter className="p-6">
116+
<Button
117+
className={cn(
118+
"w-full",
119+
plan.popular
120+
? "bg-primary text-white hover:bg-primary/90"
121+
: "",
122+
)}
123+
onClick={() => {
124+
setOpen(false);
125+
// handleSubscribe(plan.name);
126+
}}
127+
>
128+
{plan.cta}
129+
</Button>
130+
</CardFooter>
131+
</Card>
132+
))}
133+
</div>
134+
<p className="mx-auto mt-6 max-w-xl text-center text-sm text-muted-foreground">
135+
You can switch or cancel your plan anytime from your account settings.
136+
</p>
137+
</DialogContent>
138+
</Dialog>
139+
);
140+
}

src/components/ui/badge.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import * as React from "react"
2+
import { cva, type VariantProps } from "class-variance-authority"
3+
4+
import { cn } from "@/lib/utils"
5+
6+
const badgeVariants = cva(
7+
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8+
{
9+
variants: {
10+
variant: {
11+
default:
12+
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
13+
secondary:
14+
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15+
destructive:
16+
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
17+
outline: "text-foreground",
18+
},
19+
},
20+
defaultVariants: {
21+
variant: "default",
22+
},
23+
}
24+
)
25+
26+
export interface BadgeProps
27+
extends React.HTMLAttributes<HTMLDivElement>,
28+
VariantProps<typeof badgeVariants> {}
29+
30+
function Badge({ className, variant, ...props }: BadgeProps) {
31+
return (
32+
<div className={cn(badgeVariants({ variant }), className)} {...props} />
33+
)
34+
}
35+
36+
export { Badge, badgeVariants }

0 commit comments

Comments
 (0)