fix: improve onboarding and handle undefined values#243
fix: improve onboarding and handle undefined values#243Benbenzhouz wants to merge 10 commits intomainfrom
Conversation
- Replace || with ?? in AuthCallbackContent.tsx - Replace || with ?? in UserInputArea.tsx - Replace || with ?? in GoogleOAuthButton.tsx - Replace || with ?? in AddressAutocomplete.tsx - Replace || with ?? in publicApiSlice.ts - Replace || with ?? in axiosBaseQuery.ts - Fix useEffect dependency in AddressAutocomplete.tsx - Remove console.log statements in AuthCallbackContent.tsx
- Create unified getApiBaseUrl() utility function - Remove hardcoded localhost fallbacks from all files - Production environment now requires API URL to be configured - Development environment allows localhost fallback with warning - Improves code quality and prevents production misconfigurations Files updated: - src/utils/api-config.ts (new) - src/services/places.ts - src/lib/axiosBaseQuery.ts - src/features/public/publicApiSlice.ts - src/components/GoogleOAuthButton.tsx - src/app/(public)/blogs/page.tsx - src/app/(public)/blogs/components/BlogList.tsx - src/app/(public)/blogs/[id]/page.tsx
- Remove unnecessary NEXT_PUBLIC_API_BASE_URL fallback - Use only NEXT_PUBLIC_API_URL as it's the actual configured variable - Simplify code and error messages
- Change from NEXT_PUBLIC_API_URL to NEXT_PUBLIC_API_BASE_URL - This matches the actual configured environment variable name
- Keep same behavior as before: NEXT_PUBLIC_API_BASE_URL ?? localhost fallback - Remove production environment check to match original usage - Environment variable name unchanged: NEXT_PUBLIC_API_BASE_URL - Function only provides centralized management, no behavior change
…upport - Restore NEXT_PUBLIC_API_BASE_URL and NEXT_PUBLIC_API_URL support - Keep function for centralized management - Match original behavior exactly
There was a problem hiding this comment.
Pull request overview
This PR improves the onboarding flow by fixing undefined value handling and centralizes API configuration management through a new utility function. The changes address ESLint warnings and improve error handling in the OAuth callback flow.
Key Changes:
- Introduced centralized API base URL configuration via
getApiBaseUrl()utility function with fallback support for bothNEXT_PUBLIC_API_BASE_URLandNEXT_PUBLIC_API_URL - Fixed undefined value handling in
AddressAutocompleteandUserInputAreacomponents by acceptingstring | undefinedtypes and providing appropriate fallbacks - Enhanced address formatting validation to match backend requirements for Australian addresses (State Postcode format)
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
src/utils/api-config.ts |
New utility providing centralized API base URL configuration with environment variable fallback support |
src/services/places.ts |
Updated to use centralized getApiBaseUrl() function |
src/lib/axiosBaseQuery.ts |
Migrated to use getApiBaseUrl() and fixed ESLint nullish coalescing preference |
src/features/public/publicApiSlice.ts |
Updated to use centralized API configuration |
src/components/ui/AddressAutocomplete.tsx |
Enhanced to handle undefined values, improved address formatting with state/postcode validation, and added defensive null checks |
src/components/GoogleOAuthButton.tsx |
Migrated to use getApiBaseUrl() function |
src/app/onboarding/components/UserInputArea.tsx |
Updated to accept undefined userInput prop and added appropriate null coalescing operators |
src/app/auth/callback/AuthCallbackContent.tsx |
Added validation for required user fields and fallback values for optional fields |
src/app/(public)/blogs/page.tsx |
Updated to use centralized API configuration |
src/app/(public)/blogs/components/BlogList.tsx |
Updated to use centralized API configuration |
src/app/(public)/blogs/[id]/page.tsx |
Updated to use centralized API configuration |
package.json |
Added explicit port 3000 to dev script |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (value !== undefined && value !== inputValue) { | ||
| setInputValue(value ?? ''); |
There was a problem hiding this comment.
The condition value !== inputValue will be true on every render when value is undefined and inputValue is '' (empty string), causing unnecessary state updates. Consider using a more precise check like value !== undefined && value !== inputValue or checking if both are empty/undefined before updating.
| if (value !== undefined && value !== inputValue) { | |
| setInputValue(value ?? ''); | |
| const normalizedValue = value ?? ''; | |
| if (normalizedValue !== inputValue) { | |
| setInputValue(normalizedValue); |
| // Validate parsed user data | ||
| if (!parsedUser._id || !parsedUser.email) { | ||
| // eslint-disable-next-line no-console | ||
| console.error('[AuthCallback] Invalid user data:', parsedUser); | ||
| router.replace('/login?error=oauth_invalid_data'); | ||
| return; | ||
| } | ||
|
|
||
| dispatch( | ||
| setCredentials({ | ||
| csrfToken, | ||
| user: { | ||
| _id: parsedUser._id, | ||
| email: parsedUser.email, | ||
| firstName: parsedUser.firstName, | ||
| lastName: parsedUser.lastName, | ||
| role: parsedUser.role, | ||
| status: parsedUser.status, | ||
| email: parsedUser.email ?? '', | ||
| firstName: parsedUser.firstName ?? '', | ||
| lastName: parsedUser.lastName ?? '', | ||
| role: parsedUser.role ?? 'user', | ||
| status: parsedUser.status ?? 'active', | ||
| }, |
There was a problem hiding this comment.
The validation check only verifies that _id and email exist, but then provides fallback values for other fields. However, if email is an empty string (falsy but not null/undefined), it will pass validation but be overridden to an empty string anyway. The validation should either require all critical fields or only validate fields that truly cannot have fallback values.
| onClick={() => onTextSubmit(userInput)} | ||
| disabled={disabled || userInput.trim() === ''} | ||
| onClick={() => onTextSubmit(userInput ?? '')} | ||
| disabled={disabled || !userInput || userInput.trim() === ''} |
There was a problem hiding this comment.
The condition disabled || !userInput || userInput.trim() === '' is redundant. If !userInput is true (meaning userInput is undefined, null, or empty string), then userInput.trim() === '' will either throw an error (for undefined/null) or be redundant (for empty string). The check should be simplified to disabled || !userInput?.trim() which safely handles all cases.
| onClick={() => onTextSubmit(userInput)} | ||
| disabled={disabled || userInput.trim() === ''} | ||
| onClick={() => onTextSubmit(userInput ?? '')} | ||
| disabled={disabled || !userInput || userInput.trim() === ''} |
There was a problem hiding this comment.
The condition disabled || !userInput || userInput.trim() === '' is redundant. If !userInput is true (meaning userInput is undefined, null, or empty string), then userInput.trim() === '' will either throw an error (for undefined/null) or be redundant (for empty string). The check should be simplified to disabled || !userInput?.trim() which safely handles all cases.
| // Ensure postcode is 4 digits | ||
| const postcode = components.postalCode.replace(/\D/g, '').substring(0, 4); | ||
| if (postcode.length === 4) { |
There was a problem hiding this comment.
The postcode validation uses .substring(0, 4) to ensure exactly 4 digits, but only adds the postcode if the length is 4. This means postcodes like "200" (3 digits after removing non-digits) would be silently dropped. Consider whether this is the intended behavior or if validation should fail more explicitly for invalid postcodes.
| // Ensure postcode is 4 digits | |
| const postcode = components.postalCode.replace(/\D/g, '').substring(0, 4); | |
| if (postcode.length === 4) { | |
| // Ensure postcode is at most 4 digits | |
| const postcode = components.postalCode.replace(/\D/g, '').substring(0, 4); | |
| if (postcode.length > 0) { |
| // Ensure state is uppercase and 2-3 characters | ||
| const state = components.administrativeAreaLevel1 | ||
| .toUpperCase() | ||
| .substring(0, 3); | ||
| statePostcode.push(state); |
There was a problem hiding this comment.
The state abbreviation is truncated to 3 characters using .substring(0, 3), but Australian state codes should be 2-3 characters maximum. However, the comment says "2-3 characters" but the code allows any input to be truncated to 3 chars. If the input is a full state name like "New South Wales", this would result in "NEW" instead of "NSW". Consider validating that the state is already an abbreviation or mapping full names to abbreviations.
| if (value !== undefined && value !== inputValue) { | ||
| setInputValue(value ?? ''); | ||
| } | ||
| }, [value, inputValue]); |
There was a problem hiding this comment.
The useEffect dependency array includes inputValue which creates an infinite loop. When the effect runs and value !== inputValue, it calls setInputValue(value ?? ''), which updates inputValue. This change to inputValue triggers the effect again because inputValue is in the dependency array, creating a loop. Remove inputValue from the dependency array to fix this.
| }, [value, inputValue]); | |
| }, [value]); |
Changes
Onboarding Improvements
Environment Variables
Other Fixes
Environment Variables (No Changes Required)