Skip to content
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

Switch to libphonenumber-js; #9969

Closed

Conversation

rithviknishad
Copy link
Member

@rithviknishad rithviknishad commented Jan 14, 2025

Proposed Changes

@ohcnetwork/care-fe-code-reviewers

Merge Checklist

  • Add specs that demonstrate bug / test a new feature.
  • Update product documentation.
  • Ensure that UI text is kept in I18n files.
  • Prep screenshot or demo video for changelog entry, and attach it to issue.
  • Request for Peer Reviews
  • Completion of QA

Summary by CodeRabbit

  • New Features

    • Added libphonenumber-js library for enhanced phone number handling and validation
    • Introduced new PhoneInput component for improved phone number input
    • Updated phone number validation across multiple components
  • Bug Fixes

    • Improved phone number parsing and formatting
    • Enhanced phone number validation with more robust checks
  • Refactor

    • Removed custom phone number validation utilities
    • Simplified phone number handling across the application
    • Standardized phone number input and error handling
  • Documentation

    • Added phone number placeholder in English localization file

@rithviknishad rithviknishad requested a review from a team as a code owner January 14, 2025 16:39
Copy link
Contributor

coderabbitai bot commented Jan 14, 2025

Walkthrough

This pull request introduces a comprehensive overhaul of phone number handling across the application by migrating from custom phone number validation to the libphonenumber-js library. The changes involve removing custom phone number validators, constants, and form fields, and replacing them with a new PhoneInput component and standardized validation using libphonenumber-js. The implementation simplifies phone number input, validation, and formatting across multiple components.

Changes

File Change Summary
package.json Added libphonenumber-js dependency
public/locale/en.json Added phone number placeholder
src/Utils/utils.ts Removed phone number and pincode related functions
src/common/constants.tsx Removed area code constants
src/components/Form/FieldValidators.tsx Deleted entire file with phone number validation logic
src/components/ui/input-phone.tsx New phone input component added
Multiple form components Replaced PhoneNumberFormField with PhoneInput and updated validation

Assessment against linked issues

Objective Addressed Explanation
Replace PhoneNumberFormField
Delete custom phone number validator
Remove unused country phone and flags JSON

Possibly related PRs

Suggested labels

needs review, tested

Suggested reviewers

  • Jacobjeevan

Poem

🐰 A rabbit's tale of phone number glee,
Where validators dance and numbers run free!
With libphonenumber as our new friend,
Custom code complexity meets its end.
Validation magic, now simple and bright! 📱✨

Finishing Touches

  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

netlify bot commented Jan 14, 2025

Deploy Preview for care-ohc ready!

Name Link
🔨 Latest commit 4d2cb17
🔍 Latest deploy log https://app.netlify.com/sites/care-ohc/deploys/6786935ed653460008884b8b
😎 Deploy Preview https://deploy-preview-9969--care-ohc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

Deploying care-fe with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4d2cb17
Status: ✅  Deploy successful!
Preview URL: https://781ce8f3.care-fe.pages.dev
Branch Preview URL: https://rithviknishad-feat-switch-to.care-fe.pages.dev

View logs

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (3)
src/components/ui/input-phone.tsx (1)

16-21: Add proper accessibility attributes.

The input lacks proper accessibility attributes for screen readers.

     <Input
       type="tel"
       className={cn("pr-10", className)}
       ref={ref}
+      aria-label={props.placeholder ?? t("phone_number_placeholder")}
       {...props}
       maxLength={16}
src/components/Common/SearchByMultipleFields.tsx (1)

183-193: Use internationalization for the error message.

The error message "Invalid phone number" should be internationalized using the t function for consistency with the rest of the application.

Apply this diff to internationalize the error message:

-                isValidPhoneNumber(value) ? undefined : "Invalid phone number",
+                isValidPhoneNumber(value) ? undefined : t("invalid_phone"),
src/components/Facility/FacilityCreate.tsx (1)

88-89: Consider adding a custom error message.

While the validation logic is correct, consider providing a more descriptive error message that explains the expected phone number format.

 .refine(isValidPhoneNumber, t("invalid_phone_number"))
