Skip to content

feat: Add BillingSettingsThree & i18n Multi-Currency Support#362

Closed
CodenRust wants to merge 16 commits intododopayments:mainfrom
CodenRust:main
Closed

feat: Add BillingSettingsThree & i18n Multi-Currency Support#362
CodenRust wants to merge 16 commits intododopayments:mainfrom
CodenRust:main

Conversation

@CodenRust
Copy link
Copy Markdown

@CodenRust CodenRust commented Feb 14, 2026

Summary

Added BillingSettingsThree component and global i18n/multi-currency support. Removed the Analytics Dashboard to streamline the codebase.

Changes

  • Added BillingSettingsThree: User-centric billing dashboard with spend tracking, balance display, and payment method management (minimalist design, no animations)
  • Added i18n Provider: Global BillingProvider with real-time currency conversion using Frankfurter API
  • Added Documentation: Interactive demos and comprehensive guides for BillingSettingsThree and i18n-provider

Screenshots/Recordings (if UI)

2026-02-14.12-05-27.1.1.mp4

How to Test

Steps to validate locally:

  1. npm ci
  2. npm run typecheck
  3. npm run build
  4. npm run dev - Navigate to docs to verify new components render correctly
  5. Verify Analytics Dashboard is removed from sidebar and returns 404

Checklist

  • Title is clear and descriptive
  • Related issue linked (if any)
  • Tests or manual verification steps included
  • CI passes (typecheck & build)
  • Docs updated (if needed)

Summary by CodeRabbit

  • New Features

    • Added Billing Settings 3: wallet balance, spend tracking, payment methods management, and transaction history.
    • Added Customer Portal: themeable multi-tab account portal (overview, subscription, payments, history, profile, notifications, security).
    • Added i18n BillingProvider with currency conversion and formatting, plus interactive currency demo.
  • Documentation

    • New docs and demos for Billing Settings 3, i18n provider, and Customer Portal.

@vercel
Copy link
Copy Markdown

vercel bot commented Feb 14, 2026

@CodenRust is attempting to deploy a commit to the Dodo Payments Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 14, 2026

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an i18n BillingProvider, two new UI components (BillingSettingsThree, CustomerPortal) with demos, docs, and registry entries; and updates many barrel exports to narrow or reformat public type exports.

Changes

