Skip to content

Revamp UI - Figma#68

Open
rayaanr wants to merge 6 commits into
mainfrom
revamp-ui
Open

Revamp UI - Figma#68
rayaanr wants to merge 6 commits into
mainfrom
revamp-ui

Conversation

@rayaanr

@rayaanr rayaanr commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Added a “Component Preview” dev page.
    • Refreshed the wallet dashboard and introduced a redesigned subscription/membership flow with weekly/monthly selection.
    • Added v2 dashboard routes (Activity, Plans, Tasks, News) as “coming soon” pages and new reusable UI components (cards, toggles, inputs, search/copy).
  • Bug Fixes
    • Improved auth/onboarding, KYC status redirects, and clearer authentication error UI.
    • Hardened API header handling and made database connections more reliable.
  • Chores
    • Modernized linting/tooling and refreshed Tailwind/theme configuration.

@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
bitcoin-deepa-tma-bot Ready Ready Preview, Comment Jul 6, 2026 6:26am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds reusable UI components and a dev preview page, rewrites the landing, dashboard, subscription, and v2 dashboard flows, and updates theme, data, API, and build tooling.

Changes

Product UI and app flows

Layer / File(s) Summary
Core UI primitives
src/components/ui/button.tsx, src/components/ui/clickable-card.tsx, src/components/ui/plan-card.tsx, src/components/ui/text-field.tsx, src/components/ui/search-field.tsx, src/components/ui/copy-field.tsx, src/components/ui/page-title.tsx, src/components/ui/toggle-plan.tsx, src/components/ui/visible-toggle.tsx, src/components/ui/category-filter.tsx
Adds reusable client UI components for buttons, cards, copy/search/plan/category/text inputs, page titles, and visibility toggles.
Component preview page
src/app/dev/page.tsx
Adds a dev page that renders the new UI components with local state and sample interactions.
Landing page rewrite
src/app/page.tsx
Reworks the home page around a shell layout, user progress display, registration hook, and conditional existing-user messaging.
Wallet dashboard rewrite
src/app/dashboard/page.tsx
Rebuilds the dashboard around new formatting helpers, summary and subscription queries, auth/KYC checks, and a streamlined card layout.
Subscription page rewrite
src/app/dashboard/subscription/page.tsx
Reworks the subscription page into plans and membership views with plan fetching, selection, subscribe, and cancel flows.
Bottom navigation and loading page
src/components/bottomNavigation.tsx, src/components/LoadingPage.tsx
Updates bottom navigation destinations, icons, and styling, and revises the loading spinner and loading-page layout.
App context and layout
src/app/layout.tsx, src/app/context/providers.tsx, src/app/context/theme.tsx
Changes root layout and providers to use theme-aware app wrapping and a theme override-capable context.
V2 dashboard shell
src/app/v2/dashboard/layout.tsx, src/components/v2/bottomNavigation.tsx, src/components/v2/dashboard/DashboardTopBar.tsx, src/app/v2/dashboard/activity/page.tsx, src/app/v2/dashboard/news/page.tsx, src/app/v2/dashboard/plans/page.tsx, src/app/v2/dashboard/tasks/page.tsx
Adds the v2 dashboard layout, top bar, bottom navigation, auth guard, and placeholder section pages.
V2 dashboard data cards
src/app/v2/dashboard/page.tsx, src/components/v2/dashboard/*, src/hooks/query/*, src/hooks/useUser.ts
Adds the v2 wallet dashboard page, summary cards, performance grid, portfolio chart, and supporting hook logic for the data display.

Runtime, data, and tooling

Layer / File(s) Summary
Toolchain and package updates
package.json, eslint.config.mjs, next.config.mjs, postcss.config.mjs, components.json, tsconfig.json, .env.example
Updates package scripts and dependencies, ESLint, PostCSS, Next config, component metadata, and TypeScript settings.
Theme tokens and CSS
src/styles/theme.css, tailwind.config.ts
Replaces the Tailwind config file and expands the stylesheet theme and token setup for the new design system.
Database, constants, and query helpers
src/lib/constants.ts, src/lib/query-keys.ts, src/lib/formatters.ts, src/db/connect.ts, src/hooks/useAuthGuard.ts, src/hooks/query/useRegisterTelegramUser.ts, src/hooks/query/useUserCount.ts, src/hooks/query/useKycStatus.ts, src/hooks/query/useSubscriptionCurrent.ts, src/hooks/query/useTransactionHistory.ts, src/hooks/query/useWalletSummary.ts, src/lib/types.ts
Adds Telegram constants, query keys, query hooks, and a cached MongoDB connection helper.
Store and route signatures
src/lib/store.ts, src/app/api/subscription/cancel/route.ts, src/app/api/subscription/current/route.ts, src/app/api/subscription/payhere-link/route.ts, src/app/api/transaction/latest/route.ts, src/app/api/transaction/list/route.ts, src/app/api/user/[id]/route.ts, src/app/api/user/exists/[telegramId]/route.ts
Reshapes the global store state and updates API handlers to await headers or params in user, subscription, and transaction routes.

Estimated code review effort: 4 (Complex) | ~75 minutes

Possibly related PRs

Suggested labels: enhancement, Review effort 4/5

Suggested reviewers: helloscoopa, saga95, mrcentimetre

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is related to the UI refresh, but it is too vague to clearly describe the main change. Use a more specific title that names the primary scope, such as the dashboard and component/UI revamp.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch revamp-ui

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Revamp wallet, subscription, and landing UIs to match Figma + new UI kit

✨ Enhancement ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Replace landing, wallet, and subscription screens with the new Figma-based UI.
• Introduce reusable UI components (buttons, cards, inputs, toggles) for consistency.
• Update bottom navigation and add a /dev component preview page.
Diagram

graph TD
  A["Home page (/)"] --> D["UI kit components"] --> F["Bottom navigation"]
  B["Wallet page (/dashboard)"] --> D --> G["Backend APIs"]
  C["Subscription page (/dashboard/subscription)"] --> D --> G
  E["Dev preview (/dev)"] --> D
  B --> F
  C --> F
  E --> F
  G[("/api/* endpoints")]

  subgraph Legend
    direction LR
    _page["Page"] ~~~ _ui["UI component"] ~~~ _api[("API")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Centralize formatting + plan metadata utilities
  • ➕ Removes duplicated PLAN_EMOJIS and currency/number/date formatting logic
  • ➕ Reduces risk of inconsistent display between wallet and subscription pages
  • ➕ Improves testability of formatting edge cases (e.g., total_lkr parsing)
  • ➖ Small extra refactor in an already large UI PR
  • ➖ Requires deciding on a shared util/module location and API
2. Adopt a standard component library (e.g., Radix/shadcn) for primitives
  • ➕ Accessibility and interaction states handled by proven primitives
  • ➕ Less custom CSS maintenance long-term
  • ➕ Easier to add more components consistently
  • ➖ Introduces dependency and style conventions that may not match Figma exactly
  • ➖ Migration cost from existing custom components
3. Move subscription + auth fetching into React Query hooks layer
  • ➕ Creates stable, reusable data hooks (useWalletSummary/useSubscriptionCurrent)
  • ➕ Easier cache invalidation on cancel/subscribe events
  • ➕ Simplifies page components and reduces effect complexity
  • ➖ Requires new hook abstractions and query-key conventions
  • ➖ May increase indirection for simple pages

Recommendation: Keep the PR’s UI-kit approach (it’s appropriate for a Figma-driven revamp), but strongly consider a follow-up to centralize shared plan/formatting helpers and to extract React Query hooks. Also, verify the Home page navigation callback (currently set to a no-op) to avoid shipping a landing screen with non-functional primary actions.

Files changed (17) +2030 / -1424

Enhancement (16) +2029 / -1423
page.tsxRedesign wallet dashboard UI with plan + privacy toggle +406/-597

Redesign wallet dashboard UI with plan + privacy toggle

• Replaces the prior wallet UI with a new Figma-styled layout including total balance header, 24h change pill, an active plan card, investment stats card, and a chart container. Adds a balance visibility toggle and a new subscription fetch to display the active plan, while keeping Telegram auth + KYC gating.

src/app/dashboard/page.tsx

page.tsxRebuild subscription screen using new UI kit and tabs +409/-623

Rebuild subscription screen using new UI kit and tabs

• Refactors the subscription flow into a 'Plans' vs 'My Membership' tab layout with a duration toggle and new plan cards. Keeps PayHere link generation and cancel flow, adds improved empty/error states, and standardizes styling via new UI components.

src/app/dashboard/subscription/page.tsx

page.tsxAdd /dev component preview page for design system +200/-0

Add /dev component preview page for design system

• Introduces a developer-facing page to preview and validate new UI primitives (buttons, cards, plan cards, inputs, toggles, filters, copy field). Includes BottomNavigation to preview nav styling in context.

src/app/dev/page.tsx

page.tsxReplace landing page with new Figma onboarding/returning layouts +225/-80

Replace landing page with new Figma onboarding/returning layouts

• Replaces the old Telegram UI-based landing with new NewUser/ExistingUser screens, including a custom user-count progress bar and action cards. Note: the primary navigation callback is currently a no-op (goToDashboard does not route).

src/app/page.tsx

LoadingPage.tsxRedesign loading screen visuals +58/-72

Redesign loading screen visuals

• Updates LoadingPage to a new centered spinner ring with the Bitcoin Deepa logo and simplified messaging. Removes animated-dot logic and aligns colors with the new design palette.

src/components/LoadingPage.tsx

bottomNavigation.tsxRevamp bottom navigation with new icons and destinations +110/-51

Revamp bottom navigation with new icons and destinations

• Replaces icon library usage with custom SVG icons and updates nav items to Wallet, Activity, Plans, Tasks, and News. Implements a floating, blurred pill-style nav with active-state highlighting.

src/components/bottomNavigation.tsx

button.tsxIntroduce reusable Button component with variants +74/-0

Introduce reusable Button component with variants

• Adds a new Button primitive supporting variants (primary/secondary/ghost/destructive/success), loading state, and sizing. Standardizes typography, shadows, and interaction states.

src/components/ui/button.tsx

category-filter.tsxAdd category filter chips and group component +61/-0

Add category filter chips and group component

• Introduces CategoryFilter and CategoryFilterGroup components for selectable filter chips with selected/unselected styling.

src/components/ui/category-filter.tsx

clickable-card.tsxAdd ClickableCard UI pattern +81/-0

Add ClickableCard UI pattern

• Adds a card-like button component with icon, title/subtitle, chevron affordance, and disabled handling for consistent action rows.

src/components/ui/clickable-card.tsx

copy-field.tsxAdd CopyField with inline copy button +61/-0

Add CopyField with inline copy button

• Adds a copy-to-clipboard field with a dedicated copy button and transient 'Copied!' feedback state.

src/components/ui/copy-field.tsx

page-title.tsxAdd PageTitle header component +24/-0

Add PageTitle header component

• Introduces a simple title/subtitle component to standardize page headings in the new UI.

src/components/ui/page-title.tsx

plan-card.tsxAdd selectable PlanCard component +102/-0

Add selectable PlanCard component

• Introduces a rich plan card UI with radio indicator, emoji slot, pricing, 'Most Popular' badge, and selected/active highlighting.

src/components/ui/plan-card.tsx

search-field.tsxAdd SearchField input with icon +37/-0

Add SearchField input with icon

• Adds a search input component with embedded icon and standardized styling.

src/components/ui/search-field.tsx

text-field.tsxAdd TextField and TextAreaField form primitives +92/-0

Add TextField and TextAreaField form primitives

• Introduces styled input and textarea components supporting labels, disabled state, focus states, and error messaging.

src/components/ui/text-field.tsx

toggle-plan.tsxAdd weekly/monthly toggle control +41/-0

Add weekly/monthly toggle control

• Adds TogglePlan segmented control for switching plan duration between weekly and monthly.

src/components/ui/toggle-plan.tsx

visible-toggle.tsxAdd visibility toggle button for masking sensitive values +48/-0

Add visibility toggle button for masking sensitive values

• Introduces a reusable eye open/closed toggle with light/dark styling to mask/unmask displayed balances.

src/components/ui/visible-toggle.tsx

Other (1) +1 / -1
package.jsonChange dev server port +1/-1

Change dev server port

• Updates the Next.js dev script to run on port 3348 instead of 3347.

package.json

@qodo-code-review

qodo-code-review Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Home CTA navigation broken 🐞 Bug ≡ Correctness
Description
Home passes a no-op goToDashboard handler into both NewUserScreen and ExistingUserScreen, so the
primary buttons never navigate to /dashboard. This breaks the main entry flow for both new and
returning users.
Code

src/app/page.tsx[R226-228]

+  // const goToDashboard = () => router.push("/dashboard");
+  const goToDashboard = () => {};
+
Evidence
goToDashboard is explicitly set to an empty function and then passed to both the existing-user and
new-user CTA props, making all those CTAs inert.

src/app/page.tsx[226-235]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The `goToDashboard` handler in `src/app/page.tsx` is currently an empty function, so the Home screen CTAs do nothing.

### Issue Context
The UI renders buttons/cards intended to open the wallet/dashboard, but `goToDashboard` was changed from a `router.push("/dashboard")` to a no-op.

### Fix Focus Areas
- src/app/page.tsx[226-235]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Wallet errors not surfaced 🐞 Bug ◔ Observability
Description
WalletPage throws on wallet summary fetch failure but does not read/render error, and it coerces
missing data to 0, so API failures can silently show "0" balances. Subscription fetch errors are
also collapsed to null ("No active plan"), masking backend failures.
Code

src/app/dashboard/page.tsx[R250-273]

+  const { data: summary, isLoading } = useQuery<DCSummary>({
+    queryKey: queryKeys.walletSummary,
+    queryFn: async () => {
+      const res = await fetch("/api/transaction/dca-summary", {
+        headers: { "Content-Type": "application/json", Authorization: `Bearer ${authToken}` },
+      });
+      if (!res.ok) throw new Error("Failed to fetch wallet summary");
+      return res.json();
+    },
+    enabled: !!authToken,
+    staleTime: 1000 * 60 * 5,
+  });
+
+  const { data: subscription } = useQuery<Subscription | null>({
+    queryKey: ["subscription-current"],
+    queryFn: async () => {
+      const res = await fetch("/api/subscription/current", {
+        headers: { "Content-Type": "application/json", Authorization: `Bearer ${authToken}` },
+      });
+      if (!res.ok) return null;
+      return res.json();
+    },
+    enabled: !!authToken,
+  });
Evidence
The wallet summary query throws on non-OK responses, but only data and isLoading are consumed;
later, computed totals fall back to 0 when summary is missing. Subscription failures are treated
as null by returning null on non-OK responses.

src/app/dashboard/page.tsx[250-273]
src/app/dashboard/page.tsx[322-338]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Dashboard data-fetch failures can be silently masked:
- wallet summary query throws but UI ignores `error`
- subscription query returns `null` on non-OK responses

### Issue Context
The UI uses `summary?.… ?? 0` fallbacks, which converts missing data into valid-looking zeros.

### Fix Focus Areas
- src/app/dashboard/page.tsx[250-273]
- src/app/dashboard/page.tsx[322-338]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Subscription errors suppressed 🐞 Bug ◔ Observability
Description
The subscription page ignores errors while fetching current subscription (catch(() => {})), which
can silently clear or fail to update membership state. This makes it hard to distinguish “no
subscription” from “request failed.”
Code

src/app/dashboard/subscription/page.tsx[R217-246]

+  useEffect(() => {
+    if (!authToken || packages.length === 0) return;
+    fetch("/api/subscription/current", {
+      headers: { "Content-Type": "application/json", Authorization: `Bearer ${authToken}` },
+    })
+      .then((r) => r.json())
+      .then((result) => {
+        if (result.subscription) {
+          const sub = result.subscription;
+          const pkg = packages.find((p) => p.id === sub.package_id);
+          setSubscription({
+            id: sub.payhere_sub_id,
+            planName: pkg?.name ?? "Unknown Plan",
+            planType: sub.frequency ?? "monthly",
+            price: pkg?.amount ?? 0,
+            currency: "Rs",
+            startDate: sub.created_at,
+            endDate: sub.next_billing_date ?? (pkg ? calculateEndDate(sub.created_at, pkg.type) : sub.updated_at),
+            isActive: sub.is_active,
+            packageId: sub.package_id,
+            userId: sub.user_id,
+            payhereSubId: sub.payhere_sub_id,
+          });
+          setSelectedPlanId(sub.package_id);
+        } else {
+          setSubscription(null);
+        }
+      })
+      .catch(() => {});
+  }, [authToken, packages, setSubscription, calculateEndDate]);
Evidence
The client fetch for /api/subscription/current ends with .catch(() => {}), discarding errors.
The API route emits structured JSON errors for non-OK upstream responses, so the client could
surface those instead of suppressing them.

src/app/dashboard/subscription/page.tsx[217-246]
src/app/api/subscription/current/route.ts[32-52]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Current-subscription fetch errors are swallowed, removing observability and making the UI state ambiguous.

### Issue Context
The API endpoint returns structured JSON errors (with status codes), but the client currently treats failures as an empty catch.

### Fix Focus Areas
- src/app/dashboard/subscription/page.tsx[217-246]
- src/app/api/subscription/current/route.ts[32-52]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. KYC errors swallowed 🐞 Bug ☼ Reliability
Description
The KYC status check intentionally swallows all fetch errors, so users can remain in an incorrect
state (e.g., not redirected to verification) with no indication or logs. This also makes production
issues harder to diagnose.
Code

src/app/dashboard/page.tsx[R312-320]

+  useEffect(() => {
+    if (!authToken) return;
+    fetch("/api/user/kyc/status", {
+      headers: { "Content-Type": "application/json", Authorization: `Bearer ${authToken}` },
+    })
+      .then((r) => (r.ok ? r.json() : null))
+      .then((data) => { if (data && data.status !== "APPROVED") router.push("/verification"); })
+      .catch(() => {});
+  }, [authToken, router]);
Evidence
The KYC status request ends with an empty catch handler, ensuring failures are neither logged nor
reflected in UI state.

src/app/dashboard/page.tsx[312-320]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The KYC fetch path suppresses errors (`catch(() => {})`), preventing visibility and any fallback behavior.

### Issue Context
KYC status gates access/redirect to `/verification`; failures should be observable and ideally show a retry or a safe redirect.

### Fix Focus Areas
- src/app/dashboard/page.tsx[312-320]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
5. Dev preview exposed in Home ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
The Home page links users to /dev, which is a component-preview screen meant for internal UI
testing. Shipping this link makes internal/demo UI accessible in production builds.
Code

src/app/page.tsx[R126-130]

+      <div className="mt-6 flex justify-center">
+        <Link href="/dev" className="text-xs text-[#cbd5e1] underline">
+          Component Preview (Dev)
+        </Link>
+      </div>
Evidence
Home renders a visible link to /dev, and the /dev route is a component preview page with demo
interactions, indicating it’s not end-user functionality.

src/app/page.tsx[126-130]
src/app/dev/page.tsx[31-80]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Home links to `/dev` (Component Preview), exposing an internal UI sandbox to end users.

### Issue Context
`/dev` is a design-system/demo page (includes demo interactions like `alert()`), which is usually not intended for production traffic.

### Fix Focus Areas
- src/app/page.tsx[126-130]
- src/app/dev/page.tsx[31-80]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

6. Dev/start port mismatch ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
package.json changes the dev port to 3348 but keeps the start port at 3347, which can break
scripts and cause confusion when switching between dev and start. This is especially risky if
tooling/documentation assumes a single port.
Code

package.json[R6-9]

+        "dev": "next dev -p 3348",
        "build": "next build",
        "start": "next start -p 3347",
        "lint": "next lint",
Evidence
The scripts explicitly configure different ports for dev vs start.

package.json[5-9]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`dev` runs on port 3348 while `start` runs on 3347, which can cause inconsistent behavior and surprises.

### Issue Context
If consumers expect a single port (CI, docs, reverse proxy config), this mismatch can cause failures.

### Fix Focus Areas
- package.json[6-9]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread src/app/page.tsx Outdated
Comment thread src/app/page.tsx Outdated
Comment thread src/app/dashboard/page.tsx
Comment thread src/app/dashboard/page.tsx
Comment thread src/app/dashboard/subscription/page.tsx
Comment thread package.json Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 17

🧹 Nitpick comments (9)
src/components/ui/button.tsx (2)

62-65: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Missing aria-busy on loading state.

Consider adding aria-busy={loading} when loading is 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 lift

Hardcoded hex colors repeated across every new UI component.

The same brand hex values (#fa7119, #1b2027, #64748b, #475569, etc.) are duplicated verbatim across button.tsx, clickable-card.tsx, toggle-plan.tsx, and category-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 win

Guard 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 value

Consider 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 win

Icon-only toggle lacks accessible name/state.

The button has no text content, aria-label, or aria-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 value

Consider an accessible name beyond placeholder.

The input relies only on placeholder for its accessible name; screen readers may not announce it consistently. Consider allowing aria-label/aria-labelledby pass-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.

state accepts "default" | "active" | "disabled", but only the "disabled" branch has any effect (line 14/57); passing state="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 win

Label not programmatically associated with the input.

Neither TextField nor TextAreaField wires htmlFor/id between the <label> and the control, so screen readers won't associate them unless the caller manually passes matching id/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 win

Expose 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 no aria-pressed/role="radio"/aria-checked to convey isHighlighted state 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

📥 Commits

Reviewing files that changed from the base of the PR and between 2cdd162 and 6f66d04.

⛔ Files ignored due to path filters (6)
  • bun.lockb is excluded by !**/bun.lockb
  • package-lock.json is excluded by !**/package-lock.json
  • public/BDLogo_Black.svg is excluded by !**/*.svg
  • public/bd-wordmark-light.png is excluded by !**/*.png
  • public/btc-coin-3d.png is excluded by !**/*.png
  • public/gift-emoji-3d.png is excluded by !**/*.png
