-
Notifications
You must be signed in to change notification settings - Fork 21
[PROD RELEASE] - profile app & fixes #1494
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0d05768
0e8c667
0c96fd2
f46de22
ab987bd
a635f35
c87a9b3
6ce2d5c
ea91224
f26d54c
73e38fd
14b5e38
81a8658
c427399
f5cfd8e
4386adf
277aab6
e71dc1a
bc416ae
fa78cd7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -313,6 +313,7 @@ const Software: FC<SoftwareProps> = (props: SoftwareProps) => { | |
| name='softwareName' | ||
| label='Software Name *' | ||
| value={selectedSoftwareName} | ||
| forceUpdateValue | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [ |
||
| onChange={handleSoftwareNameChange} | ||
| placeholder='Type here the Software Name' | ||
| tabIndex={0} | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -297,6 +297,7 @@ const Subscriptions: FC<SubscriptionsProps> = (props: SubscriptionsProps) => { | |
| type='text' | ||
| error={formErrors.subscriptionName} | ||
| dirty | ||
| forceUpdateValue | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [ |
||
| /> | ||
| <div className={styles.formCTAs}> | ||
| {!isEditMode && <IconOutline.PlusCircleIcon />} | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,10 @@ | ||
| import { Dispatch, FC, SetStateAction, useMemo, useState } from 'react' | ||
| import { Dispatch, FC, SetStateAction, useContext, useMemo, useState } from 'react' | ||
| import { trim } from 'lodash' | ||
| import cityTimezones from 'city-timezones' | ||
| import moment from 'moment-timezone' | ||
|
|
||
| import { useCountryName, UserProfile } from '~/libs/core' | ||
| import { IconSolid } from '~/libs/ui' | ||
| import { profileContext, ProfileContextData, useCountryName, UserProfile, UserRole } from '~/libs/core' | ||
| import { IconOutline, IconSolid, Tooltip } from '~/libs/ui' | ||
|
|
||
| import { EditMemberPropertyBtn } from '../../components' | ||
|
|
||
|
|
@@ -21,7 +22,9 @@ const MemberLocalInfo: FC<MemberLocalInfoProps> = (props: MemberLocalInfoProps) | |
| const memberCountry: string | undefined | ||
| = useCountryName(props.profile?.homeCountryCode || props.profile?.competitionCountryCode) | ||
|
|
||
| const city: string | undefined = props.profile?.addresses?.[0]?.city | ||
| const address = props.profile?.addresses?.[0] | ||
|
|
||
| const city: string | undefined = address?.city | ||
|
|
||
| const memberCityTimezone: string | undefined = useMemo(() => { | ||
| if (!city) { | ||
|
|
@@ -42,6 +45,10 @@ const MemberLocalInfo: FC<MemberLocalInfoProps> = (props: MemberLocalInfoProps) | |
|
|
||
| const canEdit: boolean = props.authProfile?.handle === props.profile.handle | ||
|
|
||
| const { profile }: ProfileContextData = useContext(profileContext) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [❗❗ |
||
| const isAdminOrTM = profile?.roles?.includes(UserRole.administrator) | ||
| || profile?.roles?.includes(UserRole.talentManager) | ||
|
|
||
| const [isEditMode, setIsEditMode]: [boolean, Dispatch<SetStateAction<boolean>>] | ||
| = useState<boolean>(false) | ||
|
|
||
|
|
@@ -76,11 +83,56 @@ const MemberLocalInfo: FC<MemberLocalInfoProps> = (props: MemberLocalInfoProps) | |
| return 'Unknown location' | ||
| }, [city, memberCountry]) | ||
|
|
||
| const hasDetailedAddress: boolean = useMemo(() => { | ||
| if (!address) return false | ||
| return !!( | ||
| trim(address.streetAddr1) | ||
| || trim(address.streetAddr2) | ||
| || trim(address.stateCode) | ||
| || trim(address.zip) | ||
| ) | ||
| }, [address]) | ||
|
|
||
| const canSeeDetailedAddressIcon = canEdit || isAdminOrTM | ||
|
|
||
| const tooltipContent = useMemo(() => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [❗❗ |
||
| if (!hasDetailedAddress) return undefined | ||
|
|
||
| const addressLine = [ | ||
| trim(address?.streetAddr1), | ||
| trim(address?.streetAddr2), | ||
| trim(address?.city), | ||
| trim(address?.stateCode), | ||
| ].filter(Boolean) | ||
| .join(', ') | ||
|
|
||
| const postalCode = trim(address?.zip) | ||
|
|
||
| if (!postalCode) return addressLine | ||
|
|
||
| return ( | ||
| <> | ||
| <div>{addressLine}</div> | ||
| <div> | ||
| Zip/Postal code - | ||
| {postalCode} | ||
| </div> | ||
| </> | ||
| ) | ||
| }, [address, hasDetailedAddress]) | ||
|
|
||
| return ( | ||
| <div className={styles.container}> | ||
| <div className={styles.localInfo}> | ||
| <IconSolid.LocationMarkerIcon /> | ||
| {locationDisplay} | ||
| {hasDetailedAddress && canSeeDetailedAddressIcon && ( | ||
| <div className={styles.tooltip}> | ||
| <Tooltip content={tooltipContent} triggerOn='hover'> | ||
| <IconOutline.InformationCircleIcon className='tooltip-icon' /> | ||
| </Tooltip> | ||
| </div> | ||
| )} | ||
| { | ||
| canEdit && ( | ||
| <EditMemberPropertyBtn | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,6 @@ | |
| margin-top: $sp-4; | ||
|
|
||
| :global(.input-wrapper) { | ||
| margin-bottom: $sp-4; | ||
| margin-bottom: $sp-1; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| import { Dispatch, FC, SetStateAction, useMemo, useState } from 'react' | ||
| import { bind, omit, trim } from 'lodash' | ||
| import { omit, trim } from 'lodash' | ||
| import { toast } from 'react-toastify' | ||
| import React, { Dispatch, FC, SetStateAction, useCallback, useMemo, useState } from 'react' | ||
|
|
||
| import { BaseModal, Button, InputSelect, InputText } from '~/libs/ui' | ||
| import { | ||
|
|
@@ -13,9 +13,9 @@ import { | |
| import styles from './ModifyLocationModal.module.scss' | ||
|
|
||
| interface ModifyLocationModalProps { | ||
| onClose: () => void | ||
| onSave: () => void | ||
| profile: UserProfile | ||
| onClose: () => void | ||
| onSave: () => void | ||
| profile: UserProfile | ||
| } | ||
|
|
||
| const OMIT_ADDRESS_KEYS_ON_UPDATE = [ | ||
|
|
@@ -26,57 +26,92 @@ const ModifyLocationModal: FC<ModifyLocationModalProps> = (props: ModifyLocation | |
| const countryLookup: CountryLookup[] | undefined | ||
| = useCountryLookup() | ||
|
|
||
| const contries = useMemo(() => (countryLookup || []).map((cl: CountryLookup) => ({ | ||
| label: cl.country, | ||
| value: cl.countryCode, | ||
| })) | ||
| .sort((a, b) => a.label.localeCompare(b.label)), [countryLookup]) | ||
| const countries = useMemo( | ||
| () => (countryLookup || []).map((cl: CountryLookup) => ({ | ||
| label: cl.country, | ||
| value: cl.countryCode, | ||
| })) | ||
| .sort((a, b) => a.label.localeCompare(b.label)), | ||
| [countryLookup], | ||
| ) | ||
|
|
||
| const existingAddress = props.profile.addresses ? props.profile.addresses[0] : {} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [ |
||
|
|
||
| const [formValues, setFormValues]: [any, Dispatch<any>] = useState({ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [ |
||
| country: props.profile.homeCountryCode || props.profile.competitionCountryCode, | ||
| ...props.profile.addresses ? props.profile.addresses[0] : {}, | ||
| country: props.profile.homeCountryCode, | ||
| ...existingAddress, | ||
| }) | ||
|
|
||
| const [formSaveError, setFormSaveError]: [ | ||
| string | undefined, | ||
| Dispatch<SetStateAction<string | undefined>> | ||
| ] = useState<string | undefined>() | ||
| const [formErrors, setFormErrors]: [ | ||
| { [key: string]: string }, | ||
| Dispatch<SetStateAction<{ [key: string]: string }>> | ||
| ] = useState({}) | ||
|
|
||
| const [isSaving, setIsSaving]: [boolean, Dispatch<SetStateAction<boolean>>] | ||
| = useState<boolean>(false) | ||
| const [formSaveError, setFormSaveError] = useState<string | undefined>() | ||
| const [isSaving, setIsSaving] = useState(false) | ||
| const [isFormChanged, setIsFormChanged] = useState(false) | ||
|
|
||
| const [isFormChanged, setIsFormChanged]: [boolean, Dispatch<SetStateAction<boolean>>] | ||
| = useState<boolean>(false) | ||
| const handleFormValueChange = useCallback( | ||
| (key: string) => (event: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>): void => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [❗❗ |
||
| const value: string = event.target.value | ||
|
|
||
| function handleFormValueChange(key: string, event: React.ChangeEvent<HTMLInputElement>): void { | ||
| const oldFormValues = { ...formValues } | ||
| setFormValues({ | ||
| ...formValues, | ||
| [key]: value, | ||
| }) | ||
|
|
||
| setFormValues({ | ||
| ...oldFormValues, | ||
| [key]: event.target.value, | ||
| }) | ||
| setIsFormChanged(true) | ||
| setIsFormChanged(true) | ||
|
|
||
| setFormErrors(prev => { | ||
| if (!prev[key]) return prev | ||
| const next = { ...prev } | ||
| delete next[key] | ||
| return next | ||
| }) | ||
| }, | ||
| [formValues], | ||
| ) | ||
|
|
||
| function validate(): boolean { | ||
| const nextErrors: { [key: string]: string } = {} | ||
|
|
||
| if (!trim(formValues.city)) nextErrors.city = 'Please select a city' | ||
| if (!formValues.country) nextErrors.country = 'Please select a country' | ||
|
|
||
| setFormErrors(nextErrors) | ||
| return Object.keys(nextErrors).length === 0 | ||
| } | ||
|
|
||
| function handleLocationSave(): void { | ||
| updateMemberProfileAsync( | ||
| props.profile.handle, | ||
| { | ||
| addresses: [{ | ||
| ...props.profile.addresses ? omit(props.profile.addresses[0], OMIT_ADDRESS_KEYS_ON_UPDATE) : {}, | ||
| if (!validate()) return | ||
|
|
||
| setIsSaving(true) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [ |
||
|
|
||
| const baseAddressPayload = props.profile.addresses | ||
| ? omit(props.profile.addresses[0], OMIT_ADDRESS_KEYS_ON_UPDATE) | ||
| : {} | ||
|
|
||
| updateMemberProfileAsync(props.profile.handle, { | ||
| addresses: [ | ||
| { | ||
| ...baseAddressPayload, | ||
| city: trim(formValues.city), | ||
| }], | ||
| competitionCountryCode: formValues.country, | ||
| homeCountryCode: formValues.country, | ||
| }, | ||
| ) | ||
| stateCode: trim(formValues.stateCode), | ||
| streetAddr1: trim(formValues.streetAddr1), | ||
| streetAddr2: trim(formValues.streetAddr2), | ||
| zip: trim(formValues.zip), | ||
| }, | ||
| ], | ||
| competitionCountryCode: formValues.country, | ||
| homeCountryCode: formValues.country, | ||
| }) | ||
| .then(() => { | ||
| toast.success('Your location has been updated.', { position: toast.POSITION.BOTTOM_RIGHT }) | ||
| props.onSave() | ||
| }) | ||
| .catch((error: any) => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [💡 |
||
| toast.error('Something went wrong. Please try again.', { position: toast.POSITION.BOTTOM_RIGHT }) | ||
| setFormSaveError(error.message || error) | ||
| setFormSaveError(error?.message || error) | ||
| }) | ||
| .finally(() => { | ||
| setIsFormChanged(false) | ||
|
|
@@ -111,23 +146,77 @@ const ModifyLocationModal: FC<ModifyLocationModalProps> = (props: ModifyLocation | |
| )} | ||
| > | ||
| <p>Provide details on your location.</p> | ||
|
|
||
| <form className={styles.editForm}> | ||
| <InputText | ||
| label='City' | ||
| name='address' | ||
| label='Address' | ||
| error={formErrors.streetAddr1} | ||
| placeholder='Your address' | ||
| dirty | ||
| tabIndex={0} | ||
| type='text' | ||
| onChange={handleFormValueChange('streetAddr1')} | ||
| value={formValues.streetAddr1} | ||
| /> | ||
|
|
||
| <InputText | ||
| name='address2' | ||
| label='Address 2' | ||
| error={formErrors.streetAddr2} | ||
| placeholder='Your address continued' | ||
| dirty | ||
| tabIndex={0} | ||
| type='text' | ||
| onChange={handleFormValueChange('streetAddr2')} | ||
| value={formValues.streetAddr2} | ||
| /> | ||
|
|
||
| <InputText | ||
| name='city' | ||
| onChange={bind(handleFormValueChange, this, 'city')} | ||
| label='City *' | ||
| error={formErrors.city} | ||
| placeholder='Which city do you live in?' | ||
| dirty | ||
| tabIndex={0} | ||
| type='text' | ||
| onChange={handleFormValueChange('city')} | ||
| value={formValues.city} | ||
| /> | ||
|
|
||
| <InputText | ||
| name='state' | ||
| label='State' | ||
| error={formErrors.stateCode} | ||
| placeholder='State' | ||
| dirty | ||
| tabIndex={0} | ||
| type='text' | ||
| placeholder='Select your city name' | ||
| onChange={handleFormValueChange('stateCode')} | ||
| value={formValues.stateCode} | ||
| /> | ||
|
|
||
| <InputText | ||
| name='zip' | ||
| label='Zip/Postal Code' | ||
| error={formErrors.zip} | ||
| placeholder='Your Zip or Postal Code' | ||
| dirty | ||
| tabIndex={0} | ||
| type='text' | ||
| onChange={handleFormValueChange('zip')} | ||
| value={formValues.zip} | ||
| /> | ||
|
|
||
| <InputSelect | ||
| options={contries} | ||
| options={countries} | ||
| value={formValues.country} | ||
| onChange={bind(handleFormValueChange, this, 'country')} | ||
| onChange={handleFormValueChange('country')} | ||
| name='country' | ||
| label='Country *' | ||
| error={formErrors.country} | ||
| placeholder='Select a Country' | ||
| dirty | ||
| /> | ||
| </form> | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -91,8 +91,13 @@ const ModifyPhonesModal: FC<ModifyPhonesModalProps> = (props: ModifyPhonesModalP | |
| toast.success('Phone numbers updated successfully.', { position: toast.POSITION.BOTTOM_RIGHT }) | ||
| props.onSave() | ||
| }) | ||
| .catch(() => { | ||
| toast.error('Failed to update phone numbers.', { position: toast.POSITION.BOTTOM_RIGHT }) | ||
| .catch(error => { | ||
| const apiMessage | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [❗❗ |
||
| = error?.message || 'Failed to update phone numbers.' | ||
|
|
||
| toast.error(apiMessage, { | ||
| position: toast.POSITION.BOTTOM_RIGHT, | ||
| }) | ||
| setIsSaving(false) | ||
| }) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[⚠️
correctness]The
forceUpdateValueprop is added to theInputTextcomponent, but it's unclear what its purpose is without additional context. Ensure that this prop is necessary and correctly implemented in theInputTextcomponent to avoid unexpected behavior.