mirror of
https://github.com/ershisan99/vacancies-trends-front.git
synced 2026-02-02 12:35:58 +00:00
add grouping and totals
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { KeywordsResponse, VacancyData } from '~/services/vacancies/vacancies.types'
|
||||
import { AreaChart, MultiSelect, MultiSelectItem, Select, SelectItem } from '@tremor/react'
|
||||
import { GroupByPeriod, KeywordsResponse, VacancyData } from '~/services/vacancies/vacancies.types'
|
||||
import { AreaChart, MultiSelect, MultiSelectItem, Select, SelectItem, Switch } from '@tremor/react'
|
||||
import { useSearchParams } from '@remix-run/react'
|
||||
import { capitalize } from 'remeda'
|
||||
import { ChartTooltip } from '~/components/ui/chart-tooltip'
|
||||
|
||||
type Props = {
|
||||
data?: VacancyData
|
||||
@@ -32,6 +34,11 @@ export function VacanciesChart({ data, keywords }: Props) {
|
||||
return searchParams.get('preset') ?? 'None'
|
||||
}, [searchParams])
|
||||
|
||||
const showTotal = useMemo(() => searchParams.get('showTotal') === 'true', [searchParams])
|
||||
const groupBy: GroupByPeriod = useMemo(() => {
|
||||
return (searchParams.get('groupBy') ?? 'day') as GroupByPeriod
|
||||
}, [searchParams])
|
||||
|
||||
const setPreset = useCallback(
|
||||
(value: string | null) => {
|
||||
if (!value || value === 'None') {
|
||||
@@ -44,10 +51,27 @@ export function VacanciesChart({ data, keywords }: Props) {
|
||||
},
|
||||
[searchParams, setSearchParams]
|
||||
)
|
||||
const setShowTotal = useCallback(
|
||||
(value: boolean) => {
|
||||
if (value) {
|
||||
searchParams.set('showTotal', 'true')
|
||||
} else {
|
||||
searchParams.delete('showTotal')
|
||||
}
|
||||
setSearchParams(searchParams)
|
||||
},
|
||||
[searchParams, setSearchParams]
|
||||
)
|
||||
|
||||
const categoriesForChart = useMemo(() => {
|
||||
return data?.categories.filter(category => selectedCategories.includes(category)) ?? []
|
||||
}, [selectedCategories, data?.categories])
|
||||
const filtered = [
|
||||
...(data?.categories.filter(category => selectedCategories.includes(category)) ?? []),
|
||||
]
|
||||
if (showTotal) {
|
||||
filtered.unshift('total')
|
||||
}
|
||||
return filtered
|
||||
}, [selectedCategories, data?.categories, showTotal])
|
||||
|
||||
const setSelectedCategories = useCallback(
|
||||
(value: string[]) => {
|
||||
@@ -62,17 +86,42 @@ export function VacanciesChart({ data, keywords }: Props) {
|
||||
[searchParams, setSearchParams]
|
||||
)
|
||||
|
||||
const setGroupBy = useCallback(
|
||||
(value: string) => {
|
||||
if (!value || value === 'day') {
|
||||
searchParams.delete('groupBy')
|
||||
} else {
|
||||
searchParams.set('groupBy', value)
|
||||
}
|
||||
|
||||
setSearchParams(searchParams)
|
||||
},
|
||||
[searchParams, setSearchParams]
|
||||
)
|
||||
|
||||
const filteredData = useMemo(
|
||||
() =>
|
||||
data?.data?.filter(row => {
|
||||
for (const category of selectedCategories) {
|
||||
const value = row[category]
|
||||
if (typeof value === 'number' && value > 0) {
|
||||
return true
|
||||
data?.data
|
||||
?.filter(row => {
|
||||
for (const category of selectedCategories) {
|
||||
const value = row[category]
|
||||
if (typeof value === 'number' && value > 0) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}) ?? [],
|
||||
[data?.data, selectedCategories]
|
||||
if (showTotal) {
|
||||
const value = row['total']
|
||||
if (typeof value === 'number' && value > 0) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
})
|
||||
?.map(({ date, ...rest }) => ({
|
||||
...rest,
|
||||
date: new Date(date).toLocaleDateString('ru'),
|
||||
rawDate: date,
|
||||
})) ?? [],
|
||||
[data?.data, selectedCategories, showTotal, groupBy]
|
||||
)
|
||||
|
||||
const handlePresetChange = useCallback(
|
||||
@@ -103,6 +152,16 @@ export function VacanciesChart({ data, keywords }: Props) {
|
||||
))
|
||||
}, [presetsForSelect])
|
||||
|
||||
const groupBySelectItems = useMemo(
|
||||
() =>
|
||||
Object.entries(GroupByPeriod).map(([label, value]) => (
|
||||
<SelectItem key={'groupBy-select-item-' + label} value={value}>
|
||||
{capitalize(value)}
|
||||
</SelectItem>
|
||||
)),
|
||||
[]
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={'flex gap-4'}>
|
||||
@@ -121,6 +180,7 @@ export function VacanciesChart({ data, keywords }: Props) {
|
||||
{multiSelectItems}
|
||||
</MultiSelect>
|
||||
</div>
|
||||
|
||||
<div className="max-w-xl">
|
||||
<label
|
||||
htmlFor="presets"
|
||||
@@ -137,9 +197,31 @@ export function VacanciesChart({ data, keywords }: Props) {
|
||||
{presetSelectItems}
|
||||
</Select>
|
||||
</div>
|
||||
<div className="max-w-xl">
|
||||
<label
|
||||
htmlFor="groupBy"
|
||||
className="text-tremor-default text-tremor-content dark:text-dark-tremor-content"
|
||||
>
|
||||
Group by
|
||||
</label>
|
||||
<Select value={groupBy} id={'groupBy'} defaultValue={'day'} onValueChange={setGroupBy}>
|
||||
{groupBySelectItems}
|
||||
</Select>
|
||||
</div>
|
||||
<label
|
||||
htmlFor="Show total"
|
||||
className="max-w-xl flex self-center mt-6 gap-2 text-tremor-default text-tremor-content dark:text-dark-tremor-content select-none"
|
||||
>
|
||||
Show total
|
||||
<Switch checked={showTotal} onChange={setShowTotal} id={'Show total'} />
|
||||
</label>
|
||||
</div>
|
||||
<AreaChart
|
||||
connectNulls={true}
|
||||
customTooltip={args => (
|
||||
<ChartTooltip {...args} groupBy={groupBy} rawDate={args.payload?.payload?.rawDate} />
|
||||
)}
|
||||
enableLegendSlider
|
||||
className="h-full"
|
||||
data={filteredData}
|
||||
index="date"
|
||||
|
||||
Reference in New Issue
Block a user