+.refine(isValidPhoneNumber, t("phone_number_format_description"))
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 27fef8d and 4d2cb17.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (20)
  • package.json (1 hunks)
  • public/locale/en.json (1 hunks)
  • src/Utils/utils.ts (0 hunks)
  • src/common/constants.tsx (0 hunks)
  • src/common/static/countryPhoneAndFlags.json (0 hunks)
  • src/common/validation.tsx (0 hunks)
  • src/components/Common/SearchByMultipleFields.tsx (3 hunks)
  • src/components/Facility/CreateFacilityForm.tsx (5 hunks)
  • src/components/Facility/FacilityCreate.tsx (5 hunks)
  • src/components/Form/FieldValidators.tsx (0 hunks)
  • src/components/Form/FormFields/FormField.tsx (1 hunks)
  • src/components/Form/FormFields/PhoneNumberFormField.tsx (0 hunks)
  • src/components/Form/FormFields/Utils.ts (1 hunks)
  • src/components/Form/Utils.ts (1 hunks)
  • src/components/Patient/PatientIndex.tsx (2 hunks)
  • src/components/Patient/PatientRegistration.tsx (3 hunks)
  • src/components/Resource/ResourceCreate.tsx (5 hunks)
  • src/components/Users/UserListAndCard.tsx (3 hunks)
  • src/components/ui/input-phone.tsx (1 hunks)
  • src/pages/PublicAppointments/auth/PatientLogin.tsx (4 hunks)
💤 Files with no reviewable changes (6)
  • src/common/validation.tsx
  • src/common/static/countryPhoneAndFlags.json
  • src/components/Form/FormFields/PhoneNumberFormField.tsx
  • src/components/Form/FieldValidators.tsx
  • src/Utils/utils.ts
  • src/common/constants.tsx
🧰 Additional context used
📓 Learnings (1)
src/components/Users/UserListAndCard.tsx (1)
Learnt from: Jacobjeevan
PR: ohcnetwork/care_fe#9864
File: src/components/Users/UserListAndCard.tsx:246-248
Timestamp: 2025-01-10T06:16:10.402Z
Learning: In the frontend codebase, the `formatPhoneNumber` utility function accepts undefined as input and returns "+91 " as the default output, making additional null checks unnecessary.
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: cypress-run (1)
  • GitHub Check: OSSAR-Scan
  • GitHub Check: OSSAR-Scan
  • GitHub Check: Cloudflare Pages: care-fe
🔇 Additional comments (14)
src/components/Form/Utils.ts (1)

3-3: LGTM! Simplified error handling approach.

The change from FieldError to string | undefined aligns well with the migration to libphonenumber-js, providing a more straightforward error handling mechanism.

Also applies to: 9-9

src/components/Form/FormFields/Utils.ts (1)

31-31: LGTM! Consistent error type update.

The change to string | undefined maintains consistency with the form utilities.

src/components/Patient/PatientIndex.tsx (1)

Line range hint 1-103: Verify consistent phone number validation across the codebase.

Let's ensure all phone number validations use libphonenumber-js consistently.

✅ Verification successful

Phone number validation is consistently implemented using libphonenumber-js

The codebase consistently uses libphonenumber-js for phone number validation, with appropriate functions chosen based on the use case:

  • parsePhoneNumberWithError for parsing and strict validation in search/data processing
  • isValidPhoneNumber for form validation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for potential phone number validation patterns that might not be using libphonenumber-js
rg -l "phone.*number" | xargs rg -l "validate|regex|pattern" | grep -v "node_modules"

# Search for imports of parsePhoneNumber to ensure complete migration
rg "import.*parsePhoneNumber(?!WithError)"

Length of output: 912


Script:

#!/bin/bash
# Search for libphonenumber-js imports
rg "from ['\"]libphonenumber-js['\"]"

# Check actual phone number validation logic in identified files
for file in src/components/Auth/Login.tsx src/components/Facility/FacilityCreate.tsx src/components/Patient/PatientRegistration.tsx src/components/Users/CreateUserForm.tsx src/pages/PublicAppointments/PatientRegistration.tsx src/pages/PublicAppointments/auth/PatientLogin.tsx; do
    echo "=== $file ==="
    rg "phone.*number.*=|phone.*number.*\+" "$file" -A 2 -B 2
done

Length of output: 5101

src/pages/PublicAppointments/auth/PatientLogin.tsx (2)

77-77: LGTM! Clean and efficient phone number validation.

The validation logic is concise and uses the reliable libphonenumber-js library with proper internationalization.


161-172: LGTM! Well-structured phone input implementation.

The implementation follows best practices:

  • Proper form field structure with label and error handling
  • Clear error state on value change
  • Consistent with the form component pattern
src/components/Facility/CreateFacilityForm.tsx (1)