📒 Files selected for processing (17)
  • package.json
  • src/app/dashboard/page.tsx
  • src/app/dashboard/subscription/page.tsx
  • src/app/dev/page.tsx
  • src/app/page.tsx
  • src/components/LoadingPage.tsx
  • src/components/bottomNavigation.tsx
  • src/components/ui/button.tsx
  • src/components/ui/category-filter.tsx
  • src/components/ui/clickable-card.tsx
  • src/components/ui/copy-field.tsx
  • src/components/ui/page-title.tsx
  • src/components/ui/plan-card.tsx
  • src/components/ui/search-field.tsx
  • src/components/ui/text-field.tsx
  • src/components/ui/toggle-plan.tsx
  • src/components/ui/visible-toggle.tsx

Comment thread package.json Outdated
Comment thread src/app/dashboard/page.tsx
Comment thread src/app/dashboard/page.tsx
Comment thread src/app/dashboard/page.tsx
Comment thread src/app/dashboard/page.tsx
Comment thread src/components/ui/button.tsx
Comment thread src/components/ui/category-filter.tsx
Comment thread src/components/ui/clickable-card.tsx
Comment thread src/components/ui/copy-field.tsx
Comment thread src/components/ui/toggle-plan.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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 win

Remove leftover debugger; and sensitive response logging.

