-
Notifications
You must be signed in to change notification settings - Fork 619
[BLD-80] Dashboard: in-app wallet settings page UI improvements #7843
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BLD-80] Dashboard: in-app wallet settings page UI improvements #7843
Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7843 +/- ##
=======================================
Coverage 56.33% 56.33%
=======================================
Files 905 905
Lines 58834 58834
Branches 4158 4158
=======================================
Hits 33147 33147
Misses 25582 25582
Partials 105 105
🚀 New features to boost your workflow:
|
fe67520 to
0668018
Compare
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds per-section save buttons with isUpdating wiring and reusable Fieldset primitives to in-app wallet settings; refactors Branding layout; introduces a collapsible TierCard-based SMS country selector and updates several country display names; adds tri-state Checkbox rendering and tweaks DynamicHeight transition timing. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant UI as InAppWalletSettingsUI
participant FS as Fieldset (section)
participant API as update handler
User->>FS: Edit fields in section
User->>FS: Click "Save" (section)
FS->>UI: invoke section save (sets isUpdating=true)
UI->>API: perform update (e.g., updateApiKey / embeddedWalletService)
API-->>UI: return success/failure
UI-->>FS: set isUpdating=false
sequenceDiagram
actor User
participant CS as CountrySelector
participant TC as TierCard
participant Parent as Parent Form
User->>TC: Toggle tier checkbox / click country tile
TC->>CS: onTierToggle / onToggleCountry
CS->>Parent: onChange(selectedCountries)
Note over TC,CS: Header checkbox reflects indeterminate when partially selected
User->>TC: Click expand/collapse
TC-->>User: Show/hide tier body
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
size-limit report 📦
|
...rc/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/country-selector.tsx (1)
160-171: Consider adding keyboard navigation support for the expand/collapse button.While the Button component likely handles basic keyboard interactions, consider adding an
aria-expandedattribute to improve accessibility for screen reader users.<Button variant="ghost" className="p-0 size-7" onClick={() => setIsExpanded(!isExpanded)} + aria-expanded={isExpanded} + aria-label={isExpanded ? "Collapse tier" : "Expand tier"} >apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx (1)
349-367: Consider adding validation for the Application Name field.While the field defaults to the API Key's name when empty, consider adding client-side validation to ensure the name meets any length or character requirements.
<FormControl> <Input {...field} /> </FormControl> + <FormDescription> + Name that will be displayed in the emails sent to users.{" "} + <br className="max-sm:hidden" /> Defaults to your API Key's + name. Maximum 50 characters. + </FormDescription> <FormMessage />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/dashboard/src/@/components/ui/checkbox.tsx(2 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx(7 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/country-selector.tsx(2 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/utils.ts(5 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
apps/dashboard/src/@/components/ui/checkbox.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/utils.tsapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/country-selector.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/@/components/ui/checkbox.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/utils.tsapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/country-selector.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/@/components/ui/checkbox.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/utils.tsapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/country-selector.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
🧠 Learnings (5)
📚 Learning: 2025-07-31T16:17:42.753Z
Learnt from: MananTank
PR: thirdweb-dev/js#7768
File: apps/playground-web/src/app/navLinks.ts:1-1
Timestamp: 2025-07-31T16:17:42.753Z
Learning: Configuration files that import and reference React components (like icon components from lucide-react) need the "use client" directive, even if they primarily export static data, because the referenced components need to be executed in a client context when used by other client components.
Applied to files:
apps/dashboard/src/@/components/ui/checkbox.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Prefer composable primitives over custom markup: `Button`, `Input`, `Select`, `Tabs`, `Card`, `Sidebar`, `Separator`, `Badge`.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/country-selector.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Icons come from `lucide-react` or the project-specific `…/icons` exports – never embed raw SVG.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/country-selector.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Import UI primitives from `@/components/ui/*` (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
🧬 Code Graph Analysis (2)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/country-selector.tsx (3)
apps/dashboard/src/@/components/ui/checkbox.tsx (1)
Checkbox(34-34)apps/playground-web/src/lib/utils.ts (1)
cn(5-7)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/utils.ts (4)
tierPricing(2-8)countryNames(11-232)getCountryFlag(458-465)countryPrefixes(234-455)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx (3)
packages/thirdweb/src/react/web/ui/components/Spinner.tsx (1)
Spinner(11-34)apps/dashboard/src/@/components/ui/input.tsx (1)
Input(25-25)apps/playground-web/src/lib/utils.ts (1)
cn(5-7)
🔇 Additional comments (8)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/utils.ts (1)
120-220: LGTM! Country name updates are accurate and improve consistency.The updates to country display names align with internationally recognized standards:
- Saint names are now fully spelled out (e.g., "St Kitts" → "Saint Kitts")
- Political names reflect current realities (e.g., "Macedonia" → "North Macedonia", "Swaziland" → "Eswatini")
- Corrections for accuracy (e.g., "Korea Republic of" → "South Korea", "Reunion/Mayotte" → "Réunion")
apps/dashboard/src/@/components/ui/checkbox.tsx (1)
24-28: LGTM! Well-implemented tri-state checkbox support.The implementation correctly handles the indeterminate state using the standard
checked === "indeterminate"pattern. The icons are appropriately sized for visual consistency.apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/country-selector.tsx (3)
75-96: LGTM! Clean component decomposition with TierCard extraction.The refactoring to extract TierCard as a separate component improves modularity and readability. The data flow is clean with appropriate props passed down.
115-118: Good UX choice for partial selection indicators.The tri-state checkbox with indeterminate state for partial selections, combined with the counter display, provides clear visual feedback to users about the selection state of each tier.
185-208: LGTM! Clean country selection UI with good visual hierarchy.The Button-based country tiles with flag, name, and prefix provide excellent visual feedback and accessibility. The CheckIcon for selected states is well-positioned.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx (3)
243-296: Well-structured form with clear section separation.The refactoring to use Fieldset components with per-section Save buttons successfully addresses the PR objective of not requiring users to scroll past long country lists to save top-level settings. Each section now has its own save action, improving the user experience.
308-319: Clean implementation of per-section save with loading states.The footer with Save button and loading spinner provides good user feedback during updates. The consistent pattern across all sections (Branding, Native Apps, Authentication) ensures a uniform experience.
817-837: Well-designed reusable Fieldset components.The Fieldset and FieldsetWithDescription components provide a consistent structure for form sections with proper semantic HTML, dynamic height animations, and flexible footer support. This promotes maintainability and consistency across the form.
0668018 to
e6f10f9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (4)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx (4)
260-269: Disable the section Save button when not dirty or while updatingPrevents accidental double submits and clarifies state. You already show a spinner; disabling completes the UX.
Apply this diff:
<Button className="gap-2" type="submit" size="sm" variant="outline" + disabled={props.isUpdating || !form.formState.isDirty} > {props.isUpdating && <Spinner className="size-4" />} Save </Button>
315-322: Disable Branding section Save while updating or when no changesSame rationale as Authentication; avoids redundant submissions.
- <Button className="gap-2" type="submit" size="sm" variant="outline"> + <Button + className="gap-2" + type="submit" + size="sm" + variant="outline" + disabled={props.isUpdating || !props.form.formState.isDirty} + >
769-773: Disable Native Apps Save while updating or when no changesKeeps section Save behavior consistent.
- <Button className="gap-2" type="submit" size="sm" variant="outline"> + <Button + className="gap-2" + type="submit" + size="sm" + variant="outline" + disabled={props.isUpdating || !form.formState.isDirty} + >
95-104: trackingData is computed but not consumed by update pathInAppWalletSettingsUI requires updateApiKey(projectValues, trackingData) and you compute tracking booleans at Lines 233-236, but the upstream implementation (handleUpdateProject) ignores the second arg. Either wire this into analytics/telemetry or drop the parameter to avoid dead code.
If you intend to remove it for now, adjust the type and call site:
-const InAppWalletSettingsPageUI: React.FC< - InAppWalletSettingsPageProps & { - updateApiKey: ( - projectValues: Partial<Project>, - trackingData: UpdateAPIKeyTrackingData, - ) => void; +const InAppWalletSettingsPageUI: React.FC< + InAppWalletSettingsPageProps & { + updateApiKey: (projectValues: Partial<Project>) => void;-export const InAppWalletSettingsUI: React.FC< +export const InAppWalletSettingsUI: React.FC< InAppWalletSettingsPageProps & { - updateApiKey: ( - projectValues: Partial<Project>, - trackingData: UpdateAPIKeyTrackingData, - ) => void; + updateApiKey: (projectValues: Partial<Project>) => void;- props.updateApiKey( - { - services: newServices, - }, - { - hasCustomAuthEndpoint: !!customAuthEndpoint, - hasCustomBranding: !!branding, - hasCustomJwt: !!customAuthentication, - }, - ); + props.updateApiKey({ + services: newServices, + });Alternatively, if you plan to track, I can help add a lightweight telemetry hook that consumes these flags.
Also applies to: 228-237
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
apps/dashboard/src/@/components/ui/DynamicHeight.tsx(1 hunks)apps/dashboard/src/@/components/ui/checkbox.tsx(2 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx(9 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/country-selector.tsx(2 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/utils.ts(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/country-selector.tsx
- apps/dashboard/src/@/components/ui/checkbox.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/utils.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
apps/dashboard/src/@/components/ui/DynamicHeight.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/@/components/ui/DynamicHeight.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/@/components/ui/DynamicHeight.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
🧠 Learnings (5)
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Spacing utilities (`px-*`, `py-*`, `gap-*`) are preferred over custom margins.
Applied to files:
apps/dashboard/src/@/components/ui/DynamicHeight.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*.{tsx,jsx} : Use the `container` class with a `max-w-7xl` cap for page width consistency.
Applied to files:
apps/dashboard/src/@/components/ui/DynamicHeight.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Use React Query (`tanstack/react-query`) for all client data fetching.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Anything that consumes hooks from `tanstack/react-query` or thirdweb SDKs.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
🧬 Code Graph Analysis (1)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx (4)
packages/thirdweb/src/react/web/ui/components/Spinner.tsx (1)
Spinner(11-34)apps/portal/src/components/ui/underline-link.tsx (1)
UnderlineLink(6-16)apps/playground-web/src/lib/utils.ts (1)
cn(5-7)apps/dashboard/src/@/components/ui/DynamicHeight.tsx (1)
DynamicHeight(7-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: Size
- GitHub Check: Unit Tests
- GitHub Check: Lint Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (5)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx (5)
242-254: Per-section Save UI and spacing look goodMoving to a stacked form layout with section footers aligns with the issue objective and improves discoverability of Save actions. No concerns here.
535-541: LGTM: UnderlineLink usageGood move to standardized UnderlineLink with proper noopener/noreferrer for external links.
625-632: LGTM: Link consistencySame as above; consistent UX for documentation links.
439-455: Image upload UX is resilientGood error handling and optimistic state reset on failure; overlay spinner feedback is clear.
Also applies to: 456-461
818-824: Fade-in-0 Animation Utility VerifiedI confirmed that the
tailwindcss-animateplugin is enabled (with an empty prefix) in each app’stailwind.config.{js,ts}, so thefade-in-0andanimate-inclasses are valid and will produce the expected entrance animation. No changes needed here.
...rc/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
Show resolved
Hide resolved
...rc/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
Show resolved
Hide resolved
Merge activity
|
<!--
## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"
If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):
## Notes for the reviewer
Anything important to call out? Be sure to also clarify these in your comments.
## How to test
Unit tests, playground, etc.
-->
<!-- start pr-codex -->
---
## PR-Codex overview
This PR focuses on enhancing the UI components and improving the country selection functionality in the dashboard application. It includes updates to transitions, checkbox indicators, and country names for better clarity and usability.
### Detailed summary
- Updated transition duration in `DynamicHeight.tsx`.
- Added `MinusIcon` for indeterminate state in `checkbox.tsx`.
- Corrected country names in `utils.ts` for several countries.
- Improved structure and styling of the `CountrySelector` component.
- Added `ChevronDownIcon` and `Button` for toggling tiers in `country-selector.tsx`.
- Refactored `Fieldset` and `FieldsetWithDescription` components for better layout and usability in settings forms.
> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`
<!-- end pr-codex -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
- New Features
- Checkbox now supports an indeterminate state with a clear visual indicator.
- Wallet settings sections gain per-section Save buttons with loading spinners.
- SMS country selection adds collapsible tier cards, partial-selection counters, and improved country tiles with clear selection states.
- Refactor
- Unified fieldset layouts with consistent headers, descriptions, dividers, and footers.
- Branding settings reorganized into a responsive two‑column layout with improved flow.
- Country tier rendering moved to modular TierCard components.
- Bug Fixes
- Corrected several country display names (e.g., South Korea, North Macedonia, Eswatini, Saint Lucia).
- Style
- Adjusted a height-transition timing/easing for dynamic-height animations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
e6f10f9 to
f7aa3e6
Compare
There was a problem hiding this 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
🔭 Outside diff range comments (1)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx (1)
705-729: Buttons may submit the form unintentionally; set type="button"Inside a form, a button without an explicit type defaults to submit. The “Remove header” and “Add header” buttons should not submit the form.
- <Button + <Button + type="button" aria-label="Remove header" className="!w-auto px-3" onClick={() => { customHeaderFields.remove(customHeaderIdx); }} variant="outline" > @@ - <Button + <Button + type="button" className="w-full gap-2 bg-background" onClick={() => { customHeaderFields.append({ key: "", value: "", }); }} variant="outline" >Also applies to: 734-746
♻️ Duplicate comments (2)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx (2)
(do not wrap it in a div)
827-846: A11y: must be the first child ofThe current markup nests legend inside a div. This is invalid HTML and harms semantics for assistive tech. Make legend the first child and place actions/headers alongside without wrapping legend.
-function Fieldset(props: { - legend: string; - children: React.ReactNode; - footer?: React.ReactNode; -}) { - return ( - <div className="rounded-lg border bg-card relative"> - <DynamicHeight> - <fieldset className="p-4 md:px-6 md:py-5"> - {/* put inside div to remove default styles on legend */} - <div className="border-b pb-4 mb-5 border-dashed font-semibold text-xl tracking-tight"> - <legend> {props.legend}</legend> - </div> - - {props.children} - </fieldset> - </DynamicHeight> - {props.footer} - </div> - ); -} +function Fieldset(props: { + legend: string; + children: React.ReactNode; + footer?: React.ReactNode; + headerActions?: React.ReactNode; // new, for top-right Save, etc. +}) { + return ( + <div className="relative rounded-lg border bg-card"> + <DynamicHeight> + <fieldset className="p-4 md:px-6 md:py-5"> + <legend className="mb-5 border-b border-dashed pb-4 font-semibold text-xl tracking-tight"> + {props.legend} + </legend> + {props.headerActions ? ( + <div className="absolute right-6 top-4">{props.headerActions}</div> + ) : null} + {props.children} + </fieldset> + </DynamicHeight> + {props.footer} + </div> + ); +}
(same issue in FieldsetWithDescription)
849-873: A11y: must be direct child ofMove legend to be the first child of fieldset; place the description after it. This mirrors the fix above.
-function FieldsetWithDescription(props: { - legend: string; - children: React.ReactNode; - footer?: React.ReactNode; - description: React.ReactNode; -}) { - return ( - <div className="rounded-lg border bg-card relative"> - <DynamicHeight> - <fieldset className="p-4 md:p-6"> - {/* put inside div to remove default styles on legend */} - <div className="pr-20"> - <legend className="font-semibold text-xl tracking-tight"> - {props.legend} - </legend> - <p className="text-muted-foreground text-sm">{props.description}</p> - </div> - - {props.children} - </fieldset> - </DynamicHeight> - {props.footer} - </div> - ); -} +function FieldsetWithDescription(props: { + legend: string; + children: React.ReactNode; + footer?: React.ReactNode; + description: React.ReactNode; +}) { + return ( + <div className="relative rounded-lg border bg-card"> + <DynamicHeight> + <fieldset className="p-4 md:p-6"> + <legend className="pr-20 font-semibold text-xl tracking-tight"> + {props.legend} + </legend> + <div className="pr-20"> + <p className="text-muted-foreground text-sm">{props.description}</p> + </div> + {props.children} + </fieldset> + </DynamicHeight> + {props.footer} + </div> + ); +}
🧹 Nitpick comments (2)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx (2)
312-323: Branding footer Save is fine, but consider consistent header placementThis section likely won’t overflow, so footer is acceptable. For consistency and to avoid later regressions if content grows, you may consider headerActions here too (optional).
353-385: Place FormMessage after the control for better a11y and UXFormMessage appears above the input inside the header block. Validation messages should follow the control they describe.
<FormItem className="lg:border-r lg:border-dashed lg:pr-6"> <div className="space-y-1"> <FormLabel>Application Image URL</FormLabel> <FormDescription className="!mb-4"> Logo that will display in the emails sent to users.{" "} <br className="max-sm:hidden" /> The image must be squared with recommended size of 72x72 px. </FormDescription> - - <FormMessage /> </div> <FormControl> <AppImageFormControl client={props.client} setUri={(uri) => { props.form.setValue("branding.applicationImageUrl", uri, { shouldDirty: true, shouldTouch: true, }); }} uri={props.form.watch("branding.applicationImageUrl")} /> </FormControl> + <FormMessage />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
apps/dashboard/src/@/components/ui/DynamicHeight.tsx(1 hunks)apps/dashboard/src/@/components/ui/checkbox.tsx(2 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx(9 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/country-selector.tsx(2 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/utils.ts(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- apps/dashboard/src/@/components/ui/checkbox.tsx
- apps/dashboard/src/@/components/ui/DynamicHeight.tsx
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/utils.ts
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/sms-country-select/country-selector.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
**/*.{ts,tsx}: Write idiomatic TypeScript with explicit function declarations and return types
Limit each file to one stateless, single-responsibility function for clarity
Re-use shared types from@/typesor localtypes.tsbarrels
Prefer type aliases over interface except for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Choose composition over inheritance; leverage utility types (Partial,Pick, etc.)
Comment only ambiguous logic; avoid restating TypeScript in prose
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
Load heavy dependencies inside async paths to keep initial bundle lean (lazy loading)
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
apps/{dashboard,playground-web}/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (CLAUDE.md)
apps/{dashboard,playground-web}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/*(Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
UseNavLinkfor internal navigation with automatic active states in dashboard and playground apps
Use Tailwind CSS only – no inline styles or CSS modules
Usecn()from@/lib/utilsfor conditional class logic
Use design system tokens (e.g.,bg-card,border-border,text-muted-foreground)
Server Components (Node edge): Start files withimport "server-only";
Client Components (browser): Begin files with'use client';
Always callgetAuthToken()to retrieve JWT from cookies on server side
UseAuthorization: Bearerheader – never embed tokens in URLs
Return typed results (e.g.,Project[],User[]) – avoidany
Wrap client-side data fetching calls in React Query (@tanstack/react-query)
Use descriptive, stablequeryKeysfor React Query cache hits
ConfigurestaleTime/cacheTimein React Query based on freshness (default ≥ 60s)
Keep tokens secret via internal API routes or server actions
Never importposthog-jsin server components
Files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
🧠 Learnings (4)
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Interactive UI that relies on hooks (`useState`, `useEffect`, React Query, wallet hooks).
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
📚 Learning: 2025-07-18T19:19:55.613Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: CLAUDE.md:0-0
Timestamp: 2025-07-18T19:19:55.613Z
Learning: Applies to apps/{dashboard,playground-web}/**/*.{ts,tsx} : Import UI primitives from `@/components/ui/*` (Button, Input, Select, Tabs, Card, Sidebar, Badge, Separator) in dashboard and playground apps
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Use React Query (`tanstack/react-query`) for all client data fetching.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
📚 Learning: 2025-07-18T19:20:32.530Z
Learnt from: CR
PR: thirdweb-dev/js#0
File: .cursor/rules/dashboard.mdc:0-0
Timestamp: 2025-07-18T19:20:32.530Z
Learning: Applies to dashboard/**/*client.tsx : Anything that consumes hooks from `tanstack/react-query` or thirdweb SDKs.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx
🧬 Code Graph Analysis (1)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx (5)
packages/thirdweb/src/react/web/ui/components/Spinner.tsx (1)
Spinner(11-34)apps/dashboard/src/@/components/ui/input.tsx (1)
Input(25-25)apps/portal/src/components/ui/underline-link.tsx (1)
UnderlineLink(6-16)apps/playground-web/src/lib/utils.ts (1)
cn(5-7)apps/dashboard/src/@/components/ui/DynamicHeight.tsx (1)
DynamicHeight(7-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: Size
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: Lint Packages
- GitHub Check: Unit Tests
- GitHub Check: Build Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (6)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/wallets/settings/components/index.tsx (6)
242-242: LGTM: form stack spacingSwitching to space-y-6 is consistent with the rest of the dashboard and improves readability.
250-254: Per-section isUpdating wiring looks goodPassing isUpdating into Branding and Native Apps fieldsets enables per-section spinners as intended.
818-825: LGTM: Controlled reveal containerThe gated container reveals with a short entry animation and respects plan gating. Looks good.
256-271: AI summary discrepancy: only one Save for Authentication (not per-subsection)The AI summary states per-section Save buttons in Authentication. In this file, there is a single Save for the entire Authentication fieldset, not separate Saves for JWT, Custom Auth Endpoint, and SMS. See lines 256-271 (one footer/button) and the subsections at 535-541 and 626-632 with no separate Save controls.
Consider splitting Authentication into three fieldsets (JWT, Custom Endpoint, SMS) each with its own headerActions Save, or keep a single fieldset but move Save to the header as suggested above to avoid scrolling issues with the SMS list. Do you want me to draft that refactor?
Also applies to: 535-541, 626-632
759-775: Native Apps footer Save looks goodConsistent with the pattern; spinner integration matches the Branding section.
266-266: Spinner API Usage Is Correct
TheSpinnercomponent imported from@/components/ui/Spinner/Spinner.tsxdefines its props as{ className?: string }and applies"size-4"as the default when noclassNameis provided. Therefore, using<Spinner className="size-4" />(and similarly elsewhere) aligns with its API and requires no changes.Likely an incorrect or invalid review comment.
| <Fieldset | ||
| legend="Authentication" | ||
| footer={ | ||
| <div className="flex justify-end p-4 md:px-6 border-t border-dashed"> | ||
| <Button | ||
| className="gap-2" | ||
| type="submit" | ||
| size="sm" | ||
| variant="outline" | ||
| > | ||
| {props.isUpdating && <Spinner className="size-4" />} | ||
| Save | ||
| </Button> | ||
| </div> | ||
| } | ||
| > |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Save button for Authentication is still below the long SMS list; move actions to the header
The Save button is in the Fieldset footer, which will still be below the entire Authentication section and buried under the long SMS country list. This doesn’t satisfy BLD-80’s objective to avoid excessive scrolling for Save.
Recommend moving the Save action into the Fieldset header (top-right), so it’s always at the top of this section. Suggest also fixing semantics at the same time in the Fieldset component.
Apply this diff here to pass actions to the header instead of the footer:
- <Fieldset
- legend="Authentication"
- footer={
- <div className="flex justify-end p-4 md:px-6 border-t border-dashed">
- <Button
- className="gap-2"
- type="submit"
- size="sm"
- variant="outline"
- >
- {props.isUpdating && <Spinner className="size-4" />}
- Save
- </Button>
- </div>
- }
- >
+ <Fieldset
+ legend="Authentication"
+ headerActions={
+ <Button className="gap-2" type="submit" size="sm" variant="outline">
+ {/* NOTE: see Spinner API verification comment below */}
+ {props.isUpdating && <Spinner className="size-4" />}
+ Save
+ </Button>
+ }
+ >And update the Fieldset component to support headerActions and proper legend semantics (see a11y comment below for the component-level diff).
Committable suggestion skipped: line range outside the PR's diff.

PR-Codex overview
This PR focuses on improving the UI components and functionality of the dashboard, including updates to country names, checkbox behavior, and form handling. It enhances user interactions and overall presentation in the settings and country selection areas.
Detailed summary
DynamicHeighttransition duration from 210ms to 250ms.Checkboxcomponent to displayMinusIconfor indeterminate state.utils.tsfor consistency.ChevronDownIconandButtonfor tier expansion incountry-selector.tsx.country-selector.tsx.index.tsxand related components.FieldsetWithDescriptionfor better layout in forms.footersections with save buttons in several fieldsets for clarity.Summary by CodeRabbit
New Features
Refactor
Bug Fixes
Style