Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
background-image: url("../../assets/background.png");
background-size: cover;
background-repeat: no-repeat;

.contentLayout-outer {
width: 100%;

Expand Down Expand Up @@ -92,5 +92,5 @@
height: 24px;
color: $black-40;
stroke: $black-40;
margin-right: 24px;
margin-right: 10px;
}
222 changes: 53 additions & 169 deletions src/apps/talent-search/src/routes/talent-search/TalentSearch.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,40 @@
/* eslint-disable react/jsx-no-bind */
import {
CSSProperties,
FC,
useState,
} from 'react'
import { components, ControlProps, GroupBase,
MultiValue, Options, SingleValue, StylesConfig } from 'react-select'
import AsyncSelect from 'react-select/async'
import PropTypes from 'prop-types'
import { FC, useMemo, useState } from 'react'

import { SearchIcon } from '@heroicons/react/outline'
import { ContentLayout } from '~/libs/ui'
import { ContentLayout, IconOutline, InputMultiselectOption } from '~/libs/ui'
import { EmsiSkill, EmsiSkillSources, InputSkillSelector } from '~/libs/shared'
import { Skill } from '@talentSearch/lib/models/'
import MatcherService from '@talentSearch/lib/services/MatcherService'

import SkillPill from './components/SkillPill'
import styles from './TalentSearch.module.scss'

function search(skills:Options<Skill>): void {
alert(JSON.stringify(skills))
}

// eslint-disable-next-line react/destructuring-assignment, @typescript-eslint/typedef
const Control: React.FC<ControlProps<Skill, boolean, GroupBase<Skill>>> = ({ children, ...props }) => (
<components.Control {...props}>
{children}
<span
onClick={() => search(props.getValue())}
className={styles.searchIconSpan}
>
<SearchIcon className={styles.searchIcon} />
</span>
</components.Control>
)

Control.propTypes = {
children: PropTypes.node.isRequired,
getValue: PropTypes.func.isRequired,
}
// TODO: Make this configurable, or read from a service. We need to discuss
// how we want to handle this.
const popularSkills:Skill[][] = [
[{ emsiId: 'KS441LF7187KS0CV4B6Y', name: 'Typescript' },
{ emsiId: 'KS1244K6176NLVWV02B6', name: 'Front-End Engineering' },
{ emsiId: 'KS1214R5XG4X4PY7LGY6', name: 'Bootstrap (Front-End Framework)' }],
[{ emsiId: 'KS121F45VPV8C9W3QFYH', name: 'Cascading Style Sheets (CSS)' },
{ emsiId: 'KS1200771D9CR9LB4MWW', name: 'JavaScript (Programming Language)' }],
[{ emsiId: 'KS1200578T5QCYT0Z98G', name: 'HyperText Markup Language (HTML)' },
{ emsiId: 'ES86A20379CD2AD061F3', name: 'IOS Development' },
{ emsiId: 'KS127296VDYS7ZFWVC46', name: 'Node.js' }],
[{ emsiId: 'ES50D03AC9CFC1A0BC93', name: '.NET Development' },
{ emsiId: 'KS1219W70LY1GXZDSKW5', name: 'C++ (Programming Language)' },
{ emsiId: 'KS127SZ60YZR8B5CQKV1', name: 'PHP Development' }],
[{ emsiId: 'KS1206V6K46N1SDVJGBD', name: 'Adobe Illustrator' },
{ emsiId: 'ESD07FEE22E7EC094EB8', name: 'Ruby (Programming Language)' },
{ emsiId: 'KS120076FGP5WGWYMP0F', name: 'Java (Programming Language)' }],
[{ emsiId: 'KSPSGF5MXB6568UIQ4BK', name: 'React Native' },
{ emsiId: 'KS441PL6JPXW200W0GRQ', name: 'User Experience (UX)' }],
]