A stray debugger; statement and a console.log of 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+ supports next start --inspect for 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 value

Stylelint 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

📥 Commits

Reviewing files that changed from the base of the PR and between 6f66d04 and 69647d2.

⛔ Files ignored due to path filters (4)
  • bun.lockb is excluded by !**/bun.lockb
  • package-lock.json is excluded by !**/package-lock.json
  • public/emoji/bitcoin.svg is excluded by !**/*.svg
  • public/emoji/gift.svg is excluded by !**/*.svg
📒 Files selected for processing (40)
  • .env.example
  • .eslintrc.json
  • components.json
  • eslint.config.mjs
  • next.config.mjs
  • package.json
  • postcss.config.mjs
  • src/app/api/subscription/cancel/route.ts
  • src/app/api/subscription/current/route.ts
  • src/app/api/subscription/payhere-link/route.ts
  • src/app/api/transaction/latest/route.ts
  • src/app/api/transaction/list/route.ts
  • src/app/api/user/[id]/route.ts
  • src/app/api/user/exists/[telegramId]/route.ts
  • src/app/context/providers.tsx
  • src/app/context/theme.tsx
  • src/app/dashboard/history/page.tsx
  • src/app/dashboard/invite/page.tsx
  • src/app/dashboard/subscription/page.tsx
  • src/app/dev/page.tsx
  • src/app/layout.tsx
  • src/app/onboard/page.tsx
  • src/app/page.tsx
  • src/app/subscription/page.tsx
  • src/app/verification/page.tsx
  • src/components/skeletons/BalanceCardSkeleton.tsx
  • src/components/ui/button.tsx
  • src/components/ui/clickable-card.tsx
  • src/components/ui/plan-card.tsx
  • src/components/ui/search-field.tsx
  • src/components/ui/text-field.tsx
  • src/components/ui/visible-toggle.tsx
  • src/db/connect.ts
  • src/hooks/query/useRegisterTelegramUser.ts
  • src/hooks/query/useUserCount.ts
  • src/lib/constants.ts
  • src/lib/query-keys.ts
  • src/styles/theme.css
  • tailwind.config.ts
  • tsconfig.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

