Skip to content

feat(site): add pricing page#7708

Merged
mhartington merged 2 commits intomainfrom
feat-site-pricing
Mar 26, 2026
Merged

feat(site): add pricing page#7708
mhartington merged 2 commits intomainfrom
feat-site-pricing

Conversation

@mhartington
Copy link
Copy Markdown
Member

@mhartington mhartington commented Mar 26, 2026

Summary by CodeRabbit

  • New Features
    • Launched a full pricing page with plan comparison table, rich-HTML FAQ accordion, and "Try Prisma Postgres" CTA section.
    • Added an interactive pricing calculator with presets, sliders, monthly/yearly toggle, detailed cost breakdowns, and enterprise recommendation.
    • Introduced multi-currency support with a currency selector across all plan displays.
    • Added hero pricing grid with highlighted plan, per-plan action buttons, and external "Pay via" option for select plans.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
blog Ready Ready Preview, Comment Mar 26, 2026 7:17pm
docs Ready Ready Preview, Comment Mar 26, 2026 7:17pm
eclipse Ready Ready Preview, Comment Mar 26, 2026 7:17pm
site Ready Ready Preview, Comment Mar 26, 2026 7:17pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

Walkthrough

Adds a complete pricing section with five new files: page metadata and layout, a pricing data module, a hero plans component with currency selection, a client-side pricing calculator, and a small content wrapper to coordinate currency state across children.

Changes

Cohort / File(s) Summary
Page & Content wrapper
apps/site/src/app/pricing/page.tsx, apps/site/src/app/pricing/pricing-page-content.tsx
New Next.js pricing page exporting static metadata and default PricingPage. PricingPageContent manages currency state and composes hero + calculator components.
Pricing data & helpers
apps/site/src/app/pricing/pricing-data.ts
New exports: currency symbols and types, plans, usagePricing, comparisonSections, faqs, planOrder, planActions, and helpers formatAmountForAllCurrencies and renderPlanPoint. Contains HTML strings used with dangerouslySetInnerHTML.
Interactive UI components
apps/site/src/app/pricing/pricing-hero-plans.tsx, apps/site/src/app/pricing/pricing-calculator.tsx
Client-side React components: PricingHeroPlans (currency select, plan grid, highlights, renders plan points as HTML) and PricingCalculator (presets, sliders, billing toggle, cost breakdown, plan recommendation, expandable plan details).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the primary change: adding a new pricing page to the site application with relevant components and data structures.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@argos-ci
Copy link
Copy Markdown

argos-ci bot commented Mar 26, 2026

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ✅ No changes detected - Mar 26, 2026, 7:49 PM

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
apps/site/src/app/pricing/page.tsx (1)

42-42: Consider renaming SiteHome to match the route intent.