export const TalentSearch: FC = () => {
const [skillsFilter, setSkillsFilter] = useState<Array<Skill>>([])
const emsiSkills: EmsiSkill[] = useMemo(() => skillsFilter.map(s => ({
name: s.name,
skillId: s.emsiId,
skillSources: [EmsiSkillSources.selfPicked],
})), [skillsFilter])

function toggleSkill(skill:Skill): void {
let newFilter: Array<Skill> = []
Expand All @@ -63,12 +57,12 @@ export const TalentSearch: FC = () => {
}
}

function onChange(options:MultiValue<Skill> | SingleValue<Skill>): void {
if (Array.isArray(options)) {
setSkillsFilter(options)
} else {
setSkillsFilter([])
}
function onChange(ev: any): void {
const options = (ev.target.value as unknown) as InputMultiselectOption[]
setSkillsFilter(options.map(v => ({
emsiId: v.value,
name: v.label as string,
})))
}

function filteringSkill(skill:Skill): boolean {
Expand All @@ -82,105 +76,6 @@ export const TalentSearch: FC = () => {
return result
}

// TODO: Make this configurable, or read from a service. We need to discuss
// how we want to handle this.
const popularSkills:Skill[][] = [
[{ emsiId: 'KS441LF7187KS0CV4B6Y', name: 'Typescript' },
{ emsiId: 'KS1244K6176NLVWV02B6', name: 'Front-End Engineering' },
{ emsiId: 'KS1214R5XG4X4PY7LGY6', name: 'Bootstrap (Front-End Framework)' }],
[{ emsiId: 'KS121F45VPV8C9W3QFYH', name: 'Cascading Style Sheets (CSS)' },
{ emsiId: 'KS1200771D9CR9LB4MWW', name: 'JavaScript (Programming Language)' }],
[{ emsiId: 'KS1200578T5QCYT0Z98G', name: 'HyperText Markup Language (HTML)' },
{ emsiId: 'ES86A20379CD2AD061F3', name: 'IOS Development' },
{ emsiId: 'KS127296VDYS7ZFWVC46', name: 'Node.js' }],
[{ emsiId: 'ES50D03AC9CFC1A0BC93', name: '.NET Development' },
{ emsiId: 'KS1219W70LY1GXZDSKW5', name: 'C++ (Programming Language)' },
{ emsiId: 'KS127SZ60YZR8B5CQKV1', name: 'PHP Development' }],
[{ emsiId: 'KS1206V6K46N1SDVJGBD', name: 'Adobe Illustrator' },
{ emsiId: 'ESD07FEE22E7EC094EB8', name: 'Ruby (Programming Language)' },
{ emsiId: 'KS120076FGP5WGWYMP0F', name: 'Java (Programming Language)' }],
[{ emsiId: 'KSPSGF5MXB6568UIQ4BK', name: 'React Native' },
{ emsiId: 'KS441PL6JPXW200W0GRQ', name: 'User Experience (UX)' }],
]

const controlStyle: CSSProperties = {
borderColor: 'black',
paddingBottom: '10px',
paddingTop: '10px',
}

const placeholderStyle: CSSProperties = {
color: '#2A2A2A',
fontFamily: 'Roboto',
fontSize: '16',
fontWeight: 400,
height: '36px',
paddingTop: '4px',
}

const multiValueStyle: CSSProperties = {
backgroundColor: 'white',
border: '1px solid #d4d4d4',
borderRadius: '24px',
color: '#333',
fontFamily: 'Roboto',
fontSize: '14',
fontWeight: '400',
height: '32px',
marginRight: '10px',
paddingLeft: '8px',
paddingRight: '8px',
}

const multiValueRemoveStyle: CSSProperties = {
backgroundColor: '#d9d9d9',
border: '1px solid #d4d4d4',
borderRadius: '11px',
color: '#333',
fontSize: '12',
height: '12px',
marginBottom: 'auto',
marginLeft: '5px',
marginRight: '5px',
marginTop: 'auto',
padding: '0px',
width: '12px',
}

const hiddenStyle: CSSProperties = {
display: 'none',
}

const selectStyle: StylesConfig<Skill> = {
clearIndicator: provided => ({
...provided,
...hiddenStyle,
}),
control: provided => ({
...provided,
...controlStyle,
}),
dropdownIndicator: provided => ({
...provided,
...hiddenStyle,
}),
indicatorSeparator: provided => ({
...provided,
...hiddenStyle,
}),
multiValue: provided => ({
...provided,
...multiValueStyle,
}),
multiValueRemove: provided => ({
...provided,
...multiValueRemoveStyle,
}),
placeholder: provided => ({
...provided,
...placeholderStyle,
}),
}
return (
<ContentLayout
contentClass={styles.contentLayout}
Expand All @@ -197,42 +92,31 @@ export const TalentSearch: FC = () => {
</div>
<div className={styles.searchOptions}>
<span className={styles.searchPrompt}>Search by skills</span>
<AsyncSelect
isMulti
cacheOptions
autoFocus
defaultOptions
<InputSkillSelector
placeholder='Enter skills you are searching for...'
loadOptions={MatcherService.autoCompleteSkills}
name='skills'
styles={selectStyle}
className={styles.searchSelect}
getOptionLabel={(skill: Skill) => skill.name}
getOptionValue={(skill: Skill) => skill.emsiId}
components={{ Control }}
openMenuOnClick={false}
value={skillsFilter}
onChange={(
newValue: MultiValue<Skill> | SingleValue<Skill>,
) => onChange(newValue)}
useWrapper={false}
theme='clear'
dropdownIcon={<IconOutline.SearchIcon className={styles.searchIcon} />}
value={emsiSkills}
onChange={onChange}
/>
</div>
<div className={styles.popularSkillsContainer}>
<span className={styles.popularSkillsTitle}>Popular Skills</span>

{popularSkills.map(
row => (
<div className={styles.pillRow}>
{row.map(skill => (
<SkillPill
skill={skill}
selected={filteringSkill(skill)}
onClick={toggleSkill}
/>
))}
</div>
),
)}
{popularSkills.map((row, i) => (
// eslint-disable-next-line react/no-array-index-key
<div className={styles.pillRow} key={i}>
{row.map(skill => (
<SkillPill
key={skill.emsiId}
skill={skill}
selected={filteringSkill(skill)}
onClick={toggleSkill}
/>
))}
</div>
))}
</div>
</ContentLayout>
)
Expand Down
1 change: 1 addition & 0 deletions src/libs/shared/lib/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './contact-support-form'
export * from './modals'
export * from './input-skill-selector'
export * from './member-skill-editor'
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChangeEvent, FC } from 'react'
import { ChangeEvent, FC, ReactNode } from 'react'
import { noop } from 'lodash'

import { InputMultiselect, InputMultiselectOption } from '~/libs/ui'
import { InputMultiselect, InputMultiselectOption, InputMultiselectThemes } from '~/libs/ui'

import { autoCompleteSkills, EmsiSkill, EmsiSkillSources } from '../../services/emsi-skills'

Expand Down Expand Up @@ -29,21 +29,29 @@ const fetchSkills = (queryTerm: string): Promise<Option[]> => (

interface InputSkillSelectorProps {
readonly limit?: number
readonly label?: string
readonly loading?: boolean
readonly value?: EmsiSkill[]
readonly onChange?: (event: ChangeEvent<HTMLInputElement>) => void
readonly placeholder?: string
readonly value?: EmsiSkill[]
readonly theme?: InputMultiselectThemes
readonly useWrapper?: boolean
readonly dropdownIcon?: ReactNode
}

const InputSkillSelector: FC<InputSkillSelectorProps> = props => (
<InputMultiselect
label='Select Skills'
label={props.label ?? 'Select Skills'}
limit={props.limit}
placeholder='Type to add a skill...'
placeholder={props.placeholder ?? 'Type to add a skill...'}
onFetchOptions={fetchSkills}
name='skills'
onChange={props.onChange ?? noop}
value={props.value?.map(mapEmsiSkillToInputOption)}
loading={props.loading}
theme={props.theme}
useWrapper={props.useWrapper}
dropdownIcon={props.dropdownIcon}
/>
)

Expand Down
1 change: 1 addition & 0 deletions src/libs/shared/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './components'
export * from './containers'
export * from './hooks'
export * from './services'
export * from './utils'
1 change: 1 addition & 0 deletions src/libs/shared/lib/services/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './emsi-skills'
Loading