Refacto board (#661)
* Refacto pipeline progress board to be entity agnostic * Abstract hooks as well * Move files * Pass specific components as props * Move board hook to the generic component * Make dnd and update logic part of the board * Remove useless call and getch pipelineProgress from hook * Minot * improve typing * Revert "improve typing" This reverts commit 49bf7929b6231747cc460cbb98f68c3c10424659. * wip * Get board from initial component * Move files again * Lint * Fix story * Lint * Mock pipeline progress * Fix storybook * WIP refactor recoil * Checkpoint: compilation * Fix dnd * Fix unselect card * Checkpoint: compilation * Checkpoint: New card OK * Checkpoint: feature complete * Fix latency for delete * Linter * Fix rebase * Move files * lint * Update Stories tests * lint * Fix test * Refactor hook for company progress indexing * Remove useless type * Move boardState * remove gardcoded Id * Nit * Fix * Rename state
This commit is contained in:
138
front/src/pages/opportunities/HookCompanyBoard.tsx
Normal file
138
front/src/pages/opportunities/HookCompanyBoard.tsx
Normal file
@ -0,0 +1,138 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||
|
||||
import {
|
||||
CompanyForBoard,
|
||||
CompanyProgress,
|
||||
PipelineProgressForBoard,
|
||||
} from '@/companies/types/CompanyProgress';
|
||||
import { BoardPipelineStageColumn } from '@/ui/board/components/Board';
|
||||
import {
|
||||
Pipeline,
|
||||
PipelineStage,
|
||||
useGetCompaniesQuery,
|
||||
useGetPipelineProgressQuery,
|
||||
useGetPipelinesQuery,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import { boardState } from '../../modules/pipeline-progress/states/boardState';
|
||||
|
||||
import { companyProgressesFamilyState } from './companyProgressesFamilyState';
|
||||
import { currentPipelineState } from './currentPipelineState';
|
||||
import { isBoardLoadedState } from './isBoardLoadedState';
|
||||
export function HookCompanyBoard() {
|
||||
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,
|
||||
pipelineProgressIds:
|
||||
pipelineStage.pipelineProgresses?.map(
|
||||
(item) => item.id as string,
|
||||
) || [],
|
||||
})) || [];
|
||||
setBoard(initialBoard);
|
||||
setIsBoardLoaded(true);
|
||||
},
|
||||
});
|
||||
|
||||
const pipelineProgressIds = currentPipeline?.pipelineStages
|
||||
?.map((pipelineStage: PipelineStage) =>
|
||||
(
|
||||
pipelineStage.pipelineProgresses?.map((item) => item.id as string) || []
|
||||
).flat(),
|
||||
)
|
||||
.flat();
|
||||
|
||||
const pipelineProgressesQuery = useGetPipelineProgressQuery({
|
||||
variables: {
|
||||
where: {
|
||||
id: { in: pipelineProgressIds },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
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 <></>;
|
||||
}
|
||||
@ -1,97 +1,31 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
import { companyBoardOptions } from '@/companies/components/companyBoardOptions';
|
||||
import { CompanyBoardContext } from '@/companies/states/CompanyBoardContext';
|
||||
import { BoardActionBarButtonDeletePipelineProgress } from '@/pipeline-progress/components/BoardActionBarButtonDeletePipelineProgress';
|
||||
import { EntityBoard } from '@/pipeline-progress/components/EntityBoard';
|
||||
import { EntityBoardActionBar } from '@/pipeline-progress/components/EntityBoardActionBar';
|
||||
import { GET_PIPELINES } from '@/pipeline-progress/queries';
|
||||
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
|
||||
import { IconTargetArrow } from '@/ui/icons/index';
|
||||
import { WithTopBarContainer } from '@/ui/layout/containers/WithTopBarContainer';
|
||||
|
||||
import {
|
||||
PipelineProgress,
|
||||
PipelineStage,
|
||||
useGetPipelinesQuery,
|
||||
useUpdateOnePipelineProgressMutation,
|
||||
useUpdateOnePipelineProgressStageMutation,
|
||||
} from '../../generated/graphql';
|
||||
import { Board } from '../../modules/pipeline-progress/components/Board';
|
||||
import { useBoard } from '../../modules/pipeline-progress/hooks/useBoard';
|
||||
import { HookCompanyBoard } from './HookCompanyBoard';
|
||||
|
||||
export function Opportunities() {
|
||||
const theme = useTheme();
|
||||
|
||||
const pipelines = useGetPipelinesQuery();
|
||||
const pipelineId = pipelines.data?.findManyPipeline[0]?.id;
|
||||
|
||||
const { initialBoard, items } = useBoard(pipelineId || '');
|
||||
|
||||
const columns = useMemo(
|
||||
() =>
|
||||
initialBoard?.map(({ id, colorCode, title }) => ({
|
||||
id,
|
||||
colorCode,
|
||||
title,
|
||||
})),
|
||||
[initialBoard],
|
||||
);
|
||||
const [updatePipelineProgress] = useUpdateOnePipelineProgressMutation();
|
||||
const [updatePipelineProgressStage] =
|
||||
useUpdateOnePipelineProgressStageMutation();
|
||||
|
||||
const handleCardUpdate = useCallback(
|
||||
async (
|
||||
pipelineProgress: Pick<PipelineProgress, 'id' | 'amount' | 'closeDate'>,
|
||||
) => {
|
||||
await updatePipelineProgress({
|
||||
variables: {
|
||||
id: pipelineProgress.id,
|
||||
amount: pipelineProgress.amount,
|
||||
closeDate: pipelineProgress.closeDate || null,
|
||||
},
|
||||
refetchQueries: [getOperationName(GET_PIPELINES) ?? ''],
|
||||
});
|
||||
},
|
||||
[updatePipelineProgress],
|
||||
);
|
||||
|
||||
const handleCardMove = useCallback(
|
||||
async (
|
||||
pipelineProgressId: NonNullable<PipelineProgress['id']>,
|
||||
pipelineStageId: NonNullable<PipelineStage['id']>,
|
||||
) => {
|
||||
updatePipelineProgressStage({
|
||||
variables: {
|
||||
id: pipelineProgressId,
|
||||
pipelineStageId,
|
||||
},
|
||||
});
|
||||
},
|
||||
[updatePipelineProgressStage],
|
||||
);
|
||||
|
||||
return (
|
||||
<WithTopBarContainer
|
||||
title="Opportunities"
|
||||
icon={<IconTargetArrow size={theme.icon.size.md} />}
|
||||
>
|
||||
{items && pipelineId ? (
|
||||
<>
|
||||
<Board
|
||||
pipelineId={pipelineId}
|
||||
columns={columns || []}
|
||||
initialBoard={initialBoard}
|
||||
initialItems={items}
|
||||
onCardMove={handleCardMove}
|
||||
onCardUpdate={handleCardUpdate}
|
||||
/>
|
||||
<EntityBoardActionBar>
|
||||
<BoardActionBarButtonDeletePipelineProgress />
|
||||
</EntityBoardActionBar>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<HookCompanyBoard />
|
||||
<RecoilScope SpecificContext={CompanyBoardContext}>
|
||||
<EntityBoard boardOptions={companyBoardOptions} />
|
||||
<EntityBoardActionBar>
|
||||
<BoardActionBarButtonDeletePipelineProgress />
|
||||
</EntityBoardActionBar>
|
||||
</RecoilScope>
|
||||
</WithTopBarContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
import { CompanyProgress } from '@/companies/types/CompanyProgress';
|
||||
|
||||
export const companyProgressesFamilyState = atomFamily<
|
||||
CompanyProgress | undefined,
|
||||
string
|
||||
>({
|
||||
key: 'companyProgressesFamilyState',
|
||||
default: undefined,
|
||||
});
|
||||
8
front/src/pages/opportunities/currentPipelineState.ts
Normal file
8
front/src/pages/opportunities/currentPipelineState.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
import { Pipeline } from '~/generated/graphql';
|
||||
|
||||
export const currentPipelineState = atom<Pipeline | undefined>({
|
||||
key: 'currentPipelineState',
|
||||
default: undefined,
|
||||
});
|
||||
6
front/src/pages/opportunities/isBoardLoadedState.ts
Normal file
6
front/src/pages/opportunities/isBoardLoadedState.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const isBoardLoadedState = atom<boolean>({
|
||||
key: 'isBoardLoadedState',
|
||||
default: false,
|
||||
});
|
||||
Reference in New Issue
Block a user