Cohort / File(s) Summary
i18n Provider
src/lib/i18n-provider.tsx, src/registry/billingsdk/demo/i18n-demo.tsx, content/docs/i18n-provider.mdx, public/r/i18n-provider.json
New BillingProvider context: locale/currency state, STATIC_RATES + Frankfurter fetch, convert/formatCurrency utilities, useBilling hook, and a demo + docs. Registry and docs added.
BillingSettingsThree
src/registry/billingsdk/billing-settings-three.tsx, src/components/billingsdk/billing-settings-three.tsx, src/registry/billingsdk/demo/billing-settings-three-demo.tsx, content/docs/components/billing-settings-three/index.mdx, public/r/billing-settings-3.json
New BillingSettingsThree component with balance/spend cards, payment methods (add/delete/set-default), transactions, currency formatting, theme variants, demo and docs; registry entries added.
CustomerPortal
src/registry/billingsdk/customer-portal.tsx, src/components/billingsdk/customer-portal.tsx, src/registry/billingsdk/demo/customer-portal-demo.tsx, src/components/customer-portal-demo.tsx
New CustomerPortal component: responsive sidebar, mobile header, tabbed navigation, placeholders per tab, two demo variants (classic/minimal).
Registry & Navigation
registry.json, public/r/registry.json, content/docs/meta.json
Added registry blocks for billing-settings-3 and i18n-provider; updated documentation navigation (added i18n-provider, swapped billing-settings entries).
Barrel export surface changes
src/components/billingsdk/* (multiple files, see diff)
Many re-export adjustments: several modules had props/type re-exports removed or reordered (narrowing public type surface or formatting export lists). Review consumers relying on exported types.
Demos & Docs
src/registry/.../demo/*, content/docs/*, content/docs/components/*
Added demo pages and MDX docs for new components and provider; small doc formatting tweaks elsewhere.

Sequence Diagram(s)

sequenceDiagram
    participant App as App Component
    participant BP as BillingProvider (useBilling)
    participant API as Frankfurter API
    participant Component as BillingSettingsThree / UI

    App->>BP: Wrap app with BillingProvider
    BP->>BP: Init locale/currency and STATIC_RATES
    BP->>API: Fetch latest FX rates
    API-->>BP: Return rates (or error)
    BP->>Component: provide formatCurrency/convert via context
    Component->>BP: call formatCurrency(amount, {from})
    BP-->>Component: return converted & localized string
Loading
sequenceDiagram
    participant User as User
    participant CP as CustomerPortal
    participant State as React State
    participant Child as Tab Content

    User->>CP: Render portal with user data
    CP->>State: init activeTab, mobile menu state
    User->>CP: Click nav item
    CP->>State: set activeTab, close mobile menu
    State-->>CP: re-render
    CP->>Child: render active tab content (or placeholder)
    Child-->>User: display content
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • tsahil01
  • thepushkaraj

Poem

🐰 Hop, hop — a provider hums in tune,
New portals open beneath the moon,
Cards and currencies swirl and play,
Settings three hops in to stay,
I nibble docs and demo cake — hooray! 🍰

🚥 Pre-merge checks | ✅ 3 | ❌ 3
❌ Failed checks (1 warning, 2 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The PR description indicates 'Related issue linked (if any)' checkbox is unchecked, and no issue link is mentioned in the objectives or description. Link a related issue in the PR if this change addresses or relates to an existing issue; update the checkbox if no issue applies.
Out of Scope Changes check ❓ Inconclusive Most changes are in-scope (new BillingSettingsThree, i18n provider, docs), but numerous barrel export changes across multiple pricing/subscription modules appear tangential to the stated PR objectives. Clarify whether the export refactoring in billing-related modules (pricing tables, payment methods, etc.) is intentional and in-scope, or should be separated into a distinct cleanup PR.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat: Add BillingSettingsThree & i18n Multi-Currency Support' clearly and concisely summarizes the two main additions in the changeset: a new billing component and i18n multi-currency functionality.
Description check ✅ Passed The PR description includes all required template sections (Summary, Changes, Screenshots/Recordings, How to Test, Checklist) with substantive content for most sections, though CI pass status remains unconfirmed.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

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: 15

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
registry.json (1)

20-56: ⚠️ Potential issue | 🟠 Major

The all block doesn't include the new billing-settings-3 or i18n-provider.

New registry items should be referenced in the "all" meta-block so that @billingsdk/all installs the complete set.

Proposed fix
         "@billingsdk/payment-failure",
-        "@billingsdk/billing-screen"
+        "@billingsdk/billing-screen",
+        "@billingsdk/billing-settings-3",
+        "@billingsdk/i18n-provider"
       ]
🤖 Fix all issues with AI agents
In `@content/docs/components/billing-settings-three/index.mdx`:
- Around line 27-32: The doc text contradicts the PR description about
animations; update the component docs for BillingSettingsThree to be consistent
by clarifying whether the classic theme uses animated transitions or is strictly
no-animation: change the "Liquid Payment Methods" bullet and the "Minimalist
Aesthetic" bullet to reflect the authoritative behavior (use the component name
BillingSettingsThree and the prop minimal/onAddNew/addNewHref to guide readers),
e.g., state that liquid list uses animated transitions only when minimal is
false (or state "no animations" if PR dictates) and that minimal mode disables
all decorative/transition animations; ensure both bullets and any mention of
animations match the PR claim exactly.
- Around line 6-7: Remove the unused import by deleting the import statement
that references BillingSettingsThree; specifically remove or stop importing the
symbol BillingSettingsThree from
"@/components/billingsdk/billing-settings-three" since only
BillingSettingsThreeDemo is actually used in the MDX content and the static code
block does not require an actual import. Ensure only BillingSettingsThreeDemo
remains imported to avoid dead imports and linter warnings.

In `@content/docs/i18n-provider.mdx`:
- Around line 63-75: The example CustomPrice component is missing TypeScript
types for its props; update the function signature to explicitly type the props
(e.g., define an interface or inline type for amount as number) and annotate the
component return type if desired so CustomPrice({ amount }: { amount: number })
uses a typed amount passed into useBilling().formatCurrency; ensure references
to the useBilling import and the CustomPrice function name remain consistent.
- Around line 35-45: The RootLayout example omits TypeScript types for its
props; update the RootLayout signature to declare the children prop type (e.g.,
{ children: React.ReactNode } or React.PropsWithChildren) so the snippet is
TypeScript-safe—adjust imports if needed—and keep the existing BillingProvider
usage with defaultCurrency and defaultLocale unchanged.

In `@registry.json`:
- Around line 1061-1087: The registry entry for billing-settings-3 is missing
required registryDependencies: add "badge" (since the BillingSettingsThree
component imports Badge from "@/components/ui/badge") and add
"@billingsdk/i18n-provider" (since the component calls useBilling()/i18n
provider hooks); update the "registryDependencies" array for the
billing-settings-3 block to include both "badge" and "@billingsdk/i18n-provider"
so the component's imports resolve correctly.
- Around line 1105-1108: The registry entry's "dependencies" array currently
lists "lucide-react" and "motion" but neither src/lib/i18n-provider.tsx nor
src/registry/billingsdk/demo/i18n-demo.tsx import or use those packages; remove
"lucide-react" and "motion" from the registry.json dependencies array so only
actual imports (React, card/select/provider packages used by i18n-demo and
i18n-provider) remain, ensuring the registry entry matches the symbols
referenced by i18n-provider.tsx and i18n-demo.tsx.

In `@src/components/billingsdk/billing-settings-three.tsx`:
- Around line 3-4: Remove the unused imports from
src/components/billingsdk/billing-settings-three.tsx: delete
ComponentPropsWithoutRef, useEffect, useRef from the React import and remove
useInView, useMotionValue, useSpring from the motion/react import so the file
matches the registry copy; keep only useState and motion/AnimatePresence (or any
symbols actually used in this file) to satisfy ESLint unused-import rules.

In `@src/components/billingsdk/spend-summary.tsx`:
- Around line 24-26: The component currently prefixes totalSpend with a
hardcoded "$" which breaks i18n; update the render to use the billing formatter
instead (call useBilling().formatCurrency(totalSpend)) or accept a
formatCurrency callback prop and use that so the currency symbol and locale
match other components (refer to totalSpend, formatCurrency, and useBilling() in
spend-summary.tsx); ensure you replace the literal "$" prefix with the formatted
string returned by formatCurrency and keep prop-based option if you want the
component context-free.

In `@src/components/billingsdk/subscription-status-card.tsx`:
- Around line 35-38: The "Renews {renewalDate}" text is misleading for canceled
or past_due subscriptions; update the rendering in SubscriptionStatusCard to
choose the label based on the subscription status (e.g., if status ===
"canceled" show "Canceled on {renewalDate}", if status === "past_due" show
"Payment due {renewalDate}", otherwise show "Renews {renewalDate}"). Locate the
span that currently renders Calendar and Renews {renewalDate} and replace the
static text with a small conditional that selects the appropriate label using
the existing renewalDate and status variables so semantics match the
subscription state.
- Around line 32-34: The Badge currently shows "Inode" for any non-active status
in subscription-status-card.tsx; update the label logic in the component (where
Badge is rendered) to display meaningful status text instead of "Inode" — e.g.,
if status === "active" show "Active", else map known statuses like "canceled" ->
"Canceled", "past_due" or "past-due" -> "Past Due", etc., and fall back to a
capitalized version of the raw status string for unknown values; adjust the
Badge variant if needed to reflect destructive states.

In `@src/components/billingsdk/user-wallet-card.tsx`:
- Around line 50-54: The balance is being formatted without converting from the
card's currency; update the display in user-wallet-card.tsx to first
convert(balance, currency, currentCurrency) and then call formatCurrency on the
converted amount (similar to BillingSettingsThree). Locate the CardContent block
where formatCurrency(balance) is used and replace it with
formatCurrency(convert(balance, currency, currentCurrency)), ensuring you
reference the component props/variables named balance, currency, and
currentCurrency and keep any existing locale/format options intact.

In `@src/registry/billingsdk/billing-settings-three.tsx`:
- Around line 333-338: The status icon currently always renders CheckCircle2
inside the JSX block that uses tx.status to decide colors; change the rendering
to pick the icon based on tx.status (use CheckCircle2 for "completed", Clock for
"pending", and XCircle for "failed"), and keep or adjust the existing color
logic in the same wrapper (the div using cn and tx.status) so icon + background
reflect the actual state; update imports to include Clock and XCircle and
replace the single <CheckCircle2 /> usage with a conditional/icon-selection
expression tied to tx.status.
- Around line 3-4: Remove the unused imports from the top of the module: delete
ComponentPropsWithoutRef, useEffect, useRef from the React import and remove
useInView, useMotionValue, useSpring from the motion/react import so only
useState and the actually used motion/AnimatePresence remain; update the import
statements in billing-settings-three.tsx accordingly to avoid ESLint
unused-import errors and keep only the identifiers referenced in this file.
- Around line 169-176: The getCurrencySymbol function's replace regex doesn't
remove non-breaking spaces, so update its replacement to strip all digit
characters, ASCII/Unicode spaces and punctuation used in number formatting;
specifically change the regex used in the .replace call (referencing
getCurrencySymbol, currentCurrency and currency) to something like a
Unicode-aware character class that includes digits and whitespace (e.g., use a
regex with \p{Nd} and \p{Zs} or explicitly include \u00A0) and enable the u flag
so the currency symbol extraction removes NBSPs and other locale spaces.

In `@src/registry/billingsdk/customer-portal.tsx`:
- Around line 120-127: The mobile menu toggle Button is missing accessibility
attributes; update the Button (the component rendering the toggle using
isMobileMenuOpen and setIsMobileMenuOpen) to include
aria-expanded={isMobileMenuOpen} and a descriptive aria-label (e.g.,
aria-label={isMobileMenuOpen ? "Close menu" : "Open menu"}) so screen readers
can announce the open/closed state; ensure these props are passed to the Button
component that wraps the X and Menu icons referenced in this snippet.
🧹 Nitpick comments (6)
src/lib/i18n-provider.tsx (2)

67-80: Missing AbortController cleanup in the fetch effect.

If the component unmounts before the fetch completes, setRates will be called on an unmounted component. Add an abort signal to cancel the in-flight request on cleanup.

Proposed fix
     useEffect(() => {
+        const controller = new AbortController();
         const fetchRates = async () => {
             try {
-                const response = await fetch("https://api.frankfurter.dev/v1/latest?base=USD");
+                const response = await fetch("https://api.frankfurter.dev/v1/latest?base=USD", {
+                    signal: controller.signal,
+                });
                 const data = await response.json();
                 if (data.rates) {
                     setRates({ USD: 1, ...data.rates });
                 }
             } catch (error) {
-                console.error("Failed to fetch exchange rates:", error);
+                if (error instanceof DOMException && error.name === "AbortError") return;
+                console.error("Failed to fetch exchange rates:", error);
             }
         };
         fetchRates();
+        return () => controller.abort();
     }, []);

82-96: convert wraps a closure in useMemo — consider useCallback for clarity.

useMemo returning a function is functionally identical to useCallback, but useCallback better communicates intent. Same applies to formatCurrency on lines 98–113. This is a minor readability nit.

src/registry/billingsdk/billing-settings-three.tsx (1)

31-36: Card logos depend on an external third-party CDN (img.icons8.com).

If icons8 goes down or changes URLs, card logos will break silently (falling back to the generic icon). Consider bundling these small images as local assets or using inline SVGs for reliability. The onError fallback mitigates the impact, but this is still a runtime dependency on a third party for core UI.

src/registry/billingsdk/customer-portal.tsx (2)

174-176: Conflicting flex and hidden utilities on the same element.

Line 174 has both flex and hidden at the base breakpoint. This works in practice because Tailwind generates hidden (display: none) after flex in the stylesheet, but it's fragile and non-idiomatic. Drop the base flex and keep hidden md:flex.

✏️ Suggested fix
-<header className="h-16 border-b flex items-center px-8 hidden md:flex">
+<header className="h-16 border-b items-center px-8 hidden md:flex">

129-134: Mobile sidebar overlay has no dismiss-on-Escape or backdrop click.

When isMobileMenuOpen is true, the sidebar becomes a fixed inset-0 z-50 overlay. Users can only close it via the X button. Consider adding an onKeyDown handler for Escape and/or a backdrop <div> that closes the menu on click for improved usability and accessibility.

content/docs/i18n-provider.mdx (1)

24-29: Feature descriptions are accurate and well-organized.

The four features (real-time conversion, static fallbacks, localized formatting, auto-conversion) align with the BillingProvider implementation. One minor note: "caches them" on line 26 could be more precise—the rates are held in React state (in-memory for the provider's lifetime), not persisted to any durable cache. Consider rewording to "stores them in memory" if persistence is not planned.

Comment on lines +6 to +7
import { BillingSettingsThree } from "@/components/billingsdk/billing-settings-three";
import BillingSettingsThreeDemo from "@/registry/billingsdk/demo/billing-settings-three-demo";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

fd 'index.mdx' --full-path 'content/docs/components/billing-settings-three' --type f

Repository: dodopayments/billingsdk

Length of output: 123


🏁 Script executed:

cat -n content/docs/components/billing-settings-three/index.mdx

Repository: dodopayments/billingsdk

Length of output: 4943


Remove unused import: BillingSettingsThree is never referenced in the MDX body.

Only BillingSettingsThreeDemo is used in the Preview tab (line 16). The component is referenced in the usage code example block but that's static documentation content, not rendered JSX.

✏️ Proposed fix
-import { BillingSettingsThree } from "@/components/billingsdk/billing-settings-three";
 import BillingSettingsThreeDemo from "@/registry/billingsdk/demo/billing-settings-three-demo";
🤖 Prompt for AI Agents
In `@content/docs/components/billing-settings-three/index.mdx` around lines 6 - 7,
Remove the unused import by deleting the import statement that references
BillingSettingsThree; specifically remove or stop importing the symbol
BillingSettingsThree from "@/components/billingsdk/billing-settings-three" since
only BillingSettingsThreeDemo is actually used in the MDX content and the static
code block does not require an actual import. Ensure only
BillingSettingsThreeDemo remains imported to avoid dead imports and linter
warnings.

Comment on lines +27 to +32
- **Minimalist Metrics**: Clean, static display of wallet balances, spend amounts, and trend percentages for maximum stability and clarity.
- **Trend Indicators**: Built-in support for displaying positive/negative trends with automatic color coding and icons.
- **Liquid Payment Methods**: Smooth, animated "liquid" list for adding/removing payment methods with high-fidelity transitions.
- **Smart Brand Recognition**: Automatically displays high-quality icons for Visa, Mastercard, AMEX, and Rupay, with a fallback to standard card icons if external assets fail to load.
- **Flexible "Add New" Logic**: Support for both in-app modal callbacks (`onAddNew`) and direct external redirects (`addNewHref`).
- **Minimalist Aesthetic**: Features a zero-animation `minimal` mode that removes background decorative elements for a clean dashboard look.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Documentation contradicts PR description regarding animations.

Line 29 describes "Smooth, animated 'liquid' list… with high-fidelity transitions," while line 32 mentions "zero-animation minimal mode." The PR description explicitly states "no animations" for BillingSettingsThree. These claims are inconsistent—clarify whether the classic theme includes animations or if the feature descriptions should be corrected.

🤖 Prompt for AI Agents
In `@content/docs/components/billing-settings-three/index.mdx` around lines 27 -
32, The doc text contradicts the PR description about animations; update the
component docs for BillingSettingsThree to be consistent by clarifying whether
the classic theme uses animated transitions or is strictly no-animation: change
the "Liquid Payment Methods" bullet and the "Minimalist Aesthetic" bullet to
reflect the authoritative behavior (use the component name BillingSettingsThree
and the prop minimal/onAddNew/addNewHref to guide readers), e.g., state that
liquid list uses animated transitions only when minimal is false (or state "no
animations" if PR dictates) and that minimal mode disables all
decorative/transition animations; ensure both bullets and any mention of
animations match the PR claim exactly.

Comment on lines +35 to +45
```tsx title="app/layout.tsx"
import { BillingProvider } from "@/lib/i18n-provider";

export default function RootLayout({ children }) {
return (
<BillingProvider defaultCurrency="USD" defaultLocale="en-US">
{children}
</BillingProvider>
);
}
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Setup code example is missing TypeScript types.

The RootLayout example omits prop types for children, which is inconsistent with a TypeScript-oriented SDK. This is the first code snippet users will copy.

✏️ Suggested fix
-export default function RootLayout({ children }) {
+export default function RootLayout({ children }: { children: React.ReactNode }) {
🤖 Prompt for AI Agents
In `@content/docs/i18n-provider.mdx` around lines 35 - 45, The RootLayout example
omits TypeScript types for its props; update the RootLayout signature to declare
the children prop type (e.g., { children: React.ReactNode } or
React.PropsWithChildren) so the snippet is TypeScript-safe—adjust imports if
needed—and keep the existing BillingProvider usage with defaultCurrency and
defaultLocale unchanged.

Comment on lines +63 to +75
```tsx
import { useBilling } from "@/lib/i18n-provider";

export function CustomPrice({ amount }) {
const { formatCurrency } = useBilling();

return (
<p>
Trial ends in: {formatCurrency(amount, { from: "USD" })}
</p>
);
}
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Same missing types in the CustomPrice example.

✏️ Suggested fix
-export function CustomPrice({ amount }) {
+export function CustomPrice({ amount }: { amount: number }) {
🤖 Prompt for AI Agents
In `@content/docs/i18n-provider.mdx` around lines 63 - 75, The example CustomPrice
component is missing TypeScript types for its props; update the function
signature to explicitly type the props (e.g., define an interface or inline type
for amount as number) and annotate the component return type if desired so
CustomPrice({ amount }: { amount: number }) uses a typed amount passed into
useBilling().formatCurrency; ensure references to the useBilling import and the
CustomPrice function name remain consistent.

Comment on lines +1061 to +1087
{
"name": "billing-settings-3",
"type": "registry:block",
"title": "Billing Settings 3",
"description": "Premium billing dashboard with wallet balance and liquid UI payment methods",
"files": [
{
"path": "src/registry/billingsdk/billing-settings-three.tsx",
"type": "registry:component",
"target": "components/billingsdk/billing-settings-three.tsx"
},
{
"path": "src/registry/billingsdk/demo/billing-settings-three-demo.tsx",
"type": "registry:component",
"target": "components/billing-settings-three-demo.tsx"
}
],
"dependencies": [
"lucide-react",
"motion"
],
"registryDependencies": [
"button",
"card",
"utils"
]
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

billing-settings-3 registry block is missing badge in registryDependencies.

The BillingSettingsThree component imports and uses Badge from @/components/ui/badge (for the "Default" badge on payment methods). Add "badge" to registryDependencies. Also, since the component requires useBilling() from the i18n provider, consider adding "@billingsdk/i18n-provider" as a registry dependency.

Proposed fix
       "registryDependencies": [
         "button",
         "card",
+        "badge",
-        "utils"
+        "utils",
+        "@billingsdk/i18n-provider"
       ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
"name": "billing-settings-3",
"type": "registry:block",
"title": "Billing Settings 3",
"description": "Premium billing dashboard with wallet balance and liquid UI payment methods",
"files": [
{
"path": "src/registry/billingsdk/billing-settings-three.tsx",
"type": "registry:component",
"target": "components/billingsdk/billing-settings-three.tsx"
},
{
"path": "src/registry/billingsdk/demo/billing-settings-three-demo.tsx",
"type": "registry:component",
"target": "components/billing-settings-three-demo.tsx"
}
],
"dependencies": [
"lucide-react",
"motion"
],
"registryDependencies": [
"button",
"card",
"utils"
]
},
{
"name": "billing-settings-3",
"type": "registry:block",
"title": "Billing Settings 3",
"description": "Premium billing dashboard with wallet balance and liquid UI payment methods",
"files": [
{
"path": "src/registry/billingsdk/billing-settings-three.tsx",
"type": "registry:component",
"target": "components/billingsdk/billing-settings-three.tsx"
},
{
"path": "src/registry/billingsdk/demo/billing-settings-three-demo.tsx",
"type": "registry:component",
"target": "components/billing-settings-three-demo.tsx"
}
],
"dependencies": [
"lucide-react",
"motion"
],
"registryDependencies": [
"button",
"card",
"badge",
"utils",
"@billingsdk/i18n-provider"
]
},
🤖 Prompt for AI Agents
In `@registry.json` around lines 1061 - 1087, The registry entry for
billing-settings-3 is missing required registryDependencies: add "badge" (since
the BillingSettingsThree component imports Badge from "@/components/ui/badge")
and add "@billingsdk/i18n-provider" (since the component calls useBilling()/i18n
provider hooks); update the "registryDependencies" array for the
billing-settings-3 block to include both "badge" and "@billingsdk/i18n-provider"
so the component's imports resolve correctly.

Comment on lines +50 to +54
<CardContent>
<div className="flex items-end justify-between">
<div className="text-4xl font-bold tracking-tight">
{formatCurrency(balance)}
</div>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Balance is formatted without currency conversion.

formatCurrency(balance) displays the raw numeric value in the provider's current currency without converting from the card's currency prop. If balance is 1250 USD and the user switches to EUR, it will display €1,250.00 instead of the converted amount.

BillingSettingsThree handles this correctly by calling convert(balance, "USD", currentCurrency) before display. Apply the same pattern here:

Proposed fix
-    const { formatCurrency } = useBilling();
+    const { formatCurrency, convert, currency: currentCurrency } = useBilling();
 
     return (
         <Card className={cn(walletCardVariants({ theme }), className)}>
             {/* ... */}
             <CardContent>
                 <div className="flex items-end justify-between">
                     <div className="text-4xl font-bold tracking-tight">
