Skip to content

Conversation

sunilsabatp
Copy link
Contributor

@sunilsabatp sunilsabatp commented Dec 5, 2024

Tasks covered under this PR :

  1. Moved the translation resources (profile.json -> editProfile.json).
  2. Grouped translation keys by usage for better organization.
  3. Removed the SCSS variable declarations.

- move addressManagement translation resources (profile.json -> editProfile.json).
Copy link

vercel bot commented Dec 5, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
planet-webapp-multi-tenancy-setup ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 6, 2024 5:07am
2 Skipped Deployments
Name Status Preview Comments Updated (UTC)
planet-webapp ⬜️ Ignored (Inspect) Dec 6, 2024 5:07am
planet-webapp-temp ⬜️ Ignored (Inspect) Dec 6, 2024 5:07am

Copy link
Contributor

coderabbitai bot commented Dec 5, 2024

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This pull request introduces several enhancements across multiple files, focusing on type safety, component organization, and address management functionalities. Notable changes include the addition of new components for address handling, updates to existing components for improved type imports, and modifications to styling files. The edit.tsx file has been refined to enhance type handling and optional chaining, while new components like AddAddress, EditAddress, and DeleteAddress streamline address management within user profiles. Additionally, utility functions for address management have been encapsulated in a new file.

Changes

File Change Summary
pages/sites/[slug]/[locale]/profile/edit.tsx Reorganized type imports, adjusted React import, updated useEffect, added optional chaining in getStaticPaths, and modified filenames in getStaticProps.
public/assets/images/icons/KebabMenuIcon.tsx Introduced a new functional component KebabMenuIcon that renders an SVG kebab menu icon.
src/features/common/InputTypes/AutoCompleteCountry.tsx Reorganized type imports, removed unnecessary React import, added type assertions in useEffect, and updated sorting logic for country names.
src/features/common/types/profile.d.ts Converted standard imports to type imports, added new type import for ADDRESS_ACTIONS, and defined a new type AddressAction.
src/features/user/Profile/ForestProgress/ForestProgress.module.scss Modified CSS properties for layout, specifically adjusting the transform property in .targetModalMainContainer.
src/features/user/Settings/EditProfile/AddressManagment/AddAddress.tsx Introduced a new component for adding addresses, managing state and API interactions for address submission.
src/features/user/Settings/EditProfile/AddressManagment/AddressManagement.module.scss New styles defined for address management components, including mixins and responsive design elements.
src/features/user/Settings/EditProfile/AddressManagment/DeleteAddress.tsx Introduced a new component for deleting addresses, implementing modal handling and API interaction.
src/features/user/Settings/EditProfile/AddressManagment/EditAddress.tsx Introduced a new component for editing addresses, managing state and API interactions for address updates.
src/features/user/Settings/EditProfile/AddressManagment/UpdateAddressType.tsx Introduced a new component for managing address types within user profiles.
src/features/user/Settings/EditProfile/AddressManagment/index.tsx Added AddressManagement component to manage user addresses in profile settings.
src/features/user/Settings/EditProfile/AddressManagment/microComponents/AddressActionMenu.tsx Introduced a new component for managing address actions with a popover menu interface.
src/features/user/Settings/EditProfile/AddressManagment/microComponents/AddressDetails.tsx Introduced a new component for displaying address details.
src/features/user/Settings/EditProfile/AddressManagment/microComponents/AddressForm.tsx Introduced a new component for managing address input within a form, incorporating validation and suggestions.
src/features/user/Settings/EditProfile/AddressManagment/microComponents/AddressFormButtons.tsx Introduced a new component for rendering form buttons for address management.
src/features/user/Settings/EditProfile/AddressManagment/microComponents/AddressFormLayout.tsx Introduced a new layout component for address forms.
src/features/user/Settings/EditProfile/AddressManagment/microComponents/AddressInput.tsx Introduced a new component for managing address input with validation and suggestions.
src/features/user/Settings/EditProfile/AddressManagment/microComponents/AddressList.tsx Introduced a new component for displaying a list of addresses.
src/features/user/Settings/EditProfile/AddressManagment/microComponents/FormattedAddressBlock.tsx Introduced a new component for displaying formatted address details.
src/features/user/Settings/EditProfile/AddressManagment/microComponents/SingleAddress.tsx Introduced a new component for managing display and actions related to a single address.
src/features/user/Settings/EditProfile/EditProfileForm.tsx Removed address handling and country selection features from the form, streamlining the user experience.
src/features/user/Settings/EditProfile/index.tsx Updated to include the new AddressManagement component and modified return type for EditProfile.
src/utils/addressManagement.ts Introduced utility functions and constants for address management, including geocoding integration and address formatting functions.

