77 create new record action and remove old behavior (#9598)

Closes https://github.com/twentyhq/core-team-issues/issues/77
This commit is contained in:
Raphaël Bosi
2025-01-15 11:39:37 +01:00
committed by GitHub
parent 765dedab0a
commit 5fb6b18b18
16 changed files with 117 additions and 36 deletions

View File

@ -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,

View File

@ -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,
};
};

View File

@ -1,3 +1,4 @@
export enum NoSelectionRecordActionKeys {
EXPORT_VIEW = 'export-view-no-selection',
CREATE_NEW_RECORD = 'create-new-record-no-selection',
}

View File

@ -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',
}

View File

@ -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) => (

View File

@ -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],
);

View File

@ -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);
};
},

View File

@ -46,7 +46,8 @@ export const RecordIndexPageHeader = () => {
const shouldDisplayAddButton =
(numberOfSelectedRecords === 0 || !isCommandMenuV2Enabled) &&
!isObjectMetadataItemReadOnly;
!isObjectMetadataItemReadOnly &&
!isCommandMenuV2Enabled;
const isTable = recordIndexViewType === ViewType.Table;

View File

@ -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();

View File

@ -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 = () => {
<ActionMenuComponentInstanceContext.Provider
value={{ instanceId: `record-show-${objectRecordId}` }}
>
<StyledRightDrawerRecord hasTopBar={hasTopBar}>
<StyledRightDrawerRecord isMobile={isMobile}>
<RecordFieldValueSelectorContextProvider>
{!isNewViewableRecordLoading && (
<RecordValueSetterEffect recordId={objectRecordId} />

View File

@ -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();

View File

@ -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();

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -0,0 +1,5 @@
export const getRecordIndexIdFromObjectNamePlural = (
objectNamePlural: string,
) => {
return objectNamePlural;
};