diff --git a/src/apps/accounts/src/settings/tabs/account/AccountTab.tsx b/src/apps/accounts/src/settings/tabs/account/AccountTab.tsx index 536d321a6..b0551acda 100644 --- a/src/apps/accounts/src/settings/tabs/account/AccountTab.tsx +++ b/src/apps/accounts/src/settings/tabs/account/AccountTab.tsx @@ -5,6 +5,7 @@ import { UserProfile, UserTraits } from '~/libs/core' import { AccountRole } from './account-role' import { SecuritySection } from './security' import { UserAndPassword } from './user-and-pass' +import { MemberAddress } from './address' import styles from './AccountTab.module.scss' interface AccountTabProps { @@ -20,6 +21,8 @@ const AccountTab: FC = (props: AccountTabProps) => ( + + ) diff --git a/src/apps/accounts/src/settings/tabs/account/address/MemberAddress.module.scss b/src/apps/accounts/src/settings/tabs/account/address/MemberAddress.module.scss new file mode 100644 index 000000000..32ea795a5 --- /dev/null +++ b/src/apps/accounts/src/settings/tabs/account/address/MemberAddress.module.scss @@ -0,0 +1,27 @@ +@import '@libs/ui/styles/includes'; + +.container { + margin: $sp-8 0; + + .content { + display: grid; + grid-template-columns: repeat(2, 1fr); + margin-bottom: 0; + + @include ltelg { + grid-template-columns: 1fr; + } + + >p { + max-width: 380px; + } + + .form { + .formCTAs { + margin-top: $sp-4; + padding-top: $sp-4; + border-top: 2px solid $black-10; + } + } + } +} \ No newline at end of file diff --git a/src/apps/accounts/src/settings/tabs/account/address/MemberAddress.tsx b/src/apps/accounts/src/settings/tabs/account/address/MemberAddress.tsx new file mode 100644 index 000000000..6d55d1a18 --- /dev/null +++ b/src/apps/accounts/src/settings/tabs/account/address/MemberAddress.tsx @@ -0,0 +1,193 @@ +import { Dispatch, FC, SetStateAction, useState } from 'react' +import { toast } from 'react-toastify' +import { bind, trim } from 'lodash' +import classNames from 'classnames' + +import { + Button, + Collapsible, InputSelect, InputText, +} from '~/libs/ui' +import { + CountryLookup, + updateMemberProfileAsync, + useCountryLookup, + UserProfile, +} from '~/libs/core' + +import styles from './MemberAddress.module.scss' + +interface MemberAddressProps { + profile: UserProfile +} + +const MemberAddress: FC = (props: MemberAddressProps) => { + const countryLookup: CountryLookup[] | undefined + = useCountryLookup() + + const [formValues, setFormValues]: [any, Dispatch] = useState({ + country: props.profile.homeCountryCode || props.profile.competitionCountryCode, + ...props.profile.addresses ? props.profile.addresses[0] : {}, + }) + + const [formErrors, setFormErrors]: [ + { [key: string]: string }, + Dispatch> + ] + = useState<{ [key: string]: string }>({}) + + const [isSaving, setIsSaving]: [boolean, Dispatch>] + = useState(false) + + const [isFormChanged, setIsFormChanged]: [boolean, Dispatch>] + = useState(false) + + function handleFormValueChange(key: string, event: React.ChangeEvent): void { + const oldFormValues = { ...formValues } + + setFormValues({ + ...oldFormValues, + [key]: event.target.value, + }) + setIsFormChanged(true) + } + + function handleFormAction(): void { + if (!trim(formValues.city)) { + setFormErrors({ city: 'Please select a city' }) + return + } + + if (!formValues.country) { + setFormErrors({ country: 'Please select a country' }) + return + } + + setIsSaving(true) + + updateMemberProfileAsync( + props.profile.handle, + { + addresses: [{ + city: formValues.city, + stateCode: formValues.stateCode, + streetAddr1: formValues.streetAddr1, + streetAddr2: formValues.streetAddr2, + zip: formValues.zip, + }], + competitionCountryCode: formValues.country, + homeCountryCode: formValues.country, + }, + ) + .then(() => { + toast.success('Your account has been updated.', { position: toast.POSITION.BOTTOM_RIGHT }) + setFormErrors({}) + }) + .catch(() => { + toast.error('Something went wrong. Please try again.', { position: toast.POSITION.BOTTOM_RIGHT }) + }) + .finally(() => { + setIsFormChanged(false) + setIsSaving(false) + }) + } + + return ( + Address} + containerClass={styles.container} + contentClass={styles.content} + > +

+ By keeping this information up to date we may surprise you with a cool T-shirt. + Sharing your contact details will never result in robocalls about health insurance plans or junk mail. +

+ +
+
+ + + + + + ({ + label: cl.country, + value: cl.countryCode, + }))} + value={formValues.country} + onChange={bind(handleFormValueChange, this, 'country')} + name='country' + label='Country *' + error={formErrors.country} + placeholder='Select a Country' + dirty + /> + +
+
+
+
+
+ ) +} + +export default MemberAddress diff --git a/src/apps/accounts/src/settings/tabs/account/address/index.ts b/src/apps/accounts/src/settings/tabs/account/address/index.ts new file mode 100644 index 000000000..33f8c6ff5 --- /dev/null +++ b/src/apps/accounts/src/settings/tabs/account/address/index.ts @@ -0,0 +1 @@ +export { default as MemberAddress } from './MemberAddress' diff --git a/src/libs/core/lib/profile/data-providers/index.ts b/src/libs/core/lib/profile/data-providers/index.ts index fbb7d6d0d..1eca95f91 100644 --- a/src/libs/core/lib/profile/data-providers/index.ts +++ b/src/libs/core/lib/profile/data-providers/index.ts @@ -10,3 +10,4 @@ export * from './useMemberMFAStatus' export * from './useDiceIdConnection' export * from './useMemberTraits' export * from './useMemberDevicesLookup' +export * from './useCountryLookup' diff --git a/src/libs/core/lib/profile/data-providers/useCountryLookup.ts b/src/libs/core/lib/profile/data-providers/useCountryLookup.ts new file mode 100644 index 000000000..02c9b0972 --- /dev/null +++ b/src/libs/core/lib/profile/data-providers/useCountryLookup.ts @@ -0,0 +1,10 @@ +import { SWRResponse } from 'swr' +import useSWRImmutable from 'swr/immutable' + +import { CountryLookup, countryLookupURL } from '~/libs/core' + +export function useCountryLookup(): CountryLookup[] | undefined { + const { data }: SWRResponse = useSWRImmutable(countryLookupURL) + + return data ? data.result?.content : undefined +} diff --git a/src/libs/core/lib/profile/modify-user-profile.model.ts b/src/libs/core/lib/profile/modify-user-profile.model.ts index 20b877a92..238ecfbab 100644 --- a/src/libs/core/lib/profile/modify-user-profile.model.ts +++ b/src/libs/core/lib/profile/modify-user-profile.model.ts @@ -1,6 +1,15 @@ import { TC_TRACKS } from './user-profile.model' export interface UpdateProfileRequest { + addresses?: Array<{ + city?: string + stateCode?: string + streetAddr1?: string + streetAddr2?: string + zip?: string + }> + competitionCountryCode?: string + homeCountryCode?: string firstName?: string lastName?: string tracks?: TC_TRACKS[], diff --git a/src/libs/core/lib/profile/user-profile.model.ts b/src/libs/core/lib/profile/user-profile.model.ts index 65a0952fe..63db0e186 100644 --- a/src/libs/core/lib/profile/user-profile.model.ts +++ b/src/libs/core/lib/profile/user-profile.model.ts @@ -5,6 +5,10 @@ export type TC_TRACKS = 'DEVELOP' | 'DESIGN' | 'DATA_SCIENCE' export interface UserProfile { addresses?: Array<{ city?: string + stateCode?: string + streetAddr1?: string + streetAddr2?: string + zip?: string }> competitionCountryCode: string createdAt: number