Skip to content

Commit e85bb4b

Browse files
authored
Merge pull request #150 from razeprasine/Issue38Fix
Issue38 fix
2 parents 58e353a + cd7a7eb commit e85bb4b

5 files changed

Lines changed: 35 additions & 34 deletions

File tree

app/(merchant)/dashboard/page.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { useState, useEffect } from 'react';
3+
import { useState, useEffect, useCallback } from 'react';
44
import dynamic from 'next/dynamic';
55
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
66
import { Button } from '@/components/ui/button';
@@ -77,10 +77,14 @@ export default function DashboardPage() {
7777

7878
const firstName = user?.name?.split(' ')[0] ?? 'Merchant';
7979

80-
const handleCopy = (text: string) => {
80+
const handleCopy = useCallback((text: string) => {
8181
navigator.clipboard.writeText(text);
8282
notify.success('Copied to clipboard');
83-
};
83+
}, []);
84+
85+
const handlePeriodChange = useCallback((p: Period) => {
86+
setActivePeriod(p);
87+
}, []);
8488

8589
const toggleSimulation = () => {
8690
const nextState = !simulationEnabled;
@@ -240,7 +244,7 @@ export default function DashboardPage() {
240244
{PERIOD_OPTIONS.map((p) => (
241245
<button
242246
key={p}
243-
onClick={() => setActivePeriod(p)}
247+
onClick={() => handlePeriodChange(p)}
244248
className={cn(
245249
'min-h-[44px] min-w-[44px] px-3 py-1 rounded-md text-xs font-semibold transition-all',
246250
activePeriod === p

app/(merchant)/settings/page.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { useState } from 'react';
3+
import { useState, useCallback } from 'react';
44
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
55
import { Button } from '@/components/ui/button';
66
import { Input } from '@/components/ui/input';
@@ -27,6 +27,7 @@ const notificationOptions = [
2727
];
2828

2929
export default function SettingsPage() {
30+
const router = useRouter();
3031
const [activeTab, setActiveTab] = useState('profile');
3132
const [notificationPreferences, setNotificationPreferences] = useState<Record<string, boolean>>({
3233
paymentReceived: true,
@@ -37,11 +38,15 @@ export default function SettingsPage() {
3738
const { user, logout } = useAuthStore();
3839
const notify = useNotify();
3940

40-
const handleLogout = () => {
41+
const handleLogout = useCallback(() => {
4142
logout();
4243
notify.success('Logged out successfully');
4344
router.push('/auth/login');
44-
};
45+
}, [logout, router]);
46+
47+
const handleTabChange = useCallback((id: string) => {
48+
setActiveTab(id);
49+
}, []);
4550

4651
const toggleNotificationPreference = (id: string) => {
4752
setNotificationPreferences((current) => ({
@@ -67,7 +72,7 @@ export default function SettingsPage() {
6772
{tabs.map(({ id, label, icon: Icon }) => (
6873
<button
6974
key={id}
70-
onClick={() => setActiveTab(id)}
75+
onClick={() => handleTabChange(id)}
7176
className={cn(
7277
'w-full flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-colors text-left',
7378
activeTab === id

app/auth/login/page.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { useState, useEffect, Suspense } from 'react';
3+
import { useState, useEffect, Suspense, useCallback } from 'react';
44
import { useRouter } from 'next/navigation';
55
import Link from 'next/link';
66
import { useForm } from 'react-hook-form';
@@ -49,7 +49,7 @@ export default function LoginPage() {
4949
resolver: zodResolver(loginSchema),
5050
});
5151

52-
const onSubmit = async (data: LoginFormValues) => {
52+
const onSubmit = useCallback(async (data: LoginFormValues) => {
5353
setIsLoading(true);
5454
try {
5555
const isMockAdmin = data.email.includes('admin');
@@ -102,10 +102,10 @@ export default function LoginPage() {
102102
} finally {
103103
setIsLoading(false);
104104
}
105-
};
105+
}, [login, router, success, error]);
106106

107107
// When WalletModal reports a connected address, perform the merchant login flow
108-
const onWalletConnected = async (address: string) => {
108+
const onWalletConnected = useCallback(async (address: string) => {
109109
setIsWalletLoading(true);
110110
try {
111111
const mockToken = 'mock_jwt_token_12345';
@@ -134,7 +134,7 @@ export default function LoginPage() {
134134
} finally {
135135
setIsWalletLoading(false);
136136
}
137-
};
137+
}, [login, success, error]);
138138

139139

140140
return (

components/layout/Topbar.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { useState } from "react";
3+
import { useState, useCallback } from "react";
44
import { Bell, Search, Menu, LogOut, Settings, KeyRound } from "lucide-react";
55
import { Button } from "@/components/ui/button";
66
import { Input } from "@/components/ui/input";
@@ -34,11 +34,11 @@ export const Topbar = ({ onMenuClick, isMenuOpen, title, unreadNotificationCount
3434
? `Notifications (${unreadNotificationCount} unread)`
3535
: "Notifications";
3636

37-
const handleLogout = () => {
37+
const handleLogout = useCallback(() => {
3838
logout();
3939
notify.success("Logged out successfully");
4040
router.push("/auth/login");
41-
};
41+
}, [logout, router]);
4242

4343
const initials = user?.name
4444
? user.name

fix.md

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,17 @@
1-
29
2-
3-
user avatar
4-
5-
6-
7-
15 matches
8-
#28 Ensure all forms are usable with mobile keyboards
1+
Add useCallback for event handlers in dashboard and settings
92
Repo Avatar
103
Betta-Pay/BettaPay-Frontend
11-
Description: When mobile keyboards are open, form fields can be hidden behind the keyboard. The login, register, payment link creation, and settings forms need to be scrollable and the active input should remain visible above the keyboard.
4+
Description: Inline functions passed as props (e.g., onClick, onChange, handleCopy) create new function references on every render, causing child components to re-render even with React.memo. Wrapping these in useCallback stabilizes the references.
125

136
Requirements:
147

15-
Form containers should be within a scrollable area
16-
The or form wrapper should not have overflow: hidden that prevents scrolling to the focused input
17-
Test all forms with a mobile keyboard open (use Chrome DevTools device emulation)
18-
Ensure the submit button is reachable without scrolling excessively
8+
Wrap handleCopy, handleLogout, onSubmit, and other handlers in useCallback
9+
Include correct dependency arrays
10+
Apply across all pages with interactive elements
1911
Suggested execution steps:
2012

21-
Review all form pages and verify the scroll behavior with keyboard open
22-
In app/auth/layout.tsx, ensure the form column uses overflow-y: auto not overflow: hidden
23-
Add scroll-margin-bottom: 200px to form submit buttons so they scroll above the keyboard
24-
Test the settings page form fields (business info, security) with keyboard open
25-
Test the payment link creation dialog — dialogs can be problematic with mobile keyboards
13+
In app/(merchant)/dashboard/page.tsx, wrap handleCopy and the period toggle handler in useCallback
14+
In app/(merchant)/settings/page.tsx, wrap handleLogout and tab change handlers
15+
In app/auth/login/page.tsx, wrap onSubmit and onWalletConnected
16+
In components/layout/Topbar.tsx, wrap handleLogout
17+
Use React DevTools Profiler to verify fewer re-renders

0 commit comments

Comments
 (0)