2426 timebox refactor board with the new scope architecture (#2789)

* scoped states: wip

* scoped states: wip

* wip

* wip

* create boardFiltersScopedState and boardSortsScopedState

* wip

* reorganize hooks

* update hooks

* wip

* wip

* fix options dropdown

* clean unused selectors

* fields are working

* fix filter an sort

* fix entity count

* rename hooks

* rename states

* clean unused context

* fix recoil scope bug

* objectNameSingular instead of objectNamePlural
This commit is contained in:
bosiraphael
2023-12-05 12:15:20 +01:00
committed by GitHub
parent 5c0ad30186
commit 95a1cfeec3
84 changed files with 1204 additions and 765 deletions

View File

@ -5,17 +5,12 @@ import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { CompanyBoard } from '../board/components/CompanyBoard';
import { CompanyBoardRecoilScopeContext } from '../states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
const meta: Meta<typeof CompanyBoard> = {
title: 'Modules/Companies/Board',
component: CompanyBoard,
decorators: [
(Story) => (
<CompanyBoardRecoilScopeContext.Provider value="opportunities">
<Story />
</CompanyBoardRecoilScopeContext.Provider>
),
(Story) => <Story />,
ComponentWithRouterDecorator,
SnackBarDecorator,
],

View File

@ -1,21 +1,17 @@
import styled from '@emotion/styled';
import { BoardContext } from '@/companies/states/contexts/BoardContext';
import { mapBoardFieldDefinitionsToViewFields } from '@/companies/utils/mapBoardFieldDefinitionsToViewFields';
import { RecordBoardActionBar } from '@/ui/object/record-board/action-bar/components/RecordBoardActionBar';
import { BoardOptionsDropdownId } from '@/ui/object/record-board/components/constants/BoardOptionsDropdownId';
import {
RecordBoard,
RecordBoardProps,
} from '@/ui/object/record-board/components/RecordBoard';
import { RecordBoardContextMenu } from '@/ui/object/record-board/context-menu/components/RecordBoardContextMenu';
import { BoardOptionsDropdown } from '@/ui/object/record-board/options/components/BoardOptionsDropdown';
import { RecordBoardEffect } from '@/ui/object/record-board/components/RecordBoardEffect';
import { RecordBoardOptionsDropdown } from '@/ui/object/record-board/options/components/RecordBoardOptionsDropdown';
import { ViewBar } from '@/views/components/ViewBar';
import { useViewFields } from '@/views/hooks/internal/useViewFields';
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
import { HooksCompanyBoardEffect } from '../../components/HooksCompanyBoardEffect';
import { CompanyBoardRecoilScopeContext } from '../../states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
const StyledContainer = styled.div`
display: flex;
@ -36,34 +32,38 @@ export const CompanyBoard = ({
onEditColumnTitle,
}: CompanyBoardProps) => {
const viewBarId = 'company-board-view';
const recordBoardId = 'company-board';
const { persistViewFields } = useViewFields(viewBarId);
return (
<StyledContainer>
<BoardContext.Provider
value={{
BoardRecoilScopeContext: CompanyBoardRecoilScopeContext,
onFieldsChange: (fields) => {
persistViewFields(mapBoardFieldDefinitionsToViewFields(fields));
},
<ViewBar
viewBarId={viewBarId}
optionsDropdownButton={
<RecordBoardOptionsDropdown recordBoardId={recordBoardId} />
}
optionsDropdownScopeId={recordBoardId}
/>
<HooksCompanyBoardEffect
viewBarId={viewBarId}
recordBoardId={recordBoardId}
/>
<RecordBoardEffect
recordBoardId={recordBoardId}
onFieldsChange={(fields) => {
persistViewFields(mapBoardFieldDefinitionsToViewFields(fields));
}}
>
<ViewBar
viewBarId={viewBarId}
optionsDropdownButton={<BoardOptionsDropdown />}
optionsDropdownScopeId={BoardOptionsDropdownId}
/>
<HooksCompanyBoardEffect viewBarId={viewBarId} />
<RecordBoard
boardOptions={opportunitiesBoardOptions}
onColumnAdd={onColumnAdd}
onColumnDelete={onColumnDelete}
onEditColumnTitle={onEditColumnTitle}
/>
<RecordBoardActionBar />
<RecordBoardContextMenu />
</BoardContext.Provider>
/>
<RecordBoard
recordBoardId={recordBoardId}
boardOptions={opportunitiesBoardOptions}
onColumnAdd={onColumnAdd}
onColumnDelete={onColumnDelete}
onEditColumnTitle={onEditColumnTitle}
/>
</StyledContainer>
);
};

View File

@ -1,6 +1,6 @@
import { ReactNode, useContext } from 'react';
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import { EntityChipVariant } from '@/ui/display/chip/components/EntityChip';
@ -9,15 +9,12 @@ import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
import { Checkbox, CheckboxVariant } from '@/ui/input/components/Checkbox';
import { FieldContext } from '@/ui/object/field/contexts/FieldContext';
import { BoardCardIdContext } from '@/ui/object/record-board/contexts/BoardCardIdContext';
import { useBoardContext } from '@/ui/object/record-board/hooks/useBoardContext';
import { useCurrentCardSelected } from '@/ui/object/record-board/hooks/useCurrentCardSelected';
import { isCardInCompactViewState } from '@/ui/object/record-board/states/isCardInCompactViewState';
import { isCompactViewEnabledState } from '@/ui/object/record-board/states/isCompactViewEnabledState';
import { visibleBoardCardFieldsScopedSelector } from '@/ui/object/record-board/states/selectors/visibleBoardCardFieldsScopedSelector';
import { useCurrentRecordBoardCardSelectedInternal } from '@/ui/object/record-board/hooks/internal/useCurrentRecordBoardCardSelectedInternal';
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
import { isRecordBoardCardInCompactViewFamilyState } from '@/ui/object/record-board/states/isRecordBoardCardInCompactViewFamilyState';
import { RecordInlineCell } from '@/ui/object/record-inline-cell/components/RecordInlineCell';
import { InlineCellHotkeyScope } from '@/ui/object/record-inline-cell/types/InlineCellHotkeyScope';
import { AnimatedEaseInOut } from '@/ui/utilities/animation/components/AnimatedEaseInOut';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { getLogoUrlFromDomainName } from '~/utils';
import { companyProgressesFamilyState } from '../states/companyProgressesFamilyState';
@ -125,30 +122,28 @@ const StyledCompactIconContainer = styled.div`
`;
export const CompanyBoardCard = () => {
const { BoardRecoilScopeContext } = useBoardContext();
const { isCurrentCardSelected, setCurrentCardSelected } =
useCurrentCardSelected();
useCurrentRecordBoardCardSelectedInternal();
const boardCardId = useContext(BoardCardIdContext);
const [companyProgress] = useRecoilState(
companyProgressesFamilyState(boardCardId ?? ''),
);
const { isCompactViewEnabledState, visibleBoardCardFieldsSelector } =
useRecordBoardScopedStates();
const [isCompactViewEnabled] = useRecoilState(isCompactViewEnabledState);
const [isCardInCompactView, setIsCardInCompactView] = useRecoilState(
isCardInCompactViewState(boardCardId ?? ''),
isRecordBoardCardInCompactViewFamilyState(boardCardId ?? ''),
);
const showCompactView = isCompactViewEnabled && isCardInCompactView;
const { opportunity, company } = companyProgress ?? {};
const visibleBoardCardFields = useRecoilScopedValue(
visibleBoardCardFieldsScopedSelector,
BoardRecoilScopeContext,
);
const visibleBoardCardFields = useRecoilValue(visibleBoardCardFieldsSelector);
const useUpdateOneRecordMutation: () => [(params: any) => any, any] = () => {
const { updateOneRecord: updateOneOpportunity } = useUpdateOneRecord({

View File

@ -1,64 +1,36 @@
import { useCallback, useEffect, useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useEffect } from 'react';
import { useRecoilValue } from 'recoil';
import { Company } from '@/companies/types/Company';
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { PaginatedRecordTypeResults } from '@/object-record/types/PaginatedRecordTypeResults';
import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns';
import { Opportunity } from '@/pipeline/types/Opportunity';
import { PipelineStep } from '@/pipeline/types/PipelineStep';
import { turnFiltersIntoWhereClause } from '@/ui/object/object-filter-dropdown/utils/turnFiltersIntoWhereClause';
import { turnSortsIntoOrderBy } from '@/ui/object/object-sort-dropdown/utils/turnSortsIntoOrderBy';
import { useBoardActionBarEntries } from '@/ui/object/record-board/hooks/useBoardActionBarEntries';
import { useBoardContext } from '@/ui/object/record-board/hooks/useBoardContext';
import { useBoardContextMenuEntries } from '@/ui/object/record-board/hooks/useBoardContextMenuEntries';
import { availableBoardCardFieldsScopedState } from '@/ui/object/record-board/states/availableBoardCardFieldsScopedState';
import { boardCardFieldsScopedState } from '@/ui/object/record-board/states/boardCardFieldsScopedState';
import { isBoardLoadedState } from '@/ui/object/record-board/states/isBoardLoadedState';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
import { availableRecordBoardCardFieldsScopedState } from '@/ui/object/record-board/states/availableRecordBoardCardFieldsScopedState';
import { recordBoardCardFieldsScopedState } from '@/ui/object/record-board/states/recordBoardCardFieldsScopedState';
import { recordBoardFiltersScopedState } from '@/ui/object/record-board/states/recordBoardFiltersScopedState';
import { recordBoardSortsScopedState } from '@/ui/object/record-board/states/recordBoardSortsScopedState';
import { useSetRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useSetRecoilScopedStateV2';
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
import { useViewBar } from '@/views/hooks/useViewBar';
import { ViewType } from '@/views/types/ViewType';
import { mapViewFieldsToBoardFieldDefinitions } from '@/views/utils/mapViewFieldsToBoardFieldDefinitions';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
import { isDefined } from '~/utils/isDefined';
import { useUpdateCompanyBoardCardIds } from '../hooks/useUpdateBoardCardIds';
import { useUpdateCompanyBoard } from '../hooks/useUpdateCompanyBoardColumns';
type HooksCompanyBoardEffectProps = {
viewBarId: string;
recordBoardId: string;
};
export const HooksCompanyBoardEffect = ({
viewBarId,
recordBoardId,
}: HooksCompanyBoardEffectProps) => {
const {
setAvailableFilterDefinitions,
setAvailableSortDefinitions,
setAvailableFieldDefinitions,
setEntityCountInCurrentView,
setViewObjectMetadataId,
setViewType,
} = useViewBar({ viewBarId: viewBarId });
const {
currentViewFieldsState,
currentViewFiltersState,
currentViewSortsState,
} = useViewScopedStates({ viewScopeId: viewBarId });
const [pipelineSteps, setPipelineSteps] = useState<PipelineStep[]>([]);
const [opportunities, setOpportunities] = useState<Opportunity[]>([]);
const [companies, setCompanies] = useState<Company[]>([]);
const currentViewFields = useRecoilValue(currentViewFieldsState);
const currentViewFilters = useRecoilValue(currentViewFiltersState);
const currentViewSorts = useRecoilValue(currentViewSortsState);
} = useViewBar({ viewBarId });
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular: 'opportunity',
@ -67,88 +39,11 @@ export const HooksCompanyBoardEffect = ({
const { columnDefinitions, filterDefinitions, sortDefinitions } =
useColumnDefinitionsFromFieldMetadata(objectMetadataItem);
const [, setIsBoardLoaded] = useRecoilState(isBoardLoadedState);
const { BoardRecoilScopeContext } = useBoardContext();
const [, setBoardCardFields] = useRecoilScopedState(
boardCardFieldsScopedState,
BoardRecoilScopeContext,
);
const updateCompanyBoardCardIds = useUpdateCompanyBoardCardIds();
const updateCompanyBoard = useUpdateCompanyBoard();
const setAvailableBoardCardFields = useSetRecoilScopedStateV2(
availableBoardCardFieldsScopedState,
availableRecordBoardCardFieldsScopedState,
'company-board-view',
);
useFindManyRecords({
objectNameSingular: 'pipelineStep',
filter: {},
onCompleted: useCallback(
(data: PaginatedRecordTypeResults<PipelineStep>) => {
setPipelineSteps(data.edges.map((edge) => edge.node));
},
[],
),
});
const filter = turnFiltersIntoWhereClause(
mapViewFiltersToFilters(currentViewFilters),
objectMetadataItem?.fields ?? [],
);
const orderBy = turnSortsIntoOrderBy(
mapViewSortsToSorts(currentViewSorts),
objectMetadataItem?.fields ?? [],
);
const { fetchMoreRecords: fetchMoreOpportunities } = useFindManyRecords({
skip: !pipelineSteps.length,
objectNameSingular: 'opportunity',
filter: filter,
orderBy: orderBy,
onCompleted: useCallback(
(data: PaginatedRecordTypeResults<Opportunity>) => {
const pipelineProgresses: Array<Opportunity> = data.edges.map(
(edge) => edge.node,
);
updateCompanyBoardCardIds(pipelineProgresses);
setOpportunities(pipelineProgresses);
setIsBoardLoaded(true);
},
[setIsBoardLoaded, updateCompanyBoardCardIds],
),
});
useEffect(() => {
if (isDefined(fetchMoreOpportunities)) {
fetchMoreOpportunities();
}
}, [fetchMoreOpportunities]);
const { fetchMoreRecords: fetchMoreCompanies } = useFindManyRecords({
skip: !opportunities.length,
objectNameSingular: 'company',
filter: {
id: {
in: opportunities.map((opportunity) => opportunity.companyId || ''),
},
},
onCompleted: useCallback((data: PaginatedRecordTypeResults<Company>) => {
setCompanies(data.edges.map((edge) => edge.node));
}, []),
});
useEffect(() => {
if (isDefined(fetchMoreCompanies)) {
fetchMoreCompanies();
}
}, [fetchMoreCompanies]);
useEffect(() => {
if (!objectMetadataItem) {
return;
@ -182,26 +77,30 @@ export const HooksCompanyBoardEffect = ({
setViewType?.(ViewType.Kanban);
}, [objectMetadataItem, setViewObjectMetadataId, setViewType]);
const { setActionBarEntries } = useBoardActionBarEntries();
const { setContextMenuEntries } = useBoardContextMenuEntries();
const {
currentViewFieldsState,
currentViewFiltersState,
currentViewSortsState,
} = useViewScopedStates({ viewScopeId: viewBarId });
useEffect(() => {
if (opportunities && companies) {
setActionBarEntries();
setContextMenuEntries();
const currentViewFields = useRecoilValue(currentViewFieldsState);
const currentViewFilters = useRecoilValue(currentViewFiltersState);
const currentViewSorts = useRecoilValue(currentViewSortsState);
updateCompanyBoard(pipelineSteps, opportunities, companies);
setEntityCountInCurrentView(opportunities.length);
}
}, [
companies,
opportunities,
pipelineSteps,
setActionBarEntries,
setContextMenuEntries,
setEntityCountInCurrentView,
updateCompanyBoard,
]);
//TODO: Modify to use scopeId
const setBoardCardFields = useSetRecoilScopedStateV2(
recordBoardCardFieldsScopedState,
'company-board',
);
const setBoardCardFilters = useSetRecoilScopedStateV2(
recordBoardFiltersScopedState,
'company-board',
);
const setBoardCardSorts = useSetRecoilScopedStateV2(
recordBoardSortsScopedState,
'company-board',
);
useEffect(() => {
if (currentViewFields) {
@ -214,5 +113,29 @@ export const HooksCompanyBoardEffect = ({
}
}, [columnDefinitions, currentViewFields, setBoardCardFields]);
useEffect(() => {
if (currentViewFilters) {
setBoardCardFilters(currentViewFilters);
}
}, [currentViewFilters, setBoardCardFilters]);
useEffect(() => {
if (currentViewSorts) {
setBoardCardSorts(currentViewSorts);
}
}, [currentViewSorts, setBoardCardSorts]);
const { setEntityCountInCurrentView } = useViewBar({ viewBarId });
const { savedOpportunitiesState } = useRecordBoardScopedStates({
recordBoardScopeId: recordBoardId,
});
const savedOpportunities = useRecoilValue(savedOpportunitiesState);
useEffect(() => {
setEntityCountInCurrentView(savedOpportunities.length);
}, [savedOpportunities.length, setEntityCountInCurrentView]);
return <></>;
};

View File

@ -1,7 +1,6 @@
import { useCallback, useContext, useState } from 'react';
import { useQuery } from '@apollo/client';
import { useCreateOpportunity } from '@/companies/hooks/useCreateOpportunity';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
@ -11,6 +10,7 @@ import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picke
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
import { NewButton } from '@/ui/object/record-board/components/NewButton';
import { BoardColumnContext } from '@/ui/object/record-board/contexts/BoardColumnContext';
import { useCreateOpportunity } from '@/ui/object/record-board/hooks/internal/useCreateOpportunity';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
@ -21,7 +21,7 @@ export const NewOpportunityButton = () => {
const pipelineStepId = column?.columnDefinition.id || '';
const { enqueueSnackBar } = useSnackBar();
const { createOpportunity } = useCreateOpportunity();
const createOpportunity = useCreateOpportunity();
const {
goBackToPreviousHotkeyScope,

View File

@ -1,34 +0,0 @@
import { useRecoilCallback } from 'recoil';
import { v4 } from 'uuid';
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
import { Opportunity } from '@/pipeline/types/Opportunity';
import { boardCardIdsByColumnIdFamilyState } from '@/ui/object/record-board/states/boardCardIdsByColumnIdFamilyState';
export const useCreateOpportunity = () => {
const { createOneRecord: createOneOpportunity } =
useCreateOneRecord<Opportunity>({
objectNameSingular: 'opportunity',
});
const createOpportunity = useRecoilCallback(
({ set }) =>
async (companyId: string, pipelineStepId: string) => {
const newUuid = v4();
set(boardCardIdsByColumnIdFamilyState(pipelineStepId), (oldValue) => [
...oldValue,
newUuid,
]);
await createOneOpportunity?.({
id: newUuid,
pipelineStepId,
companyId: companyId,
});
},
[createOneOpportunity],
);
return { createOpportunity };
};

View File

@ -1,27 +0,0 @@
import { useRecoilCallback } from 'recoil';
import { Opportunity } from '@/pipeline/types/Opportunity';
import { boardCardIdsByColumnIdFamilyState } from '@/ui/object/record-board/states/boardCardIdsByColumnIdFamilyState';
import { boardColumnsState } from '@/ui/object/record-board/states/boardColumnsState';
export const useUpdateCompanyBoardCardIds = () =>
useRecoilCallback(
({ snapshot, set }) =>
(pipelineProgresses: Pick<Opportunity, 'pipelineStepId' | 'id'>[]) => {
const boardColumns = snapshot
.getLoadable(boardColumnsState)
.valueOrThrow();
for (const boardColumn of boardColumns) {
const boardCardIds = pipelineProgresses
.filter(
(pipelineProgressToFilter) =>
pipelineProgressToFilter.pipelineStepId === boardColumn.id,
)
.map((pipelineProgress) => pipelineProgress.id);
set(boardCardIdsByColumnIdFamilyState(boardColumn.id), boardCardIds);
}
},
[],
);

View File

@ -1,140 +0,0 @@
import { useRecoilCallback } from 'recoil';
import { currentPipelineStepsState } from '@/pipeline/states/currentPipelineStepsState';
import { Opportunity } from '@/pipeline/types/Opportunity';
import { PipelineStep } from '@/pipeline/types/PipelineStep';
import { entityFieldsFamilyState } from '@/ui/object/field/states/entityFieldsFamilyState';
import { boardCardIdsByColumnIdFamilyState } from '@/ui/object/record-board/states/boardCardIdsByColumnIdFamilyState';
import { boardColumnsState } from '@/ui/object/record-board/states/boardColumnsState';
import { savedBoardColumnsState } from '@/ui/object/record-board/states/savedBoardColumnsState';
import { BoardColumnDefinition } from '@/ui/object/record-board/types/BoardColumnDefinition';
import { themeColorSchema } from '@/ui/theme/utils/themeColorSchema';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { logError } from '~/utils/logError';
import { companyProgressesFamilyState } from '../states/companyProgressesFamilyState';
import { CompanyForBoard, CompanyProgressDict } from '../types/CompanyProgress';
export const useUpdateCompanyBoard = () =>
useRecoilCallback(
({ set, snapshot }) =>
(
pipelineSteps: PipelineStep[],
opportunities: Opportunity[],
companies: CompanyForBoard[],
) => {
const indexCompanyByIdReducer = (
acc: { [key: string]: CompanyForBoard },
company: CompanyForBoard,
) => ({
...acc,
[company.id]: company,
});
const companiesDict =
companies.reduce(
indexCompanyByIdReducer,
{} as { [key: string]: CompanyForBoard },
) ?? {};
const indexOpportunityByIdReducer = (
acc: CompanyProgressDict,
opportunity: Opportunity,
) => {
const company =
opportunity.companyId && companiesDict[opportunity.companyId];
if (!company) return acc;
return {
...acc,
[opportunity.id]: {
opportunity,
company,
},
};
};
const companyBoardIndex = opportunities.reduce(
indexOpportunityByIdReducer,
{} as CompanyProgressDict,
);
for (const [id, companyProgress] of Object.entries(companyBoardIndex)) {
const currentCompanyProgress = snapshot
.getLoadable(companyProgressesFamilyState(id))
.valueOrThrow();
if (!isDeeplyEqual(currentCompanyProgress, companyProgress)) {
set(companyProgressesFamilyState(id), companyProgress);
set(entityFieldsFamilyState(id), companyProgress.opportunity);
}
}
const currentPipelineSteps = snapshot
.getLoadable(currentPipelineStepsState)
.valueOrThrow();
const currentBoardColumns = snapshot
.getLoadable(boardColumnsState)
.valueOrThrow();
if (!isDeeplyEqual(pipelineSteps, currentPipelineSteps)) {
set(currentPipelineStepsState, pipelineSteps);
}
const orderedPipelineSteps = [...pipelineSteps].sort((a, b) => {
if (!a.position || !b.position) return 0;
return a.position - b.position;
});
const newBoardColumns: BoardColumnDefinition[] =
orderedPipelineSteps?.map((pipelineStep) => {
const colorValidationResult = themeColorSchema.safeParse(
pipelineStep.color,
);
if (!colorValidationResult.success) {
logError(
`Color ${pipelineStep.color} is not recognized in useUpdateCompanyBoard.`,
);
}
return {
id: pipelineStep.id,
title: pipelineStep.name,
colorCode: colorValidationResult.success
? colorValidationResult.data
: undefined,
position: pipelineStep.position ?? 0,
};
});
if (
currentBoardColumns.length === 0 &&
!isDeeplyEqual(newBoardColumns, currentBoardColumns)
) {
set(boardColumnsState, newBoardColumns);
set(savedBoardColumnsState, newBoardColumns);
}
for (const boardColumn of newBoardColumns) {
const boardCardIds = opportunities
.filter(
(opportunityToFilter) =>
opportunityToFilter.pipelineStepId === boardColumn.id,
)
.map((opportunity) => opportunity.id);
const currentBoardCardIds = snapshot
.getLoadable(boardCardIdsByColumnIdFamilyState(boardColumn.id))
.valueOrThrow();
if (!isDeeplyEqual(currentBoardCardIds, boardCardIds)) {
set(
boardCardIdsByColumnIdFamilyState(boardColumn.id),
boardCardIds,
);
}
}
},
[],
);

View File

@ -1,13 +0,0 @@
import { createContext } from 'react';
import { RecoilScopeContext } from '@/types/RecoilScopeContext';
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
import { BoardFieldDefinition } from '@/ui/object/record-board/types/BoardFieldDefinition';
export const BoardContext = createContext<{
BoardRecoilScopeContext: RecoilScopeContext;
onFieldsChange: (fields: BoardFieldDefinition<FieldMetadata>[]) => void;
}>({
BoardRecoilScopeContext: createContext<string | null>(null),
onFieldsChange: () => {},
});

View File

@ -1,5 +0,0 @@
import { createContext } from 'react';
export const CompanyBoardRecoilScopeContext = createContext<string | null>(
null,
);