-                        {formatCurrency(balance)}
+                        {formatCurrency(convert(balance, currency, currentCurrency))}
                     </div>
🤖 Prompt for AI Agents
In `@src/components/billingsdk/user-wallet-card.tsx` around lines 50 - 54, The
balance is being formatted without converting from the card's currency; update
the display in user-wallet-card.tsx to first convert(balance, currency,
currentCurrency) and then call formatCurrency on the converted amount (similar
to BillingSettingsThree). Locate the CardContent block where
formatCurrency(balance) is used and replace it with
formatCurrency(convert(balance, currency, currentCurrency)), ensuring you
reference the component props/variables named balance, currency, and
currentCurrency and keep any existing locale/format options intact.

Comment on lines +169 to +176
const getCurrencySymbol = () => {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: currentCurrency || currency,
})
.format(0)
.replace(/[0. \d]/g, "");
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

getCurrencySymbol regex may not strip non-breaking spaces.

Some locales (e.g., fr-FR) use non-breaking spaces (\u00A0) in formatted currency. The regex [0. \d] only matches ASCII space, leaving \u00A0 in the result.

Proposed fix
-            .replace(/[0. \d]/g, "");
+            .replace(/[\d\s.,\u00A0]/g, "").trim();
🤖 Prompt for AI Agents
In `@src/registry/billingsdk/billing-settings-three.tsx` around lines 169 - 176,
The getCurrencySymbol function's replace regex doesn't remove non-breaking
spaces, so update its replacement to strip all digit characters, ASCII/Unicode
spaces and punctuation used in number formatting; specifically change the regex
used in the .replace call (referencing getCurrencySymbol, currentCurrency and
currency) to something like a Unicode-aware character class that includes digits
and whitespace (e.g., use a regex with \p{Nd} and \p{Zs} or explicitly include
\u00A0) and enable the u flag so the currency symbol extraction removes NBSPs
and other locale spaces.

