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:
@ -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,
|
||||
|
||||
@ -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,
|
||||
};
|
||||
};
|
||||
@ -1,3 +1,4 @@
|
||||
export enum NoSelectionRecordActionKeys {
|
||||
EXPORT_VIEW = 'export-view-no-selection',
|
||||
CREATE_NEW_RECORD = 'create-new-record-no-selection',
|
||||
}
|
||||
|
||||
@ -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',
|
||||
}
|
||||
|
||||
@ -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) => (
|
||||
|
||||
@ -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],
|
||||
);
|
||||
|
||||
|
||||
@ -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);
|
||||
};
|
||||
},
|
||||
|
||||
@ -46,7 +46,8 @@ export const RecordIndexPageHeader = () => {
|
||||
|
||||
const shouldDisplayAddButton =
|
||||
(numberOfSelectedRecords === 0 || !isCommandMenuV2Enabled) &&
|
||||
!isObjectMetadataItemReadOnly;
|
||||
!isObjectMetadataItemReadOnly &&
|
||||
!isCommandMenuV2Enabled;
|
||||
|
||||
const isTable = recordIndexViewType === ViewType.Table;
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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} />
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
export const getRecordIndexIdFromObjectNamePlural = (
|
||||
objectNamePlural: string,
|
||||
) => {
|
||||
return objectNamePlural;
|
||||
};
|
||||
Reference in New Issue
Block a user