Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe PR adds a v2 dashboard with wallet, activity, plans, tasks, news, and verification flows. It also introduces reusable UI components, authenticated data hooks, Telegram integrations, cached database access, and migrations for Next.js, Tailwind CSS, ESLint, theming, and static assets. ChangesProduct flows and reusable UI
Platform, data, and tooling
Estimated code review effort: 5 (Critical) | ~120 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)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install timed out. The project may have too many dependencies for the sandbox. 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.
| {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
| 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.
…ng (#74) * feat: add emoji icons * feat: enhance activity dashboard with search, tabs, and grouping functionality * feat: update ActivityRow and ActivityTabs for improved styling and add surface-gift color variable * feat: enhance CopyField and CopyButton components with improved styling and functionality * feat: integrate DashboardTopBar into WalletV2Page and update user data handling * feat: update UI components with new dark mode colors and improve styling consistency * feat: refactor dashboard components to use consistent surface color variables and improve styling * feat: add success color variable and update PerformanceGrid styles for consistency * feat: add wallet dashboard components including TotalValueCard, PerformanceGrid, PlanSummaryCard, PortfolioChart, and DashboardTopBar * feat: replace BottomNavigation import with new .txt file and update related components * feat: implement gift management features with mock data and UI components * feat: add task management components including TaskHeroCard, ReferralProgressCard, ManageTasksSection, TaskFilterTabs, TaskListCard, and TaskRow with mock data * feat: implement subscription plan selection page and mock data for packages * refactor: update ReferralProgressCard, TaskHeroCard, and TaskRow components to use images and badges - Replaced gradient backgrounds with images in ReferralProgressCard and TaskHeroCard. - Updated TaskRow to use Image component for task icons instead of emoji glyphs. - Changed task data structure to remove icon background color and use image paths instead of emoji. - Introduced AvatarStack in ReferralProgressCard for better avatar display. - Updated styles for better responsiveness and visual consistency. * feat: enhance plan selection UI with image icons and improve icon retrieval logic * feat: enhance UI components with dark mode support and improve plan name handling * feat: update PlanHeroCard and PlanSummaryCard to display subscription start date and improve subscription data handling * feat: enhance task management UI with task click handling and Telegram link sharing * feat: replace bottomNavigation.txt with bottomNavigation.tsx for improved component structure * feat: restructure GiftPlanActions and ManageGiftsSection for improved layout and visibility * feat: enhance ChoosePlanPage and PlanCard with current plan display and improved selection handling * feat: add verification intro page and update gifting UI with placeholder component * feat: add useRegisterV2User hook for user registration on v2 backend and integrate it into TMASetupProvider * feat: update KYC verification redirects to new v2 path and enhance button styles in verification and plan selection pages * feat: add mobile warning modal and platform detection for verification process * feat: implement KYC status handling and initiate verification process in verification page * refactor: replace ClipboardList icon with Image in PlanHeroCard and update PlanSummaryCard layout - Updated PlanHeroCard to use an Image component for the icon instead of ClipboardList. - Refactored PlanSummaryCard to utilize Cell component for better layout and added conditional rendering for subscription details. - Removed unused ChevronRight component from PlanSummaryCard. * feat: add ChoosePlanPage component and integrate subscription plan selection functionality * feat: enhance KYC initiation process with user details and create verification details page * feat: update navigation links to verification page and enhance verification details form * feat: implement user profile update functionality and integrate with verification details page * feat: enhance ChoosePlanPage layout and update verification status messages * feat: enhance authentication error display with improved layout and retry button * feat: update CopyButton styling for improved layout and accessibility * feat: update WalletV2Page to handle current BTC price and enhance DCSummary type * feat: refactor WalletV2Page and PlansV2Page to remove mock data usage and simplify state management * feat: integrate transaction history and remove mock data from ActivityV2Page * feat: add package_name to DcaTransaction and update activity mapping for detailLabel * feat: implement subscription cancellation functionality and update query keys * feat: add snackbar notifications for subscription cancellation feedback * feat: implement news article detail page and related components * feat: update button label from "Change Plan" to "Manage Plan" in PlanHeroCard * feat: remove "View Plans" link from PlanHeroCard component * feat: add KYC verification notice and loading state to plans pages * feat: enhance plan selection UI with cancel option and improved styling * feat: refactor back button handling and consolidate imports in news components * feat: update gift link to disabled state with "Coming Soon" badge * feat: simplify LoadingPage component usage by removing fullscreen prop * Refactor image sources in Subscription and Verification pages to use local assets - Updated SubscriptionPage to use a local hourglass image instead of an external URL. - Changed verification status images in VerificationIntroPage to use local emoji assets for improved performance and reliability. * feat: add new animated emojis * refactor: comment out unused gift plan task in mock data
There was a problem hiding this comment.
Actionable comments posted: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/app/v2/dashboard/page.tsx (1)
17-34: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winDo not present unresolved wallet data as actual account balances.
useWalletSummary()returnsundefinedduring the pending phase and after an error; the current fallback rendersLKR 0, zero SATS, and other financial metrics as if they came from the wallet. TreatisLoading/isError/no-summary as a separate rendering state and only derive metrics from a successful summary.🤖 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 17 - 34, Update the dashboard data flow around useWalletSummary and the derived metrics so pending, error, or missing-summary states are rendered separately rather than as zero-valued wallet balances. Use the hook’s isLoading and isError state, and only calculate totalLkr, totalSats, dcaSpent, dcaSats, avgBtcPrice, change24h, changeLkr, and current pricing from a successfully available summary.
🧹 Nitpick comments (1)
src/app/v2/dashboard/plans/page.tsx (1)
25-29: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider centralizing the
total_lkrstring/number parsing.Handling both string-with-commas and numeric forms of
total_lkrinline here is fragile and likely to be duplicated wherever this field is consumed (e.g., other wallet/dashboard views). A shared helper insrc/lib/formatters.ts(which already housesfmtLkr,maskDigits, etc.) would keep this parsing consistent and testable in one place.🤖 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/plans/page.tsx` around lines 25 - 29, Centralize total_lkr normalization in a shared helper in formatters.ts, handling comma-separated strings, numeric values, and absent values consistently. Replace the inline conversion in the currentValueLkr calculation with that helper, and export it for reuse by other wallet/dashboard consumers.
🤖 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/api/user/kyc/initiate/route.ts`:
- Around line 12-18: Replace the direct fetch calls in
src/app/api/user/kyc/initiate/route.ts lines 12-18 and
src/app/api/user/profile/route.ts lines 12-19 with the custom fetchy wrapper
imported from `@/lib/fetchy`. Extend fetchy if needed so both route handlers
preserve the upstream response status and body behavior.
In `@src/app/onboard/page.tsx`:
- Around line 125-131: Update the style cast in the onboarding page to use an
explicitly imported type CSSProperties from React instead of
React.CSSProperties, adding the type-only import and applying CSSProperties to
the object cast.
In `@src/app/v2/plans/choose/page.tsx`:
- Around line 103-108: Update the handleSubscribe catch block to set the same
error snackbar state used by handleCancel, while retaining console.error for
diagnostics and resetting loading via the existing finally block. Ensure failed
subscriptions provide visible user feedback when the button becomes available
again.
- Around line 81-108: Update handleSubscribe to use the shared fetchy wrapper
from `@/lib/fetchy` instead of raw fetch for the PayHere link request, preserving
the existing POST payload, authorization, redirect handling, loading state, and
error flow.
In `@src/app/v2/verification/details/page.tsx`:
- Around line 153-155: Replace the inaccurate “never Shared” privacy wording in
both verification UI locations: update the text in
src/app/v2/verification/details/page.tsx (lines 153-155) and
src/app/v2/verification/page.tsx (lines 208-211) with privacy-policy-approved
language that accurately describes data handling and any third-party processing.
In `@src/app/v2/verification/page.tsx`:
- Around line 102-105: Update the verification page’s useKycStatus flow so an
unresolved enabled query renders a loading state and keeps the verification CTA
disabled instead of deriving NOT_STARTED from undefined kycData. Handle query
errors separately, and only render the Start Verification path after a
successful response explicitly confirms no status.
In `@src/components/ui/plan-card.tsx`:
- Around line 52-57: Update the Component rendering in the plan-card component
so the type attribute is applied only when rendering the button intrinsic
element. Use explicit div and button branches, or otherwise narrow the
polymorphic tag, while preserving active div behavior and inactive button
onClick behavior.
In `@src/components/v2/dashboard/activity/ActivitySearchBar.tsx`:
- Around line 33-39: Update the filter control around the Funnel IconButton to
either connect it to the existing activity filtering behavior with an onClick
handler, or remove the button entirely if filtering is not implemented; do not
leave a non-functional actionable control.
In `@src/components/v2/dashboard/plans/ManageGiftsSection.tsx`:
- Around line 24-34: Update the ManageGiftsSection component’s root container to
remove the hardcoded hidden state and include the flex display class alongside
flex-col, or gate the entire section from its caller using an explicit feature
flag if the feature remains unlaunched. If gated off, remove the unused
useState/useMemo gift filtering work; otherwise preserve the existing
GiftFilterTabs and GiftListCard rendering.
In `@src/components/v2/dashboard/tasks/mock-data.ts`:
- Around line 3-52: Replace the hard-coded dashboard account data with
authenticated server state: in src/components/v2/dashboard/tasks/mock-data.ts
lines 3-52, move fixtures to tests/stories and stop exporting them for
production; in src/app/v2/dashboard/tasks/page.tsx lines 6-10, fetch task,
reward, and referral data through TanStack Query using keys from
`@/lib/query-keys` and add loading and empty states; in
src/components/v2/dashboard/tasks/TaskHeroCard.tsx lines 7-8, pass an
authoritative timestamped exchange rate with the account data or remove the LKR
equivalent.
In `@src/components/v2/dashboard/tasks/TaskHeroCard.tsx`:
- Around line 23-26: Update btcLkrLabel to preserve satoshi-level precision by
formatting BTC with eight decimal places, or alternatively display the BTC
amount solely in sats. Ensure the 200-sat value is not rendered as ₿0.00000
while retaining the existing LKR formatting.
In `@src/components/v2/dashboard/wallet/PlanSummaryCard.tsx`:
- Around line 80-108: Derive an activeSubscription value from
subscription.isActive in PlanSummaryCard, using null for inactive or cancelled
records. Replace subscription-only checks and data access for the Active badge,
plan copy, and reward cards with activeSubscription, while preserving the
existing fallback rendering for no active subscription.
In `@src/hooks/query/useKyc.ts`:
- Line 17: Update the query configuration in useKyc to import and use the shared
KYC query-key symbol from "`@/lib/query-keys`" instead of the literal
["kyc-status"] value, preserving the existing query behavior.
In `@src/hooks/useRegisterV2User.ts`:
- Around line 18-26: The authenticateWithTelegram flow in useRegisterV2User must
clear stale credentials whenever authentication fails or resolves without a
token, then route the current Telegram user through the existing
re-registration/auth path. Update both the no-token result branch and catch
handler to call clearAuthFromStorage and trigger that path, while preserving
saveAuthToStorage for successful token responses.
In `@src/lib/activity.ts`:
- Around line 53-58: Update the fallback detailLabel in
mapDcaTransactionToActivityItem from the misspelled “Destributed” text to the
correct “Distributed” label, preserving the existing package_name-based label
behavior.
In `@src/lib/hooks.ts`:
- Around line 50-60: Update openTelegramLink in src/lib/hooks.ts#L50-L60 to use
normal browser navigation when webApp is unavailable instead of throwing, and
expose the Telegram Mini App readiness state for callers. In
src/components/v2/dashboard/tasks/ManageTasksSection.tsx#L27-L42, use that
readiness or shareToStory availability to disable the story action until
supported, or display a user-facing unavailable message.
In `@src/lib/news.ts`:
- Line 109: Update the RSS request in the news-fetching flow around FEED_URL to
use the project’s fetchy wrapper from `@/lib/fetchy` instead of direct fetch.
Extend the wrapper’s options handling as needed to preserve the existing server
revalidation setting of 900 seconds, then pass that option through the wrapper.
- Around line 108-125: Update getNewsArticles to catch failures from fetch,
response.text(), and parser.parseString, returning an empty NewsArticle array so
the page reaches its existing “Unable to load news right now” state; preserve
the current successful parsing, sorting, mapping, and filtering behavior.
In `@src/styles/theme.css`:
- Around line 46-48: Terminate both declarations in the theme token definitions
by adding semicolons to --color-surface-glass and --color-surface-tertiary,
ensuring each custom property is parsed independently.
---
Outside diff comments:
In `@src/app/v2/dashboard/page.tsx`:
- Around line 17-34: Update the dashboard data flow around useWalletSummary and
the derived metrics so pending, error, or missing-summary states are rendered
separately rather than as zero-valued wallet balances. Use the hook’s isLoading
and isError state, and only calculate totalLkr, totalSats, dcaSpent, dcaSats,
avgBtcPrice, change24h, changeLkr, and current pricing from a successfully
available summary.
---
Nitpick comments:
In `@src/app/v2/dashboard/plans/page.tsx`:
- Around line 25-29: Centralize total_lkr normalization in a shared helper in
formatters.ts, handling comma-separated strings, numeric values, and absent
values consistently. Replace the inline conversion in the currentValueLkr
calculation with that helper, and export it for reuse by other wallet/dashboard
consumers.
🪄 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 Plus
Run ID: 22bc7f01-6b45-4bc6-90b0-c6555a4c1e7a
⛔ Files ignored due to path filters (20)
bun.lockbis excluded by!**/bun.lockbpublic/avatar/boy.pngis excluded by!**/*.pngpublic/avatar/girl.pngis excluded by!**/*.pngpublic/emoji/faucet_received.svgis excluded by!**/*.svgpublic/emoji/faucet_sent.svgis excluded by!**/*.svgpublic/emoji/flag-sri-lanka.pngis excluded by!**/*.pngpublic/emoji/gift_receive.svgis excluded by!**/*.svgpublic/emoji/gift_sent.svgis excluded by!**/*.svgpublic/emoji/gift_standing_order.svgis excluded by!**/*.svgpublic/emoji/gift_standing_order_receive.svgis excluded by!**/*.svgpublic/emoji/gift_standing_order_sent.svgis excluded by!**/*.svgpublic/emoji/membership_reward.svgis excluded by!**/*.svgpublic/emoji/notepad.svgis excluded by!**/*.svgpublic/emoji/receive.svgis excluded by!**/*.svgpublic/emoji/sent.svgis excluded by!**/*.svgpublic/emoji/story.svgis excluded by!**/*.svgpublic/emoji/tasks_reward.svgis excluded by!**/*.svgpublic/emoji/tipjar_Receive.svgis excluded by!**/*.svgpublic/emoji/tipjar_sent.svgis excluded by!**/*.svgpublic/icons/sparkle.svgis excluded by!**/*.svg
📒 Files selected for processing (88)
README.mdpackage.jsonpublic/bg/note.webppublic/bg/pill.webppublic/emoji/animated/bomb.webppublic/emoji/animated/chart-increasing.webppublic/emoji/animated/coffin.webppublic/emoji/animated/cross-mark.webppublic/emoji/animated/exclamation-mark.webppublic/emoji/animated/firecracker.webppublic/emoji/animated/high-voltage.webppublic/emoji/animated/hourglass.webppublic/emoji/animated/id-card.webppublic/emoji/animated/key.webppublic/emoji/animated/locked-with-key.webppublic/emoji/animated/microscope.webppublic/emoji/animated/party-popper.webppublic/emoji/community.webpsrc/app/api/subscription/payhere-link/route.tssrc/app/api/user/kyc/initiate/route.tssrc/app/api/user/profile/route.tssrc/app/context/tma.tsxsrc/app/dashboard/layout.tsxsrc/app/onboard/page.tsxsrc/app/page.tsxsrc/app/subscription/page.tsxsrc/app/v2/dashboard/activity/page.tsxsrc/app/v2/dashboard/layout.tsxsrc/app/v2/dashboard/news/[id]/page.tsxsrc/app/v2/dashboard/news/loading.tsxsrc/app/v2/dashboard/news/page.tsxsrc/app/v2/dashboard/page.tsxsrc/app/v2/dashboard/plans/page.tsxsrc/app/v2/dashboard/tasks/page.tsxsrc/app/v2/plans/choose/page.tsxsrc/app/v2/verification/details/page.tsxsrc/app/v2/verification/page.tsxsrc/components/bottomNavigation.tsxsrc/components/skeletons/NewsCardSkeleton.tsxsrc/components/ui/copy-field.tsxsrc/components/ui/page-title.tsxsrc/components/ui/plan-card.tsxsrc/components/ui/search-field.tsxsrc/components/ui/toggle-plan.tsxsrc/components/ui/visible-toggle.tsxsrc/components/v2/bottomNavigation.tsxsrc/components/v2/dashboard/activity/ActivityGroup.tsxsrc/components/v2/dashboard/activity/ActivityRow.tsxsrc/components/v2/dashboard/activity/ActivitySearchBar.tsxsrc/components/v2/dashboard/activity/ActivityTabs.tsxsrc/components/v2/dashboard/news/NewsListSection.tsxsrc/components/v2/dashboard/plans/GiftFilterTabs.tsxsrc/components/v2/dashboard/plans/GiftListCard.tsxsrc/components/v2/dashboard/plans/GiftPlanActions.tsxsrc/components/v2/dashboard/plans/GiftRow.tsxsrc/components/v2/dashboard/plans/KycRequiredNotice.tsxsrc/components/v2/dashboard/plans/ManageGiftsSection.tsxsrc/components/v2/dashboard/plans/PlanHeroCard.tsxsrc/components/v2/dashboard/plans/mock-data.tssrc/components/v2/dashboard/tasks/ManageTasksSection.tsxsrc/components/v2/dashboard/tasks/ReferralProgressCard.tsxsrc/components/v2/dashboard/tasks/TaskFilterTabs.tsxsrc/components/v2/dashboard/tasks/TaskHeroCard.tsxsrc/components/v2/dashboard/tasks/TaskListCard.tsxsrc/components/v2/dashboard/tasks/TaskRow.tsxsrc/components/v2/dashboard/tasks/mock-data.tssrc/components/v2/dashboard/wallet/DashboardTopBar.tsxsrc/components/v2/dashboard/wallet/PerformanceGrid.tsxsrc/components/v2/dashboard/wallet/PlanSummaryCard.tsxsrc/components/v2/dashboard/wallet/PortfolioChart.tsxsrc/components/v2/dashboard/wallet/TotalValueCard.tsxsrc/hooks/query/useCancelSubscription.tssrc/hooks/query/useKyc.tssrc/hooks/query/usePackages.tssrc/hooks/query/useSubscriptionCurrent.tssrc/hooks/query/useTransactionHistory.tssrc/hooks/useRegisterV2User.tssrc/hooks/useTelegramPlatform.tssrc/hooks/useTgBackButton.tssrc/hooks/useUser.tssrc/lib/activity.tssrc/lib/formatters.tssrc/lib/hooks.tssrc/lib/news.tssrc/lib/query-keys.tssrc/lib/types.tssrc/styles/theme.csstsconfig.json
🚧 Files skipped from review as they are similar to previous changes (14)
- src/lib/query-keys.ts
- src/components/ui/page-title.tsx
- src/components/ui/toggle-plan.tsx
- src/app/subscription/page.tsx
- src/components/ui/visible-toggle.tsx
- src/hooks/useUser.ts
- src/hooks/query/useSubscriptionCurrent.ts
- src/components/ui/search-field.tsx
- src/app/context/tma.tsx
- src/hooks/query/useTransactionHistory.ts
- package.json
- src/components/v2/bottomNavigation.tsx
- src/app/page.tsx
- src/lib/formatters.ts
| const response = await fetch(`${process.env.API_BASE_URL}/user/kyc/initiate`, { | ||
| method: "POST", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| Authorization: authHeader, | ||
| }, | ||
| body: JSON.stringify(body), |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Route external requests through @/lib/fetchy.
Both new proxy calls bypass the project’s required HTTP wrapper. Extend fetchy if necessary to preserve the upstream status/body behavior these handlers need, then use it consistently.
src/app/api/user/kyc/initiate/route.ts#L12-L18: replace direct KYC initiationfetchwith the custom wrapper.src/app/api/user/profile/route.ts#L12-L19: replace direct profile-updatefetchwith the custom wrapper.
As per coding guidelines, “All HTTP requests must use the custom fetchy wrapper from @/lib/fetchy”.
📍 Affects 2 files
src/app/api/user/kyc/initiate/route.ts#L12-L18(this comment)src/app/api/user/profile/route.ts#L12-L19
🤖 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/user/kyc/initiate/route.ts` around lines 12 - 18, Replace the
direct fetch calls in src/app/api/user/kyc/initiate/route.ts lines 12-18 and
src/app/api/user/profile/route.ts lines 12-19 with the custom fetchy wrapper
imported from `@/lib/fetchy`. Extend fetchy if needed so both route handlers
preserve the upstream response status and body behavior.
Source: Coding guidelines
| style={ | ||
| { | ||
| borderRadius: "12px", | ||
| overflow: "hidden", | ||
| "--tgui--outline": "#fa7119", | ||
| "--tgui--plain_foreground": "#fa7119", | ||
| } as React.CSSProperties |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether the repository enables UMD globals and inspect the React type usage.
fd -a -g 'tsconfig*.json' -x rg -n '"allowUmdGlobalAccess"\s*:\s*true' {}
rg -n 'React\.CSSProperties|CSSProperties|from "react"' src/app/onboard/page.tsxRepository: CeyLabs/BitcoinDeepaBot-TMA
Length of output: 265
🏁 Script executed:
#!/bin/bash
# Look at TypeScript/Next config declarations and relevant file usage/imports.
set -euo pipefail
echo "== candidate tsconfig files =="
fd -a -g 'tsconfig*.json' . | sed 's#^\./##'
echo
echo "== react imports and CSSProperties usages in src/app/onboard/page.tsx =="
nl -ba src/app/onboard/page.tsx | sed -n '1,25p;120,136p'
echo
echo "== package config TypeScript/React dependencies =="
cat package.json | node -e '
const fs=require("fs");
const p=JSON.parse(fs.readFileSync("package.json"))
for (const section of ["scripts","dependencies","devDependencies","peerDependencies"]) {
console.log(section)
if (p[section]) {
for (const [k,v] of Object.entries(p[section])) { if (k.includes("typescript") || k.includes("react")) console.log(`${k}: ${v}`) }
}
}'
echo
echo "== all tsconfig allowUmdGlobalAccess =="
fd -g 'tsconfig*.json' . -x sh -c 'echo "--- $1"; rg -n "allowUmdGlobalAccess" "$1" || true' sh {}Repository: CeyLabs/BitcoinDeepaBot-TMA
Length of output: 344
🏁 Script executed:
#!/bin/bash
# Inspect config and usage text with shell-built line numbering.
set -euo pipefail
echo "== tsconfig files =="
fd -g 'tsconfig*.json' .
echo
echo "== allowUmdGlobalAccess in tsconfig files =="
while IFS= read -r f; do
echo "--- $f"
grep -n 'allowUmdGlobalAccess' "$f" || true
done < <(fd -g 'tsconfig*.json' .)
echo
echo "== relevant src/app/onboard/page.tsx imports and lines =="
awk '
NR==1,R==25 {printf "%6d %s\n", NR, $0}
NR>=120 && NR<=136 {printf "%6d %s\n", NR, $0}
END {}' src/app/onboard/page.tsx
echo
echo "== package relevant deps/scripts =="
node - <<'JS'
const fs = require('fs');
const p = JSON.parse(fs.readFileSync('package.json', 'utf8'));
const lines = ['scripts','dependencies','devDependencies','peerDependencies'];
for (const section of lines) {
console.log(section);
const vals = p[section] || {};
for (const [k,v] of Object.entries(vals)) {
if (k.includes('typescript') || k.includes('react') || k.includes('next')) console.log(`${k}: ${v}`);
}
}
JSRepository: CeyLabs/BitcoinDeepaBot-TMA
Length of output: 301
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== tsconfig files =="
fd -g 'tsconfig*.json' .
echo
echo "== allowUmdGlobalAccess =="
find . -name 'tsconfig*.json' -print0 | while IFS= read -r -d '' f; do
printf '%s\n' "=== $f ==="
grep -n 'allowUmdGlobalAccess' "$f" || true
done
echo
echo "== relevant src/app/onboard/page.tsx import and style usage =="
awk '
NR<26 {printf "%6d %s\n", NR, $0}
NR>=120 && NR<=136 {printf "%6d %s\n", NR, $0}
' src/app/onboard/page.tsx
echo
echo "== package relevant deps/scripts =="
node - <<'JS'
const fs = require('fs');
const p = JSON.parse(fs.readFileSync('package.json', 'utf8'));
for (const section of ['scripts','dependencies','devDependencies','peerDependencies']) {
console.log(section);
if (p[section]) {
for (const [k,v] of Object.entries(p[section])) {
if (/typescript|react|next/i.test(k)) console.log(`${k}: ${v}`);
}
}
}
JSRepository: CeyLabs/BitcoinDeepaBot-TMA
Length of output: 2640
Import CSSProperties explicitly.
src/app/onboard/page.tsx uses React.CSSProperties without importing React, and the repo does not enable allowUmdGlobalAccess. Import type CSSProperties from React and cast the object that way.
Proposed fix
-import { useEffect, useState } from "react";
+import { useEffect, useState, type CSSProperties } from "react";
- } as React.CSSProperties
+ } as CSSProperties📝 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.
| style={ | |
| { | |
| borderRadius: "12px", | |
| overflow: "hidden", | |
| "--tgui--outline": "#fa7119", | |
| "--tgui--plain_foreground": "#fa7119", | |
| } as React.CSSProperties | |
| style={ | |
| { | |
| borderRadius: "12px", | |
| overflow: "hidden", | |
| "--tgui--outline": "`#fa7119`", | |
| "--tgui--plain_foreground": "`#fa7119`", | |
| } as CSSProperties |
🤖 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/onboard/page.tsx` around lines 125 - 131, Update the style cast in
the onboarding page to use an explicitly imported type CSSProperties from React
instead of React.CSSProperties, adding the type-only import and applying
CSSProperties to the object cast.
Source: MCP tools
| const handleSubscribe = async () => { | ||
| const plan = packages.find((p) => p.id === selectedPlanId); | ||
| if (!plan || !authToken) return; | ||
|
|
||
| try { | ||
| setPayhereLinkLoading(true); | ||
| const res = await fetch("/api/subscription/payhere-link", { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json", Authorization: `Bearer ${authToken}` }, | ||
| body: JSON.stringify({ package_id: plan.id }), | ||
| }); | ||
| const result = await res.json(); | ||
|
|
||
| if (!res.ok) { | ||
| if (res.status === 403 && result.redirectTo) { | ||
| router.push(result.redirectTo); | ||
| return; | ||
| } | ||
| throw new Error(result.message || "Failed to generate payment link"); | ||
| } | ||
|
|
||
| if (result.link) redirectToPayHereViaPage(result.link); | ||
| } catch (err) { | ||
| console.error(err); | ||
| } finally { | ||
| setPayhereLinkLoading(false); | ||
| } | ||
| }; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Use fetchy instead of raw fetch for the PayHere link request.
This directly calls fetch() (line 87) instead of the shared fetchy wrapper used elsewhere in this PR (e.g., useCancelSubscription.ts). This bypasses whatever centralized auth/retry/timeout/error-normalization behavior fetchy provides.
🛠️ Proposed fix
- const res = await fetch("/api/subscription/payhere-link", {
- method: "POST",
- headers: { "Content-Type": "application/json", Authorization: `Bearer ${authToken}` },
- body: JSON.stringify({ package_id: plan.id }),
- });
- const result = await res.json();
-
- if (!res.ok) {
- if (res.status === 403 && result.redirectTo) {
- router.push(result.redirectTo);
- return;
- }
- throw new Error(result.message || "Failed to generate payment link");
- }
-
- if (result.link) redirectToPayHereViaPage(result.link);
+ const result = await fetchy.post<{ link?: string; redirectTo?: string; message?: string }, { package_id: string }>(
+ "/api/subscription/payhere-link",
+ { package_id: plan.id },
+ { headers: { Authorization: `Bearer ${authToken}` } }
+ );
+
+ if (result.link) redirectToPayHereViaPage(result.link);🤖 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/plans/choose/page.tsx` around lines 81 - 108, Update
handleSubscribe to use the shared fetchy wrapper from `@/lib/fetchy` instead of
raw fetch for the PayHere link request, preserving the existing POST payload,
authorization, redirect handling, loading state, and error flow.
Source: Path instructions
| } catch (err) { | ||
| console.error(err); | ||
| } finally { | ||
| setPayhereLinkLoading(false); | ||
| } | ||
| }; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Subscribe failures are silently swallowed — no user feedback.
Unlike handleCancel's catch block (which sets an error snackbar), a failed handleSubscribe only logs to console. The button re-enables but the user gets no indication anything went wrong, especially confusing after a loading spinner.
🛡️ Proposed fix
} catch (err) {
console.error(err);
+ setSnackbar({
+ tone: "error",
+ title: "Payment Link Failed",
+ description: err instanceof Error ? err.message : "Failed to generate payment link",
+ });
} finally {📝 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.
| } catch (err) { | |
| console.error(err); | |
| } finally { | |
| setPayhereLinkLoading(false); | |
| } | |
| }; | |
| } catch (err) { | |
| console.error(err); | |
| setSnackbar({ | |
| tone: "error", | |
| title: "Payment Link Failed", | |
| description: err instanceof Error ? err.message : "Failed to generate payment link", | |
| }); | |
| } finally { | |
| setPayhereLinkLoading(false); | |
| } | |
| }; |
🤖 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/plans/choose/page.tsx` around lines 103 - 108, Update the
handleSubscribe catch block to set the same error snackbar state used by
handleCancel, while retaining console.error for diagnostics and resetting
loading via the existing finally block. Ensure failed subscriptions provide
visible user feedback when the button becomes available again.
| <p className="text-center text-xs text-muted"> | ||
| Your Data is Encrypted and never Shared | ||
| </p> |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Remove the inaccurate “never shared” privacy promise.
This flow submits identity and contact data to profile and KYC endpoints, so the UI must not claim it is never shared. Replace it with privacy-policy-approved wording that accurately describes handling and any third-party processing.
src/app/v2/verification/details/page.tsx#L153-L155: replace the unconditional “Encrypted and never Shared” claim.src/app/v2/verification/page.tsx#L208-L211: replace the equivalent “Secured and never Shared” claim.
📍 Affects 2 files
src/app/v2/verification/details/page.tsx#L153-L155(this comment)src/app/v2/verification/page.tsx#L208-L211
🤖 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/verification/details/page.tsx` around lines 153 - 155, Replace the
inaccurate “never Shared” privacy wording in both verification UI locations:
update the text in src/app/v2/verification/details/page.tsx (lines 153-155) and
src/app/v2/verification/page.tsx (lines 208-211) with privacy-policy-approved
language that accurately describes data handling and any third-party processing.
| export function mapDcaTransactionToActivityItem(tx: DcaTransaction): ActivityItem { | ||
| return { | ||
| id: tx.id, | ||
| type: "membership_reward", | ||
| detailLabel: tx.package_name ? `${tx.package_name} Plan` : "Destributed", | ||
| timestamp: tx.created_at, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Fix the fallback plan label typo.
The UI renders Destributed when package_name is absent.
Proposed fix
- detailLabel: tx.package_name ? `${tx.package_name} Plan` : "Destributed",
+ detailLabel: tx.package_name ? `${tx.package_name} Plan` : "Distributed",📝 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 mapDcaTransactionToActivityItem(tx: DcaTransaction): ActivityItem { | |
| return { | |
| id: tx.id, | |
| type: "membership_reward", | |
| detailLabel: tx.package_name ? `${tx.package_name} Plan` : "Destributed", | |
| timestamp: tx.created_at, | |
| export function mapDcaTransactionToActivityItem(tx: DcaTransaction): ActivityItem { | |
| return { | |
| id: tx.id, | |
| type: "membership_reward", | |
| detailLabel: tx.package_name ? `${tx.package_name} Plan` : "Distributed", | |
| timestamp: tx.created_at, |
🤖 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/activity.ts` around lines 53 - 58, Update the fallback detailLabel in
mapDcaTransactionToActivityItem from the misspelled “Destributed” text to the
correct “Distributed” label, preserving the existing package_name-based label
behavior.
| // For t.me links (bot deep links, channel invites, share intents) — keeps | ||
| // navigation inside the Telegram client instead of bouncing to a browser. | ||
| const openTelegramLink = useCallback( | ||
| (url: string) => { | ||
| if (webApp) { | ||
| webApp.openTelegramLink(url); | ||
| } else { | ||
| throw new Error("TMA not available"); | ||
| } | ||
| }, | ||
| [webApp] |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Handle unavailable Telegram APIs instead of throwing from task CTAs. webApp is only assigned after mount, and browser users never receive it; setup/community clicks and story sharing can therefore throw uncaught errors.
src/lib/hooks.ts#L50-L60: make Telegram-link navigation fall back to normal browser navigation and expose TMA readiness.src/components/v2/dashboard/tasks/ManageTasksSection.tsx#L27-L42: disable the story action untilshareToStoryis available, or show a user-facing unavailable message.
📍 Affects 2 files
src/lib/hooks.ts#L50-L60(this comment)src/components/v2/dashboard/tasks/ManageTasksSection.tsx#L27-L42
🤖 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/hooks.ts` around lines 50 - 60, Update openTelegramLink in
src/lib/hooks.ts#L50-L60 to use normal browser navigation when webApp is
unavailable instead of throwing, and expose the Telegram Mini App readiness
state for callers. In
src/components/v2/dashboard/tasks/ManageTasksSection.tsx#L27-L42, use that
readiness or shareToStory availability to disable the story action until
supported, or display a user-facing unavailable message.
| export async function getNewsArticles(): Promise<NewsArticle[]> { | ||
| const response = await fetch(FEED_URL, { next: { revalidate: 900 } }); | ||
| if (!response.ok) return []; | ||
|
|
||
| const xml = await response.text(); | ||
| const feed = await parser.parseString(xml); | ||
|
|
||
| const items = (feed.items ?? []) | ||
| .slice() | ||
| .sort((a, b) => { | ||
| const aTime = new Date(a.isoDate ?? a.pubDate ?? 0).getTime(); | ||
| const bTime = new Date(b.isoDate ?? b.pubDate ?? 0).getTime(); | ||
| return bTime - aTime; | ||
| }); | ||
|
|
||
| return items | ||
| .map((item, index) => toArticle(item, index)) | ||
| .filter((article): article is NewsArticle => article !== null); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Handle feed and parser failures before returning to the page.
fetch, response.text(), and parser.parseString() can throw. That bypasses the page’s empty-state branch and returns a 500 instead of “Unable to load news right now.”
Proposed fix
export async function getNewsArticles(): Promise<NewsArticle[]> {
- const response = await fetch(FEED_URL, { next: { revalidate: 900 } });
- if (!response.ok) return [];
+ try {
+ const response = await fetch(FEED_URL, { next: { revalidate: 900 } });
+ if (!response.ok) return [];
- const xml = await response.text();
- const feed = await parser.parseString(xml);
+ const xml = await response.text();
+ const feed = await parser.parseString(xml);
- const items = (feed.items ?? [])
+ const items = (feed.items ?? [])
.slice()
.sort((a, b) => {
const aTime = new Date(a.isoDate ?? a.pubDate ?? 0).getTime();
const bTime = new Date(b.isoDate ?? b.pubDate ?? 0).getTime();
return bTime - aTime;
});
- return items
- .map((item, index) => toArticle(item, index))
- .filter((article): article is NewsArticle => article !== null);
+ return items
+ .map((item, index) => toArticle(item, index))
+ .filter((article): article is NewsArticle => article !== null);
+ } catch {
+ return [];
+ }
}📝 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 async function getNewsArticles(): Promise<NewsArticle[]> { | |
| const response = await fetch(FEED_URL, { next: { revalidate: 900 } }); | |
| if (!response.ok) return []; | |
| const xml = await response.text(); | |
| const feed = await parser.parseString(xml); | |
| const items = (feed.items ?? []) | |
| .slice() | |
| .sort((a, b) => { | |
| const aTime = new Date(a.isoDate ?? a.pubDate ?? 0).getTime(); | |
| const bTime = new Date(b.isoDate ?? b.pubDate ?? 0).getTime(); | |
| return bTime - aTime; | |
| }); | |
| return items | |
| .map((item, index) => toArticle(item, index)) | |
| .filter((article): article is NewsArticle => article !== null); | |
| export async function getNewsArticles(): Promise<NewsArticle[]> { | |
| try { | |
| const response = await fetch(FEED_URL, { next: { revalidate: 900 } }); | |
| if (!response.ok) return []; | |
| const xml = await response.text(); | |
| const feed = await parser.parseString(xml); | |
| const items = (feed.items ?? []) | |
| .slice() | |
| .sort((a, b) => { | |
| const aTime = new Date(a.isoDate ?? a.pubDate ?? 0).getTime(); | |
| const bTime = new Date(b.isoDate ?? b.pubDate ?? 0).getTime(); | |
| return bTime - aTime; | |
| }); | |
| return items | |
| .map((item, index) => toArticle(item, index)) | |
| .filter((article): article is NewsArticle => article !== null); | |
| } catch { | |
| return []; | |
| } | |
| } |
🤖 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/news.ts` around lines 108 - 125, Update getNewsArticles to catch
failures from fetch, response.text(), and parser.parseString, returning an empty
NewsArticle array so the page reaches its existing “Unable to load news right
now” state; preserve the current successful parsing, sorting, mapping, and
filtering behavior.
| } | ||
|
|
||
| export async function getNewsArticles(): Promise<NewsArticle[]> { | ||
| const response = await fetch(FEED_URL, { next: { revalidate: 900 } }); |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Use the project HTTP wrapper for the RSS request.
This direct fetch bypasses @/lib/fetchy. Ensure the wrapper supports the required server revalidation options, then route this request through it. As per coding guidelines, “All HTTP requests must use the custom fetchy wrapper from @/lib/fetchy”.
🤖 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/news.ts` at line 109, Update the RSS request in the news-fetching
flow around FEED_URL to use the project’s fetchy wrapper from `@/lib/fetchy`
instead of direct fetch. Extend the wrapper’s options handling as needed to
preserve the existing server revalidation setting of 900 seconds, then pass that
option through the wrapper.
Source: Coding guidelines
| --color-surface-glass: #FFFFFF99 | ||
|
|
||
| --color-surface-tertiary: #E2E8F0 |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Terminate both theme-token declarations.
Without the semicolon on Line 46, CSS consumes Line 48 into --color-surface-glass; --color-surface-tertiary is never defined and glass-color consumers receive an invalid value.
Proposed fix
- --color-surface-glass: `#FFFFFF99`
+ --color-surface-glass: `#FFFFFF99`;
- --color-surface-tertiary: `#E2E8F0`
+ --color-surface-tertiary: `#E2E8F0`;📝 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.
| --color-surface-glass: #FFFFFF99 | |
| --color-surface-tertiary: #E2E8F0 | |
| --color-surface-glass: `#FFFFFF99`; | |
| --color-surface-tertiary: `#E2E8F0`; |
🧰 Tools
🪛 Stylelint (17.14.0)
[error] 48-48: Missing var function for "--color-surface-tertiary" (custom-property-no-missing-var-function)
(custom-property-no-missing-var-function)
🤖 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` around lines 46 - 48, Terminate both declarations in
the theme token definitions by adding semicolons to --color-surface-glass and
--color-surface-tertiary, ensuring each custom property is parsed independently.
Source: Linters/SAST tools
Summary by CodeRabbit
New Features
Bug Fixes