From 84b0b78b6f628a80785fcdcb7ad98cbb9897c0c5 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Tue, 5 Nov 2024 15:16:38 +0100 Subject: [PATCH] Refactor kanban new card creation (#8339) On the kanban page, the record creation was changed a few weeks ago to enable creation on top / bottom of the columns. However, this introduced a glitch (missing background opacity). While fixing it, I have refactored the component structure to: - separate "New" button from the Empty record card --- .../RecordBoardColumnCardsContainer.tsx | 59 ++++++++++++++---- .../components/RecordBoardColumnHeader.tsx | 33 ++-------- .../RecordBoardColumnNewOpportunity.tsx | 54 ++++++++++++++++ .../RecordBoardColumnNewOpportunityButton.tsx | 62 ------------------- .../components/RecordBoardColumnNewRecord.tsx | 32 ++++++++++ ...x => RecordBoardColumnNewRecordButton.tsx} | 21 +------ .../hooks/useColumnNewCardActions.ts | 14 +---- 7 files changed, 141 insertions(+), 134 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunity.tsx delete mode 100644 packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunityButton.tsx create mode 100644 packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewRecord.tsx rename packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/{RecordBoardColumnNewButton.tsx => RecordBoardColumnNewRecordButton.tsx} (64%) diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer.tsx index 8cd90bf79f..d3e540902b 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer.tsx @@ -9,8 +9,9 @@ import { useRecordBoardStates } from '@/object-record/record-board/hooks/interna import { RecordBoardColumnCardContainerSkeletonLoader } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnCardContainerSkeletonLoader'; import { RecordBoardColumnCardsMemo } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnCardsMemo'; import { RecordBoardColumnFetchMoreLoader } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader'; -import { RecordBoardColumnNewButton } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnNewButton'; -import { RecordBoardColumnNewOpportunityButton } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunityButton'; +import { RecordBoardColumnNewOpportunity } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunity'; +import { RecordBoardColumnNewRecord } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnNewRecord'; +import { RecordBoardColumnNewRecordButton } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnNewRecordButton'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; import { useIsOpportunitiesCompanyFieldDisabled } from '@/object-record/record-board/record-board-column/hooks/useIsOpportunitiesCompanyFieldDisabled'; import { getNumberOfCardsPerColumnForSkeletonLoading } from '@/object-record/record-board/record-board-column/utils/getNumberOfCardsPerColumnForSkeletonLoading'; @@ -74,6 +75,33 @@ export const RecordBoardColumnCardsContainer = ({ // eslint-disable-next-line react/jsx-props-no-spreading {...droppableProvided?.droppableProps} > + + {(draggableProvided) => ( +
+ {objectMetadataItem.nameSingular === + CoreObjectNameSingular.Opportunity && + !isOpportunitiesCompanyFieldDisabled ? ( + + ) : ( + + )} +
+ )} +
{isRecordIndexBoardColumnLoading ? ( Array.from( { @@ -98,7 +126,7 @@ export const RecordBoardColumnCardsContainer = ({ )} @@ -108,16 +136,23 @@ export const RecordBoardColumnCardsContainer = ({ // eslint-disable-next-line react/jsx-props-no-spreading {...draggableProvided?.draggableProps} > + {objectMetadataItem.nameSingular === + CoreObjectNameSingular.Opportunity && + !isOpportunitiesCompanyFieldDisabled ? ( + + ) : ( + + )} - {objectMetadataItem.nameSingular === - CoreObjectNameSingular.Opportunity && - !isOpportunitiesCompanyFieldDisabled ? ( - - ) : ( - - )} + )} diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx index 1d91fc5406..f431954bfc 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx @@ -1,18 +1,16 @@ import styled from '@emotion/styled'; import { useContext, useState } from 'react'; -import { IconDotsVertical, IconPlus, LightIconButton, Tag } from 'twenty-ui'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; -import { RecordBoardCard } from '@/object-record/record-board/record-board-card/components/RecordBoardCard'; 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 { useColumnNewCardActions } from '@/object-record/record-board/record-board-column/hooks/useColumnNewCardActions'; import { useIsOpportunitiesCompanyFieldDisabled } from '@/object-record/record-board/record-board-column/hooks/useIsOpportunitiesCompanyFieldDisabled'; import { RecordBoardColumnHotkeyScope } from '@/object-record/record-board/types/BoardColumnHotkeyScope'; import { RecordGroupDefinitionType } from '@/object-record/record-group/types/RecordGroupDefinition'; -import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect'; import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; +import { IconDotsVertical, IconPlus, LightIconButton, Tag } from 'twenty-ui'; const StyledHeader = styled.div` align-items: center; @@ -102,12 +100,9 @@ export const RecordBoardColumnHeader = () => { const boardColumnTotal = 0; - const { - newRecord, - handleNewButtonClick, - handleCreateSuccess, - handleEntitySelect, - } = useColumnNewCardActions(columnDefinition?.id ?? ''); + const { handleNewButtonClick } = useColumnNewCardActions( + columnDefinition?.id ?? '', + ); const { isOpportunitiesCompanyFieldDisabled } = useIsOpportunitiesCompanyFieldDisabled(); @@ -173,26 +168,6 @@ export const RecordBoardColumnHeader = () => { stageId={columnDefinition.id} /> )} - {newRecord?.isCreating && - newRecord.position === 'first' && - (newRecord.isOpportunity ? ( - handleCreateSuccess('first', columnDefinition.id)} - onEntitySelected={(company) => - company && handleEntitySelect('first', company) - } - relationObjectNameSingular={CoreObjectNameSingular.Company} - relationPickerScopeId="relation-picker" - selectedRelationRecordIds={[]} - /> - ) : ( - handleCreateSuccess('first')} - position="first" - /> - ))} ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunity.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunity.tsx new file mode 100644 index 0000000000..d8215faeb9 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunity.tsx @@ -0,0 +1,54 @@ +import styled from '@emotion/styled'; + +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard'; +import { recordBoardNewRecordByColumnIdSelector } from '@/object-record/record-board/states/selectors/recordBoardNewRecordByColumnIdSelector'; +import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect'; +import { useRecoilValue } from 'recoil'; + +const StyledCompanyPickerContainer = styled.div` + 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)}; +`; + +export const RecordBoardColumnNewOpportunity = ({ + columnId, + position, +}: { + columnId: string; + position: 'last' | 'first'; +}) => { + const newRecord = useRecoilValue( + recordBoardNewRecordByColumnIdSelector({ + familyKey: columnId, + scopeId: columnId, + }), + ); + const { handleCreateSuccess, handleEntitySelect } = useAddNewCard(); + + return ( + <> + {newRecord.isCreating && newRecord.position === position && ( + + handleCreateSuccess(position, columnId, false)} + onEntitySelected={(company) => + company ? handleEntitySelect(position, company) : null + } + relationObjectNameSingular={CoreObjectNameSingular.Company} + relationPickerScopeId="relation-picker" + selectedRelationRecordIds={[]} + /> + + )} + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunityButton.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunityButton.tsx deleted file mode 100644 index 55e9e2e1e5..0000000000 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunityButton.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { useTheme } from '@emotion/react'; -import styled from '@emotion/styled'; -import { IconPlus } from 'twenty-ui'; - -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { useColumnNewCardActions } from '@/object-record/record-board/record-board-column/hooks/useColumnNewCardActions'; -import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect'; - -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}; - } -`; - -export const RecordBoardColumnNewOpportunityButton = ({ - columnId, -}: { - columnId: string; -}) => { - const theme = useTheme(); - - const { - newRecord, - handleNewButtonClick, - handleEntitySelect, - handleCreateSuccess, - } = useColumnNewCardActions(columnId); - return ( - <> - {newRecord.isCreating && - newRecord.position === 'last' && - newRecord.isOpportunity ? ( - handleCreateSuccess('last', columnId, false)} - onEntitySelected={(company) => - company ? handleEntitySelect('last', company) : null - } - relationObjectNameSingular={CoreObjectNameSingular.Company} - relationPickerScopeId="relation-picker" - selectedRelationRecordIds={[]} - /> - ) : ( - handleNewButtonClick('last', true)}> - - New - - )} - - ); -}; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewRecord.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewRecord.tsx new file mode 100644 index 0000000000..091628f35a --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewRecord.tsx @@ -0,0 +1,32 @@ +import { RecordBoardCard } from '@/object-record/record-board/record-board-card/components/RecordBoardCard'; +import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard'; +import { recordBoardNewRecordByColumnIdSelector } from '@/object-record/record-board/states/selectors/recordBoardNewRecordByColumnIdSelector'; +import { useRecoilValue } from 'recoil'; + +export const RecordBoardColumnNewRecord = ({ + columnId, + position, +}: { + columnId: string; + position: 'first' | 'last'; +}) => { + const newRecord = useRecoilValue( + recordBoardNewRecordByColumnIdSelector({ + familyKey: columnId, + scopeId: columnId, + }), + ); + const { handleCreateSuccess } = useAddNewCard(); + + return ( + <> + {newRecord.isCreating && newRecord.position === position && ( + handleCreateSuccess(position)} + position={position} + /> + )} + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewButton.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewRecordButton.tsx similarity index 64% rename from packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewButton.tsx rename to packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewRecordButton.tsx index 428b9921f5..2ecbb309a0 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewButton.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewRecordButton.tsx @@ -1,4 +1,3 @@ -import { RecordBoardCard } from '@/object-record/record-board/record-board-card/components/RecordBoardCard'; import { useColumnNewCardActions } from '@/object-record/record-board/record-board-column/hooks/useColumnNewCardActions'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; @@ -15,34 +14,20 @@ const StyledNewButton = styled.button` display: flex; gap: ${({ theme }) => theme.spacing(1)}; padding: ${({ theme }) => theme.spacing(1)}; + &:hover { background-color: ${({ theme }) => theme.background.tertiary}; } `; -export const RecordBoardColumnNewButton = ({ +export const RecordBoardColumnNewRecordButton = ({ columnId, }: { columnId: string; }) => { const theme = useTheme(); - const { newRecord, handleNewButtonClick, handleCreateSuccess } = - useColumnNewCardActions(columnId); - - if ( - newRecord.isCreating && - newRecord.position === 'last' && - !newRecord.isOpportunity - ) { - return ( - handleCreateSuccess('last')} - position="last" - /> - ); - } + const { handleNewButtonClick } = useColumnNewCardActions(columnId); return ( handleNewButtonClick('last', false)}> diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useColumnNewCardActions.ts b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useColumnNewCardActions.ts index eb498d5ae4..cb087c0813 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useColumnNewCardActions.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useColumnNewCardActions.ts @@ -1,6 +1,5 @@ import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard'; -import { recordBoardNewRecordByColumnIdSelector } from '@/object-record/record-board/states/selectors/recordBoardNewRecordByColumnIdSelector'; import { useRecoilValue } from 'recoil'; export const useColumnNewCardActions = (columnId: string) => { @@ -12,15 +11,7 @@ export const useColumnNewCardActions = (columnId: string) => { (field) => field.isLabelIdentifier, ); - const { handleAddNewCardClick, handleCreateSuccess, handleEntitySelect } = - useAddNewCard(); - - const newRecord = useRecoilValue( - recordBoardNewRecordByColumnIdSelector({ - familyKey: columnId, - scopeId: columnId, - }), - ); + const { handleAddNewCardClick } = useAddNewCard(); const handleNewButtonClick = ( position: 'first' | 'last', @@ -36,9 +27,6 @@ export const useColumnNewCardActions = (columnId: string) => { }; return { - newRecord, handleNewButtonClick, - handleCreateSuccess, - handleEntitySelect, }; };