Possibly related PRs

Suggested labels

projects redesign, MyForest 2.0

Suggested reviewers

  • mariahosfeld

🐰 In the garden, we hop and play,
With new addresses to save the day!
A form to fill, a button to press,
Managing homes, oh what a success!
With styles and types, we dance with cheer,
Address management is finally here! 🌼


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 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
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.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.

Actionable comments posted: 17

🧹 Outside diff range and nitpick comments (24)
src/features/user/Settings/EditProfile/AddressManagment/microComponents/AddressFormButtons.tsx (4)

1-3: Consider restructuring the common components directory.

The WebappButton component is accessed through a deep relative path (../../../../../common/WebappButton). Since it's a common component, consider moving it to a more accessible location (e.g., @components/common or @common) and using absolute imports to improve maintainability and reduce the complexity of import paths.


5-9: Add JSDoc documentation to the Props interface.

While the interface is well-typed, adding JSDoc documentation would improve developer experience by providing clear descriptions of each prop's purpose and usage.

+/**
+ * Props for the AddressFormButtons component
+ */
 interface Props {
+  /** Text to display on the submit button */
   text: string;
+  /** Handler function for form submission */
   handleSubmit: () => void;
+  /** Handler function for cancellation */
   handleCancel: () => void;
 }

20-21: Consider using more generic class names for better reusability.

The button class names (cancelButton and addAddressButton) are specific to address management. Since this is a reusable micro-component, consider using more generic class names like secondaryButton and primaryButton.

-        buttonClasses={styles.cancelButton}
+        buttonClasses={styles.secondaryButton}

-        buttonClasses={styles.addAddressButton}
+        buttonClasses={styles.primaryButton}

Also applies to: 27-28


11-31: Consider adding error handling and prop validation.

As this is a reusable component, consider:

  1. Wrapping the component with an error boundary to gracefully handle rendering errors
  2. Adding prop validation to ensure required props are provided with appropriate values

Example implementation:

import { ErrorBoundary } from 'react-error-boundary';

const AddressFormButtons = ({ text, handleSubmit, handleCancel }: Props) => {
  // Validate props
  if (!text) {
    throw new Error('AddressFormButtons: text prop is required');
  }

  // ... rest of the component
};

// Usage with error boundary
export default function AddressFormButtonsWithErrorBoundary(props: Props) {
  return (
    <ErrorBoundary fallback={<div>Something went wrong</div>}>
      <AddressFormButtons {...props} />
    </ErrorBoundary>
  );
}
src/features/user/Settings/EditProfile/AddressManagment/UpdateAddressType.tsx (2)

17-24: Add JSDoc comments to document the Props interface.

While the props are well-typed, adding JSDoc comments would improve documentation and provide better IDE hints.

+/**
+ * Props for the UpdateAddressType component
+ */
 interface Props {
+  /** Type of address to be set */
   addressType: 'primary' | 'mailing';
+  /** Function to control modal visibility */
   setIsModalOpen: SetState<boolean>;
+  /** Current user address if it exists */
   userAddress: Address | undefined;
+  /** Address selected for the update action */
   selectedAddressForAction: Address | null;
+  /** Callback to refresh user addresses */
   updateUserAddresses: () => Promise<void>;
+  /** Function to set the current address action */
   setAddressAction: SetState<AddressAction | null>;
 }

68-108: Enhance accessibility with ARIA labels and roles.

While the UI structure is clean, adding ARIA attributes would improve accessibility.

-    <div className={styles.addressActionContainer}>
+    <div 
+      className={styles.addressActionContainer}
+      role="dialog"
+      aria-labelledby="address-type-header"
+      aria-describedby="address-type-description"
+    >
-      <h2 className={styles.header}>
+      <h2 id="address-type-header" className={styles.header}>
         {tAddressManagement(`addressType.${addressType}`)}
       </h2>
-      <p>
+      <p id="address-type-description">
         {tAddressManagement('addressPrompts.setAddressConfirmation', {
           addressType: tAddressManagement(`addressType.${addressType}`),
         })}
src/features/user/Settings/EditProfile/AddressManagment/microComponents/AddressForm.tsx (3)

79-80: Remove unused dependencies in useCallback hooks

In the dependencies array of your useCallback hooks for handleSuggestAddress and handleGetAddress, geocoder is included but not used within the functions. Including unused dependencies can lead to unnecessary re-creations of the functions.

Apply this diff to remove geocoder from the dependencies:

  ),
-   [geocoder, country]
+   [country]
  );
  ),
-   [geocoder, setValue]
+   [setValue]
  );

Also applies to: 101-102


136-140: Remove unnecessary 'required' validation message for optional field

The address2 field is optional, but the validation messages include a 'required' message. This could cause confusion or display unnecessary error messages.

