feat: revamp pricing page + add 4-year Pro plan#414
Conversation
|
@MistakenPirate is attempting to deploy a commit to the AJEET PRATAP SINGH's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds ChangesPlan Duration, Pricing, and Explore Features
Sequence Diagram(s)sequenceDiagram
participant PricingPage
participant PricingComparison
participant PlanColumnHeader
participant PaymentFlow
participant Router
PricingPage->>PricingComparison: render tiers and comparison rows
PricingComparison->>PlanColumnHeader: render each tier column
PlanColumnHeader->>Router: route free tier to /dashboard/home
PlanColumnHeader->>PaymentFlow: mount paid tier checkout when planId is available
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
#409 mentioning this |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (3)
apps/web/src/app/(main)/(landing)/pricing/page.tsx (1)
375-381: ⚡ Quick winReplace invisible spacer with proper CSS spacing.
The transparent paragraph
<p className="text-sm text-transparent select-none">.</p>is a layout hack that maintains vertical alignment. This breaks semantic HTML and may be announced by screen readers.♻️ Use min-height instead
-{isPaid && publicPlan ? ( - <p className="text-sm text-text-tertiary"> - {formatApproxPlanPrice(publicPlan.price, publicPlan.currency)} - </p> -) : ( - <p className="text-sm text-transparent select-none">.</p> -)} +<p className="text-sm min-h-[1.25rem] text-text-tertiary"> + {isPaid && publicPlan + ? formatApproxPlanPrice(publicPlan.price, publicPlan.currency) + : null} +</p>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/`(main)/(landing)/pricing/page.tsx around lines 375 - 381, The else branch containing the paragraph with text-transparent and select-none classes is a layout hack that serves as an invisible spacer to maintain vertical alignment. Remove this paragraph element entirely and instead add proper CSS spacing using min-height on the parent container or wrapper element that holds both the paid plan price display and the empty state. This will maintain the vertical alignment without relying on semantic HTML violations that can confuse screen readers.apps/api/prisma/seed.ts (1)
79-99: ⚡ Quick winSeed subscription bypasses
plan.durationMonths.The subscription end-date is hardcoded to
Date.now() + MILLIS_PER_YEAR, which ignores the plan'sdurationMonthsfield. While this happens to match the test plan (12 months), it creates a discrepancy with the payment service logic (which respectsdurationMonths) and would break if a different plan is used.♻️ Align seed logic with payment service
+// helper to match payment service logic +function computeSubscriptionEndDate(plan: { durationMonths: number }, startDate: Date): Date { + const endDate = new Date(startDate); + endDate.setMonth(endDate.getMonth() + plan.durationMonths); + return endDate; +} + const premiumSubscription = existingSubscription ? await prisma.subscription.update({ where: { id: existingSubscription.id }, data: { planId: testPlan.id, status: 'active', startDate: new Date(), - endDate: new Date(Date.now() + MILLIS_PER_YEAR), // 1 year from now + endDate: computeSubscriptionEndDate(testPlan, new Date()), autoRenew: true, }, }) : await prisma.subscription.create({ data: { userId: premiumUser.id, planId: testPlan.id, status: 'active', startDate: new Date(), - endDate: new Date(Date.now() + MILLIS_PER_YEAR), // 1 year from now + endDate: computeSubscriptionEndDate(testPlan, new Date()), autoRenew: true, }, });Note: If you adopt the date-fns fix from the payment service review, use that same approach here.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api/prisma/seed.ts` around lines 79 - 99, The endDate calculation in both the prisma.subscription.update and prisma.subscription.create calls is hardcoded to MILLIS_PER_YEAR, which ignores the testPlan.durationMonths field and creates a discrepancy with payment service logic. Replace the hardcoded Date.now() + MILLIS_PER_YEAR calculation with a dynamic calculation based on testPlan.durationMonths, converting the plan's duration in months to milliseconds and adding it to the current date. This ensures the seed data respects the plan's configured duration and remains consistent with the payment service behavior.apps/api/scripts/seed-pro-modules.ts (1)
114-118: ⚡ Quick winDev seed subscription bypasses
plan.durationMonths.Similar to the main seed, this subscription creation hardcodes a 1-year duration (
setFullYear(+1)) instead of readingplan.durationMonths. While it matches the current plan, it creates a discrepancy with the payment service and reduces seed fidelity.♻️ Use plan's durationMonths
if (!activeSub) { + const startDate = new Date(); const endDate = new Date(); - endDate.setFullYear(endDate.getFullYear() + 1); + endDate.setMonth(endDate.getMonth() + plan.durationMonths); await prisma.subscription.create({ - data: { userId: user.id, planId: plan.id, status: "active", endDate }, + data: { userId: user.id, planId: plan.id, status: "active", startDate, endDate }, });Note: If you adopt the date-fns fix from the payment service review, use that same approach here.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api/scripts/seed-pro-modules.ts` around lines 114 - 118, The subscription creation in the prisma.subscription.create call is hardcoding a 1-year duration by using setFullYear(endDate.getFullYear() + 1) instead of respecting the plan's durationMonths property. Replace the hardcoded 1-year calculation with logic that calculates the endDate by adding plan.durationMonths to the current date. If date-fns is available and used elsewhere in the codebase, use the same date manipulation approach for consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/api/src/services/payment.service.ts`:
- Around line 311-318: The endDate calculation uses setMonth() which causes date
overflow when subscriptions start on month-end days (e.g., Jan 31 + 1 month
becomes Mar 2 instead of Feb 28/29). Replace the
endDate.setMonth(endDate.getMonth() + durationMonths) logic with proper date
arithmetic that accounts for year wraparound and clamps the day to the last
valid day of the target month. Specifically, calculate the target year and month
by dividing the total months by 12 to handle year overflow, normalize the month
value using modulo arithmetic, set the full year/month/day on endDate, then
check if the resulting month differs from the expected month and call setDate(0)
to clamp to the last day of the previous month if needed.
In `@apps/web/src/app/`(main)/(landing)/pricing/page.tsx:
- Around line 308-428: Replace all hardcoded hex color values in the PlanCard
component with Tailwind design tokens. Specifically, update the gradient colors
in the badge element (from-[`#7150E7`] to-[`#432ba0`]), the shineColor array prop
passed to the ShineBorder component (which contains `#7150E7`, `#C89BFF`, `#432BA0`),
and the shadow value in the main div's className (rgba(113,80,231,0.55)).
Consult your design token system or Tailwind configuration to identify the
correct semantic color classes or CSS custom properties that correspond to these
purple and violet tones, then replace each hardcoded value with its appropriate
token equivalent.
- Around line 147-298: Replace all hardcoded hex color values in the pricing
page component with Tailwind design tokens. Specifically: replace all instances
of `#252525` border colors with the `border-border` or `border-border-DEFAULT`
token; for the purple gradient colors (`#a472ea` and `#432ba0` used in the h1
element's span, and the Link components), and the glow color (`#7150E7`), first
verify if these are intentional brand colors and add them to design-tokens.ts if
needed, otherwise replace them with the existing official brand purple tokens;
ensure no hardcoded hex values remain in className attributes and instead use
semantic Tailwind color classes throughout the entire component structure.
---
Nitpick comments:
In `@apps/api/prisma/seed.ts`:
- Around line 79-99: The endDate calculation in both the
prisma.subscription.update and prisma.subscription.create calls is hardcoded to
MILLIS_PER_YEAR, which ignores the testPlan.durationMonths field and creates a
discrepancy with payment service logic. Replace the hardcoded Date.now() +
MILLIS_PER_YEAR calculation with a dynamic calculation based on
testPlan.durationMonths, converting the plan's duration in months to
milliseconds and adding it to the current date. This ensures the seed data
respects the plan's configured duration and remains consistent with the payment
service behavior.
In `@apps/api/scripts/seed-pro-modules.ts`:
- Around line 114-118: The subscription creation in the
prisma.subscription.create call is hardcoding a 1-year duration by using
setFullYear(endDate.getFullYear() + 1) instead of respecting the plan's
durationMonths property. Replace the hardcoded 1-year calculation with logic
that calculates the endDate by adding plan.durationMonths to the current date.
If date-fns is available and used elsewhere in the codebase, use the same date
manipulation approach for consistency.
In `@apps/web/src/app/`(main)/(landing)/pricing/page.tsx:
- Around line 375-381: The else branch containing the paragraph with
text-transparent and select-none classes is a layout hack that serves as an
invisible spacer to maintain vertical alignment. Remove this paragraph element
entirely and instead add proper CSS spacing using min-height on the parent
container or wrapper element that holds both the paid plan price display and the
empty state. This will maintain the vertical alignment without relying on
semantic HTML violations that can confuse screen readers.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 016084e6-28c3-4e73-a9e9-04fa9ebdf71c
⛔ Files ignored due to path filters (1)
apps/web/public/assets/jackedaj.jpgis excluded by!**/*.jpg
📒 Files selected for processing (6)
apps/api/prisma/migrations/20260621140000_add_plan_duration_months/migration.sqlapps/api/prisma/schema.prismaapps/api/prisma/seed.tsapps/api/scripts/seed-pro-modules.tsapps/api/src/services/payment.service.tsapps/web/src/app/(main)/(landing)/pricing/page.tsx
|
@MistakenPirate nice work! some feedbacks: pricing cards should be like this
also the 99 card should have all the features of 49 mentioned and those two extra (49 one should have red cross sign before those two) and same for the free card.
as it's more of the ui work @Aryan-205 you can take over from here |
|
damn @Aryan-205 i was banging my head thinking how to fit such a long copy in the plan layout. lol just had to add a scroll. funny how you miss the simple stuff so easily. I've got much to learn about design |
|
@Aryan-205 yes the one with table with opensox theme and colors |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/components/features/explore-features-browser-mockup.tsx`:
- Around line 27-79: The BrowserMockup component is using hardcoded hex colors
instead of design tokens, which breaks the shared styling contract. Update the
color classes in explore-features-browser-mockup.tsx to use tailwind
semantic/design-token classes for the browser chrome, window buttons, address
bar, body background, borders, and overlay accents. Keep the existing structure
in the component and replace the direct hex values near FeatureIcon, the browser
header/body wrappers, and the fallback illustration elements with token-based
classes only.
- Around line 21-23: The fallback state in explore-features-browser-mockup is
getting stuck because imageError is never cleared when imageSrc changes. Update
the component’s useState/useEffect logic in the explore-features-browser-mockup
component so that whenever imageSrc changes, imageError is reset to false before
rendering or loading the next image. Use the existing imageError, showFallback,
and setImageError symbols to locate and adjust the behavior.
In `@apps/web/src/components/features/explore-features-dashboard.tsx`:
- Around line 172-177: The pricing CTA in explore-features-dashboard is nesting
PrimaryButton (a real button from custom-button) inside Link, which creates
invalid interactive markup; update the CTA to use router-based navigation in the
client component instead of wrapping the button with Link. Locate the
explore-features-dashboard component and replace the nested Link/PrimaryButton
pattern with a click handler that uses the Next.js router push for "/pricing",
keeping PrimaryButton as the only interactive element.
In `@apps/web/src/components/features/explore-features-image-preview.tsx`:
- Around line 37-45: The preview image is being marked with unconditional
priority inside the rotating explore-feature image component, which can
over-prioritize non-critical assets; update the Image usage in
explore-features-image-preview’s image-rendering path to remove the always-on
priority flag and only enable it for truly critical/LCP cases if needed. Keep
the existing Image props and error handling intact, but make priority
conditional or omit it for the auto-advancing preview state.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 36444529-ef6b-4056-95b8-03760ea4bf7b
⛔ Files ignored due to path filters (13)
apps/web/public/assets/features/10updates-n-opportunities.pngis excluded by!**/*.pngapps/web/public/assets/features/11prorefs.pngis excluded by!**/*.pngapps/web/public/assets/features/1onboardingcall.pngis excluded by!**/*.pngapps/web/public/assets/features/2community.pngis excluded by!**/*.pngapps/web/public/assets/features/3weeklysessions.pngis excluded by!**/*.pngapps/web/public/assets/features/4unlimitedqna.pngis excluded by!**/*.pngapps/web/public/assets/features/5promodules.pngis excluded by!**/*.pngapps/web/public/assets/features/6weeklycontests.pngis excluded by!**/*.pngapps/web/public/assets/features/7hand-pickedossprojects.pngis excluded by!**/*.pngapps/web/public/assets/features/8prorecordings.pngis excluded by!**/*.pngapps/web/public/assets/features/9pvtthreads.pngis excluded by!**/*.pngapps/web/public/assets/features/pro-preview.svgis excluded by!**/*.svgpnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (6)
apps/web/src/app/(main)/(landing)/pricing/page.tsxapps/web/src/components/features/explore-features-browser-mockup.tsxapps/web/src/components/features/explore-features-dashboard.tsxapps/web/src/components/features/explore-features-data.tsapps/web/src/components/features/explore-features-image-preview.tsxapps/web/src/components/features/features.tsx
✅ Files skipped from review due to trivial changes (1)
- apps/web/src/components/features/features.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/src/app/(main)/(landing)/pricing/page.tsx
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/web/src/app/(main)/(landing)/pricing/page.tsx (2)
3-13: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winremove the unused pricing imports.
trpcandformatApproxPlanPriceare no longer referenced after the pricing query was removed, so these imports should be deleted.proposed cleanup
-import { trpc } from "`@/lib/trpc`"; -import { formatApproxPlanPrice } from "`@/lib/format-plan-price`";As per coding guidelines, “Remove unused imports”.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/`(main)/(landing)/pricing/page.tsx around lines 3 - 13, Remove the unused pricing imports from the pricing page module. In the `page.tsx` import block, delete the references to `trpc` and `formatApproxPlanPrice` since they are no longer used after the pricing query removal; keep the remaining imports in place and ensure no other identifiers in `PricingPage` or related hooks/components are affected.Source: Coding guidelines
132-137: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winupdate the stale pricing source-of-truth comment.
The page no longer calls
getPublicPlanor renders the≈ ₹…line, so this comment now describes behavior that is not present. Either remove it or replace it with the current contract: display labels are local, while checkout charges byplanId.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/`(main)/(landing)/pricing/page.tsx around lines 132 - 137, The pricing page comment is stale because it still references getPublicPlan and the “≈ ₹…” line, which are no longer part of this flow. Update or remove the note in pricing/page.tsx near the pricing card content so it matches the current contract: the displayed price labels are local copy, and the actual checkout amount is determined by planId during checkout.Source: Coding guidelines
🧹 Nitpick comments (1)
apps/web/src/app/(main)/(landing)/pricing/page.tsx (1)
196-200: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winmove the mask and blur styling into tailwind utilities.
This inline
styleblock violates the app-level rule to style html elements with Tailwind classes instead of inline CSS.As per coding guidelines, “Always use Tailwind classes for styling HTML elements; avoid using CSS or style tags”.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/`(main)/(landing)/pricing/page.tsx around lines 196 - 200, Move the inline mask and blur styling in the pricing page component into Tailwind utility classes instead of the current style object. Update the relevant JSX in the landing pricing page so the element using the mask/blur effect is styled through Tailwind classes only, preserving the same visual effect while removing the inline CSS from the component.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/app/`(main)/(landing)/pricing/page.tsx:
- Around line 346-352: The unavailable-feature icon in the pricing page still
uses the muted text color, but it should use the project’s semantic
danger/destructive token instead. Update the `X` icon styling in the pricing UI
rendering logic so excluded features render with the requested red cross
indicator, keeping the existing `value === false` branch and replacing the
current muted gray Tailwind class with the appropriate destructive color token.
- Around line 480-523: The pricing comparison is currently built from generic
divs, so assistive technologies cannot reliably map each feature to its plan
column and row. Update the comparison section in the pricing page to use
semantic table markup (table, thead, tbody, tr, th, td) or equivalent table
roles, and ensure the feature names and plan headers are exposed as labeled
row/column headers so FeatureCell entries are associated with the correct plan;
keep the existing comparisonFeatures rendering but move it into the semantic
table structure around PlanColumns, PlanColumnHeader, and FeatureCell.
- Around line 156-158: The pricing copy for the 4-year plan is using the wrong
label and comparison price. Update the plan entry in the pricing page data so
the Pro Plus tier matches the agreed `Pro+` naming and uses `$149` as
`originalPrice`, and verify any rendering logic that displays this plan reads
from that same plan object.
- Around line 416-417: The checkout flow is hardcoding the same plan name for
both paid tiers, which causes Pro+ to be mislabeled. Update the pricing page
checkout props so the selected tier’s actual name is passed through instead of a
fixed "Opensox Pro" value, using the relevant tier data in the pricing component
that renders the checkout action. Make sure the planName sent alongside planId
reflects the chosen tier in all paid-tier cases.
---
Outside diff comments:
In `@apps/web/src/app/`(main)/(landing)/pricing/page.tsx:
- Around line 3-13: Remove the unused pricing imports from the pricing page
module. In the `page.tsx` import block, delete the references to `trpc` and
`formatApproxPlanPrice` since they are no longer used after the pricing query
removal; keep the remaining imports in place and ensure no other identifiers in
`PricingPage` or related hooks/components are affected.
- Around line 132-137: The pricing page comment is stale because it still
references getPublicPlan and the “≈ ₹…” line, which are no longer part of this
flow. Update or remove the note in pricing/page.tsx near the pricing card
content so it matches the current contract: the displayed price labels are local
copy, and the actual checkout amount is determined by planId during checkout.
---
Nitpick comments:
In `@apps/web/src/app/`(main)/(landing)/pricing/page.tsx:
- Around line 196-200: Move the inline mask and blur styling in the pricing page
component into Tailwind utility classes instead of the current style object.
Update the relevant JSX in the landing pricing page so the element using the
mask/blur effect is styled through Tailwind classes only, preserving the same
visual effect while removing the inline CSS from the component.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c5664c44-5c2b-4a25-a597-2e1c258ae035
📒 Files selected for processing (3)
apps/web/src/app/(main)/(landing)/pricing/page.tsxapps/web/src/components/ui/custom-button.tsxapps/web/tailwind.config.ts
✅ Files skipped from review due to trivial changes (2)
- apps/web/tailwind.config.ts
- apps/web/src/components/ui/custom-button.tsx
- use semantic table roles so screen readers map each feature to its plan - pass the actual tier name to checkout so Pro+ isn't labelled as Pro - use the destructive token for the excluded-feature cross
3c365a9 to
2c5f32c
Compare
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|








Summary by CodeRabbit
New Features
Bug Fixes
Chores