mirror of
https://github.com/twentyhq/twenty.git
synced 2024-11-22 11:43:34 +03:00
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
This commit is contained in:
parent
be8141ce5e
commit
84b0b78b6f
@ -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}
|
||||
>
|
||||
<Draggable
|
||||
draggableId={`new-${columnDefinition.id}-top`}
|
||||
index={-1}
|
||||
isDragDisabled={true}
|
||||
>
|
||||
{(draggableProvided) => (
|
||||
<div
|
||||
ref={draggableProvided?.innerRef}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...draggableProvided?.draggableProps}
|
||||
>
|
||||
{objectMetadataItem.nameSingular ===
|
||||
CoreObjectNameSingular.Opportunity &&
|
||||
!isOpportunitiesCompanyFieldDisabled ? (
|
||||
<RecordBoardColumnNewOpportunity
|
||||
columnId={columnDefinition.id}
|
||||
position="first"
|
||||
/>
|
||||
) : (
|
||||
<RecordBoardColumnNewRecord
|
||||
columnId={columnDefinition.id}
|
||||
position="first"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
{isRecordIndexBoardColumnLoading ? (
|
||||
Array.from(
|
||||
{
|
||||
@ -98,7 +126,7 @@ export const RecordBoardColumnCardsContainer = ({
|
||||
)}
|
||||
<RecordBoardColumnFetchMoreLoader />
|
||||
<Draggable
|
||||
draggableId={`new-${columnDefinition.id}`}
|
||||
draggableId={`new-${columnDefinition.id}-bottom`}
|
||||
index={recordIds.length}
|
||||
isDragDisabled={true}
|
||||
>
|
||||
@ -108,16 +136,23 @@ export const RecordBoardColumnCardsContainer = ({
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...draggableProvided?.draggableProps}
|
||||
>
|
||||
{objectMetadataItem.nameSingular ===
|
||||
CoreObjectNameSingular.Opportunity &&
|
||||
!isOpportunitiesCompanyFieldDisabled ? (
|
||||
<RecordBoardColumnNewOpportunity
|
||||
columnId={columnDefinition.id}
|
||||
position="last"
|
||||
/>
|
||||
) : (
|
||||
<RecordBoardColumnNewRecord
|
||||
columnId={columnDefinition.id}
|
||||
position="last"
|
||||
/>
|
||||
)}
|
||||
<StyledNewButtonContainer>
|
||||
{objectMetadataItem.nameSingular ===
|
||||
CoreObjectNameSingular.Opportunity &&
|
||||
!isOpportunitiesCompanyFieldDisabled ? (
|
||||
<RecordBoardColumnNewOpportunityButton
|
||||
columnId={columnDefinition.id}
|
||||
/>
|
||||
) : (
|
||||
<RecordBoardColumnNewButton columnId={columnDefinition.id} />
|
||||
)}
|
||||
<RecordBoardColumnNewRecordButton
|
||||
columnId={columnDefinition.id}
|
||||
/>
|
||||
</StyledNewButtonContainer>
|
||||
</div>
|
||||
)}
|
||||
|
@ -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 ? (
|
||||
<SingleEntitySelect
|
||||
disableBackgroundBlur
|
||||
onCancel={() => handleCreateSuccess('first', columnDefinition.id)}
|
||||
onEntitySelected={(company) =>
|
||||
company && handleEntitySelect('first', company)
|
||||
}
|
||||
relationObjectNameSingular={CoreObjectNameSingular.Company}
|
||||
relationPickerScopeId="relation-picker"
|
||||
selectedRelationRecordIds={[]}
|
||||
/>
|
||||
) : (
|
||||
<RecordBoardCard
|
||||
isCreating={true}
|
||||
onCreateSuccess={() => handleCreateSuccess('first')}
|
||||
position="first"
|
||||
/>
|
||||
))}
|
||||
</StyledColumn>
|
||||
);
|
||||
};
|
||||
|
@ -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 && (
|
||||
<StyledCompanyPickerContainer>
|
||||
<SingleEntitySelect
|
||||
disableBackgroundBlur
|
||||
onCancel={() => handleCreateSuccess(position, columnId, false)}
|
||||
onEntitySelected={(company) =>
|
||||
company ? handleEntitySelect(position, company) : null
|
||||
}
|
||||
relationObjectNameSingular={CoreObjectNameSingular.Company}
|
||||
relationPickerScopeId="relation-picker"
|
||||
selectedRelationRecordIds={[]}
|
||||
/>
|
||||
</StyledCompanyPickerContainer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@ -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 ? (
|
||||
<SingleEntitySelect
|
||||
disableBackgroundBlur
|
||||
onCancel={() => handleCreateSuccess('last', columnId, false)}
|
||||
onEntitySelected={(company) =>
|
||||
company ? handleEntitySelect('last', company) : null
|
||||
}
|
||||
relationObjectNameSingular={CoreObjectNameSingular.Company}
|
||||
relationPickerScopeId="relation-picker"
|
||||
selectedRelationRecordIds={[]}
|
||||
/>
|
||||
) : (
|
||||
<StyledButton onClick={() => handleNewButtonClick('last', true)}>
|
||||
<IconPlus size={theme.icon.size.md} />
|
||||
New
|
||||
</StyledButton>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@ -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 && (
|
||||
<RecordBoardCard
|
||||
isCreating={true}
|
||||
onCreateSuccess={() => handleCreateSuccess(position)}
|
||||
position={position}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@ -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 (
|
||||
<RecordBoardCard
|
||||
isCreating={true}
|
||||
onCreateSuccess={() => handleCreateSuccess('last')}
|
||||
position="last"
|
||||
/>
|
||||
);
|
||||
}
|
||||
const { handleNewButtonClick } = useColumnNewCardActions(columnId);
|
||||
|
||||
return (
|
||||
<StyledNewButton onClick={() => handleNewButtonClick('last', false)}>
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user