Comment on lines +333 to +338
<div className={cn(
"w-8 h-8 rounded-full flex items-center justify-center",
tx.status === "completed" ? "bg-emerald-500/10 text-emerald-500" : "bg-yellow-500/10 text-yellow-500"
)}>
<CheckCircle2 className="w-4 h-4" />
</div>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Transaction status icon doesn't differentiate between pending and failed.

CheckCircle2 is rendered for all statuses — only the background color changes between completed (green) and non-completed (yellow). A failed transaction showing a checkmark icon (even yellow) is misleading. Consider using distinct icons (e.g., Clock for pending, XCircle for failed).

🤖 Prompt for AI Agents
In `@src/registry/billingsdk/billing-settings-three.tsx` around lines 333 - 338,
The status icon currently always renders CheckCircle2 inside the JSX block that
uses tx.status to decide colors; change the rendering to pick the icon based on
tx.status (use CheckCircle2 for "completed", Clock for "pending", and XCircle
for "failed"), and keep or adjust the existing color logic in the same wrapper
(the div using cn and tx.status) so icon + background reflect the actual state;
update imports to include Clock and XCircle and replace the single <CheckCircle2
/> usage with a conditional/icon-selection expression tied to tx.status.

Comment on lines +120 to +127
<Button
variant="ghost"
size="icon"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
>
{isMobileMenuOpen ? <X className="size-5" /> : <Menu className="size-5" />}
</Button>
</div>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Mobile menu toggle lacks accessibility attributes.

The toggle button should communicate its state to assistive technology. Add aria-expanded and aria-label so screen readers can convey the sidebar's open/closed state.

♿ Proposed fix
 <Button
     variant="ghost"
     size="icon"
     onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
+    aria-expanded={isMobileMenuOpen}
+    aria-label={isMobileMenuOpen ? "Close menu" : "Open menu"}
 >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Button
variant="ghost"
size="icon"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
>
{isMobileMenuOpen ? <X className="size-5" /> : <Menu className="size-5" />}
</Button>
</div>
<Button
variant="ghost"
size="icon"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
aria-expanded={isMobileMenuOpen}
aria-label={isMobileMenuOpen ? "Close menu" : "Open menu"}
>
{isMobileMenuOpen ? <X className="size-5" /> : <Menu className="size-5" />}
</Button>
</div>
🤖 Prompt for AI Agents
In `@src/registry/billingsdk/customer-portal.tsx` around lines 120 - 127, The
mobile menu toggle Button is missing accessibility attributes; update the Button
(the component rendering the toggle using isMobileMenuOpen and
setIsMobileMenuOpen) to include aria-expanded={isMobileMenuOpen} and a
descriptive aria-label (e.g., aria-label={isMobileMenuOpen ? "Close menu" :
"Open menu"}) so screen readers can announce the open/closed state; ensure these
props are passed to the Button component that wraps the X and Menu icons
referenced in this snippet.

@tsahil01
Copy link
Copy Markdown
Member

@CodenRust build is failing -
Error: src/components/billingsdk/spend-summary.tsx(4,30): error TS2307: Cannot find module './revenue-chart' or its corresponding type declarations.

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: 7

