mirror of
https://github.com/lingble/twenty.git
synced 2025-10-29 20:02:29 +00:00
@@ -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 { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
|
||||
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 { RecordIdentifierChip } from '@/object-record/record-index/components/RecordIndexRecordChip';
|
||||
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 { RecordValueSetterEffect } from '@/object-record/record-store/components/RecordValueSetterEffect';
|
||||
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 { Checkbox, CheckboxVariant } from '@/ui/input/components/Checkbox';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/contextMenuIsOpenState';
|
||||
import { contextMenuPositionState } from '@/ui/navigation/context-menu/states/contextMenuPositionState';
|
||||
import { AnimatedEaseInOut } from '@/ui/utilities/animation/components/AnimatedEaseInOut';
|
||||
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 }>`
|
||||
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`
|
||||
padding-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
width: 100%;
|
||||
@@ -130,7 +141,21 @@ const StyledRecordInlineCellPlaceholder = styled.div`
|
||||
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 { updateOneRecord, objectMetadataItem } =
|
||||
useContext(RecordBoardContext);
|
||||
@@ -139,7 +164,6 @@ export const RecordBoardCard = () => {
|
||||
isRecordBoardCardSelectedFamilyState,
|
||||
visibleFieldDefinitionsState,
|
||||
} = useRecordBoardStates();
|
||||
|
||||
const isCompactModeActive = useRecoilValue(isCompactModeActiveState);
|
||||
|
||||
const [isCardInCompactMode, setIsCardInCompactMode] = useState(true);
|
||||
@@ -205,66 +229,106 @@ export const RecordBoardCard = () => {
|
||||
rootMargin: '1000px',
|
||||
});
|
||||
|
||||
if (!record) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const visibleFieldDefinitionsFiltered = visibleFieldDefinitions.filter(
|
||||
(boardField) => !boardField.isLabelIdentifier,
|
||||
);
|
||||
|
||||
const labelIdentifierField = visibleFieldDefinitions.find(
|
||||
(field) => field.isLabelIdentifier,
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledBoardCardWrapper onContextMenu={handleContextMenu}>
|
||||
<RecordValueSetterEffect recordId={recordId} />
|
||||
{!isCreating && <RecordValueSetterEffect recordId={recordId} />}
|
||||
<StyledBoardCard
|
||||
ref={cardRef}
|
||||
selected={isCurrentCardSelected}
|
||||
onMouseLeave={onMouseLeaveBoard}
|
||||
onClick={() => {
|
||||
setIsCurrentCardSelected(!isCurrentCardSelected);
|
||||
if (!isCreating) {
|
||||
setIsCurrentCardSelected(!isCurrentCardSelected);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<StyledBoardCardHeader showCompactView={isCompactModeActive}>
|
||||
<RecordIdentifierChip
|
||||
objectNameSingular={objectMetadataItem.nameSingular}
|
||||
record={record}
|
||||
variant={AvatarChipVariant.Transparent}
|
||||
/>
|
||||
{isCompactModeActive && (
|
||||
<StyledCompactIconContainer className="compact-icon-container">
|
||||
<LightIconButton
|
||||
Icon={IconEye}
|
||||
accent="tertiary"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsCardInCompactMode(false);
|
||||
}}
|
||||
{isCreating && position !== undefined ? (
|
||||
<RecordInlineCellEditMode>
|
||||
<StyledTextInput
|
||||
autoFocus
|
||||
value={newLabelValue}
|
||||
onInputEnter={() =>
|
||||
handleInputEnter(
|
||||
labelIdentifierField?.label ?? '',
|
||||
newLabelValue,
|
||||
position,
|
||||
onCreateSuccess,
|
||||
)
|
||||
}
|
||||
onBlur={() =>
|
||||
handleBlur(
|
||||
labelIdentifierField?.label ?? '',
|
||||
newLabelValue,
|
||||
position,
|
||||
onCreateSuccess,
|
||||
)
|
||||
}
|
||||
onChange={(text: string) => setNewLabelValue(text)}
|
||||
placeholder={labelIdentifierField?.label}
|
||||
/>
|
||||
</StyledCompactIconContainer>
|
||||
)}
|
||||
<StyledCheckboxContainer className="checkbox-container">
|
||||
<Checkbox
|
||||
hoverable
|
||||
checked={isCurrentCardSelected}
|
||||
onChange={() => setIsCurrentCardSelected(!isCurrentCardSelected)}
|
||||
variant={CheckboxVariant.Secondary}
|
||||
</RecordInlineCellEditMode>
|
||||
) : (
|
||||
<RecordIdentifierChip
|
||||
objectNameSingular={objectMetadataItem.nameSingular}
|
||||
record={record as ObjectRecord}
|
||||
variant={AvatarChipVariant.Transparent}
|
||||
/>
|
||||
</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>
|
||||
<StyledBoardCardBody>
|
||||
<AnimatedEaseInOut
|
||||
isOpen={!isCardInCompactMode || !isCompactModeActive}
|
||||
initial={false}
|
||||
>
|
||||
|
||||
<AnimatedEaseInOut
|
||||
isOpen={!isCardInCompactMode || !isCompactModeActive}
|
||||
initial={false}
|
||||
>
|
||||
<StyledBoardCardBody>
|
||||
{visibleFieldDefinitionsFiltered.map((fieldDefinition) => (
|
||||
<PreventSelectOnClickContainer
|
||||
key={fieldDefinition.fieldMetadataId}
|
||||
>
|
||||
<FieldContext.Provider
|
||||
value={{
|
||||
recordId,
|
||||
recordId: isCreating ? '' : recordId,
|
||||
maxWidth: 156,
|
||||
recoilScopeId: recordId + fieldDefinition.fieldMetadataId,
|
||||
recoilScopeId:
|
||||
(isCreating ? 'new' : recordId) +
|
||||
fieldDefinition.fieldMetadataId,
|
||||
isLabelIdentifier: false,
|
||||
fieldDefinition: {
|
||||
disableTooltip: false,
|
||||
@@ -284,15 +348,15 @@ export const RecordBoardCard = () => {
|
||||
}}
|
||||
>
|
||||
{inView ? (
|
||||
<RecordInlineCell />
|
||||
<StyledRecordInlineCell />
|
||||
) : (
|
||||
<StyledRecordInlineCellPlaceholder />
|
||||
)}
|
||||
</FieldContext.Provider>
|
||||
</PreventSelectOnClickContainer>
|
||||
))}
|
||||
</AnimatedEaseInOut>
|
||||
</StyledBoardCardBody>
|
||||
</StyledBoardCardBody>
|
||||
</AnimatedEaseInOut>
|
||||
</StyledBoardCard>
|
||||
</StyledBoardCardWrapper>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useContext } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Draggable, DroppableProvided } from '@hello-pangea/dnd';
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
@@ -110,7 +110,7 @@ export const RecordBoardColumnCardsContainer = ({
|
||||
CoreObjectNameSingular.Opportunity ? (
|
||||
<RecordBoardColumnNewOpportunityButton />
|
||||
) : (
|
||||
<RecordBoardColumnNewButton />
|
||||
<RecordBoardColumnNewButton columnId={columnDefinition.id} />
|
||||
)}
|
||||
</StyledNewButtonContainer>
|
||||
</div>
|
||||
|
||||
@@ -4,10 +4,11 @@ import { IconDotsVertical, IconPlus, 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 { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard';
|
||||
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 { RecordBoardColumnDefinitionType } from '@/object-record/record-board/types/RecordBoardColumnDefinition';
|
||||
import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect';
|
||||
@@ -94,16 +95,16 @@ export const RecordBoardColumnHeader = () => {
|
||||
handleCancel,
|
||||
handleEntitySelect,
|
||||
} = useAddNewOpportunity('first');
|
||||
const { handleAddNewCardClick } = useAddNewCard('first');
|
||||
|
||||
const { newRecord, handleNewButtonClick, handleCreateSuccess } =
|
||||
useColumnNewCardActions(columnDefinition.id);
|
||||
|
||||
const isOpportunity =
|
||||
objectMetadataItem.nameSingular === CoreObjectNameSingular.Opportunity;
|
||||
|
||||
const handleClick = isOpportunity
|
||||
? handleAddNewOpportunityClick
|
||||
: () => {
|
||||
handleAddNewCardClick();
|
||||
};
|
||||
: () => handleNewButtonClick('first');
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -164,6 +165,13 @@ export const RecordBoardColumnHeader = () => {
|
||||
stageId={columnDefinition.id}
|
||||
/>
|
||||
)}
|
||||
{newRecord?.isCreating && newRecord.position === 'first' && (
|
||||
<RecordBoardCard
|
||||
isCreating={true}
|
||||
onCreateSuccess={() => handleCreateSuccess('first')}
|
||||
position="first"
|
||||
/>
|
||||
)}
|
||||
{isCreatingCard && (
|
||||
<SingleEntitySelect
|
||||
disableBackgroundBlur
|
||||
|
||||
@@ -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 styled from '@emotion/styled';
|
||||
import { IconPlus } from 'twenty-ui';
|
||||
|
||||
import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard';
|
||||
|
||||
const StyledButton = styled.button`
|
||||
const StyledNewButton = styled.button`
|
||||
align-items: center;
|
||||
align-self: baseline;
|
||||
background-color: ${({ theme }) => theme.background.primary};
|
||||
@@ -15,19 +15,35 @@ const StyledButton = 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 RecordBoardColumnNewButton = ({
|
||||
columnId,
|
||||
}: {
|
||||
columnId: string;
|
||||
}) => {
|
||||
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 (
|
||||
<StyledButton onClick={handleAddNewCardClick}>
|
||||
<StyledNewButton onClick={() => handleNewButtonClick('last')}>
|
||||
<IconPlus size={theme.icon.size.md} />
|
||||
New
|
||||
</StyledButton>
|
||||
</StyledNewButton>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,20 +1,135 @@
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
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) => {
|
||||
const { columnDefinition } = useContext(RecordBoardColumnContext);
|
||||
export const useAddNewCard = () => {
|
||||
const columnContext = useContext(RecordBoardColumnContext);
|
||||
const { createOneRecord, selectFieldMetadataItem } =
|
||||
useContext(RecordBoardContext);
|
||||
|
||||
const handleAddNewCardClick = () => {
|
||||
createOneRecord({
|
||||
[selectFieldMetadataItem.name]: columnDefinition.value,
|
||||
position: position,
|
||||
});
|
||||
const getColumnDefinitionId = useCallback(
|
||||
(columnId?: string) => {
|
||||
const columnDefinitionId = columnId || columnContext?.columnDefinition.id;
|
||||
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 {
|
||||
handleAddNewCardClick,
|
||||
handleCreateSuccess,
|
||||
handleCreate,
|
||||
handleBlur,
|
||||
handleInputEnter,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
@@ -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',
|
||||
},
|
||||
});
|
||||
@@ -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,
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
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 { RecordIndexPageKanbanAddMenuItem } from '@/object-record/record-index/components/RecordIndexPageKanbanAddMenuItem';
|
||||
import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext';
|
||||
@@ -37,8 +38,15 @@ export const RecordIndexPageKanbanAddButton = () => {
|
||||
RecordIndexRootPropsContext,
|
||||
);
|
||||
|
||||
const { columnIdsState } = useRecordBoardStates(recordIndexId);
|
||||
const { columnIdsState, visibleFieldDefinitionsState } =
|
||||
useRecordBoardStates(recordIndexId);
|
||||
const columnIds = useRecoilValue(columnIdsState);
|
||||
const visibleFieldDefinitions = useRecoilValue(
|
||||
visibleFieldDefinitionsState(),
|
||||
);
|
||||
const labelIdentifierField = visibleFieldDefinitions.find(
|
||||
(field) => field.isLabelIdentifier,
|
||||
);
|
||||
|
||||
const {
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
@@ -50,14 +58,12 @@ export const RecordIndexPageKanbanAddButton = () => {
|
||||
|
||||
const { closeDropdown } = useDropdown(dropdownId);
|
||||
|
||||
const {
|
||||
selectFieldMetadataItem,
|
||||
isOpportunity,
|
||||
createOpportunity,
|
||||
createRecordWithoutCompany,
|
||||
} = useRecordIndexPageKanbanAddButton({
|
||||
objectNamePlural,
|
||||
});
|
||||
const { selectFieldMetadataItem, isOpportunity, createOpportunity } =
|
||||
useRecordIndexPageKanbanAddButton({
|
||||
objectNamePlural,
|
||||
});
|
||||
|
||||
const { handleAddNewCardClick } = useAddNewCard();
|
||||
|
||||
const handleItemClick = useCallback(
|
||||
(columnDefinition: RecordBoardColumnDefinition) => {
|
||||
@@ -68,18 +74,23 @@ export const RecordIndexPageKanbanAddButton = () => {
|
||||
RelationPickerHotkeyScope.RelationPicker,
|
||||
);
|
||||
} else {
|
||||
createRecordWithoutCompany(columnDefinition);
|
||||
handleAddNewCardClick(
|
||||
labelIdentifierField?.label ?? '',
|
||||
'',
|
||||
'first',
|
||||
columnDefinition.id,
|
||||
);
|
||||
closeDropdown();
|
||||
}
|
||||
},
|
||||
[
|
||||
isOpportunity,
|
||||
createRecordWithoutCompany,
|
||||
handleAddNewCardClick,
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
closeDropdown,
|
||||
labelIdentifierField,
|
||||
],
|
||||
);
|
||||
|
||||
const handleEntitySelect = useCallback(
|
||||
(company?: EntityForSelect) => {
|
||||
setIsSelectingCompany(false);
|
||||
|
||||
@@ -45,21 +45,9 @@ export const useRecordIndexPageKanbanAddButton = ({
|
||||
}
|
||||
};
|
||||
|
||||
const createRecordWithoutCompany = (
|
||||
columnDefinition: RecordBoardColumnDefinition,
|
||||
) => {
|
||||
if (isDefined(selectFieldMetadataItem)) {
|
||||
createOneRecord({
|
||||
[selectFieldMetadataItem.name]: columnDefinition?.value,
|
||||
position: 'first',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
selectFieldMetadataItem,
|
||||
isOpportunity,
|
||||
createOpportunity,
|
||||
createRecordWithoutCompany,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -89,7 +89,6 @@ export const TextInput = ({
|
||||
onInputEnter?.();
|
||||
|
||||
if (isDefined(inputRef) && 'current' in inputRef) {
|
||||
inputRef.current?.blur();
|
||||
setIsFocused(false);
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user