WEB-884 feat: auto-fill address fields from postal code using Zippopotam API#3479
WEB-884 feat: auto-fill address fields from postal code using Zippopotam API#3479shubhamkumar9199 wants to merge 1 commit intoopenMF:devfrom
Conversation
|
Note
|
| Cohort / File(s) | Summary |
|---|---|
Address Tab integration src/app/clients/clients-view/address-tab/address-tab.component.ts |
Adds PostalCodeLookupService usage, opens dialogs then calls setupPostalCodeLookup(...) to subscribe to postalCode valueChanges (debounce, distinct, min length), chooses lookup strategy, converts to ResolvedAddress, applies/clears auto-filled city, stateProvinceId, countryId, tracks auto-filled fields, and unsubscribes on dialog close. |
Postal code lookup service src/app/shared/services/postal-code-lookup.service.ts |
New root-provided service using HttpBackend to call Zippopotam.us; exposes enabled, lookup, lookupWithFallback, resolveCountryCode, toResolvedAddress, and findBestMatch with alias expansion and staged matching; maps errors to null. |
Country code constants src/app/shared/constants/country-codes.ts |
New mappings: COUNTRY_NAME_TO_ISO_CODE, DEFAULT_LOOKUP_COUNTRY_CODES, and COUNTRY_NAME_ALIASES for normalization, aliasing, and fallback lookup ordering. |
Models src/app/shared/models/postal-code-lookup.model.ts |
New interfaces: PostalCodePlace, PostalCodeLookupResponse, and ResolvedAddress to type API responses and normalized results. |
Environment/runtime flag src/assets/env.js, src/environments/environment.ts, src/environments/environment.prod.ts |
Adds window.env.enablePostalCodeLookup (default 'false') and environment.enablePostalCodeLookup boolean derived from runtime env for feature toggle. |
Sequence Diagram
sequenceDiagram
participant User
participant FormDialog as FormDialogComponent
participant AddressTab as AddressTabComponent
participant LookupSvc as PostalCodeLookupService
participant API as Zippopotam.us API
User->>FormDialog: Open address dialog / enter postal code
FormDialog->>AddressTab: expose form & postalCode valueChanges via dialogRef.componentInstance.form
AddressTab->>AddressTab: debounceTime(600ms), distinctUntilChanged, trim, length>=3
AddressTab->>LookupSvc: lookup(countryCode?, postalCode) OR lookupWithFallback(postalCode)
LookupSvc->>API: HTTP GET
API-->>LookupSvc: PostalCodeLookupResponse | error
LookupSvc->>LookupSvc: resolveCountryCode(...) and toResolvedAddress(...)
LookupSvc-->>AddressTab: ResolvedAddress | null
AddressTab->>FormDialog: applyResolvedAddress -> set city/stateProvinceId/countryId, mark dirty, track auto-filled fields
FormDialog->>User: show auto-populated fields
AddressTab->>AddressTab: unsubscribe and clear auto-filled tracking on dialog close
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~45 minutes
Suggested reviewers
- gkbishnoi07
- IOhacker
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title clearly and specifically describes the main feature: auto-filling address fields from postal code using the Zippopotam API, which is the primary change across all modified files. |
| Docstring Coverage | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches
🧪 Generate unit tests (beta)
- Create PR with unit tests
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.
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/clients/clients-view/address-tab/address-tab.component.ts`:
- Around line 184-190: The current postal-code lookup subscription in
address-tab.component.ts exits early on a miss and leaves previously auto-filled
fields intact; update the subscribe callback for
postalCodeLookup.toResolvedAddress (and the similar block around the other
lookup at lines ~217-240) so that when response is falsy or
postalCodeLookup.toResolvedAddress(response) returns null you explicitly clear
only the fields that were auto-filled (e.g., city, state, country controls)
instead of returning; add/reuse a helper like clearAutoFilledAddress(form) and
call it on misses, and keep applyResolvedAddress(form, resolved) for successful
resolves.
- Around line 176-181: The postal-code lookup is incorrectly using an
auto-filled countryId (set by applyResolvedAddress) as if the user selected it;
change the logic so you only call postalCodeLookup.lookup(countryCode, ...) when
the country was present initially or explicitly changed by the user. Implement
this by tracking whether the country control was user-modified (e.g., check
control.dirty/wasUserChosen or add a boolean like countryManuallySelected set on
user change and cleared only when user resets), then update the switchMap that
calls getSelectedCountryCode(form) to ignore auto-filled country values unless
that manual flag is true; also apply the same fix to the duplicate lookup logic
around the other switchMap (the code referenced at lines ~233-239) so both paths
use the new manual-selection check instead of blindly using countryId from
applyResolvedAddress.
In `@src/app/shared/services/postal-code-lookup.service.ts`:
- Around line 33-44: The postal lookup currently always calls the third‑party
API via API_BASE in lookup(), which leaks client data; add a deployment opt‑in
toggle (e.g., a boolean config flag like useExternalPostalLookup) and
short‑circuit lookup() when disabled. Modify the PostalCodeLookupService to read
the flag from a configuration provider (environment, ConfigService, or injected
token) at construction, reference API_BASE and lookup() to check the flag first,
and if disabled return of(null) (or an appropriate Observable) instead of
calling externalHttp.get; ensure unit tests and callers handle the null/disabled
case.
- Around line 71-72: resolveCountryCode currently only looks up
COUNTRY_NAME_TO_ISO_CODE, so alias inputs like "United States of America"
(modeled in COUNTRY_NAME_ALIASES) return null; update
resolveCountryCode(countryName: string) to normalize the input (trim +
toLowerCase()), first check COUNTRY_NAME_ALIASES for an alias -> canonical name
mapping, and if found use that canonical name to look up
COUNTRY_NAME_TO_ISO_CODE; otherwise fall back to directly looking up the
normalized name in COUNTRY_NAME_TO_ISO_CODE and return null if still not found.
Ensure you reference resolveCountryCode, COUNTRY_NAME_ALIASES and
COUNTRY_NAME_TO_ISO_CODE in the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ca2d65b1-7303-44e1-bf8b-4dbb2e8357d2
📒 Files selected for processing (4)
src/app/clients/clients-view/address-tab/address-tab.component.tssrc/app/shared/constants/country-codes.tssrc/app/shared/models/postal-code-lookup.model.tssrc/app/shared/services/postal-code-lookup.service.ts
src/app/clients/clients-view/address-tab/address-tab.component.ts
Outdated
Show resolved
Hide resolved
a2ab8a2 to
5a17805
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/app/clients/clients-view/address-tab/address-tab.component.ts (1)
158-159:⚠️ Potential issue | 🟠 MajorTrack auto-filled values, not just field names.
Once a field is added to
autoFilledFields, later user edits are still treated as auto-filled. In practice that means a manual country change after a fallback hit is still ignored byisUserSelectedCountry, and a later miss can blank user-entered city/state/country. Storing the last auto-filled value per control is safer than storing only the control name.Suggested direction
- private autoFilledFields = new Set<string>(); + private autoFilledValues = new Map<string, unknown>(); ... - const isUserSelectedCountry = - currentCountryId && (currentCountryId === initialCountryId || !this.autoFilledFields.has('countryId')); + const autoFilledCountryId = this.autoFilledValues.get('countryId'); + const isUserSelectedCountry = + currentCountryId && + (currentCountryId === initialCountryId || currentCountryId !== autoFilledCountryId); ... - for (const fieldName of this.autoFilledFields) { + for (const [fieldName, autoFilledValue] of this.autoFilledValues) { const control = form.get(fieldName); - if (control) { + if (control?.value === autoFilledValue) { control.setValue(''); control.markAsDirty(); } } - this.autoFilledFields.clear(); + this.autoFilledValues.clear(); ... - this.autoFilledFields.add('city'); + this.autoFilledValues.set('city', address.city); ... - this.autoFilledFields.add('stateProvinceId'); + this.autoFilledValues.set('stateProvinceId', matched.id); ... - this.autoFilledFields.add('countryId'); + this.autoFilledValues.set('countryId', matched.id);Also applies to: 176-190, 244-252, 255-281
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/clients/clients-view/address-tab/address-tab.component.ts` around lines 158 - 159, The Set autoFilledFields currently only records control names so later user edits are treated as still auto-filled; replace it with a Map<string,string> (e.g. lastAutoFilledValues) that stores the last auto-filled value per control and update usages (including where autoFilledFields is mutated and checks like isUserSelectedCountry) to compare the control's current value to the stored auto-filled value before deciding it is auto-filled; ensure you update add/remove logic to set or delete the map entry with the auto-filled value (not just the key) and use equality checks against lastAutoFilledValues.get(controlName) wherever the code previously checked autoFilledFields.has(controlName).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/clients/clients-view/address-tab/address-tab.component.ts`:
- Around line 180-199: The postal code valueChanges filter currently drops
short/empty values so the postalSub subscribe never runs to clear auto-filled
fields; update the pipeline by removing the length check from the filter and
moving it inside the switchMap in postalSub (use postalCodeControl.valueChanges
-> debounceTime/distinctUntilChanged/filter only for non-null then in switchMap
check trimmed length: if length < 3 return of(null) so the downstream subscribe
always fires), keep the existing logic that chooses between
this.postalCodeLookup.lookup(...) and lookupWithFallback(...) when length >= 3,
and ensure the subscribe handler treats a null result as a signal to clear
autoFilledFields (countryId/city/state).
In `@src/assets/env.js`:
- Around line 115-117: The env default enables postal-code lookup by setting
window['env']['enablePostalCodeLookup'] = 'true' which contradicts the comment;
change the default to opt-in by setting window['env']['enablePostalCodeLookup']
= 'false' (matching the surrounding string-based env values) so the external
postal-code API is disabled unless explicitly overridden.
---
Duplicate comments:
In `@src/app/clients/clients-view/address-tab/address-tab.component.ts`:
- Around line 158-159: The Set autoFilledFields currently only records control
names so later user edits are treated as still auto-filled; replace it with a
Map<string,string> (e.g. lastAutoFilledValues) that stores the last auto-filled
value per control and update usages (including where autoFilledFields is mutated
and checks like isUserSelectedCountry) to compare the control's current value to
the stored auto-filled value before deciding it is auto-filled; ensure you
update add/remove logic to set or delete the map entry with the auto-filled
value (not just the key) and use equality checks against
lastAutoFilledValues.get(controlName) wherever the code previously checked
autoFilledFields.has(controlName).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a9d1479f-f714-4152-877f-459c76dc441f
📒 Files selected for processing (7)
src/app/clients/clients-view/address-tab/address-tab.component.tssrc/app/shared/constants/country-codes.tssrc/app/shared/models/postal-code-lookup.model.tssrc/app/shared/services/postal-code-lookup.service.tssrc/assets/env.jssrc/environments/environment.prod.tssrc/environments/environment.ts
✅ Files skipped from review due to trivial changes (2)
- src/app/shared/models/postal-code-lookup.model.ts
- src/app/shared/constants/country-codes.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/app/shared/services/postal-code-lookup.service.ts
…tam API improve improve
5a17805 to
2f5ffba
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (2)
src/app/clients/clients-view/address-tab/address-tab.component.ts (2)
238-258: Misplaced JSDoc comment.The JSDoc at lines 238-242 ("Applies the resolved address to the form fields...") describes
applyResolvedAddress, but it's positioned beforeclearAutoFilledFields. The actualapplyResolvedAddressmethod at line 258 has no documentation.📝 Suggested fix - reorder JSDoc
- /** - * Applies the resolved address to the form fields. - * Passes both full name and abbreviation to maximize match chances - * against Fineract's configured code values. - */ /** * Clears form fields that were previously set by auto-fill, * so stale data doesn't persist when a new lookup fails or returns different results. */ private clearAutoFilledFields(form: UntypedFormGroup) { // ... existing implementation } + /** + * Applies the resolved address to the form fields. + * Passes both full name and abbreviation to maximize match chances + * against Fineract's configured code values. + */ private applyResolvedAddress(form: UntypedFormGroup, address: ResolvedAddress) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/clients/clients-view/address-tab/address-tab.component.ts` around lines 238 - 258, The JSDoc describing "Applies the resolved address to the form fields..." is placed above clearAutoFilledFields but belongs to applyResolvedAddress; move that comment block so it directly precedes the applyResolvedAddress method declaration and remove the redundant empty JSDoc above clearAutoFilledFields, ensuring the comment references applyResolvedAddress and stays adjacent to the applyResolvedAddress(UntypedFormGroup, ResolvedAddress) method.
247-256: Redundant conditional insetValue.Line 251 has a ternary that evaluates to the same value on both branches:
control.setValue(fieldName === 'city' ? '' : '');This appears to be a copy-paste remnant. Simplify to:
- control.setValue(fieldName === 'city' ? '' : ''); + control.setValue('');🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/clients/clients-view/address-tab/address-tab.component.ts` around lines 247 - 256, In clearAutoFilledFields, the control.setValue call uses a redundant ternary; replace that expression with a single literal (e.g., control.setValue('')) so the loop simply sets each auto-filled control to the intended empty value, keeps control.markAsDirty(), and then clears this.autoFilledFields; reference: clearAutoFilledFields, this.autoFilledFields, control.setValue.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/app/clients/clients-view/address-tab/address-tab.component.ts`:
- Around line 238-258: The JSDoc describing "Applies the resolved address to the
form fields..." is placed above clearAutoFilledFields but belongs to
applyResolvedAddress; move that comment block so it directly precedes the
applyResolvedAddress method declaration and remove the redundant empty JSDoc
above clearAutoFilledFields, ensuring the comment references
applyResolvedAddress and stays adjacent to the
applyResolvedAddress(UntypedFormGroup, ResolvedAddress) method.
- Around line 247-256: In clearAutoFilledFields, the control.setValue call uses
a redundant ternary; replace that expression with a single literal (e.g.,
control.setValue('')) so the loop simply sets each auto-filled control to the
intended empty value, keeps control.markAsDirty(), and then clears
this.autoFilledFields; reference: clearAutoFilledFields, this.autoFilledFields,
control.setValue.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b31d2668-8347-4278-b62a-e28fb5e9238e
📒 Files selected for processing (7)
src/app/clients/clients-view/address-tab/address-tab.component.tssrc/app/shared/constants/country-codes.tssrc/app/shared/models/postal-code-lookup.model.tssrc/app/shared/services/postal-code-lookup.service.tssrc/assets/env.jssrc/environments/environment.prod.tssrc/environments/environment.ts
✅ Files skipped from review due to trivial changes (3)
- src/assets/env.js
- src/app/shared/constants/country-codes.ts
- src/app/shared/models/postal-code-lookup.model.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/environments/environment.ts
When entering a Postal Code in the client address dialog, the system now automatically queries the https://api.zippopotam.us API and auto-fills the City, State/Province, and Country fields. This reduces manual data entry errors and speeds up address capture for field officers.
Key design :
Note: State/Province and Country auto-fill only works when matching code values are configured in Fineract (Admin → System → Manage Codes under STATE
and COUNTRY). City always fills since it is a free-text field.
WEB-884
Screen.Recording.2026-04-07.183156.mp4
Please make sure these boxes are checked before submitting your pull request - thanks!
If you have multiple commits please combine them into one commit by squashing them.
Read and understood the contribution guidelines at
web-app/.github/CONTRIBUTING.md.Summary by CodeRabbit