🤖 Fix all issues with AI agents
In `@public/r/billing-settings-3.json`:
- Around line 6-7: The JSON is missing declared packages used by the component:
add "class-variance-authority" to the "dependencies" array (used via the cva
symbol), and add "badge" and "i18n-provider" to "registryDependencies" because
the component imports Badge (from "@/components/ui/badge") and calls useBilling
(from "@/lib/i18n-provider"); also ensure the consumer is signaled to include a
BillingProvider (or add a registry dependency that provides it) so useBilling()
won't throw at runtime.

In `@public/r/i18n-provider.json`:
- Around line 10-11: The fetch in the BillingProvider's useEffect (function
fetchRates) must check response.ok before calling response.json(); modify
fetchRates to do: await fetch(...); if (!response.ok) throw new Error(`HTTP
${response.status}`) (or similar) so non-2xx responses are handled by the
existing catch block and you don't attempt to parse an HTML error body—update
the error message logged in the catch in fetchRates if desired.
- Line 6: The i18n provider metadata wrongly lists unused packages; remove
"lucide-react" and "motion" from the "dependencies" array in
public/r/i18n-provider.json so the file only lists packages actually imported by
i18n-provider.tsx / i18n-demo.tsx; locate the "dependencies" entry in that JSON
and delete those two strings (or replace the array with only true runtime deps)
to avoid forcing unnecessary installs.

In `@public/r/registry.json`:
- Around line 904-923: The registry entry for "billing-settings-3" is missing
required dependencies; update its "dependencies" and/or "registryDependencies"
arrays in the JSON for the billing-settings-3 block so they include "badge",
"class-variance-authority", and "i18n-provider" (matching the individual
billing-settings-3.json), ensuring the "dependencies" and "registryDependencies"
fields in the billing-settings-3 registry object are kept in sync with the
component's manifest.
- Around line 924-943: The registry entry for "i18n-provider" incorrectly
includes unused dependencies "lucide-react" and "motion"; update the
registry.json block for this package by removing those two strings from the
"dependencies" array so it only lists actual packages used by
src/lib/i18n-provider.tsx and src/registry/billingsdk/demo/i18n-demo.tsx (which
rely on React and registry components like card/select), ensuring the
"dependencies" array reflects real imports and leaving "registryDependencies"
unchanged.

In `@src/components/billingsdk/billing-settings-three.tsx`:
- Around line 145-149: The code incorrectly assumes balance/spend are in USD by
calling convert(balance, "USD", ...); update the conversion to treat the
component prop currency as the source: call convert(balance, currency || "USD",
currentCurrency || currency || "USD") and similarly for spend (use the same
source/target logic), and replace manual getCurrencySymbol(...) + toFixed(2)
displays with the provider's formatCurrency(convertedBalance, currentCurrency ||
currency) and formatCurrency(convertedSpend, currentCurrency || currency) so
formatting is consistent; adjust useBilling usages (formatCurrency, convert) and
ensure convertedBalance/convertedSpend variable names remain aligned.

In `@src/components/billingsdk/user-wallet-card.tsx`:
- Around line 51-53: The liquid theme sub-component (UserWalletCard / billingsdk
user-wallet-card.tsx) still contains animation classes that contradict the PR
description "no animations": remove the animate-pulse class from the avatar
placeholder and remove/replace transition-all duration-300 and active:scale-95
on the card root (or add a clear comment if animations must remain) so the
component has no animations; update className in the main component (e.g., the
divs that include "animate-pulse", "transition-all duration-300", and
"active:scale-95") accordingly and ensure the change is reflected in any theme
variants referenced by BillingSettingsThree.
🧹 Nitpick comments (2)
src/components/billingsdk/user-wallet-card.tsx (1)

47-48: Hardcoded UI string "Wallet Balance" in an i18n-enabled component.

Given this PR introduces an i18n provider (BillingProvider), consider externalizing this string so it can be localized alongside currency formatting.

src/components/billingsdk/billing-settings-three.tsx (1)

152-169: useState initialized from props won't react to prop changes.

methods is seeded from initialPaymentMethods only on mount. If the parent re-renders with new payment methods (e.g. after a server fetch), the component ignores the update. The same applies to history (lines 171–195).

If props are the source of truth, derive state from them or use a key to reset the component. If internal mutation (delete/set-default) is the intent, consider syncing via useEffect or lifting state.

Comment on lines +6 to +7
"dependencies": ["lucide-react", "motion"],
"registryDependencies": ["button", "card", "utils"],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Missing dependencies and registry dependencies — consumer installs will break.

The component imports Badge (@/components/ui/badge), cva (class-variance-authority), and useBilling (@/lib/i18n-provider), but these are not declared:

Missing from Item
dependencies class-variance-authority
registryDependencies badge
registryDependencies i18n-provider (the component calls useBilling() and will throw without BillingProvider)
Proposed fix
-  "dependencies": ["lucide-react", "motion"],
-  "registryDependencies": ["button", "card", "utils"],
+  "dependencies": ["lucide-react", "motion", "class-variance-authority"],
+  "registryDependencies": ["button", "card", "badge", "i18n-provider", "utils"],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"dependencies": ["lucide-react", "motion"],
"registryDependencies": ["button", "card", "utils"],
"dependencies": ["lucide-react", "motion", "class-variance-authority"],
"registryDependencies": ["button", "card", "badge", "i18n-provider", "utils"],
🤖 Prompt for AI Agents
In `@public/r/billing-settings-3.json` around lines 6 - 7, The JSON is missing
declared packages used by the component: add "class-variance-authority" to the
"dependencies" array (used via the cva symbol), and add "badge" and
"i18n-provider" to "registryDependencies" because the component imports Badge
(from "@/components/ui/badge") and calls useBilling (from
"@/lib/i18n-provider"); also ensure the consumer is signaled to include a
BillingProvider (or add a registry dependency that provides it) so useBilling()
won't throw at runtime.

"name": "i18n-provider",
"title": "i18n Provider",
"description": "Global currency conversion and localized formatting provider",
"dependencies": ["lucide-react", "motion"],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Spurious dependencies: neither lucide-react nor motion is used.

The embedded i18n-provider.tsx and i18n-demo.tsx sources don't import lucide-react or motion. Listing them here forces consumers to install packages they don't need.

Proposed fix
-  "dependencies": ["lucide-react", "motion"],
+  "dependencies": [],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"dependencies": ["lucide-react", "motion"],
"dependencies": [],
🤖 Prompt for AI Agents
In `@public/r/i18n-provider.json` at line 6, The i18n provider metadata wrongly
lists unused packages; remove "lucide-react" and "motion" from the
"dependencies" array in public/r/i18n-provider.json so the file only lists
packages actually imported by i18n-provider.tsx / i18n-demo.tsx; locate the
"dependencies" entry in that JSON and delete those two strings (or replace the
array with only true runtime deps) to avoid forcing unnecessary installs.

