Skip to content

Commit

Permalink
Added "Add record" button in kanban view column headers dropdown (#6649)
Browse files Browse the repository at this point in the history
Closes #4629 
Refactored `RecordBoardColumnNewOpportunityButton` and
`RecordBoardColumnNewButton` to use the same logic in dropdown.

I kept those hooks inside `record-board-column` where these buttons are.
Let me know if it should be placed somewhere else.

Also Added navigation state preservation when clicked on `edit from
settings`

Thanks :)

---------

Co-authored-by: Lucas Bordeau <[email protected]>
  • Loading branch information
ehconitin and lucasbordeau authored Aug 28, 2024
1 parent ff1adb0 commit e2eaffc
Show file tree
Hide file tree
Showing 9 changed files with 212 additions and 103 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useContext, useRef } from 'react';
import styled from '@emotion/styled';
import { useCallback, useContext, useRef } from 'react';

import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import styled from '@emotion/styled';
import { useContext, useState } from 'react';
import { IconDotsVertical, Tag } from 'twenty-ui';
import { IconDotsVertical, IconPlus, Tag } from 'twenty-ui';

import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
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 { 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';
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';

Expand Down Expand Up @@ -38,11 +43,25 @@ const StyledHeaderActions = styled.div`
display: flex;
margin-left: auto;
`;
const StyledHeaderContainer = styled.div`
display: flex;
justify-content: space-between;
width: 100%;
`;
const StyledLeftContainer = styled.div`
align-items: center;
display: flex;
`;

const StyledRightContainer = styled.div`
align-items: center;
display: flex;
`;

export const RecordBoardColumnHeader = () => {
const [isBoardColumnMenuOpen, setIsBoardColumnMenuOpen] = useState(false);
const [isHeaderHovered, setIsHeaderHovered] = useState(false);

const { objectMetadataItem } = useContext(RecordBoardContext);
const { columnDefinition, recordCount } = useContext(
RecordBoardColumnContext,
);
Expand All @@ -69,49 +88,92 @@ export const RecordBoardColumnHeader = () => {

const boardColumnTotal = 0;

const {
isCreatingCard,
handleAddNewOpportunityClick,
handleCancel,
handleEntitySelect,
} = useAddNewOpportunity('first');
const { handleAddNewCardClick } = useAddNewCard('first');

const isOpportunity =
objectMetadataItem.nameSingular === CoreObjectNameSingular.Opportunity;

const handleClick = isOpportunity
? handleAddNewOpportunityClick
: () => {
handleAddNewCardClick();
};

return (
<>
<StyledHeader
onMouseEnter={() => setIsHeaderHovered(true)}
onMouseLeave={() => setIsHeaderHovered(false)}
>
<Tag
onClick={handleBoardColumnMenuOpen}
variant={
columnDefinition.type === RecordBoardColumnDefinitionType.Value
? 'solid'
: 'outline'
}
color={
columnDefinition.type === RecordBoardColumnDefinitionType.Value
? columnDefinition.color
: 'transparent'
}
text={columnDefinition.title}
weight={
columnDefinition.type === RecordBoardColumnDefinitionType.Value
? 'regular'
: 'medium'
}
/>
{!!boardColumnTotal && <StyledAmount>${boardColumnTotal}</StyledAmount>}
<StyledNumChildren>{recordCount}</StyledNumChildren>
{isHeaderHovered && columnDefinition.actions.length > 0 && (
<StyledHeaderActions>
<LightIconButton
accent="tertiary"
Icon={IconDotsVertical}
<StyledHeaderContainer>
<StyledLeftContainer>
<Tag
onClick={handleBoardColumnMenuOpen}
variant={
columnDefinition.type === RecordBoardColumnDefinitionType.Value
? 'solid'
: 'outline'
}
color={
columnDefinition.type === RecordBoardColumnDefinitionType.Value
? columnDefinition.color
: 'transparent'
}
text={columnDefinition.title}
weight={
columnDefinition.type === RecordBoardColumnDefinitionType.Value
? 'regular'
: 'medium'
}
/>
</StyledHeaderActions>
)}
{!!boardColumnTotal && (
<StyledAmount>${boardColumnTotal}</StyledAmount>
)}
<StyledNumChildren>{recordCount}</StyledNumChildren>
</StyledLeftContainer>
<StyledRightContainer>
{isHeaderHovered && (
<StyledHeaderActions>
{columnDefinition.actions.length > 0 && (
<LightIconButton
accent="tertiary"
Icon={IconDotsVertical}
onClick={handleBoardColumnMenuOpen}
/>
)}

<LightIconButton
accent="tertiary"
Icon={IconPlus}
onClick={handleClick}
/>
</StyledHeaderActions>
)}
</StyledRightContainer>
</StyledHeaderContainer>
</StyledHeader>
{isBoardColumnMenuOpen && columnDefinition.actions.length > 0 && (
<RecordBoardColumnDropdownMenu
onClose={handleBoardColumnMenuClose}
stageId={columnDefinition.id}
/>
)}
{isCreatingCard && (
<SingleEntitySelect
disableBackgroundBlur
onCancel={handleCancel}
onEntitySelected={handleEntitySelect}
relationObjectNameSingular={CoreObjectNameSingular.Company}
relationPickerScopeId="relation-picker"
selectedRelationRecordIds={[]}
/>
)}
</>
);
};
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { useContext } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconPlus } from 'twenty-ui';

import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard';

const StyledButton = styled.button`
align-items: center;
Expand All @@ -25,19 +23,9 @@ const StyledButton = styled.button`

export const RecordBoardColumnNewButton = () => {
const theme = useTheme();
const { columnDefinition } = useContext(RecordBoardColumnContext);
const { createOneRecord, selectFieldMetadataItem } =
useContext(RecordBoardContext);

const onNewClick = () => {
createOneRecord({
[selectFieldMetadataItem.name]: columnDefinition.value,
position: 'last',
});
};

const { handleAddNewCardClick } = useAddNewCard('last');
return (
<StyledButton onClick={onNewClick}>
<StyledButton onClick={handleAddNewCardClick}>
<IconPlus size={theme.icon.size.md} />
New
</StyledButton>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import { useCallback, useContext, useState } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { IconPlus } from 'twenty-ui';

import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
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 { 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 { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';

const StyledButton = styled.button`
align-items: center;
Expand All @@ -30,52 +24,13 @@ const StyledButton = styled.button`
`;

export const RecordBoardColumnNewOpportunityButton = () => {
const [isCreatingCard, setIsCreatingCard] = useState(false);

const theme = useTheme();
const { columnDefinition } = useContext(RecordBoardColumnContext);
const { createOneRecord, selectFieldMetadataItem } =
useContext(RecordBoardContext);

const {
goBackToPreviousHotkeyScope,
setHotkeyScopeAndMemorizePreviousScope,
} = usePreviousHotkeyScope();

const { resetSearchFilter } = useEntitySelectSearch({
relationPickerScopeId: 'relation-picker',
});

const handleEntitySelect = (company?: EntityForSelect) => {
setIsCreatingCard(false);
goBackToPreviousHotkeyScope();
resetSearchFilter();

if (!company) {
return;
}

createOneRecord({
name: company.name,
companyId: company.id,
position: 'last',
[selectFieldMetadataItem.name]: columnDefinition.value,
});
};

const handleNewClick = useCallback(() => {
setIsCreatingCard(true);
setHotkeyScopeAndMemorizePreviousScope(
RelationPickerHotkeyScope.RelationPicker,
);
}, [setIsCreatingCard, setHotkeyScopeAndMemorizePreviousScope]);

const handleCancel = () => {
resetSearchFilter();
goBackToPreviousHotkeyScope();
setIsCreatingCard(false);
};

isCreatingCard,
handleAddNewOpportunityClick,
handleCancel,
handleEntitySelect,
} = useAddNewOpportunity('last');
return (
<>
{isCreatingCard ? (
Expand All @@ -88,7 +43,7 @@ export const RecordBoardColumnNewOpportunityButton = () => {
selectedRelationRecordIds={[]}
/>
) : (
<StyledButton onClick={handleNewClick}>
<StyledButton onClick={handleAddNewOpportunityClick}>
<IconPlus size={theme.icon.size.md} />
New
</StyledButton>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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';

export const useAddNewCard = (position: string) => {
const { columnDefinition } = useContext(RecordBoardColumnContext);
const { createOneRecord, selectFieldMetadataItem } =
useContext(RecordBoardContext);

const handleAddNewCardClick = () => {
createOneRecord({
[selectFieldMetadataItem.name]: columnDefinition.value,
position: position,
});
};

return {
handleAddNewCardClick,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
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 handleEntitySelect = useCallback(
(company?: EntityForSelect) => {
setIsCreatingCard(false);
goBackToPreviousHotkeyScope();
resetSearchFilter();

if (company !== undefined) {
createOneRecord({
name: company.name,
companyId: company.id,
position: position,
[selectFieldMetadataItem.name]: columnDefinition.value,
});
}
},
[
columnDefinition,
createOneRecord,
goBackToPreviousHotkeyScope,
resetSearchFilter,
selectFieldMetadataItem,
position,
],
);

const handleAddNewOpportunityClick = useCallback(() => {
setIsCreatingCard(true);
setHotkeyScopeAndMemorizePreviousScope(
RelationPickerHotkeyScope.RelationPicker,
);
}, [setHotkeyScopeAndMemorizePreviousScope]);

const handleCancel = useCallback(() => {
resetSearchFilter();
goBackToPreviousHotkeyScope();
setIsCreatingCard(false);
}, [goBackToPreviousHotkeyScope, resetSearchFilter]);

return {
isCreatingCard,
handleEntitySelect,
handleAddNewOpportunityClick,
handleCancel,
};
};
Loading

0 comments on commit e2eaffc

Please sign in to comment.