Skip to content

Commit

Permalink
add mapper to ui data #133
Browse files Browse the repository at this point in the history
  • Loading branch information
ukorvl committed Dec 5, 2023
1 parent 4973ab9 commit 0966c7c
Show file tree
Hide file tree
Showing 15 changed files with 10,338 additions and 5,371 deletions.
15,472 changes: 10,138 additions & 5,334 deletions site/package-lock.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions site/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@
"dayjs": "^1.11.9",
"fs": "^0.0.1-security",
"gsap": "^3.12.1",
"html-to-text": "^9.0.5",
"i": "^0.3.7",
"immer": "^10.0.3",
"lodash.debounce": "^4.0.8",
"lodash.flatten": "^4.4.0",
"lodash.uniq": "^4.5.0",
"lottie-web": "^5.12.2",
"next": "^13.4.7",
"npm": "^10.2.4",
"prop-types": "^15.8.1",
"qs": "^6.11.2",
"react": "^18.2.0",
Expand All @@ -35,8 +39,10 @@
},
"devDependencies": {
"@biomejs/biome": "1.2.2",
"@types/html-to-text": "^9.0.4",
"@types/lodash.debounce": "^4.0.7",
"@types/lodash.flatten": "^4.4.7",
"@types/lodash.uniq": "^4.5.9",
"@types/node": "^20.5.7",
"@types/qs": "^6.9.7",
"@types/showdown": "^2.0.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
}

.remoteToggleContainer {
grid-column: 1 / 4;
grid-column: 1 / 2;
grid-row: 2 / 3;
}

Expand Down
48 changes: 39 additions & 9 deletions site/src/components/pages/OpenPositions/Filter/Filter.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,63 @@
import { Checkbox, Select, Input, SearchIcon } from '@nilfoundation/ui-kit'
import { Checkbox, Select, SELECT_SIZE } from '@nilfoundation/ui-kit'
import { PositionsFilter } from '../types'
import { produce } from 'immer'
import s from './Filter.module.scss'
import { TextFilter } from './TextFilter'

type FilterProps = {
filter: PositionsFilter
setFilter: (filter: PositionsFilter) => void
departments: string[]
locations: string[]
types: string[]
}