Comment on lines +10 to +11
"path": "src/lib/i18n-provider.tsx",
"content": "\"use client\";\n\nimport React, { createContext, useContext, useMemo, useState, useEffect } from \"react\";\n\nexport type Locale = string;\nexport type Currency = string;\n\ninterface BillingContextType {\n locale: Locale;\n currency: Currency;\n rates: Record<string, number>;\n setLocale: (locale: Locale) => void;\n setCurrency: (currency: Currency) => void;\n formatCurrency: (amount: number | string, options?: { from?: Currency }) => string;\n convert: (amount: number, from: Currency, to: Currency) => number;\n}\n\nconst BillingContext = createContext<BillingContextType | undefined>(undefined);\n\nconst STATIC_RATES = {\n \"USD\": 1.0,\n \"AUD\": 1.4312,\n \"BRL\": 5.2372,\n \"CAD\": 1.3666,\n \"CHF\": 0.77794,\n \"CNY\": 6.939,\n \"CZK\": 20.553,\n \"DKK\": 6.3314,\n \"EUR\": 0.84789,\n \"GBP\": 0.73588,\n \"HKD\": 7.8135,\n \"HUF\": 320.46,\n \"IDR\": 16873,\n \"ILS\": 3.1301,\n \"INR\": 90.66,\n \"ISK\": 123.11,\n \"JPY\": 157.09,\n \"KRW\": 1467.42,\n \"MXN\": 17.3639,\n \"MYR\": 3.9475,\n \"NOK\": 9.7249,\n \"NZD\": 1.6681,\n \"PHP\": 58.544,\n \"PLN\": 3.5768,\n \"RON\": 4.3174,\n \"SEK\": 9.0499,\n \"SGD\": 1.2732,\n \"THB\": 31.6,\n \"TRY\": 43.615,\n \"ZAR\": 16.1243\n};\n\nexport function BillingProvider({\n children,\n defaultLocale = \"en-US\",\n defaultCurrency = \"USD\",\n}: {\n children: React.ReactNode;\n defaultLocale?: Locale;\n defaultCurrency?: Currency;\n}) {\n const [locale, setLocale] = useState<Locale>(defaultLocale);\n const [currency, setCurrency] = useState<Currency>(defaultCurrency);\n const [rates, setRates] = useState<Record<string, number>>(STATIC_RATES);\n\n // Fetch exchange rates from Frankfurter\n useEffect(() => {\n const fetchRates = async () => {\n try {\n const response = await fetch(\"https://api.frankfurter.dev/v1/latest?base=USD\");\n const data = await response.json();\n if (data.rates) {\n setRates({ USD: 1, ...data.rates });\n }\n } catch (error) {\n console.error(\"Failed to fetch exchange rates:\", error);\n }\n };\n fetchRates();\n }, []);\n\n const convert = useMemo(() => {\n return (amount: number, from: Currency, to: Currency) => {\n if (from === to) return amount;\n\n // Fallback: If rates aren't loaded or currency missing, return original amount\n if (!rates[from] && from !== \"USD\") return amount;\n if (!rates[to] && to !== \"USD\") return amount;\n\n // Convert to USD (base) first, then to target\n const amountInUSD = from === \"USD\" ? amount : amount / (rates[from] || 1);\n const targetAmount = to === \"USD\" ? amountInUSD : amountInUSD * (rates[to] || 1);\n\n return targetAmount;\n };\n }, [rates]);\n\n const formatCurrency = useMemo(() => {\n return (amount: number | string, options?: { from?: Currency }) => {\n let value = typeof amount === \"string\" ? parseFloat(amount) : amount;\n if (isNaN(value)) return amount.toString();\n\n // Auto-convert if 'from' is specified and different from current currency\n if (options?.from && options.from !== currency) {\n value = convert(value, options.from, currency);\n }\n\n return new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency: currency,\n }).format(value);\n };\n }, [locale, currency, convert]);\n\n const value = useMemo(\n () => ({\n locale,\n currency,\n rates,\n setLocale,\n setCurrency,\n formatCurrency,\n convert,\n }),\n [locale, currency, rates, formatCurrency, convert]\n );\n\n return (\n <BillingContext.Provider value={value}>{children}</BillingContext.Provider>\n );\n}\n\nexport function useBilling() {\n const context = useContext(BillingContext);\n if (context === undefined) {\n throw new Error(\"useBilling must be used within a BillingProvider\");\n }\n return context;\n}\n",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing response.ok check before parsing the Frankfurter API response.

In the embedded i18n-provider.tsx, the fetch call at https://api.frankfurter.dev/v1/latest?base=USD doesn't verify response.ok. A non-2xx response (e.g. 500 with an HTML body) will throw on response.json(), and the catch block only logs — silently falling back to stale static rates without informing the consumer. Add a guard:

if (!response.ok) throw new Error(`HTTP ${response.status}`);
🤖 Prompt for AI Agents
In `@public/r/i18n-provider.json` around lines 10 - 11, The fetch in the
BillingProvider's useEffect (function fetchRates) must check response.ok before
calling response.json(); modify fetchRates to do: await fetch(...); if
(!response.ok) throw new Error(`HTTP ${response.status}`) (or similar) so
non-2xx responses are handled by the existing catch block and you don't attempt
to parse an HTML error body—update the error message logged in the catch in
fetchRates if desired.

