-
Notifications
You must be signed in to change notification settings - Fork 618
[Dashboard] Add ai usage billing banner to dashboard #8104
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
[Dashboard] Add ai usage billing banner to dashboard #8104
Conversation
Co-authored-by: joaquim.verges <joaquim.verges@gmail.com>
|
Cursor Agent can help with this pull request. Just |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Comment |
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. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8104 +/- ##
=======================================
Coverage 56.32% 56.32%
=======================================
Files 906 906
Lines 59197 59197
Branches 4176 4176
=======================================
Hits 33345 33345
Misses 25746 25746
Partials 106 106
🚀 New features to boost your workflow:
|
size-limit report 📦
|
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: 2
🧹 Nitpick comments (4)
apps/nebula/src/app/(app)/components/EmptyStateChatPageContent.tsx (1)
26-26: Addingrelativechanges the containing block for absolutely‑positioned children (Aurora). Verify no visual shift.This makes the outer wrapper the positioning context; Aurora (absolute) may render differently versus before. If the intent was only to anchor an AI banner, consider adding the banner here as well or confirm no regression across breakpoints/dark mode.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.tsx (3)
170-171: Add explicit return type and aclassNameprop; merge withcn().Meets TS/tsx guidelines and allows local overrides.
-function AIUsageBanner() { +function AIUsageBanner({ className = "" }: { className?: string }): JSX.Element { return ( - <div className="absolute top-0 left-0 right-0 z-10 flex justify-center px-4 pt-4"> + <div className={cn("absolute top-0 left-0 right-0 z-10 flex justify-center px-4 pt-4", className)}>
174-189: Use design tokens instead of raw blue colors.Dashboard code should rely on tokens (
bg-card,border-border,text-muted-foreground,text-primary) for theme consistency.- <div className="relative overflow-hidden rounded-lg border border-blue-200 bg-gradient-to-r from-blue-50 to-blue-50/50 p-3 shadow-sm dark:border-blue-800 dark:from-blue-950/50 dark:to-blue-950/20"> + <div className="relative overflow-hidden rounded-lg border border-border bg-card p-3 shadow-sm"> <div className="relative flex items-center gap-3 px-2"> - <div className="mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900"> - <InfoIcon className="size-3.5 text-blue-600 dark:text-blue-400" /> + <div className="mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-muted"> + <InfoIcon className="size-3.5 text-muted-foreground" /> </div> <div className="flex-1 text-sm"> - <p className="text-blue-800 dark:text-blue-200"> + <p className="text-muted-foreground"> thirdweb AI usage is billed based on number of tokens used. See the{" "} <a href="https://thirdweb.com/pricing" target="_blank" rel="noopener noreferrer" - className="font-medium text-blue-700 underline decoration-blue-300 underline-offset-2 transition-colors hover:text-blue-800 hover:decoration-blue-500 dark:text-blue-300 dark:decoration-blue-600 dark:hover:text-blue-200 dark:hover:decoration-blue-400" + className="font-medium text-primary underline underline-offset-2 transition-colors hover:text-primary/90" > pricing page </a> . </p>
182-193: Microcopy tweak for clarity.Consider “based on the number of tokens used” (add “the”) for correctness.
- thirdweb AI usage is billed based on number of tokens used. See + thirdweb AI usage is billed based on the number of tokens used. See
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
apps/dashboard/src/@/components/misc/AnnouncementBanner.tsx(1 hunks)apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.tsx(3 hunks)apps/nebula/src/app/(app)/components/EmptyStateChatPageContent.tsx(1 hunks)apps/playground-web/src/app/payments/x402/page.tsx(0 hunks)apps/portal/src/app/payments/x402/page.mdx(0 hunks)
💤 Files with no reviewable changes (2)
- apps/portal/src/app/payments/x402/page.mdx
- apps/playground-web/src/app/payments/x402/page.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{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
**/*.{ts,tsx}: Use explicit function declarations and explicit return types in TypeScript
Limit each file to one stateless, single‑responsibility function
Re‑use shared types from@/typeswhere applicable
Prefertypealiases overinterfaceexcept for nominal shapes
Avoidanyandunknownunless unavoidable; narrow generics when possible
Prefer composition over inheritance; use utility types (Partial, Pick, etc.)
Lazy‑import optional features and avoid top‑level side‑effects to reduce bundle size
Files:
apps/dashboard/src/@/components/misc/AnnouncementBanner.tsxapps/nebula/src/app/(app)/components/EmptyStateChatPageContent.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.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/misc/AnnouncementBanner.tsxapps/nebula/src/app/(app)/components/EmptyStateChatPageContent.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.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/misc/AnnouncementBanner.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.tsx
apps/{dashboard,playground}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
apps/{dashboard,playground}/**/*.{ts,tsx}: Import UI primitives from@/components/ui/_(e.g., Button, Input, Tabs, Card)
UseNavLinkfor internal navigation to get active state handling
Use Tailwind CSS for styling; no inline styles
Merge class names withcn()from@/lib/utilsfor conditional classes
Stick to design tokens (e.g., bg-card, border-border, text-muted-foreground)
Server Components must start withimport "server-only"; usenext/headers, server‑only env, heavy data fetching, andredirect()where appropriate
Client Components must start with'use client'; handle interactivity with hooks and browser APIs
Server-side data fetching: callgetAuthToken()from cookies, sendAuthorization: Bearer <token>header, and return typed results (avoidany)
Client-side data fetching: wrap calls in React Query with descriptive, stablequeryKeysand set sensiblestaleTime/cacheTime(≥ 60s default); keep tokens secret via internal routes or server actions
Do not importposthog-jsin server components (client-side only)
Files:
apps/dashboard/src/@/components/misc/AnnouncementBanner.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.tsx
apps/{dashboard,playground}/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Expose a
classNameprop on the root element of every component
Files:
apps/dashboard/src/@/components/misc/AnnouncementBanner.tsxapps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.tsx
🧠 Learnings (8)
📚 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} : For notices & skeletons rely on `AnnouncementBanner`, `GenericLoadingPage`, `EmptyStateCard`.
Applied to files:
apps/dashboard/src/@/components/misc/AnnouncementBanner.tsx
📚 Learning: 2025-07-07T21:21:47.488Z
Learnt from: saminacodes
PR: thirdweb-dev/js#7543
File: apps/portal/src/app/pay/page.mdx:4-4
Timestamp: 2025-07-07T21:21:47.488Z
Learning: In the thirdweb-dev/js repository, lucide-react icons must be imported with the "Icon" suffix (e.g., ExternalLinkIcon, RocketIcon) as required by the new linting rule, contrary to the typical lucide-react convention of importing without the suffix.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.tsx
📚 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/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.tsx
📚 Learning: 2025-05-30T17:14:25.332Z
Learnt from: MananTank
PR: thirdweb-dev/js#7227
File: apps/dashboard/src/app/(app)/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/OpenEditionMetadata.tsx:26-26
Timestamp: 2025-05-30T17:14:25.332Z
Learning: The ModuleCardUIProps interface already includes a client prop of type ThirdwebClient, so when components use `Omit<ModuleCardUIProps, "children" | "updateButton">`, they inherit the client prop without needing to add it explicitly.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.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)/ai/components/EmptyStateChatPageContent.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)/ai/components/EmptyStateChatPageContent.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/**/components/*.client.tsx : Client components must start with `'use client';` before imports.
Applied to files:
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.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)/ai/components/EmptyStateChatPageContent.tsx
⏰ 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). (3)
- GitHub Check: Size
- GitHub Check: Lint Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.tsx (1)
3-3: Lucide import naming LGTM.
InfoIconfollows the repo’s “Icon”-suffix convention.
| // return ( | ||
| // <AnnouncementBannerUI | ||
| // href="https://blog.thirdweb.com/the-fastest-way-to-build-web3-applications/" | ||
| // label="We have re-branded our Engine, Payments, and Connect products. Please read the full blog post for details on changes" | ||
| // trackingLabel="product-rebrand" | ||
| // /> | ||
| // ); | ||
| return null; |
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.
🧩 Analysis chain
Don’t silently disable AnnouncementBanner; gate or delete to avoid regressions.
Commenting out the render and returning null removes all global announcements and conflicts with our guidance to use AnnouncementBanner for notices. Either gate via a flag or delete the feature entirely (and its UI) to avoid dead code.
Apply one of these:
- // return (
- // <AnnouncementBannerUI
- // href="https://blog.thirdweb.com/the-fastest-way-to-build-web3-applications/"
- // label="We have re-branded our Engine, Payments, and Connect products. Please read the full blog post for details on changes"
- // trackingLabel="product-rebrand"
- // />
- // );
- return null;
+ const SHOW_ANNOUNCEMENT = false; // flip to true to (re)enable when needed
+ return SHOW_ANNOUNCEMENT ? (
+ <AnnouncementBannerUI
+ href="https://blog.thirdweb.com/the-fastest-way-to-build-web3-applications/"
+ label="We have re-branded our Engine, Payments, and Connect products. Please read the full blog post for details on changes"
+ trackingLabel="product-rebrand"
+ />
+ ) : null;Quick check to ensure no pages depend on this banner:
🏁 Script executed:
#!/bin/bash
# Find imports/usages of AnnouncementBanner
rg -nP --type=tsx -C2 '<AnnouncementBanner\b|import\s+{[^}]*AnnouncementBanner[^}]*}\s+from'Length of output: 27
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "rg version:"
rg --version || true
echo
echo "Finding AnnouncementBanner.tsx files (case-insensitive)..."
# Prefer find if fd isn't available
if command -v fd >/dev/null 2>&1; then
fd -HI -e tsx -g "*AnnouncementBanner.tsx" || true
else
find . -type f -iname "AnnouncementBanner.tsx" -print || true
fi
echo
echo "Searching repo for 'AnnouncementBanner' (imports/usages)..."
rg -nS "AnnouncementBanner" || true
echo
echo "Searching repo for JSX usage '<AnnouncementBanner'..."
rg -nS "<AnnouncementBanner\b" || true
echo
echo "Searching repo for 'AnnouncementBannerUI'..."
rg -nS "AnnouncementBannerUI" || true
echo
TARGET="apps/dashboard/src/@/components/misc/AnnouncementBanner.tsx"
if [ -f "$TARGET" ]; then
echo "== Showing $TARGET with line numbers =="
nl -ba "$TARGET" | sed -n '1,240p'
else
echo "File not found at $TARGET"
fiLength of output: 2070
Don't silently disable AnnouncementBanner — gate it or remove the feature.
AnnouncementBanner currently returns null but is imported and rendered in multiple layouts, which removes global announcements (regression).
- apps/dashboard/src/@/components/misc/AnnouncementBanner.tsx — export at line 46; UI is commented out (~lines 48–54).
- apps/dashboard/src/app/(app)/team/[team_slug]/(team)/layout.tsx — import at line 9; at line 82.
- apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/layout.tsx — import at line 7; at line 66.
- apps/dashboard/src/app/(app)/account/layout.tsx — import at line 8; at line 67.
Apply one of: gate behind a feature flag or delete the component and its usages. Suggested minimal change:
- // return (
- // <AnnouncementBannerUI
- // href="https://blog.thirdweb.com/the-fastest-way-to-build-web3-applications/"
- // label="We have re-branded our Engine, Payments, and Connect products. Please read the full blog post for details on changes"
- // trackingLabel="product-rebrand"
- // />
- // );
- return null;
+ const SHOW_ANNOUNCEMENT = false; // flip to true to (re)enable when needed
+ return SHOW_ANNOUNCEMENT ? (
+ <AnnouncementBannerUI
+ href="https://blog.thirdweb.com/the-fastest-way-to-build-web3-applications/"
+ label="We have re-branded our Engine, Payments, and Connect products. Please read the full blog post for details on changes"
+ trackingLabel="product-rebrand"
+ />
+ ) : null;📝 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.
| // return ( | |
| // <AnnouncementBannerUI | |
| // href="https://blog.thirdweb.com/the-fastest-way-to-build-web3-applications/" | |
| // label="We have re-branded our Engine, Payments, and Connect products. Please read the full blog post for details on changes" | |
| // trackingLabel="product-rebrand" | |
| // /> | |
| // ); | |
| return null; | |
| const SHOW_ANNOUNCEMENT = false; // flip to true to (re)enable when needed | |
| return SHOW_ANNOUNCEMENT ? ( | |
| <AnnouncementBannerUI | |
| href="https://blog.thirdweb.com/the-fastest-way-to-build-web3-applications/" | |
| label="We have re-branded our Engine, Payments, and Connect products. Please read the full blog post for details on changes" | |
| trackingLabel="product-rebrand" | |
| /> | |
| ) : null; |
🤖 Prompt for AI Agents
In apps/dashboard/src/@/components/misc/AnnouncementBanner.tsx around lines
47–54 the component is returning null and its UI is commented out, removing
global announcements; either gate it behind a feature flag or delete it and its
usages. To gate: restore the commented return (use AnnouncementBannerUI with the
same href/label/trackingLabel props), read a feature flag (e.g.,
isFeatureEnabled('announcementBanner') or
process.env.FEATURE_ANNOUNCEMENT_BANNER) and render the UI only when the flag is
true (default false), export the component unchanged; to remove: delete this
file or export null and then remove the AnnouncementBanner import and
<AnnouncementBanner /> occurrences from the three layout files listed (team
layout line ~82, project sidebar layout line ~66, account layout line ~67) and
run a build to ensure no unused-import errors.
| }) { | ||
| return ( | ||
| <div className="overflow-hidden py-10 lg:py-16"> | ||
| <AIUsageBanner /> |
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.
Make the banner’s positioning deterministic by making the container relative.
AIUsageBanner is absolute top-0. The nearest positioned ancestor is currently not the page section, so placement can drift. Align with the Nebula change and set the outer wrapper to relative.
Apply:
- <div className="overflow-hidden py-10 lg:py-16">
+ <div className="relative overflow-hidden py-10 lg:py-16">📝 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.
| <AIUsageBanner /> | |
| <div className="relative overflow-hidden py-10 lg:py-16"> | |
| <AIUsageBanner /> |
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/ai/components/EmptyStateChatPageContent.tsx
around line 27, the AIUsageBanner is positioned absolute (top-0) but its nearest
positioned ancestor is not the page section which makes placement drift; wrap
the <AIUsageBanner /> in the outer container that should be the positioned
ancestor and add relative positioning to that wrapper (e.g., give the outer
div/className a "relative" class) so the banner’s absolute positioning is
deterministic and anchored to the intended container.
[Dashboard] Feature: Add AI usage billing banner to Nebula chat empty state
Notes for the reviewer
This PR introduces a new
AIUsageBannercomponent toEmptyStateChatPageContent.tsx. The banner informs users about thirdweb AI usage billing and links to the pricing page. It is styled to be consistent with existing info/alert banners in the dashboard, floating at the top of the component.How to test
/nebula).https://thirdweb.com/pricing.Slack Thread
PR-Codex overview
This PR focuses on code modifications across multiple files, including changes to UI components and the addition of a new banner for AI usage information.
Detailed summary
EmptyStateChatPageContent.tsx, added<AIUsageBanner />to display information about AI usage billing.AnnouncementBanner.tsxto returnnullinstead of rendering the announcement UI.divclass inEmptyStateChatPageContent.tsxtorelativefor proper positioning.Summary by CodeRabbit