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:
@ -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>
|
||||
|
||||
37
front/src/pages/opportunities/opportunities-filters.tsx
Normal file
37
front/src/pages/opportunities/opportunities-filters.tsx
Normal 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} />
|
||||
),
|
||||
},
|
||||
];
|
||||
21
front/src/pages/opportunities/opportunities-sorts.tsx
Normal file
21
front/src/pages/opportunities/opportunities-sorts.tsx
Normal 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>>;
|
||||
18
front/src/pages/opportunities/opportunitiesBoardOptions.tsx
Normal file
18
front/src/pages/opportunities/opportunitiesBoardOptions.tsx
Normal 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,
|
||||
};
|
||||
@ -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',
|
||||
|
||||
Reference in New Issue
Block a user