feat(clerk-js,shared,ui): Add seats info to payment attempts page#8527
feat(clerk-js,shared,ui): Add seats info to payment attempts page#8527aeliox wants to merge 3 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: caaf316 The changes in this PR will be included in the next version bump. This PR includes changesets to release 20 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughThis PR introduces seat-based billing totals to payment attempts. It adds BillingPaymentTotals JSON and typed contracts, implements billingPaymentTotalsFromJSON with tests, extends the BillingPayment resource to include a totals field, adds utilities to extract and summarize seat charges (with tests), refactors the payment-attempt UI to render a conditional "Seats" line item, and adds a localization string and changeset note. Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@packages/ui/src/components/PaymentAttempts/PaymentAttemptPage.tsx`:
- Around line 232-243: The seats description currently always renders a
quantity×rate string; update the JSX in the PaymentAttemptPage component so that
when seatSummary.paidTier.quantity === null (an unlimited paid tier) you do not
render the "× {feePerBlock}" piece and instead only show the tier total,
otherwise keep the existing "{used} × {feePerBlock}" behavior; specifically
adjust the LineItems.Title description expression (using seatSummary,
seatSummary.paidTier.quantity, seatSummary.paidTier.feePerBlock and
seatSummary.paidTier.total) to branch on paidTier.quantity and produce the
appropriate string for unlimited vs. limited tiers.
🪄 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: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: 1cd96259-918b-49c3-9ce0-6aa6a4195339
📒 Files selected for processing (9)
.changeset/billing-seat-tier-rows-payment-attempt.mdpackages/clerk-js/src/core/resources/BillingPayment.tspackages/clerk-js/src/utils/__tests__/billing.test.tspackages/clerk-js/src/utils/billing.tspackages/shared/src/types/billing.tspackages/shared/src/types/json.tspackages/ui/src/components/PaymentAttempts/PaymentAttemptPage.tsxpackages/ui/src/utils/__tests__/billingPlanSeats.test.tspackages/ui/src/utils/billingPlanSeats.ts
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/hono
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/react
@clerk/react-router
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/ui
@clerk/upgrade
@clerk/vue
commit: |
| : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
| subscriptionItem.plan.annualMonthlyFee!; | ||
|
|
||
| const seatsTotal = subscriptionItem.seats != null ? getSeatsPerUnitTotal(paymentAttempt.totals) : undefined; |
There was a problem hiding this comment.
I think in this situation a null value in seats represents "Unlimited seats".
| return seatSummary.included > 0 | ||
| ? `${seatSummary.used} used − ${seatSummary.included} included → ${seatLabel} at ${rate}` | ||
| : `${seatLabel} at ${rate}`; |
There was a problem hiding this comment.
This should be done through the localization system.
| return null; | ||
| export type SeatChargeSummary = { | ||
| /** Sum of `quantity` across all tiers (paid + included). */ | ||
| used: number; |
There was a problem hiding this comment.
I think calling this "used" is misleading. This number represents the number of seats an organization was charged for, not how many seats are used. Maybe "total" instead since it represents both included and purchased seats?
|
|
||
| return seatUnitPrice.tiers[seatUnitPrice.tiers.length - 1]?.endsAfterBlock; | ||
| const lastTier = seatUnitPrice.tiers[seatUnitPrice.tiers.length - 1]; | ||
| return lastTier.endsAfterBlock != null ? lastTier.endsAfterBlock * seatUnitPrice.blockSize : null; |
| const seatLabel = `${seatsChargeable} ${seatsChargeable === 1 ? 'seat' : 'seats'}`; | ||
| const rate = `${seatSummary.paidTier.feePerBlock.currencySymbol}${seatSummary.paidTier.feePerBlock.amountFormatted}/mo`; | ||
| return seatSummary.included > 0 | ||
| ? `${seatSummary.used} used − ${seatSummary.included} included → ${seatLabel} at ${rate}` |
There was a problem hiding this comment.
I think a comma instead of an arrow here would probably be better (especially since people will be writing this string in their localizations):
5 total - 2 included, 3 seats at $10.00/mo
| * | ||
| * Per-payment cost breakdown including optional base fee and per-unit (for example, seats) subtotals. | ||
| */ | ||
| export interface BillingPaymentTotalsJSON { |
There was a problem hiding this comment.
I think this is actually our CommerceTotalsResponse type, which is shared between checkout, invoices, and payments, rather than a payments specific shape. I have one in my checkout PR, so we can probably just land this as-is in your PR, and then I'll update it to use the same type as checkout in mine. Just calling it out in case the checkout PR lands before this one.
| }, | ||
| reSubscribe: 'Resubscribe', | ||
| seats: 'Seats', | ||
| seatsWithLimit: 'Seats ({{limit}} limit)', |
There was a problem hiding this comment.
would it make sense to use our existing upToSeats localization value? It'd read a little different Up to X seats
Adds seat info (limits, usage, and costs) to the payment attempts page of an OrganizationProfile's billing section.