Skip to content

Commit 98a1acf

Browse files
authored
Merge pull request #724 from topcoder-platform/onboarding
Onboarding
2 parents 3934226 + bbf2b88 commit 98a1acf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+3965
-2
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ workflows:
262262
branches:
263263
only:
264264
- dev
265-
- talent-search
265+
- onboarding
266266

267267
- deployQa:
268268
context : org-global

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"vue.features.codeActions.enable": false
3+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
"react-helmet": "^6.1.0",
8484
"react-html-parser": "^2.0.2",
8585
"react-markdown": "8.0.6",
86+
"react-overlays": "^5.2.1",
8687
"react-redux": "^8.0.4",
8788
"react-redux-toastr": "^7.6.10",
8889
"react-responsive": "^9.0.0-beta.5",

src/apps/onboarding/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './src'
8.52 KB
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
* DateInput
3+
*
4+
* Date Input control.
5+
*/
6+
import { Portal } from 'react-overlays'
7+
import { createRef, FC, useState } from 'react'
8+
import DatePicker from 'react-datepicker'
9+
import cn from 'classnames'
10+
import moment from 'moment'
11+
import 'react-datepicker/dist/react-datepicker.css'
12+
13+
import { IconOutline } from '~/libs/ui'
14+
15+
import { ReactComponent as CalendarIcon } from '../../assets/images/calendar.svg'
16+
17+
import styles from './styles.module.scss'
18+
19+
interface CalendarContainerProps {
20+
children?: any
21+
}
22+
const CalendarContainer: FC<CalendarContainerProps> = (props: CalendarContainerProps) => {
23+
const el: any = document.getElementById('calendar-portal')
24+
25+
return <Portal container={el}>{props.children}</Portal>
26+
}
27+
28+
interface DateInputProps {
29+
style2?: boolean
30+
className?: string
31+
placeholder?: string
32+
value?: Date
33+
onChange?: (date: Date | null) => void
34+
onBlur?: () => void
35+
onFocus?: () => void
36+
allowFutureDate?: boolean
37+
disabled?: boolean
38+
}
39+
40+
const DateInput: FC<DateInputProps> = (props: DateInputProps) => {
41+
const [open, setOpen] = useState(false)
42+
const calendarRef: any = createRef<any>()
43+
return (
44+
<div
45+
className={cn(
46+
styles['datepicker-wrapper'],
47+
props.className,
48+
props.style2 ? styles.style2 : '',
49+
)}
50+
>
51+
<div
52+
onClick={function openCalendar() {
53+
calendarRef.current.setOpen(true)
54+
}}
55+
className={cn(styles.icon, styles['icon-calendar'])}
56+
>
57+
<CalendarIcon />
58+
</div>
59+
<DatePicker
60+
ref={calendarRef}
61+
dateFormat='MM/dd/yyyy'
62+
placeholderText={props.placeholder}
63+
selected={props.value}
64+
onChange={props.onChange as any}
65+
onBlur={props.onBlur}
66+
onCalendarClose={function closeCalendar() {
67+
setOpen(false)
68+
}}
69+
onFocus={props.onFocus}
70+
showYearDropdown
71+
dropdownMode='select'
72+
onCalendarOpen={function openCalendar() {
73+
setOpen(true)
74+
}}
75+
maxDate={
76+
props.allowFutureDate ? undefined : moment()
77+
.subtract(1, 'days')
78+
.toDate()
79+
}
80+
disabled={props.disabled}
81+
popperContainer={CalendarContainer}
82+
/>
83+
<div
84+
className={cn(
85+
styles.icon,
86+
styles['icon-arrow'],
87+
open ? styles['icon-arrow-open'] : '',
88+
)}
89+
onClick={function openCalendar() {
90+
calendarRef.current.setOpen(true)
91+
}}
92+
>
93+
<IconOutline.ChevronDownIcon />
94+
</div>
95+
</div>
96+
)
97+
}
98+
99+
export default DateInput
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
@import '@libs/ui/styles/includes';
2+
3+
.datepicker-wrapper {
4+
position: relative;
5+
padding: 0 10px;
6+
height: 36px;
7+
display: flex;
8+
align-items: center;
9+
10+
.icon {
11+
position: absolute;
12+
display: flex;
13+
padding: 8px 0 8px 4px;
14+
align-items: center;
15+
16+
&>svg {
17+
width: 20px;
18+
height: 20px;
19+
}
20+
}
21+
22+
.icon-calendar {
23+
left: 8px;
24+
cursor: pointer;
25+
}
26+
27+
.icon-arrow {
28+
right: 8px;
29+
top: 0;
30+
31+
&>svg {
32+
color: hsl(0, 0%, 80%);
33+
}
34+
35+
&:hover {
36+
&>svg {
37+
color: hsl(0, 0%, 60%);
38+
}
39+
}
40+
41+
&.icon-arrow-open {
42+
&>svg {
43+
color: hsl(0, 0%, 40%);
44+
}
45+
}
46+
}
47+
48+
&.error {
49+
input {
50+
border-color: #fe665d;
51+
}
52+
}
53+
54+
&>div:nth-child(2) {
55+
margin-left: 24px;
56+
}
57+
58+
&.style2 input {
59+
border: none !important;
60+
box-shadow: none !important;
61+
margin-bottom: 0 !important;
62+
font-size: 14px;
63+
font-weight: 400;
64+
65+
&::placeholder {
66+
color: $black-60;
67+
font-size: 14px;
68+
font-weight: 400;
69+
text-transform: none !important;
70+
opacity: 1;
71+
}
72+
}
73+
}
74+
75+
.datepicker-wrapper>div:nth-child(2)>div:nth-child(2)>div:nth-child(2) {
76+
z-index: 100;
77+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/**
2+
* FieldAvatar
3+
*
4+
* A Form Field Is a wrapper for input to add the label to it
5+
*/
6+
import { Dispatch, FC, SetStateAction, useEffect, useState } from 'react'
7+
import classNames from 'classnames'
8+
9+
import { Button, IconOutline } from '~/libs/ui'
10+
11+
import AvatarPlaceholder from '../../assets/images/avatar-placeholder.png'
12+
import MemberInfo from '../../models/MemberInfo'
13+
import ModalUploadPhoto from '../modal-upload-photo'
14+
15+
import styles from './styles.module.scss'
16+
17+
interface FieldAvatarProps {
18+
className?: string
19+
memberInfo?: MemberInfo,
20+
setMemberPhotoUrl: (photoUrl: string) => void
21+
updateMemberPhotoUrl: (photoUrl: string) => void
22+
}
23+
24+
const FieldAvatar: FC<FieldAvatarProps> = (props: FieldAvatarProps) => {
25+
const [imgUrl, setImgUrl] = useState<string>('')
26+
useEffect(() => {
27+
if (props.memberInfo) {
28+
setImgUrl(props.memberInfo.photoURL)
29+
}
30+
/* eslint-disable react-hooks/exhaustive-deps */
31+
}, [props.memberInfo])
32+
33+
const [isPhotoEditMode, setIsPhotoEditMode]: [boolean, Dispatch<SetStateAction<boolean>>]
34+
= useState<boolean>(false)
35+
const [isSaving, setIsSaving]: [boolean, Dispatch<SetStateAction<boolean>>]
36+
= useState<boolean>(false)
37+
38+
function handleModifyPhotoModalClose(): void {
39+
setIsPhotoEditMode(false)
40+
}
41+
42+
async function handleRemovePhoto(): Promise<void> {
43+
setIsSaving(true)
44+
try {
45+
await props.updateMemberPhotoUrl('')
46+
} catch (error) {
47+
}
48+
49+
setIsSaving(false)
50+
}
51+
52+
function showEditPhoto(): void {
53+
setIsPhotoEditMode(true)
54+
}
55+
56+
return (
57+
<div
58+
className={classNames(styles.container, props.className, 'd-flex flex-column gap-20 align-items-start')}
59+
>
60+
<h3>Photo</h3>
61+
<div className='d-flex gap-30'>
62+
<div className={classNames(
63+
'd-flex',
64+
styles.blockImg,
65+
{
66+
[styles.haveImg]: !!imgUrl,
67+
},
68+
)}
69+
>
70+
{imgUrl ? (
71+
<img className={styles.img} src={imgUrl} alt='avatar' />
72+
) : (
73+
<img className={styles.imgPlaceholder} src={AvatarPlaceholder} alt='avatar' />
74+
)}
75+
</div>
76+
<div className='d-flex flex-column align-items-start'>
77+
<span className='color-black-60'>
78+
Make a great first impression to potential customers with a
79+
professional photo that represents your style.
80+
</span>
81+
{imgUrl ? (
82+
<div className='d-flex gap-8'>
83+
<Button
84+
size='lg'
85+
secondary
86+
iconToLeft
87+
icon={IconOutline.UploadIcon}
88+
disabled={!props.memberInfo || isSaving}
89+
onClick={showEditPhoto}
90+
className='mt-16'
91+
>
92+
change
93+
</Button>
94+
<Button
95+
size='lg'
96+
secondary
97+
iconToLeft
98+
icon={IconOutline.TrashIcon}
99+
disabled={!props.memberInfo || isSaving}
100+
onClick={handleRemovePhoto}
101+
className='mt-16'
102+
>
103+
delete
104+
</Button>
105+
</div>
106+
) : (
107+
<Button
108+
size='lg'
109+
secondary
110+
iconToLeft
111+
icon={IconOutline.UploadIcon}
112+
disabled={!props.memberInfo}
113+
onClick={showEditPhoto}
114+
className='mt-16'
115+
>
116+
add image
117+
</Button>
118+
)}
119+
</div>
120+
</div>
121+
122+
{
123+
isPhotoEditMode && props.memberInfo && (
124+
<ModalUploadPhoto
125+
onClose={handleModifyPhotoModalClose}
126+
memberInfo={props.memberInfo}
127+
setMemberPhotoUrl={props.setMemberPhotoUrl}
128+
/>
129+
)
130+
}
131+
</div>
132+
)
133+
}
134+
135+
export default FieldAvatar
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
.container {
2+
strong {
3+
font-weight: bold;
4+
}
5+
6+
ul {
7+
list-style: unset;
8+
padding-left: 30px;
9+
}
10+
}
11+
12+
.blockImg {
13+
width: 120px;
14+
height: 120px;
15+
display: flex;
16+
border-radius: 100%;
17+
overflow: hidden;
18+
border: 3px dashed white;
19+
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.09);
20+
flex-shrink: 0;
21+
22+
.img {
23+
width: 100%;
24+
height: 100%;
25+
object-fit: contain;
26+
}
27+
28+
.imgPlaceholder {
29+
width: 144px;
30+
height: 144px;
31+
object-fit: contain;
32+
max-width: none;
33+
margin-left: -15px;
34+
margin-top: -11px;
35+
}
36+
}

0 commit comments

Comments
 (0)