Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR adds reusable UI components and a dev preview page, rewrites the landing, dashboard, subscription, and v2 dashboard flows, and updates theme, data, API, and build tooling. ChangesProduct UI and app flows
Runtime, data, and tooling
Estimated code review effort: 4 (Complex) | ~75 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
PR Summary by QodoRevamp wallet, subscription, and landing UIs to match Figma + new UI kit
AI Description
Diagram
High-Level Assessment
Files changed (17)
|
Code Review by Qodo
1. Home CTA navigation broken
|
There was a problem hiding this comment.
Actionable comments posted: 17
🧹 Nitpick comments (9)
src/components/ui/button.tsx (2)
62-65: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMissing
aria-busyon loading state.Consider adding
aria-busy={loading}whenloadingis true for screen-reader users.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/ui/button.tsx` around lines 62 - 65, The loading state in the Button component is missing an accessibility signal for assistive tech. Update the Button rendering logic in the button component so the interactive element sets aria-busy based on the loading state, using the existing loading prop alongside the spinner/leftIcon rendering. Keep the change localized to the Button component’s main return path so screen readers can identify when the button is busy.
16-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftHardcoded hex colors repeated across every new UI component.
The same brand hex values (
#fa7119,#1b2027,#64748b,#475569, etc.) are duplicated verbatim acrossbutton.tsx,clickable-card.tsx,toggle-plan.tsx, andcategory-filter.tsx. Extracting these into Tailwind theme tokens (tailwind.config) would centralize brand color changes and reduce drift.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/ui/button.tsx` around lines 16 - 24, The button variant styles in variantClasses are repeating hardcoded brand hex colors that also appear in other UI components, so centralize them in Tailwind theme tokens instead of inlining them here. Update Button’s variantClasses to reference the shared theme color names, and use the same tokens consistently in clickable-card, toggle-plan, and category-filter so future brand color changes only need one update.src/app/dev/page.tsx (1)
1-32: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winGuard the dev preview route from production, and consider dynamic imports.
This route showcases internal design-system components and sample plan copy but has no environment check; it will be reachable in production builds. Consider gating it behind
process.env.NODE_ENV !== "production"(e.g.,notFound()), and since it's not on any critical rendering path, the individual UI component imports (lines 4-13) are good candidates for dynamic imports per the code-splitting guideline.As per coding guidelines, "Use dynamic imports for code splitting where appropriate" for
**/*.{ts,tsx}files.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/app/dev/page.tsx` around lines 1 - 32, The DevPage preview route is currently publicly reachable in production and should be hidden behind an environment guard in the DevPage component. Add a production check near the start of DevPage and route away from production access (for example via notFound()), and consider converting the noncritical UI imports used by DevPage (such as Button, ClickableCard, PlanCard, TextField/TextAreaField, PageTitle, TogglePlan, CategoryFilterGroup, SearchField, CopyField, and BottomNavigation) to dynamic imports to follow the code-splitting guideline.Source: Coding guidelines
src/components/ui/toggle-plan.tsx (1)
14-40: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider
aria-pressed/role="tablist"for the segmented control.Improves screen reader affordance for the active/inactive toggle state.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/ui/toggle-plan.tsx` around lines 14 - 40, The segmented control in toggle-plan.tsx is missing accessible state semantics, so update the button group rendered by the toggle component to expose its active/inactive state to assistive tech. In the map over ["weekly", "monthly"] inside the toggle rendering, add the appropriate ARIA pattern such as aria-pressed on each button or a tablist/tab/tabpanel structure, and keep the active state in sync with the existing value and onChange logic in TogglePlan.src/components/ui/visible-toggle.tsx (1)
32-48: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winIcon-only toggle lacks accessible name/state.
The button has no text content,
aria-label, oraria-pressed; screen reader users can't tell what it does or whether balances are currently shown/hidden.♿ Proposed fix
<button type="button" onClick={onToggle} + aria-pressed={visible} + aria-label={visible ? "Hide balance" : "Show balance"} className={cn(🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/ui/visible-toggle.tsx` around lines 32 - 48, The VisibleToggle button is icon-only and currently exposes no accessible name or pressed state. Update VisibleToggle to add an aria-label that reflects the toggle action/state, and add aria-pressed based on the visible prop so assistive tech can announce whether content is shown or hidden. Keep the fix scoped to the VisibleToggle component and preserve the existing onClick/onToggle behavior.src/components/ui/search-field.tsx (1)
16-37: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider an accessible name beyond
placeholder.The input relies only on
placeholderfor its accessible name; screen readers may not announce it consistently. Consider allowingaria-label/aria-labelledbypass-through (already possible via...props) or defaulting one.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/ui/search-field.tsx` around lines 16 - 37, The SearchField input currently depends on placeholder text for its accessible name; update SearchField to support a proper name by ensuring aria-label or aria-labelledby can be passed through via the existing props spread, or by defaulting an accessible label on the input in the SearchField component. Keep the fix focused in SearchField and its input element so screen readers can announce it reliably.src/components/ui/text-field.tsx (2)
7-7: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
"active"state value is a no-op.
stateaccepts"default" | "active" | "disabled", but only the"disabled"branch has any effect (line 14/57); passingstate="active"changes nothing.Also applies to: 14-14, 50-50, 57-57
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/ui/text-field.tsx` at line 7, The TextField state API currently exposes "active" but it has no effect, since only the "disabled" branch is handled in the TextField component logic. Update the TextField type and all state handling in the TextField implementation to remove the no-op "active" value or replace it with a state that actually changes behavior, and make sure the checks in the TextField component branches align with the supported state values.
12-22: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winLabel not programmatically associated with the input.
Neither
TextFieldnorTextAreaFieldwireshtmlFor/idbetween the<label>and the control, so screen readers won't associate them unless the caller manually passes matchingid/htmlFor.♿ Proposed fix (TextField shown; same pattern applies to TextAreaField)
const TextField = forwardRef<HTMLInputElement, TextFieldProps>( - ({ className, state = "default", label, error, disabled, ...props }, ref) => { + ({ className, state = "default", label, error, disabled, id, ...props }, ref) => { const isDisabled = disabled || state === "disabled"; + const inputId = id ?? React.useId(); return ( <div className="flex flex-col gap-1 w-full"> {label && ( - <label className="text-[12px] font-normal text-[`#475569`] leading-[16px]"> + <label htmlFor={inputId} className="text-[12px] font-normal text-[`#475569`] leading-[16px]"> {label} </label> )} <input ref={ref} + id={inputId} disabled={isDisabled}Also applies to: 55-65
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/ui/text-field.tsx` around lines 12 - 22, The label is rendered without being programmatically linked to the input, so update TextField and TextAreaField to connect the label and control using matching id/htmlFor wiring. In TextField’s forwardRef render path, accept or derive a stable id for the input, pass it to the input element, and set the label’s htmlFor to the same value; apply the same pattern in TextAreaField so the label association works automatically even when callers do not provide their own pairing.src/components/ui/plan-card.tsx (1)
46-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExpose selection state to assistive tech.
The card acts as a radio-style selector (custom radio dot at lines 72-73) but the underlying
<button>has noaria-pressed/role="radio"/aria-checkedto conveyisHighlightedstate to screen readers.♿ Proposed fix
<button type="button" onClick={onSelect} + aria-pressed={isHighlighted} className={cn(Also applies to: 72-73
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/ui/plan-card.tsx` around lines 46 - 60, The selectable card in PlanCard is only styled as selected via isHighlighted, but the underlying button does not expose that state to assistive tech. Update the button in plan-card.tsx to communicate selection semantics using the existing isHighlighted state and onSelect handler, either by adding aria-pressed for a toggle-style control or switching to radio semantics with role="radio" and aria-checked. Make sure the custom selection dot near the bottom stays in sync with the same accessible state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@package.json`:
- Line 6: The dev server port in the docs is out of sync with the actual
scripts.dev command in package.json. Update the setup instructions in CLAUDE.md
and README.md so they reference the same port as the dev script (3348) and keep
any examples or command snippets consistent with next dev -p 3348.
In `@src/app/dashboard/page.tsx`:
- Around line 250-258: The wallet summary query is using native fetch instead of
the required HTTP wrapper. Update the useQuery queryFn in the DCSummary fetch
path to route the request through fetchy from `@/lib/fetchy`, preserving the same
endpoint, headers, and error handling behavior. Use the existing queryKey
walletSummary and the surrounding useQuery call in page.tsx to locate and
replace the direct fetch usage.
- Around line 220-224: The “Current Price” label in the dashboard card is
misleading because it renders avgBtcPrice, the same value used for “Avg Price.”
Update the JSX in page.tsx around the Current Price block so it either uses a
true current BTC price source or renames the field to reflect that it is an
average price; use the relevant render section with avgBtcPrice and SectionLabel
to locate the change.
- Around line 263-270: The current-subscription query in page.tsx is treating
the API response as a Subscription, but /api/subscription/current returns an
object containing subscription and message, so unwrap the subscription field
inside the queryFn before returning it and keep the result typed as Subscription
| null to avoid PlanCard/getPlanEmoji crashes. Also replace the inline
["subscription-current"] queryKey with the shared key from queryKeys in
`@/lib/query-keys` so the useQuery call follows the project convention.
- Around line 294-303: The auth flow in the dashboard page is persisting the
existing-user flag too early via saveAuthToStorage, which leaves localStorage
inconsistent when /api/user/exists/:id says the user is unregistered. Update the
flow around authenticateWithTelegram and the fetchy.get<UserExistsResponse>
check so only the token is saved before registration is confirmed, and set or
keep the existing-user state only in the response.registered branch. If the
onboarding branch is taken, ensure any bitcoin-deepa-is-existing-user flag is
cleared before router.push("/onboard").
- Around line 312-319: The KYC redirect logic in the dashboard effect is
checking the wrong response field and bypassing the shared request helper.
Update the `useEffect` in `page.tsx` to read `data.kycStatus` instead of
`data.status`, and route the request through `fetchy` rather than calling
`fetch` directly. Keep the existing `authToken` guard and
`router.push("/verification")` behavior, but only trigger it when the returned
KYC status is not approved.
In `@src/app/dashboard/subscription/page.tsx`:
- Around line 270-283: handleSubscribe currently only logs PayHere link
generation failures, so add a visible error state for this flow similar to
cancelError. Update the handleSubscribe logic in subscription/page.tsx to set a
dedicated payment-link error message when the fetch or redirect fails, and
render that state in the UI so users see the failure instead of just console
output. Use the existing handleSubscribe function and the current payhere link
loading state as the place to wire in the new error handling.
- Around line 199-205: Replace the remaining native fetch calls in the
subscription page with the shared fetchy wrapper and move current membership
loading to TanStack Query. Update the packages, PayHere link generation, and
cancellation requests in the subscription page to use fetchy, and refactor the
current membership logic from the existing useEffect-style state load into a
useQuery keyed with queryKeys so server state is managed consistently. Use the
existing subscription page component symbols and the queryKeys module to locate
and update the affected requests.
In `@src/app/page.tsx`:
- Around line 21-22: The user count fallback in the page data-loading logic is
treating 0 as missing, which causes valid zero counts to be replaced. Update the
count assignment in the page component’s data fetch flow so the fallback only
applies when the value is null or undefined, preserving 0 as a valid value; use
the existing fetchy.get call and setCount usage as the place to make this
change.
- Around line 203-206: The `/api/user` POST payload in `page.tsx` does not match
the route contract because `fetchy.post` is sending `username` instead of the
expected `name`. Update the request body in the onboarding flow to use the field
name required by the `/api/user` endpoint, and keep the rest of the payload
(`id`, `data`) unchanged so the `fetchy.post` call aligns with the API contract.
- Around line 226-227: The landing CTA handler is currently a no-op, so the
primary “Start/Open Wallet” flow does nothing. Restore the navigation behavior
in goToDashboard in src/app/page.tsx by wiring it back to
router.push("/dashboard"), and ensure every CTA using that callback once again
routes users to the dashboard.
In `@src/components/bottomNavigation.tsx`:
- Around line 70-76: The bottom navigation currently includes a `/news` entry
that points to a route without a matching App Router page, so either remove the
`IconNews`/`/news` item from `navItems` in `bottomNavigation` or add the missing
`news` page under the App Router. Update the `navItems` array in
`bottomNavigation.tsx` to keep only routes that exist, or create the
corresponding page component so the link resolves correctly.
In `@src/components/ui/button.tsx`:
- Around line 46-60: Default the native button type in the Button component to
prevent accidental form submission when used inside forms. Update the Button
implementation in the main button component (the one rendering the <button>
element and spreading props) so that it sets type to "button" unless an explicit
type is provided through props, while preserving the existing ref,
disabled/loading, and className handling.
In `@src/components/ui/category-filter.tsx`:
- Around line 19-33: The CategoryFilter button is missing an explicit button
type, so it can accidentally submit a surrounding form. Update the button in
CategoryFilter to set type to "button" alongside the existing onClick,
className, and label usage, matching the fix used in Button and TogglePlan. This
should be applied in the button element rendered by the CategoryFilter
component.
In `@src/components/ui/clickable-card.tsx`:
- Around line 31-43: The ClickableCard status prop currently accepts "pressed"
and "focus" but only "disabled" is used, so update ClickableCard and its related
styling logic to either handle those states explicitly or remove them from
ClickableCardProps. Use the existing ClickableCard component and the
isDisabled/status handling to locate the code, and make sure any supported
status value maps to distinct visual behavior instead of being ignored.
In `@src/components/ui/copy-field.tsx`:
- Around line 43-49: The handleCopy function in copy-field.tsx only handles the
success path from navigator.clipboard.writeText and leaves rejections unhandled.
Update handleCopy to catch writeText failures, avoid setting copied state or
calling onCopy on failure, and provide a safe fallback such as logging or a
user-facing error state; keep the existing success flow intact for the
navigator.clipboard.writeText, setCopied, and onCopy logic.
In `@src/components/ui/toggle-plan.tsx`:
- Around line 24-37: The toggle buttons in the TogglePlan component default to
submit behavior inside forms; update the button element in toggle-plan.tsx to
explicitly set type="button" so onClick for onChange(option) only changes the
plan selection. Use the existing toggle rendering block and the button keyed by
option as the place to apply the fix.
---
Nitpick comments:
In `@src/app/dev/page.tsx`:
- Around line 1-32: The DevPage preview route is currently publicly reachable in
production and should be hidden behind an environment guard in the DevPage
component. Add a production check near the start of DevPage and route away from
production access (for example via notFound()), and consider converting the
noncritical UI imports used by DevPage (such as Button, ClickableCard, PlanCard,
TextField/TextAreaField, PageTitle, TogglePlan, CategoryFilterGroup,
SearchField, CopyField, and BottomNavigation) to dynamic imports to follow the
code-splitting guideline.
In `@src/components/ui/button.tsx`:
- Around line 62-65: The loading state in the Button component is missing an
accessibility signal for assistive tech. Update the Button rendering logic in
the button component so the interactive element sets aria-busy based on the
loading state, using the existing loading prop alongside the spinner/leftIcon
rendering. Keep the change localized to the Button component’s main return path
so screen readers can identify when the button is busy.
- Around line 16-24: The button variant styles in variantClasses are repeating
hardcoded brand hex colors that also appear in other UI components, so
centralize them in Tailwind theme tokens instead of inlining them here. Update
Button’s variantClasses to reference the shared theme color names, and use the
same tokens consistently in clickable-card, toggle-plan, and category-filter so
future brand color changes only need one update.
In `@src/components/ui/plan-card.tsx`:
- Around line 46-60: The selectable card in PlanCard is only styled as selected
via isHighlighted, but the underlying button does not expose that state to
assistive tech. Update the button in plan-card.tsx to communicate selection
semantics using the existing isHighlighted state and onSelect handler, either by
adding aria-pressed for a toggle-style control or switching to radio semantics
with role="radio" and aria-checked. Make sure the custom selection dot near the
bottom stays in sync with the same accessible state.
In `@src/components/ui/search-field.tsx`:
- Around line 16-37: The SearchField input currently depends on placeholder text
for its accessible name; update SearchField to support a proper name by ensuring
aria-label or aria-labelledby can be passed through via the existing props
spread, or by defaulting an accessible label on the input in the SearchField
component. Keep the fix focused in SearchField and its input element so screen
readers can announce it reliably.
In `@src/components/ui/text-field.tsx`:
- Line 7: The TextField state API currently exposes "active" but it has no
effect, since only the "disabled" branch is handled in the TextField component
logic. Update the TextField type and all state handling in the TextField
implementation to remove the no-op "active" value or replace it with a state
that actually changes behavior, and make sure the checks in the TextField
component branches align with the supported state values.
- Around line 12-22: The label is rendered without being programmatically linked
to the input, so update TextField and TextAreaField to connect the label and
control using matching id/htmlFor wiring. In TextField’s forwardRef render path,
accept or derive a stable id for the input, pass it to the input element, and
set the label’s htmlFor to the same value; apply the same pattern in
TextAreaField so the label association works automatically even when callers do
not provide their own pairing.
In `@src/components/ui/toggle-plan.tsx`:
- Around line 14-40: The segmented control in toggle-plan.tsx is missing
accessible state semantics, so update the button group rendered by the toggle
component to expose its active/inactive state to assistive tech. In the map over
["weekly", "monthly"] inside the toggle rendering, add the appropriate ARIA
pattern such as aria-pressed on each button or a tablist/tab/tabpanel structure,
and keep the active state in sync with the existing value and onChange logic in
TogglePlan.
In `@src/components/ui/visible-toggle.tsx`:
- Around line 32-48: The VisibleToggle button is icon-only and currently exposes
no accessible name or pressed state. Update VisibleToggle to add an aria-label
that reflects the toggle action/state, and add aria-pressed based on the visible
prop so assistive tech can announce whether content is shown or hidden. Keep the
fix scoped to the VisibleToggle component and preserve the existing
onClick/onToggle behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 59250310-b598-4339-93ce-75c00ba6cc07
⛔ Files ignored due to path filters (6)
bun.lockbis excluded by!**/bun.lockbpackage-lock.jsonis excluded by!**/package-lock.jsonpublic/BDLogo_Black.svgis excluded by!**/*.svgpublic/bd-wordmark-light.pngis excluded by!**/*.pngpublic/btc-coin-3d.pngis excluded by!**/*.pngpublic/gift-emoji-3d.pngis excluded by!**/*.png
📒 Files selected for processing (17)
package.jsonsrc/app/dashboard/page.tsxsrc/app/dashboard/subscription/page.tsxsrc/app/dev/page.tsxsrc/app/page.tsxsrc/components/LoadingPage.tsxsrc/components/bottomNavigation.tsxsrc/components/ui/button.tsxsrc/components/ui/category-filter.tsxsrc/components/ui/clickable-card.tsxsrc/components/ui/copy-field.tsxsrc/components/ui/page-title.tsxsrc/components/ui/plan-card.tsxsrc/components/ui/search-field.tsxsrc/components/ui/text-field.tsxsrc/components/ui/toggle-plan.tsxsrc/components/ui/visible-toggle.tsx
* feat: update development port and add Radix UI progress component - Changed development port from 3348 to 3347 in package.json. - Added @radix-ui/react-progress dependency. - Implemented a shared Progress component using Radix UI in the UserProgress section. - Enhanced ThemeProvider to support a dev-only theme toggle for light/dark modes. - Refactored UserProgress and UserScreen components for improved readability and functionality. - Updated ClickableCard styles for dark mode compatibility. - Introduced constants for Telegram bot URL and username. - Added semantic tokens for theming in CSS. * feat: update UI components and styles for improved design consistency - Updated `components.json` to change style to "radix-nova" and added new properties. - Modified `package.json` to include new dependencies: `radix-ui` and `shadcn`, and updated versions for existing packages. - Adjusted `postcss.config.mjs` to use the new Tailwind CSS plugin. - Enhanced various UI pages (`page.tsx` files) to improve styling and responsiveness, including changes to backdrop blur and gradient classes. - Refined layout in `layout.tsx` to incorporate new font variable. - Updated skeleton components for loading states to use linear gradients. - Changed focus outline styles in `clickable-card.tsx` and `plan-card.tsx` for better accessibility. - Refactored progress component to align with new Radix UI structure. - Updated search and text fields to use hidden outlines for a cleaner look. - Expanded `theme.css` with new theme variables and custom utilities for better design control. - Removed outdated `tailwind.config.ts` as configurations are now handled in `theme.css`. * feat: update ESLint configuration, improve API header handling, and enhance UI components * feat: refactor theme handling and user registration - Removed the gift emoji image as it is no longer needed. - Integrated AppRoot from telegram-ui into the Providers component to ensure theme synchronization. - Simplified ThemeProvider by removing the development-only theme toggle. - Updated layout to remove redundant AppRoot wrapping around children. - Refactored user registration logic into a custom hook (useRegisterTelegramUser) for better separation of concerns. - Created a new hook (useUserCount) to fetch and manage user count state. - Added query key for user count to the query keys file. - Updated theme colors in CSS for consistency across the application. * feat: remove deprecated UI components and clean up unused code * feat: update bun.lockb to reflect dependency changes * feat: enhance layout to center component
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/app/api/subscription/payhere-link/route.ts (1)
109-110: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winRemove leftover
debugger;and sensitive response logging.A stray
debugger;statement and aconsole.logof the full PayHere response (which may include payment URLs/order data) are present in the success path. The debugger halts execution if an inspector is attached (Next.js 16.1+ supportsnext start --inspectfor production), and logging full payment response payloads risks leaking sensitive data into server logs.🧹 Suggested fix
- console.log("✅ PayHere link generated successfully:", data); - debugger; - return NextResponse.json({🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/app/api/subscription/payhere-link/route.ts` around lines 109 - 110, Remove the leftover debug artifacts in the PayHere success path inside the payhere-link route handler: delete the stray debugger statement and stop logging the full data payload from the PayHere response. Keep only a minimal success log in the relevant handler logic, and if needed replace it with non-sensitive metadata (for example, a generic success message) in the same response-handling block.
🧹 Nitpick comments (1)
src/styles/theme.css (1)
5-5: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueStylelint errors are expected — config lacks Tailwind v4 support.
The
@custom-variant/@utility/@theme"unknown at-rule" errors and the nested "invalid position for declaration" hits (lines 11, 14) are false positives from stylelint not recognizing Tailwind v4 syntax, not real CSS bugs. Consider adding Tailwind v4-aware stylelint config/plugin to eliminate this noise.Also applies to: 7-7, 18-18, 233-233
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/styles/theme.css` at line 5, Stylelint is flagging Tailwind v4-specific at-rules in the theme stylesheet as unknown/invalid, but these are config false positives rather than CSS issues. Update the stylelint setup to understand Tailwind v4 syntax by adding the appropriate Tailwind v4-aware plugin/config, and ensure the rules in the theme stylesheet using `@custom-variant`, `@utility`, and `@theme` are covered so the false errors go away.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@components.json`:
- Around line 6-12: Clear the Tailwind config reference in components.json for
the CSS-first setup: the current tailwind object points to tailwind.config.ts
even though the repo does not have that file. Update the components.json
tailwind settings so shadcn uses the CSS-first path via the existing css entry
and leaves tailwind.config empty, keeping the rest of the tailwind block
unchanged.
In `@src/app/page.tsx`:
- Line 115: The external Telegram launch in the page click handler uses
window.open with only "_blank", which leaves the opener accessible. Update the
onClick in page.tsx where TELEGRAM_BOT_URL is opened so it passes the safe
window features for a new tab, matching the existing external Link behavior
below by including noopener and noreferrer in the open call.
- Around line 139-145: The “Component Preview (Dev)” link in the page rendering
is currently shown to all existing users; restrict it to development-only unless
the route is meant to be public. Update the conditional around the existing user
block in `page.tsx` so the `Link` for `/dev` is rendered only when
`process.env.NODE_ENV === "development"` (or equivalent dev-only guard). Keep
the `isExisting` check, but add the environment gate in the same render path so
the preview link is hidden in production.
In `@src/db/connect.ts`:
- Line 3: Validate MONGODB_URI during startup in connect.ts instead of using the
non-null assertion. Update the initialization around the MONGODB_URI constant to
explicitly check process.env.MONGODB_URI and throw a clear configuration error
if it is missing, so callers fail fast before mongoose connection setup. Use the
existing connect logic and MONGODB_URI symbol to keep the fix localized.
In `@src/hooks/query/useRegisterTelegramUser.ts`:
- Around line 3-22: Replace the direct fetch call in registerTelegramUser with
the fetchy wrapper from `@/lib/fetchy`. Update the registerTelegramUser helper in
useRegisterTelegramUser so it uses fetchy’s request/response handling instead of
res.ok, and preserve the existing payload shape while letting the wrapper
normalize errors and API status handling for the /api/user POST flow.
In `@src/hooks/query/useUserCount.ts`:
- Around line 8-12: The HTTP call in fetchUserCount currently uses raw fetch
instead of the required fetchy wrapper. Update fetchUserCount to import and use
fetchy from "`@/lib/fetchy`" for the /api/user request, keeping the existing
response handling and error check intact while replacing the direct fetch usage.
- Around line 22-26: The fallback in useUserCount’s useEffect is masking valid
zero values because data.count || 80 treats 0 as missing. Update the count
assignment to preserve an explicit 0 while still handling undefined or null
values, and keep the change localized to the useEffect that calls setCount.
In `@src/styles/theme.css`:
- Around line 18-83: Remove the outdated `@theme` token block in `theme.css`
that defines the old `hsl(var(--...))` colors, radius formulas, and accordion
keyframes, and keep the newer `@theme inline` definitions as the single source
of truth. Update the `@theme` section so only the current theme tokens remain,
avoiding duplicate `--radius-*` and color variable definitions that can diverge.
- Around line 192-197: The `#tg-ui-root` rule is overriding the Telegram theme
tokens set at runtime, so the per-user values written by `theme.tsx` on
`document.documentElement` never apply to descendants. Update `theme.css` to
remove the hardcoded `--tg-theme-link-color` and `--tg-theme-button-color` from
`#tg-ui-root`, or, if the orange brand color is intended, remove the
corresponding runtime writes in `theme.tsx` so only one source of truth remains.
In `@tsconfig.json`:
- Line 17: The tsconfig JSX setting is drifting from Next.js defaults; revert
the `jsx` option in `tsconfig.json` back to `preserve` so the framework’s
expected TypeScript configuration is maintained. Update the existing `jsx` entry
directly and keep the rest of the compiler options unchanged.
---
Outside diff comments:
In `@src/app/api/subscription/payhere-link/route.ts`:
- Around line 109-110: Remove the leftover debug artifacts in the PayHere
success path inside the payhere-link route handler: delete the stray debugger
statement and stop logging the full data payload from the PayHere response. Keep
only a minimal success log in the relevant handler logic, and if needed replace
it with non-sensitive metadata (for example, a generic success message) in the
same response-handling block.
---
Nitpick comments:
In `@src/styles/theme.css`:
- Line 5: Stylelint is flagging Tailwind v4-specific at-rules in the theme
stylesheet as unknown/invalid, but these are config false positives rather than
CSS issues. Update the stylelint setup to understand Tailwind v4 syntax by
adding the appropriate Tailwind v4-aware plugin/config, and ensure the rules in
the theme stylesheet using `@custom-variant`, `@utility`, and `@theme` are covered so
the false errors go away.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e8f05ce0-da99-405c-ac81-f77718b459c2
⛔ Files ignored due to path filters (4)
bun.lockbis excluded by!**/bun.lockbpackage-lock.jsonis excluded by!**/package-lock.jsonpublic/emoji/bitcoin.svgis excluded by!**/*.svgpublic/emoji/gift.svgis excluded by!**/*.svg
📒 Files selected for processing (40)
.env.example.eslintrc.jsoncomponents.jsoneslint.config.mjsnext.config.mjspackage.jsonpostcss.config.mjssrc/app/api/subscription/cancel/route.tssrc/app/api/subscription/current/route.tssrc/app/api/subscription/payhere-link/route.tssrc/app/api/transaction/latest/route.tssrc/app/api/transaction/list/route.tssrc/app/api/user/[id]/route.tssrc/app/api/user/exists/[telegramId]/route.tssrc/app/context/providers.tsxsrc/app/context/theme.tsxsrc/app/dashboard/history/page.tsxsrc/app/dashboard/invite/page.tsxsrc/app/dashboard/subscription/page.tsxsrc/app/dev/page.tsxsrc/app/layout.tsxsrc/app/onboard/page.tsxsrc/app/page.tsxsrc/app/subscription/page.tsxsrc/app/verification/page.tsxsrc/components/skeletons/BalanceCardSkeleton.tsxsrc/components/ui/button.tsxsrc/components/ui/clickable-card.tsxsrc/components/ui/plan-card.tsxsrc/components/ui/search-field.tsxsrc/components/ui/text-field.tsxsrc/components/ui/visible-toggle.tsxsrc/db/connect.tssrc/hooks/query/useRegisterTelegramUser.tssrc/hooks/query/useUserCount.tssrc/lib/constants.tssrc/lib/query-keys.tssrc/styles/theme.csstailwind.config.tstsconfig.json
💤 Files with no reviewable changes (2)
- .eslintrc.json
- tailwind.config.ts
✅ Files skipped from review due to trivial changes (11)
- src/components/skeletons/BalanceCardSkeleton.tsx
- src/app/dashboard/invite/page.tsx
- src/components/ui/search-field.tsx
- .env.example
- next.config.mjs
- eslint.config.mjs
- src/app/subscription/page.tsx
- src/lib/constants.ts
- src/app/dashboard/history/page.tsx
- src/app/api/subscription/current/route.ts
- src/app/verification/page.tsx
🚧 Files skipped from review as they are similar to previous changes (7)
- src/components/ui/clickable-card.tsx
- src/components/ui/button.tsx
- src/components/ui/plan-card.tsx
- src/components/ui/visible-toggle.tsx
- src/app/dev/page.tsx
- src/components/ui/text-field.tsx
- src/app/dashboard/subscription/page.tsx
* feat: add v2 dashboard components and hooks - Implemented Activity, News, Plans, and Tasks pages for the v2 dashboard. - Created a new layout component for the dashboard with bottom navigation. - Developed Wallet page with wallet summary, transaction history, and performance metrics. - Added TotalValueCard, PlanSummaryCard, PerformanceGrid, and PortfolioChart components for enhanced dashboard visualization. - Introduced hooks for fetching subscription and transaction data. - Added utility functions for formatting currency and dates. - Integrated user authentication guard to manage access to dashboard features. * feat: enhance dashboard UI with new components and user data integration * feat: update dashboard components with new UI elements and improved error handling * feat: update dashboard components with improved styling and CSS variable integration * feat: enhance dashboard UI with new mock data integration and improved component styling * feat: enhance dashboard UI with dark mode support and improved component styling * feat: implement masking of digits in formatted strings for improved privacy * feat: update PortfolioChart to use Button component and improve button styling
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/hooks/query/useRegisterTelegramUser.ts (1)
23-28: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winGate registration on Telegram ID and dedupe per user.
usernameis optional, so this skips valid Telegram users without one; the effect can also re-submit the sameidon re-renders. Useidas the guard and submit only once per user.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/query/useRegisterTelegramUser.ts` around lines 23 - 28, The registration effect in useRegisterTelegramUser currently requires both username and id, which skips valid Telegram users without a username and can also resend the same user on re-renders. Update the useEffect guard to rely on authData?.user.id only, and add per-user deduping so mutate is called once per Telegram ID even if authData or launchParams changes; use the useRegisterTelegramUser hook and mutate call as the main points to adjust.
🧹 Nitpick comments (8)
src/components/v2/bottomNavigation.tsx (1)
112-141: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winConsider
next/linkfor prefetching instead ofrouter.pushon click.Using
onClick={() => router.push(...)}skips Next.js's automatic link prefetching that<Link>provides for viewport-visible links, which would improve perceived navigation speed for this always-visible bottom nav.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/v2/bottomNavigation.tsx` around lines 112 - 141, The bottom navigation items use router.push in SegmentedControl.Item click handlers, which bypasses Next.js link prefetching. Update the nav rendering in bottomNavigation.tsx to use next/link for each item instead of handling navigation with onClick, while keeping the existing active-state styling based on pathname and item.href. Use the existing navItems map and SegmentedControl.Item/Icon/label structure to wire the link target directly so viewport-visible items can prefetch automatically.src/components/v2/dashboard/PortfolioChart.tsx (1)
56-63: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winMonth-boundary edge case in period cutoff calculation.
cutoff.setMonth(cutoff.getMonth() - months)can roll into the following month when the current day-of-month doesn't exist in the target month (e.g. Mar 31 minus 1 month rolls to Mar 3, not Feb 28), skewing the "1M"/"3M"/etc. window. Thewindowed.length >= 2 ? windowed : pointsfallback limits real-world impact, but the filter can still silently include/exclude more data than intended.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/v2/dashboard/PortfolioChart.tsx` around lines 56 - 63, The cutoff logic in filterByPeriod is vulnerable to month-boundary rollover because setMonth can shift into the wrong month when the target month has fewer days. Update the period window calculation in filterByPeriod so the cutoff is based on a stable month boundary (for example by normalizing to the start of the day/month or using a date subtraction approach that preserves the intended calendar window), while keeping the existing fallback behavior for small result sets.src/lib/formatters.ts (1)
55-61: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win
fmtRelativeDayscompares raw millisecond deltas, not calendar days.Dividing the exact
Date.now()difference by 24h can report "Today" or "N days ago" incorrectly near midnight boundaries (e.g. a date only 10 minutes into tomorrow still rounds todiff === 0→ "Today"). Since this is used for subscription reward dates (e.g. "Next Reward"), consider normalizing both dates to midnight before diffing for calendar-accurate results.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/formatters.ts` around lines 55 - 61, `fmtRelativeDays` is using a raw millisecond delta, which can mislabel calendar dates near midnight. Update `fmtRelativeDays` to normalize both the input date and the current date to midnight before computing the day difference, then keep the existing "Today"/"In X days"/"X days ago" formatting based on that calendar-day diff. Use the existing `fmtRelativeDays` helper in `src/lib/formatters.ts` as the only place to change this behavior.src/hooks/useAuthGuard.ts (1)
12-27: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winNo loading/authenticated state exposed, causing content flash before redirect.
The hook redirects unauthenticated users inside a
useEffect, but the redirect only fires after the first render/commit. Consumers (e.g.DashboardV2Layout) renderchildrenunconditionally regardless of auth status, so an unauthenticated user briefly sees the dashboard shell before being pushed to/onboard. Consider returning anisChecking/isAuthenticatedflag so callers can gate rendering until the check completes.🔧 Suggested fix
export function useAuthGuard() { const router = useRouter(); const { isExistingUser, setIsExistingUser } = useStore(); + const [isChecking, setIsChecking] = useState(true); useEffect(() => { const token = getAuthTokenFromStorage(); if (token) { setIsExistingUser(getIsExistingUserFromStorage()); + setIsChecking(false); } else { router.push("/onboard"); } }, [router, setIsExistingUser]); - return { isExistingUser }; + return { isExistingUser, isChecking }; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hooks/useAuthGuard.ts` around lines 12 - 27, The useAuthGuard hook only exposes isExistingUser and performs the redirect in useEffect, which lets consumers like DashboardV2Layout render protected UI before auth is resolved. Update useAuthGuard to also expose a loading/checking or authenticated flag that stays false/unknown until getAuthTokenFromStorage and getIsExistingUserFromStorage have run, and set it appropriately before/after the redirect. Then have callers gate rendering of children based on that flag so unauthenticated users do not briefly see the dashboard shell before router.push("/onboard") runs.src/app/v2/dashboard/layout.tsx (1)
7-9: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
isExistingUseris destructured but unused.Dead variable — either use it to gate
children/redirect UX (seeuseAuthGuard.tscomment) or drop it.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/app/v2/dashboard/layout.tsx` around lines 7 - 9, The DashboardV2Layout component destructures isExistingUser from useAuthGuard but never uses it, leaving dead state in the layout. Update DashboardV2Layout to either use isExistingUser to conditionally render children or trigger the intended redirect/loading UX, or remove the destructuring entirely if it is not needed.src/components/v2/dashboard/PlanSummaryCard.tsx (2)
20-32: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueCustom inline
ChevronRightSVG instead oflucide-react.
TotalValueCard.tsxalready imports icons (ChevronDown,TrendingUp,TrendingDown) fromlucide-reactin this same feature area. Consider usinglucide-react'sChevronRighthere instead of a bespoke inline SVG for consistency.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/v2/dashboard/PlanSummaryCard.tsx` around lines 20 - 32, Replace the custom inline ChevronRight SVG in PlanSummaryCard with lucide-react’s ChevronRight icon for consistency with the other icons already used in this feature area. Update the ChevronRight symbol to come from lucide-react, remove the bespoke svg/path markup, and keep the existing usage points in PlanSummaryCard aligned with the imported icon API.
106-113: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueHardcoded "Rs" prefix ignores
subscription.currency.The
Subscriptiontype carries acurrencyfield (seeMOCK_SUBSCRIPTION.currencyin dashboard/page.tsx), but this component always renders a static "Rs" prefix regardless of it. Fine while LKR-only, but drifts from the data model if multi-currency is ever needed.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/v2/dashboard/PlanSummaryCard.tsx` around lines 106 - 113, The price display in PlanSummaryCard is hardcoding the "Rs" prefix instead of using the Subscription currency field, so update the currency label logic to derive from subscription.currency in the PlanSummaryCard component. Use the existing subscription object and fmtLkr(price) rendering to keep the amount formatting, but replace the static prefix with a currency-aware value so the UI stays aligned with the data model and future multi-currency support.src/app/context/tma.tsx (1)
8-14: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winSilent catch-all hides genuine failures.
The empty
catchswallows every error, including real regressions inside actual Telegram clients, with no logging to help diagnose future issues.🔍 Suggested logging for diagnosability
- } catch { + } catch (error) { // Not running inside Telegram (e.g. opened directly in a browser during // local development) — skip native setup instead of crashing the app. + if (process.env.NODE_ENV !== "production") { + console.warn("TMA setup skipped:", error); + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/app/context/tma.tsx` around lines 8 - 14, The empty catch in the tma context setup is swallowing all failures from initMiniApp and postEvent with no visibility. Update the try/catch in the tma.tsx initialization flow to catch the error as a variable, log it with enough context, and only suppress the expected non-Telegram/browser case; keep the safe fallback but avoid hiding genuine regressions in initMiniApp or postEvent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/app/page.tsx`:
- Around line 93-97: The wallet CTA is rendering a Link around an interactive
Button, which should be changed to use the Telegram UI Button in anchor mode
instead of nesting controls. Update the button usage in page.tsx where the
wallet action is rendered so the Button itself acts as the link target to
/v2/dashboard?tab=wallet, preserving the same label and styling while removing
the outer Link wrapper.
In `@src/app/v2/dashboard/page.tsx`:
- Around line 16-22: `MOCK_SUMMARY.total_lkr` is using a number while
`DCSummary` still declares it as a string, causing the dashboard mock and
consumer handling to diverge. Update the core domain type in `src/lib/types.ts`
so `DCSummary.total_lkr` accepts the actual shape handled by the app (string |
number), then align `MOCK_SUMMARY` in `dashboard/page.tsx` to that shared type.
After that, simplify the `typeof` handling around the `total_lkr` usage so it
reflects the corrected domain model instead of compensating for a mock-only
mismatch.
- Around line 44-58: The mock fallback in WalletV2Page is too broad and can hide
real fetch failures by treating any non-loading empty summary as mock data.
Update the logic around useWalletSummary, useTransactionHistory, and the derived
usingMock/summary/subscription/transactions values so mock data is only used in
development, and add an explicit error state for production when the wallet
queries fail instead of falling back to MOCK_SUMMARY, MOCK_SUBSCRIPTION, or
MOCK_TRANSACTIONS.
In `@src/components/ui/visible-toggle.tsx`:
- Around line 3-4: The VisibleToggle component currently exposes
VisibleToggleProps.className but never applies it, so callers see no styling
effect. Update the VisibleToggle implementation to either re-enable the cn(...)
merge and any related mode logic in the component body, or remove className from
the prop contract if it is intentionally unsupported; also clean up the unused
cn import and any commented-out mode code. Keep the fix centered on
VisibleToggle and VisibleToggleProps so the public API matches the actual
behavior.
In `@src/components/v2/dashboard/PortfolioChart.tsx`:
- Around line 180-195: The active period buttons in PortfolioChart lose their
selected-state styling because the conditional `period === p` class logic is
commented out. Restore the `cn(...)` branch inside the `PERIODS.map` button
render so the currently selected period gets a distinct visual style while the
others keep the default look.
In `@src/hooks/query/useKycStatus.ts`:
- Around line 12-26: The query key in useKycStatus is using a raw literal
instead of the centralized registry. Update useKycStatus to import and use the
matching key from `@/lib/query-keys`, following the same pattern as
useWalletSummary with queryKeys.walletSummary, so the query stays consistent
with app-wide invalidation and naming.
In `@src/hooks/query/useSubscriptionCurrent.ts`:
- Around line 16-26: The subscription query in useSubscriptionCurrent currently
hardcodes queryKey as a string literal, so update it to use the shared key from
`@/lib/query-keys` instead. Import the appropriate subscription current key and
replace the inline ["subscription-current"] in useQuery with that constant,
keeping the rest of the queryFn and enabled logic unchanged.
In `@src/hooks/query/useTransactionHistory.ts`:
- Around line 40-46: The transaction history fetch in useTransactionHistory is
limited to the first 100 rows, which can make PortfolioChart.buildPoints compute
incorrect running totals for users with more than one page of successful DCA
transactions. Update the fetch logic in useTransactionHistory to retrieve the
complete transaction history (for example by paging through
/api/transaction/list until all results are collected, or by requesting a
higher/unbounded limit if supported), and keep the returned data shape
compatible with TransactionListResponse so downstream invested and portfolio
calculations use the full history.
In `@src/hooks/useUser.ts`:
- Around line 14-15: useUser() currently calls useLaunchParams()
unconditionally, which can throw outside Telegram and crash browser/dev
consumers. Update the useUser hook to either guard the useLaunchParams() call
behind a Telegram environment check or return a safe fallback when Telegram
context is unavailable, while keeping telegramUser derived safely from
launchParams.initData?.user.
---
Outside diff comments:
In `@src/hooks/query/useRegisterTelegramUser.ts`:
- Around line 23-28: The registration effect in useRegisterTelegramUser
currently requires both username and id, which skips valid Telegram users
without a username and can also resend the same user on re-renders. Update the
useEffect guard to rely on authData?.user.id only, and add per-user deduping so
mutate is called once per Telegram ID even if authData or launchParams changes;
use the useRegisterTelegramUser hook and mutate call as the main points to
adjust.
---
Nitpick comments:
In `@src/app/context/tma.tsx`:
- Around line 8-14: The empty catch in the tma context setup is swallowing all
failures from initMiniApp and postEvent with no visibility. Update the try/catch
in the tma.tsx initialization flow to catch the error as a variable, log it with
enough context, and only suppress the expected non-Telegram/browser case; keep
the safe fallback but avoid hiding genuine regressions in initMiniApp or
postEvent.
In `@src/app/v2/dashboard/layout.tsx`:
- Around line 7-9: The DashboardV2Layout component destructures isExistingUser
from useAuthGuard but never uses it, leaving dead state in the layout. Update
DashboardV2Layout to either use isExistingUser to conditionally render children
or trigger the intended redirect/loading UX, or remove the destructuring
entirely if it is not needed.
In `@src/components/v2/bottomNavigation.tsx`:
- Around line 112-141: The bottom navigation items use router.push in
SegmentedControl.Item click handlers, which bypasses Next.js link prefetching.
Update the nav rendering in bottomNavigation.tsx to use next/link for each item
instead of handling navigation with onClick, while keeping the existing
active-state styling based on pathname and item.href. Use the existing navItems
map and SegmentedControl.Item/Icon/label structure to wire the link target
directly so viewport-visible items can prefetch automatically.
In `@src/components/v2/dashboard/PlanSummaryCard.tsx`:
- Around line 20-32: Replace the custom inline ChevronRight SVG in
PlanSummaryCard with lucide-react’s ChevronRight icon for consistency with the
other icons already used in this feature area. Update the ChevronRight symbol to
come from lucide-react, remove the bespoke svg/path markup, and keep the
existing usage points in PlanSummaryCard aligned with the imported icon API.
- Around line 106-113: The price display in PlanSummaryCard is hardcoding the
"Rs" prefix instead of using the Subscription currency field, so update the
currency label logic to derive from subscription.currency in the PlanSummaryCard
component. Use the existing subscription object and fmtLkr(price) rendering to
keep the amount formatting, but replace the static prefix with a currency-aware
value so the UI stays aligned with the data model and future multi-currency
support.
In `@src/components/v2/dashboard/PortfolioChart.tsx`:
- Around line 56-63: The cutoff logic in filterByPeriod is vulnerable to
month-boundary rollover because setMonth can shift into the wrong month when the
target month has fewer days. Update the period window calculation in
filterByPeriod so the cutoff is based on a stable month boundary (for example by
normalizing to the start of the day/month or using a date subtraction approach
that preserves the intended calendar window), while keeping the existing
fallback behavior for small result sets.
In `@src/hooks/useAuthGuard.ts`:
- Around line 12-27: The useAuthGuard hook only exposes isExistingUser and
performs the redirect in useEffect, which lets consumers like DashboardV2Layout
render protected UI before auth is resolved. Update useAuthGuard to also expose
a loading/checking or authenticated flag that stays false/unknown until
getAuthTokenFromStorage and getIsExistingUserFromStorage have run, and set it
appropriately before/after the redirect. Then have callers gate rendering of
children based on that flag so unauthenticated users do not briefly see the
dashboard shell before router.push("/onboard") runs.
In `@src/lib/formatters.ts`:
- Around line 55-61: `fmtRelativeDays` is using a raw millisecond delta, which
can mislabel calendar dates near midnight. Update `fmtRelativeDays` to normalize
both the input date and the current date to midnight before computing the day
difference, then keep the existing "Today"/"In X days"/"X days ago" formatting
based on that calendar-day diff. Use the existing `fmtRelativeDays` helper in
`src/lib/formatters.ts` as the only place to change this behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: bc63bd51-b5be-4dd7-82bf-02f9d03921f4
⛔ Files ignored due to path filters (16)
public/emoji/bluewhale.svgis excluded by!**/*.svgpublic/emoji/calculator.svgis excluded by!**/*.svgpublic/emoji/calendar1.svgis excluded by!**/*.svgpublic/emoji/calendar2.svgis excluded by!**/*.svgpublic/emoji/coins.svgis excluded by!**/*.svgpublic/emoji/crab.svgis excluded by!**/*.svgpublic/emoji/graph.svgis excluded by!**/*.svgpublic/emoji/shark.svgis excluded by!**/*.svgpublic/emoji/shrimp.svgis excluded by!**/*.svgpublic/emoji/wallet.svgis excluded by!**/*.svgpublic/emoji/whale.svgis excluded by!**/*.svgpublic/icons/activity.svgis excluded by!**/*.svgpublic/icons/news.svgis excluded by!**/*.svgpublic/icons/plans.svgis excluded by!**/*.svgpublic/icons/tasks.svgis excluded by!**/*.svgpublic/icons/wallet.svgis excluded by!**/*.svg
📒 Files selected for processing (30)
next.config.mjspublic/bg/star.webppublic/bg/wallet.webpsrc/app/context/tma.tsxsrc/app/page.tsxsrc/app/v2/dashboard/activity/page.tsxsrc/app/v2/dashboard/layout.tsxsrc/app/v2/dashboard/news/page.tsxsrc/app/v2/dashboard/page.tsxsrc/app/v2/dashboard/plans/page.tsxsrc/app/v2/dashboard/tasks/page.tsxsrc/components/ui/visible-toggle.tsxsrc/components/v2/bottomNavigation.tsxsrc/components/v2/dashboard/DashboardTopBar.tsxsrc/components/v2/dashboard/PerformanceGrid.tsxsrc/components/v2/dashboard/PlanSummaryCard.tsxsrc/components/v2/dashboard/PortfolioChart.tsxsrc/components/v2/dashboard/TotalValueCard.tsxsrc/db/connect.tssrc/hooks/query/useKycStatus.tssrc/hooks/query/useRegisterTelegramUser.tssrc/hooks/query/useSubscriptionCurrent.tssrc/hooks/query/useTransactionHistory.tssrc/hooks/query/useUserCount.tssrc/hooks/query/useWalletSummary.tssrc/hooks/useAuthGuard.tssrc/hooks/useUser.tssrc/lib/formatters.tssrc/lib/store.tssrc/lib/types.ts
✅ Files skipped from review due to trivial changes (2)
- src/app/v2/dashboard/plans/page.tsx
- src/app/v2/dashboard/news/page.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- src/db/connect.ts
| <Link href="/v2/dashboard?tab=wallet" className="block"> | ||
| <Button mode="filled" size="l" stretched style={{ borderRadius: "12px" }}> | ||
| {isExisting ? "Open My Wallet" : "Start Using Wallet"} | ||
| </Button> | ||
| </Link> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the relevant page and any Button/Link usage around the CTA.
printf '\n== src/app/page.tsx ==\n'
cat -n src/app/page.tsx | sed -n '1,220p'
printf '\n== Search for Button component definition and usage ==\n'
rg -n "export .*Button|function Button|const Button|forwardRef.*Button|asChild|component=\"a\"|Link href=.*Button|<Button" src -g '!**/*.d.ts'
printf '\n== Search for the specific wallet CTA ==\n'
rg -n "Open My Wallet|Start Using Wallet|/v2/dashboard\\?tab=wallet" srcRepository: CeyLabs/BitcoinDeepaBot-TMA
Length of output: 12719
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== src/app/task/page.tsx relevant Button usages ==\n'
cat -n src/app/task/page.tsx | sed -n '1,180p'
printf '\n== Search for Button Component props across repo ==\n'
rg -n 'Component="a"|Component=\{?["'\'']a["'\'']?\}|href=.*<Button|<Button[^>]*Component=' srcRepository: CeyLabs/BitcoinDeepaBot-TMA
Length of output: 7006
Use the Button’s anchor mode here
Link wraps an interactive Button, which creates nested controls. Use the Telegram UI button as an anchor instead:
- <Link href="/v2/dashboard?tab=wallet" className="block">
- <Button mode="filled" size="l" stretched style={{ borderRadius: "12px" }}>
+ <Button Component="a" href="/v2/dashboard?tab=wallet" mode="filled" size="l" stretched style={{ borderRadius: "12px" }}>
{isExisting ? "Open My Wallet" : "Start Using Wallet"}
- </Button>
- </Link>
+ </Button>📝 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.
| <Link href="/v2/dashboard?tab=wallet" className="block"> | |
| <Button mode="filled" size="l" stretched style={{ borderRadius: "12px" }}> | |
| {isExisting ? "Open My Wallet" : "Start Using Wallet"} | |
| </Button> | |
| </Link> | |
| <Button Component="a" href="/v2/dashboard?tab=wallet" mode="filled" size="l" stretched style={{ borderRadius: "12px" }}> | |
| {isExisting ? "Open My Wallet" : "Start Using Wallet"} | |
| </Button> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/app/page.tsx` around lines 93 - 97, The wallet CTA is rendering a Link
around an interactive Button, which should be changed to use the Telegram UI
Button in anchor mode instead of nesting controls. Update the button usage in
page.tsx where the wallet action is rendered so the Button itself acts as the
link target to /v2/dashboard?tab=wallet, preserving the same label and styling
while removing the outer Link wrapper.
| const MOCK_SUMMARY = { | ||
| dca: { balance: 107000, spent: 32000, avg_btc_price: 29500000 }, | ||
| total_balance: 107000, | ||
| total_lkr: 40000, | ||
| currency: "LKR", | ||
| "24_hr_change": 2.15, | ||
| }; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
MOCK_SUMMARY.total_lkr type diverges from DCSummary.
MOCK_SUMMARY.total_lkr is a number (40000) while DCSummary.total_lkr (types.ts) is declared as string. The typeof branch at Lines 61-63 exists purely to cope with this drift, which suggests the real API can also return a number and DCSummary should reflect that (string | number) rather than the mock silently diverging from the declared type.
As per coding guidelines, "Use the core domain types defined in src/lib/types.ts" — the domain type should accurately describe the shape actually handled by consumers.
Also applies to: 60-64
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/app/v2/dashboard/page.tsx` around lines 16 - 22, `MOCK_SUMMARY.total_lkr`
is using a number while `DCSummary` still declares it as a string, causing the
dashboard mock and consumer handling to diverge. Update the core domain type in
`src/lib/types.ts` so `DCSummary.total_lkr` accepts the actual shape handled by
the app (string | number), then align `MOCK_SUMMARY` in `dashboard/page.tsx` to
that shared type. After that, simplify the `typeof` handling around the
`total_lkr` usage so it reflects the corrected domain model instead of
compensating for a mock-only mismatch.
Source: Coding guidelines
| export default function WalletV2Page() { | ||
| const { balanceVisible } = useStore(); | ||
| const { subscription: realSubscription } = useUser(); | ||
|
|
||
| const { data: rawSummary, isLoading: isSummaryLoading } = useWalletSummary(); | ||
| const { data: rawTransactions } = useTransactionHistory(); | ||
|
|
||
| const usingMock = !isSummaryLoading && !rawSummary; | ||
| const summary = rawSummary ?? (usingMock ? MOCK_SUMMARY : undefined); | ||
| const subscription = realSubscription ?? (usingMock ? MOCK_SUBSCRIPTION : null); | ||
| const transactions = rawTransactions?.length | ||
| ? rawTransactions | ||
| : usingMock | ||
| ? MOCK_TRANSACTIONS | ||
| : rawTransactions; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
Mock data can silently mask real backend errors in production.
usingMock only checks !isSummaryLoading && !rawSummary — it can't distinguish "no auth token yet" from a genuine fetch failure (expired token, 401, 500, network error). If useWalletSummary() errors out in production, this renders MOCK_SUMMARY/MOCK_SUBSCRIPTION/MOCK_TRANSACTIONS as if they were the user's real wallet balance and subscription, with no error indication. For a financial dashboard this is a serious correctness/trust issue.
Gate the mock fallback to development only, and separately surface a real error state instead of silently substituting fake data.
🐛 Proposed fix
- const { data: rawSummary, isLoading: isSummaryLoading } = useWalletSummary();
+ const { data: rawSummary, isLoading: isSummaryLoading, isError: isSummaryError } = useWalletSummary();
const { data: rawTransactions } = useTransactionHistory();
- const usingMock = !isSummaryLoading && !rawSummary;
+ const usingMock =
+ process.env.NODE_ENV !== "production" &&
+ !isSummaryLoading &&
+ !isSummaryError &&
+ !rawSummary;
+
+ if (isSummaryError) {
+ // TODO: render an explicit error/retry state instead of falling through
+ }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/app/v2/dashboard/page.tsx` around lines 44 - 58, The mock fallback in
WalletV2Page is too broad and can hide real fetch failures by treating any
non-loading empty summary as mock data. Update the logic around
useWalletSummary, useTransactionHistory, and the derived
usingMock/summary/subscription/transactions values so mock data is only used in
development, and add an explicit error state for production when the wallet
queries fail instead of falling back to MOCK_SUMMARY, MOCK_SUBSCRIPTION, or
MOCK_TRANSACTIONS.
| import { Eye, EyeOff } from "lucide-react"; | ||
| import { IconButton } from "@telegram-apps/telegram-ui"; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
className prop is accepted but silently ignored.
VisibleToggleProps.className is still part of the public API, but its application (cn(...)) is commented out, so any caller passing className gets no effect. Combined with the unused cn import and commented-out mode logic, this looks like an incomplete refactor left in.
🧹 Suggested cleanup
<IconButton
- // mode={dark ? "plain" : "outline"}
mode="gray"
size="m"
onClick={onToggle}
- // className={cn(!dark && "bg-white! dark:bg-[`#1b2027`]!", className)}
+ className={cn(!dark && "bg-white! dark:bg-[`#1b2027`]!", className)}
>If the mode/className behavior is intentionally deferred, consider removing the now-dead className prop and cn import until re-enabled, to keep the component's contract accurate.
Also applies to: 14-31
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/ui/visible-toggle.tsx` around lines 3 - 4, The VisibleToggle
component currently exposes VisibleToggleProps.className but never applies it,
so callers see no styling effect. Update the VisibleToggle implementation to
either re-enable the cn(...) merge and any related mode logic in the component
body, or remove className from the prop contract if it is intentionally
unsupported; also clean up the unused cn import and any commented-out mode code.
Keep the fix centered on VisibleToggle and VisibleToggleProps so the public API
matches the actual behavior.
| {PERIODS.map((p) => ( | ||
| <Button | ||
| key={p} | ||
| mode="gray" | ||
| size="s" stretched | ||
| onClick={() => setPeriod(p)} | ||
| className={cn( | ||
| "rounded-[12px]!", | ||
| // period === p | ||
| // ? "bg-[#e2e8f0]! text-[#1b2027]! dark:bg-[#334155]! dark:text-[#f1f5f9]!" | ||
| // : "bg-[#eeeff3]! text-[#64748b]! dark:bg-transparent! dark:text-[#94a3b8]!" | ||
| )} | ||
| > | ||
| <span className="text-[12px]">{p}</span> | ||
| </Button> | ||
| ))} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Active period button has no visual indication — selected-state styling is commented out.
The className logic that would highlight the currently selected period (period === p ? ... : ...) is commented out, leaving cn("rounded-[12px]!") with no conditional styling. Users can no longer tell which period (1M/3M/6M/1Y/All) is active.
🐛 Suggested fix
className={cn(
"rounded-[12px]!",
- // period === p
- // ? "bg-[`#e2e8f0`]! text-[`#1b2027`]! dark:bg-[`#334155`]! dark:text-[`#f1f5f9`]!"
- // : "bg-[`#eeeff3`]! text-[`#64748b`]! dark:bg-transparent! dark:text-[`#94a3b8`]!"
+ period === p
+ ? "bg-[`#e2e8f0`]! text-[`#1b2027`]! dark:bg-[`#334155`]! dark:text-[`#f1f5f9`]!"
+ : "bg-[`#eeeff3`]! text-[`#64748b`]! dark:bg-transparent! dark:text-[`#94a3b8`]!"
)}📝 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.
| {PERIODS.map((p) => ( | |
| <Button | |
| key={p} | |
| mode="gray" | |
| size="s" stretched | |
| onClick={() => setPeriod(p)} | |
| className={cn( | |
| "rounded-[12px]!", | |
| // period === p | |
| // ? "bg-[#e2e8f0]! text-[#1b2027]! dark:bg-[#334155]! dark:text-[#f1f5f9]!" | |
| // : "bg-[#eeeff3]! text-[#64748b]! dark:bg-transparent! dark:text-[#94a3b8]!" | |
| )} | |
| > | |
| <span className="text-[12px]">{p}</span> | |
| </Button> | |
| ))} | |
| {PERIODS.map((p) => ( | |
| <Button | |
| key={p} | |
| mode="gray" | |
| size="s" stretched | |
| onClick={() => setPeriod(p)} | |
| className={cn( | |
| "rounded-[12px]!", | |
| period === p | |
| ? "bg-[`#e2e8f0`]! text-[`#1b2027`]! dark:bg-[`#334155`]! dark:text-[`#f1f5f9`]!" | |
| : "bg-[`#eeeff3`]! text-[`#64748b`]! dark:bg-transparent! dark:text-[`#94a3b8`]!" | |
| )} | |
| > | |
| <span className="text-[12px]">{p}</span> | |
| </Button> | |
| ))} |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/v2/dashboard/PortfolioChart.tsx` around lines 180 - 195, The
active period buttons in PortfolioChart lose their selected-state styling
because the conditional `period === p` class logic is commented out. Restore the
`cn(...)` branch inside the `PERIODS.map` button render so the currently
selected period gets a distinct visual style while the others keep the default
look.
| export function useKycStatus() { | ||
| const authToken = getAuthTokenFromStorage(); | ||
|
|
||
| return useQuery<NonNullable<User["kycStatus"]>>({ | ||
| queryKey: ["kyc-status"], | ||
| queryFn: async () => { | ||
| const data = await fetchy.get<KycStatusResponse>("/api/user/kyc/status", { | ||
| headers: { Authorization: `Bearer ${authToken}` }, | ||
| shouldCache: false, | ||
| }); | ||
| return data.status; | ||
| }, | ||
| enabled: !!authToken, | ||
| }); | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Query key not sourced from @/lib/query-keys.
queryKey: ["kyc-status"] is a raw literal, unlike useWalletSummary's queryKeys.walletSummary. This bypasses the centralized key registry and risks inconsistent invalidation/typos elsewhere in the app.
As per coding guidelines, "Use TanStack Query for server state with query keys sourced from @/lib/query-keys".
♻️ Proposed fix
+import { queryKeys } from "`@/lib/query-keys`";
+
export function useKycStatus() {
const authToken = getAuthTokenFromStorage();
return useQuery<NonNullable<User["kycStatus"]>>({
- queryKey: ["kyc-status"],
+ queryKey: queryKeys.kycStatus,📝 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.
| export function useKycStatus() { | |
| const authToken = getAuthTokenFromStorage(); | |
| return useQuery<NonNullable<User["kycStatus"]>>({ | |
| queryKey: ["kyc-status"], | |
| queryFn: async () => { | |
| const data = await fetchy.get<KycStatusResponse>("/api/user/kyc/status", { | |
| headers: { Authorization: `Bearer ${authToken}` }, | |
| shouldCache: false, | |
| }); | |
| return data.status; | |
| }, | |
| enabled: !!authToken, | |
| }); | |
| } | |
| import { queryKeys } from "`@/lib/query-keys`"; | |
| export function useKycStatus() { | |
| const authToken = getAuthTokenFromStorage(); | |
| return useQuery<NonNullable<User["kycStatus"]>>({ | |
| queryKey: queryKeys.kycStatus, | |
| queryFn: async () => { | |
| const data = await fetchy.get<KycStatusResponse>("/api/user/kyc/status", { | |
| headers: { Authorization: `Bearer ${authToken}` }, | |
| shouldCache: false, | |
| }); | |
| return data.status; | |
| }, | |
| enabled: !!authToken, | |
| }); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/hooks/query/useKycStatus.ts` around lines 12 - 26, The query key in
useKycStatus is using a raw literal instead of the centralized registry. Update
useKycStatus to import and use the matching key from `@/lib/query-keys`, following
the same pattern as useWalletSummary with queryKeys.walletSummary, so the query
stays consistent with app-wide invalidation and naming.
Source: Coding guidelines
| return useQuery<Subscription | null>({ | ||
| queryKey: ["subscription-current"], | ||
| queryFn: async () => { | ||
| const data = await fetchy.get<SubscriptionCurrentResponse>("/api/subscription/current", { | ||
| headers: { Authorization: `Bearer ${authToken}` }, | ||
| shouldCache: false, | ||
| }); | ||
| return data.subscription ?? null; | ||
| }, | ||
| enabled: !!authToken, | ||
| }); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Query key not sourced from @/lib/query-keys.
queryKey: ["subscription-current"] is a hardcoded literal instead of being imported from the shared query-keys module. As per coding guidelines, "Use TanStack Query for server state with query keys sourced from @/lib/query-keys".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/hooks/query/useSubscriptionCurrent.ts` around lines 16 - 26, The
subscription query in useSubscriptionCurrent currently hardcodes queryKey as a
string literal, so update it to use the shared key from `@/lib/query-keys`
instead. Import the appropriate subscription current key and replace the inline
["subscription-current"] in useQuery with that constant, keeping the rest of the
queryFn and enabled logic unchanged.
Source: Coding guidelines
| const data = await fetchy.get<TransactionListResponse>( | ||
| "/api/transaction/list?limit=100", | ||
| { | ||
| headers: { Authorization: `Bearer ${authToken}` }, | ||
| shouldCache: false, | ||
| } | ||
| ); |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Map the relevant files first
printf '\n== useTransactionHistory.ts outline ==\n'
ast-grep outline src/hooks/query/useTransactionHistory.ts --view expanded || true
printf '\n== portfolio chart references ==\n'
rg -n "buildPoints|cumulativeSats|cumulativeInvested|transaction/list|limit=100|TransactionListResponse" src -S
printf '\n== file sizes ==\n'
wc -l src/hooks/query/useTransactionHistory.ts src/components/PortfolioChart* 2>/dev/null || trueRepository: CeyLabs/BitcoinDeepaBot-TMA
Length of output: 2361
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Read the relevant hook and chart logic in small chunks
printf '\n== useTransactionHistory.ts (lines 1-220) ==\n'
cat -n src/hooks/query/useTransactionHistory.ts | sed -n '1,220p'
printf '\n== portfolio chart files matching buildPoints ==\n'
for f in $(rg -l "buildPoints|cumulativeSats|cumulativeInvested" src); do
echo "\n### $f"
cat -n "$f" | sed -n '1,260p'
doneRepository: CeyLabs/BitcoinDeepaBot-TMA
Length of output: 11228
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== API route for transaction list ==\n'
cat -n src/app/api/transaction/list/route.ts | sed -n '1,240p'
printf '\n== consumers of useTransactionHistory ==\n'
rg -n "useTransactionHistory\\(" src -S
printf '\n== transaction/list usages and ordering hints ==\n'
rg -n "transaction/list|order|limit|offset|page|cursor|pagination" src/app src/hooks src/components -SRepository: CeyLabs/BitcoinDeepaBot-TMA
Length of output: 23375
Fetch the full history instead of the first 100 rows. src/hooks/query/useTransactionHistory.ts:40-46 only loads one page, so users with >100 successful DCA transactions will pass an incomplete history into PortfolioChart.buildPoints, making the running invested and portfolio totals wrong.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/hooks/query/useTransactionHistory.ts` around lines 40 - 46, The
transaction history fetch in useTransactionHistory is limited to the first 100
rows, which can make PortfolioChart.buildPoints compute incorrect running totals
for users with more than one page of successful DCA transactions. Update the
fetch logic in useTransactionHistory to retrieve the complete transaction
history (for example by paging through /api/transaction/list until all results
are collected, or by requesting a higher/unbounded limit if supported), and keep
the returned data shape compatible with TransactionListResponse so downstream
invested and portfolio calculations use the full history.
| const launchParams = useLaunchParams(); | ||
| const telegramUser = launchParams.initData?.user; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Does @telegram-apps/sdk-react useLaunchParams throw an error when not running inside a Telegram Mini App?
💡 Result:
Yes, the useLaunchParams hook in @telegram-apps/sdk-react throws an error if it cannot retrieve launch parameters, which occurs when the application is running outside of a valid Telegram Mini App environment [1][2]. The underlying retrieveLaunchParams function attempts to extract parameters from known sources (such as the URL hash). If these sources are missing or invalid—which is the case when running in a standard browser environment—it throws the error: "Unable to retrieve launch parameters from any known source" [1][2][3]. To develop and test applications outside of Telegram without encountering this error, developers typically use a mocking mechanism to simulate the Telegram environment [4]. The official templates for the SDK often include utilities like mockTelegramEnv to inject these parameters into the environment during development, thereby preventing the error [4]. It is also worth noting that the availability and implementation of this hook may vary by SDK version. For instance, the hook was notably absent in some v3 releases before being reintroduced [5][6]. If you are working in an environment where the hook might fail (such as Server-Side Rendering), you should ensure you are handling the environment check or using proper mocking strategies as suggested in the project documentation and examples [7][4][2].
Citations:
- 1: [Bug]: Error: Unable to retrieve launch parameters from any known source. Telegram-Mini-Apps/tma.js#383
- 2: [Bug]: Uncaught Error: Unable to retrieve launch parameters from any known source. when I open my app outside TG environment. Telegram-Mini-Apps/tma.js#373
- 3: https://docs.telegram-mini-apps.com/packages/tma-js-bridge/launch-parameters
- 4: https://github.com/Telegram-Mini-Apps/nextjs-template
- 5: [Bug]: useLaunchParams hook missing in @telegram-apps/sdk-react v3 Telegram-Mini-Apps/tma.js#667
- 6: Feature/migration guide Telegram-Mini-Apps/tma.js#713
- 7: [Bug]: React SDK.
useLaunchParamsdoesn't work with ssr Telegram-Mini-Apps/tma.js#347
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Map the relevant files first
git ls-files 'src/hooks/useUser.ts' 'src/**/tma.tsx' 'src/**/tma.*' 'src/**/useUser.*' 'src/**' | sed -n '1,200p'
printf '\n--- useUser.ts ---\n'
cat -n src/hooks/useUser.ts
printf '\n--- tma.tsx / related files ---\n'
fd -a 'tma.tsx' src || true
fd -a 'tma.*' src || true
printf '\n--- useLaunchParams usages ---\n'
rg -n "useLaunchParams|useUser\(" src
printf '\n--- useUser import/usage contexts ---\n'
rg -n "from ['\"].*useUser['\"]|useUser" src --glob '!**/*.map'Repository: CeyLabs/BitcoinDeepaBot-TMA
Length of output: 6409
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n--- src/app/context/tma.tsx ---\n'
wc -l src/app/context/tma.tsx
cat -n src/app/context/tma.tsx
printf '\n--- useUser consumers ---\n'
sed -n '1,120p' src/components/v2/dashboard/DashboardTopBar.tsx
printf '\n--- src/app/v2/dashboard/page.tsx ---\n'
sed -n '1,120p' src/app/v2/dashboard/page.tsx
printf '\n--- src/app/page.tsx (relevant section) ---\n'
sed -n '130,190p' src/app/page.tsx
printf '\n--- src/app/dashboard/page.tsx (relevant section) ---\n'
sed -n '220,280p' src/app/dashboard/page.tsxRepository: CeyLabs/BitcoinDeepaBot-TMA
Length of output: 9247
Guard useLaunchParams() in src/hooks/useUser.ts
useLaunchParams() throws outside Telegram, so useUser() can crash every consumer in the browser/dev path that TMASetupProvider already tolerates. Return a safe fallback or gate this hook behind the Telegram environment.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/hooks/useUser.ts` around lines 14 - 15, useUser() currently calls
useLaunchParams() unconditionally, which can throw outside Telegram and crash
browser/dev consumers. Update the useUser hook to either guard the
useLaunchParams() call behind a Telegram environment check or return a safe
fallback when Telegram context is unavailable, while keeping telegramUser
derived safely from launchParams.initData?.user.
Summary by CodeRabbit