Skip to content

Commit 1150573

Browse files
authored
Merge branch 'dev' into onboarding
2 parents 95fe2a8 + ca04fd3 commit 1150573

File tree

21 files changed

+606
-121
lines changed

21 files changed

+606
-121
lines changed

src/apps/accounts/src/settings/tabs/account/AccountTab.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { UserProfile, UserTraits } from '~/libs/core'
55
import { AccountRole } from './account-role'
66
import { SecuritySection } from './security'
77
import { UserAndPassword } from './user-and-pass'
8+
import { MemberAddress } from './address'
89
import styles from './AccountTab.module.scss'
910

1011
interface AccountTabProps {
@@ -20,6 +21,8 @@ const AccountTab: FC<AccountTabProps> = (props: AccountTabProps) => (
2021

2122
<UserAndPassword profile={props.profile} memberTraits={props.memberTraits} />
2223

24+
<MemberAddress profile={props.profile} />
25+
2326
<SecuritySection profile={props.profile} />
2427
</div>
2528
)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
@import '@libs/ui/styles/includes';
2+
3+
.container {
4+
margin: $sp-8 0;
5+
6+
.content {
7+
display: grid;
8+
grid-template-columns: repeat(2, 1fr);
9+
margin-bottom: 0;
10+
11+
@include ltelg {
12+
grid-template-columns: 1fr;
13+
}
14+
15+
>p {
16+
max-width: 380px;
17+
}
18+
19+
.form {
20+
.formCTAs {
21+
margin-top: $sp-4;
22+
padding-top: $sp-4;
23+
border-top: 2px solid $black-10;
24+
}
25+
}
26+
}
27+
}
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import { Dispatch, FC, SetStateAction, useState } from 'react'
2+
import { toast } from 'react-toastify'
3+
import { bind, trim } from 'lodash'
4+
import classNames from 'classnames'
5+
6+
import {
7+
Button,
8+
Collapsible, InputSelect, InputText,
9+
} from '~/libs/ui'
10+
import {
11+
CountryLookup,
12+
updateMemberProfileAsync,
13+
useCountryLookup,
14+
UserProfile,
15+
} from '~/libs/core'
16+
17+
import styles from './MemberAddress.module.scss'
18+
19+
interface MemberAddressProps {
20+
profile: UserProfile
21+
}
22+
23+
const MemberAddress: FC<MemberAddressProps> = (props: MemberAddressProps) => {
24+
const countryLookup: CountryLookup[] | undefined
25+
= useCountryLookup()
26+
27+
const [formValues, setFormValues]: [any, Dispatch<any>] = useState({
28+
country: props.profile.homeCountryCode || props.profile.competitionCountryCode,
29+
...props.profile.addresses ? props.profile.addresses[0] : {},
30+
})
31+
32+
const [formErrors, setFormErrors]: [
33+
{ [key: string]: string },
34+
Dispatch<SetStateAction<{ [key: string]: string }>>
35+
]
36+
= useState<{ [key: string]: string }>({})
37+
38+
const [isSaving, setIsSaving]: [boolean, Dispatch<SetStateAction<boolean>>]
39+
= useState<boolean>(false)
40+
41+
const [isFormChanged, setIsFormChanged]: [boolean, Dispatch<SetStateAction<boolean>>]
42+
= useState<boolean>(false)
43+
44+
function handleFormValueChange(key: string, event: React.ChangeEvent<HTMLInputElement>): void {
45+
const oldFormValues = { ...formValues }
46+
47+
setFormValues({
48+
...oldFormValues,
49+
[key]: event.target.value,
50+
})
51+
setIsFormChanged(true)
52+
}
53+
54+
function handleFormAction(): void {
55+
if (!trim(formValues.city)) {
56+
setFormErrors({ city: 'Please select a city' })
57+
return
58+
}
59+
60+
if (!formValues.country) {
61+
setFormErrors({ country: 'Please select a country' })
62+
return
63+
}
64+
65+
setIsSaving(true)
66+
67+
updateMemberProfileAsync(
68+
props.profile.handle,
69+
{
70+
addresses: [{
71+
city: trim(formValues.city),
72+
stateCode: trim(formValues.stateCode),
73+
streetAddr1: trim(formValues.streetAddr1),
74+
streetAddr2: trim(formValues.streetAddr2),
75+
zip: trim(formValues.zip),
76+
}],
77+
competitionCountryCode: formValues.country,
78+
homeCountryCode: formValues.country,
79+
},
80+
)
81+
.then(() => {
82+
toast.success('Your account has been updated.', { position: toast.POSITION.BOTTOM_RIGHT })
83+
setFormErrors({})
84+
})
85+
.catch(() => {
86+
toast.error('Something went wrong. Please try again.', { position: toast.POSITION.BOTTOM_RIGHT })
87+
})
88+
.finally(() => {
89+
setIsFormChanged(false)
90+
setIsSaving(false)
91+
})
92+
}
93+
94+
return (
95+
<Collapsible
96+
header={<h3>Address</h3>}
97+
containerClass={styles.container}
98+
contentClass={styles.content}
99+
>
100+
<p>
101+
By keeping this information up to date we may surprise you with a cool T-shirt.
102+
Sharing your contact details will never result in robocalls about health insurance plans or junk mail.
103+
</p>
104+
105+
<form
106+
className={classNames(styles.formWrap)}
107+
>
108+
<div className={styles.form}>
109+
<InputText
110+
name='address'
111+
label='Address'
112+
error={formErrors.streetAddr1}
113+
placeholder='Your address'
114+
dirty
115+
tabIndex={0}
116+
type='text'
117+
onChange={bind(handleFormValueChange, this, 'streetAddr1')}
118+
value={formValues.streetAddr1}
119+
/>
120+
<InputText
121+
name='address2'
122+
label='Address 2'
123+
error={formErrors.streetAddr2}
124+
placeholder='Your address continued'
125+
dirty
126+
tabIndex={0}
127+
type='text'
128+
onChange={bind(handleFormValueChange, this, 'streetAddr2')}
129+
value={formValues.streetAddr2}
130+
/>
131+
<InputText
132+
name='city'
133+
label='City *'
134+
error={formErrors.city}
135+
placeholder='Which city do you live in?'
136+
dirty
137+
tabIndex={0}
138+
type='text'
139+
onChange={bind(handleFormValueChange, this, 'city')}
140+
value={formValues.city}
141+
/>
142+
<InputText
143+
name='state'
144+
label='State'
145+
error={formErrors.stateCode}
146+
placeholder='State'
147+
dirty
148+
tabIndex={0}
149+
type='text'
150+
onChange={bind(handleFormValueChange, this, 'stateCode')}
151+
value={formValues.stateCode}
152+
/>
153+
<InputText
154+
name='zip'
155+
label='Zip/Postal Code'
156+
error={formErrors.zip}
157+
placeholder='Your Zip or Postal Code'
158+
dirty
159+
tabIndex={0}
160+
type='text'
161+
onChange={bind(handleFormValueChange, this, 'zip')}
162+
value={formValues.zip}
163+
/>
164+
<InputSelect
165+
options={(countryLookup || []).map((cl: CountryLookup) => ({
166+
label: cl.country,
167+
value: cl.countryCode,
168+
}))}
169+
value={formValues.country}
170+
onChange={bind(handleFormValueChange, this, 'country')}
171+
name='country'
172+
label='Country *'
173+
error={formErrors.country}
174+
placeholder='Select a Country'
175+
dirty
176+
/>
177+
178+
<div className={styles.formCTAs}>
179+
<Button
180+
secondary
181+
size='lg'
182+
label='Save Changes'
183+
onClick={handleFormAction}
184+
disabled={isSaving || !isFormChanged}
185+
/>
186+
</div>
187+
</div>
188+
</form>
189+
</Collapsible>
190+
)
191+
}
192+
193+
export default MemberAddress
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as MemberAddress } from './MemberAddress'

src/apps/profiles/src/member-profile/education-and-certifications/EducationCard/EducationCard.module.scss

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,28 @@
22

33
.educationCard {
44
flex: 1;
5+
margin-bottom: $sp-8;
6+
font-size: 14px;
7+
line-height: 22px;
8+
color: $black-60;
9+
10+
:global(.body-main-bold) {
11+
color: $black-100;
12+
}
13+
14+
&.educationCardModalView {
15+
margin-bottom: 0;
16+
17+
.educationCardHeader {
18+
flex-direction: column;
19+
align-items: flex-start;
20+
justify-content: flex-start;
21+
22+
:global(.body-main-bold) {
23+
margin-bottom: $sp-2;
24+
}
25+
}
26+
}
527

628
.educationCardHeader {
729
display: flex;

src/apps/profiles/src/member-profile/education-and-certifications/EducationCard/EducationCard.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { FC } from 'react'
2+
import classNames from 'classnames'
23
import moment from 'moment'
34

45
import { UserTrait } from '~/libs/core'
@@ -7,10 +8,11 @@ import styles from './EducationCard.module.scss'
78

89
interface EducationCardProps {
910
education: UserTrait
11+
isModalView?: boolean
1012
}
1113

1214
const EducationCard: FC<EducationCardProps> = (props: EducationCardProps) => (
13-
<div className={styles.educationCard}>
15+
<div className={classNames(styles.educationCard, props.isModalView ? styles.educationCardModalView : '')}>
1416
<div className={styles.educationCardHeader}>
1517
<div className={styles.educationCardHeaderLeft}>
1618
<p className='body-main-bold'>

src/apps/profiles/src/member-profile/education-and-certifications/ModifyEducationModal/ModifyEducationModal.module.scss

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,25 @@
33
.container {
44
display: flex;
55
flex-direction: column;
6+
align-items: flex-start;
67

78
.educationWrap {
8-
margin-bottom: $sp-8;
9+
margin: $sp-8 0;
10+
display: grid;
11+
grid-template-columns: 1fr 1fr;
12+
gap: $sp-4;
13+
width: 100%;
914

1015
&.noItems {
1116
margin-bottom: 0;
1217
}
1318

1419
.educationCardWrap {
1520
display: flex;
16-
align-items: center;
21+
align-items: flex-start;
22+
border: 1px solid $black-20;
23+
border-radius: 8px;
24+
padding: $sp-4;
1725

1826
.actionElements {
1927
display: flex;
@@ -60,27 +68,13 @@
6068

6169
.formWrap {
6270
margin-top: $sp-4;
71+
width: 100%;
6372

6473
@include ltelg {
6574
:global(.input-wrapper) {
6675
margin-bottom: $sp-2;
6776
}
6877
}
69-
70-
.formCTAs {
71-
display: flex;
72-
align-items: center;
73-
74-
svg {
75-
width: 14px;
76-
height: 14px;
77-
margin-right: $sp-1;
78-
}
79-
80-
.ctaBtnCancel {
81-
margin-left: $sp-8;
82-
}
83-
}
8478
}
8579
}
8680

0 commit comments

Comments
 (0)