Deprecate old board (#4352)
* Deprecate old board * Fix tests * Fix tests
This commit is contained in:
@ -1,31 +0,0 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { useObjectRecordBoardDeprecated } from '@/object-record/hooks/useObjectRecordBoardDeprecated';
|
||||
import { RecordBoardDeprecatedScope } from '@/object-record/record-board-deprecated/scopes/RecordBoardDeprecatedScope';
|
||||
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
|
||||
|
||||
const recordBoardId = '783932a0-28c7-4607-b2ce-6543fa2be892';
|
||||
|
||||
const Wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<RecoilRoot>
|
||||
<RecordBoardDeprecatedScope recordBoardScopeId={recordBoardId}>
|
||||
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
|
||||
<MockedProvider addTypename={false}>{children}</MockedProvider>
|
||||
</SnackBarProviderScope>
|
||||
</RecordBoardDeprecatedScope>
|
||||
</RecoilRoot>
|
||||
);
|
||||
|
||||
describe('useObjectRecordBoardDeprecated', () => {
|
||||
it('should skip fetch if currentWorkspace is undefined', async () => {
|
||||
const { result } = renderHook(() => useObjectRecordBoardDeprecated(), {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
|
||||
expect(result.current.loading).toBe(false);
|
||||
expect(Array.isArray(result.current.opportunities)).toBe(true);
|
||||
});
|
||||
});
|
||||
@ -1,105 +0,0 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { Company } from '@/companies/types/Company';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
import { turnObjectDropdownFilterIntoQueryFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter';
|
||||
import { ObjectRecordConnection } from '@/object-record/types/ObjectRecordConnection';
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
||||
|
||||
import { useFindManyRecords } from './useFindManyRecords';
|
||||
|
||||
export const useObjectRecordBoardDeprecated = () => {
|
||||
const objectNameSingular = 'opportunity';
|
||||
|
||||
const { objectMetadataItem: foundObjectMetadataItem } = useObjectMetadataItem(
|
||||
{
|
||||
objectNameSingular,
|
||||
},
|
||||
);
|
||||
|
||||
const {
|
||||
isBoardLoadedState,
|
||||
boardFiltersState,
|
||||
boardSortsState,
|
||||
savedCompaniesState,
|
||||
savedOpportunitiesState,
|
||||
savedPipelineStepsState,
|
||||
} = useRecordBoardDeprecatedScopedStates();
|
||||
|
||||
const setIsBoardLoaded = useSetRecoilState(isBoardLoadedState);
|
||||
|
||||
const boardFilters = useRecoilValue(boardFiltersState);
|
||||
const boardSorts = useRecoilValue(boardSortsState);
|
||||
|
||||
const setSavedCompanies = useSetRecoilState(savedCompaniesState);
|
||||
|
||||
const [savedOpportunities] = useRecoilState(savedOpportunitiesState);
|
||||
|
||||
const [savedPipelineSteps, setSavedPipelineSteps] = useRecoilState(
|
||||
savedPipelineStepsState,
|
||||
);
|
||||
|
||||
const filter = turnObjectDropdownFilterIntoQueryFilter(
|
||||
boardFilters,
|
||||
foundObjectMetadataItem?.fields ?? [],
|
||||
);
|
||||
const orderBy = turnSortsIntoOrderBy(
|
||||
boardSorts,
|
||||
foundObjectMetadataItem?.fields ?? [],
|
||||
);
|
||||
|
||||
useFindManyRecords({
|
||||
objectNameSingular: CoreObjectNameSingular.PipelineStep,
|
||||
filter,
|
||||
onCompleted: useCallback(
|
||||
(data: ObjectRecordConnection<PipelineStep>) => {
|
||||
setSavedPipelineSteps(data.edges.map((edge) => edge.node));
|
||||
},
|
||||
[setSavedPipelineSteps],
|
||||
),
|
||||
});
|
||||
|
||||
const {
|
||||
records: opportunities,
|
||||
loading,
|
||||
fetchMoreRecords: fetchMoreOpportunities,
|
||||
} = useFindManyRecords<Opportunity>({
|
||||
skip: !savedPipelineSteps.length,
|
||||
objectNameSingular: CoreObjectNameSingular.Opportunity,
|
||||
filter,
|
||||
orderBy,
|
||||
onCompleted: useCallback(() => {
|
||||
setIsBoardLoaded(true);
|
||||
}, [setIsBoardLoaded]),
|
||||
});
|
||||
|
||||
const { fetchMoreRecords: fetchMoreCompanies } = useFindManyRecords({
|
||||
skip: !savedOpportunities.length,
|
||||
objectNameSingular: CoreObjectNameSingular.Company,
|
||||
filter: {
|
||||
id: {
|
||||
in: savedOpportunities.map(
|
||||
(opportunity) => opportunity.companyId || '',
|
||||
),
|
||||
},
|
||||
},
|
||||
onCompleted: useCallback(
|
||||
(data: ObjectRecordConnection<Company>) => {
|
||||
setSavedCompanies(data.edges.map((edge) => edge.node));
|
||||
},
|
||||
[setSavedCompanies],
|
||||
),
|
||||
});
|
||||
|
||||
return {
|
||||
opportunities,
|
||||
loading,
|
||||
fetchMoreOpportunities,
|
||||
fetchMoreCompanies,
|
||||
};
|
||||
};
|
||||
@ -1,16 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
import { ActionBar } from '@/ui/navigation/action-bar/components/ActionBar';
|
||||
|
||||
export const RecordBoardDeprecatedActionBar = () => {
|
||||
const { selectedCardIdsSelector } = useRecordBoardDeprecatedScopedStates();
|
||||
const selectedCardIds = useRecoilValue(selectedCardIdsSelector);
|
||||
|
||||
if (!selectedCardIds.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <ActionBar />;
|
||||
};
|
||||
@ -1,36 +0,0 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { IconPlus } from '@/ui/display/icon/index';
|
||||
|
||||
const StyledButton = styled.button`
|
||||
align-items: center;
|
||||
align-self: baseline;
|
||||
background-color: ${({ theme }) => theme.background.primary};
|
||||
border: none;
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => theme.background.tertiary};
|
||||
}
|
||||
`;
|
||||
|
||||
type NewButtonProps = {
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
export const NewButton = ({ onClick }: NewButtonProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<StyledButton onClick={onClick}>
|
||||
<IconPlus size={theme.icon.size.md} />
|
||||
New
|
||||
</StyledButton>
|
||||
);
|
||||
};
|
||||
@ -1,162 +0,0 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { DragDropContext, OnDragEndResponder } 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 { useRecoilValue } from 'recoil';
|
||||
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { RecordBoardDeprecatedActionBar } from '@/object-record/record-board-deprecated/action-bar/components/RecordBoardDeprecatedActionBar';
|
||||
import { RecordBoardDeprecatedInternalEffect } from '@/object-record/record-board-deprecated/components/RecordBoardDeprecatedInternalEffect';
|
||||
import { RecordBoardDeprecatedContextMenu } from '@/object-record/record-board-deprecated/context-menu/components/RecordBoardDeprecatedContextMenu';
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
import { useSetRecordBoardDeprecatedCardSelectedInternal } from '@/object-record/record-board-deprecated/hooks/internal/useSetRecordBoardDeprecatedCardSelectedInternal';
|
||||
import { RecordBoardDeprecatedScope } from '@/object-record/record-board-deprecated/scopes/RecordBoardDeprecatedScope';
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
||||
import { useListenClickOutsideByClassName } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
import { logError } from '~/utils/logError';
|
||||
|
||||
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
||||
import { BoardOptions } from '../types/BoardOptions';
|
||||
|
||||
import { RecordBoardDeprecatedColumn } from './RecordBoardDeprecatedColumn';
|
||||
|
||||
export type RecordBoardDeprecatedProps = {
|
||||
recordBoardId: string;
|
||||
boardOptions: BoardOptions;
|
||||
onColumnAdd?: (boardColumn: BoardColumnDefinition) => void;
|
||||
onColumnDelete?: (boardColumnId: string) => void;
|
||||
onEditColumnTitle: (params: {
|
||||
columnId: string;
|
||||
title: string;
|
||||
color: string;
|
||||
}) => void;
|
||||
};
|
||||
|
||||
const StyledBoard = styled.div`
|
||||
border-top: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
margin-left: ${({ theme }) => theme.spacing(2)};
|
||||
margin-right: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledBoardHeader = styled.div`
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
`;
|
||||
|
||||
export const RecordBoardDeprecated = ({
|
||||
recordBoardId,
|
||||
boardOptions,
|
||||
onColumnDelete,
|
||||
onEditColumnTitle,
|
||||
}: RecordBoardDeprecatedProps) => {
|
||||
const recordBoardScopeId = recordBoardId;
|
||||
|
||||
const { boardColumnsState } = useRecordBoardDeprecatedScopedStates({
|
||||
recordBoardScopeId,
|
||||
});
|
||||
const boardColumns = useRecoilValue(boardColumnsState);
|
||||
|
||||
const { updateOneRecord: updateOneOpportunity } =
|
||||
useUpdateOneRecord<Opportunity>({
|
||||
objectNameSingular: CoreObjectNameSingular.Opportunity,
|
||||
});
|
||||
|
||||
const { unselectAllActiveCards, setCardSelected } =
|
||||
useSetRecordBoardDeprecatedCardSelectedInternal({ recordBoardScopeId });
|
||||
|
||||
const updatePipelineProgressStageInDB = useCallback(
|
||||
async (pipelineProgressId: string, pipelineStepId: string) => {
|
||||
await updateOneOpportunity?.({
|
||||
idToUpdate: pipelineProgressId,
|
||||
updateOneRecordInput: {
|
||||
pipelineStepId: pipelineStepId,
|
||||
},
|
||||
});
|
||||
},
|
||||
[updateOneOpportunity],
|
||||
);
|
||||
|
||||
useListenClickOutsideByClassName({
|
||||
classNames: ['entity-board-card'],
|
||||
excludeClassNames: ['action-bar', 'context-menu'],
|
||||
callback: unselectAllActiveCards,
|
||||
});
|
||||
|
||||
const onDragEnd: OnDragEndResponder = useCallback(
|
||||
async (result) => {
|
||||
if (!boardColumns) return;
|
||||
|
||||
try {
|
||||
const draggedEntityId = result.draggableId;
|
||||
const destinationColumnId = result.destination?.droppableId;
|
||||
|
||||
if (
|
||||
draggedEntityId &&
|
||||
destinationColumnId &&
|
||||
updatePipelineProgressStageInDB
|
||||
) {
|
||||
await updatePipelineProgressStageInDB(
|
||||
draggedEntityId,
|
||||
destinationColumnId,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
logError(e);
|
||||
}
|
||||
},
|
||||
[boardColumns, updatePipelineProgressStageInDB],
|
||||
);
|
||||
|
||||
const sortedBoardColumns = [...boardColumns].sort((a, b) => {
|
||||
return a.position - b.position;
|
||||
});
|
||||
|
||||
const boardRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
return (
|
||||
<RecordBoardDeprecatedScope recordBoardScopeId={recordBoardId}>
|
||||
<RecordBoardDeprecatedContextMenu />
|
||||
<RecordBoardDeprecatedActionBar />
|
||||
<RecordBoardDeprecatedInternalEffect />
|
||||
|
||||
<StyledWrapper>
|
||||
<StyledBoardHeader />
|
||||
<ScrollWrapper>
|
||||
<StyledBoard ref={boardRef}>
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
{sortedBoardColumns.map((column) => (
|
||||
<RecordBoardDeprecatedColumn
|
||||
key={column.id}
|
||||
recordBoardColumnId={column.id}
|
||||
columnDefinition={column}
|
||||
recordBoardColumnTotal={sortedBoardColumns.length}
|
||||
recordBoardOptions={boardOptions}
|
||||
onDelete={onColumnDelete}
|
||||
onTitleEdit={onEditColumnTitle}
|
||||
/>
|
||||
))}
|
||||
</DragDropContext>
|
||||
</StyledBoard>
|
||||
</ScrollWrapper>
|
||||
<DragSelect
|
||||
dragSelectable={boardRef}
|
||||
onDragSelectionChange={setCardSelected}
|
||||
/>
|
||||
</StyledWrapper>
|
||||
</RecordBoardDeprecatedScope>
|
||||
);
|
||||
};
|
||||
@ -1,54 +0,0 @@
|
||||
import { Draggable } from '@hello-pangea/dnd';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/contextMenuIsOpenState';
|
||||
import { contextMenuPositionState } from '@/ui/navigation/context-menu/states/contextMenuPositionState';
|
||||
|
||||
import { useCurrentRecordBoardDeprecatedCardSelectedInternal } from '../hooks/internal/useCurrentRecordBoardDeprecatedCardSelectedInternal';
|
||||
import { BoardOptions } from '../types/BoardOptions';
|
||||
|
||||
export const RecordBoardDeprecatedCard = ({
|
||||
recordBoardOptions,
|
||||
cardId,
|
||||
index,
|
||||
}: {
|
||||
recordBoardOptions: BoardOptions;
|
||||
cardId: string;
|
||||
index: number;
|
||||
}) => {
|
||||
const setContextMenuPosition = useSetRecoilState(contextMenuPositionState);
|
||||
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
|
||||
|
||||
const { setCurrentCardSelected } =
|
||||
useCurrentRecordBoardDeprecatedCardSelectedInternal();
|
||||
|
||||
const handleContextMenu = (event: React.MouseEvent) => {
|
||||
event.preventDefault();
|
||||
setCurrentCardSelected(true);
|
||||
setContextMenuPosition({
|
||||
x: event.clientX,
|
||||
y: event.clientY,
|
||||
});
|
||||
setContextMenuOpenState(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<Draggable key={cardId} draggableId={cardId} index={index}>
|
||||
{(draggableProvided) => (
|
||||
<div
|
||||
ref={draggableProvided?.innerRef}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...draggableProvided?.dragHandleProps}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...draggableProvided?.draggableProps}
|
||||
className="entity-board-card"
|
||||
data-selectable-id={cardId}
|
||||
data-select-disable
|
||||
onContextMenu={handleContextMenu}
|
||||
>
|
||||
{<recordBoardOptions.CardComponent />}
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
};
|
||||
@ -1,143 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Draggable, Droppable, DroppableProvided } from '@hello-pangea/dnd';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { RecordBoardDeprecatedCard } from '@/object-record/record-board-deprecated/components/RecordBoardDeprecatedCard';
|
||||
import { RecordBoardDeprecatedColumnHeader } from '@/object-record/record-board-deprecated/components/RecordBoardDeprecatedColumnHeader';
|
||||
import { BoardCardIdContext } from '@/object-record/record-board-deprecated/contexts/BoardCardIdContext';
|
||||
import { BoardColumnDefinition } from '@/object-record/record-board-deprecated/types/BoardColumnDefinition';
|
||||
|
||||
import { BoardColumnContext } from '../contexts/BoardColumnContext';
|
||||
import { recordBoardCardIdsByColumnIdFamilyState } from '../states/recordBoardCardIdsByColumnIdFamilyState';
|
||||
import { BoardOptions } from '../types/BoardOptions';
|
||||
|
||||
const StyledPlaceholder = styled.div`
|
||||
min-height: 1px;
|
||||
`;
|
||||
|
||||
const StyledNewCardButtonContainer = styled.div`
|
||||
padding-bottom: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
const StyledColumnCardsContainer = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const StyledColumn = styled.div<{ isFirstColumn: boolean }>`
|
||||
background-color: ${({ theme }) => theme.background.primary};
|
||||
border-left: 1px solid
|
||||
${({ theme, isFirstColumn }) =>
|
||||
isFirstColumn ? 'none' : theme.border.color.light};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 200px;
|
||||
min-width: 200px;
|
||||
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
type BoardColumnCardsContainerProps = {
|
||||
children: React.ReactNode;
|
||||
droppableProvided: DroppableProvided;
|
||||
};
|
||||
|
||||
type RecordBoardDeprecatedColumnProps = {
|
||||
recordBoardColumnId: string;
|
||||
columnDefinition: BoardColumnDefinition;
|
||||
recordBoardOptions: BoardOptions;
|
||||
recordBoardColumnTotal: number;
|
||||
onDelete?: (columnId: string) => void;
|
||||
onTitleEdit: (params: {
|
||||
columnId: string;
|
||||
title: string;
|
||||
color: string;
|
||||
}) => void;
|
||||
};
|
||||
|
||||
const BoardColumnCardsContainer = ({
|
||||
children,
|
||||
droppableProvided,
|
||||
}: BoardColumnCardsContainerProps) => {
|
||||
return (
|
||||
<StyledColumnCardsContainer
|
||||
ref={droppableProvided?.innerRef}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...droppableProvided?.droppableProps}
|
||||
>
|
||||
{children}
|
||||
<StyledPlaceholder>{droppableProvided?.placeholder}</StyledPlaceholder>
|
||||
</StyledColumnCardsContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export const RecordBoardDeprecatedColumn = ({
|
||||
recordBoardColumnId,
|
||||
columnDefinition,
|
||||
recordBoardOptions,
|
||||
recordBoardColumnTotal,
|
||||
onDelete,
|
||||
onTitleEdit,
|
||||
}: RecordBoardDeprecatedColumnProps) => {
|
||||
const cardIds = useRecoilValue(
|
||||
recordBoardCardIdsByColumnIdFamilyState(recordBoardColumnId),
|
||||
);
|
||||
|
||||
const isFirstColumn = columnDefinition.position === 0;
|
||||
|
||||
return (
|
||||
<BoardColumnContext.Provider
|
||||
value={{
|
||||
id: recordBoardColumnId,
|
||||
columnDefinition: columnDefinition,
|
||||
isFirstColumn: columnDefinition.position === 0,
|
||||
isLastColumn: columnDefinition.position === recordBoardColumnTotal - 1,
|
||||
onTitleEdit: ({ title, color }) =>
|
||||
onTitleEdit({ columnId: recordBoardColumnId, title, color }),
|
||||
}}
|
||||
>
|
||||
<Droppable droppableId={recordBoardColumnId}>
|
||||
{(droppableProvided) => (
|
||||
<StyledColumn isFirstColumn={isFirstColumn}>
|
||||
<RecordBoardDeprecatedColumnHeader
|
||||
recordBoardColumnId={recordBoardColumnId}
|
||||
columnDefinition={columnDefinition}
|
||||
onDelete={onDelete}
|
||||
/>
|
||||
<BoardColumnCardsContainer droppableProvided={droppableProvided}>
|
||||
{cardIds.map((cardId, index) => (
|
||||
<BoardCardIdContext.Provider value={cardId} key={cardId}>
|
||||
<RecordBoardDeprecatedCard
|
||||
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>
|
||||
);
|
||||
};
|
||||
@ -1,149 +0,0 @@
|
||||
import { useCallback, useContext, useRef, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { IconArrowLeft, IconArrowRight, IconPencil } from '@/ui/display/icon';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
|
||||
import { BoardColumnContext } from '../contexts/BoardColumnContext';
|
||||
import { useBoardColumnsInternal } from '../hooks/internal/useRecordBoardDeprecatedColumnsInternal';
|
||||
import { BoardColumnHotkeyScope } from '../types/BoardColumnHotkeyScope';
|
||||
|
||||
import { RecordBoardDeprecatedColumnEditTitleMenu } from './RecordBoardDeprecatedColumnEditTitleMenu';
|
||||
const StyledMenuContainer = styled.div`
|
||||
position: absolute;
|
||||
top: ${({ theme }) => theme.spacing(10)};
|
||||
width: 200px;
|
||||
z-index: 1;
|
||||
`;
|
||||
|
||||
type RecordBoardDeprecatedColumnDropdownMenuProps = {
|
||||
onClose: () => void;
|
||||
onDelete?: (id: string) => void;
|
||||
stageId: string;
|
||||
};
|
||||
|
||||
type Menu = 'actions' | 'add' | 'title';
|
||||
|
||||
export const RecordBoardDeprecatedColumnDropdownMenu = ({
|
||||
onClose,
|
||||
onDelete,
|
||||
stageId,
|
||||
}: RecordBoardDeprecatedColumnDropdownMenuProps) => {
|
||||
const [currentMenu, setCurrentMenu] = useState('actions');
|
||||
const column = useContext(BoardColumnContext);
|
||||
|
||||
const boardColumnMenuRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const { handleMoveBoardColumn } = useBoardColumnsInternal();
|
||||
|
||||
const {
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
goBackToPreviousHotkeyScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
const closeMenu = useCallback(() => {
|
||||
goBackToPreviousHotkeyScope();
|
||||
onClose();
|
||||
}, [goBackToPreviousHotkeyScope, onClose]);
|
||||
|
||||
const setMenu = (menu: Menu) => {
|
||||
if (menu === 'add') {
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
RelationPickerHotkeyScope.RelationPicker,
|
||||
);
|
||||
}
|
||||
setCurrentMenu(menu);
|
||||
};
|
||||
|
||||
useListenClickOutside({
|
||||
refs: [boardColumnMenuRef],
|
||||
callback: closeMenu,
|
||||
});
|
||||
|
||||
useScopedHotkeys(
|
||||
[Key.Escape, Key.Enter],
|
||||
() => {
|
||||
closeMenu();
|
||||
},
|
||||
BoardColumnHotkeyScope.BoardColumn,
|
||||
[],
|
||||
);
|
||||
|
||||
if (!column) return <></>;
|
||||
|
||||
const { isFirstColumn, isLastColumn, columnDefinition } = column;
|
||||
|
||||
const handleColumnMoveLeft = () => {
|
||||
closeMenu();
|
||||
if (isFirstColumn) {
|
||||
return;
|
||||
}
|
||||
handleMoveBoardColumn('left', columnDefinition);
|
||||
};
|
||||
|
||||
const handleColumnMoveRight = () => {
|
||||
closeMenu();
|
||||
if (isLastColumn) {
|
||||
return;
|
||||
}
|
||||
handleMoveBoardColumn('right', columnDefinition);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledMenuContainer ref={boardColumnMenuRef}>
|
||||
<DropdownMenu data-select-disable>
|
||||
{currentMenu === 'actions' && (
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={() => setMenu('title')}
|
||||
LeftIcon={IconPencil}
|
||||
text="Edit"
|
||||
/>
|
||||
<MenuItem
|
||||
LeftIcon={IconArrowLeft}
|
||||
onClick={handleColumnMoveLeft}
|
||||
text="Move left"
|
||||
/>
|
||||
<MenuItem
|
||||
LeftIcon={IconArrowRight}
|
||||
onClick={handleColumnMoveRight}
|
||||
text="Move right"
|
||||
/>
|
||||
{/* <MenuItem
|
||||
onClick={() => setMenu('add')}
|
||||
LeftIcon={IconPlus}
|
||||
text="New opportunity"
|
||||
/> */}
|
||||
</DropdownMenuItemsContainer>
|
||||
)}
|
||||
{currentMenu === 'title' && (
|
||||
<RecordBoardDeprecatedColumnEditTitleMenu
|
||||
color={columnDefinition.colorCode ?? 'gray'}
|
||||
onClose={closeMenu}
|
||||
title={columnDefinition.title}
|
||||
onDelete={onDelete}
|
||||
stageId={stageId}
|
||||
/>
|
||||
)}
|
||||
{currentMenu === 'add' && (
|
||||
<div>add</div>
|
||||
// <SingleEntitySelect
|
||||
// disableBackgroundBlur
|
||||
// entitiesToSelect={companies.entitiesToSelect}
|
||||
// loading={companies.loading}
|
||||
// onCancel={closeMenu}
|
||||
// onEntitySelected={handleCompanySelected}
|
||||
// selectedEntity={companies.selectedEntities[0]}
|
||||
// />
|
||||
)}
|
||||
</DropdownMenu>
|
||||
</StyledMenuContainer>
|
||||
);
|
||||
};
|
||||
@ -1,133 +0,0 @@
|
||||
import { ChangeEvent, useCallback, useContext, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
import { IconTrash } from '@/ui/display/icon';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { MenuItemSelectColor } from '@/ui/navigation/menu-item/components/MenuItemSelectColor';
|
||||
import {
|
||||
MAIN_COLOR_NAMES,
|
||||
ThemeColor,
|
||||
} from '@/ui/theme/constants/MainColorNames';
|
||||
import { TEXT_INPUT_STYLE } from '@/ui/theme/constants/TextInputStyle';
|
||||
|
||||
import { BoardColumnContext } from '../contexts/BoardColumnContext';
|
||||
import { useRecordBoardDeprecated } from '../hooks/useRecordBoardDeprecated';
|
||||
|
||||
const StyledEditTitleContainer = styled.div`
|
||||
--vertical-padding: ${({ theme }) => theme.spacing(1)};
|
||||
|
||||
align-items: center;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: calc(36px - 2 * var(--vertical-padding));
|
||||
padding: var(--vertical-padding) 0;
|
||||
|
||||
width: calc(100%);
|
||||
`;
|
||||
|
||||
const StyledEditModeInput = styled.input`
|
||||
${TEXT_INPUT_STYLE}
|
||||
|
||||
background: ${({ theme }) => theme.background.transparent.lighter};
|
||||
border-color: ${({ theme }) => theme.color.blue};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
box-shadow: 0px 0px 0px 3px rgba(25, 97, 237, 0.1);
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
type RecordBoardDeprecatedColumnEditTitleMenuProps = {
|
||||
onClose: () => void;
|
||||
onDelete?: (id: string) => void;
|
||||
title: string;
|
||||
color: ThemeColor;
|
||||
stageId: string;
|
||||
};
|
||||
|
||||
export const RecordBoardDeprecatedColumnEditTitleMenu = ({
|
||||
onClose,
|
||||
onDelete,
|
||||
stageId,
|
||||
title,
|
||||
color,
|
||||
}: RecordBoardDeprecatedColumnEditTitleMenuProps) => {
|
||||
const [internalValue, setInternalValue] = useState(title);
|
||||
const { onTitleEdit } = useContext(BoardColumnContext) || {};
|
||||
|
||||
const { setBoardColumns } = useRecordBoardDeprecated({
|
||||
recordBoardScopeId: 'company-board',
|
||||
});
|
||||
|
||||
const debouncedOnUpdateTitle = debounce(
|
||||
(newTitle) => onTitleEdit?.({ title: newTitle, color }),
|
||||
200,
|
||||
);
|
||||
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const title = event.target.value;
|
||||
setInternalValue(title);
|
||||
debouncedOnUpdateTitle(title);
|
||||
|
||||
setBoardColumns((previousBoardColumns) =>
|
||||
previousBoardColumns.map((column) =>
|
||||
column.id === stageId ? { ...column, title: title } : column,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const handleColorChange = (newColor: ThemeColor) => {
|
||||
onTitleEdit?.({ title, color: newColor });
|
||||
onClose();
|
||||
setBoardColumns((previousBoardColumns) =>
|
||||
previousBoardColumns.map((column) =>
|
||||
column.id === stageId
|
||||
? { ...column, colorCode: newColor ? newColor : 'gray' }
|
||||
: column,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const handleDelete = useCallback(() => {
|
||||
setBoardColumns((previousBoardColumns) =>
|
||||
previousBoardColumns.filter((column) => column.id !== stageId),
|
||||
);
|
||||
onDelete?.(stageId);
|
||||
onClose();
|
||||
}, [onClose, onDelete, setBoardColumns, stageId]);
|
||||
|
||||
return (
|
||||
<DropdownMenuItemsContainer>
|
||||
<StyledEditTitleContainer>
|
||||
<StyledEditModeInput
|
||||
value={internalValue}
|
||||
onChange={handleChange}
|
||||
autoComplete="off"
|
||||
autoFocus
|
||||
/>
|
||||
</StyledEditTitleContainer>
|
||||
<DropdownMenuSeparator />
|
||||
{MAIN_COLOR_NAMES.map((colorName) => (
|
||||
<MenuItemSelectColor
|
||||
key={colorName}
|
||||
onClick={() => handleColorChange(colorName)}
|
||||
color={colorName}
|
||||
selected={colorName === color}
|
||||
variant="pipeline"
|
||||
/>
|
||||
))}
|
||||
<DropdownMenuSeparator />
|
||||
<MenuItem
|
||||
onClick={handleDelete}
|
||||
LeftIcon={IconTrash}
|
||||
text="Delete"
|
||||
accent="danger"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
);
|
||||
};
|
||||
@ -1,129 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { recordBoardColumnTotalsFamilySelector } from '@/object-record/record-board-deprecated/states/selectors/recordBoardDeprecatedColumnTotalsFamilySelector';
|
||||
import { BoardColumnDefinition } from '@/object-record/record-board-deprecated/types/BoardColumnDefinition';
|
||||
import { IconDotsVertical } from '@/ui/display/icon';
|
||||
import { Tag } from '@/ui/display/tag/components/Tag';
|
||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
|
||||
import { recordBoardCardIdsByColumnIdFamilyState } from '../states/recordBoardCardIdsByColumnIdFamilyState';
|
||||
import { BoardColumnHotkeyScope } from '../types/BoardColumnHotkeyScope';
|
||||
|
||||
import { RecordBoardDeprecatedColumnDropdownMenu } from './RecordBoardDeprecatedColumnDropdownMenu';
|
||||
|
||||
const StyledHeader = styled.div`
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 24px;
|
||||
justify-content: left;
|
||||
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledAmount = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
margin-left: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledNumChildren = styled.div`
|
||||
align-items: center;
|
||||
background-color: ${({ theme }) => theme.background.tertiary};
|
||||
border-radius: ${({ theme }) => theme.border.radius.rounded};
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
display: flex;
|
||||
height: 20px;
|
||||
justify-content: center;
|
||||
line-height: ${({ theme }) => theme.text.lineHeight.lg};
|
||||
margin-left: auto;
|
||||
width: 16px;
|
||||
`;
|
||||
|
||||
const StyledHeaderActions = styled.div`
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
`;
|
||||
|
||||
type RecordBoardDeprecatedColumnHeaderProps = {
|
||||
recordBoardColumnId: string;
|
||||
columnDefinition: BoardColumnDefinition;
|
||||
onDelete?: (columnId: string) => void;
|
||||
};
|
||||
|
||||
export const RecordBoardDeprecatedColumnHeader = ({
|
||||
recordBoardColumnId,
|
||||
columnDefinition,
|
||||
onDelete,
|
||||
}: RecordBoardDeprecatedColumnHeaderProps) => {
|
||||
const [isBoardColumnMenuOpen, setIsBoardColumnMenuOpen] = useState(false);
|
||||
const [isHeaderHovered, setIsHeaderHovered] = useState(false);
|
||||
|
||||
const {
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
goBackToPreviousHotkeyScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
const handleBoardColumnMenuOpen = () => {
|
||||
setIsBoardColumnMenuOpen(true);
|
||||
setHotkeyScopeAndMemorizePreviousScope(BoardColumnHotkeyScope.BoardColumn, {
|
||||
goto: false,
|
||||
});
|
||||
};
|
||||
|
||||
const handleBoardColumnMenuClose = () => {
|
||||
goBackToPreviousHotkeyScope();
|
||||
setIsBoardColumnMenuOpen(false);
|
||||
};
|
||||
|
||||
const boardColumnTotal = useRecoilValue(
|
||||
recordBoardColumnTotalsFamilySelector(recordBoardColumnId),
|
||||
);
|
||||
|
||||
const cardIds = useRecoilValue(
|
||||
recordBoardCardIdsByColumnIdFamilyState(recordBoardColumnId),
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledHeader
|
||||
onMouseEnter={() => setIsHeaderHovered(true)}
|
||||
onMouseLeave={() => setIsHeaderHovered(false)}
|
||||
>
|
||||
<Tag
|
||||
onClick={handleBoardColumnMenuOpen}
|
||||
color={columnDefinition.colorCode ?? 'gray'}
|
||||
text={columnDefinition.title}
|
||||
/>
|
||||
{!!boardColumnTotal && <StyledAmount>${boardColumnTotal}</StyledAmount>}
|
||||
{!isHeaderHovered && (
|
||||
<StyledNumChildren>{cardIds.length}</StyledNumChildren>
|
||||
)}
|
||||
{isHeaderHovered && (
|
||||
<StyledHeaderActions>
|
||||
<LightIconButton
|
||||
accent="tertiary"
|
||||
Icon={IconDotsVertical}
|
||||
onClick={handleBoardColumnMenuOpen}
|
||||
/>
|
||||
{/* <LightIconButton
|
||||
accent="tertiary"
|
||||
Icon={IconPlus}
|
||||
onClick={() => {}}
|
||||
/> */}
|
||||
</StyledHeaderActions>
|
||||
)}
|
||||
</StyledHeader>
|
||||
{isBoardColumnMenuOpen && (
|
||||
<RecordBoardDeprecatedColumnDropdownMenu
|
||||
onClose={handleBoardColumnMenuClose}
|
||||
onDelete={onDelete}
|
||||
stageId={recordBoardColumnId}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -1,25 +0,0 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { useRecordBoardDeprecated } from '@/object-record/record-board-deprecated/hooks/useRecordBoardDeprecated';
|
||||
import { BoardFieldDefinition } from '@/object-record/record-board-deprecated/types/BoardFieldDefinition';
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
|
||||
type RecordBoardDeprecatedEffectProps = {
|
||||
recordBoardId: string;
|
||||
onFieldsChange: (fields: BoardFieldDefinition<FieldMetadata>[]) => void;
|
||||
};
|
||||
|
||||
export const RecordBoardDeprecatedEffect = ({
|
||||
recordBoardId,
|
||||
onFieldsChange,
|
||||
}: RecordBoardDeprecatedEffectProps) => {
|
||||
const { setOnFieldsChange } = useRecordBoardDeprecated({
|
||||
recordBoardScopeId: recordBoardId,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setOnFieldsChange(() => onFieldsChange);
|
||||
}, [onFieldsChange, setOnFieldsChange]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
@ -1,75 +0,0 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useObjectRecordBoardDeprecated } from '@/object-record/hooks/useObjectRecordBoardDeprecated';
|
||||
import { useRecordBoardDeprecatedActionBarEntriesInternal } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedActionBarEntriesInternal';
|
||||
import { useRecordBoardDeprecatedContextMenuEntriesInternal } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedContextMenuEntriesInternal';
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
import { useUpdateCompanyBoardColumnsInternal } from '@/object-record/record-board-deprecated/hooks/internal/useUpdateCompanyBoardColumnsInternal';
|
||||
import { isNonNullable } from '~/utils/isNonNullable';
|
||||
|
||||
export type RecordBoardDeprecatedInternalEffectProps = {
|
||||
onFieldsChange: (fields: any) => void;
|
||||
};
|
||||
|
||||
export const RecordBoardDeprecatedInternalEffect = () => {
|
||||
const updateCompanyColumnsBoardInternal =
|
||||
useUpdateCompanyBoardColumnsInternal();
|
||||
const { setActionBarEntries } =
|
||||
useRecordBoardDeprecatedActionBarEntriesInternal();
|
||||
const { setContextMenuEntries } =
|
||||
useRecordBoardDeprecatedContextMenuEntriesInternal();
|
||||
|
||||
const {
|
||||
savedPipelineStepsState,
|
||||
savedOpportunitiesState,
|
||||
savedCompaniesState,
|
||||
} = useRecordBoardDeprecatedScopedStates();
|
||||
|
||||
const { fetchMoreOpportunities, fetchMoreCompanies, opportunities } =
|
||||
useObjectRecordBoardDeprecated();
|
||||
|
||||
const [savedOpportunities, setSavedOpportunities] = useRecoilState(
|
||||
savedOpportunitiesState,
|
||||
);
|
||||
const savedPipelineSteps = useRecoilValue(savedPipelineStepsState);
|
||||
const savedCompanies = useRecoilValue(savedCompaniesState);
|
||||
|
||||
useEffect(() => {
|
||||
setSavedOpportunities(opportunities);
|
||||
}, [opportunities, setSavedOpportunities]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isNonNullable(fetchMoreOpportunities)) {
|
||||
fetchMoreOpportunities();
|
||||
}
|
||||
}, [fetchMoreOpportunities]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isNonNullable(fetchMoreCompanies)) {
|
||||
fetchMoreCompanies();
|
||||
}
|
||||
}, [fetchMoreCompanies]);
|
||||
|
||||
useEffect(() => {
|
||||
if (savedOpportunities && savedCompanies) {
|
||||
setActionBarEntries();
|
||||
setContextMenuEntries();
|
||||
|
||||
updateCompanyColumnsBoardInternal(
|
||||
savedPipelineSteps,
|
||||
savedOpportunities,
|
||||
savedCompanies,
|
||||
);
|
||||
}
|
||||
}, [
|
||||
savedCompanies,
|
||||
savedOpportunities,
|
||||
savedPipelineSteps,
|
||||
setActionBarEntries,
|
||||
setContextMenuEntries,
|
||||
updateCompanyColumnsBoardInternal,
|
||||
]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
@ -1,17 +0,0 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
|
||||
import { RecordBoardDeprecatedColumnEditTitleMenu } from '../RecordBoardDeprecatedColumnEditTitleMenu';
|
||||
|
||||
const meta: Meta<typeof RecordBoardDeprecatedColumnEditTitleMenu> = {
|
||||
title: 'UI/Layout/Board/BoardColumnMenu',
|
||||
component: RecordBoardDeprecatedColumnEditTitleMenu,
|
||||
decorators: [ComponentDecorator],
|
||||
args: { color: 'green', title: 'Column title' },
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof RecordBoardDeprecatedColumnEditTitleMenu>;
|
||||
|
||||
export const AllTags: Story = {};
|
||||
@ -1 +0,0 @@
|
||||
export const BOARD_OPTIONS_DROPDOWN_ID = 'board-options-dropdown-id';
|
||||
@ -1,14 +0,0 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
import { ContextMenu } from '@/ui/navigation/context-menu/components/ContextMenu';
|
||||
|
||||
export const RecordBoardDeprecatedContextMenu = () => {
|
||||
const { selectedCardIdsSelector } = useRecordBoardDeprecatedScopedStates();
|
||||
const selectedCardIds = useRecoilValue(selectedCardIdsSelector);
|
||||
|
||||
if (!selectedCardIds.length) {
|
||||
return null;
|
||||
}
|
||||
return <ContextMenu />;
|
||||
};
|
||||
@ -1,3 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const BoardCardIdContext = createContext<string | null>(null);
|
||||
@ -1,15 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
||||
|
||||
type BoardColumnContextProps = {
|
||||
id: string;
|
||||
columnDefinition: BoardColumnDefinition;
|
||||
isFirstColumn: boolean;
|
||||
isLastColumn: boolean;
|
||||
onTitleEdit: (params: { title: string; color: string }) => void;
|
||||
};
|
||||
|
||||
export const BoardColumnContext = createContext<BoardColumnContextProps | null>(
|
||||
null,
|
||||
);
|
||||
@ -1,97 +0,0 @@
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
import { useRecordBoardDeprecated } from '@/object-record/record-board-deprecated/hooks/useRecordBoardDeprecated';
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MockedProvider>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
const recordBoardScopeId = 'recordBoardScopeId';
|
||||
|
||||
const renderHookConfig = {
|
||||
wrapper: Wrapper,
|
||||
};
|
||||
|
||||
const useRecordBoardDeprecatedHook = () => {
|
||||
const recordBoard = useRecordBoardDeprecated({ recordBoardScopeId });
|
||||
const { isBoardLoadedState, boardColumnsState, onFieldsChangeState } =
|
||||
useRecordBoardDeprecatedScopedStates({
|
||||
recordBoardScopeId: recordBoardScopeId,
|
||||
});
|
||||
const isBoardLoaded = useRecoilValue(isBoardLoadedState);
|
||||
const boardColumns = useRecoilValue(boardColumnsState);
|
||||
const onFieldsChange = useRecoilValue(onFieldsChangeState);
|
||||
|
||||
return {
|
||||
recordBoard,
|
||||
isBoardLoaded,
|
||||
boardColumns,
|
||||
onFieldsChange,
|
||||
};
|
||||
};
|
||||
|
||||
describe('useRecordBoardDeprecated', () => {
|
||||
it('should set isBoardLoadedState', async () => {
|
||||
const { result } = renderHook(
|
||||
() => useRecordBoardDeprecatedHook(),
|
||||
renderHookConfig,
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.recordBoard.setIsBoardLoaded(true);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.isBoardLoaded).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should set boardColumnsState', async () => {
|
||||
const columns = [
|
||||
{
|
||||
id: '1',
|
||||
title: '1',
|
||||
position: 1,
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
title: '1',
|
||||
position: 1,
|
||||
},
|
||||
];
|
||||
const { result } = renderHook(
|
||||
() => useRecordBoardDeprecatedHook(),
|
||||
renderHookConfig,
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.recordBoard.setBoardColumns(columns);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.boardColumns).toEqual(columns);
|
||||
});
|
||||
});
|
||||
|
||||
it('should set setOnFieldsChange', async () => {
|
||||
const onFieldsChangeFunction = () => {};
|
||||
const onFieldsChange = jest.fn(() => onFieldsChangeFunction);
|
||||
const { result } = renderHook(
|
||||
() => useRecordBoardDeprecatedHook(),
|
||||
renderHookConfig,
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.recordBoard.setOnFieldsChange(onFieldsChange);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.onFieldsChange).toEqual(onFieldsChangeFunction);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,78 +0,0 @@
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import gql from 'graphql-tag';
|
||||
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useCreateOpportunity } from '@/object-record/record-board-deprecated/hooks/internal/useCreateOpportunity';
|
||||
import { recordBoardCardIdsByColumnIdFamilyState } from '@/object-record/record-board-deprecated/states/recordBoardCardIdsByColumnIdFamilyState';
|
||||
|
||||
const mockedUuid = 'mocked-uuid';
|
||||
jest.mock('uuid', () => ({
|
||||
v4: () => mockedUuid,
|
||||
}));
|
||||
|
||||
jest.mock('@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery', () => ({
|
||||
useMapFieldMetadataToGraphQLQuery: () => () => '\n',
|
||||
}));
|
||||
|
||||
const mocks = [
|
||||
{
|
||||
request: {
|
||||
query: gql`
|
||||
mutation CreateOneOpportunity($input: OpportunityCreateInput!) {
|
||||
createOpportunity(data: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
input: {
|
||||
id: mockedUuid,
|
||||
pipelineStepId: 'pipelineStepId',
|
||||
companyId: 'New Opportunity',
|
||||
},
|
||||
},
|
||||
},
|
||||
result: jest.fn(() => ({
|
||||
data: { createOpportunity: { id: '' } },
|
||||
})),
|
||||
},
|
||||
];
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MockedProvider mocks={mocks} addTypename={false}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
const renderHookConfig = {
|
||||
wrapper: Wrapper,
|
||||
};
|
||||
|
||||
describe('useCreateOpportunity', () => {
|
||||
it('should create opportunity successfully', () => {
|
||||
const companyIdname = 'New Opportunity';
|
||||
const opportunityPipelineStepId = 'pipelineStepId';
|
||||
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
createOpportunity: useCreateOpportunity(),
|
||||
recordBoardCardIdsByColumnId: useRecoilValue(
|
||||
recordBoardCardIdsByColumnIdFamilyState(opportunityPipelineStepId),
|
||||
),
|
||||
}),
|
||||
renderHookConfig,
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.createOpportunity(
|
||||
companyIdname,
|
||||
opportunityPipelineStepId,
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.recordBoardCardIdsByColumnId).toStrictEqual([
|
||||
mockedUuid,
|
||||
]);
|
||||
});
|
||||
});
|
||||
@ -1,57 +0,0 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||
|
||||
import { BoardCardIdContext } from '@/object-record/record-board-deprecated/contexts/BoardCardIdContext';
|
||||
import { useCurrentRecordBoardDeprecatedCardSelectedInternal } from '@/object-record/record-board-deprecated/hooks/internal/useCurrentRecordBoardDeprecatedCardSelectedInternal';
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
import { RecordBoardDeprecatedScope } from '@/object-record/record-board-deprecated/scopes/RecordBoardDeprecatedScope';
|
||||
import { actionBarOpenState } from '@/ui/navigation/action-bar/states/actionBarIsOpenState';
|
||||
|
||||
const scopeId = 'scopeId';
|
||||
const boardCardId = 'boardCardId';
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<RecordBoardDeprecatedScope recordBoardScopeId={scopeId}>
|
||||
<BoardCardIdContext.Provider value={boardCardId}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</BoardCardIdContext.Provider>
|
||||
</RecordBoardDeprecatedScope>
|
||||
);
|
||||
|
||||
describe('useCurrentRecordBoardDeprecatedCardSelectedInternal', () => {
|
||||
it('should update the data when selecting and deselecting the cardId', () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
currentCardSelect:
|
||||
useCurrentRecordBoardDeprecatedCardSelectedInternal(),
|
||||
activeCardIdsState: useRecoilValue(
|
||||
useRecordBoardDeprecatedScopedStates().activeCardIdsState,
|
||||
),
|
||||
actionBarOpenState: useRecoilValue(actionBarOpenState),
|
||||
}),
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current.activeCardIdsState).toStrictEqual([]);
|
||||
expect(result.current.actionBarOpenState).toBe(false);
|
||||
expect(result.current.currentCardSelect.isCurrentCardSelected).toBe(false);
|
||||
|
||||
act(() => {
|
||||
result.current.currentCardSelect.setCurrentCardSelected(true);
|
||||
});
|
||||
|
||||
expect(result.current.activeCardIdsState).toStrictEqual([boardCardId]);
|
||||
expect(result.current.actionBarOpenState).toBe(true);
|
||||
expect(result.current.currentCardSelect.isCurrentCardSelected).toBe(true);
|
||||
|
||||
act(() => {
|
||||
result.current.currentCardSelect.setCurrentCardSelected(false);
|
||||
});
|
||||
|
||||
expect(result.current.activeCardIdsState).toStrictEqual([]);
|
||||
expect(result.current.actionBarOpenState).toBe(false);
|
||||
expect(result.current.currentCardSelect.isCurrentCardSelected).toBe(false);
|
||||
});
|
||||
});
|
||||
@ -1,124 +0,0 @@
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import gql from 'graphql-tag';
|
||||
import { RecoilRoot, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { BoardCardIdContext } from '@/object-record/record-board-deprecated/contexts/BoardCardIdContext';
|
||||
import { useCreateOpportunity } from '@/object-record/record-board-deprecated/hooks/internal/useCreateOpportunity';
|
||||
import { useCurrentRecordBoardDeprecatedCardSelectedInternal } from '@/object-record/record-board-deprecated/hooks/internal/useCurrentRecordBoardDeprecatedCardSelectedInternal';
|
||||
import { useDeleteSelectedRecordBoardDeprecatedCardsInternal } from '@/object-record/record-board-deprecated/hooks/internal/useDeleteSelectedRecordBoardDeprecatedCardsInternal';
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
import { RecordBoardDeprecatedScope } from '@/object-record/record-board-deprecated/scopes/RecordBoardDeprecatedScope';
|
||||
import { recordBoardCardIdsByColumnIdFamilyState } from '@/object-record/record-board-deprecated/states/recordBoardCardIdsByColumnIdFamilyState';
|
||||
|
||||
jest.mock('@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery', () => ({
|
||||
useMapFieldMetadataToGraphQLQuery: jest.fn().mockReturnValue(() => '\n'),
|
||||
}));
|
||||
|
||||
const mockedUuid = 'mocked-uuid';
|
||||
jest.mock('uuid', () => ({ v4: () => mockedUuid }));
|
||||
|
||||
const mocks = [
|
||||
{
|
||||
request: {
|
||||
query: gql`
|
||||
mutation DeleteManyOpportunities($filter: OpportunityFilterInput!) {
|
||||
deleteOpportunities(filter: $filter) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { filter: { id: { in: [mockedUuid] } } },
|
||||
},
|
||||
result: jest.fn(() => ({
|
||||
data: { deleteOpportunities: { id: '' } },
|
||||
})),
|
||||
},
|
||||
{
|
||||
request: {
|
||||
query: gql`
|
||||
mutation CreateOneOpportunity($input: OpportunityCreateInput!) {
|
||||
createOpportunity(data: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
input: {
|
||||
id: mockedUuid,
|
||||
pipelineStepId: 'pipelineStepId',
|
||||
companyId: 'New Opportunity',
|
||||
},
|
||||
},
|
||||
},
|
||||
result: jest.fn(() => ({
|
||||
data: { createOpportunity: { id: '' } },
|
||||
})),
|
||||
},
|
||||
];
|
||||
|
||||
const scopeId = 'scopeId';
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MockedProvider mocks={mocks} addTypename={false}>
|
||||
<RecordBoardDeprecatedScope recordBoardScopeId={scopeId}>
|
||||
<BoardCardIdContext.Provider value={mockedUuid}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</BoardCardIdContext.Provider>
|
||||
</RecordBoardDeprecatedScope>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
describe('useDeleteSelectedRecordBoardDeprecatedCardsInternal', () => {
|
||||
it('should run apollo mutation and update recoil state when delete selected cards', async () => {
|
||||
const companyIdname = 'New Opportunity';
|
||||
const opportunityPipelineStepId = 'pipelineStepId';
|
||||
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
createOpportunity: useCreateOpportunity(),
|
||||
deleteSelectedCards:
|
||||
useDeleteSelectedRecordBoardDeprecatedCardsInternal(),
|
||||
setBoardColumns: useSetRecoilState(
|
||||
useRecordBoardDeprecatedScopedStates({
|
||||
recordBoardScopeId: scopeId,
|
||||
}).boardColumnsState,
|
||||
),
|
||||
recordBoardCardIdsByColumnId: useRecoilValue(
|
||||
recordBoardCardIdsByColumnIdFamilyState(opportunityPipelineStepId),
|
||||
),
|
||||
currentSelect: useCurrentRecordBoardDeprecatedCardSelectedInternal(),
|
||||
}),
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.currentSelect.setCurrentCardSelected(true);
|
||||
result.current.setBoardColumns([
|
||||
{
|
||||
id: opportunityPipelineStepId,
|
||||
title: '1',
|
||||
position: 1,
|
||||
},
|
||||
]);
|
||||
result.current.createOpportunity(
|
||||
companyIdname,
|
||||
opportunityPipelineStepId,
|
||||
);
|
||||
});
|
||||
|
||||
expect(result.current.recordBoardCardIdsByColumnId).toStrictEqual([
|
||||
mockedUuid,
|
||||
]);
|
||||
await act(async () => {
|
||||
await result.current.deleteSelectedCards();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.recordBoardCardIdsByColumnId).toStrictEqual([]);
|
||||
expect(mocks[0].result).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,54 +0,0 @@
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useDeleteSelectedRecordBoardDeprecatedCardsInternal } from '@/object-record/record-board-deprecated/hooks/internal/useDeleteSelectedRecordBoardDeprecatedCardsInternal';
|
||||
import { useRecordBoardDeprecatedActionBarEntriesInternal } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedActionBarEntriesInternal';
|
||||
import { RecordBoardDeprecatedScope } from '@/object-record/record-board-deprecated/scopes/RecordBoardDeprecatedScope';
|
||||
import { IconTrash } from '@/ui/display/icon';
|
||||
import { actionBarEntriesState } from '@/ui/navigation/action-bar/states/actionBarEntriesState';
|
||||
|
||||
const scopeId = 'scopeId';
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MockedProvider>
|
||||
<RecordBoardDeprecatedScope recordBoardScopeId={scopeId}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</RecordBoardDeprecatedScope>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
const renderHookConfig = {
|
||||
wrapper: Wrapper,
|
||||
};
|
||||
|
||||
describe('useRecordBoardDeprecatedActionBarEntriesInternal', () => {
|
||||
it('should update actionBarEntries', async () => {
|
||||
const { result } = renderHook(() => {
|
||||
const deleteSelectedBoardCards =
|
||||
useDeleteSelectedRecordBoardDeprecatedCardsInternal();
|
||||
const newActionBarEntry = {
|
||||
label: 'Delete',
|
||||
Icon: IconTrash,
|
||||
accent: 'danger',
|
||||
onClick: deleteSelectedBoardCards,
|
||||
};
|
||||
return {
|
||||
setActionBarEntries: useRecordBoardDeprecatedActionBarEntriesInternal(),
|
||||
actionBarEntries: useRecoilValue(actionBarEntriesState),
|
||||
newActionBarEntry,
|
||||
};
|
||||
}, renderHookConfig);
|
||||
|
||||
expect(result.current.actionBarEntries).toStrictEqual([]);
|
||||
|
||||
act(() => {
|
||||
result.current.setActionBarEntries.setActionBarEntries();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(JSON.stringify(result.current.actionBarEntries)).toBe(
|
||||
JSON.stringify([result.current.newActionBarEntry]),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,117 +0,0 @@
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { RecoilRoot, useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useRecordBoardDeprecatedCardFieldsInternal } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedCardFieldsInternal';
|
||||
import { onFieldsChangeScopedState } from '@/object-record/record-board-deprecated/states/onFieldsChangeScopedState';
|
||||
import { recordBoardCardFieldsScopedState } from '@/object-record/record-board-deprecated/states/recordBoardDeprecatedCardFieldsScopedState';
|
||||
import { savedRecordBoardDeprecatedCardFieldsScopedState } from '@/object-record/record-board-deprecated/states/savedRecordBoardDeprecatedCardFieldsScopedState';
|
||||
import { FieldType } from '@/object-record/record-field/types/FieldType';
|
||||
|
||||
const recordBoardScopeId = 'recordBoardScopeId';
|
||||
|
||||
const renderHookConfig = {
|
||||
wrapper: RecoilRoot,
|
||||
};
|
||||
|
||||
describe('useRecordBoardDeprecatedCardFieldsInternal', () => {
|
||||
it('should toggle field visibility', async () => {
|
||||
const { result } = renderHook(() => {
|
||||
const [cardFieldsList, setCardFieldsList] = useRecoilState(
|
||||
recordBoardCardFieldsScopedState({ scopeId: recordBoardScopeId }),
|
||||
);
|
||||
return {
|
||||
boardCardFields: useRecordBoardDeprecatedCardFieldsInternal({
|
||||
recordBoardScopeId,
|
||||
}),
|
||||
cardFieldsList,
|
||||
setCardFieldsList,
|
||||
};
|
||||
}, renderHookConfig);
|
||||
|
||||
const field = {
|
||||
position: 0,
|
||||
fieldMetadataId: 'id',
|
||||
label: 'label',
|
||||
iconName: 'icon',
|
||||
type: 'TEXT' as FieldType,
|
||||
metadata: {
|
||||
fieldName: 'fieldName',
|
||||
},
|
||||
isVisible: true,
|
||||
};
|
||||
|
||||
act(() => {
|
||||
result.current.setCardFieldsList([field]);
|
||||
});
|
||||
|
||||
expect(result.current.cardFieldsList[0].isVisible).toBe(true);
|
||||
|
||||
act(() => {
|
||||
result.current.boardCardFields.handleFieldVisibilityChange({
|
||||
...field,
|
||||
isVisible: true,
|
||||
});
|
||||
});
|
||||
|
||||
waitFor(() => {
|
||||
expect(result.current.cardFieldsList[0].isVisible).toBe(false);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.boardCardFields.handleFieldVisibilityChange({
|
||||
...field,
|
||||
isVisible: false,
|
||||
});
|
||||
});
|
||||
|
||||
waitFor(() => {
|
||||
expect(result.current.cardFieldsList[0].isVisible).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should call the onFieldsChange callback and update board card states', async () => {
|
||||
const { result } = renderHook(() => {
|
||||
const [onFieldsChange, setOnFieldsChange] = useRecoilState(
|
||||
onFieldsChangeScopedState({ scopeId: recordBoardScopeId }),
|
||||
);
|
||||
return {
|
||||
boardCardFieldsHook: useRecordBoardDeprecatedCardFieldsInternal({
|
||||
recordBoardScopeId,
|
||||
}),
|
||||
boardCardFieldsList: useRecoilValue(
|
||||
recordBoardCardFieldsScopedState({ scopeId: recordBoardScopeId }),
|
||||
),
|
||||
savedBoardCardFieldsList: useRecoilValue(
|
||||
savedRecordBoardDeprecatedCardFieldsScopedState({
|
||||
scopeId: recordBoardScopeId,
|
||||
}),
|
||||
),
|
||||
onFieldsChange,
|
||||
setOnFieldsChange,
|
||||
};
|
||||
}, renderHookConfig);
|
||||
|
||||
const field = {
|
||||
position: 0,
|
||||
fieldMetadataId: 'id',
|
||||
label: 'label',
|
||||
iconName: 'icon',
|
||||
type: 'TEXT' as FieldType,
|
||||
metadata: {
|
||||
fieldName: 'fieldName',
|
||||
},
|
||||
isVisible: true,
|
||||
};
|
||||
const onChangeFunction = jest.fn();
|
||||
|
||||
await act(async () => {
|
||||
result.current.setOnFieldsChange(() => onChangeFunction);
|
||||
result.current.boardCardFieldsHook.handleFieldsReorder([field]);
|
||||
});
|
||||
|
||||
expect(onChangeFunction).toHaveBeenCalledWith([field]);
|
||||
expect(result.current.savedBoardCardFieldsList).toStrictEqual([field]);
|
||||
expect(result.current.boardCardFieldsList).toStrictEqual([field]);
|
||||
});
|
||||
});
|
||||
@ -1,128 +0,0 @@
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import gql from 'graphql-tag';
|
||||
import { RecoilRoot, useRecoilState, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useBoardColumnsInternal } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedColumnsInternal';
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
import { RecordBoardDeprecatedScope } from '@/object-record/record-board-deprecated/scopes/RecordBoardDeprecatedScope';
|
||||
import { BoardColumnDefinition } from '@/object-record/record-board-deprecated/types/BoardColumnDefinition';
|
||||
|
||||
jest.mock('@/object-metadata/hooks/useMapFieldMetadataToGraphQLQuery', () => ({
|
||||
useMapFieldMetadataToGraphQLQuery: jest.fn().mockReturnValue(() => '\n'),
|
||||
}));
|
||||
|
||||
const mocks = [
|
||||
{
|
||||
request: {
|
||||
query: gql`
|
||||
mutation UpdateOnePipelineStep(
|
||||
$idToUpdate: ID!
|
||||
$input: PipelineStepUpdateInput!
|
||||
) {
|
||||
updatePipelineStep(id: $idToUpdate, data: $input) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { idToUpdate: '1', input: { position: 0 } },
|
||||
},
|
||||
result: jest.fn(() => ({
|
||||
data: { updatePipelineStep: { id: '' } },
|
||||
})),
|
||||
},
|
||||
];
|
||||
|
||||
const scopeId = 'scopeId';
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MockedProvider mocks={mocks} addTypename={false}>
|
||||
<RecordBoardDeprecatedScope recordBoardScopeId={scopeId}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</RecordBoardDeprecatedScope>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
const renderHookConfig = {
|
||||
wrapper: Wrapper,
|
||||
};
|
||||
|
||||
describe('useBoardColumnsInternal', () => {
|
||||
it('should update boardColumns state when moving to left and right', async () => {
|
||||
const { result } = renderHook(() => {
|
||||
const [boardColumnsList, setBoardColumnsList] = useRecoilState(
|
||||
useRecordBoardDeprecatedScopedStates().boardColumnsState,
|
||||
);
|
||||
return {
|
||||
boardColumns: useBoardColumnsInternal(),
|
||||
boardColumnsList,
|
||||
setBoardColumnsList,
|
||||
};
|
||||
}, renderHookConfig);
|
||||
const columns: BoardColumnDefinition[] = [
|
||||
{
|
||||
id: '1',
|
||||
title: '1',
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: '2',
|
||||
position: 1,
|
||||
},
|
||||
];
|
||||
act(() => {
|
||||
result.current.setBoardColumnsList(columns);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.boardColumns.handleMoveBoardColumn('right', columns[0]);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.boardColumnsList).toStrictEqual([
|
||||
{ id: '2', title: '2', position: 0, index: 0 },
|
||||
{ id: '1', title: '1', position: 1, index: 1 },
|
||||
]);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.boardColumns.handleMoveBoardColumn('left', columns[0]);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.boardColumnsList).toStrictEqual([
|
||||
{ id: '1', title: '1', position: 0, index: 0 },
|
||||
{ id: '2', title: '2', position: 1, index: 1 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should call apollo mutation after persistBoardColumns', async () => {
|
||||
const { result } = renderHook(() => {
|
||||
return {
|
||||
boardColumns: useBoardColumnsInternal(),
|
||||
setBoardColumnsList: useSetRecoilState(
|
||||
useRecordBoardDeprecatedScopedStates().boardColumnsState,
|
||||
),
|
||||
};
|
||||
}, renderHookConfig);
|
||||
const columns: BoardColumnDefinition[] = [
|
||||
{
|
||||
id: '1',
|
||||
title: '1',
|
||||
position: 0,
|
||||
},
|
||||
];
|
||||
act(() => {
|
||||
result.current.setBoardColumnsList(columns);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.boardColumns.persistBoardColumns();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mocks[0].result).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,56 +0,0 @@
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useDeleteSelectedRecordBoardDeprecatedCardsInternal } from '@/object-record/record-board-deprecated/hooks/internal/useDeleteSelectedRecordBoardDeprecatedCardsInternal';
|
||||
import { useRecordBoardDeprecatedContextMenuEntriesInternal } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedContextMenuEntriesInternal';
|
||||
import { RecordBoardDeprecatedScope } from '@/object-record/record-board-deprecated/scopes/RecordBoardDeprecatedScope';
|
||||
import { IconTrash } from '@/ui/display/icon';
|
||||
import { contextMenuEntriesState } from '@/ui/navigation/context-menu/states/contextMenuEntriesState';
|
||||
|
||||
const scopeId = 'scopeId';
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MockedProvider>
|
||||
<RecordBoardDeprecatedScope recordBoardScopeId={scopeId}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</RecordBoardDeprecatedScope>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
describe('useRecordBoardDeprecatedContextMenuEntriesInternal', () => {
|
||||
it('should update contextEntries', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
const deleteSelectedBoardCards =
|
||||
useDeleteSelectedRecordBoardDeprecatedCardsInternal();
|
||||
const newContextEntry = {
|
||||
label: 'Delete',
|
||||
Icon: IconTrash,
|
||||
accent: 'danger',
|
||||
onClick: deleteSelectedBoardCards,
|
||||
};
|
||||
return {
|
||||
setContextEntries:
|
||||
useRecordBoardDeprecatedContextMenuEntriesInternal(),
|
||||
contextEntries: useRecoilValue(contextMenuEntriesState),
|
||||
newContextEntry,
|
||||
};
|
||||
},
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current.contextEntries).toStrictEqual([]);
|
||||
|
||||
act(() => {
|
||||
result.current.setContextEntries.setContextMenuEntries();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(JSON.stringify(result.current.contextEntries)).toBe(
|
||||
JSON.stringify([result.current.newContextEntry]),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,58 +0,0 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useSetRecordBoardDeprecatedCardSelectedInternal } from '@/object-record/record-board-deprecated/hooks/internal/useSetRecordBoardDeprecatedCardSelectedInternal';
|
||||
import { RecordBoardDeprecatedScope } from '@/object-record/record-board-deprecated/scopes/RecordBoardDeprecatedScope';
|
||||
import { isRecordBoardDeprecatedCardSelectedFamilyState } from '@/object-record/record-board-deprecated/states/isRecordBoardDeprecatedCardSelectedFamilyState';
|
||||
|
||||
const scopeId = 'scopeId';
|
||||
const boardCardId = 'boardCardId';
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<RecordBoardDeprecatedScope recordBoardScopeId={scopeId}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</RecordBoardDeprecatedScope>
|
||||
);
|
||||
|
||||
const recordBoardScopeId = 'recordBoardScopeId';
|
||||
|
||||
describe('useSetRecordBoardDeprecatedCardSelectedInternal', () => {
|
||||
it('should update the data when selecting and deselecting the cardId', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
return {
|
||||
cardSelect: useSetRecordBoardDeprecatedCardSelectedInternal({
|
||||
recordBoardScopeId,
|
||||
}),
|
||||
isSelected: useRecoilValue(
|
||||
isRecordBoardDeprecatedCardSelectedFamilyState(boardCardId),
|
||||
),
|
||||
};
|
||||
},
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current.isSelected).toBe(false);
|
||||
|
||||
act(() => {
|
||||
result.current.cardSelect.setCardSelected(boardCardId, true);
|
||||
});
|
||||
|
||||
expect(result.current.isSelected).toBe(true);
|
||||
|
||||
act(() => {
|
||||
result.current.cardSelect.setCardSelected(boardCardId, false);
|
||||
});
|
||||
|
||||
expect(result.current.isSelected).toBe(false);
|
||||
|
||||
act(() => {
|
||||
result.current.cardSelect.setCardSelected(boardCardId, true);
|
||||
result.current.cardSelect.unselectAllActiveCards();
|
||||
});
|
||||
|
||||
expect(result.current.isSelected).toBe(false);
|
||||
});
|
||||
});
|
||||
@ -1,113 +0,0 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||
|
||||
import { CompanyForBoard } from '@/companies/types/CompanyProgress';
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
import { useUpdateCompanyBoardColumnsInternal } from '@/object-record/record-board-deprecated/hooks/internal/useUpdateCompanyBoardColumnsInternal';
|
||||
import { RecordBoardDeprecatedScope } from '@/object-record/record-board-deprecated/scopes/RecordBoardDeprecatedScope';
|
||||
import { recordBoardCardIdsByColumnIdFamilyState } from '@/object-record/record-board-deprecated/states/recordBoardCardIdsByColumnIdFamilyState';
|
||||
import { currentPipelineStepsState } from '@/pipeline/states/currentPipelineStepsState';
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
||||
|
||||
const scopeId = 'scopeId';
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<RecordBoardDeprecatedScope recordBoardScopeId={scopeId}>
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
</RecordBoardDeprecatedScope>
|
||||
);
|
||||
|
||||
describe('useUpdateCompanyBoardColumnsInternal', () => {
|
||||
it('should update recoil state after updateCompanyBoardColumns call ', async () => {
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
return {
|
||||
updateCompanyBoardColumns: useUpdateCompanyBoardColumnsInternal(),
|
||||
currentPipeline: useRecoilValue(currentPipelineStepsState),
|
||||
boardColumns: useRecoilValue(
|
||||
useRecordBoardDeprecatedScopedStates().boardColumnsState,
|
||||
),
|
||||
savedBoardColumns: useRecoilValue(
|
||||
useRecordBoardDeprecatedScopedStates().savedBoardColumnsState,
|
||||
),
|
||||
idsByColumnId: useRecoilValue(
|
||||
recordBoardCardIdsByColumnIdFamilyState('1'),
|
||||
),
|
||||
};
|
||||
},
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
const pipelineSteps: PipelineStep[] = [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Step 1',
|
||||
color: 'red',
|
||||
position: 1,
|
||||
createdAt: '2024-01-12',
|
||||
updatedAt: '2024-01-12',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Step 2',
|
||||
color: 'blue',
|
||||
position: 1,
|
||||
createdAt: '2024-01-12',
|
||||
updatedAt: '2024-01-12',
|
||||
},
|
||||
];
|
||||
const opportunity: Opportunity = {
|
||||
id: '123',
|
||||
amount: {
|
||||
amountMicros: 1000000,
|
||||
currencyCode: 'USD',
|
||||
},
|
||||
closeDate: new Date('2024-02-01'),
|
||||
probability: 0.75,
|
||||
pipelineStepId: '1',
|
||||
pipelineStep: pipelineSteps[0],
|
||||
pointOfContactId: '456',
|
||||
pointOfContact: {
|
||||
id: '456',
|
||||
name: {
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
},
|
||||
avatarUrl: 'https://example.com/avatar.jpg',
|
||||
},
|
||||
};
|
||||
|
||||
const companyForBoard: CompanyForBoard = {
|
||||
id: '789',
|
||||
name: 'Acme Inc.',
|
||||
domainName: 'acme.com',
|
||||
};
|
||||
|
||||
expect(result.current.currentPipeline).toStrictEqual([]);
|
||||
expect(result.current.savedBoardColumns).toStrictEqual([]);
|
||||
expect(result.current.boardColumns).toStrictEqual([]);
|
||||
expect(result.current.idsByColumnId).toStrictEqual([]);
|
||||
|
||||
act(() => {
|
||||
result.current.updateCompanyBoardColumns(
|
||||
pipelineSteps,
|
||||
[opportunity],
|
||||
[companyForBoard],
|
||||
);
|
||||
});
|
||||
|
||||
const expectedBoardColumns = [
|
||||
{ id: '1', title: 'Step 1', colorCode: 'red', position: 1 },
|
||||
{ id: '2', title: 'Step 2', colorCode: 'blue', position: 1 },
|
||||
];
|
||||
|
||||
expect(result.current.currentPipeline).toStrictEqual(pipelineSteps);
|
||||
expect(result.current.savedBoardColumns).toStrictEqual(
|
||||
expectedBoardColumns,
|
||||
);
|
||||
expect(result.current.boardColumns).toStrictEqual(expectedBoardColumns);
|
||||
expect(result.current.idsByColumnId).toStrictEqual([opportunity.id]);
|
||||
});
|
||||
});
|
||||
@ -1,36 +0,0 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { recordBoardCardIdsByColumnIdFamilyState } from '@/object-record/record-board-deprecated/states/recordBoardCardIdsByColumnIdFamilyState';
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
|
||||
export const useCreateOpportunity = () => {
|
||||
const { createOneRecord: createOneOpportunity } =
|
||||
useCreateOneRecord<Opportunity>({
|
||||
objectNameSingular: CoreObjectNameSingular.Opportunity,
|
||||
});
|
||||
|
||||
const createOpportunity = useRecoilCallback(
|
||||
({ set }) =>
|
||||
async (companyId: string, pipelineStepId: string) => {
|
||||
const newUuid = v4();
|
||||
|
||||
set(
|
||||
recordBoardCardIdsByColumnIdFamilyState(pipelineStepId),
|
||||
(oldValue) => [...oldValue, newUuid],
|
||||
);
|
||||
|
||||
await createOneOpportunity?.({
|
||||
id: newUuid,
|
||||
name: 'Opportunity',
|
||||
pipelineStepId,
|
||||
companyId: companyId,
|
||||
});
|
||||
},
|
||||
[createOneOpportunity],
|
||||
);
|
||||
|
||||
return createOpportunity;
|
||||
};
|
||||
@ -1,50 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
import { actionBarOpenState } from '@/ui/navigation/action-bar/states/actionBarIsOpenState';
|
||||
|
||||
import { BoardCardIdContext } from '../../contexts/BoardCardIdContext';
|
||||
import { isRecordBoardDeprecatedCardSelectedFamilyState } from '../../states/isRecordBoardDeprecatedCardSelectedFamilyState';
|
||||
|
||||
export const useCurrentRecordBoardDeprecatedCardSelectedInternal = () => {
|
||||
const currentCardId = useContext(BoardCardIdContext);
|
||||
|
||||
const isCurrentCardSelected = useRecoilValue(
|
||||
isRecordBoardDeprecatedCardSelectedFamilyState(currentCardId ?? ''),
|
||||
);
|
||||
|
||||
const { activeCardIdsState } = useRecordBoardDeprecatedScopedStates();
|
||||
|
||||
const setActiveCardIds = useSetRecoilState(activeCardIdsState);
|
||||
|
||||
const setCurrentCardSelected = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(selected: boolean) => {
|
||||
if (!currentCardId) return;
|
||||
|
||||
set(
|
||||
isRecordBoardDeprecatedCardSelectedFamilyState(currentCardId),
|
||||
selected,
|
||||
);
|
||||
set(actionBarOpenState, selected);
|
||||
|
||||
if (selected) {
|
||||
setActiveCardIds((prevActiveCardIds) => [
|
||||
...prevActiveCardIds,
|
||||
currentCardId,
|
||||
]);
|
||||
} else {
|
||||
setActiveCardIds((prevActiveCardIds) =>
|
||||
prevActiveCardIds.filter((id) => id !== currentCardId),
|
||||
);
|
||||
}
|
||||
},
|
||||
[currentCardId, setActiveCardIds],
|
||||
);
|
||||
|
||||
return {
|
||||
isCurrentCardSelected,
|
||||
setCurrentCardSelected,
|
||||
};
|
||||
};
|
||||
@ -1,42 +0,0 @@
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
|
||||
import { useRemoveRecordBoardDeprecatedCardIdsInternal } from './useRemoveRecordBoardDeprecatedCardIdsInternal';
|
||||
|
||||
export const useDeleteSelectedRecordBoardDeprecatedCardsInternal = () => {
|
||||
const removeCardIds = useRemoveRecordBoardDeprecatedCardIdsInternal();
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const { deleteManyRecords: deleteManyOpportunities } = useDeleteManyRecords({
|
||||
objectNameSingular: CoreObjectNameSingular.Opportunity,
|
||||
});
|
||||
|
||||
const { selectedCardIdsSelector } = useRecordBoardDeprecatedScopedStates();
|
||||
|
||||
const deleteSelectedBoardCards = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async () => {
|
||||
const selectedCardIds = snapshot
|
||||
.getLoadable(selectedCardIdsSelector)
|
||||
.getValue();
|
||||
|
||||
await deleteManyOpportunities?.(selectedCardIds);
|
||||
removeCardIds(selectedCardIds);
|
||||
selectedCardIds.forEach((id) => {
|
||||
apolloClient.cache.evict({ id: `Opportunity:${id}` });
|
||||
});
|
||||
},
|
||||
[
|
||||
selectedCardIdsSelector,
|
||||
removeCardIds,
|
||||
deleteManyOpportunities,
|
||||
apolloClient.cache,
|
||||
],
|
||||
);
|
||||
|
||||
return deleteSelectedBoardCards;
|
||||
};
|
||||
@ -1,28 +0,0 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useDeleteSelectedRecordBoardDeprecatedCardsInternal } from '@/object-record/record-board-deprecated/hooks/internal/useDeleteSelectedRecordBoardDeprecatedCardsInternal';
|
||||
import { IconTrash } from '@/ui/display/icon';
|
||||
import { actionBarEntriesState } from '@/ui/navigation/action-bar/states/actionBarEntriesState';
|
||||
|
||||
export const useRecordBoardDeprecatedActionBarEntriesInternal = () => {
|
||||
const setActionBarEntriesRecoil = useSetRecoilState(actionBarEntriesState);
|
||||
|
||||
const deleteSelectedBoardCards =
|
||||
useDeleteSelectedRecordBoardDeprecatedCardsInternal();
|
||||
|
||||
const setActionBarEntries = useCallback(() => {
|
||||
setActionBarEntriesRecoil([
|
||||
{
|
||||
label: 'Delete',
|
||||
Icon: IconTrash,
|
||||
accent: 'danger',
|
||||
onClick: deleteSelectedBoardCards,
|
||||
},
|
||||
]);
|
||||
}, [deleteSelectedBoardCards, setActionBarEntriesRecoil]);
|
||||
|
||||
return {
|
||||
setActionBarEntries,
|
||||
};
|
||||
};
|
||||
@ -1,97 +0,0 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { RecordBoardDeprecatedScopeInternalContext } from '@/object-record/record-board-deprecated/scopes/scope-internal-context/RecordBoardDeprecatedScopeInternalContext';
|
||||
import { onFieldsChangeScopedState } from '@/object-record/record-board-deprecated/states/onFieldsChangeScopedState';
|
||||
import { recordBoardCardFieldsScopedState } from '@/object-record/record-board-deprecated/states/recordBoardDeprecatedCardFieldsScopedState';
|
||||
import { savedRecordBoardDeprecatedCardFieldsScopedState } from '@/object-record/record-board-deprecated/states/savedRecordBoardDeprecatedCardFieldsScopedState';
|
||||
import { BoardFieldDefinition } from '@/object-record/record-board-deprecated/types/BoardFieldDefinition';
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
|
||||
type useRecordBoardDeprecatedCardFieldsInternalProps = {
|
||||
recordBoardScopeId?: string;
|
||||
};
|
||||
|
||||
export const useRecordBoardDeprecatedCardFieldsInternal = (
|
||||
props?: useRecordBoardDeprecatedCardFieldsInternalProps,
|
||||
) => {
|
||||
const scopeId = useAvailableScopeIdOrThrow(
|
||||
RecordBoardDeprecatedScopeInternalContext,
|
||||
props?.recordBoardScopeId,
|
||||
);
|
||||
|
||||
const setBoardCardFields = useSetRecoilState(
|
||||
recordBoardCardFieldsScopedState({ scopeId }),
|
||||
);
|
||||
|
||||
const setSavedBoardCardFields = useSetRecoilState(
|
||||
savedRecordBoardDeprecatedCardFieldsScopedState({ scopeId }),
|
||||
);
|
||||
|
||||
const handleFieldVisibilityChange = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async (
|
||||
field: Omit<ColumnDefinition<FieldMetadata>, 'size' | 'position'>,
|
||||
) => {
|
||||
const existingFields = snapshot
|
||||
.getLoadable(recordBoardCardFieldsScopedState({ scopeId }))
|
||||
.getValue();
|
||||
|
||||
const fieldIndex = existingFields.findIndex(
|
||||
({ fieldMetadataId }) => field.fieldMetadataId === fieldMetadataId,
|
||||
);
|
||||
const fields = [...existingFields];
|
||||
|
||||
if (fieldIndex === -1) {
|
||||
fields.push({ ...field, position: existingFields.length });
|
||||
} else {
|
||||
fields[fieldIndex] = {
|
||||
...field,
|
||||
isVisible: !field.isVisible,
|
||||
position: existingFields.length,
|
||||
};
|
||||
}
|
||||
|
||||
setSavedBoardCardFields(fields);
|
||||
setBoardCardFields(fields);
|
||||
|
||||
const onFieldsChange = snapshot
|
||||
.getLoadable(onFieldsChangeScopedState({ scopeId }))
|
||||
.getValue();
|
||||
|
||||
onFieldsChange?.(fields);
|
||||
},
|
||||
[scopeId, setBoardCardFields, setSavedBoardCardFields],
|
||||
);
|
||||
|
||||
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 };
|
||||
};
|
||||
@ -1,58 +0,0 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
||||
import { useMoveViewColumns } from '@/views/hooks/useMoveViewColumns';
|
||||
|
||||
import { BoardColumnDefinition } from '../../types/BoardColumnDefinition';
|
||||
|
||||
export const useBoardColumnsInternal = () => {
|
||||
const { boardColumnsState } = useRecordBoardDeprecatedScopedStates();
|
||||
const [boardColumns, setBoardColumns] = useRecoilState(boardColumnsState);
|
||||
|
||||
const { handleColumnMove } = useMoveViewColumns();
|
||||
|
||||
const { updateOneRecord: updateOnePipelineStep } =
|
||||
useUpdateOneRecord<PipelineStep>({
|
||||
objectNameSingular: CoreObjectNameSingular.PipelineStep,
|
||||
});
|
||||
|
||||
const updatedPipelineSteps = (stages: BoardColumnDefinition[]) => {
|
||||
if (!stages.length) return;
|
||||
|
||||
return Promise.all(
|
||||
stages.map(
|
||||
(stage) =>
|
||||
updateOnePipelineStep?.({
|
||||
idToUpdate: stage.id,
|
||||
updateOneRecordInput: {
|
||||
position: stage.position,
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const persistBoardColumns = async () => {
|
||||
await updatedPipelineSteps(boardColumns);
|
||||
};
|
||||
|
||||
const handleMoveBoardColumn = (
|
||||
direction: 'left' | 'right',
|
||||
column: BoardColumnDefinition,
|
||||
) => {
|
||||
const currentColumnArrayIndex = boardColumns.findIndex(
|
||||
(tableColumn) => tableColumn.id === column.id,
|
||||
);
|
||||
const columns = handleColumnMove(
|
||||
direction,
|
||||
currentColumnArrayIndex,
|
||||
boardColumns,
|
||||
);
|
||||
setBoardColumns(columns);
|
||||
};
|
||||
|
||||
return { handleMoveBoardColumn, persistBoardColumns };
|
||||
};
|
||||
@ -1,30 +0,0 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useDeleteSelectedRecordBoardDeprecatedCardsInternal } from '@/object-record/record-board-deprecated/hooks/internal/useDeleteSelectedRecordBoardDeprecatedCardsInternal';
|
||||
import { IconTrash } from '@/ui/display/icon';
|
||||
import { contextMenuEntriesState } from '@/ui/navigation/context-menu/states/contextMenuEntriesState';
|
||||
|
||||
export const useRecordBoardDeprecatedContextMenuEntriesInternal = () => {
|
||||
const setContextMenuEntriesRecoil = useSetRecoilState(
|
||||
contextMenuEntriesState,
|
||||
);
|
||||
|
||||
const deleteSelectedBoardCards =
|
||||
useDeleteSelectedRecordBoardDeprecatedCardsInternal();
|
||||
|
||||
const setContextMenuEntries = useCallback(() => {
|
||||
setContextMenuEntriesRecoil([
|
||||
{
|
||||
label: 'Delete',
|
||||
Icon: IconTrash,
|
||||
accent: 'danger',
|
||||
onClick: deleteSelectedBoardCards,
|
||||
},
|
||||
]);
|
||||
}, [deleteSelectedBoardCards, setContextMenuEntriesRecoil]);
|
||||
|
||||
return {
|
||||
setContextMenuEntries,
|
||||
};
|
||||
};
|
||||
@ -1,59 +0,0 @@
|
||||
import { RecordBoardDeprecatedScopeInternalContext } from '@/object-record/record-board-deprecated/scopes/scope-internal-context/RecordBoardDeprecatedScopeInternalContext';
|
||||
import { getRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/utils/getRecordBoardDeprecatedScopedStates';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
|
||||
type useRecordBoardDeprecatedScopedStatesProps = {
|
||||
recordBoardScopeId?: string;
|
||||
};
|
||||
|
||||
export const useRecordBoardDeprecatedScopedStates = (
|
||||
args?: useRecordBoardDeprecatedScopedStatesProps,
|
||||
) => {
|
||||
const { recordBoardScopeId } = args ?? {};
|
||||
|
||||
const scopeId = useAvailableScopeIdOrThrow(
|
||||
RecordBoardDeprecatedScopeInternalContext,
|
||||
recordBoardScopeId,
|
||||
);
|
||||
|
||||
const {
|
||||
activeCardIdsState,
|
||||
availableBoardCardFieldsState,
|
||||
boardColumnsState,
|
||||
isBoardLoadedState,
|
||||
isCompactViewEnabledState,
|
||||
savedBoardColumnsState,
|
||||
boardFiltersState,
|
||||
boardSortsState,
|
||||
onFieldsChangeState,
|
||||
boardCardFieldsByKeySelector,
|
||||
hiddenBoardCardFieldsSelector,
|
||||
selectedCardIdsSelector,
|
||||
visibleBoardCardFieldsSelector,
|
||||
savedCompaniesState,
|
||||
savedOpportunitiesState,
|
||||
savedPipelineStepsState,
|
||||
} = getRecordBoardDeprecatedScopedStates({
|
||||
recordBoardScopeId: scopeId,
|
||||
});
|
||||
|
||||
return {
|
||||
scopeId,
|
||||
activeCardIdsState,
|
||||
availableBoardCardFieldsState,
|
||||
boardColumnsState,
|
||||
isBoardLoadedState,
|
||||
isCompactViewEnabledState,
|
||||
savedBoardColumnsState,
|
||||
boardFiltersState,
|
||||
boardSortsState,
|
||||
onFieldsChangeState,
|
||||
boardCardFieldsByKeySelector,
|
||||
hiddenBoardCardFieldsSelector,
|
||||
selectedCardIdsSelector,
|
||||
visibleBoardCardFieldsSelector,
|
||||
savedCompaniesState,
|
||||
savedOpportunitiesState,
|
||||
savedPipelineStepsState,
|
||||
};
|
||||
};
|
||||
@ -1,30 +0,0 @@
|
||||
// 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 { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
|
||||
import { recordBoardCardIdsByColumnIdFamilyState } from '../../states/recordBoardCardIdsByColumnIdFamilyState';
|
||||
|
||||
export const useRemoveRecordBoardDeprecatedCardIdsInternal = () => {
|
||||
const { boardColumnsState } = useRecordBoardDeprecatedScopedStates();
|
||||
|
||||
return useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(cardIdToRemove: string[]) => {
|
||||
const boardColumns = snapshot.getLoadable(boardColumnsState).getValue();
|
||||
|
||||
boardColumns.forEach((boardColumn) => {
|
||||
const columnCardIds = snapshot
|
||||
.getLoadable(
|
||||
recordBoardCardIdsByColumnIdFamilyState(boardColumn.id),
|
||||
)
|
||||
.getValue();
|
||||
set(
|
||||
recordBoardCardIdsByColumnIdFamilyState(boardColumn.id),
|
||||
columnCardIds.filter((cardId) => !cardIdToRemove.includes(cardId)),
|
||||
);
|
||||
});
|
||||
},
|
||||
[boardColumnsState],
|
||||
);
|
||||
};
|
||||
@ -1,62 +0,0 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
import { RecordBoardDeprecatedScopeInternalContext } from '@/object-record/record-board-deprecated/scopes/scope-internal-context/RecordBoardDeprecatedScopeInternalContext';
|
||||
import { actionBarOpenState } from '@/ui/navigation/action-bar/states/actionBarIsOpenState';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
|
||||
import { isRecordBoardDeprecatedCardSelectedFamilyState } from '../../states/isRecordBoardDeprecatedCardSelectedFamilyState';
|
||||
|
||||
export const useSetRecordBoardDeprecatedCardSelectedInternal = (props: any) => {
|
||||
const scopeId = useAvailableScopeIdOrThrow(
|
||||
RecordBoardDeprecatedScopeInternalContext,
|
||||
props?.recordBoardScopeId,
|
||||
);
|
||||
const { activeCardIdsState } = useRecordBoardDeprecatedScopedStates({
|
||||
recordBoardScopeId: scopeId,
|
||||
});
|
||||
|
||||
const setCardSelected = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
(cardId: string, selected: boolean) => {
|
||||
const activeCardIds = snapshot
|
||||
.getLoadable(activeCardIdsState)
|
||||
.getValue();
|
||||
|
||||
set(isRecordBoardDeprecatedCardSelectedFamilyState(cardId), selected);
|
||||
set(actionBarOpenState, selected || activeCardIds.length > 0);
|
||||
|
||||
if (selected) {
|
||||
set(activeCardIdsState, [...activeCardIds, cardId]);
|
||||
} else {
|
||||
set(
|
||||
activeCardIdsState,
|
||||
activeCardIds.filter((id: string) => id !== cardId),
|
||||
);
|
||||
}
|
||||
},
|
||||
[activeCardIdsState],
|
||||
);
|
||||
|
||||
const unselectAllActiveCards = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
() => {
|
||||
const activeCardIds = snapshot
|
||||
.getLoadable(activeCardIdsState)
|
||||
.getValue();
|
||||
|
||||
activeCardIds.forEach((cardId: string) => {
|
||||
set(isRecordBoardDeprecatedCardSelectedFamilyState(cardId), false);
|
||||
});
|
||||
|
||||
set(activeCardIdsState, []);
|
||||
set(actionBarOpenState, false);
|
||||
},
|
||||
[activeCardIdsState],
|
||||
);
|
||||
|
||||
return {
|
||||
setCardSelected,
|
||||
unselectAllActiveCards,
|
||||
};
|
||||
};
|
||||
@ -1,148 +0,0 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
import { recordBoardCardIdsByColumnIdFamilyState } from '@/object-record/record-board-deprecated/states/recordBoardCardIdsByColumnIdFamilyState';
|
||||
import { BoardColumnDefinition } from '@/object-record/record-board-deprecated/types/BoardColumnDefinition';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { currentPipelineStepsState } from '@/pipeline/states/currentPipelineStepsState';
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
||||
import { themeColorSchema } from '@/ui/theme/utils/themeColorSchema';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
import { logError } from '~/utils/logError';
|
||||
|
||||
import { companyProgressesFamilyState } from '../../../../companies/states/companyProgressesFamilyState';
|
||||
import {
|
||||
CompanyForBoard,
|
||||
CompanyProgressDict,
|
||||
} from '../../../../companies/types/CompanyProgress';
|
||||
|
||||
export const useUpdateCompanyBoardColumnsInternal = () => {
|
||||
const { boardColumnsState, savedBoardColumnsState } =
|
||||
useRecordBoardDeprecatedScopedStates();
|
||||
|
||||
return useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
(
|
||||
pipelineSteps: PipelineStep[],
|
||||
opportunities: Opportunity[],
|
||||
companies: CompanyForBoard[],
|
||||
) => {
|
||||
const indexCompanyByIdReducer = (
|
||||
acc: { [key: string]: CompanyForBoard },
|
||||
company: CompanyForBoard,
|
||||
) => ({
|
||||
...acc,
|
||||
[company.id]: company,
|
||||
});
|
||||
|
||||
const companiesDict =
|
||||
companies.reduce(
|
||||
indexCompanyByIdReducer,
|
||||
{} as { [key: string]: CompanyForBoard },
|
||||
) ?? {};
|
||||
|
||||
const indexOpportunityByIdReducer = (
|
||||
acc: CompanyProgressDict,
|
||||
opportunity: Opportunity,
|
||||
) => {
|
||||
const company =
|
||||
opportunity.companyId && companiesDict[opportunity.companyId];
|
||||
|
||||
if (!company) return acc;
|
||||
|
||||
return {
|
||||
...acc,
|
||||
[opportunity.id]: {
|
||||
opportunity,
|
||||
company,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const companyBoardIndex = opportunities.reduce(
|
||||
indexOpportunityByIdReducer,
|
||||
{} as CompanyProgressDict,
|
||||
);
|
||||
|
||||
for (const [id, companyProgress] of Object.entries(companyBoardIndex)) {
|
||||
const currentCompanyProgress = snapshot
|
||||
.getLoadable(companyProgressesFamilyState(id))
|
||||
.getValue();
|
||||
|
||||
if (!isDeeplyEqual(currentCompanyProgress, companyProgress)) {
|
||||
set(companyProgressesFamilyState(id), companyProgress);
|
||||
set(recordStoreFamilyState(id), companyProgress.opportunity);
|
||||
}
|
||||
}
|
||||
|
||||
const currentPipelineSteps = snapshot
|
||||
.getLoadable(currentPipelineStepsState)
|
||||
.getValue();
|
||||
|
||||
const currentBoardColumns = snapshot
|
||||
.getLoadable(boardColumnsState)
|
||||
.getValue();
|
||||
|
||||
if (!isDeeplyEqual(pipelineSteps, currentPipelineSteps)) {
|
||||
set(currentPipelineStepsState, pipelineSteps);
|
||||
}
|
||||
|
||||
const orderedPipelineSteps = [...pipelineSteps].sort((a, b) => {
|
||||
if (!a.position || !b.position) return 0;
|
||||
return a.position - b.position;
|
||||
});
|
||||
|
||||
const newBoardColumns: BoardColumnDefinition[] =
|
||||
orderedPipelineSteps?.map((pipelineStep) => {
|
||||
const colorValidationResult = themeColorSchema.safeParse(
|
||||
pipelineStep.color,
|
||||
);
|
||||
|
||||
if (!colorValidationResult.success) {
|
||||
logError(
|
||||
`Color ${pipelineStep.color} is not recognized in useUpdateCompanyBoard.`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
id: pipelineStep.id,
|
||||
title: pipelineStep.name,
|
||||
colorCode: colorValidationResult.success
|
||||
? colorValidationResult.data
|
||||
: undefined,
|
||||
position: pipelineStep.position ?? 0,
|
||||
};
|
||||
});
|
||||
if (
|
||||
currentBoardColumns.length === 0 &&
|
||||
!isDeeplyEqual(newBoardColumns, currentBoardColumns)
|
||||
) {
|
||||
set(boardColumnsState, newBoardColumns);
|
||||
set(savedBoardColumnsState, newBoardColumns);
|
||||
}
|
||||
for (const boardColumn of newBoardColumns) {
|
||||
const boardCardIds = opportunities
|
||||
.filter(
|
||||
(opportunityToFilter) =>
|
||||
opportunityToFilter.pipelineStepId === boardColumn.id,
|
||||
)
|
||||
.map((opportunity) => opportunity.id);
|
||||
|
||||
const currentBoardCardIds = snapshot
|
||||
.getLoadable(
|
||||
recordBoardCardIdsByColumnIdFamilyState(boardColumn.id),
|
||||
)
|
||||
.getValue();
|
||||
|
||||
if (!isDeeplyEqual(currentBoardCardIds, boardCardIds)) {
|
||||
set(
|
||||
recordBoardCardIdsByColumnIdFamilyState(boardColumn.id),
|
||||
boardCardIds,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
[boardColumnsState, savedBoardColumnsState],
|
||||
);
|
||||
};
|
||||
@ -1,39 +0,0 @@
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useCreateOpportunity } from '@/object-record/record-board-deprecated/hooks/internal/useCreateOpportunity';
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
import { RecordBoardDeprecatedScopeInternalContext } from '@/object-record/record-board-deprecated/scopes/scope-internal-context/RecordBoardDeprecatedScopeInternalContext';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
|
||||
type useRecordBoardDeprecatedProps = {
|
||||
recordBoardScopeId?: string;
|
||||
};
|
||||
|
||||
export const useRecordBoardDeprecated = (
|
||||
props?: useRecordBoardDeprecatedProps,
|
||||
) => {
|
||||
const scopeId = useAvailableScopeIdOrThrow(
|
||||
RecordBoardDeprecatedScopeInternalContext,
|
||||
props?.recordBoardScopeId,
|
||||
);
|
||||
|
||||
const { isBoardLoadedState, boardColumnsState, onFieldsChangeState } =
|
||||
useRecordBoardDeprecatedScopedStates({
|
||||
recordBoardScopeId: scopeId,
|
||||
});
|
||||
const setIsBoardLoaded = useSetRecoilState(isBoardLoadedState);
|
||||
|
||||
const setBoardColumns = useSetRecoilState(boardColumnsState);
|
||||
|
||||
const createOpportunity = useCreateOpportunity();
|
||||
|
||||
const setOnFieldsChange = useSetRecoilState(onFieldsChangeState);
|
||||
|
||||
return {
|
||||
scopeId,
|
||||
setIsBoardLoaded,
|
||||
setBoardColumns,
|
||||
createOpportunity,
|
||||
setOnFieldsChange,
|
||||
};
|
||||
};
|
||||
@ -1,39 +0,0 @@
|
||||
import { BOARD_OPTIONS_DROPDOWN_ID } from '@/object-record/record-board-deprecated/constants/BoardOptionsDropdownId';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
|
||||
import { Dropdown } from '../../../../ui/layout/dropdown/components/Dropdown';
|
||||
import { BoardOptionsHotkeyScope } from '../../types/BoardOptionsHotkeyScope';
|
||||
|
||||
import { RecordBoardDeprecatedOptionsDropdownButton } from './RecordBoardDeprecatedOptionsDropdownButton';
|
||||
import {
|
||||
RecordBoardDeprecatedOptionsDropdownContent,
|
||||
RecordBoardDeprecatedOptionsDropdownContentProps,
|
||||
} from './RecordBoardDeprecatedOptionsDropdownContent';
|
||||
|
||||
type RecordBoardDeprecatedOptionsDropdownProps = Pick<
|
||||
RecordBoardDeprecatedOptionsDropdownContentProps,
|
||||
'onStageAdd' | 'recordBoardId'
|
||||
>;
|
||||
|
||||
export const RecordBoardDeprecatedOptionsDropdown = ({
|
||||
onStageAdd,
|
||||
recordBoardId,
|
||||
}: RecordBoardDeprecatedOptionsDropdownProps) => {
|
||||
const { setViewEditMode } = useViewBar();
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
dropdownId={BOARD_OPTIONS_DROPDOWN_ID}
|
||||
clickableComponent={<RecordBoardDeprecatedOptionsDropdownButton />}
|
||||
dropdownComponents={
|
||||
<RecordBoardDeprecatedOptionsDropdownContent
|
||||
onStageAdd={onStageAdd}
|
||||
recordBoardId={recordBoardId}
|
||||
/>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
|
||||
onClickOutside={() => setViewEditMode('none')}
|
||||
dropdownMenuWidth={170}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,22 +0,0 @@
|
||||
import { BOARD_OPTIONS_DROPDOWN_ID } from '@/object-record/record-board-deprecated/constants/BoardOptionsDropdownId';
|
||||
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
|
||||
export const RecordBoardDeprecatedOptionsDropdownButton = () => {
|
||||
const { isDropdownOpen, toggleDropdown } = useDropdown(
|
||||
BOARD_OPTIONS_DROPDOWN_ID,
|
||||
);
|
||||
|
||||
const handleClick = () => {
|
||||
toggleDropdown();
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledHeaderDropdownButton
|
||||
isUnfolded={isDropdownOpen}
|
||||
onClick={handleClick}
|
||||
>
|
||||
Options
|
||||
</StyledHeaderDropdownButton>
|
||||
);
|
||||
};
|
||||
@ -1,240 +0,0 @@
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { OnDragEndResponder } from '@hello-pangea/dnd';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { BOARD_OPTIONS_DROPDOWN_ID } from '@/object-record/record-board-deprecated/constants/BoardOptionsDropdownId';
|
||||
import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-board-deprecated/hooks/internal/useRecordBoardDeprecatedScopedStates';
|
||||
import {
|
||||
IconBaselineDensitySmall,
|
||||
IconChevronLeft,
|
||||
IconLayoutKanban,
|
||||
IconPlus,
|
||||
IconTag,
|
||||
} from '@/ui/display/icon';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
||||
import { DropdownMenuInput } from '@/ui/layout/dropdown/components/DropdownMenuInput';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { MenuItemNavigate } from '@/ui/navigation/menu-item/components/MenuItemNavigate';
|
||||
import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemToggle';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
import { moveArrayItem } from '~/utils/array/moveArrayItem';
|
||||
|
||||
import { useRecordBoardDeprecatedCardFieldsInternal } from '../../hooks/internal/useRecordBoardDeprecatedCardFieldsInternal';
|
||||
import { BoardColumnDefinition } from '../../types/BoardColumnDefinition';
|
||||
import { BoardOptionsHotkeyScope } from '../../types/BoardOptionsHotkeyScope';
|
||||
|
||||
export type RecordBoardDeprecatedOptionsDropdownContentProps = {
|
||||
onStageAdd?: (boardColumn: BoardColumnDefinition) => void;
|
||||
recordBoardId: string;
|
||||
};
|
||||
|
||||
type BoardOptionsMenu = 'fields' | 'stage-creation' | 'stages';
|
||||
|
||||
export const RecordBoardDeprecatedOptionsDropdownContent = ({
|
||||
onStageAdd,
|
||||
recordBoardId,
|
||||
}: RecordBoardDeprecatedOptionsDropdownContentProps) => {
|
||||
const { setViewEditMode, handleViewNameSubmit } = useViewBar();
|
||||
const { viewEditModeState, currentViewSelector } = useViewScopedStates();
|
||||
|
||||
const viewEditMode = useRecoilValue(viewEditModeState);
|
||||
const currentView = useRecoilValue(currentViewSelector);
|
||||
|
||||
const stageInputRef = useRef<HTMLInputElement>(null);
|
||||
const viewEditInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const [currentMenu, setCurrentMenu] = useState<
|
||||
BoardOptionsMenu | undefined
|
||||
>();
|
||||
|
||||
const {
|
||||
boardColumnsState,
|
||||
isCompactViewEnabledState,
|
||||
hiddenBoardCardFieldsSelector,
|
||||
visibleBoardCardFieldsSelector,
|
||||
} = useRecordBoardDeprecatedScopedStates({
|
||||
recordBoardScopeId: recordBoardId,
|
||||
});
|
||||
|
||||
const [boardColumns, setBoardColumns] = useRecoilState(boardColumnsState);
|
||||
const [isCompactViewEnabled, setIsCompactViewEnabled] = useRecoilState(
|
||||
isCompactViewEnabledState,
|
||||
);
|
||||
|
||||
const hiddenBoardCardFields = useRecoilValue(hiddenBoardCardFieldsSelector);
|
||||
const hasHiddenFields = hiddenBoardCardFields.length > 0;
|
||||
|
||||
const visibleBoardCardFields = useRecoilValue(visibleBoardCardFieldsSelector);
|
||||
const hasVisibleFields = visibleBoardCardFields.length > 0;
|
||||
|
||||
const handleStageSubmit = () => {
|
||||
if (currentMenu !== 'stage-creation' || !stageInputRef?.current?.value)
|
||||
return;
|
||||
|
||||
const columnToCreate: BoardColumnDefinition = {
|
||||
id: v4(),
|
||||
colorCode: 'gray',
|
||||
position: boardColumns.length,
|
||||
title: stageInputRef.current.value,
|
||||
};
|
||||
|
||||
setBoardColumns((previousBoardColumns) => [
|
||||
...previousBoardColumns,
|
||||
columnToCreate,
|
||||
]);
|
||||
onStageAdd?.(columnToCreate);
|
||||
};
|
||||
|
||||
const resetMenu = () => setCurrentMenu(undefined);
|
||||
|
||||
const handleMenuNavigate = (menu: BoardOptionsMenu) => {
|
||||
handleViewNameSubmit();
|
||||
setCurrentMenu(menu);
|
||||
};
|
||||
|
||||
const { handleFieldVisibilityChange, handleFieldsReorder } =
|
||||
useRecordBoardDeprecatedCardFieldsInternal({
|
||||
recordBoardScopeId: recordBoardId,
|
||||
});
|
||||
|
||||
const { closeDropdown } = useDropdown(BOARD_OPTIONS_DROPDOWN_ID);
|
||||
|
||||
const handleReorderField: OnDragEndResponder = useCallback(
|
||||
(result) => {
|
||||
if (!result.destination) {
|
||||
return;
|
||||
}
|
||||
|
||||
const reorderedFields = moveArrayItem(visibleBoardCardFields, {
|
||||
fromIndex: result.source.index - 1,
|
||||
toIndex: result.destination.index - 1,
|
||||
});
|
||||
|
||||
handleFieldsReorder(reorderedFields);
|
||||
},
|
||||
[handleFieldsReorder, visibleBoardCardFields],
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
[Key.Escape],
|
||||
() => {
|
||||
setViewEditMode('none');
|
||||
closeDropdown();
|
||||
},
|
||||
BoardOptionsHotkeyScope.Dropdown,
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
Key.Enter,
|
||||
() => {
|
||||
const name = viewEditInputRef.current?.value;
|
||||
resetMenu();
|
||||
setViewEditMode('none');
|
||||
handleStageSubmit();
|
||||
handleViewNameSubmit(name);
|
||||
closeDropdown();
|
||||
},
|
||||
BoardOptionsHotkeyScope.Dropdown,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{!currentMenu && (
|
||||
<>
|
||||
<DropdownMenuInput
|
||||
ref={viewEditInputRef}
|
||||
autoFocus={viewEditMode !== 'none'}
|
||||
placeholder={
|
||||
viewEditMode === 'create'
|
||||
? 'New view'
|
||||
: viewEditMode === 'edit'
|
||||
? 'View name'
|
||||
: ''
|
||||
}
|
||||
defaultValue={viewEditMode === 'create' ? '' : currentView?.name}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItemNavigate
|
||||
onClick={() => handleMenuNavigate('fields')}
|
||||
LeftIcon={IconTag}
|
||||
text="Fields"
|
||||
/>
|
||||
<MenuItemNavigate
|
||||
onClick={() => handleMenuNavigate('stages')}
|
||||
LeftIcon={IconLayoutKanban}
|
||||
text="Stages"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItemToggle
|
||||
LeftIcon={IconBaselineDensitySmall}
|
||||
onToggleChange={setIsCompactViewEnabled}
|
||||
toggled={isCompactViewEnabled}
|
||||
text="Compact view"
|
||||
toggleSize="small"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
)}
|
||||
{currentMenu === 'stages' && (
|
||||
<>
|
||||
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetMenu}>
|
||||
Stages
|
||||
</DropdownMenuHeader>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={() => setCurrentMenu('stage-creation')}
|
||||
LeftIcon={IconPlus}
|
||||
text="Add stage"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
)}
|
||||
{currentMenu === 'stage-creation' && (
|
||||
<DropdownMenuSearchInput
|
||||
autoFocus
|
||||
placeholder="New stage"
|
||||
ref={stageInputRef}
|
||||
/>
|
||||
)}
|
||||
{currentMenu === 'fields' && (
|
||||
<>
|
||||
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetMenu}>
|
||||
Fields
|
||||
</DropdownMenuHeader>
|
||||
<DropdownMenuSeparator />
|
||||
{hasVisibleFields && (
|
||||
<ViewFieldsVisibilityDropdownSection
|
||||
title="Visible"
|
||||
fields={visibleBoardCardFields}
|
||||
isDraggable
|
||||
onDragEnd={handleReorderField}
|
||||
onVisibilityChange={handleFieldVisibilityChange}
|
||||
/>
|
||||
)}
|
||||
{hasVisibleFields && hasHiddenFields && <DropdownMenuSeparator />}
|
||||
{hasHiddenFields && (
|
||||
<ViewFieldsVisibilityDropdownSection
|
||||
title="Hidden"
|
||||
fields={hiddenBoardCardFields}
|
||||
isDraggable={false}
|
||||
onVisibilityChange={handleFieldVisibilityChange}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -1,23 +0,0 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { RecordBoardDeprecatedScopeInternalContext } from '@/object-record/record-board-deprecated/scopes/scope-internal-context/RecordBoardDeprecatedScopeInternalContext';
|
||||
|
||||
type RecordBoardDeprecatedScopeProps = {
|
||||
children: ReactNode;
|
||||
recordBoardScopeId: string;
|
||||
};
|
||||
|
||||
export const RecordBoardDeprecatedScope = ({
|
||||
children,
|
||||
recordBoardScopeId,
|
||||
}: RecordBoardDeprecatedScopeProps) => {
|
||||
return (
|
||||
<RecordBoardDeprecatedScopeInternalContext.Provider
|
||||
value={{
|
||||
scopeId: recordBoardScopeId,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</RecordBoardDeprecatedScopeInternalContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -1,7 +0,0 @@
|
||||
import { StateScopeMapKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/StateScopeMapKey';
|
||||
import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
|
||||
|
||||
type RecordBoardDeprecatedScopeInternalContextProps = StateScopeMapKey;
|
||||
|
||||
export const RecordBoardDeprecatedScopeInternalContext =
|
||||
createScopeInternalContext<RecordBoardDeprecatedScopeInternalContextProps>();
|
||||
@ -1,7 +0,0 @@
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
export const activeRecordBoardDeprecatedCardIdsScopedState =
|
||||
createStateScopeMap<string[]>({
|
||||
key: 'activeRecordBoardDeprecatedCardIdsScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,10 +0,0 @@
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
import { BoardFieldDefinition } from '../types/BoardFieldDefinition';
|
||||
|
||||
export const availableRecordBoardDeprecatedCardFieldsScopedState =
|
||||
createStateScopeMap<BoardFieldDefinition<FieldMetadata>[]>({
|
||||
key: 'availableRecordBoardDeprecatedCardFieldsScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,6 +0,0 @@
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
export const isCompactViewEnabledScopedState = createStateScopeMap<boolean>({
|
||||
key: 'isCompactViewEnabledScopedState',
|
||||
defaultValue: false,
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
export const isRecordBoardDeprecatedCardInCompactViewFamilyState = atomFamily<
|
||||
boolean,
|
||||
string
|
||||
>({
|
||||
key: 'isRecordBoardDeprecatedCardInCompactViewFamilyState',
|
||||
default: true,
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
export const isRecordBoardDeprecatedCardSelectedFamilyState = atomFamily<
|
||||
boolean,
|
||||
string
|
||||
>({
|
||||
key: 'isRecordBoardDeprecatedCardSelectedFamilyState',
|
||||
default: false,
|
||||
});
|
||||
@ -1,7 +0,0 @@
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
export const isRecordBoardDeprecatedLoadedScopedState =
|
||||
createStateScopeMap<boolean>({
|
||||
key: 'isRecordBoardDeprecatedLoadedScopedState',
|
||||
defaultValue: false,
|
||||
});
|
||||
@ -1,10 +0,0 @@
|
||||
import { BoardFieldDefinition } from '@/object-record/record-board-deprecated/types/BoardFieldDefinition';
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
export const onFieldsChangeScopedState = createStateScopeMap<
|
||||
(fields: BoardFieldDefinition<FieldMetadata>[]) => void
|
||||
>({
|
||||
key: 'onFieldsChangeScopedState',
|
||||
defaultValue: () => {},
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
export const recordBoardCardIdsByColumnIdFamilyState = atomFamily<
|
||||
string[],
|
||||
string
|
||||
>({
|
||||
key: 'recordBoardCardIdsByColumnIdFamilyState',
|
||||
default: [],
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
import { BoardColumnDefinition } from '@/object-record/record-board-deprecated/types/BoardColumnDefinition';
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
export const recordBoardColumnsScopedState = createStateScopeMap<
|
||||
BoardColumnDefinition[]
|
||||
>({
|
||||
key: 'recordBoardColumnsScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,11 +0,0 @@
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
import { BoardFieldDefinition } from '../types/BoardFieldDefinition';
|
||||
|
||||
export const recordBoardCardFieldsScopedState = createStateScopeMap<
|
||||
BoardFieldDefinition<FieldMetadata>[]
|
||||
>({
|
||||
key: 'recordBoardCardFieldsScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,7 +0,0 @@
|
||||
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
export const recordBoardFiltersScopedState = createStateScopeMap<Filter[]>({
|
||||
key: 'recordBoardFiltersScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,8 +0,0 @@
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
import { Sort } from '../../object-sort-dropdown/types/Sort';
|
||||
|
||||
export const recordBoardSortsScopedState = createStateScopeMap<Sort[]>({
|
||||
key: 'recordBoardSortsScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
export const savedOpportunitiesScopedState = createStateScopeMap<Opportunity[]>(
|
||||
{
|
||||
key: 'savedOpportunitiesScopedState',
|
||||
defaultValue: [],
|
||||
},
|
||||
);
|
||||
@ -1,9 +0,0 @@
|
||||
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
export const savedPipelineStepsScopedState = createStateScopeMap<
|
||||
PipelineStep[]
|
||||
>({
|
||||
key: 'savedPipelineStepsScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,10 +0,0 @@
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
import { BoardFieldDefinition } from '../types/BoardFieldDefinition';
|
||||
|
||||
export const savedRecordBoardDeprecatedCardFieldsScopedState =
|
||||
createStateScopeMap<BoardFieldDefinition<FieldMetadata>[]>({
|
||||
key: 'savedRecordBoardDeprecatedCardFieldsScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,10 +0,0 @@
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
||||
|
||||
export const savedRecordBoardDeprecatedColumnsScopedState = createStateScopeMap<
|
||||
BoardColumnDefinition[]
|
||||
>({
|
||||
key: 'savedRecordBoardDeprecatedColumnsScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,7 +0,0 @@
|
||||
import { Company } from '@/companies/types/Company';
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
export const savedRecordsScopedState = createStateScopeMap<Company[]>({
|
||||
key: 'savedRecordsScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,24 +0,0 @@
|
||||
import { createSelectorReadOnlyScopeMap } from '@/ui/utilities/recoil-scope/utils/createSelectorReadOnlyScopeMap';
|
||||
|
||||
import { availableRecordBoardDeprecatedCardFieldsScopedState } from '../availableRecordBoardDeprecatedCardFieldsScopedState';
|
||||
import { recordBoardCardFieldsScopedState } from '../recordBoardDeprecatedCardFieldsScopedState';
|
||||
|
||||
export const hiddenRecordBoardDeprecatedCardFieldsScopedSelector =
|
||||
createSelectorReadOnlyScopeMap({
|
||||
key: 'hiddenRecordBoardDeprecatedCardFieldsScopedSelector',
|
||||
get:
|
||||
({ scopeId }) =>
|
||||
({ get }) => {
|
||||
const fields = get(recordBoardCardFieldsScopedState({ scopeId }));
|
||||
const fieldKeys = fields.map(({ fieldMetadataId }) => fieldMetadataId);
|
||||
|
||||
const otherAvailableKeys = get(
|
||||
availableRecordBoardDeprecatedCardFieldsScopedState({ scopeId }),
|
||||
).filter(({ fieldMetadataId }) => !fieldKeys.includes(fieldMetadataId));
|
||||
|
||||
return [
|
||||
...fields.filter((field) => !field.isVisible),
|
||||
...otherAvailableKeys,
|
||||
];
|
||||
},
|
||||
});
|
||||
@ -1,16 +0,0 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
|
||||
import { BoardFieldDefinition } from '../../types/BoardFieldDefinition';
|
||||
import { recordBoardCardFieldsScopedState } from '../recordBoardDeprecatedCardFieldsScopedState';
|
||||
|
||||
export const recordBoardCardFieldsByKeyScopedSelector = selectorFamily({
|
||||
key: 'recordBoardCardFieldsByKeyScopedSelector',
|
||||
get:
|
||||
(scopeId: string) =>
|
||||
({ get }) =>
|
||||
get(recordBoardCardFieldsScopedState({ scopeId })).reduce<
|
||||
Record<string, BoardFieldDefinition<FieldMetadata>>
|
||||
>((result, field) => ({ ...result, [field.fieldMetadataId]: field }), {}),
|
||||
});
|
||||
@ -1,32 +0,0 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { companyProgressesFamilyState } from '@/companies/states/companyProgressesFamilyState';
|
||||
import { amountFormat } from '~/utils/format/amountFormat';
|
||||
|
||||
import { recordBoardCardIdsByColumnIdFamilyState } from '../recordBoardCardIdsByColumnIdFamilyState';
|
||||
|
||||
// TODO: this state should be computed during the synchronization web-hook and put in a generic
|
||||
// boardColumnTotalsFamilyState indexed by columnId.
|
||||
export const recordBoardColumnTotalsFamilySelector = selectorFamily({
|
||||
key: 'recordBoardColumnTotalsFamilySelector',
|
||||
get:
|
||||
(pipelineStepId: string) =>
|
||||
({ get }) => {
|
||||
const cardIds = get(
|
||||
recordBoardCardIdsByColumnIdFamilyState(pipelineStepId),
|
||||
);
|
||||
|
||||
const opportunities = cardIds.map((opportunityId: string) =>
|
||||
get(companyProgressesFamilyState(opportunityId)),
|
||||
);
|
||||
|
||||
const pipelineStepTotal: number =
|
||||
opportunities?.reduce(
|
||||
(acc: number, curr: any) =>
|
||||
acc + curr?.opportunity.amount.amountMicros / 1000000,
|
||||
0,
|
||||
) || 0;
|
||||
|
||||
return amountFormat(pipelineStepTotal);
|
||||
},
|
||||
});
|
||||
@ -1,27 +0,0 @@
|
||||
import { createSelectorReadOnlyScopeMap } from '@/ui/utilities/recoil-scope/utils/createSelectorReadOnlyScopeMap';
|
||||
|
||||
import { isRecordBoardDeprecatedCardSelectedFamilyState } from '../isRecordBoardDeprecatedCardSelectedFamilyState';
|
||||
import { recordBoardCardIdsByColumnIdFamilyState } from '../recordBoardCardIdsByColumnIdFamilyState';
|
||||
import { recordBoardColumnsScopedState } from '../recordBoardColumnsScopedState';
|
||||
|
||||
export const selectedRecordBoardDeprecatedCardIdsScopedSelector =
|
||||
createSelectorReadOnlyScopeMap<string[]>({
|
||||
key: 'selectedRecordBoardDeprecatedCardIdsScopedSelector',
|
||||
get:
|
||||
({ scopeId }) =>
|
||||
({ get }) => {
|
||||
const boardColumns = get(recordBoardColumnsScopedState({ scopeId }));
|
||||
|
||||
const cardIds = boardColumns.flatMap((boardColumn) =>
|
||||
get(recordBoardCardIdsByColumnIdFamilyState(boardColumn.id)),
|
||||
);
|
||||
|
||||
const selectedCardIds = cardIds.filter(
|
||||
(cardId) =>
|
||||
get(isRecordBoardDeprecatedCardSelectedFamilyState(cardId)) ===
|
||||
true,
|
||||
);
|
||||
|
||||
return selectedCardIds;
|
||||
},
|
||||
});
|
||||
@ -1,14 +0,0 @@
|
||||
import { createSelectorReadOnlyScopeMap } from '@/ui/utilities/recoil-scope/utils/createSelectorReadOnlyScopeMap';
|
||||
|
||||
import { recordBoardCardFieldsScopedState } from '../recordBoardDeprecatedCardFieldsScopedState';
|
||||
|
||||
export const visibleRecordBoardDeprecatedCardFieldsScopedSelector =
|
||||
createSelectorReadOnlyScopeMap({
|
||||
key: 'visibleRecordBoardDeprecatedCardFieldsScopedSelector',
|
||||
get:
|
||||
({ scopeId }) =>
|
||||
({ get }) =>
|
||||
get(recordBoardCardFieldsScopedState({ scopeId }))
|
||||
.filter((field) => field.isVisible)
|
||||
.sort((a, b) => a.position - b.position),
|
||||
});
|
||||
@ -1,8 +0,0 @@
|
||||
import { ThemeColor } from '@/ui/theme/constants/MainColorNames';
|
||||
|
||||
export type BoardColumnDefinition = {
|
||||
id: string;
|
||||
title: string;
|
||||
position: number;
|
||||
colorCode?: ThemeColor;
|
||||
};
|
||||
@ -1,3 +0,0 @@
|
||||
export enum BoardColumnHotkeyScope {
|
||||
BoardColumn = 'board-column',
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
|
||||
export type BoardFieldDefinition<T extends FieldMetadata> =
|
||||
FieldDefinition<T> & {
|
||||
position: number;
|
||||
isVisible?: boolean;
|
||||
viewFieldId?: string;
|
||||
};
|
||||
@ -1,6 +0,0 @@
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
export type BoardOptions = {
|
||||
newCardComponent: React.ReactNode;
|
||||
CardComponent: ComponentType;
|
||||
};
|
||||
@ -1,3 +0,0 @@
|
||||
export enum BoardOptionsHotkeyScope {
|
||||
Dropdown = 'board-options-dropdown',
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
export enum ColumnHotkeyScope {
|
||||
EditColumnName = 'EditColumnNameHotkeyScope',
|
||||
}
|
||||
@ -1,121 +0,0 @@
|
||||
import { activeRecordBoardDeprecatedCardIdsScopedState } from '@/object-record/record-board-deprecated/states/activeRecordBoardDeprecatedCardIdsScopedState';
|
||||
import { availableRecordBoardDeprecatedCardFieldsScopedState } from '@/object-record/record-board-deprecated/states/availableRecordBoardDeprecatedCardFieldsScopedState';
|
||||
import { isCompactViewEnabledScopedState } from '@/object-record/record-board-deprecated/states/isCompactViewEnabledScopedState';
|
||||
import { isRecordBoardDeprecatedLoadedScopedState } from '@/object-record/record-board-deprecated/states/isRecordBoardDeprecatedLoadedScopedState';
|
||||
import { onFieldsChangeScopedState } from '@/object-record/record-board-deprecated/states/onFieldsChangeScopedState';
|
||||
import { recordBoardColumnsScopedState } from '@/object-record/record-board-deprecated/states/recordBoardColumnsScopedState';
|
||||
import { recordBoardFiltersScopedState } from '@/object-record/record-board-deprecated/states/recordBoardDeprecatedFiltersScopedState';
|
||||
import { recordBoardSortsScopedState } from '@/object-record/record-board-deprecated/states/recordBoardDeprecatedSortsScopedState';
|
||||
import { savedOpportunitiesScopedState } from '@/object-record/record-board-deprecated/states/savedOpportunitiesScopedState';
|
||||
import { savedPipelineStepsScopedState } from '@/object-record/record-board-deprecated/states/savedPipelineStepsScopedState';
|
||||
import { savedRecordBoardDeprecatedColumnsScopedState } from '@/object-record/record-board-deprecated/states/savedRecordBoardDeprecatedColumnsScopedState';
|
||||
import { savedRecordsScopedState } from '@/object-record/record-board-deprecated/states/savedRecordsScopedState';
|
||||
import { hiddenRecordBoardDeprecatedCardFieldsScopedSelector } from '@/object-record/record-board-deprecated/states/selectors/hiddenRecordBoardDeprecatedCardFieldsScopedSelector';
|
||||
import { recordBoardCardFieldsByKeyScopedSelector } from '@/object-record/record-board-deprecated/states/selectors/recordBoardDeprecatedCardFieldsByKeyScopedSelector';
|
||||
import { selectedRecordBoardDeprecatedCardIdsScopedSelector } from '@/object-record/record-board-deprecated/states/selectors/selectedRecordBoardDeprecatedCardIdsScopedSelector';
|
||||
import { visibleRecordBoardDeprecatedCardFieldsScopedSelector } from '@/object-record/record-board-deprecated/states/selectors/visibleRecordBoardDeprecatedCardFieldsScopedSelector';
|
||||
import { getScopedStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedStateDeprecated';
|
||||
|
||||
export const getRecordBoardDeprecatedScopedStates = ({
|
||||
recordBoardScopeId,
|
||||
}: {
|
||||
recordBoardScopeId: string;
|
||||
}) => {
|
||||
const activeCardIdsState = getScopedStateDeprecated(
|
||||
activeRecordBoardDeprecatedCardIdsScopedState,
|
||||
recordBoardScopeId,
|
||||
);
|
||||
|
||||
const availableBoardCardFieldsState = getScopedStateDeprecated(
|
||||
availableRecordBoardDeprecatedCardFieldsScopedState,
|
||||
recordBoardScopeId,
|
||||
);
|
||||
|
||||
const boardColumnsState = getScopedStateDeprecated(
|
||||
recordBoardColumnsScopedState,
|
||||
recordBoardScopeId,
|
||||
);
|
||||
|
||||
const isBoardLoadedState = getScopedStateDeprecated(
|
||||
isRecordBoardDeprecatedLoadedScopedState,
|
||||
recordBoardScopeId,
|
||||
);
|
||||
|
||||
const isCompactViewEnabledState = getScopedStateDeprecated(
|
||||
isCompactViewEnabledScopedState,
|
||||
recordBoardScopeId,
|
||||
);
|
||||
|
||||
const savedBoardColumnsState = getScopedStateDeprecated(
|
||||
savedRecordBoardDeprecatedColumnsScopedState,
|
||||
recordBoardScopeId,
|
||||
);
|
||||
|
||||
const boardFiltersState = getScopedStateDeprecated(
|
||||
recordBoardFiltersScopedState,
|
||||
recordBoardScopeId,
|
||||
);
|
||||
|
||||
const boardSortsState = getScopedStateDeprecated(
|
||||
recordBoardSortsScopedState,
|
||||
recordBoardScopeId,
|
||||
);
|
||||
|
||||
const savedCompaniesState = getScopedStateDeprecated(
|
||||
savedRecordsScopedState,
|
||||
recordBoardScopeId,
|
||||
);
|
||||
|
||||
const savedOpportunitiesState = getScopedStateDeprecated(
|
||||
savedOpportunitiesScopedState,
|
||||
recordBoardScopeId,
|
||||
);
|
||||
|
||||
const savedPipelineStepsState = getScopedStateDeprecated(
|
||||
savedPipelineStepsScopedState,
|
||||
recordBoardScopeId,
|
||||
);
|
||||
|
||||
const onFieldsChangeState = getScopedStateDeprecated(
|
||||
onFieldsChangeScopedState,
|
||||
recordBoardScopeId,
|
||||
);
|
||||
|
||||
// TODO: Family scoped selector
|
||||
const boardCardFieldsByKeySelector =
|
||||
recordBoardCardFieldsByKeyScopedSelector(recordBoardScopeId);
|
||||
|
||||
const hiddenBoardCardFieldsSelector =
|
||||
hiddenRecordBoardDeprecatedCardFieldsScopedSelector({
|
||||
scopeId: recordBoardScopeId,
|
||||
});
|
||||
|
||||
const selectedCardIdsSelector =
|
||||
selectedRecordBoardDeprecatedCardIdsScopedSelector({
|
||||
scopeId: recordBoardScopeId,
|
||||
});
|
||||
|
||||
const visibleBoardCardFieldsSelector =
|
||||
visibleRecordBoardDeprecatedCardFieldsScopedSelector({
|
||||
scopeId: recordBoardScopeId,
|
||||
});
|
||||
|
||||
return {
|
||||
activeCardIdsState,
|
||||
availableBoardCardFieldsState,
|
||||
boardColumnsState,
|
||||
isBoardLoadedState,
|
||||
isCompactViewEnabledState,
|
||||
savedBoardColumnsState,
|
||||
boardFiltersState,
|
||||
boardSortsState,
|
||||
onFieldsChangeState,
|
||||
boardCardFieldsByKeySelector,
|
||||
hiddenBoardCardFieldsSelector,
|
||||
selectedCardIdsSelector,
|
||||
visibleBoardCardFieldsSelector,
|
||||
savedCompaniesState,
|
||||
savedOpportunitiesState,
|
||||
savedPipelineStepsState,
|
||||
};
|
||||
};
|
||||
@ -3,7 +3,7 @@ import styled from '@emotion/styled';
|
||||
|
||||
import { RecordBoardColumnDropdownMenu } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu';
|
||||
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
|
||||
import { BoardColumnHotkeyScope } from '@/object-record/record-board-deprecated/types/BoardColumnHotkeyScope';
|
||||
import { RecordBoardColumnHotkeyScope } from '@/object-record/record-board/types/BoardColumnHotkeyScope';
|
||||
import { IconDotsVertical } from '@/ui/display/icon';
|
||||
import { Tag } from '@/ui/display/tag/components/Tag';
|
||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||
@ -57,9 +57,12 @@ export const RecordBoardColumnHeader = () => {
|
||||
|
||||
const handleBoardColumnMenuOpen = () => {
|
||||
setIsBoardColumnMenuOpen(true);
|
||||
setHotkeyScopeAndMemorizePreviousScope(BoardColumnHotkeyScope.BoardColumn, {
|
||||
goto: false,
|
||||
});
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
RecordBoardColumnHotkeyScope.BoardColumn,
|
||||
{
|
||||
goto: false,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const handleBoardColumnMenuClose = () => {
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
export enum RecordBoardColumnHotkeyScope {
|
||||
BoardColumn = 'board-column',
|
||||
}
|
||||
@ -2,7 +2,6 @@ import { useCallback, useMemo } from 'react';
|
||||
import { OnDragEndResponder } from '@hello-pangea/dnd';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { mapBoardFieldDefinitionsToViewFields } from '@/companies/utils/mapBoardFieldDefinitionsToViewFields';
|
||||
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
|
||||
import { useObjectMetadataItemOnly } from '@/object-metadata/hooks/useObjectMetadataItemOnly';
|
||||
import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard';
|
||||
@ -12,6 +11,7 @@ import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefin
|
||||
import { useViewFields } from '@/views/hooks/internal/useViewFields';
|
||||
import { useViews } from '@/views/hooks/internal/useViews';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { mapBoardFieldDefinitionsToViewFields } from '@/views/utils/mapBoardFieldDefinitionsToViewFields';
|
||||
import { mapArrayToObject } from '~/utils/array/mapArrayToObject';
|
||||
import { moveArrayItem } from '~/utils/array/moveArrayItem';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
Reference in New Issue
Block a user