Feature/filter and sort board (#725)

* Get pipeline progress from stage IDs

* Rename hooks file

* Addd first amount filter

* Add remaining filters

* Design fixes

* Add filtering on creation date or amount

* Fix card updates and creations with the new state management

* Keep ordering when dropping a card

* Add remainint sorts

* Make board header more generic

* Move available filters and sorts to board options

* Fix decorators for test

* Add pipeline stage ids to mock data

* Adapt mock data

* Linter
This commit is contained in:
Emilien Chauvet
2023-07-17 19:32:47 -07:00
committed by GitHub
parent 9895c1d5d6
commit 6301bc2fbf
19 changed files with 784 additions and 413 deletions

View File

@ -1,26 +1,54 @@
import { useCallback, useState } from 'react';
import { useTheme } from '@emotion/react';
import { companyBoardOptions } from '@/companies/components/companyBoardOptions';
import { HookCompanyBoard } from '@/companies/components/HookCompanyBoard';
import { HooksCompanyBoard } from '@/companies/components/HooksCompanyBoard';
import { CompanyBoardContext } from '@/companies/states/CompanyBoardContext';
import { BoardActionBarButtonDeletePipelineProgress } from '@/pipeline/components/BoardActionBarButtonDeletePipelineProgress';
import { EntityBoard } from '@/pipeline/components/EntityBoard';
import { EntityBoardActionBar } from '@/pipeline/components/EntityBoardActionBar';
import {
defaultPipelineProgressOrderBy,
PipelineProgressesSelectedSortType,
} from '@/pipeline/queries';
import { reduceSortsToOrderBy } from '@/ui/filter-n-sort/helpers';
import { IconTargetArrow } from '@/ui/icon/index';
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
import { PipelineProgressOrderByWithRelationInput } from '~/generated/graphql';
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
export function Opportunities() {
const theme = useTheme();
const [orderBy, setOrderBy] = useState<
PipelineProgressOrderByWithRelationInput[]
>(defaultPipelineProgressOrderBy);
const updateSorts = useCallback(
(sorts: Array<PipelineProgressesSelectedSortType>) => {
setOrderBy(
sorts.length
? reduceSortsToOrderBy(sorts)
: defaultPipelineProgressOrderBy,
);
},
[],
);
return (
<WithTopBarContainer
title="Opportunities"
icon={<IconTargetArrow size={theme.icon.size.md} />}
>
<HookCompanyBoard />
<RecoilScope SpecificContext={CompanyBoardContext}>
<EntityBoard boardOptions={companyBoardOptions} />
<HooksCompanyBoard
availableFilters={opportunitiesBoardOptions.filters}
orderBy={orderBy}
/>
<EntityBoard
boardOptions={opportunitiesBoardOptions}
updateSorts={updateSorts}
/>
<EntityBoardActionBar>
<BoardActionBarButtonDeletePipelineProgress />
</EntityBoardActionBar>

View File

@ -0,0 +1,37 @@
import { FilterDropdownCompanySearchSelect } from '@/companies/components/FilterDropdownCompanySearchSelect';
import { CompanyBoardContext } from '@/companies/states/CompanyBoardContext';
import { FilterDefinitionByEntity } from '@/ui/filter-n-sort/types/FilterDefinitionByEntity';
import {
IconBuildingSkyscraper,
IconCalendarEvent,
IconCurrencyDollar,
} from '@/ui/icon/index';
import { icon } from '@/ui/themes/icon';
import { PipelineProgress } from '~/generated/graphql';
export const opportunitiesFilters: FilterDefinitionByEntity<PipelineProgress>[] =
[
{
field: 'amount',
label: 'Amount',
icon: <IconCurrencyDollar size={icon.size.md} stroke={icon.stroke.sm} />,
type: 'number',
},
{
field: 'closeDate',
label: 'Close date',
icon: <IconCalendarEvent size={icon.size.md} stroke={icon.stroke.sm} />,
type: 'date',
},
{
field: 'progressableId',
label: 'Company',
icon: (
<IconBuildingSkyscraper size={icon.size.md} stroke={icon.stroke.sm} />
),
type: 'entity',
entitySelectComponent: (
<FilterDropdownCompanySearchSelect context={CompanyBoardContext} />
),
},
];

View File

@ -0,0 +1,21 @@
import { SortType } from '@/ui/filter-n-sort/types/interface';
import { IconCalendarEvent, IconCurrencyDollar } from '@/ui/icon/index';
import { PipelineProgressOrderByWithRelationInput as PipelineProgresses_Order_By } from '~/generated/graphql';
export const opportunitiesSorts = [
{
key: 'createdAt',
label: 'Creation',
icon: <IconCalendarEvent size={16} />,
},
{
key: 'amount',
label: 'Amount',
icon: <IconCurrencyDollar size={16} />,
},
{
key: 'closeDate',
label: 'Expected close date',
icon: <IconCalendarEvent size={16} />,
},
] satisfies Array<SortType<PipelineProgresses_Order_By>>;

View File

@ -0,0 +1,18 @@
import { CompanyBoardCard } from '@/companies/components/CompanyBoardCard';
import { NewCompanyProgressButton } from '@/companies/components/NewCompanyProgressButton';
import { BoardOptions } from '@/pipeline/types/BoardOptions';
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
import { opportunitiesFilters } from './opportunities-filters';
import { opportunitiesSorts } from './opportunities-sorts';
export const opportunitiesBoardOptions: BoardOptions = {
newCardComponent: (
<RecoilScope>
<NewCompanyProgressButton />
</RecoilScope>
),
cardComponent: <CompanyBoardCard />,
filters: opportunitiesFilters,
sorts: opportunitiesSorts,
};

View File

@ -8,6 +8,7 @@ import {
IconPhone,
IconUser,
} from '@/ui/icon/index';
import { TableContext } from '@/ui/table/states/TableContext';
import { icon } from '@/ui/themes/icon';
import { Person } from '~/generated/graphql';
@ -37,7 +38,9 @@ export const peopleFilters: FilterDefinitionByEntity<Person>[] = [
<IconBuildingSkyscraper size={icon.size.md} stroke={icon.stroke.sm} />
),
type: 'entity',
entitySelectComponent: <FilterDropdownCompanySearchSelect />,
entitySelectComponent: (
<FilterDropdownCompanySearchSelect context={TableContext} />
),
},
{
field: 'phone',