First round of refactor EntityBoards (#1067)
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { Meta, StoryObj } from '@storybook/react';
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
import { EntityBoard } from '@/pipeline/components/EntityBoard';
|
import { EntityBoard } from '@/ui/board/components/EntityBoard';
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||||
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
|
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
|
||||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||||
|
|||||||
@ -1,33 +1,18 @@
|
|||||||
import { useEffect } from 'react';
|
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { Meta, StoryObj } from '@storybook/react';
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
import { CompanyBoardCard } from '@/companies/components/CompanyBoardCard';
|
import { CompanyBoardCard } from '@/companies/components/CompanyBoardCard';
|
||||||
|
import { BoardCardIdContext } from '@/ui/board/states/BoardCardIdContext';
|
||||||
|
import { BoardColumnContext } from '@/ui/board/states/BoardColumnContext';
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
|
||||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||||
import { mockedPipelineProgressData } from '~/testing/mock-data/pipeline-progress';
|
import { mockedPipelineProgressData } from '~/testing/mock-data/pipeline-progress';
|
||||||
|
|
||||||
import { defaultPipelineProgressOrderBy } from '../../pipeline/queries';
|
import { defaultPipelineProgressOrderBy } from '../../pipeline/queries';
|
||||||
import { BoardCardContext } from '../../pipeline/states/BoardCardContext';
|
|
||||||
import { BoardColumnContext } from '../../pipeline/states/BoardColumnContext';
|
|
||||||
import { pipelineProgressIdScopedState } from '../../pipeline/states/pipelineProgressIdScopedState';
|
|
||||||
import { HooksCompanyBoard } from '../components/HooksCompanyBoard';
|
import { HooksCompanyBoard } from '../components/HooksCompanyBoard';
|
||||||
import { CompanyBoardContext } from '../states/CompanyBoardContext';
|
import { CompanyBoardContext } from '../states/CompanyBoardContext';
|
||||||
|
|
||||||
function HookLoadFakeBoardContextState() {
|
|
||||||
const [, setPipelineProgressId] = useRecoilScopedState(
|
|
||||||
pipelineProgressIdScopedState,
|
|
||||||
BoardCardContext,
|
|
||||||
);
|
|
||||||
const pipelineProgress = mockedPipelineProgressData[1];
|
|
||||||
useEffect(() => {
|
|
||||||
setPipelineProgressId(pipelineProgress?.id || '');
|
|
||||||
}, [pipelineProgress?.id, setPipelineProgressId]);
|
|
||||||
return <></>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const meta: Meta<typeof CompanyBoardCard> = {
|
const meta: Meta<typeof CompanyBoardCard> = {
|
||||||
title: 'Modules/Companies/CompanyBoardCard',
|
title: 'Modules/Companies/CompanyBoardCard',
|
||||||
component: CompanyBoardCard,
|
component: CompanyBoardCard,
|
||||||
@ -39,12 +24,11 @@ const meta: Meta<typeof CompanyBoardCard> = {
|
|||||||
orderBy={defaultPipelineProgressOrderBy}
|
orderBy={defaultPipelineProgressOrderBy}
|
||||||
/>
|
/>
|
||||||
<RecoilScope SpecificContext={BoardColumnContext}>
|
<RecoilScope SpecificContext={BoardColumnContext}>
|
||||||
<RecoilScope SpecificContext={BoardCardContext}>
|
<BoardCardIdContext.Provider value={mockedPipelineProgressData[1].id}>
|
||||||
<HookLoadFakeBoardContextState />
|
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<Story />
|
<Story />
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
</RecoilScope>
|
</BoardCardIdContext.Provider>
|
||||||
</RecoilScope>
|
</RecoilScope>
|
||||||
</RecoilScope>
|
</RecoilScope>
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ReactNode, useCallback } from 'react';
|
import { ReactNode, useCallback, useContext } from 'react';
|
||||||
import { getOperationName } from '@apollo/client/utilities';
|
import { getOperationName } from '@apollo/client/utilities';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
@ -7,9 +7,8 @@ import { companyProgressesFamilyState } from '@/companies/states/companyProgress
|
|||||||
import { PipelineProgressPointOfContactEditableField } from '@/pipeline/editable-field/components/PipelineProgressPointOfContactEditableField';
|
import { PipelineProgressPointOfContactEditableField } from '@/pipeline/editable-field/components/PipelineProgressPointOfContactEditableField';
|
||||||
import { ProbabilityEditableField } from '@/pipeline/editable-field/components/ProbabilityEditableField';
|
import { ProbabilityEditableField } from '@/pipeline/editable-field/components/ProbabilityEditableField';
|
||||||
import { GET_PIPELINE_PROGRESS, GET_PIPELINES } from '@/pipeline/queries';
|
import { GET_PIPELINE_PROGRESS, GET_PIPELINES } from '@/pipeline/queries';
|
||||||
import { BoardCardContext } from '@/pipeline/states/BoardCardContext';
|
import { BoardCardIdContext } from '@/ui/board/states/BoardCardIdContext';
|
||||||
import { pipelineProgressIdScopedState } from '@/pipeline/states/pipelineProgressIdScopedState';
|
import { selectedBoardCardIdsState } from '@/ui/board/states/selectedBoardCardIdsState';
|
||||||
import { selectedBoardCardsState } from '@/pipeline/states/selectedBoardCardsState';
|
|
||||||
import { EntityChipVariant } from '@/ui/chip/components/EntityChip';
|
import { EntityChipVariant } from '@/ui/chip/components/EntityChip';
|
||||||
import { DateEditableField } from '@/ui/editable-field/variants/components/DateEditableField';
|
import { DateEditableField } from '@/ui/editable-field/variants/components/DateEditableField';
|
||||||
import { NumberEditableField } from '@/ui/editable-field/variants/components/NumberEditableField';
|
import { NumberEditableField } from '@/ui/editable-field/variants/components/NumberEditableField';
|
||||||
@ -19,7 +18,6 @@ import {
|
|||||||
Checkbox,
|
Checkbox,
|
||||||
CheckboxVariant,
|
CheckboxVariant,
|
||||||
} from '@/ui/input/checkbox/components/Checkbox';
|
} from '@/ui/input/checkbox/components/Checkbox';
|
||||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
|
||||||
import { useUpdateOnePipelineProgressMutation } from '~/generated/graphql';
|
import { useUpdateOnePipelineProgressMutation } from '~/generated/graphql';
|
||||||
import { getLogoUrlFromDomainName } from '~/utils';
|
import { getLogoUrlFromDomainName } from '~/utils';
|
||||||
|
|
||||||
@ -110,25 +108,25 @@ const StyledFieldContainer = styled.div`
|
|||||||
export function CompanyBoardCard() {
|
export function CompanyBoardCard() {
|
||||||
const [updatePipelineProgress] = useUpdateOnePipelineProgressMutation();
|
const [updatePipelineProgress] = useUpdateOnePipelineProgressMutation();
|
||||||
|
|
||||||
const [pipelineProgressId] = useRecoilScopedState(
|
const boardCardId = useContext(BoardCardIdContext);
|
||||||
pipelineProgressIdScopedState,
|
|
||||||
BoardCardContext,
|
|
||||||
);
|
|
||||||
const [companyProgress] = useRecoilState(
|
const [companyProgress] = useRecoilState(
|
||||||
companyProgressesFamilyState(pipelineProgressId || ''),
|
companyProgressesFamilyState(boardCardId ?? ''),
|
||||||
);
|
);
|
||||||
const { pipelineProgress, company } = companyProgress || {};
|
const { pipelineProgress, company } = companyProgress ?? {};
|
||||||
|
|
||||||
const [selectedBoardCards, setSelectedBoardCards] = useRecoilState(
|
const [selectedBoardCards, setSelectedBoardCards] = useRecoilState(
|
||||||
selectedBoardCardsState,
|
selectedBoardCardIdsState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const selected = selectedBoardCards.includes(pipelineProgressId || '');
|
const selected = selectedBoardCards.includes(boardCardId ?? '');
|
||||||
|
|
||||||
function setSelected(isSelected: boolean) {
|
function setSelected(isSelected: boolean) {
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
setSelectedBoardCards([...selectedBoardCards, pipelineProgressId || '']);
|
setSelectedBoardCards([...selectedBoardCards, boardCardId ?? '']);
|
||||||
} else {
|
} else {
|
||||||
setSelectedBoardCards(
|
setSelectedBoardCards(
|
||||||
selectedBoardCards.filter((id) => id !== pipelineProgressId),
|
selectedBoardCards.filter((id) => id !== boardCardId),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,15 +8,17 @@ import {
|
|||||||
CompanyProgress,
|
CompanyProgress,
|
||||||
PipelineProgressForBoard,
|
PipelineProgressForBoard,
|
||||||
} from '@/companies/types/CompanyProgress';
|
} from '@/companies/types/CompanyProgress';
|
||||||
import { boardState } from '@/pipeline/states/boardState';
|
|
||||||
import { currentPipelineState } from '@/pipeline/states/currentPipelineState';
|
import { currentPipelineState } from '@/pipeline/states/currentPipelineState';
|
||||||
import { isBoardLoadedState } from '@/pipeline/states/isBoardLoadedState';
|
import { boardCardIdsByColumnIdFamilyState } from '@/ui/board/states/boardCardIdsByColumnIdFamilyState';
|
||||||
import { BoardPipelineStageColumn } from '@/ui/board/components/Board';
|
import { boardColumnsState } from '@/ui/board/states/boardColumnsState';
|
||||||
|
import { isBoardLoadedState } from '@/ui/board/states/isBoardLoadedState';
|
||||||
|
import { BoardColumnDefinition } from '@/ui/board/types/BoardColumnDefinition';
|
||||||
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
||||||
import { FilterDefinition } from '@/ui/filter-n-sort/types/FilterDefinition';
|
import { FilterDefinition } from '@/ui/filter-n-sort/types/FilterDefinition';
|
||||||
import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause';
|
import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
import {
|
import {
|
||||||
|
GetPipelineProgressQuery,
|
||||||
PipelineProgressableType,
|
PipelineProgressableType,
|
||||||
PipelineProgressOrderByWithRelationInput as PipelineProgresses_Order_By,
|
PipelineProgressOrderByWithRelationInput as PipelineProgresses_Order_By,
|
||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
@ -39,13 +41,52 @@ export function HooksCompanyBoard({
|
|||||||
useInitializeCompanyBoardFilters({
|
useInitializeCompanyBoardFilters({
|
||||||
availableFilters,
|
availableFilters,
|
||||||
});
|
});
|
||||||
const [currentPipeline, setCurrentPipeline] =
|
|
||||||
useRecoilState(currentPipelineState);
|
|
||||||
|
|
||||||
const [board, setBoard] = useRecoilState(boardState);
|
const [currentPipeline] = useRecoilState(currentPipelineState);
|
||||||
|
const [, setBoardColumns] = useRecoilState(boardColumnsState);
|
||||||
|
|
||||||
const [, setIsBoardLoaded] = useRecoilState(isBoardLoadedState);
|
const [, setIsBoardLoaded] = useRecoilState(isBoardLoadedState);
|
||||||
|
|
||||||
|
const updateBoardColumns = useRecoilCallback(
|
||||||
|
({ set, snapshot }) =>
|
||||||
|
(pipeline: Pipeline) => {
|
||||||
|
const currentPipeline = snapshot
|
||||||
|
.getLoadable(currentPipelineState)
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
const currentBoardColumns = snapshot
|
||||||
|
.getLoadable(boardColumnsState)
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (JSON.stringify(pipeline) !== JSON.stringify(currentPipeline)) {
|
||||||
|
set(currentPipelineState, pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pipelineStages = pipeline?.pipelineStages ?? [];
|
||||||
|
|
||||||
|
const orderedPipelineStages = [...pipelineStages].sort((a, b) => {
|
||||||
|
if (!a.index || !b.index) return 0;
|
||||||
|
return a.index - b.index;
|
||||||
|
});
|
||||||
|
|
||||||
|
const newBoardColumns: BoardColumnDefinition[] =
|
||||||
|
orderedPipelineStages?.map((pipelineStage) => ({
|
||||||
|
id: pipelineStage.id,
|
||||||
|
title: pipelineStage.name,
|
||||||
|
colorCode: pipelineStage.color,
|
||||||
|
index: pipelineStage.index ?? 0,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (
|
||||||
|
JSON.stringify(currentBoardColumns) !==
|
||||||
|
JSON.stringify(newBoardColumns)
|
||||||
|
) {
|
||||||
|
setBoardColumns(newBoardColumns);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
useGetPipelinesQuery({
|
useGetPipelinesQuery({
|
||||||
variables: {
|
variables: {
|
||||||
where: {
|
where: {
|
||||||
@ -54,23 +95,8 @@ export function HooksCompanyBoard({
|
|||||||
},
|
},
|
||||||
onCompleted: async (data) => {
|
onCompleted: async (data) => {
|
||||||
const pipeline = data?.findManyPipeline[0] as Pipeline;
|
const pipeline = data?.findManyPipeline[0] as Pipeline;
|
||||||
setCurrentPipeline(pipeline);
|
|
||||||
const pipelineStages = pipeline?.pipelineStages;
|
updateBoardColumns(pipeline);
|
||||||
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, i) => ({
|
|
||||||
pipelineStageId: pipelineStage.id,
|
|
||||||
title: pipelineStage.name,
|
|
||||||
colorCode: pipelineStage.color,
|
|
||||||
index: pipelineStage.index || 0,
|
|
||||||
pipelineProgressIds: board?.[i].pipelineProgressIds || [],
|
|
||||||
})) || [];
|
|
||||||
setBoard(initialBoard);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -89,6 +115,29 @@ export function HooksCompanyBoard({
|
|||||||
};
|
};
|
||||||
}, [filters, pipelineStageIds]) as any;
|
}, [filters, pipelineStageIds]) as any;
|
||||||
|
|
||||||
|
const updateBoardCardIds = useRecoilCallback(
|
||||||
|
({ snapshot, set }) =>
|
||||||
|
(
|
||||||
|
pipelineProgresses: GetPipelineProgressQuery['findManyPipelineProgress'],
|
||||||
|
) => {
|
||||||
|
const boardColumns = snapshot
|
||||||
|
.getLoadable(boardColumnsState)
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
for (const boardColumn of boardColumns) {
|
||||||
|
const boardCardIds = pipelineProgresses
|
||||||
|
.filter(
|
||||||
|
(pipelineProgressToFilter) =>
|
||||||
|
pipelineProgressToFilter.pipelineStageId === boardColumn.id,
|
||||||
|
)
|
||||||
|
.map((pipelineProgress) => pipelineProgress.id);
|
||||||
|
|
||||||
|
set(boardCardIdsByColumnIdFamilyState(boardColumn.id), boardCardIds);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const pipelineProgressesQuery = useGetPipelineProgressQuery({
|
const pipelineProgressesQuery = useGetPipelineProgressQuery({
|
||||||
variables: {
|
variables: {
|
||||||
where: whereFilters,
|
where: whereFilters,
|
||||||
@ -96,18 +145,9 @@ export function HooksCompanyBoard({
|
|||||||
},
|
},
|
||||||
onCompleted: (data) => {
|
onCompleted: (data) => {
|
||||||
const pipelineProgresses = data?.findManyPipelineProgress || [];
|
const pipelineProgresses = data?.findManyPipelineProgress || [];
|
||||||
setBoard((board) =>
|
|
||||||
board?.map((boardPipelineStage) => ({
|
updateBoardCardIds(pipelineProgresses);
|
||||||
...boardPipelineStage,
|
|
||||||
pipelineProgressIds: pipelineProgresses
|
|
||||||
.filter(
|
|
||||||
(pipelineProgress) =>
|
|
||||||
pipelineProgress.pipelineStageId ===
|
|
||||||
boardPipelineStage.pipelineStageId,
|
|
||||||
)
|
|
||||||
.map((pipelineProgress) => pipelineProgress.id),
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
setIsBoardLoaded(true);
|
setIsBoardLoaded(true);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,15 +1,13 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useContext, useState } from 'react';
|
||||||
import { getOperationName } from '@apollo/client/utilities';
|
import { getOperationName } from '@apollo/client/utilities';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
import { GET_PIPELINE_PROGRESS, GET_PIPELINES } from '@/pipeline/queries';
|
import { GET_PIPELINE_PROGRESS, GET_PIPELINES } from '@/pipeline/queries';
|
||||||
import { BoardColumnContext } from '@/pipeline/states/BoardColumnContext';
|
|
||||||
import { boardState } from '@/pipeline/states/boardState';
|
|
||||||
import { currentPipelineState } from '@/pipeline/states/currentPipelineState';
|
import { currentPipelineState } from '@/pipeline/states/currentPipelineState';
|
||||||
import { pipelineStageIdScopedState } from '@/pipeline/states/pipelineStageIdScopedState';
|
|
||||||
import { BoardPipelineStageColumn } from '@/ui/board/components/Board';
|
|
||||||
import { NewButton } from '@/ui/board/components/NewButton';
|
import { NewButton } from '@/ui/board/components/NewButton';
|
||||||
|
import { boardCardIdsByColumnIdFamilyState } from '@/ui/board/states/boardCardIdsByColumnIdFamilyState';
|
||||||
|
import { BoardColumnIdContext } from '@/ui/board/states/BoardColumnIdContext';
|
||||||
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
|
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
|
||||||
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
|
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
|
||||||
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
||||||
@ -21,12 +19,8 @@ import { useFilteredSearchCompanyQuery } from '../queries';
|
|||||||
|
|
||||||
export function NewCompanyProgressButton() {
|
export function NewCompanyProgressButton() {
|
||||||
const [isCreatingCard, setIsCreatingCard] = useState(false);
|
const [isCreatingCard, setIsCreatingCard] = useState(false);
|
||||||
const [board, setBoard] = useRecoilState(boardState);
|
|
||||||
const [pipeline] = useRecoilState(currentPipelineState);
|
const [pipeline] = useRecoilState(currentPipelineState);
|
||||||
const [pipelineStageId] = useRecoilScopedState(
|
const pipelineStageId = useContext(BoardColumnIdContext);
|
||||||
pipelineStageIdScopedState,
|
|
||||||
BoardColumnContext,
|
|
||||||
);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
goBackToPreviousHotkeyScope,
|
goBackToPreviousHotkeyScope,
|
||||||
@ -41,34 +35,35 @@ export function NewCompanyProgressButton() {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleEntitySelect = useCallback(
|
const handleEntitySelect = useRecoilCallback(
|
||||||
async (company: any) => {
|
({ set }) =>
|
||||||
if (!company) return;
|
async (company: any) => {
|
||||||
|
if (!company) return;
|
||||||
|
|
||||||
setIsCreatingCard(false);
|
if (!pipelineStageId) throw new Error('pipelineStageId is not defined');
|
||||||
goBackToPreviousHotkeyScope();
|
|
||||||
|
|
||||||
const newUuid = uuidv4();
|
setIsCreatingCard(false);
|
||||||
const newBoard = JSON.parse(JSON.stringify(board));
|
|
||||||
const destinationColumnIndex = newBoard.findIndex(
|
goBackToPreviousHotkeyScope();
|
||||||
(column: BoardPipelineStageColumn) =>
|
|
||||||
column.pipelineStageId === pipelineStageId,
|
const newUuid = uuidv4();
|
||||||
);
|
|
||||||
newBoard[destinationColumnIndex].pipelineProgressIds.push(newUuid);
|
set(boardCardIdsByColumnIdFamilyState(pipelineStageId), (oldValue) => [
|
||||||
setBoard(newBoard);
|
...oldValue,
|
||||||
await createOneCompanyPipelineProgress({
|
newUuid,
|
||||||
variables: {
|
]);
|
||||||
uuid: newUuid,
|
|
||||||
pipelineStageId: pipelineStageId || '',
|
await createOneCompanyPipelineProgress({
|
||||||
pipelineId: pipeline?.id || '',
|
variables: {
|
||||||
companyId: company.id || '',
|
uuid: newUuid,
|
||||||
},
|
pipelineStageId: pipelineStageId,
|
||||||
});
|
pipelineId: pipeline?.id ?? '',
|
||||||
},
|
companyId: company.id ?? '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
[
|
[
|
||||||
goBackToPreviousHotkeyScope,
|
goBackToPreviousHotkeyScope,
|
||||||
board,
|
|
||||||
setBoard,
|
|
||||||
createOneCompanyPipelineProgress,
|
createOneCompanyPipelineProgress,
|
||||||
pipelineStageId,
|
pipelineStageId,
|
||||||
pipeline?.id,
|
pipeline?.id,
|
||||||
|
|||||||
@ -1,49 +0,0 @@
|
|||||||
import { getOperationName } from '@apollo/client/utilities';
|
|
||||||
import { useRecoilState } from 'recoil';
|
|
||||||
|
|
||||||
import { IconTrash } from '@/ui/icon/index';
|
|
||||||
import { EntityTableActionBarButton } from '@/ui/table/action-bar/components/EntityTableActionBarButton';
|
|
||||||
import { useDeleteManyPipelineProgressMutation } from '~/generated/graphql';
|
|
||||||
|
|
||||||
import { GET_PIPELINES } from '../queries';
|
|
||||||
import { boardState } from '../states/boardState';
|
|
||||||
import { selectedBoardCardsState } from '../states/selectedBoardCardsState';
|
|
||||||
|
|
||||||
export function BoardActionBarButtonDeletePipelineProgress() {
|
|
||||||
const [selectedBoardItems, setSelectedBoardItems] = useRecoilState(
|
|
||||||
selectedBoardCardsState,
|
|
||||||
);
|
|
||||||
const [board, setBoard] = useRecoilState(boardState);
|
|
||||||
|
|
||||||
const [deletePipelineProgress] = useDeleteManyPipelineProgressMutation({
|
|
||||||
refetchQueries: [getOperationName(GET_PIPELINES) ?? ''],
|
|
||||||
});
|
|
||||||
|
|
||||||
async function handleDeleteClick() {
|
|
||||||
setBoard(
|
|
||||||
board?.map((pipelineStage) => ({
|
|
||||||
...pipelineStage,
|
|
||||||
pipelineProgressIds: pipelineStage.pipelineProgressIds.filter(
|
|
||||||
(pipelineProgressId) =>
|
|
||||||
!selectedBoardItems.includes(pipelineProgressId),
|
|
||||||
),
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
|
|
||||||
setSelectedBoardItems([]);
|
|
||||||
await deletePipelineProgress({
|
|
||||||
variables: {
|
|
||||||
ids: selectedBoardItems,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<EntityTableActionBarButton
|
|
||||||
label="Delete"
|
|
||||||
icon={<IconTrash size={16} />}
|
|
||||||
type="warning"
|
|
||||||
onClick={handleDeleteClick}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
import { atom } from 'recoil';
|
|
||||||
|
|
||||||
import { BoardPipelineStageColumn } from '@/ui/board/components/Board';
|
|
||||||
|
|
||||||
export const boardColumnsState = atom<BoardPipelineStageColumn[]>({
|
|
||||||
key: 'boardColumnsState',
|
|
||||||
default: [],
|
|
||||||
});
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
import { atom } from 'recoil';
|
|
||||||
|
|
||||||
import { BoardPipelineStageColumn } from '@/ui/board/components/Board';
|
|
||||||
|
|
||||||
export const boardState = atom<BoardPipelineStageColumn[] | undefined>({
|
|
||||||
key: 'boardState',
|
|
||||||
default: undefined,
|
|
||||||
});
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import { atom } from 'recoil';
|
|
||||||
|
|
||||||
export const selectedBoardCardsState = atom<string[]>({
|
|
||||||
key: 'isBoardCardSelectedFamilyState',
|
|
||||||
default: [],
|
|
||||||
});
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
import { DropResult } from '@hello-pangea/dnd'; // Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350
|
|
||||||
|
|
||||||
export const StyledBoard = styled.div`
|
|
||||||
border-radius: ${({ theme }) => theme.spacing(2)};
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
flex-direction: row;
|
|
||||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
export type BoardPipelineStageColumn = {
|
|
||||||
pipelineStageId: string;
|
|
||||||
title: string;
|
|
||||||
index: number;
|
|
||||||
colorCode?: string;
|
|
||||||
pipelineProgressIds: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export function getOptimisticlyUpdatedBoard(
|
|
||||||
board: BoardPipelineStageColumn[],
|
|
||||||
result: DropResult,
|
|
||||||
) {
|
|
||||||
const newBoard = JSON.parse(JSON.stringify(board));
|
|
||||||
const { destination, source } = result;
|
|
||||||
if (!destination) return;
|
|
||||||
const sourceColumnIndex = newBoard.findIndex(
|
|
||||||
(column: BoardPipelineStageColumn) =>
|
|
||||||
column.pipelineStageId === source.droppableId,
|
|
||||||
);
|
|
||||||
const sourceColumn = newBoard[sourceColumnIndex];
|
|
||||||
const destinationColumnIndex = newBoard.findIndex(
|
|
||||||
(column: BoardPipelineStageColumn) =>
|
|
||||||
column.pipelineStageId === destination.droppableId,
|
|
||||||
);
|
|
||||||
const destinationColumn = newBoard[destinationColumnIndex];
|
|
||||||
if (!destinationColumn || !sourceColumn) return;
|
|
||||||
const sourceItems = sourceColumn.pipelineProgressIds;
|
|
||||||
const destinationItems = destinationColumn.pipelineProgressIds;
|
|
||||||
|
|
||||||
const [removed] = sourceItems.splice(source.index, 1);
|
|
||||||
destinationItems.splice(destination.index, 0, removed);
|
|
||||||
|
|
||||||
const newSourceColumn: BoardPipelineStageColumn = {
|
|
||||||
...sourceColumn,
|
|
||||||
pipelineProgressIds: sourceItems,
|
|
||||||
};
|
|
||||||
|
|
||||||
const newDestinationColumn = {
|
|
||||||
...destinationColumn,
|
|
||||||
pipelineProgressIds: destinationItems,
|
|
||||||
};
|
|
||||||
|
|
||||||
newBoard.splice(sourceColumnIndex, 1, newSourceColumn);
|
|
||||||
newBoard.splice(destinationColumnIndex, 1, newDestinationColumn);
|
|
||||||
return newBoard;
|
|
||||||
}
|
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { boardCardIdsByColumnIdFamilyState } from '@/ui/board/states/boardCardIdsByColumnIdFamilyState';
|
||||||
|
import { boardColumnsState } from '@/ui/board/states/boardColumnsState';
|
||||||
|
import { selectedBoardCardIdsState } from '@/ui/board/states/selectedBoardCardIdsState';
|
||||||
|
import { IconTrash } from '@/ui/icon/index';
|
||||||
|
import { EntityTableActionBarButton } from '@/ui/table/action-bar/components/EntityTableActionBarButton';
|
||||||
|
|
||||||
|
export function BoardActionBarButtonDeleteBoardCard({
|
||||||
|
onDelete,
|
||||||
|
}: {
|
||||||
|
onDelete: (deletedCardIds: string[]) => void;
|
||||||
|
}) {
|
||||||
|
const deleteBoardCardIds = useRecoilCallback(
|
||||||
|
({ set, snapshot }) =>
|
||||||
|
() => {
|
||||||
|
const boardCardIdsToDelete = snapshot
|
||||||
|
.getLoadable(selectedBoardCardIdsState)
|
||||||
|
.getValue();
|
||||||
|
|
||||||
|
const boardColumns = snapshot.getLoadable(boardColumnsState).getValue();
|
||||||
|
|
||||||
|
for (const boardColumn of boardColumns) {
|
||||||
|
const boardColumnCardIds = snapshot
|
||||||
|
.getLoadable(boardCardIdsByColumnIdFamilyState(boardColumn.id))
|
||||||
|
.getValue();
|
||||||
|
|
||||||
|
const newBoardColumnCardIds = boardColumnCardIds.filter(
|
||||||
|
(cardId) => !boardCardIdsToDelete.includes(cardId),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (newBoardColumnCardIds.length !== boardColumnCardIds.length) {
|
||||||
|
set(
|
||||||
|
boardCardIdsByColumnIdFamilyState(boardColumn.id),
|
||||||
|
newBoardColumnCardIds,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set(selectedBoardCardIdsState, []);
|
||||||
|
|
||||||
|
return boardCardIdsToDelete;
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
async function handleDeleteClick() {
|
||||||
|
const deletedCardIds = deleteBoardCardIds();
|
||||||
|
|
||||||
|
onDelete(deletedCardIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EntityTableActionBarButton
|
||||||
|
label="Delete"
|
||||||
|
icon={<IconTrash size={16} />}
|
||||||
|
type="warning"
|
||||||
|
onClick={handleDeleteClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -8,6 +8,9 @@ import { useRecoilState } from 'recoil';
|
|||||||
|
|
||||||
import { CompanyBoardContext } from '@/companies/states/CompanyBoardContext';
|
import { CompanyBoardContext } from '@/companies/states/CompanyBoardContext';
|
||||||
import { BoardHeader } from '@/ui/board/components/BoardHeader';
|
import { BoardHeader } from '@/ui/board/components/BoardHeader';
|
||||||
|
import { StyledBoard } from '@/ui/board/components/StyledBoard';
|
||||||
|
import { useUpdateBoardCardIds } from '@/ui/board/hooks/useUpdateBoardCardIds';
|
||||||
|
import { BoardColumnIdContext } from '@/ui/board/states/BoardColumnIdContext';
|
||||||
import { SelectedSortType } from '@/ui/filter-n-sort/types/interface';
|
import { SelectedSortType } from '@/ui/filter-n-sort/types/interface';
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||||
import {
|
import {
|
||||||
@ -17,13 +20,9 @@ import {
|
|||||||
useUpdateOnePipelineProgressStageMutation,
|
useUpdateOnePipelineProgressStageMutation,
|
||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
|
|
||||||
import {
|
import { GET_PIPELINE_PROGRESS } from '../../../pipeline/queries';
|
||||||
getOptimisticlyUpdatedBoard,
|
|
||||||
StyledBoard,
|
|
||||||
} from '../../ui/board/components/Board';
|
|
||||||
import { GET_PIPELINE_PROGRESS } from '../queries';
|
|
||||||
import { BoardColumnContext } from '../states/BoardColumnContext';
|
import { BoardColumnContext } from '../states/BoardColumnContext';
|
||||||
import { boardState } from '../states/boardState';
|
import { boardColumnsState } from '../states/boardColumnsState';
|
||||||
import { BoardOptions } from '../types/BoardOptions';
|
import { BoardOptions } from '../types/BoardOptions';
|
||||||
|
|
||||||
import { EntityBoardColumn } from './EntityBoardColumn';
|
import { EntityBoardColumn } from './EntityBoardColumn';
|
||||||
@ -38,13 +37,17 @@ const StyledBoardWithHeader = styled.div`
|
|||||||
export function EntityBoard({
|
export function EntityBoard({
|
||||||
boardOptions,
|
boardOptions,
|
||||||
updateSorts,
|
updateSorts,
|
||||||
|
onEditColumnTitle,
|
||||||
|
onEditColumnColor,
|
||||||
}: {
|
}: {
|
||||||
boardOptions: BoardOptions;
|
boardOptions: BoardOptions;
|
||||||
updateSorts: (
|
updateSorts: (
|
||||||
sorts: Array<SelectedSortType<PipelineProgressOrderByWithRelationInput>>,
|
sorts: Array<SelectedSortType<PipelineProgressOrderByWithRelationInput>>,
|
||||||
) => void;
|
) => void;
|
||||||
|
onEditColumnTitle: (columnId: string, title: string) => void;
|
||||||
|
onEditColumnColor: (columnId: string, color: string) => void;
|
||||||
}) {
|
}) {
|
||||||
const [board, setBoard] = useRecoilState(boardState);
|
const [boardColumns] = useRecoilState(boardColumnsState);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [updatePipelineProgressStage] =
|
const [updatePipelineProgressStage] =
|
||||||
useUpdateOnePipelineProgressStageMutation();
|
useUpdateOnePipelineProgressStageMutation();
|
||||||
@ -65,36 +68,41 @@ export function EntityBoard({
|
|||||||
[updatePipelineProgressStage],
|
[updatePipelineProgressStage],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const updateBoardCardIds = useUpdateBoardCardIds();
|
||||||
|
|
||||||
const onDragEnd: OnDragEndResponder = useCallback(
|
const onDragEnd: OnDragEndResponder = useCallback(
|
||||||
async (result) => {
|
async (result) => {
|
||||||
if (!board) return;
|
if (!boardColumns) return;
|
||||||
const newBoard = getOptimisticlyUpdatedBoard(board, result);
|
|
||||||
if (!newBoard) return;
|
updateBoardCardIds(result);
|
||||||
setBoard(newBoard);
|
|
||||||
try {
|
try {
|
||||||
const draggedEntityId = result.draggableId;
|
const draggedEntityId = result.draggableId;
|
||||||
const destinationColumnId = result.destination?.droppableId;
|
const destinationColumnId = result.destination?.droppableId;
|
||||||
draggedEntityId &&
|
|
||||||
|
// TODO: abstract
|
||||||
|
if (
|
||||||
|
draggedEntityId &&
|
||||||
destinationColumnId &&
|
destinationColumnId &&
|
||||||
updatePipelineProgressStageInDB &&
|
updatePipelineProgressStageInDB
|
||||||
(await updatePipelineProgressStageInDB(
|
) {
|
||||||
|
await updatePipelineProgressStageInDB(
|
||||||
draggedEntityId,
|
draggedEntityId,
|
||||||
destinationColumnId,
|
destinationColumnId,
|
||||||
));
|
);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[board, updatePipelineProgressStageInDB, setBoard],
|
[boardColumns, updatePipelineProgressStageInDB, updateBoardCardIds],
|
||||||
);
|
);
|
||||||
|
|
||||||
const sortedBoard = board
|
const sortedBoardColumns = [...boardColumns].sort((a, b) => {
|
||||||
? [...board].sort((a, b) => {
|
return a.index - b.index;
|
||||||
return a.index - b.index;
|
});
|
||||||
})
|
|
||||||
: [];
|
|
||||||
|
|
||||||
return (board?.length ?? 0) > 0 ? (
|
return (boardColumns?.length ?? 0) > 0 ? (
|
||||||
<StyledBoardWithHeader>
|
<StyledBoardWithHeader>
|
||||||
<BoardHeader
|
<BoardHeader
|
||||||
viewName="All opportunities"
|
viewName="All opportunities"
|
||||||
@ -105,13 +113,17 @@ export function EntityBoard({
|
|||||||
/>
|
/>
|
||||||
<StyledBoard>
|
<StyledBoard>
|
||||||
<DragDropContext onDragEnd={onDragEnd}>
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
{sortedBoard.map((column) => (
|
{sortedBoardColumns.map((column) => (
|
||||||
<RecoilScope
|
<BoardColumnIdContext.Provider value={column.id} key={column.id}>
|
||||||
SpecificContext={BoardColumnContext}
|
<RecoilScope SpecificContext={BoardColumnContext} key={column.id}>
|
||||||
key={column.pipelineStageId}
|
<EntityBoardColumn
|
||||||
>
|
boardOptions={boardOptions}
|
||||||
<EntityBoardColumn boardOptions={boardOptions} column={column} />
|
column={column}
|
||||||
</RecoilScope>
|
onEditColumnTitle={onEditColumnTitle}
|
||||||
|
onEditColumnColor={onEditColumnColor}
|
||||||
|
/>
|
||||||
|
</RecoilScope>
|
||||||
|
</BoardColumnIdContext.Provider>
|
||||||
))}
|
))}
|
||||||
</DragDropContext>
|
</DragDropContext>
|
||||||
</StyledBoard>
|
</StyledBoard>
|
||||||
@ -3,13 +3,13 @@ import { useRecoilValue } from 'recoil';
|
|||||||
|
|
||||||
import { ActionBar } from '@/ui/action-bar/components/ActionBar';
|
import { ActionBar } from '@/ui/action-bar/components/ActionBar';
|
||||||
|
|
||||||
import { selectedBoardCardsState } from '../states/selectedBoardCardsState';
|
import { selectedBoardCardIdsState } from '../states/selectedBoardCardIdsState';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
children: React.ReactNode | React.ReactNode[];
|
children: React.ReactNode | React.ReactNode[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export function EntityBoardActionBar({ children }: OwnProps) {
|
export function EntityBoardActionBar({ children }: OwnProps) {
|
||||||
const selectedBoardCards = useRecoilValue(selectedBoardCardsState);
|
const selectedBoardCards = useRecoilValue(selectedBoardCardIdsState);
|
||||||
return <ActionBar selectedIds={selectedBoardCards}>{children}</ActionBar>;
|
return <ActionBar selectedIds={selectedBoardCards}>{children}</ActionBar>;
|
||||||
}
|
}
|
||||||
@ -1,10 +1,5 @@
|
|||||||
import { useEffect } from 'react';
|
|
||||||
import { Draggable } from '@hello-pangea/dnd';
|
import { Draggable } from '@hello-pangea/dnd';
|
||||||
|
|
||||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
|
||||||
|
|
||||||
import { BoardCardContext } from '../states/BoardCardContext';
|
|
||||||
import { pipelineProgressIdScopedState } from '../states/pipelineProgressIdScopedState';
|
|
||||||
import { BoardOptions } from '../types/BoardOptions';
|
import { BoardOptions } from '../types/BoardOptions';
|
||||||
|
|
||||||
export function EntityBoardCard({
|
export function EntityBoardCard({
|
||||||
@ -16,15 +11,6 @@ export function EntityBoardCard({
|
|||||||
pipelineProgressId: string;
|
pipelineProgressId: string;
|
||||||
index: number;
|
index: number;
|
||||||
}) {
|
}) {
|
||||||
const [pipelineProgressIdFromRecoil, setPipelineProgressId] =
|
|
||||||
useRecoilScopedState(pipelineProgressIdScopedState, BoardCardContext);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (pipelineProgressIdFromRecoil !== pipelineProgressId) {
|
|
||||||
setPipelineProgressId(pipelineProgressId);
|
|
||||||
}
|
|
||||||
}, [pipelineProgressId, setPipelineProgressId, pipelineProgressIdFromRecoil]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Draggable
|
<Draggable
|
||||||
key={pipelineProgressId}
|
key={pipelineProgressId}
|
||||||
@ -1,20 +1,16 @@
|
|||||||
import { useEffect } from 'react';
|
import { useContext } from 'react';
|
||||||
import { getOperationName } from '@apollo/client/utilities';
|
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { Draggable, Droppable, DroppableProvided } from '@hello-pangea/dnd';
|
import { Draggable, Droppable, DroppableProvided } from '@hello-pangea/dnd';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { BoardPipelineStageColumn } from '@/ui/board/components/Board';
|
|
||||||
import { BoardColumn } from '@/ui/board/components/BoardColumn';
|
import { BoardColumn } from '@/ui/board/components/BoardColumn';
|
||||||
|
import { BoardCardIdContext } from '@/ui/board/states/BoardCardIdContext';
|
||||||
|
import { BoardColumnIdContext } from '@/ui/board/states/BoardColumnIdContext';
|
||||||
|
import { BoardColumnDefinition } from '@/ui/board/types/BoardColumnDefinition';
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
|
||||||
import { useUpdatePipelineStageMutation } from '~/generated/graphql';
|
|
||||||
|
|
||||||
import { GET_PIPELINES } from '../queries';
|
import { boardCardIdsByColumnIdFamilyState } from '../states/boardCardIdsByColumnIdFamilyState';
|
||||||
import { BoardCardContext } from '../states/BoardCardContext';
|
|
||||||
import { BoardColumnContext } from '../states/BoardColumnContext';
|
|
||||||
import { boardColumnTotalsFamilySelector } from '../states/boardColumnTotalsFamilySelector';
|
import { boardColumnTotalsFamilySelector } from '../states/boardColumnTotalsFamilySelector';
|
||||||
import { pipelineStageIdScopedState } from '../states/pipelineStageIdScopedState';
|
|
||||||
import { BoardOptions } from '../types/BoardOptions';
|
import { BoardOptions } from '../types/BoardOptions';
|
||||||
|
|
||||||
import { EntityBoardCard } from './EntityBoardCard';
|
import { EntityBoardCard } from './EntityBoardCard';
|
||||||
@ -54,75 +50,56 @@ const BoardColumnCardsContainer = ({
|
|||||||
export function EntityBoardColumn({
|
export function EntityBoardColumn({
|
||||||
column,
|
column,
|
||||||
boardOptions,
|
boardOptions,
|
||||||
|
onEditColumnTitle,
|
||||||
|
onEditColumnColor,
|
||||||
}: {
|
}: {
|
||||||
column: BoardPipelineStageColumn;
|
column: BoardColumnDefinition;
|
||||||
boardOptions: BoardOptions;
|
boardOptions: BoardOptions;
|
||||||
|
onEditColumnTitle: (columnId: string, title: string) => void;
|
||||||
|
onEditColumnColor: (columnId: string, color: string) => void;
|
||||||
}) {
|
}) {
|
||||||
const [pipelineStageId, setPipelineStageId] = useRecoilScopedState(
|
const boardColumnId = useContext(BoardColumnIdContext) ?? '';
|
||||||
pipelineStageIdScopedState,
|
|
||||||
BoardColumnContext,
|
|
||||||
);
|
|
||||||
const boardColumnTotal = useRecoilValue(
|
const boardColumnTotal = useRecoilValue(
|
||||||
boardColumnTotalsFamilySelector(column.pipelineStageId),
|
boardColumnTotalsFamilySelector(column.id),
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
const cardIds = useRecoilValue(
|
||||||
if (pipelineStageId !== column.pipelineStageId) {
|
boardCardIdsByColumnIdFamilyState(boardColumnId ?? ''),
|
||||||
setPipelineStageId(column.pipelineStageId);
|
);
|
||||||
}
|
|
||||||
}, [column, setPipelineStageId, pipelineStageId]);
|
|
||||||
|
|
||||||
const [updatePipelineStage] = useUpdatePipelineStageMutation();
|
|
||||||
function handleEditColumnTitle(value: string) {
|
function handleEditColumnTitle(value: string) {
|
||||||
updatePipelineStage({
|
onEditColumnTitle(boardColumnId, value);
|
||||||
variables: {
|
|
||||||
id: pipelineStageId,
|
|
||||||
data: { name: value },
|
|
||||||
},
|
|
||||||
refetchQueries: [getOperationName(GET_PIPELINES) || ''],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleEditColumnColor(value: string) {
|
function handleEditColumnColor(newColor: string) {
|
||||||
updatePipelineStage({
|
onEditColumnColor(boardColumnId, newColor);
|
||||||
variables: {
|
|
||||||
id: pipelineStageId,
|
|
||||||
data: { color: value },
|
|
||||||
},
|
|
||||||
refetchQueries: [getOperationName(GET_PIPELINES) || ''],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Droppable droppableId={column.pipelineStageId}>
|
<Droppable droppableId={column.id}>
|
||||||
{(droppableProvided) => (
|
{(droppableProvided) => (
|
||||||
<BoardColumn
|
<BoardColumn
|
||||||
onColumnColorEdit={handleEditColumnColor}
|
onColumnColorEdit={handleEditColumnColor}
|
||||||
onTitleEdit={handleEditColumnTitle}
|
onTitleEdit={handleEditColumnTitle}
|
||||||
title={column.title}
|
title={column.title}
|
||||||
color={column.colorCode}
|
color={column.colorCode}
|
||||||
pipelineStageId={column.pipelineStageId}
|
pipelineStageId={column.id}
|
||||||
totalAmount={boardColumnTotal}
|
totalAmount={boardColumnTotal}
|
||||||
isFirstColumn={column.index === 0}
|
isFirstColumn={column.index === 0}
|
||||||
numChildren={column.pipelineProgressIds.length}
|
numChildren={cardIds.length}
|
||||||
>
|
>
|
||||||
<BoardColumnCardsContainer droppableProvided={droppableProvided}>
|
<BoardColumnCardsContainer droppableProvided={droppableProvided}>
|
||||||
{column.pipelineProgressIds.map((pipelineProgressId, index) => (
|
{cardIds.map((cardId, index) => (
|
||||||
<RecoilScope
|
<BoardCardIdContext.Provider value={cardId} key={cardId}>
|
||||||
SpecificContext={BoardCardContext}
|
|
||||||
key={pipelineProgressId}
|
|
||||||
>
|
|
||||||
<EntityBoardCard
|
<EntityBoardCard
|
||||||
index={index}
|
index={index}
|
||||||
pipelineProgressId={pipelineProgressId}
|
pipelineProgressId={cardId}
|
||||||
boardOptions={boardOptions}
|
boardOptions={boardOptions}
|
||||||
/>
|
/>
|
||||||
</RecoilScope>
|
</BoardCardIdContext.Provider>
|
||||||
))}
|
))}
|
||||||
<Draggable
|
<Draggable draggableId={`new-${column.id}`} index={cardIds.length}>
|
||||||
draggableId={`new-${column.pipelineStageId}`}
|
|
||||||
index={column.pipelineProgressIds.length}
|
|
||||||
>
|
|
||||||
{(draggableProvided) => (
|
{(draggableProvided) => (
|
||||||
<div
|
<div
|
||||||
ref={draggableProvided?.innerRef}
|
ref={draggableProvided?.innerRef}
|
||||||
9
front/src/modules/ui/board/components/StyledBoard.tsx
Normal file
9
front/src/modules/ui/board/components/StyledBoard.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
export const StyledBoard = styled.div`
|
||||||
|
border-radius: ${({ theme }) => theme.spacing(2)};
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: row;
|
||||||
|
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||||
|
`;
|
||||||
@ -1,59 +1,53 @@
|
|||||||
import { DropResult } from '@hello-pangea/dnd';
|
// TODO: refactor this test with Recoil
|
||||||
|
|
||||||
import { getOptimisticlyUpdatedBoard } from '../Board';
|
|
||||||
|
|
||||||
describe('getOptimisticlyUpdatedBoard', () => {
|
describe('getOptimisticlyUpdatedBoard', () => {
|
||||||
it('should return a new board with the updated cell', () => {
|
it('should return a new board with the updated cell', () => {
|
||||||
const initialColumn1: string[] = ['item-1', 'item-2', 'item-3'];
|
// const initialColumn1: string[] = ['item-1', 'item-2', 'item-3'];
|
||||||
const initialColumn2: string[] = ['item-4', 'item-5'];
|
// const initialColumn2: string[] = ['item-4', 'item-5'];
|
||||||
|
// const finalColumn1: string[] = ['item-2', 'item-3'];
|
||||||
const finalColumn1: string[] = ['item-2', 'item-3'];
|
// const finalColumn2: string[] = ['item-4', 'item-1', 'item-5'];
|
||||||
const finalColumn2: string[] = ['item-4', 'item-1', 'item-5'];
|
// const dropResult = {
|
||||||
|
// source: {
|
||||||
const dropResult = {
|
// droppableId: 'column-1',
|
||||||
source: {
|
// index: 0,
|
||||||
droppableId: 'column-1',
|
// },
|
||||||
index: 0,
|
// destination: {
|
||||||
},
|
// droppableId: 'column-2',
|
||||||
destination: {
|
// index: 1,
|
||||||
droppableId: 'column-2',
|
// },
|
||||||
index: 1,
|
// } as DropResult;
|
||||||
},
|
// const initialBoard = [
|
||||||
} as DropResult;
|
// {
|
||||||
|
// id: 'column-1',
|
||||||
const initialBoard = [
|
// title: 'My Column',
|
||||||
{
|
// pipelineStageId: 'column-1',
|
||||||
id: 'column-1',
|
// pipelineProgressIds: initialColumn1,
|
||||||
title: 'My Column',
|
// },
|
||||||
pipelineStageId: 'column-1',
|
// {
|
||||||
pipelineProgressIds: initialColumn1,
|
// id: 'column-2',
|
||||||
},
|
// title: 'My Column',
|
||||||
{
|
// pipelineStageId: 'column-2',
|
||||||
id: 'column-2',
|
// pipelineProgressIds: initialColumn2,
|
||||||
title: 'My Column',
|
// },
|
||||||
pipelineStageId: 'column-2',
|
// ];
|
||||||
pipelineProgressIds: initialColumn2,
|
// const updatedBoard = u(
|
||||||
},
|
// initialBoard,
|
||||||
];
|
// dropResult,
|
||||||
|
// );
|
||||||
const updatedBoard = getOptimisticlyUpdatedBoard(initialBoard, dropResult);
|
// const finalBoard = [
|
||||||
|
// {
|
||||||
const finalBoard = [
|
// id: 'column-1',
|
||||||
{
|
// title: 'My Column',
|
||||||
id: 'column-1',
|
// pipelineStageId: 'column-1',
|
||||||
title: 'My Column',
|
// pipelineProgressIds: finalColumn1,
|
||||||
pipelineStageId: 'column-1',
|
// },
|
||||||
pipelineProgressIds: finalColumn1,
|
// {
|
||||||
},
|
// id: 'column-2',
|
||||||
{
|
// title: 'My Column',
|
||||||
id: 'column-2',
|
// pipelineStageId: 'column-2',
|
||||||
title: 'My Column',
|
// pipelineProgressIds: finalColumn2,
|
||||||
pipelineStageId: 'column-2',
|
// },
|
||||||
pipelineProgressIds: finalColumn2,
|
// ];
|
||||||
},
|
// expect(updatedBoard).toEqual(finalBoard);
|
||||||
];
|
// expect(updatedBoard).not.toBe(initialBoard);
|
||||||
|
|
||||||
expect(updatedBoard).toEqual(finalBoard);
|
|
||||||
expect(updatedBoard).not.toBe(initialBoard);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
86
front/src/modules/ui/board/hooks/useUpdateBoardCardIds.ts
Normal file
86
front/src/modules/ui/board/hooks/useUpdateBoardCardIds.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { DropResult } from '@hello-pangea/dnd'; // Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350
|
||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { boardCardIdsByColumnIdFamilyState } from '../states/boardCardIdsByColumnIdFamilyState';
|
||||||
|
import { boardColumnsState } from '../states/boardColumnsState';
|
||||||
|
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
||||||
|
|
||||||
|
export function useUpdateBoardCardIds() {
|
||||||
|
return useRecoilCallback(
|
||||||
|
({ snapshot, set }) =>
|
||||||
|
(result: DropResult) => {
|
||||||
|
const currentBoardColumns = snapshot
|
||||||
|
.getLoadable(boardColumnsState)
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
const newBoardColumns = [...currentBoardColumns];
|
||||||
|
|
||||||
|
const { destination, source } = result;
|
||||||
|
|
||||||
|
if (!destination) return;
|
||||||
|
|
||||||
|
const sourceColumnIndex = newBoardColumns.findIndex(
|
||||||
|
(boardColumn: BoardColumnDefinition) =>
|
||||||
|
boardColumn.id === source.droppableId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const sourceColumn = newBoardColumns[sourceColumnIndex];
|
||||||
|
|
||||||
|
const destinationColumnIndex = newBoardColumns.findIndex(
|
||||||
|
(boardColumn: BoardColumnDefinition) =>
|
||||||
|
boardColumn.id === destination.droppableId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const destinationColumn = newBoardColumns[destinationColumnIndex];
|
||||||
|
|
||||||
|
if (!destinationColumn || !sourceColumn) return;
|
||||||
|
|
||||||
|
const sourceCardIds = [
|
||||||
|
...snapshot
|
||||||
|
.getLoadable(boardCardIdsByColumnIdFamilyState(sourceColumn.id))
|
||||||
|
.valueOrThrow(),
|
||||||
|
];
|
||||||
|
|
||||||
|
const destinationCardIds = [
|
||||||
|
...snapshot
|
||||||
|
.getLoadable(
|
||||||
|
boardCardIdsByColumnIdFamilyState(destinationColumn.id),
|
||||||
|
)
|
||||||
|
.valueOrThrow(),
|
||||||
|
];
|
||||||
|
|
||||||
|
const destinationIndex =
|
||||||
|
destination.index >= destinationCardIds.length
|
||||||
|
? destinationCardIds.length - 1
|
||||||
|
: destination.index;
|
||||||
|
|
||||||
|
if (sourceColumn.id === destinationColumn.id) {
|
||||||
|
const [deletedCardId] = sourceCardIds.splice(source.index, 1);
|
||||||
|
|
||||||
|
sourceCardIds.splice(destinationIndex, 0, deletedCardId);
|
||||||
|
|
||||||
|
set(
|
||||||
|
boardCardIdsByColumnIdFamilyState(sourceColumn.id),
|
||||||
|
sourceCardIds,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const [removedCardId] = sourceCardIds.splice(source.index, 1);
|
||||||
|
|
||||||
|
destinationCardIds.splice(destinationIndex, 0, removedCardId);
|
||||||
|
|
||||||
|
set(
|
||||||
|
boardCardIdsByColumnIdFamilyState(sourceColumn.id),
|
||||||
|
sourceCardIds,
|
||||||
|
);
|
||||||
|
|
||||||
|
set(
|
||||||
|
boardCardIdsByColumnIdFamilyState(destinationColumn.id),
|
||||||
|
destinationCardIds,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newBoardColumns;
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
}
|
||||||
3
front/src/modules/ui/board/states/BoardCardIdContext.ts
Normal file
3
front/src/modules/ui/board/states/BoardCardIdContext.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
|
||||||
|
export const BoardCardIdContext = createContext<string | null>(null);
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
|
||||||
|
export const BoardColumnIdContext = createContext<string | null>(null);
|
||||||
5
front/src/modules/ui/board/states/BoardOptionsContext.ts
Normal file
5
front/src/modules/ui/board/states/BoardOptionsContext.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
|
||||||
|
import { BoardOptions } from '@/ui/board/types/BoardOptions';
|
||||||
|
|
||||||
|
export const BoardOptionsContext = createContext<BoardOptions | null>(null);
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const boardCardIdsByColumnIdFamilyState = atomFamily<string[], string>({
|
||||||
|
key: 'boardCardIdsByColumnIdFamilyState',
|
||||||
|
default: [],
|
||||||
|
});
|
||||||
@ -1,25 +1,22 @@
|
|||||||
import { selectorFamily } from 'recoil';
|
import { selectorFamily } from 'recoil';
|
||||||
|
|
||||||
import { companyProgressesFamilyState } from '@/companies/states/companyProgressesFamilyState';
|
import { companyProgressesFamilyState } from '@/companies/states/companyProgressesFamilyState';
|
||||||
import { BoardPipelineStageColumn } from '@/ui/board/components/Board';
|
|
||||||
|
|
||||||
import { boardState } from './boardState';
|
import { boardCardIdsByColumnIdFamilyState } from './boardCardIdsByColumnIdFamilyState';
|
||||||
|
|
||||||
|
// TODO: this state should be computed during the synchronization hook and put in a generic
|
||||||
|
// boardColumnTotalsFamilyState indexed by columnId.
|
||||||
export const boardColumnTotalsFamilySelector = selectorFamily({
|
export const boardColumnTotalsFamilySelector = selectorFamily({
|
||||||
key: 'boardColumnTotalsFamilySelector',
|
key: 'boardColumnTotalsFamilySelector',
|
||||||
get:
|
get:
|
||||||
(pipelineStageId: string) =>
|
(pipelineStageId: string) =>
|
||||||
({ get }) => {
|
({ get }) => {
|
||||||
const board = get(boardState);
|
const cardIds = get(boardCardIdsByColumnIdFamilyState(pipelineStageId));
|
||||||
const pipelineStage = board?.find(
|
|
||||||
(pipelineStage: BoardPipelineStageColumn) =>
|
const pipelineProgresses = cardIds.map((pipelineProgressId: string) =>
|
||||||
pipelineStage.pipelineStageId === pipelineStageId,
|
get(companyProgressesFamilyState(pipelineProgressId)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const pipelineProgresses = pipelineStage?.pipelineProgressIds.map(
|
|
||||||
(pipelineProgressId: string) =>
|
|
||||||
get(companyProgressesFamilyState(pipelineProgressId)),
|
|
||||||
);
|
|
||||||
const pipelineStageTotal: number =
|
const pipelineStageTotal: number =
|
||||||
pipelineProgresses?.reduce(
|
pipelineProgresses?.reduce(
|
||||||
(acc: number, curr: any) => acc + curr?.pipelineProgress.amount,
|
(acc: number, curr: any) => acc + curr?.pipelineProgress.amount,
|
||||||
8
front/src/modules/ui/board/states/boardColumnsState.ts
Normal file
8
front/src/modules/ui/board/states/boardColumnsState.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { atom } from 'recoil';
|
||||||
|
|
||||||
|
import { BoardColumnDefinition } from '@/ui/board/types/BoardColumnDefinition';
|
||||||
|
|
||||||
|
export const boardColumnsState = atom<BoardColumnDefinition[]>({
|
||||||
|
key: 'boardColumnsState',
|
||||||
|
default: [],
|
||||||
|
});
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { atom } from 'recoil';
|
||||||
|
|
||||||
|
export const selectedBoardCardIdsState = atom<string[]>({
|
||||||
|
key: 'selectedBoardCardIdsState',
|
||||||
|
default: [],
|
||||||
|
});
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
export type BoardColumnDefinition = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
index: number;
|
||||||
|
colorCode?: string;
|
||||||
|
};
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
import { Filter } from '../types/Filter';
|
||||||
|
|
||||||
|
export const sortScopedState = atomFamily<Filter[], string>({
|
||||||
|
key: 'sortScopedState',
|
||||||
|
default: [],
|
||||||
|
});
|
||||||
@ -42,8 +42,6 @@ export function GenericEditableNumberCellEditMode({ viewField }: OwnProps) {
|
|||||||
throw new Error('Number too big');
|
throw new Error('Number too big');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log({ numberValue });
|
|
||||||
|
|
||||||
setFieldValue(numberValue.toString());
|
setFieldValue(numberValue.toString());
|
||||||
|
|
||||||
if (currentRowEntityId && updateField) {
|
if (currentRowEntityId && updateField) {
|
||||||
|
|||||||
@ -1,20 +1,27 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
|
import { getOperationName } from '@apollo/client/utilities';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
|
|
||||||
import { HooksCompanyBoard } from '@/companies/components/HooksCompanyBoard';
|
import { HooksCompanyBoard } from '@/companies/components/HooksCompanyBoard';
|
||||||
import { CompanyBoardContext } from '@/companies/states/CompanyBoardContext';
|
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 {
|
import {
|
||||||
defaultPipelineProgressOrderBy,
|
defaultPipelineProgressOrderBy,
|
||||||
|
GET_PIPELINES,
|
||||||
PipelineProgressesSelectedSortType,
|
PipelineProgressesSelectedSortType,
|
||||||
} from '@/pipeline/queries';
|
} from '@/pipeline/queries';
|
||||||
|
import { BoardActionBarButtonDeleteBoardCard } from '@/ui/board/components/BoardActionBarButtonDeleteBoardCard';
|
||||||
|
import { EntityBoard } from '@/ui/board/components/EntityBoard';
|
||||||
|
import { EntityBoardActionBar } from '@/ui/board/components/EntityBoardActionBar';
|
||||||
|
import { BoardOptionsContext } from '@/ui/board/states/BoardOptionsContext';
|
||||||
import { reduceSortsToOrderBy } from '@/ui/filter-n-sort/helpers';
|
import { reduceSortsToOrderBy } from '@/ui/filter-n-sort/helpers';
|
||||||
import { IconTargetArrow } from '@/ui/icon/index';
|
import { IconTargetArrow } from '@/ui/icon/index';
|
||||||
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
|
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||||
import { PipelineProgressOrderByWithRelationInput } from '~/generated/graphql';
|
import {
|
||||||
|
PipelineProgressOrderByWithRelationInput,
|
||||||
|
useDeleteManyPipelineProgressMutation,
|
||||||
|
useUpdatePipelineStageMutation,
|
||||||
|
} from '~/generated/graphql';
|
||||||
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
|
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
|
||||||
|
|
||||||
export function Opportunities() {
|
export function Opportunities() {
|
||||||
@ -35,24 +42,62 @@ export function Opportunities() {
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [updatePipelineStage] = useUpdatePipelineStageMutation();
|
||||||
|
|
||||||
|
function handleEditColumnTitle(boardColumnId: string, newTitle: string) {
|
||||||
|
updatePipelineStage({
|
||||||
|
variables: {
|
||||||
|
id: boardColumnId,
|
||||||
|
data: { name: newTitle },
|
||||||
|
},
|
||||||
|
refetchQueries: [getOperationName(GET_PIPELINES) || ''],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEditColumnColor(boardColumnId: string, newColor: string) {
|
||||||
|
updatePipelineStage({
|
||||||
|
variables: {
|
||||||
|
id: boardColumnId,
|
||||||
|
data: { color: newColor },
|
||||||
|
},
|
||||||
|
refetchQueries: [getOperationName(GET_PIPELINES) || ''],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const [deletePipelineProgress] = useDeleteManyPipelineProgressMutation({
|
||||||
|
refetchQueries: [getOperationName(GET_PIPELINES) ?? ''],
|
||||||
|
});
|
||||||
|
|
||||||
|
async function handleDelete(cardIdsToDelete: string[]) {
|
||||||
|
await deletePipelineProgress({
|
||||||
|
variables: {
|
||||||
|
ids: cardIdsToDelete,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WithTopBarContainer
|
<WithTopBarContainer
|
||||||
title="Opportunities"
|
title="Opportunities"
|
||||||
icon={<IconTargetArrow size={theme.icon.size.md} />}
|
icon={<IconTargetArrow size={theme.icon.size.md} />}
|
||||||
>
|
>
|
||||||
<RecoilScope SpecificContext={CompanyBoardContext}>
|
<BoardOptionsContext.Provider value={opportunitiesBoardOptions}>
|
||||||
<HooksCompanyBoard
|
<RecoilScope SpecificContext={CompanyBoardContext}>
|
||||||
availableFilters={opportunitiesBoardOptions.filters}
|
<HooksCompanyBoard
|
||||||
orderBy={orderBy}
|
availableFilters={opportunitiesBoardOptions.filters}
|
||||||
/>
|
orderBy={orderBy}
|
||||||
<EntityBoard
|
/>
|
||||||
boardOptions={opportunitiesBoardOptions}
|
<EntityBoard
|
||||||
updateSorts={updateSorts}
|
boardOptions={opportunitiesBoardOptions}
|
||||||
/>
|
updateSorts={updateSorts}
|
||||||
<EntityBoardActionBar>
|
onEditColumnColor={handleEditColumnColor}
|
||||||
<BoardActionBarButtonDeletePipelineProgress />
|
onEditColumnTitle={handleEditColumnTitle}
|
||||||
</EntityBoardActionBar>
|
/>
|
||||||
</RecoilScope>
|
<EntityBoardActionBar>
|
||||||
|
<BoardActionBarButtonDeleteBoardCard onDelete={handleDelete} />
|
||||||
|
</EntityBoardActionBar>
|
||||||
|
</RecoilScope>
|
||||||
|
</BoardOptionsContext.Provider>
|
||||||
</WithTopBarContainer>
|
</WithTopBarContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { CompanyBoardCard } from '@/companies/components/CompanyBoardCard';
|
import { CompanyBoardCard } from '@/companies/components/CompanyBoardCard';
|
||||||
import { NewCompanyProgressButton } from '@/companies/components/NewCompanyProgressButton';
|
import { NewCompanyProgressButton } from '@/companies/components/NewCompanyProgressButton';
|
||||||
import { BoardOptions } from '@/pipeline/types/BoardOptions';
|
import { BoardOptions } from '@/ui/board/types/BoardOptions';
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||||
|
|
||||||
import { opportunitiesFilters } from './opportunities-filters';
|
import { opportunitiesFilters } from './opportunities-filters';
|
||||||
|
|||||||
Reference in New Issue
Block a user