Skip to content

Commit

Permalink
Create opportunity from board column menu (twentyhq#1323)
Browse files Browse the repository at this point in the history
- create opportunity from column menu
  • Loading branch information
brendanlaschke authored Aug 28, 2023
1 parent 6e201ba commit 0d7b869
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 5 deletions.
3 changes: 3 additions & 0 deletions front/src/modules/ui/board/components/BoardColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export type BoardColumnProps = {
children: React.ReactNode;
isFirstColumn: boolean;
numChildren: number;
stageId: string;
};

export function BoardColumn({
Expand All @@ -69,6 +70,7 @@ export function BoardColumn({
children,
isFirstColumn,
numChildren,
stageId,
}: BoardColumnProps) {
const [isBoardColumnMenuOpen, setIsBoardColumnMenuOpen] =
React.useState(false);
Expand Down Expand Up @@ -103,6 +105,7 @@ export function BoardColumn({
onTitleEdit={onTitleEdit}
title={title}
color={color}
stageId={stageId}
/>
)}
{children}
Expand Down
84 changes: 79 additions & 5 deletions front/src/modules/ui/board/components/BoardColumnMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { useRef, useState } from 'react';
import styled from '@emotion/styled';
import { IconPencil } from '@tabler/icons-react';
import { Key } from 'ts-key-enum';

import { useCreateCompanyProgress } from '@/companies/hooks/useCreateCompanyProgress';
import { useFilteredSearchCompanyQuery } from '@/companies/hooks/useFilteredSearchCompanyQuery';
import { DropdownMenuSelectableItem } from '@/ui/dropdown/components/DropdownMenuSelectableItem';
import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu';
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
import { IconPencil, IconPlus } from '@/ui/icon';
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar';
import { icon } from '@/ui/theme/constants/icon';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';

import { BoardColumnHotkeyScope } from '../types/BoardColumnHotkeyScope';

Expand All @@ -25,25 +34,73 @@ type OwnProps = {
title: string;
color: string;
onTitleEdit: (title: string, color: string) => void;
stageId: string;
};

export function BoardColumnMenu({
onClose,
onTitleEdit,
title,
color,
stageId,
}: OwnProps) {
const [openMenu, setOpenMenu] = useState('actions');
const boardColumnMenuRef = useRef(null);
const { enqueueSnackBar } = useSnackBar();
const createCompanyProgress = useCreateCompanyProgress();

function handleCompanySelected(
selectedCompany: EntityForSelect | null | undefined,
) {
if (!selectedCompany?.id) {
enqueueSnackBar(
'There was a problem with the company selection, please retry.',
{
variant: 'error',
},
);

console.error(
'There was a problem with the company selection, please retry.',
);
return;
}

createCompanyProgress(selectedCompany.id, stageId);
closeMenu();
}

function closeMenu() {
goBackToPreviousHotkeyScope();
onClose();
}

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

function setMenu(menu: string) {
if (menu === 'add') {
setHotkeyScopeAndMemorizePreviousScope(
RelationPickerHotkeyScope.RelationPicker,
);
}
setOpenMenu(menu);
}
const [searchFilter] = useRecoilScopedState(
relationPickerSearchFilterScopedState,
);
const companies = useFilteredSearchCompanyQuery({ searchFilter });

useListenClickOutside({
refs: [boardColumnMenuRef],
callback: onClose,
callback: closeMenu,
});

useScopedHotkeys(
[Key.Escape, Key.Enter],
onClose,
closeMenu,
BoardColumnHotkeyScope.BoardColumn,
[],
);
Expand All @@ -53,20 +110,37 @@ export function BoardColumnMenu({
<StyledDropdownMenu>
{openMenu === 'actions' && (
<StyledDropdownMenuItemsContainer>
<DropdownMenuSelectableItem onClick={() => setOpenMenu('title')}>
<DropdownMenuSelectableItem onClick={() => setMenu('title')}>
<IconPencil size={icon.size.md} stroke={icon.stroke.sm} />
Rename
</DropdownMenuSelectableItem>
<DropdownMenuSelectableItem onClick={() => setMenu('add')}>
<IconPlus size={icon.size.md} stroke={icon.stroke.sm} />
New opportunity
</DropdownMenuSelectableItem>
</StyledDropdownMenuItemsContainer>
)}
{openMenu === 'title' && (
<BoardColumnEditTitleMenu
color={color}
onClose={onClose}
onClose={closeMenu}
onTitleEdit={onTitleEdit}
title={title}
/>
)}

{openMenu === 'add' && (
<SingleEntitySelect
onEntitySelected={(value) => handleCompanySelected(value)}
onCancel={closeMenu}
entities={{
entitiesToSelect: companies.entitiesToSelect,
selectedEntity: companies.selectedEntities[0],
loading: companies.loading,
}}
disableBackgroundBlur={true}
/>
)}
</StyledDropdownMenu>
</StyledMenuContainer>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export function EntityBoardColumn({
totalAmount={boardColumnTotal}
isFirstColumn={column.index === 0}
numChildren={cardIds.length}
stageId={column.id}
>
<BoardColumnCardsContainer droppableProvided={droppableProvided}>
{cardIds.map((cardId, index) => (
Expand Down

0 comments on commit 0d7b869

Please sign in to comment.