263-263: LGTM! Clean phone input implementation.

The implementation follows the form field pattern and integrates well with the form structure.

src/components/Resource/ResourceCreate.tsx (3)

3-6: LGTM! Clean import of required libphonenumber-js functions.

The imports are correctly structured and only import the necessary functions.


407-410: LGTM! Clean migration to PhoneInput component.

The component replacement is correct and maintains the form integration through onValueChange.


84-85: Verify error message translation key.

The validation logic is correct, but ensure that the translation key 'invalid_phone_number' exists in the i18n files.

Run this script to check the translation key:

✅ Verification successful

Translation key 'invalid_phone_number' is properly configured

The key exists in the i18n files with translations in English, Tamil, Hindi, Kannada, and Malayalam.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the translation key exists in i18n files
rg -g '*.json' '"invalid_phone_number":'

Length of output: 395

src/components/Facility/FacilityCreate.tsx (2)

9-12: LGTM! Clean import of required libphonenumber-js functions.

The imports are correctly structured and only import the necessary functions.


178-178: LGTM! Clean request data handling.

The simplified request data handling is correct as the phone number transformation is now handled by the schema.

src/components/Form/FormFields/FormField.tsx (1)

32-32: LGTM! Simplified error type definition.

The change to use string | undefined instead of FieldError simplifies error handling and aligns with the form library's approach.

package.json (1)

104-104: LGTM! Verify the package version.

The addition of libphonenumber-js is appropriate for standardizing phone number handling. The library is well-maintained and widely used.

Run the following script to check for any security advisories and verify the latest version:

✅ Verification successful

Package version verified - all checks passed

  • Latest version (1.11.17) is being used
  • No security vulnerabilities found
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for security advisories and latest versions of the libphonenumber-js library.

# Check NPM for latest versions
curl -s https://registry.npmjs.org/libphonenumber-js | jq -r '.["dist-tags"].latest'

# Check for security advisories
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NPM, package: "libphonenumber-js") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Length of output: 445

public/locale/en.json (1)

1558-1558: LGTM! The phone number placeholder follows the correct format.

The placeholder +91xxxxxxxxxx clearly indicates the expected format for Indian phone numbers with the country code.

Comment on lines +22 to +23
value={props.value || "+91"}
placeholder={props.placeholder ?? t("phone_number_placeholder")}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider making the default country code configurable.

The hardcoded "+91" value limits the component's reusability for different regions. Consider:

  1. Making it a prop with "+91" as the default
  2. Using the user's locale to determine the default country code
-      value={props.value || "+91"}
+      value={props.value || props.defaultCountry || "+91"}

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +24 to +31
onChange={(e) => {
let value = e.target.value.replace(/[^\d+]/g, "");
if (value && !value.startsWith("+")) {
value = "+" + value;
}
props.onValueChange?.(value);
props.onChange?.(e);
}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance phone number validation and formatting.

The current implementation only handles basic input sanitization. Consider:

  1. Using parsePhoneNumberWithError for real-time validation
  2. Formatting the number as the user types
  3. Adding proper error handling for invalid numbers
       onChange={(e) => {
-        let value = e.target.value.replace(/[^\d+]/g, "");
-        if (value && !value.startsWith("+")) {
-          value = "+" + value;
-        }
-        props.onValueChange?.(value);
-        props.onChange?.(e);
+        try {
+          let value = e.target.value.replace(/[^\d+]/g, "");
+          if (value && !value.startsWith("+")) {
+            value = "+" + value;
+          }
+          const phoneNumber = parsePhoneNumberWithError(value);
+          const formattedValue = phoneNumber.format("INTERNATIONAL");
+          props.onValueChange?.(formattedValue);
+          e.target.value = formattedValue;
+          props.onChange?.(e);
+        } catch (error) {
+          props.onValueChange?.(e.target.value);
+          props.onChange?.(e);
+        }
       }}

Committable suggestion skipped: line range outside the PR's diff.

@@ -100,7 +100,7 @@ export default function PatientIndex({ facilityId }: { facilityId: string }) {
queryKey: ["patient-search", facilityId, phoneNumber],
queryFn: query.debounced(routes.searchPatient, {
body: {
phone_number: parsePhoneNumber(phoneNumber) || "",
phone_number: parsePhoneNumberWithError(phoneNumber).number.toString(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling for invalid phone numbers.

Direct usage of parsePhoneNumberWithError without error handling could throw runtime exceptions for invalid numbers.

-        phone_number: parsePhoneNumberWithError(phoneNumber).number.toString(),
+        phone_number: (() => {
+          try {
+            return parsePhoneNumberWithError(phoneNumber).number.toString();
+          } catch (error) {
+            return phoneNumber;
+          }
+        })(),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
phone_number: parsePhoneNumberWithError(phoneNumber).number.toString(),
phone_number: (() => {
try {
return parsePhoneNumberWithError(phoneNumber).number.toString();
} catch (error) {
return phoneNumber;
}
})(),

@@ -215,7 +211,7 @@ const UserListRow = ({ user }: { user: UserBase }) => {
{user.user_type}
</td>
<td id="contact" className="px-4 py-4 text-sm whitespace-nowrap">
{formatPhoneNumber(user.phone_number)}
{parsePhoneNumberWithError(user.phone_number).formatInternational()}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling for invalid phone numbers.

The current implementation may throw an error if the phone number is undefined or invalid. Consider adding error handling to gracefully handle such cases.

Apply this diff to add error handling:

-        {parsePhoneNumberWithError(user.phone_number).formatInternational()}
+        {(() => {
+          try {
+            return parsePhoneNumberWithError(user.phone_number).formatInternational();
+          } catch (error) {
+            return user.phone_number || t("not_available");
+          }
+        })()}

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +59 to +60
.refine(isValidPhoneNumber, "Invalid phone number")
.transform((value) => parsePhoneNumberWithError(value).number.toString()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance phone number validation and transformation.

The current implementation has two issues:

  1. The error message is not internationalized
  2. The transformation may throw an error for invalid phone numbers

Apply this diff to improve the implementation:

     .string()
-    .refine(isValidPhoneNumber, "Invalid phone number")
+    .refine(isValidPhoneNumber, t("invalid_phone"))
     .transform((value) => {
+      try {
         return parsePhoneNumberWithError(value).number.toString();
+      } catch (error) {
+        return value;
+      }
+    });

Committable suggestion skipped: line range outside the PR's diff.

@@ -759,7 +763,7 @@ export default function PatientRegistration(
</div>
{!patientPhoneSearch.isLoading &&
!!duplicatePatients?.length &&
!!parsePhoneNumber(debouncedNumber || "") &&
!!parsePhoneNumberWithError(debouncedNumber || "").number.toString() &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling for duplicate check.

The duplicate check could throw an error for invalid numbers.

-!!parsePhoneNumberWithError(debouncedNumber || "").number.toString() &&
+(() => {
+  try {
+    return !!parsePhoneNumberWithError(debouncedNumber || "").number.toString();
+  } catch {
+    return false;
+  }
+})() &&
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
!!parsePhoneNumberWithError(debouncedNumber || "").number.toString() &&
(() => {
try {
return !!parsePhoneNumberWithError(debouncedNumber || "").number.toString();
} catch {
return false;
}
})() &&

Comment on lines +232 to +234
phone_number: parsePhoneNumberWithError(
debouncedNumber || "",
).number.toString(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling for invalid phone numbers.

The phone number parsing could throw an error for invalid numbers. Consider adding try-catch.

-phone_number: parsePhoneNumberWithError(
-  debouncedNumber || "",
-).number.toString(),
+phone_number: (() => {
+  try {
+    return parsePhoneNumberWithError(debouncedNumber || "").number.toString();
+  } catch {
+    return "";
+  }
+})(),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
phone_number: parsePhoneNumberWithError(
debouncedNumber || "",
).number.toString(),
phone_number: (() => {
try {
return parsePhoneNumberWithError(debouncedNumber || "").number.toString();
} catch {
return "";
}
})(),

Comment on lines +237 to +239
enabled: !!parsePhoneNumberWithError(
debouncedNumber || "",
).number.toString(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling for query enablement.

Similar to above, the enablement check could throw an error.

-enabled: !!parsePhoneNumberWithError(
-  debouncedNumber || "",
-).number.toString(),
+enabled: (() => {
+  try {
+    return !!parsePhoneNumberWithError(debouncedNumber || "").number.toString();
+  } catch {
+    return false;
+  }
+})(),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
enabled: !!parsePhoneNumberWithError(
debouncedNumber || "",
).number.toString(),
enabled: (() => {
try {
return !!parsePhoneNumberWithError(debouncedNumber || "").number.toString();
} catch {
return false;
}
})(),

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Switch to libphonenumber-js in favour of custom phone number validators
1 participant