From c13dc58469e5c74b6faca75961f734c637795170 Mon Sep 17 00:00:00 2001 From: Edwin Obando Date: Mon, 17 Jun 2024 21:44:38 -0500 Subject: [PATCH] feat: contact information form --- messages/context.json | 3 +- messages/en.json | 3 +- messages/es.json | 3 +- react/AddressContainer.js | 43 +++- react/ContactInfoForm.js | 209 ++++++++++++++++++ react/ContactInfoForm.module.css | 40 ++++ ...ddressWithObjectValuesWithoutValidation.ts | 1 + react/__mocks__/newAddress.ts | 1 + react/__mocks__/whitespaceAddress.ts | 1 + react/addressContainerContext.tsx | 8 +- react/components.ts | 2 + react/country/IRL.ts | 9 + react/helpers.ts | 2 + react/index.ts | 1 + react/types/address.ts | 1 + react/types/css.d.ts | 5 + 16 files changed, 325 insertions(+), 7 deletions(-) create mode 100644 react/ContactInfoForm.js create mode 100644 react/ContactInfoForm.module.css create mode 100644 react/types/css.d.ts diff --git a/messages/context.json b/messages/context.json index bfeeb91f..faede78d 100644 --- a/messages/context.json +++ b/messages/context.json @@ -98,5 +98,6 @@ "address-form.field.municipalityDelegation": "Municipality/Delegation", "address-form.field.province": "Province", "address-form.field.receiverName": "Recipient", - "address-form.field.county": "County" + "address-form.field.county": "County", + "address-form.field.contactId": "Contact Id" } diff --git a/messages/en.json b/messages/en.json index bfeeb91f..faede78d 100644 --- a/messages/en.json +++ b/messages/en.json @@ -98,5 +98,6 @@ "address-form.field.municipalityDelegation": "Municipality/Delegation", "address-form.field.province": "Province", "address-form.field.receiverName": "Recipient", - "address-form.field.county": "County" + "address-form.field.county": "County", + "address-form.field.contactId": "Contact Id" } diff --git a/messages/es.json b/messages/es.json index 8f6e6444..054a4bc6 100644 --- a/messages/es.json +++ b/messages/es.json @@ -98,5 +98,6 @@ "address-form.field.municipalityDelegation": "Municipio/DelegaciĆ³n", "address-form.field.province": "Provincia", "address-form.field.receiverName": "Destinatario", - "address-form.field.county": "Condado" + "address-form.field.county": "Condado", + "address-form.field.contactId": "Id de Contacto" } diff --git a/react/AddressContainer.js b/react/AddressContainer.js index a0a6e2a5..d4c8a143 100644 --- a/react/AddressContainer.js +++ b/react/AddressContainer.js @@ -12,6 +12,19 @@ import { AddressContext } from './addressContainerContext' import { injectRules } from './addressRulesContext' class AddressContainer extends Component { + constructor(props) { + super(props) + this.contactInfo = { + id: null, + email: null, + firstName: null, + lastName: null, + document: null, + phone: null, + documentType: null, + } + } + componentDidMount() { if ( this.props.shouldHandleAddressChangeOnMount && @@ -106,12 +119,34 @@ class AddressContainer extends Component { onChangeAddress(validatedAddress, ...args) } + handleContactInfoChange = (field) => { + return (this.contactInfo = { + ...this.contactInfo, + ...field, + }) + } + render() { - const { children, Input, address } = this.props - const { handleAddressChange } = this + const { children, Input, address, handleCompleteOmnishipping } = this.props + const { handleAddressChange, handleContactInfoChange, contactInfo } = this + + if (!address?.contactId?.value) { + address.contactId = { value: null } + } else { + address.contactId = { value: address.contactId.value } + } return ( - + {children} ) @@ -136,6 +171,8 @@ AddressContainer.propTypes = { autoCompletePostalCode: PropTypes.bool, shouldHandleAddressChangeOnMount: PropTypes.bool, shouldAddFocusToNextInvalidField: PropTypes.bool, + contactInfo: PropTypes.object, + handleCompleteOmnishipping: PropTypes.func, } export default injectRules(AddressContainer) diff --git a/react/ContactInfoForm.js b/react/ContactInfoForm.js new file mode 100644 index 00000000..4f001dd3 --- /dev/null +++ b/react/ContactInfoForm.js @@ -0,0 +1,209 @@ +import React, { useEffect, useState } from 'react' + +import styles from './ContactInfoForm.module.css' + +const Input = ({ + id, + label, + type = 'text', + value = '', + name = '', + onChange = (_) => {}, +}) => ( +

+ + onChange(e)} + /> +

+) + +let gguid = 1 + +function getGGUID(prefix = 0) { + return `${prefix}${(gguid++ * new Date().getTime() * -1) + .toString() + .replace('-', '')}` +} + +const ContactInfoForm = ({ + address, + onChangeAddress, + contactInfo = {}, + onChangeContactInfo = (_, __) => {}, + clientProfileData, + prevContactInfo, +}) => { + const isPrevUserData = areEqual(prevContactInfo, clientProfileData) + const [useUserInfo, setUseUserInfo] = useState( + prevContactInfo?.id + ? address?.contactId?.value === prevContactInfo?.id && isPrevUserData + : true + ) + + const [localUserInfo, setLocalUserInfo] = useState( + prevContactInfo && !isPrevUserData + ? prevContactInfo + : { + email: '', + firstName: '', + lastName: '', + document: '', + phone: '', + documentType: '', + } + ) + + useEffect(() => { + if (address?.contactId?.value) { + return + } + + address.contactId.value = getGGUID(1234) + onChangeAddress(address) + onChangeContactInfo({ id: address.contactId.value }) + }, [address, onChangeAddress, onChangeContactInfo]) + + useEffect(() => { + if (useUserInfo) { + onChangeContactInfo({ + id: address?.contactId?.value ?? '', + email: clientProfileData?.email ?? '', + firstName: clientProfileData?.firstName ?? '', + lastName: clientProfileData?.lastName ?? '', + document: clientProfileData?.document ?? '', + phone: clientProfileData?.phone ?? '', + documentType: clientProfileData?.documentType ?? '', + }) + } else { + onChangeContactInfo(localUserInfo) + } + + onChangeAddress(address) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + useUserInfo, + localUserInfo, + onChangeContactInfo, + clientProfileData, + onChangeAddress, + ]) + + useEffect(() => { + // eslint-disable-next-line no-console + console.log('address contactInfo', contactInfo) + }, [contactInfo]) + + const handleInputChange = (e) => { + const { name, value } = e.target + + setLocalUserInfo((prev) => ({ ...prev, [name]: value })) + } + + return ( +
+ + {!useUserInfo ? ( +
+

Receiver Information

+
+ +
+ + +
+
+ + +
+
+
+ ) : null} +
+ ) +} + +const areEqual = (obj1, obj2) => { + // eslint-disable-next-line no-console + console.log('obj1', obj1, obj2) + if (obj1 === obj2) { + return true + } + + if (!obj1 || !obj2) { + return false + } + + for (const key in obj1) { + if (key !== 'id') { + if (obj1[key] && obj1[key] !== obj2[key]) { + // eslint-disable-next-line no-console + console.log('key', key, obj1[key], obj2[key]) + + return false + } + } + } + + return true +} + +export const getPreviousContactInfo = (state) => { + const { orderForm, addressForm } = state + + return ( + orderForm?.shippingData?.contactInformation?.find( + ({ id }) => + addressForm?.addresses?.[addressForm?.residentialId]?.contactId + ?.value === id + ) ?? null + ) +} + +export default ContactInfoForm diff --git a/react/ContactInfoForm.module.css b/react/ContactInfoForm.module.css new file mode 100644 index 00000000..f9b01682 --- /dev/null +++ b/react/ContactInfoForm.module.css @@ -0,0 +1,40 @@ +.mainContactInfoContainer { +} +.mainContactInfoForm { +} + +.contactInfoCheckbox input { + margin: 0; + margin-right: 6px; + width: 16px; + height: 16px; +} + +.contactInfoCheckbox { + display: flex; + align-items: center; + font-size: 12px; + margin-top: 16px; + margin-bottom: 12px; +} + +.contactInfoTitle { + font-size: 18px !important; + font-weight: 500 !important; +} + +.contactInfoFlex { + display: flex; + align-items: center; + gap: 12px; +} + +.contactInfoFlex .input { + width: 100%; +} + +@media (max-width: 768px) { + .contactInfoFlex { + flex-wrap: wrap; + } +} diff --git a/react/__mocks__/addressWithObjectValuesWithoutValidation.ts b/react/__mocks__/addressWithObjectValuesWithoutValidation.ts index 72b8fc3b..4c9932d9 100644 --- a/react/__mocks__/addressWithObjectValuesWithoutValidation.ts +++ b/react/__mocks__/addressWithObjectValuesWithoutValidation.ts @@ -16,4 +16,5 @@ export default { street: { value: null }, isDisposable: { value: true }, addressQuery: { value: null }, + contactId: { value: null }, } as AddressWithValidation diff --git a/react/__mocks__/newAddress.ts b/react/__mocks__/newAddress.ts index f2d8ed66..2c22dbfe 100644 --- a/react/__mocks__/newAddress.ts +++ b/react/__mocks__/newAddress.ts @@ -16,4 +16,5 @@ export default { street: { value: null }, addressQuery: { value: null }, isDisposable: { value: true }, + contactId: { value: null }, } as AddressWithValidation diff --git a/react/__mocks__/whitespaceAddress.ts b/react/__mocks__/whitespaceAddress.ts index f79964ed..f694193d 100644 --- a/react/__mocks__/whitespaceAddress.ts +++ b/react/__mocks__/whitespaceAddress.ts @@ -16,4 +16,5 @@ export default { street: { value: ' ' }, addressQuery: { value: null }, isDisposable: { value: true }, + contactId: { value: null }, } as AddressWithValidation diff --git a/react/addressContainerContext.tsx b/react/addressContainerContext.tsx index 6a858a7f..af81f307 100644 --- a/react/addressContainerContext.tsx +++ b/react/addressContainerContext.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useContext } from 'react' import type { AddressWithValidation } from './types/address' @@ -6,6 +6,9 @@ type Context = { address: AddressWithValidation handleAddressChange: (address: AddressWithValidation) => void Input: React.ComponentType + contactInfo?: any + handleContactInfoChange?: any + handleCompleteOmnishipping?: any } export const AddressContext = React.createContext( @@ -27,7 +30,10 @@ export function injectAddressContext( )} diff --git a/react/components.ts b/react/components.ts index 4d277c36..e76b4e4a 100644 --- a/react/components.ts +++ b/react/components.ts @@ -6,6 +6,7 @@ import AddressContainer from './AddressContainer' import AutoCompletedFields from './AutoCompletedFields' import AddressRules from './AddressRules' import AddressSubmitter from './AddressSubmitter' +import ContactInfoForm from './ContactInfoForm' import PostalCodeLoader from './postalCodeFrom/PostalCodeLoader' import GoogleMapsContainer from './geolocation/GoogleMapsContainer' import Map from './geolocation/Map' @@ -18,6 +19,7 @@ export default { AddressRules, AddressSubmitter, AddressSummary, + ContactInfoForm, AutoCompletedFields, CountrySelector, GoogleMapsContainer, diff --git a/react/country/IRL.ts b/react/country/IRL.ts index 61528128..45b390eb 100644 --- a/react/country/IRL.ts +++ b/react/country/IRL.ts @@ -74,6 +74,7 @@ const rules: PostalCodeRules = { size: 'large', }, { + hidden: true, name: 'receiverName', elementName: 'receiver', maxLength: 750, @@ -81,6 +82,14 @@ const rules: PostalCodeRules = { size: 'xlarge', required: true, }, + { + hidden: true, + name: 'contactId', + maxLength: 100, + label: 'contactId', + size: 'xlarge', + required: true, + }, ], geolocation: { postalCode: { diff --git a/react/helpers.ts b/react/helpers.ts index 9523adfb..0bef8d9a 100644 --- a/react/helpers.ts +++ b/react/helpers.ts @@ -7,8 +7,10 @@ import { validateAddress, } from './validateAddress' import getAddressByGeolocation from './geolocation/Utils' +import { getPreviousContactInfo } from './ContactInfoForm' export default { + getPreviousContactInfo, isValidAddress, validateField, validateAddress, diff --git a/react/index.ts b/react/index.ts index 1181cf4e..897c41c5 100644 --- a/react/index.ts +++ b/react/index.ts @@ -7,6 +7,7 @@ export { default as AddressContainer } from './AddressContainer' export { default as AutoCompletedFields } from './AutoCompletedFields' export { default as AddressRules } from './AddressRules' export { default as AddressSubmitter } from './AddressSubmitter' +export { default as ContactInfoForm } from './ContactInfoForm' export { default as components } from './components' diff --git a/react/types/address.ts b/react/types/address.ts index 21792e74..310b7176 100644 --- a/react/types/address.ts +++ b/react/types/address.ts @@ -33,6 +33,7 @@ export interface Address { geoCoordinates?: number[] | null receiverName?: string | null addressQuery?: string | null + contactId?: string | null } export type AddressValues = Address[Fields] | null diff --git a/react/types/css.d.ts b/react/types/css.d.ts new file mode 100644 index 00000000..4a332752 --- /dev/null +++ b/react/types/css.d.ts @@ -0,0 +1,5 @@ +declare module '*.css' { + const styles: Record + + export default styles +}