Kanban card creation revamp (#7169)

fixes #6957
This commit is contained in:
nitin
2024-09-25 22:00:39 +05:30
committed by GitHub
parent 89b50c020f
commit 7752510316
11 changed files with 384 additions and 95 deletions

View File

@ -1,9 +1,3 @@
import styled from '@emotion/styled';
import { ReactNode, useContext, useState } from 'react';
import { useInView } from 'react-intersection-observer';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { AvatarChipVariant, IconEye } from 'twenty-ui';
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext'; import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext';
@ -15,15 +9,24 @@ import {
import { getFieldButtonIcon } from '@/object-record/record-field/utils/getFieldButtonIcon'; import { getFieldButtonIcon } from '@/object-record/record-field/utils/getFieldButtonIcon';
import { RecordIdentifierChip } from '@/object-record/record-index/components/RecordIndexRecordChip'; import { RecordIdentifierChip } from '@/object-record/record-index/components/RecordIndexRecordChip';
import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell'; import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell';
import { RecordInlineCellEditMode } from '@/object-record/record-inline-cell/components/RecordInlineCellEditMode';
import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope'; import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope';
import { RecordValueSetterEffect } from '@/object-record/record-store/components/RecordValueSetterEffect'; import { RecordValueSetterEffect } from '@/object-record/record-store/components/RecordValueSetterEffect';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
import { Checkbox, CheckboxVariant } from '@/ui/input/components/Checkbox'; import { Checkbox, CheckboxVariant } from '@/ui/input/components/Checkbox';
import { TextInput } from '@/ui/input/components/TextInput';
import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/contextMenuIsOpenState'; import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/contextMenuIsOpenState';
import { contextMenuPositionState } from '@/ui/navigation/context-menu/states/contextMenuPositionState'; import { contextMenuPositionState } from '@/ui/navigation/context-menu/states/contextMenuPositionState';
import { AnimatedEaseInOut } from '@/ui/utilities/animation/components/AnimatedEaseInOut'; import { AnimatedEaseInOut } from '@/ui/utilities/animation/components/AnimatedEaseInOut';
import { RecordBoardScrollWrapperContext } from '@/ui/utilities/scroll/contexts/ScrollWrapperContexts'; import { RecordBoardScrollWrapperContext } from '@/ui/utilities/scroll/contexts/ScrollWrapperContexts';
import styled from '@emotion/styled';
import { ReactNode, useContext, useState } from 'react';
import { useInView } from 'react-intersection-observer';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { AvatarChipVariant, IconEye } from 'twenty-ui';
import { useAddNewCard } from '../../record-board-column/hooks/useAddNewCard';
const StyledBoardCard = styled.div<{ selected: boolean }>` const StyledBoardCard = styled.div<{ selected: boolean }>`
background-color: ${({ theme, selected }) => background-color: ${({ theme, selected }) =>
@ -61,6 +64,14 @@ const StyledBoardCard = styled.div<{ selected: boolean }>`
} }
`; `;
const StyledTextInput = styled(TextInput)`
backdrop-filter: blur(12px) saturate(200%) contrast(50%) brightness(130%);
background: ${({ theme }) => theme.background.primary};
box-shadow: ${({ theme }) => theme.boxShadow.strong};
width: ${({ theme }) => theme.spacing(53)};
border-radius: ${({ theme }) => theme.border.radius.sm};
`;
const StyledBoardCardWrapper = styled.div` const StyledBoardCardWrapper = styled.div`
padding-bottom: ${({ theme }) => theme.spacing(2)}; padding-bottom: ${({ theme }) => theme.spacing(2)};
width: 100%; width: 100%;
@ -130,7 +141,21 @@ const StyledRecordInlineCellPlaceholder = styled.div`
height: 24px; height: 24px;
`; `;
export const RecordBoardCard = () => { const StyledRecordInlineCell = styled(RecordInlineCell)`
height: 24px;
`;
export const RecordBoardCard = ({
isCreating = false,
onCreateSuccess,
position,
}: {
isCreating?: boolean;
onCreateSuccess?: () => void;
position?: 'first' | 'last';
}) => {
const [newLabelValue, setNewLabelValue] = useState('');
const { handleBlur, handleInputEnter } = useAddNewCard();
const { recordId } = useContext(RecordBoardCardContext); const { recordId } = useContext(RecordBoardCardContext);
const { updateOneRecord, objectMetadataItem } = const { updateOneRecord, objectMetadataItem } =
useContext(RecordBoardContext); useContext(RecordBoardContext);
@ -139,7 +164,6 @@ export const RecordBoardCard = () => {
isRecordBoardCardSelectedFamilyState, isRecordBoardCardSelectedFamilyState,
visibleFieldDefinitionsState, visibleFieldDefinitionsState,
} = useRecordBoardStates(); } = useRecordBoardStates();
const isCompactModeActive = useRecoilValue(isCompactModeActiveState); const isCompactModeActive = useRecoilValue(isCompactModeActiveState);
const [isCardInCompactMode, setIsCardInCompactMode] = useState(true); const [isCardInCompactMode, setIsCardInCompactMode] = useState(true);
@ -205,66 +229,106 @@ export const RecordBoardCard = () => {
rootMargin: '1000px', rootMargin: '1000px',
}); });
if (!record) {
return null;
}
const visibleFieldDefinitionsFiltered = visibleFieldDefinitions.filter( const visibleFieldDefinitionsFiltered = visibleFieldDefinitions.filter(
(boardField) => !boardField.isLabelIdentifier, (boardField) => !boardField.isLabelIdentifier,
); );
const labelIdentifierField = visibleFieldDefinitions.find(
(field) => field.isLabelIdentifier,
);
return ( return (
<StyledBoardCardWrapper onContextMenu={handleContextMenu}> <StyledBoardCardWrapper onContextMenu={handleContextMenu}>
<RecordValueSetterEffect recordId={recordId} /> {!isCreating && <RecordValueSetterEffect recordId={recordId} />}
<StyledBoardCard <StyledBoardCard
ref={cardRef} ref={cardRef}
selected={isCurrentCardSelected} selected={isCurrentCardSelected}
onMouseLeave={onMouseLeaveBoard} onMouseLeave={onMouseLeaveBoard}
onClick={() => { onClick={() => {
setIsCurrentCardSelected(!isCurrentCardSelected); if (!isCreating) {
setIsCurrentCardSelected(!isCurrentCardSelected);
}
}} }}
> >
<StyledBoardCardHeader showCompactView={isCompactModeActive}> <StyledBoardCardHeader showCompactView={isCompactModeActive}>
<RecordIdentifierChip {isCreating && position !== undefined ? (
objectNameSingular={objectMetadataItem.nameSingular} <RecordInlineCellEditMode>
record={record} <StyledTextInput
variant={AvatarChipVariant.Transparent} autoFocus
/> value={newLabelValue}
{isCompactModeActive && ( onInputEnter={() =>
<StyledCompactIconContainer className="compact-icon-container"> handleInputEnter(
<LightIconButton labelIdentifierField?.label ?? '',
Icon={IconEye} newLabelValue,
accent="tertiary" position,
onClick={(e) => { onCreateSuccess,
e.stopPropagation(); )
setIsCardInCompactMode(false); }
}} onBlur={() =>
handleBlur(
labelIdentifierField?.label ?? '',
newLabelValue,
position,
onCreateSuccess,
)
}
onChange={(text: string) => setNewLabelValue(text)}
placeholder={labelIdentifierField?.label}
/> />
</StyledCompactIconContainer> </RecordInlineCellEditMode>
)} ) : (
<StyledCheckboxContainer className="checkbox-container"> <RecordIdentifierChip
<Checkbox objectNameSingular={objectMetadataItem.nameSingular}
hoverable record={record as ObjectRecord}
checked={isCurrentCardSelected} variant={AvatarChipVariant.Transparent}
onChange={() => setIsCurrentCardSelected(!isCurrentCardSelected)}
variant={CheckboxVariant.Secondary}
/> />
</StyledCheckboxContainer> )}
{!isCreating && (
<>
{isCompactModeActive && (
<StyledCompactIconContainer className="compact-icon-container">
<LightIconButton
Icon={IconEye}
accent="tertiary"
onClick={(e) => {
e.stopPropagation();
setIsCardInCompactMode(false);
}}
/>
</StyledCompactIconContainer>
)}
<StyledCheckboxContainer className="checkbox-container">
<Checkbox
hoverable
checked={isCurrentCardSelected}
onChange={() =>
setIsCurrentCardSelected(!isCurrentCardSelected)
}
variant={CheckboxVariant.Secondary}
/>
</StyledCheckboxContainer>
</>
)}
</StyledBoardCardHeader> </StyledBoardCardHeader>
<StyledBoardCardBody>
<AnimatedEaseInOut <AnimatedEaseInOut
isOpen={!isCardInCompactMode || !isCompactModeActive} isOpen={!isCardInCompactMode || !isCompactModeActive}
initial={false} initial={false}
> >
<StyledBoardCardBody>
{visibleFieldDefinitionsFiltered.map((fieldDefinition) => ( {visibleFieldDefinitionsFiltered.map((fieldDefinition) => (
<PreventSelectOnClickContainer <PreventSelectOnClickContainer
key={fieldDefinition.fieldMetadataId} key={fieldDefinition.fieldMetadataId}
> >
<FieldContext.Provider <FieldContext.Provider
value={{ value={{
recordId, recordId: isCreating ? '' : recordId,
maxWidth: 156, maxWidth: 156,
recoilScopeId: recordId + fieldDefinition.fieldMetadataId, recoilScopeId:
(isCreating ? 'new' : recordId) +
fieldDefinition.fieldMetadataId,
isLabelIdentifier: false, isLabelIdentifier: false,
fieldDefinition: { fieldDefinition: {
disableTooltip: false, disableTooltip: false,
@ -284,15 +348,15 @@ export const RecordBoardCard = () => {
}} }}
> >
{inView ? ( {inView ? (
<RecordInlineCell /> <StyledRecordInlineCell />
) : ( ) : (
<StyledRecordInlineCellPlaceholder /> <StyledRecordInlineCellPlaceholder />
)} )}
</FieldContext.Provider> </FieldContext.Provider>
</PreventSelectOnClickContainer> </PreventSelectOnClickContainer>
))} ))}
</AnimatedEaseInOut> </StyledBoardCardBody>
</StyledBoardCardBody> </AnimatedEaseInOut>
</StyledBoardCard> </StyledBoardCard>
</StyledBoardCardWrapper> </StyledBoardCardWrapper>
); );

View File

@ -1,6 +1,6 @@
import React, { useContext } from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { Draggable, DroppableProvided } from '@hello-pangea/dnd'; import { Draggable, DroppableProvided } from '@hello-pangea/dnd';
import { useContext } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
@ -110,7 +110,7 @@ export const RecordBoardColumnCardsContainer = ({
CoreObjectNameSingular.Opportunity ? ( CoreObjectNameSingular.Opportunity ? (
<RecordBoardColumnNewOpportunityButton /> <RecordBoardColumnNewOpportunityButton />
) : ( ) : (
<RecordBoardColumnNewButton /> <RecordBoardColumnNewButton columnId={columnDefinition.id} />
)} )}
</StyledNewButtonContainer> </StyledNewButtonContainer>
</div> </div>

View File

@ -4,10 +4,11 @@ import { IconDotsVertical, IconPlus, Tag } from 'twenty-ui';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; 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 { RecordBoardColumnDropdownMenu } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu';
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard';
import { useAddNewOpportunity } from '@/object-record/record-board/record-board-column/hooks/useAddNewOpportunity'; import { useAddNewOpportunity } from '@/object-record/record-board/record-board-column/hooks/useAddNewOpportunity';
import { useColumnNewCardActions } from '@/object-record/record-board/record-board-column/hooks/useColumnNewCardActions';
import { RecordBoardColumnHotkeyScope } from '@/object-record/record-board/types/BoardColumnHotkeyScope'; import { RecordBoardColumnHotkeyScope } from '@/object-record/record-board/types/BoardColumnHotkeyScope';
import { RecordBoardColumnDefinitionType } from '@/object-record/record-board/types/RecordBoardColumnDefinition'; import { RecordBoardColumnDefinitionType } from '@/object-record/record-board/types/RecordBoardColumnDefinition';
import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect'; import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect';
@ -94,16 +95,16 @@ export const RecordBoardColumnHeader = () => {
handleCancel, handleCancel,
handleEntitySelect, handleEntitySelect,
} = useAddNewOpportunity('first'); } = useAddNewOpportunity('first');
const { handleAddNewCardClick } = useAddNewCard('first');
const { newRecord, handleNewButtonClick, handleCreateSuccess } =
useColumnNewCardActions(columnDefinition.id);
const isOpportunity = const isOpportunity =
objectMetadataItem.nameSingular === CoreObjectNameSingular.Opportunity; objectMetadataItem.nameSingular === CoreObjectNameSingular.Opportunity;
const handleClick = isOpportunity const handleClick = isOpportunity
? handleAddNewOpportunityClick ? handleAddNewOpportunityClick
: () => { : () => handleNewButtonClick('first');
handleAddNewCardClick();
};
return ( return (
<> <>
@ -164,6 +165,13 @@ export const RecordBoardColumnHeader = () => {
stageId={columnDefinition.id} stageId={columnDefinition.id}
/> />
)} )}
{newRecord?.isCreating && newRecord.position === 'first' && (
<RecordBoardCard
isCreating={true}
onCreateSuccess={() => handleCreateSuccess('first')}
position="first"
/>
)}
{isCreatingCard && ( {isCreatingCard && (
<SingleEntitySelect <SingleEntitySelect
disableBackgroundBlur disableBackgroundBlur

View File

@ -1,10 +1,10 @@
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 { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { IconPlus } from 'twenty-ui'; import { IconPlus } from 'twenty-ui';
import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard'; const StyledNewButton = styled.button`
const StyledButton = styled.button`
align-items: center; align-items: center;
align-self: baseline; align-self: baseline;
background-color: ${({ theme }) => theme.background.primary}; background-color: ${({ theme }) => theme.background.primary};
@ -15,19 +15,35 @@ const StyledButton = styled.button`
display: flex; display: flex;
gap: ${({ theme }) => theme.spacing(1)}; gap: ${({ theme }) => theme.spacing(1)};
padding: ${({ theme }) => theme.spacing(1)}; padding: ${({ theme }) => theme.spacing(1)};
&:hover { &:hover {
background-color: ${({ theme }) => theme.background.tertiary}; background-color: ${({ theme }) => theme.background.tertiary};
} }
`; `;
export const RecordBoardColumnNewButton = () => { export const RecordBoardColumnNewButton = ({
columnId,
}: {
columnId: string;
}) => {
const theme = useTheme(); const theme = useTheme();
const { handleAddNewCardClick } = useAddNewCard('last');
const { newRecord, handleNewButtonClick, handleCreateSuccess } =
useColumnNewCardActions(columnId);
if (newRecord.isCreating && newRecord.position === 'last') {
return (
<RecordBoardCard
isCreating={true}
onCreateSuccess={() => handleCreateSuccess('last')}
position="last"
/>
);
}
return ( return (
<StyledButton onClick={handleAddNewCardClick}> <StyledNewButton onClick={() => handleNewButtonClick('last')}>
<IconPlus size={theme.icon.size.md} /> <IconPlus size={theme.icon.size.md} />
New New
</StyledButton> </StyledNewButton>
); );
}; };

View File

@ -1,20 +1,135 @@
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
import { useContext } from 'react'; import { recordBoardNewRecordByColumnIdSelector } from '@/object-record/record-board/states/selectors/recordBoardNewRecordByColumnIdSelector';
import { useCallback, useContext } from 'react';
import { useRecoilCallback } from 'recoil';
import { v4 as uuidv4 } from 'uuid';
export const useAddNewCard = (position: string) => { export const useAddNewCard = () => {
const { columnDefinition } = useContext(RecordBoardColumnContext); const columnContext = useContext(RecordBoardColumnContext);
const { createOneRecord, selectFieldMetadataItem } = const { createOneRecord, selectFieldMetadataItem } =
useContext(RecordBoardContext); useContext(RecordBoardContext);
const handleAddNewCardClick = () => { const getColumnDefinitionId = useCallback(
createOneRecord({ (columnId?: string) => {
[selectFieldMetadataItem.name]: columnDefinition.value, const columnDefinitionId = columnId || columnContext?.columnDefinition.id;
position: position, if (!columnDefinitionId) {
}); throw new Error('Column ID is required');
}
return columnDefinitionId;
},
[columnContext],
);
const addNewCard = useCallback(
(set: any, columnDefinitionId: string, position: 'first' | 'last') => {
set(
recordBoardNewRecordByColumnIdSelector({
familyKey: columnDefinitionId,
scopeId: columnDefinitionId,
}),
{
id: uuidv4(),
columnId: columnDefinitionId,
isCreating: true,
position,
},
);
},
[],
);
const createRecord = useCallback(
(
labelIdentifier: string,
labelValue: string,
position: 'first' | 'last',
) => {
if (labelValue !== '') {
createOneRecord({
[selectFieldMetadataItem.name]: columnContext?.columnDefinition.value,
position,
[labelIdentifier.toLowerCase()]: labelValue,
});
}
},
[createOneRecord, columnContext, selectFieldMetadataItem],
);
const handleAddNewCardClick = useRecoilCallback(
({ set }) =>
(
labelIdentifier: string,
labelValue: string,
position: 'first' | 'last',
columnId?: string,
): void => {
const columnDefinitionId = getColumnDefinitionId(columnId);
addNewCard(set, columnDefinitionId, position);
createRecord(labelIdentifier, labelValue, position);
},
[addNewCard, createRecord, getColumnDefinitionId],
);
const handleCreateSuccess = useRecoilCallback(
({ set }) =>
(position: 'first' | 'last', columnId?: string): void => {
const columnDefinitionId = getColumnDefinitionId(columnId);
set(
recordBoardNewRecordByColumnIdSelector({
familyKey: columnDefinitionId,
scopeId: columnDefinitionId,
}),
{
id: '',
columnId: columnDefinitionId,
isCreating: false,
position,
},
);
},
[getColumnDefinitionId],
);
const handleCreate = (
labelIdentifier: string,
labelValue: string,
position: 'first' | 'last',
onCreateSuccess?: () => void,
) => {
if (labelValue.trim() !== '' && position !== undefined) {
handleAddNewCardClick(labelIdentifier, labelValue.trim(), position);
onCreateSuccess?.();
}
};
const handleBlur = (
labelIdentifier: string,
labelValue: string,
position: 'first' | 'last',
onCreateSuccess?: () => void,
) => {
if (labelValue.trim() === '') {
onCreateSuccess?.();
} else {
handleCreate(labelIdentifier, labelValue, position, onCreateSuccess);
}
};
const handleInputEnter = (
labelIdentifier: string,
labelValue: string,
position: 'first' | 'last',
onCreateSuccess?: () => void,
) => {
handleCreate(labelIdentifier, labelValue, position, onCreateSuccess);
}; };
return { return {
handleAddNewCardClick, handleAddNewCardClick,
handleCreateSuccess,
handleCreate,
handleBlur,
handleInputEnter,
}; };
}; };

View File

@ -0,0 +1,38 @@
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) => {
const { visibleFieldDefinitionsState } = useRecordBoardStates();
const visibleFieldDefinitions = useRecoilValue(
visibleFieldDefinitionsState(),
);
const labelIdentifierField = visibleFieldDefinitions.find(
(field) => field.isLabelIdentifier,
);
const { handleAddNewCardClick, handleCreateSuccess } = useAddNewCard();
const newRecord = useRecoilValue(
recordBoardNewRecordByColumnIdSelector({
familyKey: columnId,
scopeId: columnId,
}),
);
const handleNewButtonClick = (position: 'first' | 'last') => {
handleAddNewCardClick(
labelIdentifierField?.label ?? '',
'',
position,
columnId,
);
};
return {
newRecord,
handleNewButtonClick,
handleCreateSuccess,
};
};

View File

@ -0,0 +1,19 @@
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState';
export type NewCard = {
id: string;
columnId: string;
isCreating: boolean;
position: 'first' | 'last';
};
export const recordBoardNewRecordByColumnIdComponentFamilyState =
createComponentFamilyState<NewCard, string>({
key: 'recordBoardNewRecordByColumnIdComponentFamilyState',
defaultValue: {
id: '',
columnId: '',
isCreating: false,
position: 'last',
},
});

View File

@ -0,0 +1,31 @@
import { createComponentFamilySelector } from '@/ui/utilities/state/component-state/utils/createComponentFamilySelector';
import {
NewCard,
recordBoardNewRecordByColumnIdComponentFamilyState,
} from '../recordBoardNewRecordByColumnIdComponentFamilyState';
export const recordBoardNewRecordByColumnIdSelector =
createComponentFamilySelector<NewCard, string>({
key: 'recordBoardNewRecordByColumnIdSelector',
get:
({ familyKey, scopeId }: { familyKey: string; scopeId: string }) =>
({ get }) => {
return get(
recordBoardNewRecordByColumnIdComponentFamilyState({
familyKey,
scopeId,
}),
) as NewCard;
},
set:
({ familyKey, scopeId }: { familyKey: string; scopeId: string }) =>
({ set }, newValue) => {
set(
recordBoardNewRecordByColumnIdComponentFamilyState({
familyKey,
scopeId,
}),
newValue as NewCard,
);
},
});

View File

@ -1,5 +1,6 @@
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard';
import { RecordBoardColumnDefinition } from '@/object-record/record-board/types/RecordBoardColumnDefinition'; import { RecordBoardColumnDefinition } from '@/object-record/record-board/types/RecordBoardColumnDefinition';
import { RecordIndexPageKanbanAddMenuItem } from '@/object-record/record-index/components/RecordIndexPageKanbanAddMenuItem'; import { RecordIndexPageKanbanAddMenuItem } from '@/object-record/record-index/components/RecordIndexPageKanbanAddMenuItem';
import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext'; import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext';
@ -37,8 +38,15 @@ export const RecordIndexPageKanbanAddButton = () => {
RecordIndexRootPropsContext, RecordIndexRootPropsContext,
); );
const { columnIdsState } = useRecordBoardStates(recordIndexId); const { columnIdsState, visibleFieldDefinitionsState } =
useRecordBoardStates(recordIndexId);
const columnIds = useRecoilValue(columnIdsState); const columnIds = useRecoilValue(columnIdsState);
const visibleFieldDefinitions = useRecoilValue(
visibleFieldDefinitionsState(),
);
const labelIdentifierField = visibleFieldDefinitions.find(
(field) => field.isLabelIdentifier,
);
const { const {
setHotkeyScopeAndMemorizePreviousScope, setHotkeyScopeAndMemorizePreviousScope,
@ -50,14 +58,12 @@ export const RecordIndexPageKanbanAddButton = () => {
const { closeDropdown } = useDropdown(dropdownId); const { closeDropdown } = useDropdown(dropdownId);
const { const { selectFieldMetadataItem, isOpportunity, createOpportunity } =
selectFieldMetadataItem, useRecordIndexPageKanbanAddButton({
isOpportunity, objectNamePlural,
createOpportunity, });
createRecordWithoutCompany,
} = useRecordIndexPageKanbanAddButton({ const { handleAddNewCardClick } = useAddNewCard();
objectNamePlural,
});
const handleItemClick = useCallback( const handleItemClick = useCallback(
(columnDefinition: RecordBoardColumnDefinition) => { (columnDefinition: RecordBoardColumnDefinition) => {
@ -68,18 +74,23 @@ export const RecordIndexPageKanbanAddButton = () => {
RelationPickerHotkeyScope.RelationPicker, RelationPickerHotkeyScope.RelationPicker,
); );
} else { } else {
createRecordWithoutCompany(columnDefinition); handleAddNewCardClick(
labelIdentifierField?.label ?? '',
'',
'first',
columnDefinition.id,
);
closeDropdown(); closeDropdown();
} }
}, },
[ [
isOpportunity, isOpportunity,
createRecordWithoutCompany, handleAddNewCardClick,
setHotkeyScopeAndMemorizePreviousScope, setHotkeyScopeAndMemorizePreviousScope,
closeDropdown, closeDropdown,
labelIdentifierField,
], ],
); );
const handleEntitySelect = useCallback( const handleEntitySelect = useCallback(
(company?: EntityForSelect) => { (company?: EntityForSelect) => {
setIsSelectingCompany(false); setIsSelectingCompany(false);

View File

@ -45,21 +45,9 @@ export const useRecordIndexPageKanbanAddButton = ({
} }
}; };
const createRecordWithoutCompany = (
columnDefinition: RecordBoardColumnDefinition,
) => {
if (isDefined(selectFieldMetadataItem)) {
createOneRecord({
[selectFieldMetadataItem.name]: columnDefinition?.value,
position: 'first',
});
}
};
return { return {
selectFieldMetadataItem, selectFieldMetadataItem,
isOpportunity, isOpportunity,
createOpportunity, createOpportunity,
createRecordWithoutCompany,
}; };
}; };

View File

@ -89,7 +89,6 @@ export const TextInput = ({
onInputEnter?.(); onInputEnter?.();
if (isDefined(inputRef) && 'current' in inputRef) { if (isDefined(inputRef) && 'current' in inputRef) {
inputRef.current?.blur();
setIsFocused(false); setIsFocused(false);
} }
}, },