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:
@ -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,
|
||||
],
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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 <></>;
|
||||
};
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 };
|
||||
};
|
||||
@ -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);
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
@ -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: () => {},
|
||||
});
|
||||
@ -1,5 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const CompanyBoardRecoilScopeContext = createContext<string | null>(
|
||||
null,
|
||||
);
|
||||
Reference in New Issue
Block a user