Skip to content

fix: eager-load header logos #45#46

Open
cmnisal wants to merge 3 commits intodevfrom
cmnisal/wrap-image-components-in-containers
Open

fix: eager-load header logos #45#46
cmnisal wants to merge 3 commits intodevfrom
cmnisal/wrap-image-components-in-containers

Conversation

@cmnisal
Copy link
Contributor

@cmnisal cmnisal commented Aug 16, 2025

PR Type

Enhancement, Bug fix, Documentation


Description

Major KYC Integration: Added comprehensive KYC verification system with status checking, initiation endpoints, and verification page with mobile device detection
TanStack Query Migration: Migrated data fetching across dashboard, history, and subscription pages from manual state management to TanStack Query with centralized query keys
React Hook Form Integration: Refactored subscription form to use React Hook Form with Zod validation for improved form handling
DCA Chart Component: Added new Bitcoin price chart visualization with Chart.js integration and transaction data display
Image Container Fixes: Wrapped Image components in proper containers with responsive sizing and removed hardcoded background colors
Loading State Improvements: Added skeleton components for balance cards, list items, and charts with consistent animated placeholders
Payment Flow Enhancement: Added KYC verification requirement for PayHere payments with proper error handling
UI/UX Improvements: Updated currency display to Sinhala rupee symbol, simplified navigation, and enhanced loading states


Diagram Walkthrough

flowchart LR
  A["User Registration"] --> B["KYC Verification Check"]
  B --> C["KYC Status API"]
  C --> D["Verification Page"]
  D --> E["PayHere Payment"]
  F["Dashboard"] --> G["TanStack Query"]
  G --> H["Wallet Summary"]
  G --> I["Transaction History"]
  G --> J["DCA Chart"]
  K["Form Handling"] --> L["React Hook Form"]
  L --> M["Zod Validation"]
Loading

File Walkthrough

Relevant files
Enhancement
17 files
route.ts
Add KYC verification requirement for PayHere payments       

src/app/api/subscription/payhere-link/route.ts

• Added KYC status verification before generating PayHere payment
links
• Implemented comprehensive error handling for KYC check
failures
• Returns 403 status with redirect to verification page if
KYC not approved

+48/-0   
route.ts
Add KYC status checking API endpoint                                         

src/app/api/user/kyc/status/route.ts

• Created new API endpoint to check user KYC verification status

Forwards requests to external API with proper authorization headers

Includes comprehensive error handling and response formatting

+42/-0   
route.ts
Add KYC initiation API endpoint                                                   

src/app/api/user/kyc/initiate/route.ts

• Created new API endpoint to initiate KYC verification process

Forwards POST requests to external KYC service with authentication

Handles errors and provides structured response format

+38/-0   
types.ts
Add KYC status types to User interface                                     

src/lib/types.ts

• Added kycStatus field to User interface with comprehensive status
enum
• Includes all possible KYC states: NOT_STARTED, IN_PROGRESS,
APPROVED, DECLINED, etc.

+9/-0     
page.tsx
Refactor transaction history with TanStack Query and modal details

src/app/dashboard/history/page.tsx

• Migrated from manual state management to TanStack Query for data
fetching
• Replaced pagination with simple transaction list display

Added detailed transaction modal with comprehensive transaction
information
• Improved loading states with skeleton components

+334/-249
page.tsx
Migrate dashboard to TanStack Query with KYC integration 

src/app/dashboard/page.tsx

• Migrated wallet summary fetching to TanStack Query
• Added KYC
status checking with automatic redirect to verification
• Replaced
manual loading states with skeleton components
• Improved error
handling and user feedback

+273/-252
page.tsx
Refactor subscription form with React Hook Form and KYC flow

src/app/subscription/page.tsx

• Migrated form handling from manual state to React Hook Form with Zod
validation
• Added KYC verification redirect handling in registration
flow
• Improved form validation and error display
• Enhanced loading
states and user experience