Using a route-specific name like PricingPage improves readability and future maintenance.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/pricing/page.tsx` at line 42, Rename the default exported
React component function SiteHome in page.tsx to a route-specific name like
PricingPage: change the function declaration and the default export to
PricingPage, and update any imports/usages referencing SiteHome elsewhere in the
codebase to the new name to keep the route intent clear and maintainable.
apps/site/src/app/pricing/pricing-data.ts (1)

42-52: convertToAllCurrencies is misnamed and invites wrong usage.

This helper currently formats the same numeric amount with different symbols; it does not convert amounts between currencies. Please rename it (or add real conversion) to avoid incorrect assumptions in future updates.

💡 Suggested rename (no behavior change)
-export function convertToAllCurrencies(
+export function formatAmountForAllCurrencies(
   amount: number,
   digits: number,
 ): CurrencyMap {
@@
-    price: convertToAllCurrencies(0, 0),
+    price: formatAmountForAllCurrencies(0, 0),
@@
-        price: convertToAllCurrencies(0.008, 4),
+        price: formatAmountForAllCurrencies(0.008, 4),
@@
-        price: convertToAllCurrencies(2, 2),
+        price: formatAmountForAllCurrencies(2, 2),
@@
-    price: convertToAllCurrencies(10, 0),
+    price: formatAmountForAllCurrencies(10, 0),
@@
-        price: convertToAllCurrencies(0.002, 4),
+        price: formatAmountForAllCurrencies(0.002, 4),
@@
-        price: convertToAllCurrencies(1.5, 2),
+        price: formatAmountForAllCurrencies(1.5, 2),
@@
-    price: convertToAllCurrencies(49, 0),
+    price: formatAmountForAllCurrencies(49, 0),
@@
-        price: convertToAllCurrencies(0.001, 4),
+        price: formatAmountForAllCurrencies(0.001, 4),
@@
-        price: convertToAllCurrencies(1, 2),
+        price: formatAmountForAllCurrencies(1, 2),
@@
-    price: convertToAllCurrencies(129, 0),
+    price: formatAmountForAllCurrencies(129, 0),

Also applies to: 81-133

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/pricing/pricing-data.ts` around lines 42 - 52, The function
convertToAllCurrencies is misnamed because it only formats the same numeric
amount with different currency symbols rather than performing currency
conversion; rename the function to a clearer name (e.g.,
formatAmountWithCurrencySymbols or formatForAllCurrencySymbols) and update its
export and all callers to use the new name, keeping the same signature (amount:
number, digits: number) and return type CurrencyMap; also rename any other
similar helpers in this file (the related block around the other helper
occurrences) to maintain consistent naming and avoid future misuse of symbols
and symbols variable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/site/src/app/pricing/page.tsx`:
- Around line 57-59: The "Use Prisma Postgres" button in page.tsx is rendered
without any click behavior; replace or augment the <button> element (the one
with text "Use Prisma Postgres") so it actually navigates or performs an action:
either wrap it in Next.js' <Link> to the intended route or add an onClick that
calls router.push(...) using useRouter from 'next/navigation' (or
window.location.href for external URLs). Ensure the handler or Link points to
the correct target (signup, docs, or product route) and preserves the existing
classes ("rounded-full bg-background-ppg-reverse px-4 py-2 text-base
font-sans-display text-white").

In `@apps/site/src/app/pricing/pricing-calculator.tsx`:
- Around line 61-118: The PLAN_COPY object and related formatting functions
(formatCurrency, formatCompactCurrency, getPlanDescription) are hard-coded and
diverge from the canonical pricing data used elsewhere; replace the local
PLAN_COPY constants with values sourced from the shared pricing data model
(import the central pricing/pricingConfig or equivalent), and update
getPlanDescription to reference that imported model for operationsPrice,
operationsSuffix, storageIncludedGb, and storagePrice so the calculator uses the
same source of truth (also update any repeated constants used around lines
385-394 to use the same imported model). Ensure you keep the existing formatting
helpers but have them accept pricing fields from the shared model rather than
local hard-coded values.
- Around line 82-90: The code currently applies a flat 0.75 multiplier for
yearly pricing which conflicts with the "tiered on annual" label; replace the
single multiplier with explicit tiered-annual pricing logic: add a function
(e.g., calculateTieredAnnualCost) that computes annual costs by applying tiered
discounts per-operations and per-storage tiers using the plan fields
(operationsPrice, storagePrice, storageIncludedGb, operationsSuffix/title
identifiers) and use that function wherever the literal 0.75 multiplier is
currently applied (search for the 0.75 usage in this file) so the displayed
operationsSuffix "/M ops (tiered on annual)" accurately reflects the computed
tiered annual total; update any code paths that format/display the annual price
to call the new tiered calculation.

In `@apps/site/src/app/pricing/pricing-data.ts`:
- Around line 170-172: The two rows labeled "Cache tag invalidations" and "Cache
purge requests" currently contain compliance/support tier values (e.g.,
"Community", "GDPR", "HIPAA", "SOC2") which is incorrect; replace those arrays
so each contains cache-related availability values for each plan (e.g., "No",
"Yes (manual)", "Yes (auto)", "Yes (instant)" or plan-specific limits) and move
the compliance values into the appropriate "Compliance" or "Data protection" row
instead; locate the two arrays that start with the exact strings "Cache tag
invalidations" and "Cache purge requests" in pricing-data.ts and update their
per-plan entries to reflect cache behavior rather than compliance tiers.

---

Nitpick comments:
In `@apps/site/src/app/pricing/page.tsx`:
- Line 42: Rename the default exported React component function SiteHome in
page.tsx to a route-specific name like PricingPage: change the function
declaration and the default export to PricingPage, and update any imports/usages
referencing SiteHome elsewhere in the codebase to the new name to keep the route
intent clear and maintainable.

In `@apps/site/src/app/pricing/pricing-data.ts`:
- Around line 42-52: The function convertToAllCurrencies is misnamed because it
only formats the same numeric amount with different currency symbols rather than
performing currency conversion; rename the function to a clearer name (e.g.,
formatAmountWithCurrencySymbols or formatForAllCurrencySymbols) and update its
export and all callers to use the new name, keeping the same signature (amount:
number, digits: number) and return type CurrencyMap; also rename any other
similar helpers in this file (the related block around the other helper
occurrences) to maintain consistent naming and avoid future misuse of symbols
and symbols variable.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3d9e4931-203c-4dd3-978b-d93b1db90b6e

📥 Commits

Reviewing files that changed from the base of the PR and between edda9c3 and 175c0e8.

⛔ Files ignored due to path filters (1)
  • apps/site/public/og/og-pricing.png is excluded by !**/*.png
📒 Files selected for processing (5)
  • apps/site/src/app/pricing/page.tsx
  • apps/site/src/app/pricing/pricing-calculator.tsx
  • apps/site/src/app/pricing/pricing-data.ts
  • apps/site/src/app/pricing/pricing-hero-plans.tsx
  • apps/site/src/app/pricing/pricing-page-content.tsx

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (5)
apps/site/src/app/pricing/pricing-calculator.tsx (3)

21-23: Custom cn utility is fine, but consider using an existing solution.

This is a common pattern. If @prisma/eclipse or your codebase already exports a cn utility (often from tailwind-merge or clsx), you could avoid the duplication. Otherwise, this implementation works correctly for simple class concatenation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/pricing/pricing-calculator.tsx` around lines 21 - 23, The
custom classnames helper function cn(...classes) duplicates common utilities;
replace its local implementation by importing and using the shared utility
(e.g., a exported cn or a wrapper around clsx/tailwind-merge) if available in
the codebase or `@prisma/eclipse`, otherwise keep as-is; locate the local function
named cn in pricing-calculator.tsx and swap its definition for an import of the
shared cn/clsx/tailwind-merge wrapper to avoid duplication and ensure consistent
class merging across the app.

529-538: Remove commented-out code before merging.

This commented block appears to be placeholder code for a future "Compute Size" feature. Dead code clutters the codebase and can cause confusion. Either implement it, remove it, or track it in an issue.

🧹 Remove the commented block
 <div className="">
-  {/* <div className="space-y-2">
-    <div className="text-sm text-foreground-neutral">Compute Size</div>
-    <div className="rounded-[12px] border border-stroke-neutral bg-background-neutral px-3 py-3 text-sm text-foreground-neutral-weaker">
-      Included and auto-scaled by Prisma Postgres
-    </div>
-    <p className="m-0 text-[10px] leading-4 text-foreground-neutral-weaker">
-      vCPU, RAM, cores, micro, xl, C-3PO... etc.
-    </p>
-  </div> */}
-
   <div className="space-y-2">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/pricing/pricing-calculator.tsx` around lines 529 - 538,
Remove the commented-out JSX block for the "Compute Size" placeholder in
pricing-calculator.tsx (the multi-line comment containing "Compute Size",
"Included and auto-scaled by Prisma Postgres", and the vCPU/RAM note) — delete
that dead code from the component or, if you want to keep the idea, create a
tracked issue and move the content to that issue instead of leaving it commented
in the file.

265-265: Unnecessary use of dangerouslySetInnerHTML for plain text.

The description prop comes from getPlanDescription(), which returns a plain text string (no HTML tags). Using dangerouslySetInnerHTML here is overkill and triggers security warnings unnecessarily.

♻️ Simplified approach
-<p className="m-0 max-w-[277px] text-xs leading-4 text-foreground-neutral-weaker" dangerouslySetInnerHTML={{ __html: description }} />
+<p className="m-0 max-w-[277px] text-xs leading-4 text-foreground-neutral-weaker">
+  {description}
+</p>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/pricing/pricing-calculator.tsx` at line 265, The paragraph
is using dangerouslySetInnerHTML unnecessarily for plain text from
getPlanDescription(); replace the element that uses dangerouslySetInnerHTML={{
__html: description }} with a normal React child (e.g., <p
...>{description}</p>) so the text is rendered safely and without security
warnings; locate the usage in pricing-calculator.tsx (the description variable)
and remove dangerouslySetInnerHTML while preserving the existing className and
props.
apps/site/src/app/pricing/page.tsx (1)

65-85: Consider sourcing plan labels from the shared pricing data.

The plan labels ["Free", "Starter", "Pro", "Business"] are hardcoded here, but similar plan information likely exists in pricing-data.ts. This creates a maintenance risk if plan names change in one place but not the other.

♻️ Suggested approach

Import and derive the labels from your pricing data source:

+import { planOrder, plans } from "./pricing-data";
 ...
