Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added "Add record" button in kanban view column headers dropdown #6649

Merged
merged 8 commits into from
Aug 28, 2024
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]);
Comment on lines +49 to +54
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider adding a debounce to prevent rapid consecutive clicks


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

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