Comment thread components.json
Comment thread src/app/page.tsx Outdated
Comment thread src/app/page.tsx Outdated
Comment thread src/db/connect.ts Outdated
Comment thread src/hooks/query/useRegisterTelegramUser.ts Outdated
Comment thread src/hooks/query/useUserCount.ts Outdated
Comment thread src/hooks/query/useUserCount.ts
Comment thread src/styles/theme.css
Comment thread src/styles/theme.css
Comment thread tsconfig.json
* 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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 win

Gate registration on Telegram ID and dedupe per user. username is optional, so this skips valid Telegram users without one; the effect can also re-submit the same id on re-renders. Use id as 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 win

Consider next/link for prefetching instead of router.push on 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 win

Month-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. The windowed.length >= 2 ? windowed : points fallback 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

fmtRelativeDays compares 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 to diff === 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 win

No 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) render children unconditionally regardless of auth status, so an unauthenticated user briefly sees the dashboard shell before being pushed to /onboard. Consider returning an isChecking/isAuthenticated flag 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

isExistingUser is destructured but unused.

Dead variable — either use it to gate children/redirect UX (see useAuthGuard.ts comment) 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 value

Custom inline ChevronRight SVG instead of lucide-react.

TotalValueCard.tsx already imports icons (ChevronDown, TrendingUp, TrendingDown) from lucide-react in this same feature area. Consider using lucide-react's ChevronRight here 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 value

