-
Notifications
You must be signed in to change notification settings - Fork 4
fix: improve onboarding and handle undefined values #243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
41e67a4
755e8b5
f53122e
d1989ab
7e971c7
7ba06d1
c362809
8fb6596
a8d1562
2b11b1e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,7 +23,7 @@ interface AddressComponents { | |
| } | ||
|
|
||
| interface UserInputAreaProps { | ||
| userInput: string; | ||
| userInput: string | undefined; | ||
| setUserInput: (value: string) => void; | ||
| onTextSubmit: (input: string) => void; | ||
| disabled?: boolean; | ||
|
|
@@ -73,7 +73,7 @@ export default function UserInputArea({ | |
| onAddressSelect, | ||
| }: UserInputAreaProps) { | ||
| const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { | ||
| if (e.key === 'Enter' && userInput.trim()) { | ||
| if (e.key === 'Enter' && userInput?.trim()) { | ||
| e.preventDefault(); | ||
| onTextSubmit(userInput); | ||
| } | ||
|
|
@@ -91,7 +91,7 @@ export default function UserInputArea({ | |
| }; | ||
|
|
||
| const handleAddressKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { | ||
| if (e.key === 'Enter' && userInput.trim()) { | ||
| if (e.key === 'Enter' && userInput?.trim()) { | ||
| e.preventDefault(); | ||
| onTextSubmit(userInput); | ||
| } | ||
|
|
@@ -103,7 +103,7 @@ export default function UserInputArea({ | |
| <Box sx={{ display: 'flex', alignItems: 'flex-end', gap: 1 }}> | ||
| <Box sx={{ flex: 1 }}> | ||
| <AddressAutocomplete | ||
| value={userInput} | ||
| value={userInput ?? ''} | ||
| onChange={setUserInput} | ||
| onAddressSelect={handleAddressSelect} | ||
| displayFullAddress={true} | ||
|
|
@@ -113,8 +113,8 @@ export default function UserInputArea({ | |
| /> | ||
| </Box> | ||
| <SendIconBtn | ||
| onClick={() => onTextSubmit(userInput)} | ||
| disabled={disabled || userInput.trim() === ''} | ||
| onClick={() => onTextSubmit(userInput ?? '')} | ||
| disabled={disabled || !userInput || userInput.trim() === ''} | ||
|
||
| sx={{ mb: 0.5 }} | ||
| > | ||
| <ArrowUpwardRoundedIcon fontSize="small" /> | ||
|
|
@@ -128,16 +128,16 @@ export default function UserInputArea({ | |
| fullWidth | ||
| variant="outlined" | ||
| placeholder="Enter your message..." | ||
| value={userInput} | ||
| value={userInput ?? ''} | ||
| onChange={e => setUserInput(e.target.value)} | ||
| onKeyDown={handleKeyDown} | ||
| disabled={disabled} | ||
| InputProps={{ | ||
| endAdornment: ( | ||
| <InputAdornment position="end"> | ||
| <SendIconBtn | ||
| onClick={() => onTextSubmit(userInput)} | ||
| disabled={disabled || userInput.trim() === ''} | ||
| onClick={() => onTextSubmit(userInput ?? '')} | ||
| disabled={disabled || !userInput || userInput.trim() === ''} | ||
|
||
| > | ||
| <ArrowUpwardRoundedIcon fontSize="small" /> | ||
| </SendIconBtn> | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -28,7 +28,7 @@ interface AddressComponents { | |||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| interface AddressAutocompleteProps { | ||||||||||||||
| value: string; | ||||||||||||||
| value: string | undefined; | ||||||||||||||
| onChange: (value: string) => void; | ||||||||||||||
| onAddressSelect: ( | ||||||||||||||
| address: string, | ||||||||||||||
|
|
@@ -87,19 +87,20 @@ const AddressAutocomplete: React.FC<AddressAutocompleteProps> = ({ | |||||||||||||
| }) => { | ||||||||||||||
| const [suggestions, setSuggestions] = useState<AutocompletePrediction[]>([]); | ||||||||||||||
| const [loading, setLoading] = useState(false); | ||||||||||||||
| const [inputValue, setInputValue] = useState(value); | ||||||||||||||
| const [inputValue, setInputValue] = useState(value ?? ''); | ||||||||||||||
|
|
||||||||||||||
| // Debounced fetch for autocomplete | ||||||||||||||
| const debouncedFetch = useMemo( | ||||||||||||||
| () => | ||||||||||||||
| debounce(async (input: string) => { | ||||||||||||||
| if (!input || input.trim().length < 2) { | ||||||||||||||
| debounce(async (input: string | undefined) => { | ||||||||||||||
| const inputStr = input ?? ''; | ||||||||||||||
| if (!inputStr || inputStr.trim().length < 2) { | ||||||||||||||
| setSuggestions([]); | ||||||||||||||
| return; | ||||||||||||||
| } | ||||||||||||||
| setLoading(true); | ||||||||||||||
| try { | ||||||||||||||
| const data = await fetchPlaceAutocomplete(input, { | ||||||||||||||
| const data = await fetchPlaceAutocomplete(inputStr, { | ||||||||||||||
| country: 'au', | ||||||||||||||
| types: 'address', | ||||||||||||||
| }); | ||||||||||||||
|
|
@@ -113,8 +114,15 @@ const AddressAutocomplete: React.FC<AddressAutocompleteProps> = ({ | |||||||||||||
| [], | ||||||||||||||
| ); | ||||||||||||||
|
|
||||||||||||||
| // Sync inputValue with value prop when it changes externally | ||||||||||||||
| useEffect(() => { | ||||||||||||||
| void debouncedFetch(inputValue); | ||||||||||||||
| if (value !== undefined && value !== inputValue) { | ||||||||||||||
| setInputValue(value ?? ''); | ||||||||||||||
|
Comment on lines
+119
to
+120
|
||||||||||||||
| if (value !== undefined && value !== inputValue) { | |
| setInputValue(value ?? ''); | |
| const normalizedValue = value ?? ''; | |
| if (normalizedValue !== inputValue) { | |
| setInputValue(normalizedValue); |
Copilot
AI
Dec 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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]); |
Copilot
AI
Dec 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Copilot
AI
Dec 21, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The validation check only verifies that
_idandemailexist, but then provides fallback values for other fields. However, ifemailis 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.