Skip to content

Revamp UI - Figma#68

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

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

Conversation

@rayaanr

@rayaanr rayaanr commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Introduced a redesigned dashboard with Wallet, Activity, Plans, Tasks, and News sections.
    • Added transaction activity search, category filters, expandable details, and copy-to-clipboard support.
    • Added subscription plan selection, membership management, cancellation, and KYC verification flows.
    • Added balance visibility controls, performance charts, rewards, referral progress, and task views.
    • Added news browsing with featured articles, categories, details, and related stories.
    • Added refreshed navigation, loading states, reusable form controls, and updated visual styling.
  • Bug Fixes

    • Improved authentication, subscription, transaction, KYC, and profile request handling.
    • Improved loading and error states across key screens.

@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 23, 2026 4:49pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds a v2 dashboard with wallet, activity, plans, tasks, news, and verification flows. It also introduces reusable UI components, authenticated data hooks, Telegram integrations, cached database access, and migrations for Next.js, Tailwind CSS, ESLint, theming, and static assets.

Changes

Product flows and reusable UI

Layer / File(s) Summary
Shared UI primitives
src/components/ui/*, src/components/LoadingPage.tsx
Adds reusable controls for buttons, cards, fields, filters, copy actions, visibility toggles, plan selection, and loading states.
Landing and registration flow
src/app/page.tsx, src/hooks/query/useRegisterTelegramUser.ts, src/hooks/query/useUserCount.ts
Reworks Telegram registration, user-count progress, theme-aware layout, and links to v2 dashboard routes.
Wallet and subscription dashboard rewrite
src/app/dashboard/*, src/lib/store.ts
Rebuilds wallet and subscription pages around authenticated summaries, current subscriptions, KYC checks, plan selection, cancellation, and balance visibility.
V2 dashboard shell and navigation
src/app/v2/dashboard/layout.tsx, src/app/v2/dashboard/page.tsx, src/components/v2/bottomNavigation.tsx, src/components/v2/dashboard/wallet/*
Adds the authenticated v2 shell, five-route navigation, wallet metric cards, portfolio chart, and shared balance masking.
Activity history flow
src/app/v2/dashboard/activity/*, src/components/v2/dashboard/activity/*, src/lib/activity.ts
Normalizes successful transactions into searchable, categorized, date-grouped activity rows with expandable settlement details.
V2 plans and gifts
src/app/v2/dashboard/plans/*, src/app/v2/plans/choose/page.tsx, src/components/v2/dashboard/plans/*
Adds KYC-gated plan management, subscription actions, plan summaries, and gift-plan UI backed by mock gift activity.
V2 tasks and referrals
src/app/v2/dashboard/tasks/*, src/components/v2/dashboard/tasks/*
Adds task filtering, task reward summaries, mock task data, referral progress, and Telegram share actions.
V2 news
src/app/v2/dashboard/news/*, src/components/v2/dashboard/news/*, src/lib/news.ts
Adds RSS ingestion, sanitization, featured articles, category filtering, article details, related articles, and loading states.
V2 verification flow
src/app/v2/verification/*, src/hooks/query/useKyc.ts, src/app/api/user/profile/route.ts
Adds KYC status handling, mobile checks, profile updates, validated details submission, and KYC initiation.
Component preview page
src/app/dev/page.tsx
Adds an interactive preview page for the shared design-system components.

Platform, data, and tooling

Layer / File(s) Summary
Authentication and app context
src/app/context/*, src/hooks/useAuthGuard.ts, src/hooks/useRegisterV2User.ts, src/hooks/useUser.ts
Adds v2 authentication registration, centralized user identity, Telegram-safe initialization, themed app roots, and theme overrides.
Data hooks and persistence
src/hooks/query/*, src/lib/formatters.ts, src/lib/types.ts, src/db/connect.ts
Adds data normalization hooks, query keys, formatting helpers, expanded models, and cached Mongoose connection handling.
API route compatibility
src/app/api/subscription/*, src/app/api/transaction/*, src/app/api/user/*
Updates asynchronous Next.js request APIs, forwards KYC/profile bodies, and routes PayHere KYC failures to v2 verification.
Toolchain and theme migration
package.json, eslint.config.mjs, postcss.config.mjs, components.json, src/styles/theme.css, tsconfig.json, next.config.mjs
Updates dependencies, linting, Tailwind/PostCSS configuration, theme tokens, TypeScript settings, dev origins, and cache headers.
Static assets and UI styling support
public/bg/*, public/emoji/*, src/app/*, src/components/skeletons/*
Replaces image assets and updates gradient, blur, loading, onboarding, history, invite, subscription, verification, and skeleton styling.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: enhancement

Suggested reviewers: mrcentimetre

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.02% 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 generic and doesn't describe the specific UI, routing, or dependency changes in the patch. Rename it to reflect the main change, e.g. "Revamp dashboard and v2 UI for Figma redesign".
✅ 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

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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

❤️ 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 thread src/app/v2/dashboard/page.tsx Outdated
Comment thread src/app/v2/dashboard/page.tsx Outdated
Comment thread src/components/ui/visible-toggle.tsx
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 thread src/hooks/query/useKycStatus.ts Outdated
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 thread src/hooks/query/useSubscriptionCurrent.ts
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.

…ng (#74)

* feat: add emoji icons

* feat: enhance activity dashboard with search, tabs, and grouping functionality

* feat: update ActivityRow and ActivityTabs for improved styling and add surface-gift color variable

* feat: enhance CopyField and CopyButton components with improved styling and functionality

* feat: integrate DashboardTopBar into WalletV2Page and update user data handling

* feat: update UI components with new dark mode colors and improve styling consistency

* feat: refactor dashboard components to use consistent surface color variables and improve styling

* feat: add success color variable and update PerformanceGrid styles for consistency

* feat: add wallet dashboard components including TotalValueCard, PerformanceGrid, PlanSummaryCard, PortfolioChart, and DashboardTopBar

* feat: replace BottomNavigation import with new .txt file and update related components

* feat: implement gift management features with mock data and UI components

* feat: add task management components including TaskHeroCard, ReferralProgressCard, ManageTasksSection, TaskFilterTabs, TaskListCard, and TaskRow with mock data

* feat: implement subscription plan selection page and mock data for packages

* refactor: update ReferralProgressCard, TaskHeroCard, and TaskRow components to use images and badges

- Replaced gradient backgrounds with images in ReferralProgressCard and TaskHeroCard.
- Updated TaskRow to use Image component for task icons instead of emoji glyphs.
- Changed task data structure to remove icon background color and use image paths instead of emoji.
- Introduced AvatarStack in ReferralProgressCard for better avatar display.
- Updated styles for better responsiveness and visual consistency.

* feat: enhance plan selection UI with image icons and improve icon retrieval logic

* feat: enhance UI components with dark mode support and improve plan name handling

* feat: update PlanHeroCard and PlanSummaryCard to display subscription start date and improve subscription data handling

* feat: enhance task management UI with task click handling and Telegram link sharing

* feat: replace bottomNavigation.txt with bottomNavigation.tsx for improved component structure

* feat: restructure GiftPlanActions and ManageGiftsSection for improved layout and visibility

* feat: enhance ChoosePlanPage and PlanCard with current plan display and improved selection handling

* feat: add verification intro page and update gifting UI with placeholder component

* feat: add useRegisterV2User hook for user registration on v2 backend and integrate it into TMASetupProvider

* feat: update KYC verification redirects to new v2 path and enhance button styles in verification and plan selection pages

* feat: add mobile warning modal and platform detection for verification process

* feat: implement KYC status handling and initiate verification process in verification page

* refactor: replace ClipboardList icon with Image in PlanHeroCard and update PlanSummaryCard layout

- Updated PlanHeroCard to use an Image component for the icon instead of ClipboardList.
- Refactored PlanSummaryCard to utilize Cell component for better layout and added conditional rendering for subscription details.
- Removed unused ChevronRight component from PlanSummaryCard.

* feat: add ChoosePlanPage component and integrate subscription plan selection functionality

* feat: enhance KYC initiation process with user details and create verification details page

* feat: update navigation links to verification page and enhance verification details form

* feat: implement user profile update functionality and integrate with verification details page

* feat: enhance ChoosePlanPage layout and update verification status messages

* feat: enhance authentication error display with improved layout and retry button

* feat: update CopyButton styling for improved layout and accessibility

* feat: update WalletV2Page to handle current BTC price and enhance DCSummary type

* feat: refactor WalletV2Page and PlansV2Page to remove mock data usage and simplify state management

* feat: integrate transaction history and remove mock data from ActivityV2Page

* feat: add package_name to DcaTransaction and update activity mapping for detailLabel

* feat: implement subscription cancellation functionality and update query keys

* feat: add snackbar notifications for subscription cancellation feedback

* feat: implement news article detail page and related components

* feat: update button label from "Change Plan" to "Manage Plan" in PlanHeroCard

* feat: remove "View Plans" link from PlanHeroCard component

* feat: add KYC verification notice and loading state to plans pages

* feat: enhance plan selection UI with cancel option and improved styling

* feat: refactor back button handling and consolidate imports in news components

* feat: update gift link to disabled state with "Coming Soon" badge

* feat: simplify LoadingPage component usage by removing fullscreen prop

* Refactor image sources in Subscription and Verification pages to use local assets

- Updated SubscriptionPage to use a local hourglass image instead of an external URL.
- Changed verification status images in VerificationIntroPage to use local emoji assets for improved performance and reliability.

* feat: add new animated emojis

* refactor: comment out unused gift plan task in mock data

@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: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/v2/dashboard/page.tsx (1)

17-34: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not present unresolved wallet data as actual account balances.

useWalletSummary() returns undefined during the pending phase and after an error; the current fallback renders LKR 0, zero SATS, and other financial metrics as if they came from the wallet. Treat isLoading/isError/no-summary as a separate rendering state and only derive metrics from a successful summary.

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

In `@src/app/v2/dashboard/page.tsx` around lines 17 - 34, Update the dashboard
data flow around useWalletSummary and the derived metrics so pending, error, or
missing-summary states are rendered separately rather than as zero-valued wallet
balances. Use the hook’s isLoading and isError state, and only calculate
totalLkr, totalSats, dcaSpent, dcaSats, avgBtcPrice, change24h, changeLkr, and
current pricing from a successfully available summary.
🧹 Nitpick comments (1)
src/app/v2/dashboard/plans/page.tsx (1)

25-29: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider centralizing the total_lkr string/number parsing.

Handling both string-with-commas and numeric forms of total_lkr inline here is fragile and likely to be duplicated wherever this field is consumed (e.g., other wallet/dashboard views). A shared helper in src/lib/formatters.ts (which already houses fmtLkr, maskDigits, etc.) would keep this parsing consistent and testable in one place.

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

In `@src/app/v2/dashboard/plans/page.tsx` around lines 25 - 29, Centralize
total_lkr normalization in a shared helper in formatters.ts, handling
comma-separated strings, numeric values, and absent values consistently. Replace
the inline conversion in the currentValueLkr calculation with that helper, and
export it for reuse by other wallet/dashboard consumers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/api/user/kyc/initiate/route.ts`:
- Around line 12-18: Replace the direct fetch calls in
src/app/api/user/kyc/initiate/route.ts lines 12-18 and
src/app/api/user/profile/route.ts lines 12-19 with the custom fetchy wrapper
imported from `@/lib/fetchy`. Extend fetchy if needed so both route handlers
preserve the upstream response status and body behavior.

In `@src/app/onboard/page.tsx`:
- Around line 125-131: Update the style cast in the onboarding page to use an
explicitly imported type CSSProperties from React instead of
React.CSSProperties, adding the type-only import and applying CSSProperties to
the object cast.

In `@src/app/v2/plans/choose/page.tsx`:
- Around line 103-108: Update the handleSubscribe catch block to set the same
error snackbar state used by handleCancel, while retaining console.error for
diagnostics and resetting loading via the existing finally block. Ensure failed
subscriptions provide visible user feedback when the button becomes available
again.
- Around line 81-108: Update handleSubscribe to use the shared fetchy wrapper
from `@/lib/fetchy` instead of raw fetch for the PayHere link request, preserving
the existing POST payload, authorization, redirect handling, loading state, and
error flow.

In `@src/app/v2/verification/details/page.tsx`:
- Around line 153-155: Replace the inaccurate “never Shared” privacy wording in
both verification UI locations: update the text in
src/app/v2/verification/details/page.tsx (lines 153-155) and
src/app/v2/verification/page.tsx (lines 208-211) with privacy-policy-approved
language that accurately describes data handling and any third-party processing.

In `@src/app/v2/verification/page.tsx`:
- Around line 102-105: Update the verification page’s useKycStatus flow so an
unresolved enabled query renders a loading state and keeps the verification CTA
disabled instead of deriving NOT_STARTED from undefined kycData. Handle query
errors separately, and only render the Start Verification path after a
successful response explicitly confirms no status.

In `@src/components/ui/plan-card.tsx`:
- Around line 52-57: Update the Component rendering in the plan-card component
so the type attribute is applied only when rendering the button intrinsic
element. Use explicit div and button branches, or otherwise narrow the
polymorphic tag, while preserving active div behavior and inactive button
onClick behavior.

In `@src/components/v2/dashboard/activity/ActivitySearchBar.tsx`:
- Around line 33-39: Update the filter control around the Funnel IconButton to
either connect it to the existing activity filtering behavior with an onClick
handler, or remove the button entirely if filtering is not implemented; do not
leave a non-functional actionable control.

In `@src/components/v2/dashboard/plans/ManageGiftsSection.tsx`:
- Around line 24-34: Update the ManageGiftsSection component’s root container to
remove the hardcoded hidden state and include the flex display class alongside
flex-col, or gate the entire section from its caller using an explicit feature
flag if the feature remains unlaunched. If gated off, remove the unused
useState/useMemo gift filtering work; otherwise preserve the existing
GiftFilterTabs and GiftListCard rendering.

In `@src/components/v2/dashboard/tasks/mock-data.ts`:
- Around line 3-52: Replace the hard-coded dashboard account data with
authenticated server state: in src/components/v2/dashboard/tasks/mock-data.ts
lines 3-52, move fixtures to tests/stories and stop exporting them for
production; in src/app/v2/dashboard/tasks/page.tsx lines 6-10, fetch task,
reward, and referral data through TanStack Query using keys from
`@/lib/query-keys` and add loading and empty states; in
src/components/v2/dashboard/tasks/TaskHeroCard.tsx lines 7-8, pass an
authoritative timestamped exchange rate with the account data or remove the LKR
equivalent.

In `@src/components/v2/dashboard/tasks/TaskHeroCard.tsx`:
- Around line 23-26: Update btcLkrLabel to preserve satoshi-level precision by
formatting BTC with eight decimal places, or alternatively display the BTC
amount solely in sats. Ensure the 200-sat value is not rendered as ₿0.00000
while retaining the existing LKR formatting.

In `@src/components/v2/dashboard/wallet/PlanSummaryCard.tsx`:
- Around line 80-108: Derive an activeSubscription value from
subscription.isActive in PlanSummaryCard, using null for inactive or cancelled
records. Replace subscription-only checks and data access for the Active badge,
plan copy, and reward cards with activeSubscription, while preserving the
existing fallback rendering for no active subscription.

In `@src/hooks/query/useKyc.ts`:
- Line 17: Update the query configuration in useKyc to import and use the shared
KYC query-key symbol from "`@/lib/query-keys`" instead of the literal
["kyc-status"] value, preserving the existing query behavior.

In `@src/hooks/useRegisterV2User.ts`:
- Around line 18-26: The authenticateWithTelegram flow in useRegisterV2User must
clear stale credentials whenever authentication fails or resolves without a
token, then route the current Telegram user through the existing
re-registration/auth path. Update both the no-token result branch and catch
handler to call clearAuthFromStorage and trigger that path, while preserving
saveAuthToStorage for successful token responses.

In `@src/lib/activity.ts`:
- Around line 53-58: Update the fallback detailLabel in
mapDcaTransactionToActivityItem from the misspelled “Destributed” text to the
correct “Distributed” label, preserving the existing package_name-based label
behavior.

In `@src/lib/hooks.ts`:
- Around line 50-60: Update openTelegramLink in src/lib/hooks.ts#L50-L60 to use
normal browser navigation when webApp is unavailable instead of throwing, and
expose the Telegram Mini App readiness state for callers. In
src/components/v2/dashboard/tasks/ManageTasksSection.tsx#L27-L42, use that
readiness or shareToStory availability to disable the story action until
supported, or display a user-facing unavailable message.

In `@src/lib/news.ts`:
- Line 109: Update the RSS request in the news-fetching flow around FEED_URL to
use the project’s fetchy wrapper from `@/lib/fetchy` instead of direct fetch.
Extend the wrapper’s options handling as needed to preserve the existing server
revalidation setting of 900 seconds, then pass that option through the wrapper.
- Around line 108-125: Update getNewsArticles to catch failures from fetch,
response.text(), and parser.parseString, returning an empty NewsArticle array so
the page reaches its existing “Unable to load news right now” state; preserve
the current successful parsing, sorting, mapping, and filtering behavior.

In `@src/styles/theme.css`:
- Around line 46-48: Terminate both declarations in the theme token definitions
by adding semicolons to --color-surface-glass and --color-surface-tertiary,
ensuring each custom property is parsed independently.

---

Outside diff comments:
In `@src/app/v2/dashboard/page.tsx`:
- Around line 17-34: Update the dashboard data flow around useWalletSummary and
the derived metrics so pending, error, or missing-summary states are rendered
separately rather than as zero-valued wallet balances. Use the hook’s isLoading
and isError state, and only calculate totalLkr, totalSats, dcaSpent, dcaSats,
avgBtcPrice, change24h, changeLkr, and current pricing from a successfully
available summary.

---

Nitpick comments:
In `@src/app/v2/dashboard/plans/page.tsx`:
- Around line 25-29: Centralize total_lkr normalization in a shared helper in
formatters.ts, handling comma-separated strings, numeric values, and absent
values consistently. Replace the inline conversion in the currentValueLkr
calculation with that helper, and export it for reuse by other wallet/dashboard
consumers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 22bc7f01-6b45-4bc6-90b0-c6555a4c1e7a

📥 Commits

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

⛔ Files ignored due to path filters (20)
  • bun.lockb is excluded by !**/bun.lockb
  • public/avatar/boy.png is excluded by !**/*.png
  • public/avatar/girl.png is excluded by !**/*.png
  • public/emoji/faucet_received.svg is excluded by !**/*.svg
  • public/emoji/faucet_sent.svg is excluded by !**/*.svg
  • public/emoji/flag-sri-lanka.png is excluded by !**/*.png
  • public/emoji/gift_receive.svg is excluded by !**/*.svg
  • public/emoji/gift_sent.svg is excluded by !**/*.svg
  • public/emoji/gift_standing_order.svg is excluded by !**/*.svg
  • public/emoji/gift_standing_order_receive.svg is excluded by !**/*.svg
  • public/emoji/gift_standing_order_sent.svg is excluded by !**/*.svg
  • public/emoji/membership_reward.svg is excluded by !**/*.svg
  • public/emoji/notepad.svg is excluded by !**/*.svg
  • public/emoji/receive.svg is excluded by !**/*.svg
  • public/emoji/sent.svg is excluded by !**/*.svg
  • public/emoji/story.svg is excluded by !**/*.svg
  • public/emoji/tasks_reward.svg is excluded by !**/*.svg
  • public/emoji/tipjar_Receive.svg is excluded by !**/*.svg
  • public/emoji/tipjar_sent.svg is excluded by !**/*.svg
  • public/icons/sparkle.svg is excluded by !**/*.svg
📒 Files selected for processing (88)
  • README.md
  • package.json
  • public/bg/note.webp
  • public/bg/pill.webp
  • public/emoji/animated/bomb.webp
  • public/emoji/animated/chart-increasing.webp
  • public/emoji/animated/coffin.webp
  • public/emoji/animated/cross-mark.webp
  • public/emoji/animated/exclamation-mark.webp
  • public/emoji/animated/firecracker.webp
  • public/emoji/animated/high-voltage.webp
  • public/emoji/animated/hourglass.webp
  • public/emoji/animated/id-card.webp
  • public/emoji/animated/key.webp
  • public/emoji/animated/locked-with-key.webp
  • public/emoji/animated/microscope.webp
  • public/emoji/animated/party-popper.webp
  • public/emoji/community.webp
  • src/app/api/subscription/payhere-link/route.ts
  • src/app/api/user/kyc/initiate/route.ts
  • src/app/api/user/profile/route.ts
  • src/app/context/tma.tsx
  • src/app/dashboard/layout.tsx
  • src/app/onboard/page.tsx
  • src/app/page.tsx
  • src/app/subscription/page.tsx
  • src/app/v2/dashboard/activity/page.tsx
  • src/app/v2/dashboard/layout.tsx
  • src/app/v2/dashboard/news/[id]/page.tsx
  • src/app/v2/dashboard/news/loading.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/app/v2/plans/choose/page.tsx
  • src/app/v2/verification/details/page.tsx
  • src/app/v2/verification/page.tsx
  • src/components/bottomNavigation.tsx
  • src/components/skeletons/NewsCardSkeleton.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/toggle-plan.tsx
  • src/components/ui/visible-toggle.tsx
  • src/components/v2/bottomNavigation.tsx
  • src/components/v2/dashboard/activity/ActivityGroup.tsx
  • src/components/v2/dashboard/activity/ActivityRow.tsx
  • src/components/v2/dashboard/activity/ActivitySearchBar.tsx
  • src/components/v2/dashboard/activity/ActivityTabs.tsx
  • src/components/v2/dashboard/news/NewsListSection.tsx
  • src/components/v2/dashboard/plans/GiftFilterTabs.tsx
  • src/components/v2/dashboard/plans/GiftListCard.tsx
  • src/components/v2/dashboard/plans/GiftPlanActions.tsx
  • src/components/v2/dashboard/plans/GiftRow.tsx
  • src/components/v2/dashboard/plans/KycRequiredNotice.tsx
  • src/components/v2/dashboard/plans/ManageGiftsSection.tsx
  • src/components/v2/dashboard/plans/PlanHeroCard.tsx
  • src/components/v2/dashboard/plans/mock-data.ts
  • src/components/v2/dashboard/tasks/ManageTasksSection.tsx
  • src/components/v2/dashboard/tasks/ReferralProgressCard.tsx
  • src/components/v2/dashboard/tasks/TaskFilterTabs.tsx
  • src/components/v2/dashboard/tasks/TaskHeroCard.tsx
  • src/components/v2/dashboard/tasks/TaskListCard.tsx
  • src/components/v2/dashboard/tasks/TaskRow.tsx
  • src/components/v2/dashboard/tasks/mock-data.ts
  • src/components/v2/dashboard/wallet/DashboardTopBar.tsx
  • src/components/v2/dashboard/wallet/PerformanceGrid.tsx
  • src/components/v2/dashboard/wallet/PlanSummaryCard.tsx
  • src/components/v2/dashboard/wallet/PortfolioChart.tsx
  • src/components/v2/dashboard/wallet/TotalValueCard.tsx
  • src/hooks/query/useCancelSubscription.ts
  • src/hooks/query/useKyc.ts
  • src/hooks/query/usePackages.ts
  • src/hooks/query/useSubscriptionCurrent.ts
  • src/hooks/query/useTransactionHistory.ts
  • src/hooks/useRegisterV2User.ts
  • src/hooks/useTelegramPlatform.ts
  • src/hooks/useTgBackButton.ts
  • src/hooks/useUser.ts
  • src/lib/activity.ts
  • src/lib/formatters.ts
  • src/lib/hooks.ts
  • src/lib/news.ts
  • src/lib/query-keys.ts
  • src/lib/types.ts
  • src/styles/theme.css
  • tsconfig.json
🚧 Files skipped from review as they are similar to previous changes (14)
  • src/lib/query-keys.ts
  • src/components/ui/page-title.tsx
  • src/components/ui/toggle-plan.tsx
  • src/app/subscription/page.tsx
  • src/components/ui/visible-toggle.tsx
  • src/hooks/useUser.ts
  • src/hooks/query/useSubscriptionCurrent.ts
  • src/components/ui/search-field.tsx
  • src/app/context/tma.tsx
  • src/hooks/query/useTransactionHistory.ts
  • package.json
  • src/components/v2/bottomNavigation.tsx
  • src/app/page.tsx
  • src/lib/formatters.ts

Comment on lines 12 to +18
const response = await fetch(`${process.env.API_BASE_URL}/user/kyc/initiate`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: authHeader,
},
body: JSON.stringify(body),

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

Route external requests through @/lib/fetchy.

Both new proxy calls bypass the project’s required HTTP wrapper. Extend fetchy if necessary to preserve the upstream status/body behavior these handlers need, then use it consistently.

  • src/app/api/user/kyc/initiate/route.ts#L12-L18: replace direct KYC initiation fetch with the custom wrapper.
  • src/app/api/user/profile/route.ts#L12-L19: replace direct profile-update fetch with the custom wrapper.

As per coding guidelines, “All HTTP requests must use the custom fetchy wrapper from @/lib/fetchy”.

📍 Affects 2 files
  • src/app/api/user/kyc/initiate/route.ts#L12-L18 (this comment)
  • src/app/api/user/profile/route.ts#L12-L19
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/api/user/kyc/initiate/route.ts` around lines 12 - 18, Replace the
direct fetch calls in src/app/api/user/kyc/initiate/route.ts lines 12-18 and
src/app/api/user/profile/route.ts lines 12-19 with the custom fetchy wrapper
imported from `@/lib/fetchy`. Extend fetchy if needed so both route handlers
preserve the upstream response status and body behavior.

Source: Coding guidelines

Comment thread src/app/onboard/page.tsx
Comment on lines +125 to +131
style={
{
borderRadius: "12px",
overflow: "hidden",
"--tgui--outline": "#fa7119",
"--tgui--plain_foreground": "#fa7119",
} as React.CSSProperties

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether the repository enables UMD globals and inspect the React type usage.
fd -a -g 'tsconfig*.json' -x rg -n '"allowUmdGlobalAccess"\s*:\s*true' {}
rg -n 'React\.CSSProperties|CSSProperties|from "react"' src/app/onboard/page.tsx

Repository: CeyLabs/BitcoinDeepaBot-TMA

Length of output: 265


🏁 Script executed:

#!/bin/bash
# Look at TypeScript/Next config declarations and relevant file usage/imports.
set -euo pipefail

echo "== candidate tsconfig files =="
fd -a -g 'tsconfig*.json' . | sed 's#^\./##'

echo
echo "== react imports and CSSProperties usages in src/app/onboard/page.tsx =="
nl -ba src/app/onboard/page.tsx | sed -n '1,25p;120,136p'

echo
echo "== package config TypeScript/React dependencies =="
cat package.json | node -e '
const fs=require("fs");
const p=JSON.parse(fs.readFileSync("package.json"))
for (const section of ["scripts","dependencies","devDependencies","peerDependencies"]) {
  console.log(section)
  if (p[section]) {
    for (const [k,v] of Object.entries(p[section])) { if (k.includes("typescript") || k.includes("react")) console.log(`${k}: ${v}`) }
  }
}'

echo
echo "== all tsconfig allowUmdGlobalAccess =="
fd -g 'tsconfig*.json' . -x sh -c 'echo "--- $1"; rg -n "allowUmdGlobalAccess" "$1" || true' sh {}

Repository: CeyLabs/BitcoinDeepaBot-TMA

Length of output: 344


🏁 Script executed:

#!/bin/bash
# Inspect config and usage text with shell-built line numbering.
set -euo pipefail

echo "== tsconfig files =="
fd -g 'tsconfig*.json' .

echo
echo "== allowUmdGlobalAccess in tsconfig files =="
while IFS= read -r f; do
  echo "--- $f"
  grep -n 'allowUmdGlobalAccess' "$f" || true
done < <(fd -g 'tsconfig*.json' .)

echo
echo "== relevant src/app/onboard/page.tsx imports and lines =="
awk '
NR==1,R==25 {printf "%6d  %s\n", NR, $0}
NR>=120 && NR<=136 {printf "%6d  %s\n", NR, $0}
END {}' src/app/onboard/page.tsx

echo
echo "== package relevant deps/scripts =="
node - <<'JS'
const fs = require('fs');
const p = JSON.parse(fs.readFileSync('package.json', 'utf8'));
const lines = ['scripts','dependencies','devDependencies','peerDependencies'];
for (const section of lines) {
  console.log(section);
  const vals = p[section] || {};
  for (const [k,v] of Object.entries(vals)) {
    if (k.includes('typescript') || k.includes('react') || k.includes('next')) console.log(`${k}: ${v}`);
  }
}
JS

Repository: CeyLabs/BitcoinDeepaBot-TMA

Length of output: 301


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== tsconfig files =="
fd -g 'tsconfig*.json' .

echo
echo "== allowUmdGlobalAccess =="
find . -name 'tsconfig*.json' -print0 | while IFS= read -r -d '' f; do
  printf '%s\n' "=== $f ==="
  grep -n 'allowUmdGlobalAccess' "$f" || true
done

echo
echo "== relevant src/app/onboard/page.tsx import and style usage =="
awk '
NR<26 {printf "%6d  %s\n", NR, $0}
NR>=120 && NR<=136 {printf "%6d  %s\n", NR, $0}
' src/app/onboard/page.tsx

echo
echo "== package relevant deps/scripts =="
node - <<'JS'
const fs = require('fs');
const p = JSON.parse(fs.readFileSync('package.json', 'utf8'));
for (const section of ['scripts','dependencies','devDependencies','peerDependencies']) {
  console.log(section);
  if (p[section]) {
    for (const [k,v] of Object.entries(p[section])) {
      if (/typescript|react|next/i.test(k)) console.log(`${k}: ${v}`);
    }
  }
}
JS

Repository: CeyLabs/BitcoinDeepaBot-TMA

Length of output: 2640


Import CSSProperties explicitly.

src/app/onboard/page.tsx uses React.CSSProperties without importing React, and the repo does not enable allowUmdGlobalAccess. Import type CSSProperties from React and cast the object that way.

Proposed fix
-import { useEffect, useState } from "react";
+import { useEffect, useState, type CSSProperties } from "react";

-                    } as React.CSSProperties
+                    } as CSSProperties
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
style={
{
borderRadius: "12px",
overflow: "hidden",
"--tgui--outline": "#fa7119",
"--tgui--plain_foreground": "#fa7119",
} as React.CSSProperties
style={
{
borderRadius: "12px",
overflow: "hidden",
"--tgui--outline": "`#fa7119`",
"--tgui--plain_foreground": "`#fa7119`",
} as CSSProperties
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/onboard/page.tsx` around lines 125 - 131, Update the style cast in
the onboarding page to use an explicitly imported type CSSProperties from React
instead of React.CSSProperties, adding the type-only import and applying
CSSProperties to the object cast.

Source: MCP tools

Comment on lines +81 to +108
const handleSubscribe = async () => {
const plan = packages.find((p) => p.id === selectedPlanId);
if (!plan || !authToken) return;

try {
setPayhereLinkLoading(true);
const res = await fetch("/api/subscription/payhere-link", {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${authToken}` },
body: JSON.stringify({ package_id: plan.id }),
});
const result = await res.json();

if (!res.ok) {
if (res.status === 403 && result.redirectTo) {
router.push(result.redirectTo);
return;
}
throw new Error(result.message || "Failed to generate payment link");
}

if (result.link) redirectToPayHereViaPage(result.link);
} catch (err) {
console.error(err);
} finally {
setPayhereLinkLoading(false);
}
};

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 | 🟠 Major | ⚡ Quick win

Use fetchy instead of raw fetch for the PayHere link request.

This directly calls fetch() (line 87) instead of the shared fetchy wrapper used elsewhere in this PR (e.g., useCancelSubscription.ts). This bypasses whatever centralized auth/retry/timeout/error-normalization behavior fetchy provides.

🛠️ Proposed fix
-      const res = await fetch("/api/subscription/payhere-link", {
-        method: "POST",
-        headers: { "Content-Type": "application/json", Authorization: `Bearer ${authToken}` },
-        body: JSON.stringify({ package_id: plan.id }),
-      });
-      const result = await res.json();
-
-      if (!res.ok) {
-        if (res.status === 403 && result.redirectTo) {
-          router.push(result.redirectTo);
-          return;
-        }
-        throw new Error(result.message || "Failed to generate payment link");
-      }
-
-      if (result.link) redirectToPayHereViaPage(result.link);
+      const result = await fetchy.post<{ link?: string; redirectTo?: string; message?: string }, { package_id: string }>(
+        "/api/subscription/payhere-link",
+        { package_id: plan.id },
+        { headers: { Authorization: `Bearer ${authToken}` } }
+      );
+
+      if (result.link) redirectToPayHereViaPage(result.link);
As per path instructions, `**/*.{ts,tsx}`: "All HTTP requests must use the custom fetchy wrapper from `@/lib/fetchy`".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/v2/plans/choose/page.tsx` around lines 81 - 108, Update
handleSubscribe to use the shared fetchy wrapper from `@/lib/fetchy` instead of
raw fetch for the PayHere link request, preserving the existing POST payload,
authorization, redirect handling, loading state, and error flow.