-{["Free", "Starter", "Pro", "Business"].map((label) => (
+{planOrder.map((planKey) => (
   <TableHead
-    key={label}
+    key={planKey}
     className="bg-background-neutral-weak text-left text-background-neutral-weak"
   >
     <Badge
       size="lg"
       className="rounded-md"
       color={
-        label === "Pro"
+        planKey === "pro"
           ? "ppg"
-          : label === "Starter"
+          : planKey === "starter"
             ? "orm"
-            : label === "Business"
+            : planKey === "business"
               ? "warning"
               : "neutral"
       }
-      label={label}
+      label={plans[planKey].title}
     />
   </TableHead>
 ))}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/pricing/page.tsx` around lines 65 - 85, Replace the
hardcoded plan labels array with the canonical list from the shared pricing data
source (pricing-data.ts) so the header generation uses the same single source of
truth; locate the map that renders TableHead and Badge (the JSX mapping over
["Free","Starter","Pro","Business"] in page.tsx), import the exported plans
array or function from pricing-data.ts (e.g., pricingPlans or getPricingData)
and map over that to derive label (plan.name) and color logic, updating any
references to label === "Pro"/"Starter"/"Business" to use the plan identifier or
name from the pricing data to keep coloring and labels consistent.
apps/site/src/app/pricing/pricing-hero-plans.tsx (1)

131-134: Minor: Unusual spacing pattern.

The { ' ' } on line 132 is an unconventional way to add a space. This works but is a bit awkward to read.

🧹 Cleaner alternative
 <span className="text-2xl text-foreground-neutral-weak">
-   { ' ' } / month
+   {" / month"}
 </span>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/pricing/pricing-hero-plans.tsx` around lines 131 - 134, The
JSX contains an unconventional explicit token { ' ' } inside the <span> in
pricing-hero-plans.tsx; replace that with a cleaner space token such as {" "} or
include the space directly in the surrounding string (e.g., " / month") to make
the JSX readable and consistent — edit the <span> in the PricingHeroPlans
component (or the JSX block containing the text "/ month") to remove { ' ' } and
use one of these cleaner alternatives.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/site/src/app/pricing/page.tsx`:
- Around line 65-85: Replace the hardcoded plan labels array with the canonical
list from the shared pricing data source (pricing-data.ts) so the header
generation uses the same single source of truth; locate the map that renders
TableHead and Badge (the JSX mapping over ["Free","Starter","Pro","Business"] in
page.tsx), import the exported plans array or function from pricing-data.ts
(e.g., pricingPlans or getPricingData) and map over that to derive label
(plan.name) and color logic, updating any references to label ===
"Pro"/"Starter"/"Business" to use the plan identifier or name from the pricing
data to keep coloring and labels consistent.

In `@apps/site/src/app/pricing/pricing-calculator.tsx`:
- Around line 21-23: The custom classnames helper function cn(...classes)
duplicates common utilities; replace its local implementation by importing and
using the shared utility (e.g., a exported cn or a wrapper around
clsx/tailwind-merge) if available in the codebase or `@prisma/eclipse`, otherwise
keep as-is; locate the local function named cn in pricing-calculator.tsx and
swap its definition for an import of the shared cn/clsx/tailwind-merge wrapper
to avoid duplication and ensure consistent class merging across the app.
- Around line 529-538: Remove the commented-out JSX block for the "Compute Size"
placeholder in pricing-calculator.tsx (the multi-line comment containing
"Compute Size", "Included and auto-scaled by Prisma Postgres", and the vCPU/RAM
note) — delete that dead code from the component or, if you want to keep the
idea, create a tracked issue and move the content to that issue instead of
leaving it commented in the file.
- Line 265: The paragraph is using dangerouslySetInnerHTML unnecessarily for
plain text from getPlanDescription(); replace the element that uses
dangerouslySetInnerHTML={{ __html: description }} with a normal React child
(e.g., <p ...>{description}</p>) so the text is rendered safely and without
security warnings; locate the usage in pricing-calculator.tsx (the description
variable) and remove dangerouslySetInnerHTML while preserving the existing
className and props.

In `@apps/site/src/app/pricing/pricing-hero-plans.tsx`:
- Around line 131-134: The JSX contains an unconventional explicit token { ' ' }
inside the <span> in pricing-hero-plans.tsx; replace that with a cleaner space
token such as {" "} or include the space directly in the surrounding string
(e.g., " / month") to make the JSX readable and consistent — edit the <span> in
the PricingHeroPlans component (or the JSX block containing the text "/ month")
to remove { ' ' } and use one of these cleaner alternatives.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7a9ef49b-7928-45e9-864a-1e50311b96a3

📥 Commits

Reviewing files that changed from the base of the PR and between 175c0e8 and a89d85e.

📒 Files selected for processing (4)
  • apps/site/src/app/pricing/page.tsx
  • apps/site/src/app/pricing/pricing-calculator.tsx
  • apps/site/src/app/pricing/pricing-data.ts
  • apps/site/src/app/pricing/pricing-hero-plans.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/site/src/app/pricing/pricing-data.ts

@mhartington mhartington merged commit 6f79795 into main Mar 26, 2026
18 checks passed
@mhartington mhartington deleted the feat-site-pricing branch March 26, 2026 19:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants