mirror of
https://github.com/twentyhq/twenty.git
synced 2025-01-08 09:47:07 +03:00
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:
parent
5c0ad30186
commit
95a1cfeec3
@ -5,17 +5,12 @@ import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
|||||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||||
|
|
||||||
import { CompanyBoard } from '../board/components/CompanyBoard';
|
import { CompanyBoard } from '../board/components/CompanyBoard';
|
||||||
import { CompanyBoardRecoilScopeContext } from '../states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
|
|
||||||
|
|
||||||
const meta: Meta<typeof CompanyBoard> = {
|
const meta: Meta<typeof CompanyBoard> = {
|
||||||
title: 'Modules/Companies/Board',
|
title: 'Modules/Companies/Board',
|
||||||
component: CompanyBoard,
|
component: CompanyBoard,
|
||||||
decorators: [
|
decorators: [
|
||||||
(Story) => (
|
(Story) => <Story />,
|
||||||
<CompanyBoardRecoilScopeContext.Provider value="opportunities">
|
|
||||||
<Story />
|
|
||||||
</CompanyBoardRecoilScopeContext.Provider>
|
|
||||||
),
|
|
||||||
ComponentWithRouterDecorator,
|
ComponentWithRouterDecorator,
|
||||||
SnackBarDecorator,
|
SnackBarDecorator,
|
||||||
],
|
],
|
||||||
|
@ -1,21 +1,17 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { BoardContext } from '@/companies/states/contexts/BoardContext';
|
|
||||||
import { mapBoardFieldDefinitionsToViewFields } from '@/companies/utils/mapBoardFieldDefinitionsToViewFields';
|
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 {
|
import {
|
||||||
RecordBoard,
|
RecordBoard,
|
||||||
RecordBoardProps,
|
RecordBoardProps,
|
||||||
} from '@/ui/object/record-board/components/RecordBoard';
|
} from '@/ui/object/record-board/components/RecordBoard';
|
||||||
import { RecordBoardContextMenu } from '@/ui/object/record-board/context-menu/components/RecordBoardContextMenu';
|
import { RecordBoardEffect } from '@/ui/object/record-board/components/RecordBoardEffect';
|
||||||
import { BoardOptionsDropdown } from '@/ui/object/record-board/options/components/BoardOptionsDropdown';
|
import { RecordBoardOptionsDropdown } from '@/ui/object/record-board/options/components/RecordBoardOptionsDropdown';
|
||||||
import { ViewBar } from '@/views/components/ViewBar';
|
import { ViewBar } from '@/views/components/ViewBar';
|
||||||
import { useViewFields } from '@/views/hooks/internal/useViewFields';
|
import { useViewFields } from '@/views/hooks/internal/useViewFields';
|
||||||
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
|
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
|
||||||
|
|
||||||
import { HooksCompanyBoardEffect } from '../../components/HooksCompanyBoardEffect';
|
import { HooksCompanyBoardEffect } from '../../components/HooksCompanyBoardEffect';
|
||||||
import { CompanyBoardRecoilScopeContext } from '../../states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
|
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -36,34 +32,38 @@ export const CompanyBoard = ({
|
|||||||
onEditColumnTitle,
|
onEditColumnTitle,
|
||||||
}: CompanyBoardProps) => {
|
}: CompanyBoardProps) => {
|
||||||
const viewBarId = 'company-board-view';
|
const viewBarId = 'company-board-view';
|
||||||
|
const recordBoardId = 'company-board';
|
||||||
|
|
||||||
const { persistViewFields } = useViewFields(viewBarId);
|
const { persistViewFields } = useViewFields(viewBarId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<BoardContext.Provider
|
<ViewBar
|
||||||
value={{
|
viewBarId={viewBarId}
|
||||||
BoardRecoilScopeContext: CompanyBoardRecoilScopeContext,
|
optionsDropdownButton={
|
||||||
onFieldsChange: (fields) => {
|
<RecordBoardOptionsDropdown recordBoardId={recordBoardId} />
|
||||||
persistViewFields(mapBoardFieldDefinitionsToViewFields(fields));
|
}
|
||||||
},
|
optionsDropdownScopeId={recordBoardId}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<HooksCompanyBoardEffect
|
||||||
|
viewBarId={viewBarId}
|
||||||
|
recordBoardId={recordBoardId}
|
||||||
|
/>
|
||||||
|
<RecordBoardEffect
|
||||||
|
recordBoardId={recordBoardId}
|
||||||
|
onFieldsChange={(fields) => {
|
||||||
|
persistViewFields(mapBoardFieldDefinitionsToViewFields(fields));
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
<ViewBar
|
|
||||||
viewBarId={viewBarId}
|
<RecordBoard
|
||||||
optionsDropdownButton={<BoardOptionsDropdown />}
|
recordBoardId={recordBoardId}
|
||||||
optionsDropdownScopeId={BoardOptionsDropdownId}
|
boardOptions={opportunitiesBoardOptions}
|
||||||
/>
|
onColumnAdd={onColumnAdd}
|
||||||
<HooksCompanyBoardEffect viewBarId={viewBarId} />
|
onColumnDelete={onColumnDelete}
|
||||||
<RecordBoard
|
onEditColumnTitle={onEditColumnTitle}
|
||||||
boardOptions={opportunitiesBoardOptions}
|
/>
|
||||||
onColumnAdd={onColumnAdd}
|
|
||||||
onColumnDelete={onColumnDelete}
|
|
||||||
onEditColumnTitle={onEditColumnTitle}
|
|
||||||
/>
|
|
||||||
<RecordBoardActionBar />
|
|
||||||
<RecordBoardContextMenu />
|
|
||||||
</BoardContext.Provider>
|
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ReactNode, useContext } from 'react';
|
import { ReactNode, useContext } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||||
import { EntityChipVariant } from '@/ui/display/chip/components/EntityChip';
|
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 { Checkbox, CheckboxVariant } from '@/ui/input/components/Checkbox';
|
||||||
import { FieldContext } from '@/ui/object/field/contexts/FieldContext';
|
import { FieldContext } from '@/ui/object/field/contexts/FieldContext';
|
||||||
import { BoardCardIdContext } from '@/ui/object/record-board/contexts/BoardCardIdContext';
|
import { BoardCardIdContext } from '@/ui/object/record-board/contexts/BoardCardIdContext';
|
||||||
import { useBoardContext } from '@/ui/object/record-board/hooks/useBoardContext';
|
import { useCurrentRecordBoardCardSelectedInternal } from '@/ui/object/record-board/hooks/internal/useCurrentRecordBoardCardSelectedInternal';
|
||||||
import { useCurrentCardSelected } from '@/ui/object/record-board/hooks/useCurrentCardSelected';
|
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
import { isCardInCompactViewState } from '@/ui/object/record-board/states/isCardInCompactViewState';
|
import { isRecordBoardCardInCompactViewFamilyState } from '@/ui/object/record-board/states/isRecordBoardCardInCompactViewFamilyState';
|
||||||
import { isCompactViewEnabledState } from '@/ui/object/record-board/states/isCompactViewEnabledState';
|
|
||||||
import { visibleBoardCardFieldsScopedSelector } from '@/ui/object/record-board/states/selectors/visibleBoardCardFieldsScopedSelector';
|
|
||||||
import { RecordInlineCell } from '@/ui/object/record-inline-cell/components/RecordInlineCell';
|
import { RecordInlineCell } from '@/ui/object/record-inline-cell/components/RecordInlineCell';
|
||||||
import { InlineCellHotkeyScope } from '@/ui/object/record-inline-cell/types/InlineCellHotkeyScope';
|
import { InlineCellHotkeyScope } from '@/ui/object/record-inline-cell/types/InlineCellHotkeyScope';
|
||||||
import { AnimatedEaseInOut } from '@/ui/utilities/animation/components/AnimatedEaseInOut';
|
import { AnimatedEaseInOut } from '@/ui/utilities/animation/components/AnimatedEaseInOut';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
|
||||||
import { getLogoUrlFromDomainName } from '~/utils';
|
import { getLogoUrlFromDomainName } from '~/utils';
|
||||||
|
|
||||||
import { companyProgressesFamilyState } from '../states/companyProgressesFamilyState';
|
import { companyProgressesFamilyState } from '../states/companyProgressesFamilyState';
|
||||||
@ -125,30 +122,28 @@ const StyledCompactIconContainer = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const CompanyBoardCard = () => {
|
export const CompanyBoardCard = () => {
|
||||||
const { BoardRecoilScopeContext } = useBoardContext();
|
|
||||||
|
|
||||||
const { isCurrentCardSelected, setCurrentCardSelected } =
|
const { isCurrentCardSelected, setCurrentCardSelected } =
|
||||||
useCurrentCardSelected();
|
useCurrentRecordBoardCardSelectedInternal();
|
||||||
const boardCardId = useContext(BoardCardIdContext);
|
const boardCardId = useContext(BoardCardIdContext);
|
||||||
|
|
||||||
const [companyProgress] = useRecoilState(
|
const [companyProgress] = useRecoilState(
|
||||||
companyProgressesFamilyState(boardCardId ?? ''),
|
companyProgressesFamilyState(boardCardId ?? ''),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { isCompactViewEnabledState, visibleBoardCardFieldsSelector } =
|
||||||
|
useRecordBoardScopedStates();
|
||||||
|
|
||||||
const [isCompactViewEnabled] = useRecoilState(isCompactViewEnabledState);
|
const [isCompactViewEnabled] = useRecoilState(isCompactViewEnabledState);
|
||||||
|
|
||||||
const [isCardInCompactView, setIsCardInCompactView] = useRecoilState(
|
const [isCardInCompactView, setIsCardInCompactView] = useRecoilState(
|
||||||
isCardInCompactViewState(boardCardId ?? ''),
|
isRecordBoardCardInCompactViewFamilyState(boardCardId ?? ''),
|
||||||
);
|
);
|
||||||
|
|
||||||
const showCompactView = isCompactViewEnabled && isCardInCompactView;
|
const showCompactView = isCompactViewEnabled && isCardInCompactView;
|
||||||
|
|
||||||
const { opportunity, company } = companyProgress ?? {};
|
const { opportunity, company } = companyProgress ?? {};
|
||||||
|
|
||||||
const visibleBoardCardFields = useRecoilScopedValue(
|
const visibleBoardCardFields = useRecoilValue(visibleBoardCardFieldsSelector);
|
||||||
visibleBoardCardFieldsScopedSelector,
|
|
||||||
BoardRecoilScopeContext,
|
|
||||||
);
|
|
||||||
|
|
||||||
const useUpdateOneRecordMutation: () => [(params: any) => any, any] = () => {
|
const useUpdateOneRecordMutation: () => [(params: any) => any, any] = () => {
|
||||||
const { updateOneRecord: updateOneOpportunity } = useUpdateOneRecord({
|
const { updateOneRecord: updateOneOpportunity } = useUpdateOneRecord({
|
||||||
|
@ -1,64 +1,36 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { Company } from '@/companies/types/Company';
|
|
||||||
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
|
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
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 { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns';
|
||||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
import { availableRecordBoardCardFieldsScopedState } from '@/ui/object/record-board/states/availableRecordBoardCardFieldsScopedState';
|
||||||
import { turnFiltersIntoWhereClause } from '@/ui/object/object-filter-dropdown/utils/turnFiltersIntoWhereClause';
|
import { recordBoardCardFieldsScopedState } from '@/ui/object/record-board/states/recordBoardCardFieldsScopedState';
|
||||||
import { turnSortsIntoOrderBy } from '@/ui/object/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
import { recordBoardFiltersScopedState } from '@/ui/object/record-board/states/recordBoardFiltersScopedState';
|
||||||
import { useBoardActionBarEntries } from '@/ui/object/record-board/hooks/useBoardActionBarEntries';
|
import { recordBoardSortsScopedState } from '@/ui/object/record-board/states/recordBoardSortsScopedState';
|
||||||
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 { useSetRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useSetRecoilScopedStateV2';
|
import { useSetRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useSetRecoilScopedStateV2';
|
||||||
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
|
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
|
||||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||||
import { ViewType } from '@/views/types/ViewType';
|
import { ViewType } from '@/views/types/ViewType';
|
||||||
import { mapViewFieldsToBoardFieldDefinitions } from '@/views/utils/mapViewFieldsToBoardFieldDefinitions';
|
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 = {
|
type HooksCompanyBoardEffectProps = {
|
||||||
viewBarId: string;
|
viewBarId: string;
|
||||||
|
recordBoardId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HooksCompanyBoardEffect = ({
|
export const HooksCompanyBoardEffect = ({
|
||||||
viewBarId,
|
viewBarId,
|
||||||
|
recordBoardId,
|
||||||
}: HooksCompanyBoardEffectProps) => {
|
}: HooksCompanyBoardEffectProps) => {
|
||||||
const {
|
const {
|
||||||
setAvailableFilterDefinitions,
|
setAvailableFilterDefinitions,
|
||||||
setAvailableSortDefinitions,
|
setAvailableSortDefinitions,
|
||||||
setAvailableFieldDefinitions,
|
setAvailableFieldDefinitions,
|
||||||
setEntityCountInCurrentView,
|
|
||||||
setViewObjectMetadataId,
|
setViewObjectMetadataId,
|
||||||
setViewType,
|
setViewType,
|
||||||
} = useViewBar({ viewBarId: viewBarId });
|
} = useViewBar({ 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);
|
|
||||||
|
|
||||||
const { objectMetadataItem } = useObjectMetadataItem({
|
const { objectMetadataItem } = useObjectMetadataItem({
|
||||||
objectNameSingular: 'opportunity',
|
objectNameSingular: 'opportunity',
|
||||||
@ -67,88 +39,11 @@ export const HooksCompanyBoardEffect = ({
|
|||||||
const { columnDefinitions, filterDefinitions, sortDefinitions } =
|
const { columnDefinitions, filterDefinitions, sortDefinitions } =
|
||||||
useColumnDefinitionsFromFieldMetadata(objectMetadataItem);
|
useColumnDefinitionsFromFieldMetadata(objectMetadataItem);
|
||||||
|
|
||||||
const [, setIsBoardLoaded] = useRecoilState(isBoardLoadedState);
|
|
||||||
|
|
||||||
const { BoardRecoilScopeContext } = useBoardContext();
|
|
||||||
|
|
||||||
const [, setBoardCardFields] = useRecoilScopedState(
|
|
||||||
boardCardFieldsScopedState,
|
|
||||||
BoardRecoilScopeContext,
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateCompanyBoardCardIds = useUpdateCompanyBoardCardIds();
|
|
||||||
const updateCompanyBoard = useUpdateCompanyBoard();
|
|
||||||
|
|
||||||
const setAvailableBoardCardFields = useSetRecoilScopedStateV2(
|
const setAvailableBoardCardFields = useSetRecoilScopedStateV2(
|
||||||
availableBoardCardFieldsScopedState,
|
availableRecordBoardCardFieldsScopedState,
|
||||||
'company-board-view',
|
'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(() => {
|
useEffect(() => {
|
||||||
if (!objectMetadataItem) {
|
if (!objectMetadataItem) {
|
||||||
return;
|
return;
|
||||||
@ -182,26 +77,30 @@ export const HooksCompanyBoardEffect = ({
|
|||||||
setViewType?.(ViewType.Kanban);
|
setViewType?.(ViewType.Kanban);
|
||||||
}, [objectMetadataItem, setViewObjectMetadataId, setViewType]);
|
}, [objectMetadataItem, setViewObjectMetadataId, setViewType]);
|
||||||
|
|
||||||
const { setActionBarEntries } = useBoardActionBarEntries();
|
const {
|
||||||
const { setContextMenuEntries } = useBoardContextMenuEntries();
|
currentViewFieldsState,
|
||||||
|
currentViewFiltersState,
|
||||||
|
currentViewSortsState,
|
||||||
|
} = useViewScopedStates({ viewScopeId: viewBarId });
|
||||||
|
|
||||||
useEffect(() => {
|
const currentViewFields = useRecoilValue(currentViewFieldsState);
|
||||||
if (opportunities && companies) {
|
const currentViewFilters = useRecoilValue(currentViewFiltersState);
|
||||||
setActionBarEntries();
|
const currentViewSorts = useRecoilValue(currentViewSortsState);
|
||||||
setContextMenuEntries();
|
|
||||||
|
|
||||||
updateCompanyBoard(pipelineSteps, opportunities, companies);
|
//TODO: Modify to use scopeId
|
||||||
setEntityCountInCurrentView(opportunities.length);
|
const setBoardCardFields = useSetRecoilScopedStateV2(
|
||||||
}
|
recordBoardCardFieldsScopedState,
|
||||||
}, [
|
'company-board',
|
||||||
companies,
|
);
|
||||||
opportunities,
|
const setBoardCardFilters = useSetRecoilScopedStateV2(
|
||||||
pipelineSteps,
|
recordBoardFiltersScopedState,
|
||||||
setActionBarEntries,
|
'company-board',
|
||||||
setContextMenuEntries,
|
);
|
||||||
setEntityCountInCurrentView,
|
|
||||||
updateCompanyBoard,
|
const setBoardCardSorts = useSetRecoilScopedStateV2(
|
||||||
]);
|
recordBoardSortsScopedState,
|
||||||
|
'company-board',
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentViewFields) {
|
if (currentViewFields) {
|
||||||
@ -214,5 +113,29 @@ export const HooksCompanyBoardEffect = ({
|
|||||||
}
|
}
|
||||||
}, [columnDefinitions, currentViewFields, setBoardCardFields]);
|
}, [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 <></>;
|
return <></>;
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { useCallback, useContext, useState } from 'react';
|
import { useCallback, useContext, useState } from 'react';
|
||||||
import { useQuery } from '@apollo/client';
|
import { useQuery } from '@apollo/client';
|
||||||
|
|
||||||
import { useCreateOpportunity } from '@/companies/hooks/useCreateOpportunity';
|
|
||||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
|
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
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 { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
||||||
import { NewButton } from '@/ui/object/record-board/components/NewButton';
|
import { NewButton } from '@/ui/object/record-board/components/NewButton';
|
||||||
import { BoardColumnContext } from '@/ui/object/record-board/contexts/BoardColumnContext';
|
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 { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ export const NewOpportunityButton = () => {
|
|||||||
const pipelineStepId = column?.columnDefinition.id || '';
|
const pipelineStepId = column?.columnDefinition.id || '';
|
||||||
|
|
||||||
const { enqueueSnackBar } = useSnackBar();
|
const { enqueueSnackBar } = useSnackBar();
|
||||||
const { createOpportunity } = useCreateOpportunity();
|
const createOpportunity = useCreateOpportunity();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
goBackToPreviousHotkeyScope,
|
goBackToPreviousHotkeyScope,
|
||||||
|
@ -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,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,
|
|
||||||
);
|
|
118
front/src/modules/object-record/hooks/useObjectRecordBoard.ts
Normal file
118
front/src/modules/object-record/hooks/useObjectRecordBoard.ts
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { Company } from '@/companies/types/Company';
|
||||||
|
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||||
|
import { PaginatedRecordTypeResults } from '@/object-record/types/PaginatedRecordTypeResults';
|
||||||
|
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 { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
|
import { useUpdateCompanyBoardCardIdsInternal } from '@/ui/object/record-board/hooks/internal/useUpdateCompanyBoardCardIdsInternal';
|
||||||
|
|
||||||
|
import { useFindManyRecords } from './useFindManyRecords';
|
||||||
|
|
||||||
|
export const useObjectRecordBoard = () => {
|
||||||
|
const objectNameSingular = 'opportunity';
|
||||||
|
const updateCompanyBoardCardIds = useUpdateCompanyBoardCardIdsInternal();
|
||||||
|
|
||||||
|
const { objectMetadataItem: foundObjectMetadataItem } = useObjectMetadataItem(
|
||||||
|
{
|
||||||
|
objectNameSingular,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
isBoardLoadedState,
|
||||||
|
boardFiltersState,
|
||||||
|
boardSortsState,
|
||||||
|
savedCompaniesState,
|
||||||
|
savedOpportunitiesState,
|
||||||
|
savedPipelineStepsState,
|
||||||
|
} = useRecordBoardScopedStates();
|
||||||
|
|
||||||
|
const setIsBoardLoaded = useSetRecoilState(isBoardLoadedState);
|
||||||
|
|
||||||
|
const boardFilters = useRecoilValue(boardFiltersState);
|
||||||
|
const boardSorts = useRecoilValue(boardSortsState);
|
||||||
|
|
||||||
|
const setSavedCompanies = useSetRecoilState(savedCompaniesState);
|
||||||
|
|
||||||
|
const [savedOpportunities, setSavedOpportunities] = useRecoilState(
|
||||||
|
savedOpportunitiesState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [savedPipelineSteps, setSavedPipelineSteps] = useRecoilState(
|
||||||
|
savedPipelineStepsState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const filter = turnFiltersIntoWhereClause(
|
||||||
|
boardFilters,
|
||||||
|
foundObjectMetadataItem?.fields ?? [],
|
||||||
|
);
|
||||||
|
const orderBy = turnSortsIntoOrderBy(
|
||||||
|
boardSorts,
|
||||||
|
foundObjectMetadataItem?.fields ?? [],
|
||||||
|
);
|
||||||
|
|
||||||
|
useFindManyRecords({
|
||||||
|
objectNameSingular: 'pipelineStep',
|
||||||
|
filter: {},
|
||||||
|
onCompleted: useCallback(
|
||||||
|
(data: PaginatedRecordTypeResults<PipelineStep>) => {
|
||||||
|
setSavedPipelineSteps(data.edges.map((edge) => edge.node));
|
||||||
|
},
|
||||||
|
[setSavedPipelineSteps],
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
records: opportunities,
|
||||||
|
loading,
|
||||||
|
fetchMoreRecords: fetchMoreOpportunities,
|
||||||
|
} = useFindManyRecords({
|
||||||
|
skip: !savedPipelineSteps.length,
|
||||||
|
objectNameSingular: 'opportunity',
|
||||||
|
filter: filter,
|
||||||
|
orderBy: orderBy,
|
||||||
|
onCompleted: useCallback(
|
||||||
|
(data: PaginatedRecordTypeResults<Opportunity>) => {
|
||||||
|
const pipelineProgresses: Array<Opportunity> = data.edges.map(
|
||||||
|
(edge) => edge.node,
|
||||||
|
);
|
||||||
|
|
||||||
|
updateCompanyBoardCardIds(pipelineProgresses);
|
||||||
|
|
||||||
|
setSavedOpportunities(pipelineProgresses);
|
||||||
|
setIsBoardLoaded(true);
|
||||||
|
},
|
||||||
|
[setIsBoardLoaded, setSavedOpportunities, updateCompanyBoardCardIds],
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
const { fetchMoreRecords: fetchMoreCompanies } = useFindManyRecords({
|
||||||
|
skip: !savedOpportunities.length,
|
||||||
|
objectNameSingular: 'company',
|
||||||
|
filter: {
|
||||||
|
id: {
|
||||||
|
in: savedOpportunities.map(
|
||||||
|
(opportunity) => opportunity.companyId || '',
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onCompleted: useCallback(
|
||||||
|
(data: PaginatedRecordTypeResults<Company>) => {
|
||||||
|
setSavedCompanies(data.edges.map((edge) => edge.node));
|
||||||
|
},
|
||||||
|
[setSavedCompanies],
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
opportunities,
|
||||||
|
loading,
|
||||||
|
fetchMoreOpportunities,
|
||||||
|
fetchMoreCompanies,
|
||||||
|
};
|
||||||
|
};
|
@ -1,5 +1,4 @@
|
|||||||
import { OpportunityPicker } from '@/companies/components/OpportunityPicker';
|
import { OpportunityPicker } from '@/companies/components/OpportunityPicker';
|
||||||
import { useCreateOpportunity } from '@/companies/hooks/useCreateOpportunity';
|
|
||||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
||||||
import { IconPlus } from '@/ui/display/icon/index';
|
import { IconPlus } from '@/ui/display/icon/index';
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
@ -9,6 +8,7 @@ import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/Rela
|
|||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||||
|
import { useCreateOpportunity } from '@/ui/object/record-board/hooks/internal/useCreateOpportunity';
|
||||||
import { logError } from '~/utils/logError';
|
import { logError } from '~/utils/logError';
|
||||||
|
|
||||||
export const PipelineAddButton = () => {
|
export const PipelineAddButton = () => {
|
||||||
@ -18,7 +18,7 @@ export const PipelineAddButton = () => {
|
|||||||
dropdownScopeId: 'add-pipeline-progress',
|
dropdownScopeId: 'add-pipeline-progress',
|
||||||
});
|
});
|
||||||
|
|
||||||
const { createOpportunity } = useCreateOpportunity();
|
const createOpportunity = useCreateOpportunity();
|
||||||
|
|
||||||
const handleCompanySelected = (
|
const handleCompanySelected = (
|
||||||
selectedCompany: EntityForSelect | null,
|
selectedCompany: EntityForSelect | null,
|
||||||
|
@ -13,7 +13,10 @@ export const useEntitySelectSearch = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const [relationPickerSearchFilter, setRelationPickerSearchFilter] =
|
const [relationPickerSearchFilter, setRelationPickerSearchFilter] =
|
||||||
useRecoilScopedState(relationPickerSearchFilterScopedState);
|
useRecoilScopedState(
|
||||||
|
relationPickerSearchFilterScopedState,
|
||||||
|
RelationPickerRecoilScopeContext,
|
||||||
|
);
|
||||||
|
|
||||||
const debouncedSetSearchFilter = debounce(
|
const debouncedSetSearchFilter = debounce(
|
||||||
setRelationPickerSearchFilter,
|
setRelationPickerSearchFilter,
|
||||||
|
@ -2,10 +2,10 @@ import React from 'react';
|
|||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { ActionBar } from '@/ui/navigation/action-bar/components/ActionBar';
|
import { ActionBar } from '@/ui/navigation/action-bar/components/ActionBar';
|
||||||
|
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
import { selectedCardIdsSelector } from '../../states/selectors/selectedCardIdsSelector';
|
|
||||||
|
|
||||||
export const RecordBoardActionBar = () => {
|
export const RecordBoardActionBar = () => {
|
||||||
|
const { selectedCardIdsSelector } = useRecordBoardScopedStates();
|
||||||
const selectedCardIds = useRecoilValue(selectedCardIdsSelector);
|
const selectedCardIds = useRecoilValue(selectedCardIdsSelector);
|
||||||
return <ActionBar selectedIds={selectedCardIds}></ActionBar>;
|
return <ActionBar selectedIds={selectedCardIds}></ActionBar>;
|
||||||
};
|
};
|
||||||
|
@ -6,24 +6,26 @@ import { useRecoilValue } from 'recoil';
|
|||||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||||
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
|
||||||
import { BoardColumnContext } from '@/ui/object/record-board/contexts/BoardColumnContext';
|
import { RecordBoardActionBar } from '@/ui/object/record-board/action-bar/components/RecordBoardActionBar';
|
||||||
import { useSetCardSelected } from '@/ui/object/record-board/hooks/useSetCardSelected';
|
import { RecordBoardInternalEffect } from '@/ui/object/record-board/components/RecordBoardInternalEffect';
|
||||||
import { useUpdateBoardCardIds } from '@/ui/object/record-board/hooks/useUpdateBoardCardIds';
|
import { RecordBoardContextMenu } from '@/ui/object/record-board/context-menu/components/RecordBoardContextMenu';
|
||||||
import { boardColumnsState } from '@/ui/object/record-board/states/boardColumnsState';
|
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
|
import { useSetRecordBoardCardSelectedInternal } from '@/ui/object/record-board/hooks/internal/useSetRecordBoardCardSelectedInternal';
|
||||||
|
import { useUpdateRecordBoardCardIdsInternal } from '@/ui/object/record-board/hooks/internal/useUpdateRecordBoardCardIdsInternal';
|
||||||
|
import { RecordBoardScope } from '@/ui/object/record-board/scopes/RecordBoardScope';
|
||||||
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
import { useListenClickOutsideByClassName } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
import { useListenClickOutsideByClassName } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
|
||||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||||
import { logError } from '~/utils/logError';
|
import { logError } from '~/utils/logError';
|
||||||
|
|
||||||
import { BoardColumnRecoilScopeContext } from '../states/recoil-scope-contexts/BoardColumnRecoilScopeContext';
|
|
||||||
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
||||||
import { BoardOptions } from '../types/BoardOptions';
|
import { BoardOptions } from '../types/BoardOptions';
|
||||||
|
|
||||||
import { RecordBoardColumn } from './RecordBoardColumn';
|
import { RecordBoardColumn } from './RecordBoardColumn';
|
||||||
|
|
||||||
export type RecordBoardProps = {
|
export type RecordBoardProps = {
|
||||||
|
recordBoardId: string;
|
||||||
boardOptions: BoardOptions;
|
boardOptions: BoardOptions;
|
||||||
onColumnAdd?: (boardColumn: BoardColumnDefinition) => void;
|
onColumnAdd?: (boardColumn: BoardColumnDefinition) => void;
|
||||||
onColumnDelete?: (boardColumnId: string) => void;
|
onColumnDelete?: (boardColumnId: string) => void;
|
||||||
@ -54,10 +56,16 @@ const StyledBoardHeader = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const RecordBoard = ({
|
export const RecordBoard = ({
|
||||||
|
recordBoardId,
|
||||||
boardOptions,
|
boardOptions,
|
||||||
onColumnDelete,
|
onColumnDelete,
|
||||||
onEditColumnTitle,
|
onEditColumnTitle,
|
||||||
}: RecordBoardProps) => {
|
}: RecordBoardProps) => {
|
||||||
|
const recordBoardScopeId = recordBoardId;
|
||||||
|
|
||||||
|
const { boardColumnsState } = useRecordBoardScopedStates({
|
||||||
|
recordBoardScopeId,
|
||||||
|
});
|
||||||
const boardColumns = useRecoilValue(boardColumnsState);
|
const boardColumns = useRecoilValue(boardColumnsState);
|
||||||
|
|
||||||
const { updateOneRecord: updateOneOpportunity } =
|
const { updateOneRecord: updateOneOpportunity } =
|
||||||
@ -65,7 +73,8 @@ export const RecordBoard = ({
|
|||||||
objectNameSingular: 'opportunity',
|
objectNameSingular: 'opportunity',
|
||||||
});
|
});
|
||||||
|
|
||||||
const { unselectAllActiveCards, setCardSelected } = useSetCardSelected();
|
const { unselectAllActiveCards, setCardSelected } =
|
||||||
|
useSetRecordBoardCardSelectedInternal({ recordBoardScopeId });
|
||||||
|
|
||||||
const updatePipelineProgressStageInDB = useCallback(
|
const updatePipelineProgressStageInDB = useCallback(
|
||||||
async (pipelineProgressId: string, pipelineStepId: string) => {
|
async (pipelineProgressId: string, pipelineStepId: string) => {
|
||||||
@ -85,7 +94,9 @@ export const RecordBoard = ({
|
|||||||
callback: unselectAllActiveCards,
|
callback: unselectAllActiveCards,
|
||||||
});
|
});
|
||||||
|
|
||||||
const updateBoardCardIds = useUpdateBoardCardIds();
|
const updateBoardCardIds = useUpdateRecordBoardCardIdsInternal({
|
||||||
|
recordBoardScopeId,
|
||||||
|
});
|
||||||
|
|
||||||
const onDragEnd: OnDragEndResponder = useCallback(
|
const onDragEnd: OnDragEndResponder = useCallback(
|
||||||
async (result) => {
|
async (result) => {
|
||||||
@ -128,41 +139,35 @@ export const RecordBoard = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledWrapper>
|
<RecordBoardScope recordBoardScopeId={recordBoardId}>
|
||||||
<StyledBoardHeader />
|
<RecordBoardContextMenu />
|
||||||
<ScrollWrapper>
|
<RecordBoardActionBar />
|
||||||
<StyledBoard ref={boardRef}>
|
<RecordBoardInternalEffect />
|
||||||
<DragDropContext onDragEnd={onDragEnd}>
|
|
||||||
{sortedBoardColumns.map((column) => (
|
<StyledWrapper>
|
||||||
<BoardColumnContext.Provider
|
<StyledBoardHeader />
|
||||||
key={column.id}
|
<ScrollWrapper>
|
||||||
value={{
|
<StyledBoard ref={boardRef}>
|
||||||
id: column.id,
|
<DragDropContext onDragEnd={onDragEnd}>
|
||||||
columnDefinition: column,
|
{sortedBoardColumns.map((column) => (
|
||||||
isFirstColumn: column.position === 0,
|
<RecordBoardColumn
|
||||||
isLastColumn:
|
|
||||||
column.position === sortedBoardColumns.length - 1,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<RecoilScope
|
|
||||||
CustomRecoilScopeContext={BoardColumnRecoilScopeContext}
|
|
||||||
key={column.id}
|
key={column.id}
|
||||||
>
|
recordBoardColumnId={column.id}
|
||||||
<RecordBoardColumn
|
columnDefinition={column}
|
||||||
boardOptions={boardOptions}
|
recordBoardColumnTotal={sortedBoardColumns.length}
|
||||||
onDelete={onColumnDelete}
|
recordBoardOptions={boardOptions}
|
||||||
onTitleEdit={onEditColumnTitle}
|
onDelete={onColumnDelete}
|
||||||
/>
|
onTitleEdit={onEditColumnTitle}
|
||||||
</RecoilScope>
|
/>
|
||||||
</BoardColumnContext.Provider>
|
))}
|
||||||
))}
|
</DragDropContext>
|
||||||
</DragDropContext>
|
</StyledBoard>
|
||||||
</StyledBoard>
|
</ScrollWrapper>
|
||||||
</ScrollWrapper>
|
<DragSelect
|
||||||
<DragSelect
|
dragSelectable={boardRef}
|
||||||
dragSelectable={boardRef}
|
onDragSelectionChange={setCardSelected}
|
||||||
onDragSelectionChange={setCardSelected}
|
/>
|
||||||
/>
|
</StyledWrapper>
|
||||||
</StyledWrapper>
|
</RecordBoardScope>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,22 +4,23 @@ import { useSetRecoilState } from 'recoil';
|
|||||||
import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/contextMenuIsOpenState';
|
import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/contextMenuIsOpenState';
|
||||||
import { contextMenuPositionState } from '@/ui/navigation/context-menu/states/contextMenuPositionState';
|
import { contextMenuPositionState } from '@/ui/navigation/context-menu/states/contextMenuPositionState';
|
||||||
|
|
||||||
import { useCurrentCardSelected } from '../hooks/useCurrentCardSelected';
|
import { useCurrentRecordBoardCardSelectedInternal } from '../hooks/internal/useCurrentRecordBoardCardSelectedInternal';
|
||||||
import { BoardOptions } from '../types/BoardOptions';
|
import { BoardOptions } from '../types/BoardOptions';
|
||||||
|
|
||||||
export const RecordBoardCard = ({
|
export const RecordBoardCard = ({
|
||||||
boardOptions,
|
recordBoardOptions,
|
||||||
cardId,
|
cardId,
|
||||||
index,
|
index,
|
||||||
}: {
|
}: {
|
||||||
boardOptions: BoardOptions;
|
recordBoardOptions: BoardOptions;
|
||||||
cardId: string;
|
cardId: string;
|
||||||
index: number;
|
index: number;
|
||||||
}) => {
|
}) => {
|
||||||
const setContextMenuPosition = useSetRecoilState(contextMenuPositionState);
|
const setContextMenuPosition = useSetRecoilState(contextMenuPositionState);
|
||||||
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
|
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
|
||||||
|
|
||||||
const { setCurrentCardSelected } = useCurrentCardSelected();
|
const { setCurrentCardSelected } =
|
||||||
|
useCurrentRecordBoardCardSelectedInternal();
|
||||||
|
|
||||||
const handleContextMenu = (event: React.MouseEvent) => {
|
const handleContextMenu = (event: React.MouseEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -45,7 +46,7 @@ export const RecordBoardCard = ({
|
|||||||
data-select-disable
|
data-select-disable
|
||||||
onContextMenu={handleContextMenu}
|
onContextMenu={handleContextMenu}
|
||||||
>
|
>
|
||||||
{<boardOptions.CardComponent />}
|
{<recordBoardOptions.CardComponent />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Draggable>
|
</Draggable>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useContext, useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
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';
|
||||||
@ -8,12 +8,12 @@ import { Tag } from '@/ui/display/tag/components/Tag';
|
|||||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||||
import { RecordBoardCard } from '@/ui/object/record-board/components/RecordBoardCard';
|
import { RecordBoardCard } from '@/ui/object/record-board/components/RecordBoardCard';
|
||||||
import { BoardCardIdContext } from '@/ui/object/record-board/contexts/BoardCardIdContext';
|
import { BoardCardIdContext } from '@/ui/object/record-board/contexts/BoardCardIdContext';
|
||||||
|
import { BoardColumnDefinition } from '@/ui/object/record-board/types/BoardColumnDefinition';
|
||||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
|
||||||
|
|
||||||
import { BoardColumnContext } from '../contexts/BoardColumnContext';
|
import { BoardColumnContext } from '../contexts/BoardColumnContext';
|
||||||
import { boardCardIdsByColumnIdFamilyState } from '../states/boardCardIdsByColumnIdFamilyState';
|
import { recordBoardCardIdsByColumnIdFamilyState } from '../states/recordBoardCardIdsByColumnIdFamilyState';
|
||||||
import { boardColumnTotalsFamilySelector } from '../states/selectors/boardColumnTotalsFamilySelector';
|
import { recordBoardColumnTotalsFamilySelector } from '../states/selectors/recordBoardColumnTotalsFamilySelector';
|
||||||
import { BoardColumnHotkeyScope } from '../types/BoardColumnHotkeyScope';
|
import { BoardColumnHotkeyScope } from '../types/BoardColumnHotkeyScope';
|
||||||
import { BoardOptions } from '../types/BoardOptions';
|
import { BoardOptions } from '../types/BoardOptions';
|
||||||
|
|
||||||
@ -87,7 +87,10 @@ type BoardColumnCardsContainerProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type RecordBoardColumnProps = {
|
type RecordBoardColumnProps = {
|
||||||
boardOptions: BoardOptions;
|
recordBoardColumnId: string;
|
||||||
|
columnDefinition: BoardColumnDefinition;
|
||||||
|
recordBoardOptions: BoardOptions;
|
||||||
|
recordBoardColumnTotal: number;
|
||||||
onDelete?: (columnId: string) => void;
|
onDelete?: (columnId: string) => void;
|
||||||
onTitleEdit: (columnId: string, title: string, color: string) => void;
|
onTitleEdit: (columnId: string, title: string, color: string) => void;
|
||||||
};
|
};
|
||||||
@ -109,12 +112,13 @@ const BoardColumnCardsContainer = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const RecordBoardColumn = ({
|
export const RecordBoardColumn = ({
|
||||||
boardOptions,
|
recordBoardColumnId,
|
||||||
|
columnDefinition,
|
||||||
|
recordBoardOptions,
|
||||||
|
recordBoardColumnTotal,
|
||||||
onDelete,
|
onDelete,
|
||||||
onTitleEdit,
|
onTitleEdit,
|
||||||
}: RecordBoardColumnProps) => {
|
}: RecordBoardColumnProps) => {
|
||||||
const column = useContext(BoardColumnContext);
|
|
||||||
|
|
||||||
const [isBoardColumnMenuOpen, setIsBoardColumnMenuOpen] = useState(false);
|
const [isBoardColumnMenuOpen, setIsBoardColumnMenuOpen] = useState(false);
|
||||||
const [isHeaderHovered, setIsHeaderHovered] = useState(false);
|
const [isHeaderHovered, setIsHeaderHovered] = useState(false);
|
||||||
|
|
||||||
@ -135,96 +139,110 @@ export const RecordBoardColumn = ({
|
|||||||
setIsBoardColumnMenuOpen(false);
|
setIsBoardColumnMenuOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const boardColumnId = column?.id || '';
|
|
||||||
|
|
||||||
const boardColumnTotal = useRecoilValue(
|
const boardColumnTotal = useRecoilValue(
|
||||||
boardColumnTotalsFamilySelector(boardColumnId),
|
recordBoardColumnTotalsFamilySelector(recordBoardColumnId),
|
||||||
);
|
);
|
||||||
|
|
||||||
const cardIds = useRecoilValue(
|
const cardIds = useRecoilValue(
|
||||||
boardCardIdsByColumnIdFamilyState(boardColumnId),
|
recordBoardCardIdsByColumnIdFamilyState(recordBoardColumnId),
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleTitleEdit = (title: string, color: string) => {
|
const handleTitleEdit = (title: string, color: string) => {
|
||||||
onTitleEdit(boardColumnId, title, color);
|
onTitleEdit(recordBoardColumnId, title, color);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!column) return <></>;
|
const isFirstColumn = columnDefinition.position === 0;
|
||||||
|
|
||||||
const { isFirstColumn, columnDefinition } = column;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Droppable droppableId={column.id}>
|
<BoardColumnContext.Provider
|
||||||
{(droppableProvided) => (
|
value={{
|
||||||
<StyledColumn isFirstColumn={isFirstColumn}>
|
id: recordBoardColumnId,
|
||||||
<StyledHeader
|
columnDefinition: columnDefinition,
|
||||||
onMouseEnter={() => setIsHeaderHovered(true)}
|
isFirstColumn: columnDefinition.position === 0,
|
||||||
onMouseLeave={() => setIsHeaderHovered(false)}
|
isLastColumn: columnDefinition.position === recordBoardColumnTotal - 1,
|
||||||
>
|
}}
|
||||||
<Tag
|
>
|
||||||
onClick={handleBoardColumnMenuOpen}
|
<Droppable droppableId={recordBoardColumnId}>
|
||||||
color={columnDefinition.colorCode ?? 'gray'}
|
{(droppableProvided) => (
|
||||||
text={columnDefinition.title}
|
<StyledColumn isFirstColumn={isFirstColumn}>
|
||||||
/>
|
<StyledHeader
|
||||||
{!!boardColumnTotal && (
|
onMouseEnter={() => setIsHeaderHovered(true)}
|
||||||
<StyledAmount>${boardColumnTotal}</StyledAmount>
|
onMouseLeave={() => setIsHeaderHovered(false)}
|
||||||
)}
|
>
|
||||||
{!isHeaderHovered && (
|
<Tag
|
||||||
<StyledNumChildren>{cardIds.length}</StyledNumChildren>
|
onClick={handleBoardColumnMenuOpen}
|
||||||
)}
|
color={columnDefinition.colorCode ?? 'gray'}
|
||||||
{isHeaderHovered && (
|
text={columnDefinition.title}
|
||||||
<StyledHeaderActions>
|
/>
|
||||||
<LightIconButton
|
{!!boardColumnTotal && (
|
||||||
accent="tertiary"
|
<StyledAmount>${boardColumnTotal}</StyledAmount>
|
||||||
Icon={IconDotsVertical}
|
)}
|
||||||
onClick={handleBoardColumnMenuOpen}
|
{!isHeaderHovered && (
|
||||||
/>
|
<StyledNumChildren>{cardIds.length}</StyledNumChildren>
|
||||||
{/* <LightIconButton
|
)}
|
||||||
|
{isHeaderHovered && (
|
||||||
|
<StyledHeaderActions>
|
||||||
|
<LightIconButton
|
||||||
|
accent="tertiary"
|
||||||
|
Icon={IconDotsVertical}
|
||||||
|
onClick={handleBoardColumnMenuOpen}
|
||||||
|
/>
|
||||||
|
{/* <LightIconButton
|
||||||
accent="tertiary"
|
accent="tertiary"
|
||||||
Icon={IconPlus}
|
Icon={IconPlus}
|
||||||
onClick={() => {}}
|
onClick={() => {}}
|
||||||
/> */}
|
/> */}
|
||||||
</StyledHeaderActions>
|
</StyledHeaderActions>
|
||||||
)}
|
|
||||||
</StyledHeader>
|
|
||||||
{isBoardColumnMenuOpen && (
|
|
||||||
<RecordBoardColumnDropdownMenu
|
|
||||||
onClose={handleBoardColumnMenuClose}
|
|
||||||
onDelete={onDelete}
|
|
||||||
onTitleEdit={handleTitleEdit}
|
|
||||||
stageId={boardColumnId}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<BoardColumnCardsContainer droppableProvided={droppableProvided}>
|
|
||||||
{cardIds.map((cardId, index) => (
|
|
||||||
<BoardCardIdContext.Provider value={cardId} key={cardId}>
|
|
||||||
<RecordBoardCard
|
|
||||||
index={index}
|
|
||||||
cardId={cardId}
|
|
||||||
boardOptions={boardOptions}
|
|
||||||
/>
|
|
||||||
</BoardCardIdContext.Provider>
|
|
||||||
))}
|
|
||||||
<Draggable
|
|
||||||
draggableId={`new-${column.id}`}
|
|
||||||
index={cardIds.length}
|
|
||||||
isDragDisabled={true}
|
|
||||||
>
|
|
||||||
{(draggableProvided) => (
|
|
||||||
<div
|
|
||||||
ref={draggableProvided?.innerRef}
|
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
|
||||||
{...draggableProvided?.draggableProps}
|
|
||||||
>
|
|
||||||
<StyledNewCardButtonContainer>
|
|
||||||
<RecoilScope>{boardOptions.newCardComponent}</RecoilScope>
|
|
||||||
</StyledNewCardButtonContainer>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</Draggable>
|
</StyledHeader>
|
||||||
</BoardColumnCardsContainer>
|
{isBoardColumnMenuOpen && (
|
||||||
</StyledColumn>
|
<RecordBoardColumnDropdownMenu
|
||||||
)}
|
onClose={handleBoardColumnMenuClose}
|
||||||
</Droppable>
|
onDelete={onDelete}
|
||||||
|
onTitleEdit={handleTitleEdit}
|
||||||
|
stageId={recordBoardColumnId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isBoardColumnMenuOpen && (
|
||||||
|
<RecordBoardColumnDropdownMenu
|
||||||
|
onClose={handleBoardColumnMenuClose}
|
||||||
|
onDelete={onDelete}
|
||||||
|
onTitleEdit={handleTitleEdit}
|
||||||
|
stageId={recordBoardColumnId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<BoardColumnCardsContainer droppableProvided={droppableProvided}>
|
||||||
|
{cardIds.map((cardId, index) => (
|
||||||
|
<BoardCardIdContext.Provider value={cardId} key={cardId}>
|
||||||
|
<RecordBoardCard
|
||||||
|
index={index}
|
||||||
|
cardId={cardId}
|
||||||
|
recordBoardOptions={recordBoardOptions}
|
||||||
|
/>
|
||||||
|
</BoardCardIdContext.Provider>
|
||||||
|
))}
|
||||||
|
<Draggable
|
||||||
|
draggableId={`new-${recordBoardColumnId}`}
|
||||||
|
index={cardIds.length}
|
||||||
|
isDragDisabled={true}
|
||||||
|
>
|
||||||
|
{(draggableProvided) => (
|
||||||
|
<div
|
||||||
|
ref={draggableProvided?.innerRef}
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
{...draggableProvided?.draggableProps}
|
||||||
|
>
|
||||||
|
<StyledNewCardButtonContainer>
|
||||||
|
{recordBoardOptions.newCardComponent}
|
||||||
|
</StyledNewCardButtonContainer>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Draggable>
|
||||||
|
</BoardColumnCardsContainer>
|
||||||
|
</StyledColumn>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
|
</BoardColumnContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -12,7 +12,7 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
|||||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||||
|
|
||||||
import { BoardColumnContext } from '../contexts/BoardColumnContext';
|
import { BoardColumnContext } from '../contexts/BoardColumnContext';
|
||||||
import { useBoardColumns } from '../hooks/useBoardColumns';
|
import { useBoardColumnsInternal } from '../hooks/internal/useRecordBoardColumnsInternal';
|
||||||
import { BoardColumnHotkeyScope } from '../types/BoardColumnHotkeyScope';
|
import { BoardColumnHotkeyScope } from '../types/BoardColumnHotkeyScope';
|
||||||
|
|
||||||
import { RecordBoardColumnEditTitleMenu } from './RecordBoardColumnEditTitleMenu';
|
import { RecordBoardColumnEditTitleMenu } from './RecordBoardColumnEditTitleMenu';
|
||||||
@ -43,7 +43,7 @@ export const RecordBoardColumnDropdownMenu = ({
|
|||||||
|
|
||||||
const boardColumnMenuRef = useRef<HTMLDivElement>(null);
|
const boardColumnMenuRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const { handleMoveBoardColumn } = useBoardColumns();
|
const { handleMoveBoardColumn } = useBoardColumnsInternal();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
setHotkeyScopeAndMemorizePreviousScope,
|
setHotkeyScopeAndMemorizePreviousScope,
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
import { ChangeEvent, useCallback, useState } from 'react';
|
import { ChangeEvent, useCallback, useState } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilState } from 'recoil';
|
|
||||||
|
|
||||||
import { IconTrash } from '@/ui/display/icon';
|
import { IconTrash } from '@/ui/display/icon';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||||
import { MenuItemSelectColor } from '@/ui/navigation/menu-item/components/MenuItemSelectColor';
|
import { MenuItemSelectColor } from '@/ui/navigation/menu-item/components/MenuItemSelectColor';
|
||||||
|
import { useRecordBoard } from '@/ui/object/record-board/hooks/useRecordBoard';
|
||||||
import { mainColorNames, ThemeColor } from '@/ui/theme/constants/colors';
|
import { mainColorNames, ThemeColor } from '@/ui/theme/constants/colors';
|
||||||
import { textInputStyle } from '@/ui/theme/constants/effects';
|
import { textInputStyle } from '@/ui/theme/constants/effects';
|
||||||
import { debounce } from '~/utils/debounce';
|
import { debounce } from '~/utils/debounce';
|
||||||
|
|
||||||
import { boardColumnsState } from '../states/boardColumnsState';
|
|
||||||
|
|
||||||
const StyledEditTitleContainer = styled.div`
|
const StyledEditTitleContainer = styled.div`
|
||||||
--vertical-padding: ${({ theme }) => theme.spacing(1)};
|
--vertical-padding: ${({ theme }) => theme.spacing(1)};
|
||||||
|
|
||||||
@ -58,7 +56,9 @@ export const RecordBoardColumnEditTitleMenu = ({
|
|||||||
color,
|
color,
|
||||||
}: RecordBoardColumnEditTitleMenuProps) => {
|
}: RecordBoardColumnEditTitleMenuProps) => {
|
||||||
const [internalValue, setInternalValue] = useState(title);
|
const [internalValue, setInternalValue] = useState(title);
|
||||||
const [, setBoardColumns] = useRecoilState(boardColumnsState);
|
|
||||||
|
const { setBoardColumns } = useRecordBoard();
|
||||||
|
|
||||||
const debouncedOnUpdateTitle = debounce(
|
const debouncedOnUpdateTitle = debounce(
|
||||||
(newTitle) => onTitleEdit(newTitle, color),
|
(newTitle) => onTitleEdit(newTitle, color),
|
||||||
200,
|
200,
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
||||||
|
import { useRecordBoard } from '@/ui/object/record-board/hooks/useRecordBoard';
|
||||||
|
import { BoardFieldDefinition } from '@/ui/object/record-board/types/BoardFieldDefinition';
|
||||||
|
|
||||||
|
type RecordBoardEffectProps = {
|
||||||
|
recordBoardId: string;
|
||||||
|
onFieldsChange: (fields: BoardFieldDefinition<FieldMetadata>[]) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RecordBoardEffect = ({
|
||||||
|
recordBoardId,
|
||||||
|
onFieldsChange,
|
||||||
|
}: RecordBoardEffectProps) => {
|
||||||
|
const { setOnFieldsChange } = useRecordBoard({
|
||||||
|
recordBoardScopeId: recordBoardId,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setOnFieldsChange(() => onFieldsChange);
|
||||||
|
}, [onFieldsChange, setOnFieldsChange]);
|
||||||
|
|
||||||
|
return <></>;
|
||||||
|
};
|
@ -0,0 +1,66 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { useObjectRecordBoard } from '@/object-record/hooks/useObjectRecordBoard';
|
||||||
|
import { useRecordBoardActionBarEntriesInternal } from '@/ui/object/record-board/hooks/internal/useRecordBoardActionBarEntriesInternal';
|
||||||
|
import { useRecordBoardContextMenuEntriesInternal } from '@/ui/object/record-board/hooks/internal/useRecordBoardContextMenuEntriesInternal';
|
||||||
|
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
|
import { useUpdateCompanyBoardColumnsInternal } from '@/ui/object/record-board/hooks/internal/useUpdateCompanyBoardColumnsInternal';
|
||||||
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
|
export type RecordBoardInternalEffectProps = {
|
||||||
|
onFieldsChange: (fields: any) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RecordBoardInternalEffect = ({}) => {
|
||||||
|
const updateCompanyColumnsBoardInternal =
|
||||||
|
useUpdateCompanyBoardColumnsInternal();
|
||||||
|
const { setActionBarEntries } = useRecordBoardActionBarEntriesInternal();
|
||||||
|
const { setContextMenuEntries } = useRecordBoardContextMenuEntriesInternal();
|
||||||
|
|
||||||
|
const { fetchMoreOpportunities, fetchMoreCompanies } = useObjectRecordBoard();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isDefined(fetchMoreOpportunities)) {
|
||||||
|
fetchMoreOpportunities();
|
||||||
|
}
|
||||||
|
}, [fetchMoreOpportunities]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isDefined(fetchMoreCompanies)) {
|
||||||
|
fetchMoreCompanies();
|
||||||
|
}
|
||||||
|
}, [fetchMoreCompanies]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
savedPipelineStepsState,
|
||||||
|
savedOpportunitiesState,
|
||||||
|
savedCompaniesState,
|
||||||
|
} = useRecordBoardScopedStates();
|
||||||
|
|
||||||
|
const savedPipelineSteps = useRecoilValue(savedPipelineStepsState);
|
||||||
|
const savedOpportunities = useRecoilValue(savedOpportunitiesState);
|
||||||
|
const savedCompanies = useRecoilValue(savedCompaniesState);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (savedOpportunities && savedCompanies) {
|
||||||
|
setActionBarEntries();
|
||||||
|
setContextMenuEntries();
|
||||||
|
|
||||||
|
updateCompanyColumnsBoardInternal(
|
||||||
|
savedPipelineSteps,
|
||||||
|
savedOpportunities,
|
||||||
|
savedCompanies,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
savedCompanies,
|
||||||
|
savedOpportunities,
|
||||||
|
savedPipelineSteps,
|
||||||
|
setActionBarEntries,
|
||||||
|
setContextMenuEntries,
|
||||||
|
updateCompanyColumnsBoardInternal,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return <></>;
|
||||||
|
};
|
@ -1,11 +1,10 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { ContextMenu } from '@/ui/navigation/context-menu/components/ContextMenu';
|
import { ContextMenu } from '@/ui/navigation/context-menu/components/ContextMenu';
|
||||||
|
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
import { selectedCardIdsSelector } from '../../states/selectors/selectedCardIdsSelector';
|
|
||||||
|
|
||||||
export const RecordBoardContextMenu = () => {
|
export const RecordBoardContextMenu = () => {
|
||||||
|
const { selectedCardIdsSelector } = useRecordBoardScopedStates();
|
||||||
const selectedCardIds = useRecoilValue(selectedCardIdsSelector);
|
const selectedCardIds = useRecoilValue(selectedCardIdsSelector);
|
||||||
return <ContextMenu selectedIds={selectedCardIds}></ContextMenu>;
|
return <ContextMenu selectedIds={selectedCardIds}></ContextMenu>;
|
||||||
};
|
};
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { createContext } from 'react';
|
|
||||||
|
|
||||||
import { BoardOptions } from '@/ui/object/record-board/types/BoardOptions';
|
|
||||||
|
|
||||||
export const BoardOptionsContext = createContext<BoardOptions | null>(null);
|
|
@ -3,7 +3,7 @@ import { v4 } from 'uuid';
|
|||||||
|
|
||||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||||
import { boardCardIdsByColumnIdFamilyState } from '@/ui/object/record-board/states/boardCardIdsByColumnIdFamilyState';
|
import { recordBoardCardIdsByColumnIdFamilyState } from '@/ui/object/record-board/states/recordBoardCardIdsByColumnIdFamilyState';
|
||||||
|
|
||||||
export const useCreateOpportunity = () => {
|
export const useCreateOpportunity = () => {
|
||||||
const { createOneRecord: createOneOpportunity } =
|
const { createOneRecord: createOneOpportunity } =
|
||||||
@ -16,10 +16,10 @@ export const useCreateOpportunity = () => {
|
|||||||
async (companyId: string, pipelineStepId: string) => {
|
async (companyId: string, pipelineStepId: string) => {
|
||||||
const newUuid = v4();
|
const newUuid = v4();
|
||||||
|
|
||||||
set(boardCardIdsByColumnIdFamilyState(pipelineStepId), (oldValue) => [
|
set(
|
||||||
...oldValue,
|
recordBoardCardIdsByColumnIdFamilyState(pipelineStepId),
|
||||||
newUuid,
|
(oldValue) => [...oldValue, newUuid],
|
||||||
]);
|
);
|
||||||
|
|
||||||
await createOneOpportunity?.({
|
await createOneOpportunity?.({
|
||||||
id: newUuid,
|
id: newUuid,
|
||||||
@ -30,5 +30,5 @@ export const useCreateOpportunity = () => {
|
|||||||
[createOneOpportunity],
|
[createOneOpportunity],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { createOpportunity };
|
return createOpportunity;
|
||||||
};
|
};
|
@ -2,18 +2,20 @@ import { useContext } from 'react';
|
|||||||
import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { actionBarOpenState } from '@/ui/navigation/action-bar/states/actionBarIsOpenState';
|
import { actionBarOpenState } from '@/ui/navigation/action-bar/states/actionBarIsOpenState';
|
||||||
import { activeCardIdsState } from '@/ui/object/record-board/states/activeCardIdsState';
|
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
|
|
||||||
import { BoardCardIdContext } from '../contexts/BoardCardIdContext';
|
import { BoardCardIdContext } from '../../contexts/BoardCardIdContext';
|
||||||
import { isCardSelectedFamilyState } from '../states/isCardSelectedFamilyState';
|
import { isRecordBoardCardSelectedFamilyState } from '../../states/isRecordBoardCardSelectedFamilyState';
|
||||||
|
|
||||||
export const useCurrentCardSelected = () => {
|
export const useCurrentRecordBoardCardSelectedInternal = () => {
|
||||||
const currentCardId = useContext(BoardCardIdContext);
|
const currentCardId = useContext(BoardCardIdContext);
|
||||||
|
|
||||||
const isCurrentCardSelected = useRecoilValue(
|
const isCurrentCardSelected = useRecoilValue(
|
||||||
isCardSelectedFamilyState(currentCardId ?? ''),
|
isRecordBoardCardSelectedFamilyState(currentCardId ?? ''),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { activeCardIdsState } = useRecordBoardScopedStates();
|
||||||
|
|
||||||
const setActiveCardIds = useSetRecoilState(activeCardIdsState);
|
const setActiveCardIds = useSetRecoilState(activeCardIdsState);
|
||||||
|
|
||||||
const setCurrentCardSelected = useRecoilCallback(
|
const setCurrentCardSelected = useRecoilCallback(
|
||||||
@ -21,7 +23,7 @@ export const useCurrentCardSelected = () => {
|
|||||||
(selected: boolean) => {
|
(selected: boolean) => {
|
||||||
if (!currentCardId) return;
|
if (!currentCardId) return;
|
||||||
|
|
||||||
set(isCardSelectedFamilyState(currentCardId), selected);
|
set(isRecordBoardCardSelectedFamilyState(currentCardId), selected);
|
||||||
set(actionBarOpenState, selected);
|
set(actionBarOpenState, selected);
|
||||||
|
|
||||||
if (selected) {
|
if (selected) {
|
@ -3,13 +3,12 @@ import { useRecoilCallback } from 'recoil';
|
|||||||
|
|
||||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||||
|
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
|
|
||||||
import { selectedCardIdsSelector } from '../states/selectors/selectedCardIdsSelector';
|
import { useRemoveRecordBoardCardIdsInternal } from './useRemoveRecordBoardCardIdsInternal';
|
||||||
|
|
||||||
import { useRemoveCardIds } from './useRemoveCardIds';
|
export const useDeleteSelectedRecordBoardCardsInternal = () => {
|
||||||
|
const removeCardIds = useRemoveRecordBoardCardIdsInternal();
|
||||||
export const useDeleteSelectedBoardCards = () => {
|
|
||||||
const removeCardIds = useRemoveCardIds();
|
|
||||||
const apolloClient = useApolloClient();
|
const apolloClient = useApolloClient();
|
||||||
|
|
||||||
const { deleteOneRecord: deleteOneOpportunity } =
|
const { deleteOneRecord: deleteOneOpportunity } =
|
||||||
@ -17,6 +16,8 @@ export const useDeleteSelectedBoardCards = () => {
|
|||||||
objectNameSingular: 'opportunity',
|
objectNameSingular: 'opportunity',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { selectedCardIdsSelector } = useRecordBoardScopedStates();
|
||||||
|
|
||||||
const deleteSelectedBoardCards = useRecoilCallback(
|
const deleteSelectedBoardCards = useRecoilCallback(
|
||||||
({ snapshot }) =>
|
({ snapshot }) =>
|
||||||
async () => {
|
async () => {
|
||||||
@ -34,7 +35,12 @@ export const useDeleteSelectedBoardCards = () => {
|
|||||||
apolloClient.cache.evict({ id: `Opportunity:${id}` });
|
apolloClient.cache.evict({ id: `Opportunity:${id}` });
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[apolloClient.cache, removeCardIds, deleteOneOpportunity],
|
[
|
||||||
|
selectedCardIdsSelector,
|
||||||
|
removeCardIds,
|
||||||
|
deleteOneOpportunity,
|
||||||
|
apolloClient.cache,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
return deleteSelectedBoardCards;
|
return deleteSelectedBoardCards;
|
@ -3,12 +3,12 @@ import { useSetRecoilState } from 'recoil';
|
|||||||
|
|
||||||
import { IconTrash } from '@/ui/display/icon';
|
import { IconTrash } from '@/ui/display/icon';
|
||||||
import { actionBarEntriesState } from '@/ui/navigation/action-bar/states/actionBarEntriesState';
|
import { actionBarEntriesState } from '@/ui/navigation/action-bar/states/actionBarEntriesState';
|
||||||
import { useDeleteSelectedBoardCards } from '@/ui/object/record-board/hooks/useDeleteSelectedBoardCards';
|
import { useDeleteSelectedRecordBoardCardsInternal } from '@/ui/object/record-board/hooks/internal/useDeleteSelectedRecordBoardCardsInternal';
|
||||||
|
|
||||||
export const useBoardActionBarEntries = () => {
|
export const useRecordBoardActionBarEntriesInternal = () => {
|
||||||
const setActionBarEntriesRecoil = useSetRecoilState(actionBarEntriesState);
|
const setActionBarEntriesRecoil = useSetRecoilState(actionBarEntriesState);
|
||||||
|
|
||||||
const deleteSelectedBoardCards = useDeleteSelectedBoardCards();
|
const deleteSelectedBoardCards = useDeleteSelectedRecordBoardCardsInternal();
|
||||||
|
|
||||||
const setActionBarEntries = useCallback(() => {
|
const setActionBarEntries = useCallback(() => {
|
||||||
setActionBarEntriesRecoil([
|
setActionBarEntriesRecoil([
|
@ -0,0 +1,73 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
||||||
|
import { RecordBoardScopeInternalContext } from '@/ui/object/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext';
|
||||||
|
import { onFieldsChangeScopedState } from '@/ui/object/record-board/states/onFieldsChangeScopedState';
|
||||||
|
import { recordBoardCardFieldsScopedState } from '@/ui/object/record-board/states/recordBoardCardFieldsScopedState';
|
||||||
|
import { savedRecordBoardCardFieldsScopedState } from '@/ui/object/record-board/states/savedRecordBoardCardFieldsScopedState';
|
||||||
|
import { BoardFieldDefinition } from '@/ui/object/record-board/types/BoardFieldDefinition';
|
||||||
|
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
|
||||||
|
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||||
|
|
||||||
|
type useRecordBoardCardFieldsInternalProps = {
|
||||||
|
recordBoardScopeId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useRecordBoardCardFieldsInternal = (
|
||||||
|
props?: useRecordBoardCardFieldsInternalProps,
|
||||||
|
) => {
|
||||||
|
const scopeId = useAvailableScopeIdOrThrow(
|
||||||
|
RecordBoardScopeInternalContext,
|
||||||
|
props?.recordBoardScopeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const setBoardCardFields = useSetRecoilState(
|
||||||
|
recordBoardCardFieldsScopedState({ scopeId }),
|
||||||
|
);
|
||||||
|
|
||||||
|
const setSavedBoardCardFields = useSetRecoilState(
|
||||||
|
savedRecordBoardCardFieldsScopedState({ scopeId }),
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleFieldVisibilityChange = (
|
||||||
|
field: Omit<ColumnDefinition<FieldMetadata>, 'size' | 'position'>,
|
||||||
|
) => {
|
||||||
|
setBoardCardFields((previousFields) =>
|
||||||
|
previousFields.map((previousField) =>
|
||||||
|
previousField.fieldMetadataId === field.fieldMetadataId
|
||||||
|
? { ...previousField, isVisible: !field.isVisible }
|
||||||
|
: previousField,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFieldsChange = useRecoilCallback(
|
||||||
|
({ snapshot }) =>
|
||||||
|
async (fields: BoardFieldDefinition<FieldMetadata>[]) => {
|
||||||
|
setSavedBoardCardFields(fields);
|
||||||
|
setBoardCardFields(fields);
|
||||||
|
|
||||||
|
const onFieldsChange = snapshot
|
||||||
|
.getLoadable(onFieldsChangeScopedState({ scopeId }))
|
||||||
|
.getValue();
|
||||||
|
|
||||||
|
await onFieldsChange?.(fields);
|
||||||
|
},
|
||||||
|
[scopeId, setBoardCardFields, setSavedBoardCardFields],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleFieldsReorder = useCallback(
|
||||||
|
async (fields: BoardFieldDefinition<FieldMetadata>[]) => {
|
||||||
|
const updatedFields = fields.map((column, index) => ({
|
||||||
|
...column,
|
||||||
|
position: index,
|
||||||
|
}));
|
||||||
|
|
||||||
|
await handleFieldsChange(updatedFields);
|
||||||
|
},
|
||||||
|
[handleFieldsChange],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { handleFieldVisibilityChange, handleFieldsReorder };
|
||||||
|
};
|
@ -2,12 +2,13 @@ import { useRecoilState } from 'recoil';
|
|||||||
|
|
||||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||||
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
||||||
|
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
import { useMoveViewColumns } from '@/views/hooks/useMoveViewColumns';
|
import { useMoveViewColumns } from '@/views/hooks/useMoveViewColumns';
|
||||||
|
|
||||||
import { boardColumnsState } from '../states/boardColumnsState';
|
import { BoardColumnDefinition } from '../../types/BoardColumnDefinition';
|
||||||
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
|
||||||
|
|
||||||
export const useBoardColumns = () => {
|
export const useBoardColumnsInternal = () => {
|
||||||
|
const { boardColumnsState } = useRecordBoardScopedStates();
|
||||||
const [boardColumns, setBoardColumns] = useRecoilState(boardColumnsState);
|
const [boardColumns, setBoardColumns] = useRecoilState(boardColumnsState);
|
||||||
|
|
||||||
const { handleColumnMove } = useMoveViewColumns();
|
const { handleColumnMove } = useMoveViewColumns();
|
@ -3,14 +3,14 @@ import { useSetRecoilState } from 'recoil';
|
|||||||
|
|
||||||
import { IconTrash } from '@/ui/display/icon';
|
import { IconTrash } from '@/ui/display/icon';
|
||||||
import { contextMenuEntriesState } from '@/ui/navigation/context-menu/states/contextMenuEntriesState';
|
import { contextMenuEntriesState } from '@/ui/navigation/context-menu/states/contextMenuEntriesState';
|
||||||
import { useDeleteSelectedBoardCards } from '@/ui/object/record-board/hooks/useDeleteSelectedBoardCards';
|
import { useDeleteSelectedRecordBoardCardsInternal } from '@/ui/object/record-board/hooks/internal/useDeleteSelectedRecordBoardCardsInternal';
|
||||||
|
|
||||||
export const useBoardContextMenuEntries = () => {
|
export const useRecordBoardContextMenuEntriesInternal = () => {
|
||||||
const setContextMenuEntriesRecoil = useSetRecoilState(
|
const setContextMenuEntriesRecoil = useSetRecoilState(
|
||||||
contextMenuEntriesState,
|
contextMenuEntriesState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const deleteSelectedBoardCards = useDeleteSelectedBoardCards();
|
const deleteSelectedBoardCards = useDeleteSelectedRecordBoardCardsInternal();
|
||||||
|
|
||||||
const setContextMenuEntries = useCallback(() => {
|
const setContextMenuEntries = useCallback(() => {
|
||||||
setContextMenuEntriesRecoil([
|
setContextMenuEntriesRecoil([
|
@ -0,0 +1,59 @@
|
|||||||
|
import { RecordBoardScopeInternalContext } from '@/ui/object/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext';
|
||||||
|
import { getRecordBoardScopedStates } from '@/ui/object/record-board/utils/getRecordBoardScopedStates';
|
||||||
|
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||||
|
|
||||||
|
type useRecordBoardScopedStates = {
|
||||||
|
recordBoardScopeId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useRecordBoardScopedStates = (
|
||||||
|
args?: useRecordBoardScopedStates,
|
||||||
|
) => {
|
||||||
|
const { recordBoardScopeId } = args ?? {};
|
||||||
|
|
||||||
|
const scopeId = useAvailableScopeIdOrThrow(
|
||||||
|
RecordBoardScopeInternalContext,
|
||||||
|
recordBoardScopeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
activeCardIdsState,
|
||||||
|
availableBoardCardFieldsState,
|
||||||
|
boardColumnsState,
|
||||||
|
isBoardLoadedState,
|
||||||
|
isCompactViewEnabledState,
|
||||||
|
savedBoardColumnsState,
|
||||||
|
boardFiltersState,
|
||||||
|
boardSortsState,
|
||||||
|
onFieldsChangeState,
|
||||||
|
boardCardFieldsByKeySelector,
|
||||||
|
hiddenBoardCardFieldsSelector,
|
||||||
|
selectedCardIdsSelector,
|
||||||
|
visibleBoardCardFieldsSelector,
|
||||||
|
savedCompaniesState,
|
||||||
|
savedOpportunitiesState,
|
||||||
|
savedPipelineStepsState,
|
||||||
|
} = getRecordBoardScopedStates({
|
||||||
|
recordBoardScopeId: scopeId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
scopeId,
|
||||||
|
activeCardIdsState,
|
||||||
|
availableBoardCardFieldsState,
|
||||||
|
boardColumnsState,
|
||||||
|
isBoardLoadedState,
|
||||||
|
isCompactViewEnabledState,
|
||||||
|
savedBoardColumnsState,
|
||||||
|
boardFiltersState,
|
||||||
|
boardSortsState,
|
||||||
|
onFieldsChangeState,
|
||||||
|
boardCardFieldsByKeySelector,
|
||||||
|
hiddenBoardCardFieldsSelector,
|
||||||
|
selectedCardIdsSelector,
|
||||||
|
visibleBoardCardFieldsSelector,
|
||||||
|
savedCompaniesState,
|
||||||
|
savedOpportunitiesState,
|
||||||
|
savedPipelineStepsState,
|
||||||
|
};
|
||||||
|
};
|
@ -1,11 +1,14 @@
|
|||||||
// 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
|
// 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 { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
import { boardCardIdsByColumnIdFamilyState } from '../states/boardCardIdsByColumnIdFamilyState';
|
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
import { boardColumnsState } from '../states/boardColumnsState';
|
|
||||||
|
|
||||||
export const useRemoveCardIds = () =>
|
import { recordBoardCardIdsByColumnIdFamilyState } from '../../states/recordBoardCardIdsByColumnIdFamilyState';
|
||||||
useRecoilCallback(
|
|
||||||
|
export const useRemoveRecordBoardCardIdsInternal = () => {
|
||||||
|
const { boardColumnsState } = useRecordBoardScopedStates();
|
||||||
|
|
||||||
|
return useRecoilCallback(
|
||||||
({ snapshot, set }) =>
|
({ snapshot, set }) =>
|
||||||
(cardIdToRemove: string[]) => {
|
(cardIdToRemove: string[]) => {
|
||||||
const boardColumns = snapshot
|
const boardColumns = snapshot
|
||||||
@ -14,13 +17,16 @@ export const useRemoveCardIds = () =>
|
|||||||
|
|
||||||
boardColumns.forEach((boardColumn) => {
|
boardColumns.forEach((boardColumn) => {
|
||||||
const columnCardIds = snapshot
|
const columnCardIds = snapshot
|
||||||
.getLoadable(boardCardIdsByColumnIdFamilyState(boardColumn.id))
|
.getLoadable(
|
||||||
|
recordBoardCardIdsByColumnIdFamilyState(boardColumn.id),
|
||||||
|
)
|
||||||
.valueOrThrow();
|
.valueOrThrow();
|
||||||
set(
|
set(
|
||||||
boardCardIdsByColumnIdFamilyState(boardColumn.id),
|
recordBoardCardIdsByColumnIdFamilyState(boardColumn.id),
|
||||||
columnCardIds.filter((cardId) => !cardIdToRemove.includes(cardId)),
|
columnCardIds.filter((cardId) => !cardIdToRemove.includes(cardId)),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[],
|
[boardColumnsState],
|
||||||
);
|
);
|
||||||
|
};
|
@ -1,17 +1,27 @@
|
|||||||
import { useRecoilCallback } from 'recoil';
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
import { actionBarOpenState } from '@/ui/navigation/action-bar/states/actionBarIsOpenState';
|
import { actionBarOpenState } from '@/ui/navigation/action-bar/states/actionBarIsOpenState';
|
||||||
|
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
|
import { RecordBoardScopeInternalContext } from '@/ui/object/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext';
|
||||||
|
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||||
|
|
||||||
import { activeCardIdsState } from '../states/activeCardIdsState';
|
import { isRecordBoardCardSelectedFamilyState } from '../../states/isRecordBoardCardSelectedFamilyState';
|
||||||
import { isCardSelectedFamilyState } from '../states/isCardSelectedFamilyState';
|
|
||||||
|
export const useSetRecordBoardCardSelectedInternal = (props: any) => {
|
||||||
|
const scopeId = useAvailableScopeIdOrThrow(
|
||||||
|
RecordBoardScopeInternalContext,
|
||||||
|
props?.recordBoardScopeId,
|
||||||
|
);
|
||||||
|
const { activeCardIdsState } = useRecordBoardScopedStates({
|
||||||
|
recordBoardScopeId: scopeId,
|
||||||
|
});
|
||||||
|
|
||||||
export const useSetCardSelected = () => {
|
|
||||||
const setCardSelected = useRecoilCallback(
|
const setCardSelected = useRecoilCallback(
|
||||||
({ set, snapshot }) =>
|
({ set, snapshot }) =>
|
||||||
(cardId: string, selected: boolean) => {
|
(cardId: string, selected: boolean) => {
|
||||||
const activeCardIds = snapshot.getLoadable(activeCardIdsState).contents;
|
const activeCardIds = snapshot.getLoadable(activeCardIdsState).contents;
|
||||||
|
|
||||||
set(isCardSelectedFamilyState(cardId), selected);
|
set(isRecordBoardCardSelectedFamilyState(cardId), selected);
|
||||||
set(actionBarOpenState, selected || activeCardIds.length > 0);
|
set(actionBarOpenState, selected || activeCardIds.length > 0);
|
||||||
|
|
||||||
if (selected) {
|
if (selected) {
|
||||||
@ -23,6 +33,7 @@ export const useSetCardSelected = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
[activeCardIdsState],
|
||||||
);
|
);
|
||||||
|
|
||||||
const unselectAllActiveCards = useRecoilCallback(
|
const unselectAllActiveCards = useRecoilCallback(
|
||||||
@ -31,13 +42,13 @@ export const useSetCardSelected = () => {
|
|||||||
const activeCardIds = snapshot.getLoadable(activeCardIdsState).contents;
|
const activeCardIds = snapshot.getLoadable(activeCardIdsState).contents;
|
||||||
|
|
||||||
activeCardIds.forEach((cardId: string) => {
|
activeCardIds.forEach((cardId: string) => {
|
||||||
set(isCardSelectedFamilyState(cardId), false);
|
set(isRecordBoardCardSelectedFamilyState(cardId), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
set(activeCardIdsState, []);
|
set(activeCardIdsState, []);
|
||||||
set(actionBarOpenState, false);
|
set(actionBarOpenState, false);
|
||||||
},
|
},
|
||||||
[],
|
[activeCardIdsState],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
@ -0,0 +1,32 @@
|
|||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||||
|
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
|
import { recordBoardCardIdsByColumnIdFamilyState } from '@/ui/object/record-board/states/recordBoardCardIdsByColumnIdFamilyState';
|
||||||
|
|
||||||
|
export const useUpdateCompanyBoardCardIdsInternal = () => {
|
||||||
|
const { boardColumnsState } = useRecordBoardScopedStates();
|
||||||
|
|
||||||
|
return useRecoilCallback(
|
||||||
|
({ snapshot, set }) =>
|
||||||
|
(pipelineProgresses: Pick<Opportunity, 'pipelineStepId' | 'id'>[]) => {
|
||||||
|
const boardColumns = snapshot
|
||||||
|
.getLoadable(boardColumnsState)
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
for (const boardColumn of boardColumns) {
|
||||||
|
const boardCardIds = pipelineProgresses
|
||||||
|
.filter((pipelineProgressToFilter) => {
|
||||||
|
return pipelineProgressToFilter.pipelineStepId === boardColumn.id;
|
||||||
|
})
|
||||||
|
.map((pipelineProgress) => pipelineProgress.id);
|
||||||
|
|
||||||
|
set(
|
||||||
|
recordBoardCardIdsByColumnIdFamilyState(boardColumn.id),
|
||||||
|
boardCardIds,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[boardColumnsState],
|
||||||
|
);
|
||||||
|
};
|
@ -4,19 +4,24 @@ import { currentPipelineStepsState } from '@/pipeline/states/currentPipelineStep
|
|||||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||||
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
||||||
import { entityFieldsFamilyState } from '@/ui/object/field/states/entityFieldsFamilyState';
|
import { entityFieldsFamilyState } from '@/ui/object/field/states/entityFieldsFamilyState';
|
||||||
import { boardCardIdsByColumnIdFamilyState } from '@/ui/object/record-board/states/boardCardIdsByColumnIdFamilyState';
|
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
import { boardColumnsState } from '@/ui/object/record-board/states/boardColumnsState';
|
import { recordBoardCardIdsByColumnIdFamilyState } from '@/ui/object/record-board/states/recordBoardCardIdsByColumnIdFamilyState';
|
||||||
import { savedBoardColumnsState } from '@/ui/object/record-board/states/savedBoardColumnsState';
|
|
||||||
import { BoardColumnDefinition } from '@/ui/object/record-board/types/BoardColumnDefinition';
|
import { BoardColumnDefinition } from '@/ui/object/record-board/types/BoardColumnDefinition';
|
||||||
import { themeColorSchema } from '@/ui/theme/utils/themeColorSchema';
|
import { themeColorSchema } from '@/ui/theme/utils/themeColorSchema';
|
||||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||||
import { logError } from '~/utils/logError';
|
import { logError } from '~/utils/logError';
|
||||||
|
|
||||||
import { companyProgressesFamilyState } from '../states/companyProgressesFamilyState';
|
import { companyProgressesFamilyState } from '../../../../../companies/states/companyProgressesFamilyState';
|
||||||
import { CompanyForBoard, CompanyProgressDict } from '../types/CompanyProgress';
|
import {
|
||||||
|
CompanyForBoard,
|
||||||
|
CompanyProgressDict,
|
||||||
|
} from '../../../../../companies/types/CompanyProgress';
|
||||||
|
|
||||||
export const useUpdateCompanyBoard = () =>
|
export const useUpdateCompanyBoardColumnsInternal = () => {
|
||||||
useRecoilCallback(
|
const { boardColumnsState, savedBoardColumnsState } =
|
||||||
|
useRecordBoardScopedStates();
|
||||||
|
|
||||||
|
return useRecoilCallback(
|
||||||
({ set, snapshot }) =>
|
({ set, snapshot }) =>
|
||||||
(
|
(
|
||||||
pipelineSteps: PipelineStep[],
|
pipelineSteps: PipelineStep[],
|
||||||
@ -125,16 +130,19 @@ export const useUpdateCompanyBoard = () =>
|
|||||||
.map((opportunity) => opportunity.id);
|
.map((opportunity) => opportunity.id);
|
||||||
|
|
||||||
const currentBoardCardIds = snapshot
|
const currentBoardCardIds = snapshot
|
||||||
.getLoadable(boardCardIdsByColumnIdFamilyState(boardColumn.id))
|
.getLoadable(
|
||||||
|
recordBoardCardIdsByColumnIdFamilyState(boardColumn.id),
|
||||||
|
)
|
||||||
.valueOrThrow();
|
.valueOrThrow();
|
||||||
|
|
||||||
if (!isDeeplyEqual(currentBoardCardIds, boardCardIds)) {
|
if (!isDeeplyEqual(currentBoardCardIds, boardCardIds)) {
|
||||||
set(
|
set(
|
||||||
boardCardIdsByColumnIdFamilyState(boardColumn.id),
|
recordBoardCardIdsByColumnIdFamilyState(boardColumn.id),
|
||||||
boardCardIds,
|
boardCardIds,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[],
|
[boardColumnsState, savedBoardColumnsState],
|
||||||
);
|
);
|
||||||
|
};
|
@ -1,12 +1,30 @@
|
|||||||
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 { 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 { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
import { boardCardIdsByColumnIdFamilyState } from '../states/boardCardIdsByColumnIdFamilyState';
|
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
import { boardColumnsState } from '../states/boardColumnsState';
|
import { RecordBoardScopeInternalContext } from '@/ui/object/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext';
|
||||||
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||||
|
|
||||||
export const useUpdateBoardCardIds = () =>
|
import { recordBoardCardIdsByColumnIdFamilyState } from '../../states/recordBoardCardIdsByColumnIdFamilyState';
|
||||||
useRecoilCallback(
|
import { BoardColumnDefinition } from '../../types/BoardColumnDefinition';
|
||||||
|
|
||||||
|
type useUpdateRecordBoardCardIdsInternalProps = {
|
||||||
|
recordBoardScopeId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateRecordBoardCardIdsInternal = (
|
||||||
|
props: useUpdateRecordBoardCardIdsInternalProps,
|
||||||
|
) => {
|
||||||
|
const scopeId = useAvailableScopeIdOrThrow(
|
||||||
|
RecordBoardScopeInternalContext,
|
||||||
|
props?.recordBoardScopeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { boardColumnsState } = useRecordBoardScopedStates({
|
||||||
|
recordBoardScopeId: scopeId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return useRecoilCallback(
|
||||||
({ snapshot, set }) =>
|
({ snapshot, set }) =>
|
||||||
(result: DropResult) => {
|
(result: DropResult) => {
|
||||||
const currentBoardColumns = snapshot
|
const currentBoardColumns = snapshot
|
||||||
@ -37,14 +55,16 @@ export const useUpdateBoardCardIds = () =>
|
|||||||
|
|
||||||
const sourceCardIds = [
|
const sourceCardIds = [
|
||||||
...snapshot
|
...snapshot
|
||||||
.getLoadable(boardCardIdsByColumnIdFamilyState(sourceColumn.id))
|
.getLoadable(
|
||||||
|
recordBoardCardIdsByColumnIdFamilyState(sourceColumn.id),
|
||||||
|
)
|
||||||
.valueOrThrow(),
|
.valueOrThrow(),
|
||||||
];
|
];
|
||||||
|
|
||||||
const destinationCardIds = [
|
const destinationCardIds = [
|
||||||
...snapshot
|
...snapshot
|
||||||
.getLoadable(
|
.getLoadable(
|
||||||
boardCardIdsByColumnIdFamilyState(destinationColumn.id),
|
recordBoardCardIdsByColumnIdFamilyState(destinationColumn.id),
|
||||||
)
|
)
|
||||||
.valueOrThrow(),
|
.valueOrThrow(),
|
||||||
];
|
];
|
||||||
@ -60,7 +80,7 @@ export const useUpdateBoardCardIds = () =>
|
|||||||
sourceCardIds.splice(destinationIndex, 0, deletedCardId);
|
sourceCardIds.splice(destinationIndex, 0, deletedCardId);
|
||||||
|
|
||||||
set(
|
set(
|
||||||
boardCardIdsByColumnIdFamilyState(sourceColumn.id),
|
recordBoardCardIdsByColumnIdFamilyState(sourceColumn.id),
|
||||||
sourceCardIds,
|
sourceCardIds,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -69,17 +89,18 @@ export const useUpdateBoardCardIds = () =>
|
|||||||
destinationCardIds.splice(destinationIndex, 0, removedCardId);
|
destinationCardIds.splice(destinationIndex, 0, removedCardId);
|
||||||
|
|
||||||
set(
|
set(
|
||||||
boardCardIdsByColumnIdFamilyState(sourceColumn.id),
|
recordBoardCardIdsByColumnIdFamilyState(sourceColumn.id),
|
||||||
sourceCardIds,
|
sourceCardIds,
|
||||||
);
|
);
|
||||||
|
|
||||||
set(
|
set(
|
||||||
boardCardIdsByColumnIdFamilyState(destinationColumn.id),
|
recordBoardCardIdsByColumnIdFamilyState(destinationColumn.id),
|
||||||
destinationCardIds,
|
destinationCardIds,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newBoardColumns;
|
return newBoardColumns;
|
||||||
},
|
},
|
||||||
[],
|
[boardColumnsState],
|
||||||
);
|
);
|
||||||
|
};
|
@ -1,61 +0,0 @@
|
|||||||
import { useCallback } from 'react';
|
|
||||||
|
|
||||||
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
|
||||||
import { savedBoardCardFieldsFamilyState } from '@/ui/object/record-board/states/savedBoardCardFieldsFamilyState';
|
|
||||||
import { BoardFieldDefinition } from '@/ui/object/record-board/types/BoardFieldDefinition';
|
|
||||||
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
|
|
||||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
|
||||||
|
|
||||||
import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState';
|
|
||||||
|
|
||||||
import { useBoardContext } from './useBoardContext';
|
|
||||||
|
|
||||||
export const useBoardCardFields = () => {
|
|
||||||
const { BoardRecoilScopeContext, onFieldsChange } = useBoardContext();
|
|
||||||
|
|
||||||
const [, setBoardCardFields] = useRecoilScopedState(
|
|
||||||
boardCardFieldsScopedState,
|
|
||||||
BoardRecoilScopeContext,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [, setSavedBoardCardFields] = useRecoilScopedState(
|
|
||||||
savedBoardCardFieldsFamilyState,
|
|
||||||
BoardRecoilScopeContext,
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleFieldVisibilityChange = (
|
|
||||||
field: Omit<ColumnDefinition<FieldMetadata>, 'size' | 'position'>,
|
|
||||||
) => {
|
|
||||||
setBoardCardFields((previousFields) =>
|
|
||||||
previousFields.map((previousField) =>
|
|
||||||
previousField.fieldMetadataId === field.fieldMetadataId
|
|
||||||
? { ...previousField, isVisible: !field.isVisible }
|
|
||||||
: previousField,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFieldsChange = useCallback(
|
|
||||||
async (fields: BoardFieldDefinition<FieldMetadata>[]) => {
|
|
||||||
setSavedBoardCardFields(fields);
|
|
||||||
setBoardCardFields(fields);
|
|
||||||
|
|
||||||
await onFieldsChange?.(fields);
|
|
||||||
},
|
|
||||||
[setBoardCardFields, setSavedBoardCardFields, onFieldsChange],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleFieldsReorder = useCallback(
|
|
||||||
async (fields: BoardFieldDefinition<FieldMetadata>[]) => {
|
|
||||||
const updatedFields = fields.map((column, index) => ({
|
|
||||||
...column,
|
|
||||||
position: index,
|
|
||||||
}));
|
|
||||||
|
|
||||||
await handleFieldsChange(updatedFields);
|
|
||||||
},
|
|
||||||
[handleFieldsChange],
|
|
||||||
);
|
|
||||||
|
|
||||||
return { handleFieldVisibilityChange, handleFieldsReorder };
|
|
||||||
};
|
|
@ -1,7 +0,0 @@
|
|||||||
import { useContext } from 'react';
|
|
||||||
|
|
||||||
import { BoardContext } from '@/companies/states/contexts/BoardContext';
|
|
||||||
|
|
||||||
export const useBoardContext = () => {
|
|
||||||
return useContext(BoardContext);
|
|
||||||
};
|
|
@ -0,0 +1,37 @@
|
|||||||
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { useCreateOpportunity } from '@/ui/object/record-board/hooks/internal/useCreateOpportunity';
|
||||||
|
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
|
import { RecordBoardScopeInternalContext } from '@/ui/object/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext';
|
||||||
|
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||||
|
|
||||||
|
type useRecordBoardProps = {
|
||||||
|
recordBoardScopeId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useRecordBoard = (props?: useRecordBoardProps) => {
|
||||||
|
const scopeId = useAvailableScopeIdOrThrow(
|
||||||
|
RecordBoardScopeInternalContext,
|
||||||
|
props?.recordBoardScopeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { isBoardLoadedState, boardColumnsState, onFieldsChangeState } =
|
||||||
|
useRecordBoardScopedStates({
|
||||||
|
recordBoardScopeId: scopeId,
|
||||||
|
});
|
||||||
|
const setIsBoardLoaded = useSetRecoilState(isBoardLoadedState);
|
||||||
|
|
||||||
|
const setBoardColumns = useSetRecoilState(boardColumnsState);
|
||||||
|
|
||||||
|
const createOpportunity = useCreateOpportunity();
|
||||||
|
|
||||||
|
const setOnFieldsChange = useSetRecoilState(onFieldsChangeState);
|
||||||
|
|
||||||
|
return {
|
||||||
|
scopeId,
|
||||||
|
setIsBoardLoaded,
|
||||||
|
setBoardColumns,
|
||||||
|
createOpportunity,
|
||||||
|
setOnFieldsChange,
|
||||||
|
};
|
||||||
|
};
|
@ -5,28 +5,32 @@ import { DropdownScope } from '../../../../layout/dropdown/scopes/DropdownScope'
|
|||||||
import { BoardOptionsDropdownId } from '../../components/constants/BoardOptionsDropdownId';
|
import { BoardOptionsDropdownId } from '../../components/constants/BoardOptionsDropdownId';
|
||||||
import { BoardOptionsHotkeyScope } from '../../types/BoardOptionsHotkeyScope';
|
import { BoardOptionsHotkeyScope } from '../../types/BoardOptionsHotkeyScope';
|
||||||
|
|
||||||
import { BoardOptionsDropdownButton } from './BoardOptionsDropdownButton';
|
import { RecordBoardOptionsDropdownButton } from './RecordBoardOptionsDropdownButton';
|
||||||
import {
|
import {
|
||||||
BoardOptionsDropdownContent,
|
RecordBoardOptionsDropdownContent,
|
||||||
BoardOptionsDropdownContentProps,
|
RecordBoardOptionsDropdownContentProps,
|
||||||
} from './BoardOptionsDropdownContent';
|
} from './RecordBoardOptionsDropdownContent';
|
||||||
|
|
||||||
type BoardOptionsDropdownProps = Pick<
|
type RecordBoardOptionsDropdownProps = Pick<
|
||||||
BoardOptionsDropdownContentProps,
|
RecordBoardOptionsDropdownContentProps,
|
||||||
'onStageAdd'
|
'onStageAdd' | 'recordBoardId'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export const BoardOptionsDropdown = ({
|
export const RecordBoardOptionsDropdown = ({
|
||||||
onStageAdd,
|
onStageAdd,
|
||||||
}: BoardOptionsDropdownProps) => {
|
recordBoardId,
|
||||||
|
}: RecordBoardOptionsDropdownProps) => {
|
||||||
const { setViewEditMode } = useViewBar();
|
const { setViewEditMode } = useViewBar();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownScope dropdownScopeId={BoardOptionsDropdownId}>
|
<DropdownScope dropdownScopeId={BoardOptionsDropdownId}>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
clickableComponent={<BoardOptionsDropdownButton />}
|
clickableComponent={<RecordBoardOptionsDropdownButton />}
|
||||||
dropdownComponents={
|
dropdownComponents={
|
||||||
<BoardOptionsDropdownContent onStageAdd={onStageAdd} />
|
<RecordBoardOptionsDropdownContent
|
||||||
|
onStageAdd={onStageAdd}
|
||||||
|
recordBoardId={recordBoardId}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
dropdownHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
|
dropdownHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
|
||||||
onClickOutside={() => setViewEditMode('none')}
|
onClickOutside={() => setViewEditMode('none')}
|
@ -1,7 +1,7 @@
|
|||||||
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
||||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||||
|
|
||||||
export const BoardOptionsDropdownButton = () => {
|
export const RecordBoardOptionsDropdownButton = () => {
|
||||||
const { isDropdownOpen, toggleDropdown } = useDropdown();
|
const { isDropdownOpen, toggleDropdown } = useDropdown();
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
@ -1,10 +1,9 @@
|
|||||||
import { useCallback, useContext, useRef, useState } from 'react';
|
import { useCallback, useRef, useState } from 'react';
|
||||||
import { OnDragEndResponder } from '@hello-pangea/dnd';
|
import { OnDragEndResponder } from '@hello-pangea/dnd';
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
import { BoardContext } from '@/companies/states/contexts/BoardContext';
|
|
||||||
import {
|
import {
|
||||||
IconBaselineDensitySmall,
|
IconBaselineDensitySmall,
|
||||||
IconChevronLeft,
|
IconChevronLeft,
|
||||||
@ -21,23 +20,20 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
|||||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||||
import { MenuItemNavigate } from '@/ui/navigation/menu-item/components/MenuItemNavigate';
|
import { MenuItemNavigate } from '@/ui/navigation/menu-item/components/MenuItemNavigate';
|
||||||
import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemToggle';
|
import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemToggle';
|
||||||
|
import { useRecordBoardScopedStates } from '@/ui/object/record-board/hooks/internal/useRecordBoardScopedStates';
|
||||||
import { ThemeColor } from '@/ui/theme/constants/colors';
|
import { ThemeColor } from '@/ui/theme/constants/colors';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
|
||||||
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
|
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
|
||||||
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
|
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
|
||||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||||
|
|
||||||
import { useBoardCardFields } from '../../hooks/useBoardCardFields';
|
import { useRecordBoardCardFieldsInternal } from '../../hooks/internal/useRecordBoardCardFieldsInternal';
|
||||||
import { boardColumnsState } from '../../states/boardColumnsState';
|
|
||||||
import { isCompactViewEnabledState } from '../../states/isCompactViewEnabledState';
|
|
||||||
import { hiddenBoardCardFieldsScopedSelector } from '../../states/selectors/hiddenBoardCardFieldsScopedSelector';
|
|
||||||
import { visibleBoardCardFieldsScopedSelector } from '../../states/selectors/visibleBoardCardFieldsScopedSelector';
|
|
||||||
import { BoardColumnDefinition } from '../../types/BoardColumnDefinition';
|
import { BoardColumnDefinition } from '../../types/BoardColumnDefinition';
|
||||||
import { BoardOptionsHotkeyScope } from '../../types/BoardOptionsHotkeyScope';
|
import { BoardOptionsHotkeyScope } from '../../types/BoardOptionsHotkeyScope';
|
||||||
|
|
||||||
export type BoardOptionsDropdownContentProps = {
|
export type RecordBoardOptionsDropdownContentProps = {
|
||||||
onStageAdd?: (boardColumn: BoardColumnDefinition) => void;
|
onStageAdd?: (boardColumn: BoardColumnDefinition) => void;
|
||||||
|
recordBoardId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type BoardOptionsMenu = 'fields' | 'stage-creation' | 'stages';
|
type BoardOptionsMenu = 'fields' | 'stage-creation' | 'stages';
|
||||||
@ -49,12 +45,12 @@ type ColumnForCreate = {
|
|||||||
title: string;
|
title: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BoardOptionsDropdownContent = ({
|
export const RecordBoardOptionsDropdownContent = ({
|
||||||
onStageAdd,
|
onStageAdd,
|
||||||
}: BoardOptionsDropdownContentProps) => {
|
recordBoardId,
|
||||||
|
}: RecordBoardOptionsDropdownContentProps) => {
|
||||||
const { setViewEditMode, handleViewNameSubmit } = useViewBar();
|
const { setViewEditMode, handleViewNameSubmit } = useViewBar();
|
||||||
const { viewEditModeState, currentViewSelector } = useViewScopedStates();
|
const { viewEditModeState, currentViewSelector } = useViewScopedStates();
|
||||||
const { BoardRecoilScopeContext } = useContext(BoardContext);
|
|
||||||
|
|
||||||
const viewEditMode = useRecoilValue(viewEditModeState);
|
const viewEditMode = useRecoilValue(viewEditModeState);
|
||||||
const currentView = useRecoilValue(currentViewSelector);
|
const currentView = useRecoilValue(currentViewSelector);
|
||||||
@ -66,21 +62,22 @@ export const BoardOptionsDropdownContent = ({
|
|||||||
BoardOptionsMenu | undefined
|
BoardOptionsMenu | undefined
|
||||||
>();
|
>();
|
||||||
|
|
||||||
|
const {
|
||||||
|
boardColumnsState,
|
||||||
|
isCompactViewEnabledState,
|
||||||
|
hiddenBoardCardFieldsSelector,
|
||||||
|
visibleBoardCardFieldsSelector,
|
||||||
|
} = useRecordBoardScopedStates({ recordBoardScopeId: recordBoardId });
|
||||||
|
|
||||||
const [boardColumns, setBoardColumns] = useRecoilState(boardColumnsState);
|
const [boardColumns, setBoardColumns] = useRecoilState(boardColumnsState);
|
||||||
const [isCompactViewEnabled, setIsCompactViewEnabled] = useRecoilState(
|
const [isCompactViewEnabled, setIsCompactViewEnabled] = useRecoilState(
|
||||||
isCompactViewEnabledState,
|
isCompactViewEnabledState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const hiddenBoardCardFields = useRecoilScopedValue(
|
const hiddenBoardCardFields = useRecoilValue(hiddenBoardCardFieldsSelector);
|
||||||
hiddenBoardCardFieldsScopedSelector,
|
|
||||||
BoardRecoilScopeContext,
|
|
||||||
);
|
|
||||||
|
|
||||||
const hasHiddenFields = hiddenBoardCardFields.length > 0;
|
const hasHiddenFields = hiddenBoardCardFields.length > 0;
|
||||||
const visibleBoardCardFields = useRecoilScopedValue(
|
|
||||||
visibleBoardCardFieldsScopedSelector,
|
const visibleBoardCardFields = useRecoilValue(visibleBoardCardFieldsSelector);
|
||||||
BoardRecoilScopeContext,
|
|
||||||
);
|
|
||||||
const hasVisibleFields = visibleBoardCardFields.length > 0;
|
const hasVisibleFields = visibleBoardCardFields.length > 0;
|
||||||
|
|
||||||
const handleStageSubmit = () => {
|
const handleStageSubmit = () => {
|
||||||
@ -109,7 +106,9 @@ export const BoardOptionsDropdownContent = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { handleFieldVisibilityChange, handleFieldsReorder } =
|
const { handleFieldVisibilityChange, handleFieldsReorder } =
|
||||||
useBoardCardFields();
|
useRecordBoardCardFieldsInternal({
|
||||||
|
recordBoardScopeId: recordBoardId,
|
||||||
|
});
|
||||||
|
|
||||||
const { closeDropdown } = useDropdown();
|
const { closeDropdown } = useDropdown();
|
||||||
|
|
@ -0,0 +1,23 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
import { RecordBoardScopeInternalContext } from '@/ui/object/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext';
|
||||||
|
|
||||||
|
type RecordBoardScopeProps = {
|
||||||
|
children: ReactNode;
|
||||||
|
recordBoardScopeId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RecordBoardScope = ({
|
||||||
|
children,
|
||||||
|
recordBoardScopeId,
|
||||||
|
}: RecordBoardScopeProps) => {
|
||||||
|
return (
|
||||||
|
<RecordBoardScopeInternalContext.Provider
|
||||||
|
value={{
|
||||||
|
scopeId: recordBoardScopeId,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</RecordBoardScopeInternalContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,7 @@
|
|||||||
|
import { ScopedStateKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/ScopedStateKey';
|
||||||
|
import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
|
||||||
|
|
||||||
|
type RecordBoardScopeInternalContext = ScopedStateKey;
|
||||||
|
|
||||||
|
export const RecordBoardScopeInternalContext =
|
||||||
|
createScopeInternalContext<RecordBoardScopeInternalContext>();
|
@ -1,6 +0,0 @@
|
|||||||
import { atom } from 'recoil';
|
|
||||||
|
|
||||||
export const activeCardIdsState = atom<string[]>({
|
|
||||||
key: 'activeCardIdsState',
|
|
||||||
default: [],
|
|
||||||
});
|
|
@ -0,0 +1,6 @@
|
|||||||
|
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||||
|
|
||||||
|
export const activeRecordBoardCardIdsScopedState = createScopedState<string[]>({
|
||||||
|
key: 'activeRecordBoardCardIdsScopedState',
|
||||||
|
defaultValue: [],
|
||||||
|
});
|
@ -0,0 +1,11 @@
|
|||||||
|
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
||||||
|
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||||
|
|
||||||
|
import { BoardFieldDefinition } from '../types/BoardFieldDefinition';
|
||||||
|
|
||||||
|
export const availableRecordBoardCardFieldsScopedState = createScopedState<
|
||||||
|
BoardFieldDefinition<FieldMetadata>[]
|
||||||
|
>({
|
||||||
|
key: 'availableRecordBoardCardFieldsScopedState',
|
||||||
|
defaultValue: [],
|
||||||
|
});
|
@ -1,13 +0,0 @@
|
|||||||
import { atomFamily } from 'recoil';
|
|
||||||
|
|
||||||
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
|
||||||
|
|
||||||
import { BoardFieldDefinition } from '../types/BoardFieldDefinition';
|
|
||||||
|
|
||||||
export const boardCardFieldsScopedState = atomFamily<
|
|
||||||
BoardFieldDefinition<FieldMetadata>[],
|
|
||||||
string
|
|
||||||
>({
|
|
||||||
key: 'boardCardFieldsScopedState',
|
|
||||||
default: [],
|
|
||||||
});
|
|
@ -1,6 +0,0 @@
|
|||||||
import { atomFamily } from 'recoil';
|
|
||||||
|
|
||||||
export const boardCardIdsByColumnIdFamilyState = atomFamily<string[], string>({
|
|
||||||
key: 'boardCardIdsByColumnIdFamilyState',
|
|
||||||
default: [],
|
|
||||||
});
|
|
@ -1,8 +0,0 @@
|
|||||||
import { atom } from 'recoil';
|
|
||||||
|
|
||||||
import { BoardColumnDefinition } from '@/ui/object/record-board/types/BoardColumnDefinition';
|
|
||||||
|
|
||||||
export const boardColumnsState = atom<BoardColumnDefinition[]>({
|
|
||||||
key: 'boardColumnsState',
|
|
||||||
default: [],
|
|
||||||
});
|
|
@ -1,6 +0,0 @@
|
|||||||
import { atom } from 'recoil';
|
|
||||||
|
|
||||||
export const isBoardLoadedState = atom<boolean>({
|
|
||||||
key: 'isBoardLoadedState',
|
|
||||||
default: false,
|
|
||||||
});
|
|
@ -1,6 +0,0 @@
|
|||||||
import { atomFamily } from 'recoil';
|
|
||||||
|
|
||||||
export const isCardInCompactViewState = atomFamily<boolean, string>({
|
|
||||||
key: 'isCardInCompactViewState',
|
|
||||||
default: true,
|
|
||||||
});
|
|
@ -1,6 +0,0 @@
|
|||||||
import { atomFamily } from 'recoil';
|
|
||||||
|
|
||||||
export const isCardSelectedFamilyState = atomFamily<boolean, string>({
|
|
||||||
key: 'isCardSelectedFamilyState',
|
|
||||||
default: false,
|
|
||||||
});
|
|
@ -0,0 +1,6 @@
|
|||||||
|
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||||
|
|
||||||
|
export const isCompactViewEnabledScopedState = createScopedState<boolean>({
|
||||||
|
key: 'isCompactViewEnabledScopedState',
|
||||||
|
defaultValue: false,
|
||||||
|
});
|
@ -1,6 +0,0 @@
|
|||||||
import { atom } from 'recoil';
|
|
||||||
|
|
||||||
export const isCompactViewEnabledState = atom<boolean>({
|
|
||||||
key: 'isCompactViewEnabledState',
|
|
||||||
default: false,
|
|
||||||
});
|
|
@ -0,0 +1,9 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const isRecordBoardCardInCompactViewFamilyState = atomFamily<
|
||||||
|
boolean,
|
||||||
|
string
|
||||||
|
>({
|
||||||
|
key: 'isRecordBoardCardInCompactViewFamilyState',
|
||||||
|
default: true,
|
||||||
|
});
|
@ -0,0 +1,8 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const isRecordBoardCardSelectedFamilyState = atomFamily<boolean, string>(
|
||||||
|
{
|
||||||
|
key: 'isRecordBoardCardSelectedFamilyState',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
);
|
@ -0,0 +1,6 @@
|
|||||||
|
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||||
|
|
||||||
|
export const isRecordBoardLoadedScopedState = createScopedState<boolean>({
|
||||||
|
key: 'isRecordBoardLoadedScopedState',
|
||||||
|
defaultValue: false,
|
||||||
|
});
|
@ -0,0 +1,10 @@
|
|||||||
|
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
||||||
|
import { BoardFieldDefinition } from '@/ui/object/record-board/types/BoardFieldDefinition';
|
||||||
|
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||||
|
|
||||||
|
export const onFieldsChangeScopedState = createScopedState<
|
||||||
|
(fields: BoardFieldDefinition<FieldMetadata>[]) => void
|
||||||
|
>({
|
||||||
|
key: 'onFieldsChangeScopedState',
|
||||||
|
defaultValue: () => {},
|
||||||
|
});
|
@ -1,3 +0,0 @@
|
|||||||
import { createContext } from 'react';
|
|
||||||
|
|
||||||
export const BoardColumnRecoilScopeContext = createContext<string | null>(null);
|
|
@ -3,9 +3,9 @@ import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScope
|
|||||||
|
|
||||||
import { BoardFieldDefinition } from '../types/BoardFieldDefinition';
|
import { BoardFieldDefinition } from '../types/BoardFieldDefinition';
|
||||||
|
|
||||||
export const availableBoardCardFieldsScopedState = createScopedState<
|
export const recordBoardCardFieldsScopedState = createScopedState<
|
||||||
BoardFieldDefinition<FieldMetadata>[]
|
BoardFieldDefinition<FieldMetadata>[]
|
||||||
>({
|
>({
|
||||||
key: 'availableBoardCardFieldsScopedState',
|
key: 'recordBoardCardFieldsScopedState',
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
});
|
});
|
@ -0,0 +1,9 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const recordBoardCardIdsByColumnIdFamilyState = atomFamily<
|
||||||
|
string[],
|
||||||
|
string
|
||||||
|
>({
|
||||||
|
key: 'recordBoardCardIdsByColumnIdFamilyState',
|
||||||
|
default: [],
|
||||||
|
});
|
@ -0,0 +1,9 @@
|
|||||||
|
import { BoardColumnDefinition } from '@/ui/object/record-board/types/BoardColumnDefinition';
|
||||||
|
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||||
|
|
||||||
|
export const recordBoardColumnsScopedState = createScopedState<
|
||||||
|
BoardColumnDefinition[]
|
||||||
|
>({
|
||||||
|
key: 'recordBoardColumnsScopedState',
|
||||||
|
defaultValue: [],
|
||||||
|
});
|
@ -0,0 +1,7 @@
|
|||||||
|
import { Filter } from '@/ui/object/object-filter-dropdown/types/Filter';
|
||||||
|
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||||
|
|
||||||
|
export const recordBoardFiltersScopedState = createScopedState<Filter[]>({
|
||||||
|
key: 'recordBoardFiltersScopedState',
|
||||||
|
defaultValue: [],
|
||||||
|
});
|
@ -0,0 +1,8 @@
|
|||||||
|
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||||
|
|
||||||
|
import { Sort } from '../../object-sort-dropdown/types/Sort';
|
||||||
|
|
||||||
|
export const recordBoardSortsScopedState = createScopedState<Sort[]>({
|
||||||
|
key: 'recordBoardSortsScopedState',
|
||||||
|
defaultValue: [],
|
||||||
|
});
|
@ -1,13 +0,0 @@
|
|||||||
import { atomFamily } from 'recoil';
|
|
||||||
|
|
||||||
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
|
||||||
|
|
||||||
import { BoardFieldDefinition } from '../types/BoardFieldDefinition';
|
|
||||||
|
|
||||||
export const savedBoardCardFieldsFamilyState = atomFamily<
|
|
||||||
BoardFieldDefinition<FieldMetadata>[],
|
|
||||||
string | undefined
|
|
||||||
>({
|
|
||||||
key: 'savedBoardCardFieldsFamilyState',
|
|
||||||
default: [],
|
|
||||||
});
|
|
@ -1,8 +0,0 @@
|
|||||||
import { atom } from 'recoil';
|
|
||||||
|
|
||||||
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
|
||||||
|
|
||||||
export const savedBoardColumnsState = atom<BoardColumnDefinition[]>({
|
|
||||||
key: 'savedBoardColumnsState',
|
|
||||||
default: [],
|
|
||||||
});
|
|
@ -0,0 +1,7 @@
|
|||||||
|
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||||
|
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||||
|
|
||||||
|
export const savedOpportunitiesScopedState = createScopedState<Opportunity[]>({
|
||||||
|
key: 'savedOpportunitiesScopedState',
|
||||||
|
defaultValue: [],
|
||||||
|
});
|
@ -0,0 +1,7 @@
|
|||||||
|
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
||||||
|
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||||
|
|
||||||
|
export const savedPipelineStepsScopedState = createScopedState<PipelineStep[]>({
|
||||||
|
key: 'savedPipelineStepsScopedState',
|
||||||
|
defaultValue: [],
|
||||||
|
});
|
@ -0,0 +1,11 @@
|
|||||||
|
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
||||||
|
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||||
|
|
||||||
|
import { BoardFieldDefinition } from '../types/BoardFieldDefinition';
|
||||||
|
|
||||||
|
export const savedRecordBoardCardFieldsScopedState = createScopedState<
|
||||||
|
BoardFieldDefinition<FieldMetadata>[]
|
||||||
|
>({
|
||||||
|
key: 'savedRecordBoardCardFieldsScopedState',
|
||||||
|
defaultValue: [],
|
||||||
|
});
|
@ -0,0 +1,10 @@
|
|||||||
|
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||||
|
|
||||||
|
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
||||||
|
|
||||||
|
export const savedRecordBoardColumnsScopedState = createScopedState<
|
||||||
|
BoardColumnDefinition[]
|
||||||
|
>({
|
||||||
|
key: 'savedRecordBoardColumnsScopedState',
|
||||||
|
defaultValue: [],
|
||||||
|
});
|
@ -0,0 +1,7 @@
|
|||||||
|
import { Company } from '@/companies/types/Company';
|
||||||
|
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||||
|
|
||||||
|
export const savedRecordsScopedState = createScopedState<Company[]>({
|
||||||
|
key: 'savedRecordsScopedState',
|
||||||
|
defaultValue: [],
|
||||||
|
});
|
@ -1,23 +0,0 @@
|
|||||||
import { selectorFamily } from 'recoil';
|
|
||||||
|
|
||||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
|
||||||
|
|
||||||
import { boardCardFieldsScopedState } from '../boardCardFieldsScopedState';
|
|
||||||
import { savedBoardCardFieldsFamilyState } from '../savedBoardCardFieldsFamilyState';
|
|
||||||
|
|
||||||
export const canPersistBoardCardFieldsScopedFamilySelector = selectorFamily({
|
|
||||||
key: 'canPersistBoardCardFieldsScopedFamilySelector',
|
|
||||||
get:
|
|
||||||
({
|
|
||||||
recoilScopeId,
|
|
||||||
viewId,
|
|
||||||
}: {
|
|
||||||
recoilScopeId: string;
|
|
||||||
viewId: string | undefined;
|
|
||||||
}) =>
|
|
||||||
({ get }) =>
|
|
||||||
!isDeeplyEqual(
|
|
||||||
get(savedBoardCardFieldsFamilyState(viewId)),
|
|
||||||
get(boardCardFieldsScopedState(recoilScopeId)),
|
|
||||||
),
|
|
||||||
});
|
|
@ -1,12 +0,0 @@
|
|||||||
import { selector } from 'recoil';
|
|
||||||
|
|
||||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
|
||||||
|
|
||||||
import { boardColumnsState } from '../boardColumnsState';
|
|
||||||
import { savedBoardColumnsState } from '../savedBoardColumnsState';
|
|
||||||
|
|
||||||
export const canPersistBoardColumnsSelector = selector<boolean>({
|
|
||||||
key: 'canPersistBoardCardFieldsScopedFamilySelector',
|
|
||||||
get: ({ get }) =>
|
|
||||||
!isDeeplyEqual(get(boardColumnsState), get(savedBoardColumnsState)),
|
|
||||||
});
|
|
@ -1,22 +0,0 @@
|
|||||||
import { selectorFamily } from 'recoil';
|
|
||||||
|
|
||||||
import { availableBoardCardFieldsScopedState } from '../availableBoardCardFieldsScopedState';
|
|
||||||
import { boardCardFieldsScopedState } from '../boardCardFieldsScopedState';
|
|
||||||
|
|
||||||
export const hiddenBoardCardFieldsScopedSelector = selectorFamily({
|
|
||||||
key: 'hiddenBoardCardFieldsScopedSelector',
|
|
||||||
get:
|
|
||||||
(scopeId: string) =>
|
|
||||||
({ get }) => {
|
|
||||||
const fields = get(boardCardFieldsScopedState(scopeId));
|
|
||||||
const fieldKeys = fields.map(({ fieldMetadataId }) => fieldMetadataId);
|
|
||||||
const otherAvailableKeys = get(
|
|
||||||
availableBoardCardFieldsScopedState({ scopeId }),
|
|
||||||
).filter(({ fieldMetadataId }) => !fieldKeys.includes(fieldMetadataId));
|
|
||||||
|
|
||||||
return [
|
|
||||||
...fields.filter((field) => !field.isVisible),
|
|
||||||
...otherAvailableKeys,
|
|
||||||
];
|
|
||||||
},
|
|
||||||
});
|
|
@ -0,0 +1,22 @@
|
|||||||
|
import { createScopedSelector } from '@/ui/utilities/recoil-scope/utils/createScopedSelector';
|
||||||
|
|
||||||
|
import { availableRecordBoardCardFieldsScopedState } from '../availableRecordBoardCardFieldsScopedState';
|
||||||
|
import { recordBoardCardFieldsScopedState } from '../recordBoardCardFieldsScopedState';
|
||||||
|
|
||||||
|
export const hiddenRecordBoardCardFieldsScopedSelector = createScopedSelector({
|
||||||
|
key: 'hiddenRecordBoardCardFieldsScopedSelector',
|
||||||
|
get:
|
||||||
|
({ scopeId }) =>
|
||||||
|
({ get }) => {
|
||||||
|
const fields = get(recordBoardCardFieldsScopedState({ scopeId }));
|
||||||
|
const fieldKeys = fields.map(({ fieldMetadataId }) => fieldMetadataId);
|
||||||
|
const otherAvailableKeys = get(
|
||||||
|
availableRecordBoardCardFieldsScopedState({ scopeId }),
|
||||||
|
).filter(({ fieldMetadataId }) => !fieldKeys.includes(fieldMetadataId));
|
||||||
|
|
||||||
|
return [
|
||||||
|
...fields.filter((field) => !field.isVisible),
|
||||||
|
...otherAvailableKeys,
|
||||||
|
];
|
||||||
|
},
|
||||||
|
});
|
@ -3,14 +3,14 @@ import { selectorFamily } from 'recoil';
|
|||||||
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
||||||
|
|
||||||
import { BoardFieldDefinition } from '../../types/BoardFieldDefinition';
|
import { BoardFieldDefinition } from '../../types/BoardFieldDefinition';
|
||||||
import { boardCardFieldsScopedState } from '../boardCardFieldsScopedState';
|
import { recordBoardCardFieldsScopedState } from '../recordBoardCardFieldsScopedState';
|
||||||
|
|
||||||
export const boardCardFieldsByKeyScopedSelector = selectorFamily({
|
export const recordBoardCardFieldsByKeyScopedSelector = selectorFamily({
|
||||||
key: 'boardCardFieldsByKeyScopedSelector',
|
key: 'recordBoardCardFieldsByKeyScopedSelector',
|
||||||
get:
|
get:
|
||||||
(scopeId: string) =>
|
(scopeId: string) =>
|
||||||
({ get }) =>
|
({ get }) =>
|
||||||
get(boardCardFieldsScopedState(scopeId)).reduce<
|
get(recordBoardCardFieldsScopedState({ scopeId })).reduce<
|
||||||
Record<string, BoardFieldDefinition<FieldMetadata>>
|
Record<string, BoardFieldDefinition<FieldMetadata>>
|
||||||
>((result, field) => ({ ...result, [field.fieldMetadataId]: field }), {}),
|
>((result, field) => ({ ...result, [field.fieldMetadataId]: field }), {}),
|
||||||
});
|
});
|
@ -2,16 +2,18 @@ import { selectorFamily } from 'recoil';
|
|||||||
|
|
||||||
import { companyProgressesFamilyState } from '@/companies/states/companyProgressesFamilyState';
|
import { companyProgressesFamilyState } from '@/companies/states/companyProgressesFamilyState';
|
||||||
|
|
||||||
import { boardCardIdsByColumnIdFamilyState } from '../boardCardIdsByColumnIdFamilyState';
|
import { recordBoardCardIdsByColumnIdFamilyState } from '../recordBoardCardIdsByColumnIdFamilyState';
|
||||||
|
|
||||||
// TODO: this state should be computed during the synchronization web-hook and put in a generic
|
// TODO: this state should be computed during the synchronization web-hook and put in a generic
|
||||||
// boardColumnTotalsFamilyState indexed by columnId.
|
// boardColumnTotalsFamilyState indexed by columnId.
|
||||||
export const boardColumnTotalsFamilySelector = selectorFamily({
|
export const recordBoardColumnTotalsFamilySelector = selectorFamily({
|
||||||
key: 'boardColumnTotalsFamilySelector',
|
key: 'recordBoardColumnTotalsFamilySelector',
|
||||||
get:
|
get:
|
||||||
(pipelineStepId: string) =>
|
(pipelineStepId: string) =>
|
||||||
({ get }) => {
|
({ get }) => {
|
||||||
const cardIds = get(boardCardIdsByColumnIdFamilyState(pipelineStepId));
|
const cardIds = get(
|
||||||
|
recordBoardCardIdsByColumnIdFamilyState(pipelineStepId),
|
||||||
|
);
|
||||||
|
|
||||||
const opportunities = cardIds.map((opportunityId: string) =>
|
const opportunities = cardIds.map((opportunityId: string) =>
|
||||||
get(companyProgressesFamilyState(opportunityId)),
|
get(companyProgressesFamilyState(opportunityId)),
|
@ -1,16 +0,0 @@
|
|||||||
import { selectorFamily } from 'recoil';
|
|
||||||
|
|
||||||
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
|
|
||||||
|
|
||||||
import { BoardFieldDefinition } from '../../types/BoardFieldDefinition';
|
|
||||||
import { savedBoardCardFieldsFamilyState } from '../savedBoardCardFieldsFamilyState';
|
|
||||||
|
|
||||||
export const savedBoardCardFieldsByKeyFamilySelector = selectorFamily({
|
|
||||||
key: 'savedBoardCardFieldsByKeyFamilySelector',
|
|
||||||
get:
|
|
||||||
(viewId: string | undefined) =>
|
|
||||||
({ get }) =>
|
|
||||||
get(savedBoardCardFieldsFamilyState(viewId)).reduce<
|
|
||||||
Record<string, BoardFieldDefinition<FieldMetadata>>
|
|
||||||
>((result, field) => ({ ...result, [field.fieldMetadataId]: field }), {}),
|
|
||||||
});
|
|
@ -1,22 +0,0 @@
|
|||||||
import { selector } from 'recoil';
|
|
||||||
|
|
||||||
import { boardCardIdsByColumnIdFamilyState } from '../boardCardIdsByColumnIdFamilyState';
|
|
||||||
import { boardColumnsState } from '../boardColumnsState';
|
|
||||||
import { isCardSelectedFamilyState } from '../isCardSelectedFamilyState';
|
|
||||||
|
|
||||||
export const selectedCardIdsSelector = selector<string[]>({
|
|
||||||
key: 'selectedCardIdsSelector',
|
|
||||||
get: ({ get }) => {
|
|
||||||
const boardColumns = get(boardColumnsState);
|
|
||||||
|
|
||||||
const cardIds = boardColumns.flatMap((boardColumn) =>
|
|
||||||
get(boardCardIdsByColumnIdFamilyState(boardColumn.id)),
|
|
||||||
);
|
|
||||||
|
|
||||||
const selectedCardIds = cardIds.filter(
|
|
||||||
(cardId) => get(isCardSelectedFamilyState(cardId)) === true,
|
|
||||||
);
|
|
||||||
|
|
||||||
return selectedCardIds;
|
|
||||||
},
|
|
||||||
});
|
|
@ -0,0 +1,26 @@
|
|||||||
|
import { createScopedSelector } from '@/ui/utilities/recoil-scope/utils/createScopedSelector';
|
||||||
|
|
||||||
|
import { isRecordBoardCardSelectedFamilyState } from '../isRecordBoardCardSelectedFamilyState';
|
||||||
|
import { recordBoardCardIdsByColumnIdFamilyState } from '../recordBoardCardIdsByColumnIdFamilyState';
|
||||||
|
import { recordBoardColumnsScopedState } from '../recordBoardColumnsScopedState';
|
||||||
|
|
||||||
|
export const selectedRecordBoardCardIdsScopedSelector = createScopedSelector<
|
||||||
|
string[]
|
||||||
|
>({
|
||||||
|
key: 'selectedRecordBoardCardIdsScopedSelector',
|
||||||
|
get:
|
||||||
|
({ scopeId }) =>
|
||||||
|
({ get }) => {
|
||||||
|
const boardColumns = get(recordBoardColumnsScopedState({ scopeId }));
|
||||||
|
|
||||||
|
const cardIds = boardColumns.flatMap((boardColumn) =>
|
||||||
|
get(recordBoardCardIdsByColumnIdFamilyState(boardColumn.id)),
|
||||||
|
);
|
||||||
|
|
||||||
|
const selectedCardIds = cardIds.filter(
|
||||||
|
(cardId) => get(isRecordBoardCardSelectedFamilyState(cardId)) === true,
|
||||||
|
);
|
||||||
|
|
||||||
|
return selectedCardIds;
|
||||||
|
},
|
||||||
|
});
|
@ -1,13 +0,0 @@
|
|||||||
import { selectorFamily } from 'recoil';
|
|
||||||
|
|
||||||
import { boardCardFieldsScopedState } from '../boardCardFieldsScopedState';
|
|
||||||
|
|
||||||
export const visibleBoardCardFieldsScopedSelector = selectorFamily({
|
|
||||||
key: 'visibleBoardCardFieldsScopedSelector',
|
|
||||||
get:
|
|
||||||
(scopeId: string) =>
|
|
||||||
({ get }) =>
|
|
||||||
get(boardCardFieldsScopedState(scopeId))
|
|
||||||
.filter((field) => field.isVisible)
|
|
||||||
.sort((a, b) => a.position - b.position),
|
|
||||||
});
|
|
@ -0,0 +1,13 @@
|
|||||||
|
import { createScopedSelector } from '@/ui/utilities/recoil-scope/utils/createScopedSelector';
|
||||||
|
|
||||||
|
import { recordBoardCardFieldsScopedState } from '../recordBoardCardFieldsScopedState';
|
||||||
|
|
||||||
|
export const visibleRecordBoardCardFieldsScopedSelector = createScopedSelector({
|
||||||
|
key: 'visibleRecordBoardCardFieldsScopedSelector',
|
||||||
|
get:
|
||||||
|
({ scopeId }) =>
|
||||||
|
({ get }) =>
|
||||||
|
get(recordBoardCardFieldsScopedState({ scopeId }))
|
||||||
|
.filter((field) => field.isVisible)
|
||||||
|
.sort((a, b) => a.position - b.position),
|
||||||
|
});
|
@ -0,0 +1,120 @@
|
|||||||
|
import { activeRecordBoardCardIdsScopedState } from '@/ui/object/record-board/states/activeRecordBoardCardIdsScopedState';
|
||||||
|
import { availableRecordBoardCardFieldsScopedState } from '@/ui/object/record-board/states/availableRecordBoardCardFieldsScopedState';
|
||||||
|
import { isCompactViewEnabledScopedState } from '@/ui/object/record-board/states/isCompactViewEnabledScopedState';
|
||||||
|
import { isRecordBoardLoadedScopedState } from '@/ui/object/record-board/states/isRecordBoardLoadedScopedState';
|
||||||
|
import { onFieldsChangeScopedState } from '@/ui/object/record-board/states/onFieldsChangeScopedState';
|
||||||
|
import { recordBoardColumnsScopedState } from '@/ui/object/record-board/states/recordBoardColumnsScopedState';
|
||||||
|
import { recordBoardFiltersScopedState } from '@/ui/object/record-board/states/recordBoardFiltersScopedState';
|
||||||
|
import { recordBoardSortsScopedState } from '@/ui/object/record-board/states/recordBoardSortsScopedState';
|
||||||
|
import { savedOpportunitiesScopedState } from '@/ui/object/record-board/states/savedOpportunitiesScopedState';
|
||||||
|
import { savedPipelineStepsScopedState } from '@/ui/object/record-board/states/savedPipelineStepsScopedState';
|
||||||
|
import { savedRecordBoardColumnsScopedState } from '@/ui/object/record-board/states/savedRecordBoardColumnsScopedState';
|
||||||
|
import { savedRecordsScopedState } from '@/ui/object/record-board/states/savedRecordsScopedState';
|
||||||
|
import { hiddenRecordBoardCardFieldsScopedSelector } from '@/ui/object/record-board/states/selectors/hiddenRecordBoardCardFieldsScopedSelector';
|
||||||
|
import { recordBoardCardFieldsByKeyScopedSelector } from '@/ui/object/record-board/states/selectors/recordBoardCardFieldsByKeyScopedSelector';
|
||||||
|
import { selectedRecordBoardCardIdsScopedSelector } from '@/ui/object/record-board/states/selectors/selectedRecordBoardCardIdsScopedSelector';
|
||||||
|
import { visibleRecordBoardCardFieldsScopedSelector } from '@/ui/object/record-board/states/selectors/visibleRecordBoardCardFieldsScopedSelector';
|
||||||
|
import { getScopedState } from '@/ui/utilities/recoil-scope/utils/getScopedState';
|
||||||
|
|
||||||
|
export const getRecordBoardScopedStates = ({
|
||||||
|
recordBoardScopeId,
|
||||||
|
}: {
|
||||||
|
recordBoardScopeId: string;
|
||||||
|
}) => {
|
||||||
|
const activeCardIdsState = getScopedState(
|
||||||
|
activeRecordBoardCardIdsScopedState,
|
||||||
|
recordBoardScopeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const availableBoardCardFieldsState = getScopedState(
|
||||||
|
availableRecordBoardCardFieldsScopedState,
|
||||||
|
recordBoardScopeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const boardColumnsState = getScopedState(
|
||||||
|
recordBoardColumnsScopedState,
|
||||||
|
recordBoardScopeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const isBoardLoadedState = getScopedState(
|
||||||
|
isRecordBoardLoadedScopedState,
|
||||||
|
recordBoardScopeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const isCompactViewEnabledState = getScopedState(
|
||||||
|
isCompactViewEnabledScopedState,
|
||||||
|
recordBoardScopeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const savedBoardColumnsState = getScopedState(
|
||||||
|
savedRecordBoardColumnsScopedState,
|
||||||
|
recordBoardScopeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const boardFiltersState = getScopedState(
|
||||||
|
recordBoardFiltersScopedState,
|
||||||
|
recordBoardScopeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const boardSortsState = getScopedState(
|
||||||
|
recordBoardSortsScopedState,
|
||||||
|
recordBoardScopeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const savedCompaniesState = getScopedState(
|
||||||
|
savedRecordsScopedState,
|
||||||
|
recordBoardScopeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const savedOpportunitiesState = getScopedState(
|
||||||
|
savedOpportunitiesScopedState,
|
||||||
|
recordBoardScopeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const savedPipelineStepsState = getScopedState(
|
||||||
|
savedPipelineStepsScopedState,
|
||||||
|
recordBoardScopeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const onFieldsChangeState = getScopedState(
|
||||||
|
onFieldsChangeScopedState,
|
||||||
|
recordBoardScopeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: Family scoped selector
|
||||||
|
const boardCardFieldsByKeySelector =
|
||||||
|
recordBoardCardFieldsByKeyScopedSelector(recordBoardScopeId);
|
||||||
|
|
||||||
|
const hiddenBoardCardFieldsSelector =
|
||||||
|
hiddenRecordBoardCardFieldsScopedSelector({
|
||||||
|
scopeId: recordBoardScopeId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedCardIdsSelector = selectedRecordBoardCardIdsScopedSelector({
|
||||||
|
scopeId: recordBoardScopeId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const visibleBoardCardFieldsSelector =
|
||||||
|
visibleRecordBoardCardFieldsScopedSelector({
|
||||||
|
scopeId: recordBoardScopeId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
activeCardIdsState,
|
||||||
|
availableBoardCardFieldsState,
|
||||||
|
boardColumnsState,
|
||||||
|
isBoardLoadedState,
|
||||||
|
isCompactViewEnabledState,
|
||||||
|
savedBoardColumnsState,
|
||||||
|
boardFiltersState,
|
||||||
|
boardSortsState,
|
||||||
|
onFieldsChangeState,
|
||||||
|
boardCardFieldsByKeySelector,
|
||||||
|
hiddenBoardCardFieldsSelector,
|
||||||
|
selectedCardIdsSelector,
|
||||||
|
visibleBoardCardFieldsSelector,
|
||||||
|
savedCompaniesState,
|
||||||
|
savedOpportunitiesState,
|
||||||
|
savedPipelineStepsState,
|
||||||
|
};
|
||||||
|
};
|
@ -1,7 +1,6 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { CompanyBoard } from '@/companies/board/components/CompanyBoard';
|
import { CompanyBoard } from '@/companies/board/components/CompanyBoard';
|
||||||
import { CompanyBoardRecoilScopeContext } from '@/companies/states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
|
|
||||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||||
import { PipelineAddButton } from '@/pipeline/components/PipelineAddButton';
|
import { PipelineAddButton } from '@/pipeline/components/PipelineAddButton';
|
||||||
import { usePipelineSteps } from '@/pipeline/hooks/usePipelineSteps';
|
import { usePipelineSteps } from '@/pipeline/hooks/usePipelineSteps';
|
||||||
@ -10,9 +9,6 @@ import { IconTargetArrow } from '@/ui/display/icon';
|
|||||||
import { PageBody } from '@/ui/layout/page/PageBody';
|
import { PageBody } from '@/ui/layout/page/PageBody';
|
||||||
import { PageContainer } from '@/ui/layout/page/PageContainer';
|
import { PageContainer } from '@/ui/layout/page/PageContainer';
|
||||||
import { PageHeader } from '@/ui/layout/page/PageHeader';
|
import { PageHeader } from '@/ui/layout/page/PageHeader';
|
||||||
import { BoardOptionsContext } from '@/ui/object/record-board/contexts/BoardOptionsContext';
|
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
|
||||||
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
|
|
||||||
|
|
||||||
const StyledBoardContainer = styled.div`
|
const StyledBoardContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -45,24 +41,18 @@ export const Opportunities = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<RecoilScope>
|
<PageHeader title="Opportunities" Icon={IconTargetArrow}>
|
||||||
<PageHeader title="Opportunities" Icon={IconTargetArrow}>
|
<PipelineAddButton />
|
||||||
<PipelineAddButton />
|
</PageHeader>
|
||||||
</PageHeader>
|
<PageBody>
|
||||||
<PageBody>
|
<StyledBoardContainer>
|
||||||
<BoardOptionsContext.Provider value={opportunitiesBoardOptions}>
|
<CompanyBoard
|
||||||
<CompanyBoardRecoilScopeContext.Provider value="opportunities">
|
onColumnAdd={handlePipelineStepAdd}
|
||||||
<StyledBoardContainer>
|
onColumnDelete={handlePipelineStepDelete}
|
||||||
<CompanyBoard
|
onEditColumnTitle={handleEditColumnTitle}
|
||||||
onColumnAdd={handlePipelineStepAdd}
|
/>
|
||||||
onColumnDelete={handlePipelineStepDelete}
|
</StyledBoardContainer>
|
||||||
onEditColumnTitle={handleEditColumnTitle}
|
</PageBody>
|
||||||
/>
|
|
||||||
</StyledBoardContainer>
|
|
||||||
</CompanyBoardRecoilScopeContext.Provider>
|
|
||||||
</BoardOptionsContext.Provider>
|
|
||||||
</PageBody>
|
|
||||||
</RecoilScope>
|
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
import { CompanyBoardCard } from '@/companies/components/CompanyBoardCard';
|
import { CompanyBoardCard } from '@/companies/components/CompanyBoardCard';
|
||||||
import { NewOpportunityButton } from '@/companies/components/NewOpportunityButton';
|
import { NewOpportunityButton } from '@/companies/components/NewOpportunityButton';
|
||||||
import { BoardOptions } from '@/ui/object/record-board/types/BoardOptions';
|
import { BoardOptions } from '@/ui/object/record-board/types/BoardOptions';
|
||||||
|
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||||
|
|
||||||
export const opportunitiesBoardOptions: BoardOptions = {
|
export const opportunitiesBoardOptions: BoardOptions = {
|
||||||
newCardComponent: <NewOpportunityButton />,
|
newCardComponent: (
|
||||||
|
<RecoilScope>
|
||||||
|
<NewOpportunityButton />
|
||||||
|
</RecoilScope>
|
||||||
|
),
|
||||||
CardComponent: CompanyBoardCard,
|
CardComponent: CompanyBoardCard,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user