Source: Path instructions

Comment on lines +103 to +108
} catch (err) {
console.error(err);
} finally {
setPayhereLinkLoading(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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Subscribe failures are silently swallowed — no user feedback.

Unlike handleCancel's catch block (which sets an error snackbar), a failed handleSubscribe only logs to console. The button re-enables but the user gets no indication anything went wrong, especially confusing after a loading spinner.

🛡️ Proposed fix
     } catch (err) {
       console.error(err);
+      setSnackbar({
+        tone: "error",
+        title: "Payment Link Failed",
+        description: err instanceof Error ? err.message : "Failed to generate payment link",
+      });
     } finally {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (err) {
console.error(err);
} finally {
setPayhereLinkLoading(false);
}
};
} catch (err) {
console.error(err);
setSnackbar({
tone: "error",
title: "Payment Link Failed",
description: err instanceof Error ? err.message : "Failed to generate payment link",
});
} finally {
setPayhereLinkLoading(false);
}
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/v2/plans/choose/page.tsx` around lines 103 - 108, Update the
handleSubscribe catch block to set the same error snackbar state used by
handleCancel, while retaining console.error for diagnostics and resetting
loading via the existing finally block. Ensure failed subscriptions provide
visible user feedback when the button becomes available again.

Comment on lines +153 to +155
<p className="text-center text-xs text-muted">
Your Data is Encrypted and never Shared
</p>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Remove the inaccurate “never shared” privacy promise.

This flow submits identity and contact data to profile and KYC endpoints, so the UI must not claim it is never shared. Replace it with privacy-policy-approved wording that accurately describes handling and any third-party processing.

  • src/app/v2/verification/details/page.tsx#L153-L155: replace the unconditional “Encrypted and never Shared” claim.
  • src/app/v2/verification/page.tsx#L208-L211: replace the equivalent “Secured and never Shared” claim.
📍 Affects 2 files
  • src/app/v2/verification/details/page.tsx#L153-L155 (this comment)
  • src/app/v2/verification/page.tsx#L208-L211
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/v2/verification/details/page.tsx` around lines 153 - 155, Replace the
inaccurate “never Shared” privacy wording in both verification UI locations:
update the text in src/app/v2/verification/details/page.tsx (lines 153-155) and
src/app/v2/verification/page.tsx (lines 208-211) with privacy-policy-approved
language that accurately describes data handling and any third-party processing.