Apply this diff to remove the 'required' validation message:

  validationMessages={{
-   required: t('validationErrors.addressRequired'),
    invalid: t('validationErrors.addressInvalid'),
  }}

95-98: Include 'state' when setting address details from fetched data

When updating form values with fetched address details in handleGetAddress, you set the address, city, and zipCode fields but omit the state. If the state information is available, it should be included to provide a complete address.

Modify the code to set the state value:

    async (value: string) => {
      const details = await fetchAddressDetails(value);
      if (details) {
        setValue('address', details.address, { shouldValidate: true });
        setValue('city', details.city, { shouldValidate: true });
        setValue('zipCode', details.zipCode, { shouldValidate: true });
+       setValue('state', details.state, { shouldValidate: true });
      }
src/features/user/Settings/EditProfile/AddressManagment/index.tsx (1)

172-172: Remove unnecessary Fragment wrapper around 'renderModalContent'

The Fragment component wrapping renderModalContent is unnecessary since it contains only a single child. Removing it simplifies the code without impacting functionality.

Apply this diff to remove the unnecessary Fragment:

-          <>{renderModalContent}</>
+          {renderModalContent}
🧰 Tools
🪛 Biome (1.9.4)

[error] 172-172: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

src/features/user/Settings/EditProfile/AddressManagment/microComponents/FormattedAddressBlock.tsx (1)

21-26: Consider adding semantic HTML improvements

While using the address tag is semantically correct, consider adding ARIA labels for better accessibility.

-    <address>
+    <address aria-label="User address details">
       <p>{address}</p>
       {address2 !== null && <p>{address2}</p>}
       <p>{cityStatePostalString}</p>
     </address>
src/features/user/Settings/EditProfile/AddressManagment/microComponents/AddressList.tsx (3)

21-21: Consider using optional chaining consistently

While using nullish coalescing is good, the optional chaining on addresses?.length seems unnecessary since addresses is required by the Props interface.

-const addressCount = addresses?.length ?? 0;
+const addressCount = addresses.length;

24-35: Add empty state handling and error boundary

The component should handle empty states and potential rendering errors gracefully.

+  if (addressCount === 0) {
+    return (
+      <div className={styles.addressListContainer}>
+        <p>No addresses found</p>
+      </div>
+    );
+  }
+
   return (
     <div className={styles.addressListContainer}>
       {addresses.map((address) => (
+        address && (
         <SingleAddress
           key={address.id}
           userAddress={address}
           addressCount={addressCount}
           setAddressAction={setAddressAction}
           setIsModalOpen={setIsModalOpen}
           setSelectedAddressForAction={setSelectedAddressForAction}
         />
+        )
       ))}
     </div>
   );

8-13: Consider adding JSDoc comments for better documentation

The Props interface would benefit from JSDoc comments explaining the purpose of each prop.

+/**
+ * Props for the AddressList component
+ * @interface Props
+ * @property {Address[]} addresses - List of user addresses
+ * @property {SetState<AddressAction | null>} setAddressAction - Callback to set the current address action
+ * @property {SetState<Address | null>} setSelectedAddressForAction - Callback to set the selected address
+ * @property {SetState<boolean>} setIsModalOpen - Callback to control modal visibility
+ */
interface Props {
  addresses: Address[];
  setAddressAction: SetState<AddressAction | null>;
  setSelectedAddressForAction: SetState<Address | null>;
  setIsModalOpen: SetState<boolean>;
}
src/features/user/Settings/EditProfile/AddressManagment/microComponents/SingleAddress.tsx (2)

1-15: Fix typo in directory name

The directory name contains a typo: "AddressManagment" should be "AddressManagement". This affects import paths and could cause confusion.


9-15: Add validation for addressCount prop

Consider adding runtime validation to ensure addressCount is a positive number, as negative values wouldn't make sense in this context.

Example validation:

 interface Props {
   userAddress: Address;
-  addressCount: number;
+  addressCount: number & { __brand: 'PositiveNumber' };
   setIsModalOpen: SetState<boolean>;
   setAddressAction: SetState<AddressAction | null>;
   setSelectedAddressForAction: SetState<Address | null>;
 }
+
+// Type guard for positive numbers
+const isPositiveNumber = (n: number): n is number & { __brand: 'PositiveNumber' } => n > 0;
src/features/user/Settings/EditProfile/AddressManagment/microComponents/AddressInput.tsx (1)

47-52: Add error handling for invalid suggestion objects

The getOptionLabel function could throw if an invalid object is passed. Consider adding error handling.

 const getOptionLabel = (option: string | AddressOption): string => {
   if (typeof option === 'string') {
     return option;
   }
-  return 'text' in option ? option.text : '';
+  try {
+    if (!option || typeof option !== 'object') {
+      console.warn('Invalid option object received:', option);
+      return '';
+    }
+    return 'text' in option ? option.text : '';
+  } catch (error) {
+    console.error('Error processing option:', error);
+    return '';
+  }
 };
src/features/user/Settings/EditProfile/AddressManagment/DeleteAddress.tsx (2)

16-21: Props interface looks good but could be more strict

The Props interface is well-defined, but addressId being optional (undefined) could lead to runtime issues if not properly handled.

Consider making addressId required and handling the undefined case at a higher level:

interface Props {
  setIsModalOpen: SetState<boolean>;
-  addressId: string | undefined;
+  addressId: string;
  updateUserAddresses: () => Promise<void>;
  setAddressAction: SetState<AddressAction | null>;
}

67-81: Improve accessibility of action buttons

The button container needs ARIA labels for better screen reader support.

-<div className={styles.buttonContainer}>
+<div className={styles.buttonContainer} role="group" aria-label="Address deletion actions">
   <WebappButton
     text={tCommon('cancel')}
     elementType="button"
     variant="secondary"
     onClick={handleCancel}
+    aria-label={tCommon('cancel')}
   />
   <WebappButton
     text={tAddressManagement('deleteAction.delete')}
     elementType="button"
     variant="primary"
     onClick={deleteAddress}
+    aria-label={tAddressManagement('deleteAction.delete')}
   />
 </div>
src/features/user/Settings/EditProfile/AddressManagment/EditAddress.tsx (1)

47-88: Optimize useCallback dependencies

The useCallback hook includes unnecessary dependencies that could cause unnecessary re-renders:

  1. handleError and putAuthenticatedRequest are stable and don't need to be dependencies
  2. Some dependencies could be memoized at a higher level
  useCallback(
    async (data: FormData) => {
      // ... function implementation ...
    },
    [
      contextLoaded,
      user,
      token,
      country,
      selectedAddressForAction?.type,
      selectedAddressForAction?.id,
      tenantConfig.id,
      logoutUser,
      updateUserAddresses,
-     handleError,
-     putAuthenticatedRequest,
    ]
  );
src/features/user/Settings/EditProfile/AddressManagment/microComponents/AddressActionMenu.tsx (1)

90-104: Improve Popover accessibility and positioning

The Popover component needs additional ARIA attributes and better positioning logic.

<Popover
  id={id}
  open={open}
  anchorEl={popoverAnchor}
  onClose={closePopover}
+ role="menu"
+ aria-label={tAddressManagement('actions.menuLabel')}
  anchorOrigin={{
    vertical: 'bottom',
    horizontal: 'left',
  }}
+ transformOrigin={{
+   vertical: 'top',
+   horizontal: 'left',
+ }}
  sx={{
    '& .MuiPaper-root': {
      borderRadius: '12px',
+     marginTop: '4px',
    },
  }}
>
src/features/user/Settings/EditProfile/AddressManagment/AddAddress.tsx (1)

18-38: Consider improving type safety in FormData interface.

The type definitions could be more precise to prevent potential runtime issues.

Consider this improvement:

 export type FormData = {
-  address: string | undefined;
-  address2: string | null;
-  city: string | undefined;
-  zipCode: string | undefined;
-  state: string | null;
+  address: string;
+  address2: string | undefined;
+  city: string;
+  zipCode: string;
+  state: string | undefined;
 };

This change:

  • Makes required fields non-optional
  • Standardizes optional fields to use undefined instead of mixing with null
src/features/common/InputTypes/AutoCompleteCountry.tsx (1)

71-72: Consider using a type-safe translation key approach.

The current implementation uses type assertions for country codes, but a more robust solution would be to ensure type safety at the translation key level.

Consider creating a type-safe translation utility:

type CountryTranslationKey = `country.${Lowercase<CountryCode>}`;

function getCountryTranslationKey(code: CountryCode): CountryTranslationKey {
  return `country.${code.toLowerCase()}` as CountryTranslationKey;
}

// Usage:
const label = t(getCountryTranslationKey(countryCode));

This approach would provide compile-time safety for translation keys without needing type assertions.

Also applies to: 96-97, 107-107

src/features/user/Settings/EditProfile/AddressManagment/AddressManagement.module.scss (1)

177-200: Consider improving mobile responsiveness implementation.

The mobile styles could be more maintainable and accessible.

  1. Consider using CSS variables for breakpoints:
:root {
  --breakpoint-xs: 576px;
  --min-modal-width: 320px;
}

@media (max-width: var(--breakpoint-xs)) {
  .addressFormLayout {
    min-width: var(--min-modal-width);
    // ...
  }
}
  1. Add touch target sizes for mobile:
 .buttonContainer {
   @include flex-container(column);
   
   button {
     width: 100%;
+    min-height: 44px; // iOS minimum touch target size
   }
 }
🛑 Comments failed to post (17)
src/features/user/Settings/EditProfile/AddressManagment/UpdateAddressType.tsx (1)

41-63: 🛠️ Refactor suggestion

Add validation and improve error handling in updateAddress function.

While the function handles the happy path well, it could be improved in terms of validation and error handling.

 const updateAddress = async (addressType: 'primary' | 'mailing') => {
   if (!contextLoaded || !user || !token) return;
+  if (!selectedAddressForAction?.id) {
+    setErrors([{ message: 'No address selected for update' }]);
+    return;
+  }
   setIsUploadingData(true);
   const bodyToSend = {
     type: addressType,
   };
   try {
     const res = await putAuthenticatedRequest<Address>(
       tenantConfig.id,
       `/app/addresses/${selectedAddressForAction?.id}`,
       bodyToSend,
       token,
       logoutUser
     );
-    if (res) updateUserAddresses();
+    if (res) {
+      await updateUserAddresses();
+    } else {
+      setErrors([{ message: 'Failed to update address' }]);
+    }
   } catch (error) {
     setErrors(handleError(error as APIError));
   } finally {
📝 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.

  const updateAddress = async (addressType: 'primary' | 'mailing') => {
    if (!contextLoaded || !user || !token) return;
    if (!selectedAddressForAction?.id) {
      setErrors([{ message: 'No address selected for update' }]);
      return;
    }
    setIsUploadingData(true);
    const bodyToSend = {
      type: addressType,
    };
    try {
      const res = await putAuthenticatedRequest<Address>(
        tenantConfig.id,
        `/app/addresses/${selectedAddressForAction?.id}`,
        bodyToSend,
        token,
        logoutUser
      );
      if (res) {
        await updateUserAddresses();
      } else {
        setErrors([{ message: 'Failed to update address' }]);
      }
    } catch (error) {
      setErrors(handleError(error as APIError));
    } finally {
      setIsUploadingData(false);
      setIsModalOpen(false);
      setAddressAction(null);
    }
  };
src/features/user/Settings/EditProfile/AddressManagment/microComponents/AddressForm.tsx (1)

209-215: 🛠️ Refactor suggestion

Integrate 'country' field with form control for consistency

The CountrySelect component is handling its own state separately from react-hook-form, which may lead to inconsistencies in validation and form submission behavior compared to other fields.

Consider using the Controller component to integrate the country field with react-hook-form:

- <CountrySelect
-   countries={allCountries}
-   label={t('fieldLabels.country')}
-   name="country"
-   defaultValue={country}
-   onChange={setCountry}
- />
+ <Controller
+   name="country"
+   control={control}
+   defaultValue={country}
+   rules={{
+     required: t('validationErrors.countryRequired'),
+   }}
+   render={({ field: { onChange, value, onBlur } }) => (
+     <CountrySelect
+       countries={allCountries}
+       label={t('fieldLabels.country')}
+       value={value}
+       onChange={(newValue) => {
+         onChange(newValue);
+         setCountry(newValue);
+       }}
+       onBlur={onBlur}
+     />
+   )}
+ />

This integrates country into the form's control flow, ensuring consistent validation and state management.

📝 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.

        <Controller
          name="country"
          control={control}
          defaultValue={country}
          rules={{
            required: t('validationErrors.countryRequired'),
          }}
          render={({ field: { onChange, value, onBlur } }) => (
            <CountrySelect
              countries={allCountries}
              label={t('fieldLabels.country')}
              value={value}
              onChange={(newValue) => {
                onChange(newValue);
                setCountry(newValue);
              }}
              onBlur={onBlur}
            />
          )}
        />
src/utils/addressManagement.ts (1)

8-8: ⚠️ Potential issue

Correct the class name 'GeocoderArcGIs' to 'GeocoderArcGIS'

The imported class name GeocoderArcGIs is misspelled. It should be GeocoderArcGIS, with 'GIS' in uppercase letters.

Apply this diff to correct the class name:

-import GeocoderArcGIs from 'geocoder-arcgis';
+import GeocoderArcGIS from 'geocoder-arcgis';

And update the class instantiation:

-export const geocoder = new GeocoderArcGIs(
+export const geocoder = new GeocoderArcGIS(

Also applies to: 59-59

src/features/user/Settings/EditProfile/AddressManagment/index.tsx (3)

43-49: ⚠️ Potential issue

Avoid mutating state directly when sorting 'userAddresses'

The sort method mutates the array in place. Since userAddresses is a state variable, mutating it directly can lead to unexpected behavior. Create a copy of the array before sorting to prevent direct mutation of state.

Apply this diff to create a new array before sorting:

-    return userAddresses.sort((a, b) => {
+    return [...userAddresses].sort((a, b) => {
       return (
         addressTypeOrder.indexOf(a.type) - addressTypeOrder.indexOf(b.type)
       );
     });
📝 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.

  const sortedAddresses = useMemo(() => {
    return [...userAddresses].sort((a, b) => {
      return (
        addressTypeOrder.indexOf(a.type) - addressTypeOrder.indexOf(b.type)
      );
    });
  }, [userAddresses]);

51-64: ⚠️ Potential issue

Include 'setErrors' in the dependency array of 'updateUserAddresses'

The setErrors function is used within updateUserAddresses but is not included in the dependency array of the useCallback hook. To ensure consistent behavior and prevent potential issues with stale closures, include setErrors in the dependency array.

Apply this diff to add setErrors to the dependency array:

   }, [
     user,
     token,
     contextLoaded,
     tenantConfig.id,
     logoutUser,
+    setErrors,
   ]);
📝 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.

  const updateUserAddresses = useCallback(async () => {
    if (!user || !token || !contextLoaded) return;
    try {
      const res = await getAuthenticatedRequest<Address[]>(
        tenantConfig.id,
        '/app/addresses',
        token,
        logoutUser
      );
      if (res) setUserAddresses(res);
    } catch (error) {
      setErrors(handleError(error as APIError));
    }
  }, [user, token, contextLoaded, tenantConfig.id, logoutUser, setErrors]);

79-138: ⚠️ Potential issue

Add 'setAddressAction' to the dependency array of 'renderModalContent'

The setAddressAction function is used within the renderModalContent useMemo but is not included in the dependency array. This could lead to inconsistencies if setAddressAction changes. Include it in the dependency array to ensure proper memoization.

Apply this diff to add setAddressAction to the dependency array:

   }, [
     setIsModalOpen,
     setUserAddresses,
     selectedAddressForAction,
     updateUserAddresses,
     primaryAddress,
     billingAddress,
     addressAction,
+    setAddressAction,
   ]);
📝 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.

  const renderModalContent = useMemo(() => {
    switch (addressAction) {
      case ADDRESS_ACTIONS.ADD:
        return (
          <AddAddress
            setIsModalOpen={setIsModalOpen}
            setUserAddresses={setUserAddresses}
            setAddressAction={setAddressAction}
          />
        );
      case ADDRESS_ACTIONS.EDIT:
        return (
          <EditAddress
            setIsModalOpen={setIsModalOpen}
            selectedAddressForAction={selectedAddressForAction}
            updateUserAddresses={updateUserAddresses}
            setAddressAction={setAddressAction}
          />
        );
      case ADDRESS_ACTIONS.DELETE:
        return (
          <DeleteAddress
            addressId={selectedAddressForAction?.id}
            setIsModalOpen={setIsModalOpen}
            updateUserAddresses={updateUserAddresses}
            setAddressAction={setAddressAction}
          />
        );
      case ADDRESS_ACTIONS.SET_PRIMARY:
        return (
          <UpdateAddressType
            addressType={ADDRESS_TYPE.PRIMARY}
            userAddress={primaryAddress}
            setAddressAction={setAddressAction}
            setIsModalOpen={setIsModalOpen}
            selectedAddressForAction={selectedAddressForAction}
            updateUserAddresses={updateUserAddresses}
          />
        );
      case ADDRESS_ACTIONS.SET_BILLING:
        return (
          <UpdateAddressType
            addressType={ADDRESS_TYPE.MAILING}
            userAddress={billingAddress}
            setAddressAction={setAddressAction}
            setIsModalOpen={setIsModalOpen}
            selectedAddressForAction={selectedAddressForAction}
            updateUserAddresses={updateUserAddresses}
          />
        );
    }
  }, [
    setIsModalOpen,
    setUserAddresses,
    selectedAddressForAction,
    updateUserAddresses,
    primaryAddress,
    billingAddress,
    addressAction,
    setAddressAction,
  ]);
src/features/user/Settings/EditProfile/AddressManagment/microComponents/AddressFormLayout.tsx (1)

1-7: 🛠️ Refactor suggestion

Type 'children' prop as 'ReactNode' instead of 'JSX.Element'

The children prop should be typed as ReactNode to accommodate all possible types of React children, including strings, numbers, null, or arrays of elements.

Apply this diff to update the import and type definition:

-import type { JSX } from 'react';
+import type { ReactNode } from 'react';

 interface Props {
-  children: JSX.Element;
+  children: ReactNode;
   label: string;
 }
📝 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.

import type { ReactNode } from 'react';
import styles from '../AddressManagement.module.scss';

interface Props {
  children: ReactNode;
  label: string;
}
src/features/user/Settings/EditProfile/index.tsx (1)

6-6: 🛠️ Refactor suggestion

Correct the directory name 'AddressManagment' to 'AddressManagement'

The import path includes a misspelled directory name AddressManagment. It should be AddressManagement to reflect correct spelling and maintain consistency across the project.

Update the import statement and rename the directory accordingly:

-import AddressManagement from './AddressManagment';
+import AddressManagement from './AddressManagement';

Ensure all references and import paths are updated to match the corrected directory name.

📝 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.

import AddressManagement from './AddressManagement';
src/features/user/Settings/EditProfile/AddressManagment/microComponents/FormattedAddressBlock.tsx (1)

15-15: 🛠️ Refactor suggestion

Consider improving type safety of country code conversion

The type assertion as Lowercase<CountryCode> might be unsafe. Consider validating the country code before conversion.

-const countryName = tCountry(country.toLowerCase() as Lowercase<CountryCode>);
+const lowerCountry = country.toLowerCase();
+const isValidCountry = (code: string): code is Lowercase<CountryCode> => {
+  return /^[a-z]{2}$/.test(code);
+};
+const countryName = isValidCountry(lowerCountry) 
+  ? tCountry(lowerCountry)
+  : country;
📝 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.

  const lowerCountry = country.toLowerCase();
  const isValidCountry = (code: string): code is Lowercase<CountryCode> => {
    return /^[a-z]{2}$/.test(code);
  };
  const countryName = isValidCountry(lowerCountry) 
    ? tCountry(lowerCountry)
    : country;
src/features/user/Settings/EditProfile/AddressManagment/microComponents/AddressInput.tsx (3)

13-13: 🛠️ Refactor suggestion

Improve type safety of form Control

The Control<any> type reduces type safety. Consider using a generic type parameter for the form values.

-interface AddressInputProps {
+interface AddressInputProps<T extends Record<string, any>> {
   name: string;
-  control: Control<any>;
+  control: Control<T>;
   // ...
 }

-const AddressInput: React.FC<AddressInputProps> = ({
+const AddressInput = <T extends Record<string, any>>({
   // ...props
-}: AddressInputProps) => {
+}: AddressInputProps<T>) => {

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


6-9: 🛠️ Refactor suggestion

Improve type safety of AddressOption interface

The interface uses [key: string]: any which reduces type safety. Consider defining specific optional properties instead.

 interface AddressOption {
   text: string;
-  [key: string]: any;
+  id?: string;
+  details?: {
+    street?: string;
+    city?: string;
+    country?: string;
+  };
 }

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


63-66: 🛠️ Refactor suggestion

Add debouncing to onInputChange callback

The onInputChange callback is called on every input change, which could lead to excessive API calls if used for address suggestions. Consider adding debouncing.

+import { useCallback } from 'react';
+import debounce from 'lodash/debounce';

 const AddressInput: React.FC<AddressInputProps> = ({
   // ...props
 }) => {
+  const debouncedInputChange = useCallback(
+    debounce((value: string) => {
+      onInputChange?.(value);
+    }, 300),
+    [onInputChange]
+  );
+
   return (
     <Controller
       // ...
       render={({ field, fieldState: { error } }) => (
         <Autocomplete
           // ...
           onInputChange={(_, newValue) => {
             field.onChange(newValue);
-            onInputChange?.(newValue);
+            debouncedInputChange(newValue);
           }}

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

src/features/user/Settings/EditProfile/AddressManagment/DeleteAddress.tsx (1)

36-54: 🛠️ Refactor suggestion

Enhance error handling and user feedback

The deleteAddress function could benefit from additional error handling and user feedback:

  1. No validation of addressId before API call
  2. No specific error message for different failure scenarios
  3. No success feedback to user

Consider implementing these improvements:

const deleteAddress = async () => {
-  if (!contextLoaded || !user || !token) return;
+  if (!contextLoaded || !user || !token || !addressId) {
+    setErrors([{ message: 'Unable to delete address: missing required data' }]);
+    return;
+  }
  try {
    setIsLoading(true);
    await deleteAuthenticatedRequest(
      tenantConfig.id,
      `/app/addresses/${addressId}`,
      token,
      logoutUser
    );
    updateUserAddresses();
+   // Consider showing success toast/notification
  } catch (error) {
-   setErrors(handleError(error as APIError));
+   const apiError = error as APIError;
+   setErrors(handleError(apiError));
+   // Log error for monitoring
+   console.error('Failed to delete address:', apiError);
  } finally {

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

src/features/user/Settings/EditProfile/AddressManagment/EditAddress.tsx (1)

64-66: 🛠️ Refactor suggestion

Improve type safety of API response handling

The response handling could be more robust with type checking.

-  if (res && updateUserAddresses) {
+  if (res?.id && updateUserAddresses) {
     updateUserAddresses();
   }

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

src/features/user/Settings/EditProfile/AddressManagment/microComponents/AddressActionMenu.tsx (1)

106-118: 🛠️ Refactor suggestion

Enhance accessibility and keyboard navigation

The menu items need improved accessibility support:

  1. Missing keyboard navigation
  2. Incomplete ARIA attributes
  3. Improper use of role="button" on list items
{addressActionConfig.map((item, key) => {
  if (!item.shouldRender) return;
  return (
    <li
      key={key}
      className={styles.action}
-     onClick={() => handleActionClick(item.action)}
-     role="button"
    >
+     <button
+       onClick={() => handleActionClick(item.action)}
+       className={styles.actionButton}
+       role="menuitem"
+       tabIndex={0}
+       onKeyDown={(e) => {
+         if (e.key === 'Enter' || e.key === ' ') {
+           handleActionClick(item.action);
+         }
+       }}
+     >
      {item.label}
+     </button>
    </li>
  );
})}
📝 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.

          {addressActionConfig.map((item, key) => {
            if (!item.shouldRender) return;
            return (
              <li
                key={key}
                className={styles.action}
              >
                <button
                  onClick={() => handleActionClick(item.action)}
                  className={styles.actionButton}
                  role="menuitem"
                  tabIndex={0}
                  onKeyDown={(e) => {
                    if (e.key === 'Enter' || e.key === ' ') {
                      handleActionClick(item.action);
                    }
                  }}
                >
                  {item.label}
                </button>
              </li>
            );
          })}
src/features/user/Settings/EditProfile/AddressManagment/AddAddress.tsx (1)

56-96: 🛠️ Refactor suggestion

Optimize useCallback dependencies and add validation.

Two improvements could be made to this implementation:

  1. Remove unnecessary dependencies:
   [
     contextLoaded,
     user,
     token,
     country,
     logoutUser,
     setUserAddresses,
-    handleError,
     setIsLoading,
     setIsModalOpen,
-    postAuthenticatedRequest,
   ]
  1. Add country validation:
   async (data: FormData) => {
     if (!contextLoaded || !user || !token) return;
+    if (!country) {
+      setErrors([{ message: 'Please select a country' }]);
+      return;
+    }
     setIsLoading(true);
📝 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.

  const addAddress = useCallback(
    async (data: FormData) => {
      if (!contextLoaded || !user || !token) return;
      if (!country) {
        setErrors([{ message: 'Please select a country' }]);
        return;
      }
      setIsLoading(true);
      const bodyToSend = {
        ...data,
        country,
        type: ADDRESS_TYPE.OTHER,
      };
      try {
        const res = await postAuthenticatedRequest<Address>(
          tenantConfig.id,
          '/app/addresses',
          bodyToSend,
          token,
          logoutUser
        );
        if (res && setUserAddresses) {
          setUserAddresses((prevAddresses) => [...prevAddresses, res]);
        }
      } catch (error) {
        setErrors(handleError(error as APIError));
      } finally {
        setIsLoading(false);
        setIsModalOpen(false);
        setAddressAction(null);
      }
    },
    [
      contextLoaded,
      user,
      token,
      country,
      logoutUser,
      setUserAddresses,
      setIsLoading,
      setIsModalOpen,
    ]
  );
src/features/user/Settings/EditProfile/AddressManagment/AddressManagement.module.scss (1)

51-69: 🛠️ Refactor suggestion

Add accessibility improvements for interactive elements.

The kebab menu button could be more accessible.

 .kebabMenuButton {
   display: flex;
   justify-content: center;
   align-items: center;
   height: 34px;
   width: 34px;
   border-radius: 50%;
   background-color: transparent;
   cursor: pointer;
+  // Add focus styles for keyboard navigation
+  &:focus-visible {
+    outline: 2px solid $primaryColor;
+    outline-offset: 2px;
+  }
 
   svg {
     height: 20px;
     width: 12px;
   }
 }

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

Copy link
Collaborator

@mohitb35 mohitb35 left a comment

Choose a reason for hiding this comment

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

@sunilsabatp Made some minor changes to the translation keys, LGTM now.

@sunilsabatp sunilsabatp merged commit 4b28506 into feature/address-mgmt Dec 6, 2024
5 checks passed
@sunilsabatp sunilsabatp deleted the address-mgmt/separate-translation branch December 6, 2024 05:32
@coderabbitai coderabbitai bot mentioned this pull request Dec 27, 2024
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.

2 participants