Skip to content

Commit 8c38e0a

Browse files
committed
feat: Introduce PRO plan with a new pricing section, add a refund policy page, and update related legal documents and navigation.
1 parent 0fb942d commit 8c38e0a

7 files changed

Lines changed: 253 additions & 22 deletions

File tree

client/src/Pages/Plans/index.jsx

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useQueryClient } from "@tanstack/react-query"
22
import { Check, Crown, Sparkles, Zap } from "lucide-react"
33
import { useContext, useEffect, useState } from "react"
4+
import { Link, useNavigate } from "react-router-dom"
45
import { toast } from "sonner"
56
import { paymentKeys } from "@/api/payment"
67
import { ChatContext } from "@/Context/ChatContext"
@@ -40,10 +41,15 @@ const planMeta = {
4041
}
4142

4243
export default function PlansPage() {
43-
const { user } = useContext(Context)
44-
const { socket } = useContext(ChatContext)
44+
const userCtx = useContext(Context)
45+
const chatCtx = useContext(ChatContext)
46+
const user = userCtx?.user || null
47+
const socket = chatCtx?.socket || null
48+
const navigate = useNavigate()
4549
const queryClient = useQueryClient()
46-
const { data: entitlement, isLoading: entitlementLoading } = useEntitlementQuery()
50+
const { data: entitlement, isLoading: entitlementLoading } = useEntitlementQuery({
51+
enabled: !!user,
52+
})
4753
const { data: dbPlans, isLoading: plansLoading } = usePlansQuery()
4854
const createOrderMutation = useCreatePaymentOrderMutation()
4955
const [processingPlan, setProcessingPlan] = useState(null)
@@ -91,6 +97,11 @@ export default function PlansPage() {
9197
const handleUpgrade = async (planCode) => {
9298
if (planCode === "FREE" || currentPlan === planCode) return
9399

100+
if (!user) {
101+
navigate("/login?returnTo=/plans")
102+
return
103+
}
104+
94105
setProcessingPlan(planCode)
95106

96107
try {
@@ -146,7 +157,7 @@ export default function PlansPage() {
146157
}))
147158

148159
return (
149-
<div className="container mx-auto px-4 py-8 max-w-5xl">
160+
<div className="container mx-auto px-4 py-28 max-w-5xl">
150161
<div className="text-center mb-10">
151162
<h1 className="text-3xl font-bold mb-3 flex items-center justify-center gap-2">
152163
<Sparkles className="h-8 w-8 text-primary" />
@@ -222,9 +233,30 @@ export default function PlansPage() {
222233
</div>
223234

224235
<p className="text-center text-xs text-muted-foreground mt-8">
225-
Payments are processed securely via Razorpay. Your subscription will be activated after
226-
payment confirmation.
236+
Payments are processed securely via Razorpay. All purchases are final and non-refundable.
227237
</p>
238+
<div className="flex items-center justify-center gap-4 mt-3 text-xs text-muted-foreground">
239+
<Link
240+
to="/refund-policy"
241+
className="hover:text-foreground underline underline-offset-2 transition-colors"
242+
>
243+
Refund Policy
244+
</Link>
245+
<span className="text-border"></span>
246+
<Link
247+
to="/terms-of-services"
248+
className="hover:text-foreground underline underline-offset-2 transition-colors"
249+
>
250+
Terms of Service
251+
</Link>
252+
<span className="text-border"></span>
253+
<Link
254+
to="/privacy-policy"
255+
className="hover:text-foreground underline underline-offset-2 transition-colors"
256+
>
257+
Privacy Policy
258+
</Link>
259+
</div>
228260
</div>
229261
)
230262
}

client/src/Routes.jsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import React, { lazy, Suspense } from "react"
22
import { Loader2 } from "lucide-react"
3-
import { useNavigate } from "react-router-dom"
3+
import { Navigate, useLocation } from "react-router-dom"
44
import { useIsMobile } from "./hooks/use-mobile"
5-
import { useLocation } from "react-router-dom"
65
import { useSidebar } from "./components/ui/sidebar"
76
import Navbar from "./components/Navbar"
87
import { AppSidebar } from "./components/AppSidebar"
@@ -36,6 +35,7 @@ const Chat = lazy(() => import("./components/Chat/Chat"))
3635
const Home = lazy(() => import("./components/LandingPage/Home"))
3736
const PrivacyPolicy = lazy(() => import("./components/LandingPage/PrivacyPolicy"))
3837
const TermsOfService = lazy(() => import("./components/LandingPage/TermOfService"))
38+
const RefundPolicy = lazy(() => import("./components/LandingPage/RefundPolicy"))
3939
const NotFoundPage = lazy(() => import("./components/NotFound"))
4040
const Dashboard = lazy(() => import("./components/Posts/Dashboard"))
4141
const PostDetail = lazy(() => import("./components/Posts/PostDetail"))
@@ -82,7 +82,6 @@ export const privateRoutes = [
8282
{ path: "/music/my-playlist/:id", element: <UserPlaylistDetails /> },
8383
{ path: "/music/history", element: <HistoryPage /> },
8484
{ path: "/music/sync", element: <GroupMusic /> },
85-
{ path: "/plans", element: <PlansPage /> },
8685
{ path: "/payments/history", element: <PaymentHistoryPage /> },
8786
]
8887

@@ -94,12 +93,13 @@ export const publicRoutes = [
9493
{ path: "/passkey-login", element: <PasskeyLogin /> },
9594
{ path: "/privacy-policy", element: <PrivacyPolicy /> },
9695
{ path: "/terms-of-services", element: <TermsOfService /> },
96+
{ path: "/refund-policy", element: <RefundPolicy /> },
97+
{ path: "/plans", element: <PlansPage /> },
9798
{ path: "*", element: <NotFoundPage /> },
9899
]
99100

100101
// Protected Routes Component
101102
export const ProtectedRoutes = () => {
102-
const navigate = useNavigate()
103103
const { user, loading } = useContext(Context)
104104
const { open } = useSidebar()
105105
const isMobile = useIsMobile()
@@ -111,12 +111,11 @@ export const ProtectedRoutes = () => {
111111
}
112112

113113
if (!user?.email) {
114-
return navigate(`/login?returnTo=${location.pathname}`)
114+
return <Navigate to={`/login?returnTo=${location.pathname}`} replace />
115115
}
116116

117117
if (!user?.verified) {
118-
toast.error("Please verify your email first")
119-
return navigate("/verify", { state: { email: user?.email } })
118+
return <Navigate to="/verify" state={{ email: user?.email }} replace />
120119
}
121120

122121
return (

client/src/components/LandingPage/Footer.jsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ const Footer = memo(() => {
2222
<a href="#features" className="text-white/40 hover:text-white transition-colors">
2323
Features
2424
</a>
25+
<Link to="/plans" className="text-white/40 hover:text-white transition-colors">
26+
Pricing
27+
</Link>
2528
<a href="#download" className="text-white/40 hover:text-white transition-colors">
2629
Download
2730
</a>
@@ -34,6 +37,9 @@ const Footer = memo(() => {
3437
>
3538
Terms
3639
</Link>
40+
<Link to="/refund-policy" className="text-white/40 hover:text-white transition-colors">
41+
Refund Policy
42+
</Link>
3743
</div>
3844

3945
{/* Social */}

client/src/components/LandingPage/Home.jsx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import {
22
ArrowRight,
3+
Check,
4+
Crown,
35
Download,
46
Headphones,
57
Loader2,
@@ -8,6 +10,7 @@ import {
810
Share2,
911
Users,
1012
Video,
13+
Zap,
1114
} from "lucide-react"
1215
import { lazy, memo, Suspense } from "react"
1316
import { Link } from "react-router-dom"
@@ -239,6 +242,75 @@ const MobileApp = memo(() => (
239242
</section>
240243
))
241244

245+
const Pricing = memo(() => (
246+
<section id="pricing" className="py-24 px-6">
247+
<div className="max-w-4xl mx-auto">
248+
<div className="text-center mb-16">
249+
<h2 className="text-3xl sm:text-4xl md:text-5xl font-bold text-white mb-4">
250+
Simple <span className="text-emerald-400">pricing</span>
251+
</h2>
252+
<p className="text-white/50 max-w-xl mx-auto">Start free. Upgrade when you need more.</p>
253+
</div>
254+
255+
<div className="grid md:grid-cols-2 gap-6 max-w-3xl mx-auto">
256+
<div className="relative p-8 rounded-3xl bg-white/3 border border-white/8 hover:border-white/20 transition-all">
257+
<h3 className="text-xl font-bold text-white mb-1">Free</h3>
258+
<p className="text-white/40 text-sm mb-6">Get started with the basics</p>
259+
<div className="text-4xl font-bold text-white mb-8">
260+
₹0 <span className="text-base font-normal text-white/40">forever</span>
261+
</div>
262+
<ul className="space-y-3">
263+
{[
264+
"Music streaming",
265+
"Create playlists",
266+
"Up to 2 members in groups",
267+
"3 songs in group queue",
268+
].map((f) => (
269+
<li key={f} className="flex items-center gap-2 text-sm text-white/60">
270+
<Check className="h-4 w-4 text-emerald-400 shrink-0" />
271+
{f}
272+
</li>
273+
))}
274+
</ul>
275+
</div>
276+
277+
<div className="relative p-8 rounded-3xl bg-white/3 border border-emerald-500/30 hover:border-emerald-500/50 transition-all">
278+
<div className="absolute -top-3 left-1/2 -translate-x-1/2 px-3 py-1 rounded-full bg-emerald-500 text-xs font-semibold text-black flex items-center gap-1">
279+
<Crown className="h-3 w-3" /> Popular
280+
</div>
281+
<h3 className="text-xl font-bold text-white mb-1 flex items-center gap-2">
282+
<Zap className="h-5 w-5 text-yellow-500" /> PRO
283+
</h3>
284+
<p className="text-white/40 text-sm mb-6">Unlock everything</p>
285+
<div className="text-4xl font-bold text-white mb-8">
286+
₹299 <span className="text-base font-normal text-white/40">/year</span>
287+
</div>
288+
<ul className="space-y-3">
289+
{[
290+
"Everything in Free",
291+
"Up to 10 members in groups",
292+
"50 songs in group queue",
293+
"Real-time group chat",
294+
"Priority support",
295+
].map((f) => (
296+
<li key={f} className="flex items-center gap-2 text-sm text-white/60">
297+
<Check className="h-4 w-4 text-emerald-400 shrink-0" />
298+
{f}
299+
</li>
300+
))}
301+
</ul>
302+
<Link to="/plans">
303+
<Button className="w-full mt-8 h-12 rounded-xl bg-white text-black hover:bg-white/90 font-semibold">
304+
Upgrade to PRO
305+
<ArrowRight className="ml-2 h-4 w-4" />
306+
</Button>
307+
</Link>
308+
</div>
309+
</div>
310+
</div>
311+
</section>
312+
))
313+
242314
const FinalCTA = memo(() => (
243315
<section className="py-24 px-6">
244316
<div className="max-w-3xl mx-auto text-center">
@@ -278,6 +350,7 @@ const Home = () => {
278350
<Hero />
279351
<Features />
280352
<MobileApp />
353+
<Pricing />
281354
<Suspense
282355
fallback={
283356
<div className="h-32 flex items-center justify-center">

client/src/components/LandingPage/PrivacyPolicy.jsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const PrivacyPolicy = () => {
77
<div className="max-w-4xl mx-auto px-6 py-14 lg:px-8 shadow-lg rounded-lg">
88
<header className="border-b pb-6 mb-6">
99
<h1 className="text-3xl font-bold ">Privacy Policy</h1>
10-
<p className="mt-2 text-gray-600">Last Updated: January 10, 2025</p>
10+
<p className="mt-2 text-gray-600">Last Updated: February 15, 2026</p>
1111
</header>
1212

1313
<main className="space-y-6 leading-relaxed">
@@ -29,7 +29,12 @@ const PrivacyPolicy = () => {
2929
</li>
3030
<li>
3131
<strong>Usage Data:</strong> Information about how you interact with our website,
32-
such as pages visited and features used.
32+
such as pages visited, features used, and music listening history.
33+
</li>
34+
<li>
35+
<strong>Payment Information:</strong> Transaction details processed through
36+
Razorpay. We do not store your payment card details — all payment data is handled
37+
securely by Razorpay.
3338
</li>
3439
</ul>
3540
</section>
@@ -40,6 +45,8 @@ const PrivacyPolicy = () => {
4045
<ul className="list-disc list-inside space-y-2 mt-2">
4146
<li>To provide and maintain the SyncVibe platform.</li>
4247
<li>To improve user experience and add new features.</li>
48+
<li>To process payments and manage your subscription.</li>
49+
<li>To personalize your music recommendations.</li>
4350
<li>To communicate with you about updates, promotions, and support.</li>
4451
</ul>
4552
</section>
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
const RefundPolicy = () => {
2+
document.title = "Refund Policy - SyncVibe"
3+
window.scrollTo(0, 0)
4+
5+
return (
6+
<div className="py-10">
7+
<div className="max-w-4xl mx-auto px-6 py-14 lg:px-8 shadow-lg rounded-lg">
8+
<header className="border-b pb-6 mb-6">
9+
<h1 className="text-3xl font-bold ">Refund & Cancellation Policy</h1>
10+
<p className="mt-2 text-gray-600">Last Updated: February 15, 2026</p>
11+
</header>
12+
13+
<main className="space-y-6 leading-relaxed">
14+
<section>
15+
<h2 className="text-xl font-semibold ">1. No Refunds</h2>
16+
<p className="mt-2">
17+
All purchases made on SyncVibe, including PRO plan subscriptions, are{" "}
18+
<strong>final and non-refundable</strong>. Once a payment has been successfully
19+
processed, no refunds will be issued under any circumstances.
20+
</p>
21+
<p className="mt-2">
22+
By completing a purchase, you acknowledge and agree that you have reviewed the
23+
features and benefits of the plan before making the payment, and you accept this
24+
no-refund policy.
25+
</p>
26+
</section>
27+
28+
<section>
29+
<h2 className="text-xl font-semibold ">2. Cancellation of Subscription</h2>
30+
<p className="mt-2">
31+
SyncVibe PRO is a one-time purchase and does not auto-renew. There is no recurring
32+
subscription to cancel. Your PRO access remains active for the duration of the
33+
purchased plan period.
34+
</p>
35+
</section>
36+
37+
<section>
38+
<h2 className="text-xl font-semibold ">3. Duplicate Payments</h2>
39+
<p className="mt-2">
40+
If you are charged more than once for the same purchase due to a technical error,
41+
please contact us within 7 days of the transaction. We will investigate and process a
42+
refund for the duplicate charge only.
43+
</p>
44+
</section>
45+
46+
<section>
47+
<h2 className="text-xl font-semibold ">4. Failed Transactions</h2>
48+
<p className="mt-2">
49+
If a payment fails or is not completed, no charge will be applied to your account. If
50+
your account was debited but the transaction was not completed on our end, please
51+
contact us with your transaction details, and we will resolve the issue within 5-7
52+
business days.
53+
</p>
54+
</section>
55+
56+
<section>
57+
<h2 className="text-xl font-semibold ">5. Payment Gateway</h2>
58+
<p className="mt-2">
59+
All payments are processed securely through Razorpay. SyncVibe does not store your
60+
payment card details. For any payment-related disputes, you may also contact
61+
Razorpay's support directly.
62+
</p>
63+
</section>
64+
65+
<section>
66+
<h2 className="text-xl font-semibold ">6. Service Modifications</h2>
67+
<p className="mt-2">
68+
SyncVibe reserves the right to modify, suspend, or discontinue any features or
69+
services at any time. In such cases, no refunds will be issued for the remaining
70+
duration of any active plan.
71+
</p>
72+
</section>
73+
74+
<section>
75+
<h2 className="text-xl font-semibold ">7. Contact Us</h2>
76+
<p className="mt-2">
77+
If you have any questions about this Refund & Cancellation Policy or need assistance
78+
with a payment issue, please contact us at{" "}
79+
<a href="mailto:info@thakur.dev" className="text-blue-600 hover:underline">
80+
info@thakur.dev
81+
</a>
82+
.
83+
</p>
84+
</section>
85+
</main>
86+
</div>
87+
</div>
88+
)
89+
}
90+
91+
export default RefundPolicy

0 commit comments

Comments
 (0)