Comment thread src/lib/activity.ts
Comment on lines +53 to +58
export function mapDcaTransactionToActivityItem(tx: DcaTransaction): ActivityItem {
return {
id: tx.id,
type: "membership_reward",
detailLabel: tx.package_name ? `${tx.package_name} Plan` : "Destributed",
timestamp: tx.created_at,

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

Fix the fallback plan label typo.

The UI renders Destributed when package_name is absent.

Proposed fix
-        detailLabel: tx.package_name ? `${tx.package_name} Plan` : "Destributed",
+        detailLabel: tx.package_name ? `${tx.package_name} Plan` : "Distributed",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function mapDcaTransactionToActivityItem(tx: DcaTransaction): ActivityItem {
return {
id: tx.id,
type: "membership_reward",
detailLabel: tx.package_name ? `${tx.package_name} Plan` : "Destributed",
timestamp: tx.created_at,
export function mapDcaTransactionToActivityItem(tx: DcaTransaction): ActivityItem {
return {
id: tx.id,
type: "membership_reward",
detailLabel: tx.package_name ? `${tx.package_name} Plan` : "Distributed",
timestamp: tx.created_at,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/activity.ts` around lines 53 - 58, Update the fallback detailLabel in
mapDcaTransactionToActivityItem from the misspelled “Destributed” text to the
correct “Distributed” label, preserving the existing package_name-based label
behavior.

Comment thread src/lib/hooks.ts
Comment on lines +50 to +60
// For t.me links (bot deep links, channel invites, share intents) — keeps
// navigation inside the Telegram client instead of bouncing to a browser.
const openTelegramLink = useCallback(
(url: string) => {
if (webApp) {
webApp.openTelegramLink(url);
} else {
throw new Error("TMA not available");
}
},
[webApp]

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

Handle unavailable Telegram APIs instead of throwing from task CTAs. webApp is only assigned after mount, and browser users never receive it; setup/community clicks and story sharing can therefore throw uncaught errors.

  • src/lib/hooks.ts#L50-L60: make Telegram-link navigation fall back to normal browser navigation and expose TMA readiness.
  • src/components/v2/dashboard/tasks/ManageTasksSection.tsx#L27-L42: disable the story action until shareToStory is available, or show a user-facing unavailable message.
📍 Affects 2 files
  • src/lib/hooks.ts#L50-L60 (this comment)
  • src/components/v2/dashboard/tasks/ManageTasksSection.tsx#L27-L42
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/hooks.ts` around lines 50 - 60, Update openTelegramLink in
src/lib/hooks.ts#L50-L60 to use normal browser navigation when webApp is
unavailable instead of throwing, and expose the Telegram Mini App readiness
state for callers. In
src/components/v2/dashboard/tasks/ManageTasksSection.tsx#L27-L42, use that
readiness or shareToStory availability to disable the story action until
supported, or display a user-facing unavailable message.

Comment thread src/lib/news.ts
Comment on lines +108 to +125
export async function getNewsArticles(): Promise<NewsArticle[]> {
const response = await fetch(FEED_URL, { next: { revalidate: 900 } });
if (!response.ok) return [];

const xml = await response.text();
const feed = await parser.parseString(xml);

const items = (feed.items ?? [])
.slice()
.sort((a, b) => {
const aTime = new Date(a.isoDate ?? a.pubDate ?? 0).getTime();
const bTime = new Date(b.isoDate ?? b.pubDate ?? 0).getTime();
return bTime - aTime;
});

return items
.map((item, index) => toArticle(item, index))
.filter((article): article is NewsArticle => article !== null);

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 | 🟠 Major | ⚡ Quick win

Handle feed and parser failures before returning to the page.

fetch, response.text(), and parser.parseString() can throw. That bypasses the page’s empty-state branch and returns a 500 instead of “Unable to load news right now.”

Proposed fix
 export async function getNewsArticles(): Promise<NewsArticle[]> {
-    const response = await fetch(FEED_URL, { next: { revalidate: 900 } });
-    if (!response.ok) return [];
+    try {
+        const response = await fetch(FEED_URL, { next: { revalidate: 900 } });
+        if (!response.ok) return [];
 
-    const xml = await response.text();
-    const feed = await parser.parseString(xml);
+        const xml = await response.text();
+        const feed = await parser.parseString(xml);
 
-    const items = (feed.items ?? [])
+        const items = (feed.items ?? [])
         .slice()
         .sort((a, b) => {
             const aTime = new Date(a.isoDate ?? a.pubDate ?? 0).getTime();
             const bTime = new Date(b.isoDate ?? b.pubDate ?? 0).getTime();
             return bTime - aTime;
         });
 
-    return items
-        .map((item, index) => toArticle(item, index))
-        .filter((article): article is NewsArticle => article !== null);
+        return items
+            .map((item, index) => toArticle(item, index))
+            .filter((article): article is NewsArticle => article !== null);
+    } catch {
+        return [];
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function getNewsArticles(): Promise<NewsArticle[]> {
const response = await fetch(FEED_URL, { next: { revalidate: 900 } });
if (!response.ok) return [];
const xml = await response.text();
const feed = await parser.parseString(xml);
const items = (feed.items ?? [])
.slice()
.sort((a, b) => {
const aTime = new Date(a.isoDate ?? a.pubDate ?? 0).getTime();
const bTime = new Date(b.isoDate ?? b.pubDate ?? 0).getTime();
return bTime - aTime;
});
return items
.map((item, index) => toArticle(item, index))
.filter((article): article is NewsArticle => article !== null);
export async function getNewsArticles(): Promise<NewsArticle[]> {
try {
const response = await fetch(FEED_URL, { next: { revalidate: 900 } });
if (!response.ok) return [];
const xml = await response.text();
const feed = await parser.parseString(xml);
const items = (feed.items ?? [])
.slice()
.sort((a, b) => {
const aTime = new Date(a.isoDate ?? a.pubDate ?? 0).getTime();
const bTime = new Date(b.isoDate ?? b.pubDate ?? 0).getTime();
return bTime - aTime;
});
return items
.map((item, index) => toArticle(item, index))
.filter((article): article is NewsArticle => article !== null);
} catch {
return [];
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/news.ts` around lines 108 - 125, Update getNewsArticles to catch
failures from fetch, response.text(), and parser.parseString, returning an empty
NewsArticle array so the page reaches its existing “Unable to load news right
now” state; preserve the current successful parsing, sorting, mapping, and
filtering behavior.

Comment thread src/lib/news.ts
}

export async function getNewsArticles(): Promise<NewsArticle[]> {
const response = await fetch(FEED_URL, { next: { revalidate: 900 } });

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

Use the project HTTP wrapper for the RSS request.

This direct fetch bypasses @/lib/fetchy. Ensure the wrapper supports the required server revalidation options, then route this request through it. As per coding guidelines, “All HTTP requests must use the custom fetchy wrapper from @/lib/fetchy”.

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

In `@src/lib/news.ts` at line 109, Update the RSS request in the news-fetching
flow around FEED_URL to use the project’s fetchy wrapper from `@/lib/fetchy`
instead of direct fetch. Extend the wrapper’s options handling as needed to
preserve the existing server revalidation setting of 900 seconds, then pass that
option through the wrapper.

Source: Coding guidelines

Comment thread src/styles/theme.css
Comment on lines +46 to +48
--color-surface-glass: #FFFFFF99

--color-surface-tertiary: #E2E8F0

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

Terminate both theme-token declarations.

Without the semicolon on Line 46, CSS consumes Line 48 into --color-surface-glass; --color-surface-tertiary is never defined and glass-color consumers receive an invalid value.

Proposed fix
-  --color-surface-glass: `#FFFFFF99`
+  --color-surface-glass: `#FFFFFF99`;
 
-  --color-surface-tertiary: `#E2E8F0`
+  --color-surface-tertiary: `#E2E8F0`;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
--color-surface-glass: #FFFFFF99
--color-surface-tertiary: #E2E8F0
--color-surface-glass: `#FFFFFF99`;
--color-surface-tertiary: `#E2E8F0`;
🧰 Tools
🪛 Stylelint (17.14.0)

[error] 48-48: Missing var function for "--color-surface-tertiary" (custom-property-no-missing-var-function)

(custom-property-no-missing-var-function)

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

In `@src/styles/theme.css` around lines 46 - 48, Terminate both declarations in
the theme token definitions by adding semicolons to --color-surface-glass and
--color-surface-tertiary, ensuring each custom property is parsed independently.

Source: Linters/SAST tools

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