diff --git a/package.json b/package.json index e5dc52ef81..1755205a00 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "react-error-boundary": "^4.0.11", "react-helmet-async": "^1.3.0", "react-hook-form": "^7.45.1", - "react-hotkeys-hook": "^4.4.3", + "react-hotkeys-hook": "^4.4.4", "react-icons": "^4.12.0", "react-intersection-observer": "^9.5.2", "react-loading-skeleton": "^3.3.1", diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenu.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenu.tsx index 68fdaac249..430301c54e 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenu.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenu.tsx @@ -1,6 +1,7 @@ import { useMemo, useRef, useState } from 'react'; import styled from '@emotion/styled'; import { useRecoilValue } from 'recoil'; +import { Key } from 'ts-key-enum'; import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer'; import { Activity } from '@/activities/types/Activity'; @@ -124,7 +125,7 @@ export const CommandMenu = () => { ); useScopedHotkeys( - 'esc', + [Key.Escape], () => { closeCommandMenu(); }, diff --git a/packages/twenty-front/src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenu.tsx b/packages/twenty-front/src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenu.tsx index 978d3967ec..7b2d0bd6a9 100644 --- a/packages/twenty-front/src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenu.tsx +++ b/packages/twenty-front/src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenu.tsx @@ -1,4 +1,5 @@ import { useRecoilValue } from 'recoil'; +import { Key } from 'ts-key-enum'; import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; @@ -22,6 +23,7 @@ export const KeyboardShortcutMenu = () => { isKeyboardShortcutMenuOpenedState, ); const { closeCommandMenu } = useCommandMenu(); + useScopedHotkeys( 'shift+?,meta+?', () => { @@ -33,12 +35,12 @@ export const KeyboardShortcutMenu = () => { ); useScopedHotkeys( - 'esc', + [Key.Escape], () => { closeKeyboardShortcutMenu(); }, - AppHotkeyScope.KeyboardShortcutMenu, - [toggleKeyboardShortcutMenu], + AppHotkeyScope.KeyboardShortcutMenuOpen, + [closeKeyboardShortcutMenu], ); return ( @@ -46,8 +48,8 @@ export const KeyboardShortcutMenu = () => { {isKeyboardShortcutMenuOpened && ( - {keyboardShortcutsTable.map((TableShortcut) => ( - + {keyboardShortcutsTable.map((TableShortcut, index) => ( + ))} diff --git a/packages/twenty-front/src/modules/object-record/record-board-deprecated/components/RecordBoardDeprecated.tsx b/packages/twenty-front/src/modules/object-record/record-board-deprecated/components/RecordBoardDeprecated.tsx index 61bbd75f39..854a138297 100644 --- a/packages/twenty-front/src/modules/object-record/record-board-deprecated/components/RecordBoardDeprecated.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board-deprecated/components/RecordBoardDeprecated.tsx @@ -12,9 +12,7 @@ import { useRecordBoardDeprecatedScopedStates } from '@/object-record/record-boa import { useSetRecordBoardDeprecatedCardSelectedInternal } from '@/object-record/record-board-deprecated/hooks/internal/useSetRecordBoardDeprecatedCardSelectedInternal'; import { RecordBoardDeprecatedScope } from '@/object-record/record-board-deprecated/scopes/RecordBoardDeprecatedScope'; import { Opportunity } from '@/pipeline/types/Opportunity'; -import { PageHotkeyScope } from '@/types/PageHotkeyScope'; import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect'; -import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useListenClickOutsideByClassName } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; import { logError } from '~/utils/logError'; @@ -129,12 +127,6 @@ export const RecordBoardDeprecated = ({ const boardRef = useRef(null); - useScopedHotkeys( - 'escape', - unselectAllActiveCards, - PageHotkeyScope.OpportunitiesPage, - ); - return ( diff --git a/packages/twenty-front/src/modules/object-record/record-board-deprecated/components/RecordBoardDeprecatedColumnDropdownMenu.tsx b/packages/twenty-front/src/modules/object-record/record-board-deprecated/components/RecordBoardDeprecatedColumnDropdownMenu.tsx index 4c20960063..69e99b95d0 100644 --- a/packages/twenty-front/src/modules/object-record/record-board-deprecated/components/RecordBoardDeprecatedColumnDropdownMenu.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board-deprecated/components/RecordBoardDeprecatedColumnDropdownMenu.tsx @@ -69,7 +69,9 @@ export const RecordBoardDeprecatedColumnDropdownMenu = ({ useScopedHotkeys( [Key.Escape, Key.Enter], - closeMenu, + () => { + closeMenu(); + }, BoardColumnHotkeyScope.BoardColumn, [], ); diff --git a/packages/twenty-front/src/modules/object-record/record-board-deprecated/options/components/RecordBoardDeprecatedOptionsDropdownContent.tsx b/packages/twenty-front/src/modules/object-record/record-board-deprecated/options/components/RecordBoardDeprecatedOptionsDropdownContent.tsx index 58514c6c81..e673f1de3d 100644 --- a/packages/twenty-front/src/modules/object-record/record-board-deprecated/options/components/RecordBoardDeprecatedOptionsDropdownContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board-deprecated/options/components/RecordBoardDeprecatedOptionsDropdownContent.tsx @@ -123,7 +123,7 @@ export const RecordBoardDeprecatedOptionsDropdownContent = ({ ); useScopedHotkeys( - Key.Escape, + [Key.Escape], () => { setViewEditMode('none'); closeDropdown(); diff --git a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx index 3cfddff76c..3fff6daa51 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx @@ -2,15 +2,19 @@ import { useContext, useRef } from 'react'; import styled from '@emotion/styled'; import { DragDropContext, OnDragEndResponder } from '@hello-pangea/dnd'; // Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350 import { useRecoilCallback, useRecoilValue } from 'recoil'; +import { Key } from 'ts-key-enum'; import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; +import { useRecordBoardSelection } from '@/object-record/record-board/hooks/useRecordBoardSelection'; import { RecordBoardColumn } from '@/object-record/record-board/record-board-column/components/RecordBoardColumn'; import { RecordBoardScope } from '@/object-record/record-board/scopes/RecordBoardScope'; +import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope'; import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect'; +import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; +import { useListenClickOutsideByClassName } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; -import { FieldMetadataType } from '~/generated-metadata/graphql'; export type RecordBoardProps = { recordBoardId: string; @@ -40,17 +44,25 @@ const StyledBoardHeader = styled.div` `; export const RecordBoard = ({ recordBoardId }: RecordBoardProps) => { - const { updateOneRecord, objectMetadataItem } = + const { updateOneRecord, selectFieldMetadataItem } = useContext(RecordBoardContext); const boardRef = useRef(null); const { getColumnIdsState, columnsFamilySelector } = useRecordBoardStates(recordBoardId); + const columnIds = useRecoilValue(getColumnIdsState()); - const selectFieldMetadataItem = objectMetadataItem.fields.find( - (field) => field.type === FieldMetadataType.Select, - ); + const { resetRecordSelection, setRecordAsSelected } = + useRecordBoardSelection(recordBoardId); + + useListenClickOutsideByClassName({ + classNames: ['record-board-card'], + excludeClassNames: ['action-bar', 'context-menu'], + callback: resetRecordSelection, + }); + + useScopedHotkeys([Key.Escape], resetRecordSelection, TableHotkeyScope.Table); const onDragEnd: OnDragEndResponder = useRecoilCallback( ({ snapshot }) => @@ -106,7 +118,8 @@ export const RecordBoard = ({ recordBoardId }: RecordBoardProps) => { {}} + onDragSelectionStart={resetRecordSelection} + onDragSelectionChange={setRecordAsSelected} /> diff --git a/packages/twenty-front/src/modules/object-record/record-board/contexts/RecordBoardContext.ts b/packages/twenty-front/src/modules/object-record/record-board/contexts/RecordBoardContext.ts index 8043ec985b..d8d7f14907 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/contexts/RecordBoardContext.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/contexts/RecordBoardContext.ts @@ -1,10 +1,12 @@ import { createContext } from 'react'; +import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; type RecordBoardContextProps = { objectMetadataItem: ObjectMetadataItem; + selectFieldMetadataItem: FieldMetadataItem; createOneRecord: (recordInput: Partial) => void; updateOneRecord: ({ idToUpdate, diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/useResetBoardRecordSelection.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoardSelection.ts similarity index 53% rename from packages/twenty-front/src/modules/object-record/record-board/hooks/useResetBoardRecordSelection.ts rename to packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoardSelection.ts index 6266eb5367..ae89223c79 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/hooks/useResetBoardRecordSelection.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoardSelection.ts @@ -2,7 +2,7 @@ import { useRecoilCallback } from 'recoil'; import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; -export const useResetBoardRecordSelection = (recordBoardId?: string) => { +export const useRecordBoardSelection = (recordBoardId?: string) => { const { getSelectedRecordIdsSelector, isRecordBoardCardSelectedFamilyState } = useRecordBoardStates(recordBoardId); @@ -20,5 +20,21 @@ export const useResetBoardRecordSelection = (recordBoardId?: string) => { [getSelectedRecordIdsSelector, isRecordBoardCardSelectedFamilyState], ); - return { resetRecordSelection }; + const setRecordAsSelected = useRecoilCallback( + ({ snapshot, set }) => + (recordId: string, isSelected: boolean) => { + const isRecordCurrentlySelected = snapshot + .getLoadable(isRecordBoardCardSelectedFamilyState(recordId)) + .getValue(); + + if (isRecordCurrentlySelected === isSelected) { + return; + } + + set(isRecordBoardCardSelectedFamilyState(recordId), isSelected); + }, + [isRecordBoardCardSelectedFamilyState], + ); + + return { resetRecordSelection, setRecordAsSelected }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCardDraggableContainer.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCardDraggableContainer.tsx index f8f0b9c8de..af763fa60e 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCardDraggableContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCardDraggableContainer.tsx @@ -18,7 +18,7 @@ export const RecordBoardCardDraggableContainer = ({ {...draggableProvided?.dragHandleProps} // eslint-disable-next-line react/jsx-props-no-spreading {...draggableProvided?.draggableProps} - className="entity-board-card" + className="record-board-card" data-selectable-id={recordId} data-select-disable > diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumn.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumn.tsx index 5a048c78f6..769f261ddc 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumn.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumn.tsx @@ -14,6 +14,7 @@ const StyledColumn = styled.div<{ isFirstColumn: boolean }>` isFirstColumn ? 'none' : theme.border.color.light}; display: flex; flex-direction: column; + height: fit-content; max-width: 200px; min-width: 200px; @@ -60,6 +61,7 @@ export const RecordBoardColumn = ({ columnDefinition: columnDefinition, isFirstColumn: isFirstColumn, isLastColumn: isLastColumn, + recordCount: recordIds.length, }} > diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer.tsx index d9bc202be9..236a431455 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer.tsx @@ -1,8 +1,10 @@ -import React from 'react'; +import React, { useContext } from 'react'; import styled from '@emotion/styled'; -import { DroppableProvided } from '@hello-pangea/dnd'; +import { Draggable, DroppableProvided } from '@hello-pangea/dnd'; import { RecordBoardColumnCardsMemo } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnCardsMemo'; +import { RecordBoardColumnNewButton } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnNewButton'; +import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; const StyledPlaceholder = styled.div` min-height: 1px; @@ -14,6 +16,10 @@ const StyledColumnCardsContainer = styled.div` flex-direction: column; `; +const StyledNewButtonContainer = styled.div` + padding-bottom: ${({ theme }) => theme.spacing(4)}; +`; + type RecordBoardColumnCardsContainerProps = { recordIds: string[]; droppableProvided: DroppableProvided; @@ -23,6 +29,8 @@ export const RecordBoardColumnCardsContainer = ({ recordIds, droppableProvided, }: RecordBoardColumnCardsContainerProps) => { + const { columnDefinition } = useContext(RecordBoardColumnContext); + return ( {droppableProvided?.placeholder} + + {(draggableProvided) => ( +
+ + + +
+ )} +
); }; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx index 31d47a39a3..d1e36a3ecc 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx @@ -46,7 +46,9 @@ export const RecordBoardColumnHeader = () => { const [isBoardColumnMenuOpen, setIsBoardColumnMenuOpen] = useState(false); const [isHeaderHovered, setIsHeaderHovered] = useState(false); - const { columnDefinition } = useContext(RecordBoardColumnContext); + const { columnDefinition, recordCount } = useContext( + RecordBoardColumnContext, + ); const { setHotkeyScopeAndMemorizePreviousScope, @@ -66,7 +68,6 @@ export const RecordBoardColumnHeader = () => { }; const boardColumnTotal = 0; - const cardIds = []; return ( <> @@ -81,7 +82,7 @@ export const RecordBoardColumnHeader = () => { /> {!!boardColumnTotal && ${boardColumnTotal}} {!isHeaderHovered && ( - {cardIds.length} + {recordCount} )} {isHeaderHovered && ( diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewButton.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewButton.tsx new file mode 100644 index 0000000000..deac52c76d --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewButton.tsx @@ -0,0 +1,44 @@ +import { useContext } from 'react'; +import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; + +import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; +import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; +import { IconPlus } from '@/ui/display/icon/index'; + +const StyledButton = styled.button` + align-items: center; + align-self: baseline; + background-color: ${({ theme }) => theme.background.primary}; + border: none; + border-radius: ${({ theme }) => theme.border.radius.sm}; + color: ${({ theme }) => theme.font.color.tertiary}; + cursor: pointer; + display: flex; + gap: ${({ theme }) => theme.spacing(1)}; + padding: ${({ theme }) => theme.spacing(1)}; + + &:hover { + background-color: ${({ theme }) => theme.background.tertiary}; + } +`; + +export const RecordBoardColumnNewButton = () => { + const theme = useTheme(); + const { columnDefinition } = useContext(RecordBoardColumnContext); + const { createOneRecord, selectFieldMetadataItem } = + useContext(RecordBoardContext); + + const onNewClick = () => { + createOneRecord({ + [selectFieldMetadataItem.name]: columnDefinition.value, + }); + }; + + return ( + + + New + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext.ts b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext.ts index 7016dcce81..8a9ced3eb0 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext.ts @@ -6,6 +6,7 @@ type RecordBoardColumnContextProps = { columnDefinition: RecordBoardColumnDefinition; isFirstColumn: boolean; isLastColumn: boolean; + recordCount: number; }; export const RecordBoardColumnContext = diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents.ts index 36e180652b..c4aa0b3085 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents.ts @@ -1,3 +1,5 @@ +import { Key } from 'ts-key-enum'; + import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { isDefined } from '~/utils/isDefined'; @@ -41,7 +43,7 @@ export const useRegisterInputEvents = ({ ); useScopedHotkeys( - 'esc', + [Key.Escape], () => { onEscape?.(inputValue); }, diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardContainer.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardContainer.tsx index 80f8ad7c66..d11fcb4ac4 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardContainer.tsx @@ -6,6 +6,7 @@ import { RecordBoardActionBar } from '@/object-record/record-board/action-bar/co import { RecordBoard } from '@/object-record/record-board/components/RecordBoard'; import { RecordBoardContextMenu } from '@/object-record/record-board/context-menu/components/RecordBoardContextMenu'; import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; +import { FieldMetadataType } from '~/generated-metadata/graphql'; type RecordIndexBoardContainerProps = { recordBoardId: string; @@ -20,14 +21,23 @@ export const RecordIndexBoardContainer = ({ }: RecordIndexBoardContainerProps) => { const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular }); + const selectFieldMetadataItem = objectMetadataItem.fields.find( + (field) => field.type === FieldMetadataType.Select, + ); + const { deleteOneRecord } = useDeleteOneRecord({ objectNameSingular }); const { updateOneRecord } = useUpdateOneRecord({ objectNameSingular }); const { createOneRecord } = useCreateOneRecord({ objectNameSingular }); + if (!selectFieldMetadataItem) { + return; + } + return ( { const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular, }); - useLoadRecordIndexBoard(objectNameSingular, recordBoardId); + useLoadRecordIndexBoard({ objectNameSingular, recordBoardId, viewBarId }); const navigate = useNavigate(); @@ -31,9 +33,13 @@ export const RecordIndexBoardContainerEffect = ({ navigate(`/settings/objects/${objectMetadataItem.namePlural}`); }, [navigate, objectMetadataItem.namePlural]); - const { setColumns, setObjectSingularName, getSelectedRecordIdsSelector } = - useRecordBoard(recordBoardId); - const { resetRecordSelection } = useResetBoardRecordSelection(recordBoardId); + const { + setColumns, + setObjectSingularName, + getSelectedRecordIdsSelector, + setFieldDefinitions, + } = useRecordBoard(recordBoardId); + const { resetRecordSelection } = useRecordBoardSelection(recordBoardId); useEffect(() => { setObjectSingularName(objectNameSingular); @@ -53,6 +59,14 @@ export const RecordIndexBoardContainerEffect = ({ setColumns, ]); + const recordIndexFieldDefinitions = useRecoilValue( + recordIndexFieldDefinitionsState, + ); + + useEffect(() => { + setFieldDefinitions(recordIndexFieldDefinitions); + }, [objectMetadataItem, setFieldDefinitions, recordIndexFieldDefinitions]); + const selectedRecordIds = useRecoilValue(getSelectedRecordIdsSelector()); const { setActionBarEntries, setContextMenuEntries } = useRecordActionBar({ diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts index f7a394f8ed..208c48d611 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts @@ -10,11 +10,19 @@ import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/s import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState'; import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState'; import { useSetRecordInStore } from '@/object-record/record-store/hooks/useSetRecordInStore'; +import { useViewBar } from '@/views/hooks/useViewBar'; -export const useLoadRecordIndexBoard = ( - objectNameSingular: string, - recordBoardId: string, -) => { +type UseLoadRecordIndexBoardProps = { + objectNameSingular: string; + viewBarId: string; + recordBoardId: string; +}; + +export const useLoadRecordIndexBoard = ({ + objectNameSingular, + viewBarId, + recordBoardId, +}: UseLoadRecordIndexBoardProps) => { const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular, }); @@ -47,6 +55,10 @@ export const useLoadRecordIndexBoard = ( orderBy, }); + const { setEntityCountInCurrentView } = useViewBar({ + viewBarId, + }); + useEffect(() => { setRecordIdsInBoard(records); }, [records, setRecordIdsInBoard]); @@ -55,6 +67,10 @@ export const useLoadRecordIndexBoard = ( setRecordsInStore(records); }, [records, setRecordsInStore]); + useEffect(() => { + setEntityCountInCurrentView(records.length); + }, [records.length, setEntityCountInCurrentView]); + return { records, loading, diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableInternalEffect.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableInternalEffect.tsx index 6f74a8aa34..cb8be2bbca 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableInternalEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableInternalEffect.tsx @@ -1,3 +1,5 @@ +import { Key } from 'ts-key-enum'; + import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; @@ -28,7 +30,7 @@ export const RecordTableInternalEffect = ({ }); useScopedHotkeys( - 'escape', + [Key.Escape], () => { resetTableRowSelection(); }, diff --git a/packages/twenty-front/src/modules/object-record/record-table/options/components/TableOptionsDropdownContent.tsx b/packages/twenty-front/src/modules/object-record/record-table/options/components/TableOptionsDropdownContent.tsx index 94bb6eb15f..2cfe4c8112 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/options/components/TableOptionsDropdownContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/options/components/TableOptionsDropdownContent.tsx @@ -80,7 +80,7 @@ export const TableOptionsDropdownContent = ({ const resetMenu = () => setCurrentMenu(undefined); useScopedHotkeys( - Key.Escape, + [Key.Escape], () => { closeDropdown(); }, diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx index 8d6c4d9981..6385b06c42 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx @@ -59,7 +59,7 @@ export const SingleEntitySelectMenuItems = ({ ); useScopedHotkeys( - Key.Escape, + [Key.Escape], () => { onCancel?.(); }, diff --git a/packages/twenty-front/src/modules/ui/field/input/components/DoubleTextInput.tsx b/packages/twenty-front/src/modules/ui/field/input/components/DoubleTextInput.tsx index 9333403956..3a5b387aa0 100644 --- a/packages/twenty-front/src/modules/ui/field/input/components/DoubleTextInput.tsx +++ b/packages/twenty-front/src/modules/ui/field/input/components/DoubleTextInput.tsx @@ -94,7 +94,7 @@ export const DoubleTextInput = ({ ); useScopedHotkeys( - Key.Escape, + [Key.Escape], () => { onEscape({ firstValue: firstInternalValue, diff --git a/packages/twenty-front/src/modules/ui/input/components/AutosizeTextInput.tsx b/packages/twenty-front/src/modules/ui/input/components/AutosizeTextInput.tsx index c347c8158b..95d40a4d03 100644 --- a/packages/twenty-front/src/modules/ui/input/components/AutosizeTextInput.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/AutosizeTextInput.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import { HotkeysEvent } from 'react-hotkeys-hook/dist/types'; import TextareaAutosize from 'react-textarea-autosize'; import styled from '@emotion/styled'; +import { Key } from 'ts-key-enum'; import { IconArrowRight } from '@/ui/display/icon/index'; import { Button } from '@/ui/input/button/components/Button'; @@ -151,7 +152,7 @@ export const AutosizeTextInput = ({ ); useScopedHotkeys( - 'esc', + Key.Escape, (event: KeyboardEvent) => { if (!isFocused) { return; diff --git a/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx b/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx index 57f1bc0b42..1169b1587a 100644 --- a/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx +++ b/packages/twenty-front/src/modules/ui/layout/dropdown/components/Dropdown.tsx @@ -94,7 +94,7 @@ export const Dropdown = ({ }); useScopedHotkeys( - Key.Escape, + [Key.Escape], () => { closeDropdown(); }, diff --git a/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawer.tsx b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawer.tsx index 2ec927823f..8d5419819b 100644 --- a/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawer.tsx +++ b/packages/twenty-front/src/modules/ui/layout/right-drawer/components/RightDrawer.tsx @@ -66,7 +66,10 @@ export const RightDrawer = () => { useScopedHotkeys( [Key.Escape], - () => closeRightDrawer(), + + () => { + closeRightDrawer(); + }, RightDrawerHotkeyScope.RightDrawer, [setIsRightDrawerOpen], ); diff --git a/packages/twenty-front/src/modules/ui/utilities/hotkey/types/AppHotkeyScope.ts b/packages/twenty-front/src/modules/ui/utilities/hotkey/types/AppHotkeyScope.ts index cd063563e5..9fc1a460ec 100644 --- a/packages/twenty-front/src/modules/ui/utilities/hotkey/types/AppHotkeyScope.ts +++ b/packages/twenty-front/src/modules/ui/utilities/hotkey/types/AppHotkeyScope.ts @@ -4,4 +4,5 @@ export enum AppHotkeyScope { CommandMenu = 'command-menu', CommandMenuOpen = 'command-menu-open', KeyboardShortcutMenu = 'keyboard-shortcut-menu', + KeyboardShortcutMenuOpen = 'keyboard-shortcut-menu-open', } diff --git a/packages/twenty-front/src/modules/views/hooks/useViewBar.ts b/packages/twenty-front/src/modules/views/hooks/useViewBar.ts index a4b6354579..2111217a2e 100644 --- a/packages/twenty-front/src/modules/views/hooks/useViewBar.ts +++ b/packages/twenty-front/src/modules/views/hooks/useViewBar.ts @@ -127,8 +127,9 @@ export const useViewBar = (props?: UseViewProps) => { if (!isDeeplyEqual(savedViewFields, queriedViewFields)) { set(currentViewFieldsState, queriedViewFields); set(savedViewFieldsState, queriedViewFields); - onViewFieldsChange?.(queriedViewFields); } + + onViewFieldsChange?.(queriedViewFields); }, [scopeId], ); diff --git a/yarn.lock b/yarn.lock index f29e29e4ce..45da2979c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -38381,13 +38381,13 @@ __metadata: languageName: node linkType: hard -"react-hotkeys-hook@npm:^4.4.3": - version: 4.4.3 - resolution: "react-hotkeys-hook@npm:4.4.3" +"react-hotkeys-hook@npm:^4.4.4": + version: 4.4.4 + resolution: "react-hotkeys-hook@npm:4.4.4" peerDependencies: react: ">=16.8.1" react-dom: ">=16.8.1" - checksum: ef79e279129f6e55d81c8762b1da214d9c6ee4617b9597dbc3f93057cba8d166831508967b8e3f763a0c4ce0af3b59e6888c6fc94d152deac9335020b2ba80df + checksum: afe7418c8bd0ecd3a3f315648b84d9978d06a02e55f93df23aaa00e56613fca839f9ff4d10387e8336454702b4aa03cef97f3e67bfe3e121e42f16d84685f55a languageName: node linkType: hard @@ -43453,7 +43453,7 @@ __metadata: react-error-boundary: "npm:^4.0.11" react-helmet-async: "npm:^1.3.0" react-hook-form: "npm:^7.45.1" - react-hotkeys-hook: "npm:^4.4.3" + react-hotkeys-hook: "npm:^4.4.4" react-icons: "npm:^4.12.0" react-intersection-observer: "npm:^9.5.2" react-loading-skeleton: "npm:^3.3.1"