mirror of
https://github.com/ershisan99/vacancies-trends-front.git
synced 2025-12-16 20:59:25 +00:00
get keywords from api to avoid duplication, optimize some code
This commit is contained in:
@@ -1,73 +1,91 @@
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { ALL_KEYWORDS, Keyword, KEYWORDS } from '~/services/vacancies/vacancies.constants'
|
||||
import { VacancyData } from '~/services/vacancies/vacancies.types'
|
||||
import { KeywordsResponse, VacancyData } from '~/services/vacancies/vacancies.types'
|
||||
import { AreaChart, MultiSelect, MultiSelectItem, Select, SelectItem } from '@tremor/react'
|
||||
import { useSearchParams } from '@remix-run/react'
|
||||
|
||||
type Props = {
|
||||
data: VacancyData
|
||||
data?: VacancyData
|
||||
keywords?: KeywordsResponse
|
||||
}
|
||||
|
||||
const presets = {
|
||||
None: [],
|
||||
All: ALL_KEYWORDS,
|
||||
Backend: KEYWORDS.BACKEND,
|
||||
Databases: KEYWORDS.DATABASES,
|
||||
DevOps: KEYWORDS.DEVOPS,
|
||||
Frontend: KEYWORDS.FRONTEND,
|
||||
'Frontend Frameworks': KEYWORDS.FRONTEND_FRAMEWORK,
|
||||
Mobile: KEYWORDS.MOBILE,
|
||||
ORM: KEYWORDS.ORM,
|
||||
Styles: KEYWORDS.STYLES,
|
||||
'State Management': KEYWORDS.STATE_MANAGEMENT,
|
||||
Testing: KEYWORDS.TESTING,
|
||||
}
|
||||
|
||||
const presetsForSelect = Object.entries(presets).map(
|
||||
([label, value]) =>
|
||||
({
|
||||
label,
|
||||
value,
|
||||
}) as const
|
||||
)
|
||||
|
||||
export function VacanciesChart({ data }: Props) {
|
||||
export function VacanciesChart({ data, keywords }: Props) {
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const [preset, setPreset] = useState('None' as keyof typeof presets)
|
||||
const [preset, setPreset] = useState('None')
|
||||
|
||||
const presetsForSelect = useMemo(
|
||||
() =>
|
||||
Object.entries(keywords?.presets ?? {}).map(
|
||||
([label, value]) =>
|
||||
({
|
||||
label,
|
||||
value,
|
||||
}) as const
|
||||
),
|
||||
[keywords?.presets]
|
||||
)
|
||||
|
||||
const selectedCategories = useMemo(
|
||||
() => searchParams.get('categories')?.split(',') || [],
|
||||
[searchParams]
|
||||
)
|
||||
|
||||
const setSelectedCategories = useCallback((value: Keyword[]) => {
|
||||
if (value.length === 0) {
|
||||
searchParams.delete('categories')
|
||||
} else {
|
||||
searchParams.set('categories', value.join(','))
|
||||
}
|
||||
const categoriesForChart = useMemo(() => {
|
||||
return data?.categories.filter(category => selectedCategories.includes(category)) ?? []
|
||||
}, [selectedCategories, data?.categories])
|
||||
|
||||
setSearchParams(searchParams)
|
||||
}, [])
|
||||
const setSelectedCategories = useCallback(
|
||||
(value: string[]) => {
|
||||
if (value.length === 0) {
|
||||
searchParams.delete('categories')
|
||||
} else {
|
||||
searchParams.set('categories', value.join(','))
|
||||
}
|
||||
|
||||
const sortedCategories = useMemo(
|
||||
() => sortCategoriesByVacancies(selectedCategories, data),
|
||||
[selectedCategories, data]
|
||||
setSearchParams(searchParams)
|
||||
},
|
||||
[searchParams, setSearchParams]
|
||||
)
|
||||
|
||||
const filteredData = useMemo(
|
||||
() =>
|
||||
data.filter(row => {
|
||||
data?.data?.filter(row => {
|
||||
for (const category of selectedCategories) {
|
||||
// @ts-expect-error
|
||||
if (row[category] > 0) {
|
||||
const value = row[category]
|
||||
if (typeof value === 'number' && value > 0) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}),
|
||||
[data, selectedCategories]
|
||||
}) ?? [],
|
||||
[data?.data, selectedCategories]
|
||||
)
|
||||
|
||||
const handlePresetChange = useCallback(
|
||||
(value: string) => {
|
||||
setPreset(value)
|
||||
setSelectedCategories(keywords?.presets[value] ?? [])
|
||||
},
|
||||
[keywords?.presets, setSelectedCategories]
|
||||
)
|
||||
|
||||
const handleKeywordsChange = useCallback(
|
||||
(value: string[]) => {
|
||||
setSelectedCategories(value ?? [])
|
||||
},
|
||||
[setSelectedCategories]
|
||||
)
|
||||
|
||||
const multiSelectItems = useMemo(() => {
|
||||
return keywords?.allKeywords.map(category => (
|
||||
<MultiSelectItem key={'category-select-' + category} value={category} />
|
||||
))
|
||||
}, [keywords?.allKeywords])
|
||||
|
||||
const presetSelectItems = useMemo(() => {
|
||||
return presetsForSelect.map(category => (
|
||||
<SelectItem key={'preset-select-item-' + category.label} value={category.label} />
|
||||
))
|
||||
}, [presetsForSelect])
|
||||
|
||||
return (
|
||||
<div className={'flex h-full flex-col gap-6 p-8'}>
|
||||
<div className={'flex gap-4'}>
|
||||
@@ -80,12 +98,10 @@ export function VacanciesChart({ data }: Props) {
|
||||
</label>
|
||||
<MultiSelect
|
||||
id={'categories'}
|
||||
onValueChange={value => setSelectedCategories(value)}
|
||||
onValueChange={handleKeywordsChange}
|
||||
value={selectedCategories}
|
||||
>
|
||||
{ALL_KEYWORDS.map(category => (
|
||||
<MultiSelectItem key={'category-select-' + category} value={category} />
|
||||
))}
|
||||
{multiSelectItems}
|
||||
</MultiSelect>
|
||||
</div>
|
||||
<div className="max-w-xl">
|
||||
@@ -99,14 +115,9 @@ export function VacanciesChart({ data }: Props) {
|
||||
value={preset}
|
||||
id={'presets'}
|
||||
defaultValue={'All'}
|
||||
onValueChange={value => {
|
||||
setPreset(value as keyof typeof presets)
|
||||
setSelectedCategories(presets[value as keyof typeof presets] as Keyword[])
|
||||
}}
|
||||
onValueChange={handlePresetChange}
|
||||
>
|
||||
{presetsForSelect.map(category => (
|
||||
<SelectItem key={category.label} value={category.label} />
|
||||
))}
|
||||
{presetSelectItems}
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -115,7 +126,7 @@ export function VacanciesChart({ data }: Props) {
|
||||
className="h-full"
|
||||
data={filteredData}
|
||||
index="date"
|
||||
categories={sortedCategories}
|
||||
categories={categoriesForChart}
|
||||
yAxisWidth={60}
|
||||
startEndOnly={false}
|
||||
intervalType="preserveStartEnd"
|
||||
@@ -124,23 +135,3 @@ export function VacanciesChart({ data }: Props) {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const sortCategoriesByVacancies = (categories: string[], data: VacancyData) => {
|
||||
const entryToCompare = data.at(-1)
|
||||
return categories.sort((a, b) => {
|
||||
if (!entryToCompare) {
|
||||
return 0
|
||||
}
|
||||
|
||||
if (entryToCompare[a] === undefined && entryToCompare[b] === undefined) {
|
||||
return 0
|
||||
}
|
||||
if (entryToCompare[a] > entryToCompare[b]) {
|
||||
return -1
|
||||
}
|
||||
if (entryToCompare[a] < entryToCompare[b]) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user