Comment on lines +904 to +923
{
"name": "billing-settings-3",
"type": "registry:block",
"title": "Billing Settings 3",
"description": "Premium billing dashboard with wallet balance and liquid UI payment methods",
"files": [
{
"path": "src/registry/billingsdk/billing-settings-three.tsx",
"type": "registry:component",
"target": "components/billingsdk/billing-settings-three.tsx"
},
{
"path": "src/registry/billingsdk/demo/billing-settings-three-demo.tsx",
"type": "registry:component",
"target": "components/billing-settings-three-demo.tsx"
}
],
"dependencies": ["lucide-react", "motion"],
"registryDependencies": ["button", "card", "utils"]
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

billing-settings-3 registry entry is missing required dependencies.

Same issue as in public/r/billing-settings-3.jsonbadge, class-variance-authority, and i18n-provider are missing. This manifest and the individual JSON must stay in sync.

Proposed fix
       "dependencies": ["lucide-react", "motion"],
-      "registryDependencies": ["button", "card", "utils"]
+      "dependencies": ["lucide-react", "motion", "class-variance-authority"],
+      "registryDependencies": ["button", "card", "badge", "i18n-provider", "utils"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
"name": "billing-settings-3",
"type": "registry:block",
"title": "Billing Settings 3",
"description": "Premium billing dashboard with wallet balance and liquid UI payment methods",
"files": [
{
"path": "src/registry/billingsdk/billing-settings-three.tsx",
"type": "registry:component",
"target": "components/billingsdk/billing-settings-three.tsx"
},
{
"path": "src/registry/billingsdk/demo/billing-settings-three-demo.tsx",
"type": "registry:component",
"target": "components/billing-settings-three-demo.tsx"
}
],
"dependencies": ["lucide-react", "motion"],
"registryDependencies": ["button", "card", "utils"]
},
{
"name": "billing-settings-3",
"type": "registry:block",
"title": "Billing Settings 3",
"description": "Premium billing dashboard with wallet balance and liquid UI payment methods",
"files": [
{
"path": "src/registry/billingsdk/billing-settings-three.tsx",
"type": "registry:component",
"target": "components/billingsdk/billing-settings-three.tsx"
},
{
"path": "src/registry/billingsdk/demo/billing-settings-three-demo.tsx",
"type": "registry:component",
"target": "components/billing-settings-three-demo.tsx"
}
],
"dependencies": ["lucide-react", "motion", "class-variance-authority"],
"registryDependencies": ["button", "card", "badge", "i18n-provider", "utils"]
},
🤖 Prompt for AI Agents
In `@public/r/registry.json` around lines 904 - 923, The registry entry for
"billing-settings-3" is missing required dependencies; update its "dependencies"
and/or "registryDependencies" arrays in the JSON for the billing-settings-3
block so they include "badge", "class-variance-authority", and "i18n-provider"
(matching the individual billing-settings-3.json), ensuring the "dependencies"
and "registryDependencies" fields in the billing-settings-3 registry object are
kept in sync with the component's manifest.

Comment on lines +924 to 943
{
"name": "i18n-provider",
"type": "registry:block",
"title": "i18n Provider",
"description": "Global currency conversion and localized formatting provider",
"files": [
{
"path": "src/lib/i18n-provider.tsx",
"type": "registry:lib",
"target": "lib/i18n-provider.tsx"
},
{
"path": "src/registry/billingsdk/demo/i18n-demo.tsx",
"type": "registry:component",
"target": "components/i18n-demo.tsx"
}
],
"dependencies": ["lucide-react", "motion"],
"registryDependencies": ["button", "card", "select", "utils"]
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

i18n-provider lists lucide-react and motion as dependencies but neither is used.

The provider (i18n-provider.tsx) only uses React, and the demo (i18n-demo.tsx) uses Card/Select UI components — no direct imports from lucide-react or motion.

Proposed fix
-      "dependencies": ["lucide-react", "motion"],
+      "dependencies": [],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
"name": "i18n-provider",
"type": "registry:block",
"title": "i18n Provider",
"description": "Global currency conversion and localized formatting provider",
"files": [
{
"path": "src/lib/i18n-provider.tsx",
"type": "registry:lib",
"target": "lib/i18n-provider.tsx"
},
{
"path": "src/registry/billingsdk/demo/i18n-demo.tsx",
"type": "registry:component",
"target": "components/i18n-demo.tsx"
}
],
"dependencies": ["lucide-react", "motion"],
"registryDependencies": ["button", "card", "select", "utils"]
}
{
"name": "i18n-provider",
"type": "registry:block",
"title": "i18n Provider",
"description": "Global currency conversion and localized formatting provider",
"files": [
{
"path": "src/lib/i18n-provider.tsx",
"type": "registry:lib",
"target": "lib/i18n-provider.tsx"
},
{
"path": "src/registry/billingsdk/demo/i18n-demo.tsx",
"type": "registry:component",
"target": "components/i18n-demo.tsx"
}
],
"dependencies": [],
"registryDependencies": ["button", "card", "select", "utils"]
}
🤖 Prompt for AI Agents
In `@public/r/registry.json` around lines 924 - 943, The registry entry for
"i18n-provider" incorrectly includes unused dependencies "lucide-react" and
"motion"; update the registry.json block for this package by removing those two
strings from the "dependencies" array so it only lists actual packages used by
src/lib/i18n-provider.tsx and src/registry/billingsdk/demo/i18n-demo.tsx (which
rely on React and registry components like card/select), ensuring the
"dependencies" array reflects real imports and leaving "registryDependencies"
unchanged.

Comment on lines +145 to +149
const { formatCurrency, currency: currentCurrency, convert } = useBilling();

// Convert values from USD if needed
const convertedBalance = convert(balance, "USD", currentCurrency || currency);
const convertedSpend = convert(spend, "USD", currentCurrency || currency);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Balance/spend always assumed to be in USD — currency prop is misleading.

convert(balance, "USD", ...) hardcodes the source currency. If a consumer passes balance={1250} currency="EUR", the value is still treated as USD. Additionally, the cards use manual getCurrencySymbol() + toFixed(2) formatting instead of the provider's formatCurrency(), creating an inconsistency with how transaction amounts are rendered (line 430).

Consider using formatCurrency consistently and documenting (or using) the currency prop as the source currency:

Proposed fix
-  const convertedBalance = convert(balance, "USD", currentCurrency || currency);
-  const convertedSpend = convert(spend, "USD", currentCurrency || currency);
+  const sourceCurrency = currency;
+  const targetCurrency = currentCurrency || currency;
+  const convertedBalance = convert(balance, sourceCurrency, targetCurrency);
+  const convertedSpend = convert(spend, sourceCurrency, targetCurrency);

And for display, use formatCurrency instead of manual symbol + toFixed:

-              <span className="mr-1 text-xl font-medium opacity-70">
-                {getCurrencySymbol()}
-              </span>
-              <span>{convertedBalance.toFixed(2)}</span>
+              <span>{formatCurrency(balance, { from: currency })}</span>
🤖 Prompt for AI Agents
In `@src/components/billingsdk/billing-settings-three.tsx` around lines 145 - 149,
The code incorrectly assumes balance/spend are in USD by calling
convert(balance, "USD", ...); update the conversion to treat the component prop
currency as the source: call convert(balance, currency || "USD", currentCurrency
|| currency || "USD") and similarly for spend (use the same source/target
logic), and replace manual getCurrencySymbol(...) + toFixed(2) displays with the
provider's formatCurrency(convertedBalance, currentCurrency || currency) and
formatCurrency(convertedSpend, currentCurrency || currency) so formatting is
consistent; adjust useBilling usages (formatCurrency, convert) and ensure
convertedBalance/convertedSpend variable names remain aligned.

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: 1

🤖 Fix all issues with AI agents
In `@src/components/billingsdk/billing-settings-three.tsx`:
- Line 362: The action-buttons container currently uses the class string "flex
items-center gap-2 opacity-0 transition-opacity group-hover:opacity-100" which
makes the "Make Default" and delete buttons invisible but still focusable; fix
this by either adding keyboard-focus visibility to the parent (e.g., add
group-focus-within:opacity-100 or group-focus:opacity-100 to that same class
string) or by adding focus:opacity-100 to the buttons themselves (the "Make
Default" and delete button elements) so they become visible when focused via
keyboard.

</div>
</div>

<div className="flex items-center gap-2 opacity-0 transition-opacity group-hover:opacity-100">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Action buttons with opacity-0 are invisible but still focusable — keyboard accessibility issue.

The "Make Default" and delete buttons use opacity-0 group-hover:opacity-100, so they're invisible until hover. However, they remain in the tab order, meaning keyboard users will focus on invisible buttons with no visual indication. Consider adding focus-within:opacity-100 on the group or focus:opacity-100 on the buttons themselves.

Proposed fix
-                    <div className="flex items-center gap-2 opacity-0 transition-opacity group-hover:opacity-100">
+                    <div className="flex items-center gap-2 opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100">
🤖 Prompt for AI Agents
In `@src/components/billingsdk/billing-settings-three.tsx` at line 362, The
action-buttons container currently uses the class string "flex items-center
gap-2 opacity-0 transition-opacity group-hover:opacity-100" which makes the
"Make Default" and delete buttons invisible but still focusable; fix this by
either adding keyboard-focus visibility to the parent (e.g., add
group-focus-within:opacity-100 or group-focus:opacity-100 to that same class
string) or by adding focus:opacity-100 to the buttons themselves (the "Make
Default" and delete button elements) so they become visible when focused via
keyboard.

Copy link
Copy Markdown
Member

@tsahil01 tsahil01 left a comment

Choose a reason for hiding this comment

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

Hey @CodenRust can you check this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@CodenRust We reuse these components in other files, so please check the other components in this directory.

You need to import them from the registry directory and then export them again from here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I feel this file is not being used anywhere, Can you tell whats the use of this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same for this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hey even this is not being used anywhere.

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: 2

🤖 Fix all issues with AI agents
In `@src/components/billingsdk/cancel-subscription-dialog.tsx`:
- Around line 1-2: The barrel currently only re-exports CancelSubscriptionDialog
so imports of CancelSubscriptionDialogProps break; update the export statement
to also re-export the type CancelSubscriptionDialogProps from the original
module (i.e. change the export in the CancelSubscriptionDialog barrel to include
both CancelSubscriptionDialog and type CancelSubscriptionDialogProps) so files
like subscription-management that import CancelSubscriptionDialogProps continue
to work.

In `@src/components/billingsdk/pricing-table-five.tsx`:
- Around line 1-2: The barrel file currently only re-exports the component
(export { PricingTableFive } from "@/registry/billingsdk/pricing-table-five")
and must also re-export the Props type to preserve the public API; update this
file to export the type alias (type PricingTableFiveProps) from the registry
implementation alongside the component export, e.g. add a matching export for
the type from "@/registry/billingsdk/pricing-table-five"; apply the same change
to the other pricing-table barrel files so each exports both the component
(PricingTableFive) and its Props type (PricingTableFiveProps).
🧹 Nitpick comments (1)
src/components/subscription-management-demo.tsx (1)

45-47: Unnecessary async on onKeepSubscription.

This callback contains no await and the prop type is (planId: string) => void. The async keyword is superfluous here — unlike onCancel, there's no Promise being constructed or awaited.

Suggested fix
-          onKeepSubscription: async (planId: string) => {
+          onKeepSubscription: (planId: string) => {
             console.log("keep subscription", planId);
           },

Comment on lines +1 to +2
"use client";
export {
CancelSubscriptionDialog,
type CancelSubscriptionDialogProps,
} from "@/registry/billingsdk/cancel-subscription-dialog";
export { CancelSubscriptionDialog } from "@/registry/billingsdk/cancel-subscription-dialog";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for imports of CancelSubscriptionDialogProps from the barrel module
rg "from.*cancel-subscription-dialog" -t ts -t tsx --max-count=20

Repository: dodopayments/billingsdk

Length of output: 94


🏁 Script executed:

#!/bin/bash
# Broader search for any references to CancelSubscriptionDialogProps
rg "CancelSubscriptionDialogProps" -t ts -t tsx -A 2 -B 2

Repository: dodopayments/billingsdk

Length of output: 94


🏁 Script executed:

#!/bin/bash
# Check the actual content of the cancel-subscription-dialog module to see what's exported
fd cancel-subscription-dialog --type f | head -20

Repository: dodopayments/billingsdk

Length of output: 410


🏁 Script executed:

#!/bin/bash
# Search for CancelSubscriptionDialogProps without type flags
rg "CancelSubscriptionDialogProps" --max-count=50

Repository: dodopayments/billingsdk

Length of output: 18597


🏁 Script executed:

#!/bin/bash
# Check imports from the barrel module specifically
rg "from.*cancel-subscription-dialog\"" --max-count=50

Repository: dodopayments/billingsdk

Length of output: 805


🏁 Script executed:

#!/bin/bash
# Read the barrel module file
cat -n src/components/billingsdk/cancel-subscription-dialog.tsx

Repository: dodopayments/billingsdk

Length of output: 187


🏁 Script executed:

#!/bin/bash
# Read the actual implementation module
cat -n src/registry/billingsdk/cancel-subscription-dialog.tsx

Repository: dodopayments/billingsdk

Length of output: 12122


The barrel module must re-export CancelSubscriptionDialogProps to prevent breaking imports.

src/registry/billingsdk/subscription-management.tsx imports CancelSubscriptionDialogProps from this barrel module. Removing or failing to re-export this type will break the import statement on line 12 of that file where it's used to type the cancelSubscription prop.

Add the type re-export to the barrel:

export { CancelSubscriptionDialog, type CancelSubscriptionDialogProps } from "@/registry/billingsdk/cancel-subscription-dialog";
🤖 Prompt for AI Agents
In `@src/components/billingsdk/cancel-subscription-dialog.tsx` around lines 1 - 2,
The barrel currently only re-exports CancelSubscriptionDialog so imports of
CancelSubscriptionDialogProps break; update the export statement to also
re-export the type CancelSubscriptionDialogProps from the original module (i.e.
change the export in the CancelSubscriptionDialog barrel to include both
CancelSubscriptionDialog and type CancelSubscriptionDialogProps) so files like
subscription-management that import CancelSubscriptionDialogProps continue to
work.

Comment on lines +1 to +2
"use client";
export { PricingTableFive } from "@/registry/billingsdk/pricing-table-five";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if any file in the repo imports the removed Props types
rg -n 'PricingTableFiveProps|PricingTableOneProps|PricingTableFourProps' --type=ts --type=tsx -g '!src/registry/**' -g '!src/components/billingsdk/pricing-table-*'

Repository: dodopayments/billingsdk

Length of output: 94


🏁 Script executed:

cat -n src/components/billingsdk/pricing-table-five.tsx

Repository: dodopayments/billingsdk

Length of output: 171


🏁 Script executed:

fd 'pricing-table.*\.tsx$' src/components/billingsdk/ | xargs -I {} sh -c 'echo "=== {} ===" && head -2 {}'

Repository: dodopayments/billingsdk

Length of output: 1110


🏁 Script executed:

# Try rg search without the tsx file type filter, or use correct flags
rg 'PricingTableFiveProps|PricingTableOneProps|PricingTableFourProps' --type ts -g '!src/registry/**' -g '!src/components/billingsdk/pricing-table-*'

Repository: dodopayments/billingsdk

Length of output: 49


🏁 Script executed:

# Check the registry files to see if Props types are exported there
fd 'pricing-table-five' src/registry/

Repository: dodopayments/billingsdk

Length of output: 170


🏁 Script executed:

# Check what is exported from one of the registry files
head -30 src/registry/billingsdk/pricing-table-five.tsx

Repository: dodopayments/billingsdk

Length of output: 1005


🏁 Script executed:

# Search for any exports in the barrel files including Props or types
rg 'export.*Props' src/components/billingsdk/

Repository: dodopayments/billingsdk

Length of output: 176


🏁 Script executed:

# Check if PricingTableFiveProps type exists in the registry implementation
rg 'type PricingTableFiveProps|interface PricingTableFiveProps' src/registry/billingsdk/pricing-table-five.tsx

Repository: dodopayments/billingsdk

Length of output: 127


🏁 Script executed:

# Check the git diff to see what was removed from this file
git diff HEAD src/components/billingsdk/pricing-table-five.tsx

Repository: dodopayments/billingsdk

Length of output: 49


🏁 Script executed:

# Check if similar pricing table components export their Props types
rg 'export.*type.*PricingTable.*Props' src/

Repository: dodopayments/billingsdk

Length of output: 49


🏁 Script executed:

# Check what is exported from the registry pricing-table-five.tsx
rg '^export' src/registry/billingsdk/pricing-table-five.tsx

Repository: dodopayments/billingsdk

Length of output: 162


🏁 Script executed:

# Search for imports of PricingTableFiveProps or similar Props types in the entire codebase
rg 'import.*PricingTableFiveProps|import.*PricingTableOneProps|import.*PricingTableFourProps'

Repository: dodopayments/billingsdk

Length of output: 50379


🏁 Script executed:

# Check the full export structure of the registry file
tail -20 src/registry/billingsdk/pricing-table-five.tsx

Repository: dodopayments/billingsdk

Length of output: 649


Export PricingTableFiveProps type from the barrel file to avoid breaking changes.

The Props interface is defined in the registry implementation but not re-exported from src/components/billingsdk/pricing-table-five.tsx. This breaks the public API for consumers importing the type from the barrel file. Other components in this codebase (e.g., Banner) export their Props types, so this pattern should be consistent. Add type PricingTableFiveProps to the export:

"use client";
export { PricingTableFive } from "@/registry/billingsdk/pricing-table-five";
+ export type { PricingTableFiveProps } from "@/registry/billingsdk/pricing-table-five";

Apply the same fix to the other pricing-table files.

🤖 Prompt for AI Agents
In `@src/components/billingsdk/pricing-table-five.tsx` around lines 1 - 2, The
barrel file currently only re-exports the component (export { PricingTableFive }
from "@/registry/billingsdk/pricing-table-five") and must also re-export the
Props type to preserve the public API; update this file to export the type alias
(type PricingTableFiveProps) from the registry implementation alongside the
component export, e.g. add a matching export for the type from
"@/registry/billingsdk/pricing-table-five"; apply the same change to the other
pricing-table barrel files so each exports both the component (PricingTableFive)
and its Props type (PricingTableFiveProps).

@tsahil01
Copy link
Copy Markdown
Member

Hey @CodenRust why are there 46 file changes? Is it required for billing settings 3?

Copy link
Copy Markdown
Member

@tsahil01 tsahil01 left a comment

Choose a reason for hiding this comment

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

@CodenRust can you expain why do we need to add these files?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@CodenRust why do we have to commit these files?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same for this

@tsahil01
Copy link
Copy Markdown
Member

@CodenRust apologies, but we’re unable to merge this PR at the moment as it contains several unnecessary changes.
Could you please review and update it accordingly? Thanks so much!

@tsahil01 tsahil01 marked this pull request as draft February 14, 2026 09:39
@tsahil01 tsahil01 closed this Feb 23, 2026
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