Hardcoded "Rs" prefix ignores subscription.currency.

The Subscription type carries a currency field (see MOCK_SUBSCRIPTION.currency in 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 win

Silent catch-all hides genuine failures.

The empty catch swallows 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

📥 Commits

Reviewing files that changed from the base of the PR and between 5058353 and fc2384e.

⛔ Files ignored due to path filters (16)
  • public/emoji/bluewhale.svg is excluded by !**/*.svg
  • public/emoji/calculator.svg is excluded by !**/*.svg
  • public/emoji/calendar1.svg is excluded by !**/*.svg
  • public/emoji/calendar2.svg is excluded by !**/*.svg
  • public/emoji/coins.svg is excluded by !**/*.svg
  • public/emoji/crab.svg is excluded by !**/*.svg
  • public/emoji/graph.svg is excluded by !**/*.svg
  • public/emoji/shark.svg is excluded by !**/*.svg
  • public/emoji/shrimp.svg is excluded by !**/*.svg
  • public/emoji/wallet.svg is excluded by !**/*.svg
  • public/emoji/whale.svg is excluded by !**/*.svg
  • public/icons/activity.svg is excluded by !**/*.svg
  • public/icons/news.svg is excluded by !**/*.svg
  • public/icons/plans.svg is excluded by !**/*.svg
  • public/icons/tasks.svg is excluded by !**/*.svg
  • public/icons/wallet.svg is excluded by !**/*.svg
📒 Files selected for processing (30)
  • next.config.mjs
  • public/bg/star.webp
  • public/bg/wallet.webp
  • src/app/context/tma.tsx
  • src/app/page.tsx
  • src/app/v2/dashboard/activity/page.tsx
  • src/app/v2/dashboard/layout.tsx
  • src/app/v2/dashboard/news/page.tsx
  • src/app/v2/dashboard/page.tsx
  • src/app/v2/dashboard/plans/page.tsx
  • src/app/v2/dashboard/tasks/page.tsx
  • src/components/ui/visible-toggle.tsx
  • src/components/v2/bottomNavigation.tsx
  • src/components/v2/dashboard/DashboardTopBar.tsx
  • src/components/v2/dashboard/PerformanceGrid.tsx
  • src/components/v2/dashboard/PlanSummaryCard.tsx
  • src/components/v2/dashboard/PortfolioChart.tsx
  • src/components/v2/dashboard/TotalValueCard.tsx
  • src/db/connect.ts
  • src/hooks/query/useKycStatus.ts
  • src/hooks/query/useRegisterTelegramUser.ts
  • src/hooks/query/useSubscriptionCurrent.ts
  • src/hooks/query/useTransactionHistory.ts
  • src/hooks/query/useUserCount.ts
  • src/hooks/query/useWalletSummary.ts
  • src/hooks/useAuthGuard.ts
  • src/hooks/useUser.ts
  • src/lib/formatters.ts
  • src/lib/store.ts
  • src/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

Comment thread src/app/page.tsx
Comment on lines +93 to +97
<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>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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" src

Repository: 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=' src

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

Suggested change
<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.

Comment on lines +16 to +22
const MOCK_SUMMARY = {
dca: { balance: 107000, spent: 32000, avg_btc_price: 29500000 },
total_balance: 107000,
total_lkr: 40000,
currency: "LKR",
"24_hr_change": 2.15,
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

MOCK_SUMMARY.total_lkr type diverges from DCSummary.

MOCK_SUMMARY.total_lkr is a number (40000) while DCSummary.total_lkr (types.ts) is declared as string. The typeof branch at Lines 61-63 exists purely to cope with this drift, which suggests the real API can also return a number and DCSummary should reflect that (string | number) rather than the mock silently diverging from the declared type.

As per coding guidelines, "Use the core domain types defined in src/lib/types.ts" — the domain type should accurately describe the shape actually handled by consumers.

Also applies to: 60-64

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/v2/dashboard/page.tsx` around lines 16 - 22, `MOCK_SUMMARY.total_lkr`
is using a number while `DCSummary` still declares it as a string, causing the
dashboard mock and consumer handling to diverge. Update the core domain type in
`src/lib/types.ts` so `DCSummary.total_lkr` accepts the actual shape handled by
the app (string | number), then align `MOCK_SUMMARY` in `dashboard/page.tsx` to
that shared type. After that, simplify the `typeof` handling around the
`total_lkr` usage so it reflects the corrected domain model instead of
compensating for a mock-only mismatch.

Source: Coding guidelines

Comment on lines +44 to +58
export default function WalletV2Page() {
const { balanceVisible } = useStore();
const { subscription: realSubscription } = useUser();

const { data: rawSummary, isLoading: isSummaryLoading } = useWalletSummary();
const { data: rawTransactions } = useTransactionHistory();

const usingMock = !isSummaryLoading && !rawSummary;
const summary = rawSummary ?? (usingMock ? MOCK_SUMMARY : undefined);
const subscription = realSubscription ?? (usingMock ? MOCK_SUBSCRIPTION : null);
const transactions = rawTransactions?.length
? rawTransactions
: usingMock
? MOCK_TRANSACTIONS
: rawTransactions;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Mock data can silently mask real backend errors in production.

usingMock only checks !isSummaryLoading && !rawSummary — it can't distinguish "no auth token yet" from a genuine fetch failure (expired token, 401, 500, network error). If useWalletSummary() errors out in production, this renders MOCK_SUMMARY/MOCK_SUBSCRIPTION/MOCK_TRANSACTIONS as if they were the user's real wallet balance and subscription, with no error indication. For a financial dashboard this is a serious correctness/trust issue.

Gate the mock fallback to development only, and separately surface a real error state instead of silently substituting fake data.

🐛 Proposed fix
-  const { data: rawSummary, isLoading: isSummaryLoading } = useWalletSummary();
+  const { data: rawSummary, isLoading: isSummaryLoading, isError: isSummaryError } = useWalletSummary();
   const { data: rawTransactions } = useTransactionHistory();

-  const usingMock = !isSummaryLoading && !rawSummary;
+  const usingMock =
+    process.env.NODE_ENV !== "production" &&
+    !isSummaryLoading &&
+    !isSummaryError &&
+    !rawSummary;
+
+  if (isSummaryError) {
+    // TODO: render an explicit error/retry state instead of falling through
+  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/v2/dashboard/page.tsx` around lines 44 - 58, The mock fallback in
WalletV2Page is too broad and can hide real fetch failures by treating any
non-loading empty summary as mock data. Update the logic around
useWalletSummary, useTransactionHistory, and the derived
usingMock/summary/subscription/transactions values so mock data is only used in
development, and add an explicit error state for production when the wallet
queries fail instead of falling back to MOCK_SUMMARY, MOCK_SUBSCRIPTION, or
MOCK_TRANSACTIONS.

Comment on lines +3 to +4
import { Eye, EyeOff } from "lucide-react";
import { IconButton } from "@telegram-apps/telegram-ui";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

className prop is accepted but silently ignored.

VisibleToggleProps.className is still part of the public API, but its application (cn(...)) is commented out, so any caller passing className gets no effect. Combined with the unused cn import and commented-out mode logic, this looks like an incomplete refactor left in.

🧹 Suggested cleanup
     <IconButton
-      // mode={dark ? "plain" : "outline"}
       mode="gray"
       size="m"
       onClick={onToggle}
-      // className={cn(!dark && "bg-white! dark:bg-[`#1b2027`]!", className)}
+      className={cn(!dark && "bg-white! dark:bg-[`#1b2027`]!", className)}
     >

If the mode/className behavior is intentionally deferred, consider removing the now-dead className prop and cn import until re-enabled, to keep the component's contract accurate.

Also applies to: 14-31

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/ui/visible-toggle.tsx` around lines 3 - 4, The VisibleToggle
component currently exposes VisibleToggleProps.className but never applies it,
so callers see no styling effect. Update the VisibleToggle implementation to
either re-enable the cn(...) merge and any related mode logic in the component
body, or remove className from the prop contract if it is intentionally
unsupported; also clean up the unused cn import and any commented-out mode code.
Keep the fix centered on VisibleToggle and VisibleToggleProps so the public API
matches the actual behavior.

Comment on lines +180 to +195
{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>
))}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 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.

Suggested change
{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.

Comment on lines +12 to +26
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,
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 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.

Suggested change
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

Comment on lines +16 to +26
return useQuery<Subscription | null>({
queryKey: ["subscription-current"],
queryFn: async () => {
const data = await fetchy.get<SubscriptionCurrentResponse>("/api/subscription/current", {
headers: { Authorization: `Bearer ${authToken}` },
shouldCache: false,
});
return data.subscription ?? null;
},
enabled: !!authToken,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Query key not sourced from @/lib/query-keys.

queryKey: ["subscription-current"] is a hardcoded literal instead of being imported from the shared query-keys module. As per coding guidelines, "Use TanStack Query for server state with query keys sourced from @/lib/query-keys".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/query/useSubscriptionCurrent.ts` around lines 16 - 26, The
subscription query in useSubscriptionCurrent currently hardcodes queryKey as a
string literal, so update it to use the shared key from `@/lib/query-keys`
instead. Import the appropriate subscription current key and replace the inline
["subscription-current"] in useQuery with that constant, keeping the rest of the
queryFn and enabled logic unchanged.

Source: Coding guidelines

Comment on lines +40 to +46
const data = await fetchy.get<TransactionListResponse>(
"/api/transaction/list?limit=100",
{
headers: { Authorization: `Bearer ${authToken}` },
shouldCache: false,
}
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ 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 || true

Repository: 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'
done

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

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

Comment thread src/hooks/useUser.ts
Comment on lines +14 to +15
const launchParams = useLaunchParams();
const telegramUser = launchParams.initData?.user;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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


🏁 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.tsx

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants