Fix dropdown menu - make full row clickable

- BeneficiaryMenu: dropdownItem now has width: 100%
- Increased minWidth to 180 and added overflow: hidden
- Users can now tap anywhere on the menu row, not just the text

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Sergei 2026-01-09 18:41:35 -08:00
parent 973e9b7ebe
commit 2e72398818
8 changed files with 258 additions and 5 deletions

3
.maestro/tap-mama.yaml Normal file
View File

@ -0,0 +1,3 @@
appId: host.exp.Exponent
---
- tapOn: "Mama3030"

View File

@ -835,8 +835,8 @@ router.patch('/:id/avatar', async (req, res) => {
.eq('beneficiary_id', beneficiaryId)
.single();
if (accessError || !access || !['custodian', 'guardian'].includes(access.role)) {
return res.status(403).json({ error: 'Only custodian or guardian can update avatar' });
if (accessError || !access || !['custodian', 'guardian', 'caretaker'].includes(access.role)) {
return res.status(403).json({ error: 'Only custodian, guardian or caretaker can update avatar' });
}
// Validate base64 if provided

View File

@ -160,14 +160,17 @@ const styles = StyleSheet.create({
dropdownMenu: {
backgroundColor: AppColors.surface,
borderRadius: BorderRadius.lg,
minWidth: 160,
minWidth: 180,
overflow: 'hidden',
...Shadows.lg,
},
dropdownItem: {
flexDirection: 'row',
alignItems: 'center',
padding: Spacing.md,
paddingVertical: Spacing.md,
paddingHorizontal: Spacing.md,
gap: Spacing.sm,
width: '100%',
},
dropdownItemText: {
fontSize: FontSizes.base,

19
expo-19000.log Normal file
View File

@ -0,0 +1,19 @@
Starting project at /Users/sergei/Desktop/WellNuo
/Users/sergei/Desktop/WellNuo/node_modules/expo/node_modules/@expo/cli/build/src/utils/errors.js:130
throw error;
^
RangeError [ERR_SOCKET_BAD_PORT]: options.port should be >= 0 and < 65536. Received type number (65536).
at Server.listen (node:net:2059:5)
at /Users/sergei/Desktop/WellNuo/node_modules/freeport-async/index.js:8:12
at new Promise (<anonymous>)
at testPortAsync (/Users/sergei/Desktop/WellNuo/node_modules/freeport-async/index.js:6:10)
at availableAsync (/Users/sergei/Desktop/WellNuo/node_modules/freeport-async/index.js:24:17)
at /Users/sergei/Desktop/WellNuo/node_modules/freeport-async/index.js:37:23
at new Promise (<anonymous>)
at freePortRangeAsync (/Users/sergei/Desktop/WellNuo/node_modules/freeport-async/index.js:33:10)
at /Users/sergei/Desktop/WellNuo/node_modules/freeport-async/index.js:43:18 {
code: 'ERR_SOCKET_BAD_PORT'
}
Node.js v20.19.5

226
expo-8081.log Normal file
View File

@ -0,0 +1,226 @@
Starting project at /Users/sergei/Desktop/WellNuo
React Compiler enabled
Starting Metro Bundler
The following packages should be updated for best compatibility with the installed expo version:
expo@54.0.30 - expected version: ~54.0.31
expo-constants@18.0.12 - expected version: ~18.0.13
Your project may not work correctly until you install the expected versions of the packages.
Opening exp://172.20.10.10:8081 on 8081
Waiting on http://localhost:8081
Logs for your project will appear below.
Could not fetch new Expo development certificate, falling back to cached certificate
iOS Bundled 531ms node_modules/expo-router/entry.js (1421 modules)
LOG expo-speech-recognition not available
LOG [Voice] expo-speech-recognition not available in Expo Go
LOG [Layout] Still initializing auth state...
LOG [AuthContext] checkAuth starting...
LOG [AuthContext] checkAuth: Checking token...
LOG [AuthContext] checkAuth: Token exists=true, length=187
LOG [AuthContext] checkAuth: isAuth=true
LOG [AuthContext] checkAuth: Getting stored user...
LOG [API] getStoredUser: token exists = true , userId = 62
LOG [API] getStoredUser: Fetching profile from server...
LOG [API] getStoredUser: Profile response ok = true , error = undefined
LOG [AuthContext] checkAuth: User found=true
LOG [AuthContext] checkAuth: Finished
LOG [Layout] Auth check: {"hasInitialRedirect": false, "inAuthGroup": false, "isAuthenticated": true}
LOG [API] getAllBeneficiaries - token exists: true length: 187
LOG [API] getAllBeneficiaries - Fetching from: https://wellnuo.smartlaunchhub.com/api/me/beneficiaries
LOG [API] getAllBeneficiaries - Response status: 200
LOG [API] getAllBeneficiaries - Data: {"beneficiaries":[{"accessId":41,"id":21,"role":"custodian","grantedAt":"2026-01-09T04:15:44.403Z","name":"Mama","phone":null,"address":null,"avatarUrl":null,"createdAt":"2026-01-09T04:15:43.079Z","su
LOG [API] getWellNuoBeneficiary - Raw response: {"id":21,"name":"Mama","hasDevices":false,"equipmentStatus":"none"}
LOG [API] getAllBeneficiaries - token exists: true length: 187
LOG [API] getAllBeneficiaries - Fetching from: https://wellnuo.smartlaunchhub.com/api/me/beneficiaries
LOG [API] getWellNuoBeneficiary - Raw response: {"id":21,"name":"Mama","hasDevices":false,"equipmentStatus":"none"}
LOG [API] getAllBeneficiaries - Response status: 200
LOG [API] getAllBeneficiaries - Data: {"beneficiaries":[{"accessId":41,"id":21,"role":"custodian","grantedAt":"2026-01-09T04:15:44.403Z","name":"Mama","phone":null,"address":null,"avatarUrl":null,"createdAt":"2026-01-09T04:15:43.079Z","su
LOG [API] getWellNuoBeneficiary - Raw response: {"id":22,"name":"Mama2","hasDevices":false,"equipmentStatus":"none"}
LOG [Purchase] Creating payment sheet for userId: 62 beneficiaryId: 22
ERROR Payment error: [Error: ephemeralKey is not defined]
Code: purchase.tsx
  119 |
 120 | if (!data.paymentIntent) {
> 121 | throw new Error(data.error || 'Failed to create payment sheet');
 | ^
 122 | }
 123 |
 124 | const { error: initError } = await initPaymentSheet({
Call Stack
handlePurchase (app/(auth)/purchase.tsx:121:24)
LOG [Purchase] Creating payment sheet for userId: 62 beneficiaryId: 22
ERROR Payment error: [Error: ephemeralKey is not defined]
Code: purchase.tsx
  119 |
 120 | if (!data.paymentIntent) {
> 121 | throw new Error(data.error || 'Failed to create payment sheet');
 | ^
 122 | }
 123 |
 124 | const { error: initError } = await initPaymentSheet({
Call Stack
handlePurchase (app/(auth)/purchase.tsx:121:24)
Could not fetch new Expo development certificate, falling back to cached certificate
iOS Bundled 83ms node_modules/expo-router/entry.js (1 module)
LOG expo-speech-recognition not available
LOG [Voice] expo-speech-recognition not available in Expo Go
LOG [Layout] Still initializing auth state...
LOG [AuthContext] checkAuth starting...
LOG [AuthContext] checkAuth: Checking token...
LOG [AuthContext] checkAuth: Token exists=true, length=187
LOG [AuthContext] checkAuth: isAuth=true
LOG [AuthContext] checkAuth: Getting stored user...
LOG [API] getStoredUser: token exists = true , userId = 62
LOG [API] getStoredUser: Fetching profile from server...
LOG [API] getStoredUser: Profile response ok = true , error = undefined
LOG [AuthContext] checkAuth: User found=true
LOG [AuthContext] checkAuth: Finished
LOG [Layout] Auth check: {"hasInitialRedirect": false, "inAuthGroup": false, "isAuthenticated": true}
LOG [API] getAllBeneficiaries - token exists: true length: 187
LOG [API] getAllBeneficiaries - Fetching from: https://wellnuo.smartlaunchhub.com/api/me/beneficiaries
LOG [API] getAllBeneficiaries - Response status: 200
LOG [API] getAllBeneficiaries - Data: {"beneficiaries":[{"accessId":41,"id":21,"role":"custodian","grantedAt":"2026-01-09T04:15:44.403Z","name":"Mama","phone":null,"address":null,"avatarUrl":null,"createdAt":"2026-01-09T04:15:43.079Z","su
LOG [API] getWellNuoBeneficiary - Raw response: {"id":22,"name":"Mama2","hasDevices":false,"equipmentStatus":"none"}
LOG [API] getWellNuoBeneficiary - Raw response: {"id":22,"name":"Mama2","hasDevices":false,"equipmentStatus":"none"}
ERROR Payment error: [Error: ephemeralKey is not defined]
Code: purchase.tsx
  113 |
 114 | if (!data.paymentIntent) {
> 115 | throw new Error(data.error || 'Failed to create payment sheet');
 | ^
 116 | }
 117 |
 118 | const { error: initError } = await initPaymentSheet({
Call Stack
handlePurchase (app/(tabs)/beneficiaries/[id]/purchase.tsx:115:24)
Could not fetch new Expo development certificate, falling back to cached certificate
iOS node_modules/expo-router/entry.js ░░░░░░░░░░░░░░░░ 0.0% (0/1)
iOS Bundled 68ms node_modules/expo-router/entry.js (1 module)
LOG expo-speech-recognition not available
LOG [Voice] expo-speech-recognition not available in Expo Go
LOG [Layout] Still initializing auth state...
LOG [AuthContext] checkAuth starting...
LOG [AuthContext] checkAuth: Checking token...
LOG [AuthContext] checkAuth: Token exists=true, length=187
LOG [AuthContext] checkAuth: isAuth=true
LOG [AuthContext] checkAuth: Getting stored user...
LOG [API] getStoredUser: token exists = true , userId = 62
LOG [API] getStoredUser: Fetching profile from server...
LOG [API] getStoredUser: Profile response ok = true , error = undefined
LOG [AuthContext] checkAuth: User found=true
LOG [AuthContext] checkAuth: Finished
LOG [Layout] Auth check: {"hasInitialRedirect": false, "inAuthGroup": false, "isAuthenticated": true}
LOG [API] getAllBeneficiaries - token exists: true length: 187
LOG [API] getAllBeneficiaries - Fetching from: https://wellnuo.smartlaunchhub.com/api/me/beneficiaries
LOG [API] getAllBeneficiaries - Response status: 200
LOG [API] getAllBeneficiaries - Data: {"beneficiaries":[]}
LOG [API] getAllBeneficiaries - token exists: true length: 187
LOG [API] getAllBeneficiaries - Fetching from: https://wellnuo.smartlaunchhub.com/api/me/beneficiaries
LOG [API] getAllBeneficiaries - Response status: 200
LOG [API] getAllBeneficiaries - Data: {"beneficiaries":[]}
LOG [API] getAllBeneficiaries - token exists: true length: 187
LOG [API] getAllBeneficiaries - Fetching from: https://wellnuo.smartlaunchhub.com/api/me/beneficiaries
LOG [API] getAllBeneficiaries - Response status: 200
LOG [API] getAllBeneficiaries - Data: {"beneficiaries":[{"accessId":41,"id":21,"role":"custodian","grantedAt":"2026-01-09T04:15:44.403Z","name":"Mama","phone":null,"address":null,"createdAt":"2026-01-09T04:15:43.079Z","subscription":{"pla
LOG [API] getWellNuoBeneficiary - Raw response: {"id":21,"name":"Mama","hasDevices":false,"equipmentStatus":"none"}
LOG [API] getWellNuoBeneficiary - Raw response: {"id":21,"name":"Mama","hasDevices":false,"equipmentStatus":"none"}
LOG [API] getAllBeneficiaries - token exists: true length: 187
LOG [API] getAllBeneficiaries - Fetching from: https://wellnuo.smartlaunchhub.com/api/me/beneficiaries
LOG [API] getAllBeneficiaries - Response status: 200
LOG [API] getAllBeneficiaries - Data: {"beneficiaries":[{"accessId":41,"id":21,"role":"custodian","grantedAt":"2026-01-09T04:15:44.403Z","name":"Mama","phone":null,"address":null,"createdAt":"2026-01-09T04:15:43.079Z","subscription":{"pla
LOG [API] getWellNuoBeneficiary - Raw response: {"id":23,"name":"Mama3","hasDevices":false,"equipmentStatus":"none"}
LOG [Purchase] Creating payment sheet for userId: 62 beneficiaryId: 23
LOG [Purchase] Payment successful, updating equipment status...
LOG [API] getWellNuoBeneficiary - Raw response: {"id":23,"name":"Mama3","hasDevices":false,"equipmentStatus":"ordered"}
LOG [API] getWellNuoBeneficiary - Raw response: {"id":23,"name":"Mama3","hasDevices":true,"equipmentStatus":"demo"}
LOG [API] getWellNuoBeneficiary - Raw response: {"id":23,"name":"Mama3","hasDevices":true,"equipmentStatus":"demo"}
ERROR Payment error: [Error: Failed to create subscription]
Code: subscription.tsx
  143 |
 144 | if (!data.clientSecret) {
> 145 | throw new Error(data.error || 'Failed to create subscription');
 | ^
 146 | }
 147 |
 148 | // 2. Initialize the Payment Sheet
Call Stack
handleSubscribe (app/(tabs)/beneficiaries/[id]/subscription.tsx:145:24)
LOG [API] getWellNuoBeneficiary - Raw response: {"id":23,"name":"Mama3","hasDevices":true,"equipmentStatus":"demo"}
LOG [DEBUG] loadBeneficiary: fetching id=23
LOG [API] getWellNuoBeneficiary - Raw response: {"id":23,"name":"Mama3","hasDevices":true,"equipmentStatus":"demo"}
LOG [DEBUG] loadBeneficiary: response.ok=true
LOG [DEBUG] loadBeneficiary: got beneficiary id=23, name=Mama3
LOG [DEBUG] loadBeneficiary: fetching id=23
LOG [API] getWellNuoBeneficiary - Raw response: {"id":23,"name":"Mama3","hasDevices":true,"equipmentStatus":"demo"}
LOG [DEBUG] loadBeneficiary: response.ok=true
LOG [DEBUG] loadBeneficiary: got beneficiary id=23, name=Mama3
LOG [DEBUG] handleSubscribe: START
LOG [DEBUG] handleSubscribe: beneficiary={"id":23,"name":"Mama3"}
LOG [DEBUG] handleSubscribe: token=eyJhbGciOiJIUzI1NiIs...
LOG [DEBUG] handleSubscribe: calling https://wellnuo.smartlaunchhub.com/api/stripe/create-subscription-payment-sheet
LOG [DEBUG] handleSubscribe: body={"beneficiaryId":23}
LOG [DEBUG] handleSubscribe: response.status=200
LOG [DEBUG] handleSubscribe: response data={"subscriptionId":"sub_1SnYNwP0gvUw6M9CMooRyBOz","ephemeralKey":"ek_test_YWNjdF8xUDNrZHFQMGd2VXc2TTlDLGlmOGdHVldBc1ppNkxxZmM1N1B2WXJuZWNSZ1lRbkg_00BcXeNVrC","customer":"cus_Tl4QHSfVqjstea","publishableKey":"pk_test_51P3kdqP0gvUw6M9C7ixPQHqbPcvga4G5kAYx1h6QXQAt1psbrC2rrmOojW0fTeQzaxD1Q9RKS3zZ23MCvjjZpWLi00eCFWRHMk"}
ERROR Payment error: [Error: Failed to create subscription]
Code: subscription.tsx
  180 |
 181 | if (!data.clientSecret) {
> 182 | throw new Error(data.error || 'Failed to create subscription');
 | ^
 183 | }
 184 |
 185 | // 2. Initialize the Payment Sheet
Call Stack
handleSubscribe (app/(tabs)/beneficiaries/[id]/subscription.tsx:182:24)
Unable to resolve "react-native-root-toast" from "app/(tabs)/beneficiaries/[id]/subscription.tsx"
  21 | import type { Beneficiary } from '@/types';
 22 | import * as ExpoClipboard from 'expo-clipboard';
> 23 | import Toast from 'react-native-root-toast';
 | ^
 24 |
 25 | const STRIPE_API_URL = 'https://wellnuo.smartlaunchhub.com/api/stripe';
 26 | const SUBSCRIPTION_PRICE = 49; // $49/month
Import stack:
app/(tabs)/beneficiaries/[id]/subscription.tsx
| import "react-native-root-toast"
app (require.context)
Unable to resolve "react-native-root-toast" from "app/(tabs)/beneficiaries/[id]/subscription.tsx"
  21 | import type { Beneficiary } from '@/types';
 22 | import * as ExpoClipboard from 'expo-clipboard';
> 23 | import Toast from 'react-native-root-toast';
 | ^
 24 |
 25 | const STRIPE_API_URL = 'https://wellnuo.smartlaunchhub.com/api/stripe';
 26 | const SUBSCRIPTION_PRICE = 49; // $49/month
Import stack:
app/(tabs)/beneficiaries/[id]/subscription.tsx
| import "react-native-root-toast"
app (require.context)
Unable to resolve "react-native-root-toast" from "app/(tabs)/beneficiaries/[id]/subscription.tsx"
  21 | import type { Beneficiary } from '@/types';
 22 | import * as ExpoClipboard from 'expo-clipboard';
> 23 | import Toast from 'react-native-root-toast';
 | ^
 24 |
 25 | const STRIPE_API_URL = 'https://wellnuo.smartlaunchhub.com/api/stripe';
 26 | const SUBSCRIPTION_PRICE = 49; // $49/month
Import stack:
app/(tabs)/beneficiaries/[id]/subscription.tsx
| import "react-native-root-toast"
app (require.context)

1
package-lock.json generated
View File

@ -22,6 +22,7 @@
"expo-clipboard": "~8.0.8",
"expo-constants": "~18.0.12",
"expo-crypto": "~15.0.8",
"expo-file-system": "~19.0.21",
"expo-font": "~14.0.10",
"expo-haptics": "~15.0.8",
"expo-image": "~3.0.11",

View File

@ -25,6 +25,7 @@
"expo-clipboard": "~8.0.8",
"expo-constants": "~18.0.12",
"expo-crypto": "~15.0.8",
"expo-file-system": "~19.0.21",
"expo-font": "~14.0.10",
"expo-haptics": "~15.0.8",
"expo-image": "~3.0.11",

View File

@ -778,7 +778,7 @@ class ApiService {
if (imageUri) {
// Read file as base64 string using expo-file-system
const base64Data = await FileSystem.readAsStringAsync(imageUri, {
encoding: FileSystem.EncodingType.Base64,
encoding: 'base64',
});
// Determine mime type from URI extension