Skip to content

Commit 3e5f850

Browse files
committed
Initial implementation of talent search screen 1
1 parent fde1972 commit 3e5f850

File tree

7 files changed

+357
-31
lines changed

7 files changed

+357
-31
lines changed
1.25 MB
Loading
Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,96 @@
1-
@import "@libs/ui/styles/includes";
21
@import '@talentSearch/styles/variables';
32

43
.contentLayout {
54
width: 100%;
65
padding-top: 30px;
7-
6+
background-image: url("../../assets/background.png");
7+
background-size: cover;
8+
background-repeat: no-repeat;
9+
810
.contentLayout-outer {
911
width: 100%;
1012

1113
.contentLayout-inner {
1214
width: 100%;
15+
padding: 86px 48px 48px 48px;
1316
overflow: visible;
1417
}
1518
}
1619
}
1720

18-
.header{
19-
padding-bottom: 20px;
21+
.popularSkillsContainer{
22+
margin-top: 16px;
23+
margin-left: auto;
24+
margin-right: auto;
25+
text-align: center;
26+
}
27+
28+
.pillRow{
29+
margin-top: 20px;
30+
width:100%;
31+
margin-left:auto;
32+
margin-right:auto;
33+
}
34+
35+
.popularSkillsTitle{
36+
font-family: 'Roboto';
37+
font-weight: 700;
38+
font-size: 20px;
39+
line-height: 26px;
40+
text-align: center;
41+
color: #fff;
42+
}
43+
44+
.searchHeader{
45+
text-align: center;
46+
}
47+
48+
.searchHeaderText{
49+
font-family: 'Roboto';
50+
font-weight: 500;
51+
font-size: 30px;
52+
line-height: 20px;
53+
letter-spacing: 0.5px;
54+
color: white;
55+
}
56+
57+
.subHeader{
58+
padding-top: 49px;
59+
text-align: center;
2060
}
2161

22-
.options{
23-
max-width: 600px;
24-
padding-bottom: 30px;
25-
}
62+
.subHeaderText{
63+
font-family: 'Barlow';
64+
font-weight: 400;
65+
font-size: 25px;
66+
line-height: 30px;
67+
color: white;
68+
69+
}
70+
.searchPrompt{
71+
font-family: 'Roboto';
72+
font-weight: 700;
73+
font-size: 16px;
74+
line-height: 48px;
75+
color: white;
76+
}
77+
78+
.searchOptions{
79+
margin-left: auto;
80+
margin-right: auto;
81+
padding: 70px 32px 32px 32px;
82+
max-width: 931px;
83+
width: 100%;
84+
}
85+
86+
.searchIconSpan{
87+
cursor: pointer;
88+
}
89+
90+
.searchIcon{
91+
width: 24px;
92+
height: 24px;
93+
color: #aaaaaa;
94+
stroke: #aaaaaa;
95+
margin-right: 24px;
96+
}

src/apps/talent-search/src/routes/talent-search/TalentSearch.tsx

Lines changed: 211 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,247 @@
1+
/* eslint-disable react/destructuring-assignment */
2+
/* eslint-disable @typescript-eslint/no-unused-vars */
3+
/* eslint-disable arrow-body-style */
4+
/* eslint-disable @typescript-eslint/typedef */
5+
/* eslint-disable indent */
6+
/* eslint-disable sort-keys */
17
/* eslint-disable ordered-imports/ordered-imports */
28
/* eslint-disable react/jsx-no-bind */
39
import {
410
FC,
511
useState,
12+
CSSProperties
613
} from 'react'
714

15+
import { components, ControlProps, Options, GroupBase, MultiValue, SingleValue, StylesConfig, ActionMeta } from 'react-select'
816
import AsyncSelect from 'react-select/async'
17+
918
import { ContentLayout } from '~/libs/ui'
10-
import MatcherService from '@talentSearch/lib/services/MatcherService'
1119
import { Skill } from '@talentSearch/lib/models/'
12-
import SkillSearchResults from './components/skill-search-results/SkillSearchResults'
13-
20+
import MatcherService from '@talentSearch/lib/services/MatcherService'
21+
import { SearchIcon } from '@heroicons/react/outline'
22+
import SkillPill from './components/SkillPill'
1423
import styles from './TalentSearch.module.scss'
1524

25+
function search(skills:Options<Skill>): void {
26+
alert("Searching skills: " + JSON.stringify(skills))
27+
}
28+
29+
const Control: React.FC<ControlProps<Skill, boolean, GroupBase<Skill>>> = ({children, ...props }) => (
30+
<components.Control {...props}>
31+
{children}
32+
<span onClick={() => search(props.getValue())} className={styles.searchIconSpan}><SearchIcon className={styles.searchIcon} /></span>
33+
</components.Control>
34+
)
35+
1636
export const TalentSearch: FC = () => {
17-
const [skillsFilter, setSkillsFilter] = useState<ReadonlyArray<Skill>>([])
37+
const [skillsFilter, setSkillsFilter] = useState<Array<Skill>>([])
38+
39+
function search(): void {
40+
alert("Searching for skills: " + JSON.stringify(skillsFilter))
41+
}
42+
43+
function toggleSkill(skill:Skill, pill:SkillPill): void {
44+
let newFilter: Array<Skill> = []
45+
let deleted: boolean = false
46+
if (skillsFilter) {
47+
// Either delete the value from the list, if we're toggling one that's already in the list
48+
// Or add the new item to the list
49+
skillsFilter.forEach((filterSkill, index) => {
50+
if (filterSkill.emsiId === skill.emsiId) {
51+
deleted = true
52+
}
53+
else {
54+
newFilter.push(filterSkill)
55+
}
56+
})
57+
if (deleted === false) {
58+
newFilter = skillsFilter.concat(skill)
59+
}
60+
61+
setSkillsFilter(newFilter)
62+
}
63+
}
64+
65+
function onChange(options:MultiValue<Skill> | SingleValue<Skill>, meta:ActionMeta<Skill>): void {
66+
if (Array.isArray(options)) {
67+
setSkillsFilter(options)
68+
}
69+
else {
70+
setSkillsFilter([])
71+
}
72+
}
73+
74+
function filteringSkill(skill:Skill): boolean {
75+
let result: boolean = false
76+
skillsFilter.forEach((filterSkill, index) => {
77+
if (filterSkill.emsiId === skill.emsiId) {
78+
result = true
79+
}
80+
})
1881

82+
return result
83+
}
84+
85+
const popularSkills:Skill[][] = [
86+
[{ name: 'Typescript', emsiId: 'KS441LF7187KS0CV4B6Y' },
87+
{ name: 'Front-End Engineering', emsiId: 'KS1244K6176NLVWV02B6' },
88+
{ name: 'Bootstrap (Front-End Framework)', emsiId: 'KS1214R5XG4X4PY7LGY6' }],
89+
[{ name: 'Cascading Style Sheets (CSS)', emsiId: 'KS121F45VPV8C9W3QFYH' },
90+
{ name: 'JavaScript (Programming Language)', emsiId: 'KS1200771D9CR9LB4MWW' }],
91+
[{ name: 'HyperText Markup Language (HTML)', emsiId: 'KS1200578T5QCYT0Z98G' },
92+
{ name: 'IOS Development', emsiId: 'ES86A20379CD2AD061F3' },
93+
{ name: 'Node.js', emsiId: 'KS127296VDYS7ZFWVC46' }],
94+
[{ name: '.NET Development', emsiId: 'ES50D03AC9CFC1A0BC93' },
95+
{ name: 'C++ (Programming Language)', emsiId: 'KS1219W70LY1GXZDSKW5' },
96+
{ name: 'PHP Development', emsiId: 'KS127SZ60YZR8B5CQKV1' }],
97+
[{ name: 'Adobe Illustrator', emsiId: 'KS1206V6K46N1SDVJGBD' },
98+
{ name: 'Ruby (Programming Language)', emsiId: 'ESD07FEE22E7EC094EB8' },
99+
{ name: 'Java (Programming Language)', emsiId: 'KS120076FGP5WGWYMP0F' }],
100+
[{ name: 'React Native', emsiId: 'KSPSGF5MXB6568UIQ4BK' },
101+
{ name: 'User Experience (UX)', emsiId: 'KS441PL6JPXW200W0GRQ' }],
102+
]
103+
104+
const controlStyle: CSSProperties = {
105+
borderColor: 'black',
106+
paddingTop: '10px',
107+
paddingBottom: '10px',
108+
}
109+
110+
const placeholderStyle: CSSProperties = {
111+
height: '36px',
112+
paddingTop: '4px',
113+
color: '#2A2A2A',
114+
fontSize: '16',
115+
fontFamily: 'Roboto',
116+
fontWeight: 400,
117+
}
118+
119+
120+
const multiValueStyle: CSSProperties = {
121+
backgroundColor: 'white',
122+
border: '1px solid #d4d4d4',
123+
color: '#333',
124+
borderRadius: '24px',
125+
height: '32px',
126+
fontFamily: 'Roboto',
127+
fontWeight: '400',
128+
fontSize: '14',
129+
paddingRight: '8px',
130+
paddingLeft: '8px',
131+
marginRight: '10px',
132+
}
133+
134+
const multiValueRemoveStyle: CSSProperties = {
135+
width: '12px',
136+
height: '12px',
137+
backgroundColor: '#d9d9d9',
138+
color: '#333',
139+
marginTop: 'auto',
140+
marginBottom: 'auto',
141+
marginRight: '5px',
142+
marginLeft: '5px',
143+
borderRadius: '11px',
144+
border: '1px solid #d4d4d4',
145+
fontSize: '12',
146+
padding: '0px',
147+
}
148+
149+
const hiddenStyle: CSSProperties = {
150+
display: 'none',
151+
}
152+
153+
const selectStyle: StylesConfig<Skill> = {
154+
control: (provided, state) => {
155+
return {
156+
...provided,
157+
...controlStyle,
158+
}
159+
},
160+
multiValue: (provided, state) => {
161+
return {
162+
...provided,
163+
...multiValueStyle,
164+
}
165+
},
166+
multiValueRemove: (provided, state) => {
167+
return {
168+
...provided,
169+
...multiValueRemoveStyle,
170+
}
171+
},
172+
clearIndicator: (provided, state) => {
173+
return {
174+
...provided,
175+
...hiddenStyle,
176+
}
177+
},
178+
dropdownIndicator: (provided, state) => {
179+
return {
180+
...provided,
181+
...hiddenStyle,
182+
}
183+
},
184+
indicatorSeparator: (provided, state) => {
185+
return {
186+
...provided,
187+
...hiddenStyle,
188+
}
189+
},
190+
placeholder: (provided, state) => {
191+
return {
192+
...provided,
193+
...placeholderStyle,
194+
}
195+
},
196+
}
19197
return (
20198
<ContentLayout
21199
contentClass={styles.contentLayout}
22200
outerClass={styles['contentLayout-outer']}
23201
innerClass={styles['contentLayout-inner']}
24202
>
25-
<div className={styles.header}>
26-
<h2>Talent search</h2>
203+
<div className={styles.searchHeader}>
204+
<span className={styles.searchHeaderText}>Looking for a technology expert?</span>
205+
</div>
206+
<div className={styles.subHeader}>
207+
<span className={styles.subHeaderText}>
208+
Search thousands of skills to match with our global experts.
209+
</span>
27210
</div>
28-
<div className={styles.options}>
29-
<h4>Select Skills:</h4>
211+
<div className={styles.searchOptions}>
212+
<span className={styles.searchPrompt}>Search by skills</span>
30213
<AsyncSelect
31214
isMulti
32215
cacheOptions
33216
autoFocus
34217
defaultOptions
35-
placeholder='Start typing to autocomplete available EMSI skills'
218+
placeholder='Enter skills you are searching for...'
36219
loadOptions={MatcherService.autoCompleteSkills}
37220
name='skills'
38-
className='basic-multi-select'
39-
classNamePrefix='select'
221+
styles={selectStyle}
222+
className={styles.searchSelect}
40223
getOptionLabel={(skill: Skill) => skill.name}
41224
getOptionValue={(skill: Skill) => skill.emsiId}
42-
onChange={(option: readonly Skill[]) => {
43-
setSkillsFilter(option)
44-
}}
225+
components={{ Control }}
226+
openMenuOnClick={false}
227+
value={skillsFilter}
228+
onChange={(newValue: MultiValue<Skill> | SingleValue<Skill>, actionMeta: ActionMeta<Skill>) =>
229+
onChange(newValue, actionMeta)}
45230
/>
46231
</div>
232+
<div className={styles.popularSkillsContainer}>
233+
<span className={styles.popularSkillsTitle}>Popular Skills</span>
47234

48-
<h2>Search Results</h2>
49-
<hr />
50-
<SkillSearchResults
51-
skillsFilter={skillsFilter}
52-
/>
53-
235+
{popularSkills.map(row =>
236+
<div className={styles.pillRow}>
237+
{row.map(skill =>
238+
<SkillPill skill={skill}
239+
selected={ filteringSkill(skill) }
240+
onClick={toggleSkill}
241+
/> )}
242+
</div>
243+
)}
244+
</div>
54245
</ContentLayout>
55246
)
56247
}

0 commit comments

Comments
 (0)