mirror of
https://github.com/twentyhq/twenty.git
synced 2024-11-26 04:17:15 +03:00
Kanban card creation followup (#7285)
@Bonapara #7002 @FelixMalfait #6316 ;) Naming could be improved, do let me know! https://github.com/user-attachments/assets/b10c9120-644d-4943-bc65-ec0d62f9986f
This commit is contained in:
parent
5f9435c718
commit
04579144ca
@ -141,10 +141,6 @@ const StyledRecordInlineCellPlaceholder = styled.div`
|
||||
height: 24px;
|
||||
`;
|
||||
|
||||
const StyledRecordInlineCell = styled(RecordInlineCell)`
|
||||
height: 24px;
|
||||
`;
|
||||
|
||||
export const RecordBoardCard = ({
|
||||
isCreating = false,
|
||||
onCreateSuccess,
|
||||
@ -348,7 +344,7 @@ export const RecordBoardCard = ({
|
||||
}}
|
||||
>
|
||||
{inView ? (
|
||||
<StyledRecordInlineCell />
|
||||
<RecordInlineCell />
|
||||
) : (
|
||||
<StyledRecordInlineCellPlaceholder />
|
||||
)}
|
||||
|
@ -12,6 +12,7 @@ import { RecordBoardColumnFetchMoreLoader } from '@/object-record/record-board/r
|
||||
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 { 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';
|
||||
import { isRecordIndexBoardColumnLoadingFamilyState } from '@/object-record/states/isRecordBoardColumnLoadingFamilyState';
|
||||
|
||||
@ -64,6 +65,8 @@ export const RecordBoardColumnCardsContainer = ({
|
||||
const numberOfFields = visibleFieldDefinitions.length;
|
||||
|
||||
const isCompactModeActive = useRecoilValue(isCompactModeActiveState);
|
||||
const { isOpportunitiesCompanyFieldDisabled } =
|
||||
useIsOpportunitiesCompanyFieldDisabled();
|
||||
|
||||
return (
|
||||
<StyledColumnCardsContainer
|
||||
@ -107,8 +110,11 @@ export const RecordBoardColumnCardsContainer = ({
|
||||
>
|
||||
<StyledNewButtonContainer>
|
||||
{objectMetadataItem.nameSingular ===
|
||||
CoreObjectNameSingular.Opportunity ? (
|
||||
<RecordBoardColumnNewOpportunityButton />
|
||||
CoreObjectNameSingular.Opportunity &&
|
||||
!isOpportunitiesCompanyFieldDisabled ? (
|
||||
<RecordBoardColumnNewOpportunityButton
|
||||
columnId={columnDefinition.id}
|
||||
/>
|
||||
) : (
|
||||
<RecordBoardColumnNewButton columnId={columnDefinition.id} />
|
||||
)}
|
||||
|
@ -7,8 +7,8 @@ import { RecordBoardContext } from '@/object-record/record-board/contexts/Record
|
||||
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 { useAddNewOpportunity } from '@/object-record/record-board/record-board-column/hooks/useAddNewOpportunity';
|
||||
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 { RecordBoardColumnDefinitionType } from '@/object-record/record-board/types/RecordBoardColumnDefinition';
|
||||
import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect';
|
||||
@ -90,21 +90,18 @@ export const RecordBoardColumnHeader = () => {
|
||||
const boardColumnTotal = 0;
|
||||
|
||||
const {
|
||||
isCreatingCard,
|
||||
handleAddNewOpportunityClick,
|
||||
handleCancel,
|
||||
handleEntitySelect,
|
||||
} = useAddNewOpportunity('first');
|
||||
newRecord,
|
||||
handleNewButtonClick,
|
||||
handleCreateSuccess,
|
||||
|
||||
const { newRecord, handleNewButtonClick, handleCreateSuccess } =
|
||||
useColumnNewCardActions(columnDefinition.id);
|
||||
handleEntitySelect,
|
||||
} = useColumnNewCardActions(columnDefinition.id);
|
||||
const { isOpportunitiesCompanyFieldDisabled } =
|
||||
useIsOpportunitiesCompanyFieldDisabled();
|
||||
|
||||
const isOpportunity =
|
||||
objectMetadataItem.nameSingular === CoreObjectNameSingular.Opportunity;
|
||||
|
||||
const handleClick = isOpportunity
|
||||
? handleAddNewOpportunityClick
|
||||
: () => handleNewButtonClick('first');
|
||||
objectMetadataItem.nameSingular === CoreObjectNameSingular.Opportunity &&
|
||||
!isOpportunitiesCompanyFieldDisabled;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -152,7 +149,7 @@ export const RecordBoardColumnHeader = () => {
|
||||
<LightIconButton
|
||||
accent="tertiary"
|
||||
Icon={IconPlus}
|
||||
onClick={handleClick}
|
||||
onClick={() => handleNewButtonClick('first', isOpportunity)}
|
||||
/>
|
||||
</StyledHeaderActions>
|
||||
)}
|
||||
@ -165,23 +162,26 @@ export const RecordBoardColumnHeader = () => {
|
||||
stageId={columnDefinition.id}
|
||||
/>
|
||||
)}
|
||||
{newRecord?.isCreating && newRecord.position === 'first' && (
|
||||
<RecordBoardCard
|
||||
isCreating={true}
|
||||
onCreateSuccess={() => handleCreateSuccess('first')}
|
||||
position="first"
|
||||
/>
|
||||
)}
|
||||
{isCreatingCard && (
|
||||
<SingleEntitySelect
|
||||
disableBackgroundBlur
|
||||
onCancel={handleCancel}
|
||||
onEntitySelected={handleEntitySelect}
|
||||
relationObjectNameSingular={CoreObjectNameSingular.Company}
|
||||
relationPickerScopeId="relation-picker"
|
||||
selectedRelationRecordIds={[]}
|
||||
/>
|
||||
)}
|
||||
{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"
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -30,7 +30,11 @@ export const RecordBoardColumnNewButton = ({
|
||||
const { newRecord, handleNewButtonClick, handleCreateSuccess } =
|
||||
useColumnNewCardActions(columnId);
|
||||
|
||||
if (newRecord.isCreating && newRecord.position === 'last') {
|
||||
if (
|
||||
newRecord.isCreating &&
|
||||
newRecord.position === 'last' &&
|
||||
!newRecord.isOpportunity
|
||||
) {
|
||||
return (
|
||||
<RecordBoardCard
|
||||
isCreating={true}
|
||||
@ -41,7 +45,7 @@ export const RecordBoardColumnNewButton = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledNewButton onClick={() => handleNewButtonClick('last')}>
|
||||
<StyledNewButton onClick={() => handleNewButtonClick('last', false)}>
|
||||
<IconPlus size={theme.icon.size.md} />
|
||||
New
|
||||
</StyledNewButton>
|
||||
|
@ -3,7 +3,7 @@ import styled from '@emotion/styled';
|
||||
import { IconPlus } from 'twenty-ui';
|
||||
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
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 { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect';
|
||||
|
||||
const StyledButton = styled.button`
|
||||
@ -23,27 +23,36 @@ const StyledButton = styled.button`
|
||||
}
|
||||
`;
|
||||
|
||||
export const RecordBoardColumnNewOpportunityButton = () => {
|
||||
export const RecordBoardColumnNewOpportunityButton = ({
|
||||
columnId,
|
||||
}: {
|
||||
columnId: string;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const {
|
||||
isCreatingCard,
|
||||
handleAddNewOpportunityClick,
|
||||
handleCancel,
|
||||
newRecord,
|
||||
handleNewButtonClick,
|
||||
handleEntitySelect,
|
||||
} = useAddNewOpportunity('last');
|
||||
handleCreateSuccess,
|
||||
} = useColumnNewCardActions(columnId);
|
||||
return (
|
||||
<>
|
||||
{isCreatingCard ? (
|
||||
{newRecord.isCreating &&
|
||||
newRecord.position === 'last' &&
|
||||
newRecord.isOpportunity ? (
|
||||
<SingleEntitySelect
|
||||
disableBackgroundBlur
|
||||
onCancel={handleCancel}
|
||||
onEntitySelected={handleEntitySelect}
|
||||
onCancel={() => handleCreateSuccess('last', columnId, false)}
|
||||
onEntitySelected={(company) =>
|
||||
company ? handleEntitySelect('last', company) : null
|
||||
}
|
||||
relationObjectNameSingular={CoreObjectNameSingular.Company}
|
||||
relationPickerScopeId="relation-picker"
|
||||
selectedRelationRecordIds={[]}
|
||||
/>
|
||||
) : (
|
||||
<StyledButton onClick={handleAddNewOpportunityClick}>
|
||||
<StyledButton onClick={() => handleNewButtonClick('last', true)}>
|
||||
<IconPlus size={theme.icon.size.md} />
|
||||
New
|
||||
</StyledButton>
|
||||
|
@ -1,15 +1,28 @@
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
|
||||
import { recordBoardNewRecordByColumnIdSelector } from '@/object-record/record-board/states/selectors/recordBoardNewRecordByColumnIdSelector';
|
||||
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
|
||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { useCallback, useContext } from 'react';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { RecoilState, useRecoilCallback } from 'recoil';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
type SetFunction = <T>(
|
||||
recoilVal: RecoilState<T>,
|
||||
valOrUpdater: T | ((currVal: T) => T),
|
||||
) => void;
|
||||
|
||||
export const useAddNewCard = () => {
|
||||
const columnContext = useContext(RecordBoardColumnContext);
|
||||
const { createOneRecord, selectFieldMetadataItem } =
|
||||
useContext(RecordBoardContext);
|
||||
|
||||
const {
|
||||
goBackToPreviousHotkeyScope,
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
const getColumnDefinitionId = useCallback(
|
||||
(columnId?: string) => {
|
||||
const columnDefinitionId = columnId || columnContext?.columnDefinition.id;
|
||||
@ -21,8 +34,13 @@ export const useAddNewCard = () => {
|
||||
[columnContext],
|
||||
);
|
||||
|
||||
const addNewCard = useCallback(
|
||||
(set: any, columnDefinitionId: string, position: 'first' | 'last') => {
|
||||
const addNewItem = useCallback(
|
||||
(
|
||||
set: SetFunction,
|
||||
columnDefinitionId: string,
|
||||
position: 'first' | 'last',
|
||||
isOpportunity: boolean,
|
||||
) => {
|
||||
set(
|
||||
recordBoardNewRecordByColumnIdSelector({
|
||||
familyKey: columnDefinitionId,
|
||||
@ -33,6 +51,8 @@ export const useAddNewCard = () => {
|
||||
columnId: columnDefinitionId,
|
||||
isCreating: true,
|
||||
position,
|
||||
isOpportunity,
|
||||
company: null,
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -44,12 +64,19 @@ export const useAddNewCard = () => {
|
||||
labelIdentifier: string,
|
||||
labelValue: string,
|
||||
position: 'first' | 'last',
|
||||
isOpportunity: boolean,
|
||||
company?: EntityForSelect,
|
||||
) => {
|
||||
if (labelValue !== '') {
|
||||
if (
|
||||
(isOpportunity && company !== null) ||
|
||||
(!isOpportunity && labelValue !== '')
|
||||
) {
|
||||
createOneRecord({
|
||||
[selectFieldMetadataItem.name]: columnContext?.columnDefinition.value,
|
||||
position,
|
||||
[labelIdentifier.toLowerCase()]: labelValue,
|
||||
...(isOpportunity
|
||||
? { companyId: company?.id, name: company?.name }
|
||||
: { [labelIdentifier.toLowerCase()]: labelValue }),
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -62,18 +89,34 @@ export const useAddNewCard = () => {
|
||||
labelIdentifier: string,
|
||||
labelValue: string,
|
||||
position: 'first' | 'last',
|
||||
isOpportunity: boolean,
|
||||
columnId?: string,
|
||||
): void => {
|
||||
const columnDefinitionId = getColumnDefinitionId(columnId);
|
||||
addNewCard(set, columnDefinitionId, position);
|
||||
createRecord(labelIdentifier, labelValue, position);
|
||||
addNewItem(set, columnDefinitionId, position, isOpportunity);
|
||||
if (isOpportunity) {
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
RelationPickerHotkeyScope.RelationPicker,
|
||||
);
|
||||
} else {
|
||||
createRecord(labelIdentifier, labelValue, position, isOpportunity);
|
||||
}
|
||||
},
|
||||
[addNewCard, createRecord, getColumnDefinitionId],
|
||||
[
|
||||
addNewItem,
|
||||
createRecord,
|
||||
getColumnDefinitionId,
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
],
|
||||
);
|
||||
|
||||
const handleCreateSuccess = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(position: 'first' | 'last', columnId?: string): void => {
|
||||
(
|
||||
position: 'first' | 'last',
|
||||
columnId?: string,
|
||||
isOpportunity = false,
|
||||
): void => {
|
||||
const columnDefinitionId = getColumnDefinitionId(columnId);
|
||||
set(
|
||||
recordBoardNewRecordByColumnIdSelector({
|
||||
@ -85,10 +128,15 @@ export const useAddNewCard = () => {
|
||||
columnId: columnDefinitionId,
|
||||
isCreating: false,
|
||||
position,
|
||||
isOpportunity: Boolean(isOpportunity),
|
||||
company: null,
|
||||
},
|
||||
);
|
||||
if (isOpportunity === true) {
|
||||
goBackToPreviousHotkeyScope();
|
||||
}
|
||||
},
|
||||
[getColumnDefinitionId],
|
||||
[getColumnDefinitionId, goBackToPreviousHotkeyScope],
|
||||
);
|
||||
|
||||
const handleCreate = (
|
||||
@ -98,7 +146,13 @@ export const useAddNewCard = () => {
|
||||
onCreateSuccess?: () => void,
|
||||
) => {
|
||||
if (labelValue.trim() !== '' && position !== undefined) {
|
||||
handleAddNewCardClick(labelIdentifier, labelValue.trim(), position);
|
||||
handleAddNewCardClick(
|
||||
labelIdentifier,
|
||||
labelValue.trim(),
|
||||
position,
|
||||
false,
|
||||
'',
|
||||
);
|
||||
onCreateSuccess?.();
|
||||
}
|
||||
};
|
||||
@ -125,11 +179,25 @@ export const useAddNewCard = () => {
|
||||
handleCreate(labelIdentifier, labelValue, position, onCreateSuccess);
|
||||
};
|
||||
|
||||
const handleEntitySelect = useCallback(
|
||||
(
|
||||
position: 'first' | 'last',
|
||||
company: EntityForSelect,
|
||||
columnId?: string,
|
||||
) => {
|
||||
const columnDefinitionId = getColumnDefinitionId(columnId);
|
||||
createRecord('', '', position, true, company);
|
||||
handleCreateSuccess(position, columnDefinitionId, true);
|
||||
},
|
||||
[createRecord, handleCreateSuccess, getColumnDefinitionId],
|
||||
);
|
||||
|
||||
return {
|
||||
handleAddNewCardClick,
|
||||
handleCreateSuccess,
|
||||
handleCreate,
|
||||
handleBlur,
|
||||
handleInputEnter,
|
||||
handleEntitySelect,
|
||||
};
|
||||
};
|
||||
|
@ -1,75 +0,0 @@
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
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 { useEntitySelectSearch } from '@/object-record/relation-picker/hooks/useEntitySelectSearch';
|
||||
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
|
||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { useCallback, useContext, useState } from 'react';
|
||||
|
||||
export const useAddNewOpportunity = (position: string) => {
|
||||
const [isCreatingCard, setIsCreatingCard] = useState(false);
|
||||
|
||||
const { columnDefinition } = useContext(RecordBoardColumnContext);
|
||||
const { createOneRecord, selectFieldMetadataItem } =
|
||||
useContext(RecordBoardContext);
|
||||
|
||||
const {
|
||||
goBackToPreviousHotkeyScope,
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
const { resetSearchFilter } = useEntitySelectSearch({
|
||||
relationPickerScopeId: 'relation-picker',
|
||||
});
|
||||
const { isOpportunitiesCompanyFieldDisabled } =
|
||||
useIsOpportunitiesCompanyFieldDisabled();
|
||||
const handleEntitySelect = useCallback(
|
||||
(company?: EntityForSelect) => {
|
||||
setIsCreatingCard(false);
|
||||
goBackToPreviousHotkeyScope();
|
||||
resetSearchFilter();
|
||||
createOneRecord({
|
||||
name: company?.name,
|
||||
companyId: company?.id,
|
||||
position: position,
|
||||
[selectFieldMetadataItem.name]: columnDefinition.value,
|
||||
});
|
||||
},
|
||||
[
|
||||
columnDefinition,
|
||||
createOneRecord,
|
||||
goBackToPreviousHotkeyScope,
|
||||
resetSearchFilter,
|
||||
selectFieldMetadataItem,
|
||||
position,
|
||||
],
|
||||
);
|
||||
|
||||
const handleAddNewOpportunityClick = useCallback(() => {
|
||||
if (isOpportunitiesCompanyFieldDisabled) {
|
||||
handleEntitySelect();
|
||||
} else {
|
||||
setIsCreatingCard(true);
|
||||
}
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
RelationPickerHotkeyScope.RelationPicker,
|
||||
);
|
||||
}, [
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
isOpportunitiesCompanyFieldDisabled,
|
||||
handleEntitySelect,
|
||||
]);
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
resetSearchFilter();
|
||||
goBackToPreviousHotkeyScope();
|
||||
setIsCreatingCard(false);
|
||||
}, [goBackToPreviousHotkeyScope, resetSearchFilter]);
|
||||
|
||||
return {
|
||||
isCreatingCard,
|
||||
handleEntitySelect,
|
||||
handleAddNewOpportunityClick,
|
||||
handleCancel,
|
||||
};
|
||||
};
|
@ -12,7 +12,8 @@ export const useColumnNewCardActions = (columnId: string) => {
|
||||
(field) => field.isLabelIdentifier,
|
||||
);
|
||||
|
||||
const { handleAddNewCardClick, handleCreateSuccess } = useAddNewCard();
|
||||
const { handleAddNewCardClick, handleCreateSuccess, handleEntitySelect } =
|
||||
useAddNewCard();
|
||||
|
||||
const newRecord = useRecoilValue(
|
||||
recordBoardNewRecordByColumnIdSelector({
|
||||
@ -21,11 +22,15 @@ export const useColumnNewCardActions = (columnId: string) => {
|
||||
}),
|
||||
);
|
||||
|
||||
const handleNewButtonClick = (position: 'first' | 'last') => {
|
||||
const handleNewButtonClick = (
|
||||
position: 'first' | 'last',
|
||||
isOpportunity: boolean,
|
||||
) => {
|
||||
handleAddNewCardClick(
|
||||
labelIdentifierField?.label ?? '',
|
||||
'',
|
||||
position,
|
||||
isOpportunity,
|
||||
columnId,
|
||||
);
|
||||
};
|
||||
@ -34,5 +39,6 @@ export const useColumnNewCardActions = (columnId: string) => {
|
||||
newRecord,
|
||||
handleNewButtonClick,
|
||||
handleCreateSuccess,
|
||||
handleEntitySelect,
|
||||
};
|
||||
};
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
|
||||
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState';
|
||||
|
||||
export type NewCard = {
|
||||
@ -5,6 +6,8 @@ export type NewCard = {
|
||||
columnId: string;
|
||||
isCreating: boolean;
|
||||
position: 'first' | 'last';
|
||||
isOpportunity: boolean;
|
||||
company: EntityForSelect | null;
|
||||
};
|
||||
|
||||
export const recordBoardNewRecordByColumnIdComponentFamilyState =
|
||||
@ -15,5 +18,7 @@ export const recordBoardNewRecordByColumnIdComponentFamilyState =
|
||||
columnId: '',
|
||||
isCreating: false,
|
||||
position: 'last',
|
||||
isOpportunity: false,
|
||||
company: null,
|
||||
},
|
||||
});
|
||||
|
@ -1,24 +1,21 @@
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
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 { useIsOpportunitiesCompanyFieldDisabled } from '@/object-record/record-board/record-board-column/hooks/useIsOpportunitiesCompanyFieldDisabled';
|
||||
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';
|
||||
import { useRecordIndexPageKanbanAddButton } from '@/object-record/record-index/hooks/useRecordIndexPageKanbanAddButton';
|
||||
import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect';
|
||||
import { useEntitySelectSearch } from '@/object-record/relation-picker/hooks/useEntitySelectSearch';
|
||||
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
|
||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState';
|
||||
import { IconButton } from '@/ui/input/button/components/IconButton';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import styled from '@emotion/styled';
|
||||
import { useCallback, useContext, useState } from 'react';
|
||||
import { useCallback, useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconPlus, isDefined } from 'twenty-ui';
|
||||
import { IconPlus } from 'twenty-ui';
|
||||
|
||||
const StyledDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
|
||||
width: 100%;
|
||||
@ -30,13 +27,21 @@ const StyledDropDownMenu = styled(DropdownMenu)`
|
||||
|
||||
export const RecordIndexPageKanbanAddButton = () => {
|
||||
const dropdownId = `record-index-page-add-button-dropdown`;
|
||||
const [isSelectingCompany, setIsSelectingCompany] = useState(false);
|
||||
const [selectedColumnDefinition, setSelectedColumnDefinition] =
|
||||
useState<RecordBoardColumnDefinition>();
|
||||
|
||||
const { recordIndexId, objectNamePlural } = useContext(
|
||||
const { recordIndexId, objectNameSingular } = useContext(
|
||||
RecordIndexRootPropsContext,
|
||||
);
|
||||
const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular });
|
||||
|
||||
const recordIndexKanbanFieldMetadataId = useRecoilValue(
|
||||
recordIndexKanbanFieldMetadataIdState,
|
||||
);
|
||||
|
||||
const selectFieldMetadataItem = objectMetadataItem.fields.find(
|
||||
(field) => field.id === recordIndexKanbanFieldMetadataId,
|
||||
);
|
||||
const isOpportunity =
|
||||
objectMetadataItem.nameSingular === CoreObjectNameSingular.Opportunity;
|
||||
|
||||
const { columnIdsState, visibleFieldDefinitionsState } =
|
||||
useRecordBoardStates(recordIndexId);
|
||||
@ -48,73 +53,32 @@ export const RecordIndexPageKanbanAddButton = () => {
|
||||
(field) => field.isLabelIdentifier,
|
||||
);
|
||||
|
||||
const {
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
goBackToPreviousHotkeyScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
const { resetSearchFilter } = useEntitySelectSearch({
|
||||
relationPickerScopeId: 'relation-picker',
|
||||
});
|
||||
|
||||
const { closeDropdown } = useDropdown(dropdownId);
|
||||
|
||||
const { selectFieldMetadataItem, isOpportunity, createOpportunity } =
|
||||
useRecordIndexPageKanbanAddButton({
|
||||
objectNamePlural,
|
||||
});
|
||||
|
||||
const { isOpportunitiesCompanyFieldDisabled } =
|
||||
useIsOpportunitiesCompanyFieldDisabled();
|
||||
const { handleAddNewCardClick } = useAddNewCard();
|
||||
|
||||
const handleItemClick = useCallback(
|
||||
(columnDefinition: RecordBoardColumnDefinition) => {
|
||||
if (isOpportunity) {
|
||||
setIsSelectingCompany(true);
|
||||
setSelectedColumnDefinition(columnDefinition);
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
RelationPickerHotkeyScope.RelationPicker,
|
||||
);
|
||||
} else {
|
||||
handleAddNewCardClick(
|
||||
labelIdentifierField?.label ?? '',
|
||||
'',
|
||||
'first',
|
||||
columnDefinition.id,
|
||||
);
|
||||
closeDropdown();
|
||||
}
|
||||
const isOpportunityEnabled =
|
||||
isOpportunity && !isOpportunitiesCompanyFieldDisabled;
|
||||
handleAddNewCardClick(
|
||||
labelIdentifierField?.label ?? '',
|
||||
'',
|
||||
'first',
|
||||
isOpportunityEnabled,
|
||||
columnDefinition.id,
|
||||
);
|
||||
closeDropdown();
|
||||
},
|
||||
[
|
||||
isOpportunity,
|
||||
handleAddNewCardClick,
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
closeDropdown,
|
||||
labelIdentifierField,
|
||||
isOpportunitiesCompanyFieldDisabled,
|
||||
],
|
||||
);
|
||||
const handleEntitySelect = useCallback(
|
||||
(company?: EntityForSelect) => {
|
||||
setIsSelectingCompany(false);
|
||||
goBackToPreviousHotkeyScope();
|
||||
resetSearchFilter();
|
||||
if (isDefined(company) && isDefined(selectedColumnDefinition)) {
|
||||
createOpportunity(company, selectedColumnDefinition);
|
||||
}
|
||||
closeDropdown();
|
||||
},
|
||||
[
|
||||
createOpportunity,
|
||||
goBackToPreviousHotkeyScope,
|
||||
resetSearchFilter,
|
||||
selectedColumnDefinition,
|
||||
closeDropdown,
|
||||
],
|
||||
);
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
resetSearchFilter();
|
||||
goBackToPreviousHotkeyScope();
|
||||
setIsSelectingCompany(false);
|
||||
}, [goBackToPreviousHotkeyScope, resetSearchFilter]);
|
||||
|
||||
if (!selectFieldMetadataItem) {
|
||||
return null;
|
||||
@ -137,27 +101,16 @@ export const RecordIndexPageKanbanAddButton = () => {
|
||||
dropdownId={dropdownId}
|
||||
dropdownComponents={
|
||||
<StyledDropDownMenu>
|
||||
{isOpportunity && isSelectingCompany ? (
|
||||
<SingleEntitySelect
|
||||
disableBackgroundBlur
|
||||
onCancel={handleCancel}
|
||||
onEntitySelected={handleEntitySelect}
|
||||
relationObjectNameSingular={CoreObjectNameSingular.Company}
|
||||
relationPickerScopeId="relation-picker"
|
||||
selectedRelationRecordIds={[]}
|
||||
/>
|
||||
) : (
|
||||
<StyledDropdownMenuItemsContainer>
|
||||
{columnIds.map((columnId) => (
|
||||
<RecordIndexPageKanbanAddMenuItem
|
||||
key={columnId}
|
||||
columnId={columnId}
|
||||
recordIndexId={recordIndexId}
|
||||
onItemClick={handleItemClick}
|
||||
/>
|
||||
))}
|
||||
</StyledDropdownMenuItemsContainer>
|
||||
)}
|
||||
<StyledDropdownMenuItemsContainer>
|
||||
{columnIds.map((columnId) => (
|
||||
<RecordIndexPageKanbanAddMenuItem
|
||||
key={columnId}
|
||||
columnId={columnId}
|
||||
recordIndexId={recordIndexId}
|
||||
onItemClick={handleItemClick}
|
||||
/>
|
||||
))}
|
||||
</StyledDropdownMenuItemsContainer>
|
||||
</StyledDropDownMenu>
|
||||
}
|
||||
dropdownHotkeyScope={{ scope: dropdownId }}
|
||||
|
@ -1,53 +0,0 @@
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { RecordBoardColumnDefinition } from '@/object-record/record-board/types/RecordBoardColumnDefinition';
|
||||
import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState';
|
||||
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
type useRecordIndexPageKanbanAddButtonProps = {
|
||||
objectNamePlural: string;
|
||||
};
|
||||
|
||||
export const useRecordIndexPageKanbanAddButton = ({
|
||||
objectNamePlural,
|
||||
}: useRecordIndexPageKanbanAddButtonProps) => {
|
||||
const { objectNameSingular } = useObjectNameSingularFromPlural({
|
||||
objectNamePlural,
|
||||
});
|
||||
const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular });
|
||||
|
||||
const recordIndexKanbanFieldMetadataId = useRecoilValue(
|
||||
recordIndexKanbanFieldMetadataIdState,
|
||||
);
|
||||
const { createOneRecord } = useCreateOneRecord({ objectNameSingular });
|
||||
|
||||
const selectFieldMetadataItem = objectMetadataItem.fields.find(
|
||||
(field) => field.id === recordIndexKanbanFieldMetadataId,
|
||||
);
|
||||
const isOpportunity =
|
||||
objectMetadataItem.nameSingular === CoreObjectNameSingular.Opportunity;
|
||||
|
||||
const createOpportunity = (
|
||||
company: EntityForSelect,
|
||||
columnDefinition: RecordBoardColumnDefinition,
|
||||
) => {
|
||||
if (isDefined(selectFieldMetadataItem)) {
|
||||
createOneRecord({
|
||||
name: company.name,
|
||||
companyId: company.id,
|
||||
position: 'first',
|
||||
[selectFieldMetadataItem.name]: columnDefinition?.value,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
selectFieldMetadataItem,
|
||||
isOpportunity,
|
||||
createOpportunity,
|
||||
};
|
||||
};
|
@ -4,8 +4,8 @@ import { ReactElement, useContext } from 'react';
|
||||
import {
|
||||
AppTooltip,
|
||||
IconComponent,
|
||||
TooltipDelay,
|
||||
OverflowingTextWithTooltip,
|
||||
TooltipDelay,
|
||||
} from 'twenty-ui';
|
||||
|
||||
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
|
||||
@ -55,11 +55,9 @@ const StyledInlineCellBaseContainer = styled.div`
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
height: 24px;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
|
||||
user-select: none;
|
||||
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user