Files
twenty_crm/front/src/modules/companies/components/HooksCompanyBoard.tsx
Emilien Chauvet 6301bc2fbf 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
2023-07-17 19:32:47 -07:00

173 lines
5.3 KiB
TypeScript

import { useEffect, useMemo } from 'react';
import { useRecoilCallback, useRecoilState } from 'recoil';
import { useInitializeCompanyBoardFilters } from '@/companies/hooks/useInitializeCompanyBoardFilters';
import { companyProgressesFamilyState } from '@/companies/states/companyProgressesFamilyState';
import {
CompanyForBoard,
CompanyProgress,
PipelineProgressForBoard,
} from '@/companies/types/CompanyProgress';
import { boardState } from '@/pipeline/states/boardState';
import { currentPipelineState } from '@/pipeline/states/currentPipelineState';
import { isBoardLoadedState } from '@/pipeline/states/isBoardLoadedState';
import { BoardPipelineStageColumn } from '@/ui/board/components/Board';
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
import { FilterDefinition } from '@/ui/filter-n-sort/types/FilterDefinition';
import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause';
import { useRecoilScopedValue } from '@/ui/recoil-scope/hooks/useRecoilScopedValue';
import { PipelineProgressOrderByWithRelationInput as PipelineProgresses_Order_By } from '~/generated/graphql';
import {
Pipeline,
useGetCompaniesQuery,
useGetPipelineProgressQuery,
useGetPipelinesQuery,
} from '~/generated/graphql';
import { CompanyBoardContext } from '../states/CompanyBoardContext';
export function HooksCompanyBoard({
availableFilters,
orderBy,
}: {
availableFilters: FilterDefinition[];
orderBy: PipelineProgresses_Order_By[];
}) {
useInitializeCompanyBoardFilters({
availableFilters,
});
const [currentPipeline, setCurrentPipeline] =
useRecoilState(currentPipelineState);
const [, setBoard] = useRecoilState(boardState);
const [, setIsBoardLoaded] = useRecoilState(isBoardLoadedState);
useGetPipelinesQuery({
onCompleted: async (data) => {
const pipeline = data?.findManyPipeline[0] as Pipeline;
setCurrentPipeline(pipeline);
const pipelineStages = pipeline?.pipelineStages;
const orderedPipelineStages = pipelineStages
? [...pipelineStages].sort((a, b) => {
if (!a.index || !b.index) return 0;
return a.index - b.index;
})
: [];
const initialBoard: BoardPipelineStageColumn[] =
orderedPipelineStages?.map((pipelineStage) => ({
pipelineStageId: pipelineStage.id,
title: pipelineStage.name,
colorCode: pipelineStage.color,
index: pipelineStage.index || 0,
pipelineProgressIds: [],
})) || [];
setBoard(initialBoard);
},
});
const pipelineStageIds = currentPipeline?.pipelineStages
?.map((pipelineStage) => pipelineStage.id)
.flat();
const filters = useRecoilScopedValue(filtersScopedState, CompanyBoardContext);
const whereFilters = useMemo(() => {
return {
AND: [
{ pipelineStageId: { in: pipelineStageIds } },
...filters.map(turnFilterIntoWhereClause),
],
};
}, [filters, pipelineStageIds]) as any;
const pipelineProgressesQuery = useGetPipelineProgressQuery({
variables: {
where: whereFilters,
orderBy,
},
onCompleted: (data) => {
const pipelineProgresses = data?.findManyPipelineProgress || [];
setBoard((board) =>
board?.map((boardPipelineStage) => ({
...boardPipelineStage,
pipelineProgressIds: pipelineProgresses
.filter(
(pipelineProgress) =>
pipelineProgress.pipelineStageId ===
boardPipelineStage.pipelineStageId,
)
.map((pipelineProgress) => pipelineProgress.id),
})),
);
setIsBoardLoaded(true);
},
});
const pipelineProgresses =
pipelineProgressesQuery.data?.findManyPipelineProgress || [];
const entitiesQueryResult = useGetCompaniesQuery({
variables: {
where: {
id: {
in: pipelineProgresses.map((item) => item.progressableId),
},
},
},
});
const indexCompanyByIdReducer = (
acc: { [key: string]: CompanyForBoard },
company: CompanyForBoard,
) => ({
...acc,
[company.id]: company,
});
const companiesDict =
entitiesQueryResult.data?.companies.reduce(
indexCompanyByIdReducer,
{} as { [key: string]: CompanyForBoard },
) || {};
const indexPipelineProgressByIdReducer = (
acc: {
[key: string]: CompanyProgress;
},
pipelineProgress: PipelineProgressForBoard,
) => {
const company = companiesDict[pipelineProgress.progressableId];
return {
...acc,
[pipelineProgress.id]: {
pipelineProgress,
company,
},
};
};
const companyBoardIndex = pipelineProgresses.reduce(
indexPipelineProgressByIdReducer,
{} as { [key: string]: CompanyProgress },
);
const synchronizeCompanyProgresses = useRecoilCallback(
({ set }) =>
(companyBoardIndex: { [key: string]: CompanyProgress }) => {
Object.entries(companyBoardIndex).forEach(([id, companyProgress]) => {
set(companyProgressesFamilyState(id), companyProgress);
});
},
[],
);
const loading =
entitiesQueryResult.loading || pipelineProgressesQuery.loading;
useEffect(() => {
!loading && synchronizeCompanyProgresses(companyBoardIndex);
}, [loading, companyBoardIndex, synchronizeCompanyProgresses]);
return <></>;
}