+149/-272
page.tsx
Add complete KYC verification page with status management

src/app/verification/page.tsx

• Created comprehensive KYC verification page with multiple status
handling
• Implemented mobile device detection and desktop warnings

Added status checking, verification initiation, and continuation flows

• Includes detailed UI for different verification states

+626/-0 
page.tsx
Migrate subscription management to TanStack Query               

src/app/dashboard/subscription/page.tsx

• Migrated package fetching to TanStack Query
• Improved subscription
management with better state handling
• Enhanced error handling and
loading states

+52/-55 
DCAChart.tsx
Add DCA Bitcoin price chart component                                       

src/components/DCAChart.tsx

• Created new DCA chart component for visualizing Bitcoin price
history
• Implements Chart.js with transaction data and average price
line
• Includes comprehensive tooltip and formatting features

+234/-0 
bottomNavigation.tsx
Simplify bottom navigation with Next.js Links                       

src/components/bottomNavigation.tsx

• Replaced custom page transition with standard Next.js Link
components
• Simplified navigation implementation

+4/-7     
BalanceCardSkeleton.tsx
Add balance card skeleton loading component                           

src/components/skeletons/BalanceCardSkeleton.tsx

• Created skeleton loading component for balance card
• Provides
animated placeholder during data loading

+19/-0   
page.tsx
Wrap main logo in container with responsive sizing             

src/app/page.tsx

• Wrapped the main logo Image component in a container div with fixed
dimensions
• Added fill and priority props to the Image component for
better loading performance
• Removed explicit width and height props
from Image in favor of container sizing

+3/-1     
providers.tsx
Replace page transitions with React Query provider             

src/app/context/providers.tsx

• Added React Query support by importing QueryClient and
QueryClientProvider
• Replaced PageTransitionProvider with
QueryClientProvider in the provider tree
• Added state management for
queryClient instance using useState

+4/-2     
ListItemSkeleton.tsx
Add new list item skeleton loading component                         

src/components/skeletons/ListItemSkeleton.tsx

• Created new skeleton component for list items with animated loading
placeholders
• Includes header section with title and value
placeholders
• Uses gray color scheme with pulse animation for loading
states

+15/-0   
ChartSkeleton.tsx
Add new chart skeleton loading component                                 

src/components/skeletons/ChartSkeleton.tsx

• Created new skeleton component for chart loading states
• Features
animated placeholder with title and chart area sections
• Uses
consistent gray color scheme with pulse animation

+12/-0   
loading.tsx
Add loading state for dashboard route                                       

src/app/dashboard/loading.tsx

• Added new loading page component for dashboard route
• Imports and
renders the existing LoadingPage component

+5/-0     
Configuration changes
1 files
query-keys.ts
Add centralized TanStack Query keys configuration               

src/lib/query-keys.ts

• Created centralized query keys configuration for TanStack Query

Defined keys for wallet-summary, transactions, and packages queries

+5/-0     
Documentation
1 files
CLAUDE.md
Add Claude AI development documentation                                   

CLAUDE.md

• Added comprehensive development documentation for Claude AI

Includes project architecture, tech stack, and development guidelines

• Documents core domain models and development patterns

+98/-0   
Formatting
4 files
page.tsx
Update currency display and styling                                           

src/app/payment/success/page.tsx

• Updated currency display from "LKR" to "රු." (Sinhala rupee symbol)

• Removed hardcoded background color styling

+3/-3     
page.tsx
Remove hardcoded background styling                                           

src/app/payment/cancel/page.tsx

• Removed hardcoded background color styling

+1/-1     
LoadingPage.tsx
Remove hardcoded background from loading overlay                 

src/components/LoadingPage.tsx

• Removed hardcoded background color bg-[#202020] from fullscreen
loading overlay
• Simplified styling by relying on inherited
background colors

+1/-1     
layout.tsx
Remove hardcoded background from root layout                         

src/app/layout.tsx

• Removed hardcoded background color bg-[#202020] from body element

Simplified body styling to rely on default or inherited background

+1/-1     
Dependencies
1 files
package.json
Add TanStack Query and React Hook Form dependencies           

package.json

• Added TanStack Query and React Hook Form dependencies
• Added
Hookform resolvers for Zod integration

+3/-0     
Bug fix
2 files
DCAChart.tsx
Add null safety to DCA chart data handling                             

src/components/ui/DCAChart.tsx

• Added null checks for transaction data fields
• Improved error
handling for missing price and satoshi data

+2/-1     
page.tsx
Fix image container and remove hardcoded styling                 

src/app/onboard/page.tsx

• Wrapped Image component in container div with proper sizing

Removed hardcoded background color styling

+4/-7     
Additional files
2 files
transition.tsx +0/-48   
theme.css +0/-6     

@railway-app
Copy link

railway-app bot commented Aug 16, 2025

This PR was not deployed automatically as @cmnisal does not have access to the Railway project.

In order to get automatic PR deploys, please add @cmnisal to your team on Railway.

@coderabbitai
Copy link

coderabbitai bot commented Aug 16, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch cmnisal/wrap-image-components-in-containers

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

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

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@qodo-code-review
Copy link
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 No relevant tests
🔒 Security concerns

Authorization header handling:
New KYC API routes proxy the Authorization header directly, which is good, but be consistent with header casing. One route reads "Authorization" and another reads "authorization"; while Node normalizes headers, standardizing reduces risk of missing tokens in some environments. Also, the verification page redirects to external URLs returned from the API (result.url) without validation. Ensure backend only returns trusted domains and consider validating URL origin client-side before window.location.href to avoid open redirect risks.

⚡ Recommended focus areas for review

Logic Edge Case

KYC check treats any non-APPROVED status and any fetch failure as 403 with redirect, which may block payments even for temporary API hiccups; consider distinguishing transient errors (5xx/network) from genuine KYC-required states and surfacing a retriable 502/503 instead.

// Check KYC status before generating PayHere link
try {
    const kycResponse = await fetch(`${process.env.API_BASE_URL}/user/kyc/status`, {
        method: "GET",
        headers: {
            "Content-Type": "application/json",
            Authorization: `Bearer ${token}`,
        },
    });

    if (kycResponse.ok) {
        const kycData = await kycResponse.json();

        if (kycData.status !== "APPROVED") {
            return NextResponse.json(
                {
                    error: "KYC Verification Required",
                    message:
                        "Please complete KYC verification before proceeding with payment",
                    kycStatus: kycData.status,
                    redirectTo: "/verification",
                },
                { status: 403 }
            );
        }
    } else {
        // If KYC status check fails, assume KYC is required
        return NextResponse.json(
            {
                error: "KYC Verification Required",
                message: "Please complete KYC verification before proceeding with payment",
                redirectTo: "/verification",
            },
            { status: 403 }
        );
    }
} catch (kycError) {
    console.error("Error checking KYC status:", kycError);
    return NextResponse.json(
        {
            error: "KYC Verification Required",
            message: "Please complete KYC verification before proceeding with payment",
            redirectTo: "/verification",
        },
        { status: 403 }
    );
}
Navigation Consistency

Post-approval redirects use two different targets (/dashboard/subscription vs /subscription) depending on code path; align to a single route to avoid confusion and broken flows.

            if (response.ok) {
                setVerificationStatus(result.status);

                // Store verification URL if available (for IN_PROGRESS status)
                if (result.url) {
                    setVerificationUrl(result.url);
                }

                if (result.status === "APPROVED") {
                    // Redirect to subscription page after successful verification
                    setTimeout(() => {
                        router.push("/dashboard/subscription");
                    }, 2000);
                }
            }
        } catch (error) {
            console.error("Status check error:", error);
        }
    };

    if (authToken) {
        checkStatusOnLoad();
    }
}, [authToken, router]);
Query Key Scope

React Query key for wallet summary is static and not scoped by auth token or user; if multiple users share a device/session the cache could leak or show stale cross-user data. Include a user identifier or token hash in the queryKey.

const {
    data: summary,
    isLoading,
    error: summaryError,
    refetch: refetchSummary,
} = 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,
});

Co-authored-by: qodo-merge-pro[bot] <151058649+qodo-merge-pro[bot]@users.noreply.github.com>
@vercel
Copy link

vercel bot commented Aug 16, 2025

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

Project Deployment Preview Comments Updated (UTC)
bitcoin-deepa-tma-bot Ready Ready Preview Comment Aug 16, 2025 9:43pm

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Aug 16, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Differentiate KYC vs server errors

Avoid treating any non-OK response as a KYC failure. For transient server errors
or network issues, return a 502/500 instead of 403 to prevent redirect loops and
incorrect denial. Only block with 403 when KYC is definitively not approved.

src/app/api/subscription/payhere-link/route.ts [38-71]

 const kycResponse = await fetch(`${process.env.API_BASE_URL}/user/kyc/status`, {
     method: "GET",
     headers: {
         "Content-Type": "application/json",
         Authorization: `Bearer ${token}`,
     },
 });
 
 if (kycResponse.ok) {
     const kycData = await kycResponse.json();
 
     if (kycData.status !== "APPROVED") {
         return NextResponse.json(
             {
                 error: "KYC Verification Required",
                 message:
                     "Please complete KYC verification before proceeding with payment",
                 kycStatus: kycData.status,
                 redirectTo: "/verification",
             },
             { status: 403 }
         );
     }
-} else {
-    // If KYC status check fails, assume KYC is required
+} else if (kycResponse.status === 401 || kycResponse.status === 403) {
+    // Auth-related failure -> require KYC/auth
     return NextResponse.json(
         {
             error: "KYC Verification Required",
             message: "Please complete KYC verification before proceeding with payment",
             redirectTo: "/verification",
         },
         { status: 403 }
     );
+} else {
+    // Upstream error or transient issue -> surface as server error
+    const errBody = await kycResponse.text().catch(() => "");
+    return NextResponse.json(
+        {
+            error: "KYC status check failed",
+            message: "Unable to verify KYC status. Please try again later.",
+            details: errBody?.slice(0, 200),
+        },
+        { status: 502 }
+    );
 }
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that treating all upstream API errors as a 403 KYC failure is incorrect, improving robustness by distinguishing between client and server errors.

Medium
Fix stuck loading without token

When authToken is missing, loading stays true causing a permanent loading UI.
Ensure you clear the loading state on early return to prevent a stuck spinner.

src/components/DCAChart.tsx [35-66]

 const fetchTransactions = async () => {
     setLoading(true);
 
-    if (!authToken) return;
+    if (!authToken) {
+        setLoading(false);
+        return;
+    }
 
     try {
-        setLoading(true);
         const res = await fetch("/api/transaction/list", {
             method: "GET",
             headers: {
                 "Content-Type": "application/json",
                 Authorization: `Bearer ${authToken}`,
             },
         });
 
         if (!res.ok) throw new Error("Failed to fetch transactions");
         const data = await res.json();
         const txList = Array.isArray(data) ? data : data.transactions.transactions || [];
 
-        // Filter only successful transactions
         const successfulTx = txList.filter((tx: Transaction) => tx.status === "SUCCESS");
-        setTransactions(successfulTx.slice(0, 10)); // Show last 10 successful transactions
+        setTransactions(successfulTx.slice(0, 10));
     } catch (error) {
         console.error("Error fetching transactions:", error);
     } finally {
         setLoading(false);
     }
 };

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies a bug where the UI would get stuck in a loading state if authToken is missing, and the proposed fix correctly resolves the issue.

Medium
Avoid skewing by skipping invalid prices

Returning 0 for missing prices can silently skew averages and charts. Instead,
skip entries without a valid numeric price to keep datasets aligned with labels
or explicitly mark them as null if the chart supports gaps. Ensure corresponding
labels/data arrays remain in sync after filtering.

src/components/ui/DCAChart.tsx [107-111]

-const bitcoinPrices = sortedTx.map((tx) => {
-    if (!tx.btc_price_at_purchase) return 0;
-    const priceStr = tx.btc_price_at_purchase.replace(/,/g, "");
-    return Number(priceStr);
-});
+const pricePoints = sortedTx
+    .map((tx) => {
+        const raw = tx?.btc_price_at_purchase;
+        if (!raw) return null;
+        const n = Number(String(raw).replace(/,/g, ""));
+        return Number.isFinite(n) ? n : null;
+    })
+    .filter((v): v is number => v !== null);
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a significant issue where returning 0 for a missing price will skew chart data, and proposes a more robust way to handle invalid data; this is a high-impact functional bug fix.

Medium
Guard against NaN tooltip values

If satoshis_purchased is non-numeric after cleanup, Number yields NaN and the
label shows "NaN". Add a finite-number guard and return a safe fallback to
prevent broken tooltips. This avoids crashing formatters and misleading text.

src/components/ui/DCAChart.tsx [178-181]

 if (tx && tx.satoshis_purchased) {
     const satsStr = tx.satoshis_purchased.replace(/,/g, "");
     const sats = Number(satsStr);
-    return `Purchased: ${formatLargeNumber(sats)} sats`;
+    if (Number.isFinite(sats)) {
+        return `Purchased: ${formatLargeNumber(sats)} sats`;
+    }
+    return "Purchased: —";
+}

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that converting a non-numeric string to a number can result in NaN, leading to broken tooltips, and provides a good defensive check to improve robustness.

Medium
General
Unify approval redirect path

The redirect path differs between initial status check and later checks, which
can cause inconsistent navigation. Use a single, consistent post-approval route
to avoid user confusion and routing bugs.

src/app/verification/page.tsx [88-102]

 if (response.ok) {
     setVerificationStatus(result.status);
 
-    // Store verification URL if available (for IN_PROGRESS status)
     if (result.url) {
         setVerificationUrl(result.url);
     }
 
     if (result.status === "APPROVED") {
-        // Redirect to subscription page after successful verification
+        const redirectPath = "/subscription"; // single canonical route
         setTimeout(() => {
-            router.push("/dashboard/subscription");
+            router.push(redirectPath);
         }, 2000);
     }
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies inconsistent redirect paths for KYC approval, which could cause confusion, and proposes a valid fix to unify them.

Medium
Add sizes for fill Image
Suggestion Impact:The commit added sizes="130px" to the component with fill and priority, matching the suggestion.

code diff:

+                            <Image
+                                src="/BDLogo_White.svg"
+                                alt="Bitcoin Deepa"
+                                fill
+                                priority
+                                sizes="130px"
+                            />

When using fill, the container should define position: relative and an explicit
size, which you did, but also set sizes to help the browser pick the correct
resource and avoid layout shifts. Add a rounded priority-safe attribute to
ensure correct responsive behavior.

src/app/onboard/page.tsx [127-129]

 <div className="relative h-[130px] w-[130px]">
-    <Image src="/BDLogo_White.svg" alt="Bitcoin Deepa" fill priority />
+    <Image src="/BDLogo_White.svg" alt="Bitcoin Deepa" fill priority sizes="130px" />
 </div>

[Suggestion processed]

Suggestion importance[1-10]: 5

__

Why: The suggestion correctly recommends adding the sizes attribute to the Image component for better performance and responsive behavior, which is a good practice for optimization.

Low
  • Update

Co-authored-by: qodo-merge-pro[bot] <151058649+qodo-merge-pro[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant