diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultActionsConfigV2.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultActionsConfigV2.ts index f7dd5b223500..c68e731d360c 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultActionsConfigV2.ts +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultActionsConfigV2.ts @@ -1,6 +1,7 @@ import { useDeleteMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useDeleteMultipleRecordsAction'; import { useExportMultipleRecordsAction } from '@/action-menu/actions/record-actions/multiple-records/hooks/useExportMultipleRecordsAction'; import { MultipleRecordsActionKeys } from '@/action-menu/actions/record-actions/multiple-records/types/MultipleRecordsActionKeys'; +import { useCreateNewTableRecordNoSelectionRecordAction } from '@/action-menu/actions/record-actions/no-selection/hooks/useCreateNewTableRecordNoSelectionRecordAction'; import { NoSelectionRecordActionKeys } from '@/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey'; import { useAddToFavoritesSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useAddToFavoritesSingleRecordAction'; import { useDeleteSingleRecordAction } from '@/action-menu/actions/record-actions/single-record/hooks/useDeleteSingleRecordAction'; @@ -24,6 +25,7 @@ import { IconFileExport, IconHeart, IconHeartOff, + IconPlus, IconTrash, IconTrashX, } from 'twenty-ui'; @@ -34,6 +36,18 @@ export const DEFAULT_ACTIONS_CONFIG_V2: Record< actionHook: ActionHook; } > = { + createNewRecord: { + type: ActionMenuEntryType.Standard, + scope: ActionMenuEntryScope.RecordSelection, + key: NoSelectionRecordActionKeys.CREATE_NEW_RECORD, + label: 'Create new record', + shortLabel: 'New record', + position: 0, + isPinned: true, + Icon: IconPlus, + availableOn: [ActionViewType.INDEX_PAGE_NO_SELECTION], + actionHook: useCreateNewTableRecordNoSelectionRecordAction, + }, exportNoteToPdf: { type: ActionMenuEntryType.Standard, scope: ActionMenuEntryScope.RecordSelection, diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/hooks/useCreateNewTableRecordNoSelectionRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/hooks/useCreateNewTableRecordNoSelectionRecordAction.ts new file mode 100644 index 000000000000..f640ff6a786f --- /dev/null +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/hooks/useCreateNewTableRecordNoSelectionRecordAction.ts @@ -0,0 +1,24 @@ +import { ActionHookWithObjectMetadataItem } from '@/action-menu/actions/types/ActionHook'; +import { useCreateNewTableRecord } from '@/object-record/record-table/hooks/useCreateNewTableRecords'; +import { getRecordIndexIdFromObjectNamePlural } from '@/object-record/utils/getRecordIndexIdFromObjectNamePlural'; + +export const useCreateNewTableRecordNoSelectionRecordAction: ActionHookWithObjectMetadataItem = + ({ objectMetadataItem }) => { + const recordTableId = getRecordIndexIdFromObjectNamePlural( + objectMetadataItem.namePlural, + ); + + const { createNewTableRecord } = useCreateNewTableRecord({ + objectMetadataItem, + recordTableId, + }); + + const onClick = () => { + createNewTableRecord(); + }; + + return { + shouldBeRegistered: true, + onClick, + }; + }; diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey.ts index 1b56de13e6fe..606b49928a28 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey.ts +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/no-selection/types/NoSelectionRecordActionsKey.ts @@ -1,3 +1,4 @@ export enum NoSelectionRecordActionKeys { EXPORT_VIEW = 'export-view-no-selection', + CREATE_NEW_RECORD = 'create-new-record-no-selection', } diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey.ts index b711d8076a81..128faca609a0 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey.ts +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/types/SingleRecordActionsKey.ts @@ -5,5 +5,5 @@ export enum SingleRecordActionKeys { REMOVE_FROM_FAVORITES = 'remove-from-favorites-single-record', NAVIGATE_TO_NEXT_RECORD = 'navigate-to-next-record-single-record', NAVIGATE_TO_PREVIOUS_RECORD = 'navigate-to-previous-record-single-record', - EXPORT_NOTE_TO_PDF = 'export-note-to-pdf', + EXPORT_NOTE_TO_PDF = 'export-note-to-pdf-single-record', } diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuButtons.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuButtons.tsx index 80c1080e0d58..04a45427c9fa 100644 --- a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuButtons.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuButtons.tsx @@ -1,23 +1,14 @@ import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector'; -import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { Button } from 'twenty-ui'; export const RecordIndexActionMenuButtons = () => { - const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2( - contextStoreNumberOfSelectedRecordsComponentState, - ); - const actionMenuEntries = useRecoilComponentValueV2( actionMenuEntriesComponentSelector, ); const pinnedEntries = actionMenuEntries.filter((entry) => entry.isPinned); - if (contextStoreNumberOfSelectedRecords === 0) { - return null; - } - return ( <> {pinnedEntries.map((entry, index) => ( diff --git a/packages/twenty-front/src/modules/action-menu/components/RightDrawerActionMenuDropdown.tsx b/packages/twenty-front/src/modules/action-menu/components/RightDrawerActionMenuDropdown.tsx index 4c60207d9288..a35f7dbef1bd 100644 --- a/packages/twenty-front/src/modules/action-menu/components/RightDrawerActionMenuDropdown.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/RightDrawerActionMenuDropdown.tsx @@ -8,11 +8,14 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { RightDrawerHotkeyScope } from '@/ui/layout/right-drawer/types/RightDrawerHotkeyScope'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; +import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useTheme } from '@emotion/react'; import { Key } from 'ts-key-enum'; import { Button, MenuItem } from 'twenty-ui'; +import { FeatureFlagKey } from '~/generated/graphql'; export const RightDrawerActionMenuDropdown = () => { const actionMenuEntries = useRecoilComponentValueV2( @@ -27,6 +30,10 @@ export const RightDrawerActionMenuDropdown = () => { const theme = useTheme(); + const isCommandMenuV2Enabled = useIsFeatureEnabled( + FeatureFlagKey.IsCommandMenuV2Enabled, + ); + useScopedHotkeys( [Key.Escape, 'ctrl+o,meta+o'], () => { @@ -45,7 +52,9 @@ export const RightDrawerActionMenuDropdown = () => { getRightDrawerActionMenuDropdownIdFromActionMenuId(actionMenuId), ); }, - RightDrawerHotkeyScope.RightDrawer, + isCommandMenuV2Enabled + ? AppHotkeyScope.CommandMenuOpen + : RightDrawerHotkeyScope.RightDrawer, [openDropdown], ); diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts index f5a6b0715c1a..9afb5d1f76b9 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenu.ts @@ -17,6 +17,7 @@ import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-sto import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; import { mainContextStoreComponentInstanceIdState } from '@/context-store/states/mainContextStoreComponentInstanceId'; import { viewableRecordIdState } from '@/object-record/record-right-drawer/states/viewableRecordIdState'; +import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState'; import { emitRightDrawerCloseEvent } from '@/ui/layout/right-drawer/utils/emitRightDrawerCloseEvent'; import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState'; @@ -242,9 +243,10 @@ export const useCommandMenu = () => { const openRecordInCommandMenu = useRecoilCallback( ({ set }) => { - return (recordId: string) => { + return (recordId: string, objectNameSingular: string) => { openCommandMenu(); set(commandMenuPageState, CommandMenuPages.ViewRecord); + set(viewableRecordNameSingularState, objectNameSingular); set(viewableRecordIdState, recordId); }; }, diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeader.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeader.tsx index 623e1cb7e397..229f69b52b60 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeader.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageHeader.tsx @@ -46,7 +46,8 @@ export const RecordIndexPageHeader = () => { const shouldDisplayAddButton = (numberOfSelectedRecords === 0 || !isCommandMenuV2Enabled) && - !isObjectMetadataItemReadOnly; + !isObjectMetadataItemReadOnly && + !isCommandMenuV2Enabled; const isTable = recordIndexViewType === ViewType.Table; diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageTableAddButtonNoGroup.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageTableAddButtonNoGroup.tsx index b9d4fbe65ba1..66b8c3a5830f 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageTableAddButtonNoGroup.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageTableAddButtonNoGroup.tsx @@ -4,9 +4,12 @@ import { PageAddButton } from '@/ui/layout/page/components/PageAddButton'; import { PageHotkeysEffect } from '@/ui/layout/page/components/PageHotkeysEffect'; export const RecordIndexPageTableAddButtonNoGroup = () => { - const { recordIndexId } = useRecordIndexContextOrThrow(); + const { recordIndexId, objectMetadataItem } = useRecordIndexContextOrThrow(); - const { createNewTableRecord } = useCreateNewTableRecord(recordIndexId); + const { createNewTableRecord } = useCreateNewTableRecord({ + objectMetadataItem, + recordTableId: recordIndexId, + }); const handleCreateNewTableRecord = () => { createNewTableRecord(); diff --git a/packages/twenty-front/src/modules/object-record/record-right-drawer/components/RightDrawerRecord.tsx b/packages/twenty-front/src/modules/object-record/record-right-drawer/components/RightDrawerRecord.tsx index c532bf4bb97d..4cd7a5507892 100644 --- a/packages/twenty-front/src/modules/object-record/record-right-drawer/components/RightDrawerRecord.tsx +++ b/packages/twenty-front/src/modules/object-record/record-right-drawer/components/RightDrawerRecord.tsx @@ -10,21 +10,15 @@ import { useRecordShowPage } from '@/object-record/record-show/hooks/useRecordSh import { RecordValueSetterEffect } from '@/object-record/record-store/components/RecordValueSetterEffect'; import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; -import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import styled from '@emotion/styled'; -import { FeatureFlagKey } from '~/generated/graphql'; -const StyledRightDrawerRecord = styled.div<{ hasTopBar: boolean }>` - height: ${({ theme, hasTopBar }) => - hasTopBar ? `calc(100% - ${theme.spacing(16)})` : '100%'}; +const StyledRightDrawerRecord = styled.div<{ isMobile: boolean }>` + height: ${({ theme, isMobile }) => + isMobile ? `calc(100% - ${theme.spacing(16)})` : '100%'}; `; export const RightDrawerRecord = () => { - const isCommandMenuV2Enabled = useIsFeatureEnabled( - FeatureFlagKey.IsCommandMenuV2Enabled, - ); const isMobile = useIsMobile(); - const hasTopBar = isCommandMenuV2Enabled || isMobile; const viewableRecordNameSingular = useRecoilValue( viewableRecordNameSingularState, @@ -56,7 +50,7 @@ export const RightDrawerRecord = () => { - + {!isNewViewableRecordLoading && ( diff --git a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateNoGroupNoRecordAtAll.tsx b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateNoGroupNoRecordAtAll.tsx index e48e9b1cee5f..540a45366ac5 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateNoGroupNoRecordAtAll.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateNoGroupNoRecordAtAll.tsx @@ -8,7 +8,10 @@ import { useCreateNewTableRecord } from '@/object-record/record-table/hooks/useC export const RecordTableEmptyStateNoGroupNoRecordAtAll = () => { const { objectMetadataItem, recordTableId } = useRecordTableContextOrThrow(); - const { createNewTableRecord } = useCreateNewTableRecord(recordTableId); + const { createNewTableRecord } = useCreateNewTableRecord({ + objectMetadataItem, + recordTableId, + }); const handleButtonClick = () => { createNewTableRecord(); diff --git a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateNoRecordFoundForFilter.tsx b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateNoRecordFoundForFilter.tsx index 22bebeb4d7db..329ff8de58c9 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateNoRecordFoundForFilter.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateNoRecordFoundForFilter.tsx @@ -8,7 +8,10 @@ import { useCreateNewTableRecord } from '@/object-record/record-table/hooks/useC export const RecordTableEmptyStateNoRecordFoundForFilter = () => { const { recordTableId, objectMetadataItem } = useRecordTableContextOrThrow(); - const { createNewTableRecord } = useCreateNewTableRecord(recordTableId); + const { createNewTableRecord } = useCreateNewTableRecord({ + objectMetadataItem, + recordTableId, + }); const handleButtonClick = () => { createNewTableRecord(); diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/useCreateNewTableRecords.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/useCreateNewTableRecords.ts index 8e3739fdcdf6..a5718cb27014 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/useCreateNewTableRecords.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/useCreateNewTableRecords.ts @@ -1,4 +1,6 @@ -import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext'; +import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; +import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord'; import { DEFAULT_CELL_SCOPE } from '@/object-record/record-table/record-table-cell/hooks/useOpenRecordTableCellV2'; import { useSelectedTableCellEditMode } from '@/object-record/record-table/record-table-cell/hooks/useSelectedTableCellEditMode'; import { recordTablePendingRecordIdByGroupComponentFamilyState } from '@/object-record/record-table/states/recordTablePendingRecordIdByGroupComponentFamilyState'; @@ -8,13 +10,19 @@ import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/drop import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; +import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useRecoilCallback } from 'recoil'; import { v4 } from 'uuid'; +import { FeatureFlagKey } from '~/generated/graphql'; import { isDefined } from '~/utils/isDefined'; -export const useCreateNewTableRecord = (recordTableId: string) => { - const { objectMetadataItem } = useRecordIndexContextOrThrow(); - +export const useCreateNewTableRecord = ({ + objectMetadataItem, + recordTableId, +}: { + objectMetadataItem: ObjectMetadataItem; + recordTableId: string; +}) => { const { setSelectedTableCellEditMode } = useSelectedTableCellEditMode({ scopeId: recordTableId, }); @@ -35,9 +43,27 @@ export const useCreateNewTableRecord = (recordTableId: string) => { const { setActiveDropdownFocusIdAndMemorizePrevious } = useSetActiveDropdownFocusIdAndMemorizePrevious(); - const createNewTableRecord = () => { + const { openRecordInCommandMenu } = useCommandMenu(); + + const isCommandMenuV2Enabled = useIsFeatureEnabled( + FeatureFlagKey.IsCommandMenuV2Enabled, + ); + + const { createOneRecord } = useCreateOneRecord({ + objectNameSingular: objectMetadataItem.nameSingular, + shouldMatchRootQueryFilter: true, + }); + + const createNewTableRecord = async () => { const recordId = v4(); + if (isCommandMenuV2Enabled) { + await createOneRecord({ id: recordId }); + + openRecordInCommandMenu(recordId, objectMetadataItem.nameSingular); + return; + } + setPendingRecordId(recordId); setSelectedTableCellEditMode(-1, 0); setHotkeyScope(DEFAULT_CELL_SCOPE.scope, DEFAULT_CELL_SCOPE.customScopes); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderCell.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderCell.tsx index 0b83fb3fea98..7b53df9c4411 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderCell.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderCell.tsx @@ -201,7 +201,10 @@ export const RecordTableHeaderCell = ({ const disableColumnResize = column.isLabelIdentifier && isMobile && !isRecordTableScrolledLeft; - const { createNewTableRecord } = useCreateNewTableRecord(recordTableId); + const { createNewTableRecord } = useCreateNewTableRecord({ + objectMetadataItem, + recordTableId, + }); const handlePlusButtonClick = () => { createNewTableRecord(); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupSectionAddNew.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupSectionAddNew.tsx index 929472098cc1..c765095b0dde 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupSectionAddNew.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupSectionAddNew.tsx @@ -7,7 +7,7 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/ import { IconPlus } from 'twenty-ui'; export const RecordTableRecordGroupSectionAddNew = () => { - const { recordTableId } = useRecordTableContextOrThrow(); + const { recordTableId, objectMetadataItem } = useRecordTableContextOrThrow(); const currentRecordGroupId = useCurrentRecordGroupId(); @@ -15,8 +15,10 @@ export const RecordTableRecordGroupSectionAddNew = () => { recordIndexAllRecordIdsComponentSelector, ); - const { createNewTableRecordInGroup } = - useCreateNewTableRecord(recordTableId); + const { createNewTableRecordInGroup } = useCreateNewTableRecord({ + objectMetadataItem, + recordTableId, + }); const handleAddNewRecord = () => { createNewTableRecordInGroup(currentRecordGroupId); diff --git a/packages/twenty-front/src/modules/object-record/utils/getRecordIndexIdFromObjectNamePlural.ts b/packages/twenty-front/src/modules/object-record/utils/getRecordIndexIdFromObjectNamePlural.ts new file mode 100644 index 000000000000..4dd869f86b5f --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/utils/getRecordIndexIdFromObjectNamePlural.ts @@ -0,0 +1,5 @@ +export const getRecordIndexIdFromObjectNamePlural = ( + objectNamePlural: string, +) => { + return objectNamePlural; +};