export const Filter = ({ filter, setFilter }: FilterProps) => {
const setDepartment = (department: string) =>
export const Filter = ({ filter, setFilter, departments, locations, types }: FilterProps) => {
const setFilterValue = (key: keyof PositionsFilter, value: PositionsFilter[typeof key]) =>
setFilter(
produce(filter, (draft) => {
draft.department = department
;(draft[key] as PositionsFilter[typeof key]) = value
}),
)

const departmentsOptions = departments.map((department) => ({
label: department,
value: department,
}))

const locationsOptions = locations.map((location) => ({
label: location,
value: location,
}))

const typesOptions = types.map((type) => ({
label: type,
value: type,
}))

return (
<div className={s.filter}>
<Select />
<Select />
<Select />
<Select
placeholder="Choose Department"
size={SELECT_SIZE.small}
options={departmentsOptions}
onChange={({ value }) => setFilterValue('department', (value.at(0)?.id as string) ?? '')}
searchable={false}
/>
<Select placeholder="Choose Work Type" size={SELECT_SIZE.small} options={typesOptions} searchable={false} />
<Select placeholder="Choose Location" size={SELECT_SIZE.small} options={locationsOptions} searchable={false} />
<div className={s.remoteToggleContainer}>
<Checkbox checkmarkType="toggle" labelPlacement="left" checked={filter.remote}>
<Checkbox
checkmarkType="toggle"
labelPlacement="left"
checked={filter.remote}
onChange={({ target }) => setFilterValue('remote', target.checked)}
>
Remote only
</Checkbox>
</div>
<div className={s.titleTitleFilterContainer}>
<Input placeholder="Search Job Title" startEnhancer={<SearchIcon />} />
<TextFilter setFilterValue={(value) => setFilterValue('title', value)} />
</div>
</div>
)
Expand Down
30 changes: 30 additions & 0 deletions site/src/components/pages/OpenPositions/Filter/TextFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { ReactElement } from 'react'
import { useState, useRef } from 'react'
import debounce from 'lodash.debounce'
import { INPUT_SIZE, Input, SearchIcon } from '@nilfoundation/ui-kit'

type TextFilterProps = {
setFilterValue: (value: string) => void
}

export const TextFilter = ({ setFilterValue }: TextFilterProps): ReactElement => {
const [value, setValue] = useState('')
const debouncedSearch = useRef(
debounce((value) => {
setFilterValue(value ?? '')
}, 200),
).current

return (
<Input
placeholder="Search by title"
value={value}
onChange={(e) => {
setValue(e.target.value)
debouncedSearch(e.target.value)
}}
startEnhancer={<SearchIcon />}
size={INPUT_SIZE.small}
/>
)
}
26 changes: 21 additions & 5 deletions site/src/components/pages/OpenPositions/OpenPositions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,24 @@ import Icon from 'components/Icon'

import s from './OpenPositions.module.scss'
import DottedSection from './DottedSection'
import { Position as PositionType } from 'src/freshteam/types'
import { UIPosition } from 'src/freshteam/types'
import { HeadingXLarge, HeadingXXLarge, LabelMedium, PRIMITIVE_COLORS } from '@nilfoundation/ui-kit'
import { getPageTitleOverrides, getCommonHeadingOverrides } from './overrides'
import { useGroupPositionsByDepartments } from './useGroupPositionsByDepartments'
import { Filter } from './Filter/Filter'
import { Card } from 'components/Card'
import { useFilterPositions } from './useFilterPositions'
import { useState } from 'react'
import { PositionsFilter } from './types'
import { Position } from './Position/Position'
import { mapTypeToDisplayType } from './utils/mapTypeToDisplayType'
import uniq from 'lodash.uniq'

type OpenPositionsProps = {
jobsPostings: PositionType[]
jobsPostings: UIPosition[]
}

const departmensOrder = ['Engineering', 'Developer Relations', 'Marketing', 'Human Resources']

const OpenPositions = ({ jobsPostings = [] }: OpenPositionsProps) => {
const { isMobile } = useViewport()
const [filter, setFilter] = useState<PositionsFilter>({
Expand All @@ -32,10 +35,16 @@ const OpenPositions = ({ jobsPostings = [] }: OpenPositionsProps) => {
title: '',
remote: true,
})

const filteredJobsPositions = useFilterPositions(jobsPostings, filter)
const positionsByDepartmentMap = useGroupPositionsByDepartments(filteredJobsPositions)
const sortedByTitleJobsPostings = filteredJobsPositions.sort((a, b) => a.title.localeCompare(b.title))
const positionsByDepartmentMap = useGroupPositionsByDepartments(sortedByTitleJobsPostings, departmensOrder)
const departments = Object.keys(positionsByDepartmentMap)

const availableDepartments = uniq(jobsPostings.map((p) => p.department).filter((d) => !!d))
const availableLocations = uniq(jobsPostings.map((p) => p.branch.city).filter((d) => !!d))
const availableTypes = uniq(jobsPostings.map((p) => mapTypeToDisplayType(p.type)).filter((d) => !!d))

return (
<>
<Container className={s.root}>
Expand All @@ -57,7 +66,13 @@ const OpenPositions = ({ jobsPostings = [] }: OpenPositionsProps) => {
<div className={s.content}>
<div className={s.wrapper}>
<HeadingXXLarge overrides={getPageTitleOverrides()}>Open Positions</HeadingXXLarge>
<Filter filter={filter} setFilter={setFilter} />
<Filter
filter={filter}
setFilter={setFilter}
departments={availableDepartments}
locations={availableLocations}
types={availableTypes}
/>
{departments.length === 0 && (
<div>
<LabelMedium justifyContent="center" color={PRIMITIVE_COLORS.gray300}>
Expand All @@ -67,6 +82,7 @@ const OpenPositions = ({ jobsPostings = [] }: OpenPositionsProps) => {
)}
{departments.map((department) => {
const positions = positionsByDepartmentMap[department]

return (
<>
<div key={department} className={s.department}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.position {
margin-bottom: 24px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 32px;
width: 100%;
height: 198px
Expand All @@ -9,4 +10,14 @@
.type {
display: flex;
gap: 10px;
}

.title {
grid-column: 1 / 2;
}

.description {
grid-column: 2 / 3;
height: 48px;
overflow: hidden;
}
19 changes: 13 additions & 6 deletions site/src/components/pages/OpenPositions/Position/Position.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import { Card } from 'components/Card'
import s from './Position.module.scss'
import { Position as PositionType } from 'src/freshteam/types'
import { LabelMedium, PRIMITIVE_COLORS } from '@nilfoundation/ui-kit'
import { labelOverrides } from './overrides'
import { UIPosition } from 'src/freshteam/types'
import { HeadingXLarge, LabelMedium, PRIMITIVE_COLORS } from '@nilfoundation/ui-kit'
import { labelOverrides, titleOverrides } from './overrides'
import { mapTypeToDisplayType } from '../utils/mapTypeToDisplayType'

type PositionProps = {
position: PositionType
position: UIPosition
}

export const Position = ({ position: { id, remote, type } }: PositionProps) => {
export const Position = ({ position: { id, remote, type, title, plainTextDescription } }: PositionProps) => {
return (
<Card key={id} className={s.position} href={{ query: { jobId: id } }}>
<div className={s.type}>
<LabelMedium overrides={labelOverrides}>{remote ? 'Remote' : 'Onsite'}</LabelMedium>
<LabelMedium overrides={labelOverrides}>{type === 'full_time' ? 'Full Time' : 'Part Time'}</LabelMedium>
<LabelMedium overrides={labelOverrides}>{mapTypeToDisplayType(type)}</LabelMedium>
</div>
<div className={s.title}>
<HeadingXLarge overrides={titleOverrides}>{title}</HeadingXLarge>
</div>
<div className={s.description}>
<LabelMedium color={PRIMITIVE_COLORS.gray300}>{plainTextDescription}</LabelMedium>
</div>
</Card>
)
Expand Down
11 changes: 11 additions & 0 deletions site/src/components/pages/OpenPositions/Position/overrides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ export const labelOverrides: BlockOverrides = {
style: {
color: PRIMITIVE_COLORS.gray300,
fontWeight: 400,
lineHeight: '16px',
},
},
}

export const titleOverrides: BlockOverrides = {
Block: {
style: {
color: PRIMITIVE_COLORS.gray50,
fontWeight: 400,
letterSpacing: '-0.96px',
},
},
}
14 changes: 8 additions & 6 deletions site/src/components/pages/OpenPositions/useFilterPositions.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { Job } from 'src/freshteam/types'
import { UIPosition } from 'src/freshteam/types'
import { PositionsFilter } from './types'

export const useFilterPositions = (positions: Job[], filter: PositionsFilter) => {
export const useFilterPositions = (positions: UIPosition[], filter: PositionsFilter) => {
const filteredPositions = positions.filter((position) => {
const titleFilter = filter.title ? position.title === filter.title : true
const departmentFilter = filter.department ? position.department.name === filter.department : true
const titleFilter = filter.title ? position.title.toLowerCase() === filter.title.toLowerCase() : true
const departmentFilter = filter.department
? position.department.toLowerCase() === filter.department.toLowerCase()
: true
const remoteFilter = filter.remote ? position.remote === filter.remote : true
const typeFilter = filter.type ? position.type === filter.type : true
const locationFilter = filter.location ? position.branch.city === filter.location : true
const typeFilter = filter.type ? position.type.toLowerCase() === filter.type.toLowerCase() : true
const locationFilter = filter.location ? position.branch.city.toLowerCase() === filter.location.toLowerCase() : true

return titleFilter && departmentFilter && remoteFilter && typeFilter && locationFilter
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
import { useMemo } from 'react'
import { Position } from 'src/freshteam/types'
import { UIPosition } from 'src/freshteam/types'

export const useGroupPositionsByDepartments = (positions: Position[]) => {
export const useGroupPositionsByDepartments = (positions: UIPosition[], order?: Array<UIPosition['department']>) => {
return useMemo(() => {
const departments = positions.reduce((acc, position) => {
const department = position.department.name
const department = position.department
const departmentPositions = acc[department] || []

return {
...acc,
[department]: [...departmentPositions, position],
}
}, {} as Record<string, Position[]>)
}, {} as Record<string, UIPosition[]>)

if (order) {
return order
.filter((x) => Object.keys(departments).includes(x))
.reduce((acc, department) => {
return {
...acc,
[department]: departments[department],
}
}, {} as Record<string, UIPosition[]>)
}

return departments
}, [positions])
}, [positions, order])
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Position } from 'src/freshteam/types'

export const mapTypeToDisplayType = (type: Position['type']): string => {
switch (type) {
case 'full_time':
return 'Full Time'
case 'part_time':
return 'Part Time'
default:
return ''
}
}
11 changes: 6 additions & 5 deletions site/src/freshteam/api.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { client } from './client'
import { Position, PositionStatus } from './types'
import { mapPositionToUIPosition } from './mappers'
import { PositionStatus, UIPosition } from './types'

interface Api {
getJobPostings(s: PositionStatus): Promise<Position[]>
getJobPostings(s: PositionStatus): Promise<UIPosition[]>
}

export const api = {
getJobPostings: async (status?: PositionStatus): Promise<Position[]> => {
getJobPostings: async (status?: PositionStatus): Promise<UIPosition[]> => {
let url = 'job_postings'

if (status) {
url += `?status=${status}`
}

const result = await client.get(url).then((res) => res)
console.log(Array.isArray(result), Object.keys(result))
return result.data

return result.data.map(mapPositionToUIPosition)
},
} satisfies Api
15 changes: 15 additions & 0 deletions site/src/freshteam/mappers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Position, UIPosition } from './types'
import { convert } from 'html-to-text'

export const mapPositionToUIPosition = (position: Position): UIPosition => {
return {
id: position.id,
title: position.title,
description: position.description,
plainTextDescription: convert(position.description, { wordwrap: false, limits: { maxBaseElements: 200 } }),
remote: position.remote,
type: position.type,
branch: position.branch,
department: position.department.name,
}
}
Loading

0 comments on commit 0966c7c

Please sign in to comment.