diff --git a/front/src/modules/companies/components/HooksCompanyBoard.tsx b/front/src/modules/companies/components/HooksCompanyBoard.tsx index 9bdd03f07..7493169a5 100644 --- a/front/src/modules/companies/components/HooksCompanyBoard.tsx +++ b/front/src/modules/companies/components/HooksCompanyBoard.tsx @@ -27,8 +27,12 @@ import { CompanyBoardRecoilScopeContext } from '../states/recoil-scope-contexts/ export function HooksCompanyBoard({ orderBy, + setActionBar, + setContextMenu, }: { orderBy: PipelineProgresses_Order_By[]; + setActionBar?: () => void; + setContextMenu?: () => void; }) { const setFieldsDefinitionsState = useSetRecoilState( viewFieldsDefinitionsState, @@ -113,6 +117,13 @@ export function HooksCompanyBoard({ const loading = loadingGetPipelines || loadingGetPipelineProgress || loadingGetCompanies; + if (setActionBar) { + setActionBar(); + } + if (setContextMenu) { + setContextMenu(); + } + useEffect(() => { if (!loading && pipeline && pipelineProgresses && companiesData) { updateCompanyBoard(pipeline, pipelineProgresses, companiesData.companies); diff --git a/front/src/modules/companies/hooks/useActionBarEntries.tsx b/front/src/modules/companies/hooks/useActionBarEntries.tsx new file mode 100644 index 000000000..184689c8f --- /dev/null +++ b/front/src/modules/companies/hooks/useActionBarEntries.tsx @@ -0,0 +1,46 @@ +import { useSetRecoilState } from 'recoil'; + +import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds'; +import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity'; +import { ActionBarEntry } from '@/ui/action-bar/components/ActionBarEntry'; +import { actionBarEntriesState } from '@/ui/action-bar/states/ActionBarEntriesState'; +import { IconCheckbox, IconNotes, IconTrash } from '@/ui/icon'; +import { ActivityType } from '~/generated/graphql'; + +import { useDeleteSelectedComapnies } from './useDeleteCompanies'; + +export function useActionBarEntries() { + const setActionBarEntries = useSetRecoilState(actionBarEntriesState); + + const openCreateActivityRightDrawer = + useOpenCreateActivityDrawerForSelectedRowIds(); + + async function handleActivityClick(type: ActivityType) { + openCreateActivityRightDrawer(type, ActivityTargetableEntityType.Company); + } + + const deleteSelectedCompanies = useDeleteSelectedComapnies(); + return () => { + setActionBarEntries([ + } + onClick={() => handleActivityClick(ActivityType.Note)} + key="note" + />, + } + onClick={() => handleActivityClick(ActivityType.Task)} + key="task" + />, + } + type="danger" + onClick={() => deleteSelectedCompanies()} + key="delete" + />, + ]); + }; +} diff --git a/front/src/modules/companies/hooks/useContextMenuEntries.tsx b/front/src/modules/companies/hooks/useContextMenuEntries.tsx new file mode 100644 index 000000000..3f5689989 --- /dev/null +++ b/front/src/modules/companies/hooks/useContextMenuEntries.tsx @@ -0,0 +1,47 @@ +import { IconCheckbox, IconNotes, IconTrash } from '@tabler/icons-react'; +import { useSetRecoilState } from 'recoil'; + +import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds'; +import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity'; +import { ContextMenuEntry } from '@/ui/context-menu/components/ContextMenuEntry'; +import { contextMenuEntriesState } from '@/ui/context-menu/states/ContextMenuEntriesState'; +import { ActivityType } from '~/generated/graphql'; + +import { useDeleteSelectedComapnies } from './useDeleteCompanies'; + +export function useContextMenuEntries() { + const setContextMenuEntries = useSetRecoilState(contextMenuEntriesState); + + const openCreateActivityRightDrawer = + useOpenCreateActivityDrawerForSelectedRowIds(); + + async function handleButtonClick(type: ActivityType) { + openCreateActivityRightDrawer(type, ActivityTargetableEntityType.Company); + } + + const deleteSelectedCompanies = useDeleteSelectedComapnies(); + + return () => { + setContextMenuEntries([ + } + onClick={() => handleButtonClick(ActivityType.Note)} + key="note" + />, + } + onClick={() => handleButtonClick(ActivityType.Task)} + key="task" + />, + } + type="danger" + onClick={() => deleteSelectedCompanies()} + key="delete" + />, + ]); + }; +} diff --git a/front/src/modules/companies/table/components/TableActionBarButtonDeleteCompanies.tsx b/front/src/modules/companies/hooks/useDeleteCompanies.ts similarity index 75% rename from front/src/modules/companies/table/components/TableActionBarButtonDeleteCompanies.tsx rename to front/src/modules/companies/hooks/useDeleteCompanies.ts index 14c7315aa..c5625168e 100644 --- a/front/src/modules/companies/table/components/TableActionBarButtonDeleteCompanies.tsx +++ b/front/src/modules/companies/hooks/useDeleteCompanies.ts @@ -2,14 +2,12 @@ import { getOperationName } from '@apollo/client/utilities'; import { useRecoilState, useRecoilValue } from 'recoil'; import { GET_PIPELINES } from '@/pipeline/queries'; -import { IconTrash } from '@/ui/icon/index'; -import { EntityTableActionBarButton } from '@/ui/table/action-bar/components/EntityTableActionBarButton'; import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection'; import { selectedRowIdsSelector } from '@/ui/table/states/selectors/selectedRowIdsSelector'; import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState'; import { useDeleteManyCompaniesMutation } from '~/generated/graphql'; -export function TableActionBarButtonDeleteCompanies() { +export function useDeleteSelectedComapnies() { const selectedRowIds = useRecoilValue(selectedRowIdsSelector); const resetRowSelection = useResetTableRowSelection(); @@ -20,7 +18,7 @@ export function TableActionBarButtonDeleteCompanies() { const [tableRowIds, setTableRowIds] = useRecoilState(tableRowIdsState); - async function handleDeleteClick() { + async function deleteSelectedCompanies() { const rowIdsToDelete = selectedRowIds; resetRowSelection(); @@ -43,12 +41,5 @@ export function TableActionBarButtonDeleteCompanies() { }); } - return ( - } - type="warning" - onClick={handleDeleteClick} - /> - ); + return deleteSelectedCompanies; } diff --git a/front/src/modules/companies/table/components/CompanyTable.tsx b/front/src/modules/companies/table/components/CompanyTable.tsx index d69246ad9..943daf703 100644 --- a/front/src/modules/companies/table/components/CompanyTable.tsx +++ b/front/src/modules/companies/table/components/CompanyTable.tsx @@ -2,6 +2,8 @@ import { useMemo } from 'react'; import { useRecoilValue } from 'recoil'; import { companyViewFields } from '@/companies/constants/companyViewFields'; +import { useActionBarEntries } from '@/companies/hooks/useActionBarEntries'; +import { useContextMenuEntries } from '@/companies/hooks/useContextMenuEntries'; import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState'; import { sortsOrderByScopedState } from '@/ui/filter-n-sort/states/sortScopedState'; import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause'; @@ -51,6 +53,9 @@ export function CompanyTable() { return { AND: filters.map(turnFilterIntoWhereClause) }; }, [filters]) as any; + const setContextMenu = useContextMenuEntries(); + const setActionBar = useActionBarEntries(); + return ( <> - handleButtonClick(ActivityType.Note)} - /> - handleButtonClick(ActivityType.Task)} - /> - - ); -} diff --git a/front/src/modules/people/hooks/useActionBarEntries.tsx b/front/src/modules/people/hooks/useActionBarEntries.tsx new file mode 100644 index 000000000..d3bf44c8f --- /dev/null +++ b/front/src/modules/people/hooks/useActionBarEntries.tsx @@ -0,0 +1,81 @@ +import { getOperationName } from '@apollo/client/utilities'; +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; + +import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds'; +import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity'; +import { ActionBarEntry } from '@/ui/action-bar/components/ActionBarEntry'; +import { actionBarEntriesState } from '@/ui/action-bar/states/ActionBarEntriesState'; +import { IconCheckbox, IconNotes, IconTrash } from '@/ui/icon'; +import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection'; +import { selectedRowIdsSelector } from '@/ui/table/states/selectors/selectedRowIdsSelector'; +import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState'; +import { ActivityType, useDeleteManyPersonMutation } from '~/generated/graphql'; + +import { GET_PEOPLE } from '../queries'; + +export function useActionBarEntries() { + const setActionBarEntries = useSetRecoilState(actionBarEntriesState); + + const openCreateActivityRightDrawer = + useOpenCreateActivityDrawerForSelectedRowIds(); + + async function handleActivityClick(type: ActivityType) { + openCreateActivityRightDrawer(type, ActivityTargetableEntityType.Person); + } + + const selectedRowIds = useRecoilValue(selectedRowIdsSelector); + const [tableRowIds, setTableRowIds] = useRecoilState(tableRowIdsState); + + const resetRowSelection = useResetTableRowSelection(); + + const [deleteManyPerson] = useDeleteManyPersonMutation({ + refetchQueries: [getOperationName(GET_PEOPLE) ?? ''], + }); + + async function handleDeleteClick() { + const rowIdsToDelete = selectedRowIds; + + resetRowSelection(); + + await deleteManyPerson({ + variables: { + ids: rowIdsToDelete, + }, + optimisticResponse: { + __typename: 'Mutation', + deleteManyPerson: { + count: rowIdsToDelete.length, + }, + }, + update: () => { + setTableRowIds( + tableRowIds.filter((id) => !rowIdsToDelete.includes(id)), + ); + }, + }); + } + + return () => { + setActionBarEntries([ + } + onClick={() => handleActivityClick(ActivityType.Note)} + key="note" + />, + } + onClick={() => handleActivityClick(ActivityType.Task)} + key="task" + />, + } + type="danger" + onClick={handleDeleteClick} + key="delte" + />, + ]); + }; +} diff --git a/front/src/modules/people/hooks/useContextMenuEntries.tsx b/front/src/modules/people/hooks/useContextMenuEntries.tsx new file mode 100644 index 000000000..9e26db4cf --- /dev/null +++ b/front/src/modules/people/hooks/useContextMenuEntries.tsx @@ -0,0 +1,81 @@ +import { getOperationName } from '@apollo/client/utilities'; +import { IconCheckbox, IconNotes, IconTrash } from '@tabler/icons-react'; +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; + +import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds'; +import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity'; +import { ContextMenuEntry } from '@/ui/context-menu/components/ContextMenuEntry'; +import { contextMenuEntriesState } from '@/ui/context-menu/states/ContextMenuEntriesState'; +import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection'; +import { selectedRowIdsSelector } from '@/ui/table/states/selectors/selectedRowIdsSelector'; +import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState'; +import { ActivityType, useDeleteManyPersonMutation } from '~/generated/graphql'; + +import { GET_PEOPLE } from '../queries'; + +export function useContextMenuEntries() { + const setContextMenuEntries = useSetRecoilState(contextMenuEntriesState); + + const openCreateActivityRightDrawer = + useOpenCreateActivityDrawerForSelectedRowIds(); + + async function handleActivityClick(type: ActivityType) { + openCreateActivityRightDrawer(type, ActivityTargetableEntityType.Person); + } + + const selectedRowIds = useRecoilValue(selectedRowIdsSelector); + const [tableRowIds, setTableRowIds] = useRecoilState(tableRowIdsState); + + const resetRowSelection = useResetTableRowSelection(); + + const [deleteManyPerson] = useDeleteManyPersonMutation({ + refetchQueries: [getOperationName(GET_PEOPLE) ?? ''], + }); + + async function handleDeleteClick() { + const rowIdsToDelete = selectedRowIds; + + resetRowSelection(); + + await deleteManyPerson({ + variables: { + ids: rowIdsToDelete, + }, + optimisticResponse: { + __typename: 'Mutation', + deleteManyPerson: { + count: rowIdsToDelete.length, + }, + }, + update: () => { + setTableRowIds( + tableRowIds.filter((id) => !rowIdsToDelete.includes(id)), + ); + }, + }); + } + + return () => { + setContextMenuEntries([ + } + onClick={() => handleActivityClick(ActivityType.Note)} + key="note" + />, + } + onClick={() => handleActivityClick(ActivityType.Task)} + key="task" + />, + } + type="danger" + onClick={handleDeleteClick} + key="delete" + />, + ]); + }; +} diff --git a/front/src/modules/people/table/components/PeopleTable.tsx b/front/src/modules/people/table/components/PeopleTable.tsx index 3cc935d1d..db3bf4bf9 100644 --- a/front/src/modules/people/table/components/PeopleTable.tsx +++ b/front/src/modules/people/table/components/PeopleTable.tsx @@ -2,6 +2,8 @@ import { useMemo } from 'react'; import { useRecoilValue } from 'recoil'; import { peopleViewFields } from '@/people/constants/peopleViewFields'; +import { useActionBarEntries } from '@/people/hooks/useActionBarEntries'; +import { useContextMenuEntries } from '@/people/hooks/useContextMenuEntries'; import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState'; import { sortsOrderByScopedState } from '@/ui/filter-n-sort/states/sortScopedState'; import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause'; @@ -51,6 +53,9 @@ export function PeopleTable() { return { AND: filters.map(turnFilterIntoWhereClause) }; }, [filters]) as any; + const setContextMenu = useContextMenuEntries(); + const setActionBar = useActionBarEntries(); + return ( <> - handleButtonClick(ActivityType.Note)} - /> - handleButtonClick(ActivityType.Task)} - /> - - ); -} diff --git a/front/src/modules/people/table/components/TableActionBarButtonDeletePeople.tsx b/front/src/modules/people/table/components/TableActionBarButtonDeletePeople.tsx deleted file mode 100644 index d7b10a923..000000000 --- a/front/src/modules/people/table/components/TableActionBarButtonDeletePeople.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { getOperationName } from '@apollo/client/utilities'; -import { useRecoilState, useRecoilValue } from 'recoil'; - -import { GET_PEOPLE } from '@/people/queries'; -import { IconTrash } from '@/ui/icon/index'; -import { EntityTableActionBarButton } from '@/ui/table/action-bar/components/EntityTableActionBarButton'; -import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection'; -import { selectedRowIdsSelector } from '@/ui/table/states/selectors/selectedRowIdsSelector'; -import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState'; -import { useDeleteManyPersonMutation } from '~/generated/graphql'; - -export function TableActionBarButtonDeletePeople() { - const selectedRowIds = useRecoilValue(selectedRowIdsSelector); - const [tableRowIds, setTableRowIds] = useRecoilState(tableRowIdsState); - - const resetRowSelection = useResetTableRowSelection(); - - const [deleteManyPerson] = useDeleteManyPersonMutation({ - refetchQueries: [getOperationName(GET_PEOPLE) ?? ''], - }); - - async function handleDeleteClick() { - const rowIdsToDelete = selectedRowIds; - - resetRowSelection(); - - await deleteManyPerson({ - variables: { - ids: rowIdsToDelete, - }, - optimisticResponse: { - __typename: 'Mutation', - deleteManyPerson: { - count: rowIdsToDelete.length, - }, - }, - update: () => { - setTableRowIds( - tableRowIds.filter((id) => !rowIdsToDelete.includes(id)), - ); - }, - }); - } - - return ( - } - type="warning" - onClick={handleDeleteClick} - /> - ); -} diff --git a/front/src/modules/ui/action-bar/components/ActionBar.tsx b/front/src/modules/ui/action-bar/components/ActionBar.tsx index ff36ab093..ece4c25f9 100644 --- a/front/src/modules/ui/action-bar/components/ActionBar.tsx +++ b/front/src/modules/ui/action-bar/components/ActionBar.tsx @@ -1,66 +1,48 @@ -import React, { useEffect } from 'react'; +import React, { useRef } from 'react'; import styled from '@emotion/styled'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; +import { useRecoilValue } from 'recoil'; -import { contextMenuPositionState } from '@/ui/table/states/contextMenuPositionState'; +import { actionBarEntriesState } from '@/ui/action-bar/states/ActionBarEntriesState'; +import { contextMenuOpenState } from '@/ui/context-menu/states/ContextMenuIsOpenState'; -import { PositionType } from '../types/PositionType'; +import { actionBarOpenState } from '../states/ActionBarIsOpenState'; type OwnProps = { - children: React.ReactNode | React.ReactNode[]; selectedIds: string[]; }; -type StyledContainerProps = { - position: PositionType; -}; - -const StyledContainer = styled.div` +const StyledContainerActionBar = styled.div` align-items: center; background: ${({ theme }) => theme.background.secondary}; border: 1px solid ${({ theme }) => theme.border.color.medium}; border-radius: ${({ theme }) => theme.border.radius.md}; - bottom: ${(props) => (props.position.x ? 'auto' : '38px')}; + bottom: 38px; box-shadow: ${({ theme }) => theme.boxShadow.strong}; display: flex; height: 48px; - left: ${(props) => (props.position.x ? `${props.position.x}px` : '50%')}; + left: 50%; padding-left: ${({ theme }) => theme.spacing(2)}; padding-right: ${({ theme }) => theme.spacing(2)}; - position: ${(props) => (props.position.x ? 'fixed' : 'absolute')}; - top: ${(props) => (props.position.y ? `${props.position.y}px` : 'auto')}; + position: absolute; + top: auto; transform: translateX(-50%); z-index: 1; `; -export function ActionBar({ children, selectedIds }: OwnProps) { - const position = useRecoilValue(contextMenuPositionState); - const setContextMenuPosition = useSetRecoilState(contextMenuPositionState); +export function ActionBar({ selectedIds }: OwnProps) { + const actionBarOpen = useRecoilValue(actionBarOpenState); + const contextMenuOpen = useRecoilValue(contextMenuOpenState); + const actionBarEntries = useRecoilValue(actionBarEntriesState); + const wrapperRef = useRef(null); - useEffect(() => { - function handleClickOutside(event: MouseEvent) { - if (!(event.target as HTMLElement).closest('.action-bar')) { - setContextMenuPosition({ x: null, y: null }); - } - } - - document.addEventListener('mousedown', handleClickOutside); - - // Cleanup the event listener when the component unmounts - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, [setContextMenuPosition]); - - if (selectedIds.length === 0) { + if (selectedIds.length === 0 || !actionBarOpen || contextMenuOpen) { return null; } - return ( - - {children} - + + {actionBarEntries} + ); } diff --git a/front/src/modules/ui/table/action-bar/components/EntityTableActionBarButton.tsx b/front/src/modules/ui/action-bar/components/ActionBarEntry.tsx similarity index 79% rename from front/src/modules/ui/table/action-bar/components/EntityTableActionBarButton.tsx rename to front/src/modules/ui/action-bar/components/ActionBarEntry.tsx index 911995198..f9650f7b5 100644 --- a/front/src/modules/ui/table/action-bar/components/EntityTableActionBarButton.tsx +++ b/front/src/modules/ui/action-bar/components/ActionBarEntry.tsx @@ -4,18 +4,18 @@ import styled from '@emotion/styled'; type OwnProps = { icon: ReactNode; label: string; - type?: 'standard' | 'warning'; + type?: 'standard' | 'danger'; onClick: () => void; }; type StyledButtonProps = { - type: 'standard' | 'warning'; + type: 'standard' | 'danger'; }; const StyledButton = styled.div` border-radius: ${({ theme }) => theme.border.radius.sm}; color: ${(props) => - props.type === 'warning' + props.type === 'danger' ? props.theme.color.red : props.theme.font.color.secondary}; cursor: pointer; @@ -27,7 +27,8 @@ const StyledButton = styled.div` user-select: none; &:hover { - background: ${({ theme }) => theme.background.tertiary}; + background: ${({ theme, type }) => + type === 'danger' ? theme.tag.background.red : theme.background.tertiary}; } `; @@ -36,7 +37,7 @@ const StyledButtonLabel = styled.div` margin-left: ${({ theme }) => theme.spacing(2)}; `; -export function EntityTableActionBarButton({ +export function ActionBarEntry({ label, icon, type = 'standard', diff --git a/front/src/modules/ui/action-bar/components/__stories__/ActionBar.stories.tsx b/front/src/modules/ui/action-bar/components/__stories__/ActionBar.stories.tsx index fdb64a245..8ce5f2b1f 100644 --- a/front/src/modules/ui/action-bar/components/__stories__/ActionBar.stories.tsx +++ b/front/src/modules/ui/action-bar/components/__stories__/ActionBar.stories.tsx @@ -1,14 +1,39 @@ +import { MemoryRouter } from 'react-router-dom'; import type { Meta, StoryObj } from '@storybook/react'; +import { useSetRecoilState } from 'recoil'; +import { useActionBarEntries } from '@/companies/hooks/useActionBarEntries'; +import { CompanyTableMockMode } from '@/companies/table/components/CompanyTableMockMode'; +import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext'; +import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; +import { actionBarOpenState } from '../../states/ActionBarIsOpenState'; import { ActionBar } from '../ActionBar'; +function FilledActionBar(props: { selectedIds: string[] }) { + const setActionBar = useActionBarEntries(); + setActionBar(); + const setActionBarOpenState = useSetRecoilState(actionBarOpenState); + setActionBarOpenState(true); + return ; +} + const meta: Meta = { title: 'UI/ActionBar/ActionBar', - component: ActionBar, - decorators: [ComponentDecorator], - args: { children: 'Lorem ipsum', selectedIds: [] }, + component: FilledActionBar, + decorators: [ + (Story) => ( + + + + + + + ), + ComponentDecorator, + ], + args: { selectedIds: ['TestId'] }, }; export default meta; diff --git a/front/src/modules/ui/action-bar/states/ActionBarEntriesState.ts b/front/src/modules/ui/action-bar/states/ActionBarEntriesState.ts new file mode 100644 index 000000000..81b436523 --- /dev/null +++ b/front/src/modules/ui/action-bar/states/ActionBarEntriesState.ts @@ -0,0 +1,7 @@ +import { ReactElement } from 'react'; +import { atom } from 'recoil'; + +export const actionBarEntriesState = atom({ + key: 'actionBarEntriesState', + default: [], +}); diff --git a/front/src/modules/ui/action-bar/states/ActionBarIsOpenState.ts b/front/src/modules/ui/action-bar/states/ActionBarIsOpenState.ts new file mode 100644 index 000000000..3d150e978 --- /dev/null +++ b/front/src/modules/ui/action-bar/states/ActionBarIsOpenState.ts @@ -0,0 +1,6 @@ +import { atom } from 'recoil'; + +export const actionBarOpenState = atom({ + key: 'actionBarOpenState', + default: false, +}); diff --git a/front/src/modules/ui/board/components/BoardActionBarButtonDeleteBoardCard.tsx b/front/src/modules/ui/board/components/BoardActionBarButtonDeleteBoardCard.tsx index 430fbeb80..1f1f6713c 100644 --- a/front/src/modules/ui/board/components/BoardActionBarButtonDeleteBoardCard.tsx +++ b/front/src/modules/ui/board/components/BoardActionBarButtonDeleteBoardCard.tsx @@ -2,8 +2,8 @@ import { getOperationName } from '@apollo/client/utilities'; import { useRecoilValue } from 'recoil'; import { GET_PIPELINES } from '@/pipeline/queries'; +import { ActionBarEntry } from '@/ui/action-bar/components/ActionBarEntry'; import { IconTrash } from '@/ui/icon/index'; -import { EntityTableActionBarButton } from '@/ui/table/action-bar/components/EntityTableActionBarButton'; import { useDeleteManyPipelineProgressMutation } from '~/generated/graphql'; import { useRemoveCardIds } from '../hooks/useRemoveCardIds'; @@ -38,11 +38,12 @@ export function BoardActionBarButtonDeleteBoardCard() { } return ( - } - type="warning" - onClick={() => handleDelete()} + type="danger" + onClick={handleDelete} + key="delete" /> ); } diff --git a/front/src/modules/ui/board/components/EntityBoardActionBar.tsx b/front/src/modules/ui/board/components/EntityBoardActionBar.tsx index 9bbe3416b..149f48fdd 100644 --- a/front/src/modules/ui/board/components/EntityBoardActionBar.tsx +++ b/front/src/modules/ui/board/components/EntityBoardActionBar.tsx @@ -5,11 +5,7 @@ import { ActionBar } from '@/ui/action-bar/components/ActionBar'; import { selectedCardIdsSelector } from '../states/selectors/selectedCardIdsSelector'; -type OwnProps = { - children: React.ReactNode | React.ReactNode[]; -}; - -export function EntityBoardActionBar({ children }: OwnProps) { - const selectedCardIds = useRecoilValue(selectedCardIdsSelector); - return {children}; +export function EntityBoardActionBar() { + const selectedBoardCards = useRecoilValue(selectedCardIdsSelector); + return ; } diff --git a/front/src/modules/ui/board/hooks/useActionBarEntries.tsx b/front/src/modules/ui/board/hooks/useActionBarEntries.tsx new file mode 100644 index 000000000..a1b23f58c --- /dev/null +++ b/front/src/modules/ui/board/hooks/useActionBarEntries.tsx @@ -0,0 +1,13 @@ +import { useSetRecoilState } from 'recoil'; + +import { actionBarEntriesState } from '@/ui/action-bar/states/ActionBarEntriesState'; + +import { BoardActionBarButtonDeleteBoardCard } from '../components/BoardActionBarButtonDeleteBoardCard'; + +export function useActionBarEntries() { + const setActionBarEntries = useSetRecoilState(actionBarEntriesState); + + return () => { + setActionBarEntries([]); + }; +} diff --git a/front/src/modules/ui/board/hooks/useCurrentCardSelected.ts b/front/src/modules/ui/board/hooks/useCurrentCardSelected.ts index 9012081f3..f2785631a 100644 --- a/front/src/modules/ui/board/hooks/useCurrentCardSelected.ts +++ b/front/src/modules/ui/board/hooks/useCurrentCardSelected.ts @@ -1,5 +1,7 @@ import { useContext } from 'react'; -import { useRecoilCallback, useRecoilState } from 'recoil'; +import { useRecoilCallback, useRecoilState, useSetRecoilState } from 'recoil'; + +import { actionBarOpenState } from '@/ui/action-bar/states/ActionBarIsOpenState'; import { BoardCardIdContext } from '../contexts/BoardCardIdContext'; import { isCardSelectedFamilyState } from '../states/isCardSelectedFamilyState'; @@ -10,6 +12,7 @@ export function useCurrentCardSelected() { const [isCardSelected] = useRecoilState( isCardSelectedFamilyState(currentCardId ?? ''), ); + const setActionBarOpenState = useSetRecoilState(actionBarOpenState); const setCurrentCardSelected = useRecoilCallback( ({ set }) => @@ -17,6 +20,7 @@ export function useCurrentCardSelected() { if (!currentCardId) return; set(isCardSelectedFamilyState(currentCardId), selected); + setActionBarOpenState(true); }, [], ); diff --git a/front/src/modules/ui/board/hooks/useSetCardSelected.ts b/front/src/modules/ui/board/hooks/useSetCardSelected.ts index 2bb3763f2..a22fa1f87 100644 --- a/front/src/modules/ui/board/hooks/useSetCardSelected.ts +++ b/front/src/modules/ui/board/hooks/useSetCardSelected.ts @@ -1,9 +1,14 @@ -import { useRecoilCallback } from 'recoil'; +import { useRecoilCallback, useSetRecoilState } from 'recoil'; + +import { actionBarOpenState } from '@/ui/action-bar/states/ActionBarIsOpenState'; import { isCardSelectedFamilyState } from '../states/isCardSelectedFamilyState'; export function useSetCardSelected() { + const setActionBarOpenState = useSetRecoilState(actionBarOpenState); + return useRecoilCallback(({ set }) => (cardId: string, selected: boolean) => { set(isCardSelectedFamilyState(cardId), selected); + setActionBarOpenState(true); }); } diff --git a/front/src/modules/ui/context-menu/components/ContextMenu.tsx b/front/src/modules/ui/context-menu/components/ContextMenu.tsx new file mode 100644 index 000000000..12ab9c01c --- /dev/null +++ b/front/src/modules/ui/context-menu/components/ContextMenu.tsx @@ -0,0 +1,64 @@ +import React, { useRef } from 'react'; +import styled from '@emotion/styled'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; + +import { actionBarOpenState } from '@/ui/action-bar/states/ActionBarIsOpenState'; +import { contextMenuPositionState } from '@/ui/context-menu/states/ContextMenuPositionState'; +import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; + +import { contextMenuEntriesState } from '../states/ContextMenuEntriesState'; +import { contextMenuOpenState } from '../states/ContextMenuIsOpenState'; +import { PositionType } from '../types/PositionType'; + +type OwnProps = { + selectedIds: string[]; +}; + +type StyledContainerProps = { + position: PositionType; +}; + +const StyledContainerContextMenu = styled.div` + align-items: flex-start; + background: ${({ theme }) => theme.background.secondary}; + border: 1px solid ${({ theme }) => theme.border.color.light}; + border-radius: ${({ theme }) => theme.border.radius.md}; + box-shadow: ${({ theme }) => theme.boxShadow.strong}; + display: flex; + flex-direction: column; + gap: 1px; + + left: ${(props) => `${props.position.x}px`}; + position: fixed; + top: ${(props) => `${props.position.y}px`}; + + transform: translateX(-50%); + width: 160px; + z-index: 1; +`; + +export function ContextMenu({ selectedIds }: OwnProps) { + const position = useRecoilValue(contextMenuPositionState); + const contextMenuOpen = useRecoilValue(contextMenuOpenState); + const contextMenuEntries = useRecoilValue(contextMenuEntriesState); + const setContextMenuOpenState = useSetRecoilState(contextMenuOpenState); + const setActionBarOpenState = useSetRecoilState(actionBarOpenState); + const wrapperRef = useRef(null); + + useListenClickOutside({ + refs: [wrapperRef], + callback: () => { + setContextMenuOpenState(false); + setActionBarOpenState(true); + }, + }); + + if (selectedIds.length === 0 || !contextMenuOpen) { + return null; + } + return ( + + {contextMenuEntries} + + ); +} diff --git a/front/src/modules/ui/context-menu/components/ContextMenuEntry.tsx b/front/src/modules/ui/context-menu/components/ContextMenuEntry.tsx new file mode 100644 index 000000000..7979b097f --- /dev/null +++ b/front/src/modules/ui/context-menu/components/ContextMenuEntry.tsx @@ -0,0 +1,56 @@ +import { ReactNode } from 'react'; +import styled from '@emotion/styled'; + +type OwnProps = { + icon: ReactNode; + label: string; + type?: 'standard' | 'danger'; + onClick: () => void; +}; + +type StyledButtonProps = { + type: 'standard' | 'danger'; +}; + +const StyledButton = styled.div` + align-items: center; + align-self: stretch; + border-radius: ${({ theme }) => theme.border.radius.sm}; + color: ${(props) => + props.type === 'danger' + ? props.theme.color.red + : props.theme.font.color.secondary}; + cursor: pointer; + display: flex; + gap: ${({ theme }) => theme.spacing(2)}; + + height: 32px; + padding-left: ${({ theme }) => theme.spacing(1)}; + padding-right: ${({ theme }) => theme.spacing(1)}; + transition: background 0.1s ease; + user-select: none; + + &:hover { + background: ${({ theme, type }) => + type === 'danger' ? theme.tag.background.red : theme.background.tertiary}; + } +`; + +const StyledButtonLabel = styled.div` + font-weight: ${({ theme }) => theme.font.weight.medium}; + margin-left: ${({ theme }) => theme.spacing(2)}; +`; + +export function ContextMenuEntry({ + label, + icon, + type = 'standard', + onClick, +}: OwnProps) { + return ( + + {icon} + {label} + + ); +} diff --git a/front/src/modules/ui/context-menu/components/__stories__/ContextMenu.stories.tsx b/front/src/modules/ui/context-menu/components/__stories__/ContextMenu.stories.tsx new file mode 100644 index 000000000..8146b9150 --- /dev/null +++ b/front/src/modules/ui/context-menu/components/__stories__/ContextMenu.stories.tsx @@ -0,0 +1,48 @@ +import { MemoryRouter } from 'react-router-dom'; +import type { Meta, StoryObj } from '@storybook/react'; +import { useSetRecoilState } from 'recoil'; + +import { useContextMenuEntries } from '@/companies/hooks/useContextMenuEntries'; +import { CompanyTableMockMode } from '@/companies/table/components/CompanyTableMockMode'; +import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext'; +import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; +import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; + +import { contextMenuOpenState } from '../../states/ContextMenuIsOpenState'; +import { contextMenuPositionState } from '../../states/ContextMenuPositionState'; +import { ContextMenu } from '../ContextMenu'; + +function FilledContextMenu(props: { selectedIds: string[] }) { + const setContextMenu = useContextMenuEntries(); + setContextMenu(); + const setContextMenuPosition = useSetRecoilState(contextMenuPositionState); + setContextMenuPosition({ + x: 100, + y: 10, + }); + const setContextMenuOpenState = useSetRecoilState(contextMenuOpenState); + setContextMenuOpenState(true); + return ; +} + +const meta: Meta = { + title: 'UI/ContextMenu/ContextMenu', + component: FilledContextMenu, + decorators: [ + (Story) => ( + + + + + + + ), + ComponentDecorator, + ], + args: { selectedIds: ['TestId'] }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/front/src/modules/ui/context-menu/states/ContextMenuEntriesState.ts b/front/src/modules/ui/context-menu/states/ContextMenuEntriesState.ts new file mode 100644 index 000000000..e79628b6a --- /dev/null +++ b/front/src/modules/ui/context-menu/states/ContextMenuEntriesState.ts @@ -0,0 +1,7 @@ +import { ReactElement } from 'react'; +import { atom } from 'recoil'; + +export const contextMenuEntriesState = atom({ + key: 'contextMenuEntriesState', + default: [], +}); diff --git a/front/src/modules/ui/context-menu/states/ContextMenuIsOpenState.ts b/front/src/modules/ui/context-menu/states/ContextMenuIsOpenState.ts new file mode 100644 index 000000000..139bd3af4 --- /dev/null +++ b/front/src/modules/ui/context-menu/states/ContextMenuIsOpenState.ts @@ -0,0 +1,6 @@ +import { atom } from 'recoil'; + +export const contextMenuOpenState = atom({ + key: 'contextMenuOpenState', + default: false, +}); diff --git a/front/src/modules/ui/table/states/contextMenuPositionState.ts b/front/src/modules/ui/context-menu/states/ContextMenuPositionState.ts similarity index 71% rename from front/src/modules/ui/table/states/contextMenuPositionState.ts rename to front/src/modules/ui/context-menu/states/ContextMenuPositionState.ts index 1832437b5..7cd20a1c6 100644 --- a/front/src/modules/ui/table/states/contextMenuPositionState.ts +++ b/front/src/modules/ui/context-menu/states/ContextMenuPositionState.ts @@ -1,6 +1,6 @@ import { atom } from 'recoil'; -import { PositionType } from '@/ui/action-bar/types/PositionType'; +import { PositionType } from '@/ui/context-menu/types/PositionType'; export const contextMenuPositionState = atom({ key: 'contextMenuPositionState', diff --git a/front/src/modules/ui/action-bar/types/PositionType.ts b/front/src/modules/ui/context-menu/types/PositionType.ts similarity index 100% rename from front/src/modules/ui/action-bar/types/PositionType.ts rename to front/src/modules/ui/context-menu/types/PositionType.ts diff --git a/front/src/modules/ui/table/action-bar/components/EntityTableActionBar.tsx b/front/src/modules/ui/table/action-bar/components/EntityTableActionBar.tsx index 77d0ac93e..7591b3c64 100644 --- a/front/src/modules/ui/table/action-bar/components/EntityTableActionBar.tsx +++ b/front/src/modules/ui/table/action-bar/components/EntityTableActionBar.tsx @@ -5,12 +5,8 @@ import { ActionBar } from '@/ui/action-bar/components/ActionBar'; import { selectedRowIdsSelector } from '../../states/selectors/selectedRowIdsSelector'; -type OwnProps = { - children: React.ReactNode | React.ReactNode[]; -}; - -export function EntityTableActionBar({ children }: OwnProps) { +export function EntityTableActionBar() { const selectedRowIds = useRecoilValue(selectedRowIdsSelector); - return {children}; + return ; } diff --git a/front/src/modules/ui/table/action-bar/components/TableActionBarButtonOpenComments.tsx b/front/src/modules/ui/table/action-bar/components/TableActionBarButtonOpenComments.tsx deleted file mode 100644 index 8da8db328..000000000 --- a/front/src/modules/ui/table/action-bar/components/TableActionBarButtonOpenComments.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { IconNotes } from '@/ui/icon/index'; - -import { EntityTableActionBarButton } from './EntityTableActionBarButton'; - -type OwnProps = { - onClick: () => void; -}; - -export function TableActionBarButtonToggleComments({ onClick }: OwnProps) { - return ( - } - onClick={onClick} - /> - ); -} diff --git a/front/src/modules/ui/table/action-bar/components/TableActionBarButtonOpenTasks.tsx b/front/src/modules/ui/table/action-bar/components/TableActionBarButtonOpenTasks.tsx deleted file mode 100644 index b407ec239..000000000 --- a/front/src/modules/ui/table/action-bar/components/TableActionBarButtonOpenTasks.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { IconCheckbox } from '@/ui/icon/index'; - -import { EntityTableActionBarButton } from './EntityTableActionBarButton'; - -type OwnProps = { - onClick: () => void; -}; - -export function TableActionBarButtonToggleTasks({ onClick }: OwnProps) { - return ( - } - onClick={onClick} - /> - ); -} diff --git a/front/src/modules/ui/table/components/CheckboxCell.tsx b/front/src/modules/ui/table/components/CheckboxCell.tsx index 91e06da77..0b3a4749a 100644 --- a/front/src/modules/ui/table/components/CheckboxCell.tsx +++ b/front/src/modules/ui/table/components/CheckboxCell.tsx @@ -2,10 +2,10 @@ import { useCallback } from 'react'; import styled from '@emotion/styled'; import { useSetRecoilState } from 'recoil'; +import { actionBarOpenState } from '@/ui/action-bar/states/ActionBarIsOpenState'; import { Checkbox } from '@/ui/input/checkbox/components/Checkbox'; import { useCurrentRowSelected } from '../hooks/useCurrentRowSelected'; -import { contextMenuPositionState } from '../states/contextMenuPositionState'; const StyledContainer = styled.div` align-items: center; @@ -18,14 +18,13 @@ const StyledContainer = styled.div` `; export function CheckboxCell() { - const setContextMenuPosition = useSetRecoilState(contextMenuPositionState); - + const setActionBarOpenState = useSetRecoilState(actionBarOpenState); const { currentRowSelected, setCurrentRowSelected } = useCurrentRowSelected(); const handleClick = useCallback(() => { setCurrentRowSelected(!currentRowSelected); - setContextMenuPosition({ x: null, y: null }); - }, [currentRowSelected, setContextMenuPosition, setCurrentRowSelected]); + setActionBarOpenState(true); + }, [currentRowSelected, setActionBarOpenState, setCurrentRowSelected]); return ( diff --git a/front/src/modules/ui/table/components/EntityTableCell.tsx b/front/src/modules/ui/table/components/EntityTableCell.tsx index 0a0055002..c27f7d9bd 100644 --- a/front/src/modules/ui/table/components/EntityTableCell.tsx +++ b/front/src/modules/ui/table/components/EntityTableCell.tsx @@ -1,28 +1,29 @@ import { useContext } from 'react'; import { useSetRecoilState } from 'recoil'; +import { contextMenuOpenState } from '@/ui/context-menu/states/ContextMenuIsOpenState'; +import { contextMenuPositionState } from '@/ui/context-menu/states/ContextMenuPositionState'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { ColumnIndexContext } from '../contexts/ColumnIndexContext'; import { ViewFieldContext } from '../contexts/ViewFieldContext'; import { GenericEditableCell } from '../editable-cell/components/GenericEditableCell'; import { useCurrentRowSelected } from '../hooks/useCurrentRowSelected'; -import { contextMenuPositionState } from '../states/contextMenuPositionState'; export function EntityTableCell({ cellIndex }: { cellIndex: number }) { const setContextMenuPosition = useSetRecoilState(contextMenuPositionState); + const setContextMenuOpenState = useSetRecoilState(contextMenuOpenState); const { setCurrentRowSelected } = useCurrentRowSelected(); function handleContextMenu(event: React.MouseEvent) { event.preventDefault(); - setCurrentRowSelected(true); - setContextMenuPosition({ x: event.clientX, y: event.clientY, }); + setContextMenuOpenState(true); } const viewField = useContext(ViewFieldContext); diff --git a/front/src/modules/ui/table/components/GenericEntityTableData.tsx b/front/src/modules/ui/table/components/GenericEntityTableData.tsx index 58aae7899..f23920830 100644 --- a/front/src/modules/ui/table/components/GenericEntityTableData.tsx +++ b/front/src/modules/ui/table/components/GenericEntityTableData.tsx @@ -8,12 +8,16 @@ export function GenericEntityTableData({ orderBy = defaultOrderBy, whereFilters, filterDefinitionArray, + setActionBar, + setContextMenu, }: { useGetRequest: any; getRequestResultKey: string; orderBy?: any; whereFilters?: any; filterDefinitionArray: FilterDefinition[]; + setActionBar?: () => void; + setContextMenu?: () => void; }) { const setEntityTableData = useSetEntityTableData(); useGetRequest({ @@ -24,5 +28,11 @@ export function GenericEntityTableData({ }, }); + if (setActionBar) { + setActionBar(); + } + if (setContextMenu) { + setContextMenu(); + } return <>; } diff --git a/front/src/modules/ui/table/context-menu/components/EntityTableContextMenu.tsx b/front/src/modules/ui/table/context-menu/components/EntityTableContextMenu.tsx new file mode 100644 index 000000000..bd49c6cb8 --- /dev/null +++ b/front/src/modules/ui/table/context-menu/components/EntityTableContextMenu.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { useRecoilValue } from 'recoil'; + +import { ContextMenu } from '@/ui/context-menu/components/ContextMenu'; + +import { selectedRowIdsSelector } from '../../states/selectors/selectedRowIdsSelector'; + +export function EntityTableContextMenu() { + const selectedRowIds = useRecoilValue(selectedRowIdsSelector); + return ; +} diff --git a/front/src/pages/companies/Companies.tsx b/front/src/pages/companies/Companies.tsx index 980212445..4045a884a 100644 --- a/front/src/pages/companies/Companies.tsx +++ b/front/src/pages/companies/Companies.tsx @@ -4,12 +4,11 @@ import styled from '@emotion/styled'; import { v4 } from 'uuid'; import { CompanyTable } from '@/companies/table/components/CompanyTable'; -import { TableActionBarButtonCreateActivityCompany } from '@/companies/table/components/TableActionBarButtonCreateActivityCompany'; -import { TableActionBarButtonDeleteCompanies } from '@/companies/table/components/TableActionBarButtonDeleteCompanies'; import { SEARCH_COMPANY_QUERY } from '@/search/queries/search'; import { IconBuildingSkyscraper } from '@/ui/icon'; import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer'; import { EntityTableActionBar } from '@/ui/table/action-bar/components/EntityTableActionBar'; +import { EntityTableContextMenu } from '@/ui/table/context-menu/components/EntityTableContextMenu'; import { useUpsertEntityTableItem } from '@/ui/table/hooks/useUpsertEntityTableItem'; import { useUpsertTableRowId } from '@/ui/table/hooks/useUpsertTableRowId'; import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext'; @@ -73,10 +72,8 @@ export function Companies() { - - - - + + ); diff --git a/front/src/pages/companies/CompaniesMockMode.tsx b/front/src/pages/companies/CompaniesMockMode.tsx index cf3e2d08e..1a1c72b1d 100644 --- a/front/src/pages/companies/CompaniesMockMode.tsx +++ b/front/src/pages/companies/CompaniesMockMode.tsx @@ -2,8 +2,6 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { CompanyTableMockMode } from '@/companies/table/components/CompanyTableMockMode'; -import { TableActionBarButtonCreateActivityCompany } from '@/companies/table/components/TableActionBarButtonCreateActivityCompany'; -import { TableActionBarButtonDeleteCompanies } from '@/companies/table/components/TableActionBarButtonDeleteCompanies'; import { IconBuildingSkyscraper } from '@/ui/icon'; import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer'; import { EntityTableActionBar } from '@/ui/table/action-bar/components/EntityTableActionBar'; @@ -28,10 +26,7 @@ export function CompaniesMockMode() { - - - - + diff --git a/front/src/pages/opportunities/Opportunities.tsx b/front/src/pages/opportunities/Opportunities.tsx index 2e0f2b733..bf8b60616 100644 --- a/front/src/pages/opportunities/Opportunities.tsx +++ b/front/src/pages/opportunities/Opportunities.tsx @@ -7,10 +7,10 @@ import { defaultPipelineProgressOrderBy, PipelineProgressesSelectedSortType, } from '@/pipeline/queries'; -import { BoardActionBarButtonDeleteBoardCard } from '@/ui/board/components/BoardActionBarButtonDeleteBoardCard'; import { EntityBoard } from '@/ui/board/components/EntityBoard'; import { EntityBoardActionBar } from '@/ui/board/components/EntityBoardActionBar'; import { BoardOptionsContext } from '@/ui/board/contexts/BoardOptionsContext'; +import { useActionBarEntries } from '@/ui/board/hooks/useActionBarEntries'; import { reduceSortsToOrderBy } from '@/ui/filter-n-sort/helpers'; import { IconTargetArrow } from '@/ui/icon/index'; import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer'; @@ -63,6 +63,8 @@ export function Opportunities() { }); } + const setActionBar = useActionBarEntries(); + return ( - + - - - + diff --git a/front/src/pages/people/People.tsx b/front/src/pages/people/People.tsx index 4f944e057..6f2845285 100644 --- a/front/src/pages/people/People.tsx +++ b/front/src/pages/people/People.tsx @@ -3,11 +3,10 @@ import styled from '@emotion/styled'; import { v4 } from 'uuid'; import { PeopleTable } from '@/people/table/components/PeopleTable'; -import { TableActionBarButtonCreateActivityPeople } from '@/people/table/components/TableActionBarButtonCreateActivityPeople'; -import { TableActionBarButtonDeletePeople } from '@/people/table/components/TableActionBarButtonDeletePeople'; import { IconUser } from '@/ui/icon'; import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer'; import { EntityTableActionBar } from '@/ui/table/action-bar/components/EntityTableActionBar'; +import { EntityTableContextMenu } from '@/ui/table/context-menu/components/EntityTableContextMenu'; import { useUpsertEntityTableItem } from '@/ui/table/hooks/useUpsertEntityTableItem'; import { useUpsertTableRowId } from '@/ui/table/hooks/useUpsertTableRowId'; import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext'; @@ -66,10 +65,8 @@ export function People() { - - - - + + );